diff --git a/Makefile.am b/Makefile.am
index 9adc837..56458a9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,45 +1,10 @@
-bin_PROGRAMS = metamath
-
-noinst_HEADERS = \
-	mmcmdl.h \
-	mmcmds.h \
-	mmdata.h \
-	mmhlpa.h \
-	mmhlpb.h \
-	mminou.h \
-	mmmaci.h \
-	mmpars.h \
-	mmpfas.h \
-	mmunif.h \
-	mmutil.h \
-	mmveri.h \
-	mmvstr.h \
-	mmword.h \
-	mmwtex.h
-
-metamath_SOURCES = \
-	metamath.c \
-	mmcmdl.c \
-	mmcmds.c \
-	mmdata.c \
-	mmhlpa.c \
-	mmhlpb.c \
-	mminou.c \
-	mmmaci.c \
-	mmpars.c \
-	mmpfas.c \
-	mmunif.c \
-	mmutil.c \
-	mmveri.c \
-	mmvstr.c \
-	mmword.c \
-	mmwtex.c \
-	$(noinst_HEADERS)
+# This file is input to automake, that expands this high-level make description
+# to a Makefile containing the usual goals, and being capable of handling
+# portability issues.
 
+SUBDIRS = src
 
+# the following files are not used during build, but are distributed to users
 EXTRA_DIST = \
 	LICENSE.TXT \
-	README.TXT \
-	__README.TXT
-
-man_MANS = metamath.1
+	README.TXT
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..e2110ca
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,797 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# This file is input to automake, that expands this high-level make description
+# to a Makefile containing the usual goals, and being capable of handling
+# portability issues.
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
+	$(am__configure_deps) $(am__DIST_COMMON)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-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 \
+	tags-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
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	cscope distdir distdir-am dist dist-all distcheck
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \
+	config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
+	$(top_srcdir)/config/compile $(top_srcdir)/config/config.guess \
+	$(top_srcdir)/config/config.sub \
+	$(top_srcdir)/config/install-sh $(top_srcdir)/config/missing \
+	config/compile config/config.guess config/config.sub \
+	config/install-sh config/missing
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+  if test -d "$(distdir)"; then \
+    find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+      && rm -rf "$(distdir)" \
+      || { sleep 5 && rm -rf "$(distdir)"; }; \
+  else :; fi
+am__post_remove_distdir = $(am__remove_distdir)
+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"
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+DIST_TARGETS = dist-gzip
+# Exists only to be overridden by the user if desired.
+AM_DISTCHECK_DVI_TARGET = dvi
+distuninstallcheck_listfiles = find . -type f -print
+am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
+  | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+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@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = src
+
+# the following files are not used during build, but are distributed to users
+EXTRA_DIST = \
+	LICENSE.TXT \
+	README.TXT
+
+all: config.h
+	$(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+am--refresh: Makefile
+	@:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+	      $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    echo ' $(SHELL) ./config.status'; \
+	    $(SHELL) ./config.status;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	$(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	$(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+	@test -f $@ || rm -f stamp-h1
+	@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+	@rm -f stamp-h1
+	cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in:  $(am__configure_deps) 
+	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+	rm -f stamp-h1
+	touch $@
+
+distclean-hdr:
+	-rm -f config.h stamp-h1
+
+# 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.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	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"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	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; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscope: cscope.files
+	test ! -s cscope.files \
+	  || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
+clean-cscope:
+	-rm -f cscope.files
+cscope.files: clean-cscope cscopelist
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+	-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
+distdir: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+	$(am__remove_distdir)
+	test -d "$(distdir)" || mkdir "$(distdir)"
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(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
+	-test -n "$(am__skip_mode_fix)" \
+	|| find "$(distdir)" -type d ! -perm -755 \
+		-exec chmod u+rwx,go+rx {} \; -o \
+	  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+	|| chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+	tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
+	$(am__post_remove_distdir)
+
+dist-bzip2: distdir
+	tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
+	$(am__post_remove_distdir)
+
+dist-lzip: distdir
+	tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
+	$(am__post_remove_distdir)
+
+dist-xz: distdir
+	tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
+	$(am__post_remove_distdir)
+
+dist-zstd: distdir
+	tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst
+	$(am__post_remove_distdir)
+
+dist-tarZ: distdir
+	@echo WARNING: "Support for distribution archives compressed with" \
+		       "legacy program 'compress' is deprecated." >&2
+	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+	$(am__post_remove_distdir)
+
+dist-shar: distdir
+	@echo WARNING: "Support for shar distribution archives is" \
+	               "deprecated." >&2
+	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+	shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
+	$(am__post_remove_distdir)
+
+dist-zip: distdir
+	-rm -f $(distdir).zip
+	zip -rq $(distdir).zip $(distdir)
+	$(am__post_remove_distdir)
+
+dist dist-all:
+	$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
+	$(am__post_remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+	case '$(DIST_ARCHIVES)' in \
+	*.tar.gz*) \
+	  eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
+	*.tar.bz2*) \
+	  bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+	*.tar.lz*) \
+	  lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
+	*.tar.xz*) \
+	  xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+	*.tar.Z*) \
+	  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+	*.shar.gz*) \
+	  eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
+	*.zip*) \
+	  unzip $(distdir).zip ;;\
+	*.tar.zst*) \
+	  zstd -dc $(distdir).tar.zst | $(am__untar) ;;\
+	esac
+	chmod -R a-w $(distdir)
+	chmod u+w $(distdir)
+	mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
+	chmod a-w $(distdir)
+	test -d $(distdir)/_build || exit 0; \
+	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+	  && am__cwd=`pwd` \
+	  && $(am__cd) $(distdir)/_build/sub \
+	  && ../../configure \
+	    $(AM_DISTCHECK_CONFIGURE_FLAGS) \
+	    $(DISTCHECK_CONFIGURE_FLAGS) \
+	    --srcdir=../.. --prefix="$$dc_install_base" \
+	  && $(MAKE) $(AM_MAKEFLAGS) \
+	  && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \
+	  && $(MAKE) $(AM_MAKEFLAGS) check \
+	  && $(MAKE) $(AM_MAKEFLAGS) install \
+	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+	  && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+	  && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+	        distuninstallcheck \
+	  && chmod -R a-w "$$dc_install_base" \
+	  && ({ \
+	       (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+	            distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+	      } || { rm -rf "$$dc_destdir"; exit 1; }) \
+	  && rm -rf "$$dc_destdir" \
+	  && $(MAKE) $(AM_MAKEFLAGS) dist \
+	  && rm -rf $(DIST_ARCHIVES) \
+	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+	  && cd "$$am__cwd" \
+	  || exit 1
+	$(am__post_remove_distdir)
+	@(echo "$(distdir) archives ready for distribution: "; \
+	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+	  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+	@test -n '$(distuninstallcheck_dir)' || { \
+	  echo 'ERROR: trying to run $@ with an empty' \
+	       '$$(distuninstallcheck_dir)' >&2; \
+	  exit 1; \
+	}; \
+	$(am__cd) '$(distuninstallcheck_dir)' || { \
+	  echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
+	  exit 1; \
+	}; \
+	test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
+	   || { echo "ERROR: files left after uninstall:" ; \
+	        if test -n "$(DESTDIR)"; then \
+	          echo "  (check DESTDIR support)"; \
+	        fi ; \
+	        $(distuninstallcheck_listfiles) ; \
+	        exit 1; } >&2
+distcleancheck: distclean
+	@if test '$(srcdir)' = . ; then \
+	  echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+	  exit 1 ; \
+	fi
+	@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+	  || { echo "ERROR: files left in build directory after distclean:" ; \
+	       $(distcleancheck_listfiles) ; \
+	       exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile config.h
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	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-recursive
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -rf $(top_srcdir)/autom4te.cache
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) all install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+	am--refresh check check-am clean clean-cscope clean-generic \
+	cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \
+	dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \
+	dist-zstd distcheck distclean distclean-generic distclean-hdr \
+	distclean-tags distcleancheck distdir distuninstallcheck dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-info install-info-am install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs installdirs-am \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# 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/README.TXT b/README.TXT
index 5a35c5c..6279127 100644
--- a/README.TXT
+++ b/README.TXT
@@ -21,10 +21,11 @@ Instructions
 For Windows, click on "metamath.exe" and type "read set.mm".
 
 For Unix/Linux/Cygwin/MacOSX using the gcc compiler, compile with the
-command "gcc m*.c -o metamath", then type "./metamath set.mm" to run.
+command "cd src && gcc m*.c -o metamath", then type
+"./metamath set.mm" to run.
 
 As an alternative, if you have autoconf, automake, and a C compiler, you
-can compile with the command "autoreconf -i && ./configure && make".
+can compile with the command "cd src && autoreconf -i && ./configure && make".
 This "autoconf" approach automatically finds your compiler and its
 options, and configure takes the usual options (e.g., "--prefix=/usr").
 The resulting executable will typically be faster because it will check for
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..2154e6d
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,1150 @@
+# generated automatically by aclocal 1.16.5 -*- Autoconf -*-
+
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
+
+# This file 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.
+
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.71],,
+[m4_warning([this file was generated for autoconf 2.71.
+You have another version of autoconf.  It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically 'autoreconf'.])])
+
+# Copyright (C) 2002-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.16'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version.  Point them to the right macro.
+m4_if([$1], [1.16.5], [],
+      [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too.  Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.16.5])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
+
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to '$srcdir/foo'.  In other projects, it is set to
+# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is '.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+])
+
+# AM_CONDITIONAL                                            -*- Autoconf -*-
+
+# Copyright (C) 1997-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ([2.52])dnl
+ m4_if([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+  AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+
+# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+m4_if([$1], [CC],   [depcc="$CC"   am_compiler_list=],
+      [$1], [CXX],  [depcc="$CXX"  am_compiler_list=],
+      [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+      [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
+      [$1], [UPC],  [depcc="$UPC"  am_compiler_list=],
+      [$1], [GCJ],  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                    [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  am__universal=false
+  m4_case([$1], [CC],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac],
+    [CXX],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac])
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_$1_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE([dependency-tracking], [dnl
+AS_HELP_STRING(
+  [--enable-dependency-tracking],
+  [do not reject slow dependency extractors])
+AS_HELP_STRING(
+  [--disable-dependency-tracking],
+  [speeds up one-time build])])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+AC_SUBST([am__nodep])dnl
+_AM_SUBST_NOTMAKE([am__nodep])dnl
+])
+
+# Generate code to set up dependency tracking.              -*- Autoconf -*-
+
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+  # Older Autoconf quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  # TODO: see whether this extra hack can be removed once we start
+  # requiring Autoconf 2.70 or later.
+  AS_CASE([$CONFIG_FILES],
+          [*\'*], [eval set x "$CONFIG_FILES"],
+          [*], [set x $CONFIG_FILES])
+  shift
+  # Used to flag and report bootstrapping failures.
+  am_rc=0
+  for am_mf
+  do
+    # Strip MF so we end up with the name of the file.
+    am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile which includes
+    # dependency-tracking related rules and includes.
+    # Grep'ing the whole file directly is not great: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+      || continue
+    am_dirpart=`AS_DIRNAME(["$am_mf"])`
+    am_filepart=`AS_BASENAME(["$am_mf"])`
+    AM_RUN_LOG([cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles]) || am_rc=$?
+  done
+  if test $am_rc -ne 0; then
+    AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
+    for automatic dependency tracking.  If GNU make was not used, consider
+    re-running the configure script with MAKE="gmake" (or whatever is
+    necessary).  You can also try re-running configure with the
+    '--disable-dependency-tracking' option to at least be able to build
+    the package (albeit without support for automatic dependency tracking).])
+  fi
+  AS_UNSET([am_dirpart])
+  AS_UNSET([am_filepart])
+  AS_UNSET([am_mf])
+  AS_UNSET([am_rc])
+  rm -f conftest-deps.mk
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking is enabled.
+# This creates each '.Po' and '.Plo' makefile fragment that we'll need in
+# order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+     [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
+
+# Do all the work for Automake.                             -*- Autoconf -*-
+
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
+#
+# This file 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 macro actually does too much.  Some checks are only needed if
+# your package does certain things.  But this isn't really a big deal.
+
+dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
+m4_define([AC_PROG_CC],
+m4_defn([AC_PROG_CC])
+[_AM_PROG_CC_C_O
+])
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out.  PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition.  After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.65])dnl
+m4_ifdef([_$0_ALREADY_INIT],
+  [m4_fatal([$0 expanded multiple times
+]m4_defn([_$0_ALREADY_INIT]))],
+  [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl
+dnl Autoconf wants to disallow AM_ names.  We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[AC_DIAGNOSE([obsolete],
+             [$0: two- and three-arguments forms are deprecated.])
+m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(
+  m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]),
+  [ok:ok],,
+  [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+AM_MISSING_PROG([AUTOCONF], [autoconf])
+AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+AM_MISSING_PROG([AUTOHEADER], [autoheader])
+AM_MISSING_PROG([MAKEINFO], [makeinfo])
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
+# We need awk for the "check" target (and possibly the TAP driver).  The
+# system "awk" is bad on some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+	      [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+			     [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+		  [_AM_DEPENDENCIES([CC])],
+		  [m4_define([AC_PROG_CC],
+			     m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+		  [_AM_DEPENDENCIES([CXX])],
+		  [m4_define([AC_PROG_CXX],
+			     m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+		  [_AM_DEPENDENCIES([OBJC])],
+		  [m4_define([AC_PROG_OBJC],
+			     m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
+		  [_AM_DEPENDENCIES([OBJCXX])],
+		  [m4_define([AC_PROG_OBJCXX],
+			     m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
+])
+# Variables for tags utilities; see am/tags.am
+if test -z "$CTAGS"; then
+  CTAGS=ctags
+fi
+AC_SUBST([CTAGS])
+if test -z "$ETAGS"; then
+  ETAGS=etags
+fi
+AC_SUBST([ETAGS])
+if test -z "$CSCOPE"; then
+  CSCOPE=cscope
+fi
+AC_SUBST([CSCOPE])
+
+AC_REQUIRE([AM_SILENT_RULES])dnl
+dnl The testsuite driver may need to know about EXEEXT, so add the
+dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This
+dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+  [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes.  So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+  cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present.  This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message.  This
+can help us improve future automake versions.
+
+END
+  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+    echo 'Configuration will proceed anyway, since you have set the' >&2
+    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+    echo >&2
+  else
+    cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+    AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
+  fi
+fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
+])
+
+dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+AC_SUBST([install_sh])])
+
+# Copyright (C) 2003-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot.  For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Check to see how 'make' treats includes.	            -*- Autoconf -*-
+
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check whether make has an 'include' directive that can support all
+# the idioms we need for our automatic dependency tracking code.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
+cat > confinc.mk << 'END'
+am__doit:
+	@echo this is the am__doit target >confinc.out
+.PHONY: am__doit
+END
+am__include="#"
+am__quote=
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+  AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
+  AS_CASE([$?:`cat confinc.out 2>/dev/null`],
+      ['0:this is the am__doit target'],
+      [AS_CASE([$s],
+          [BSD], [am__include='.include' am__quote='"'],
+          [am__include='include' am__quote=''])])
+  if test "$am__include" != "#"; then
+    _am_result="yes ($s style)"
+    break
+  fi
+done
+rm -f confinc.* confmf.*
+AC_MSG_RESULT([${_am_result}])
+AC_SUBST([am__include])])
+AC_SUBST([am__quote])])
+
+# Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
+
+# Copyright (C) 1997-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it is modern enough.
+# If it is, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+  MISSING="\${SHELL} '$am_aux_dir/missing'"
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  AC_MSG_WARN(['missing' script is too old or missing])
+fi
+])
+
+# Helper functions for option handling.                     -*- Autoconf -*-
+
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# --------------------
+# Set option NAME.  Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_PROG_CC_C_O
+# ---------------
+# Like AC_PROG_CC_C_O, but changed for automake.  We rewrite AC_PROG_CC
+# to automatically call this.
+AC_DEFUN([_AM_PROG_CC_C_O],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+AC_LANG_PUSH([C])dnl
+AC_CACHE_CHECK(
+  [whether $CC understands -c and -o together],
+  [am_cv_prog_cc_c_o],
+  [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i])
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+AC_LANG_POP([C])])
+
+# For backward compatibility.
+AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
+
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+   ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   (exit $ac_status); }])
+
+# Check to make sure that the build environment is sane.    -*- Autoconf -*-
+
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[[\\\"\#\$\&\'\`$am_lf]]*)
+    AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+  *[[\\\"\#\$\&\'\`$am_lf\ \	]]*)
+    AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$[*]" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$[*]" != "X $srcdir/configure conftest.file" \
+	&& test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment])
+     fi
+     if test "$[2]" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT([yes])
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+AC_CONFIG_COMMANDS_PRE(
+  [AC_MSG_CHECKING([that generated files are newer than configure])
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   AC_MSG_RESULT([done])])
+rm -f conftest.file
+])
+
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# ("yes" being less verbose, "no" or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+  [--enable-silent-rules],
+  [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+  [--disable-silent-rules],
+  [verbose build output (undo: "make V=0")])dnl
+])
+case $enable_silent_rules in @%:@ (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+dnl
+dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+   [am_cv_make_support_nested_variables],
+   [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+  dnl Using '$V' instead of '$(V)' breaks IRIX make.
+  AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor 'install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in "make install-strip", and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
+if test "$cross_compiling" != no; then
+  AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# --------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball.                            -*- Autoconf -*-
+
+# Copyright (C) 2004-2021 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of 'v7', 'ustar', or 'pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+#     tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+#     $(am__untar) < result.tar
+#
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+
+m4_if([$1], [v7],
+  [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+
+  [m4_case([$1],
+    [ustar],
+     [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
+      # There is notably a 21 bits limit for the UID and the GID.  In fact,
+      # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
+      # and bug#13588).
+      am_max_uid=2097151 # 2^21 - 1
+      am_max_gid=$am_max_uid
+      # The $UID and $GID variables are not portable, so we need to resort
+      # to the POSIX-mandated id(1) utility.  Errors in the 'id' calls
+      # below are definitely unexpected, so allow the users to see them
+      # (that is, avoid stderr redirection).
+      am_uid=`id -u || echo unknown`
+      am_gid=`id -g || echo unknown`
+      AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
+      if test $am_uid -le $am_max_uid; then
+         AC_MSG_RESULT([yes])
+      else
+         AC_MSG_RESULT([no])
+         _am_tools=none
+      fi
+      AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
+      if test $am_gid -le $am_max_gid; then
+         AC_MSG_RESULT([yes])
+      else
+        AC_MSG_RESULT([no])
+        _am_tools=none
+      fi],
+
+  [pax],
+    [],
+
+  [m4_fatal([Unknown tar format])])
+
+  AC_MSG_CHECKING([how to create a $1 tar archive])
+
+  # Go ahead even if we have the value already cached.  We do so because we
+  # need to set the values for the 'am__tar' and 'am__untar' variables.
+  _am_tools=${am_cv_prog_tar_$1-$_am_tools}
+
+  for _am_tool in $_am_tools; do
+    case $_am_tool in
+    gnutar)
+      for _am_tar in tar gnutar gtar; do
+        AM_RUN_LOG([$_am_tar --version]) && break
+      done
+      am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+      am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+      am__untar="$_am_tar -xf -"
+      ;;
+    plaintar)
+      # Must skip GNU tar: if it does not support --format= it doesn't create
+      # ustar tarball either.
+      (tar --version) >/dev/null 2>&1 && continue
+      am__tar='tar chf - "$$tardir"'
+      am__tar_='tar chf - "$tardir"'
+      am__untar='tar xf -'
+      ;;
+    pax)
+      am__tar='pax -L -x $1 -w "$$tardir"'
+      am__tar_='pax -L -x $1 -w "$tardir"'
+      am__untar='pax -r'
+      ;;
+    cpio)
+      am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+      am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+      am__untar='cpio -i -H $1 -d'
+      ;;
+    none)
+      am__tar=false
+      am__tar_=false
+      am__untar=false
+      ;;
+    esac
+
+    # If the value was cached, stop now.  We just wanted to have am__tar
+    # and am__untar set.
+    test -n "${am_cv_prog_tar_$1}" && break
+
+    # tar/untar a dummy directory, and stop if the command works.
+    rm -rf conftest.dir
+    mkdir conftest.dir
+    echo GrepMe > conftest.dir/file
+    AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+    rm -rf conftest.dir
+    if test -s conftest.tar; then
+      AM_RUN_LOG([$am__untar <conftest.tar])
+      AM_RUN_LOG([cat conftest.dir/file])
+      grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+    fi
+  done
+  rm -rf conftest.dir
+
+  AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+  AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..d9788a1
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,92 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+   to 0 otherwise. */
+#undef HAVE_MALLOC
+
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+   and to 0 otherwise. */
+#undef HAVE_REALLOC
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#undef HAVE_STDBOOL_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strcspn' function. */
+#undef HAVE_STRCSPN
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if the system has the type `_Bool'. */
+#undef HAVE__BOOL
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to rpl_malloc if the replacement function should be used. */
+#undef malloc
+
+/* Define to rpl_realloc if the replacement function should be used. */
+#undef realloc
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
diff --git a/config/compile b/config/compile
new file mode 100755
index 0000000..df363c8
--- /dev/null
+++ b/config/compile
@@ -0,0 +1,348 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand '-c -o'.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program 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 2, or (at your option)
+# any later version.
+#
+# This program 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 <https://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+nl='
+'
+
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent tools from complaining about whitespace usage.
+IFS=" ""	$nl"
+
+file_conv=
+
+# func_file_conv build_file lazy
+# Convert a $build file to $host form and store it in $file
+# Currently only supports Windows hosts. If the determined conversion
+# type is listed in (the comma separated) LAZY, no conversion will
+# take place.
+func_file_conv ()
+{
+  file=$1
+  case $file in
+    / | /[!/]*) # absolute file, and not a UNC file
+      if test -z "$file_conv"; then
+	# lazily determine how to convert abs files
+	case `uname -s` in
+	  MINGW*)
+	    file_conv=mingw
+	    ;;
+	  CYGWIN* | MSYS*)
+	    file_conv=cygwin
+	    ;;
+	  *)
+	    file_conv=wine
+	    ;;
+	esac
+      fi
+      case $file_conv/,$2, in
+	*,$file_conv,*)
+	  ;;
+	mingw/*)
+	  file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
+	  ;;
+	cygwin/* | msys/*)
+	  file=`cygpath -m "$file" || echo "$file"`
+	  ;;
+	wine/*)
+	  file=`winepath -w "$file" || echo "$file"`
+	  ;;
+      esac
+      ;;
+  esac
+}
+
+# func_cl_dashL linkdir
+# Make cl look for libraries in LINKDIR
+func_cl_dashL ()
+{
+  func_file_conv "$1"
+  if test -z "$lib_path"; then
+    lib_path=$file
+  else
+    lib_path="$lib_path;$file"
+  fi
+  linker_opts="$linker_opts -LIBPATH:$file"
+}
+
+# func_cl_dashl library
+# Do a library search-path lookup for cl
+func_cl_dashl ()
+{
+  lib=$1
+  found=no
+  save_IFS=$IFS
+  IFS=';'
+  for dir in $lib_path $LIB
+  do
+    IFS=$save_IFS
+    if $shared && test -f "$dir/$lib.dll.lib"; then
+      found=yes
+      lib=$dir/$lib.dll.lib
+      break
+    fi
+    if test -f "$dir/$lib.lib"; then
+      found=yes
+      lib=$dir/$lib.lib
+      break
+    fi
+    if test -f "$dir/lib$lib.a"; then
+      found=yes
+      lib=$dir/lib$lib.a
+      break
+    fi
+  done
+  IFS=$save_IFS
+
+  if test "$found" != yes; then
+    lib=$lib.lib
+  fi
+}
+
+# func_cl_wrapper cl arg...
+# Adjust compile command to suit cl
+func_cl_wrapper ()
+{
+  # Assume a capable shell
+  lib_path=
+  shared=:
+  linker_opts=
+  for arg
+  do
+    if test -n "$eat"; then
+      eat=
+    else
+      case $1 in
+	-o)
+	  # configure might choose to run compile as 'compile cc -o foo foo.c'.
+	  eat=1
+	  case $2 in
+	    *.o | *.[oO][bB][jJ])
+	      func_file_conv "$2"
+	      set x "$@" -Fo"$file"
+	      shift
+	      ;;
+	    *)
+	      func_file_conv "$2"
+	      set x "$@" -Fe"$file"
+	      shift
+	      ;;
+	  esac
+	  ;;
+	-I)
+	  eat=1
+	  func_file_conv "$2" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-I*)
+	  func_file_conv "${1#-I}" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-l)
+	  eat=1
+	  func_cl_dashl "$2"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-l*)
+	  func_cl_dashl "${1#-l}"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-L)
+	  eat=1
+	  func_cl_dashL "$2"
+	  ;;
+	-L*)
+	  func_cl_dashL "${1#-L}"
+	  ;;
+	-static)
+	  shared=false
+	  ;;
+	-Wl,*)
+	  arg=${1#-Wl,}
+	  save_ifs="$IFS"; IFS=','
+	  for flag in $arg; do
+	    IFS="$save_ifs"
+	    linker_opts="$linker_opts $flag"
+	  done
+	  IFS="$save_ifs"
+	  ;;
+	-Xlinker)
+	  eat=1
+	  linker_opts="$linker_opts $2"
+	  ;;
+	-*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+	*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
+	  func_file_conv "$1"
+	  set x "$@" -Tp"$file"
+	  shift
+	  ;;
+	*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
+	  func_file_conv "$1" mingw
+	  set x "$@" "$file"
+	  shift
+	  ;;
+	*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+      esac
+    fi
+    shift
+  done
+  if test -n "$linker_opts"; then
+    linker_opts="-link$linker_opts"
+  fi
+  exec "$@" $linker_opts
+  exit 1
+}
+
+eat=
+
+case $1 in
+  '')
+     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand '-c -o'.
+Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file 'INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit $?
+    ;;
+  cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
+  icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
+    func_cl_wrapper "$@"      # Doesn't return...
+    ;;
+esac
+
+ofile=
+cfile=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+	# configure might choose to run compile as 'compile cc -o foo foo.c'.
+	# So we strip '-o arg' only if arg is an object.
+	eat=1
+	case $2 in
+	  *.o | *.obj)
+	    ofile=$2
+	    ;;
+	  *)
+	    set x "$@" -o "$2"
+	    shift
+	    ;;
+	esac
+	;;
+      *.c)
+	cfile=$1
+	set x "$@" "$1"
+	shift
+	;;
+      *)
+	set x "$@" "$1"
+	shift
+	;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no '-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # '.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use '[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/config/config.guess b/config/config.guess
new file mode 100755
index 0000000..7f76b62
--- /dev/null
+++ b/config/config.guess
@@ -0,0 +1,1754 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2022-01-09'
+
+# This file 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.
+#
+# This program 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 <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2022 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+# Just in case it came from the environment.
+GUESS=
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+    # prevent multiple calls if $tmp is already set
+    test "$tmp" && return 0
+    : "${TMPDIR=/tmp}"
+    # shellcheck disable=SC2039,SC3028
+    { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+	{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+	{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+	{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+    dummy=$tmp/dummy
+    case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+	,,)    echo "int x;" > "$dummy.c"
+	       for driver in cc gcc c89 c99 ; do
+		   if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+		       CC_FOR_BUILD=$driver
+		       break
+		   fi
+	       done
+	       if test x"$CC_FOR_BUILD" = x ; then
+		   CC_FOR_BUILD=no_compiler_found
+	       fi
+	       ;;
+	,,*)   CC_FOR_BUILD=$CC ;;
+	,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+    esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case $UNAME_SYSTEM in
+Linux|GNU|GNU/*)
+	LIBC=unknown
+
+	set_cc_for_build
+	cat <<-EOF > "$dummy.c"
+	#include <features.h>
+	#if defined(__UCLIBC__)
+	LIBC=uclibc
+	#elif defined(__dietlibc__)
+	LIBC=dietlibc
+	#elif defined(__GLIBC__)
+	LIBC=gnu
+	#else
+	#include <stdarg.h>
+	/* First heuristic to detect musl libc.  */
+	#ifdef __DEFINED_va_list
+	LIBC=musl
+	#endif
+	#endif
+	EOF
+	cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+	eval "$cc_set_libc"
+
+	# Second heuristic to detect musl libc.
+	if [ "$LIBC" = unknown ] &&
+	   command -v ldd >/dev/null &&
+	   ldd --version 2>&1 | grep -q ^musl; then
+		LIBC=musl
+	fi
+
+	# If the system lacks a compiler, then just pick glibc.
+	# We could probably try harder.
+	if [ "$LIBC" = unknown ]; then
+		LIBC=gnu
+	fi
+	;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+	    /sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+	    /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+	    echo unknown)`
+	case $UNAME_MACHINE_ARCH in
+	    aarch64eb) machine=aarch64_be-unknown ;;
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    sh5el) machine=sh5le-unknown ;;
+	    earmv*)
+		arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+		endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+		machine=${arch}${endian}-unknown
+		;;
+	    *) machine=$UNAME_MACHINE_ARCH-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently (or will in the future) and ABI.
+	case $UNAME_MACHINE_ARCH in
+	    earm*)
+		os=netbsdelf
+		;;
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep -q __ELF__
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+		os=netbsd
+		;;
+	esac
+	# Determine ABI tags.
+	case $UNAME_MACHINE_ARCH in
+	    earm*)
+		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+		abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case $UNAME_VERSION in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	GUESS=$machine-${os}${release}${abi-}
+	;;
+    *:Bitrig:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
+	;;
+    *:OpenBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
+	;;
+    *:SecBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
+	;;
+    *:LibertyBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
+	;;
+    *:MidnightBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
+	;;
+    *:ekkoBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
+	;;
+    *:SolidBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
+	;;
+    *:OS108:*:*)
+	GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
+	;;
+    macppc:MirBSD:*:*)
+	GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
+	;;
+    *:MirBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
+	;;
+    *:Sortix:*:*)
+	GUESS=$UNAME_MACHINE-unknown-sortix
+	;;
+    *:Twizzler:*:*)
+	GUESS=$UNAME_MACHINE-unknown-twizzler
+	;;
+    *:Redox:*:*)
+	GUESS=$UNAME_MACHINE-unknown-redox
+	;;
+    mips:OSF1:*.*)
+	GUESS=mips-dec-osf1
+	;;
+    alpha:OSF1:*:*)
+	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+	trap '' 0
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case $ALPHA_CPU_TYPE in
+	    "EV4 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE=alpha ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE=alphaev5 ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE=alphaev56 ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE=alphapca56 ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE=alphapca57 ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE=alphaev6 ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE=alphaev67 ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE=alphaev69 ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE=alphaev7 ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE=alphaev79 ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
+	;;
+    Amiga*:UNIX_System_V:4.0:*)
+	GUESS=m68k-unknown-sysv4
+	;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	GUESS=$UNAME_MACHINE-unknown-amigaos
+	;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	GUESS=$UNAME_MACHINE-unknown-morphos
+	;;
+    *:OS/390:*:*)
+	GUESS=i370-ibm-openedition
+	;;
+    *:z/VM:*:*)
+	GUESS=s390-ibm-zvmoe
+	;;
+    *:OS400:*:*)
+	GUESS=powerpc-ibm-os400
+	;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	GUESS=arm-acorn-riscix$UNAME_RELEASE
+	;;
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
+	GUESS=arm-unknown-riscos
+	;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	GUESS=hppa1.1-hitachi-hiuxmpp
+	;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	case `(/bin/universe) 2>/dev/null` in
+	    att) GUESS=pyramid-pyramid-sysv3 ;;
+	    *)   GUESS=pyramid-pyramid-bsd   ;;
+	esac
+	;;
+    NILE*:*:*:dcosx)
+	GUESS=pyramid-pyramid-svr4
+	;;
+    DRS?6000:unix:4.0:6*)
+	GUESS=sparc-icl-nx6
+	;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) GUESS=sparc-icl-nx7 ;;
+	esac
+	;;
+    s390x:SunOS:*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
+	;;
+    sun4H:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-hal-solaris2$SUN_REL
+	;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-sun-solaris2$SUN_REL
+	;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+	GUESS=i386-pc-auroraux$UNAME_RELEASE
+	;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+	set_cc_for_build
+	SUN_ARCH=i386
+	# If there is a compiler, see if it is configured for 64-bit objects.
+	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+	# This test works for both compilers.
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+		(CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
+		grep IS_64BIT_ARCH >/dev/null
+	    then
+		SUN_ARCH=x86_64
+	    fi
+	fi
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
+	;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-sun-solaris3$SUN_REL
+	;;
+    sun4*:SunOS:*:*)
+	case `/usr/bin/arch -k` in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
+	GUESS=sparc-sun-sunos$SUN_REL
+	;;
+    sun3*:SunOS:*:*)
+	GUESS=m68k-sun-sunos$UNAME_RELEASE
+	;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+	case `/bin/arch` in
+	    sun3)
+		GUESS=m68k-sun-sunos$UNAME_RELEASE
+		;;
+	    sun4)
+		GUESS=sparc-sun-sunos$UNAME_RELEASE
+		;;
+	esac
+	;;
+    aushp:SunOS:*:*)
+	GUESS=sparc-auspex-sunos$UNAME_RELEASE
+	;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+	GUESS=m68k-milan-mint$UNAME_RELEASE
+	;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+	GUESS=m68k-hades-mint$UNAME_RELEASE
+	;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+	GUESS=m68k-unknown-mint$UNAME_RELEASE
+	;;
+    m68k:machten:*:*)
+	GUESS=m68k-apple-machten$UNAME_RELEASE
+	;;
+    powerpc:machten:*:*)
+	GUESS=powerpc-apple-machten$UNAME_RELEASE
+	;;
+    RISC*:Mach:*:*)
+	GUESS=mips-dec-mach_bsd4.3
+	;;
+    RISC*:ULTRIX:*:*)
+	GUESS=mips-dec-ultrix$UNAME_RELEASE
+	;;
+    VAX*:ULTRIX*:*:*)
+	GUESS=vax-dec-ultrix$UNAME_RELEASE
+	;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	GUESS=clipper-intergraph-clix$UNAME_RELEASE
+	;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	set_cc_for_build
+	sed 's/^	//' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+	  dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+	  SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+	    { echo "$SYSTEM_NAME"; exit; }
+	GUESS=mips-mips-riscos$UNAME_RELEASE
+	;;
+    Motorola:PowerMAX_OS:*:*)
+	GUESS=powerpc-motorola-powermax
+	;;
+    Motorola:*:4.3:PL8-*)
+	GUESS=powerpc-harris-powermax
+	;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	GUESS=powerpc-harris-powermax
+	;;
+    Night_Hawk:Power_UNIX:*:*)
+	GUESS=powerpc-harris-powerunix
+	;;
+    m88k:CX/UX:7*:*)
+	GUESS=m88k-harris-cxux7
+	;;
+    m88k:*:4*:R4*)
+	GUESS=m88k-motorola-sysv4
+	;;
+    m88k:*:3*:R3*)
+	GUESS=m88k-motorola-sysv3
+	;;
+    AViiON:dgux:*:*)
+	# DG/UX returns AViiON for all architectures
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
+	then
+	    if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+	       test "$TARGET_BINARY_INTERFACE"x = x
+	    then
+		GUESS=m88k-dg-dgux$UNAME_RELEASE
+	    else
+		GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
+	    fi
+	else
+	    GUESS=i586-dg-dgux$UNAME_RELEASE
+	fi
+	;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	GUESS=m88k-dolphin-sysv3
+	;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	GUESS=m88k-motorola-sysv3
+	;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	GUESS=m88k-tektronix-sysv3
+	;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	GUESS=m68k-tektronix-bsd
+	;;
+    *:IRIX*:*:*)
+	IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
+	GUESS=mips-sgi-irix$IRIX_REL
+	;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	GUESS=romp-ibm-aix    # uname -m gives an 8 hex-code CPU id
+	;;                    # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	GUESS=i386-ibm-aix
+	;;
+    ia64:AIX:*:*)
+	if test -x /usr/bin/oslevel ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+	fi
+	GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
+	;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		set_cc_for_build
+		sed 's/^		//' << EOF > "$dummy.c"
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+		then
+			GUESS=$SYSTEM_NAME
+		else
+			GUESS=rs6000-ibm-aix3.2.5
+		fi
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		GUESS=rs6000-ibm-aix3.2.4
+	else
+		GUESS=rs6000-ibm-aix3.2
+	fi
+	;;
+    *:AIX:*:[4567])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if test -x /usr/bin/lslpp ; then
+		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
+			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+	else
+		IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+	fi
+	GUESS=$IBM_ARCH-ibm-aix$IBM_REV
+	;;
+    *:AIX:*:*)
+	GUESS=rs6000-ibm-aix
+	;;
+    ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+	GUESS=romp-ibm-bsd4.4
+	;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	GUESS=romp-ibm-bsd$UNAME_RELEASE    # 4.3 with uname added to
+	;;                                  # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	GUESS=rs6000-bull-bosx
+	;;
+    DPX/2?00:B.O.S.:*:*)
+	GUESS=m68k-bull-sysv3
+	;;
+    9000/[34]??:4.3bsd:1.*:*)
+	GUESS=m68k-hp-bsd
+	;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	GUESS=m68k-hp-bsd4.4
+	;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+	case $UNAME_MACHINE in
+	    9000/31?)            HP_ARCH=m68000 ;;
+	    9000/[34]??)         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if test -x /usr/bin/getconf; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+		    case $sc_cpu_version in
+		      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+		      532)                      # CPU_PA_RISC2_0
+			case $sc_kernel_bits in
+			  32) HP_ARCH=hppa2.0n ;;
+			  64) HP_ARCH=hppa2.0w ;;
+			  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
+			esac ;;
+		    esac
+		fi
+		if test "$HP_ARCH" = ""; then
+		    set_cc_for_build
+		    sed 's/^		//' << EOF > "$dummy.c"
+
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
+
+		int main ()
+		{
+		#if defined(_SC_KERNEL_BITS)
+		    long bits = sysconf(_SC_KERNEL_BITS);
+		#endif
+		    long cpu  = sysconf (_SC_CPU_VERSION);
+
+		    switch (cpu)
+			{
+			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+			case CPU_PA_RISC2_0:
+		#if defined(_SC_KERNEL_BITS)
+			    switch (bits)
+				{
+				case 64: puts ("hppa2.0w"); break;
+				case 32: puts ("hppa2.0n"); break;
+				default: puts ("hppa2.0"); break;
+				} break;
+		#else  /* !defined(_SC_KERNEL_BITS) */
+			    puts ("hppa2.0"); break;
+		#endif
+			default: puts ("hppa1.0"); break;
+			}
+		    exit (0);
+		}
+EOF
+		    (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if test "$HP_ARCH" = hppa2.0w
+	then
+	    set_cc_for_build
+
+	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+	    # generating 64-bit code.  GNU and HP use different nomenclature:
+	    #
+	    # $ CC_FOR_BUILD=cc ./config.guess
+	    # => hppa2.0w-hp-hpux11.23
+	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+	    # => hppa64-hp-hpux11.23
+
+	    if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+		grep -q __LP64__
+	    then
+		HP_ARCH=hppa2.0w
+	    else
+		HP_ARCH=hppa64
+	    fi
+	fi
+	GUESS=$HP_ARCH-hp-hpux$HPUX_REV
+	;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+	GUESS=ia64-hp-hpux$HPUX_REV
+	;;
+    3050*:HI-UX:*:*)
+	set_cc_for_build
+	sed 's/^	//' << EOF > "$dummy.c"
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+		{ echo "$SYSTEM_NAME"; exit; }
+	GUESS=unknown-hitachi-hiuxwe2
+	;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+	GUESS=hppa1.1-hp-bsd
+	;;
+    9000/8??:4.3bsd:*:*)
+	GUESS=hppa1.0-hp-bsd
+	;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	GUESS=hppa1.0-hp-mpeix
+	;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+	GUESS=hppa1.1-hp-osf
+	;;
+    hp8??:OSF1:*:*)
+	GUESS=hppa1.0-hp-osf
+	;;
+    i*86:OSF1:*:*)
+	if test -x /usr/sbin/sysversion ; then
+	    GUESS=$UNAME_MACHINE-unknown-osf1mk
+	else
+	    GUESS=$UNAME_MACHINE-unknown-osf1
+	fi
+	;;
+    parisc*:Lites*:*:*)
+	GUESS=hppa1.1-hp-lites
+	;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	GUESS=c1-convex-bsd
+	;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	GUESS=c34-convex-bsd
+	;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	GUESS=c38-convex-bsd
+	;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	GUESS=c4-convex-bsd
+	;;
+    CRAY*Y-MP:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=ymp-cray-unicos$CRAY_REL
+	;;
+    CRAY*[A-Z]90:*:*:*)
+	echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*TS:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=t90-cray-unicos$CRAY_REL
+	;;
+    CRAY*T3E:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=alphaev5-cray-unicosmk$CRAY_REL
+	;;
+    CRAY*SV1:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=sv1-cray-unicos$CRAY_REL
+	;;
+    *:UNICOS/mp:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=craynv-cray-unicosmp$CRAY_REL
+	;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+	GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+	;;
+    5000:UNIX_System_V:4.*:*)
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+	GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+	;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
+	;;
+    sparc*:BSD/OS:*:*)
+	GUESS=sparc-unknown-bsdi$UNAME_RELEASE
+	;;
+    *:BSD/OS:*:*)
+	GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
+	;;
+    arm:FreeBSD:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	set_cc_for_build
+	if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_PCS_VFP
+	then
+	    FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	    GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
+	else
+	    FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	    GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
+	fi
+	;;
+    *:FreeBSD:*:*)
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	case $UNAME_PROCESSOR in
+	    amd64)
+		UNAME_PROCESSOR=x86_64 ;;
+	    i386)
+		UNAME_PROCESSOR=i586 ;;
+	esac
+	FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
+	;;
+    i*:CYGWIN*:*)
+	GUESS=$UNAME_MACHINE-pc-cygwin
+	;;
+    *:MINGW64*:*)
+	GUESS=$UNAME_MACHINE-pc-mingw64
+	;;
+    *:MINGW*:*)
+	GUESS=$UNAME_MACHINE-pc-mingw32
+	;;
+    *:MSYS*:*)
+	GUESS=$UNAME_MACHINE-pc-msys
+	;;
+    i*:PW*:*)
+	GUESS=$UNAME_MACHINE-pc-pw32
+	;;
+    *:SerenityOS:*:*)
+        GUESS=$UNAME_MACHINE-pc-serenity
+        ;;
+    *:Interix*:*)
+	case $UNAME_MACHINE in
+	    x86)
+		GUESS=i586-pc-interix$UNAME_RELEASE
+		;;
+	    authenticamd | genuineintel | EM64T)
+		GUESS=x86_64-unknown-interix$UNAME_RELEASE
+		;;
+	    IA64)
+		GUESS=ia64-unknown-interix$UNAME_RELEASE
+		;;
+	esac ;;
+    i*:UWIN*:*)
+	GUESS=$UNAME_MACHINE-pc-uwin
+	;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+	GUESS=x86_64-pc-cygwin
+	;;
+    prep*:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=powerpcle-unknown-solaris2$SUN_REL
+	;;
+    *:GNU:*:*)
+	# the GNU system
+	GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
+	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
+	GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
+	;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
+	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
+	;;
+    *:Minix:*:*)
+	GUESS=$UNAME_MACHINE-unknown-minix
+	;;
+    aarch64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    aarch64_be:Linux:*:*)
+	UNAME_MACHINE=aarch64_be
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+	esac
+	objdump --private-headers /bin/sh | grep -q ld.so.1
+	if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    arm*:Linux:*:*)
+	set_cc_for_build
+	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_EABI__
+	then
+	    GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	else
+	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+		| grep -q __ARM_PCS_VFP
+	    then
+		GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
+	    else
+		GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
+	    fi
+	fi
+	;;
+    avr32*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    cris:Linux:*:*)
+	GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+	;;
+    crisv32:Linux:*:*)
+	GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+	;;
+    e2k:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    frv:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    hexagon:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    i*86:Linux:*:*)
+	GUESS=$UNAME_MACHINE-pc-linux-$LIBC
+	;;
+    ia64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    k1om:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    m32r*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    m68*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+	set_cc_for_build
+	IS_GLIBC=0
+	test x"${LIBC}" = xgnu && IS_GLIBC=1
+	sed 's/^	//' << EOF > "$dummy.c"
+	#undef CPU
+	#undef mips
+	#undef mipsel
+	#undef mips64
+	#undef mips64el
+	#if ${IS_GLIBC} && defined(_ABI64)
+	LIBCABI=gnuabi64
+	#else
+	#if ${IS_GLIBC} && defined(_ABIN32)
+	LIBCABI=gnuabin32
+	#else
+	LIBCABI=${LIBC}
+	#endif
+	#endif
+
+	#if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+	CPU=mipsisa64r6
+	#else
+	#if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+	CPU=mipsisa32r6
+	#else
+	#if defined(__mips64)
+	CPU=mips64
+	#else
+	CPU=mips
+	#endif
+	#endif
+	#endif
+
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	MIPS_ENDIAN=el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	MIPS_ENDIAN=
+	#else
+	MIPS_ENDIAN=
+	#endif
+	#endif
+EOF
+	cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
+	eval "$cc_set_vars"
+	test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+	;;
+    mips64el:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    openrisc*:Linux:*:*)
+	GUESS=or1k-unknown-linux-$LIBC
+	;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    padre:Linux:*:*)
+	GUESS=sparc-unknown-linux-$LIBC
+	;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	GUESS=hppa64-unknown-linux-$LIBC
+	;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
+	  PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
+	  *)    GUESS=hppa-unknown-linux-$LIBC ;;
+	esac
+	;;
+    ppc64:Linux:*:*)
+	GUESS=powerpc64-unknown-linux-$LIBC
+	;;
+    ppc:Linux:*:*)
+	GUESS=powerpc-unknown-linux-$LIBC
+	;;
+    ppc64le:Linux:*:*)
+	GUESS=powerpc64le-unknown-linux-$LIBC
+	;;
+    ppcle:Linux:*:*)
+	GUESS=powerpcle-unknown-linux-$LIBC
+	;;
+    riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
+	;;
+    sh64*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    sh*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    tile*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    vax:Linux:*:*)
+	GUESS=$UNAME_MACHINE-dec-linux-$LIBC
+	;;
+    x86_64:Linux:*:*)
+	set_cc_for_build
+	LIBCABI=$LIBC
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
+		(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		grep IS_X32 >/dev/null
+	    then
+		LIBCABI=${LIBC}x32
+	    fi
+	fi
+	GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI
+	;;
+    xtensa*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	GUESS=i386-sequent-sysv4
+	;;
+    i*86:UNIX_SV:4.2MP:2.*)
+	# Unixware is an offshoot of SVR4, but it has its own version
+	# number series starting with 2...
+	# I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+	# Use sysv4.2uw... so that sysv4* matches it.
+	GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
+	;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	GUESS=$UNAME_MACHINE-pc-os2-emx
+	;;
+    i*86:XTS-300:*:STOP)
+	GUESS=$UNAME_MACHINE-unknown-stop
+	;;
+    i*86:atheos:*:*)
+	GUESS=$UNAME_MACHINE-unknown-atheos
+	;;
+    i*86:syllable:*:*)
+	GUESS=$UNAME_MACHINE-pc-syllable
+	;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+	GUESS=i386-unknown-lynxos$UNAME_RELEASE
+	;;
+    i*86:*DOS:*:*)
+	GUESS=$UNAME_MACHINE-pc-msdosdjgpp
+	;;
+    i*86:*:4.*:*)
+	UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
+	else
+		GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
+	fi
+	;;
+    i*86:*:5:[678]*)
+	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
+	else
+		GUESS=$UNAME_MACHINE-pc-sysv32
+	fi
+	;;
+    pc:*:*:*)
+	# Left here for compatibility:
+	# uname -m prints for DJGPP always 'pc', but it prints nothing about
+	# the processor, so we play safe by assuming i586.
+	# Note: whatever this is, it MUST be the same as what config.sub
+	# prints for the "djgpp" host, or else GDB configure will decide that
+	# this is a cross-build.
+	GUESS=i586-pc-msdosdjgpp
+	;;
+    Intel:Mach:3*:*)
+	GUESS=i386-pc-mach3
+	;;
+    paragon:*:*:*)
+	GUESS=i860-intel-osf1
+	;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  GUESS=i860-stardent-sysv$UNAME_RELEASE    # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  GUESS=i860-unknown-sysv$UNAME_RELEASE     # Unknown i860-SVR4
+	fi
+	;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	GUESS=m68010-convergent-sysv
+	;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	GUESS=m68k-convergent-sysv
+	;;
+    M680?0:D-NIX:5.3:*)
+	GUESS=m68k-diab-dnix
+	;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+	OS_REL='.3'
+	test -r /etc/.relid \
+	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	    && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	GUESS=m68k-unknown-lynxos$UNAME_RELEASE
+	;;
+    mc68030:UNIX_System_V:4.*:*)
+	GUESS=m68k-atari-sysv4
+	;;
+    TSUNAMI:LynxOS:2.*:*)
+	GUESS=sparc-unknown-lynxos$UNAME_RELEASE
+	;;
+    rs6000:LynxOS:2.*:*)
+	GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
+	;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+	GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
+	;;
+    SM[BE]S:UNIX_SV:*:*)
+	GUESS=mips-dde-sysv$UNAME_RELEASE
+	;;
+    RM*:ReliantUNIX-*:*:*)
+	GUESS=mips-sni-sysv4
+	;;
+    RM*:SINIX-*:*:*)
+	GUESS=mips-sni-sysv4
+	;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		GUESS=$UNAME_MACHINE-sni-sysv4
+	else
+		GUESS=ns32k-sni-sysv
+	fi
+	;;
+    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	GUESS=i586-unisys-sysv4
+	;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	GUESS=hppa1.1-stratus-sysv4
+	;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	GUESS=i860-stratus-sysv4
+	;;
+    i*86:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	GUESS=$UNAME_MACHINE-stratus-vos
+	;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	GUESS=hppa1.1-stratus-vos
+	;;
+    mc68*:A/UX:*:*)
+	GUESS=m68k-apple-aux$UNAME_RELEASE
+	;;
+    news*:NEWS-OS:6*:*)
+	GUESS=mips-sony-newsos6
+	;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if test -d /usr/nec; then
+		GUESS=mips-nec-sysv$UNAME_RELEASE
+	else
+		GUESS=mips-unknown-sysv$UNAME_RELEASE
+	fi
+	;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	GUESS=powerpc-be-beos
+	;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	GUESS=powerpc-apple-beos
+	;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	GUESS=i586-pc-beos
+	;;
+    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+	GUESS=i586-pc-haiku
+	;;
+    x86_64:Haiku:*:*)
+	GUESS=x86_64-unknown-haiku
+	;;
+    SX-4:SUPER-UX:*:*)
+	GUESS=sx4-nec-superux$UNAME_RELEASE
+	;;
+    SX-5:SUPER-UX:*:*)
+	GUESS=sx5-nec-superux$UNAME_RELEASE
+	;;
+    SX-6:SUPER-UX:*:*)
+	GUESS=sx6-nec-superux$UNAME_RELEASE
+	;;
+    SX-7:SUPER-UX:*:*)
+	GUESS=sx7-nec-superux$UNAME_RELEASE
+	;;
+    SX-8:SUPER-UX:*:*)
+	GUESS=sx8-nec-superux$UNAME_RELEASE
+	;;
+    SX-8R:SUPER-UX:*:*)
+	GUESS=sx8r-nec-superux$UNAME_RELEASE
+	;;
+    SX-ACE:SUPER-UX:*:*)
+	GUESS=sxace-nec-superux$UNAME_RELEASE
+	;;
+    Power*:Rhapsody:*:*)
+	GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
+	;;
+    *:Rhapsody:*:*)
+	GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
+	;;
+    arm64:Darwin:*:*)
+	GUESS=aarch64-apple-darwin$UNAME_RELEASE
+	;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	case $UNAME_PROCESSOR in
+	    unknown) UNAME_PROCESSOR=powerpc ;;
+	esac
+	if command -v xcode-select > /dev/null 2> /dev/null && \
+		! xcode-select --print-path > /dev/null 2> /dev/null ; then
+	    # Avoid executing cc if there is no toolchain installed as
+	    # cc will be a stub that puts up a graphical alert
+	    # prompting the user to install developer tools.
+	    CC_FOR_BUILD=no_compiler_found
+	else
+	    set_cc_for_build
+	fi
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		   (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		   grep IS_64BIT_ARCH >/dev/null
+	    then
+		case $UNAME_PROCESSOR in
+		    i386) UNAME_PROCESSOR=x86_64 ;;
+		    powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		esac
+	    fi
+	    # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+	    if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+		   (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		   grep IS_PPC >/dev/null
+	    then
+		UNAME_PROCESSOR=powerpc
+	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # uname -m returns i386 or x86_64
+	    UNAME_PROCESSOR=$UNAME_MACHINE
+	fi
+	GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
+	;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = x86; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
+	;;
+    *:QNX:*:4*)
+	GUESS=i386-pc-qnx
+	;;
+    NEO-*:NONSTOP_KERNEL:*:*)
+	GUESS=neo-tandem-nsk$UNAME_RELEASE
+	;;
+    NSE-*:NONSTOP_KERNEL:*:*)
+	GUESS=nse-tandem-nsk$UNAME_RELEASE
+	;;
+    NSR-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsr-tandem-nsk$UNAME_RELEASE
+	;;
+    NSV-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsv-tandem-nsk$UNAME_RELEASE
+	;;
+    NSX-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsx-tandem-nsk$UNAME_RELEASE
+	;;
+    *:NonStop-UX:*:*)
+	GUESS=mips-compaq-nonstopux
+	;;
+    BS2000:POSIX*:*:*)
+	GUESS=bs2000-siemens-sysv
+	;;
+    DS/*:UNIX_System_V:*:*)
+	GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
+	;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "${cputype-}" = 386; then
+	    UNAME_MACHINE=i386
+	elif test "x${cputype-}" != x; then
+	    UNAME_MACHINE=$cputype
+	fi
+	GUESS=$UNAME_MACHINE-unknown-plan9
+	;;
+    *:TOPS-10:*:*)
+	GUESS=pdp10-unknown-tops10
+	;;
+    *:TENEX:*:*)
+	GUESS=pdp10-unknown-tenex
+	;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	GUESS=pdp10-dec-tops20
+	;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	GUESS=pdp10-xkl-tops20
+	;;
+    *:TOPS-20:*:*)
+	GUESS=pdp10-unknown-tops20
+	;;
+    *:ITS:*:*)
+	GUESS=pdp10-unknown-its
+	;;
+    SEI:*:*:SEIUX)
+	GUESS=mips-sei-seiux$UNAME_RELEASE
+	;;
+    *:DragonFly:*:*)
+	DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
+	;;
+    *:*VMS:*:*)
+	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case $UNAME_MACHINE in
+	    A*) GUESS=alpha-dec-vms ;;
+	    I*) GUESS=ia64-dec-vms ;;
+	    V*) GUESS=vax-dec-vms ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	GUESS=i386-pc-xenix
+	;;
+    i*86:skyos:*:*)
+	SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
+	GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
+	;;
+    i*86:rdos:*:*)
+	GUESS=$UNAME_MACHINE-pc-rdos
+	;;
+    i*86:Fiwix:*:*)
+	GUESS=$UNAME_MACHINE-pc-fiwix
+	;;
+    *:AROS:*:*)
+	GUESS=$UNAME_MACHINE-unknown-aros
+	;;
+    x86_64:VMkernel:*:*)
+	GUESS=$UNAME_MACHINE-unknown-esx
+	;;
+    amd64:Isilon\ OneFS:*:*)
+	GUESS=x86_64-unknown-onefs
+	;;
+    *:Unleashed:*:*)
+	GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
+	;;
+esac
+
+# Do we have a guess based on uname results?
+if test "x$GUESS" != x; then
+    echo "$GUESS"
+    exit
+fi
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+  "4"
+#else
+  ""
+#endif
+  ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+  struct utsname un;
+
+  uname(&un);
+  if (strncmp(un.version, "V2", 2) == 0) {
+    printf ("i386-sequent-ptx2\n"); exit (0);
+  }
+  if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+    printf ("i386-sequent-ptx1\n"); exit (0);
+  }
+  printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+  printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+  printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname un;
+  uname (&un);
+  printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname *un;
+  uname (&un);
+  printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
+	{ echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case $UNAME_MACHINE:$UNAME_SYSTEM in
+    mips:Linux | mips64:Linux)
+	# If we got here on MIPS GNU/Linux, output extra information.
+	cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+	;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+and
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+our_year=`echo $timestamp | sed 's,-.*,,'`
+thisyear=`date +%Y`
+# shellcheck disable=SC2003
+script_age=`expr "$thisyear" - "$our_year"`
+if test "$script_age" -lt 3 ; then
+   cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM  = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config/config.sub b/config/config.sub
new file mode 100755
index 0000000..dba16e8
--- /dev/null
+++ b/config/config.sub
@@ -0,0 +1,1890 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2022-01-03'
+
+# This file 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.
+#
+# This program 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 <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2022 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo "$1"
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Split fields of configuration type
+# shellcheck disable=SC2162
+saved_IFS=$IFS
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+IFS=$saved_IFS
+
+# Separate into logical components for further validation
+case $1 in
+	*-*-*-*-*)
+		echo Invalid configuration \`"$1"\': more than four components >&2
+		exit 1
+		;;
+	*-*-*-*)
+		basic_machine=$field1-$field2
+		basic_os=$field3-$field4
+		;;
+	*-*-*)
+		# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+		# parts
+		maybe_os=$field2-$field3
+		case $maybe_os in
+			nto-qnx* | linux-* | uclinux-uclibc* \
+			| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+			| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+			| storm-chaos* | os2-emx* | rtmk-nova*)
+				basic_machine=$field1
+				basic_os=$maybe_os
+				;;
+			android-linux)
+				basic_machine=$field1-unknown
+				basic_os=linux-android
+				;;
+			*)
+				basic_machine=$field1-$field2
+				basic_os=$field3
+				;;
+		esac
+		;;
+	*-*)
+		# A lone config we happen to match not fitting any pattern
+		case $field1-$field2 in
+			decstation-3100)
+				basic_machine=mips-dec
+				basic_os=
+				;;
+			*-*)
+				# Second component is usually, but not always the OS
+				case $field2 in
+					# Prevent following clause from handling this valid os
+					sun*os*)
+						basic_machine=$field1
+						basic_os=$field2
+						;;
+					zephyr*)
+						basic_machine=$field1-unknown
+						basic_os=$field2
+						;;
+					# Manufacturers
+					dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
+					| att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
+					| unicom* | ibm* | next | hp | isi* | apollo | altos* \
+					| convergent* | ncr* | news | 32* | 3600* | 3100* \
+					| hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
+					| ultra | tti* | harris | dolphin | highlevel | gould \
+					| cbm | ns | masscomp | apple | axis | knuth | cray \
+					| microblaze* | sim | cisco \
+					| oki | wec | wrs | winbond)
+						basic_machine=$field1-$field2
+						basic_os=
+						;;
+					*)
+						basic_machine=$field1
+						basic_os=$field2
+						;;
+				esac
+			;;
+		esac
+		;;
+	*)
+		# Convert single-component short-hands not valid as part of
+		# multi-component configurations.
+		case $field1 in
+			386bsd)
+				basic_machine=i386-pc
+				basic_os=bsd
+				;;
+			a29khif)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			adobe68k)
+				basic_machine=m68010-adobe
+				basic_os=scout
+				;;
+			alliant)
+				basic_machine=fx80-alliant
+				basic_os=
+				;;
+			altos | altos3068)
+				basic_machine=m68k-altos
+				basic_os=
+				;;
+			am29k)
+				basic_machine=a29k-none
+				basic_os=bsd
+				;;
+			amdahl)
+				basic_machine=580-amdahl
+				basic_os=sysv
+				;;
+			amiga)
+				basic_machine=m68k-unknown
+				basic_os=
+				;;
+			amigaos | amigados)
+				basic_machine=m68k-unknown
+				basic_os=amigaos
+				;;
+			amigaunix | amix)
+				basic_machine=m68k-unknown
+				basic_os=sysv4
+				;;
+			apollo68)
+				basic_machine=m68k-apollo
+				basic_os=sysv
+				;;
+			apollo68bsd)
+				basic_machine=m68k-apollo
+				basic_os=bsd
+				;;
+			aros)
+				basic_machine=i386-pc
+				basic_os=aros
+				;;
+			aux)
+				basic_machine=m68k-apple
+				basic_os=aux
+				;;
+			balance)
+				basic_machine=ns32k-sequent
+				basic_os=dynix
+				;;
+			blackfin)
+				basic_machine=bfin-unknown
+				basic_os=linux
+				;;
+			cegcc)
+				basic_machine=arm-unknown
+				basic_os=cegcc
+				;;
+			convex-c1)
+				basic_machine=c1-convex
+				basic_os=bsd
+				;;
+			convex-c2)
+				basic_machine=c2-convex
+				basic_os=bsd
+				;;
+			convex-c32)
+				basic_machine=c32-convex
+				basic_os=bsd
+				;;
+			convex-c34)
+				basic_machine=c34-convex
+				basic_os=bsd
+				;;
+			convex-c38)
+				basic_machine=c38-convex
+				basic_os=bsd
+				;;
+			cray)
+				basic_machine=j90-cray
+				basic_os=unicos
+				;;
+			crds | unos)
+				basic_machine=m68k-crds
+				basic_os=
+				;;
+			da30)
+				basic_machine=m68k-da30
+				basic_os=
+				;;
+			decstation | pmax | pmin | dec3100 | decstatn)
+				basic_machine=mips-dec
+				basic_os=
+				;;
+			delta88)
+				basic_machine=m88k-motorola
+				basic_os=sysv3
+				;;
+			dicos)
+				basic_machine=i686-pc
+				basic_os=dicos
+				;;
+			djgpp)
+				basic_machine=i586-pc
+				basic_os=msdosdjgpp
+				;;
+			ebmon29k)
+				basic_machine=a29k-amd
+				basic_os=ebmon
+				;;
+			es1800 | OSE68k | ose68k | ose | OSE)
+				basic_machine=m68k-ericsson
+				basic_os=ose
+				;;
+			gmicro)
+				basic_machine=tron-gmicro
+				basic_os=sysv
+				;;
+			go32)
+				basic_machine=i386-pc
+				basic_os=go32
+				;;
+			h8300hms)
+				basic_machine=h8300-hitachi
+				basic_os=hms
+				;;
+			h8300xray)
+				basic_machine=h8300-hitachi
+				basic_os=xray
+				;;
+			h8500hms)
+				basic_machine=h8500-hitachi
+				basic_os=hms
+				;;
+			harris)
+				basic_machine=m88k-harris
+				basic_os=sysv3
+				;;
+			hp300 | hp300hpux)
+				basic_machine=m68k-hp
+				basic_os=hpux
+				;;
+			hp300bsd)
+				basic_machine=m68k-hp
+				basic_os=bsd
+				;;
+			hppaosf)
+				basic_machine=hppa1.1-hp
+				basic_os=osf
+				;;
+			hppro)
+				basic_machine=hppa1.1-hp
+				basic_os=proelf
+				;;
+			i386mach)
+				basic_machine=i386-mach
+				basic_os=mach
+				;;
+			isi68 | isi)
+				basic_machine=m68k-isi
+				basic_os=sysv
+				;;
+			m68knommu)
+				basic_machine=m68k-unknown
+				basic_os=linux
+				;;
+			magnum | m3230)
+				basic_machine=mips-mips
+				basic_os=sysv
+				;;
+			merlin)
+				basic_machine=ns32k-utek
+				basic_os=sysv
+				;;
+			mingw64)
+				basic_machine=x86_64-pc
+				basic_os=mingw64
+				;;
+			mingw32)
+				basic_machine=i686-pc
+				basic_os=mingw32
+				;;
+			mingw32ce)
+				basic_machine=arm-unknown
+				basic_os=mingw32ce
+				;;
+			monitor)
+				basic_machine=m68k-rom68k
+				basic_os=coff
+				;;
+			morphos)
+				basic_machine=powerpc-unknown
+				basic_os=morphos
+				;;
+			moxiebox)
+				basic_machine=moxie-unknown
+				basic_os=moxiebox
+				;;
+			msdos)
+				basic_machine=i386-pc
+				basic_os=msdos
+				;;
+			msys)
+				basic_machine=i686-pc
+				basic_os=msys
+				;;
+			mvs)
+				basic_machine=i370-ibm
+				basic_os=mvs
+				;;
+			nacl)
+				basic_machine=le32-unknown
+				basic_os=nacl
+				;;
+			ncr3000)
+				basic_machine=i486-ncr
+				basic_os=sysv4
+				;;
+			netbsd386)
+				basic_machine=i386-pc
+				basic_os=netbsd
+				;;
+			netwinder)
+				basic_machine=armv4l-rebel
+				basic_os=linux
+				;;
+			news | news700 | news800 | news900)
+				basic_machine=m68k-sony
+				basic_os=newsos
+				;;
+			news1000)
+				basic_machine=m68030-sony
+				basic_os=newsos
+				;;
+			necv70)
+				basic_machine=v70-nec
+				basic_os=sysv
+				;;
+			nh3000)
+				basic_machine=m68k-harris
+				basic_os=cxux
+				;;
+			nh[45]000)
+				basic_machine=m88k-harris
+				basic_os=cxux
+				;;
+			nindy960)
+				basic_machine=i960-intel
+				basic_os=nindy
+				;;
+			mon960)
+				basic_machine=i960-intel
+				basic_os=mon960
+				;;
+			nonstopux)
+				basic_machine=mips-compaq
+				basic_os=nonstopux
+				;;
+			os400)
+				basic_machine=powerpc-ibm
+				basic_os=os400
+				;;
+			OSE68000 | ose68000)
+				basic_machine=m68000-ericsson
+				basic_os=ose
+				;;
+			os68k)
+				basic_machine=m68k-none
+				basic_os=os68k
+				;;
+			paragon)
+				basic_machine=i860-intel
+				basic_os=osf
+				;;
+			parisc)
+				basic_machine=hppa-unknown
+				basic_os=linux
+				;;
+			psp)
+				basic_machine=mipsallegrexel-sony
+				basic_os=psp
+				;;
+			pw32)
+				basic_machine=i586-unknown
+				basic_os=pw32
+				;;
+			rdos | rdos64)
+				basic_machine=x86_64-pc
+				basic_os=rdos
+				;;
+			rdos32)
+				basic_machine=i386-pc
+				basic_os=rdos
+				;;
+			rom68k)
+				basic_machine=m68k-rom68k
+				basic_os=coff
+				;;
+			sa29200)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			sei)
+				basic_machine=mips-sei
+				basic_os=seiux
+				;;
+			sequent)
+				basic_machine=i386-sequent
+				basic_os=
+				;;
+			sps7)
+				basic_machine=m68k-bull
+				basic_os=sysv2
+				;;
+			st2000)
+				basic_machine=m68k-tandem
+				basic_os=
+				;;
+			stratus)
+				basic_machine=i860-stratus
+				basic_os=sysv4
+				;;
+			sun2)
+				basic_machine=m68000-sun
+				basic_os=
+				;;
+			sun2os3)
+				basic_machine=m68000-sun
+				basic_os=sunos3
+				;;
+			sun2os4)
+				basic_machine=m68000-sun
+				basic_os=sunos4
+				;;
+			sun3)
+				basic_machine=m68k-sun
+				basic_os=
+				;;
+			sun3os3)
+				basic_machine=m68k-sun
+				basic_os=sunos3
+				;;
+			sun3os4)
+				basic_machine=m68k-sun
+				basic_os=sunos4
+				;;
+			sun4)
+				basic_machine=sparc-sun
+				basic_os=
+				;;
+			sun4os3)
+				basic_machine=sparc-sun
+				basic_os=sunos3
+				;;
+			sun4os4)
+				basic_machine=sparc-sun
+				basic_os=sunos4
+				;;
+			sun4sol2)
+				basic_machine=sparc-sun
+				basic_os=solaris2
+				;;
+			sun386 | sun386i | roadrunner)
+				basic_machine=i386-sun
+				basic_os=
+				;;
+			sv1)
+				basic_machine=sv1-cray
+				basic_os=unicos
+				;;
+			symmetry)
+				basic_machine=i386-sequent
+				basic_os=dynix
+				;;
+			t3e)
+				basic_machine=alphaev5-cray
+				basic_os=unicos
+				;;
+			t90)
+				basic_machine=t90-cray
+				basic_os=unicos
+				;;
+			toad1)
+				basic_machine=pdp10-xkl
+				basic_os=tops20
+				;;
+			tpf)
+				basic_machine=s390x-ibm
+				basic_os=tpf
+				;;
+			udi29k)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			ultra3)
+				basic_machine=a29k-nyu
+				basic_os=sym1
+				;;
+			v810 | necv810)
+				basic_machine=v810-nec
+				basic_os=none
+				;;
+			vaxv)
+				basic_machine=vax-dec
+				basic_os=sysv
+				;;
+			vms)
+				basic_machine=vax-dec
+				basic_os=vms
+				;;
+			vsta)
+				basic_machine=i386-pc
+				basic_os=vsta
+				;;
+			vxworks960)
+				basic_machine=i960-wrs
+				basic_os=vxworks
+				;;
+			vxworks68)
+				basic_machine=m68k-wrs
+				basic_os=vxworks
+				;;
+			vxworks29k)
+				basic_machine=a29k-wrs
+				basic_os=vxworks
+				;;
+			xbox)
+				basic_machine=i686-pc
+				basic_os=mingw32
+				;;
+			ymp)
+				basic_machine=ymp-cray
+				basic_os=unicos
+				;;
+			*)
+				basic_machine=$1
+				basic_os=
+				;;
+		esac
+		;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+	# Here we handle the default manufacturer of certain CPU types.  It is in
+	# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		cpu=hppa1.1
+		vendor=winbond
+		;;
+	op50n)
+		cpu=hppa1.1
+		vendor=oki
+		;;
+	op60c)
+		cpu=hppa1.1
+		vendor=oki
+		;;
+	ibm*)
+		cpu=i370
+		vendor=ibm
+		;;
+	orion105)
+		cpu=clipper
+		vendor=highlevel
+		;;
+	mac | mpw | mac-mpw)
+		cpu=m68k
+		vendor=apple
+		;;
+	pmac | pmac-mpw)
+		cpu=powerpc
+		vendor=apple
+		;;
+
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		cpu=m68000
+		vendor=att
+		;;
+	3b*)
+		cpu=we32k
+		vendor=att
+		;;
+	bluegene*)
+		cpu=powerpc
+		vendor=ibm
+		basic_os=cnk
+		;;
+	decsystem10* | dec10*)
+		cpu=pdp10
+		vendor=dec
+		basic_os=tops10
+		;;
+	decsystem20* | dec20*)
+		cpu=pdp10
+		vendor=dec
+		basic_os=tops20
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		cpu=m68k
+		vendor=motorola
+		;;
+	dpx2*)
+		cpu=m68k
+		vendor=bull
+		basic_os=sysv3
+		;;
+	encore | umax | mmax)
+		cpu=ns32k
+		vendor=encore
+		;;
+	elxsi)
+		cpu=elxsi
+		vendor=elxsi
+		basic_os=${basic_os:-bsd}
+		;;
+	fx2800)
+		cpu=i860
+		vendor=alliant
+		;;
+	genix)
+		cpu=ns32k
+		vendor=ns
+		;;
+	h3050r* | hiux*)
+		cpu=hppa1.1
+		vendor=hitachi
+		basic_os=hiuxwe2
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		cpu=m68000
+		vendor=hp
+		;;
+	hp9k3[2-9][0-9])
+		cpu=m68k
+		vendor=hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	i*86v32)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv32
+		;;
+	i*86v4*)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv4
+		;;
+	i*86v)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv
+		;;
+	i*86sol2)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=solaris2
+		;;
+	j90 | j90-cray)
+		cpu=j90
+		vendor=cray
+		basic_os=${basic_os:-unicos}
+		;;
+	iris | iris4d)
+		cpu=mips
+		vendor=sgi
+		case $basic_os in
+		    irix*)
+			;;
+		    *)
+			basic_os=irix4
+			;;
+		esac
+		;;
+	miniframe)
+		cpu=m68000
+		vendor=convergent
+		;;
+	*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		cpu=m68k
+		vendor=atari
+		basic_os=mint
+		;;
+	news-3600 | risc-news)
+		cpu=mips
+		vendor=sony
+		basic_os=newsos
+		;;
+	next | m*-next)
+		cpu=m68k
+		vendor=next
+		case $basic_os in
+		    openstep*)
+		        ;;
+		    nextstep*)
+			;;
+		    ns2*)
+		      basic_os=nextstep2
+			;;
+		    *)
+		      basic_os=nextstep3
+			;;
+		esac
+		;;
+	np1)
+		cpu=np1
+		vendor=gould
+		;;
+	op50n-* | op60c-*)
+		cpu=hppa1.1
+		vendor=oki
+		basic_os=proelf
+		;;
+	pa-hitachi)
+		cpu=hppa1.1
+		vendor=hitachi
+		basic_os=hiuxwe2
+		;;
+	pbd)
+		cpu=sparc
+		vendor=tti
+		;;
+	pbb)
+		cpu=m68k
+		vendor=tti
+		;;
+	pc532)
+		cpu=ns32k
+		vendor=pc532
+		;;
+	pn)
+		cpu=pn
+		vendor=gould
+		;;
+	power)
+		cpu=power
+		vendor=ibm
+		;;
+	ps2)
+		cpu=i386
+		vendor=ibm
+		;;
+	rm[46]00)
+		cpu=mips
+		vendor=siemens
+		;;
+	rtpc | rtpc-*)
+		cpu=romp
+		vendor=ibm
+		;;
+	sde)
+		cpu=mipsisa32
+		vendor=sde
+		basic_os=${basic_os:-elf}
+		;;
+	simso-wrs)
+		cpu=sparclite
+		vendor=wrs
+		basic_os=vxworks
+		;;
+	tower | tower-32)
+		cpu=m68k
+		vendor=ncr
+		;;
+	vpp*|vx|vx-*)
+		cpu=f301
+		vendor=fujitsu
+		;;
+	w65)
+		cpu=w65
+		vendor=wdc
+		;;
+	w89k-*)
+		cpu=hppa1.1
+		vendor=winbond
+		basic_os=proelf
+		;;
+	none)
+		cpu=none
+		vendor=none
+		;;
+	leon|leon[3-9])
+		cpu=sparc
+		vendor=$basic_machine
+		;;
+	leon-*|leon[3-9]-*)
+		cpu=sparc
+		vendor=`echo "$basic_machine" | sed 's/-.*//'`
+		;;
+
+	*-*)
+		# shellcheck disable=SC2162
+		saved_IFS=$IFS
+		IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+		IFS=$saved_IFS
+		;;
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+		cpu=$basic_machine
+		vendor=pc
+		;;
+	# These rules are duplicated from below for sake of the special case above;
+	# i.e. things that normalized to x86 arches should also default to "pc"
+	pc98)
+		cpu=i386
+		vendor=pc
+		;;
+	x64 | amd64)
+		cpu=x86_64
+		vendor=pc
+		;;
+	# Recognize the basic CPU types without company name.
+	*)
+		cpu=$basic_machine
+		vendor=unknown
+		;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+	# Here we handle the default manufacturer of certain CPU types in canonical form. It is in
+	# some cases the only manufacturer, in others, it is the most popular.
+	craynv-unknown)
+		vendor=cray
+		basic_os=${basic_os:-unicosmp}
+		;;
+	c90-unknown | c90-cray)
+		vendor=cray
+		basic_os=${Basic_os:-unicos}
+		;;
+	fx80-unknown)
+		vendor=alliant
+		;;
+	romp-unknown)
+		vendor=ibm
+		;;
+	mmix-unknown)
+		vendor=knuth
+		;;
+	microblaze-unknown | microblazeel-unknown)
+		vendor=xilinx
+		;;
+	rs6000-unknown)
+		vendor=ibm
+		;;
+	vax-unknown)
+		vendor=dec
+		;;
+	pdp11-unknown)
+		vendor=dec
+		;;
+	we32k-unknown)
+		vendor=att
+		;;
+	cydra-unknown)
+		vendor=cydrome
+		;;
+	i370-ibm*)
+		vendor=ibm
+		;;
+	orion-unknown)
+		vendor=highlevel
+		;;
+	xps-unknown | xps100-unknown)
+		cpu=xps100
+		vendor=honeywell
+		;;
+
+	# Here we normalize CPU types with a missing or matching vendor
+	armh-unknown | armh-alt)
+		cpu=armv7l
+		vendor=alt
+		basic_os=${basic_os:-linux-gnueabihf}
+		;;
+	dpx20-unknown | dpx20-bull)
+		cpu=rs6000
+		vendor=bull
+		basic_os=${basic_os:-bosx}
+		;;
+
+	# Here we normalize CPU types irrespective of the vendor
+	amd64-*)
+		cpu=x86_64
+		;;
+	blackfin-*)
+		cpu=bfin
+		basic_os=linux
+		;;
+	c54x-*)
+		cpu=tic54x
+		;;
+	c55x-*)
+		cpu=tic55x
+		;;
+	c6x-*)
+		cpu=tic6x
+		;;
+	e500v[12]-*)
+		cpu=powerpc
+		basic_os=${basic_os}"spe"
+		;;
+	mips3*-*)
+		cpu=mips64
+		;;
+	ms1-*)
+		cpu=mt
+		;;
+	m68knommu-*)
+		cpu=m68k
+		basic_os=linux
+		;;
+	m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+		cpu=s12z
+		;;
+	openrisc-*)
+		cpu=or32
+		;;
+	parisc-*)
+		cpu=hppa
+		basic_os=linux
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		cpu=i586
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+		cpu=i686
+		;;
+	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+		cpu=i686
+		;;
+	pentium4-*)
+		cpu=i786
+		;;
+	pc98-*)
+		cpu=i386
+		;;
+	ppc-* | ppcbe-*)
+		cpu=powerpc
+		;;
+	ppcle-* | powerpclittle-*)
+		cpu=powerpcle
+		;;
+	ppc64-*)
+		cpu=powerpc64
+		;;
+	ppc64le-* | powerpc64little-*)
+		cpu=powerpc64le
+		;;
+	sb1-*)
+		cpu=mipsisa64sb1
+		;;
+	sb1el-*)
+		cpu=mipsisa64sb1el
+		;;
+	sh5e[lb]-*)
+		cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+		;;
+	spur-*)
+		cpu=spur
+		;;
+	strongarm-* | thumb-*)
+		cpu=arm
+		;;
+	tx39-*)
+		cpu=mipstx39
+		;;
+	tx39el-*)
+		cpu=mipstx39el
+		;;
+	x64-*)
+		cpu=x86_64
+		;;
+	xscale-* | xscalee[bl]-*)
+		cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+		;;
+	arm64-* | aarch64le-*)
+		cpu=aarch64
+		;;
+
+	# Recognize the canonical CPU Types that limit and/or modify the
+	# company names they are paired with.
+	cr16-*)
+		basic_os=${basic_os:-elf}
+		;;
+	crisv32-* | etraxfs*-*)
+		cpu=crisv32
+		vendor=axis
+		;;
+	cris-* | etrax*-*)
+		cpu=cris
+		vendor=axis
+		;;
+	crx-*)
+		basic_os=${basic_os:-elf}
+		;;
+	neo-tandem)
+		cpu=neo
+		vendor=tandem
+		;;
+	nse-tandem)
+		cpu=nse
+		vendor=tandem
+		;;
+	nsr-tandem)
+		cpu=nsr
+		vendor=tandem
+		;;
+	nsv-tandem)
+		cpu=nsv
+		vendor=tandem
+		;;
+	nsx-tandem)
+		cpu=nsx
+		vendor=tandem
+		;;
+	mipsallegrexel-sony)
+		cpu=mipsallegrexel
+		vendor=sony
+		;;
+	tile*-*)
+		basic_os=${basic_os:-linux-gnu}
+		;;
+
+	*)
+		# Recognize the canonical CPU types that are allowed with any
+		# company name.
+		case $cpu in
+			1750a | 580 \
+			| a29k \
+			| aarch64 | aarch64_be \
+			| abacus \
+			| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+			| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
+			| alphapca5[67] | alpha64pca5[67] \
+			| am33_2.0 \
+			| amdgcn \
+			| arc | arceb | arc32 | arc64 \
+			| arm | arm[lb]e | arme[lb] | armv* \
+			| avr | avr32 \
+			| asmjs \
+			| ba \
+			| be32 | be64 \
+			| bfin | bpf | bs2000 \
+			| c[123]* | c30 | [cjt]90 | c4x \
+			| c8051 | clipper | craynv | csky | cydra \
+			| d10v | d30v | dlx | dsp16xx \
+			| e2k | elxsi | epiphany \
+			| f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+			| h8300 | h8500 \
+			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+			| hexagon \
+			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
+			| ip2k | iq2000 \
+			| k1om \
+			| le32 | le64 \
+			| lm32 \
+			| loongarch32 | loongarch64 | loongarchx32 \
+			| m32c | m32r | m32rle \
+			| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
+			| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+			| m88110 | m88k | maxq | mb | mcore | mep | metag \
+			| microblaze | microblazeel \
+			| mips | mipsbe | mipseb | mipsel | mipsle \
+			| mips16 \
+			| mips64 | mips64eb | mips64el \
+			| mips64octeon | mips64octeonel \
+			| mips64orion | mips64orionel \
+			| mips64r5900 | mips64r5900el \
+			| mips64vr | mips64vrel \
+			| mips64vr4100 | mips64vr4100el \
+			| mips64vr4300 | mips64vr4300el \
+			| mips64vr5000 | mips64vr5000el \
+			| mips64vr5900 | mips64vr5900el \
+			| mipsisa32 | mipsisa32el \
+			| mipsisa32r2 | mipsisa32r2el \
+			| mipsisa32r3 | mipsisa32r3el \
+			| mipsisa32r5 | mipsisa32r5el \
+			| mipsisa32r6 | mipsisa32r6el \
+			| mipsisa64 | mipsisa64el \
+			| mipsisa64r2 | mipsisa64r2el \
+			| mipsisa64r3 | mipsisa64r3el \
+			| mipsisa64r5 | mipsisa64r5el \
+			| mipsisa64r6 | mipsisa64r6el \
+			| mipsisa64sb1 | mipsisa64sb1el \
+			| mipsisa64sr71k | mipsisa64sr71kel \
+			| mipsr5900 | mipsr5900el \
+			| mipstx39 | mipstx39el \
+			| mmix \
+			| mn10200 | mn10300 \
+			| moxie \
+			| mt \
+			| msp430 \
+			| nds32 | nds32le | nds32be \
+			| nfp \
+			| nios | nios2 | nios2eb | nios2el \
+			| none | np1 | ns16k | ns32k | nvptx \
+			| open8 \
+			| or1k* \
+			| or32 \
+			| orion \
+			| picochip \
+			| pdp10 | pdp11 | pj | pjl | pn | power \
+			| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
+			| pru \
+			| pyramid \
+			| riscv | riscv32 | riscv32be | riscv64 | riscv64be \
+			| rl78 | romp | rs6000 | rx \
+			| s390 | s390x \
+			| score \
+			| sh | shl \
+			| sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
+			| sh[1234]e[lb] |  sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
+			| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
+			| sparclite \
+			| sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+			| spu \
+			| tahoe \
+			| thumbv7* \
+			| tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+			| tron \
+			| ubicom32 \
+			| v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
+			| vax \
+			| visium \
+			| w65 \
+			| wasm32 | wasm64 \
+			| we32k \
+			| x86 | x86_64 | xc16x | xgate | xps100 \
+			| xstormy16 | xtensa* \
+			| ymp \
+			| z8k | z80)
+				;;
+
+			*)
+				echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+				exit 1
+				;;
+		esac
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+	digital*)
+		vendor=dec
+		;;
+	commodore*)
+		vendor=cbm
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if test x$basic_os != x
+then
+
+# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
+# set os.
+case $basic_os in
+	gnu/linux*)
+		kernel=linux
+		os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
+		;;
+	os2-emx)
+		kernel=os2
+		os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
+		;;
+	nto-qnx*)
+		kernel=nto
+		os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
+		;;
+	*-*)
+		# shellcheck disable=SC2162
+		saved_IFS=$IFS
+		IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+		IFS=$saved_IFS
+		;;
+	# Default OS when just kernel was specified
+	nto*)
+		kernel=nto
+		os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
+		;;
+	linux*)
+		kernel=linux
+		os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
+		;;
+	*)
+		kernel=
+		os=$basic_os
+		;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
+case $os in
+	# First match some system type aliases that might get confused
+	# with valid system types.
+	# solaris* is a basic system type, with this one exception.
+	auroraux)
+		os=auroraux
+		;;
+	bluegene*)
+		os=cnk
+		;;
+	solaris1 | solaris1.*)
+		os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
+		;;
+	solaris)
+		os=solaris2
+		;;
+	unixware*)
+		os=sysv4.2uw
+		;;
+	# es1800 is here to avoid being matched by es* (a different OS)
+	es1800*)
+		os=ose
+		;;
+	# Some version numbers need modification
+	chorusos*)
+		os=chorusos
+		;;
+	isc)
+		os=isc2.2
+		;;
+	sco6)
+		os=sco5v6
+		;;
+	sco5)
+		os=sco3.2v5
+		;;
+	sco4)
+		os=sco3.2v4
+		;;
+	sco3.2.[4-9]*)
+		os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
+		;;
+	sco*v* | scout)
+		# Don't match below
+		;;
+	sco*)
+		os=sco3.2v2
+		;;
+	psos*)
+		os=psos
+		;;
+	qnx*)
+		os=qnx
+		;;
+	hiux*)
+		os=hiuxwe2
+		;;
+	lynx*178)
+		os=lynxos178
+		;;
+	lynx*5)
+		os=lynxos5
+		;;
+	lynxos*)
+		# don't get caught up in next wildcard
+		;;
+	lynx*)
+		os=lynxos
+		;;
+	mac[0-9]*)
+		os=`echo "$os" | sed -e 's|mac|macos|'`
+		;;
+	opened*)
+		os=openedition
+		;;
+	os400*)
+		os=os400
+		;;
+	sunos5*)
+		os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+		;;
+	sunos6*)
+		os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+		;;
+	wince*)
+		os=wince
+		;;
+	utek*)
+		os=bsd
+		;;
+	dynix*)
+		os=bsd
+		;;
+	acis*)
+		os=aos
+		;;
+	atheos*)
+		os=atheos
+		;;
+	syllable*)
+		os=syllable
+		;;
+	386bsd)
+		os=bsd
+		;;
+	ctix* | uts*)
+		os=sysv
+		;;
+	nova*)
+		os=rtmk-nova
+		;;
+	ns2)
+		os=nextstep2
+		;;
+	# Preserve the version number of sinix5.
+	sinix5.*)
+		os=`echo "$os" | sed -e 's|sinix|sysv|'`
+		;;
+	sinix*)
+		os=sysv4
+		;;
+	tpf*)
+		os=tpf
+		;;
+	triton*)
+		os=sysv3
+		;;
+	oss*)
+		os=sysv3
+		;;
+	svr4*)
+		os=sysv4
+		;;
+	svr3)
+		os=sysv3
+		;;
+	sysvr4)
+		os=sysv4
+		;;
+	ose*)
+		os=ose
+		;;
+	*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+		os=mint
+		;;
+	dicos*)
+		os=dicos
+		;;
+	pikeos*)
+		# Until real need of OS specific support for
+		# particular features comes up, bare metal
+		# configurations are quite functional.
+		case $cpu in
+		    arm*)
+			os=eabi
+			;;
+		    *)
+			os=elf
+			;;
+		esac
+		;;
+	*)
+		# No normalization, but not necessarily accepted, that comes below.
+		;;
+esac
+
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+kernel=
+case $cpu-$vendor in
+	score-*)
+		os=elf
+		;;
+	spu-*)
+		os=elf
+		;;
+	*-acorn)
+		os=riscix1.2
+		;;
+	arm*-rebel)
+		kernel=linux
+		os=gnu
+		;;
+	arm*-semi)
+		os=aout
+		;;
+	c4x-* | tic4x-*)
+		os=coff
+		;;
+	c8051-*)
+		os=elf
+		;;
+	clipper-intergraph)
+		os=clix
+		;;
+	hexagon-*)
+		os=elf
+		;;
+	tic54x-*)
+		os=coff
+		;;
+	tic55x-*)
+		os=coff
+		;;
+	tic6x-*)
+		os=coff
+		;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=tops20
+		;;
+	pdp11-*)
+		os=none
+		;;
+	*-dec | vax-*)
+		os=ultrix4.2
+		;;
+	m68*-apollo)
+		os=domain
+		;;
+	i386-sun)
+		os=sunos4.0.2
+		;;
+	m68000-sun)
+		os=sunos3
+		;;
+	m68*-cisco)
+		os=aout
+		;;
+	mep-*)
+		os=elf
+		;;
+	mips*-cisco)
+		os=elf
+		;;
+	mips*-*)
+		os=elf
+		;;
+	or32-*)
+		os=coff
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=sysv3
+		;;
+	sparc-* | *-sun)
+		os=sunos4.1.1
+		;;
+	pru-*)
+		os=elf
+		;;
+	*-be)
+		os=beos
+		;;
+	*-ibm)
+		os=aix
+		;;
+	*-knuth)
+		os=mmixware
+		;;
+	*-wec)
+		os=proelf
+		;;
+	*-winbond)
+		os=proelf
+		;;
+	*-oki)
+		os=proelf
+		;;
+	*-hp)
+		os=hpux
+		;;
+	*-hitachi)
+		os=hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=sysv
+		;;
+	*-cbm)
+		os=amigaos
+		;;
+	*-dg)
+		os=dgux
+		;;
+	*-dolphin)
+		os=sysv3
+		;;
+	m68k-ccur)
+		os=rtu
+		;;
+	m88k-omron*)
+		os=luna
+		;;
+	*-next)
+		os=nextstep
+		;;
+	*-sequent)
+		os=ptx
+		;;
+	*-crds)
+		os=unos
+		;;
+	*-ns)
+		os=genix
+		;;
+	i370-*)
+		os=mvs
+		;;
+	*-gould)
+		os=sysv
+		;;
+	*-highlevel)
+		os=bsd
+		;;
+	*-encore)
+		os=bsd
+		;;
+	*-sgi)
+		os=irix
+		;;
+	*-siemens)
+		os=sysv4
+		;;
+	*-masscomp)
+		os=rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=uxpv
+		;;
+	*-rom68k)
+		os=coff
+		;;
+	*-*bug)
+		os=coff
+		;;
+	*-apple)
+		os=macos
+		;;
+	*-atari*)
+		os=mint
+		;;
+	*-wrs)
+		os=vxworks
+		;;
+	*)
+		os=none
+		;;
+esac
+
+fi
+
+# Now, validate our (potentially fixed-up) OS.
+case $os in
+	# Sometimes we do "kernel-libc", so those need to count as OSes.
+	musl* | newlib* | relibc* | uclibc*)
+		;;
+	# Likewise for "kernel-abi"
+	eabi* | gnueabi*)
+		;;
+	# VxWorks passes extra cpu info in the 4th filed.
+	simlinux | simwindows | spe)
+		;;
+	# Now accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST end in a * to match a version number.
+	gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+	     | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
+	     | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+	     | sym* |  plan9* | psp* | sim* | xray* | os68k* | v88r* \
+	     | hiux* | abug | nacl* | netware* | windows* \
+	     | os9* | macos* | osx* | ios* \
+	     | mpw* | magic* | mmixware* | mon960* | lnews* \
+	     | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+	     | aos* | aros* | cloudabi* | sortix* | twizzler* \
+	     | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+	     | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+	     | mirbsd* | netbsd* | dicos* | openedition* | ose* \
+	     | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \
+	     | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
+	     | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+	     | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+	     | udi* | lites* | ieee* | go32* | aux* | hcos* \
+	     | chorusrdb* | cegcc* | glidix* | serenity* \
+	     | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+	     | midipix* | mingw32* | mingw64* | mint* \
+	     | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+	     | interix* | uwin* | mks* | rhapsody* | darwin* \
+	     | openstep* | oskit* | conix* | pw32* | nonstopux* \
+	     | storm-chaos* | tops10* | tenex* | tops20* | its* \
+	     | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
+	     | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
+	     | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+	     | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+	     | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+	     | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+	     | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
+	     | fiwix* )
+		;;
+	# This one is extra strict with allowed versions
+	sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+		# Don't forget version if it is 3.2v4 or newer.
+		;;
+	none)
+		;;
+	*)
+		echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os in
+	linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \
+		   | linux-musl* | linux-relibc* | linux-uclibc* )
+		;;
+	uclinux-uclibc* )
+		;;
+	-dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* )
+		# These are just libc implementations, not actual OSes, and thus
+		# require a kernel.
+		echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
+		exit 1
+		;;
+	kfreebsd*-gnu* | kopensolaris*-gnu*)
+		;;
+	vxworks-simlinux | vxworks-simwindows | vxworks-spe)
+		;;
+	nto-qnx*)
+		;;
+	os2-emx)
+		;;
+	*-eabi* | *-gnueabi*)
+		;;
+	-*)
+		# Blank kernel with real OS is always fine.
+		;;
+	*-*)
+		echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
+		exit 1
+		;;
+esac
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+case $vendor in
+	unknown)
+		case $cpu-$os in
+			*-riscix*)
+				vendor=acorn
+				;;
+			*-sunos*)
+				vendor=sun
+				;;
+			*-cnk* | *-aix*)
+				vendor=ibm
+				;;
+			*-beos*)
+				vendor=be
+				;;
+			*-hpux*)
+				vendor=hp
+				;;
+			*-mpeix*)
+				vendor=hp
+				;;
+			*-hiux*)
+				vendor=hitachi
+				;;
+			*-unos*)
+				vendor=crds
+				;;
+			*-dgux*)
+				vendor=dg
+				;;
+			*-luna*)
+				vendor=omron
+				;;
+			*-genix*)
+				vendor=ns
+				;;
+			*-clix*)
+				vendor=intergraph
+				;;
+			*-mvs* | *-opened*)
+				vendor=ibm
+				;;
+			*-os400*)
+				vendor=ibm
+				;;
+			s390-* | s390x-*)
+				vendor=ibm
+				;;
+			*-ptx*)
+				vendor=sequent
+				;;
+			*-tpf*)
+				vendor=ibm
+				;;
+			*-vxsim* | *-vxworks* | *-windiss*)
+				vendor=wrs
+				;;
+			*-aux*)
+				vendor=apple
+				;;
+			*-hms*)
+				vendor=hitachi
+				;;
+			*-mpw* | *-macos*)
+				vendor=apple
+				;;
+			*-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
+				vendor=atari
+				;;
+			*-vos*)
+				vendor=stratus
+				;;
+		esac
+		;;
+esac
+
+echo "$cpu-$vendor-${kernel:+$kernel-}$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config/depcomp b/config/depcomp
new file mode 100755
index 0000000..715e343
--- /dev/null
+++ b/config/depcomp
@@ -0,0 +1,791 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1999-2021 Free Software Foundation, Inc.
+
+# This program 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 2, or (at your option)
+# any later version.
+
+# This program 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 <https://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+  '')
+    echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+    exit 1;
+    ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+  depmode     Dependency tracking mode.
+  source      Source file read by 'PROGRAMS ARGS'.
+  object      Object file output by 'PROGRAMS ARGS'.
+  DEPDIR      directory where to store dependencies.
+  depfile     Dependency file to output.
+  tmpdepfile  Temporary file to use when outputting dependencies.
+  libtool     Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "depcomp $scriptversion"
+    exit $?
+    ;;
+esac
+
+# Get the directory component of the given path, and save it in the
+# global variables '$dir'.  Note that this directory component will
+# be either empty or ending with a '/' character.  This is deliberate.
+set_dir_from ()
+{
+  case $1 in
+    */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
+      *) dir=;;
+  esac
+}
+
+# Get the suffix-stripped basename of the given path, and save it the
+# global variable '$base'.
+set_base_from ()
+{
+  base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
+}
+
+# If no dependency file was actually created by the compiler invocation,
+# we still have to create a dummy depfile, to avoid errors with the
+# Makefile "include basename.Plo" scheme.
+make_dummy_depfile ()
+{
+  echo "#dummy" > "$depfile"
+}
+
+# Factor out some common post-processing of the generated depfile.
+# Requires the auxiliary global variable '$tmpdepfile' to be set.
+aix_post_process_depfile ()
+{
+  # If the compiler actually managed to produce a dependency file,
+  # post-process it.
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form 'foo.o: dependency.h'.
+    # Do two passes, one to just change these to
+    #   $object: dependency.h
+    # and one to simply output
+    #   dependency.h:
+    # which is needed to avoid the deleted-header problem.
+    { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
+      sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
+    } > "$depfile"
+    rm -f "$tmpdepfile"
+  else
+    make_dummy_depfile
+  fi
+}
+
+# A tabulation character.
+tab='	'
+# A newline character.
+nl='
+'
+# Character ranges might be problematic outside the C locale.
+# These definitions help.
+upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
+lower=abcdefghijklmnopqrstuvwxyz
+digits=0123456789
+alpha=${upper}${lower}
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Avoid interferences from the environment.
+gccflag= dashmflag=
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+  # This is just like dashmstdout with a different argument.
+  dashmflag=-xM
+  depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+  # This is just like msvisualcpp but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvisualcpp
+fi
+
+if test "$depmode" = msvc7msys; then
+  # This is just like msvc7 but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvc7
+fi
+
+if test "$depmode" = xlc; then
+  # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
+  gccflag=-qmakedep=gcc,-MF
+  depmode=gcc
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am.  Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+  for arg
+  do
+    case $arg in
+    -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+    *)  set fnord "$@" "$arg" ;;
+    esac
+    shift # fnord
+    shift # $arg
+  done
+  "$@"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
+## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
+## (see the conditional assignment to $gccflag above).
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).  Also, it might not be
+##   supported by the other compilers which use the 'gcc' depmode.
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The second -e expression handles DOS-style file names with drive
+  # letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the "deleted header file" problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+## Some versions of gcc put a space before the ':'.  On the theory
+## that the space means something, we add a space to the output as
+## well.  hp depmode also adds that space, but also prefixes the VPATH
+## to the object.  Take care to not repeat it in the output.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like '#:fec' to the end of the
+    # dependency line.
+    tr ' ' "$nl" < "$tmpdepfile" \
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
+      | tr "$nl" ' ' >> "$depfile"
+    echo >> "$depfile"
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' "$nl" < "$tmpdepfile" \
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+      >> "$depfile"
+  else
+    make_dummy_depfile
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+xlc)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  In older versions, this file always lives in the
+  # current directory.  Also, the AIX compiler puts '$object:' at the
+  # start of each line; $object doesn't have directory information.
+  # Version 6 uses the directory in both cases.
+  set_dir_from "$object"
+  set_base_from "$object"
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$base.u
+    tmpdepfile3=$dir.libs/$base.u
+    "$@" -Wc,-M
+  else
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$dir$base.u
+    tmpdepfile3=$dir$base.u
+    "$@" -M
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  aix_post_process_depfile
+  ;;
+
+tcc)
+  # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
+  # FIXME: That version still under development at the moment of writing.
+  #        Make that this statement remains true also for stable, released
+  #        versions.
+  # It will wrap lines (doesn't matter whether long or short) with a
+  # trailing '\', as in:
+  #
+  #   foo.o : \
+  #    foo.c \
+  #    foo.h \
+  #
+  # It will put a trailing '\' even on the last line, and will use leading
+  # spaces rather than leading tabs (at least since its commit 0394caf7
+  # "Emit spaces for -MD").
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
+  # We have to change lines of the first kind to '$object: \'.
+  sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
+  # And for each line of the second kind, we have to emit a 'dep.h:'
+  # dummy dependency, to avoid the deleted-header problem.
+  sed -n -e 's|^  *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+## The order of this option in the case statement is important, since the
+## shell code in configure will try each of these formats in the order
+## listed in this file.  A plain '-MD' option would be understood by many
+## compilers, so we must ensure this comes after the gcc and icc options.
+pgcc)
+  # Portland's C compiler understands '-MD'.
+  # Will always output deps to 'file.d' where file is the root name of the
+  # source file under compilation, even if file resides in a subdirectory.
+  # The object file name does not affect the name of the '.d' file.
+  # pgcc 10.2 will output
+  #    foo.o: sub/foo.c sub/foo.h
+  # and will wrap long lines using '\' :
+  #    foo.o: sub/foo.c ... \
+  #     sub/foo.h ... \
+  #     ...
+  set_dir_from "$object"
+  # Use the source, not the object, to determine the base name, since
+  # that's sadly what pgcc will do too.
+  set_base_from "$source"
+  tmpdepfile=$base.d
+
+  # For projects that build the same source file twice into different object
+  # files, the pgcc approach of using the *source* file root name can cause
+  # problems in parallel builds.  Use a locking strategy to avoid stomping on
+  # the same $tmpdepfile.
+  lockdir=$base.d-lock
+  trap "
+    echo '$0: caught signal, cleaning up...' >&2
+    rmdir '$lockdir'
+    exit 1
+  " 1 2 13 15
+  numtries=100
+  i=$numtries
+  while test $i -gt 0; do
+    # mkdir is a portable test-and-set.
+    if mkdir "$lockdir" 2>/dev/null; then
+      # This process acquired the lock.
+      "$@" -MD
+      stat=$?
+      # Release the lock.
+      rmdir "$lockdir"
+      break
+    else
+      # If the lock is being held by a different process, wait
+      # until the winning process is done or we timeout.
+      while test -d "$lockdir" && test $i -gt 0; do
+        sleep 1
+        i=`expr $i - 1`
+      done
+    fi
+    i=`expr $i - 1`
+  done
+  trap - 1 2 13 15
+  if test $i -le 0; then
+    echo "$0: failed to acquire lock after $numtries attempts" >&2
+    echo "$0: check lockdir '$lockdir'" >&2
+    exit 1
+  fi
+
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each line is of the form `foo.o: dependent.h',
+  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+  # Do two passes, one to just change these to
+  # `$object: dependent.h' and one to simply `dependent.h:'.
+  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp2)
+  # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+  # compilers, which have integrated preprocessors.  The correct option
+  # to use with these is +Maked; it writes dependencies to a file named
+  # 'foo.d', which lands next to the object file, wherever that
+  # happens to be.
+  # Much of this is similar to the tru64 case; see comments there.
+  set_dir_from  "$object"
+  set_base_from "$object"
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir.libs/$base.d
+    "$@" -Wc,+Maked
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    "$@" +Maked
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+     rm -f "$tmpdepfile1" "$tmpdepfile2"
+     exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
+    # Add 'dependent.h:' lines.
+    sed -ne '2,${
+               s/^ *//
+               s/ \\*$//
+               s/$/:/
+               p
+             }' "$tmpdepfile" >> "$depfile"
+  else
+    make_dummy_depfile
+  fi
+  rm -f "$tmpdepfile" "$tmpdepfile2"
+  ;;
+
+tru64)
+  # The Tru64 compiler uses -MD to generate dependencies as a side
+  # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
+  # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+  # dependencies in 'foo.d' instead, so we check for that too.
+  # Subdirectories are respected.
+  set_dir_from  "$object"
+  set_base_from "$object"
+
+  if test "$libtool" = yes; then
+    # Libtool generates 2 separate objects for the 2 libraries.  These
+    # two compilations output dependencies in $dir.libs/$base.o.d and
+    # in $dir$base.o.d.  We have to check for both files, because
+    # one of the two compilations can be disabled.  We should prefer
+    # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+    # automatically cleaned when .libs/ is deleted, while ignoring
+    # the former would cause a distcleancheck panic.
+    tmpdepfile1=$dir$base.o.d          # libtool 1.5
+    tmpdepfile2=$dir.libs/$base.o.d    # Likewise.
+    tmpdepfile3=$dir.libs/$base.d      # Compaq CCC V6.2-504
+    "$@" -Wc,-MD
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    tmpdepfile3=$dir$base.d
+    "$@" -MD
+  fi
+
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  # Same post-processing that is required for AIX mode.
+  aix_post_process_depfile
+  ;;
+
+msvc7)
+  if test "$libtool" = yes; then
+    showIncludes=-Wc,-showIncludes
+  else
+    showIncludes=-showIncludes
+  fi
+  "$@" $showIncludes > "$tmpdepfile"
+  stat=$?
+  grep -v '^Note: including file: ' "$tmpdepfile"
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The first sed program below extracts the file names and escapes
+  # backslashes for cygpath.  The second sed program outputs the file
+  # name when reading, but also accumulates all include files in the
+  # hold buffer in order to output them again at the end.  This only
+  # works with sed implementations that can handle large buffers.
+  sed < "$tmpdepfile" -n '
+/^Note: including file:  *\(.*\)/ {
+  s//\1/
+  s/\\/\\\\/g
+  p
+}' | $cygpath_u | sort -u | sed -n '
+s/ /\\ /g
+s/\(.*\)/'"$tab"'\1 \\/p
+s/.\(.*\) \\/\1:/
+H
+$ {
+  s/.*/'"$tab"'/
+  G
+  p
+}' >> "$depfile"
+  echo >> "$depfile" # make sure the fragment doesn't end with a backslash
+  rm -f "$tmpdepfile"
+  ;;
+
+msvc7msys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove '-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  test -z "$dashmflag" && dashmflag=-M
+  # Require at least two characters before searching for ':'
+  # in the target name.  This is to cope with DOS-style filenames:
+  # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
+  "$@" $dashmflag |
+    sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this sed invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  "$@" || exit $?
+  # Remove any Libtool call
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+  # X makedepend
+  shift
+  cleared=no eat=no
+  for arg
+  do
+    case $cleared in
+    no)
+      set ""; shift
+      cleared=yes ;;
+    esac
+    if test $eat = yes; then
+      eat=no
+      continue
+    fi
+    case "$arg" in
+    -D*|-I*)
+      set fnord "$@" "$arg"; shift ;;
+    # Strip any option that makedepend may not understand.  Remove
+    # the object too, otherwise makedepend will parse it as a source file.
+    -arch)
+      eat=yes ;;
+    -*|$object)
+      ;;
+    *)
+      set fnord "$@" "$arg"; shift ;;
+    esac
+  done
+  obj_suffix=`echo "$object" | sed 's/^.*\././'`
+  touch "$tmpdepfile"
+  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  rm -f "$depfile"
+  # makedepend may prepend the VPATH from the source file name to the object.
+  # No need to regex-escape $object, excess matching of '.' is harmless.
+  sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process the last invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed '1,2d' "$tmpdepfile" \
+    | tr ' ' "$nl" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove '-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  "$@" -E \
+    | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+             -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+    | sed '$ s: \\$::' > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  IFS=" "
+  for arg
+  do
+    case "$arg" in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+        set fnord "$@"
+        shift
+        shift
+        ;;
+    *)
+        set fnord "$@" "$arg"
+        shift
+        shift
+        ;;
+    esac
+  done
+  "$@" -E 2>/dev/null |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
+  echo "$tab" >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvcmsys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/config/install-sh b/config/install-sh
new file mode 100755
index 0000000..ec298b5
--- /dev/null
+++ b/config/install-sh
@@ -0,0 +1,541 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2020-11-14.01; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab='	'
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -p            pass -p to $cpprog.
+  -s            $stripprog installed files.
+  -S SUFFIX     attempt to back up existing files, with suffix SUFFIX.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Email bug reports to bug-automake@gnu.org.
+Automake home page: https://www.gnu.org/software/automake/
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
+
+    -o) chowncmd="$chownprog $2"
+        shift;;
+
+    -p) cpprog="$cpprog -p";;
+
+    -s) stripcmd=$stripprog;;
+
+    -S) backupsuffix="$2"
+        shift;;
+
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
+
+    -T) is_target_a_directory=never;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --) shift
+        break;;
+
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+    # Don't chown directories that already exist.
+    if test $dstdir_status = 0; then
+      chowncmd=""
+    fi
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dstbase=`basename "$src"`
+      case $dst in
+	*/) dst=$dst$dstbase;;
+	*)  dst=$dst/$dstbase;;
+      esac
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  case $dstdir in
+    */) dstdirslash=$dstdir;;
+    *)  dstdirslash=$dstdir/;;
+  esac
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+	# The $RANDOM variable is not portable (e.g., dash).  Use it
+	# here however when possible just to lower collision chance.
+	tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+	trap '
+	  ret=$?
+	  rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+	  exit $ret
+	' 0
+
+	# Because "mkdir -p" follows existing symlinks and we likely work
+	# directly in world-writeable /tmp, make sure that the '$tmpdir'
+	# directory is successfully created first before we actually test
+	# 'mkdir -p'.
+	if (umask $mkdir_umask &&
+	    $mkdirprog $mkdir_mode "$tmpdir" &&
+	    exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+	then
+	  if test -z "$dir_arg" || {
+	       # Check for POSIX incompatibilities with -m.
+	       # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+	       # other-writable bit of parent directory when it shouldn't.
+	       # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+	       test_tmpdir="$tmpdir/a"
+	       ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+	       case $ls_ld_tmpdir in
+		 d????-?r-*) different_mode=700;;
+		 d????-?--*) different_mode=755;;
+		 *) false;;
+	       esac &&
+	       $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+		 ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+		 test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+	       }
+	     }
+	  then posix_mkdir=:
+	  fi
+	  rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+	else
+	  # Remove any dirs left behind by ancient mkdir implementations.
+	  rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+	fi
+	trap '' 0;;
+    esac
+
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask $mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=${dstdirslash}_inst.$$_
+    rmtmp=${dstdirslash}_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask &&
+     { test -z "$stripcmd" || {
+	 # Create $dsttmp read-write so that cp doesn't create it read-only,
+	 # which would cause strip to fail.
+	 if test -z "$doit"; then
+	   : >"$dsttmp" # No need to fork-exec 'touch'.
+	 else
+	   $doit touch "$dsttmp"
+	 fi
+       }
+     } &&
+     $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # If $backupsuffix is set, and the file being installed
+      # already exists, attempt a backup.  Don't worry if it fails,
+      # e.g., if mv doesn't support -f.
+      if test -n "$backupsuffix" && test -f "$dst"; then
+        $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+      fi
+
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/config/missing b/config/missing
new file mode 100755
index 0000000..1fe1611
--- /dev/null
+++ b/config/missing
@@ -0,0 +1,215 @@
+#! /bin/sh
+# Common wrapper for a few potentially missing GNU programs.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1996-2021 Free Software Foundation, Inc.
+# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program 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 2, or (at your option)
+# any later version.
+
+# This program 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 <https://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try '$0 --help' for more information"
+  exit 1
+fi
+
+case $1 in
+
+  --is-lightweight)
+    # Used by our autoconf macros to check whether the available missing
+    # script is modern enough.
+    exit 0
+    ;;
+
+  --run)
+    # Back-compat with the calling convention used by older automake.
+    shift
+    ;;
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+
+Supported PROGRAM values:
+  aclocal   autoconf  autoheader   autom4te  automake  makeinfo
+  bison     yacc      flex         lex       help2man
+
+Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
+'g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+    exit $?
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing $scriptversion (GNU Automake)"
+    exit $?
+    ;;
+
+  -*)
+    echo 1>&2 "$0: unknown '$1' option"
+    echo 1>&2 "Try '$0 --help' for more information"
+    exit 1
+    ;;
+
+esac
+
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch.  This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+  msg="probably too old"
+elif test $st -eq 127; then
+  # Program was missing.
+  msg="missing on your system"
+else
+  # Program was found and executed, but failed.  Give up.
+  exit $st
+fi
+
+perl_URL=https://www.perl.org/
+flex_URL=https://github.com/westes/flex
+gnu_software_URL=https://www.gnu.org/software
+
+program_details ()
+{
+  case $1 in
+    aclocal|automake)
+      echo "The '$1' program is part of the GNU Automake package:"
+      echo "<$gnu_software_URL/automake>"
+      echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/autoconf>"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+    autoconf|autom4te|autoheader)
+      echo "The '$1' program is part of the GNU Autoconf package:"
+      echo "<$gnu_software_URL/autoconf/>"
+      echo "It also requires GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+  esac
+}
+
+give_advice ()
+{
+  # Normalize program name to check for.
+  normalized_program=`echo "$1" | sed '
+    s/^gnu-//; t
+    s/^gnu//; t
+    s/^g//; t'`
+
+  printf '%s\n' "'$1' is $msg."
+
+  configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+  case $normalized_program in
+    autoconf*)
+      echo "You should only need it if you modified 'configure.ac',"
+      echo "or m4 files included by it."
+      program_details 'autoconf'
+      ;;
+    autoheader*)
+      echo "You should only need it if you modified 'acconfig.h' or"
+      echo "$configure_deps."
+      program_details 'autoheader'
+      ;;
+    automake*)
+      echo "You should only need it if you modified 'Makefile.am' or"
+      echo "$configure_deps."
+      program_details 'automake'
+      ;;
+    aclocal*)
+      echo "You should only need it if you modified 'acinclude.m4' or"
+      echo "$configure_deps."
+      program_details 'aclocal'
+      ;;
+   autom4te*)
+      echo "You might have modified some maintainer files that require"
+      echo "the 'autom4te' program to be rebuilt."
+      program_details 'autom4te'
+      ;;
+    bison*|yacc*)
+      echo "You should only need it if you modified a '.y' file."
+      echo "You may want to install the GNU Bison package:"
+      echo "<$gnu_software_URL/bison/>"
+      ;;
+    lex*|flex*)
+      echo "You should only need it if you modified a '.l' file."
+      echo "You may want to install the Fast Lexical Analyzer package:"
+      echo "<$flex_URL>"
+      ;;
+    help2man*)
+      echo "You should only need it if you modified a dependency" \
+           "of a man page."
+      echo "You may want to install the GNU Help2man package:"
+      echo "<$gnu_software_URL/help2man/>"
+    ;;
+    makeinfo*)
+      echo "You should only need it if you modified a '.texi' file, or"
+      echo "any other file indirectly affecting the aspect of the manual."
+      echo "You might want to install the Texinfo package:"
+      echo "<$gnu_software_URL/texinfo/>"
+      echo "The spurious makeinfo call might also be the consequence of"
+      echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+      echo "want to install GNU make:"
+      echo "<$gnu_software_URL/make/>"
+      ;;
+    *)
+      echo "You might have modified some files without having the proper"
+      echo "tools for further handling them.  Check the 'README' file, it"
+      echo "often tells you about the needed prerequisites for installing"
+      echo "this package.  You may also peek at any GNU archive site, in"
+      echo "case some other package contains this missing '$1' program."
+      ;;
+  esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+                       -e '2,$s/^/         /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/configure b/configure
new file mode 100755
index 0000000..2ec0aed
--- /dev/null
+++ b/configure
@@ -0,0 +1,6452 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.71 for metamath 0.199.pre-29-Jan-2022.
+#
+# Report bugs to <nm.NOSPAM@alum.mit.edu>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
+# Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else $as_nop
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" ""	$as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh).  This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2)            ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="as_nop=:
+if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else \$as_nop
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" )
+then :
+
+else \$as_nop
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+blah=\$(echo \$(echo blah))
+test x\"\$blah\" = xblah || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+  if (eval "$as_required") 2>/dev/null
+then :
+  as_have_required=yes
+else $as_nop
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
+then :
+
+else $as_nop
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null
+then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+  if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi
+fi
+
+
+      if test "x$CONFIG_SHELL" != x
+then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno
+then :
+  printf "%s\n" "$0: This script requires a shell more modern than all"
+  printf "%s\n" "$0: the shells that I found on your system."
+  if test ${ZSH_VERSION+y} ; then
+    printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and
+$0: nm.NOSPAM@alum.mit.edu about your system, including any
+$0: error possibly output before this message. Then install
+$0: a modern shell, or manually run the script under such a
+$0: shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+  return $?
+}
+as_nop=as_fn_nop
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else $as_nop
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else $as_nop
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+  return $?
+}
+as_nop=as_fn_nop
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  printf "%s\n" "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n.  New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='metamath'
+PACKAGE_TARNAME='metamath'
+PACKAGE_VERSION='0.199.pre-29-Jan-2022'
+PACKAGE_STRING='metamath 0.199.pre-29-Jan-2022'
+PACKAGE_BUGREPORT='nm.NOSPAM@alum.mit.edu'
+PACKAGE_URL=''
+
+ac_unique_file="src/metamath.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stddef.h>
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_header_c_list=
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+AM_CFLAGS
+LIBOBJS
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+am__nodep
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
+CSCOPE
+ETAGS
+CTAGS
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL
+am__quote'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_dependency_tracking
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir runstatedir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures metamath 0.199.pre-29-Jan-2022 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/metamath]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of metamath 0.199.pre-29-Jan-2022:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-silent-rules   less verbose build output (undo: "make V=1")
+  --disable-silent-rules  verbose build output (undo: "make V=0")
+  --enable-dependency-tracking
+                          do not reject slow dependency extractors
+  --disable-dependency-tracking
+                          speeds up one-time build
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <nm.NOSPAM@alum.mit.edu>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for configure.gnu first; this name is used for a wrapper for
+    # Metaconfig's "Configure" on case-insensitive file systems.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+metamath configure 0.199.pre-29-Jan-2022
+generated by GNU Autoconf 2.71
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest.beam
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$3=yes"
+else $as_nop
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+if (sizeof ($2))
+	 return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+if (sizeof (($2)))
+	    return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that
+# executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: program exited with status $ac_status" >&5
+       printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+   which can conflict with char $2 (); below.  */
+
+#include <limits.h>
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main (void)
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  eval "$3=yes"
+else $as_nop
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+ac_configure_args_raw=
+for ac_arg
+do
+  case $ac_arg in
+  *\'*)
+    ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+  esac
+  as_fn_append ac_configure_args_raw " '$ac_arg'"
+done
+
+case $ac_configure_args_raw in
+  *$as_nl*)
+    ac_safe_unquote= ;;
+  *)
+    ac_unsafe_z='|&;<>()$`\\"*?[ ''	' # This string ends in space, tab.
+    ac_unsafe_a="$ac_unsafe_z#~"
+    ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g"
+    ac_configure_args_raw=`      printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
+esac
+
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by metamath $as_me 0.199.pre-29-Jan-2022, which was
+generated by GNU Autoconf 2.71.  Invocation command line was
+
+  $ $0$ac_configure_args_raw
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    printf "%s\n" "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Sanitize IFS.
+  IFS=" ""	$as_nl"
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    printf "%s\n" "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    printf "%s\n" "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      printf "%s\n" "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      printf "%s\n" "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	printf "%s\n" "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      printf "%s\n" "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      printf "%s\n" "$as_me: caught signal $ac_signal"
+    printf "%s\n" "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+printf "%s\n" "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+  ac_site_files="$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+  ac_site_files="$prefix/share/config.site $prefix/etc/config.site"
+else
+  ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+fi
+
+for ac_site_file in $ac_site_files
+do
+  case $ac_site_file in #(
+  */*) :
+     ;; #(
+  *) :
+    ac_site_file=./$ac_site_file ;;
+esac
+  if test -f "$ac_site_file" && test -r "$ac_site_file"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+printf "%s\n" "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+printf "%s\n" "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Test code for whether the C compiler supports C89 (global declarations)
+ac_c_conftest_c89_globals='
+/* Does the compiler advertise C89 conformance?
+   Do not test the value of __STDC__, because some compilers set it to 0
+   while being otherwise adequately conformant. */
+#if !defined __STDC__
+# error "Compiler does not advertise C89 conformance"
+#endif
+
+#include <stddef.h>
+#include <stdarg.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7 src/conf.sh.  */
+struct buf { int x; };
+struct buf * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not \xHH hex character constants.
+   These do not provoke an error unfortunately, instead are silently treated
+   as an "x".  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously \x00 != x always comes out true, for an
+   array size at least.  It is necessary to write \x00 == 0 to get something
+   that is true only with -std.  */
+int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) '\''x'\''
+int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int),
+               int, int);'
+
+# Test code for whether the C compiler supports C89 (body of main).
+ac_c_conftest_c89_main='
+ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]);
+'
+
+# Test code for whether the C compiler supports C99 (global declarations)
+ac_c_conftest_c99_globals='
+// Does the compiler advertise C99 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# error "Compiler does not advertise C99 conformance"
+#endif
+
+#include <stdbool.h>
+extern int puts (const char *);
+extern int printf (const char *, ...);
+extern int dprintf (int, const char *, ...);
+extern void *malloc (size_t);
+
+// Check varargs macros.  These examples are taken from C99 6.10.3.5.
+// dprintf is used instead of fprintf to avoid needing to declare
+// FILE and stderr.
+#define debug(...) dprintf (2, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+  int x = 1234;
+  int y = 5678;
+  debug ("Flag");
+  debug ("X = %d\n", x);
+  showlist (The first, second, and third items.);
+  report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+  #error "your preprocessor is broken"
+#endif
+#if BIG_OK
+#else
+  #error "your preprocessor is broken"
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+  int datasize;
+  double data[];
+};
+
+struct named_init {
+  int number;
+  const wchar_t *name;
+  double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+  // See if C++-style comments work.
+  // Iterate through items via the restricted pointer.
+  // Also check for declarations in for loops.
+  for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i)
+    continue;
+  return 0;
+}
+
+// Check varargs and va_copy.
+static bool
+test_varargs (const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+  va_list args_copy;
+  va_copy (args_copy, args);
+
+  const char *str = "";
+  int number = 0;
+  float fnumber = 0;
+
+  while (*format)
+    {
+      switch (*format++)
+	{
+	case '\''s'\'': // string
+	  str = va_arg (args_copy, const char *);
+	  break;
+	case '\''d'\'': // int
+	  number = va_arg (args_copy, int);
+	  break;
+	case '\''f'\'': // float
+	  fnumber = va_arg (args_copy, double);
+	  break;
+	default:
+	  break;
+	}
+    }
+  va_end (args_copy);
+  va_end (args);
+
+  return *str && number && fnumber;
+}
+'
+
+# Test code for whether the C compiler supports C99 (body of main).
+ac_c_conftest_c99_main='
+  // Check bool.
+  _Bool success = false;
+  success |= (argc != 0);
+
+  // Check restrict.
+  if (test_restrict ("String literal") == 0)
+    success = true;
+  char *restrict newvar = "Another string";
+
+  // Check varargs.
+  success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234);
+  test_varargs_macros ();
+
+  // Check flexible array members.
+  struct incomplete_array *ia =
+    malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+  ia->datasize = 10;
+  for (int i = 0; i < ia->datasize; ++i)
+    ia->data[i] = i * 1.234;
+
+  // Check named initializers.
+  struct named_init ni = {
+    .number = 34,
+    .name = L"Test wide string",
+    .average = 543.34343,
+  };
+
+  ni.number = 58;
+
+  int dynamic_array[ni.number];
+  dynamic_array[0] = argv[0][0];
+  dynamic_array[ni.number - 1] = 543;
+
+  // work around unused variable warnings
+  ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\''
+	 || dynamic_array[ni.number - 1] != 543);
+'
+
+# Test code for whether the C compiler supports C11 (global declarations)
+ac_c_conftest_c11_globals='
+// Does the compiler advertise C11 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L
+# error "Compiler does not advertise C11 conformance"
+#endif
+
+// Check _Alignas.
+char _Alignas (double) aligned_as_double;
+char _Alignas (0) no_special_alignment;
+extern char aligned_as_int;
+char _Alignas (0) _Alignas (int) aligned_as_int;
+
+// Check _Alignof.
+enum
+{
+  int_alignment = _Alignof (int),
+  int_array_alignment = _Alignof (int[100]),
+  char_alignment = _Alignof (char)
+};
+_Static_assert (0 < -_Alignof (int), "_Alignof is signed");
+
+// Check _Noreturn.
+int _Noreturn does_not_return (void) { for (;;) continue; }
+
+// Check _Static_assert.
+struct test_static_assert
+{
+  int x;
+  _Static_assert (sizeof (int) <= sizeof (long int),
+                  "_Static_assert does not work in struct");
+  long int y;
+};
+
+// Check UTF-8 literals.
+#define u8 syntax error!
+char const utf8_literal[] = u8"happens to be ASCII" "another string";
+
+// Check duplicate typedefs.
+typedef long *long_ptr;
+typedef long int *long_ptr;
+typedef long_ptr long_ptr;
+
+// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1.
+struct anonymous
+{
+  union {
+    struct { int i; int j; };
+    struct { int k; long int l; } w;
+  };
+  int m;
+} v1;
+'
+
+# Test code for whether the C compiler supports C11 (body of main).
+ac_c_conftest_c11_main='
+  _Static_assert ((offsetof (struct anonymous, i)
+		   == offsetof (struct anonymous, w.k)),
+		  "Anonymous union alignment botch");
+  v1.i = 2;
+  v1.w.k = 5;
+  ok |= v1.i != 5;
+'
+
+# Test code for whether the C compiler supports C11 (complete).
+ac_c_conftest_c11_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+${ac_c_conftest_c11_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  ${ac_c_conftest_c99_main}
+  ${ac_c_conftest_c11_main}
+  return ok;
+}
+"
+
+# Test code for whether the C compiler supports C99 (complete).
+ac_c_conftest_c99_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  ${ac_c_conftest_c99_main}
+  return ok;
+}
+"
+
+# Test code for whether the C compiler supports C89 (complete).
+ac_c_conftest_c89_program="${ac_c_conftest_c89_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  return ok;
+}
+"
+
+as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H"
+as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H"
+as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H"
+as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H"
+as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H"
+as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H"
+as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H"
+as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H"
+as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H"
+
+# Auxiliary files required by this configure script.
+ac_aux_files="config.guess config.sub compile missing install-sh"
+
+# Locations in which to look for auxiliary files.
+ac_aux_dir_candidates="${srcdir}/config"
+
+# Search for a directory containing all of the required auxiliary files,
+# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates.
+# If we don't find one directory that contains all the files we need,
+# we report the set of missing files from the *first* directory in
+# $ac_aux_dir_candidates and give up.
+ac_missing_aux_files=""
+ac_first_candidate=:
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in $ac_aux_dir_candidates
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+  as_found=:
+
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}:  trying $as_dir" >&5
+  ac_aux_dir_found=yes
+  ac_install_sh=
+  for ac_aux in $ac_aux_files
+  do
+    # As a special case, if "install-sh" is required, that requirement
+    # can be satisfied by any of "install-sh", "install.sh", or "shtool",
+    # and $ac_install_sh is set appropriately for whichever one is found.
+    if test x"$ac_aux" = x"install-sh"
+    then
+      if test -f "${as_dir}install-sh"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}install-sh found" >&5
+        ac_install_sh="${as_dir}install-sh -c"
+      elif test -f "${as_dir}install.sh"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}install.sh found" >&5
+        ac_install_sh="${as_dir}install.sh -c"
+      elif test -f "${as_dir}shtool"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}shtool found" >&5
+        ac_install_sh="${as_dir}shtool install -c"
+      else
+        ac_aux_dir_found=no
+        if $ac_first_candidate; then
+          ac_missing_aux_files="${ac_missing_aux_files} install-sh"
+        else
+          break
+        fi
+      fi
+    else
+      if test -f "${as_dir}${ac_aux}"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}${ac_aux} found" >&5
+      else
+        ac_aux_dir_found=no
+        if $ac_first_candidate; then
+          ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}"
+        else
+          break
+        fi
+      fi
+    fi
+  done
+  if test "$ac_aux_dir_found" = yes; then
+    ac_aux_dir="$as_dir"
+    break
+  fi
+  ac_first_candidate=false
+
+  as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+  as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5
+fi
+
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+if test -f "${ac_aux_dir}config.guess"; then
+  ac_config_guess="$SHELL ${ac_aux_dir}config.guess"
+fi
+if test -f "${ac_aux_dir}config.sub"; then
+  ac_config_sub="$SHELL ${ac_aux_dir}config.sub"
+fi
+if test -f "$ac_aux_dir/configure"; then
+  ac_configure="$SHELL ${ac_aux_dir}configure"
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+printf "%s\n" "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+printf "%s\n" "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file'
+	    and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+    # Put auxiliary autoconf scripts in config/
+
+
+    # Do not expect full GNU conformance (files like NEWS might be missing).
+    # Minimal checks only.
+am__api_version='1.16'
+
+
+
+  # Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+printf %s "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test ${ac_cv_path_install+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    # Account for fact that we put trailing slashes in our PATH walk.
+case $as_dir in #((
+  ./ | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    rm -rf conftest.one conftest.two conftest.dir
+	    echo one > conftest.one
+	    echo two > conftest.two
+	    mkdir conftest.dir
+	    if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" &&
+	      test -s conftest.one && test -s conftest.two &&
+	      test -s conftest.dir/conftest.one &&
+	      test -s conftest.dir/conftest.two
+	    then
+	      ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c"
+	      break 3
+	    fi
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test ${ac_cv_path_install+y}; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+printf "%s\n" "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+printf %s "checking whether build environment is sane... " >&6; }
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[\\\"\#\$\&\'\`$am_lf]*)
+    as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+  *[\\\"\#\$\&\'\`$am_lf\ \	]*)
+    as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$*" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$*" != "X $srcdir/configure conftest.file" \
+	&& test "$*" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	as_fn_error $? "ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment" "$LINENO" 5
+     fi
+     if test "$2" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
+   test "$2" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+
+rm -f conftest.file
+
+test "$program_prefix" != NONE &&
+  program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_script"`
+
+
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+
+
+  if test x"${MISSING+set}" != xset; then
+  MISSING="\${SHELL} '$am_aux_dir/missing'"
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+printf "%s\n" "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_STRIP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+printf "%s\n" "$STRIP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_STRIP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+printf "%s\n" "$ac_ct_STRIP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5
+printf %s "checking for a race-free mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if test ${ac_cv_path_mkdir+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_prog in mkdir gmkdir; do
+	 for ac_exec_ext in '' $ac_executable_extensions; do
+	   as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue
+	   case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #(
+	     'mkdir ('*'coreutils) '* | \
+	     'BusyBox '* | \
+	     'mkdir (fileutils) '4.1*)
+	       ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext
+	       break 3;;
+	   esac
+	 done
+       done
+  done
+IFS=$as_save_IFS
+
+fi
+
+  test -d ./--version && rmdir ./--version
+  if test ${ac_cv_path_mkdir+y}; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+printf "%s\n" "$MKDIR_P" >&6; }
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_AWK+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+printf "%s\n" "$AWK" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  test -n "$AWK" && break
+done
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval test \${ac_cv_prog_make_${ac_make}_set+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+	@echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+  SET_MAKE=
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+# Check whether --enable-silent-rules was given.
+if test ${enable_silent_rules+y}
+then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=1;;
+esac
+am_make=${MAKE-make}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+printf %s "checking whether $am_make supports nested variables... " >&6; }
+if test ${am_cv_make_support_nested_variables+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if printf "%s\n" 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+printf "%s\n" "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  am__isrc=' -I$(srcdir)'
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='metamath'
+ VERSION='0.199.pre-29-Jan-2022'
+
+
+printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
+
+
+printf "%s\n" "#define VERSION \"$VERSION\"" >>confdefs.h
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+mkdir_p='$(MKDIR_P)'
+
+# We need awk for the "check" target (and possibly the TAP driver).  The
+# system "awk" is bad on some platforms.
+# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
+
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar  pax cpio none'
+
+am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
+
+
+
+
+
+# Variables for tags utilities; see am/tags.am
+if test -z "$CTAGS"; then
+  CTAGS=ctags
+fi
+
+if test -z "$ETAGS"; then
+  ETAGS=etags
+fi
+
+if test -z "$CSCOPE"; then
+  CSCOPE=cscope
+fi
+
+
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes.  So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+  cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present.  This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message.  This
+can help us improve future automake versions.
+
+END
+  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+    echo 'Configuration will proceed anyway, since you have set the' >&2
+    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+    echo >&2
+  else
+    cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+    as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
+  fi
+fi
+
+
+    # unique file name likely not existing outside the source tree.
+    # catches incorrect --srcdir parameter in autoconf call.
+
+
+    # requires a config.h.in file, that is used as a template for a config.h,
+    # modified according to following commands.  A script created by autoconf
+    # (configure) will finally create the desired config.h.
+    # Metamath sources do not include this config.h so far, although it is
+    # common practise to do so.
+ac_config_headers="$ac_config_headers config.h"
+
+
+    # requires a Makefile.am, that is modified according to following commands.
+    # AC_OUTPUT creates a Makefile.in based on this modifications..
+ac_config_files="$ac_config_files Makefile src/Makefile"
+
+
+# Checks for programs.
+
+    # add this to support the tools folder
+# AC_PROG_AWK
+
+    # test existence of a C compiler
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
+set dummy ${ac_tool_prefix}clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}clang"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "clang", so it can be a program name with args.
+set dummy clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="clang"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+
+
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion -version; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+printf %s "checking whether the C compiler works... " >&6; }
+ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else $as_nop
+  ac_file=''
+fi
+if test -z "$ac_file"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+printf %s "checking for C compiler default output file name... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+printf "%s\n" "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+printf %s "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else $as_nop
+  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+printf "%s\n" "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main (void)
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+printf %s "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+printf "%s\n" "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+printf %s "checking for suffix of object files... " >&6; }
+if test ${ac_cv_objext+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+printf "%s\n" "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+printf %s "checking whether the compiler supports GNU C... " >&6; }
+if test ${ac_cv_c_compiler_gnu+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_compiler_gnu=yes
+else $as_nop
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+y}
+ac_save_CFLAGS=$CFLAGS
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+printf %s "checking whether $CC accepts -g... " >&6; }
+if test ${ac_cv_prog_cc_g+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_g=yes
+else $as_nop
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+if test $ac_test_CFLAGS; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+ac_prog_cc_stdc=no
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+printf %s "checking for $CC option to enable C11 features... " >&6; }
+if test ${ac_cv_prog_cc_c11+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c11=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c11_program
+_ACEOF
+for ac_arg in '' -std=gnu11
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c11=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c11" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c11" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c11" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+     CC="$CC $ac_cv_prog_cc_c11"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
+  ac_prog_cc_stdc=c11
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+printf %s "checking for $CC option to enable C99 features... " >&6; }
+if test ${ac_cv_prog_cc_c99+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c99_program
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c99" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c99" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+     CC="$CC $ac_cv_prog_cc_c99"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+  ac_prog_cc_stdc=c99
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+printf %s "checking for $CC option to enable C89 features... " >&6; }
+if test ${ac_cv_prog_cc_c89+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c89_program
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c89" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c89" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+     CC="$CC $ac_cv_prog_cc_c89"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+  ac_prog_cc_stdc=c89
+fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+  ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+printf %s "checking whether $CC understands -c and -o together... " >&6; }
+if test ${am_cv_prog_cc_c_o+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+   ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+printf "%s\n" "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
+printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; }
+cat > confinc.mk << 'END'
+am__doit:
+	@echo this is the am__doit target >confinc.out
+.PHONY: am__doit
+END
+am__include="#"
+am__quote=
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+  { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
+   (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }
+  case $?:`cat confinc.out 2>/dev/null` in #(
+  '0:this is the am__doit target') :
+    case $s in #(
+  BSD) :
+    am__include='.include' am__quote='"' ;; #(
+  *) :
+    am__include='include' am__quote='' ;;
+esac ;; #(
+  *) :
+     ;;
+esac
+  if test "$am__include" != "#"; then
+    _am_result="yes ($s style)"
+    break
+  fi
+done
+rm -f confinc.* confmf.*
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
+printf "%s\n" "${_am_result}" >&6; }
+
+# Check whether --enable-dependency-tracking was given.
+if test ${enable_dependency_tracking+y}
+then :
+  enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
+else
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC"   am_compiler_list=
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+printf %s "checking dependency style of $depcc... " >&6; }
+if test ${am_cv_CC_dependencies_compiler_type+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+printf "%s\n" "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+
+    # find an appropriate install executable
+    # should be replaced with AC_PROG_MAKE_SET
+
+
+# Checks for libraries.
+
+# Checks for header files.
+
+    # Fill config.h.in with macros HAVE_<header>_H and define them to 1,
+    # if a standard complient header is found.
+
+ac_header= ac_cache=
+for ac_item in $ac_header_c_list
+do
+  if test $ac_cache; then
+    ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
+    if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
+      printf "%s\n" "#define $ac_item 1" >> confdefs.h
+    fi
+    ac_header= ac_cache=
+  elif test $ac_header; then
+    ac_cache=$ac_item
+  else
+    ac_header=$ac_item
+  fi
+done
+
+
+
+
+
+
+
+
+if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
+then :
+
+printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default"
+if test "x$ac_cv_header_limits_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LIMITS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdlib_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_STDLIB_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default"
+if test "x$ac_cv_header_string_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRING_H 1" >>confdefs.h
+
+fi
+
+
+   # config.h: set HAVE_STDBOOL_H
+ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default"
+if test "x$ac_cv_type__Bool" = xyes
+then :
+
+printf "%s\n" "#define HAVE__BOOL 1" >>confdefs.h
+
+
+fi
+
+   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5
+printf %s "checking for stdbool.h that conforms to C99... " >&6; }
+if test ${ac_cv_header_stdbool_h+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdbool.h>
+
+             #ifndef __bool_true_false_are_defined
+               #error "__bool_true_false_are_defined is not defined"
+             #endif
+             char a[__bool_true_false_are_defined == 1 ? 1 : -1];
+
+             /* Regardless of whether this is C++ or "_Bool" is a
+                valid type name, "true" and "false" should be usable
+                in #if expressions and integer constant expressions,
+                and "bool" should be a valid type name.  */
+
+             #if !true
+               #error "'true' is not true"
+             #endif
+             #if true != 1
+               #error "'true' is not equal to 1"
+             #endif
+             char b[true == 1 ? 1 : -1];
+             char c[true];
+
+             #if false
+               #error "'false' is not false"
+             #endif
+             #if false != 0
+               #error "'false' is not equal to 0"
+             #endif
+             char d[false == 0 ? 1 : -1];
+
+             enum { e = false, f = true, g = false * true, h = true * 256 };
+
+             char i[(bool) 0.5 == true ? 1 : -1];
+             char j[(bool) 0.0 == false ? 1 : -1];
+             char k[sizeof (bool) > 0 ? 1 : -1];
+
+             struct sb { bool s: 1; bool t; } s;
+             char l[sizeof s.t > 0 ? 1 : -1];
+
+             /* The following fails for
+                HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */
+             bool m[h];
+             char n[sizeof m == h * sizeof m[0] ? 1 : -1];
+             char o[-1 - (bool) 0 < 0 ? 1 : -1];
+             /* Catch a bug in an HP-UX C compiler.  See
+         https://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
+         https://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
+              */
+             bool p = true;
+             bool *pp = &p;
+
+             /* C 1999 specifies that bool, true, and false are to be
+                macros, but C++ 2011 and later overrule this.  */
+             #if __cplusplus < 201103
+              #ifndef bool
+               #error "bool is not defined"
+              #endif
+              #ifndef false
+               #error "false is not defined"
+              #endif
+              #ifndef true
+               #error "true is not defined"
+              #endif
+             #endif
+
+             /* If _Bool is available, repeat with it all the tests
+                above that used bool.  */
+             #ifdef HAVE__BOOL
+               struct sB { _Bool s: 1; _Bool t; } t;
+
+               char q[(_Bool) 0.5 == true ? 1 : -1];
+               char r[(_Bool) 0.0 == false ? 1 : -1];
+               char u[sizeof (_Bool) > 0 ? 1 : -1];
+               char v[sizeof t.t > 0 ? 1 : -1];
+
+               _Bool w[h];
+               char x[sizeof m == h * sizeof m[0] ? 1 : -1];
+               char y[-1 - (_Bool) 0 < 0 ? 1 : -1];
+               _Bool z = true;
+               _Bool *pz = &p;
+             #endif
+
+int
+main (void)
+{
+
+             bool ps = &s;
+             *pp |= p;
+             *pp |= ! p;
+
+             #ifdef HAVE__BOOL
+               _Bool pt = &t;
+               *pz |= z;
+               *pz |= ! z;
+             #endif
+
+             /* Refer to every declared value, so they cannot be
+                discarded as unused.  */
+             return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !j + !k
+                     + !l + !m + !n + !o + !p + !pp + !ps
+             #ifdef HAVE__BOOL
+                     + !q + !r + !u + !v + !w + !x + !y + !z + !pt
+             #endif
+                    );
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_header_stdbool_h=yes
+else $as_nop
+  ac_cv_header_stdbool_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5
+printf "%s\n" "$ac_cv_header_stdbool_h" >&6; }
+
+if test $ac_cv_header_stdbool_h = yes; then
+
+printf "%s\n" "#define HAVE_STDBOOL_H 1" >>confdefs.h
+
+fi
+
+
+# Checks for typedefs, structures, and compiler characteristics.
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes
+then :
+
+else $as_nop
+
+printf "%s\n" "#define size_t unsigned int" >>confdefs.h
+
+fi
+
+
+# Checks for library functions.
+
+
+  # Make sure we can run config.sub.
+$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+printf %s "checking build system type... " >&6; }
+if test ${ac_cv_build+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+printf "%s\n" "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+printf %s "checking host system type... " >&6; }
+if test ${ac_cv_host+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+printf "%s\n" "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5
+printf %s "checking for GNU libc compatible malloc... " >&6; }
+if test ${ac_cv_func_malloc_0_nonnull+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "$cross_compiling" = yes
+then :
+  case "$host_os" in # ((
+		  # Guess yes on platforms where we know the result.
+		  *-gnu* | freebsd* | netbsd* | openbsd* | bitrig* \
+		  | hpux* | solaris* | cygwin* | mingw* | msys* )
+		    ac_cv_func_malloc_0_nonnull=yes ;;
+		  # If we don't know, assume the worst.
+		  *) ac_cv_func_malloc_0_nonnull=no ;;
+		esac
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+int
+main (void)
+{
+void *p = malloc (0);
+		   int result = !p;
+		   free (p);
+		   return result;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  ac_cv_func_malloc_0_nonnull=yes
+else $as_nop
+  ac_cv_func_malloc_0_nonnull=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5
+printf "%s\n" "$ac_cv_func_malloc_0_nonnull" >&6; }
+if test $ac_cv_func_malloc_0_nonnull = yes
+then :
+
+printf "%s\n" "#define HAVE_MALLOC 1" >>confdefs.h
+
+else $as_nop
+  printf "%s\n" "#define HAVE_MALLOC 0" >>confdefs.h
+
+   case " $LIBOBJS " in
+  *" malloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS malloc.$ac_objext"
+ ;;
+esac
+
+
+printf "%s\n" "#define malloc rpl_malloc" >>confdefs.h
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible realloc" >&5
+printf %s "checking for GNU libc compatible realloc... " >&6; }
+if test ${ac_cv_func_realloc_0_nonnull+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "$cross_compiling" = yes
+then :
+  case "$host_os" in # ((
+		  # Guess yes on platforms where we know the result.
+		  *-gnu* | freebsd* | netbsd* | openbsd* | bitrig* \
+		  | hpux* | solaris* | cygwin* | mingw* | msys* )
+		    ac_cv_func_realloc_0_nonnull=yes ;;
+		  # If we don't know, assume the worst.
+		  *) ac_cv_func_realloc_0_nonnull=no ;;
+		esac
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+int
+main (void)
+{
+void *p = realloc (0, 0);
+		   int result = !p;
+		   free (p);
+		   return result;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  ac_cv_func_realloc_0_nonnull=yes
+else $as_nop
+  ac_cv_func_realloc_0_nonnull=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_realloc_0_nonnull" >&5
+printf "%s\n" "$ac_cv_func_realloc_0_nonnull" >&6; }
+if test $ac_cv_func_realloc_0_nonnull = yes
+then :
+
+printf "%s\n" "#define HAVE_REALLOC 1" >>confdefs.h
+
+else $as_nop
+  printf "%s\n" "#define HAVE_REALLOC 0" >>confdefs.h
+
+   case " $LIBOBJS " in
+  *" realloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS realloc.$ac_objext"
+ ;;
+esac
+
+
+printf "%s\n" "#define realloc rpl_realloc" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_func "$LINENO" "strchr" "ac_cv_func_strchr"
+if test "x$ac_cv_func_strchr" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRCHR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strcspn" "ac_cv_func_strcspn"
+if test "x$ac_cv_func_strcspn" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRCSPN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strstr" "ac_cv_func_strstr"
+if test "x$ac_cv_func_strstr" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRSTR 1" >>confdefs.h
+
+fi
+
+
+    # copied to Makefile.am
+# Enable gcc warning flags, but only if they seem to work
+new_CFLAGS="-Wall -Wextra"
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS $new_CFLAGS"
+
+    # configure displays this message
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gcc warning flags" >&5
+printf %s "checking for gcc warning flags... " >&6; }
+
+    # compile this program with new_CFLAGS enabled, see if flags are
+    # accepted, provide them to automake
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <stdio.h>
+int f() {
+  return 0;
+}
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+   AM_CFLAGS="$AM_CFLAGS $new_CFLAGS"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+
+    # compile the following program with a bunch of optimization flags.
+    # If accepted, provide them to automake.
+    # Take care of a possible collision with flag -O2.
+# Try to optimize.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for optimization flags" >&5
+printf %s "checking for optimization flags... " >&6; }
+new_CFLAGS="-O3 -funroll-loops -finline-functions -fomit-frame-pointer"
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS $new_CFLAGS"
+# Remove any existing "-O2", or it will override what we're doing.
+CFLAGS=$( printf "%s" "$CFLAGS" | sed -e 's/ -O2/ /' )
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <stdio.h>
+int f() {
+  return 0;
+}
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+   CFLAGS="$saved_CFLAGS"
+   CFLAGS=$( printf "%s" "$CFLAGS" | sed -e 's/ -O2/ /' )
+   AM_CFLAGS="$AM_CFLAGS $new_CFLAGS"
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+   CFLAGS="$saved_CFLAGS"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+
+# Can we use "inline"? We don't use AC_C _INLINE because metamath.c
+# does not include the autoconf-generated file.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for 'inline' support" >&5
+printf %s "checking for 'inline' support... " >&6; }
+new_CFLAGS="-DINLINE=inline"
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS $new_CFLAGS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+    inline int f(void) {}
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+   AM_CFLAGS="$AM_CFLAGS $new_CFLAGS"
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+CFLAGS="$saved_CFLAGS"
+
+    # replace all @AM_CFLAGS@ and @CFLAGS@ variables in Makefile.am
+    # with values found here
+echo "CFLAGS=$CFLAGS"
+
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
+printf %s "checking that generated files are newer than configure... " >&6; }
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5
+printf "%s\n" "done" >&6; }
+ if test -n "$EXEEXT"; then
+  am__EXEEXT_TRUE=
+  am__EXEEXT_FALSE='#'
+else
+  am__EXEEXT_TRUE='#'
+  am__EXEEXT_FALSE=
+fi
+
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+  as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else $as_nop
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" ""	$as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh).  This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2)            ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  printf "%s\n" "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else $as_nop
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else $as_nop
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n.  New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by metamath $as_me 0.199.pre-29-Jan-2022, which was
+generated by GNU Autoconf 2.71.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <nm.NOSPAM@alum.mit.edu>."
+
+_ACEOF
+ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
+ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config='$ac_cs_config_escaped'
+ac_cs_version="\\
+metamath config.status 0.199.pre-29-Jan-2022
+configured by $0, generated by GNU Autoconf 2.71,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    printf "%s\n" "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    printf "%s\n" "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    printf "%s\n" "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  printf "%s\n" "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+    "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files
+  test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers
+  test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = ""
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=[	 ]*/{
+h
+s///
+s/^/:/
+s/[	 ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[	 ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+printf "%s\n" "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`printf "%s\n" "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+  ac_MKDIR_P=$MKDIR_P
+  case $MKDIR_P in
+  [\\/$]* | ?:[\\/]* ) ;;
+  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      printf "%s\n" "/* $configure_input  */" >&1 \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+printf "%s\n" "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    printf "%s\n" "/* $configure_input  */" >&1 \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$_am_arg" : 'X\(//\)[^/]' \| \
+	 X"$_am_arg" : 'X\(//\)$' \| \
+	 X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$_am_arg" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+  :C)  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+printf "%s\n" "$as_me: executing $ac_file commands" >&6;}
+ ;;
+  esac
+
+
+  case $ac_file$ac_mode in
+    "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+  # Older Autoconf quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  # TODO: see whether this extra hack can be removed once we start
+  # requiring Autoconf 2.70 or later.
+  case $CONFIG_FILES in #(
+  *\'*) :
+    eval set x "$CONFIG_FILES" ;; #(
+  *) :
+    set x $CONFIG_FILES ;; #(
+  *) :
+     ;;
+esac
+  shift
+  # Used to flag and report bootstrapping failures.
+  am_rc=0
+  for am_mf
+  do
+    # Strip MF so we end up with the name of the file.
+    am_mf=`printf "%s\n" "$am_mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile which includes
+    # dependency-tracking related rules and includes.
+    # Grep'ing the whole file directly is not great: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+      || continue
+    am_dirpart=`$as_dirname -- "$am_mf" ||
+$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$am_mf" : 'X\(//\)[^/]' \| \
+	 X"$am_mf" : 'X\(//\)$' \| \
+	 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$am_mf" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+    am_filepart=`$as_basename -- "$am_mf" ||
+$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$am_mf" : 'X\(//\)$' \| \
+	 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$am_mf" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+    { echo "$as_me:$LINENO: cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles" >&5
+   (cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } || am_rc=$?
+  done
+  if test $am_rc -ne 0; then
+    { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "Something went wrong bootstrapping makefile fragments
+    for automatic dependency tracking.  If GNU make was not used, consider
+    re-running the configure script with MAKE=\"gmake\" (or whatever is
+    necessary).  You can also try re-running configure with the
+    '--disable-dependency-tracking' option to at least be able to build
+    the package (albeit without support for automatic dependency tracking).
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+  { am_dirpart=; unset am_dirpart;}
+  { am_filepart=; unset am_filepart;}
+  { am_mf=; unset am_mf;}
+  { am_rc=; unset am_rc;}
+  rm -f conftest-deps.mk
+}
+ ;;
+
+  esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
diff --git a/configure.ac b/configure.ac
index 263d3d8..a223b98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,22 +1,58 @@
 #                                               -*- Autoconf -*-
 # Process this file with autoconf to produce a configure script.
 
+# seems to be created by autoscan and adapted to the needs of Metamath
+
+    # the version of autoconf needed to translate this file into a portable
+    # configure script.  As of this writing version 2.71 is published.
 AC_PREREQ([2.65])
-AC_INIT([metamath], [0.114], [nm.NOSPAM@alum.mit.edu])
-AC_CONFIG_SRCDIR([metamath.c])
-AC_CONFIG_HEADERS([config.h])
 
+    # program name, version, email to report bugs
+AC_INIT([metamath], [m4_esyscmd_s([./build.sh -va])], [nm.NOSPAM@alum.mit.edu])
+
+    # Put auxiliary autoconf scripts in config/
+AC_CONFIG_AUX_DIR([config])
+
+    # Do not expect full GNU conformance (files like NEWS might be missing).
+    # Minimal checks only.
 AM_INIT_AUTOMAKE([foreign])
-AC_CONFIG_FILES([Makefile])
+
+    # unique file name likely not existing outside the source tree.
+    # catches incorrect --srcdir parameter in autoconf call.
+AC_CONFIG_SRCDIR([src/metamath.c])
+
+    # requires a config.h.in file, that is used as a template for a config.h,
+    # modified according to following commands.  A script created by autoconf
+    # (configure) will finally create the desired config.h.
+    # Metamath sources do not include this config.h so far, although it is
+    # common practise to do so.
+AC_CONFIG_HEADERS([config.h])
+
+    # requires a Makefile.am, that is modified according to following commands.
+    # AC_OUTPUT creates a Makefile.in based on this modifications..
+AC_CONFIG_FILES([Makefile src/Makefile])
 
 # Checks for programs.
+
+    # add this to support the tools folder
+# AC_PROG_AWK
+
+    # test existence of a C compiler
 AC_PROG_CC
+
+    # find an appropriate install executable
+    # should be replaced with AC_PROG_MAKE_SET
 AC_PROG_INSTALL
 
 # Checks for libraries.
 
 # Checks for header files.
+
+    # Fill config.h.in with macros HAVE_<header>_H and define them to 1,
+    # if a standard complient header is found.
 AC_CHECK_HEADERS([limits.h stdlib.h string.h])
+
+   # config.h: set HAVE_STDBOOL_H
 AC_HEADER_STDBOOL
 
 # Checks for typedefs, structures, and compiler characteristics.
@@ -27,11 +63,17 @@ AC_FUNC_MALLOC
 AC_FUNC_REALLOC
 AC_CHECK_FUNCS([strchr strcspn strstr])
 
+    # copied to Makefile.am
 # Enable gcc warning flags, but only if they seem to work
 new_CFLAGS="-Wall -Wextra"
 saved_CFLAGS="$CFLAGS"
 CFLAGS="$CFLAGS $new_CFLAGS"
+
+    # configure displays this message
 AC_MSG_CHECKING([[for gcc warning flags]])
+
+    # compile this program with new_CFLAGS enabled, see if flags are
+    # accepted, provide them to automake
 AC_LINK_IFELSE(
   [AC_LANG_PROGRAM([[
 #include <stdio.h>
@@ -43,6 +85,9 @@ int f() {
   [AC_MSG_RESULT([no])
    AM_CFLAGS="$AM_CFLAGS $new_CFLAGS"])
 
+    # compile the following program with a bunch of optimization flags.
+    # If accepted, provide them to automake.
+    # Take care of a possible collision with flag -O2.
 # Try to optimize.
 AC_MSG_CHECKING([[for optimization flags]])
 new_CFLAGS="-O3 -funroll-loops -finline-functions -fomit-frame-pointer"
@@ -79,6 +124,8 @@ AC_COMPILE_IFELSE(
   [AC_MSG_RESULT([no])])
 CFLAGS="$saved_CFLAGS"
 
+    # replace all @AM_CFLAGS@ and @CFLAGS@ variables in Makefile.am
+    # with values found here
 echo "CFLAGS=$CFLAGS"
 AC_SUBST([AM_CFLAGS])
 AC_SUBST([CFLAGS])
diff --git a/mmcmdl.h b/mmcmdl.h
deleted file mode 100644
index b797c9f..0000000
--- a/mmcmdl.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2018  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMCMDL_H_
-#define METAMATH_MMCMDL_H_
-
-#include "mmvstr.h"
-#include "mmdata.h"
-
-flag processCommandLine(void);
-flag getFullArg(long arg, vstring cmdList);
-void parseCommandLine(vstring line);
-flag lastArgMatches(vstring argString);
-flag cmdMatches(vstring cmdString);
-long switchPos(vstring swString);
-void printCommandError(vstring line, long arg, vstring errorMsg);
-void freeCommandLine(void); /* 4-May-2017 Ari Ferrera */
-
-#define DEFAULT_COLUMN 16
-extern pntrString *g_rawArgPntr;
-extern nmbrString *g_rawArgNmbr;
-extern long g_rawArgs;
-extern pntrString *g_fullArg;
-extern vstring g_fullArgString; /* 1-Nov-2013 nm g_fullArg as one string */
-extern vstring g_commandPrompt;
-extern vstring g_commandLine;
-extern long g_showStatement;
-extern vstring g_logFileName;
-extern vstring g_texFileName;
-extern flag g_PFASmode; /* Proof assistant mode, invoked by PROVE command */
-/* 15-Aug-2020 nm g_queryMode is global only within mmcmdl.c */
-/* extern flag g_queryMode; */ /* If 1, explicit questions will be asked even if
-                          a field in the input command line is optional */
-extern flag g_sourceChanged; /* Flag that user made some change to the source
-                              file*/
-extern flag g_proofChanged; /* Flag that user made some change to proof in
-                             progress*/
-extern flag g_commandEcho; /* Echo full command */
-extern flag g_memoryStatus; /* Always show memory */
-
-/* 31-Dec-2017 nm */
-extern flag g_sourceHasBeenRead; /* 1 if a source file has been read in */
-/* 31-Dec-2017 nm */
-extern vstring g_rootDirectory; /* Directory to use for included files */
-
-
-#endif /* METAMATH_MMCMDL_H_ */
diff --git a/mmcmds.h b/mmcmds.h
deleted file mode 100644
index 839e984..0000000
--- a/mmcmds.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMCMDS_H_
-#define METAMATH_MMCMDS_H_
-
-#include "mmvstr.h"
-#include "mmdata.h"
-
-/* Type (i.e. print) a statement */
-void typeStatement(long statemNum,
-  flag briefFlag,
-  flag commentOnlyFlag,
-  flag texFlag,
-  flag htmlFlg);
-/* Type (i.e. print) a proof */
-void typeProof(long statemNum,
-  flag pipFlag, /* Type proofInProgress instead of source file proof */
-  long startStep, long endStep,
-  long endIndent,
-  flag essentialFlag,
-  flag renumberFlag,
-  flag unknownFlag,
-  flag notUnifiedFlag,
-  flag reverseFlag,
-  flag noIndentFlag,
-  long startColumn,
-  flag skipRepeatedSteps, /* 28-Jun-2013 nm Added */
-  flag texFlag,
-  flag htmlFlg);
-/* Show details of step */
-void showDetailStep(long statemNum, long detailStep);
-/* Summary of statements in proof */
-void proofStmtSumm(long statemNum, flag essentialFlag, flag texFlag);
-/* Traces back the statements used by a proof, recursively. */
-flag traceProof(long statemNum,
-  flag essentialFlag,
-  flag axiomFlag,
-  vstring matchList, /* 19-May-2013 nm */
-  vstring traceToList, /* 18-Jul-2015 nm */
-  flag testOnlyFlag /* 20-May-2013 nm */);
-void traceProofWork(long statemNum,
-  flag essentialFlag,
-  vstring traceToList, /* 18-Jul-2015 nm */
-  vstring *statementUsedFlagsP, /* 'y'/'n' flag that statement is used */
-  nmbrString **unprovedListP);
-/* Traces back the statements used by a proof, recursively, with tree display.*/
-void traceProofTree(long statemNum,
-  flag essentialFlag, long endIndent);
-void traceProofTreeRec(long statemNum,
-  flag essentialFlag, long endIndent, long recursDepth);
-/* Counts the number of steps a completely exploded proof would require */
-/* (Recursive) */
-/* 0 is returned if some assertions have incomplete proofs. */
-double countSteps(long statemNum, flag essentialFlag);
-/* Traces what statements require the use of a given statement */
-vstring traceUsage(long statemNum,
-  flag recursiveFlag,
-  long cutoffStmt /* if nonzero, stop scan there */ /* 18-Jul-2015 nm */);
-vstring htmlDummyVars(long showStmt);  /* 12-Aug-2017 nm */
-vstring htmlAllowedSubst(long showStmt);  /* 4-Jan-2014 nm */
-
-void readInput(void);
-/* WRITE SOURCE command */
-void writeSource(/* flag cleanFlag, 3-May-2017 */ /* 1 = "/ CLEAN" qualifier was chosen */
-                flag reformatFlag /* 1 = "/ FORMAT", 2 = "/REWRAP" */,
-                /* 31-Dec-2017 nm */
-                flag splitFlag,  /* /SPLIT - write out separate $[ $] includes */
-                flag noVersioningFlag, /* /NO_VERSIONING - no ~1 backup */
-                flag keepSplitsFlag, /* /KEEP_SPLITS - don't delete included files
-                                      when /SPIT is not specified */
-                vstring extractLabels /* "" means write everything */
-                );
-
-/* 24-Aug-2020 nm */
-/* Get info for WRITE SOURCE ... / EXTRACT */
-void writeExtractedSource(vstring extractLabels, /* EXTRACT argument provided by user */
-  vstring fullOutput_fn, flag noVersioningFlag);
-
-void fixUndefinedLabels(vstring extractNeeded, vstring *buf);
-
-void writeDict(void);
-void eraseSource(void);
-void verifyProofs(vstring labelMatch, flag verifyFlag);
-
-
-/* 7-Nov-2015 nm Added this function for date consistency */
-/* If checkFiles = 0, do not open external files.
-   If checkFiles = 1, check mm*.html, presence of gifs, etc. */
-void verifyMarkup(vstring labelMatch, flag dateSkip, flag topDateSkip,
-    flag fileSkip,
-    flag underscoreSkip, /* 25-Jun-2020 nm */
-    flag mathboxSkip, /* 17-Jul-2020 nm */
-    flag verboseMode); /* 26-Dec-2016 nm */
-
-/* 10-Dec-2018 nm Added */
-void processMarkup(vstring inputFileName, vstring outputFileName,
-    flag processCss, long actionBits);
-
-/* 3-May-2016 nm */
-/* List "discouraged" statements with "(Proof modification is discouraged."
-   and "(New usage is discourged.)" comment markup tags. */
-void showDiscouraged(void);
-
-/* 14-Sep-2012 nm */
-/* Take a relative step FIRST, LAST, +nn, -nn (relative to the unknown
-   essential steps) or ALL, and return the actual step for use by ASSIGN,
-   IMPROVE, REPLACE, LET (or 0 in case of ALL, used by IMPROVE).  In case
-   stepStr is an unsigned integer nn, it is assumed to already be an actual
-   step and is returned as is.  If format is illegal, -1 is returned.  */
-long getStepNum(vstring relStep, /* User's argument */
-   nmbrString *pfInProgress, /* proofInProgress.proof */
-   flag allFlag /* 1 = "ALL" is permissable */);
-
-/* 22-Apr-2015 nm */
-/* Convert the actual step numbers of an unassigned step to the relative
-   -1, -2, etc. offset for SHOW NEW_PROOF ...  /UNKNOWN, to make it easier
-   for the user to ASSIGN the relative step number. A 0 is returned
-   for the last unknown step.  The step numbers of known steps are
-   unchanged.  */
-/* The caller must deallocate the returned nmbrString. */
-nmbrString *getRelStepNums(nmbrString *pfInProgress);
-
-/* 19-Sep-2012 nm */
-/* This procedure finds the next statement number whose label matches
-   stmtName.  Wildcards are allowed.  If uniqueFlag is 1,
-   there must be exactly one match, otherwise an error message is printed,
-   and -1 is returned.  If uniqueFlag is 0, the next match is
-   returned, or -1 if there are no more matches.  No error messages are
-   printed when uniqueFlag is 0, except for the special case of
-   startStmt=1.  For use by PROVE, REPLACE, ASSIGN. */
-long getStatementNum(vstring stmtName, /* Possibly with wildcards */
-    long startStmt, /* Starting statement number (1 for full scan) */
-    long maxStmt, /* Must be LESS THAN this statement number */
-    flag aAllowed, /* 1 means $a is allowed */
-    flag pAllowed, /* 1 means $p is allowed */
-    flag eAllowed, /* 1 means $e is allowed */
-    flag fAllowed, /* 1 means $f is allowed */
-    flag efOnlyForMaxStmt, /* If 1, $e and $f must belong to maxStmt */
-    flag uniqueFlag); /* If 1, match must be unique */
-
-/*extern vstring mainFileName;*/  /* 15-Aug-2020 nm Obsolete on 28-Dec-05 */
-
-/* For HELP processing */
-extern flag g_printHelp;
-void H(vstring helpLine);
-
-/* For MIDI files - added 8/28/00 */
-extern flag g_midiFlag; /* Set to 1 if typeProof() is to output MIDI file */
-extern vstring g_midiParam; /* Parameter string for MIDI file */
-void outputMidi(long plen, nmbrString *indentationLevels,
-  nmbrString *logicalFlags, vstring g_midiParameter, vstring statementLabel);
-
-
-#endif /* METAMATH_MMCMDS_H_ */
diff --git a/mmdata.h b/mmdata.h
deleted file mode 100644
index 6c205e3..0000000
--- a/mmdata.h
+++ /dev/null
@@ -1,524 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* mmdata.h - includes for some principal data structures and data-string
-   handling */
-
-#ifndef METAMATH_MMDATA_H_
-#define METAMATH_MMDATA_H_
-
-#include "mmvstr.h"
-
-/*E*/extern long db,db0,db1,db2,db3,db4,db5,db6,db7,db8,db9;/* debugging flags & variables */
-typedef char flag; /* A "flag" is simply a character intended for use as a
-                      yes/no logical Boolean; 0 = no and 1 = yes */
-
-extern flag g_listMode; /* 0 = metamath, 1 = list utility */
-extern flag g_toolsMode; /* In metamath mode:  0 = metamath, 1 = tools */
-
-typedef long nmbrString; /* String of numbers */
-typedef void* pntrString; /* String of pointers */
-
-
-
-enum mTokenType { var_, con_ };
-#define lb_ '{' /* ${ */
-#define rb_ '}' /* $} */
-#define v_  'v' /* $v */
-#define c_  'c' /* $c */
-#define a_  'a' /* $a */
-#define d_  'd' /* $d */
-#define e_  'e' /* $e */
-#define f_  'f' /* $f */
-#define p_  'p' /* $p */
-#define eq_ '=' /* $= */
-#define sc_ '.' /* $. (historically, used to be $; (semicolon) ) */
-#define illegal_ '?' /* anything else */
-/* Global variables related to current statement */
-extern int g_currentScope;
-/*extern long beginScopeStmtNum;*/ /* 15-Aug-2020 nm changed to local in mmpars.c */
-
-struct statement_struct { /* Array index is statement number, starting at 1 */
-  long lineNum; /* Line number in file; 0 means not yet determined */
-  vstring fileName; /* File statement is in; "" means not yet determined */
-  vstring labelName; /* Label of statement */
-  flag uniqueLabel; /* Flag that label is unique (future implementations may
-                      allow duplicate labels on hypotheses) */
-  char type;    /* 2nd character of keyword, e.g. 'e' for $e */
-  int scope;    /* Block scope level, increased by ${ and decreased by $};
-       ${ has scope _before_ the increase; $} has scope _before_ the decrease */
-  long beginScopeStatementNum;  /* statement of previous ${ ; 0 if we're in
-                outermost block */
-  long endScopeStatementNum;  /* statement of next $} (populated for opening
-                                 ${ only, 0 otherwise); g_statements+1 if
-                              we're in outermost block */ /* 24-Aug-2020 nm */
-  vstring statementPtr; /* Pointer to end of (unmodified) label section used
-             to determine file and line number for error or info messages about
-             the statement */
-  vstring labelSectionPtr; /* Source code before statement keyword
-                 - will be updated if labelSection changed */
-  long labelSectionLen;
-  /* 3-May-2017 nm Added labelSectionChanged */
-  char labelSectionChanged; /* Default is 0; if 1, labelSectionPtr points to an
-                               allocated vstring that must be freed by ERASE */
-  vstring mathSectionPtr; /* Source code between keyword and $= or $. */
-  long mathSectionLen;
-  /* 3-May-2017 nm Added mathSectionChanged */
-  char mathSectionChanged; /* Default is 0; if 1, mathSectionPtr points to an
-                               allocated vstring that must be freed by ERASE */
-  vstring proofSectionPtr; /* Source code between $= and $. */
-  long proofSectionLen;
-  /* 3-May-2017 nm Added proofSectionChanged */
-  char proofSectionChanged; /* Default is 0; if 1, proofSectionPtr points to an
-                               allocated vstring that must be freed by ERASE */
-  nmbrString *mathString; /* Parsed mathSection */
-  long mathStringLen;
-  nmbrString *proofString; /* Parsed proofSection (used by $p's only */
-  nmbrString *reqHypList; /* Required hypotheses (excluding $d's) */
-  nmbrString *optHypList; /* Optional hypotheses (excluding $d's) */
-  long numReqHyp; /* Number of required hypotheses */
-  nmbrString *reqVarList; /* Required variables */
-  nmbrString *optVarList; /* Optional variables */
-  nmbrString *reqDisjVarsA; /* Required disjoint variables, 1st of pair */
-  nmbrString *reqDisjVarsB; /* Required disjoint variables, 2nd of pair */
-  nmbrString *reqDisjVarsStmt; /* Req disjoint variables, statem number */
-  nmbrString *optDisjVarsA; /* Optional disjoint variables, 1st of pair */
-  nmbrString *optDisjVarsB; /* Optional disjoint variables, 2nd of pair */
-  nmbrString *optDisjVarsStmt; /* Opt disjoint variables, statem number */
-  long pinkNumber; /* 25-Oct-02 The $a/$p sequence number for web pages */
-  /* 18-Dec-2016 nm */
-  long headerStartStmt; /* # of stmt following previous $a, $p */
-  };
-
-/* Sort keys for statement labels (allocated by parseLabels) */
-extern long *g_labelKey;
-
-struct includeCall_struct {
-  /* This structure holds all information related to $[ $] (include) statements
-     in the input source files, for error message processing. */
-  vstring source_fn;  /* Name of the file where the
-       inclusion source is located (= parent file for $( Begin $[... etc.) */
-  vstring included_fn;  /* Name of the file in the
-       inclusion statement e.g. "$( Begin $[ included_fn..." */
-  long current_offset;  /* This is the starting
-      character position of the included file w.r.t entire source buffer */
-  long current_line; /* The line number
-      of the start of the included file (=1) or the continuation line of
-      the parent file */
-  flag pushOrPop; /* 0 means included file, 1 means continuation of parent */
-  vstring current_includeSource; /* (Currently) assigned
-      only if we may need it for a later Begin comparison */
-  long current_includeLength; /* Length of the file
-      to be included (0 if the file was previously included) */
-  };
-
-struct mathToken_struct {
-  vstring tokenName; /* may be used more than once at different scopes */
-  long length; /* to speed up parsing scans */
-  char tokenType; /* variable or constant - (char)var_ or (char)con_ */
-  flag active;  /* 1 if token is recognized in current scope */
-  int scope;    /* scope level token was declared at */
-  long tmp;     /* Temporary field use to speed up certain functions */
-  long statement; /* Statement declared in */
-  long endStatement; /* Statement of end of scope it was declared in */
-  };
-
-/* Sort keys for math tokens (allocated by parseMathDecl) */
-extern long *g_mathKey;
-
-extern long g_MAX_STATEMENTS;
-extern long g_MAX_MATHTOKENS;
-extern struct statement_struct *g_Statement;
-/*Obs*/ /*extern struct label_struct *label;*/
-
-/* Warning: mathToken[i] is 0-based, not 1-based! */
-extern struct mathToken_struct *g_MathToken;
-extern long g_statements, /*labels,*/ g_mathTokens;
-/*extern long maxMathTokenLength;*/ /* 15-Aug-2020 nm Not used */
-
-extern long g_MAX_INCLUDECALLS;
-extern struct includeCall_struct *g_IncludeCall;
-extern long g_includeCalls;
-
-extern char *g_sourcePtr; /* Pointer to buffer in memory with input source */
-extern long g_sourceLen; /* Number of chars. in all inputs files combined (after includes)*/
-
-/* 4-May-2016 nm */
-/* For use by getMarkupFlag() */
-#define PROOF_DISCOURAGED_MARKUP "(Proof modification is discouraged.)"
-#define USAGE_DISCOURAGED_MARKUP "(New usage is discouraged.)"
-/* Mode argument for getMarkupFlag() */
-#define PROOF_DISCOURAGED 1
-#define USAGE_DISCOURAGED 2
-#define RESET 0
-/* Mode argument for getContrib() */
-#define GC_RESET 0
-#define GC_RESET_STMT 10
-#define CONTRIBUTOR 1
-#define CONTRIB_DATE 2
-#define REVISER 3
-#define REVISE_DATE 4
-#define SHORTENER 5
-#define SHORTEN_DATE 6
-#define MOST_RECENT_DATE 7
-#define GC_ERROR_CHECK_SILENT 8
-#define GC_ERROR_CHECK_PRINT 9
-
-/* 12-May-2017 nm */
-/* DATE_BELOW_PROOF, if defined, causes generation and error-checking of
-   the date below the proof (which some day will be obsolete).  Comment
-   it out when this checking is no longer needed. */
-/*#define DATE_BELOW_PROOF*/
-
-/* 14-May-2017 nm */
-/* TODO: someday we should create structures to hold global vars, and
-   clear their string components in eraseSource() */
-extern vstring g_contributorName;
-#define DEFAULT_CONTRIBUTOR "?who?"
-
-extern vstring g_proofDiscouragedMarkup;
-extern vstring g_usageDiscouragedMarkup;
-extern flag g_globalDiscouragement; /* SET DISCOURAGEMENT */
-
-/* Allocation and deallocation in memory pool */
-void *poolFixedMalloc(long size /* bytes */);
-void *poolMalloc(long size /* bytes */);
-void poolFree(void *ptr);
-void addToUsedPool(void *ptr);
-/* Purges reset memory pool usage */
-void memFreePoolPurge(flag untilOK);
-/* Statistics */
-void getPoolStats(long *freeAlloc, long *usedAlloc, long *usedActual);
-
-/* Initial memory allocation */
-void initBigArrays(void);
-
-/* Find the number of free memory bytes */
-long getFreeSpace(long max);
-
-/* Fatal memory allocation error */
-void outOfMemory(vstring msg);
-
-/* Bug check error */
-void bug(int bugNum);
-
-
-/* Null nmbrString -- -1 flags the end of a nmbrString */
-struct nullNmbrStruct {
-    long poolLoc;
-    long allocSize;
-    long actualSize;
-    nmbrString nullElement; };
-extern struct nullNmbrStruct g_NmbrNull;
-#define NULL_NMBRSTRING &(g_NmbrNull.nullElement)
-
-/* Null pntrString -- NULL flags the end of a pntrString */
-struct nullPntrStruct {
-    long poolLoc;
-    long allocSize;
-    long actualSize;
-    pntrString nullElement; };
-extern struct nullPntrStruct g_PntrNull;
-#define NULL_PNTRSTRING &(g_PntrNull.nullElement)
-
-
-/* 26-Apr-2008 nm Added */
-/* This function returns a 1 if any entry in a comma-separated list
-   matches using the matches() function. */
-flag matchesList(vstring testString, vstring pattern, char wildCard,
-    char oneCharWildCard);
-
-/* This function returns a 1 if the first argument matches the pattern of
-   the second argument.  The second argument may have 0-or-more and
-   exactly-1 character match wildcards, typically '*' and '?'.*/
-/* 30-Jan-06 nm Added single-character-match argument */
-flag matches(vstring testString, vstring pattern, char wildCard,
-    char oneCharWildCard);
-
-
-
-/*******************************************************************/
-/*********** Number string functions *******************************/
-/*******************************************************************/
-
-/******* Special pupose routines for better
-      memory allocation (use with caution) *******/
-
-extern long g_nmbrTempAllocStackTop;   /* Top of stack for nmbrTempAlloc funct */
-extern long g_nmbrStartTempAllocStack; /* Where to start freeing temporary
-    allocation when nmbrLet() is called (normally 0, except for nested
-    nmbrString functions) */
-
-/* Make string have temporary allocation to be released by next nmbrLet() */
-/* Warning:  after nmbrMakeTempAlloc() is called, the nmbrString may NOT be
-   assigned again with nmbrLet() */
-void nmbrMakeTempAlloc(nmbrString *s);
-                                    /* Make string have temporary allocation to be
-                                    released by next nmbrLet() */
-
-/**************************************************/
-
-
-/* String assignment - MUST be used to assign vstrings */
-void nmbrLet(nmbrString **target,nmbrString *source);
-
-/* String concatenation - last argument MUST be NULL */
-nmbrString *nmbrCat(nmbrString *string1,...);
-
-/* Emulation of nmbrString functions similar to BASIC string functions */
-nmbrString *nmbrSeg(nmbrString *sin, long p1, long p2);
-nmbrString *nmbrMid(nmbrString *sin, long p, long l);
-nmbrString *nmbrLeft(nmbrString *sin, long n);
-nmbrString *nmbrRight(nmbrString *sin, long n);
-
-/* Allocate and return an "empty" string n "characters" long */
-nmbrString *nmbrSpace(long n);
-
-long nmbrLen(nmbrString *s);
-long nmbrAllocLen(nmbrString *s);
-void nmbrZapLen(nmbrString *s, long length);
-
-/* Search for string2 in string 1 starting at start_position */
-long nmbrInstr(long start, nmbrString *sin, nmbrString *s);
-
-/* Search for string2 in string 1 in reverse starting at start_position */
-/* (Reverse nmbrInstr) */
-long nmbrRevInstr(long start_position,nmbrString *string1,
-    nmbrString *string2);
-
-/* 1 if strings are equal, 0 otherwise */
-int nmbrEq(nmbrString *sout,nmbrString *sin);
-
-/* Converts mString to a vstring with one space between tokens */
-vstring nmbrCvtMToVString(nmbrString *s);
-
-/* Converts rString to a vstring with one space between tokens */
-/* 25-Jan-2016 nm Added explicitFormat, statemNum */
-vstring nmbrCvtRToVString(nmbrString *s,
-    flag explicitTargets,    /* 25-Jan-2016 */
-    long statemNum);        /* 25-Jan-2016 */
-
-/* Get step numbers in an rString - needed by cvtRToVString & elsewhere */
-nmbrString *nmbrGetProofStepNumbs(nmbrString *reason);
-
-/* Converts any nmbrString to an ASCII string of numbers corresponding
-   to the .tokenNum field -- used for debugging only. */
-vstring nmbrCvtAnyToVString(nmbrString *s);
-
-/* Extract variables from a math token string */
-nmbrString *nmbrExtractVars(nmbrString *m);
-
-/* Determine if an element is in a nmbrString; return position if it is */
-long nmbrElementIn(long start, nmbrString *g, long element);
-
-/* Add a single number to end of a nmbrString - faster than nmbrCat */
-nmbrString *nmbrAddElement(nmbrString *g, long element);
-
-/* Get the set union of two math token strings (presumably
-   variable lists) */
-nmbrString *nmbrUnion(nmbrString *m1,nmbrString *m2);
-
-/* Get the set intersection of two math token strings (presumably
-   variable lists) */
-nmbrString *nmbrIntersection(nmbrString *m1,nmbrString *m2);
-
-/* Get the set difference m1-m2 of two math token strings (presumably
-   variable lists) */
-nmbrString *nmbrSetMinus(nmbrString *m1,nmbrString *m2);
-
-
-
-/* This is a utility function that returns the length of a subproof that
-   ends at step */
-long nmbrGetSubproofLen(nmbrString *proof, long step);
-
-/* This function returns a "squished" proof, putting in {} references
-   to previous subproofs. */
-nmbrString *nmbrSquishProof(nmbrString *proof);
-
-/* This function unsquishes a "squished" proof, replacing {} references
-   to previous subproofs by the subproofs themselvs.  The returned nmbrString
-   must be deallocated by the caller. */
-nmbrString *nmbrUnsquishProof(nmbrString *proof);
-
-/* This function returns the indentation level vs. step number of a proof
-   string.  This information is used for formatting proof displays.  The
-   function calls itself recursively, but the first call should be with
-   startingLevel = 0.  The caller is responsible for deallocating the
-   result. */
-nmbrString *nmbrGetIndentation(nmbrString *proof,
-  long startingLevel);
-
-/* This function returns essential (1) or floating (0) vs. step number of a
-   proof string.  This information is used for formatting proof displays.  The
-   function calls itself recursively, but the first call should be with
-   startingLevel = 0.  The caller is responsible for deallocating the
-   result. */
-nmbrString *nmbrGetEssential(nmbrString *proof);
-
-/* This function returns the target hypothesis vs. step number of a proof
-   string.  This information is used for formatting proof displays.  The
-   function calls itself recursively.  The caller is responsible for
-   deallocating the result.  statemNum is the statement being proved. */
-nmbrString *nmbrGetTargetHyp(nmbrString *proof, long statemNum);
-
-/* Converts a proof string to a compressed-proof-format ASCII string.
-   Normally, the proof string would be compacted with squishProof first,
-   although it's not a requirement. */
-/* The statement number is needed because required hypotheses are
-   implicit in the compressed proof. */
-vstring compressProof(nmbrString *proof, long statemNum,
-    flag oldCompressionAlgorithm);
-
-/* Gets length of the ASCII form of a compressed proof */
-long compressedProofSize(nmbrString *proof, long statemNum);
-
-
-/*******************************************************************/
-/*********** Pointer string functions ******************************/
-/*******************************************************************/
-
-/******* Special pupose routines for better
-      memory allocation (use with caution) *******/
-
-extern long g_pntrTempAllocStackTop;   /* Top of stack for pntrTempAlloc funct */
-extern long g_pntrStartTempAllocStack; /* Where to start freeing temporary
-    allocation when pntrLet() is called (normally 0, except for nested
-    pntrString functions) */
-
-/* Make string have temporary allocation to be released by next pntrLet() */
-/* Warning:  after pntrMakeTempAlloc() is called, the pntrString may NOT be
-   assigned again with pntrLet() */
-void pntrMakeTempAlloc(pntrString *s);
-                                    /* Make string have temporary allocation to be
-                                    released by next pntrLet() */
-
-/**************************************************/
-
-
-/* String assignment - MUST be used to assign vstrings */
-void pntrLet(pntrString **target,pntrString *source);
-
-/* String concatenation - last argument MUST be NULL */
-pntrString *pntrCat(pntrString *string1,...);
-
-/* Emulation of pntrString functions similar to BASIC string functions */
-pntrString *pntrSeg(pntrString *sin, long p1, long p2);
-pntrString *pntrMid(pntrString *sin, long p, long length);
-pntrString *pntrLeft(pntrString *sin, long n);
-pntrString *pntrRight(pntrString *sin, long n);
-
-/* Allocate and return an "empty" string n "characters" long */
-pntrString *pntrSpace(long n);
-
-/* Allocate and return an "empty" string n "characters" long
-   initialized to nmbrStrings instead of vStrings */
-pntrString *pntrNSpace(long n);
-
-/* Allocate and return an "empty" string n "characters" long
-   initialized to pntrStrings instead of vStrings */
-pntrString *pntrPSpace(long n);
-
-long pntrLen(pntrString *s);
-long pntrAllocLen(pntrString *s);
-void pntrZapLen(pntrString *s, long length);
-
-/* Search for string2 in string 1 starting at start_position */
-long pntrInstr(long start, pntrString *sin, pntrString *s);
-
-/* Search for string2 in string 1 in reverse starting at start_position */
-/* (Reverse pntrInstr) */
-long pntrRevInstr(long start_position,pntrString *string1,
-    pntrString *string2);
-
-/* 1 if strings are equal, 0 otherwise */
-int pntrEq(pntrString *sout,pntrString *sin);
-
-/* Add a single null string element to a pntrString - faster than pntrCat */
-pntrString *pntrAddElement(pntrString *g);
-
-/* Add a single null pntrString element to a pntrString - faster than pntrCat */
-pntrString *pntrAddGElement(pntrString *g);
-
-/* Utility functions */
-
-/* 0/1 knapsack algorithm */
-long knapsack01(long items, long *size, long *worth, long maxSize,
-       char *itemIncluded /* output: 1 = item included, 0 = not included */);
-
-/* 2D matrix allocation and deallocation */
-long **alloc2DMatrix(size_t xsize, size_t ysize);
-void free2DMatrix(long **matrix, size_t xsize /*, size_t ysize*/);
-
-/* Returns the amount of indentation of a statement label.  Used to
-   determine how much to indent a saved proof. */
-long getSourceIndentation(long statemNum);
-
-/* Returns any comment (description) that occurs just before a statement */
-vstring getDescription(long statemNum);
-
-/* Returns the label section of a statement with all comments except the
-   last removed. */
-vstring getDescriptionAndLabel(long statemNum);
-
-/* Reconstruct the full header from the strings returned by
-   getSectionHeadings() */
-/*** deleted 12-Sep-2020
-vstring buildHeader(vstring header, vstring hdrComment, vstring decoration);
-***/
-
-/* Returns 1 if comment has an "is discouraged" markup tag */
-flag getMarkupFlag(long statemNum, char mode);
-
-/***** deleted 3-May-2017
-/@ Extract any contributors and dates from statement description.
-   If missing, the corresponding return strings are blank. @/
-flag getContrib(long stmtNum,
-    vstring @contributor, vstring @contribDate,
-    vstring @revisor, vstring @reviseDate,
-    vstring @shortener, vstring @shortenDate,
-    vstring @mostRecentDate,
-    flag printErrorsFlag,
-    flag mode /@ 0 == RESET = reset, 1 = normal @/);
-************/
-
-/* Extract any contributors and dates from statement description.
-   If missing, the corresponding return string is blank.
-   See GC_RESET etc. modes above.  Caller must deallocate returned
-   string. */
-vstring getContrib(long stmtNum, char mode);
-
-
-/*#ifdef DATE_BELOW_PROOF*/ /* 12-May-2017 nm */
-/* 14-May-2017 nm - re-enabled for purpose of converting old .mm's */
-/* Extract up to 2 dates after a statement's proof.  If no date is present,
-   date1 will be blank.  If no 2nd date is present, date2 will be blank. */
-void getProofDate(long stmtNum, vstring *date1, vstring *date2);
-/*#endif*/
-
-/* Get date, month, year fields from a dd-mmm-yyyy date string,
-   where dd may be 1 or 2 digits, mmm is 1st 3 letters of month,
-   and yyyy is 2 or 4 digits.  A 1 is returned if an error was detected. */
-flag parseDate(vstring dateStr, long *dd, long *mmm, long *yyyy);
-
-/* Build date from numeric fields.  mmm should be a number from 1 to 12. */
-void buildDate(long dd, long mmm, long yyyy, vstring *dateStr);
-
-/* Compare two dates in the form dd-mmm-yyyy.  -1 = date1 < date2,
-   0 = date1 = date2,  1 = date1 > date2.  There is no error checking. */
-flag compareDates(vstring date1, vstring date2);
-
-/* 17-Nov-2015 nm */
-extern vstring g_qsortKey;
-      /* Used by qsortStringCmp; pointer only, do not deallocate */
-/* Comparison function for qsort */
-int qsortStringCmp(const void *p1, const void *p2);
-
-/* 4-May-2017 Ari Ferrera */
-/* Call on exit to free memory */
-void freeData(void);
-
-#endif /* METAMATH_MMDATA_H_ */
diff --git a/mminou.h b/mminou.h
deleted file mode 100644
index fc119f4..0000000
--- a/mminou.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMINOU_H_
-#define METAMATH_MMINOU_H_
-
-#include <stdio.h>
-
-#include "mmvstr.h"
-#include "mmdata.h"
-
-extern int g_errorCount;     /* Total error count */
-
-/* Global variables used by print2() */
-extern flag g_logFileOpenFlag;
-extern FILE *g_logFilePtr;
-extern FILE *g_listFile_fp;
-/* Global variables used by print2() */
-extern flag g_outputToString;
-extern vstring g_printString;
-/* Global variables used by cmdInput() */
-#define MAX_COMMAND_FILE_NESTING 10
-extern long g_commandFileNestingLevel;
-extern FILE *g_commandFilePtr[MAX_COMMAND_FILE_NESTING + 1];
-extern vstring g_commandFileName[MAX_COMMAND_FILE_NESTING + 1];
-extern flag g_commandFileSilent[MAX_COMMAND_FILE_NESTING + 1];
-extern flag g_commandFileSilentFlag;
-                                    /* 23-Oct-2006 nm For SUBMIT ... /SILENT */
-
-extern FILE /* *inputDef_fp,*/ *g_input_fp /*,*g_output_fp*/;  /* File pointers */
-                             /* 31-Dec-2017 nm g_output_fp deleted */
-extern vstring /* inputDef_fn,*/ g_input_fn, g_output_fn;  /* File names */
-
-/* 19-Jun-2020 nm No longer needed since printBuffer is now dynamically
-   allocated. */
-/*****************************
-/@ PRINTBUFFERSIZE should be at least as long as the longest string we
-   expect (an unfortunate, dangerous limitation of C?) - although if >79
-   chars are output on a line bug #1505 warning will occur @/
-#define PRINTBUFFERSIZE 10001
-**********************************/
-
-/* Warning:  never call print2 with string longer than PRINTBUFFERSIZE - 1 */
-/* print2 returns 0 if the user has quit the printout. */
-flag print2(char* fmt,...);
-extern long g_screenHeight; /* Height of screen */ /* 18-Nov-05 nm Added */
-extern long g_screenWidth; /* Width of screen */
-#define MAX_LEN 79 /* Default width of screen */
-#define SCREEN_HEIGHT 23 /* Lines on screen, minus 1 to account for prompt */
-extern flag g_scrollMode; /* Flag for continuous or prompted scroll */
-extern flag g_quitPrint; /* Flag that user typed 'q' to last scrolling prompt */
-
-/* printLongLine automatically puts a newline \n in the output line. */
-void printLongLine(vstring line, vstring startNextLine, vstring breakMatch);
-vstring cmdInput(FILE *stream,vstring ask);
-vstring cmdInput1(vstring ask);
-
-enum severity {notice_,warning_,error_,fatal_};
-void errorMessage(vstring line, long lineNum, long column, long tokenLength,
-  vstring error, vstring fileName, long statementNum, flag warnFlag);
-
-/* Opens files with error message; opens output files with
-   backup of previous version.   Mode must be "r" or "w". */
-FILE *fSafeOpen(vstring fileName, vstring mode, flag noVersioningFlag);
-
-/* Renames a file with backup of previous version.  If non-zero
-   is returned, there was an error. */
-int fSafeRename(vstring oldFileName, vstring newFileName);
-
-/* Finds the name of the first file of the form filePrefix +
-   nnn + ".tmp" that does not exist.  THE CALLER MUST DEALLOCATE
-   THE RETURNED STRING. */
-vstring fGetTmpName(vstring filePrefix);
-
-/* This function returns a character string containing the entire contents of
-   an ASCII file, or Unicode file with only ASCII characters.   On some
-   systems it is faster than reading the file line by line.  THE CALLER
-   MUST DEALLOCATE THE RETURNED STRING.  If a NULL is returned, the file
-   could not be opened or had a non-ASCII Unicode character or some other
-   problem.   If verbose is 0, error and warning messages are suppressed. */
-/* 31-Dec-2017 nm Add charCount return argument */
-vstring readFileToString(vstring fileName, char verbose, long *charCount);
-
-/* 16-Aug-2016 nm */
-/* Returns total elapsed time in seconds since starting session (for the
-   lcc compiler) or the CPU time used (for the gcc compiler).  The
-   argument is assigned the time since the last call to this function. */
-double getRunTime(double *timeSinceLastCall);
-
-/* Call before exiting to free memory allocated by this module */
-void freeInOu(void); /* 4-May-2017 Ari Ferrera */
-
-#endif /* METAMATH_MMINOU_H_*/
diff --git a/mmmaci.c b/mmmaci.c
deleted file mode 100644
index 4fde04f..0000000
--- a/mmmaci.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2005  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#include <string.h>
-
-/***********************************/
-/*  Macintosh interface functions  */
-/***********************************/
-
-#ifdef THINK_C
-
-#include "mmmaci.h"
-
-#define kBaseResID 128
-#define kMoveToFront (WindowPtr)-1L
-
-#define kHorizontalPixel 30
-#define kVerticalPixel 50
-
-
-
-/* Macintosh tool box initialization */
-void ToolBoxInit(void)
-{
-  InitGraf(&thePort); /*??? Crashes console interface */
-  InitFonts();
-  InitWindows();
-  InitMenus();
-  TEInit();
-  InitDialogs(nil);
-  InitCursor();
-}
-
-/* Macintosh window initialization */
-void WindowInit(void)
-{
-  WindowPtr window;
-  window = GetNewWindow(kBaseResID, nil, kMoveToFront);
-  if (window == nil)
-  {
-    SysBeep(10); /* Couldn't load the WIND resource!!! */
-    ExitToShell();
-  }
-
-  ShowWindow(window);
-  SetPort(window);
-  /* MoveTo(kHorizontalPixel, kVerticalPixel); */
-  /* DrawString("\pHello, world!"); */
-}
-
-/* Draw the opening window */
-void DrawMyPicture(void)
-{
-  Rect pictureRect;
-  WindowPtr window;
-  PicHandle picture;
-
-  window = FrontWindow();
-  pictureRect = window->portRect;
-  picture = GetPicture(kBaseResID);
-
-  if (picture == nil) {
-    SysBeep(10); /* Couldn't load the PICT resource!!! */
-    ExitToShell();
-  }
-
-  CenterPict(picture, &pictureRect);
-  DrawPicture(picture, &pictureRect);
-}
-
-/* Center picture */
-void CenterPict(PicHandle picture, Rect *destRectPtr)
-{
-  Rect windRect, pictRect;
-  windRect = *destRectPtr;
-  pictRect = (**(picture)).picFrame;
-  OffsetRect(&pictRect, windRect.left - pictRect.left,
-      windRect.top - pictRect.top);
-  OffsetRect(&pictRect, (windRect.right - pictRect.right)/2,
-     (windRect.bottom - pictRect.bottom)/2);
-  *destRectPtr = pictRect;
-}
-
-#endif /* end ifdef THINK_C */
diff --git a/mmmaci.h b/mmmaci.h
deleted file mode 100644
index 5f6b28a..0000000
--- a/mmmaci.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2005  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/***********************************/
-/*  Macintosh interface functions  */
-/***********************************/
-
-#ifndef METAMATH_MMMACI_H_
-#define METAMATH_MMMACI_H_
-
-void ToolBoxInit(void);
-void WindowInit(void);
-void DrawMyPicture(void);
-
-void CenterPict(PicHandle picture, Rect *destRectPtr);
-
-#endif /* METAMATH_MMMACI_H_ */
diff --git a/mmpars.h b/mmpars.h
deleted file mode 100644
index 451253c..0000000
--- a/mmpars.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2018  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMPARS_H_
-#define METAMATH_MMPARS_H_
-
-#include "mmvstr.h"
-#include "mmdata.h"
-
-char *readRawSource(/*vstring inputFn,*/ /* 2-Feb-2018 nm Unused argument */
-    vstring inputBuf, long *size);
-void parseKeywords(void);
-void parseLabels(void);
-void parseMathDecl(void);
-void parseStatements(void);
-char parseProof(long statemNum);
-char parseCompressedProof(long statemNum);
-nmbrString *getProof(long statemNum, flag printFlag);
-
-void rawSourceError(char *startFile, char *ptr, long tokenLen,
-    /*long lineNum,*/                       /* 2-Feb-2018 nm */
-    /*vstring fileName,*/ vstring errMsg);  /* 2-Feb-2018 nm */
-void sourceError(char *ptr, long tokenLen, long stmtNum, vstring errMsg);
-void mathTokenError(long tokenNum /* 0 is 1st one */,
-    nmbrString *tokenList, long stmtNum, vstring errMsg);
-vstring shortDumpRPNStack(void);
-
-/* Label comparison for qsort */
-int labelSortCmp(const void *key1, const void *key2);
-
-/* Label comparison for bsearch */
-int labelSrchCmp(const void *key, const void *data);
-
-/* Math token comparison for qsort */
-int mathSortCmp(const void *key1, const void *key2);
-
-/* Math token label comparison for bsearch */
-int mathSrchCmp(const void *key, const void *data);
-
-/* Hypothesis and local label comparison for qsort */
-int hypAndLocSortCmp(const void *key1, const void *key2);
-
-/* Hypothesis and local label comparison for bsearch */
-int hypAndLocSrchCmp(const void *key, const void *data);
-
-/* This function returns the length of the white space starting at ptr.
-   Comments are considered white space.  ptr should point to the first character
-   of the white space.  If ptr does not point to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned. */
-long whiteSpaceLen(char *ptr);
-
-/* 31-Dec-2017 nm For .mm file splitting */
-/* Like whiteSpaceLen except comments are not whitespace */
-long rawWhiteSpaceLen(char *ptr);
-
-/* This function returns the length of the token (non-white-space) starting at
-   ptr.  Comments are considered white space.  ptr should point to the first
-   character of the token.  If ptr points to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned.  If ptr
-   points to a keyword, 0 is returned.  A keyword ends a token. */
-long tokenLen(char *ptr);
-
-/* Unlike tokenLen(), keywords are not treated as special.  In particular:
-   if ptr points to a keyword, 0 is NOT returned (instead, 2 is returned),
-   and a keyword does NOT end a token (which is a relic of days before
-   whitespace surrounding a token was part of the spec, but still serves
-   a useful purpose in token() for friendlier error detection). */
-long rawTokenLen(char *ptr);
-
-/* This function returns the length of the proof token starting at
-   ptr.  Comments are considered white space.  ptr should point to the first
-   character of the token.  If ptr points to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned.  If ptr
-   points to a keyword, 0 is returned.  A keyword ends a token.
-   ":" is considered a token. */
-long proofTokenLen(char *ptr);
-
-/* 9-Jan-2018 nm */
-/* Counts the number of \n between start for length chars.
-   If length = -1, then use end-of-string 0 to stop.
-   If length >= 0, then scan at most length chars, but stop
-       if end-of-string 0 is found. */
-long countLines(vstring start, long length);
-
-/* Array with sort keys for all possible labels, including the ones for
-   hypotheses (which may not always be active) */
-/* This array is used to see if any label is used anywhere, and is used
-   to make sure there are no conflicts when local labels inside of compact
-   proofs are generated. */
-extern long *g_allLabelKeyBase;
-extern long g_numAllLabelKeys;
-
-/* Working structure for parsing proofs */
-/* This structure should be deallocated by the ERASE command. */
-extern long g_wrkProofMaxSize; /* Maximum size so far - it may grow */
-struct sortHypAndLoc {  /* Used for sorting hypAndLocLabel field */
-  long labelTokenNum;
-  void *labelName;
-};
-struct wrkProof_struct {
-  long numTokens; /* Number of tokens in proof */
-  long numSteps; /* Number of steps in proof */
-  long numLocalLabels; /* Number of local labels */
-  long numHypAndLoc; /* Number of active hypotheses and local labels */
-  char *localLabelPoolPtr; /* Next free location in local label pool */
-  long RPNStackPtr; /* Offset of end of RPNStack */
-  long errorCount; /* Errors in proof - used to suppress too many error msgs */
-  flag errorSeverity; /* 0 = OK, 1 = unk step, 2 = error, 3 = severe error,
-                          4 = not a $p statement */
-
-  /* The following pointers will always be allocated with g_wrkProofMaxSize
-     entries.  If a function needs more than g_wrkProofMaxSize, it must
-     reallocate all of these and increase g_wrkProofMaxSize. */
-  nmbrString *tokenSrcPtrNmbr; /* Source parsed into tokens vs. token number
-                                    - token size */
-  pntrString *tokenSrcPtrPntr; /* Source parsed into tokens vs. token number
-                                    - token src ptrs */
-  nmbrString *stepSrcPtrNmbr; /* Pointer to label token in source file
-                                   vs. step number - label size */
-  pntrString *stepSrcPtrPntr; /* Pointer to label token in source file
-                                   vs. step number - label src ptrs */
-  flag *localLabelFlag; /* 1 means step has a local label declaration */
-  struct sortHypAndLoc *hypAndLocLabel;
-                        /* Sorted ptrs to hyp and local label names + token# */
-  char *localLabelPool; /* String pool to hold local labels */
-  nmbrString *proofString; /* The proof in RPN - statement # if > 0
-                             or -(step # + 1000) of local label decl if < -1 */
-  pntrString *mathStringPtrs; /* Ptr to math string vs. each step */
-                      /* (Allocated in verifyProof() as needed by nmbrLet()) */
-  nmbrString *RPNStack; /* Stack for RPN parsing */
-
-  /* For compressed proof parsing */
-  nmbrString *compressedPfLabelMap; /* Map from compressed label to actual */
-  long compressedPfNumLabels; /* Number of compressed labels */
-
-};
-extern struct wrkProof_struct g_WrkProof;
-
-/* Converts an ASCII string to a nmbrString of math symbols.  statemNum
-   provides the context for the parse (to get correct active symbols) */
-nmbrString *parseMathTokens(vstring userText, long statemNum);
-
-/* 12-Jun-2011 nm Added reformatFlag */
-vstring outputStatement(long stmt, /*flag cleanFlag,*/ flag reformatFlag);
-/* 12-Jun-2011 nm */
-/* Caller must deallocate return string */
-vstring rewrapComment(vstring comment);
-
-/* 10/10/02 */
-/* Lookup $a or $p label and return statement number.
-   Return -1 if not found. */
-long lookupLabel(vstring label);
-
-/* 31-Dec-2017 nm For file splitting */
-
-/* Get the next real $[...$] or virtual $( Begin $[... inclusion */
-void getNextInclusion(char *fileBuf, long startOffset, /* inputs */
-    /* outputs: */
-    long *cmdPos1, long *cmdPos2,
-    long *endPos1, long *endPos2,
-    char *cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
-                      'I' = "[$...",
-                      'S' = "$( Skip [$...",
-                      'E' = Start missing matched End
-                      'N' = no include found */
-    vstring *fileName /* name of included file */
-    );
-
-/* This function transfers the content of the statement[] array
-   to a linear buffer in preparation for creating the output file. */
-vstring writeSourceToBuffer(void);
-
-/* 31-Dec-2017 nm */
-/* This function creates split files containing $[ $] inclusions, from
-   a nonsplit source with $( Begin $[... etc. inclusions */
-/* Note that *fileBuf is assigned to the empty string upon return, to
-   conserve memory */
-void writeSplitSource(vstring *fileBuf, vstring fileName,
-    flag noVersioningFlag, flag noDeleteFlag);
-
-/* When "write source" does not have the "/split" qualifier, by default
-   (i.e. without "/no_delete") the included modules are "deleted" (renamed
-   to ~1) since their content will be in the main output file. */
-void deleteSplits(vstring *fileBuf, flag noVersioningFlag);
-
-/* 9-Jan-2018 nm */
-/* Get file name and line number given a pointer into the read buffer */
-/* The user must deallocate the returned string (file name) */
-/* The globals includeCall structure and includeCalls are used */
-vstring getFileAndLineNum(vstring buffPtr/*start of read buffer*/,
-    vstring currentPtr/*place at which to get file name and line no*/,
-    long *lineNum/*return argument*/);
-
-/* 9-Jan-2018 nm */
-/* statement[stmtNum].fileName and .lineNum are initialized to "" and 0.
-   To save CPU time, they aren't normally assigned until needed, but once
-   assigned they can be reused without looking them up again.  This function
-   will assign them if they haven't been assigned yet.  It just returns if
-   they have already been assigned. */
-/* The globals statement[] and sourcePtr are used */
-void assignStmtFileAndLineNum(long stmtNum);
-
-/* Initial read of source file */
-vstring readSourceAndIncludes(vstring inputFn, long *size);
-
-/* Recursively expand the source of an included file */
-char *readInclude(vstring fileBuf, long fileBufOffset,
-    /*vstring inclFileName,*/ vstring sourceFileName,
-    long *size, long parentLineNum, flag *errorFlag);
-
-#endif /* METAMATH_MMPARS_H_ */
diff --git a/mmpfas.h b/mmpfas.h
deleted file mode 100644
index b1b04d4..0000000
--- a/mmpfas.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMPFAS_H_
-#define METAMATH_MMPFAS_H_
-
-#include "mmvstr.h"
-#include "mmdata.h"
-
-extern long g_proveStatement; /* The statement to be proved */
-extern flag g_proofChangedFlag; /* Flag to push 'undo' stack */
-
-extern long g_userMaxProveFloat; /* Upper limit for proveFloating */
-
-extern long g_dummyVars; /* The number of dummy variables currently declared */
-extern long g_pipDummyVars; /* Number of dummy vars used by proof in progress */
-
-/* Structure for holding a proof in progress. */
-/* This structure should be deallocated after use. */
-struct pip_struct {
-  nmbrString *proof; /* The proof itself */
-  pntrString *target; /* Left hand side of = in display */
-  pntrString *source; /* Right hand side of = in display */
-  pntrString *user; /* User-specified math string assignments to step */
-};
-extern struct pip_struct g_ProofInProgress;
-
-/* Interactively select statement assignments that match */
-/* maxEssential is the maximum number of essential hypotheses that a
-   statement may have in order to be included in the matched list. */
-void interactiveMatch(long step, long maxEssential);
-
-/* Assign a statement to an unknown proof step */
-void assignStatement(long statemNum, long step);
-
-/* Find proof of formula by using the replaceStatement() algorithm i.e.
-   see if any statement matches current step AND each of its hypotheses
-   matches a proof in progress hypothesis or some step already in the proof.
-   If a proof is found, it is returned, otherwise an empty (length 0) proof is
-   returned. */
-/* The caller must deallocate the returned nmbrString. */
-nmbrString *proveByReplacement(long prfStmt,
-    long prfStep, /* 0 means step 1 */
-    flag noDistinct, /* 1 means don't try statements with $d's */
-    flag dummyVarFlag, /* 0 means no dummy vars are in prfStmt */
-    flag searchMethod, /* 1 means to try proveFloating on $e's also */
-    long improveDepth,
-    flag overrideFlag, /* 1 means to override usage locks */ /* 3-May-2016 nm */
-    flag mathboxFlag /* 1 means allow mathboxes */ /* 5-Aug-2020 nm */
-    );
-
-nmbrString *replaceStatement(long replStatemNum,
-    long prfStep,
-    long provStmtNum,
-    flag subProofFlag, /* If 1, then scan only subproof at prfStep to look for
-   matches, instead of whole proof, for faster speed (used by MINIMIZE_WITH) */
-    flag noDistinct, /* 1 means don't try statements with $d's */
-    flag searchMethod, /* 1 means to try proveFloating on $e's also */
-    long improveDepth,
-    flag overrideFlag, /* 1 means to override usage locks */ /* 3-May-2016 nm */
-    flag mathboxFlag /* 1 means allow mathboxes */ /* 5-Aug-2020 nm */
-    );
-
-/* 22-Aug-2012 nm Added this function */
-/* This function identifies all steps in the proof in progress that (1) are
-   independent of step refStep, (2) have no dummy variables, (3) are
-   not $f's or $e's, and (4) have subproofs that are complete
-   (no unassigned steps).  A "Y" is returned for each such step,
-   and "N" is returned for all other steps.  The "Y" steps can be used
-   for scanning for useful subproofs outside of the subProof of refStep.
-   Note: The caller must deallocate the returned vstring. */
-vstring getIndepKnownSteps(long proofStmt, long refStep);
-
-/* 22-Aug-2012 nm Added this function */
-/* This function classifies each proof step in g_ProofInProgress.proof
-   as known or unknown ('K' or 'U' in the returned string) depending
-   on whether the step has a completely known subproof.
-   Note: The caller must deallocate the returned vstring. */
-vstring getKnownSubProofs(void);
-
-/* Add a subproof in place of an unknown step to g_ProofInProgress.  The
-   .target, .source, and .user fields are initialized to empty (except
-   .target of the deleted unknown step is retained). */
-void addSubProof(nmbrString *subProof, long step);
-
-/* 11-Sep-2016 nm */
-/* This function eliminates any occurrences of statement sourceStmtNum in the
-   targetProof by substituting it with the proof of sourceStmtNum.  An empty
-   nmbrString is returned if there was an error. */
-/* Normally, targetProof is the global g_ProofInProgress.proof.  However,
-   we make it an argument in case in the future we'd like to do this
-   outside of the proof assistant. */
-nmbrString *expandProof(nmbrString *targetProof,
-    long sourceStmtNum /*, long targetStmtNum*/);
-
-/* Delete a subproof starting (in reverse from) step.  The step is replaced
-   with an unknown step, and its .target field is retained. */
-void deleteSubProof(long step);
-
-/* Check to see if a statement will match the g_ProofInProgress.target (or .user)
-   of an unknown step.  Returns 1 if match, 0 if not, 2 if unification
-   timed out. */
-char checkStmtMatch(long statemNum, long step);
-
-/* Check to see if a (user-specified) math string will match the
-   g_ProofInProgress.target (or .user) of an step.  Returns 1 if match, 0 if
-   not, 2 if unification timed out. */
-char checkMStringMatch(nmbrString *mString, long step);
-
-/* Find proof of formula or simple theorem (no new vars in $e's) */
-/* maxEDepth is the maximum depth at which statements with $e hypotheses are
-   considered.  A value of 0 means none are considered. */
-nmbrString *proveFloating(nmbrString *mString, long statemNum, long maxEDepth,
-    long step, flag noDistinct,
-    /* 3-May-2016 nm */
-    flag overrideFlag, /* 0 means respect usage locks
-                         1 means to override usage locks
-                         2 means override silently */
-    flag mathboxFlag /* 1 means allow mathboxes */ /* 5-Aug-2020 nm */
-);
-
-/* 22-Aug-2012 nm Added this function */
-/* This function does quick check for some common conditions that prevent
-   a trial statement (scheme) from being unified with a given instance.
-   Return value 0 means it can't be unified, 1 means it might be unifiable. */
-char quickMatchFilter(long trialStmt, nmbrString *mString,
-    long dummyVarFlag /* 0 if no dummy vars in mString */);
-
-/* Shorten proof by using specified statement. */
-void minimizeProof(long repStatemNum, long prvStatemNum, flag
-    allowGrowthFlag);
-
-/* Initialize g_ProofInProgress.source of the step, and .target of all
-   hypotheses, to schemes using new dummy variables. */
-void initStep(long step);
-
-/* Look for completely known subproofs in g_ProofInProgress.proof and
-   assign g_ProofInProgress.target and .source.  Calls assignKnownSteps(). */
-void assignKnownSubProofs(void);
-
-/* This function assigns math strings to all steps (g_ProofInProgress.target and
-   .source fields) in a subproof with all known steps. */
-void assignKnownSteps(long startStep, long sbProofLen);
-
-/* Interactive unify a step.  Calls interactiveUnify(). */
-/* If messageFlag is 1, a message will be issued if the
-   step is already unified.   If messageFlag is 0, show the step #
-   being unified.  If messageFlag is 2, don't print step #, and do nothing
-   if step is already unified. */
-void interactiveUnifyStep(long step, char messageFlag);
-
-/* Interactively select one of several possible unifications */
-/* Returns:  0 = no unification possible
-             1 = unification was selected; held in stateVector
-             2 = unification timed out
-             3 = no unification was selected */
-char interactiveUnify(nmbrString *schemeA, nmbrString *schemeB,
-    pntrString **stateVector);
-
-/* Automatically unify steps with unique unification */
-void autoUnify(flag congrats);
-
-/* Make stateVector substitutions in all steps.  The stateVector must
-   contain the result of a valid unification. */
-void makeSubstAll(pntrString *stateVector);
-
-/* Replace a dummy variable with a user-specified math string */
-void replaceDummyVar(long dummyVar, nmbrString *mString);
-
-/* Get subproof length of a proof, starting at endStep and going backwards */
-long subproofLen(nmbrString *proof, long endStep);
-
-/* 25-Aug-2012 nm Added this function */
-/* If testStep has no dummy variables, return 0;
-   if testStep has isolated dummy variables (that don't affect rest of
-   proof), return 1;
-   if testStep has dummy variables used elsewhere in proof, return 2 */
-char checkDummyVarIsolation(long testStep); /* 0=1st step, 1=2nd, etc. */
-
-/* 25-Aug-2012 nm Added this function */
-/* Given a starting step, find its parent (the step it is a hypothesis of) */
-/* If the starting step is the last proof step, just return it */
-long getParentStep(long startStep); /* 0=1st step, 1=2nd, etc. */
-
-/* Adds a dummy variable to end of mathToken array */
-/* (Note:  it now grows forever, but purging it might worsen fragmentation) */
-void declareDummyVars(long numNewVars);
-
-/* Copies the Proof Assistant proof state */
-void copyProofStruct(struct pip_struct *outProofStruct,
-    struct pip_struct inProofStruct);
-
-/* Initiailizes the Proof Assistant proof state */
-void initProofStruct(struct pip_struct *proofStruct, nmbrString *proof,
-    long proveStatement);
-
-/* Clears the Proof Assistant proof state */
-void deallocProofStruct(struct pip_struct *proofStruct);
-
-/* Actions for processUndoStack() */
-#define PUS_INIT 1
-#define PUS_PUSH 2
-#define PUS_UNDO 3
-#define PUS_REDO 4
-#define PUS_NEW_SIZE 5
-#define PUS_GET_SIZE 6
-#define PUS_GET_STATUS 7
-/* Handle the PUSH, UNDO, and REDO commands */
-long processUndoStack(struct pip_struct *proofStruct,
-    char action,
-    vstring info, /* Info to print upon UNDO or REDO */
-    long newStackSize); /* Used only by NEW_SIZE */
-
-#endif /* METAMATH_MMPFAS_H_ */
diff --git a/mmutil.c b/mmutil.c
deleted file mode 100644
index 3501671..0000000
--- a/mmutil.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2005  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/******************************************************************************
- *
- *  This is a collection of useful functions
- *
- ******************************************************************************/
-
-/* (This file is currently empty.) */
-/* The include makes file non-empty for ANSI compliance. */
-#include <stdio.h>
diff --git a/mmutil.h b/mmutil.h
deleted file mode 100644
index 8a6c05a..0000000
--- a/mmutil.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2005  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* (This file is currently empty.) */
diff --git a/mmvstr.h b/mmvstr.h
deleted file mode 100644
index e2ef7e4..0000000
--- a/mmvstr.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2011  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/*
-mmvstr.h - VMS-BASIC variable length string library routines header
-This is an emulation of the string functions available in VMS BASIC.
-*/
-
-/******************************************************************************
-
-Variable-length string handler
-------------------------------
-
-     This collection of string-handling functions emulate most of the
-string functions of VMS BASIC.  The objects manipulated by these
-functions are strings of a special type called 'vstring' which have no
-pre-defined upper length limit but are dynamically allocated and
-deallocated as needed.  To use the vstring functions within a program,
-all vstrings must be initially set to the null string when declared or
-before first used, for example:
-
-        vstring string1 = "";
-        vstring stringArray[] = {"", "", ""};
-
-        vstring bigArray[100][10]; /- Must be initialized before using -/
-        int i, j;
-        for (i = 0; i < 100; i++)
-          for (j = 0; j < 10; j++)
-            bigArray[i][j] = ""; /- Initialize -/
-
-
-     After initialization, vstrings should be assigned with the 'let(&'
-function only; for example the statements
-
-        let(&string1, "abc");
-        let(&string1, string2);
-        let(&string1, left(string2, 3));
-
-all assign the second argument to 'string1'.  The 'let(&' function must
-_not_ be used to initialize a vstring the first time.
-
-     Any local vstrings in a function must be deallocated before returning
-from the function, otherwise there will be memory leakage and eventual
-memory overflow.  To deallocate, assign the vstring to "" with 'let(&':
-
-        void abc(void) {
-          vstring xyz = "";
-          ...
-          let(&xyz, "");
-        }
-
-     The 'cat' function emulates the '+' concatenation operator in BASIC.
-It has a variable number of arguments, and the last argument should always
-be NULL.  For example,
-
-        let(&string1,cat("abc","def",NULL));
-
-assigns "abcdef" to 'string1'.  Warning: 0 will work instead of NULL on the
-VAX but not on the Macintosh, so always use NULL.
-
-     All other functions are generally used exactly like their BASIC
-equivalents.  For example, the BASIC statement
-
-        let string1$=left$("def",len(right$("xxx",2)))+"ghi"+string2$
-
-is emulated in c as
-
-        let(&string1,cat(left("def",len(right("xxx",2))),"ghi",string2,NULL));
-
-Note that ANSII c does not allow "$" as part of an identifier
-name, so the names in c have had the "$" suffix removed.
-
-     The string arguments of the vstring functions may be either standard c
-strings or vstrings (except that the first argument of the 'let(&' function
-must be a vstring).  The standard c string functions may use vstrings or
-vstring functions as their string arguments, as long as the vstring variable
-itself (which is a char * pointer) is not modified and no attempt is made to
-increase the length of a vstring.  Caution must be excercised when
-assigning standard c string pointers to vstrings or the results of
-vstring functions, as the memory space may be deallocated when the
-'let(&' function is next executed.  For example,
-
-        char *stdstr; /- A standard c string pointer -/
-         ...
-        stdstr=left("abc",2);
-
-will assign "ab" to 'stdstr', but this assignment will be lost when the
-next 'let(&' function is executed.  To be safe, use 'strcpy':
-
-        char stdstr1[80]; /- A fixed length standard c string -/
-         ...
-        strcpy(stdstr1,left("abc",2));
-
-Here, of course, the user must ensure that the string copied to 'stdstr1'
-does not exceed 79 characters in length.
-
-     The vstring functions allocate temporary memory whenever they are called.
-This temporary memory is deallocated whenever a 'let(&' assignment is
-made.  The user should be aware of this when using vstring functions
-outside of 'let(&' assignments; for example
-
-        for (i=0; i<10000; i++)
-          print2("%s\n",left(string1,70));
-
-will allocate another 70 bytes or so of memory each pass through the loop.
-If necessary, dummy 'let(&' assignments can be made periodically to clear
-this temporary memory:
-
-        for (i=0; i<10000; i++)
-          {
-          print2("%s\n",left(string1,70));
-          let(&dummy,"");
-          }
-
-It should be noted that the 'linput' function assigns its target string
-with 'let(&' and thus has the same effect as 'let(&'.
-
-******************************************************************************/
-
-
-#ifndef METAMATH_MMVSTR_H_
-#define METAMATH_MMVSTR_H_
-
-typedef char* vstring;
-#define vstringdef(x) vstring x = ""
-
-/* Emulation of BASIC string assignment */
-/* 'let' MUST be used to assign vstrings, e.g. 'let(&abc, "Hello"); */
-/* Empty string deallocates memory, e.g. 'let(&abc, ""); */
-void let(vstring *target, vstring source);
-
-/* Emulation of BASIC string concatenation - last argument MUST be NULL */
-/* vstring cat(vstring string1, ..., stringN, NULL); */
-/* e.g. 'let(&abc, cat("Hello", " ", left("worldx", 5), "!", NULL);' */
-vstring cat(vstring string1,...);
-
-/* Emulation of BASIC linput (line input) statement; returns NULL if EOF */
-/* Note that linput assigns target string with let(&target,...) */
-  /*
-    BASIC:  linput "what";a$
-    c:      linput(NULL,"what?",&a);
-
-    BASIC:  linput #1,a$                        (error trap on EOF)
-    c:      if (!linput(file1,NULL,&a)) break;  (break on EOF)
-
-  */
-/* returns whether a (possibly empty) line was successfully read */
-int linput(FILE *stream,const char* ask,vstring *target);
-
-/* Emulation of BASIC string functions */
-/* Indices are 1-based */
-vstring seg(vstring sin, long p1, long p2);
-vstring mid(vstring sin, long p, long l);
-vstring left(vstring sin, long n);
-vstring right(vstring sin, long n);
-vstring edit(vstring sin, long control);
-vstring space(long n);
-vstring string(long n, char c);
-vstring chr(long n);
-vstring xlate(vstring sin, vstring control);
-vstring date(void);
-vstring time_(void);
-vstring num(double x);
-vstring num1(double x);
-vstring str(double x);
-long len(vstring s);
-long instr(long start, vstring sin, vstring s);
-long rinstr(vstring string1, vstring string2);
-long ascii_(vstring c);
-double val(vstring s);
-
-/* Emulation of Progress 4GL string functions, added 11/25/98 */
-vstring entry(long element, vstring list);
-long lookup(vstring expression, vstring list);
-long numEntries(vstring list);
-long entryPosition(long element, vstring list);
-
-/* Routines may/may not be written (lowest priority):
-vstring place$(vstring sout);
-vstring prod$(vstring sout);
-vstring quo$(vstring sout);
-*/
-
-/******* Special purpose routines for better
-      memory allocation (use with caution) *******/
-
-extern long g_tempAllocStackTop;   /* Top of stack for tempAlloc functon */
-extern long g_startTempAllocStack; /* Where to start freeing temporary allocation
-    when let() is called (normally 0, except for nested vstring functions) */
-
-/* Make string have temporary allocation to be released by next let() */
-/* Warning:  after makeTempAlloc() is called, the vstring may NOT be
-   assigned again with let() */
-void makeTempAlloc(vstring s);    /* Make string have temporary allocation to be
-                                    released by next let() */
-#endif /* METAMATH_MMVSTR_H_ */
diff --git a/mmwtex.h b/mmwtex.h
deleted file mode 100644
index 80568b6..0000000
--- a/mmwtex.h
+++ /dev/null
@@ -1,244 +0,0 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMWTEX_H_
-#define METAMATH_MMWTEX_H_
-
-#include "mmvstr.h"
-#include "mmdata.h"
-
-/* Colors for HTML pages. */
-#define GREEN_TITLE_COLOR "\"#006633\""
-#define MINT_BACKGROUND_COLOR "\"#EEFFFA\""
-#define PINK_NUMBER_COLOR "\"#FA8072\""      /* =salmon; was FF6666 */
-#define PURPLISH_BIBLIO_COLOR "\"#FAEEFF\""
-
-
-/* 29-Jul-2008 nm Sandbox stuff */
-#define SANDBOX_COLOR "\"#FFFFD9\""
-
-/* TeX flags */
-/* 14-Sep-2010 nm Removed simpleTexFlag; added g_oldTexFlag */
-extern flag g_oldTexFlag; /* Use macros in output; obsolete; take out someday */
-
-/* HTML flags */
-extern flag g_htmlFlag;  /* HTML flag: 0 = TeX, 1 = HTML */
-extern flag g_altHtmlFlag;  /* Use "althtmldef" instead of "htmldef".  This is
-    intended to allow the generation of pages with the old Symbol font
-    instead of the individual GIF files. */
-extern flag g_briefHtmlFlag;  /* Output statement only, for statement display
-                in other HTML pages, such as the Proof Explorer home page */
-extern long g_extHtmlStmt; /* At this statement and above, use the exthtmlxxx
-    variables for title, links, etc.  This was put in to allow proper
-    generation of the Hilbert Space Explorer extension to the set.mm
-    database. */
-extern vstring g_extHtmlTitle; /* Title of extended section if any; set by
-    by exthtmltitle command in special $t comment of database source */
-extern vstring g_htmlVarColor; /* Set by htmlvarcolor commands */
-/* Added 26-Aug-2017 nm for use by mmcmds.c */
-extern vstring g_htmlHome; /* Set by htmlhome command */
-/* Added 10/13/02 for use in metamath.c bibliography cross-reference */
-extern vstring g_htmlBibliography; /* Optional; set by htmlbibliography command */
-extern vstring g_extHtmlBibliography; /* Optional; set by exthtmlbibliography
-                                       command */
-extern vstring g_htmlCSS; /* Set by htmlcss commands */  /* 14-Jan-2016 nm */
-/* Added 14-Jan-2016 */
-extern vstring g_htmlFont; /* Optional; set by g_htmlFont command */
-
-void eraseTexDefs(void); /* Undo readTexDefs() */
-
-/* TeX/HTML/ALT_HTML word-processor-specific routines */
-/* Returns 2 if there were severe parsing errors, 1 if there were warnings but
-   no errors, 0 if no errors or warnings */
-flag readTexDefs(
-  flag errorsOnly,  /* 1 = supprees non-error messages */
-  flag noGifCheck   /* 1 = don't check for missing GIFs */);
-
-extern flag g_texDefsRead;
-struct texDef_struct {  /* 27-Oct-2012 nm Made global for "erase" */
-  vstring tokenName; /* ASCII token */
-  vstring texEquiv; /* Converted to TeX */
-};
-extern struct texDef_struct *g_TexDefs; /* 27-Oct-2012 nm Now glob for "erase" */
-
-
-long texDefWhiteSpaceLen(char *ptr);
-long texDefTokenLen(char *ptr);
-/* Token comparison for qsort */
-int texSortCmp(const void *key1, const void *key2);
-/* Token comparison for bsearch */
-int texSrchCmp(const void *key, const void *data);
-/* Convert ascii to a string of \tt tex */
-/* (The caller must surround it by {\tt }) */
-vstring asciiToTt(vstring s);
-vstring tokenToTex(vstring mtoken, long statemNum);
-/* Converts a comment section in math mode to TeX.  Each math token
-   MUST be separated by white space.   TeX "$" does not surround the output. */
-vstring asciiMathToTex(vstring mathComment, long statemNum);
-/* Gets the next section of a comment that is in the current mode (text,
-   label, or math).  If 1st char. is not "$", text mode is assumed.
-   mode = 0 means end of comment reached.  srcptr is left at 1st char.
-   of start of next comment section. */
-vstring getCommentModeSection(vstring *srcptr, char *mode);
-void printTexHeader(flag texHeaderFlag);
-
-/* Prints an embedded comment in TeX.  The commentPtr must point to the first
-   character after the "$(" in the comment.  The printout ends when the first
-   "$)" or null character is encountered.   commentPtr must not be a temporary
-   allocation.  htmlCenterFlag, if 1, means to center the HTML and add a
-   "Description:" prefix.*/
-/* void printTexComment(vstring commentPtr, char htmlCenterFlag); */
-/* 17-Nov-2015 nm Added 3rd & 4th arguments; returns 1 if error/warning */
-flag printTexComment(vstring commentPtr,    /* Sends result to g_texFilePtr */
-    flag htmlCenterFlag, /* 1 = htmlCenterFlag */
-    long actionBits, /* see indicators below */
-    flag noFileCheck /* 1 = noFileCheck */);
-/* Indicators for actionBits */
-#define ERRORS_ONLY 1
-#define PROCESS_SYMBOLS 2
-#define PROCESS_LABELS 4
-#define ADD_COLORED_LABEL_NUMBER 8
-#define PROCESS_BIBREFS 16
-#define PROCESS_UNDERSCORES 32
-/* CONVERT_TO_HTML means '<' to '&lt;'; unless <HTML> in comment (and strip it) */
-#define CONVERT_TO_HTML 64
-/* METAMATH_COMMENT means $) (as well as end-of-string) terminates string. */
-#define METAMATH_COMMENT 128
-/* PROCESS_ALL is for convenience */
-#define PROCESS_EVERYTHING PROCESS_SYMBOLS + PROCESS_LABELS \
-     + ADD_COLORED_LABEL_NUMBER + PROCESS_BIBREFS \
-     + PROCESS_UNDERSCORES + CONVERT_TO_HTML + METAMATH_COMMENT
-
-void printTexLongMath(nmbrString *proofStep, vstring startPrefix,
-    vstring contPrefix, long hypStmt, long indentationLevel);
-void printTexTrailer(flag texHeaderFlag);
-
-/* Added 4-Dec-03
-   Function implementing WRITE THEOREM_LIST / THEOREMS_PER_PAGE nn */
-void writeTheoremList(long theoremsPerPage, flag showLemmas,
-    flag noVersioning);
-
-#define HUGE_DECORATION "####"
-#define BIG_DECORATION "#*#*"
-#define SMALL_DECORATION "=-=-"
-#define TINY_DECORATION "-.-."
-
-/* 2-Aug-2009 nm - broke this function out from writeTheoremList() */
-/* 20-Jun-2014 nm - added hugeHdrAddr */
-/* 21-Aug-2017 nm - added tinyHdrAddr */
-/* 6-Aug-2019 nm - changed return type from void to flag (=char) */
-/* 24-Aug-2020 nm - added fineResolution */
-/* 12-Sep-2020 nm - added fullComment */
-flag getSectionHeadings(long stmt, vstring *hugeHdrTitle,
-    vstring *bigHdrTitle,
-    vstring *smallHdrTitle,
-    vstring *tinyHdrTitle,
-    /* Added 8-May-2015 nm */
-    vstring *hugeHdrComment,
-    vstring *bigHdrComment,
-    vstring *smallHdrComment,
-    vstring *tinyHdrComment,
-    /* Added 24-Aug-2020 nm */
-    flag fineResolution, /* 0 = consider just successive $a/$p, 1 = all stmts */
-    flag fullComment /* 1 = put $( + header + comment + $) into xxxHdrTitle */
-    );
-
-/****** 15-Aug-2020 nm Obsolete
-/@ TeX symbol dictionary @/
-extern FILE @g_tex_dict_fp;     /@ File pointers @/
-extern vstring g_tex_dict_fn;   /@ File names @/
-******/
-
-/* TeX normal output */
-extern flag g_texFileOpenFlag;
-extern FILE *g_texFilePtr;
-
-/* Pink statement number for HTML pages */
-/* 10/10/02 (This is no longer used?) */
-/*
-long pinkNumber(long statemNum);
-*/
-
-/* Pink statement number HTML code for HTML pages - added 10/10/02 */
-/* Warning: caller must deallocate returned string */
-vstring pinkHTML(long statemNum);
-
-/* Pink statement number range HTML code for HTML pages, separated by a
-   "-" - added 24-Aug-04 */
-/* Warning: caller must deallocate returned string */
-vstring pinkRangeHTML(long statemNum1, long statemNum2);
-
-#define PINK_NBSP "&nbsp;" /* Either "" or "&nbsp;" depending on taste, it is
-                  the separator between a statement href and its pink number */
-
-/* 30-Jan-04 nm Comment out the following line to go back to the pink-only
-   color for the little statement numbers on the HTML pages */
-#define RAINBOW_OPTION /* "Rainbow" instead of pink color for little numbers */
-
-#ifdef RAINBOW_OPTION
-/* This function converts a "spectrum" color (1 to maxColor) to an
-   RBG value in hex notation for HTML.  The caller must deallocate the
-   returned vstring.  color = 1 (red) to maxColor (violet). */
-/* ndm 10-Jan-04 */
-vstring spectrumToRGB(long color, long maxColor);
-#endif
-
-#define INDENT_HTML_PROOFS /* nm 3-Feb-04 - indentation experiment */
-
-/* Added 20-Sep-03 (broken out of printTexLongMath() for better
-   modularization) */
-/* Returns the HTML code for GIFs (!g_altHtmlFlag) or Unicode (g_altHtmlFlag),
-   or LaTeX when !g_htmlFlag, for the math string (hypothesis or conclusion) that
-   is passed in. */
-/* Warning: The caller must deallocate the returned vstring. */
-vstring getTexLongMath(nmbrString *mathString, long statemNum);
-
-/* Added 18-Sep-03 (transferred from metamath.c) */
-/* Returns the TeX, or HTML code for GIFs (!g_altHtmlFlag) or Unicode
-   (g_altHtmlFlag), for a statement's hypotheses and assertion in the form
-   hyp & ... & hyp => assertion */
-/* Warning: The caller must deallocate the returned vstring. */
-/* 14-Sep-2010 nm Changed name from getHTMLHypAndAssertion() */
-vstring getTexOrHtmlHypAndAssertion(long statemNum);
-
-/* Added 17-Nov-2015 (broken out of metamath.c for better modularization) */
-/* For WRITE BIBLIOGRAPHY command and error checking by VERIFY MARKUP */
-/* Returns 0 if OK, 1 if error or warning found */
-flag writeBibliography(vstring bibFile,
-    vstring labelMatch, /* Normally "*" except by verifyMarkup() */
-    flag errorsOnly,  /* 1 = no output, just warning msgs if any */
-    flag noFileCheck); /* 1 = ignore missing external files (gifs, bib, etc.) */
-
-/* 5-Aug-2020 nm */
-/* Globals to hold mathbox information.  They should be re-initialized
-   by the ERASE command (eraseSource()).  g_mathboxStmt = 0 indicates
-   it and the other variables haven't been initialized. */
-extern long g_mathboxStmt; /* stmt# of "mathbox" label; statements+1 if none */
-extern long g_mathboxes; /* # of mathboxes */
-/* The following 3 "strings" are 0-based e.g. g_mathboxStart[0] is for
-   mathbox #1 */
-extern nmbrString *g_mathboxStart; /* Start stmt vs. mathbox # */
-extern nmbrString *g_mathboxEnd; /* End stmt vs. mathbox # */
-extern pntrString *g_mathboxUser; /* User name vs. mathbox # */
-
-/* 5-Aug-2020 nm */
-/* Returns 1 if statements are in different mathboxes */
-flag inDiffMathboxes(long stmt1, long stmt2);
-/* Returns the user of the mathbox that a statement is in, or ""
-   if the statement is not in a mathbox. */
-/* Caller should NOT deallocate returned string (it points directly to
-   g_mathboxUser[] entry); use directly in print2() messages */
-vstring getMathboxUser(long stmt);
-/* Returns the mathbox number (starting at 1) that stmt is in, or 0 if not
-   in a mathbox */
-long getMathboxNum(long stmt);
-/* Populates mathbox information */
-void assignMathboxInfo(void);
-/* Creates lists of mathbox starts and user names */
-long getMathboxLoc(nmbrString **mathboxStart, nmbrString **mathboxEnd,
-    pntrString **mathboxUser);
-
-#endif /* METAMATH_MMWTEX_H_ */
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..e04ec24
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,44 @@
+# This file is input to automake, that expands this high-level make description
+# to a Makefile containing the usual goals, and being capable of handling
+# portability issues.
+
+# the executable created by make
+bin_PROGRAMS = metamath
+
+# the following header files are used during build, but need not be installed.
+# There is nothing exported to external software.
+noinst_HEADERS = \
+	mmcmdl.h \
+	mmcmds.h \
+	mmdata.h \
+	mmhlpa.h \
+	mmhlpb.h \
+	mminou.h \
+	mmpars.h \
+	mmpfas.h \
+	mmunif.h \
+	mmveri.h \
+	mmvstr.h \
+	mmword.h \
+	mmwtex.h
+
+# files used to build the executable.
+metamath_SOURCES = \
+	metamath.c \
+	mmcmdl.c \
+	mmcmds.c \
+	mmdata.c \
+	mmhlpa.c \
+	mmhlpb.c \
+	mminou.c \
+	mmpars.c \
+	mmpfas.c \
+	mmunif.c \
+	mmveri.c \
+	mmvstr.c \
+	mmword.c \
+	mmwtex.c \
+	$(noinst_HEADERS)
+
+# man pages for the executable´
+dist_man_MANS = metamath.1
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..875a073
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,752 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# This file is input to automake, that expands this high-level make description
+# to a Makefile containing the usual goals, and being capable of handling
+# portability issues.
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = metamath$(EXEEXT)
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+	$(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"
+PROGRAMS = $(bin_PROGRAMS)
+am__objects_1 =
+am_metamath_OBJECTS = metamath.$(OBJEXT) mmcmdl.$(OBJEXT) \
+	mmcmds.$(OBJEXT) mmdata.$(OBJEXT) mmhlpa.$(OBJEXT) \
+	mmhlpb.$(OBJEXT) mminou.$(OBJEXT) mmpars.$(OBJEXT) \
+	mmpfas.$(OBJEXT) mmunif.$(OBJEXT) mmveri.$(OBJEXT) \
+	mmvstr.$(OBJEXT) mmword.$(OBJEXT) mmwtex.$(OBJEXT) \
+	$(am__objects_1)
+metamath_OBJECTS = $(am_metamath_OBJECTS)
+metamath_LDADD = $(LDADD)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/metamath.Po ./$(DEPDIR)/mmcmdl.Po \
+	./$(DEPDIR)/mmcmds.Po ./$(DEPDIR)/mmdata.Po \
+	./$(DEPDIR)/mmhlpa.Po ./$(DEPDIR)/mmhlpb.Po \
+	./$(DEPDIR)/mminou.Po ./$(DEPDIR)/mmpars.Po \
+	./$(DEPDIR)/mmpfas.Po ./$(DEPDIR)/mmunif.Po \
+	./$(DEPDIR)/mmveri.Po ./$(DEPDIR)/mmvstr.Po \
+	./$(DEPDIR)/mmword.Po ./$(DEPDIR)/mmwtex.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(metamath_SOURCES)
+DIST_SOURCES = $(metamath_SOURCES)
+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 = $(dist_man_MANS)
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in \
+	$(top_srcdir)/config/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+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@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# the following header files are used during build, but need not be installed.
+# There is nothing exported to external software.
+noinst_HEADERS = \
+	mmcmdl.h \
+	mmcmds.h \
+	mmdata.h \
+	mmhlpa.h \
+	mmhlpb.h \
+	mminou.h \
+	mmpars.h \
+	mmpfas.h \
+	mmunif.h \
+	mmveri.h \
+	mmvstr.h \
+	mmword.h \
+	mmwtex.h
+
+
+# files used to build the executable.
+metamath_SOURCES = \
+	metamath.c \
+	mmcmdl.c \
+	mmcmds.c \
+	mmdata.c \
+	mmhlpa.c \
+	mmhlpb.c \
+	mminou.c \
+	mmpars.c \
+	mmpfas.c \
+	mmunif.c \
+	mmveri.c \
+	mmvstr.c \
+	mmword.c \
+	mmwtex.c \
+	$(noinst_HEADERS)
+
+
+# man pages for the executable´
+dist_man_MANS = metamath.1
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+	esac;
+
+$(top_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 \
+	  ; 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) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+	      $(INSTALL_PROGRAM_ENV) $(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:
+	-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+metamath$(EXEEXT): $(metamath_OBJECTS) $(metamath_DEPENDENCIES) $(EXTRA_metamath_DEPENDENCIES) 
+	@rm -f metamath$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(metamath_OBJECTS) $(metamath_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metamath.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmcmdl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmcmds.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmdata.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmhlpa.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmhlpb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mminou.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmpars.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmpfas.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmunif.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmveri.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmvstr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmword.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmwtex.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+	@$(MKDIR_P) $(@D)
+	@echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+install-man1: $(dist_man_MANS)
+	@$(NORMAL_INSTALL)
+	@list1=''; \
+	list2='$(dist_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='$(dist_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)
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS) $(HEADERS)
+installdirs:
+	for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+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-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+		-rm -f ./$(DEPDIR)/metamath.Po
+	-rm -f ./$(DEPDIR)/mmcmdl.Po
+	-rm -f ./$(DEPDIR)/mmcmds.Po
+	-rm -f ./$(DEPDIR)/mmdata.Po
+	-rm -f ./$(DEPDIR)/mmhlpa.Po
+	-rm -f ./$(DEPDIR)/mmhlpb.Po
+	-rm -f ./$(DEPDIR)/mminou.Po
+	-rm -f ./$(DEPDIR)/mmpars.Po
+	-rm -f ./$(DEPDIR)/mmpfas.Po
+	-rm -f ./$(DEPDIR)/mmunif.Po
+	-rm -f ./$(DEPDIR)/mmveri.Po
+	-rm -f ./$(DEPDIR)/mmvstr.Po
+	-rm -f ./$(DEPDIR)/mmword.Po
+	-rm -f ./$(DEPDIR)/mmwtex.Po
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man1
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+		-rm -f ./$(DEPDIR)/metamath.Po
+	-rm -f ./$(DEPDIR)/mmcmdl.Po
+	-rm -f ./$(DEPDIR)/mmcmds.Po
+	-rm -f ./$(DEPDIR)/mmdata.Po
+	-rm -f ./$(DEPDIR)/mmhlpa.Po
+	-rm -f ./$(DEPDIR)/mmhlpb.Po
+	-rm -f ./$(DEPDIR)/mminou.Po
+	-rm -f ./$(DEPDIR)/mmpars.Po
+	-rm -f ./$(DEPDIR)/mmpfas.Po
+	-rm -f ./$(DEPDIR)/mmunif.Po
+	-rm -f ./$(DEPDIR)/mmveri.Po
+	-rm -f ./$(DEPDIR)/mmvstr.Po
+	-rm -f ./$(DEPDIR)/mmword.Po
+	-rm -f ./$(DEPDIR)/mmwtex.Po
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-man
+
+uninstall-man: uninstall-man1
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+	clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \
+	distclean distclean-compile distclean-generic 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-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+	ps ps-am tags tags-am uninstall uninstall-am \
+	uninstall-binPROGRAMS uninstall-man uninstall-man1
+
+.PRECIOUS: Makefile
+
+
+# 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/metamath.1 b/src/metamath.1
similarity index 100%
rename from metamath.1
rename to src/metamath.1
diff --git a/metamath.c b/src/metamath.c
similarity index 70%
rename from metamath.c
rename to src/metamath.c
index 55c6ac0..cfcf754 100644
--- a/metamath.c
+++ b/src/metamath.c
@@ -1,8103 +1,6459 @@
-/*****************************************************************************/
-/* Program name:  metamath                                                   */
-/* Copyright (C) 2020 NORMAN MEGILL  nm at alum.mit.edu  http://metamath.org */
-/* License terms:  GNU General Public License Version 2 or any later version */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* Copyright notice:  All code in this program that was written by Norman
-   Megill is public domain.  However, the project includes code contributions
-   from other people which may be GPL licensed.  For more details see:
-   https://github.com/metamath/metamath-exe/issues/7#issuecomment-675555069 */
-
-/* The overall functionality of the modules is as follows:
-    metamath.c - Contains main(); executes or calls commands
-    mmcmdl.c - Command line interpreter
-    mmcmds.c - Extends metamath.c command() to execute SHOW and other
-               commands; added after command() became too bloated (still is:)
-    mmdata.c - Defines global data structures and manipulates arrays
-               with functions similar to BASIC string functions;
-               memory management; converts between proof formats
-    mmhlpa.c - The help file, part 1.
-    mmhlpb.c - The help file, part 2.
-    mminou.c - Basic input and output interface
-    mmmaci.c - THINK C Macintosh interface (obsolete)
-    mmpars.c - Parses the source file
-    mmpfas.c - Proof Assistant
-    mmunif.c - Unification algorithm for Proof Assistant
-    mmutil.c - Miscellaneous I/O utilities (reserved for future use)
-    mmveri.c - Proof verifier for source file
-    mmvstr.c - BASIC-like string functions
-    mmwtex.c - LaTeX/HTML source generation
-    mmword.c - File revision utility (for TOOLS> UPDATE) (not generally useful)
-*/
-
-/* Compilation instructions (gcc on Unix/Linus/Cygwin, lcc on Windows):
-   1. Make sure each .c file above is present in the compilation directory and
-      that each .c file (except metamath.c) has its corresponding .h file
-      present.
-   2. In the directory where these files are present, type:
-         gcc m*.c -o metamath
-   3. For full error checking, use:
-         gcc m*.c -o metamath -O2 -Wall -Wextra -Wmissing-prototypes \
-             -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-align \
-             -Wredundant-decls -Wnested-externs -Winline -Wno-long-long \
-             -Wconversion -Wstrict-prototypes -std=c99 -pedantic -Wunused-result
-      Note: gcc 4.9.2 (on Debian) fails with "unknown type name `ssize_t'" if
-      -std=c99 is used, so omit -std=c99 to work around this problem.
-   4. For faster runtime, use these gcc options:
-         gcc m*.c -o metamath -O3 -funroll-loops -finline-functions \
-             -fomit-frame-pointer -Wall -std=c99 -pedantic -fno-strict-overflow
-   5. The Windows version in the download was compiled with lcc-win32 version 3.8:
-         lc -O m*.c -o metamath.exe
-   6. On Linux, if you have autoconf, automake, and a C compiler, you
-      can compile with the command "autoreconf -i && ./configure && make".
-      See the README.TXT file for more information.
-*/
-
-
-
-#define MVERSION "0.195 30-Dec-2020"
-/* 0.195 nm 30-Dec-2020 metamath.c - temporarily disable /REWRAP until bug fixed
-   27-Sep-2020 nm mmwtex.c - prevent "htmlexturl" links from wrapping */
-/* 0.194 26-Dec-2020 nm mmwtex.c - add keyword "htmlexturl" to $t
-   statement in .mm file */
-/* 0.193 12-Sep-2020 nm mmcmds.c mmdata.c,h mmwtex.c,h mmhlpa.c - make the
-   output of /EXTRACT stable in the sense that, with the same <label-list>
-   parameter, extract(extract(file)) = extract(file) except that the date
-   stamp at the top will be updated.  (The first extraction even if "*" will
-   usually be different because it discards non-relevant content.  Note that
-   the include file directives "$( $[ Begin..." etc. and comments with "$j" are
-   currently discarded.) */
-/* 0.192 4-Sep-2020 nm metamath.c - fix bug */
-/* 0.191 4-Sep-2020 nm metamath.c - add comment close */
-/* 0.190 4-Sep-2020 nm mmcmds.c - fix bug in writeExtractedSource() */
-/* 0.189 4-Sep-2020 nm mmhlpa.c - add help for WRITE SOURCE .. /EXTRACT ...
-   24-Aug-2020 nm metamath.c mmcmdl.c mmcmds.c,h mmdata.c,h mmhlpa.c
-     mmpars.c mmpfas.c mmunif.c mmwtex.c,h - Added
-     WRITE SOURCE ... /EXTRACT ... */
-/* 0.188 23-Aug-2020 nm mmwtex.c, mmhlpa.c Added CONCLUSION FACT INTRODUCTION
-     PARAGRAPH SCOLIA SCOLION SUBSECTION TABLE to [bib] keywords */
-/* 0.187 15-Aug-2020 nm All m*.c, m*.h - put "g_" in front of all global
-     variable names e.g. "statements" becomes "g_statements"; also capitalized
-     1st letter of original name in case of global structs e.g. "statement"
-     becomes "g_Statement".
-   9-Aug-2020 nm mmcmdl.c, mmhlpa.c - add HELP BIBLIOGRAPHY */
-/* 0.186 8-Aug-2020 nm mmwtex.c, mmhlpa.c - add CONJECTURE, RESULT to [bib]
-     keywords
-   8-Aug-2020 nm mmpfas.c, metamath.c - print message when IMPROVE or
-     MINIMIZE_WITH uses another mathbox */
-/* 0.185 5-Aug-2020 nm metamath.c mmcmdl.c mmhlpb.c mmpfas.c,h mmcmds.c
-     mmwtex.c,h - add /INCLUDE_MATHBOXES to to IMPROVE; notify user upon ASSIGN
-     from another mathbox.
-   18-Jul-2020 nm mmcmds.c, mmdata.c, mmhlpb.c, metamath.c - "PROVE =" will now
-     resume the previous MM-PA session if there was one; allow "~" to start/end
-     with blank (meaning first/last statement); add "@1234" */
-/* 0.184 17-Jul-2020 nm metamath.c mmcmdl.c mmcmds.c,h mmhlpb.c mmwtex.c,h -
-     add checking for mathbox independence to VERIFY MARKUP; add /MATHBOX_SKIP
-   4-Jul-2020 nm mmwtex.c - correct error msg for missing althtmldef
-   3-Jul-2020 nm metamath.c, mmhlpa.c - allow space in TOOLS> BREAK */
-/* 0.183 30-Jun-2020 30-Jun-2020 nm mmpars.c - refine prevention of
-     WRITE SOURCE.../REWRAP from modifying comments containing "<HTML>"
-     (specifically, remove indentation alignment).
-   25-Jun-2020 nm metamath.c, mmcmds.c,h mmcmdl.c mmhlpb.c - add underscore
-     checking in VERIFY MARKUP and add /UNDERSCORE_SKIP qualifier; also check
-     for trailing space on lines.
-   20-Jun-2020 nm mmcmds.c - check for discouragement tags in *ALT, *OLD
-     labels in VERIFY MARKUP.
-   19-Jun-2020 nm mminou.c,h, metamath.c, mmwtex.c - dynamically allocate
-     buffer in print2() using vsnprintf() to calculate size needed
-   18-Jun-2020 nm mmpars.c - remove error check for $e <- $f assignments.  See
-     https://groups.google.com/d/msg/metamath/Cx_d84uorf8/0FrNYTM9BAAJ */
-/* 0.182 12-Apr-2020 nm mmwtex.c, mmphlpa.c - add "Claim" to bib ref types */
-/* 0.181 12-Feb-2020 nm (reported by David Starner) metamath.c - fix bug causing
-     new axioms to be used by MINIMIZE_WITH */
-/* 0.180 10-Dec-2019 nm (bj 13-Sep-2019) mmpars.c - fix "line 0" in error msg
-     when label clashes with math symbol
-   8-Dec-2019 nm (bj 13-Oct-2019) mmhlpa.c - improve TOOLS> HELP INSERT, DELETE
-   8-Dec-2019 nm (bj 19-Sep-2019) mminou.c - change bug 1511 to error message
-   30-Nov-2019 nm (bj 12-Oct-2019) mmwtex.c - trigger Most Recent link on
-     mmtheorems.html when there is a mathbox statement (currently set.mm and
-     iset.mm).
-   30-Nov-2019 nm (bj 13-Sep-2019) mmhlpa.c - improve help for TOOLS> DELETE and
-     SUBSTITUTE.
-   30-Nov-2019 nm (bj 13-Sep-2019) mmwtex.c - change "g_htmlHome" in warnings to
-     "htmlhome". */
-/* 0.179 29-Nov-2019 nm (bj 22-Sep-2019) metamath.c - MINIMIZE_WITH axiom trace
-     now starts from current NEW_PROOF instead of SAVEd proof.
-   23-Nov-2019 nm (bj 4-Oct-2019) metamath.c - make sure traceback flags are
-     cleared after MINIMIZE_WITH
-   20-Nov-2019 nm mmhlpa.c - add url pointer to HELP WRITE SOURCE /SPLIT
-   18-Nov-2019 nm mmhlpa.c - clarify HELP WRITE SOURCE /REWRAP
-   15-Oct-2019 nm mmdata.c - add bug check info for user
-   14-Oct-2019 nm mmcmds.c - use '|->' (not 'e.') as syntax hint for maps-to
-   14-Oct-2019 nm mmwtex.c - remove extraneous </TD> */
-/* 0.178 10-Aug-2019 nm mminou.c - eliminate redundant fopen in fSafeOpen
-   6-Aug-2019 nm mmwtex.c,h, mmcmds.c - Add error check for >1 line
-     section name or missing closing decoration line in getSectionHeadings()
-   4-Aug-2019 nm mmhlpb.c, mmcmdl.c, metamath.c - Add /ALLOW_NEW_AXIOMS,
-     renamed /ALLOW_GROWTH to /MAY_GROW
-   17-Jul-2019 nm mmcmdl.c, mmhlpa.c, metamath.c - Add /NO_VERSIONING to
-     WRITE THEOREM_LIST
-   17-Jul-2019 nm metamath.c - Change line of dashes between SHOW STATEMENT
-     output from hardcoded 79 to current g_screenWidth */
-/* 0.177 27-Apr-2019 nm mmcmds.c -"set" -> "setvar" in htmlAllowedSubst.
-   mmhlpb.c - fix typos in HELP IMPROVE. */
-/* 0.176 25-Mar-2019 nm metamath.c mmcmds.h mmcmds.c mmcmdl.c mmhlpb.c -
-   add /TOP_DATE_SKIP to VERIFY MARKUP */
-/* 0.175 8-Mar-2019 nm mmvstr.c - eliminate warning in gcc 8.3 (patch
-   provided by David Starner) */
-/* 0.174 22-Feb-2019 nm mmwtex.c - fix erroneous warning when using "[["
-   bracket escape in comment */
-/* 0.173 3-Feb-2019 nm mmwtex.c - fix infinite loop when "[" was the first
-   character in a comment */
-/* 0.172 25-Jan-2019 nm mmwtex.c - comment out bug 2343 trap (not a bug) */
-/* 0.171 13-Dec-2018 nm metamath.c, mmcmdl.c, mmhlpa.c, mmcmds.c,h, mmwtex.c,h
-   - add fine-grained qualfiers to MARKUP command */
-/* 0.170 12-Dec-2018 nm mmwtex.c - restore line accidentally deleted in 0.169 */
-/* 0.169 10-Dec-2018 nm metamath.c, mmcmds.c,h, mmcmdl.c, mmpars.c, mmhlpa.c,
-   mmwtex.c - Add MARKUP command.
-   9-Dec-2018 nm mmwtex.c - escape literal "[" with "[[" in comments. */
-/* 0.168 8-Dec-2018 nm metamath.c - validate that /NO_REPEATED_STEPS is used
-   only with /LEMMON.
-   8-Dec-2018 nm mmcmds.c - fix bug #256 reported by Jim Kingdon
-   (https://github.com/metamath/set.mm/issues/497). */
-/* 0.167 13-Nov-2018 nm mmcmds.c - SHOW TRACE_BACK .../COUNT now uses proof
-   the way it's stored (previously, it always uncompressed the proof).  The
-   new step count (for compressed proofs) corresponds to the step count the
-   user would see on the web pages.
-   12-Nov-2018 nm mmcmds.c - added unlimited precision arithmetic
-   for SHOW TRACE_BACK .../COUNT/ESSENTIAL */
-/* 0.166 31-Oct-2018 nm mmwtex.c - workaround Chrome anchor bug
-   30-Oct-2018 nm mmcmds.c - put "This theorem is referenced by" after
-   axioms and definitions used in HTML; use "(None)" instead of suppressing
-   line if nothing is referenced */
-/* 0.165 20-Oct-2018 nm mmwtex.c - added ~ mmtheorems#abc type anchor
-   in TOC details.  mmwtex.c - fix bug (reported by Benoit Jubin) that
-   changes "_" in labels to subscript.  mmcmdl.c - remove unused COMPLETE
-   qualifier from SHOW PROOF.  mmwtex.c - enhance special cases of web page
-   spacing identified by Benoit Jubin */
-/* 0.164 5-Sep-2018 nm mmwtex.c, mmhlpb.c - added NOTE to bib keywords
-   14-Aug-2018 nm metamath.c - added defaultScrollMode to prevent
-   SET SCROLL CONTINUOUS from reverting to PROMPTED after a SUBMIT command */
-/* 0.163 4-Aug-2018 nm mmwtex.c - removed 2nd "sandbox:bighdr" anchor
-   in mmtheorems.html; removed Firefox and IE references; changed breadcrumb
-   font to be consistent with other pages; put asterisk next to TOC entries
-   that have associated comments */
-/* FOR FUTURE REFERENCE: search for "Thierry" in mmwtex.c to modify the link
-   to tirix.org structured proof site */
-/* 0.162-thierry 3-Jun-2018 nm mmwtex.c - add link to tirix.org structured
-   proofs */
-/* 0.162 3-Jun-2018 nm mmpars.c - re-enabled error check for $c not in
-   outermost scope.  mmhlpa.c mmhlpb.c- improve some help messages.
-   mmwtex.c - added "OBSERVATION", "PROOF", AND "STATEMENT" keywords for
-   WRITE BIBLIOGRAPHY */
-/* 0.161 2-Feb-2018 nm mmpars.c,h mmcmds.c mmwtex.c - fix wrong file name
-   and line number in error messages */
-/* 0.160 24-Jan-2018 nm mmpars.c - fix bug introduced in version 0.158 */
-/* 0.159 23-Jan-2018 nm mmpars.c - fix crash due to missing include file */
-/* 0.158 22-Jan-2018 nm mminou.c - strip CRs from Windows SUBMIT files
-   run on Linux */
-/* 0.157 15-Jan-2018 nm Major rewrite of READ-related functions.
-     Added HELP MARKUP.
-   9-Jan-2018 nm Track line numbers for error messages in included files
-   1-Jan-2018 nm Changed HOME_DIRECTORY to ROOT_DIRECTORY
-   31-Dec-2017 nm metamath.c mmcmdl.c,h mmpars.c,h mmcmds.c,h mminou.c,h
-     mmwtex.c mmhlpb.c mmdata.h - add virtual includes "$( Begin $[...$] $)",
-     $( End $[...$] $)", "$( Skip $[...$] $)" */
-/* 0.156 8-Dec-2017 nm mmwtex.c - fix bug that incorrectly gave "verify markup"
-   errors when there was a mathbox statement without an "extended" section */
-/* 0.155 8-Oct-2017 nm mmcmdl.c - restore accidentally removed HELP HTML;
-   mmhlpb.c mmwtex.c mmwtex.h,c mmcmds.c metamath.c - improve HELP and make
-   other cosmetic changes per Benoit Jubin's suggestions */
-/* 0.154 2-Oct-2017 nm mmunif.h,c mmcmds.c - add 2 more variables to ERASE;
-   metamath.c mmcmdl.c - remove obsolete OPEN/CLOSE HTML; mmhlpa.c mmhlpb.c -
-   fix typos reported by Benoit Jubin */
-/* 0.153 1-Oct-2017 nm mmunif.c,h mmcmds.c - Re-initialize internal nmbrStrings
-   in unify() after 'erase' command reported by Benoit Jubin */
-/* 0.152 26-Sep-2017 nm mmcmds.c - change default links from mpegif to mpeuni;
-   metamath.c - enforce minimum screen width = 3 to prevent crash reported
-   by Benoit Jubin */
-/* 0.151 20-Sep-2017 nm mmwtex.c - better matching to insert space between
-   A and y in "E. x e. ran A y R x" to prevent spurious spaces in thms rncoeq,
-   dfiun3g as reported by Benoit Jubin */
-/* 0.150 26-Aug-2017 nm mmcmds.c,mmwtex.h - fix hyperlink for Distinct variable
-   etc. lists so that it will point to mmset.html on other Explorers like NF.
-   Move the "Dummy variables..." to print after the "Proof of Theorem..."
-   line. */
-/* 0.149 21-Aug-2017 nm mmwtex.c,h mmcmds.c mmhlpb.c - add a subsubsection
-     "tiny" header with separator "-.-." to table of contents and theorem list;
-     see HELP WRITE THEOREM_LIST
-   21-Aug-2017 nm mmcmds.c - remove bug check 255
-   19-Aug-2017 nm mmcmds.c - change mmset.html links to
-     ../mpeuni/mmset.html so they will work in NF Explorer etc. */
-/* 0.148 14-Aug-2017 nm mmcmds.c - hyperlink "Dummy variable(s)" */
-/* 0.147 13-Aug-2017 nm mmcmds.c,h - add "Dummy variable x is distinct from all
-   other variables." to proof web page */
-/* 0.146 26-Jun-2017 nm mmwtex.c - fix handling of local labels in
-     'show proof.../tex' (bug 2341 reported by Eric Parfitt) */
-/* 0.145 16-Jun-2017 nm metamath.c mmpars.c - fix bug 1741 during
-     MINIMIZE_WITH; mmpfas.c - make duplicate bug numbers unique; mmhlpa.c
-     mmhlpb.c - adjust to prevent lcc compiler "Function too big for the
-     optimizer"
-   29-May-2017 nm mmwtex.c mmhlpa.c - take out extraneous  <HTML>...</HTML>
-     markup tags in HTML output so w3c validator will pass */
-/* 0.144 15-May-2017 nm metamath.c mmcmds.c - add "(Revised by..." tag for
-     conversion of legacy .mm's if there is a 2nd date under the proof */
-/* 0.143 14-May-2017 nm metamath.c mmdata.c,h mmcmdl.c mmcmds.c mmhlpb.c -
-     added SET CONTRIBUTOR; for missing "(Contributed by..." use date
-     below proof if it exists, otherwise use today's date, in order to update
-     old .mm files.
-   14-May-2017 Ari Ferrera - mmcmds.c - fix memory leaks in ERASE */
-/* 0.142 12-May-2017 nm metamath.c mmdata.c,h mmcmds.c - added
-     "#define DATE_BELOW_PROOF" in mmdata.h that if uncommented, will enable
-     use of the (soon-to-be obsolete) date below the proof
-   4-May-2017 Ari Ferrera - mmcmds.c metamath.c mmdata.c mmcmdl.c
-     mminou.c mminou.h mmcmdl.h mmdata.h - fixed memory leaks and warnings
-     found by valgrind.
-   3-May-2017 nm - metamath.c mmdata.c,h mmcmds.c,h mmpars.c,h mmhlpb.c
-     mmcmdl.c mmwtex.c - added xxChanged flags to statement structure so
-     that any part of the source can be changed;  removed /CLEAN qualifier
-     of WRITE SOURCE; automatically put "(Contributed by ?who?..." during
-     SAVE NEW_PROOF or SAVE PROOF when it is missing; more VERIFY MARKUP
-     checking. */
-/* 0.141 2-May-2017 nm mmdata.c, metamath.c, mmcmds.c, mmhlpb.c - use
-   getContrib() date for WRITE RECENT instead of date below proof.  This lets
-   us list recent $a's as well as $p's.  Also, add caching to getContrib() for
-   speedup. */
-/* 0.140 1-May-2017 nm mmwtex.c, mmcmds.c, metamath.c - fix some LaTeX issues
-   reported by Ari Ferrera */
-/* 0.139 2-Jan-2017 nm metamath.c - print only one line for
-     'save proof * /compressed/fast' */
-/* 0.138 26-Dec-2016 nm mmwtex.c - remove extraneous </TD> causing w3c
-   validation failure; put space after 1st x in "F/ x x = x";
-   mmcmds.c - added checking for lines > 79 chars in VERIFY MARKUP;
-   mmcmds.c, mmcmdl.c, metamath.c, mmhlpb.c, mmcmds.h - added /VERBOSE to
-   VERIFY MARKUP */
-/* 0.137 20-Dec-2016 nm mmcmds.c - check ax-XXX $a vs axXXX $p label convention
-     in 'verify markup'
-   18-Dec-2016 nm mmwtex.c, mmpars.c, mmdata.h - use true "header area"
-     between successive $a/$p for getSectionHeadings()  mmcmds.c - add
-     header comment checking
-   13-Dec-2016 nm mmdata.c,h - enhanced compareDates() to treat empty string as
-     older date.
-   13-Dec-2016 nm metamath.c, mmcmds.c - moved mm* and Microsoft illegal file
-     name label check to verifyMarkup() (the VERIFY MARKUP command) instead of
-     checking on READ; added check of set.mm Version date to verifyMarkup().
-   13-Dec-2016 nm mmwtex.c,h - don't treat bracketed description text with
-     space as a bib label; add labelMatch parameter to writeBibliography() */
-/* 0.136 10-Oct-2016 mminou.c - fix resource leak bug reported by David
-   Binderman */
-/* 0.135 11-Sep-2016, 14-Sep-2016 metamath.c, mmpfas.c,h, mmdata.c,h,
-   mmpars.c,h mmcmds.c, mmcmdl.c, mmhlpb.c - added EXPAND command */
-/* 0.134 16-Aug-2016 mmwtex.c - added breadcrumbs to theorem pages;
-   metamath.c, mmcmdl.c, mmhlpb.c, mminou.c,.h - added /TIME to SAVE PROOF,
-   SHOW STATEMENT.../[ALT}HTML, MINIMIZE_WITH */
-/* 0.133 13-Aug-2016 mmwtex.c - improve mobile display with <head> tag
-   mmpars.c - use updated Macintosh information */
-/* 0.132 10-Jul-2016 metamath.c, mmcmdl.c, mmcmds.c,.h, mmdata.c,.h, mmhlpb.c,
-   mmpfas.c - change "restricted" to "discouraged" to match set.mm markup
-   tags; add SET DISCOURAGEMENT OFF|ON (default ON) to turn off blocking for
-   convenience of advanced users
-   6-Jul-2016 metamath.c - add "(void)" in front of "system(...)" to
-   suppress -Wunused-result warning */
-/* 0.131 10-Jun-2016 mminou.c - reverted change of 22-May-2016 because
-   'minimize_with' depends on error message in string to prevent DV violations.
-   Todo:  write a DV-checking routine for 'minimize_with', then revert
-   the 22-May-2016 fix for bug 126 (which only occurs when writing web
-   pages for .mm file with errors).
-   9-Jun-2016 mmcmdl.c, metamath.c - added _EXIT_PA for use with
-   scripts that will give an error message outside of MM-PA> rather
-   than exiting metamath */
-/* 0.130 25-May-2016 mmpars.c - workaround clang warning about j = j;
-      mmvstr.c - workaround gcc -Wstrict-overflow warning */
-/* 0.129 24-May-2016 mmdata.c - fix bug 1393 */
-/* 0.128 22-May-2016 mminou.c - fixed error message going to html page
-      instead of to screen, triggering bug 126. */
-/* 0.127 10-May-2016 metamath.c, mmcmdl.c, mmhlpb.c - added /OVERRIDE to
-      PROVE */
-/* 0.126 3-May-2016 metamath.c, mmdata.h, mmdata.c, mmcmds.h, mmcmds.c,
-      mmcmdl.c, mmhlpb.c, mmpars.c - added getMarkupFlag() in mmdata.c;
-      Added /OVERRIDE added to ASSIGN, REPLACE, IMPROVE, MINIMIZE_WITH,
-      SAVE NEW_PROOF;  PROVE gives warning about SAVE NEW_PROOF for locked
-      proof.  Added SHOW RESTRICTED command.
-   3-May-2016 m*.c - fix numerous conversion warnings provided by gcc 5.3.0 */
-/* 0.125 10-Mar-2016 mmpars.c - fixed bug parsing /EXPLICIT/PACKED format
-   8-Mar-2016 nm mmdata.c - added "#nnn" to SHOW STATEMENT etc. to reference
-      statement number e.g. SHOW STATEMENT #58 shows a1i in set.mm.
-   7-Mar-2016 nm mmwtex.c - added space between } and { in HTML output
-   6-Mar-2016 nm mmpars.c - disabled wrapping of formula lines in
-       WRITE SOURCE.../REWRAP
-   2-Mar-2016 nm metamat.c, mmcmdl.c, mmhlpb.c - added /FAST to
-       SAVE PROOF, SHOW PROOF */
-/* 0.123 25-Jan-2016 nm mmpars.c, mmdata.h, mmdata.c, mmpfas.c, mmcmds.,
-   metamath.c, mmcmdl.c, mmwtex.c - unlocked SHOW PROOF.../PACKED,
-   added SHOW PROOF.../EXPLICIT */
-/* 0.122 14-Jan-2016 nm metamath.c, mmcmds.c, mmwtex.c, mmwtex.h - surrounded
-      math HTML output with "<SPAN [g_htmlFont]>...</SPAN>; added htmlcss and
-      htmlfont $t commands
-   10-Jan-2016 nm mmwtex.c - delete duplicate -4px style; metamath.c -
-     add &nbsp; after char on mmascii.html
-   3-Jan-2016 nm mmwtex.c - fix bug when doing SHOW STATEMENT * /ALT_HTML after
-   VERIFY MARKUP */
-/* 0.121 17-Nov-2015 nm metamath.c, mmcmdl.h, mmcmdl.c, mmcmds.h, mmcmds.c,
-       mmwtex.h, mmwtex.c, mmdata.h, mmdata.c -
-   1. Moved WRITE BIBLIOGRAPHY code from metamath.c to its own function in
-      mmwtex.c; moved qsortStringCmp() from metamath.c to mmdata.c
-   2. Added $t, comment markup, and bibliography checking to VERIFY MARKUP
-   3. Added options to bug() bug-check interception to select aborting,
-      stepping to next bug, or ignoring subsequent bugs
-   4. SHOW STATEMENT can now use both /HTML and /ALT_HTML in same session
-   5. Added /HTML, /ALT_HTML to WRITE THEOREM_LIST and
-      WRITE RECENT_ADDITIONS */
-/* 0.120 7-Nov-2015 nm metamath.c, mmcmdl.c, mmpars.c - add VERIFY MARKUP
-   4-Nov-2015 nm metamath.c, mmcmds.c/h, mmdata.c/h - move getDescription,
-       getSourceIndentation from mmcmds.c to mmdata.c.
-       metamath.c, mmdata.c - add and call parseDate() instead of in-line
-       code; add getContrib(), getProofDate(), buildDate(), compareDates(). */
-/* 0.119 18-Oct-2015 nm mmwtex.c - add summary TOC to Theorem List; improve
-       math symbol GIF image alignment
-   2-Oct-2015 nm metamath.c, mmpfas.c, mmwtex.c - fix miscellaneous small
-       bugs or quirks */
-/* 0.118 18-Jul-2015 nm metamath.c, mmcmds.h, mmcmds.c, mmcmdl.c, mmhlpb.h,
-   mmhlpb.c - added /TO qualifier to SHOW TRACE_BACK.  See
-   HELP SHOW TRACE_BACK. */
-/* 0.117 30-May-2015
-   1. nm mmwtex.c - move <A NAME... tag to math symbol cell in proof pages so
-      hyperlink will jump to top of cell (reported by Alan Sare)
-   2. daw mmpfas.c - add INLINE speedup if compiler permits
-   3. nm metamath.c, mminou.c, mmwtex.c, mmpfas.c - fix clang -Wall warnings
-      (reported by David A. Wheeler) */
-/* 0.116 9-May-2015 nm mmwtex.c - adjust paragraph break in 'write th';
-   Statement List renamed Theorem List;  prevent space in after paragraph
-   in Theorem List; remove stray "(";  put header and header comment
-   in same table cell; fix <TITLE> of Theorem List pages */
-/* 0.115 8-May-2015 nm mmwtex.c - added section header comments to
-       WRITE THEOREM_LIST and broke out Table of Contents page
-   24-Apr-2015 nm metamath.c - add # bytes to end of "---Clip out the proof";
-       reverted to no blank lines there (see 0.113 item 3) */
-/* 0.114 22-Apr-2015 nm mmcmds.c - put [-1], [-2],... offsets on 'show
-   new_proof/unknown' */
-/* 0.113 19-Apr-2015 so, nm metamath.c, mmdata.c
-   1. SHOW LABEL % will show statements with changed proofs
-   2. SHOW LABEL = will show the statement being proved in MM-PA
-   3. Added blank lines before, after "---------Clip out the proof" proof
-   4. Generate date only if the proof is complete */
-/* 0.112 15-Apr-2015 nm metamath.c - fix bug 1121 (reported by S. O'Rear);
-   mwtex.c - add "img { margin-bottom: -4px }" to CSS to align symbol GIFs;
-   mwtex.c - remove some hard coding for set.mm, for use with new nf.mm;
-   metamath.c - fix comment parsing in WRITE BIBLIOGRAPHY to ignore
-   math symbols  */
-/* 0.111 22-Nov-2014 nm metamath.c, mmcmds.c, mmcmdl.c, mmhlpb.c - added
-   /NO_NEW_AXIOMS_FROM qualifier to MINIMIZE_WITH; see HELP MINIMIZE_WITH.
-   21-Nov-2014 Stepan O'Rear mmdata.c, mmhlpb.c - added ~ label range specifier
-   to wildcards; see HELP SEARCH */
-/* 0.110 2-Nov-2014 nm mmcmds.c - fixed bug 1114 (reported by Stefan O'Rear);
-   metamath.c, mmhlpb.c - added "SHOW STATEMENT =" to show the statement
-   being proved in MM-PA (based on patch submitted by Stefan O'Rear) */
-/* 0.109 20-Aug-2014 nm mmwtex.c - fix corrupted HTML caused by misinterpreting
-   math symbols as comment markup (math symbols with _ [ ] or ~).  Also,
-   allow https:// as well as http:// in ~ label markup.
-   11-Jul-2014 wl mmdata.c - fix obscure crash in debugging mode db9 */
-/* 0.108 25-Jun-2014 nm
-   (1) metamath.c, mmcmdl.c, mmhlpb.c - MINIMIZE_WITH now checks the size
-   of the compressed proof, prevents $d violations, and tries forward and
-   reverse statment scanning order; /NO_DISTINCT, /BRIEF, /REVERSE
-   qualifiers were removed.
-   (2) mminou.c - prevent hard breaks (in the middle of a word) in too-long
-   lines (e.g. long URLs) in WRITE SOURCE .../REWRAP; just overflow the
-   screen width instead.
-   (3) mmpfas.c - fixed memory leak in replaceStatement()
-   (4) mmpfas.c - suppress inf. growth with MINIMIZE_WITH idi/ALLOW_GROWTH */
-/* 0.107 21-Jun-2014 nm metamath.c, mmcmdl.c, mmhlpb.c - added /SIZE qualifier
-   to SHOW PROOF; added SHOW ELAPSED_TIME; mmwtex.c - reformatted WRITE
-   THEOREM_LIST output; now "$(", newline, "######" starts a "part" */
-/* 0.106 30-Mar-2014 nm mmwtex.c - fix bug introduced by 0.105 that disabled
-   hyperlinks on literature refs in HTML comment.  metamath.c - improve
-   messages */
-/* 0.105 15-Feb-2014 nm mmwtex.c - prevented illegal LaTeX output for certain
-   special characters in comments. */
-/* 0.104 14-Feb-2014 nm mmwtex.c - fixed bug 2312, mmcmds.c - enhanced ASSIGN
-   error message. */
-/* 0.103 4-Jan-2014 nm mmcmds.c,h - added "Allowed substitution hints" below
-   the "Distinct variable groups" list on generated web pages
-   mmwtex.c - added "*" to indicate DV's occur in Statement List entries. */
-/* 0.102 2-Jan-2014 nm mminou.c - made compressed proof line wrapping more
-   uniform at start of compressed part of proof */
-/* 0.101 27-Dec-2013 nm mmdata.h,c, mminou.c, mmcmdl.c, mmhlpb.c, mmvstr.c -
-   Improved storage efficiency of /COMPRESSED proofs (but with 20% slower run
-   time); added /OLD_COMPRESSION to specify old algorithm; removed end-of-line
-   space after label list in old algorithm; fixed linput() bug */
-/* 0.100 30-Nov-2013 nm mmpfas.c - reversed statement scan order in
-   proveFloating(), to speed up SHOW STATEMENT df-* /HTML; metamath.c - remove
-   the unknown date place holder in SAVE NEW_PROOF; Wolf Lammen mmvstr.c -
-   some cleanup */
-/* 0.07.99 1-Nov-2013 nm metamath.c, mmpfas.h,c, mmcmdl.h,c, mmhlpa.c,
-   mmhlpb.c - added UNDO, REDO, SET UNDO commands (see HELP UNDO) */
-/* 0.07.98 30-Oct-2013 Wolf Lammen mmvstr.c,h, mmiou.c, mmpars.c,
-   mmdata.c  - improve code style and program structure */
-/* 0.07.97 20-Oct-2013 Wolf Lammen mmvstr.c,h, metamath.c - improved linput();
-   nm mmcmds.c, mmdata.c - tolerate bad proofs in SHOW TRACE_BACK etc. */
-/* 0.07.96 20-Sep-2013 Wolf Lammen mmvstr.c - revised cat();
-   nm mmwtex.c, mminou.c - change a print2 to printLongLine to fix bug 1150 */
-/* 0.07.95 18-Sep-2013 Wolf Lammen mmvstr.c - optimized cat();
-   nm metamath.c, mmcmds.c, mmdata.c, mmpars.c, mmpfas.c, mmvstr.c,
-   mmwtex.c - suppress some clang warnings */
-/* 0.07.94 28-Aug-2013 Alexey Merkulov mmcmds.c, mmpars.c - fixed several
-   memory leaks found by valgrind --leak-check=full --show-possibly-lost=no */
-/* 0.07.93 8-Jul-2013 Wolf Lammen mmvstr.c - simplified let() function;
-   also many minor changes in m*.c and m*.h to assist future refactoring */
-/* 0.07.92 28-Jun-2013 nm metamath.c mmcmds.c,h mmcmdl.c mmhlpb.c- added
-   /NO_REPEATED_STEPS for /LEMMON mode of SHOW PROOF, SHOW NEW_PROOF.
-   This reverts the /LEMMON mode default display change of 31-Jan-2010
-   and invokes it when desired via /NO_REPEATED_STEPS. */
-/* 0.07.91 20-May-2013 nm metamath.c mmpfas.c,h mmcmds.c,h mmcmdl.c
-   mmhlpb.c- added /FORBID qualifier to MINIMIZE_WITH */
-/* 0.07.90 19-May-2013 nm metamath.c mmcmds.c mmcmdl.c mmhlpb.c - added /MATCH
-   qualifier to SHOW TRACE_BACK */
-/* 0.07.88 18-Nov-2012 nm mmcmds.c - fixed bug 243 */
-/* 0.07.87 17-Nov-2012 nm mmwtex.c - fixed formatting problem when label
-   markup ends a comment in SHOW PROOF ... /HTML */
-/* 0.07.86 27-Oct-2012 nm mmcmds.c, mmwtex.c, mmwtex.h - fixed ERASE bug
-   caused by imperfect re-initialization; reported by Wolf Lammen */
-/* 0.07.85 10-Oct-2012 nm metamath.c, mmcmdl.c, mmwtex.c, mmwtex.h, mmhlpb.c -
-   added /SHOW_LEMMAS to WRITE THEOREM_LIST to bypass lemma math suppression */
-/* 0.07.84 9-Oct-2012 nm mmcmds.c - fixed bug in getStatementNum() */
-/* 0.07.83 19-Sep-2012 nm mmwtex.c - fixed bug reported by Wolf Lammen */
-/* 0.07.82 16-Sep-2012 nm metamath.c, mmpfas.c - fixed REPLACE infinite loop;
-   improved REPLACE message for shared dummy variables */
-/* 0.07.81 14-Sep-2012 nm metamath.c, mmcmds.c, mmcmds.h, mmcmdl.c, mmhlpb.c
-   - added FIRST, LAST, +nn, -nn where missing from ASSIGN, REPLACE,
-   IMPROVE, LET STEP.  Wildcards are allowed for PROVE, ASSIGN, REPLACE
-   labels provided there is a unique match. */
-/* 0.07.80 4-Sep-2012 nm metamath.c, mmpfas.c, mmpfas.h, mmcmdl.c, mmhlpb.c
-   - added / 1, / 2, / 3, / SUBPROOFS to IMPROVE to specify search level */
-/* 0.07.79 31-Aug-2012 nm m*.c - clean up some gcc warnings */
-/* 0.07.78 28-Aug-2012 nm mmpfas.c - fix bug in 0.07.77. */
-/* 0.07.77 25-Aug-2012 nm metamath.c, mmpfas.c - Enhanced IMPROVE algorithm to
-   allow non-shared dummy variables in unknown steps */
-/* 0.07.76 22-Aug-2012 nm metamath.c, mmpfas.c, mmcmdl.c, mmhlpb.c -
-   Enhanced IMPROVE algorithm to also try REPLACE algorithm */
-/* 0.07.75 14-Aug-2012 nm metamath.c - MINIMIZE_WITH now checks current
-   mathbox (but not other mathboxes) even if /INCLUDE_MATHBOXES is omitted */
-/* 0.07.74 18-Mar-2012 nm mmwtex.c, mmcmds.c, metamath.c - improved texToken()
-   error message */
-/* 0.07.73 26-Dec-2011 nm mmwtex.c, mmpars.c - added <HTML>...</HTML> in
-   comments for passing through raw HTML code into HTML files generated with
-   SHOw STATEMENT xxx / HTML */
-/* 0.07.72 25-Dec-2011 nm (obsolete) */
-/* 0.07.71 10-Nov-2011 nm metamath.c, mmcmdl.c - added /REV to MINIMIZE_WITH */
-/* 0.07.70 6-Aug-2011 nm mmwtex.c - fix handling of double quotes inside
-   of htmldef strings to match spec in Metamath book Appendix A p. 156 */
-/* 0.07.69 9-Jul-2011 nm mmpars.c, mmvstr.c - Untab file in WRITE SOURCE
-   ... /REWRAP */
-/* 0.07.68 3-Jul-2011 nm metamath.c, mminou.h, mminou.c - Nested SUBMIT calls
-   (SUBMIT calls inside of a SUBMIT command file) are now allowed.
-   Also, mmdata.c - fix memory leak. */
-/* 0.07.67 2-Jul-2011 nm metamath.c, mmcmdl.c, mmhlpa.c - Added TAG command
-   to TOOLS.  See HELP TAG under TOOLS.  (The old special-purpose TAG command
-   was renamed to UPDATE.) */
-/* 0.07.66 1-Jul-2011 nm metamath.c, mmcmds.c, mmpars.c, mmpars.h - Added code
-   to strip spurious "$( [?] $)" in WRITE SOURCE ... /CLEAN output */
-/* 0.07.65 30-Jun-2011 nm mmwtex.c - Prevent processing [...] bibliography
-   brackets inside of `...` math strings in comments. */
-/* 0.07.64 28-Jun-2011 nm metamath.c, mmcmdl.c - Added /INCLUDE_MATHBOXES
-   qualifier to MINIMIZE_WITH; without it, MINIMIZE_WITH * will skip
-   checking user mathboxes. */
-/* 0.07.63 26-Jun-2011 nm mmwtex.c - check that .gifs exist for htmldefs */
-/* 0.07.62 18-Jun-2011 nm mmpars.c - fixed bug where redeclaration of active
-   $v was not detected */
-/* 0.07.61 12-Jun-2011 nm mmpfas.c, mmcmds.c, metamath.c, mmhlpb.c - added
-   /FORMAT and /REWRAP qualifiers to WRITE SOURCE to format according to set.mm
-   conventions - set HELP WRITE SOURCE */
-/* 0.07.60 7-Jun-2011 nm mmpfas.c - fixed bug 1805 which occurred when doing
-   MINIMIZE_WITH weq/ALLOW_GROWTH after DELETE DELETE FLOATING_HYPOTHESES */
-/* 0.07.59 11-Dec-2010 nm mmpfas.c - increased default SET SEARCH_LIMIT from
-   10000 to 25000 to accomodate df-plig web page in set.mm */
-/* 0.07.58 9-Dec-2010 nm mmpars.c - detect if same symbol is used with both
-   $c and $v, in order to conform with Metamath spec */
-/* 0.07.57 19-Oct-2010 nm mmpars.c - fix bug causing incorrect line count
-   for error messages when non-ASCII character was found; mminou.h -
-   increase SET WIDTH maximum from 999 to 9999 */
-/* 0.07.56 27-Sep-2010 nm mmpars.c, mmpfas.c - check for $a's with
-   one token e.g. "$a wff $."; if found, turn SET EMPTY_SUBSTITUTION ON
-   automatically.  (Suggested by Mel O'Cat; patent pending.) */
-/* 0.07.55 26-Sep-2010 nm mmunif.c, mmcmds.c, mmunif.h - check for mismatched
-   brackets in all $a's, so that if there are any, the bracket matching
-   algorithm (for fewer ambiguous unifications) in mmunif.c will be turned
-   off. */
-/* 0.07.54 25-Sep-2010 nm mmpars.c - added $f checking to conform to the
-   current Metamath spec, so footnote 2 on p. 92 of Metamath book is
-   no longer applicable. */
-/* 0.07.53 24-Sep-2010 nm mmveri.c - fixed bug(2106), reported by Michal
-   Burger */
-/* 0.07.52 14-Sep-2010 nm metamath.c, mmwtex.h, mmwtex.c, mmcmds.c,
-   mmcmdl.c, mmhlpb.c - rewrote the LaTeX output for easier hand-editing
-   and embedding in LaTeX documents.  The old LaTeX output is still
-   available with /OLD_TEX on OPEN TEX, SHOW STATEMENT, and SHOW PROOF,
-   but it is obsolete and will be deleted eventually if no one objects.  The
-   new /TEX output also replaces the old /SIMPLE_TEX, which was removed. */
-/* 0.07.51 9-Sep-2010 Stefan Allen mmwtex.c - put hyperlinks on hypothesis
-   label references in SHOW STATEMENT * /HTML, ALT_HTML output */
-/* 0.07.50 21-Feb-2010 nm mminou.c - "./metamath < empty", where "empty" is a
-   0-byte file, now exits metamath instead of producing an infinite loop.
-   Also, ^D now exits metamath.  Reported by Cai Yufei */
-/* 0.07.49 31-Jan-2010 nm mmcmds.c - Lemmon-style proofs (SHOW PROOF xxx
-   /LEMON/RENUMBER) no longer have steps with dummy labels; instead, steps
-   are now the same as in HTML page proofs.  (There is a line to comment
-   out if you want to revert to old behavior.) */
-/* 0.07.48 11-Sep-2009 nm mmpars.c, mm, mmvstr.c, mmdata.c - Added detection of
-   non-whitespace around keywords (mmpars.c); small changes to eliminate
-   warnings in gcc 3.4.4 (mmvstr.c, mmdata.c) */
-/* 0.07.47 2-Aug-2009 nm mmwtex.c, mmwtex.h - added user name to mathbox
-   pages */
-/* 0.07.46 24-Jul-2009 nm metamath.c, mmwtex.c - changed name of sandbox
-   to "mathbox" */
-/* 0.07.45 15-Jun-2009 nm metamath.c, mmhlpb.c - put "!" before each line of
-   SET ECHO ON output to make them easy to identity for creating scripts */
-/* 0.07.44 12-May-2009 Stefan Allan, nm metamath.c, mmcmdl.c, mmwtex.c -
-   added SHOW STATEMENT / MNEMONICS - see HELP SHOW STATEMENT */
-/* 0.07.43 29-Aug-2008 nm mmwtex.c - workaround for Unicode huge font bug in
-   FireFox 3 */
-/* 0.07.42 8-Aug-2008 nm metamath.c - added sandbox, Hilbert Space colors to
-   Definition List */
-/* 0.07.41 29-Jul-2008 nm metamath.c, mmwtex.h, mmwtex.c - Added handling of
-   sandbox section of Metamath Proof Explorer web pages */
-/* 0.07.40 6-Jul-2008 nm metamath.c, mmcmdl.c, mmhlpa.c, mmhlpb.c - Added
-   / NO_VERSIONING qualifier for SHOW STATEMENT, so website can be regenerated
-   in place with less temporary space required.  Also, the wildcard trigger
-   for mmdefinitions.html, etc. is more flexible (see HELP HTML). */
-/* 0.07.39 21-May-2008 nm metamath.c, mmhlpb.c - Added wildcard handling to
-   statement label in SHOW TRACE_BACK.  All wildcards now allow
-   comma-separated lists [i.e. matchesList() instead of matches()] */
-/* 0.07.38 26-Apr-2008 nm metamath.c, mmdata.h, mmdata.c, mmvstr.c, mmhlpb.c -
-   Enhanced / EXCEPT qualifier for MINIMIZE_WITH to handle comma-separated
-   wildcard list. */
-/* 0.07.37 14-Apr-2008 nm metamath.c, mmcmdl.c, mmhlpb.c - Added / JOIN
-   qualifier to SEARCH. */
-/* 0.07.36 7-Jan-2008 nm metamath.c, mmcmdl.c, mmhlpb.c - Added wildcard
-   handling for labels in SHOW USAGE. */
-/* 0.07.35 2-Jan-2008 nm mmcmdl.c, metamath.c, mmhlpb.c - Changed keywords
-   COMPACT to PACKED and COLUMN to START_COLUMN so that SAVE/SHOW proof can use
-   C to abbreviate COMPRESSED.  (PACKED format is supported but "unofficial,"
-   used mainly for debugging purposes, and is not listed in HELP SAVE
-   PROOF.) */
-/* 0.07.34 19-Nov-2007 nm mmwtex.c, mminou.c - Added tooltips to proof step
-   hyperlinks in SHOW STATEMENT.../HTML,ALT_HTML output (suggested by Reinder
-   Verlinde) */
-/* 0.07.33 19-Jul-2007 nm mminou.c, mmvstr.c, mmdata.c, mmword.c - added fflush
-   after each printf() call for proper behavior inside emacs (suggested by
-   Frederic Line) */
-/* 0.07.32 29-Apr-2007 nm mminou.c - fSafeOpen now stops at gap; e.g. if ~2
-   doesn't exist, ~1 will be renamed to ~2, but any ~3, etc. are not touched */
-/* 0.07.31 5-Apr-2007 nm mmwtex.c - Don't make "_" in hyperlink a subscript */
-/* 0.07.30 8-Feb-2007 nm mmcmds.c, mmwtex.c Added HTML statement number info to
-   SHOW STATEMENT.../FULL; friendlier "Contents+1" link in mmtheorems*.html */
-/* 0.07.29 6-Feb-2007 Jason Orendorff mmpfas.c - Patch to eliminate the
-   duplicate "Exceeded trial limit at step n" messages */
-/* 0.07.28 22-Dec-2006 nm mmhlpb.c - Added info on quotes to HELP LET */
-/* 0.07.27 23-Oct-2006 nm metamath.c, mminou.c, mmhlpa.c, mmhlpb.c - Added
-   / SILENT qualifier to SUBMIT command */
-/* 0.07.26 12-Oct-2006 nm mminou.c - Fixed bug when SUBMIT file was missing
-   a new-line at end of file (reported by Marnix Klooster) */
-/* 0.07.25 10-Oct-2006 nm metamath.c - Fixed bug invoking TOOLS as a ./metamath
-   command-line argument */
-/* 0.07.24 25-Sep-2006 nm mmcmdl.c Fixed bug in
-   SHOW NEW_PROOF/START_COLUMN nn/LEM */
-/* 0.07.23 31-Aug-2006 nm mmwtex.c - Added Home and Contents links to bottom of
-   WRITE THEOREM_LIST pages */
-/* 0.07.22 26-Aug-2006 nm metamath.c, mmcmdl.c, mmhlpb.c - Changed 'IMPROVE
-   STEP <step>' to 'IMPROVE <step>' for user convenience and to be consistent
-   with ASSIGN <step> */
-/* 0.07.21 20-Aug-2006 nm mmwtex.c - Revised small colored numbers so that all
-   colors have the same grayscale brightness.. */
-/* 0.07.20 19-Aug-2006 nm mmpars.c - Made the error "Required hypotheses may
-   not be explicitly declared" in a compressed proof non-severe, so that we
-   can still SAVE the proof to reformat and recover it. */
-/* 0.07.19 11-Aug-06 nm mmcmds.c - "Distinct variable group(s)" is now
-   "group" or "groups" as appropriate. */
-/* 0.07.18 31-Jul-06 nm mmwtex.c - added table to contents to p.1 of output of
-   WRITE THEOREM_LIST command. */
-/* 0.07.17 4-Jun-06 nm mmpars.c - do not allow labels to match math symbols
-   (new spec proposed by O'Cat).   mmwtex.c - made theorem name 1st in title,
-   for readability in Firefox tabs. */
-/* 0.07.16 16-Apr-06 nm metamath.c, mmcmdl.c, mmpfas.c, mmhlpb.c - allow step
-   to be negative (relative to end of proof) for ASSIGN, UNIFY, and LET STEP
-   (see their HELPs).  Added INITIALIZE USER to delete LET STEP assignments
-   (see HELP INITIALIZE).  Fixed bug in LET STEP (mmpfas.c). */
-/* 0.07.15 10-Apr-06 nm metamath.c, mmvstr.c - change dates from 2-digit to
-   4-digit year; make compatible with older 2-digit year. */
-/* 0.07.14 21-Mar-06 nm mmpars.c - fix bug 1722 when compressed proof has
-   "Z" at beginning of proof instead of after a proof step. */
-/* 0.07.13 3-Feb-06 nm mmpfas.c - minor improvement to MINIMIZE_WITH */
-/* 0.07.12 30-Jan-06 nm metamath.c, mmcmds.c, mmdata.c, mmdata.h, mmhlpa.c,
-   mmhlpb.c - added "?" wildcard to match single character. See HELP SEARCH. */
-/* 0.07.11 7-Jan-06 nm metamath.c, mmcmdl.c, mmhlpb.c - added EXCEPT qualifier
-   to MINIMIZE_WITH */
-/* 0.07.10 28-Dec-05 nm metamath.c, mmcmds.c - cosmetic tweaks */
-/* 0.07.10 11-Dec-05 nm metamath.c, mmcmdl.c, mmhlpb.c - added ASSIGN FIRST
-   and IMPROVE FIRST commands.  Also enhanced READ error message */
-/* 0.07.9 1-Dec-05 nm mmvstr.c - added comment on how to make portable */
-/* 0.07.9 18-Nov-05 nm metamath.c, mminou.c, mminou.h, mmcmdl.c, mmhlpb.c -
-   added SET HEIGHT command; changed SET SCREEN_WIDTH to SET WIDTH; changed
-   SET HENTY_FILTER to SET JEREMY_HENTY_FILTER (to make H for HEIGHT
-   unambiguous); added HELP for SET JEREMY_HENTY_FILTER */
-/* 0.07.8 15-Nov-05 nm mmunif.c - now detects wrong order in bracket matching
-   heuristic to further reduce ambiguous unifications in Proof Assistant */
-/* 0.07.7 12-Nov-05 nm mmunif.c - add "[","]" and "[_","]_" bracket matching
-   heuristic to reduce ambiguous unifications in Proof Assistant.
-   mmwtex.c - added heuristic for HTML spacing after "sum_" token. */
-/* 0.07.6 15-Oct-05 nm mmcmds.c,mmpars.c - fixed compressed proof algorithm
-   to match spec in book (with new algorithm due to Marnix Klooster).
-   Users are warned to convert proofs when the old compression is found. */
-/* 0.07.5 6-Oct-05 nm mmpars.c - fixed bug that reset "severe error in
-   proof" flag when a proof with severe error also had unknown steps */
-/* 0.07.4 1-Oct-05 nm mmcmds.c - ignored bug 235, which could happen for
-   non-standard logics */
-/* 0.07.3 17-Sep-05 nm mmpars.c - reinstated duplicate local label checking to
-   conform to strict spec */
-/* 0.07.2 19-Aug-05 nm mmwtex.c - suppressed math content for lemmas in
-   WRITE THEOREMS output */
-/* 0.07.1 28-Jul-05 nm Added SIMPLE_TEX qualifier to SHOW STATEMENT */
-/* 0.07:  Official 0.07 22-Jun-05 corresponding to Metamath book */
-/* 0.07x:  Fixed to work with AMD64 with 64-bit longs by
-   Waldek Hebisch; deleted unused stuff in mmdata.c */
-/* 0.07w:  .mm date format like "$( [7-Sep-04] $)" is now
-   generated and permitted (old one is tolerated too for compatibility) */
-/* Metamath Proof Verifier - main program */
-/* See the book "Metamath" for description of Metamath and run instructions */
-
-/*****************************************************************************/
-
-
-/*----------------------------------------------------------------------*/
-
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-/* #include <time.h> */ /* 21-Jun-2014 nm For ELAPSED_TIME */
-#ifdef THINK_C
-#include <console.h>
-#endif
-#include "mmutil.h"
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mmcmdl.h"
-#include "mmcmds.h"
-#include "mmhlpa.h"
-#include "mmhlpb.h"
-#include "mminou.h"
-#include "mmpars.h"
-#include "mmveri.h"
-#include "mmpfas.h"
-#include "mmunif.h"
-#include "mmword.h"
-#include "mmwtex.h"
-#ifdef THINK_C
-#include "mmmaci.h"
-#endif
-
-void command(int argc, char *argv[]);
-
-int main(int argc, char *argv[])
-{
-
-/* argc is the number of arguments; argv points to an array containing them */
-#ifdef THINK_C
-/* Set console attributes */
-console_options.pause_atexit = 0; /* No pause at exit */
-console_options.title = (unsigned char*)"\pMetamath";
-#endif
-
-#ifdef THINK_C
-  /* The standard stream triggers the console package to initialize the
-     Macintosh Toolbox managers and use the console interface.  cshow must
-     be called before using our own window to prevent crashing (THINK C
-     Standard Library Reference p. 43). */
-  cshow(stdout);
-  /* Initialize MacIntosh interface */
-  /*ToolBoxInit(); */ /* cshow did this automatically */
-  /* Display opening window */
-  /*
-  WindowInit();
-  DrawMyPicture();
-  */
-  /* Wait for mouse click or key */
-  /*while (!Button());*/
-#endif
-
-
-  /****** If g_listMode is set to 1 here, the startup will be Text Tools
-          utilities, and Metamath will be disabled ***************************/
-  /* (Historically, this mode was used for the creation of a stand-alone
-     "TOOLS>" utility for people not interested in Metamath.  This utility
-     was named "LIST.EXE", "tools.exe", and "tools" on VMS, DOS, and Unix
-     platforms respectively.  The UPDATE command of TOOLS (mmword.c) was
-     custom-written in accordance with the version control requirements of a
-     company that used it; it documents the differences between two versions
-     of a program as C-style comments embedded in the newer version.) */
-  g_listMode = 0; /* Force Metamath mode as startup */
-
-
-  g_toolsMode = g_listMode;
-
-  if (!g_listMode) {
-    /*print2("Metamath - Version %s\n", MVERSION);*/
-    print2("Metamath - Version %s%s", MVERSION, space(27 - (long)strlen(MVERSION)));
-  }
-  /* if (argc < 2) */ print2("Type HELP for help, EXIT to exit.\n");
-
-  /* Allocate big arrays */
-  initBigArrays();
-
-  /* 14-May-2017 nm */
-  /* Set the default contributor */
-  let(&g_contributorName, DEFAULT_CONTRIBUTOR);
-
-  /* Process a command line until EXIT */
-  command(argc, argv);
-
-  /* Close logging command file */
-  if (g_listMode && g_listFile_fp != NULL) {
-    fclose(g_listFile_fp);
-  }
-
-  return 0;
-
-}
-
-
-
-
-void command(int argc, char *argv[])
-{
-  /* Command line user interface -- this is an infinite loop; it fetches and
-     processes a command; returns only if the command is 'EXIT' or 'QUIT' and
-     never returns otherwise. */
-  long argsProcessed = 0;  /* Number of argv initial command-line
-                                     arguments processed so far */
-
-  long /*c,*/ i, j, k, m, l, n, p, q, r, s /*,tokenNum*/;
-  long stmt, step;
-  int subType = 0;
-#define SYNTAX 4
-  vstring str1 = "", str2 = "", str3 = "", str4 = "", str5= "";
-  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated directly */
-  nmbrString *nmbrTmp = NULL_NMBRSTRING;
-  nmbrString *nmbrSaveProof = NULL_NMBRSTRING;
-  /*pntrString *pntrTmpPtr;*/ /* Pointer only; not allocated directly */
-  pntrString *pntrTmp = NULL_PNTRSTRING;
-  pntrString *expandedProof = NULL_PNTRSTRING;
-  flag tmpFlag;
-
-  /* 1-Nov-2013 nm proofSavedFlag tells us there was at least one
-     SAVE NEW_PROOF during the MM-PA session while the UNDO stack wasn't
-     empty, meaning that "UNDO stack empty" is no longer a reliable indication
-     that the proof wasn't changed.  It is cleared upon entering MM-PA, and
-     set by SAVE NEW_PROOF. */
-  flag proofSavedFlag = 0;
-
-  /* Variables for SHOW PROOF */
-  flag pipFlag; /* Proof-in-progress flag */
-  long outStatement; /* Statement for SHOW PROOF or SHOW NEW_PROOF */
-  flag explicitTargets; /* For SAVE PROOF /EXPLICIT */
-  long startStep; long endStep;
-  /* long startIndent; */
-  long endIndent; /* Also for SHOW TRACE_BACK */
-  flag essentialFlag; /* Also for SHOW TRACE_BACK */
-  flag renumberFlag; /* Flag to use essential step numbering */
-  flag unknownFlag;
-  flag notUnifiedFlag;
-  flag reverseFlag;
-  long detailStep;
-  flag noIndentFlag; /* Flag to use non-indented display */
-  long splitColumn; /* Column at which formula starts in nonindented display */
-  flag skipRepeatedSteps; /* NO_REPEATED_STEPS qualifier */ /* 28-Jun-2013 nm */
-  flag texFlag; /* Flag for TeX */
-  flag saveFlag; /* Flag to save in source */
-  flag fastFlag; /* Flag for SAVE PROOF.../FAST */ /* 2-Jan-2017 nm */
-  long indentation; /* Number of spaces to indent proof */
-  vstring labelMatch = ""; /* SHOW PROOF <label> argument */
-
-  flag axiomFlag; /* For SHOW TRACE_BACK */
-  flag treeFlag; /* For SHOW TRACE_BACK */ /* 19-May-2013 nm */
-  flag countStepsFlag; /* For SHOW TRACE_BACK */ /* 19-May-2013 nm */
-  flag matchFlag; /* For SHOW TRACE_BACK */ /* 19-May-2013 nm */
-  vstring matchList = "";  /* For SHOW TRACE_BACK */ /* 19-May-2013 nm */
-  vstring traceToList = ""; /* For SHOW TRACE_BACK */ /* 18-Jul-2015 nm */
-  flag recursiveFlag; /* For SHOW USAGE */
-  long fromLine, toLine; /* For TYPE, SEARCH */
-  flag joinFlag; /* For SEARCH */
-  long searchWindow; /* For SEARCH */
-  FILE *type_fp; /* For TYPE, SEARCH */
-  long maxEssential; /* For MATCH */
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-                                            /* For ASSIGN/IMPROVE FIRST/LAST */
-  long improveDepth; /* For IMPROVE */
-  flag searchAlg; /* For IMPROVE */ /* 22-Aug-2012 nm */
-  flag searchUnkSubproofs;  /* For IMPROVE */ /* 4-Sep-2012 nm */
-  flag dummyVarIsoFlag; /* For IMPROVE */ /* 25-Aug-2012 nm */
-  long improveAllIter; /* For IMPROVE ALL */ /* 25-Aug-2012 nm */
-  flag proofStepUnk; /* For IMPROVE ALL */ /* 25-Aug-2012 nm */
-
-  flag texHeaderFlag; /* For OPEN TEX, CLOSE TEX */
-  flag commentOnlyFlag; /* For SHOW STATEMENT */
-  flag briefFlag; /* For SHOW STATEMENT */
-  flag linearFlag; /* For SHOW LABELS */
-  vstring bgcolor = ""; /* For SHOW STATEMENT definition list */
-                                                            /* 8-Aug-2008 nm */
-
-  flag verboseMode, mayGrowFlag /*, noDistinctFlag*/; /* For MINIMIZE_WITH */
-  long prntStatus; /* For MINIMIZE_WITH */
-  flag hasWildCard; /* For MINIMIZE_WITH */
-  long exceptPos; /* For MINIMIZE_WITH */
-  flag mathboxFlag; /* For MINIMIZE_WITH */ /* 28-Jun-2011 nm */
-  long thisMathboxStartStmt; /* For MINIMIZE_WITH */ /* 14-Aug-2012 nm */
-  flag forwFlag; /* For MINIMIZE_WITH */ /* 11-Nov-2011 nm */
-  long forbidMatchPos;  /* For MINIMIZE_WITH */ /* 20-May-2013 nm */
-  vstring forbidMatchList = "";  /* For MINIMIZE_WITH */ /* 20-May-2013 nm */
-  long noNewAxiomsMatchPos;  /* For NO_NEW_AXIOMS_FROM */ /* 22-Nov-2014 nm */
-  vstring noNewAxiomsMatchList = "";  /* For NO_NEW_AXIOMS_FROM */ /* 22-Nov-2014 */
-  long allowNewAxiomsMatchPos;  /* For NO_NEW_AXIOMS_FROM */ /* 4-Aug-2019 nm */
-  vstring allowNewAxiomsMatchList = "";  /* For NO_NEW_AXIOMS_FROM */ /* 4-Aug-2019 */
-  vstring traceProofFlags = ""; /* For NO_NEW_AXIOMS_FROM */ /* 22-Nov-2014 nm */
-  vstring traceTrialFlags = ""; /* For NO_NEW_AXIOMS_FROM */ /* 22-Nov-2014 nm */
-  flag overrideFlag; /* For discouraged statement /OVERRIDE */ /* 3-May-2016 nm */
-
-  struct pip_struct saveProofForReverting = {
-       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
-                                   /* For MINIMIZE_WITH */ /* 20-May-2013 nm */
-  long origCompressedLength; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
-  long oldCompressedLength = 0; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
-  long newCompressedLength = 0; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
-  long forwardCompressedLength = 0; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
-  long forwardLength = 0; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
-  vstring saveZappedProofSectionPtr; /* Pointer only */ /* For MINIMIZE_WITH */
-  long saveZappedProofSectionLen; /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
-  flag saveZappedProofSectionChanged; /* For MINIMIZE_WITH */ /* 16-Jun-2017 nm */
-
-  struct pip_struct saveOrigProof = {
-       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
-                                   /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
-  struct pip_struct save1stPassProof = {
-       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
-                                   /* For MINIMIZE_WITH */ /* 25-Jun-2014 nm */
-  long forwRevPass; /* 1 = forward pass */ /* 25-Jun-2014 nm */
-
-  long sourceStatement; /* For EXPAND */ /* 11-Sep-2016 nm */
-
-  flag showLemmas; /* For WRITE THEOREM_LIST */ /* 10-Oct-2012 nm */
-  flag noVersioning; /* For WRITE THEOREM_LIST & others */ /* 17-Jul-2019 nm */
-  long theoremsPerPage; /* For WRITE THEOREM_LIST */ /* 17-Jul-2019 nm */
-
-  /* g_toolsMode-specific variables */
-  flag commandProcessedFlag = 0; /* Set when the first command line processed;
-                                    used to exit shell command line mode */
-  FILE *list1_fp;
-  FILE *list2_fp;
-  FILE *list3_fp;
-  vstring list2_fname = "", list2_ftmpname = "";
-  vstring list3_ftmpname = "";
-  vstring oldstr = "", newstr = "";
-  long lines, changedLines, oldChangedLines, twoMatches, p1, p2;
-  long firstChangedLine;
-  flag cmdMode, changedFlag, outMsgFlag;
-  double sum;
-  vstring bufferedLine = "";
-  vstring tagStartMatch = "";  /* 2-Jul-2011 nm For TAG command */
-  long tagStartCount = 0;      /* 2-Jul-2011 nm For TAG command */
-  vstring tagEndMatch = "";    /* 2-Jul-2011 nm For TAG command */
-  long tagEndCount = 0;        /* 2-Jul-2011 nm For TAG command */
-  long tagStartCounter = 0;    /* 2-Jul-2011 nm For TAG command */
-  long tagEndCounter = 0;      /* 2-Jul-2011 nm For TAG command */
-
-  /* 21-Jun-2014 */
-  /* 16-Aug-2016 nm Now in getElapasedTime() */
-  /* clock_t timePrevious = 0; */  /* For SHOW ELAPSED_TIME command */
-  /* clock_t timeNow = 0; */       /* For SHOW ELAPSED_TIME command */
-  /* 16-Aug-2016 nm */
-  double timeTotal = 0;
-  double timeIncr = 0;
-  flag printTime;  /* Set by "/ TIME" in SAVE PROOF and others */
-
-  /* 14-Aug-2018 nm */
-  flag defaultScrollMode = 1; /* Default to prompted mode */
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  p = 0;
-  q = 0;
-  s = 0;
-  texHeaderFlag = 0;
-  firstChangedLine = 0;
-  tagStartCount = 0;           /* 2-Jul-2011 nm For TAG command */
-  tagEndCount = 0;             /* 2-Jul-2011 nm For TAG command */
-  tagStartCounter = 0;         /* 2-Jul-2011 nm For TAG command */
-  tagEndCounter = 0;           /* 2-Jul-2011 nm For TAG command */
-
-  while (1) {
-
-    if (g_listMode) {
-      /* If called from the OS shell with arguments, do one command
-         then exit program. */
-      /* (However, let a SUBMIT job complete) */
-      if (argc > 1 && commandProcessedFlag &&
-             g_commandFileNestingLevel == 0) return;
-    }
-
-    g_errorCount = 0; /* Reset error count before each read or proof parse. */
-
-    /* Deallocate stuff that may have been used in previous pass */
-    let(&str1,"");
-    let(&str2,"");
-    let(&str3,"");
-    let(&str4,"");
-    let(&str5,"");
-    nmbrLet(&nmbrTmp, NULL_NMBRSTRING);
-    pntrLet(&pntrTmp, NULL_PNTRSTRING);
-    nmbrLet(&nmbrSaveProof, NULL_NMBRSTRING);
-    nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-    j = nmbrLen(g_rawArgNmbr);
-    if (j != g_rawArgs) bug(1110);
-    j = pntrLen(g_rawArgPntr);
-    if (j != g_rawArgs) bug(1111);
-    g_rawArgs = 0;
-    for (i = 0; i < j; i++) let((vstring *)(&g_rawArgPntr[i]), "");
-    pntrLet(&g_rawArgPntr, NULL_PNTRSTRING);
-    nmbrLet(&g_rawArgNmbr, NULL_NMBRSTRING);
-    j = pntrLen(g_fullArg);
-    for (i = 0; i < j; i++) let((vstring *)(&g_fullArg[i]),"");
-    pntrLet(&g_fullArg,NULL_PNTRSTRING);
-    j = pntrLen(expandedProof);
-    if (j) {
-      for (i = 0; i < j; i++) {
-        let((vstring *)(&expandedProof[i]),"");
-      }
-     pntrLet(&expandedProof,NULL_PNTRSTRING);
-    }
-
-    let(&list2_fname, "");
-    let(&list2_ftmpname, "");
-    let(&list3_ftmpname, "");
-    let(&oldstr, "");
-    let(&newstr, "");
-    let(&labelMatch, "");
-    /* (End of space deallocation) */
-
-    g_midiFlag = 0; /* 8/28/00 Initialize here in case SHOW PROOF exits early */
-
-    if (g_memoryStatus) {
-      /*??? Change to user-friendly message */
-#ifdef THINK_C
-      print2("Memory:  string %ld xxxString %ld free %ld\n",db,db3,(long)FreeMem());
-      getPoolStats(&i, &j, &k);
-      print2("Pool:  free alloc %ld  used alloc %ld  used actual %ld\n",i,j,k);
-#else
-      print2("Memory:  string %ld xxxString %ld\n",db,db3);
-#endif
-      getPoolStats(&i, &j, &k);
-      print2("Pool:  free alloc %ld  used alloc %ld  used actual %ld\n",i,j,k);
-    }
-
-    if (!g_toolsMode) {
-      if (g_PFASmode) {
-        let(&g_commandPrompt,"MM-PA> ");
-      } else {
-        let(&g_commandPrompt,"MM> ");
-      }
-    } else {
-      if (g_listMode) {
-        let(&g_commandPrompt,"Tools> ");
-      } else {
-        let(&g_commandPrompt,"TOOLS> ");
-      }
-    }
-
-    let(&g_commandLine,""); /* Deallocate previous contents */
-
-    if (!commandProcessedFlag && argc > 1 && argsProcessed < argc - 1
-        && g_commandFileNestingLevel == 0) {
-      /* if (g_toolsMode) { */  /* 10-Oct-2006 nm Fix bug: changed to g_listMode */
-      if (g_listMode) {
-        /* If program was compiled in TOOLS mode, the command-line argument
-           is assumed to be a single TOOLS command; build the equivalent
-           TOOLS command */
-        for (i = 1; i < argc; i++) {
-          argsProcessed++;
-          /* Put quotes around an argument with spaces or tabs or quotes
-             or empty string */
-          if (instr(1, argv[i], " ") || instr(1, argv[i], "\t")
-              || instr(1, argv[i], "\"") || instr(1, argv[i], "'")
-              || (argv[i])[0] == 0) {
-            /* If it contains a double quote, use a single quote */
-            if (instr(1, argv[i], "\"")) {
-              let(&str1, cat("'", argv[i], "'", NULL));
-            } else {
-              /* (??? (TODO)Case of both ' and " is not handled) */
-              let(&str1, cat("\"", argv[i], "\"", NULL));
-            }
-          } else {
-            let(&str1, argv[i]);
-          }
-          let(&g_commandLine, cat(g_commandLine, (i == 1) ? "" : " ", str1, NULL));
-        }
-      } else {
-        /* If program was compiled in default (Metamath) mode, each command-line
-           argument is considered a full Metamath command.  User is responsible
-           for ensuring necessary quotes around arguments are passed in. */
-        argsProcessed++;
-        g_scrollMode = 0; /* Set continuous scrolling until completed */
-        let(&g_commandLine, cat(g_commandLine, argv[argsProcessed], NULL));
-        if (argc == 2 && instr(1, argv[1], " ") == 0) {
-          /* Assume the user intended a READ command.  This special mode allows
-             invocation via "metamath xxx.mm". */
-          if (instr(1, g_commandLine, "\"") || instr(1, g_commandLine, "'")) {
-            /* If it already has quotes don't put quotes */
-            let(&g_commandLine, cat("READ ", g_commandLine, NULL));
-          } else {
-            /* Put quotes so / won't be interpreted as qualifier separator */
-            let(&g_commandLine, cat("READ \"", g_commandLine, "\"", NULL));
-          }
-
-          /***** 3-Jan-2017 nm  This is now done with SET ROOT_DIRECTORY
-          /@ 31-Dec-2017 nm @/
-          /@ This block of code can be removed without side effects @/
-          /@ "Hidden" hack for NM's convenience. :)  If there is an =, change
-             it to space.  This lets users invoke with "metamath set.mm/h=test"
-             or "metamath mbox/aa.mm/home=test" to specify an implicit read with
-             /ROOT_DIRECTORY without having a space in the argument (a space
-             means don't assume a read command). @/
-          i = instr(1, g_commandLine, "=");
-          if (i != 0) {
-            /@ Change 'READ "set.mm/h=test"' to 'READ "set.mm" / "h" "test"' @/
-            let(&g_commandLine, cat(left(g_commandLine, i - 1), "\" \"",
-                right(g_commandLine, i + 1), NULL));
-            while (i > 0) {
-              if (g_commandLine[i - 1] == '/') {
-                let(&g_commandLine, cat(left(g_commandLine, i - 1),
-                    "\" / \"", right(g_commandLine, i + 1), NULL));
-                break;
-              }
-              i--;
-            }
-            /@ Change ' to " to prevent mismatched quotes @/
-            i = (long)strlen(g_commandLine);
-            while (i > 0) {
-              if (g_commandLine[i - 1] == '\'') {
-                g_commandLine[i - 1] = '"';
-              }
-              i--;
-            }
-          } /@ if i != 0 (end of 31-Dec-2017 NM hack) @/
-          */
-
-        }
-      }
-      print2("%s\n", cat(g_commandPrompt, g_commandLine, NULL));
-    } else {
-      /* Get command from user input or SUBMIT script file */
-      g_commandLine = cmdInput1(g_commandPrompt);
-    }
-    if (argsProcessed == argc && !commandProcessedFlag) {
-      commandProcessedFlag = 1;
-      g_scrollMode = defaultScrollMode; /* Set prompted (default) scroll mode */
-    }
-    if (argsProcessed == argc - 1) {
-      argsProcessed++; /* Indicates restore scroll mode next time around */
-      if (g_toolsMode) {
-        /* If program was compiled in TOOLS mode, we're only going to execute
-           one command; set flag to exit next time around */
-        commandProcessedFlag = 1;
-      }
-    }
-
-    /* See if it's an operating system command */
-    /* (This is a command line that begins with a quote) */
-    if (g_commandLine[0] == '\'' || g_commandLine[0] == '\"') {
-      /* See if this computer has this feature */
-      if (!system(NULL)) {
-        print2("?This computer does not accept an operating system command.\n");
-        continue;
-      } else {
-        /* Strip off quote and trailing quote if any */
-        let(&str1, right(g_commandLine, 2));
-        if (g_commandLine[0]) { /* (Prevent stray pointer if empty string) */
-          if (g_commandLine[0] == g_commandLine[strlen(g_commandLine) - 1]) {
-            let(&str1, left(str1, (long)(strlen(str1)) - 1));
-          }
-        }
-        /* Do the operating system command */
-        (void)system(str1);
-#ifdef VAXC
-        printf("\n"); /* Last line from VAX doesn't have new line */
-#endif
-        continue;
-      }
-    }
-
-    parseCommandLine(g_commandLine);
-    if (g_rawArgs == 0) {
-      continue; /* Empty or comment line */
-    }
-    if (!processCommandLine()) {
-      continue;
-    }
-
-    if (g_commandEcho || (g_toolsMode && g_listFile_fp != NULL)) {
-      /* Build the complete command and print it for the user */
-      k = pntrLen(g_fullArg);
-      let(&str1,"");
-      for (i = 0; i < k; i++) {
-        if (instr(1, g_fullArg[i], " ") || instr(1, g_fullArg[i], "\t")
-            || instr(1, g_fullArg[i], "\"") || instr(1, g_fullArg[i], "'")
-            || ((char *)(g_fullArg[i]))[0] == 0) {
-          /* If the argument has spaces or tabs or quotes
-             or is empty string, put quotes around it */
-          if (instr(1, g_fullArg[i], "\"")) {
-            let(&str1, cat(str1, "'", g_fullArg[i], "' ", NULL));
-          } else {
-            /* (???Case of both ' and " is not handled) */
-            let(&str1, cat(str1, "\"", g_fullArg[i], "\" ", NULL));
-          }
-        } else {
-          let(&str1, cat(str1, g_fullArg[i], " ", NULL));
-        }
-      }
-      let(&str1, left(str1, (long)(strlen(str1)) - 1)); /* Trim trailing spc */
-      if (g_toolsMode && g_listFile_fp != NULL) {
-        /* Put line in list.tmp as command */
-        fprintf(g_listFile_fp, "%s\n", str1);  /* Print to list command file */
-      }
-      if (g_commandEcho) {
-        /* 15-Jun-2009 nm Added code line below */
-        /* Put special character "!" before line for easier extraction to
-           build SUBMIT files; see also SET ECHO ON output below */
-        let(&str1, cat("!", str1, NULL));
-        /* The tilde is a special flag for printLongLine to print a
-           tilde before the carriage return in a split line, not after */
-        printLongLine(str1, "~", " ");
-      }
-    }
-
-    if (cmdMatches("BEEP") || cmdMatches("B")) {
-      /* Print a bell (if user types ahead "B", the bell lets him know when
-         his command is finished - useful for long-running commands */
-      print2("%c",7);
-      continue;
-    }
-
-    if (cmdMatches("HELP")) {
-      /* Build the complete command */
-      k = pntrLen(g_fullArg);
-      let(&str1,"");
-      for (i = 0; i < k; i++) {
-        let(&str1, cat(str1, g_fullArg[i], " ", NULL));
-      }
-      let(&str1, left(str1, (long)(strlen(str1)) - 1));
-      if (g_toolsMode) {
-        help0(str1);
-        help1(str1);
-      } else {
-        help1(str1);
-        help2(str1);
-        help3(str1); /* 18-Jul-2015 nm */
-      }
-      continue;
-    }
-
-
-    if (cmdMatches("SET SCROLL")) {
-      if (cmdMatches("SET SCROLL CONTINUOUS")) {
-        defaultScrollMode = 0;
-        g_scrollMode = 0;
-        print2("Continuous scrolling is now in effect.\n");
-      } else {
-        defaultScrollMode = 1;
-        g_scrollMode = 1;
-        print2("Prompted scrolling is now in effect.\n");
-      }
-      continue;
-    }
-
-    if (cmdMatches("EXIT") || cmdMatches("QUIT")
-        || cmdMatches("_EXIT_PA")) { /* 9-Jun-2016 - for MM-PA> exit
-            in scripts, so it will error out in MM> (if for some reason
-            MM-PA wasn't entered) instead of exiting metamath */
-    /*???        || !strcmp(cmd,"^Z")) { */
-
-      /* 9-Jun-2016 */
-      if (cmdMatches("_EXIT_PA")) {
-        if (!g_PFASmode || (g_toolsMode && !g_listMode)) bug(1127);
-                 /* mmcmdl.c should have caught this */
-      }
-
-      if (g_toolsMode && !g_listMode) {
-        /* Quitting tools command from within Metamath */
-        if (!g_PFASmode) {
-          print2(
- "Exiting the Text Tools.  Type EXIT again to exit Metamath.\n");
-        } else {
-          print2(
- "Exiting the Text Tools.  Type EXIT again to exit the Proof Assistant.\n");
-        }
-        g_toolsMode = 0;
-        continue;
-      }
-
-      if (g_PFASmode) {
-
-        if (g_proofChanged &&
-              /* If g_proofChanged, but the UNDO stack is empty (and
-                 there were no other conditions such as stack overflow),
-                 the proof didn't really change, so it is safe to
-                 exit MM-PA without warning */
-              (processUndoStack(NULL, PUS_GET_STATUS, "", 0)
-                 /* However, if the proof was saved earlier, UNDO stack
-                    empty no longer indicates proof didn't change */
-                 || proofSavedFlag)) {
-          print2(
-              "Warning:  You have not saved changes to the proof of \"%s\".\n",
-              g_Statement[g_proveStatement].labelName); /* 21-Jan-06 nm */
-          /* 17-Aug-04 nm Added / FORCE qualifier */
-          if (switchPos("/ FORCE") == 0) {
-            str1 = cmdInput1("Do you want to EXIT anyway (Y, N) <N>? ");
-            if (str1[0] != 'y' && str1[0] != 'Y') {
-              print2("Use SAVE NEW_PROOF to save the proof.\n");
-              continue;
-            }
-          } else {
-            /* User specified / FORCE, so answer question automatically */
-            print2("Do you want to EXIT anyway (Y, N) <N>? Y\n");
-          }
-        }
-
-        g_proofChanged = 0;
-        processUndoStack(NULL, PUS_INIT, "", 0);
-        proofSavedFlag = 0; /* Will become 1 if proof is ever saved */
-
-        print2(
- "Exiting the Proof Assistant.  Type EXIT again to exit Metamath.\n");
-
-        /* Deallocate proof structure */
-        deallocProofStruct(&g_ProofInProgress); /* 20-May-2013 nm */
-
-        /**** old deallocation before 20-May-2013
-        i = nmbrLen(g_ProofInProgress.proof);
-        nmbrLet(&g_ProofInProgress.proof, NULL_NMBRSTRING);
-        for (j = 0; j < i; j++) {
-          nmbrLet((nmbrString **)(&((g_ProofInProgress.target)[j])),
-              NULL_NMBRSTRING);
-          nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[j])),
-              NULL_NMBRSTRING);
-          nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[j])),
-              NULL_NMBRSTRING);
-        }
-        pntrLet(&g_ProofInProgress.target, NULL_PNTRSTRING);
-        pntrLet(&g_ProofInProgress.source, NULL_PNTRSTRING);
-        pntrLet(&g_ProofInProgress.user, NULL_PNTRSTRING);
-        *** end of old code before 20-May-2013 */
-
-        g_PFASmode = 0;
-        continue;
-      } else {
-        if (g_sourceChanged) {
-          print2("Warning:  You have not saved changes to the source.\n");
-          /* 17-Aug-04 nm Added / FORCE qualifier */
-          if (switchPos("/ FORCE") == 0) {
-            str1 = cmdInput1("Do you want to EXIT anyway (Y, N) <N>? ");
-            if (str1[0] != 'y' && str1[0] != 'Y') {
-              print2("Use WRITE SOURCE to save the changes.\n");
-              continue;
-            }
-          } else {
-            /* User specified / FORCE, so answer question automatically */
-            print2("Do you want to EXIT anyway (Y, N) <N>? Y\n");
-          }
-          g_sourceChanged = 0;
-        }
-
-        if (g_texFileOpenFlag) {
-          print2("The %s file \"%s\" was closed.\n",
-              g_htmlFlag ? "HTML" : "LaTeX", g_texFileName);
-          printTexTrailer(texHeaderFlag);
-          fclose(g_texFilePtr);
-          g_texFileOpenFlag = 0;
-        }
-        if (g_logFileOpenFlag) {
-          print2("The log file \"%s\" was closed %s %s.\n",g_logFileName,
-              date(),time_());
-          fclose(g_logFilePtr);
-          g_logFileOpenFlag = 0;
-        }
-
-        /* 4-May-2017 Ari Ferrera */
-        /* Free remaining allocations before exiting */
-        freeCommandLine();
-        freeInOu();
-        memFreePoolPurge(0);
-        eraseSource();
-        freeData(); /* Call AFTER eraseSource()(->initBigArrays->malloc) */
-        let(&g_commandPrompt,"");
-        let(&g_commandLine,"");
-        let(&g_input_fn,"");
-        let(&g_contributorName, ""); /* 14-May-2017 nm */
-
-        return; /* Exit from program */
-      }
-    }
-
-    if (cmdMatches("SUBMIT")) {
-      if (g_commandFileNestingLevel == MAX_COMMAND_FILE_NESTING) {
-        printf("?The SUBMIT nesting level has been exceeded.\n");
-        continue;
-      }
-      g_commandFilePtr[g_commandFileNestingLevel + 1] = fSafeOpen(g_fullArg[1], "r",
-          0/*noVersioningFlag*/);
-      if (!g_commandFilePtr[g_commandFileNestingLevel + 1]) continue;
-                                      /* Couldn't open (err msg was provided) */
-      g_commandFileNestingLevel++;
-      g_commandFileName[g_commandFileNestingLevel] = ""; /* Initialize if nec. */
-      let(&g_commandFileName[g_commandFileNestingLevel], g_fullArg[1]);
-
-      /* 23-Oct-2006 nm Added / SILENT */
-      g_commandFileSilent[g_commandFileNestingLevel] = 0;
-      if (switchPos("/ SILENT")
-          || g_commandFileSilentFlag /* Propagate silence from outer level */) {
-        g_commandFileSilent[g_commandFileNestingLevel] = 1;
-      } else {
-        g_commandFileSilent[g_commandFileNestingLevel] = 0;
-      }
-      g_commandFileSilentFlag = g_commandFileSilent[g_commandFileNestingLevel];
-      if (!g_commandFileSilentFlag)
-        print2("Taking command lines from file \"%s\"...\n",
-            g_commandFileName[g_commandFileNestingLevel]);
-
-      continue;
-    }
-
-    if (g_toolsMode) {
-      /* Start of g_toolsMode-specific commands */
-#define ADD_MODE 1
-#define DELETE_MODE 2
-#define CLEAN_MODE 3
-#define SUBSTITUTE_MODE 4
-#define SWAP_MODE 5
-#define INSERT_MODE 6
-#define BREAK_MODE 7
-#define BUILD_MODE 8
-#define MATCH_MODE 9
-#define RIGHT_MODE 10
-#define TAG_MODE 11  /* 2-Jul-2011 nm Added TAG command */
-      cmdMode = 0;
-      if (cmdMatches("ADD")) cmdMode = ADD_MODE;
-      else if (cmdMatches("DELETE")) cmdMode = DELETE_MODE;
-      else if (cmdMatches("CLEAN")) cmdMode = CLEAN_MODE;
-      else if (cmdMatches("SUBSTITUTE") || cmdMatches("S"))
-        cmdMode = SUBSTITUTE_MODE;
-      else if (cmdMatches("SWAP")) cmdMode = SWAP_MODE;
-      else if (cmdMatches("INSERT")) cmdMode = INSERT_MODE;
-      else if (cmdMatches("BREAK")) cmdMode = BREAK_MODE;
-      else if (cmdMatches("BUILD")) cmdMode = BUILD_MODE;
-      else if (cmdMatches("MATCH")) cmdMode = MATCH_MODE;
-      else if (cmdMatches("RIGHT")) cmdMode = RIGHT_MODE;
-      else if (cmdMatches("TAG")) cmdMode = TAG_MODE;
-      if (cmdMode) {
-        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
-        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
-        if (cmdMode == RIGHT_MODE) {
-          /* Find the longest line */
-          p = 0;
-          while (linput(list1_fp, NULL, &str1)) {
-            if (p < (signed)(strlen(str1))) p = (long)(strlen(str1));
-          }
-          rewind(list1_fp);
-        }
-        let(&list2_fname, g_fullArg[1]);
-        if (list2_fname[strlen(list2_fname) - 2] == '~') {
-          let(&list2_fname, left(list2_fname, (long)(strlen(list2_fname)) - 2));
-          print2("The output file will be called %s.\n", list2_fname);
-        }
-        let(&list2_ftmpname, "");
-        list2_ftmpname = fGetTmpName("zz~tools");
-        list2_fp = fSafeOpen(list2_ftmpname, "w", 0/*noVersioningFlag*/);
-        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
-        lines = 0;
-        changedLines = 0;
-        twoMatches = 0;
-        changedFlag = 0;
-        outMsgFlag = 0;
-        switch (cmdMode) {
-          case ADD_MODE:
-            break;
-          case TAG_MODE:  /* 2-Jul-2011 nm Added TAG command */
-            let(&tagStartMatch, g_fullArg[4]);
-            tagStartCount = (long)val(g_fullArg[5]);
-            if (tagStartCount == 0) tagStartCount = 1; /* Default */
-            let(&tagEndMatch, g_fullArg[6]);
-            tagEndCount = (long)val(g_fullArg[7]);
-            if (tagEndCount == 0) tagEndCount = 1; /* Default */
-            tagStartCounter = 0;
-            tagEndCounter = 0;
-            break;
-          case DELETE_MODE:
-            break;
-          case CLEAN_MODE:
-            let(&str4, edit(g_fullArg[2], 32));
-            q = 0;
-            if (instr(1, str4, "P") > 0) q = q + 1;
-            if (instr(1, str4, "D") > 0) q = q + 2;
-            if (instr(1, str4, "G") > 0) q = q + 4;
-            if (instr(1, str4, "B") > 0) q = q + 8;
-            if (instr(1, str4, "R") > 0) q = q + 16;
-            if (instr(1, str4, "C") > 0) q = q + 32;
-            if (instr(1, str4, "E") > 0) q = q + 128;
-            if (instr(1, str4, "Q") > 0) q = q + 256;
-            if (instr(1, str4, "L") > 0) q = q + 512;
-            if (instr(1, str4, "T") > 0) q = q + 1024;
-            if (instr(1, str4, "U") > 0) q = q + 2048;
-            if (instr(1, str4, "V") > 0) q = q + 4096;
-            break;
-          case SUBSTITUTE_MODE:
-            let(&newstr, g_fullArg[3]); /* The replacement string */
-            if (((vstring)(g_fullArg[4]))[0] == 'A' ||
-                ((vstring)(g_fullArg[4]))[0] == 'a') { /* ALL */
-              q = -1;
-            } else {
-              q = (long)val(g_fullArg[4]);
-              if (q == 0) q = 1;    /* The occurrence # of string to subst */
-            }
-            s = instr(1, g_fullArg[2], "\\n");
-            if (s) {
-              /*s = 1;*/ /* Replace lf flag */
-              q = 1; /* Only 1st occurrence makes sense in this mode */
-            }
-            if (!strcmp(g_fullArg[3], "\\n")) {
-              let(&newstr, "\n"); /* Replace with lf */
-            }
-            break;
-          case SWAP_MODE:
-            break;
-          case INSERT_MODE:
-            p = (long)val(g_fullArg[3]);
-            break;
-          case BREAK_MODE:
-            outMsgFlag = 1;
-            break;
-          case BUILD_MODE:
-            let(&str4, "");
-            outMsgFlag = 1;
-            break;
-          case MATCH_MODE:
-            outMsgFlag = 1;
-        } /* End switch */
-        let(&bufferedLine, "");
-        /*
-        while (linput(list1_fp, NULL, &str1)) {
-        */
-        while (1) {
-          if (bufferedLine[0]) {
-            /* Get input from buffered line (from rejected \n replacement) */
-            let(&str1, bufferedLine);
-            let(&bufferedLine, "");
-          } else {
-            if (!linput(list1_fp, NULL, &str1)) break;
-          }
-          lines++;
-          oldChangedLines = changedLines;
-          let(&str2, str1);
-          switch (cmdMode) {
-            case ADD_MODE:
-              let(&str2, cat(g_fullArg[2], str1, g_fullArg[3], NULL));
-              if (strcmp(str1, str2)) changedLines++;
-              break;
-            case TAG_MODE:   /* 2-Jul-2011 nm Added TAG command */
-              if (tagStartCounter < tagStartCount) {
-                if (instr(1, str1, tagStartMatch)) tagStartCounter++;
-              }
-              if (tagStartCounter == tagStartCount &&
-                  tagEndCounter < tagEndCount) { /* We're in tagging range */
-                let(&str2, cat(g_fullArg[2], str1, g_fullArg[3], NULL));
-                if (strcmp(str1, str2)) changedLines++;
-                if (instr(1, str1, tagEndMatch)) tagEndCounter++;
-              }
-              break;
-            case DELETE_MODE:
-              p1 = instr(1, str1, g_fullArg[2]);
-              if (strlen(g_fullArg[2]) == 0) p1 = 1;
-              p2 = instr(p1, str1, g_fullArg[3]);
-              if (strlen(g_fullArg[3]) == 0) p2 = (long)strlen(str1) + 1;
-              if (p1 != 0 && p2 != 0) {
-                let(&str2, cat(left(str1, p1 - 1), right(str1, p2
-                    + (long)strlen(g_fullArg[3])), NULL));
-                changedLines++;
-              }
-              break;
-            case CLEAN_MODE:
-              if (q) {
-                let(&str2, edit(str1, q));
-                if (strcmp(str1, str2)) changedLines++;
-              }
-              break;
-            case SUBSTITUTE_MODE:
-              let(&str2, str1);
-              p = 0;
-              p1 = 0;
-
-              k = 1;
-              /* See if an additional match on line is required */
-              if (((vstring)(g_fullArg[5]))[0] != 0) {
-                if (!instr(1, str2, g_fullArg[5])) {
-                  /* No match on line; prevent any substitution */
-                  k = 0;
-                }
-              }
-
-              if (s && k) { /* We're asked to replace a newline char */
-                /* Read in the next line */
-                /*
-                if (linput(list1_fp, NULL, &str4)) {
-                  let(&str2, cat(str1, "\\n", str4, NULL));
-                */
-                if (linput(list1_fp, NULL, &bufferedLine)) {
-                  /* Join the next line and see if the string matches */
-                  if (instr(1, cat(str1, "\\n", bufferedLine, NULL),
-                      g_fullArg[2])) {
-                    let(&str2, cat(str1, "\\n", bufferedLine, NULL));
-                    let(&bufferedLine, "");
-                  } else {
-                    k = 0; /* No match - leave bufferedLine for next pass */
-                  }
-                } else { /* EOF reached */
-                  print2("Warning: file %s has an odd number of lines\n",
-                      g_fullArg[1]);
-                }
-              }
-
-              while (k) {
-                p1 = instr(p1 + 1, str2, g_fullArg[2]);
-                if (!p1) break;
-                p++;
-                if (p == q || q == -1) {
-                  let(&str2, cat(left(str2, p1 - 1), newstr,
-                      right(str2, p1 + (long)strlen(g_fullArg[2])), NULL));
-                  if (newstr[0] == '\n') {
-                    /* Replacement string is an lf */
-                    lines++;
-                    changedLines++;
-                  }
-                  /* 14-Sep-2010 nm Continue the search after the replacement
-                     string, so that "SUBST 1.tmp abbb ab a ''" will change
-                     "abbbab" to "abba" rather than "aa" */
-                  p1 = p1 + (long)strlen(newstr) - 1;
-                  /* p1 = p1 - (long)strlen(g_fullArg[2]) + (long)strlen(newstr); */ /* bad */
-                  if (q != -1) break;
-                }
-              }
-              if (strcmp(str1, str2)) changedLines++;
-              break;
-            case SWAP_MODE:
-              p1 = instr(1, str1, g_fullArg[2]);
-              if (p1) {
-                p2 = instr(p1 + 1, str1, g_fullArg[2]);
-                if (p2) twoMatches++;
-                let(&str2, cat(right(str1, p1) + (long)strlen(g_fullArg[2]),
-                    g_fullArg[2], left(str1, p1 - 1), NULL));
-                if (strcmp(str1, str2)) changedLines++;
-              }
-              break;
-            case INSERT_MODE:
-              if ((signed)(strlen(str2)) < p - 1)
-                let(&str2, cat(str2, space(p - 1 - (long)strlen(str2)), NULL));
-              let(&str2, cat(left(str2, p - 1), g_fullArg[2],
-                  right(str2, p), NULL));
-              if (strcmp(str1, str2)) changedLines++;
-              break;
-            case BREAK_MODE:
-              let(&str2, str1);
-              changedLines++;
-              for (i = 0; i < (signed)(strlen(g_fullArg[2])); i++) {
-                p = 0;
-                while (1) {
-                  p = instr(p + 1, str2, chr(((vstring)(g_fullArg[2]))[i]));
-                  if (!p) break;
-                  /* Put spaces arount special one-char tokens */
-                  let(&str2, cat(left(str2, p - 1), " ",
-                      mid(str2, p, 1),
-                      " ", right(str2, p + 1), NULL));
-                  /*p++;*/
-                  /* Even though space is always a separator, it can be used
-                     to suppress all default tokens.  Go past 2nd space to prevent
-                     infinite loop in that case. */
-                  p += 2; /* 3-Jul-2020 nm */
-                }
-              }
-              let(&str2, edit(str2, 8 + 16 + 128)); /* Reduce & trim spaces */
-              for (p = (long)strlen(str2) - 1; p >= 0; p--) {
-                if (str2[p] == ' ') {
-                  str2[p] = '\n';
-                  changedLines++;
-                }
-              }
-              if (!str2[0]) changedLines--; /* Don't output blank line */
-              break;
-            case BUILD_MODE:
-              if (str2[0] != 0) { /* Ignore blank lines */
-                if (str4[0] == 0) {
-                  let(&str4, str2);
-                } else {
-                  if ((long)strlen(str4) + (long)strlen(str2) > 72) {
-                    let(&str4, cat(str4, "\n", str2, NULL));
-                    changedLines++;
-                  } else {
-                    let(&str4, cat(str4, " ", str2, NULL));
-                  }
-                }
-                p = instr(1, str4, "\n");
-                if (p) {
-                  let(&str2, left(str4, p - 1));
-                  let(&str4, right(str4, p + 1));
-                } else {
-                  let(&str2, "");
-                }
-              }
-              break;
-            case MATCH_MODE:
-              if (((vstring)(g_fullArg[2]))[0] == 0) {
-                /* Match any non-blank line */
-                p = str1[0];
-              } else {
-                p = instr(1, str1, g_fullArg[2]);
-              }
-              if (((vstring)(g_fullArg[3]))[0] == 'n' ||
-                  ((vstring)(g_fullArg[3]))[0] == 'N') {
-                p = !p;
-              }
-              if (p) changedLines++;
-              break;
-            case RIGHT_MODE:
-              let(&str2, cat(space(p - (long)strlen(str2)), str2, NULL));
-              if (strcmp(str1, str2)) changedLines++;
-              break;
-          } /* End switch(cmdMode) */
-          if (lines == 1) let(&str3, left(str2, 79)); /* For msg */
-          if (oldChangedLines != changedLines && !changedFlag) {
-            changedFlag = 1;
-            let(&str3, left(str2, 79)); /* For msg */
-            firstChangedLine = lines;
-            if ((cmdMode == SUBSTITUTE_MODE && newstr[0] == '\n')
-                || cmdMode == BUILD_MODE) /* Joining lines */ {
-              firstChangedLine = 1; /* Better message */
-            }
-          }
-          if (((cmdMode != BUILD_MODE && cmdMode != BREAK_MODE)
-              || str2[0] != 0)
-              && (cmdMode != MATCH_MODE || p))
-            fprintf(list2_fp, "%s\n", str2);
-        } /* Next input line */
-        if (cmdMode == BUILD_MODE) {
-          if (str4[0]) {
-            /* Output last partial line */
-            fprintf(list2_fp, "%s\n", str4);
-            changedLines++;
-            if (!str3[0]) {
-              let(&str3, str4); /* For msg */
-            }
-          }
-        }
-        /* Remove any lines after lf for readability of msg */
-        p = instr(1, str3, "\n");
-        if (p) let(&str3, left(str3, p - 1));
-        if (!outMsgFlag) {
-          /* 18-Aug-2011 nm Make message depend on line counts */
-          if (!changedFlag) {
-            if (!lines) {
-              print2("The file %s has no lines.\n", g_fullArg[1]);
-            } else {
-              print2(
-"The file %s has %ld line%s; none were changed.  First line:\n",
-                list2_fname, lines, (lines == 1) ? "" : "s");
-              print2("%s\n", str3);
-            }
-          } else {
-            print2(
-"The file %s has %ld line%s; %ld w%s changed.  First changed line is %ld:\n",
-                list2_fname,
-                lines,  (lines == 1) ? "" : "s",
-                changedLines,  (changedLines == 1) ? "as" : "ere",
-                firstChangedLine);
-            print2("%s\n", str3);
-          }
-          if (twoMatches > 0) {
-            /* For SWAP command */
-            print2(
-"Warning:  %ld line%s more than one \"%s\".  The first one was used.\n",
-                twoMatches, (twoMatches == 1) ? " has" : "s have", g_fullArg[2]);
-          }
-        } else {
-          /* if (changedLines == 0) let(&str3, ""); */
-          print2(
-"The input had %ld line%s, the output has %ld line%s.%s\n",
-              lines, (lines == 1) ? "" : "s",
-              changedLines, (changedLines == 1) ? "" : "s",
-              (changedLines == 0) ? "" : " First output line:");
-          if (changedLines != 0) print2("%s\n", str3);
-        }
-        fclose(list1_fp);
-        fclose(list2_fp);
-        fSafeRename(list2_ftmpname, list2_fname);
-        /* Deallocate string memory */
-        let(&tagStartMatch, "");  /* 2-Jul-2011 nm Added TAG command */
-        let(&tagEndMatch, "");  /* 2-Jul-2011 nm Added TAG command */
-        continue;
-      } /* end if cmdMode for ADD, etc. */
-
-#define SORT_MODE 1
-#define UNDUPLICATE_MODE 2
-#define DUPLICATE_MODE 3
-#define UNIQUE_MODE 4
-#define REVERSE_MODE 5
-      cmdMode = 0;
-      if (cmdMatches("SORT")) cmdMode = SORT_MODE;
-      else if (cmdMatches("UNDUPLICATE")) cmdMode = UNDUPLICATE_MODE;
-      else if (cmdMatches("DUPLICATE")) cmdMode = DUPLICATE_MODE;
-      else if (cmdMatches("UNIQUE")) cmdMode = UNIQUE_MODE;
-      else if (cmdMatches("REVERSE")) cmdMode = REVERSE_MODE;
-      if (cmdMode) {
-        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
-        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
-        let(&list2_fname, g_fullArg[1]);
-        if (list2_fname[strlen(list2_fname) - 2] == '~') {
-          let(&list2_fname, left(list2_fname, (long)strlen(list2_fname) - 2));
-          print2("The output file will be called %s.\n", list2_fname);
-        }
-        let(&list2_ftmpname, "");
-        list2_ftmpname = fGetTmpName("zz~tools");
-        list2_fp = fSafeOpen(list2_ftmpname, "w", 0/*noVersioningFlag*/);
-        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
-
-        /* Count the lines */
-        lines = 0;
-        while (linput(list1_fp, NULL, &str1)) lines++;
-        if (cmdMode != SORT_MODE  && cmdMode != REVERSE_MODE) {
-          print2("The input file has %ld lines.\n", lines);
-        }
-
-        /* Close and reopen the input file */
-        fclose(list1_fp);
-        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
-        /* Allocate memory */
-        pntrLet(&pntrTmp, pntrSpace(lines));
-        /* Assign the lines to string array */
-        for (i = 0; i < lines; i++) linput(list1_fp, NULL,
-            (vstring *)(&pntrTmp[i]));
-
-        /* Sort */
-        if (cmdMode != REVERSE_MODE) {
-          if (cmdMode == SORT_MODE) {
-            g_qsortKey = g_fullArg[2]; /* Do not deallocate! */
-          } else {
-            g_qsortKey = "";
-          }
-          qsort(pntrTmp, (size_t)lines, sizeof(void *), qsortStringCmp);
-        } else { /* Reverse the lines */
-          for (i = lines / 2; i < lines; i++) {
-            g_qsortKey = pntrTmp[i]; /* Use g_qsortKey as handy tmp var here */
-            pntrTmp[i] = pntrTmp[lines - 1 - i];
-            pntrTmp[lines - 1 - i] = g_qsortKey;
-          }
-        }
-
-        /* Output sorted lines */
-        changedLines = 0;
-        let(&str3, "");
-        for (i = 0; i < lines; i++) {
-          j = 0; /* Flag that line should be printed */
-          switch (cmdMode) {
-            case SORT_MODE:
-            case REVERSE_MODE:
-              j = 1;
-              break;
-            case UNDUPLICATE_MODE:
-              if (i == 0) {
-                j = 1;
-              } else {
-                if (strcmp((vstring)(pntrTmp[i - 1]), (vstring)(pntrTmp[i]))) {
-                  j = 1;
-                }
-              }
-              break;
-            case DUPLICATE_MODE:
-              if (i > 0) {
-                if (!strcmp((vstring)(pntrTmp[i - 1]), (vstring)(pntrTmp[i]))) {
-                  if (i == lines - 1) {
-                    j = 1;
-                  } else {
-                    if (strcmp((vstring)(pntrTmp[i]),
-                        (vstring)(pntrTmp[i + 1]))) {
-                      j = 1;
-                    }
-                  }
-                }
-              }
-              break;
-            case UNIQUE_MODE:
-              if (i < lines - 1) {
-                if (strcmp((vstring)(pntrTmp[i]), (vstring)(pntrTmp[i + 1]))) {
-                  if (i == 0) {
-                    j = 1;
-                  } else {
-                    if (strcmp((vstring)(pntrTmp[i - 1]),
-                        (vstring)(pntrTmp[i]))) {
-                      j = 1;
-                    }
-                  }
-                }
-              } else {
-                if (i == 0) {
-                  j = 1;
-                } else {
-                  if (strcmp((vstring)(pntrTmp[i - 1]),
-                        (vstring)(pntrTmp[i]))) {
-                      j = 1;
-                  }
-                }
-              }
-              break;
-          } /* end switch (cmdMode) */
-          if (j) {
-            fprintf(list2_fp, "%s\n", (vstring)(pntrTmp[i]));
-            changedLines++;
-            if (changedLines == 1)
-              let(&str3, left((vstring)(pntrTmp[i]), 79));
-          }
-        } /* next i */
-        print2("The output file has %ld lines.  The first line is:\n",
-            changedLines);
-        print2("%s\n", str3);
-
-        /* Deallocate memory */
-        for (i = 0; i < lines; i++) let((vstring *)(&pntrTmp[i]), "");
-        pntrLet(&pntrTmp,NULL_PNTRSTRING);
-
-        fclose(list1_fp);
-        fclose(list2_fp);
-        fSafeRename(list2_ftmpname, list2_fname);
-        continue;
-      } /* end if cmdMode for SORT, etc. */
-
-      if (cmdMatches("PARALLEL")) {
-        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
-        list2_fp = fSafeOpen(g_fullArg[2], "r", 0/*noVersioningFlag*/);
-        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
-        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
-        let(&list3_ftmpname, "");
-        list3_ftmpname = fGetTmpName("zz~tools");
-        list3_fp = fSafeOpen(list3_ftmpname, "w", 0/*noVersioningFlag*/);
-        if (!list3_fp) continue; /* Couldn't open it (error msg was provided) */
-
-        p1 = 1; p2 = 1; /* not eof */
-        p = 0; q = 0; /* lines */
-        j = 0; /* 1st line flag */
-        let(&str3, "");
-        while (1) {
-          let(&str1, "");
-          if (p1) {
-            p1 = linput(list1_fp, NULL, &str1);
-            if (p1) p++;
-            else let(&str1, "");
-          }
-          let(&str2, "");
-          if (p2) {
-            p2 = linput(list2_fp, NULL, &str2);
-            if (p2) q++;
-            else let(&str2, "");
-          }
-          if (!p1 && !p2) break;
-          let(&str4, cat(str1, g_fullArg[4], str2, NULL));
-          fprintf(list3_fp, "%s\n", str4);
-          if (!j) {
-            let(&str3, str4); /* Save 1st line for msg */
-            j = 1;
-          }
-        }
-        if (p == q) {
-          print2(
-"The input files each had %ld lines.  The first output line is:\n", p);
-        } else {
-          print2(
-"Warning: file \"%s\" had %ld lines while file \"%s\" had %ld lines.\n",
-              g_fullArg[1], p, g_fullArg[2], q);
-          if (p < q) p = q;
-          print2("The output file \"%s\" has %ld lines.  The first line is:\n",
-              g_fullArg[3], p);
-        }
-        print2("%s\n", str3);
-
-        fclose(list1_fp);
-        fclose(list2_fp);
-        fclose(list3_fp);
-        fSafeRename(list3_ftmpname, g_fullArg[3]);
-        continue;
-      }
-
-
-      if (cmdMatches("NUMBER")) {
-        list1_fp = fSafeOpen(g_fullArg[1], "w", 0/*noVersioningFlag*/);
-        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
-        j = (long)strlen(str(val(g_fullArg[2])));
-        k = (long)strlen(str(val(g_fullArg[3])));
-        if (k > j) j = k;
-        for (i = (long)val(g_fullArg[2]); i <= val(g_fullArg[3]);
-            i = i + (long)val(g_fullArg[4])) {
-          let(&str1, str((double)i));
-          fprintf(list1_fp, "%s\n", str1);
-        }
-        fclose(list1_fp);
-        continue;
-      }
-
-      if (cmdMatches("COUNT")) {
-        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
-        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
-        p1 = 0;
-        p2 = 0;
-        lines = 0;
-        q = 0; /* Longest line length */
-        i = 0; /* First longest line */
-        j = 0; /* Number of longest lines */
-        sum = 0.0; /* Sum of numeric content of lines */
-        firstChangedLine = 0;
-        while (linput(list1_fp, NULL, &str1)) {
-          lines++;
-
-          /* Longest line */
-          if (q < (signed)(strlen(str1))) {
-            q = (long)strlen(str1);
-            let(&str4, str1);
-            i = lines;
-            j = 0;
-          }
-
-          if (q == (signed)(strlen(str1))) {
-            j++;
-          }
-
-          if (instr(1, str1, g_fullArg[2])) {
-            if (!firstChangedLine) {
-              firstChangedLine = lines;
-              let(&str3, str1);
-            }
-            p1++;
-            p = 0;
-            while (1) {
-              p = instr(p + 1, str1, g_fullArg[2]);
-              if (!p) break;
-              p2++;
-            }
-          }
-          sum = sum + val(str1);
-        }
-        print2(
-"The file has %ld lines.  The string \"%s\" occurs %ld times on %ld lines.\n",
-            lines, g_fullArg[2], p2, p1);
-        if (firstChangedLine) {
-          print2("The first occurrence is on line %ld:\n", firstChangedLine);
-          print2("%s\n", str3);
-        }
-        print2(
-"The first longest line (out of %ld) is line %ld and has %ld characters:\n",
-            j, i, q);
-        printLongLine(str4, "    "/*startNextLine*/, ""/*breakMatch*/);
-            /* breakMatch empty means break line anywhere */  /* 6-Dec-03 */
- /* print2("If each line were a number, their sum would be %s\n", str((double)sum)); */
-        printLongLine(cat(
-            "Stripping all but digits, \".\", and \"-\", the sum of lines is ",
-            str((double)sum), NULL), "    ", " ");
-        fclose(list1_fp);
-        continue;
-      }
-
-      if (cmdMatches("TYPE") || cmdMatches("T")) {
-        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
-        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
-        if (g_rawArgs == 2) {
-          n = 10;
-        } else {
-          if (((vstring)(g_fullArg[2]))[0] == 'A' ||
-              ((vstring)(g_fullArg[2]))[0] == 'a') { /* ALL */
-            n = -1;
-          } else {
-            n = (long)val(g_fullArg[2]);
-          }
-        }
-        for (i = 0; i < n || n == -1; i++) {
-          if (!linput(list1_fp, NULL, &str1)) break;
-          if (!print2("%s\n", str1)) break;
-        }
-        fclose(list1_fp);
-        continue;
-      } /* end TYPE */
-
-      if (cmdMatches("UPDATE")) {
-        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
-        list2_fp = fSafeOpen(g_fullArg[2], "r", 0/*noVersioningFlag*/);
-        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
-        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
-        if (!getRevision(g_fullArg[4])) {
-          print2(
-"?The revision tag must be of the form /*nn*/ or /*#nn*/.  Please try again.\n");
-          continue;
-        }
-        let(&list3_ftmpname, "");
-        list3_ftmpname = fGetTmpName("zz~tools");
-        list3_fp = fSafeOpen(list3_ftmpname, "w", 0/*noVersioningFlag*/);
-        if (!list3_fp) continue; /* Couldn't open it (error msg was provided) */
-
-        revise(list1_fp, list2_fp, list3_fp, g_fullArg[4],
-            (long)val(g_fullArg[5]));
-
-        fSafeRename(list3_ftmpname, g_fullArg[3]);
-        continue;
-      }
-
-      if (cmdMatches("COPY") || cmdMatches("C")) {
-        let(&list2_ftmpname, "");
-        list2_ftmpname = fGetTmpName("zz~tools");
-        list2_fp = fSafeOpen(list2_ftmpname, "w", 0/*noVersioningFlag*/);
-        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
-        let(&str4, cat(g_fullArg[1], ",", NULL));
-        lines = 0;
-        j = 0; /* Error flag */
-        while (1) {
-          if (!str4[0]) break; /* Done scanning list */
-          p = instr(1, str4, ",");
-          let(&str3, left(str4, p - 1));
-          let(&str4, right(str4, p + 1));
-          list1_fp = fSafeOpen((str3), "r", 0/*noVersioningFlag*/);
-          if (!list1_fp) { /* Couldn't open it (error msg was provided) */
-            j = 1; /* Error flag */
-            break;
-          }
-          n = 0;
-          while (linput(list1_fp, NULL, &str1)) {
-            lines++; n++;
-            fprintf(list2_fp, "%s\n", str1);
-          }
-          if (instr(1, g_fullArg[1], ",")) { /* More than 1 input file */
-            print2("The input file \"%s\" has %ld lines.\n", str3, n);
-          }
-          fclose(list1_fp);
-        }
-        if (j) continue; /* One of the input files couldn't be opened */
-        fclose(list2_fp);
-        print2("The output file \"%s\" has %ld lines.\n", g_fullArg[2], lines);
-        fSafeRename(list2_ftmpname, g_fullArg[2]);
-        continue;
-      }
-
-      print2("?This command has not been implemented yet.\n");
-      continue;
-    } /* End of g_toolsMode-specific commands */
-
-    if (cmdMatches("TOOLS")) {
-      print2(
-"Entering the Text Tools utilities.  Type HELP for help, EXIT to exit.\n");
-      g_toolsMode = 1;
-      continue;
-    }
-
-    if (cmdMatches("READ")) {
-      /*if (g_statements) {*/
-      /* 31-Dec-2017 nm */
-      /* We can't use 'statements > 0' for the test since the source
-         could be just a comment */
-      if (g_sourceHasBeenRead == 1) {
-        printLongLine(cat(
-            "?Sorry, reading of more than one source file is not allowed.  ",
-            "The file \"", g_input_fn, "\" has already been READ in.  ",
-                                                             /* 11-Dec-05 nm */
-            "You may type ERASE to start over.  Note that additional source ",
-        "files may be included in the source file with \"$[ <filename> $]\".",
-                                                             /* 11-Dec-05 nm */
-            NULL),"  "," ");
-        continue;
-      }
-
-      let(&g_input_fn, g_fullArg[1]);
-
-      /***** 3-Jan-2017 nm  This is now done with SET ROOT_DIRECTORY
-      /@ 31-Dec-2017 nm - Added ROOT_DIRECTORY switch @/
-      /@ TODO - remove this and just use SET ROOT_DIRECTORY instead @/
-      i = switchPos("/ ROOT_DIRECTORY"); /@ Statement match to skip @/
-      if (i != 0) {
-        let(&g_rootDirectory, edit(g_fullArg[i + 1], 2/@discard spaces,tabs@/));
-        if (g_rootDirectory[0] != 0) {  /@ Not an empty directory path @/
-          /@ Add trailing "/" to g_rootDirectory if missing @/
-          if (instr(1, g_rootDirectory, "\\") != 0
-              || instr(1, g_input_fn, "\\") != 0 ) {
-            /@ Using Windows-style path (not really supported, but at least
-               make full path consistent) @/
-            if (g_rootDirectory[strlen(g_rootDirectory) - 1] != '\\') {
-              let(&g_rootDirectory, cat(g_rootDirectory, "\\", NULL));
-            }
-          } else {
-            if (g_rootDirectory[strlen(g_rootDirectory) - 1] != '/') {
-              let(&g_rootDirectory, cat(g_rootDirectory, "/", NULL));
-            }
-          }
-        }
-      /@
-      } else {
-        let(&g_rootDirectory, "");
-      @/
-      }
-      */
-
-      let(&str1, cat(g_rootDirectory, g_input_fn, NULL));
-      g_input_fp = fSafeOpen(str1, "r", 0/*noVersioningFlag*/);
-      if (!g_input_fp) continue; /* Couldn't open it (error msg was provided
-                                     by fSafeOpen) */
-      fclose(g_input_fp);
-
-      readInput();
-      /*g_sourceHasBeenRead = 1;*/ /* Global variable - set in readInput() */
-
-      if (switchPos("/ VERIFY")) {
-        verifyProofs("*",1); /* Parse and verify */
-      } else {
-        /* verifyProofs("*",0); */ /* Parse only (for gross error checking) */
-      }
-
-      /* 13-Dec-2016 nm Moved to verifyMarkup in mmcmds.c */
-      /*
-      /@ 10/21/02 - detect Microsoft bugs reported by several users, when the
-         HTML output files are named "con.html" etc. @/
-      /@ If we want a standard error message underlining token, this could go
-         in mmpars.c @/
-      /@ From Microsoft's site:
-         "The following reserved words cannot be used as the name of a file:
-         CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7,
-         COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
-         Also, reserved words followed by an extension - for example,
-         NUL.tx7 - are invalid file names." @/
-      /@ Check for labels that will lead to illegal Microsoft file names for
-         Windows users.  Don't bother checking CLOCK$ since $ is already
-         illegal @/
-      let(&str1, cat(
-         ",CON,PRN,AUX,NUL,COM1,COM2,COM3,COM4,COM5,COM6,COM7,",
-         "COM8,COM9,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,", NULL));
-      for (i = 1; i <= g_statements; i++) {
-        let(&str2, cat(",", edit(g_Statement[i].labelName, 32/@uppercase@/), ",",
-            NULL));
-        if (instr(1, str1, str2) ||
-            /@ 5-Jan-04 mm@.html is reserved for mmtheorems.html, etc. @/
-            !strcmp(",MM", left(str2, 3))) {
-          print2("\n");
-          assignStmtFileAndLineNum(j); /@ 9-Jan-2018 nm @/
-          printLongLine(cat("?Warning in statement \"",
-              g_Statement[i].labelName, "\" at line ",
-              str((double)(g_Statement[i].lineNum)),
-              " in file \"", g_Statement[i].fileName,
-              "\".  To workaround a Microsoft operating system limitation, the",
-              " the following reserved words cannot be used for label names:",
-              " CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5,",
-              " COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6,",
-              " LPT7, LPT8, and LPT9.  Also, \"mm@.html\" is reserved for",
-              " Metamath file names.  Use another name for this label.", NULL),
-              "", " ");
-          g_errorCount++;
-        }
-      }
-      /@ 10/21/02 end @/
-      */
-
-      if (g_sourceHasBeenRead == 1) {  /* 9-Jan-2018 nm */
-        if (!g_errorCount) {
-          let(&str1, "No errors were found.");
-          if (!switchPos("/ VERIFY")) {
-              let(&str1, cat(str1,
-         "  However, proofs were not checked.  Type VERIFY PROOF *",
-         " if you want to check them.",
-              NULL));
-          }
-          printLongLine(str1, "", " ");
-        } else {
-          print2("\n");
-          if (g_errorCount == 1) {
-            print2("One error was found.\n");
-          } else {
-            print2("%ld errors were found.\n", (long)g_errorCount);
-          }
-        }
-      } /* g_sourceHasBeenRead == 1 */
-
-      continue;
-    }
-
-    if (cmdMatches("WRITE SOURCE")) {
-      let(&g_output_fn, g_fullArg[2]);
-
-      /********* Deleted 28-Dec-2013 nm Now opened in writeSource()
-      g_output_fp = fSafeOpen(g_output_fn, "w", 0/@noVersioningFlag@/);
-      if (!g_output_fp) continue; /@ Couldn't open it (error msg was provided)@/
-      ********/
-
-      /******* Deleted 3-May-2017 nm
-      /@ Added 24-Oct-03 nm @/
-      if (switchPos("/ CLEAN") > 0) {
-        c = 1; /@ Clean out any proof-in-progress (that user has flagged
-                        with a ? in its date comment field) @/
-      } else {
-        c = 0; /@ Output all proofs (normal) @/
-      }
-      *******/
-
-      /********* Deleted 3-May-2017 nm
-      writeSource((char)c, (char)r); /@ Added arg 24-Oct-03 nm 12-Jun-2011 nm @/
-      fclose(g_output_fp);
-      if (c == 0) g_sourceChanged = 0; /@ Don't unset flag if CLEAN option
-                                    since some new proofs may not be saved. @/
-      ***********/
-
-
-      /***** 3-Jan-2017 nm  This is now done with SET ROOT_DIRECTORY
-      /@ 31-Dec-2017 nm - Added ROOT_DIRECTORY switch @/
-      /@ TODO - remove this qualifier; too confusing.
-         Use SET ROOT_DIRECTORY instead. @/
-      i = switchPos("/ ROOT_DIRECTORY"); /@ Statement match to skip @/
-      if (i != 0) {
-        let(&g_rootDirectory, edit(g_fullArg[i + 1], 2/@discard spaces,tabs@/));
-        /@ Add trailing "/" to g_rootDirectory if missing @/
-        if (instr(1, g_rootDirectory, "\\") != 0
-            || instr(1, g_input_fn, "\\") != 0 ) {
-          /@ Using Windows-style path (not really supported, but at least
-             make full path consistent) @/
-          if (g_rootDirectory[strlen(g_rootDirectory) - 1] != '\\') {
-            let(&g_rootDirectory, cat(g_rootDirectory, "\\", NULL));
-          }
-        } else {
-          if (g_rootDirectory[strlen(g_rootDirectory) - 1] != '/') {
-            let(&g_rootDirectory, cat(g_rootDirectory, "/", NULL));
-          }
-        }
-      /@
-      } else {
-        let(&g_rootDirectory, "");
-      @/
-      }
-      */
-
-      /* Added 12-Jun-2011 nm */
-      if (switchPos("/ REWRAP") > 0) {
-        r = 2; /* Re-wrap then format (more aggressive than / FORMAT) */
-      } else if (switchPos("/ FORMAT") > 0) {
-        r = 1; /* Format output according to set.mm standard */
-      } else {
-        r = 0; /* Keep formatting as-is */
-      }
-      /* 30-Dec-2020 nm Temporarily disable / REWRAP until bug fixed */
-      if (r > 0) {
-        print2(
-"*** (30-Dec-2020) /REWRAP and /FORMAT are temporarily bypassed in\n");
-        print2(
-"*** version 0.195 while a bug is being fixed.\n");
-        r = 0;
-      }
-
-      /* 24-Aug-2020 nm */
-      i = switchPos("/ EXTRACT");
-      if (i > 0) {
-        let(&str1, g_fullArg[i + 1]); /* List of labels */
-        if (r > 0
-            || switchPos("/ SPLIT") > 0
-            || switchPos("/ KEEP_INCLUDES") > 0) {
-          print2(
-"?You may not use / SPLIT, / REWRAP, or / KEEP_INCLUDES with / EXTRACT.\n");
-          continue;
-        }
-      } else {
-        let(&str1, ""); /* Empty string means full db */
-      }
-
-      /* 3-May-2017 nm */
-      writeSource((char)r, /* Rewrap type */ /* Added arg 12-Jun-2011 nm */
-        ((switchPos("/ SPLIT") > 0) ? 1 : 0),          /* 31-Dec-2017 nm */
-        ((switchPos("/ NO_VERSIONING") > 0) ? 1 : 0),  /* 31-Dec-2017 nm */
-        ((switchPos("/ KEEP_INCLUDES") > 0) ? 1 : 0),   /* 31-Dec-2017 nm */
-        str1 /* Label list to extract */  /* 24-Aug-2020 nm */
-          );
-      /*fclose(g_output_fp);*/
-      g_sourceChanged = 0;
-
-      let(&str1, ""); /* Deallocate */ /* 24-Aug-2020 nm */
-      continue;
-    } /* End of WRITE SOURCE */
-
-
-    if (cmdMatches("WRITE THEOREM_LIST")) {
-      /* 4-Dec-03 - Write out an HTML summary of the theorems to
-         mmtheorems.html, mmtheorems1.html,... */
-      /* THEOREMS_PER_PAGE is the default number of proof descriptions to output. */
-#define THEOREMS_PER_PAGE 100
-      /* theoremsPerPage is the actual number of proof descriptions to output. */
-      /* See if the user overrode the default. */
-      i = switchPos("/ THEOREMS_PER_PAGE");
-      if (i != 0) {
-        theoremsPerPage = (long)val(g_fullArg[i + 1]); /* Use user's value */
-      } else {
-        theoremsPerPage = THEOREMS_PER_PAGE; /* Use the default value */
-      }
-      showLemmas = (switchPos("/ SHOW_LEMMAS") != 0);
-      noVersioning = (switchPos("/ NO_VERSIONING") != 0);
-
-      /**** 17-Nov-2015 nm Deleted, no longer need this restriction
-      if (!g_texDefsRead) {
-        g_htmlFlag = 1;
-        print2("Reading definitions from $t statement of %s...\n", g_input_fn);
-        if (2/@error@/ == readTexDefs(0 /@ 1 = check errors only @/,
-            0 /@ 1 = no GIF file existence check @/ )) {
-          continue; /@ An error occurred @/
-        }
-      } else {
-        /@ Current limitation - can only read def's from .mm file once @/
-        if (!g_htmlFlag) {
-          print2("?You cannot use both LaTeX and HTML in the same session.\n");
-          print2(
-              "You must EXIT and restart Metamath to switch to the other.\n");
-          continue;
-        }
-      }
-      *** end of 17-Nov-2015 deletion */
-
-      g_htmlFlag = 1;
-      /* If not specified, for backwards compatibility in scripts
-         leave g_altHtmlFlag at current value */
-      if (switchPos("/ HTML") != 0) {
-        if (switchPos("/ ALT_HTML") != 0) {
-          print2("?Please specify only one of / HTML and / ALT_HTML.\n");
-          continue;
-        }
-        g_altHtmlFlag = 0;
-      } else {
-        if (switchPos("/ ALT_HTML") != 0) g_altHtmlFlag = 1;
-      }
-
-      if (2/*error*/ == readTexDefs(0 /* 1 = check errors only */,
-          0 /* 1 = no GIF file existence check */ )) {
-        continue; /* An error occurred */
-      }
-
-      /* Output the theorem list */
-      writeTheoremList(theoremsPerPage, showLemmas,
-          noVersioning); /* (located in mmwtex.c) */
-      continue;
-    }  /* End of "WRITE THEOREM_LIST" */
-
-
-    if (cmdMatches("WRITE BIBLIOGRAPHY")) {
-      /* 10/10/02 */
-      /* This command builds the bibliographical cross-references to various
-         textbooks and updates the user-specified file normally called
-         mmbiblio.html. */
-
-      /* 17-Nov-2015 nm code moved to writeBibliography() in mmwtex.c */
-      /* (Also called by verifyMarkup() in mmcmds.c for error checking) */
-      writeBibliography(g_fullArg[2],
-          "*", /* labelMatch - all labels */
-          0,  /* 1 = no output, just warning msgs if any */
-          0); /* 1 = ignore missing external files (gifs, bib, etc.) */
-      continue;
-    }  /* End of "WRITE BIBLIOGRAPHY" */
-
-
-    if (cmdMatches("WRITE RECENT_ADDITIONS")) {
-      /* 18-Sep-03 -
-         This utility creates a list of recent proof descriptions and updates
-         the user-specified file normally called mmrecent.html.
-       */
-
-      /* RECENT_COUNT is the default number of proof descriptions to output. */
-#define RECENT_COUNT 100
-      /* i is the actual number of proof descriptions to output. */
-      /* See if the user overrode the default. */
-      i = switchPos("/ LIMIT");
-      if (i) {
-        i = (long)val(g_fullArg[i + 1]); /* Use user's value */
-      } else {
-        i = RECENT_COUNT; /* Use the default value */
-      }
-
-      /**** 17-Nov-2015 nm Deleted because readTeXDefs() now handles everything
-      if (!g_texDefsRead) {
-        g_htmlFlag = 1;
-        print2("Reading definitions from $t statement of %s...\n", g_input_fn);
-        if (2/@error@/ == readTexDefs(0 /@ 1 = check errors only @/,
-            0 /@ 1 = no GIF file existence check @/  )) {
-          continue; /@ An error occurred @/
-        }
-      } else {
-        /@ Current limitation - can only read def's from .mm file once @/
-        if (!g_htmlFlag) {
-          print2("?You cannot use both LaTeX and HTML in the same session.\n");
-          print2(
-              "You must EXIT and restart Metamath to switch to the other.\n");
-          continue;
-        }
-      }
-      **** end of 17-Nov-2015 deletion */
-
-      g_htmlFlag = 1;
-      /* If not specified, for backwards compatibility in scripts
-         leave g_altHtmlFlag at current value */
-      if (switchPos("/ HTML") != 0) {
-        if (switchPos("/ ALT_HTML") != 0) {
-          print2("?Please specify only one of / HTML and / ALT_HTML.\n");
-          continue;
-        }
-        g_altHtmlFlag = 0;
-      } else {
-        if (switchPos("/ ALT_HTML") != 0) g_altHtmlFlag = 1;
-      }
-
-      /* readTexDefs() rereads based on changed in g_htmlFlag, g_altHtmlFlag */
-      if (2/*error*/ == readTexDefs(0 /* 1 = check errors only */,
-          0 /* 1 = no GIF file existence check */  )) {
-        continue; /* An error occurred */
-      }
-
-      tmpFlag = 0; /* Error flag to recover input file */
-      list1_fp = fSafeOpen(g_fullArg[2], "r", 0/*noVersioningFlag*/);
-      if (list1_fp == NULL) {
-        /* Couldn't open it (error msg was provided)*/
-        continue;
-      }
-      fclose(list1_fp);
-      /* This will rename the input mmrecent.html as mmrecent.html~1 */
-      list2_fp = fSafeOpen(g_fullArg[2], "w", 0/*noVersioningFlag*/);
-      if (list2_fp == NULL) {
-          /* Couldn't open it (error msg was provided)*/
-        continue;
-      }
-      /* Note: in older versions the "~1" string was OS-dependent, but we
-         don't support VAX or THINK C anymore...  Anyway we reopen it
-         here with the renamed file in case the OS won't let us rename
-         an opened file during the fSafeOpen for write above. */
-      list1_fp = fSafeOpen(cat(g_fullArg[2], "~1", NULL), "r",
-          0/*noVersioningFlag*/);
-      if (list1_fp == NULL) bug(1117);
-
-      /* Transfer the input file up to the special "<!-- #START# -->" comment */
-      while (1) {
-        if (!linput(list1_fp, NULL, &str1)) {
-          print2(
-"?Error: Could not find \"<!-- #START# -->\" line in input file \"%s\".\n",
-              g_fullArg[2]);
-          tmpFlag = 1; /* Error flag to recover input file */
-          break;
-        }
-        /* 13-May-04 nm Put in "last updated" stamp */
-        if (!strcmp(left(str1, 21), "<!-- last updated -->")) {
-          let(&str1, cat(left(str1, 21), " <I>Last updated on ", date(),
-          /* ??Future: make "EDT"/"EST" or other automatic */
-          /*  " at ", time_(), " EST.</I>", NULL)); */
-          /* 10-Nov-04 nm Just make it "ET" for "Eastern Time" */
-            " at ", time_(), " ET.</I>", NULL));
-        }
-        fprintf(list2_fp, "%s\n", str1);
-        if (!strcmp(str1, "<!-- #START# -->")) break;
-      }
-      if (tmpFlag) goto wrrecent_error;
-
-      /* Get and parse today's date */
-      parseDate(date(), &k /*dd*/, &l /*mmm*/, &m /*yyyy*/); /* 4-Nov-2015 */
-
-      /************ Deleted 4-Nov-2015
-      let(&str1, date());
-      j = instr(1, str1, "-");
-      k = (long)val(left(str1, j - 1)); /@ Day @/
-#define MONTHS "JanFebMarAprMayJunJulAugSepOctNovDec"
-      l = ((instr(1, MONTHS, mid(str1, j + 1, 3)) - 1) / 3) + 1; /@ 1 = Jan @/
-      m = str1[j + 6]; /@ Character after 2-digit year @/  /@ nm 10-Apr-06 @/
-      if (m == ' ' || m == ']') {                          /@ nm 10-Apr-06 @/
-        /@ Handle 2-digit year @/
-        m = (long)val(mid(str1, j + 5, 2));  /@ Year @/
-#define START_YEAR 93 /@ Earliest 19xx year in set.mm database @/
-        if (m < START_YEAR) {
-          m = m + 2000;
-        } else {
-          m = m + 1900;
-        }
-      } else {                                            /@ nm 10-Apr-06 @/
-        /@ Handle 4-digit year @/                         /@ nm 10-Apr-06 @/
-        m = (long)val(mid(str1, j + 5, 4));  /@ Year @/         /@ nm 10-Apr-06 @/
-      }                                                   /@ nm 10-Apr-06 @/
-      *********** end of 4-Nov-2015 deletion */
-
-#define START_YEAR 93 /* Earliest 19xx year in set.mm database */
-      n = 0; /* Count of how many output so far */
-      while (n < i /*RECENT_COUNT*/ && m > START_YEAR + 1900 - 1) {
-
-        /* Build date string to match */
-        /* 4-Nov-2015 nm */
-        /*buildDate(k, l, m, &str5);*/
-        buildDate(k, l, m, &str1); /* 2-May-2017 nm */
-
-        /***** Deleted 2-May-2017 nm - we no longer match date below proof
-        let(&str5, cat("$([", str5, "]$)", NULL));
-        /@ 2-digit year is obsolete, but keep for backwards compatibility @/
-        let(&str1, cat(left(str5, (long)strlen(str5) - 7),
-            right(str5, (long)strlen(str5) - 4), NULL));
-        *******/
-
-        /************ Deleted 4-Nov-2015
-#define MONTHS "JanFebMarAprMayJunJulAugSepOctNovDec"
-        /@ Match for 2-digit year OBSOLETE @/
-        let(&str1, cat("$([", str((double)k), "-", mid(MONTHS, 3 @ l - 2, 3), "-",
-            right(str((double)m), 3), "]$)", NULL));
-        /@ Match for 4-digit year @/  /@ nm 10-Apr-06 @/
-        let(&str5, cat("$([", str((double)k), "-", mid(MONTHS, 3 @ l - 2, 3), "-",
-            str((double)m), "]$)", NULL));
-        *********** end of 4-Nov-2015 deletion */
-
-        for (stmt = g_statements; stmt >= 1; stmt--) {
-
-          if (g_Statement[stmt].type != (char)p_
-                && g_Statement[stmt].type != (char)a_) {
-            continue;
-          }
-
-          /******** Deleted 2-May-2017 nm
-          /@ Get the comment section after the statement @/
-          let(&str2, space(g_Statement[stmt + 1].labelSectionLen));
-          memcpy(str2, g_Statement[stmt + 1].labelSectionPtr,
-              (size_t)(g_Statement[stmt + 1].labelSectionLen));
-          p = instr(1, str2, "$)");
-          let(&str2, left(str2, p + 1)); /@ Get 1st comment (if any) @/
-          let(&str2, edit(str2, 2)); /@ Discard spaces @/
-          ******** end of 2-May-2017 deletion */
-          /* 2-May-2017 nm */
-          /******* Deleted 3-May-2017 nm
-          /@ In the call below, str3 is a dummy variable for a placeholder
-             (its value will be undefined because of multiple occurrences) @/
-          getContrib(stmt/@stmtNum@/, &str3, &str3, &str3, &str3, &str3, &str3,
-              &str2, /@mostRecentDate@/
-              0, /@printErrorsFlag@/
-              1); /@normal mode@/
-          **********/
-          /* 3-May-2017 nm */
-          let(&str2, "");
-          str2 = getContrib(stmt/*stmtNum*/, MOST_RECENT_DATE);
-
-          /* See if the date comment matches */
-          /*if (instr(1, str2, str1) || instr(1, str2, str5)) {*/ /* 10-Apr-06 */
-          if (!strcmp(str2, str1)) { /* 2-May-2017 nm */
-            /* We have a match, so increment the match count */
-            n++;
-            let(&str3, "");
-            str3 = getDescription(stmt);
-            let(&str4, "");
-            str4 = pinkHTML(stmt); /* Get little pink number */
-            /* Output the description comment */
-            /* Break up long lines for text editors with printLongLine */
-            let(&g_printString, "");
-            g_outputToString = 1;
-            print2("\n"); /* Blank line for HTML human readability */
-            printLongLine(cat(
-
-                /*
-                (stmt < g_extHtmlStmt) ?
-                     "<TR>" :
-                     cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
-                */
-
-                /* 29-Jul-2008 nm Sandbox stuff */
-                (stmt < g_extHtmlStmt)
-                   ? "<TR>"
-                   : (stmt < g_mathboxStmt)
-                       ? cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">",
-                           NULL)
-                       : cat("<TR BGCOLOR=", SANDBOX_COLOR, ">", NULL),
-
-                "<TD NOWRAP>",  /* IE breaks up the date */
-                /* mid(str1, 4, (long)strlen(str1) - 6), */ /* Date */
-                /* Use 4-digit year */   /* 10-Apr-06 */
-                /* mid(str5, 4, (long)strlen(str5) - 6), */ /* Date */ /* 10-Apr-06 */
-                str2, /* Date */ /* 2-May-2017 nm */
-                "</TD><TD ALIGN=CENTER><A HREF=\"",
-                g_Statement[stmt].labelName, ".html\">",
-                g_Statement[stmt].labelName, "</A>",
-                str4, "</TD><TD ALIGN=LEFT>", NULL),  /* Description */
-                            /* 28-Dec-05 nm Added ALIGN=LEFT for IE */
-              " ",  /* Start continuation line with space */
-              "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-            g_showStatement = stmt; /* For printTexComment */
-            g_outputToString = 0; /* For printTexComment */
-            g_texFilePtr = list2_fp;
-            /* 18-Sep-03 ???Future - make this just return a string??? */
-            /* printTexComment(str3, 0); */
-            /* 17-Nov-2015 nm Added 3rd & 4th arguments */
-            printTexComment(str3,              /* Sends result to g_texFilePtr */
-                0, /* 1 = htmlCenterFlag */
-                PROCESS_EVERYTHING, /* actionBits */ /* 13-Dec-2018 nm */
-                0  /* 1 = noFileCheck */ );
-            g_texFilePtr = NULL;
-            g_outputToString = 1; /* Restore after printTexComment */
-
-            /* Get HTML hypotheses => assertion */
-            let(&str4, "");
-            str4 = getTexOrHtmlHypAndAssertion(stmt); /* In mmwtex.c */
-            printLongLine(cat("</TD></TR><TR",
-
-                  /*
-                  (s < g_extHtmlStmt) ?
-                       ">" :
-                       cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
-                  */
-
-                  /* 29-Jul-2008 nm Sandbox stuff */
-                  (stmt < g_extHtmlStmt)
-                     ? ">"
-                     : (stmt < g_mathboxStmt)
-                         ? cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">",
-                             NULL)
-                         : cat(" BGCOLOR=", SANDBOX_COLOR, ">", NULL),
-
-                /*** old
-                "<TD BGCOLOR=white>&nbsp;</TD><TD COLSPAN=2 ALIGN=CENTER>",
-                str4, "</TD></TR>", NULL),
-                ****/
-                /* 27-Oct-03 nm */
-                "<TD COLSPAN=3 ALIGN=CENTER>",
-                str4, "</TD></TR>", NULL),
-
-                " ",  /* Start continuation line with space */
-                "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-            g_outputToString = 0;
-            fprintf(list2_fp, "%s", g_printString);
-            let(&g_printString, "");
-
-            if (n >= i /*RECENT_COUNT*/) break; /* We're done */
-
-            /* 27-Oct-03 nm Put separator row if not last theorem */
-            g_outputToString = 1;
-            printLongLine(cat("<TR BGCOLOR=white><TD COLSPAN=3>",
-                "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>", NULL),
-                " ",  /* Start continuation line with space */
-                "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-            /* 29-Jul-04 nm Put the previous, current, and next statement
-               labels in HTML comments so a script can use them to update
-               web site incrementally.  This would be done by searching
-               for "For script" and gather label between = and --> then
-               regenerate just those statements.  Previous and next labels
-               are included to prevent dead links if they don't exist yet. */
-            /* This section can be deleted without side effects */
-            /* Find the previous statement with a web page */
-            j = 0;
-            for (q = stmt - 1; q >= 1; q--) {
-              if (g_Statement[q].type == (char)p_ ||
-                  g_Statement[q].type == (char)a_ ) {
-                j = q;
-                break;
-              }
-            }
-            /* 13-Dec-2018 nm This isn't used anywhere yet.  But fix error
-               in current label and also identify previous, current, next */
-            if (j) print2("<!-- For script: previous = %s -->\n",
-                g_Statement[j].labelName);
-            /* Current statement */
-            print2("<!-- For script: current = %s -->\n",
-                g_Statement[stmt].labelName);
-            /* Find the next statement with a web page */
-            j = 0;
-            for (q = stmt + 1; q <= g_statements; q++) {
-              if (g_Statement[q].type == (char)p_ ||
-                  g_Statement[q].type == (char)a_ ) {
-                j = q;
-                break;
-              }
-            }
-            if (j) print2("<!-- For script: next = %s -->\n",
-                g_Statement[j].labelName);
-            /* End of 29-Jul-04 section */
-
-            g_outputToString = 0;
-            fprintf(list2_fp, "%s", g_printString);
-            let(&g_printString, "");
-
-          }
-        } /* Next stmt - statement number */
-        /* Decrement date */
-        if (k > 1) {
-          k--; /* Decrement day */
-        } else {
-          k = 31; /* Non-existent day 31's will never match, which is OK */
-          if (l > 1) {
-            l--; /* Decrement month */
-          } else {
-            l = 12; /* Dec */
-            m --; /* Decrement year */
-          }
-        }
-      } /* next while - Scan next date */
-
-
-      /* Discard the input file up to the special "<!-- #END# -->" comment */
-      while (1) {
-        if (!linput(list1_fp, NULL, &str1)) {
-          print2(
-"?Error: Could not find \"<!-- #END# -->\" line in input file \"%s\".\n",
-              g_fullArg[2]);
-          tmpFlag = 1; /* Error flag to recover input file */
-          break;
-        }
-        if (!strcmp(str1, "<!-- #END# -->")) {
-          fprintf(list2_fp, "%s\n", str1);
-          break;
-        }
-      }
-      if (tmpFlag) goto wrrecent_error;
-
-      /* Transfer the rest of the input file */
-      while (1) {
-        if (!linput(list1_fp, NULL, &str1)) {
-          break;
-        }
-
-        /* Update the date stamp at the bottom of the HTML page. */
-        /* This is just a nicety; no error check is done. */
-        if (!strcmp("This page was last updated on ", left(str1, 30))) {
-          let(&str1, cat(left(str1, 30), date(), ".", NULL));
-        }
-
-        fprintf(list2_fp, "%s\n", str1);
-      }
-
-      print2("The %ld most recent theorem(s) were written.\n", n);
-
-     wrrecent_error:
-      fclose(list1_fp);
-      fclose(list2_fp);
-      if (tmpFlag) {
-        /* Recover input files in case of error */
-        remove(g_fullArg[2]);  /* Delete output file */
-        rename(cat(g_fullArg[2], "~1", NULL), g_fullArg[2]);
-            /* Restore input file name */
-        print2("?The file \"%s\" was not modified.\n", g_fullArg[2]);
-      }
-      continue;
-    }  /* End of "WRITE RECENT_ADDITIONS" */
-
-
-    if (cmdMatches("SHOW LABELS")) {
-      linearFlag = 0;
-      if (switchPos("/ LINEAR")) linearFlag = 1;
-      if (switchPos("/ ALL")) {
-        m = 1;  /* Include $e, $f statements */
-        print2(
-"The labels that match are shown with statement number, label, and type.\n");
-      } else {
-        m = 0;  /* Show $a, $p only */
-        print2(
-"The assertions that match are shown with statement number, label, and type.\n");
-      }
-      j = 0;
-      k = 0;
-      let(&str2, ""); /* Line so far */
-#define COL 20 /* Column width */
-#define MIN_SPACE 2 /* At least this many spaces between columns */
-      for (i = 1; i <= g_statements; i++) {
-        if (!g_Statement[i].labelName[0]) continue; /* No label */
-        if (!m && g_Statement[i].type != (char)p_ &&
-            g_Statement[i].type != (char)a_) continue; /* No /ALL switch */
-        /* 30-Jan-06 nm Added single-character-match wildcard argument */
-        if (!matchesList(g_Statement[i].labelName, g_fullArg[2], '*', '?')) {
-          continue;
-        }
-
-        /* 2-Oct-2015 nm */
-        let(&str1, cat(str((double)i), " ",
-            g_Statement[i].labelName, " $", chr(g_Statement[i].type),
-            NULL));
-        if (!str2[0]) {
-          j = 0; /* # of fields on line so far */
-        }
-        k = ((long)strlen(str2) + MIN_SPACE > j * COL)
-            ? (long)strlen(str2) + MIN_SPACE : j * COL;
-                /* Position before new str1 starts */
-        if (k + (long)strlen(str1) > g_screenWidth || linearFlag) {
-          if (j == 0) {
-            /* In case of huge label, force it out anyway */
-            printLongLine(str1, "", " ");
-          } else {
-            /* Line width exceeded, postpone adding str1 */
-            print2("%s\n", str2);
-            let(&str2, str1);
-            j = 1;
-          }
-        } else {
-          /* Add new field to line */
-          if (j == 0) {
-            let(&str2, str1); /* Don't put space before 1st label on line */
-          } else {
-            let(&str2, cat(str2, space(k - (long)strlen(str2)), str1, NULL));
-          }
-          j++;
-        }
-
-        /* 2-Oct-2015 nm Deleted
-        let(&str1,cat(str((double)i)," ",
-            g_Statement[i].labelName," $",chr(g_Statement[i].type)," ",NULL));
-#define COL 19 /@ Characters per column @/
-        if (j + (long)strlen(str1) > MAX_LEN
-            || (linearFlag && j != 0)) { /@ j != 0 to suppress 1st CR @/
-          print2("\n");
-          j = 0;
-          k = 0;
-        }
-        if (strlen(str1) > COL || linearFlag) {
-          j = j + (long)strlen(str1);
-          k = k + (long)strlen(str1) - COL;
-          print2(str1);
-        } else {
-          if (k == 0) {
-            j = j + COL;
-            print2("%s%s",str1,space(COL - (long)strlen(str1)));
-          } else {
-            k = k - (COL - (long)strlen(str1));
-            if (k > 0) {
-              print2(str1);
-              j = j + (long)strlen(str1);
-            } else {
-              print2("%s%s",str1,space(COL - (long)strlen(str1)));
-              j = j + COL;
-              k = 0;
-            }
-          }
-        }
-        */
-
-      } /* next i */
-      /* print2("\n"); */
-      if (str2[0]) {
-        print2("%s\n", str2);
-        let(&str2, "");
-      }
-      let(&str1, "");
-      continue;
-    }
-
-    if (cmdMatches("SHOW DISCOURAGED")) {  /* was SHOW RESTRICTED */
-      showDiscouraged(); /* In mmcmds.c */
-      continue;
-    }
-
-    if (cmdMatches("SHOW SOURCE")) {
-
-      /* 14-Sep-2012 nm */
-      /* Currently, SHOW SOURCE only handles one statement at a time,
-         so use getStatementNum().  Eventually, SHOW SOURCE may become
-         obsolete; I don't think anyone uses it. */
-      s = getStatementNum(g_fullArg[2],
-          1/*startStmt*/,
-          g_statements + 1  /*maxStmt*/,
-          1/*aAllowed*/,
-          1/*pAllowed*/,
-          1/*eAllowed*/,
-          1/*fAllowed*/,
-          0/*efOnlyForMaxStmt*/,
-          1/*uniqueFlag*/);
-      if (s == -1) {
-        continue; /* Error msg was provided */
-      }
-      g_showStatement = s; /* Update for future defaults */
-
-      /*********** 14-Sep-2012 replaced by getStatementNum()
-      for (i = 1; i <= g_statements; i++) {
-        if (!strcmp(g_fullArg[2],g_Statement[i].labelName)) break;
-      }
-      if (i > g_statements) {
-        printLongLine(cat("?There is no statement with label \"",
-            g_fullArg[2], "\".  ",
-            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
-        g_showStatement = 0;
-        continue;
-      }
-      g_showStatement = i;
-      ************** end 14-Sep-2012 *******/
-
-      let(&str1, "");
-      str1 = outputStatement(g_showStatement, /*0, 3-May-2017 */ /* cleanFlag */
-          0 /* reformatFlag */);
-      let(&str1,edit(str1,128)); /* Trim trailing spaces */
-      if (str1[strlen(str1)-1] == '\n') let(&str1, left(str1,
-          (long)strlen(str1) - 1));
-      printLongLine(str1, "", "");
-      let(&str1,""); /* Deallocate vstring */
-      continue;
-    } /* if (cmdMatches("SHOW SOURCE")) */
-
-
-    if (cmdMatches("SHOW STATEMENT") && (
-        switchPos("/ HTML")
-        || switchPos("/ BRIEF_HTML")
-        || switchPos("/ ALT_HTML")
-        || switchPos("/ BRIEF_ALT_HTML"))) {
-      /* Special processing for the / HTML qualifiers - for each matching
-         statement, a .html file is opened, the statement is output,
-         and depending on statement type a proof or other information
-         is output. */
-      /* if (g_rawArgs != 5) { */  /* obsolete */
-
-      /* 16-Aug-2016 nm */
-      noVersioning = (switchPos("/ NO_VERSIONING") != 0);
-      i = 5;  /* # arguments with only / HTML or / ALT_HTML */
-      if (noVersioning) i = i + 2;
-      if (switchPos("/ TIME")) i = i + 2;
-      if (g_rawArgs != i) {
-        printLongLine(cat("?The HTML qualifiers may not be combined with",
-            " others except / NO_VERSIONING and / TIME.\n", NULL), "  ", " ");
-        continue;
-      }
-
-      /* 16-Aug-2016 nm */
-      printTime = 0;
-      if (switchPos("/ TIME") != 0) {
-        printTime = 1;
-      }
-
-      /*** 17-Nov-2014 nm This restriction has been removed.
-      if (g_texDefsRead) {
-        /@ Current limitation - can only read def's from .mm file once @/
-        if (!g_htmlFlag) {
-          print2("?You cannot use both LaTeX and HTML in the same session.\n");
-          print2(
-              "You must EXIT and restart Metamath to switch to the other.\n");
-          goto htmlDone;
-        } else {
-          if ((switchPos("/ ALT_HTML") || switchPos("/ BRIEF_ALT_HTML"))
-              == (g_altHtmlFlag == 0)) {
-            print2(
-              "?You cannot use both HTML and ALT_HTML in the same session.\n");
-            print2(
-              "You must EXIT and restart Metamath to switch to the other.\n");
-            goto htmlDone;
-          }
-        }
-      }
-      *** End 17-Nov-2014 deletion */
-
-      g_htmlFlag = 1; /* 17-Nov-2015 nm */
-
-      if (switchPos("/ BRIEF_HTML") || switchPos("/ BRIEF_ALT_HTML")) {
-        if (strcmp(g_fullArg[2], "*")) {
-          print2(
-              "?For BRIEF_HTML or BRIEF_ALT_HTML, the label must be \"*\"\n");
-          goto htmlDone;
-        }
-        g_briefHtmlFlag = 1;
-      } else {
-        g_briefHtmlFlag = 0;
-      }
-
-      if (switchPos("/ ALT_HTML") || switchPos("/ BRIEF_ALT_HTML")) {
-        g_altHtmlFlag = 1;
-      } else {
-        g_altHtmlFlag = 0;
-      }
-
-      q = 0;
-
-      /* Special feature:  if the match statement starts with "*", we
-         will also output mmascii.html, mmtheoremsall.html, and
-         mmdefinitions.html.  So, with
-                 SHOW STATEMENT * / HTML
-         these will be output plus all statements; with
-                 SHOW STATEMENT *! / HTML
-         these will be output with no statements (since ! is illegal in a
-         statement label); with
-                 SHOW STATEMENT ?* / HTML
-         all statements will be output, but without mmascii.html etc. */
-      /* if (instr(1, g_fullArg[2], "*") || g_briefHtmlFlag) { */  /* obsolete */
-      if (((char *)(g_fullArg[2]))[0] == '*' || g_briefHtmlFlag) {
-                                                            /* 6-Jul-2008 nm */
-        s = -2; /* -2 is for ASCII table; -1 is for theorems;
-                    0 is for definitions */
-      } else {
-        s = 1;
-      }
-
-      for (s = s + 0; s <= g_statements; s++) {
-
-        if (s > 0 && g_briefHtmlFlag) break; /* Only do summaries */
-
-        /*
-           s = -2:  mmascii.html
-           s = -1:  mmtheoremsall.html (used to be mmtheorems.html)
-           s = 0:   mmdefinitions.html
-           s > 0:   normal statement
-        */
-
-        if (s > 0) {
-          if (!g_Statement[s].labelName[0]) continue; /* No label */
-          /* 30-Jan-06 nm Added single-character-match wildcard argument */
-          if (!matchesList(g_Statement[s].labelName, g_fullArg[2], '*', '?'))
-            continue;
-          if (g_Statement[s].type != (char)a_
-              && g_Statement[s].type != (char)p_) continue;
-        }
-
-        q = 1; /* Flag that at least one matching statement was found */
-
-        if (s > 0) {
-          g_showStatement = s;
-        } else {
-          /* We set it to 1 here so we will output the Metamath Proof
-             Explorer and not the Hilbert Space Explorer header for
-             definitions and theorems lists, when g_showStatement is
-             compared to g_extHtmlStmt in printTexHeader in mmwtex.c */
-          g_showStatement = 1;
-        }
-
-
-        /*** Open the html file ***/
-        g_htmlFlag = 1;
-        /* Open the html output file */
-        switch (s) {
-          case -2:
-            let(&g_texFileName, "mmascii.html");
-            break;
-          case -1:
-            let(&g_texFileName, "mmtheoremsall.html");
-            break;
-          case 0:
-            let(&g_texFileName, "mmdefinitions.html");
-            break;
-          default:
-            let(&g_texFileName, cat(g_Statement[g_showStatement].labelName, ".html",
-                NULL));
-        }
-        print2("Creating HTML file \"%s\"...\n", g_texFileName);
-        g_texFilePtr = fSafeOpen(g_texFileName, "w",    /* 17-Jul-2019 nm */
-            noVersioning /*noVersioningFlag*/);
-        /****** old code before 17-Jul-2019 *******
-        if (switchPos("/ NO_VERSIONING") == 0) {
-          g_texFilePtr = fSafeOpen(g_texFileName, "w", 0/@noVersioningFlag@/);
-        } else {
-          /@ 6-Jul-2008 nm Added / NO_VERSIONING @/
-          /@ Don't create the backup versions ~1, ~2,... @/
-          g_texFilePtr = fopen(g_texFileName, "w");
-          if (!g_texFilePtr) print2("?Could not open the file \"%s\".\n",
-              g_texFileName);
-        }
-        ********* end of old code before 17-Jul-2019 *******/
-        if (!g_texFilePtr) goto htmlDone; /* Couldn't open it (err msg was
-            provided) */
-        g_texFileOpenFlag = 1;
-        printTexHeader((s > 0) ? 1 : 0 /*texHeaderFlag*/);
-        if (!g_texDefsRead) {
-          /* 9/6/03 If there was an error reading the $t xx.mm statement,
-             g_texDefsRead won't be set, and we should close out file and skip
-             further processing.  Otherwise we will be attempting to process
-             uninitialized htmldef arrays and such. */
-          print2("?HTML generation was aborted due to the error above.\n");
-          s = g_statements + 1; /* To force loop to exit */
-          goto ABORT_S; /* Go to end of loop where file is closed out */
-        }
-
-        if (s <= 0) {
-          g_outputToString = 1;
-          if (s == -2) {
-            printLongLine(cat("<CENTER><FONT COLOR=", GREEN_TITLE_COLOR,
-                "><B>",
-                "Symbol to ASCII Correspondence for Text-Only Browsers",
-                " (in order of appearance in $c and $v statements",
-                     " in the database)",
-                "</B></FONT></CENTER><P>", NULL), "", "\"");
-          }
-          /* 13-Oct-2006 nm todo - </CENTER> still appears - where is it? */
-          if (!g_briefHtmlFlag) print2("<CENTER>\n");
-          print2("<TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
-              MINT_BACKGROUND_COLOR);
-          /* For bobby.cast.org approval */
-          switch (s) {
-            case -2:
-              print2("SUMMARY=\"Symbol to ASCII correspondences\">\n");
-              break;
-            case -1:
-              print2("SUMMARY=\"List of theorems\">\n");
-              break;
-            case 0:
-              print2("SUMMARY=\"List of syntax, axioms and definitions\">\n");
-              break;
-          }
-          switch (s) {
-            case -2:
-              print2("<TR ALIGN=LEFT><TD><B>\n");
-              break;
-            case -1:
-              print2(
-         "<CAPTION><B>List of Theorems</B></CAPTION><TR ALIGN=LEFT><TD><B>\n");
-              break;
-            case 0:
-              printLongLine(cat(
-/*"<CAPTION><B>List of Syntax (not <FONT COLOR=\"#00CC00\">|-&nbsp;</FONT>), ",*/
-                  /* 2/9/02 (in case |- suppressed) */
-                  "<CAPTION><B>List of Syntax, ",
-                  "Axioms (<FONT COLOR=", GREEN_TITLE_COLOR, ">ax-</FONT>) and",
-                  " Definitions (<FONT COLOR=", GREEN_TITLE_COLOR,
-                  ">df-</FONT>)", "</B></CAPTION><TR ALIGN=LEFT><TD><B>",
-                  NULL), "", "\"");
-              break;
-          }
-          switch (s) {
-            case -2:
-              print2("Symbol</B></TD><TD><B>ASCII\n");
-              break;
-            case -1:
-              print2(
-                  "Ref</B></TD><TD><B>Description\n");
-              break;
-            case 0:
-              printLongLine(cat(
-                  "Ref</B></TD><TD><B>",
-                "Expression (see link for any distinct variable requirements)",
-                NULL), "", "\"");
-              break;
-          }
-          print2("</B></TD></TR>\n");
-          m = 0; /* Statement number map */
-          let(&str3, ""); /* For storing ASCII token list in s=-2 mode */
-          let(&bgcolor, MINT_BACKGROUND_COLOR); /* 8-Aug-2008 nm Initialize */
-          for (i = 1; i <= g_statements; i++) {
-
-            /* 8-Aug-2008 nm Commented out: */
-            /*
-            if (i == g_extHtmlStmt && s != -2) {
-              / * Print a row that identifies the start of the extended
-                 database (e.g. Hilbert Space Explorer) * /
-              printLongLine(cat(
-                  "<TR><TD COLSPAN=2 ALIGN=CENTER><A NAME=\"startext\"></A>",
-                  "The list of syntax, axioms (ax-) and definitions (df-) for",
-                  " the <B><FONT COLOR=", GREEN_TITLE_COLOR, ">",
-                  g_extHtmlTitle,
-                  "</FONT></B> starts here</TD></TR>", NULL), "", "\"");
-            }
-            */
-
-            /* 8-Aug-2008 nm */
-            if (s != -2 && (i == g_extHtmlStmt || i == g_mathboxStmt)) {
-              /* Print a row that identifies the start of the extended
-                 database (e.g. Hilbert Space Explorer) or the user
-                 sandboxes */
-              if (i == g_extHtmlStmt) {
-                let(&bgcolor, PURPLISH_BIBLIO_COLOR);
-              } else {
-                let(&bgcolor, SANDBOX_COLOR);
-              }
-              printLongLine(cat("<TR BGCOLOR=", bgcolor,
-                  "><TD COLSPAN=2 ALIGN=CENTER><A NAME=\"startext\"></A>",
-                  "The list of syntax, axioms (ax-) and definitions (df-) for",
-                  " the <B><FONT COLOR=", GREEN_TITLE_COLOR, ">",
-                  (i == g_extHtmlStmt) ?
-                    g_extHtmlTitle :
-                    /*"User Sandboxes",*/
-                    /* 24-Jul-2009 nm Changed name of sandbox to "mathbox" */
-                    "User Mathboxes",
-                  "</FONT></B> starts here</TD></TR>", NULL), "", "\"");
-            }
-
-            if (g_Statement[i].type == (char)p_ ||
-                g_Statement[i].type == (char)a_ ) m++;
-            if ((s == -1 && g_Statement[i].type != (char)p_)
-                || (s == 0 && g_Statement[i].type != (char)a_)
-                || (s == -2 && g_Statement[i].type != (char)c_
-                    && g_Statement[i].type != (char)v_)
-                ) continue;
-            switch (s) {
-              case -2:
-                /* Print symbol to ASCII table entry */
-                /* It's a $c or $v statement, so each token generates a
-                   table row */
-                for (j = 0; j < g_Statement[i].mathStringLen; j++) {
-                  let(&str1, g_MathToken[(g_Statement[i].mathString)[j]].tokenName);
-                  /* Output each token only once in case of multiple decl. */
-                  if (!instr(1, str3, cat(" ", str1, " ", NULL))) {
-                    let(&str3, cat(str3, " ", str1, " ", NULL));
-                    let(&str2, "");
-                    str2 = tokenToTex(g_MathToken[(g_Statement[i].mathString)[j]
-                        ].tokenName, i/*stmt# for error msgs*/);
-                    /* 2/9/02  Skip any tokens (such as |- in QL Explorer) that
-                       may be suppressed */
-                    if (!str2[0]) continue;
-                    /* Convert special characters to HTML entities */
-                    for (k = 0; k < (signed)(strlen(str1)); k++) {
-                      if (str1[k] == '&') {
-                        let(&str1, cat(left(str1, k), "&amp;",
-                            right(str1, k + 2), NULL));
-                        k = k + 4;
-                      }
-                      if (str1[k] == '<') {
-                        let(&str1, cat(left(str1, k), "&lt;",
-                            right(str1, k + 2), NULL));
-                        k = k + 3;
-                      }
-                      if (str1[k] == '>') {
-                        let(&str1, cat(left(str1, k), "&gt;",
-                            right(str1, k + 2), NULL));
-                        k = k + 3;
-                      }
-                    } /* next k */
-                    printLongLine(cat("<TR ALIGN=LEFT><TD>",
-                        (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
-                                                           /* 14-Jan-2016 nm */
-                        str2,
-                        (g_altHtmlFlag ? "</SPAN>" : ""),    /* 14-Jan-2016 nm */
-                        "&nbsp;", /* 10-Jan-2016 nm This will prevent a
-                                     -4px shifted image from overlapping the
-                                     lower border of the table cell */
-                        "</TD><TD><TT>",
-                        str1,
-                        "</TT></TD></TR>", NULL), "", "\"");
-                  }
-                } /* next j */
-                /* Close out the string now to prevent memory overflow */
-                fprintf(g_texFilePtr, "%s", g_printString);
-                let(&g_printString, "");
-                break;
-              case -1: /* Falls through to next case */
-              case 0:
-                /* Count the number of essential hypotheses k */
-                /* Not needed anymore??? since getTexOrHtmlHypAndAssertion() */
-                /*
-                k = 0;
-                j = nmbrLen(g_Statement[i].reqHypList);
-                for (n = 0; n < j; n++) {
-                  if (g_Statement[g_Statement[i].reqHypList[n]].type
-                      == (char)e_) {
-                    k++;
-                  }
-                }
-                */
-                let(&str1, "");
-                if (s == 0 || g_briefHtmlFlag) {
-                  let(&str1, "");
-                  /* 18-Sep-03 Get HTML hypotheses => assertion */
-                  str1 = getTexOrHtmlHypAndAssertion(i);
-                                                /* In mmwtex.c */
-                  let(&str1, cat(str1, "</TD></TR>", NULL));
-                }
-
-                /* 13-Oct-2006 nm Made some changes to BRIEF_HTML/_ALT_HTML
-                   to use its mmtheoremsall.html output for the Palm PDA */
-                if (g_briefHtmlFlag) {
-                  /* Get page number in mmtheorems*.html of WRITE THEOREMS */
-                  k = ((g_Statement[i].pinkNumber - 1) /
-                      THEOREMS_PER_PAGE) + 1; /* Page # */
-                  let(&str2, cat("<TR ALIGN=LEFT><TD ALIGN=LEFT>",
-                      /*"<FONT COLOR=\"#FA8072\">",*/
-                      "<FONT COLOR=ORANGE>",
-                      str((double)(g_Statement[i].pinkNumber)), "</FONT> ",
-                      "<FONT COLOR=GREEN><A HREF=\"",
-                      "mmtheorems", (k == 1) ? "" : str((double)k), ".html#",
-                      g_Statement[i].labelName,
-                      "\">", g_Statement[i].labelName,
-                      "</A></FONT>", NULL));
-                  let(&str1, cat(str2, " ", str1, NULL));
-                } else {
-                  /* Get little pink (or rainbow-colored) number */
-                  let(&str4, "");
-                  str4 = pinkHTML(i);
-                  let(&str2, cat("<TR BGCOLOR=", bgcolor, /* 8-Aug-2008 nm */
-                      " ALIGN=LEFT><TD><A HREF=\"",
-                      g_Statement[i].labelName,
-                      ".html\">", g_Statement[i].labelName,
-                      "</A>", str4, NULL));
-                  let(&str1, cat(str2, "</TD><TD>", str1, NULL));
-                }
-                /* End of 13-Oct-2006 changed section */
-
-                print2("\n");  /* New line for HTML source readability */
-                printLongLine(str1, "", "\"");
-
-                if (s == 0 || g_briefHtmlFlag) {
-                              /* Set s == 0 here for Web site version,
-                                 s == s for symbol version of theorem list */
-                  /* The below has been replaced by
-                     getTexOrHtmlHypAndAssertion(i) above. */
-                  /*printTexLongMath(g_Statement[i].mathString, "", "", 0, 0);*/
-                  /*g_outputToString = 1;*/ /* Is reset by printTexLongMath */
-                } else {
-                  /* Theorems are listed w/ description; otherwise file is too
-                     big for convenience */
-                  let(&str1, "");
-                  str1 = getDescription(i);
-                  if (strlen(str1) > 29)
-                    let(&str1, cat(left(str1, 26), "...", NULL));
-                  let(&str1, cat(str1, "</TD></TR>", NULL));
-                  printLongLine(str1, "", "\"");
-                }
-                /* Close out the string now to prevent overflow */
-                fprintf(g_texFilePtr, "%s", g_printString);
-                let(&g_printString, "");
-                break;
-            } /* end switch */
-          } /* next i (statement number) */
-          /* print2("</TABLE></CENTER>\n"); */ /* 8/8/03 Removed - already
-              done somewhere else, causing validator.w3.org to fail */
-          g_outputToString = 0;  /* closing will write out the string */
-          let(&bgcolor, ""); /* Deallocate (to improve fragmentation) */
-
-        } else { /* s > 0 */
-
-          /* 16-Aug-2016 nm */
-          if (printTime == 1) {
-            getRunTime(&timeIncr);  /* This call just resets the time */
-          }
-
-          /*** Output the html statement body ***/
-          typeStatement(g_showStatement,
-              0 /*briefFlag*/,
-              0 /*commentOnlyFlag*/,
-              1 /*texFlag*/,   /* means latex or html */
-              1 /*g_htmlFlag*/);
-
-          /* 16-Aug-2016 nm */
-          if (printTime == 1) {
-            getRunTime(&timeIncr);
-            print2("SHOW STATEMENT run time = %6.2f sec for \"%s\"\n",
-                timeIncr,
-                g_texFileName);
-          }
-
-        } /* if s <= 0 */
-
-       ABORT_S:
-        /*** Close the html file ***/
-        printTexTrailer(1 /*texHeaderFlag*/);
-        fclose(g_texFilePtr);
-        g_texFileOpenFlag = 0;
-        let(&g_texFileName,"");
-
-      } /* next s */
-
-      if (!q) {
-        /* No matching statement was found */
-        printLongLine(cat("?There is no statement whose label matches \"",
-            g_fullArg[2], "\".  ",
-            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
-        continue;
-      }
-
-      /* Complete the command processing to bypass normal SHOW STATEMENT
-         (non-html) below. */
-     htmlDone:
-      continue;
-    } /* if (cmdMatches("SHOW STATEMENT") && switchPos("/ HTML")...) */
-
-
-    /******* Section for / MNEMONICS added 12-May-2009 by Stefan Allan *****/
-    /* Write mnemosyne.txt */
-    if (cmdMatches("SHOW STATEMENT") && switchPos("/ MNEMONICS")) {
-
-      /*** 17-Nov-2015 nm Deleted - removed g_htmlFlag, g_altHtmlFlag
-            change prohibition
-      if (!g_texDefsRead) {
-        g_htmlFlag = 1;     /@ Use HTML, not TeX section @/
-        g_altHtmlFlag = 1;  /@ Use Unicode, not GIF @/
-        print2("Reading definitions from $t statement of %s...\n", g_input_fn);
-        if (2/@error@/ == readTexDefs(0 /@ 1 = check errors only @/,
-            0 /@ 1 = no GIF file existence check @/  )) {
-          print2(
-          "?There was an error in the $t comment's LaTeX/HTML definitions.\n");
-          print2("?HTML generation was aborted due to the error above.\n");
-          continue; /@ An error occurred @/
-        }
-      } else {
-        /@ Current limitation - can only read def's from .mm file once @/
-        if (!g_htmlFlag) {
-          print2("?You cannot use both LaTeX and HTML in the same session.\n");
-          print2(
-              "You must EXIT and restart Metamath to switch to the other.\n");
-          continue;
-        } else {
-          if (!g_altHtmlFlag) {
-            print2(
-              "?You cannot use both HTML and ALT_HTML in the same session.\n");
-            print2(
-              "You must EXIT and restart Metamath to switch to the other.\n");
-            continue;
-          }
-        }
-      }
-      *** end of 17-Nov-2015 deletion */
-
-      g_htmlFlag = 1;     /* Use HTML, not TeX section */
-      g_altHtmlFlag = 1;  /* Use Unicode, not GIF */
-      /* readTexDefs() rereads based on changes to g_htmlFlag, g_altHtmlFlag */
-      if (2/*error*/ == readTexDefs(0 /* 1 = check errors only */,
-          0 /* 1 = no GIF file existence check */  )) {
-        continue; /* An error occurred */
-      }
-
-      let(&g_texFileName, "mnemosyne.txt");
-      g_texFilePtr = fSafeOpen(g_texFileName, "w", 0/*noVersioningFlag*/);
-      if (!g_texFilePtr) {
-        /* Couldn't open file; error message was provided by fSafeOpen */
-        continue;
-      }
-      print2("Creating Mnemosyne file \"%s\"...\n", g_texFileName);
-
-      for (s = 1; s <= g_statements; s++) {
-        g_showStatement = s;
-/*
-        if (strcmp("|-", g_MathToken[
-         (g_Statement[g_showStatement].mathString)[0]].tokenName)) {
-           subType = SYNTAX;
-        }
-*/
-        if (!g_Statement[s].labelName[0]) continue; /* No label */
-        /* 30-Jan-06 nm Added single-character-match wildcard argument */
-        if (!matchesList(g_Statement[s].labelName, g_fullArg[2], '*', '?'))
-          continue;
-        if (g_Statement[s].type != (char)a_
-            && g_Statement[s].type != (char)p_)
-          continue;
-
-        let(&str1, cat("<CENTER><B><FONT SIZE=\"+1\">",
-            " <FONT COLOR=", GREEN_TITLE_COLOR,
-            " SIZE = \"+3\">", g_Statement[g_showStatement].labelName,
-            "</FONT></FONT></B>", "</CENTER>", NULL));
-        fprintf(g_texFilePtr, "%s", str1);
-
-        let(&str1, cat("<TABLE>",NULL));
-        fprintf(g_texFilePtr, "%s", str1);
-
-        j = nmbrLen(g_Statement[g_showStatement].reqHypList);
-        for (i = 0; i < j; i++) {
-          k = g_Statement[g_showStatement].reqHypList[i];
-          if (g_Statement[k].type != (char)e_
-              && !(subType == SYNTAX
-              && g_Statement[k].type == (char)f_))
-            continue;
-
-          let(&str1, cat("<TR ALIGN=LEFT><TD><FONT SIZE=\"+2\">",
-              g_Statement[k].labelName, "</FONT></TD><TD><FONT SIZE=\"+2\">",
-              NULL));
-          fprintf(g_texFilePtr, "%s", str1);
-
-          /* Print hypothesis */
-          let(&str1, ""); /* Free any previous allocation to str1 */
-          /* getTexLongMath does not return a temporary allocation; must
-             assign str1 directly, not with let().  It will be deallocated
-             with the next let(&str1,...). */
-          str1 = getTexLongMath(g_Statement[k].mathString,
-              k/*stmt# for err msgs*/);
-          fprintf(g_texFilePtr, "%s</FONT></TD>", str1);
-        }
-
-
-        let(&str1, "</TABLE>");
-        fprintf(g_texFilePtr, "%s", str1);
-
-        let(&str1, "<BR><FONT SIZE=\"+2\">What is the conclusion?</FONT>");
-        fprintf(g_texFilePtr, "%s\n", str1);
-
-        let(&str1, "<FONT SIZE=\"+3\">");
-        fprintf(g_texFilePtr, "%s", str1);
-
-        let(&str1, ""); /* Free any previous allocation to str1 */
-        /* getTexLongMath does not return a temporary allocation */
-        str1 = getTexLongMath(g_Statement[s].mathString, s);
-        fprintf(g_texFilePtr, "%s", str1);
-
-        let(&str1, "</FONT>");
-        fprintf(g_texFilePtr, "%s\n",str1);
-      } /*  for(s=1;s<g_statements;++s) */
-
-      fclose(g_texFilePtr);
-      g_texFileOpenFlag = 0;
-      let(&g_texFileName,"");
-      let(&str1,"");
-      let(&str2,"");
-
-      continue;
-    } /* if (cmdMatches("SHOW STATEMENT") && switchPos("/ MNEMONICS")) */
-    /** End of section for / MNEMONICS added 12-May-2009 by Stefan Allan *****/
-
-    /* If we get here, the user did not specify one of the qualifiers /HTML,
-       /BRIEF_HTML, /ALT_HTML, or /BRIEF_ALT_HTML */
-    if (cmdMatches("SHOW STATEMENT") && !switchPos("/ HTML")) {
-
-      texFlag = 0;
-      /* 14-Sep-2010 nm Added OLD_TEX */
-      if (switchPos("/ TEX") || switchPos("/ OLD_TEX")
-          || switchPos("/ HTML"))
-        texFlag = 1;
-
-      briefFlag = 1;
-      g_oldTexFlag = 0;
-      if (switchPos("/ TEX")) briefFlag = 0;
-      /* 14-Sep-2010 nm Added OLD_TEX */
-      if (switchPos("/ OLD_TEX")) briefFlag = 0;
-      if (switchPos("/ OLD_TEX")) g_oldTexFlag = 1;
-      if (switchPos("/ FULL")) briefFlag = 0;
-
-      commentOnlyFlag = 0;
-      if (switchPos("/ COMMENT")) {
-        commentOnlyFlag = 1;
-        briefFlag = 1;
-      }
-
-
-      if (switchPos("/ FULL")) {
-        briefFlag = 0;
-        commentOnlyFlag = 0;
-      }
-
-      if (texFlag) {
-        if (!g_texFileOpenFlag) {
-          print2(
-      "?You have not opened a %s file.  Use the OPEN TEX command first.\n",
-              g_htmlFlag ? "HTML" : "LaTeX");
-          continue;
-        }
-      }
-
-      if (texFlag && (commentOnlyFlag || briefFlag)) {
-        print2("?TEX qualifier should be used alone\n");
-        continue;
-      }
-
-      q = 0;
-
-      for (s = 1; s <= g_statements; s++) {
-        if (!g_Statement[s].labelName[0]) continue; /* No label */
-        /* We are not in MM-PA mode, or the statement isn't "=" */
-        /* 30-Jan-06 nm Added single-character-match wildcard argument */
-        if (!matchesList(g_Statement[s].labelName, g_fullArg[2], '*', '?'))
-          continue;
-
-        if (briefFlag || commentOnlyFlag || texFlag) {
-          /* For brief or comment qualifier, if label has wildcards,
-             show only $p and $a's */
-          /* 30-Jan-06 nm Added single-character-match wildcard argument */
-          if (g_Statement[s].type != (char)p_
-              && g_Statement[s].type != (char)a_ && (instr(1, g_fullArg[2], "*")
-                  || instr(1, g_fullArg[2], "?")))
-            continue;
-        }
-
-        if (q && !texFlag) {
-          /* 17-Jul-2019 nm Changed "79" to "g_screenWidth" */
-          if (!print2("%s\n", string(g_screenWidth, '-'))) /* Put line between
-                                                   statements */
-            break; /* Break for speedup if user quit */
-        }
-        if (texFlag) print2("Outputting statement \"%s\"...\n",
-            g_Statement[s].labelName);
-
-        q = 1; /* Flag that at least one matching statement was found */
-
-        g_showStatement = s;
-
-
-        typeStatement(g_showStatement,
-            briefFlag,
-            commentOnlyFlag,
-            texFlag,
-            g_htmlFlag);
-      } /* Next s */
-
-      if (!q) {
-        /* No matching statement was found */
-        printLongLine(cat("?There is no statement whose label matches \"",
-            g_fullArg[2], "\".  ",
-            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
-        continue;
-      }
-
-      if (texFlag && !g_htmlFlag) {
-        print2("The LaTeX source was written to \"%s\".\n", g_texFileName);
-        /* 14-Sep-2010 nm Added OLD_TEX */
-        g_oldTexFlag = 0;
-      }
-      continue;
-    } /* (cmdMatches("SHOW STATEMENT") && !switchPos("/ HTML")) */
-
-    if (cmdMatches("SHOW SETTINGS")) {
-      print2("Metamath settings on %s at %s:\n",date(),time_());
-      if (g_commandEcho) {
-        print2("(SET ECHO...) Command ECHO is ON.\n");
-      } else {
-        print2("(SET ECHO...) Command ECHO is OFF.\n");
-      }
-      if (defaultScrollMode == 1) {
-        print2("(SET SCROLL...) SCROLLing mode is PROMPTED.\n");
-      } else {
-        print2("(SET SCROLL...) SCROLLing mode is CONTINUOUS.\n");
-      }
-      print2("(SET WIDTH...) Screen display WIDTH is %ld.\n", g_screenWidth);
-      print2("(SET HEIGHT...) Screen display HEIGHT is %ld.\n",
-          g_screenHeight + 1);
-      if (g_sourceHasBeenRead == 1) {
-        print2("(READ...) %ld statements have been read from \"%s\".\n",
-            g_statements, g_input_fn);
-      } else {
-        print2("(READ...) No source file has been read in yet.\n");
-      }
-      print2("(SET ROOT_DIRECTORY...) Root directory is \"%s\".\n",
-          g_rootDirectory);
-      print2(
-     "(SET DISCOURAGEMENT...) Blocking based on \"discouraged\" tags is %s.\n",
-          (g_globalDiscouragement ? "ON" : "OFF"));
-      print2(
-     "(SET CONTRIBUTOR...) The current contributor is \"%s\".\n",
-          g_contributorName);
-      if (g_PFASmode) {
-        print2("(PROVE...) The statement you are proving is \"%s\".\n",
-            g_Statement[g_proveStatement].labelName);
-      }
-      print2("(SET UNDO...) The maximum number of UNDOs is %ld.\n",
-          processUndoStack(NULL, PUS_GET_SIZE, "", 0));
-      print2(
-    "(SET UNIFICATION_TIMEOUT...) The unification timeout parameter is %ld.\n",
-          g_userMaxUnifTrials);
-      print2(
-    "(SET SEARCH_LIMIT...) The SEARCH_LIMIT for the IMPROVE command is %ld.\n",
-          g_userMaxProveFloat);
-      if (g_minSubstLen) {
-        print2(
-     "(SET EMPTY_SUBSTITUTION...) EMPTY_SUBSTITUTION is not allowed (OFF).\n");
-      } else {
-        print2(
-          "(SET EMPTY_SUBSTITUTION...) EMPTY_SUBSTITUTION is allowed (ON).\n");
-      }
-      if (g_hentyFilter) { /* 18-Nov-05 nm Added to the SHOW listing */
-        print2(
-              "(SET JEREMY_HENTY_FILTER...) The Henty filter is turned ON.\n");
-      } else {
-        print2(
-             "(SET JEREMY_HENTY_FILTER...) The Henty filter is turned OFF.\n");
-      }
-      if (g_showStatement) {
-        print2("(SHOW...) The default statement for SHOW commands is \"%s\".\n",
-            g_Statement[g_showStatement].labelName);
-      }
-      if (g_logFileOpenFlag) {
-        print2("(OPEN LOG...) The log file \"%s\" is open.\n", g_logFileName);
-      } else {
-        print2("(OPEN LOG...) No log file is currently open.\n");
-      }
-      if (g_texFileOpenFlag) {
-        print2("The %s file \"%s\" is open.\n", g_htmlFlag ? "HTML" : "LaTeX",
-            g_texFileName);
-      }
-      /* 17-Nov-2015 nm */
-      print2(
-  "(SHOW STATEMENT.../[TEX,HTML,ALT_HTML]) Current output mode is %s.\n",
-          g_htmlFlag
-             ? (g_altHtmlFlag ? "ALT_HTML" : "HTML")
-             : "TEX (LaTeX)");
-      /* 21-Jun-2014 */
-      print2("The program is compiled for a %ld-bit CPU.\n",
-          (long)(8 * sizeof(long)));
-      print2(
- "sizeof(short)=%ld, sizeof(int)=%ld, sizeof(long)=%ld, sizeof(size_t)=%ld.\n",
-        (long)(sizeof(short)),
-        (long)(sizeof(int)), (long)(sizeof(long)), (long)(sizeof(size_t)));
-      continue;
-    }
-
-    if (cmdMatches("SHOW MEMORY")) {
-      /*print2("%ld bytes of data memory have been used.\n",db+db3);*/
-      j = 32000000; /* The largest we'ed ever look for */
-#ifdef THINK_C
-      i = FreeMem();
-#else
-      i = getFreeSpace(j);
-#endif
-      if (i > j-3) {
-        print2("At least %ld bytes of memory are free.\n",j);
-      } else {
-        print2("%ld bytes of memory are free.\n",i);
-      }
-      continue;
-    }
-
-    /* 21-Jun-2014 */
-    if (cmdMatches("SHOW ELAPSED_TIME")) {
-      timeTotal = getRunTime(&timeIncr);
-      print2(
-      "Time since last SHOW ELAPSED_TIME command = %6.2f s; total = %6.2f s\n",
-          timeIncr, timeTotal);
-      continue;
-    } /* if (cmdMatches("SHOW ELAPSED_TIME")) */
-
-
-    if (cmdMatches("SHOW TRACE_BACK")) {
-
-
-      /* Pre-21-May-2008
-        for (i = 1; i <= g_statements; i++) {
-          if (!strcmp(g_fullArg[2],g_Statement[i].labelName)) break;
-        }
-        if (i > g_statements) {
-          printLongLine(cat("?There is no statement with label \"",
-              g_fullArg[2], "\".  ",
-              "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
-          g_showStatement = 0;
-          continue;
-        }
-       */
-
-      essentialFlag = 0;
-      axiomFlag = 0;
-      endIndent = 0;
-      i = switchPos("/ ESSENTIAL");
-      if (i) essentialFlag = 1; /* Limit trace to essential steps only */
-      i = switchPos("/ ALL");
-      if (i) essentialFlag = 0;
-      i = switchPos("/ AXIOMS");
-      if (i) axiomFlag = 1; /* Limit trace printout to axioms */
-      i = switchPos("/ DEPTH"); /* Limit depth of printout */
-      if (i) endIndent = (long)val(g_fullArg[i + 1]);
-
-       /* 19-May-2013 nm */
-      i = switchPos("/ COUNT_STEPS");
-      countStepsFlag = (i != 0 ? 1 : 0);
-      i = switchPos("/ TREE");
-      treeFlag = (i != 0 ? 1 : 0);
-      i = switchPos("/ MATCH");
-      matchFlag = (i != 0 ? 1 : 0);
-      if (matchFlag) {
-        let(&matchList, g_fullArg[i + 1]);
-      } else {
-        let(&matchList, "");
-      }
-      i = switchPos("/ TO");
-      if (i != 0) {
-        let(&traceToList, g_fullArg[i + 1]);
-      } else {
-        let(&traceToList, "");
-      }
-      if (treeFlag) {
-        if (axiomFlag) {
-          print2(
-              "(Note:  The AXIOMS switch is ignored in TREE mode.)\n");
-        }
-        if (countStepsFlag) {
-          print2(
-              "(Note:  The COUNT_STEPS switch is ignored in TREE mode.)\n");
-        }
-        if (matchFlag) {
-          print2(
-  "(Note: The MATCH list is ignored in TREE mode.)\n");
-        }
-      } else {
-        if (endIndent != 0) {
-          print2(
-  "(Note:  The DEPTH is ignored if the TREE switch is not used.)\n");
-        }
-        if (countStepsFlag) {
-          if (matchFlag) {
-            print2(
-  "(Note: The MATCH list is ignored in COUNT_STEPS mode.)\n");
-          }
-        }
-      }
-
-      /* 21-May-2008 nm Added wildcard handling */
-      g_showStatement = 0;
-      for (i = 1; i <= g_statements; i++) {
-        if (g_Statement[i].type != (char)p_)
-          continue; /* Not a $p statement; skip it */
-        /* Wildcard matching */
-        if (!matchesList(g_Statement[i].labelName, g_fullArg[2], '*', '?'))
-          continue;
-
-        g_showStatement = i;
-        /*** start of 19-May-2013 deletion
-        j = switchPos("/ TREE");
-        if (j) {
-          if (axiomFlag) {
-            print2(
-                "(Note:  The AXIOMS switch is ignored in TREE mode.)\n");
-          }
-          if (switchPos("/ COUNT_STEPS")) {
-            print2(
-                "(Note:  The COUNT_STEPS switch is ignored in TREE mode.)\n");
-          }
-          traceProofTree(g_showStatement, essentialFlag, endIndent);
-        } else {
-          if (endIndent != 0) {
-           print2(
-"(Note:  The DEPTH is ignored if the TREE switch is not used.)\n");
-          }
-
-          j = switchPos("/ COUNT_STEPS");
-          if (j) {
-            countSteps(g_showStatement, essentialFlag);
-          } else {
-            traceProof(g_showStatement, essentialFlag, axiomFlag);
-          }
-        }
-        *** end of 19-May-2013 deletion */
-
-
-        /* 19-May-2013 nm - move /TREE and /COUNT_STEPS to outside loop,
-           assigning new variables, for cleaner code. */
-        if (treeFlag) {
-          traceProofTree(g_showStatement, essentialFlag, endIndent);
-        } else {
-          if (countStepsFlag) {
-            countSteps(g_showStatement, essentialFlag);
-          } else {
-            traceProof(g_showStatement,
-                essentialFlag,
-                axiomFlag,
-                matchList, /* 19-May-2013 nm */
-                traceToList, /* 18-Jul-2015 nm */
-                0 /* testOnlyFlag */ /* 20-May-2013 nm */);
-          }
-        }
-
-      /* 21-May-2008 nm Added wildcard handling */
-      } /* next i */
-      if (g_showStatement == 0) {
-        printLongLine(cat("?There are no $p labels matching \"",
-            g_fullArg[2], "\".  ",
-            "See HELP SHOW TRACE_BACK for matching rules.", NULL), "", " ");
-      }
-
-      let(&matchList, ""); /* Deallocate memory */
-      let(&traceToList, ""); /* Deallocate memory */
-      continue;
-    } /* if (cmdMatches("SHOW TRACE_BACK")) */
-
-
-    if (cmdMatches("SHOW USAGE")) {
-
-      /* 7-Jan-2008 nm Added / ALL qualifier */
-      if (switchPos("/ ALL")) {
-        m = 1;  /* Always include $e, $f statements */
-      } else {
-        m = 0;  /* If wildcards are used, show $a, $p only */
-      }
-
-      g_showStatement = 0;
-      for (i = 1; i <= g_statements; i++) { /* 7-Jan-2008 */
-
-        /* 7-Jan-2008 nm Added wildcard handling */
-        if (!g_Statement[i].labelName[0]) continue; /* No label */
-        if (!m && g_Statement[i].type != (char)p_ &&
-            g_Statement[i].type != (char)a_
-            /* A wildcard-free user-specified statement is always matched even
-               if it's a $e, i.e. it overrides omission of / ALL */
-            && (instr(1, g_fullArg[2], "*")
-              || instr(1, g_fullArg[2], "?")))
-          continue; /* No /ALL switch and wildcard and not $p, $a */
-        /* Wildcard matching */
-        if (!matchesList(g_Statement[i].labelName, g_fullArg[2], '*', '?'))
-          continue;
-
-        g_showStatement = i;
-        recursiveFlag = 0;
-        j = switchPos("/ RECURSIVE");
-        if (j) recursiveFlag = 1; /* Recursive (indirect) usage */
-        j = switchPos("/ DIRECT");
-        if (j) recursiveFlag = 0; /* Direct references only */
-
-        let(&str1, "");
-        str1 = traceUsage(g_showStatement,
-            recursiveFlag,
-            0 /* cutoffStmt */);
-
-
-
-        /************* 18-Jul-2015 nm Start of deleted code ************/
-        /*
-        /@ Count the number of statements = # of spaces @/
-        k = (long)strlen(str1) - (long)strlen(edit(str1, 2));
-
-        if (!k) {
-          printLongLine(cat("Statement \"",
-              g_Statement[g_showStatement].labelName,
-              "\" is not referenced in the proof of any statement.", NULL),
-              "", " ");
-        } else {
-          if (recursiveFlag) {
-            let(&str2, "\" directly or indirectly affects");
-          } else {
-            let(&str2, "\" is directly referenced in");
-          }
-          if (k == 1) {
-            printLongLine(cat("Statement \"",
-                g_Statement[g_showStatement].labelName,
-                str2, " the proof of ",
-                str((double)k), " statement:", NULL), "", " ");
-          } else {
-            printLongLine(cat("Statement \"",
-                g_Statement[g_showStatement].labelName,
-                str2, " the proofs of ",
-                str((double)k), " statements:", NULL), "", " ");
-          }
-        }
-
-        if (k) {
-          let(&str1, cat(" ", str1, NULL));
-        } else {
-          let(&str1, "  (None)");
-        }
-
-        /@ Print the output @/
-        printLongLine(str1, "  ", " ");
-        */
-        /********* 18-Jul-2015 nm End of deleted code ****************/
-
-
-        /************* 18-Jul-2015 nm Start of new code ************/
-        /* 18-Jul-2015 nm */
-        /* str1[0] will be 'Y' or 'N' depending on whether there are any
-           statements.  str1[i] will be 'Y' or 'N' depending on whether
-           g_Statement[i] uses g_showStatement. */
-        /* Count the number of statements k = # of 'Y' */
-        k = 0;
-        if (str1[0] == 'Y') {
-          /* There is at least one statement using g_showStatement */
-          for (j = g_showStatement + 1; j <= g_statements; j++) {
-            if (str1[j] == 'Y') {
-              k++;
-            } else {
-              if (str1[j] != 'N') bug(1124); /* Must be 'Y' or 'N' */
-            }
-          }
-        } else {
-          if (str1[0] != 'N') bug(1125); /* Must be 'Y' or 'N' */
-        }
-
-        if (k == 0) {
-          printLongLine(cat("Statement \"",
-              g_Statement[g_showStatement].labelName,
-              "\" is not referenced in the proof of any statement.", NULL),
-              "", " ");
-        } else {
-          if (recursiveFlag) {
-            let(&str2, "\" directly or indirectly affects");
-          } else {
-            let(&str2, "\" is directly referenced in");
-          }
-          if (k == 1) {
-            printLongLine(cat("Statement \"",
-                g_Statement[g_showStatement].labelName,
-                str2, " the proof of ",
-                str((double)k), " statement:", NULL), "", " ");
-          } else {
-            printLongLine(cat("Statement \"",
-                g_Statement[g_showStatement].labelName,
-                str2, " the proofs of ",
-                str((double)k), " statements:", NULL), "", " ");
-          }
-        }
-
-        if (k != 0) {
-          let(&str3, " "); /* Line buffer */
-          for (j = g_showStatement + 1; j <= g_statements; j++) {
-            if (str1[j] == 'Y') {
-              /* Since the output list could be huge, don't build giant
-                 string (very slow) but output it line by line */
-              if ((long)strlen(str3) + 1 +
-                  (long)strlen(g_Statement[j].labelName) > g_screenWidth) {
-                /* Output and reset the line buffer */
-                print2("%s\n", str3);
-                let(&str3, " ");
-              }
-              let(&str3, cat(str3, " ", g_Statement[j].labelName, NULL));
-            }
-          }
-          if (strlen(str3) > 1) print2("%s\n", str3);
-          let(&str3, "");
-        } else {
-          print2("  (None)\n");
-        } /* if (k != 0) */
-        /********* 18-Jul-2015 nm End of new code ****************/
-
-
-      } /* next i (statement matching wildcard list) */
-
-      if (g_showStatement == 0) {
-        printLongLine(cat("?There are no labels matching \"",
-            g_fullArg[2], "\".  ",
-            "See HELP SHOW USAGE for matching rules.", NULL), "", " ");
-      }
-      continue;
-    } /* if cmdMatches("SHOW USAGE") */
-
-
-    if (cmdMatches("SHOW PROOF")
-        || cmdMatches("SHOW NEW_PROOF")
-        || cmdMatches("SAVE PROOF")
-        || cmdMatches("SAVE NEW_PROOF")
-        || cmdMatches("MIDI")) {
-      if (switchPos("/ HTML")) {
-        print2("?HTML qualifier is obsolete - use SHOW STATEMENT * / HTML\n");
-        continue;
-      }
-
-      /* 3-May-2016 nm */
-      if (cmdMatches("SAVE NEW_PROOF")
-          && getMarkupFlag(g_proveStatement, PROOF_DISCOURAGED)) {
-        if (switchPos("/ OVERRIDE") == 0 && g_globalDiscouragement == 1) {
-          /* print2("\n"); */ /* Enable for more emphasis */
-          print2(
-">>> ?Error: Attempt to overwrite a proof whose modification is discouraged.\n");
-          print2(
-   ">>> Use SAVE NEW_PROOF ... / OVERRIDE if you really want to do this.\n");
-          /* print2("\n"); */ /* Enable for more emphasis */
-          continue;
-        } else {
-          /* print2("\n"); */ /* Enable for more emphasis */
-          print2(
-">>> ?Warning: You are overwriting a proof whose modification is discouraged.\n");
-          /* print2("\n"); */ /* Enable for more emphasis */
-        }
-      }
-
-      if (cmdMatches("SHOW PROOF") || cmdMatches("SAVE PROOF")) {
-        pipFlag = 0;
-      } else {
-        pipFlag = 1; /* Proof-in-progress (NEW_PROOF) flag */
-      }
-      if (cmdMatches("SHOW")) {
-        saveFlag = 0;
-      } else {
-        saveFlag = 1; /* The command is SAVE PROOF */
-      }
-
-      /* 16-Aug-2016 nm */
-      printTime = 0;
-      if (switchPos("/ TIME") != 0) {  /* / TIME legal in SAVE mode only */
-        printTime = 1;
-      }
-
-      /* 27-Dec-2013 nm */
-      i = switchPos("/ OLD_COMPRESSION");
-      if (i) {
-        if (!switchPos("/ COMPRESSED")) {
-          print2("?/ OLD_COMPRESSION must be accompanied by / COMPRESSED.\n");
-          continue;
-        }
-      }
-
-      /* 2-Mar-2016 nm */
-      i = switchPos("/ FAST");
-      if (i) {
-        if (!switchPos("/ COMPRESSED") && !switchPos("/ PACKED")) {
-          print2("?/ FAST must be accompanied by / COMPRESSED or / PACKED.\n");
-          continue;
-        }
-        fastFlag = 1;
-      } else {
-        fastFlag = 0;
-      }
-
-      /* 2-Mar-2016 nm */
-      if (switchPos("/ EXPLICIT")) {
-        if (switchPos("/ COMPRESSED")) {
-          print2("?/ COMPRESSED and / EXPLICIT may not be used together.\n");
-          continue;
-        } else if (switchPos("/ NORMAL")) {
-          print2("?/ NORMAL and / EXPLICIT may not be used together.\n");
-          continue;
-        }
-      }
-      if (switchPos("/ NORMAL")) {
-        if (switchPos("/ COMPRESSED")) {
-          print2("?/ NORMAL and / COMPRESSED may not be used together.\n");
-          continue;
-        }
-      }
-
-
-      /* Establish defaults for omitted qualifiers */
-      startStep = 0;
-      endStep = 0;
-      /* startIndent = 0; */ /* Not used */
-      endIndent = 0;
-      /*essentialFlag = 0;*/
-      essentialFlag = 1; /* 10/9/99 - friendlier default */
-      renumberFlag = 0;
-      unknownFlag = 0;
-      notUnifiedFlag = 0;
-      reverseFlag = 0;
-      detailStep = 0;
-      noIndentFlag = 0;
-      splitColumn = DEFAULT_COLUMN;
-      skipRepeatedSteps = 0; /* 28-Jun-2013 nm */
-      texFlag = 0;
-
-      i = switchPos("/ FROM_STEP");
-      if (i) startStep = (long)val(g_fullArg[i + 1]);
-      i = switchPos("/ TO_STEP");
-      if (i) endStep = (long)val(g_fullArg[i + 1]);
-      i = switchPos("/ DEPTH");
-      if (i) endIndent = (long)val(g_fullArg[i + 1]);
-      /* 10/9/99 - ESSENTIAL is retained for downwards compatibility, but is
-         now the default, so we ignore it. */
-      /*
-      i = switchPos("/ ESSENTIAL");
-      if (i) essentialFlag = 1;
-      */
-      i = switchPos("/ ALL");
-      if (i) essentialFlag = 0;
-      if (i && switchPos("/ ESSENTIAL")) {
-        print2("?You may not specify both / ESSENTIAL and / ALL.\n");
-        continue;
-      }
-      i = switchPos("/ RENUMBER");
-      if (i) renumberFlag = 1;
-      i = switchPos("/ UNKNOWN");
-      if (i) unknownFlag = 1;
-      i = switchPos("/ NOT_UNIFIED"); /* pip mode only */
-      if (i) notUnifiedFlag = 1;
-      i = switchPos("/ REVERSE");
-      if (i) reverseFlag = 1;
-      i = switchPos("/ LEMMON");
-      if (i) noIndentFlag = 1;
-      i = switchPos("/ START_COLUMN");
-      if (i) splitColumn = (long)val(g_fullArg[i + 1]);
-      i = switchPos("/ NO_REPEATED_STEPS"); /* 28-Jun-2013 nm */
-      if (i) skipRepeatedSteps = 1;         /* 28-Jun-2013 nm */
-
-      /* 8-Dec-2018 nm */
-      /* If NO_REPEATED_STEPS is specified, indentation (tree) mode will be
-         misleading because a hypothesis assignment will be suppressed if the
-         same assignment occurred earlier, i.e. it is no longer a "tree". */
-      if (skipRepeatedSteps == 1 && noIndentFlag == 0) {
-        print2("?You must specify / LEMMON with / NO_REPEATED_STEPS\n");
-        continue;
-      }
-
-      i = switchPos("/ TEX") || switchPos("/ HTML")
-          /* 14-Sep-2010 nm Added OLDE_TEX */
-          || switchPos("/ OLD_TEX");
-      if (i) texFlag = 1;
-
-      /* 14-Sep-2010 nm Added OLD_TEX */
-      g_oldTexFlag = 0;
-      if (switchPos("/ OLD_TEX")) g_oldTexFlag = 1;
-
-      if (cmdMatches("MIDI")) { /* 8/28/00 */
-        g_midiFlag = 1;
-        pipFlag = 0;
-        saveFlag = 0;
-        let(&labelMatch, g_fullArg[1]);
-        i = switchPos("/ PARAMETER"); /* MIDI only */
-        if (i) {
-          let(&g_midiParam, g_fullArg[i + 1]);
-        } else {
-          let(&g_midiParam, "");
-        }
-      } else {
-        g_midiFlag = 0;
-        if (!pipFlag) let(&labelMatch, g_fullArg[2]);
-      }
-
-
-      if (texFlag) {
-        if (!g_texFileOpenFlag) {
-          print2(
-     "?You have not opened a %s file.  Use the OPEN %s command first.\n",
-              g_htmlFlag ? "HTML" : "LaTeX",
-              g_htmlFlag ? "HTML" : "TEX");
-          continue;
-        }
-        /**** this is now done after outputting
-        print2("The %s source was written to \"%s\".\n",
-            g_htmlFlag ? "HTML" : "LaTeX", g_texFileName);
-        */
-      }
-
-      i = switchPos("/ DETAILED_STEP"); /* non-pip mode only */
-      if (i) {
-        detailStep = (long)val(g_fullArg[i + 1]);
-        if (!detailStep) detailStep = -1; /* To use as flag; error message
-                                             will occur in showDetailStep() */
-      }
-
-/*??? Need better warnings for switch combinations that don't make sense */
-
-      /* 2-Jan-2017 nm */
-      /* Print a single message for "/compressed/fast" */
-      if (switchPos("/ COMPRESSED") && fastFlag
-          && !strcmp("*", labelMatch)) {
-        print2(
-            "Reformatting and saving (but not recompressing) all proofs...\n");
-      }
-
-
-      q = 0;  /* Flag that at least one matching statement was found */
-      for (stmt = 1; stmt <= g_statements; stmt++) {
-        /* If pipFlag (NEW_PROOF), we will iterate exactly once.  This
-           loop of course will be entered because there is a least one
-           statement, and at the end of the s loop we break out of it. */
-        /* If !pipFlag, get the next statement: */
-        if (!pipFlag) {
-          if (g_Statement[stmt].type != (char)p_) continue; /* Not $p */
-          /* 30-Jan-06 nm Added single-character-match wildcard argument */
-          if (!matchesList(g_Statement[stmt].labelName, labelMatch, '*', '?'))
-            continue;
-          g_showStatement = stmt;
-        }
-
-        q = 1; /* Flag that at least one matching statement was found */
-
-        if (detailStep) {
-          /* Show the details of just one step */
-          showDetailStep(g_showStatement, detailStep);
-          continue;
-        }
-
-        if (switchPos("/ STATEMENT_SUMMARY")) { /* non-pip mode only */
-          /* Just summarize the statements used in the proof */
-          proofStmtSumm(g_showStatement, essentialFlag, texFlag);
-          continue;
-        }
-
-        /* 21-Jun-2014 */
-        if (switchPos("/ SIZE")) { /* non-pip mode only */
-          /* Just print the size of the stored proof and continue */
-          let(&str1, space(g_Statement[g_showStatement].proofSectionLen));
-          memcpy(str1, g_Statement[g_showStatement].proofSectionPtr,
-              (size_t)(g_Statement[g_showStatement].proofSectionLen));
-          n = instr(1, str1, "$.");
-          if (n == 0) {
-            /* The original source truncates the proof before $. */
-            n = g_Statement[g_showStatement].proofSectionLen;
-          } else {
-            /* If a proof is saved, it includes the $. (Should we
-               revisit or document better how/why this is done?) */
-            n = n - 1;
-          }
-          print2("The proof source for \"%s\" has %ld characters.\n",
-              g_Statement[g_showStatement].labelName, n);
-          continue;
-        }
-
-        if (switchPos("/ PACKED") || switchPos("/ NORMAL") ||
-            switchPos("/ COMPRESSED") || switchPos("/ EXPLICIT") || saveFlag) {
-          /*??? Add error msg if other switches were specified. (Ignore them.)*/
-
-          /* 16-Aug-2016 nm */
-          if (saveFlag) {
-            if (printTime == 1) {
-              getRunTime(&timeIncr);  /* This call just resets the time */
-            }
-          }
-
-          if (!pipFlag) {
-            outStatement = g_showStatement;
-          } else {
-            outStatement = g_proveStatement;
-          }
-
-          explicitTargets = (switchPos("/ EXPLICIT") != 0) ? 1 : 0;
-
-          /* Get the amount to indent the proof by */
-          indentation = 2 + getSourceIndentation(outStatement);
-
-          if (!pipFlag) {
-            parseProof(g_showStatement);  /* Prints message if severe error */
-            if (g_WrkProof.errorSeverity > 1) {  /* 21-Aug-04 nm */
-                              /* Prevent bugtrap in nmbrSquishProof -> */
-                              /* nmbrGetSubProofLen if proof corrupted */
-              print2(
-          "?The proof has a severe error and cannot be displayed or saved.\n");
-              continue;
-            }
-            /* verifyProof(g_showStatement); */ /* Not necessary */
-            if (fastFlag) { /* 2-Mar-2016 nm */
-              /* 2-Mar-2016 nm */
-              /* Use the proof as is */
-              nmbrLet(&nmbrSaveProof, g_WrkProof.proofString);
-            } else {
-              /* Make sure the proof is uncompressed */
-              nmbrLet(&nmbrSaveProof, nmbrUnsquishProof(g_WrkProof.proofString));
-            }
-          } else {
-            nmbrLet(&nmbrSaveProof, g_ProofInProgress.proof);
-          }
-          if (switchPos("/ PACKED")  || switchPos("/ COMPRESSED")) {
-            if (!fastFlag) { /* 2-Mar-2016 nm */
-              nmbrLet(&nmbrSaveProof, nmbrSquishProof(nmbrSaveProof));
-            }
-          }
-
-          if (switchPos("/ COMPRESSED")) {
-            let(&str1, compressProof(nmbrSaveProof,
-                outStatement, /* g_showStatement or g_proveStatement based on pipFlag */
-                (switchPos("/ OLD_COMPRESSION")) ? 1 : 0  /* 27-Dec-2013 nm */
-                ));
-          } else {
-            let(&str1, nmbrCvtRToVString(nmbrSaveProof,
-                /* 25-Jan-2016 nm */
-                explicitTargets, /*explicitTargets*/
-                outStatement /*statemNum, used only if explicitTargets*/));
-          }
-
-
-          if (saveFlag) {
-            /* ??? This is a problem when mixing html and save proof */
-            if (g_printString[0]) bug(1114);
-            let(&g_printString, "");
-
-            /* Set flag for print2() to put output in g_printString instead
-               of displaying it on the screen */
-            g_outputToString = 1;
-
-          } else {
-            if (!print2("Proof of \"%s\":\n", g_Statement[outStatement].labelName))
-              break; /* Break for speedup if user quit */
-            print2(
-"---------Clip out the proof below this line to put it in the source file:\n");
-            /* 19-Apr-2015 so */
-            /* 24-Apr-2015 nm Reverted */
-            /*print2("\n");*/ /* Add a blank line to make clipping easier */
-          }
-          if (switchPos("/ COMPRESSED")) {
-            printLongLine(cat(space(indentation), str1, " $.", NULL),
-              space(indentation), "& "); /* "&" is special flag to break
-                  compressed part of proof anywhere */
-          } else {
-            printLongLine(cat(space(indentation), str1," $.", NULL),
-              space(indentation), " ");
-          }
-
-          /* 24-Apr-2015 nm */
-          l = (long)(strlen(str1)); /* Save length for printout below */
-
-          /* 3-May-2017 nm Rewrote the if block below */
-          /* 12-Jun-2011 nm Removed pipFlag condition so that a date
-             stamp will always be created if it doesn't exist */
-          if /*(pipFlag)*/ (1  /* Add the date proof was created */
-              /* 19-Apr-2015 so */
-              /* SOREAR Only generate date if the proof looks complete.
-                 This is not intended as a grading mechanism, just trying
-                 to avoid premature output */
-              && !nmbrElementIn(1, nmbrSaveProof, -(long)'?')) {
-
-            /* 3-May-2017 nm */
-            /* Add a "(Contributed by...)" date if it isn't there */
-            let(&str2, "");
-            str2 = getContrib(outStatement, CONTRIBUTOR);
-            if (str2[0] == 0) { /* The is no contributor, so add one */
-
-              /* 14-May-2017 nm */
-              /* See if there is a date below the proof (for converting old
-                 .mm files).  Someday this will be obsolete, with str3 and
-                 str4 always returned as "". */
-              getProofDate(outStatement, &str3, &str4);
-              /* If there are two dates below the proof, the first on is
-                 the revision date and the second the "Contributed by" date. */
-              if (str4[0] != 0) { /* There are 2 dates below the proof */
-                let(&str5, str3); /* 1st date is Revised by... */
-                let(&str3, str4); /* 2nd date is Contributed by... */
-              } else {
-                let(&str5, "");
-              }
-              /* If there is no date below proof, use today's date */
-              if (str3[0] == 0) let(&str3, date());
-              let(&str4, cat("\n", space(indentation + 1),
-                  /*"(Contributed by ?who?, ", date(), ".) ", NULL));*/
-                  "(Contributed by ", g_contributorName,
-                      ", ", str3, ".) ", NULL)); /* 14-May-2017 nm */
-              /* If there is a 2nd date below proof, add a "(Revised by..."
-                 tag */
-              if (str5[0] != 0) {
-                /* Use the DEFAULT_CONTRIBUTOR ?who? because we don't
-                   know the reviser name (using the contributor name may
-                   be incorrect).  Also, this will trigger a warning in
-                   VERIFY MARKUP since it may be a proof shortener rather than
-                   a reviser. */
-                let(&str4, cat(str4, "\n", space(indentation + 1),
-                    "(Revised by ", DEFAULT_CONTRIBUTOR,
-                        ", ", str5, ".) ", NULL)); /* 15-May-2017 nm */
-              }
-
-              let(&str3, space(g_Statement[outStatement].labelSectionLen));
-              /* str3 will have the statement's label section w/ comment */
-              memcpy(str3, g_Statement[outStatement].labelSectionPtr,
-                  (size_t)(g_Statement[outStatement].labelSectionLen));
-              i = rinstr(str3, "$)");  /* The last "$)" occurrence */
-              if (i != 0    /* A description comment exists */
-                  && saveFlag) { /* and we are saving the proof */
-                /* This isn't a perfect wrapping but we assume
-                   'write source .../rewrap' will be done eventually. */
-                /* str3 will have the updated comment */
-                let(&str3, cat(left(str3, i - 1), str4, right(str3, i), NULL));
-                if (g_Statement[outStatement].labelSectionChanged == 1) {
-                  /* Deallocate old comment if not original source */
-                  let(&str4, ""); /* Deallocate any previous str4 content */
-                  str4 = g_Statement[outStatement].labelSectionPtr;
-                  let(&str4, ""); /* Deallocate the old content */
-                }
-                /* Set flag that this is not the original source */
-                g_Statement[outStatement].labelSectionChanged = 1;
-                g_Statement[outStatement].labelSectionLen = (long)strlen(str3);
-                /* We do a direct assignment instead of let(&...) because
-                   labelSectionPtr may point to the middle of the giant input
-                   file buffer, which we don't want to deallocate */
-                g_Statement[outStatement].labelSectionPtr = str3;
-                /* Reset str3 without deallocating with let(), since it
-                   was assigned to labelSectionPtr */
-                str3 = "";
-                /* Reset the cache for this statement in getContrib() */
-                str3 = getContrib(outStatement, GC_RESET_STMT);
-              } /* if i != 0 */
-
-#ifdef DATE_BELOW_PROOF /* 12-May-2017 nm */
-
-              /* Add a date below the proof.  It actually goes in the
-                 label section of the next statement; the proof section
-                 is not changed. */
-              /* (This will become obsolete eventually) */
-              let(&str3, space(g_Statement[outStatement + 1].labelSectionLen));
-              /* str3 will have the next statement's label section w/ comment */
-              memcpy(str3, g_Statement[outStatement + 1].labelSectionPtr,
-                  (size_t)(g_Statement[outStatement + 1].labelSectionLen));
-              let(&str5, ""); /* We need to guarantee this
-                                for the screen printout later */
-              if (instr(1, str3, "$( [") == 0) {
-                /* There is no date below proof (if there is, don't do
-                   anything; if it is wrong, 'verify markup' will check it) */
-
-                /* Save str5 for screen printout later! */
-                let(&str5, cat(space(indentation),
-                    "$( [", date(), "] $)", NULL)); /* str4 will be used
-                                          for the screen printout later */
-
-                if (saveFlag) { /* save proof, not show proof */
-
-                  if (g_Statement[outStatement + 1].labelSectionChanged == 1) {
-                    /* Deallocate old comment if not original source */
-                    let(&str4, ""); /* Deallocate any previous str4 content */
-                    str4 = g_Statement[outStatement + 1].labelSectionPtr;
-                    let(&str4, ""); /* Deallocate the old content */
-                  }
-                  /* str3 starts after the "$." ending the proof, and should
-                     start with "\n" */
-                  let(&str3, edit(str3, 8/* Discard leading spaces and tabs */));
-                  if (str3[0] != '\n') let(&str3, cat("\n", str3, NULL));
-                  /* Add the date after the proof */
-                  let(&str3, cat("\n", str5, str3, NULL));
-                  /* Set flag that this is not the original source */
-                  g_Statement[outStatement + 1].labelSectionChanged = 1;
-                  g_Statement[outStatement + 1].labelSectionLen
-                      = (long)strlen(str3);
-                  /* We do a direct assignment instead of let(&...) because
-                     labelSectionPtr may point to the middle of the giant input
-                     file buffer, which we don't want to deallocate */
-                  g_Statement[outStatement + 1].labelSectionPtr = str3;
-                  /* Reset str3 without deallocating with let(), since it
-                     was assigned to labelSectionPtr */
-                  str3 = "";
-                  /* Reset the cache for this statement in getContrib() */
-                  str3 = getContrib(outStatement + 1, GC_RESET_STMT);
-                } /* if saveFlag */
-              } /* if (instr(1, str3, "$( [") == 0) */
-
-#endif /* end #ifdef DATE_BELOW_PROOF */ /* 12-May-2017 nm */
-
-            } /* if str2[0] == 0 */
-
-            /* At this point, str4 contains the "$( [date] $)" comment
-               if it would have been added to the saved proof, for use
-               by "show proof" */
-
-
-            /********* deleted 3-May-2017 - date below proof is obsolete
-            /@ 12-Jun-2011 nm Removed pipFlag condition so that a date
-               stamp will always be created if it doesn't exist @/
-            if ( /@ pipFlag && @/ !instr(1, str2, "$([")
-                /@ 7-Sep-04 Allow both "$([<date>])$" and "$( [<date>] )$" @/
-                /@ 19-Apr-2015 so @/
-                /@ SOREAR Only generate date if the proof looks complete.
-                   This is not intended as a grading mechanism, just trying
-                   to avoid premature output @/
-                && !nmbrElementIn(1, nmbrSaveProof, -(long)'?')
-                && !instr(1, str2, "$( [")) {
-            /@ 6/13/98 end @/
-              /@ No date stamp existed before.  Create one for today's
-                 date.  Note that the characters after "$." at the end of
-                 the proof normally go in the labelSection of the next
-                 statement, but a special mode in outputStatement() (in
-                 mmpars.c) will output the date stamp characters for a saved
-                 proof. @/
-              /@ print2("%s$([%s]$)\n", space(indentation), date()); @/
-              /@ 4/23/04 nm Initialize with a "?" date followed by today's
-                 date.  The "?" date can be edited by the user when the
-                 proof is becomes "official." @/
-              /@print2("%s$([?]$) $([%s]$)\n", space(indentation), date());@/
-              /@ 7-Sep-04 Put space around "[<date>]" @/
-              /@print2("%s$( [?] $) $( [%s] $)\n", space(indentation), date());@/
-              /@ 30-Nov-2013 remove the unknown date placeholder @/
-              print2("%s$( [%s] $)\n", space(indentation), date());
-            } else {
-              if (saveFlag && (instr(1, str2, "$([")
-                  /@ 7-Sep-04 Allow both "$([<date>])$" and "$( [<date>] )$" @/
-                  || instr(1, str2, "$( ["))) {
-                /@ An old date stamp existed, and we're saving the proof to
-                   the output file.  Make sure the indentation of the old
-                   date stamp (which exists in the labelSection of the
-                   next statement) matches the indentation of the saved
-                   proof.  To do this, we "delete" the indentation spaces
-                   on the old date in the labelSection of the next statement,
-                   and we put the actual required indentation spaces at
-                   the end of the saved proof.  This is done because the
-                   labelSectionPtr of the next statement does not point to
-                   an isolated string that can be allocated/deallocated but
-                   rather to a place in the input source buffer. @/
-                /@ Correct the indentation on old date @/
-                while ((g_Statement[outStatement + 1].labelSectionPtr)[0] !=
-                    '$') {
-                  /@ "Delete" spaces before old date (by moving source
-                     buffer pointer forward), and also "delete"
-                     the \n that comes before those spaces @/
-                  /@ If the proof is saved a 2nd time, this loop will
-                     not be entered because the pointer will already be
-                     at the "$". @/
-                  (g_Statement[outStatement + 1].labelSectionPtr)++;
-                  (g_Statement[outStatement + 1].labelSectionLen)--;
-                }
-                if (!g_outputToString) bug(1115);
-                /@ The final \n will not appear in final output (done in
-                   outputStatement() in mmpars.c) because the proofSectionLen
-                   below is adjusted to omit it.  This will allow the
-                   space(indentation) to appear before the old date without an
-                   intervening \n. @/
-                print2("%s\n", space(indentation));
-              }
-            }
-            ******** end of deletion 3-May-2017 ******/
-          } /* if / *(pipFlag)* / (1) */
-
-          if (saveFlag) {
-            g_sourceChanged = 1;
-            g_proofChanged = 0;
-            if (processUndoStack(NULL, PUS_GET_STATUS, "", 0)) {
-              /* The UNDO stack may not be empty */
-              proofSavedFlag = 1; /* UNDO stack empty no longer reliably
-                             indicates that proof hasn't changed */
-            }
-
-            /******** deleted 3-May-2017 nm (before proofSectionChanged added)
-            /@ ASCII 1 is a flag that string was allocated and not part of
-               original source file text buffer @/
-            let(&g_printString, cat(chr(1), "\n", g_printString, NULL));
-            if (g_Statement[outStatement].proofSectionPtr[-1] == 1) {
-              /@ Deallocate old proof if not original source @/
-              let(&str1, ""); /@ Deallocate any previous str1 content @/
-              str1 = g_Statement[outStatement].proofSectionPtr - 1;
-              let(&str1, ""); /@ Deallocate the proof section @/
-            }
-            g_Statement[outStatement].proofSectionPtr = g_printString + 1;
-            /@ Subtr 1 char for ASCII 1 at beg, 1 char for "\n" at the end
-               (which is the first char of next statement's labelSection) @/
-            g_Statement[outStatement].proofSectionLen
-                = (long)strlen(g_printString) - 2;
-            /@ Reset g_printString without deallocating @/
-            g_printString = "";
-            g_outputToString = 0;
-            **********/
-
-            /* 3-May-2017 nm */
-            /* Add an initial \n which will go after the "$=" and the
-               beginning of the proof */
-            let(&g_printString, cat("\n", g_printString, NULL));
-            if (g_Statement[outStatement].proofSectionChanged == 1) {
-              /* Deallocate old proof if not original source */
-              let(&str1, ""); /* Deallocate any previous str1 content */
-              str1 = g_Statement[outStatement].proofSectionPtr;
-              let(&str1, ""); /* Deallocate the proof section */
-            }
-            /* Set flag that this is not the original source */
-            g_Statement[outStatement].proofSectionChanged = 1;
-            if (strcmp(" $.\n",
-                right(g_printString, (long)strlen(g_printString) - 3))) {
-              bug(1128);
-            }
-            /* Note that g_printString ends with "$.\n", but those 3 characters
-               should not be in the proofSection.  (The "$." keyword is
-               added between proofSection and next labelSection when the
-               output is written by writeOutput.)  Thus we subtract 3
-               from the length.  But there is no need to truncate the
-               string; later deallocation will take care of the whole
-               string. */
-            g_Statement[outStatement].proofSectionLen
-                = (long)strlen(g_printString) - 3;
-            /* We do a direct assignment instead of let(&...) because
-               proofSectionPtr may point to the middle of the giant input
-               file string, which we don't want to deallocate */
-            g_Statement[outStatement].proofSectionPtr = g_printString;
-            /* Reset g_printString without deallocating with let(), since it
-               was assigned to proofSectionPtr */
-            g_printString = "";
-            g_outputToString = 0;
-
-
-            if (!pipFlag) {
-              if (!(fastFlag && !strcmp("*", labelMatch))) {
-                printLongLine(
-                    cat("The proof of \"", g_Statement[outStatement].labelName,
-                    "\" has been reformatted and saved internally.",
-                    NULL), "", " ");
-              }
-            } else {
-              printLongLine(cat("The new proof of \"", g_Statement[outStatement].labelName,
-                  "\" has been saved internally.",
-                  NULL), "", " ");
-            }
-
-            /* 16-Aug-2016 nm */
-            if (printTime == 1) {
-              getRunTime(&timeIncr);
-              print2("SAVE PROOF run time = %6.2f sec for \"%s\"\n", timeIncr,
-                  g_Statement[outStatement].labelName);
-            }
-
-          } else {
-
-#ifdef DATE_BELOW_PROOF /* 12-May-2017 nm */
-
-            /* Print the date on the screen if it would be added to the file */
-            if (str5[0] != 0) print2("%s\n", str5);
-
-#endif /*#ifdef DATE_BELOW_PROOF*/ /* 12-May-2017 nm */
-
-            /* 19-Apr-2015 so */
-            /* 24-Apr-2015 nm Reverted */
-            /*print2("\n");*/ /* Add a blank line to make clipping easier */
-            print2(cat(
-                "---------The proof of \"", g_Statement[outStatement].labelName,
-                /* "\" to clip out ends above this line.\n",NULL)); */
-                /* 24-Apr-2015 nm */
-                "\" (", str((double)l), " bytes) ends above this line.\n", NULL));
-          } /* End if saveFlag */
-          nmbrLet(&nmbrSaveProof, NULL_NMBRSTRING);
-          if (pipFlag) break; /* Only one iteration for NEW_PROOF stuff */
-          continue;  /* to next s iteration */
-        } /* end if (switchPos("/ PACKED") || switchPos("/ NORMAL") ||
-            switchPos("/ COMPRESSED") || switchPos("/ EXPLICIT") || saveFlag) */
-
-        if (saveFlag) bug(1112); /* Shouldn't get here */
-
-        if (!pipFlag) {
-          outStatement = g_showStatement;
-        } else {
-          outStatement = g_proveStatement;
-        }
-        if (texFlag) {
-          g_outputToString = 1; /* Flag for print2 to add to g_printString */
-          if (!g_htmlFlag) {
-            if (!g_oldTexFlag) {
-              /* 14-Sep-2010 nm */
-              print2("\\begin{proof}\n");
-              print2("\\begin{align}\n");
-            } else {
-              print2("\n");
-              print2("\\vspace{1ex} %%1\n");
-              printLongLine(cat("Proof of ",
-                  "{\\tt ",
-                  asciiToTt(g_Statement[outStatement].labelName),
-                  "}:", NULL), "", " ");
-              print2("\n");
-              print2("\n");
-            }
-          } else { /* g_htmlFlag */
-            bug(1118);
-            /*???? The code below is obsolete - now down in show statement*/
-            /*
-            print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s>\n",
-                MINT_BACKGROUND_COLOR);
-            print2("<CAPTION><B>Proof of Theorem <FONT\n");
-            printLongLine(cat("   COLOR=", GREEN_TITLE_COLOR, ">",
-                asciiToTt(g_Statement[outStatement].labelName),
-                "</FONT></B></CAPTION>", NULL), "", "\"");
-            print2(
-                "<TR><TD><B>Step</B></TD><TD><B>Hyp</B></TD><TD><B>Ref</B>\n");
-            print2("</TD><TD><B>Expression</B></TD></TR>\n");
-            */
-          }
-          g_outputToString = 0;
-          /* 8/26/99: Obsolete: */
-          /* printTexLongMath in typeProof will do this
-          fprintf(g_texFilePtr, "%s", g_printString);
-          let(&g_printString, "");
-          */
-          /* 8/26/99: printTeXLongMath now clears g_printString in LaTeX
-             mode before starting its output, so we must put out the
-             g_printString ourselves here */
-          fprintf(g_texFilePtr, "%s", g_printString);
-          let(&g_printString, ""); /* We'll clr it anyway */
-        } else { /* !texFlag */
-          /* Terminal output - display the statement if wildcard is used */
-          if (!pipFlag) {
-            /* 30-Jan-06 nm Added single-character-match wildcard argument */
-            if (instr(1, labelMatch, "*") || instr(1, labelMatch, "?")) {
-              if (!print2("Proof of \"%s\":\n", g_Statement[outStatement].labelName))
-                break; /* Break for speedup if user quit */
-            }
-          }
-        }
-
-
-        if (texFlag) print2("Outputting proof of \"%s\"...\n",
-            g_Statement[outStatement].labelName);
-
-        typeProof(outStatement,
-            pipFlag,
-            startStep,
-            endStep,
-            endIndent,
-            essentialFlag,
-
-            /* 1-May-2017 nm */
-            /* In SHOW PROOF xxx /TEX, we use renumber steps mode so that
-               the first step is step 1.  The step number is checked for
-               step 1 in mmwtex.c to prevent a spurious \\ (newline) at the
-               start of the proof.  Note that
-               SHOW PROOF is not available in HTML mode, so texFlag will
-               always mean LaTeX mode here. */
-            (texFlag ? 1 : renumberFlag),
-            /*was: renumberFlag,*/
-
-            unknownFlag,
-            notUnifiedFlag,
-            reverseFlag,
-
-            /* 1-May-2017 nm */
-            /* In SHOW PROOF xxx /TEX, we use Lemmon mode so that the
-               hypothesis reference list will be available.  Note that
-               SHOW PROOF is not available in HTML mode, so texFlag will
-               always mean LaTeX mode here. */
-            (texFlag ? 1 : noIndentFlag),
-            /*was: noIndentFlag,*/
-
-            splitColumn,
-            skipRepeatedSteps, /* 28-Jun-2013 nm */
-            texFlag,
-            g_htmlFlag);
-        if (texFlag) {
-          if (!g_htmlFlag) {
-            /* 14-Sep-2010 nm */
-            if (!g_oldTexFlag) {
-              g_outputToString = 1;
-              print2("\\end{align}\n");
-              print2("\\end{proof}\n");
-              print2("\n");
-              g_outputToString = 0;
-              fprintf(g_texFilePtr, "%s", g_printString);
-              let(&g_printString, "");
-            } else {
-            }
-          } else { /* g_htmlFlag */
-            g_outputToString = 1;
-            print2("</TABLE>\n");
-            print2("</CENTER>\n");
-            /* print trailer will close out string later */
-            g_outputToString = 0;
-          }
-        }
-
-        /*???CLEAN UP */
-        /*nmbrLet(&g_WrkProof.proofString, nmbrSaveProof);*/
-
-        /*E*/ if (0) { /* for debugging: */
-          printLongLine(nmbrCvtRToVString(g_WrkProof.proofString,
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum, used only if explicitTargets*/)," "," ");
-          print2("\n");
-
-          nmbrLet(&nmbrSaveProof, nmbrSquishProof(g_WrkProof.proofString));
-          printLongLine(nmbrCvtRToVString(nmbrSaveProof,
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum, used only if explicitTargets*/)," "," ");
-          print2("\n");
-
-          nmbrLet(&nmbrTmp, nmbrUnsquishProof(nmbrSaveProof));
-          printLongLine(nmbrCvtRToVString(nmbrTmp,
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum, used only if explicitTargets*/)," "," ");
-
-          nmbrLet(&nmbrTmp, nmbrGetTargetHyp(nmbrSaveProof,g_showStatement));
-          printLongLine(nmbrCvtAnyToVString(nmbrTmp)," "," "); print2("\n");
-
-          nmbrLet(&nmbrTmp, nmbrGetEssential(nmbrSaveProof));
-          printLongLine(nmbrCvtAnyToVString(nmbrTmp)," "," "); print2("\n");
-
-          cleanWrkProof(); /* Deallocate verifyProof storage */
-        } /* end debugging */
-
-        if (pipFlag) break; /* Only one iteration for NEW_PROOF stuff */
-      } /* Next stmt */
-      if (!q) {
-        /* No matching statement was found */
-        printLongLine(cat("?There is no $p statement whose label matches \"",
-            (cmdMatches("MIDI")) ? g_fullArg[1] : g_fullArg[2],
-            "\".  ",
-            "Use SHOW LABELS to see list of valid labels.", NULL), "", " ");
-      } else {
-        if (saveFlag) {
-          print2("Remember to use WRITE SOURCE to save changes permanently.\n");
-        }
-        if (texFlag) {
-          print2("The LaTeX source was written to \"%s\".\n", g_texFileName);
-         /* 14-Sep-2010 nm Added OLD_TEX */
-         g_oldTexFlag = 0;
-        }
-      }
-
-      continue;
-    } /* if (cmdMatches("SHOW/SAVE [NEW_]PROOF" or" MIDI") */
-
-
-/*E*/ /*???????? DEBUG command for debugging only */
-    if (cmdMatches("DBG")) {
-      print2("DEBUGGING MODE IS FOR DEVELOPER'S USE ONLY!\n");
-      print2("Argument:  %s\n", g_fullArg[1]);
-      nmbrLet(&nmbrTmp, parseMathTokens(g_fullArg[1], g_proveStatement));
-      for (j = 0; j < 3; j++) {
-        print2("Trying depth %ld\n", j);
-        nmbrTmpPtr = proveFloating(nmbrTmp, g_proveStatement, j, 0, 0,
-            1/*overrideFlag*/, 1/*mathboxFlag*/);
-        if (nmbrLen(nmbrTmpPtr)) break;
-      }
-
-      print2("Result:  %s\n", nmbrCvtRToVString(nmbrTmpPtr,
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum, used only if explicitTargets*/));
-      nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);
-
-      continue;
-    }
-/*E*/ /*???????? DEBUG command for debugging only */
-
-    if (cmdMatches("PROVE")) {
-
-      /* 14-Sep-2012 nm */
-      /* Get the unique statement matching the g_fullArg[1] pattern */
-      i = getStatementNum(g_fullArg[1],
-          1/*startStmt*/,
-          g_statements + 1  /*maxStmt*/,
-          0/*aAllowed*/,
-          1/*pAllowed*/,
-          0/*eAllowed*/,
-          0/*fAllowed*/,
-          0/*efOnlyForMaxStmt*/,
-          1/*uniqueFlag*/);
-      if (i == -1) {
-        continue; /* Error msg was provided if not unique */
-      }
-      g_proveStatement = i;
-
-
-      /* 10-May-2016 nm */
-      /* 1 means to override usage locks */
-      overrideFlag = ( (switchPos("/ OVERRIDE")) ? 1 : 0)
-         || g_globalDiscouragement == 0;
-      if (getMarkupFlag(g_proveStatement, PROOF_DISCOURAGED)) {
-        if (overrideFlag == 0) {
-          /* print2("\n"); */ /* Enable for more emphasis */
-          print2(
- ">>> ?Error: Modification of this statement's proof is discouraged.\n"
-              );
-          print2(
- ">>> You must use PROVE ... / OVERRIDE to work on it.\n");
-          /* print2("\n"); */ /* Enable for more emphasis */
-          continue;
-        }
-      }
-
-
-      /*********** 14-Sep-2012 replaced by getStatementNum()
-      /@??? Make sure only $p statements are allowed. @/
-      for (i = 1; i <= g_statements; i++) {
-        if (!strcmp(g_fullArg[1],g_Statement[i].labelName)) break;
-      }
-      if (i > g_statements) {
-        printLongLine(cat("?There is no statement with label \"",
-            g_fullArg[1], "\".  ",
-            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
-        g_proveStatement = 0;
-        continue;
-      }
-      g_proveStatement = i;
-      if (g_Statement[g_proveStatement].type != (char)p_) {
-        printLongLine(cat("?Statement \"", g_fullArg[1],
-            "\" is not a $p statement.", NULL), "", " ");
-        g_proveStatement = 0;
-        continue;
-      }
-      ************** end of 14-Sep-2012 deletion ************/
-
-      print2(
-"Entering the Proof Assistant.  HELP PROOF_ASSISTANT for help, EXIT to exit.\n");
-
-      /* Obsolete:
-      print2("You will be working on the proof of statement %s:\n",
-          g_Statement[g_proveStatement].labelName);
-      printLongLine(cat("  $p ", nmbrCvtMToVString(
-          g_Statement[g_proveStatement].mathString), NULL), "    ", " ");
-      */
-
-      g_PFASmode = 1; /* Set mode for commands here and in mmcmdl.c */
-      /* Note:  Proof Assistant mode can equivalently be determined by:
-            nmbrLen(g_ProofInProgress.proof) != 0  */
-
-      parseProof(g_proveStatement);
-      verifyProof(g_proveStatement); /* Necessary to set RPN stack ptrs
-                                      before calling cleanWrkProof() */
-      if (g_WrkProof.errorSeverity > 1) {
-        print2(
-             "The starting proof has a severe error.  It will not be used.\n");
-        nmbrLet(&nmbrSaveProof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
-      } else {
-        nmbrLet(&nmbrSaveProof, g_WrkProof.proofString);
-      }
-      cleanWrkProof(); /* Deallocate verifyProof storage */
-
-      /* 11-Sep-2016 nm */
-      /* Initialize the structure needed for the Proof Assistant */
-      initProofStruct(&g_ProofInProgress, nmbrSaveProof, g_proveStatement);
-
-      /****** 11-Sep-2016 nm Replaced by initProofStruct()
-      /@ Right now, only non-packed proofs are handled. @/
-      nmbrLet(&nmbrSaveProof, nmbrUnsquishProof(nmbrSaveProof));
-
-      /@ Assign initial proof structure @/
-      if (nmbrLen(g_ProofInProgress.proof)) bug(1113); /@ Should've been deall.@/
-      nmbrLet(&g_ProofInProgress.proof, nmbrSaveProof);
-      i = nmbrLen(g_ProofInProgress.proof);
-      pntrLet(&g_ProofInProgress.target, pntrNSpace(i));
-      pntrLet(&g_ProofInProgress.source, pntrNSpace(i));
-      pntrLet(&g_ProofInProgress.user, pntrNSpace(i));
-      nmbrLet((nmbrString @@)(&((g_ProofInProgress.target)[i - 1])),
-          g_Statement[g_proveStatement].mathString);
-      g_pipDummyVars = 0;
-
-      /@ Assign known subproofs @/
-      assignKnownSubProofs();
-
-      /@ Initialize remaining steps @/
-      for (j = 0; j < i/@proof length@/; j++) {
-        if (!nmbrLen((g_ProofInProgress.source)[j])) {
-          initStep(j);
-        }
-      }
-
-      /@ Unify whatever can be unified @/
-      autoUnify(0); /@ 0 means no "congrats" message @/
-      **** end of 11-Sep-2016 deletion ************/
-
-/*
-      print2(
-"Periodically save your work with SAVE NEW_PROOF and WRITE SOURCE.\n");
-      print2(
-"There is no UNDO command yet.  You can OPEN LOG to track what you've done.\n");
-*/
-      /* Show the user the statement to be proved */
-      print2("You will be working on statement (from \"SHOW STATEMENT %s\"):\n",
-          g_Statement[g_proveStatement].labelName);
-      typeStatement(g_proveStatement /*g_showStatement*/,
-          1 /*briefFlag*/,
-          0 /*commentOnlyFlag*/,
-          0 /*texFlag*/,
-          0 /*g_htmlFlag*/);
-
-      if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
-        print2(
-        "Note:  The proof you are starting with is already complete.\n");
-      } else {
-
-        print2(
-     "Unknown step summary (from \"SHOW NEW_PROOF / UNKNOWN\"):\n");
-        /* 6/14/98 - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-        typeProof(g_proveStatement,
-            1 /*pipFlag*/,
-            0 /*startStep*/,
-            0 /*endStep*/,
-            0 /*endIndent*/,
-            1 /*essentialFlag*/,
-            0 /*renumberFlag*/,
-            1 /*unknownFlag*/,
-            0 /*notUnifiedFlag*/,
-            0 /*reverseFlag*/,
-            0 /*noIndentFlag*/,
-            0 /*splitColumn*/,
-            0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-            0 /*texFlag*/,
-            0 /*g_htmlFlag*/);
-        /* 6/14/98 end */
-      }
-
-      /* 3-May-2016 nm */
-      if (getMarkupFlag(g_proveStatement, PROOF_DISCOURAGED)) {
-        /* print2("\n"); */ /* Enable for more emphasis */
-        print2(
-">>> ?Warning: Modification of this statement's proof is discouraged.\n"
-            );
-        /*
-        print2(
-">>> You must use SAVE NEW_PROOF ... / OVERRIDE to save it.\n");
-        */
-        /* print2("\n"); */ /* Enable for more emphasis */
-      }
-
-      processUndoStack(NULL, PUS_INIT, "", 0); /* Optional? */
-      /* Put the initial proof into the UNDO stack; we don't need
-         the info string since it won't be undone */
-      processUndoStack(&g_ProofInProgress, PUS_PUSH, "", 0);
-      continue;
-    }
-
-
-    /* 1-Nov-2013 nm Added UNDO */
-    if (cmdMatches("UNDO")) {
-      processUndoStack(&g_ProofInProgress, PUS_UNDO, "", 0);
-      g_proofChanged = 1; /* Maybe make this more intelligent some day */
-      /* 6/14/98 - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-      typeProof(g_proveStatement,
-          1 /*pipFlag*/,
-          0 /*startStep*/,
-          0 /*endStep*/,
-          0 /*endIndent*/,
-          1 /*essentialFlag*/,
-          0 /*renumberFlag*/,
-          1 /*unknownFlag*/,
-          0 /*notUnifiedFlag*/,
-          0 /*reverseFlag*/,
-          0 /*noIndentFlag*/,
-          0 /*splitColumn*/,
-          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-          0 /*texFlag*/,
-          0 /*g_htmlFlag*/);
-      /* 6/14/98 end */
-      continue;
-    }
-
-    /* 1-Nov-2013 nm Added REDO */
-    if (cmdMatches("REDO")) {
-      processUndoStack(&g_ProofInProgress, PUS_REDO, "", 0);
-      g_proofChanged = 1; /* Maybe make this more intelligent some day */
-      /* 6/14/98 - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-      typeProof(g_proveStatement,
-          1 /*pipFlag*/,
-          0 /*startStep*/,
-          0 /*endStep*/,
-          0 /*endIndent*/,
-          1 /*essentialFlag*/,
-          0 /*renumberFlag*/,
-          1 /*unknownFlag*/,
-          0 /*notUnifiedFlag*/,
-          0 /*reverseFlag*/,
-          0 /*noIndentFlag*/,
-          0 /*splitColumn*/,
-          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-          0 /*texFlag*/,
-          0 /*g_htmlFlag*/);
-      /* 6/14/98 end */
-      continue;
-    }
-
-    if (cmdMatches("UNIFY")) {
-      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-      g_proofChangedFlag = 0;
-      if (cmdMatches("UNIFY STEP")) {
-
-        s = (long)val(g_fullArg[2]); /* Step number */
-        if (s > m || s < 1) {
-          print2("?The step must be in the range from 1 to %ld.\n", m);
-          continue;
-        }
-
-        interactiveUnifyStep(s - 1, 1); /* 2nd arg. means print msg if
-                                           already unified */
-
-        /* (The interactiveUnifyStep handles all messages.) */
-        /* print2("... */
-
-        autoUnify(1);
-        if (g_proofChangedFlag) {
-          g_proofChanged = 1; /* Cumulative flag */
-          processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-        }
-        continue;
-      }
-
-      /* "UNIFY ALL" */
-      if (!switchPos("/ INTERACTIVE")) {
-        autoUnify(1);
-        if (!g_proofChangedFlag) {
-          print2("No new unifications were made.\n");
-        } else {
-          print2(
-  "Steps were unified.  SHOW NEW_PROOF / NOT_UNIFIED to see any remaining.\n");
-          g_proofChanged = 1; /* Cumulative flag */
-          processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-        }
-      } else {
-        q = 0;
-        while (1) {
-          /* Repeat the unifications over and over until done, since
-             a later unification may improve the ability of an aborted earlier
-             one to be done without timeout */
-          g_proofChangedFlag = 0; /* This flag is set by autoUnify() and
-                                   interactiveUnifyStep() */
-          autoUnify(0);
-          for (s = m - 1; s >= 0; s--) {
-            interactiveUnifyStep(s, 0); /* 2nd arg. means no msg if already
-                                           unified */
-          }
-          autoUnify(1); /* 1 means print congratulations if complete */
-          if (!g_proofChangedFlag) {
-            if (!q) {
-              print2("No new unifications were made.\n");
-            } else {
-              /* If q=1, then we are in the 2nd or later pass, which means
-                 there was a change in the 1st pass. */
-              print2(
-  "Steps were unified.  SHOW NEW_PROOF / NOT_UNIFIED to see any remaining.\n");
-              g_proofChanged = 1; /* Cumulative flag */
-              processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-            }
-            break; /* while (1) */
-          } else {
-            q = 1; /* Flag that we're starting a 2nd or later pass */
-          }
-        } /* End while (1) */
-      }
-      /* 6/14/98 - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-      typeProof(g_proveStatement,
-          1 /*pipFlag*/,
-          0 /*startStep*/,
-          0 /*endStep*/,
-          0 /*endIndent*/,
-          1 /*essentialFlag*/,
-          0 /*renumberFlag*/,
-          1 /*unknownFlag*/,
-          0 /*notUnifiedFlag*/,
-          0 /*reverseFlag*/,
-          0 /*noIndentFlag*/,
-          0 /*splitColumn*/,
-          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-          0 /*texFlag*/,
-          0 /*g_htmlFlag*/);
-      /* 6/14/98 end */
-      continue;
-    }
-
-    if (cmdMatches("MATCH")) {
-
-      maxEssential = -1; /* Default:  no maximum */
-      i = switchPos("/ MAX_ESSENTIAL_HYP");
-      if (i) maxEssential = (long)val(g_fullArg[i + 1]);
-
-      if (cmdMatches("MATCH STEP")) {
-
-        s = (long)val(g_fullArg[2]); /* Step number */
-        m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-        if (s > m || s < 1) {
-          print2("?The step must be in the range from 1 to %ld.\n", m);
-          continue;
-        }
-        if ((g_ProofInProgress.proof)[s - 1] != -(long)'?') {
-          print2(
-    "?Step %ld is already assigned.  Only unknown steps can be matched.\n", s);
-          continue;
-        }
-
-        interactiveMatch(s - 1, maxEssential);
-        n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
-        if (n != m) {
-          if (s != m) {
-            printLongLine(cat("Steps ", str((double)s), ":",
-                str((double)m), " are now ", str((double)(s - m + n)), ":",
-                str((double)n), ".",
-                NULL),
-                "", " ");
-          } else {
-            printLongLine(cat("Step ", str((double)m), " is now step ", str((double)n), ".",
-                NULL),
-                "", " ");
-          }
-        }
-
-        autoUnify(1);
-        g_proofChanged = 1; /* Cumulative flag */
-        /* 1-Nov-2013 nm Why is g_proofChanged set unconditionally above?
-           Do we need the processUndoStack() call? */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-
-        continue;
-      } /* End if MATCH STEP */
-
-      if (cmdMatches("MATCH ALL")) {
-
-        m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-
-        k = 0;
-        g_proofChangedFlag = 0;
-
-        if (switchPos("/ ESSENTIAL")) {
-          nmbrLet(&nmbrTmp, nmbrGetEssential(g_ProofInProgress.proof));
-        }
-
-        for (s = m; s > 0; s--) {
-          /* Match only unknown steps */
-          if ((g_ProofInProgress.proof)[s - 1] != -(long)'?') continue;
-          /* Match only essential steps if specified */
-          if (switchPos("/ ESSENTIAL")) {
-            if (!nmbrTmp[s - 1]) continue;
-          }
-
-          interactiveMatch(s - 1, maxEssential);
-          if (g_proofChangedFlag) {
-            k = s; /* Save earliest step changed */
-            g_proofChangedFlag = 0;
-          }
-          print2("\n");
-        }
-        if (k) {
-          g_proofChangedFlag = 1; /* Restore it */
-          g_proofChanged = 1; /* Cumulative flag */
-          processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-          print2("Steps %ld and above have been renumbered.\n", k);
-        }
-        autoUnify(1);
-
-        continue;
-      } /* End if MATCH ALL */
-    }
-
-    if (cmdMatches("LET")) {
-
-      g_errorCount = 0;
-      nmbrLet(&nmbrTmp, parseMathTokens(g_fullArg[4], g_proveStatement));
-      if (g_errorCount) {
-        /* Parsing issued error message(s) */
-        g_errorCount = 0;
-        continue;
-      }
-
-      if (cmdMatches("LET VARIABLE")) {
-        if (((vstring)(g_fullArg[2]))[0] != '$') {
-          print2(
-   "?The target variable must be of the form \"$<integer>\", e.g. \"$23\".\n");
-          continue;
-        }
-        n = (long)val(right(g_fullArg[2], 2));
-        if (n < 1 || n > g_pipDummyVars) {
-          print2("?The target variable must be between $1 and $%ld.\n",
-              g_pipDummyVars);
-          continue;
-        }
-
-        replaceDummyVar(n, nmbrTmp);
-
-        autoUnify(1);
-
-
-        g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
-        g_proofChanged = 1; /* Cumulative flag */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-
-      }
-
-      if (cmdMatches("LET STEP")) {
-
-        /* 14-Sep-2012 nm */
-        s = getStepNum(g_fullArg[2], g_ProofInProgress.proof,
-            0 /* ALL not allowed */);
-        if (s == -1) continue;  /* Error; message was provided already */
-
-        /************** 14-Sep-2012 replaced by getStepNum()
-        s = (long)val(g_fullArg[2]); /@ Step number @/
-
-        /@ 16-Apr-06 nm Added LET STEP n where n <= 0: 0 = last,
-           -1 = penultimate, etc. _unknown_ step @/
-        /@ Unlike ASSIGN LAST/FIRST and IMPROVE LAST/FIRST, it probably
-           doesn't make sense to add LAST/FIRST to LET STEP since known
-           steps can also be LET.  The main purpose of LET STEP n, n<=0, is
-           to use with scripting for mmj2 imports. @/
-        offset = 0;
-        if (s <= 0) {
-          offset = - s + 1;
-          s = 1; /@ Temp. until we figure out which step @/
-        }
-        /@ End of 16-Apr-06 @/
-
-        m = nmbrLen(g_ProofInProgress.proof); /@ Original proof length @/
-        if (s > m || s < 1) {
-          print2("?The step must be in the range from 1 to %ld.\n", m);
-          continue;
-        }
-
-        /@ 16-Apr-06 nm Added LET STEP n where n <= 0: 0 = last,
-           1 = penultimate, etc. _unknown_ step @/
-        if (offset > 0) {  /@ step <= 0 @/
-          /@ Get the essential step flags @/
-          s = 0; /@ Use as flag that step was found @/
-          nmbrLet(&essentialFlags, nmbrGetEssential(g_ProofInProgress.proof));
-          /@ Scan proof backwards until last essential unknown step found @/
-          /@ 16-Apr-06 - count back 'offset' unknown steps @/
-          j = offset;
-          for (i = m; i >= 1; i--) {
-            if (essentialFlags[i - 1]
-                && (g_ProofInProgress.proof)[i - 1] == -(long)'?') {
-              j--;
-              if (j == 0) {
-                /@ Found it @/
-                s = i;
-                break;
-              }
-            }
-          } /@ Next i @/
-          if (s == 0) {
-            if (offset == 1) {
-              print2("?There are no unknown essential steps.\n");
-            } else {
-              print2("?There are not at least %ld unknown essential steps.\n",
-                offset);
-            }
-            continue;
-          }
-        } /@ if offset > 0 @/
-        /@ End of 16-Apr-06 @/
-        ******************** end 14-Sep-2012 deletion ********/
-
-        /* Check to see if the statement selected is allowed */
-        if (!checkMStringMatch(nmbrTmp, s - 1)) {
-          printLongLine(cat("?Step ", str((double)s), " cannot be unified with \"",
-              nmbrCvtMToVString(nmbrTmp),"\".", NULL), " ", " ");
-          continue;
-        }
-
-        /* Assign the user string */
-        nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[s - 1])), nmbrTmp);
-
-        autoUnify(1);
-        g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
-        g_proofChanged = 1; /* Cumulative flag */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-      }
-      /* 6/14/98 - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-      typeProof(g_proveStatement,
-          1 /*pipFlag*/,
-          0 /*startStep*/,
-          0 /*endStep*/,
-          0 /*endIndent*/,
-          1 /*essentialFlag*/,
-          0 /*renumberFlag*/,
-          1 /*unknownFlag*/,
-          0 /*notUnifiedFlag*/,
-          0 /*reverseFlag*/,
-          0 /*noIndentFlag*/,
-          0 /*splitColumn*/,
-          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-          0 /*texFlag*/,
-          0 /*g_htmlFlag*/);
-      /* 6/14/98 end */
-      continue;
-    }
-
-
-    if (cmdMatches("ASSIGN")) {
-
-      /* 10/4/99 - Added LAST - this means the last unknown step shown
-         with SHOW NEW_PROOF/ESSENTIAL/UNKNOWN */
-      /* 11-Dec-05 nm - Added FIRST - this means the first unknown step shown
-         with SHOW NEW_PROOF/ESSENTIAL/UNKNOWN */
-      /* 16-Apr-06 nm - Handle nonpositive step number: 0 = last,
-         -1 = penultimate, etc.*/
-      /* 14-Sep-2012 nm All the above now done by getStepNum() */
-      s = getStepNum(g_fullArg[1], g_ProofInProgress.proof,
-          0 /* ALL not allowed */);
-      if (s == -1) continue;  /* Error; message was provided already */
-
-      /* 3-May-2016 nm */
-      /* 1 means to override usage locks */
-      overrideFlag = ( (switchPos("/ OVERRIDE")) ? 1 : 0)
-         || g_globalDiscouragement == 0;
-
-      /****** replaced by getStepNum()  nm 14-Sep-2012
-      offset = 0; /@ 16-Apr-06 @/
-      let(&str1, g_fullArg[1]); /@ To avoid void pointer problems with g_fullArg @/
-      if (toupper((unsigned char)(str1[0])) == 'L'
-          || toupper((unsigned char)(str1[0])) == 'F') {
-                                          /@ "ASSIGN LAST" or "ASSIGN FIRST" @/
-                                          /@ 11-Dec-05 nm @/
-        s = 1; /@ Temporary until we figure out which step @/
-        offset = 1;          /@ 16-Apr-06 @/
-      } else {
-        s = (long)val(g_fullArg[1]); /@ Step number @/
-        if (strcmp(g_fullArg[1], str((double)s))) {
-          print2("?Expected either a number or FIRST or LAST after ASSIGN.\n");
-                                                             /@ 11-Dec-05 nm @/
-          continue;
-        }
-        if (s <= 0) {         /@ 16-Apr-06 @/
-          offset = - s + 1;   /@ 16-Apr-06 @/
-          s = 1; /@ Temporary until we figure out which step @/ /@ 16-Apr-06 @/
-        }                     /@ 16-Apr-06 @/
-      }
-      ******************** end 14-Sep-2012 deletion ********/
-
-      /* 14-Sep-2012 nm */
-      /* Get the unique statement matching the g_fullArg[2] pattern */
-      k = getStatementNum(g_fullArg[2],
-          1/*startStmt*/,
-          g_proveStatement  /*maxStmt*/,
-          1/*aAllowed*/,
-          1/*pAllowed*/,
-          1/*eAllowed*/,
-          1/*fAllowed*/,
-          1/*efOnlyForMaxStmt*/,
-          1/*uniqueFlag*/);
-      if (k == -1) {
-        continue; /* Error msg was provided if not unique */
-      }
-
-      /*********** 14-Sep-2012 replaced by getStatementNum()
-      for (i = 1; i <= g_statements; i++) {
-        if (!strcmp(g_fullArg[2], g_Statement[i].labelName)) {
-          /@ If a $e or $f, it must be a hypothesis of the statement
-             being proved @/
-          if (g_Statement[i].type == (char)e_ || g_Statement[i].type == (char)f_){
-            if (!nmbrElementIn(1, g_Statement[g_proveStatement].reqHypList, i) &&
-                !nmbrElementIn(1, g_Statement[g_proveStatement].optHypList, i))
-                continue;
-          }
-          break;
-        }
-      }
-      if (i > g_statements) {
-        printLongLine(cat("?The statement with label \"",
-            g_fullArg[2],
-            "\" was not found or is not a hypothesis of the statement ",
-            "being proved.  ",
-            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
-        continue;
-      }
-      k = i;
-
-      if (k >= g_proveStatement) {
-        print2(
-   "?You must specify a statement that occurs earlier the one being proved.\n");
-        continue;
-      }
-      ***************** end of 14-Sep-2012 deletion ************/
-
-      /* 3-May-2016 nm */
-      if (getMarkupFlag(k, USAGE_DISCOURAGED)) {
-        if (overrideFlag == 0) {
-          /* print2("\n"); */ /* Enable for more emphasis */
-          print2(
-">>> ?Error: Attempt to assign a statement whose usage is discouraged.\n");
-          print2(
-       ">>> Use ASSIGN ... / OVERRIDE if you really want to do this.\n");
-          /* print2("\n"); */ /* Enable for more emphasis */
-          continue;
-        } else {
-          /* print2("\n"); */ /* Enable for more emphasis */
-          print2(
-">>> ?Warning: You are assigning a statement whose usage is discouraged.\n");
-          /* print2("\n"); */ /* Enable for more emphasis */
-        }
-      }
-
-      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-
-
-      /****** replaced by getStepNum()  nm 14-Sep-2012
-      if (s > m || s < 1) {
-        print2("?The step must be in the range from 1 to %ld.\n", m);
-        continue;
-      }
-
-      /@ 10/4/99 - For ASSIGN FIRST/LAST command, figure out the last unknown
-         essential step @/                      /@ 11-Dec-05 nm - Added LAST @/
-      /@if (toupper(str1[0]) == 'L' || toupper(str1[0]) == 'F') {@/
-                                /@ "ASSIGN LAST or FIRST" @/ /@ 11-Dec-05 nm @/
-      if (offset > 0) {  /@ LAST, FIRST, or step <= 0 @/ /@ 16-Apr-06 @/
-        /@ Get the essential step flags @/
-        s = 0; /@ Use as flag that step was found @/
-        nmbrLet(&essentialFlags, nmbrGetEssential(g_ProofInProgress.proof));
-        /@ if (toupper((unsigned char)(str1[0])) == 'L') { @/
-        if (toupper((unsigned char)(str1[0])) != 'F') {   /@ 16-Apr-06 @/
-          /@ Scan proof backwards until last essential unknown step is found @/
-          /@ 16-Apr-06 - count back 'offset' unknown steps @/
-          j = offset;      /@ 16-Apr-06 @/
-          for (i = m; i >= 1; i--) {
-            if (essentialFlags[i - 1]
-                && (g_ProofInProgress.proof)[i - 1] == -(long)'?') {
-              j--;          /@ 16-Apr-06 @/
-              if (j == 0) {  /@ 16-Apr-06 @/
-                /@ Found it @/
-                s = i;
-                break;
-              }             /@ 16-Apr-06 @/
-            }
-          } /@ Next i @/
-        } else {
-          /@ 11-Dec-05 nm Added ASSIGN FIRST @/
-          /@ Scan proof forwards until first essential unknown step is found @/
-          for (i = 1; i <= m; i++) {
-            if (essentialFlags[i - 1]
-                && (g_ProofInProgress.proof)[i - 1] == -(long)'?') {
-              /@ Found it @/
-              s = i;
-              break;
-            }
-          } /@ Next i @/
-        }
-        if (s == 0) {
-          if (offset == 1) {                                /@ 16-Apr-06 @/
-            print2("?There are no unknown essential steps.\n");
-          } else {                                          /@ 16-Apr-06 @/
-            print2("?There are not at least %ld unknown essential steps.\n",
-              offset);                                      /@ 16-Apr-06 @/
-          }                                                 /@ 16-Apr-06 @/
-          continue;
-        }
-      }
-      ******************** end 14-Sep-2012 deletion ********/
-
-      /* Check to see that the step is an unknown step */
-      if ((g_ProofInProgress.proof)[s - 1] != -(long)'?') {
-        print2(
-        "?Step %ld is already assigned.  You can only assign unknown steps.\n"
-            , s);
-        continue;
-      }
-
-      /* Check to see if the statement selected is allowed */
-      if (!checkStmtMatch(k, s - 1)) {
-        print2("?Statement \"%s\" cannot be unified with step %ld.\n",
-          g_Statement[k].labelName, s);
-        continue;
-      }
-
-      assignStatement(k /*statement#*/, s - 1 /*step*/);
-
-      n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
-      autoUnify(1);
-
-      /* Automatically interact with user if step not unified */
-      /* ???We might want to add a setting to defeat this if user doesn't
-         like it */
-      /* 8-Apr-05 nm Since ASSIGN LAST is often run from a commmand file, don't
-         interact if / NO_UNIFY is specified, so response is predictable */
-      if (switchPos("/ NO_UNIFY") == 0) {
-        interactiveUnifyStep(s - m + n - 1, 2); /* 2nd arg. means print msg if
-                                                 already unified */
-      } /* if NO_UNIFY flag not set */
-
-      /******* 8-Apr-05 nm Commented out:
-      if (m == n) {
-        print2("Step %ld was assigned statement %s.\n",
-          s, g_Statement[k].labelName);
-      } else {
-        if (s != m) {
-          printLongLine(cat("Step ", str((double)s),
-              " was assigned statement ", g_Statement[k].labelName,
-              ".  Steps ", str((double)s), ":",
-              str((double)m), " are now ", str((double)(s - m + n)), ":", str((double)n), ".",
-              NULL),
-              "", " ");
-        } else {
-          printLongLine(cat("Step ", str((double)s),
-              " was assigned statement ", g_Statement[k].labelName,
-              ".  Step ", str((double)m), " is now step ", str((double)n), ".",
-              NULL),
-              "", " ");
-        }
-      }
-      *********/
-      /* 8-Apr-05 nm Added: */
-      /* 1-Nov-2013 nm No longer needed because of UNDO
-      printLongLine(cat("To undo the assignment, DELETE STEP ",
-              str((double)(s - m + n)), " and if needed INITIALIZE, UNIFY.",
-              NULL),
-              "", " ");
-      */
-
-      /* 5-Aug-2020 nm */
-      /* See if it's in another mathbox; if so, let user know */
-      assignMathboxInfo();
-      if (k > g_mathboxStmt && g_proveStatement > g_mathboxStmt) {
-        if (k < g_mathboxStart[getMathboxNum(g_proveStatement) - 1]) {
-          printLongLine(cat("\"", g_Statement[k].labelName,
-                "\" is in the mathbox for ",
-                g_mathboxUser[getMathboxNum(k) - 1], ".",
-                NULL),
-              "", " ");
-        }
-      }
-
-      /* 6/14/98 - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-      typeProof(g_proveStatement,
-          1 /*pipFlag*/,
-          0 /*startStep*/,
-          0 /*endStep*/,
-          0 /*endIndent*/,
-          1 /*essentialFlag*/,
-          0 /*renumberFlag*/,
-          1 /*unknownFlag*/,
-          0 /*notUnifiedFlag*/,
-          0 /*reverseFlag*/,
-          0 /*noIndentFlag*/,
-          0 /*splitColumn*/,
-          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-          0 /*texFlag*/,
-          0 /*g_htmlFlag*/);
-      /* 6/14/98 end */
-
-
-      g_proofChangedFlag = 1; /* Flag to push 'undo' stack (future) */
-      g_proofChanged = 1; /* Cumulative flag */
-      processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-      continue;
-
-    } /* cmdMatches("ASSIGN") */
-
-
-    if (cmdMatches("REPLACE")) {
-
-      /* s = (long)val(g_fullArg[1]);  obsolete */ /* Step number */
-
-      /* 3-May-2016 nm */
-      /* 1 means to override usage locks */
-      overrideFlag = ( (switchPos("/ OVERRIDE")) ? 1 : 0)
-         || g_globalDiscouragement == 0;
-
-      /* 14-Sep-2012 nm */
-      step = getStepNum(g_fullArg[1], g_ProofInProgress.proof,
-          0 /* ALL not allowed */);
-      if (step == -1) continue;  /* Error; message was provided already */
-
-      /* This limitation is due to the assignKnownSteps call below which
-         does not tolerate unknown steps. */
-      /******* 10/20/02  Limitation removed
-      if (nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
-        print2("?The proof must be complete before you can use REPLACE.\n");
-        continue;
-      }
-      *******/
-
-      /* 14-Sep-2012 nm */
-      /* Get the unique statement matching the g_fullArg[2] pattern */
-      stmt = getStatementNum(g_fullArg[2],
-          1/*startStmt*/,
-          g_proveStatement  /*maxStmt*/,
-          1/*aAllowed*/,
-          1/*pAllowed*/,
-          0/*eAllowed*/, /* Not implemented (yet?) */
-          0/*fAllowed*/, /* Not implemented (yet?) */
-          1/*efOnlyForMaxStmt*/,
-          1/*uniqueFlag*/);
-      if (stmt == -1) {
-        continue; /* Error msg was provided if not unique */
-      }
-
-      /*********** 14-Sep-2012 replaced by getStatementNum()
-      for (i = 1; i <= g_statements; i++) {
-        if (!strcmp(g_fullArg[2], g_Statement[i].labelName)) {
-          /@ If a $e or $f, it must be a hypothesis of the statement
-             being proved @/
-          if (g_Statement[i].type == (char)e_ || g_Statement[i].type == (char)f_){
-            if (!nmbrElementIn(1, g_Statement[g_proveStatement].reqHypList, i) &&
-                !nmbrElementIn(1, g_Statement[g_proveStatement].optHypList, i))
-                continue;
-          }
-          break;
-        }
-      }
-      if (i > g_statements) {
-        printLongLine(cat("?The statement with label \"",
-            g_fullArg[2],
-            "\" was not found or is not a hypothesis of the statement ",
-            "being proved.  ",
-            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
-        continue;
-      }
-      k = i;
-
-      if (k >= g_proveStatement) {
-        print2(
-   "?You must specify a statement that occurs earlier the one being proved.\n");
-        continue;
-      }
-      ****************************** end of 14-Sep-2012 deletion *********/
-
-      /* 3-May-2016 nm */
-      if (getMarkupFlag(stmt, USAGE_DISCOURAGED)) {
-        if (overrideFlag == 0) {
-          /* print2("\n"); */ /* Enable for more emphasis */
-          print2(
-">>> ?Error: Attempt to assign a statement whose usage is discouraged.\n");
-          print2(
-       ">>> Use REPLACE ... / OVERRIDE if you really want to do this.\n");
-          /* print2("\n"); */ /* Enable for more emphasis */
-          continue;
-        } else {
-          /* print2("\n"); */ /* Enable for more emphasis */
-          print2(
-">>> ?Warning: You are assigning a statement whose usage is discouraged.\n");
-          /* print2("\n"); */ /* Enable for more emphasis */
-        }
-      }
-
-      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-
-      /************** 14-Sep-2012 replaced by getStepNum()
-      if (s > m || s < 1) {
-        print2("?The step must be in the range from 1 to %ld.\n", m);
-        continue;
-      }
-      ************* end of 14-Sep-2012 deletion **************/
-
-      /* Check to see that the step is a known step */
-      /* 22-Aug-2012 nm This check was deleted because it is unnecessary  */
-      /*
-      if ((g_ProofInProgress.proof)[s - 1] == -(long)'?') {
-        print2(
-        "?Step %ld is unknown.  You can only replace known steps.\n"
-            , s);
-        continue;
-      }
-      */
-
-      /* 10/20/02  Set a flag that proof has unknown steps (for autoUnify()
-         call below) */
-      if (nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
-        p = 1;
-      } else {
-        p = 0;
-      }
-
-      /* Check to see if the statement selected is allowed */
-      if (!checkStmtMatch(stmt, step - 1)) {
-        print2("?Statement \"%s\" cannot be unified with step %ld.\n",
-          g_Statement[stmt].labelName, step);
-        continue;
-      }
-
-      /* 16-Sep-2012 nm */
-      /* Check dummy variable status of step */
-      /* For use in message later */
-      dummyVarIsoFlag = checkDummyVarIsolation(step - 1);
-            /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/
-
-      /* Do the replacement */
-      nmbrTmpPtr = replaceStatement(stmt /*statement#*/,
-          step - 1 /*step*/,
-          g_proveStatement,
-          0,/*scan whole proof to maximize chance of a match*/
-          0/*noDistinct*/,
-          1/* try to prove $e's */,
-          1/*improveDepth*/,
-          overrideFlag,   /* 3-May-2016 nm */
-          /* Currently REPLACE (not often used) allows other mathboxes
-             silently; TODO: we may want to notify user like for ASSIGN */
-          1/*mathboxFlag*/ /* 5-Aug-2020 nm */
-          );
-      if (!nmbrLen(nmbrTmpPtr)) {
-        print2(
-           "?Hypotheses of statement \"%s\" do not match known proof steps.\n",
-            g_Statement[stmt].labelName);
-        continue;
-      }
-
-      /* Get the subproof at step s */
-      q = subproofLen(g_ProofInProgress.proof, step - 1);
-      deleteSubProof(step - 1);
-      addSubProof(nmbrTmpPtr, step - q);
-
-      /* 10/20/02 Replaced "assignKnownSteps" with code from entry of PROVE
-         command so REPLACE can be done in partial proofs */
-      /*assignKnownSteps(s - q, nmbrLen(nmbrTmpPtr));*/  /* old code */
-      /* Assign known subproofs */
-      assignKnownSubProofs();
-      /* Initialize remaining steps */
-      i = nmbrLen(g_ProofInProgress.proof);
-      for (j = 0; j < i; j++) {
-        if (!nmbrLen((g_ProofInProgress.source)[j])) {
-          initStep(j);
-        }
-      }
-      /* Unify whatever can be unified */
-      /* If proof wasn't complete before (p = 1), but is now, print congrats
-         for user */
-      autoUnify((char)p); /* 0 means no "congrats" message */
-      /* end 10/20/02 */
-
-      nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING); /* Deallocate memory */
-
-      n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
-      if (nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
-        /* The proof is not complete, so print step numbers that changed */
-        if (m == n) {
-          print2("Step %ld was replaced with statement %s.\n",
-            step, g_Statement[stmt].labelName);
-        } else {
-          if (step != m) {
-            printLongLine(cat("Step ", str((double)step),
-                " was replaced with statement ", g_Statement[stmt].labelName,
-                ".  Steps ", str((double)step), ":",
-                str((double)m), " are now ", str((double)(step - m + n)), ":",
-                str((double)n), ".",
-                NULL),
-                "", " ");
-          } else {
-            printLongLine(cat("Step ", str((double)step),
-                " was replaced with statement ", g_Statement[stmt].labelName,
-                ".  Step ", str((double)m), " is now step ", str((double)n), ".",
-                NULL),
-                "", " ");
-          }
-        }
-      }
-      /*autoUnify(1);*/
-
-      /************ delete 19-Sep-2012 nm - not needed for REPLACE *******
-      /@ Automatically interact with user if step not unified @/
-      /@ ???We might want to add a setting to defeat this if user doesn't
-         like it @/
-      if (1 /@ ???Future setting flag @/) {
-        interactiveUnifyStep(step - m + n - 1, 2); /@ 2nd arg. means print
-                                         msg if already unified @/
-      }
-      *************** end 19-Sep-2012 deletion ********************/
-
-      g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
-      g_proofChanged = 1; /* Cumulative flag */
-      processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-
-      /* 16-Sep-2012 nm */
-      /*
-      if (dummyVarIsoFlag == 2 && g_proofChangedFlag) {
-        printLongLine(cat(
-     "Assignments to shared working variables ($nn) are guesses.  If "
-     "incorrect, to undo DELETE STEP ",
-              str((double)(step - m + n)),
-      ", INITIALIZE, UNIFY, then assign them manually with LET ",
-      "and try REPLACE again.",
-              NULL),
-              "", " ");
-      }
-      */
-      /* 25-Feb-2014 nm */
-      if (dummyVarIsoFlag == 2 && g_proofChangedFlag) {
-        printLongLine(cat(
-     "Assignments to shared working variables ($nn) are guesses.  If "
-     "incorrect, UNDO then assign them manually with LET ",
-      "and try REPLACE again.",
-              NULL),
-              "", " ");
-      }
-
-
-      /* 14-Sep-2012 nm - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-      if (g_proofChangedFlag)
-        typeProof(g_proveStatement,
-            1 /*pipFlag*/,
-            0 /*startStep*/,
-            0 /*endStep*/,
-            0 /*endIndent*/,
-            1 /*essentialFlag*/,
-            0 /*renumberFlag*/,
-            1 /*unknownFlag*/,
-            0 /*notUnifiedFlag*/,
-            0 /*reverseFlag*/,
-            0 /*noIndentFlag*/,
-            0 /*splitColumn*/,
-            0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-            0 /*texFlag*/,
-            0 /*g_htmlFlag*/);
-      /* 14-Sep-2012 end */
-
-
-      continue;
-
-    } /* REPLACE */
-
-
-    if (cmdMatches("IMPROVE")) {
-
-      improveDepth = 0; /* Depth */
-      i = switchPos("/ DEPTH");
-      if (i) improveDepth = (long)val(g_fullArg[i + 1]);
-      if (switchPos("/ NO_DISTINCT")) p = 1; else p = 0;
-                        /* p = 1 means don't try to use statements with $d's */
-      /* 22-Aug-2012 nm Added */
-      searchAlg = 1; /* Default */
-      if (switchPos("/ 1")) searchAlg = 1;
-      if (switchPos("/ 2")) searchAlg = 2;
-      if (switchPos("/ 3")) searchAlg = 3;
-      /* 4-Sep-2012 nm Added */
-      searchUnkSubproofs = 0;
-      if (switchPos("/ SUBPROOFS")) searchUnkSubproofs = 1;
-
-      mathboxFlag = (switchPos("/ INCLUDE_MATHBOXES") != 0); /* 5-Aug-2020 */
-      /* 5-Aug-2020 nm */
-      assignMathboxInfo(); /* In case it hasn't been assigned yet */
-      if (g_proveStatement > g_mathboxStmt) {
-        /* We're in a mathbox */
-        i = getMathboxNum(g_proveStatement);
-        if (i <= 0) bug(1130);
-        thisMathboxStartStmt = g_mathboxStart[i - 1];
-      } else {
-        thisMathboxStartStmt = g_mathboxStmt;
-      }
-
-      /* 3-May-2016 nm */
-      /* 1 means to override usage locks */
-      overrideFlag = ( (switchPos("/ OVERRIDE")) ? 1 : 0)
-         || g_globalDiscouragement == 0;
-
-      /* 14-Sep-2012 nm */
-      s = getStepNum(g_fullArg[1], g_ProofInProgress.proof,
-          1 /* 1 = "ALL" is permissible; returns 0 */);
-      if (s == -1) continue;  /* Error; message was provided already */
-
-      if (s != 0) {  /* s=0 means ALL */
-
-      /**************** 14-Sep-2012 nm replaced with getStepNum()
-      /@ 26-Aug-2006 nm Changed 'IMPROVE STEP <step>' to 'IMPROVE <step>' @/
-      let(&str1, g_fullArg[1]); /@ To avoid void pointer problems with g_fullArg @/
-      if (toupper((unsigned char)(str1[0])) != 'A') {
-        /@ 16-Apr-06 nm - Handle nonpositive step number: 0 = last,
-           -1 = penultimate, etc.@/
-        offset = 0; /@ 16-Apr-06 @/
-        /@ 10/4/99 - Added LAST - this means the last unknown step shown
-           with SHOW NEW_PROOF/ESSENTIAL/UNKNOWN @/
-        if (toupper((unsigned char)(str1[0])) == 'L'
-            || toupper((unsigned char)(str1[0])) == 'F') {
-                                        /@ 'IMPROVE LAST' or 'IMPROVE FIRST' @/
-          s = 1; /@ Temporary until we figure out which step @/
-          offset = 1;          /@ 16-Apr-06 @/
-        } else {
-          if (toupper((unsigned char)(str1[0])) == 'S') {
-            print2(
-           "?\"IMPROVE STEP <step>\" is obsolete.  Use \"IMPROVE <step>\".\n");
-            continue;
-          }
-          s = (long)val(g_fullArg[1]); /@ Step number @/
-          if (strcmp(g_fullArg[1], str((double)s))) {
-            print2(
-                "?Expected a number or FIRST or LAST or ALL after IMPROVE.\n");
-            continue;
-          }
-          if (s <= 0) {         /@ 16-Apr-06 @/
-            offset = - s + 1;   /@ 16-Apr-06 @/
-            s = 1; /@ Temporary until we figure out step @/ /@ 16-Apr-06 @/
-          }                     /@ 16-Apr-06 @/
-        }
-        /@ End of 26-Aug-2006 change @/
-
-      /@ ------- Old code before 26-Aug-2006 -------
-      if (cmdMatches("IMPROVE STEP") || cmdMatches("IMPROVE LAST") ||
-          cmdMatches("IMPROVE FIRST")) {                     /# 11-Dec-05 nm #/
-
-        /# 16-Apr-06 nm - Handle nonpositive step number: 0 = last,
-           -1 = penultimate, etc.#/
-        offset = 0; /# 16-Apr-06 #/
-        /# 10/4/99 - Added LAST - this means the last unknown step shown
-           with SHOW NEW_PROOF/ESSENTIAL/UNKNOWN #/
-        if (cmdMatches("IMPROVE LAST") || cmdMatches("IMPROVE FIRST")) {
-                               /# "IMPROVE LAST or FIRST" #/ /# 11-Dec-05 nm #/
-          s = 1; /# Temporary until we figure out which step #/
-          offset = 1;          /# 16-Apr-06 #/
-        } else {
-          s = val(g_fullArg[2]); /# Step number #/
-          if (s <= 0) {         /# 16-Apr-06 #/
-            offset = - s + 1;   /# 16-Apr-06 #/
-            s = 1; /# Temp. until we figure out which step #/ /# 16-Apr-06 #/
-          }                     /# 16-Apr-06 #/
-        }
-        ------- End of old code ------- @/
-        **************** end of 14-Sep-2012 nm ************/
-
-        m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-
-
-        /**************** 14-Sep-2012 nm replaced with getStepNum()
-        if (s > m || s < 1) {
-          print2("?The step must be in the range from 1 to %ld.\n", m);
-          continue;
-        }
-
-
-        /@ 10/4/99 - For IMPROVE FIRST/LAST command, figure out the last
-           unknown essential step @/           /@ 11-Dec-05 nm - Added FIRST @/
-        /@if (cmdMatches("IMPROVE LAST") || cmdMatches("IMPROVE FIRST")) {@/
-                               /@ IMPROVE LAST or FIRST @/ /@ 11-Dec-05 nm @/
-        if (offset > 0) {  /@ LAST, FIRST, or step <= 0 @/ /@ 16-Apr-06 @/
-          /@ Get the essential step flags @/
-          s = 0; /@ Use as flag that step was found @/
-          nmbrLet(&essentialFlags, nmbrGetEssential(g_ProofInProgress.proof));
-          /@if (cmdMatches("IMPROVE LAST")) {@/
-          /@if (!cmdMatches("IMPROVE FIRST")) {@/   /@ 16-Apr-06 @/
-          if (toupper((unsigned char)(str1[0])) != 'F') { /@ 26-Aug-2006 @/
-            /@ Scan proof backwards until last essential unknown step found @/
-            /@ 16-Apr-06 - count back 'offset' unknown steps @/
-            j = offset;      /@ 16-Apr-06 @/
-            for (i = m; i >= 1; i--) {
-              if (essentialFlags[i - 1]
-                  && (g_ProofInProgress.proof)[i - 1] == -(long)'?') {
-                j--;           /@ 16-Apr-06 @/
-                if (j == 0) {  /@ 16-Apr-06 @/
-                  /@ Found it @/
-                  s = i;
-                  break;
-                }              /@ 16-Apr-06 @/
-              }
-            } /@ Next i @/
-          } else {
-            /@ 11-Dec-05 nm Added IMPROVE FIRST @/
-            /@ Scan proof forwards until first essential unknown step found @/
-            for (i = 1; i <= m; i++) {
-              if (essentialFlags[i - 1]
-                  && (g_ProofInProgress.proof)[i - 1] == -(long)'?') {
-                /@ Found it @/
-                s = i;
-                break;
-              }
-            } /@ Next i @/
-          }
-          if (s == 0) {
-            if (offset == 1) {                                /@ 16-Apr-06 @/
-              print2("?There are no unknown essential steps.\n");
-            } else {                                          /@ 16-Apr-06 @/
-              print2("?There are not at least %ld unknown essential steps.\n",
-                offset);                                      /@ 16-Apr-06 @/
-            }                                                 /@ 16-Apr-06 @/
-            continue;
-          }
-        } /@ if offset > 0 @/
-        **************** end of 14-Sep-2012 nm ************/
-
-        /* Get the subproof at step s */
-        q = subproofLen(g_ProofInProgress.proof, s - 1);
-        nmbrLet(&nmbrTmp, nmbrSeg(g_ProofInProgress.proof, s - q + 1, s));
-
-        /*???Shouldn't this be just known?*/
-        /* Check to see that the subproof has an unknown step. */
-        if (!nmbrElementIn(1, nmbrTmp, -(long)'?')) {
-          print2(
-              "?Step %ld already has a proof and cannot be improved.\n",
-              s);
-          continue;
-        }
-
-        /* 25-Aug-2012 nm */
-        /* Check dummy variable status of step */
-        dummyVarIsoFlag = checkDummyVarIsolation(s - 1);
-              /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/
-        if (dummyVarIsoFlag == 2) {
-          print2(
-  "?Step %ld target has shared dummy variables and cannot be improved.\n", s);
-          continue; /* Don't try to improve
-                                 dummy variables that aren't isolated */
-        }
-
-        /********* Deleted old code 25-Aug-2012 nm
-        /@ Check to see that the step has no dummy variables. @/
-        j = 0; /@ Break flag @/
-        for (i = 0; i < nmbrLen((g_ProofInProgress.target)[s - 1]); i++) {
-          if (((nmbrString @)((g_ProofInProgress.target)[s - 1]))[i] > mathTokens) {
-            j = 1;
-            break;
-          }
-        }
-        if (j) {
-          print2(
-   "?Step %ld target has dummy variables and cannot be improved.\n", s);
-          continue;
-        }
-        ********/
-
-
-        if (dummyVarIsoFlag == 0) { /* No dummy vars */ /* 25-Aug-2012 nm */
-          /* Only use proveFloating if no dummy vars */
-          nmbrTmpPtr = proveFloating((g_ProofInProgress.target)[s - 1],
-              g_proveStatement, improveDepth, s - 1, (char)p/*NO_DISTINCT*/,
-              overrideFlag,  /* 3-May-2016 nm */
-              mathboxFlag /* 5-Aug-2020 nm */
-              );
-        } else {
-          nmbrTmpPtr = NULL_NMBRSTRING; /* Initialize */ /* 25-Aug-2012 nm */
-        }
-        if (!nmbrLen(nmbrTmpPtr)) {
-          /* A proof for the step was not found with proveFloating(). */
-
-          /* 22-Aug-2012 nm Next, try REPLACE algorithm */
-          if (searchAlg == 2 || searchAlg == 3) {
-            nmbrTmpPtr = proveByReplacement(g_proveStatement,
-              s - 1/*prfStep*/, /* 0 means step 1 */
-              (char)p/*NO_DISTINCT*/, /* 1 means don't try stmts with $d's */
-              dummyVarIsoFlag,
-              (char)(searchAlg - 2), /*0=proveFloat for $fs, 1=$e's also */
-              improveDepth, /* 4-Sep-2012 */
-              overrideFlag,  /* 3-May-2016 nm */
-              mathboxFlag /* 5-Aug-2020 nm */
-              );
-          }
-          if (!nmbrLen(nmbrTmpPtr)) {
-            print2("A proof for step %ld was not found.\n", s);
-            /* REPLACE algorithm also failed */
-            continue;
-          }
-        }
-
-        /* If q=1, subproof must be an unknown step, so don't bother to
-           delete it */
-        /*???Won't q always be 1 here?*/
-        if (q > 1) deleteSubProof(s - 1);
-        addSubProof(nmbrTmpPtr, s - q);
-        assignKnownSteps(s - q, nmbrLen(nmbrTmpPtr));
-        nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);
-
-        n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
-        if (m == n) {
-          print2("A 1-step proof was found for step %ld.\n", s);
-        } else {
-          if (s != m || q != 1) {
-            printLongLine(cat("A ", str((double)(n - m + 1)),
-                "-step proof was found for step ", str((double)s),
-                ".  Steps ", str((double)s), ":",
-                str((double)m), " are now ", str((double)(s - q + 1 - m + n)),
-                ":", str((double)n), ".",
-                NULL),
-                "", " ");
-          } else {
-            printLongLine(cat("A ", str((double)(n - m + 1)),
-                "-step proof was found for step ", str((double)s),
-                ".  Step ", str((double)m), " is now step ", str((double)n), ".",
-                NULL),
-                "", " ");
-          }
-        }
-
-        autoUnify(1); /* To get 'congrats' message if proof complete */
-        g_proofChanged = 1; /* Cumulative flag */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-
-        /* End if s != 0 i.e. not IMPROVE ALL */   /* 14-Sep-2012 nm */
-      } else {
-        /* Here, getStepNum() returned 0, meaning ALL */  /* 14-Sep-2012 nm */
-
-        /*if (cmdMatches("IMPROVE ALL")) {*/  /* obsolete */
-
-        if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
-          print2("The proof is already complete.\n");
-          continue;
-        }
-
-        n = 0; /* Earliest step that changed */
-
-        g_proofChangedFlag = 0;
-
-        for (improveAllIter = 1; improveAllIter <= 4; improveAllIter++) {
-                                                           /* 25-Aug-2012 nm */
-          if (improveAllIter == 1 && (searchAlg == 2 || searchAlg == 3))
-            print2("Pass 1:  Trying to match cut-free statements...\n");
-          if (improveAllIter == 2) {
-            if (searchAlg == 2) {
-              print2("Pass 2:  Trying to match all statements...\n");
-            } else {
-              print2(
-"Pass 2:  Trying to match all statements, with cut-free hypothesis matches...\n"
-                  );
-            }
-          }
-          if (improveAllIter == 3 && searchUnkSubproofs)
-            print2("Pass 3:  Trying to replace incomplete subproofs...\n");
-          if (improveAllIter == 4) {
-            if (searchUnkSubproofs) {
-              print2("Pass 4:  Repeating pass 1...\n");
-            } else {
-              print2("Pass 3:  Repeating pass 1...\n");
-            }
-          }
-          /* improveAllIter = 1: run proveFloating only */
-          /* improveAllIter = 2: run proveByReplacement on unknown steps */
-          /* improveAllIter = 3: run proveByReplacement on steps with
-                                   incomplete subproofs */
-          /* improveAllIter = 4: if something changed, run everything again */
-
-          if (improveAllIter == 3 && !searchUnkSubproofs) continue;
-
-          m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-
-          for (s = m; s > 0; s--) {
-
-            proofStepUnk = ((g_ProofInProgress.proof)[s - 1] == -(long)'?')
-                ? 1 : 0;   /* 25-Aug-2012 nm Added for clearer code */
-
-            /* 22-Aug-2012 nm I think this is really too conservative, even
-               with the old algorithm, but keep it to imitate the old one */
-            if (improveAllIter == 1 || searchAlg == 1) { /* 22-Aug-2012 nm */
-              /* If the step is known and unified, don't do it, since nothing
-                 would be accomplished. */
-              if (!proofStepUnk) {
-                if (nmbrEq((g_ProofInProgress.target)[s - 1],
-                    (g_ProofInProgress.source)[s - 1])) continue;
-              }
-            }
-
-            /* Get the subproof at step s */
-            q = subproofLen(g_ProofInProgress.proof, s - 1);
-            if (proofStepUnk && q != 1) {
-              bug(1120); /* 25-Aug-2012 nm Consistency check */
-            }
-            nmbrLet(&nmbrTmp, nmbrSeg(g_ProofInProgress.proof, s - q + 1, s));
-
-            /* Improve only subproofs with unknown steps */
-            if (!nmbrElementIn(1, nmbrTmp, -(long)'?')) continue;
-
-            nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* No longer needed - dealloc */
-
-            /* 25-Aug-2012 nm */
-            /* Check dummy variable status of step */
-            dummyVarIsoFlag = checkDummyVarIsolation(s - 1);
-                  /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/
-            if (dummyVarIsoFlag == 2) continue; /* Don't try to improve
-                                     dummy variables that aren't isolated */
-
-            /********* Deleted old code now done by checkDummyVarIsolation()
-                       25-Aug-2012 nm
-            /@ Check to see that the step has no dummy variables. @/
-            j = 0; /@ Break flag @/
-            for (i = 0; i < nmbrLen((g_ProofInProgress.target)[s - 1]); i++) {
-              if (((nmbrString @)((g_ProofInProgress.target)[s - 1]))[i] >
-                  mathTokens) {
-                j = 1;
-                break;
-              }
-            }
-            if (j) {
-              /@ Step has dummy variables and cannot be improved. @/
-              continue;
-            }
-            ********/
-
-            if (dummyVarIsoFlag == 0
-                && (improveAllIter == 1
-                  || improveAllIter == 4)) {
-                /* No dummy vars */ /* 25-Aug-2012 nm */
-              /* Only use proveFloating if no dummy vars */
-              nmbrTmpPtr = proveFloating((g_ProofInProgress.target)[s - 1],
-                  g_proveStatement, improveDepth, s - 1,
-                  (char)p/*NO_DISTINCT*/,
-                  overrideFlag,  /* 3-May-2016 nm */
-                  mathboxFlag /* 5-Aug-2020 nm */
-                  );
-            } else {
-              nmbrTmpPtr = NULL_NMBRSTRING; /* Init */ /* 25-Aug-2012 nm */
-            }
-            if (!nmbrLen(nmbrTmpPtr)) {
-              /* A proof for the step was not found with proveFloating(). */
-
-              /* 22-Aug-2012 nm Next, try REPLACE algorithm */
-              if ((searchAlg == 2 || searchAlg == 3)
-                  && ((improveAllIter == 2 && proofStepUnk)
-                    || (improveAllIter == 3 && !proofStepUnk)
-                    /*|| improveAllIter == 4*/)) {
-                nmbrTmpPtr = proveByReplacement(g_proveStatement,
-                  s - 1/*prfStep*/, /* 0 means step 1 */
-                  (char)p/*NO_DISTINCT*/, /* 1 means don't try stmts w/ $d's */
-                  dummyVarIsoFlag,
-                  (char)(searchAlg - 2),/*searchMethod: 0 or 1*/
-                  improveDepth,                        /* 4-Sep-2012 */
-                  overrideFlag,   /* 3-May-2016 nm */
-                  mathboxFlag /* 5-Aug-2020 nm */
-                  );
-
-              }
-              if (!nmbrLen(nmbrTmpPtr)) {
-                /* REPLACE algorithm also failed */
-                continue;
-              }
-            }
-
-            /* If q=1, subproof must be an unknown step, so don't bother to
-               delete it */
-            if (q > 1) deleteSubProof(s - 1);
-            addSubProof(nmbrTmpPtr, s - q);
-            assignKnownSteps(s - q, nmbrLen(nmbrTmpPtr));
-            print2("A proof of length %ld was found for step %ld.\n",
-                nmbrLen(nmbrTmpPtr), s);
-            if (nmbrLen(nmbrTmpPtr) || q != 1) n = s - q + 1;
-                                               /* Save earliest step changed */
-            nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);
-            g_proofChangedFlag = 1;
-            s = s - q + 1; /* Adjust step position to account for deleted subpr */
-          } /* Next step s */
-
-          if (g_proofChangedFlag) {
-            autoUnify(0); /* 0 = No 'Congrats' if done */
-          }
-
-          if (!g_proofChangedFlag
-              && ( (improveAllIter == 2 && !searchUnkSubproofs)
-                 || improveAllIter == 3
-                 || searchAlg == 1)) {
-            print2("No new subproofs were found.\n");
-            break; /* out of improveAllIter loop */
-          }
-          if (g_proofChangedFlag) {
-            g_proofChanged = 1; /* Cumulative flag */
-          }
-
-          if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
-            break; /* Proof is complete */
-          }
-
-          if (searchAlg == 1) break; /* Old algorithm does just 1st pass */
-
-        } /* Next improveAllIter */
-
-        if (g_proofChangedFlag) {
-          if (n > 0) {
-            /* n is the first step number changed.  It will be 0 if
-               the numbering didn't change e.g. a $e was assigned to
-               an unknown step. */
-            print2("Steps %ld and above have been renumbered.\n", n);
-          }
-          processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-        }
-        if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
-          /* This is a redundant call; its purpose is just to give
-             the message if the proof is complete */
-          autoUnify(1); /* 1 = 'Congrats' if done */
-        }
-
-      } /* End if IMPROVE ALL */
-
-      /* 6/14/98 - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-      if (g_proofChangedFlag)
-        typeProof(g_proveStatement,
-            1 /*pipFlag*/,
-            0 /*startStep*/,
-            0 /*endStep*/,
-            0 /*endIndent*/,
-            1 /*essentialFlag*/,
-            0 /*renumberFlag*/,
-            1 /*unknownFlag*/,
-            0 /*notUnifiedFlag*/,
-            0 /*reverseFlag*/,
-            0 /*noIndentFlag*/,
-            0 /*splitColumn*/,
-            0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-            0 /*texFlag*/,
-            0 /*g_htmlFlag*/);
-      /* 6/14/98 end */
-
-      continue;
-
-    } /* cmdMatches("IMPROVE") */
-
-
-    if (cmdMatches("MINIMIZE_WITH")) {
-
-      /* 16-Aug-2016 nm */
-      printTime = 0;
-      if (switchPos("/ TIME") != 0) {
-        printTime = 1;
-      }
-      if (printTime == 1) {
-        getRunTime(&timeIncr);  /* This call just resets the time */
-      }
-
-      /* q = 0; */ /* Line length */  /* 25-Jun-2014 deleted */
-      prntStatus = 0; /* Status flag to help determine messages
-                         0 = no statement was matched during scan (mainly for
-                             error message if user typo in label field)
-                         1 = a statement was matched but no shorter proof
-                         2 = shorter proof found */
-      /* verboseMode = (switchPos("/ BRIEF") == 0); */ /* Non-verbose mode */
-      /* 4-Feb-2013 nm VERBOSE is now default */
-      verboseMode = (switchPos("/ VERBOSE") != 0); /* Verbose mode */
-      /* 30-Jan-06 nm Added single-character-match wildcard argument */
-      if (!(instr(1, g_fullArg[1], "*") || instr(1, g_fullArg[1], "?"))) i = 1;
-          /* 16-Feb-05 If no wildcard was used, switch to non-verbose mode
-             since there is no point to it and an annoying extra blank line
-             results */
-      mayGrowFlag = (switchPos("/ MAY_GROW") != 0);
-                  /* Mode to replace even if it doesn't reduce proof length */
-      /* 25-Jun-2014 nm /NO_DISTINCT is obsolete
-      noDistinctFlag = (switchPos("/ NO_DISTINCT") != 0);
-      */
-                                          /* Skip trying statements with $d */
-      exceptPos = switchPos("/ EXCEPT"); /* Statement match to skip */
-                                                               /* 7-Jan-06 */
-
-      /* 4-Aug-2019 nm */
-      allowNewAxiomsMatchPos = switchPos("/ ALLOW_NEW_AXIOMS");
-      if (allowNewAxiomsMatchPos != 0) {
-        let(&allowNewAxiomsMatchList, g_fullArg[allowNewAxiomsMatchPos + 1]);
-      } else {
-        let(&allowNewAxiomsMatchList, "");
-      }
-
-      /* 20-May-2013 nm */
-      noNewAxiomsMatchPos = switchPos("/ NO_NEW_AXIOMS_FROM");
-      if (noNewAxiomsMatchPos != 0) {
-        let(&noNewAxiomsMatchList, g_fullArg[noNewAxiomsMatchPos + 1]);
-      } else {
-        let(&noNewAxiomsMatchList, "");
-      }
-
-      /* 20-May-2013 nm */
-      forbidMatchPos = switchPos("/ FORBID");
-      if (forbidMatchPos != 0) {
-        let(&forbidMatchList, g_fullArg[forbidMatchPos + 1]);
-      } else {
-        let(&forbidMatchList, "");
-      }
-
-      mathboxFlag = (switchPos("/ INCLUDE_MATHBOXES") != 0); /* 28-Jun-2011 */
-      /* 25-Jun-2014 nm /REVERSE is obsolete
-      forwFlag = (switchPos("/ REVERSE") != 0); /@ 10-Nov-2011 nm @/
-      */
-      /****** 5-Aug-2020 nm Replaced w/ assignMathboxInfo() below
-      if (g_mathboxStmt == 0) { /@ Look up "mathbox" label if it hasn't been @/
-        g_mathboxStmt = lookupLabel("mathbox");
-        if (g_mathboxStmt == -1)
-          g_mathboxStmt = g_statements + 1;  /@ Default beyond db end if none @/
-      }
-      *******/
-
-      /* 3-May-2016 nm */
-      /* Flag to override any "usage locks" placed in the comment markup */
-      overrideFlag = (switchPos("/ OVERRIDE") != 0)
-           || g_globalDiscouragement == 0;
-
-      /* 25-Jun-2014 nm */
-      /* If a single statement is specified, don't bother to do certain
-         actions or print some of the messages */
-      /* 18-Jul-2020 nm New code*/
-      hasWildCard = 0;
-      /* (Note strpbrk warning in mmpars.c) */
-      /*if (strpbrk(g_fullArg[1], "*?=~%#@,") != NULL) {*/
-      /* Set hasWildCard only when there are potentially > 1 matches */
-      if (strpbrk(g_fullArg[1], "*?~%,") != NULL) {
-        /* (See matches() function for processing of these)
-           "*" 0 or more char match
-           "?" 1 char match
-           "=" Most recent PROVE command statement  = one statement match
-           "~" Statement range
-           "%" List of modified statements
-           "#" Internal statement number            = one statement match
-           "@" Web page statement number            = one statement match
-           "," Comma-separated fields */
-        hasWildCard = 1;
-      }
-      /****** 18-Jul-2020 nm Deleted old code:
-      hasWildCard = (instr(1, g_fullArg[1], "*")
-          || instr(1, g_fullArg[1], "?")
-          || instr(1, g_fullArg[1], ",")
-          || instr(1, g_fullArg[1], "~") /@ 3-May-2014 nm label~label range @/
-          );
-      ********/
-
-      g_proofChangedFlag = 0;
-
-      /* Added 14-Aug-2012 nm */
-      /* Always scan statements in current mathbox, even if
-         "/ INCLUDE_MATHBOXES" is omitted */
-
-      /* 5-Aug-2020 nm */
-      assignMathboxInfo(); /* In case it hasn't been assigned yet */
-      if (g_proveStatement > g_mathboxStmt) {
-        /* We're in a mathbox */
-        i = getMathboxNum(g_proveStatement);
-        if (i <= 0) bug(1130);
-        thisMathboxStartStmt = g_mathboxStart[i - 1];
-      } else {
-        thisMathboxStartStmt = g_mathboxStmt;
-      }
-      /****** 5-Aug-2020 deleted old code
-      thisMathboxStartStmt = g_mathboxStmt;
-                /@ Will become start of current (g_proveStatement's) mathbox @/
-      if (g_proveStatement > g_mathboxStmt) {
-        /@ We're in a mathbox @/
-        for (k = g_proveStatement; k >= g_mathboxStmt; k--) {
-          let(&str1, left(g_Statement[k].labelSectionPtr,
-              g_Statement[k].labelSectionLen));
-          /@ Heuristic to match beginning of mathbox @/
-          if (instr(1, str1, "Mathbox for") != 0) {
-             /@ Found beginning of current mathbox @/
-             thisMathboxStartStmt = k;
-             break;
-          }
-        }
-      }
-      **************/
-
-      /* 25-Jun-2014 nm */
-      copyProofStruct(&saveOrigProof, g_ProofInProgress);
-
-      /* 12-Sep-2016 nm TODO replace this w/ compressedProofSize */
-      /* 25-Jun-2014 nm Get the current (original) compressed proof length
-         to compare it when a shorter non-compressed proof is found, to see
-         if the compressed proof also decreased in size */
-      nmbrLet(&nmbrSaveProof, g_ProofInProgress.proof);   /* Redundant? */
-      nmbrLet(&nmbrSaveProof, nmbrSquishProof(g_ProofInProgress.proof));
-      /* We only care about length; str1 will be discarded */
-      let(&str1, compressProof(nmbrSaveProof,
-          g_proveStatement, /* statement being proved */
-          0 /* Normal (not "fast") compression */
-          ));
-      origCompressedLength = (long)strlen(str1);
-      print2(
-"Bytes refer to compressed proof size, steps to uncompressed length.\n");
-
-      /* 25-Jun-2014 nm forwRevPass outer loop added */
-      /* Scan forward, then reverse, then pick best result */
-      for (forwRevPass = 1; forwRevPass <= 2; forwRevPass++) {
-
-        /* 25-Jun-2014 nm */
-        if (forwRevPass == 1) {
-          if (hasWildCard) print2("Scanning forward through statements...\n");
-          forwFlag = 1;
-        } else {
-          /* If growth allowed, don't bother with reverse pass */
-          if (mayGrowFlag) break;
-          /* If nothing was found on forward pass, don't bother with rev pass */
-          if (!g_proofChangedFlag) break;
-          /* If only one statement was specified, don't bother with rev pass */
-          if (!hasWildCard) break;
-          print2("Scanning backward through statements...\n");
-          forwFlag = 0;
-          /* Save proof and length from 1st pass; re-initialize */
-          copyProofStruct(&save1stPassProof, g_ProofInProgress);
-          forwardLength = nmbrLen(g_ProofInProgress.proof);
-          forwardCompressedLength = oldCompressedLength;
-          /* Start over from original proof */
-          copyProofStruct(&g_ProofInProgress, saveOrigProof);
-        }
-
-        /* 20-May-2013 nm */
-        /*
-        if (forbidMatchList[0]) { /@ User provided a /FORBID list @/
-          /@ Save the proof structure in case we have to revert a
-             forbidden match. @/
-          copyProofStruct(&saveProofForReverting, g_ProofInProgress);
-        }
-        */
-        /* 25-Jun-2014 nm */
-        copyProofStruct(&saveProofForReverting, g_ProofInProgress);
-
-        oldCompressedLength = origCompressedLength;
-
-        /* for (k = 1; k < g_proveStatement; k++) { */
-        /* 10-Nov-2011 nm */
-        /* If forwFlag is 0, scan from g_proveStatement-1 to 1
-           If forwFlag is 1, scan from 1 to g_proveStatement-1 */
-        for (k = forwFlag ? 1 : (g_proveStatement - 1);
-             k * (forwFlag ? 1 : -1) < (forwFlag ? g_proveStatement : 0);
-             k = k + (forwFlag ? 1 : -1)) {
-          /* 28-Jun-2011 */
-          /* Scan mathbox statements only if INCLUDE_MATHBOXES specified */
-          /*if (!mathboxFlag && k >= g_mathboxStmt) continue;*/
-          /* 14-Aug-2012 nm */
-          if (!mathboxFlag && k >= g_mathboxStmt && k < thisMathboxStartStmt) {
-            continue;
-          }
-
-          if (g_Statement[k].type != (char)p_ && g_Statement[k].type != (char)a_)
-            continue;
-          /* 30-Jan-06 nm Added single-character-match wildcard argument */
-          if (!matchesList(g_Statement[k].labelName, g_fullArg[1], '*', '?'))
-            continue;
-          /* 25-Jun-2014 nm /NO_DISTINCT is obsolete
-          if (noDistinctFlag) {
-            /@ Skip the statement if it has a $d requirement.  This option
-               prevents illegal minimizations that would violate $d requirements
-               since MINIMIZE_WITH does not check for $d violations. @/
-            if (nmbrLen(g_Statement[k].reqDisjVarsA)) {
-              /@ 30-Jan-06 nm Added single-character-match wildcard argument @/
-              if (!(instr(1, g_fullArg[1], "@") || instr(1, g_fullArg[1], "?")))
-                print2("?\"%s\" has a $d requirement\n", g_fullArg[1]);
-              continue;
-            }
-          }
-          */
-
-          /* 7-Jan-06 nm - Added EXCEPT switch */
-          if (exceptPos != 0) {
-            /* Skip any match to the EXCEPT argument */
-            /* 30-Jan-06 nm Added single-character-match wildcard argument */
-            if (matchesList(g_Statement[k].labelName, g_fullArg[exceptPos + 1],
-                '*', '?'))
-              continue;
-          }
-
-          /* 20-May-2013 nm */
-          if (forbidMatchList[0]) { /* User provided a /FORBID list */
-            /* First, we check to make sure we're not trying a statement
-               in the forbidMatchList directly (traceProof() won't find
-               this) */
-            if (matchesList(g_Statement[k].labelName, forbidMatchList, '*', '?'))
-              continue;
-          }
-
-          /* 3-May-2016 nm */
-          /* Check to see if statement comment specified a usage
-             restriction */
-          if (!overrideFlag) {
-            if (getMarkupFlag(k, USAGE_DISCOURAGED)) {
-              continue;
-            }
-          }
-
-          /* Print individual labels */
-          if (prntStatus == 0) prntStatus = 1; /* Matched at least one */
-          /* 25-Jun-2014 nm Don't list matched statements anymore
-          if (verboseMode) {
-            q = q + (long)strlen(g_Statement[k].labelName) + 1;
-            if (q > 72) {
-              q = (long)strlen(g_Statement[k].labelName) + 1;
-              print2("\n");
-            }
-            print2("%s ",g_Statement[k].labelName);
-          }
-          */
-
-          m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-          nmbrLet(&nmbrTmp, g_ProofInProgress.proof);
-          minimizeProof(k /* trial statement */,
-              g_proveStatement /* statement being proved in MM-PA */,
-              (char)mayGrowFlag /* mayGrowFlag */);
-
-          n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
-          if (!nmbrEq(nmbrTmp, g_ProofInProgress.proof)) {
-            /* The proof got shorter (or it changed if MAY_GROW) */
-
-            /* 20-May-2013 nm Because of the slow speed of traceBack(),
-               we only want to check the /FORBID list in the relatively
-               rare case where a minimization occurred.  If the FORBID
-               list is matched, we then need to revert back to the
-               original proof. */
-            if (forbidMatchList[0]) { /* User provided a /FORBID list */
-              if (g_Statement[k].type == (char)p_) {
-                /* We only care about tracing $p statements */
-                /* See if the TRACE_BACK list includes a match to the
-                   /FORBID argument */
-                if (traceProof(k,
-                    0, /*essentialFlag*/
-                    0, /*axiomFlag*/
-                    forbidMatchList,
-                    "", /*traceToList*/ /* 18-Jul-2015 nm */
-                    1 /* testOnlyFlag */)) {
-                  /* Yes, a forbidden statement occurred in traceProof() */
-                  /* Revert the proof to before minimization */
-                  copyProofStruct(&g_ProofInProgress, saveProofForReverting);
-                  /* Skip further printout and flag setting */
-                  continue; /* Continue at 'Next k' loop end below */
-                }
-              }
-            }
-
-
-            /* 22-Nov-2014 nm Because of the slow speed of traceBack(),
-               we only want to check the /NO_NEW_AXIOMS_FROM list in the
-               relatively rare case where a minimization occurred.  If the
-               NO_NEW_AXIOMS_FROM condition applies, we then need to revert
-               back to the original proof. */
-            /* 4-Aug-2019 nm */
-            if (n == n + 0) {  /* By default, no new axioms are permitted */
-            /*if (noNewAxiomsMatchList[0]) {*/ /* User provided /NO_NEW_AXIOMS_FROM */
-              /* If we haven't called trace yet for the theorem being proved,
-                 do it now. */
-              if (traceProofFlags[0] == 0) {
-
-                /******** start of 29-Nov-2019 nm ************/
-                /* traceProofWork() was written to use the SAVEd proof and
-                   not the proof in progress.  In order to use the proof in
-                   progress, we temporarily put the proof in progress into the
-                   (SAVEd) statement structure to trick traceProofWork() into using
-                   the proof in progress instead of the SAVEd proof */
-                /* Bad code line between 4-Aug-2019 and 12-Feb-2020: */
-                /*nmbrLet(&nmbrSaveProof, nmbrSquishProof(g_ProofInProgress.proof));*/ /* Bad! */
-                /* 12-Feb-2020 nm */
-                /* Use the version of the proof in progress that existed *before* the
-                   MINIMIZE_WITH command was invoked */
-                nmbrLet(&nmbrSaveProof, nmbrSquishProof(saveProofForReverting.proof));
-                let(&str1, compressProof(nmbrSaveProof,
-                    g_proveStatement, /* statement being proved in MM-PA */
-                    0 /* Normal (not "fast") compression */
-                    ));
-                saveZappedProofSectionPtr
-                    = g_Statement[g_proveStatement].proofSectionPtr;
-                saveZappedProofSectionLen
-                    = g_Statement[g_proveStatement].proofSectionLen;
-                saveZappedProofSectionChanged
-                    = g_Statement[g_proveStatement].proofSectionChanged;
-                /* Set flag that this is not the original source */
-                g_Statement[g_proveStatement].proofSectionChanged = 1;
-                /* str1 has the new compressed trial proof after minimization */
-                /* Put space before and after to satisfy "space around token"
-                   requirement, to prevent possible error messages, and also
-                   add "$." since parseCompressedProof() expects it */
-                let(&str1, cat(" ", str1, " $.", NULL));
-                /* Don't include the "$." in the length */
-                g_Statement[g_proveStatement].proofSectionLen
-                    = (long)strlen(str1) - 2;
-                /* We don't deallocate previous proofSectionPtr content because
-                   we will recover it after the verifyProof() */
-                g_Statement[g_proveStatement].proofSectionPtr = str1;
-                /******** end of 29-Nov-2019 nm ************/
-
-                traceProofWork(g_proveStatement,
-                    1 /*essentialFlag*/,
-                    "", /*traceToList*/ /* 18-Jul-2015 nm */
-                    &traceProofFlags, /* y/n list of flags */
-                    &nmbrTmp /* unproved list - ignored */);
-                nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* Discard */
-
-                /******** start of 29-Nov-2019 nm ************/
-                /* Restore the SAVEd proof */
-                g_Statement[g_proveStatement].proofSectionPtr
-                    = saveZappedProofSectionPtr;
-                g_Statement[g_proveStatement].proofSectionLen
-                    = saveZappedProofSectionLen;
-                g_Statement[g_proveStatement].proofSectionChanged
-                    = saveZappedProofSectionChanged; /* 16-Jun-2017 nm */
-                /******** end of 29-Nov-2019 nm ************/
-
-              }
-              let(&traceTrialFlags, "");
-              traceProofWork(k, /* The trial statement */
-                  1 /*essentialFlag*/,
-                  "", /*traceToList*/ /* 18-Jul-2015 nm */
-                  &traceTrialFlags, /* Y/N list of flags */
-                  &nmbrTmp /* unproved list - ignored */);
-              nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* Discard */
-              j = 1; /* 1 = ok to use trial statement */
-              for (i = 1; i < g_proveStatement; i++) {
-                if (g_Statement[i].type != (char)a_) continue; /* Not $a */
-                if (traceProofFlags[i] == 'Y') continue;
-                         /* If the axiom is already used by the proof, we
-                            don't care if the trial statement depends on it */
-                if (matchesList(g_Statement[i].labelName, allowNewAxiomsMatchList,
-                    '*', '?') == 1
-                      &&
-                    matchesList(g_Statement[i].labelName, noNewAxiomsMatchList,
-                    '*', '?') != 1) { /* 4-Aug-2019 nm */
-                  /* If the axiom is in the list to allow and not in the list
-                     to disallow, we don't care if the trial statement depends
-                     on it */
-                  continue;
-                }
-                /*
-                if (matchesList(g_Statement[i].labelName, noNewAxiomsMatchList,
-                    '@', '?') != 1) {
-                  /@ If the axiom isn't in the list to avoid, we don't
-                     care if the trial statement depends on it @/
-                  continue;
-                }
-                */
-                if (traceTrialFlags[i] == 'Y') {
-                  /* The trial statement uses an axiom that the current
-                     proof should avoid, so we abort it */
-                  j = 0; /* 0 = don't use trial statement */
-                  break;
-                }
-              } /* next i */
-              if (j == 0) {
-                /* A forbidden axiom is used by the trial proof */
-                /* Revert the proof to before minimization */
-                copyProofStruct(&g_ProofInProgress, saveProofForReverting);
-                /* Skip further printout and flag setting */
-                continue; /* Continue at 'Next k' loop end below */
-              }
-            } /* end if (true) */
-
-
-            /* 25-Jun-2014 nm Make sure the compressed proof length
-               decreased, otherwise revert.  Also, we will use the
-               compressed proof for the $d check next */
-            if (nmbrLen(g_Statement[k].reqDisjVarsA) || !mayGrowFlag) {
-              nmbrLet(&nmbrSaveProof, g_ProofInProgress.proof);
-              nmbrLet(&nmbrSaveProof, nmbrSquishProof(g_ProofInProgress.proof));
-              let(&str1, compressProof(nmbrSaveProof,
-                  g_proveStatement, /* statement being proved in MM-PA */
-                  0 /* Normal (not "fast") compression */
-                  ));
-              newCompressedLength = (long)strlen(str1);
-              if (!mayGrowFlag && newCompressedLength > oldCompressedLength) {
-                /* The compressed proof length increased, so don't use it.
-                   (If it stayed the same, we will use it because the uncompressed
-                   length did decrease.) */
-                /* Revert the proof to before minimization */
-                if (verboseMode) {
-                  print2(
- "Reverting \"%s\": Uncompressed steps:  old = %ld, new = %ld\n",
-                      g_Statement[k].labelName,
-                      m, n );
-                  print2(
- "    but compressed size:  old = %ld bytes, new = %ld bytes\n",
-                      oldCompressedLength, newCompressedLength);
-                }
-                copyProofStruct(&g_ProofInProgress, saveProofForReverting);
-                /* Skip further printout and flag setting */
-                continue; /* Continue at 'Next k' loop end below */
-              }
-            } /* if (nmbrLen(g_Statement[k].reqDisjVarsA) || !mayGrowFlag) */
-
-            /* 25-Jun-2014 nm */
-            /* Make sure there are no $d violations, otherwise revert */
-            /* This requires the str1 from above */
-            if (nmbrLen(g_Statement[k].reqDisjVarsA)) {
-              /* There is currently no way to verify a proof that doesn't
-                 read and parse the source directly.  This should be
-                 changed in the future to make the program more modular.  But
-                 for now, we temporarily zap the source with new compressed
-                 proof and see if there are any $d violations by looking at
-                 the error message output */
-              saveZappedProofSectionPtr
-                  = g_Statement[g_proveStatement].proofSectionPtr;
-              saveZappedProofSectionLen
-                  = g_Statement[g_proveStatement].proofSectionLen;
-
-              /****** Obsolete due to 3-May-2017 update
-              /@ (search for "chr(1)" above for explanation) @/
-              let(&str1, cat(chr(1), "\n", str1, " $.\n", NULL));
-              g_Statement[g_proveStatement].proofSectionPtr = str1 + 1; /@ Compressed
-                                                     proof generated above @/
-              g_Statement[g_proveStatement].proofSectionLen =
-                  newCompressedLength + 4;
-              *******/
-
-              /******** start of 16-Jun-2017 nm ************/
-              saveZappedProofSectionChanged
-                  = g_Statement[g_proveStatement].proofSectionChanged;
-              /* Set flag that this is not the original source */
-              g_Statement[g_proveStatement].proofSectionChanged = 1;
-              /* str1 has the new compressed trial proof after minimization */
-              /* Put space before and after to satisfy "space around token"
-                 requirement, to prevent possible error messages, and also
-                 add "$." since parseCompressedProof() expects it */
-              let(&str1, cat(" ", str1, " $.", NULL));
-              /* Don't include the "$." in the length */
-              g_Statement[g_proveStatement].proofSectionLen = (long)strlen(str1) - 2;
-              /* We don't deallocate previous proofSectionPtr content because
-                 we will recover it after the verifyProof() */
-              g_Statement[g_proveStatement].proofSectionPtr = str1;
-              /******** end of 16-Jun-2017 nm ************/
-
-              g_outputToString = 1; /* Suppress error messages */
-              /* i = parseProof(g_proveStatement); */
-              /* if (i != 0) bug(1121); */
-              /* i = verifyProof(g_proveStatement); */
-              /* if (i != 0) bug(1122); */
-              /* 15-Apr-2015 nm parseProof, verifyProof, cleanWkrProof must be
-                 called in sequence to assign the g_WrkProof structure, verify
-                 the proof, and deallocate the g_WrkProof structure.  Either none
-                 of them or all of them must be called. */
-              parseProof(g_proveStatement);
-              verifyProof(g_proveStatement); /* Must be called even if error
-                                  occurred in parseProof, to init RPN stack
-                                  for cleanWrkProof() */
-              /* 15-Apr-2015 nm - don't change proof if there is an error
-                 (which could be pre-existing). */
-              i = (g_WrkProof.errorSeverity > 1);
-              /**** Here we look at the screen output sent to a string.
-                    This is rather crude, and someday the ability to
-                    check proofs and $d violations should be modularized *****/
-              j = instr(1, g_printString,
-                  "There is a disjoint variable ($d) violation");
-              g_outputToString = 0; /* Restore to normal output */
-              let(&g_printString, ""); /* Clear out the stored error messages */
-              cleanWrkProof(); /* Deallocate verifyProof storage */
-              g_Statement[g_proveStatement].proofSectionPtr
-                  = saveZappedProofSectionPtr;
-              g_Statement[g_proveStatement].proofSectionLen
-                  = saveZappedProofSectionLen;
-              g_Statement[g_proveStatement].proofSectionChanged
-                  = saveZappedProofSectionChanged; /* 16-Jun-2017 nm */
-              if (i != 0 || j != 0) {
-                /* There was a verify proof error (j!=0) or $d violation (i!=0)
-                   so don't used minimized proof */
-                /* Revert the proof to before minimization */
-                copyProofStruct(&g_ProofInProgress, saveProofForReverting);
-                /* Skip further printout and flag setting */
-                continue; /* Continue at 'Next k' loop end below */
-              }
-            } /* if (nmbrLen(g_Statement[k].reqDisjVarsA)) */
-
-            /* 25-Jun-2014 nm - not needed since trials now suppressed */
-            /*
-            if (verboseMode) {
-              print2("\n");
-            }
-            */
-
-            /*if (nmbrLen(g_Statement[k].reqDisjVarsA) || !mayGrowFlag) {*/
-
-            /* 3-May-2016 nm */
-            /* Warn user if a discouraged statement is overridden */
-            if (getMarkupFlag(k, USAGE_DISCOURAGED)) {
-              if (!overrideFlag) bug(1126);
-              /* print2("\n"); */ /* Enable for more emphasis */
-              print2(
-                  ">>> ?Warning: Overriding discouraged usage of \"%s\".\n",
-                  g_Statement[k].labelName);
-              /* print2("\n"); */ /* Enable for more emphasis */
-            }
-
-            if (!mayGrowFlag) {
-              /* Note:  this is the length BEFORE indentation and wrapping,
-                 so it is less than SHOW PROOF ... /SIZE */
-              if (newCompressedLength < oldCompressedLength) {
-                print2(
-     "Proof of \"%s\" decreased from %ld to %ld bytes using \"%s\".\n",
-                    g_Statement[g_proveStatement].labelName,
-                    oldCompressedLength, newCompressedLength,
-                    g_Statement[k].labelName);
-              } else {
-                if (newCompressedLength > oldCompressedLength) bug(1123);
-                print2(
-     "Proof of \"%s\" stayed at %ld bytes using \"%s\".\n",
-                    g_Statement[g_proveStatement].labelName,
-                    oldCompressedLength,
-                    g_Statement[k].labelName);
-                print2(
-    "    (Uncompressed steps decreased from %ld to %ld).\n",
-                    m, n );
-              }
-              /* (We don't care about compressed length if MAY_GROW) */
-              oldCompressedLength = newCompressedLength;
-            }
-
-            if (n < m && (mayGrowFlag || verboseMode)) {
-              print2(
-      "%sProof of \"%s\" decreased from %ld to %ld steps using \"%s\".\n",
-                (mayGrowFlag ? "" : "    "),
-                g_Statement[g_proveStatement].labelName,
-                m, n, g_Statement[k].labelName);
-            }
-            /* MAY_GROW possibility */
-            if (m < n) print2(
-      "Proof of \"%s\" increased from %ld to %ld steps using \"%s\".\n",
-                g_Statement[g_proveStatement].labelName,
-                m, n, g_Statement[k].labelName);
-            /* MAY_GROW possibility */
-            if (m == n) print2(
-                "Proof of \"%s\" remained at %ld steps using \"%s\".\n",
-                g_Statement[g_proveStatement].labelName,
-                m, g_Statement[k].labelName);
-            /* Distinct variable warning (obsolete) */
-            /*
-            if (nmbrLen(g_Statement[k].reqDisjVarsA)) {
-              printLongLine(cat("Note: \"", g_Statement[k].labelName,
-                  "\" has $d constraints.",
-                  "  SAVE NEW_PROOF then VERIFY PROOF to check them.",
-                  NULL), "", " ");
-            }
-            */
-            /* q = 0; */ /* Line length for label list */ /* 25-Jun-2014 del */
-
-            /* 8-Aug-2020 nm */
-            /* See if it's in another mathbox; if so, let user know */
-            assignMathboxInfo();
-            if (k > g_mathboxStmt && g_proveStatement > g_mathboxStmt) {
-              if (k < g_mathboxStart[getMathboxNum(g_proveStatement) - 1]) {
-                printLongLine(cat("\"", g_Statement[k].labelName,
-                      "\" is in the mathbox for ",
-                      g_mathboxUser[getMathboxNum(k) - 1], ".",
-                      NULL),
-                    "  ", " ");
-              }
-            }
-
-            prntStatus = 2; /* Found one */
-            g_proofChangedFlag = 1;
-
-            /* deleted 25-Jun-2014:
-            /@ 20-May-2012 nm @/
-            if (forbidMatchList[0]) { /@ User provided a /FORBID list @/
-              /@ Save the changed proof in case we have to restore
-                 it later @/
-              copyProofStruct(&saveProofForReverting, g_ProofInProgress);
-            }
-            */
-            /* 25-Jun-2014 nm */
-            /* Save the changed proof in case we have to restore
-               it later */
-            copyProofStruct(&saveProofForReverting, g_ProofInProgress);
-
-          }
-
-        } /* Next k (statement) */
-        /* 25-Jun-2014 nm - not needed since trials now suppressed */
-        /*
-        if (verboseMode) {
-          if (prntStatus) print2("\n");
-        }
-        */
-
-        if (g_proofChangedFlag && forwRevPass == 2) {
-          /* 25-Jun-2014 nm */
-          /* Check whether the reverse pass found a better proof than the
-             forward pass */
-          if (verboseMode) {
-            print2(
-"Forward vs. backward: %ld vs. %ld bytes; %ld vs. %ld steps\n",
-                      forwardCompressedLength,
-                      oldCompressedLength,
-                      forwardLength,
-                      nmbrLen(g_ProofInProgress.proof));
-          }
-          if (oldCompressedLength < forwardCompressedLength
-               || (oldCompressedLength == forwardCompressedLength &&
-                   nmbrLen(g_ProofInProgress.proof) < forwardLength)) {
-            /* The reverse pass was better */
-            print2("The backward scan results were used.\n");
-          } else {
-            copyProofStruct(&g_ProofInProgress, save1stPassProof);
-            print2("The forward scan results were used.\n");
-          }
-        }
-
-      } /* next forwRevPass */
-
-      if (prntStatus == 1 && !mayGrowFlag)
-        print2("No shorter proof was found.\n");
-      if (prntStatus == 1 && mayGrowFlag)
-        print2("The proof was not changed.\n");
-      if (!prntStatus /* && !noDistinctFlag */)
-        print2("?No earlier %s$p or $a label matches \"%s\".\n",
-            (overrideFlag ? "" : "(allowed) "),  /* 3-May-2016 nm */
-            g_fullArg[1]);
-      /* 25-Jun-2014 nm /NO_DISTINCT is obsolete
-      if (!prntStatus && noDistinctFlag) {
-        /@ 30-Jan-06 nm Added single-character-match wildcard argument @/
-        if (instr(1, g_fullArg[1], "@") || instr(1, g_fullArg[1], "?"))
-          print2("?No earlier $p or $a label (without $d) matches \"%s\".\n",
-              g_fullArg[1]);
-      }
-      */
-      /* 28-Jun-2011 nm */
-      if (!mathboxFlag && g_proveStatement >= g_mathboxStmt) {
-        print2(
-  "(Other mathboxes were not checked.  Use / INCLUDE_MATHBOXES to include them.)\n");
-      }
-
-      /* 16-Aug-2016 nm */
-      if (printTime == 1) {
-        getRunTime(&timeIncr);
-        print2("MINIMIZE_WITH run time = %7.2f sec for \"%s\"\n", timeIncr,
-            g_Statement[g_proveStatement].labelName);
-      }
-
-      /* 23-Nov-2019 nm */
-      let(&str1, ""); /* Deallocate memory */
-      nmbrLet(&nmbrSaveProof, NULL_NMBRSTRING); /* Deallocate memory */
-
-      /* 23-Nov-2019 nm */
-      /* Clear these Y/N trace strings unconditionally since new axioms are no
-        longer  allowed by default, so they may become set regardless of
-        qualifiers */
-      let(&traceProofFlags, ""); /* Deallocate memory */
-      let(&traceTrialFlags, ""); /* Deallocate memory */
-
-      /* 4-Aug-2019 nm */
-      if (allowNewAxiomsMatchList[0]) { /* User provided /NO_NEW_AXIOMS_FROM list */
-        let(&allowNewAxiomsMatchList, ""); /* Deallocate memory */
-      }
-
-      /* 22-Nov-2014 nm */
-      if (noNewAxiomsMatchList[0]) { /* User provided /ALLOW_NEW_AXIOMS list */
-        let(&noNewAxiomsMatchList, ""); /* Deallocate memory */
-      }
-
-      /* 20-May-2013 nm */
-      if (forbidMatchList[0]) { /* User provided a /FORBID list */
-        /*deallocProofStruct(&saveProofForReverting);*/ /* Deallocate memory */
-        let(&forbidMatchList, ""); /* Deallocate memory */
-      }
-
-      /* 25-Jun-2014 nm */
-      deallocProofStruct(&saveProofForReverting); /* Deallocate memory */
-      deallocProofStruct(&saveOrigProof); /* Deallocate memory */
-      deallocProofStruct(&save1stPassProof); /* Deallocate memory */
-
-      if (g_proofChangedFlag) {
-        g_proofChanged = 1; /* Cumulative flag */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-      }
-      continue;
-
-    } /* End if MINIMIZE_WITH */
-
-
-    /* 11-Sep-2016 nm Added EXPAND command */
-    if (cmdMatches("EXPAND")) {
-
-      g_proofChangedFlag = 0;
-      nmbrLet(&nmbrSaveProof, g_ProofInProgress.proof);
-      s = compressedProofSize(nmbrSaveProof, g_proveStatement);
-
-      for (i = g_proveStatement - 1; i >= 1; i--) {
-        if (g_Statement[i].type != (char)p_) continue; /* Not a $p */
-        /* 30-Jan-06 nm Added single-character-match wildcard argument */
-        if (!matchesList(g_Statement[i].labelName, g_fullArg[1], '*', '?')) {
-          continue;
-        }
-        sourceStatement = i;
-
-        nmbrTmp = expandProof(nmbrSaveProof,
-            sourceStatement /*, g_proveStatement*/);
-
-        if (!nmbrEq(nmbrTmp, nmbrSaveProof)) {
-          g_proofChangedFlag = 1;
-          n = compressedProofSize(nmbrTmp, g_proveStatement);
-          printLongLine(cat("Proof of \"",
-            g_Statement[g_proveStatement].labelName, "\" ",
-            (s == n ? cat("stayed at ", str((double)s), NULL)
-                : cat((s < n ? "increased from " : " decreased from "),
-                    str((double)s), " to ", str((double)n), NULL)),
-            " bytes after expanding \"",
-            g_Statement[sourceStatement].labelName, "\".", NULL), " ", " ");
-          s = n;
-          nmbrLet(&nmbrSaveProof, nmbrTmp);
-        }
-      }
-
-      if (g_proofChangedFlag) {
-        g_proofChanged = 1; /* Cumulative flag */
-        /* Clear the existing proof structure */
-        deallocProofStruct(&g_ProofInProgress);
-        /* Then rebuild proof structure from new proof nmbrTmp */
-        initProofStruct(&g_ProofInProgress, nmbrTmp, g_proveStatement);
-        /* Save the new proof structure on the undo stack */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-      } else {
-        print2("No expansion occurred.  The proof was not changed.\n");
-      }
-      nmbrLet(&nmbrSaveProof, NULL_NMBRSTRING);
-      nmbrLet(&nmbrTmp, NULL_NMBRSTRING);
-      continue;
-    } /* EXPAND */
-
-
-    if (cmdMatches("DELETE STEP") || (cmdMatches("DELETE ALL"))) {
-
-      if (cmdMatches("DELETE STEP")) {
-        s = (long)val(g_fullArg[2]); /* Step number */
-      } else {
-        s = nmbrLen(g_ProofInProgress.proof);
-      }
-      if ((g_ProofInProgress.proof)[s - 1] == -(long)'?') {
-        print2("?Step %ld is unknown and cannot be deleted.\n", s);
-        continue;
-      }
-      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-      if (s > m || s < 1) {
-        print2("?The step must be in the range from 1 to %ld.\n", m);
-        continue;
-      }
-
-      deleteSubProof(s - 1);
-      n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
-      if (m == n) {
-        print2("Step %ld was deleted.\n", s);
-      } else {
-        if (n > 1) {
-          printLongLine(cat("A ", str((double)(m - n + 1)),
-              "-step subproof at step ", str((double)s),
-              " was deleted.  Steps ", str((double)s), ":",
-              str((double)m), " are now ", str((double)(s - m + n)), ":",
-              str((double)n), ".",
-              NULL),
-              "", " ");
-        } else {
-          print2("The entire proof was deleted.\n");
-        }
-      }
-
-      /* 6/14/98 - Automatically display new unknown steps
-         ???Future - add switch to enable/defeat this */
-      typeProof(g_proveStatement,
-          1 /*pipFlag*/,
-          0 /*startStep*/,
-          0 /*endStep*/,
-          0 /*endIndent*/,
-          1 /*essentialFlag*/,
-          0 /*renumberFlag*/,
-          1 /*unknownFlag*/,
-          0 /*notUnifiedFlag*/,
-          0 /*reverseFlag*/,
-          0 /*noIndentFlag*/,
-          0 /*splitColumn*/,
-          0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-          0 /*texFlag*/,
-          0 /*g_htmlFlag*/);
-      /* 6/14/98 end */
-
-      g_proofChanged = 1; /* Cumulative flag */
-      processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-
-      continue;
-
-    }
-
-    if (cmdMatches("DELETE FLOATING_HYPOTHESES")) {
-
-      /* Get the essential step flags */
-      nmbrLet(&nmbrTmp, nmbrGetEssential(g_ProofInProgress.proof));
-
-      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-
-      n = 0; /* Earliest step that changed */
-      g_proofChangedFlag = 0;
-
-      for (s = m; s > 0; s--) {
-
-        /* Skip essential steps and unknown steps */
-        if (nmbrTmp[s - 1] == 1) continue; /* Not floating */
-        if ((g_ProofInProgress.proof)[s - 1] == -(long)'?') continue; /* Unknown */
-
-        /* Get the subproof length at step s */
-        q = subproofLen(g_ProofInProgress.proof, s - 1);
-
-        deleteSubProof(s - 1);
-
-        n = s - q + 1; /* Save earliest step changed */
-        g_proofChangedFlag = 1;
-        s = s - q + 1; /* Adjust step position to account for deleted subpr */
-      } /* Next step s */
-
-      if (g_proofChangedFlag) {
-        print2("All floating-hypothesis steps were deleted.\n");
-
-        if (n) {
-          print2("Steps %ld and above have been renumbered.\n", n);
-        }
-
-        /* 6/14/98 - Automatically display new unknown steps
-           ???Future - add switch to enable/defeat this */
-        typeProof(g_proveStatement,
-            1 /*pipFlag*/,
-            0 /*startStep*/,
-            0 /*endStep*/,
-            0 /*endIndent*/,
-            1 /*essentialFlag*/,
-            0 /*renumberFlag*/,
-            1 /*unknownFlag*/,
-            0 /*notUnifiedFlag*/,
-            0 /*reverseFlag*/,
-            0 /*noIndentFlag*/,
-            0 /*splitColumn*/,
-            0 /*skipRepeatedSteps*/, /* 28-Jun-2013 nm */
-            0 /*texFlag*/,
-            0 /*g_htmlFlag*/);
-        /* 6/14/98 end */
-
-        g_proofChanged = 1; /* Cumulative flag */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-      } else {
-        print2("?There are no floating-hypothesis steps to delete.\n");
-      }
-
-      continue;
-
-    } /* End if DELETE FLOATING_HYPOTHESES */
-
-    if (cmdMatches("INITIALIZE")) {
-
-      if (cmdMatches("INITIALIZE ALL")) {
-        i = nmbrLen(g_ProofInProgress.proof);
-
-        /* Reset the dummy variable counter (all will be refreshed) */
-        g_pipDummyVars = 0;
-
-        /* Initialize all steps */
-        for (j = 0; j < i; j++) {
-          initStep(j);
-        }
-
-        /* Assign known subproofs */
-        assignKnownSubProofs();
-
-        print2("All steps have been initialized.\n");
-        g_proofChanged = 1; /* Cumulative flag */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-        continue;
-      }
-
-        /* Added 16-Apr-06 nm */
-      if (cmdMatches("INITIALIZE USER")) {
-        i = nmbrLen(g_ProofInProgress.proof);
-        /* Delete all LET STEP assignments */
-        for (j = 0; j < i; j++) {
-          nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[j])),
-              NULL_NMBRSTRING);
-        }
-        print2(
-      "All LET STEP user assignments have been initialized (i.e. deleted).\n");
-        g_proofChanged = 1; /* Cumulative flag */
-        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-        continue;
-      }
-      /* End 16-Apr-06 */
-
-      /* cmdMatches("INITIALIZE STEP") */
-      s = (long)val(g_fullArg[2]); /* Step number */
-      if (s > nmbrLen(g_ProofInProgress.proof) || s < 1) {
-        print2("?The step must be in the range from 1 to %ld.\n",
-            nmbrLen(g_ProofInProgress.proof));
-        continue;
-      }
-
-      initStep(s - 1);
-
-      /* Also delete LET STEPs, per HELP INITIALIZE */          /* 16-Apr-06 */
-      nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[s - 1])),  /* 16-Apr-06 */
-              NULL_NMBRSTRING);                                 /* 16-Apr-06 */
-
-      print2(
-          "Step %ld and its hypotheses have been initialized.\n",
-          s);
-
-      g_proofChanged = 1; /* Cumulative flag */
-      processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
-      continue;
-
-    }
-
-
-    if (cmdMatches("SEARCH")) {
-      if (switchPos("/ ALL")) {
-        m = 1;  /* Include $e, $f statements */
-      } else {
-        m = 0;  /* Show $a, $p only */
-      }
-
-      /* 14-Apr-2008 nm added */
-      if (switchPos("/ JOIN")) {
-        joinFlag = 1;  /* Join $e's to $a,$p for matching */
-      } else {
-        joinFlag = 0;  /* Search $a,$p by themselves */
-      }
-
-      if (switchPos("/ COMMENTS")) {
-        n = 1;  /* Search comments */
-      } else {
-        n = 0;  /* Search statement math symbols */
-      }
-
-      let(&str1, g_fullArg[2]); /* String to match */
-
-      if (n) { /* COMMENTS switch */
-        /* Trim leading, trailing spaces; reduce white space to space;
-           convert to upper case */
-        let(&str1, edit(str1, 8 + 16 + 128 + 32));
-      } else { /* No COMMENTS switch */
-        /* Trim leading, trailing spaces; reduce white space to space */
-        let(&str1, edit(str1, 8 + 16 + 128));
-
-        /* Change all spaces to double spaces */
-        q = (long)strlen(str1);
-        let(&str3, space(q + q));
-        s = 0;
-        for (p = 0; p < q; p++) {
-          str3[p + s] = str1[p];
-          if (str1[p] == ' ') {
-            s++;
-            str3[p + s] = str1[p];
-          }
-        }
-        let(&str1, left(str3, q + s));
-
-        /* 30-Jan-06 nm Added single-character-match wildcard argument "$?"
-           (or "?" for convenience).  Use ASCII 3 for the exactly-1-char
-           wildcard character.  This is a single-character
-           match, not a single-token match:  we need "??" to match "ph". */
-        while (1) {
-          p = instr(1, str1, "$?");
-          if (!p) break;
-          let(&str1, cat(left(str1, p - 1), chr(3), right(str1, p + 2), NULL));
-        }
-        /* Allow just "?" for convenience. */
-        while (1) {
-          p = instr(1, str1, "?");
-          if (!p) break;
-          let(&str1, cat(left(str1, p - 1), chr(3), right(str1, p + 1), NULL));
-        }
-        /* End of 30-Jan-06 addition */
-
-        /* Change wildcard to ASCII 2 (to be different from printable chars) */
-        /* 1/3/02 (Why are we matching with and without space? I'm not sure.)*/
-        /* 30-Jan-06 nm Answer:  We need the double-spacing, and the removal
-           of space in the "with spaces" case, so that "ph $ ph" will match
-           "ph  ph" (0-token case) - "ph  $  ph" won't match this in the
-           (character-based, not token-based) matches().  The "with spaces"
-           case is for matching whole tokens, whereas the "without spaces"
-           case is for matching part of a token. */
-        while (1) {
-          p = instr(1, str1, " $* ");
-          if (!p) break;
-          /* This removes the space before and after the $* */
-          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 4), NULL));
-        }
-        while (1) {
-          p = instr(1, str1, "$*");
-          if (!p) break;
-          /* This simply replaces $* with chr(2) */
-          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 2), NULL));
-        }
-        /* 1/3/02  Also allow a plain $ as a wildcard, for convenience. */
-        while (1) {
-          p = instr(1, str1, " $ ");
-          if (!p) break;
-          /* 30-Jan-06 nm Bug fix - changed "2" to "3" below in order to
-             properly match 0 tokens */
-          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 3), NULL));
-        }
-        while (1) {
-          /* Note: the "$" shortcut must be done last to avoid picking up
-             "$*" and "$?". */
-          p = instr(1, str1, "$");
-          if (!p) break;
-          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 1), NULL));
-        }
-
-        /* Add wildcards to beginning and end to match middle of any string */
-        let(&str1, cat(chr(2), " ", str1, " ", chr(2), NULL));
-      } /* End no COMMENTS switch */
-
-      for (i = 1; i <= g_statements; i++) {
-        if (!g_Statement[i].labelName[0]) continue; /* No label */
-        if (!m && g_Statement[i].type != (char)p_ &&
-            g_Statement[i].type != (char)a_) {
-          continue; /* No /ALL switch */
-        }
-        /* 30-Jan-06 nm Added single-character-match wildcard argument */
-        if (!matchesList(g_Statement[i].labelName, g_fullArg[1], '*', '?'))
-          continue;
-        if (n) { /* COMMENTS switch */
-          let(&str2, "");
-          str2 = getDescription(i); /* str2 must be deallocated here */
-          /* Strip linefeeds and reduce spaces; cvt to uppercase */
-          j = instr(1, edit(str2, 4 + 8 + 16 + 128 + 32), str1);
-          if (!j) { /* No match */
-            let(&str2, "");
-            continue;
-          }
-          /* Strip linefeeds and reduce spaces */
-          let(&str2, edit(str2, 4 + 8 + 16 + 128));
-          j = j + ((long)strlen(str1) / 2); /* Center of match location */
-          p = g_screenWidth - 7 - (long)strlen(str((double)i)) - (long)strlen(g_Statement[i].labelName);
-                        /* Longest comment portion that will fit in one line */
-          q = (long)strlen(str2); /* Length of comment */
-          if (q <= p) { /* Use entire comment */
-            let(&str3, str2);
-          } else {
-            if (q - j <= p / 2) { /* Use right part of comment */
-              let(&str3, cat("...", right(str2, q - p + 4), NULL));
-            } else {
-              if (j <= p / 2) { /* Use left part of comment */
-                let(&str3, cat(left(str2, p - 3), "...", NULL));
-              } else { /* Use middle part of comment */
-                let(&str3, cat("...", mid(str2, j - p / 2, p - 6), "...",
-                    NULL));
-              }
-            }
-          }
-          print2("%s\n", cat(str((double)i), " ", g_Statement[i].labelName, " $",
-              chr(g_Statement[i].type), " \"", str3, "\"", NULL));
-          let(&str2, "");
-        } else { /* No COMMENTS switch */
-          let(&str2,nmbrCvtMToVString(g_Statement[i].mathString));
-
-          /* 14-Apr-2008 nm JOIN flag */
-          tmpFlag = 0; /* Flag that $p or $a is already in string */
-          if (joinFlag && (g_Statement[i].type == (char)p_ ||
-              g_Statement[i].type == (char)a_)) {
-            /* If $a or $p, prepend $e's to string to match */
-            k = nmbrLen(g_Statement[i].reqHypList);
-            for (j = k - 1; j >= 0; j--) {
-              p = g_Statement[i].reqHypList[j];
-              if (g_Statement[p].type == (char)e_) {
-                let(&str2, cat("$e ",
-                    nmbrCvtMToVString(g_Statement[p].mathString),
-                    tmpFlag ? "" : cat(" $", chr(g_Statement[i].type), NULL),
-                    " ", str2, NULL));
-                tmpFlag = 1; /* Flag that a $p or $a was added */
-              }
-            }
-          }
-
-          /* Change all spaces to double spaces */
-          q = (long)strlen(str2);
-          let(&str3, space(q + q));
-          s = 0;
-          for (p = 0; p < q; p++) {
-            str3[p + s] = str2[p];
-            if (str2[p] == ' ') {
-              s++;
-              str3[p + s] = str2[p];
-            }
-          }
-          let(&str2, left(str3, q + s));
-
-          let(&str2, cat(" ", str2, " ", NULL));
-          /* 30-Jan-06 nm Added single-character-match wildcard argument */
-          /* We should use matches() and not matchesList() here, because
-             commas can be legal token characters in math symbols */
-          if (!matches(str2, str1, 2/* ascii 2 0-or-more-token match char*/,
-              3/* ascii 3 single-token-match char*/))
-            continue;
-          let(&str2, edit(str2, 8 + 16 + 128)); /* Trim leading, trailing
-              spaces; reduce white space to space */
-          printLongLine(cat(str((double)i)," ",
-              g_Statement[i].labelName,
-              tmpFlag ? "" : cat(" $", chr(g_Statement[i].type), NULL),
-              " ", str2,
-              NULL), "    ", " ");
-        } /* End no COMMENTS switch */
-      } /* Next i */
-      continue;
-    }
-
-
-    if (cmdMatches("SET ECHO")) {
-      if (cmdMatches("SET ECHO ON")) {
-        g_commandEcho = 1;
-        /* 15-Jun-2009 nm Added "!" (see 15-Jun-2009 note above) */
-        print2("!SET ECHO ON\n");
-        print2("Command line echoing is now turned on.\n");
-      } else {
-        g_commandEcho = 0;
-        print2("Command line echoing is now turned off.\n");
-      }
-      continue;
-    }
-
-    if (cmdMatches("SET MEMORY_STATUS")) {
-      if (cmdMatches("SET MEMORY_STATUS ON")) {
-        print2("Memory status display has been turned on.\n");
-        print2("This command is intended for debugging purposes only.\n");
-        g_memoryStatus = 1;
-      } else {
-        g_memoryStatus = 0;
-        print2("Memory status display has been turned off.\n");
-      }
-      continue;
-    }
-
-
-    if (cmdMatches("SET JEREMY_HENTY_FILTER")) {
-      if (cmdMatches("SET JEREMY_HENTY_FILTER ON")) {
-        print2("The unification equivalence filter has been turned on.\n");
-        print2("This command is intended for debugging purposes only.\n");
-        g_hentyFilter = 1;
-      } else {
-        print2("This command is intended for debugging purposes only.\n");
-        print2("The unification equivalence filter has been turned off.\n");
-        g_hentyFilter = 0;
-      }
-      continue;
-    }
-
-
-    if (cmdMatches("SET EMPTY_SUBSTITUTION")) {
-      if (cmdMatches("SET EMPTY_SUBSTITUTION ON")) {
-        g_minSubstLen = 0;
-        print2("Substitutions with empty symbol sequences is now allowed.\n");
-        continue;
-      }
-      if (cmdMatches("SET EMPTY_SUBSTITUTION OFF")) {
-        g_minSubstLen = 1;
-        printLongLine(cat("The ability to substitute empty expressions",
-            " for variables  has been turned off.  Note that this may",
-            " make the Proof Assistant too restrictive in some cases.",
-            NULL),
-            "", " ");
-        continue;
-      }
-    }
-
-
-    if (cmdMatches("SET SEARCH_LIMIT")) {
-      s = (long)val(g_fullArg[2]); /* Timeout value */
-      print2("IMPROVE search limit has been changed from %ld to %ld\n",
-          g_userMaxProveFloat, s);
-      g_userMaxProveFloat = s;
-      continue;
-    }
-
-    if (cmdMatches("SET WIDTH")) { /* 18-Nov-85 nm Was SCREEN_WIDTH */
-      s = (long)val(g_fullArg[2]); /* Screen width value */
-
-      /************ 19-Jun-2020 nm printBuffer is now dynamically allocated
-      if (s >= PRINTBUFFERSIZE - 1) {
-        print2(
-"?Maximum screen width is %ld.  Recompile with larger PRINTBUFFERSIZE in\n",
-            (long)(PRINTBUFFERSIZE - 2));
-        print2("mminou.h if you need more.\n");
-        continue;
-      }
-      *************/
-
-      /* 26-Sep-2017 nm */
-      /* TODO: figure out why s=2 crashes program! */
-      if (s < 3) s = 3; /* Less than 3 may cause a segmentation fault */
-      i = g_screenWidth;
-      g_screenWidth = s; /* 26-Sep-2017 nm - print with new screen width */
-      print2("Screen width has been changed from %ld to %ld.\n",
-          i, s);
-      continue;
-    }
-
-
-    if (cmdMatches("SET HEIGHT")) {  /* 18-Nov-05 nm Added */
-      s = (long)val(g_fullArg[2]); /* Screen height value */
-      if (s < 2) s = 2;  /* Less than 2 makes no sense */
-      i = g_screenHeight;
-      g_screenHeight = s - 1;
-      print2("Screen height has been changed from %ld to %ld.\n",
-          i + 1, s);
-      /* g_screenHeight is one less than the physical screen to account for the
-         prompt line after pausing. */
-      continue;
-    }
-
-
-    /* 10-Jul-2016 nm */
-    if (cmdMatches("SET DISCOURAGEMENT")) {
-      if (!strcmp(g_fullArg[2], "ON")) {
-        g_globalDiscouragement = 1;
-        print2("\"(...is discouraged.)\" markup tags are now honored.\n");
-      } else if (!strcmp(g_fullArg[2], "OFF")) {
-        print2(
-    "\"(...is discouraged.)\" markup tags are no longer honored.\n");
-        /* print2("\n"); */ /* Enable for more emphasis */
-        print2(
-">>> ?Warning: This setting is intended for advanced users only.  Please turn\n");
-        print2(
-">>> it back ON if you are not intimately familiar with this database.\n");
-        /* print2("\n"); */ /* Enable for more emphasis */
-        g_globalDiscouragement = 0;
-      } else {
-        bug(1129);
-      }
-      continue;
-    }
-
-
-    /* 14-May-2017 nm */
-    if (cmdMatches("SET CONTRIBUTOR")) {
-      print2("\"Contributed by...\" name was changed from \"%s\" to \"%s\"\n",
-          g_contributorName, g_fullArg[2]);
-      let(&g_contributorName, g_fullArg[2]);
-      continue;
-    }
-
-
-    /* 31-Dec-2017 nm */
-    if (cmdMatches("SET ROOT_DIRECTORY")) {
-      let(&str1, g_rootDirectory); /* Save previous one */
-      let(&g_rootDirectory, edit(g_fullArg[2], 2/*discard spaces,tabs*/));
-      if (g_rootDirectory[0] != 0) {  /* Not an empty directory path */
-        /* Add trailing "/" to g_rootDirectory if missing */
-        if (instr(1, g_rootDirectory, "\\") != 0
-            || instr(1, g_input_fn, "\\") != 0
-            || instr(1, g_output_fn, "\\") != 0 ) {
-          /* Using Windows-style path (not really supported, but at least
-             make full path consistent) */
-          if (g_rootDirectory[strlen(g_rootDirectory) - 1] != '\\') {
-            let(&g_rootDirectory, cat(g_rootDirectory, "\\", NULL));
-          }
-        } else {
-          if (g_rootDirectory[strlen(g_rootDirectory) - 1] != '/') {
-            let(&g_rootDirectory, cat(g_rootDirectory, "/", NULL));
-          }
-        }
-      }
-      if (strcmp(str1, g_rootDirectory)){
-        print2("Root directory was changed from \"%s\" to \"%s\"\n",
-            str1, g_rootDirectory);
-      }
-      let(&str1, "");
-      continue;
-    }
-
-
-    /* 1-Nov-2013 nm Added UNDO */
-    if (cmdMatches("SET UNDO")) {
-      s = (long)val(g_fullArg[2]); /* Maximum UNDOs */
-      if (s < 0) s = 0;  /* Less than 0 UNDOs makes no sense */
-      /* Reset the stack size if it changed */
-      if (processUndoStack(NULL, PUS_GET_SIZE, "", 0) != s) {
-        print2(
-            "The maximum number of UNDOs was changed from %ld to %ld\n",
-            processUndoStack(NULL, PUS_GET_SIZE, "", 0), s);
-        processUndoStack(NULL, PUS_NEW_SIZE, "", s);
-        if (g_PFASmode == 1) {
-          /* If we're in the Proof Assistant, assign the first stack
-             entry with the current proof (the stack was erased) */
-          processUndoStack(&g_ProofInProgress, PUS_PUSH, "", 0);
-        }
-      } else {
-        print2("The maximum number of UNDOs was not changed.\n");
-      }
-      continue;
-    }
-
-
-    if (cmdMatches("SET UNIFICATION_TIMEOUT")) {
-      s = (long)val(g_fullArg[2]); /* Timeout value */
-      print2("Unification timeout has been changed from %ld to %ld\n",
-          g_userMaxUnifTrials,s);
-      g_userMaxUnifTrials = s;
-      continue;
-    }
-
-
-    if (cmdMatches("OPEN LOG")) {
-        /* Open a log file */
-        let(&g_logFileName, g_fullArg[2]);
-        g_logFilePtr = fSafeOpen(g_logFileName, "w", 0/*noVersioningFlag*/);
-        if (!g_logFilePtr) continue; /* Couldn't open it (err msg was provided) */
-        g_logFileOpenFlag = 1;
-        print2("The log file \"%s\" was opened %s %s.\n",g_logFileName,
-            date(),time_());
-        continue;
-    }
-
-    if (cmdMatches("CLOSE LOG")) {
-        /* Close the log file */
-        if (!g_logFileOpenFlag) {
-          print2("?Sorry, there is no log file currently open.\n");
-        } else {
-          print2("The log file \"%s\" was closed %s %s.\n",g_logFileName,
-              date(),time_());
-          fclose(g_logFilePtr);
-          g_logFileOpenFlag = 0;
-        }
-        let(&g_logFileName,"");
-        continue;
-    }
-
-    if (cmdMatches("OPEN TEX")) {
-    /* 2-Oct-2017 nm OPEN HTML is very obsolete, no need to warn anymore
-    if (cmdMatches("OPEN TEX") || cmdMatches("OPEN HTML") ) {
-      if (cmdMatches("OPEN HTML")) {
-        print2("?OPEN HTML is obsolete - use SHOW STATEMENT * / HTML\n");
-        continue;
-      }
-    */
-
-      /* 17-Nov-2015 TODO: clean up mixed LaTeX/HTML attempts (check
-         g_texFileOpenFlag when switching to HTML & close LaTeX file) */
-
-      if (g_texDefsRead) {
-        /* Current limitation - can only read .def once */
-        /* 2-Oct-2017 nm OPEN HTML is obsolete */
-        /*if (cmdMatches("OPEN HTML") != g_htmlFlag) {*/
-        if (g_htmlFlag) {
-          /* Actually it isn't clear to me this is still the case, but
-             to be safe I left it in */
-          print2("?You cannot use both LaTeX and HTML in the same session.\n");
-          print2(
-              "?You must EXIT and restart Metamath to switch to the other.\n");
-          continue;
-        }
-      }
-      /* 2-Oct-2017 nm OPEN HTML is obsolete */
-      /*g_htmlFlag = cmdMatches("OPEN HTML");*/
-
-      /* Open a TeX file */
-      let(&g_texFileName,g_fullArg[2]);
-      if (switchPos("/ NO_HEADER")) {
-        texHeaderFlag = 0;
-      } else {
-        texHeaderFlag = 1;
-      }
-      /* 14-Sep-2010 nm Added OLD_TEX */
-      if (switchPos("/ OLD_TEX")) {
-        g_oldTexFlag = 1;
-      } else {
-        g_oldTexFlag = 0;
-      }
-      g_texFilePtr = fSafeOpen(g_texFileName, "w", 0/*noVersioningFlag*/);
-      if (!g_texFilePtr) continue; /* Couldn't open it (err msg was provided) */
-      g_texFileOpenFlag = 1;
-      /* 2-Oct-2017 nm OPEN HTML is obsolete */
-      print2("Created %s output file \"%s\".\n",
-          g_htmlFlag ? "HTML" : "LaTeX", g_texFileName);
-      printTexHeader(texHeaderFlag);
-      g_oldTexFlag = 0;
-      continue;
-    }
-
-    /* 2-Oct-2017 nm CLOSE HTML is obsolete */
-    /******
-    if (cmdMatches("CLOSE TEX") || cmdMatches("CLOSE HTML")) {
-      if (cmdMatches("CLOSE HTML")) {
-        print2("?CLOSE HTML is obsolete - use SHOW STATEMENT @ / HTML\n");
-        continue;
-      }
-      /@ Close the TeX file @/
-      if (!g_texFileOpenFlag) {
-        print2("?Sorry, there is no %s file currently open.\n",
-            g_htmlFlag ? "HTML" : "LaTeX");
-      } else {
-        print2("The %s output file \"%s\" has been closed.\n",
-            g_htmlFlag ? "HTML" : "LaTeX", g_texFileName);
-        printTexTrailer(texHeaderFlag);
-        fclose(g_texFilePtr);
-        g_texFileOpenFlag = 0;
-      }
-      let(&g_texFileName,"");
-      continue;
-    }
-    *****/
-    if (cmdMatches("CLOSE TEX")) {
-      /* Close the TeX file */
-      if (!g_texFileOpenFlag) {
-        print2("?Sorry, there is no LaTeX file currently open.\n");
-      } else {
-        print2("The LaTeX output file \"%s\" has been closed.\n",
-            g_texFileName);
-        printTexTrailer(texHeaderFlag);
-        fclose(g_texFilePtr);
-        g_texFileOpenFlag = 0;
-      }
-      let(&g_texFileName,"");
-      continue;
-    }
-
-    /* Similar to Unix 'more' */
-    if (cmdMatches("MORE")) {
-      list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
-      if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
-      while (1) {
-        if (!linput(list1_fp, NULL, &str1)) break; /* End of file */
-        /* Print a line on the screen */
-        if (!print2("%s\n", str1)) break; /* User typed Q */
-      }
-      fclose(list1_fp);
-      continue;
-    } /* end MORE */
-
-
-    if (cmdMatches("FILE SEARCH")) {
-      /* Search the contents of a file and type on the screen */
-
-      type_fp = fSafeOpen(g_fullArg[2], "r", 0/*noVersioningFlag*/);
-      if (!type_fp) continue; /* Couldn't open it (error msg was provided) */
-      fromLine = 0;
-      toLine = 0;
-      searchWindow = 0;
-      i = switchPos("/ FROM_LINE");
-      if (i) fromLine = (long)val(g_fullArg[i + 1]);
-      i = switchPos("/ TO_LINE");
-      if (i) toLine = (long)val(g_fullArg[i + 1]);
-      i = switchPos("/ WINDOW");
-      if (i) searchWindow = (long)val(g_fullArg[i + 1]);
-      /*??? Implement SEARCH /WINDOW */
-      if (i) print2("Sorry, WINDOW has not be implemented yet.\n");
-
-      let(&str2, g_fullArg[3]); /* Search string */
-      let(&str2, edit(str2, 32)); /* Convert to upper case */
-
-      tmpFlag = 0;
-
-      /* Search window buffer */
-      pntrLet(&pntrTmp, pntrSpace(searchWindow));
-
-      j = 0; /* Line # */
-      m = 0; /* # matches */
-      while (linput(type_fp, NULL, &str1)) {
-        j++;
-        if (j > toLine && toLine != 0) break;
-        if (j >= fromLine || fromLine == 0) {
-          let(&str3, edit(str1, 32)); /* Convert to upper case */
-          if (instr(1, str3, str2)) { /* Match occurred */
-            if (!tmpFlag) {
-              tmpFlag = 1;
-              print2(
-                    "The line number in the file is shown before each line.\n");
-            }
-            m++;
-            if (!print2("%ld:  %s\n", j, left(str1,
-                MAX_LEN - (long)strlen(str((double)j)) - 3))) break;
-          }
-        }
-        for (k = 1; k < searchWindow; k++) {
-          let((vstring *)(&pntrTmp[k - 1]), pntrTmp[k]);
-        }
-        if (searchWindow > 0)
-            let((vstring *)(&pntrTmp[searchWindow - 1]), str1);
-      }
-      if (!tmpFlag) {
-        print2("There were no matches.\n");
-      } else {
-        if (m == 1) {
-          print2("There was %ld matching line in the file %s.\n", m,
-              g_fullArg[2]);
-        } else {
-          print2("There were %ld matching lines in the file %s.\n", m,
-              g_fullArg[2]);
-        }
-      }
-
-      fclose(type_fp);
-
-      /* Deallocate search window buffer */
-      for (i = 0; i < searchWindow; i++) {
-        let((vstring *)(&pntrTmp[i]), "");
-      }
-      pntrLet(&pntrTmp, NULL_PNTRSTRING);
-
-
-      continue;
-    }
-
-
-    if (cmdMatches("SET UNIVERSE") || cmdMatches("ADD UNIVERSE") ||
-        cmdMatches("DELETE UNIVERSE")) {
-
-      /*continue;*/ /* ???Not implemented */
-    } /* end if xxx UNIVERSE */
-
-
-
-    if (cmdMatches("SET DEBUG FLAG")) {
-      print2("Notice:  The DEBUG mode is intended for development use only.\n");
-      print2("The printout will not be meaningful to the user.\n");
-      i = (long)val(g_fullArg[3]);
-      if (i == 4) db4 = 1;  /* Not used */
-      if (i == 5) db5 = 1;  /* mmpars.c statistics; mmunif.c overview */
-      if (i == 6) db6 = 1;  /* mmunif.c details */
-      if (i == 7) db7 = 1;  /* mmunif.c more details; mmveri.c */
-      if (i == 8) db8 = 1;  /* mmpfas.c unification calls */
-      if (i == 9) db9 = 1;  /* memory */ /* use SET MEMORY_STATUS ON instead */
-      continue;
-    }
-    if (cmdMatches("SET DEBUG OFF")) {
-      db4 = 0;
-      db5 = 0;
-      db6 = 0;
-      db7 = 0;
-      db8 = 0;
-      db9 = 0;
-      print2("The DEBUG mode has been turned off.\n");
-      continue;
-    }
-
-    if (cmdMatches("ERASE")) {
-      if (g_sourceChanged) {
-        print2("Warning:  You have not saved changes to the source.\n");
-        str1 = cmdInput1("Do you want to ERASE anyway (Y, N) <N>? ");
-        if (str1[0] != 'y' && str1[0] != 'Y') {
-          print2("Use WRITE SOURCE to save the changes.\n");
-          continue;
-        }
-        g_sourceChanged = 0;
-      }
-      eraseSource();
-      g_sourceHasBeenRead = 0; /* Global variable */ /* 31-Dec-2017 nm */
-      g_showStatement = 0;
-      g_proveStatement = 0;
-      print2("Metamath has been reset to the starting state.\n");
-      continue;
-    }
-
-    if (cmdMatches("VERIFY PROOF")) {
-      if (switchPos("/ SYNTAX_ONLY")) {
-        verifyProofs(g_fullArg[2],0); /* Parse only */
-      } else {
-        verifyProofs(g_fullArg[2],1); /* Parse and verify */
-      }
-      continue;
-    }
-
-    /* 7-Nov-2015 nm New */  /* 17-Nov-2015 nm Updated */
-    /* 25-Jun-2020 nm Added UNDERSCORE_SKIP */
-    /* 17-Jul-2020 nm Added MATHBOX_SKIP */
-    if (cmdMatches("VERIFY MARKUP")) {
-      i = (switchPos("/ DATE_SKIP") != 0) ? 1 : 0;
-      j = (switchPos("/ TOP_DATE_SKIP") != 0) ? 1 : 0;
-      k = (switchPos("/ FILE_SKIP") != 0) ? 1 : 0;
-      l = (switchPos("/ UNDERSCORE_SKIP") != 0) ? 1 : 0;
-      m = (switchPos("/ MATHBOX_SKIP") != 0) ? 1 : 0;
-      n = (switchPos("/ VERBOSE") != 0) ? 1 : 0;
-      if (i == 1 && j == 1) {
-        printf(
-            "?Only one of / DATE_SKIP and / TOP_DATE_SKIP may be specified.\n");
-        continue;
-      }
-      verifyMarkup(g_fullArg[2],
-          (flag)i, /* 1 = skip checking date consistency */
-          (flag)j, /* 1 = skip checking top date only */
-          (flag)k, /* 1 = skip checking external files GIF, mmset.html,... */
-          (flag)l, /* 1 = skip checking labels for underscores */
-          (flag)m, /* 1 = skip checking mathbox cross-references */
-          (flag)n); /* 1 = verbose mode */  /* 26-Dec-2016 nm */
-      continue;
-    }
-
-    /* 10-Dec-2018 nm Added */
-    if (cmdMatches("MARKUP")) {
-      g_htmlFlag = 1;
-      g_altHtmlFlag = (switchPos("/ ALT_HTML") != 0);
-      if ((switchPos("/ HTML") != 0) == (switchPos("/ ALT_HTML") != 0)) {
-        print2("?Please specify exactly one of / HTML and / ALT_HTML.\n");
-        continue;
-      }
-      i = 0;
-      i = ((switchPos("/ SYMBOLS") != 0) ? PROCESS_SYMBOLS : 0)
-          + ((switchPos("/ LABELS") != 0) ? PROCESS_LABELS : 0)
-          + ((switchPos("/ NUMBER_AFTER_LABEL") != 0) ? ADD_COLORED_LABEL_NUMBER : 0)
-          + ((switchPos("/ BIB_REFS") != 0) ? PROCESS_BIBREFS : 0)
-          + ((switchPos("/ UNDERSCORES") != 0) ? PROCESS_UNDERSCORES : 0);
-      processMarkup(g_fullArg[1], /* Input file */
-          g_fullArg[2],  /* Output file */
-          (switchPos("/ CSS") != 0),
-          i); /* Action bits */
-      continue;
-    }
-
-    print2("?This command has not been implemented.\n");
-    continue;
-
-  }
-} /* command */
-
-
+/*****************************************************************************/
+/* Program name:  metamath                                                   */
+/* Copyright (C) 2021 NORMAN MEGILL  nm at alum.mit.edu  http://metamath.org */
+/* License terms:  GNU General Public License Version 2 or any later version */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* Copyright notice:  All code in this program that was written by Norman
+   Megill is public domain.  However, the project includes code contributions
+   from other people which may be GPL licensed.  For more details see:
+   https://github.com/metamath/metamath-exe/issues/7#issuecomment-675555069 */
+
+/*! \file
+ * Contains main(), the starting point of metamath; executes or calls commands
+ */
+
+/* The overall functionality of the modules is as follows:
+    metamath.c - Contains main(); executes or calls commands
+    mmcmdl.c - Command line interpreter
+    mmcmds.c - Extends metamath.c command() to execute SHOW and other
+               commands; added after command() became too bloated (still is:)
+    mmdata.c - Defines global data structures and manipulates arrays
+               with functions similar to BASIC string functions;
+               memory management; converts between proof formats
+    mmhlpa.c - The help file, part 1.
+    mmhlpb.c - The help file, part 2.
+    mminou.c - Basic input and output interface
+    mmpars.c - Parses the source file
+    mmpfas.c - Proof Assistant
+    mmunif.c - Unification algorithm for Proof Assistant
+    mmveri.c - Proof verifier for source file
+    mmvstr.c - BASIC-like string functions
+    mmwtex.c - LaTeX/HTML source generation
+    mmword.c - File revision utility (for TOOLS> UPDATE) (not generally useful)
+*/
+
+/* Compilation instructions (gcc on Unix/Linus/Cygwin, lcc on Windows):
+   1. Make sure each .c file above is present in the compilation directory and
+      that each .c file (except metamath.c) has its corresponding .h file
+      present.
+   2. In the directory where these files are present, type:
+         gcc m*.c -o metamath
+   3. For full error checking, use:
+         gcc m*.c -o metamath -O2 -Wall -Wextra -Wmissing-prototypes \
+             -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-align \
+             -Wredundant-decls -Wnested-externs -Winline -Wno-long-long \
+             -Wconversion -Wstrict-prototypes -std=c99 -pedantic -Wunused-result
+      Note: gcc 4.9.2 (on Debian) fails with "unknown type name `ssize_t'" if
+      -std=c99 is used, so omit -std=c99 to work around this problem.
+   4. For faster runtime, use these gcc options:
+         gcc m*.c -o metamath -O3 -funroll-loops -finline-functions \
+             -fomit-frame-pointer -Wall -std=c99 -pedantic -fno-strict-overflow
+   5. The Windows version in the download was compiled with lcc-win32 version 3.8:
+         lc -O m*.c -o metamath.exe
+   6. On Linux, if you have autoconf, automake, and a C compiler, you
+      can compile with the command "autoreconf -i && ./configure && make".
+      See the README.TXT file for more information.
+*/
+
+
+/*! \def MVERSION
+ * The current version of metamath.  It is incremented each time the software
+ * is modified.  When main versions are released, the version consists of a
+ * major version, followed by a dot and a three-digit minor version.
+ * Pre-release versions are further followed by a free style suffix that
+ * should allow ordering.
+ * The version string is extracted and then processed by shell and perl
+ * scripts.  To avoid problems during replacements:
+ * - use only printable characters from the ASCII range;
+ * - avoid characters from the following set, eligible for escaping in text, regular
+ *     expressions and so on like -begin of list ][`*+^$'?"{/}\ end of list-;
+ * - use no space character other than simple space (U+0020);
+ * - never use space characters at the beginning or at the end;
+ * - the length is limited to 26 characters.
+ */
+#define MVERSION "0.199.pre 29-Jan-2022"
+/* 0.199.pre
+   30-Dec-2021 mc metamath.c mmdata.c mminou.c mmmaci.c -
+     Remove mmmaci and everything related to THINK_C compiler
+   4-Jan-2022 mc - change VERIFY MARKUP /TOP_DATE_SKIP and /FILE_SKIP to
+     /TOP_DATE_CHECK and /FILE_CHECK (with opposite meaning), and make the
+     skip behavior the default. */
+/* 0.198 nm 7-Aug-2021 mmpars.c - Fix cosmetic bug in WRITE SOURCE ... /REWRAP
+   that prevented end of sentence (e.g. period) from appearing in column 79,
+   thus causing some lines to be shorter than necessary. */
+/* 0.197 nm 2-Aug-2021 mmpars.c - put two spaces between $c,v on same line
+   in /rewrap; mmwtex.c mmhlpa.c mminou.c - minor edits */
+/* 0.196 nm 31-Dec-2020 metamath.c mmpars.c - fix bug that deleted comments
+   that were followed by ${, $}, $c, $v, $d on the same line */
+/* 0.195 nm 30-Dec-2020 metamath.c - temporarily disable /REWRAP until bug fixed
+   27-Sep-2020 nm mmwtex.c - prevent "htmlexturl" links from wrapping */
+/* 0.194 26-Dec-2020 nm mmwtex.c - add keyword "htmlexturl" to $t
+   statement in .mm file */
+/* 0.193 12-Sep-2020 nm mmcmds.c mmdata.c,h mmwtex.c,h mmhlpa.c - make the
+   output of /EXTRACT stable in the sense that, with the same <label-list>
+   parameter, extract(extract(file)) = extract(file) except that the date
+   stamp at the top will be updated.  (The first extraction even if "*" will
+   usually be different because it discards non-relevant content.  Note that
+   the include file directives "$( $[ Begin..." etc. and comments with "$j" are
+   currently discarded.) */
+/* 0.192 4-Sep-2020 nm metamath.c - fix bug */
+/* 0.191 4-Sep-2020 nm metamath.c - add comment close */
+/* 0.190 4-Sep-2020 nm mmcmds.c - fix bug in writeExtractedSource() */
+/* 0.189 4-Sep-2020 nm mmhlpa.c - add help for WRITE SOURCE .. /EXTRACT ...
+   24-Aug-2020 nm metamath.c mmcmdl.c mmcmds.c,h mmdata.c,h mmhlpa.c
+     mmpars.c mmpfas.c mmunif.c mmwtex.c,h - Added
+     WRITE SOURCE ... /EXTRACT ... */
+/* 0.188 23-Aug-2020 nm mmwtex.c, mmhlpa.c Added CONCLUSION FACT INTRODUCTION
+     PARAGRAPH SCOLIA SCOLION SUBSECTION TABLE to [bib] keywords */
+/* 0.187 15-Aug-2020 nm All m*.c, m*.h - put "g_" in front of all global
+     variable names e.g. "statements" becomes "g_statements"; also capitalized
+     1st letter of original name in case of global structs e.g. "statement"
+     becomes "g_Statement".
+   9-Aug-2020 nm mmcmdl.c, mmhlpa.c - add HELP BIBLIOGRAPHY */
+/* 0.186 8-Aug-2020 nm mmwtex.c, mmhlpa.c - add CONJECTURE, RESULT to [bib]
+     keywords
+   8-Aug-2020 nm mmpfas.c, metamath.c - print message when IMPROVE or
+     MINIMIZE_WITH uses another mathbox */
+/* 0.185 5-Aug-2020 nm metamath.c mmcmdl.c mmhlpb.c mmpfas.c,h mmcmds.c
+     mmwtex.c,h - add /INCLUDE_MATHBOXES to to IMPROVE; notify user upon ASSIGN
+     from another mathbox.
+   18-Jul-2020 nm mmcmds.c, mmdata.c, mmhlpb.c, metamath.c - "PROVE =" will now
+     resume the previous MM-PA session if there was one; allow "~" to start/end
+     with blank (meaning first/last statement); add "@1234" */
+/* 0.184 17-Jul-2020 nm metamath.c mmcmdl.c mmcmds.c,h mmhlpb.c mmwtex.c,h -
+     add checking for mathbox independence to VERIFY MARKUP; add /MATHBOX_SKIP
+   4-Jul-2020 nm mmwtex.c - correct error msg for missing althtmldef
+   3-Jul-2020 nm metamath.c, mmhlpa.c - allow space in TOOLS> BREAK */
+/* 0.183 30-Jun-2020 30-Jun-2020 nm mmpars.c - refine prevention of
+     WRITE SOURCE.../REWRAP from modifying comments containing "<HTML>"
+     (specifically, remove indentation alignment).
+   25-Jun-2020 nm metamath.c, mmcmds.c,h mmcmdl.c mmhlpb.c - add underscore
+     checking in VERIFY MARKUP and add /UNDERSCORE_SKIP qualifier; also check
+     for trailing space on lines.
+   20-Jun-2020 nm mmcmds.c - check for discouragement tags in *ALT, *OLD
+     labels in VERIFY MARKUP.
+   19-Jun-2020 nm mminou.c,h, metamath.c, mmwtex.c - dynamically allocate
+     buffer in print2() using vsnprintf() to calculate size needed
+   18-Jun-2020 nm mmpars.c - remove error check for $e <- $f assignments.  See
+     https://groups.google.com/d/msg/metamath/Cx_d84uorf8/0FrNYTM9BAAJ */
+/* 0.182 12-Apr-2020 nm mmwtex.c, mmphlpa.c - add "Claim" to bib ref types */
+/* 0.181 12-Feb-2020 nm (reported by David Starner) metamath.c - fix bug causing
+     new axioms to be used by MINIMIZE_WITH */
+/* 0.180 10-Dec-2019 nm (bj 13-Sep-2019) mmpars.c - fix "line 0" in error msg
+     when label clashes with math symbol
+   8-Dec-2019 nm (bj 13-Oct-2019) mmhlpa.c - improve TOOLS> HELP INSERT, DELETE
+   8-Dec-2019 nm (bj 19-Sep-2019) mminou.c - change bug 1511 to error message
+   30-Nov-2019 nm (bj 12-Oct-2019) mmwtex.c - trigger Most Recent link on
+     mmtheorems.html when there is a mathbox statement (currently set.mm and
+     iset.mm).
+   30-Nov-2019 nm (bj 13-Sep-2019) mmhlpa.c - improve help for TOOLS> DELETE and
+     SUBSTITUTE.
+   30-Nov-2019 nm (bj 13-Sep-2019) mmwtex.c - change "g_htmlHome" in warnings to
+     "htmlhome". */
+/* 0.179 29-Nov-2019 nm (bj 22-Sep-2019) metamath.c - MINIMIZE_WITH axiom trace
+     now starts from current NEW_PROOF instead of SAVEd proof.
+   23-Nov-2019 nm (bj 4-Oct-2019) metamath.c - make sure traceback flags are
+     cleared after MINIMIZE_WITH
+   20-Nov-2019 nm mmhlpa.c - add url pointer to HELP WRITE SOURCE /SPLIT
+   18-Nov-2019 nm mmhlpa.c - clarify HELP WRITE SOURCE /REWRAP
+   15-Oct-2019 nm mmdata.c - add bug check info for user
+   14-Oct-2019 nm mmcmds.c - use '|->' (not 'e.') as syntax hint for maps-to
+   14-Oct-2019 nm mmwtex.c - remove extraneous </TD> */
+/* 0.178 10-Aug-2019 nm mminou.c - eliminate redundant fopen in fSafeOpen
+   6-Aug-2019 nm mmwtex.c,h, mmcmds.c - Add error check for >1 line
+     section name or missing closing decoration line in getSectionHeadings()
+   4-Aug-2019 nm mmhlpb.c, mmcmdl.c, metamath.c - Add /ALLOW_NEW_AXIOMS,
+     renamed /ALLOW_GROWTH to /MAY_GROW
+   17-Jul-2019 nm mmcmdl.c, mmhlpa.c, metamath.c - Add /NO_VERSIONING to
+     WRITE THEOREM_LIST
+   17-Jul-2019 nm metamath.c - Change line of dashes between SHOW STATEMENT
+     output from hardcoded 79 to current g_screenWidth */
+/* 0.177 27-Apr-2019 nm mmcmds.c -"set" -> "setvar" in htmlAllowedSubst.
+   mmhlpb.c - fix typos in HELP IMPROVE. */
+/* 0.176 25-Mar-2019 nm metamath.c mmcmds.h mmcmds.c mmcmdl.c mmhlpb.c -
+   add /TOP_DATE_SKIP to VERIFY MARKUP */
+/* 0.175 8-Mar-2019 nm mmvstr.c - eliminate warning in gcc 8.3 (patch
+   provided by David Starner) */
+/* 0.174 22-Feb-2019 nm mmwtex.c - fix erroneous warning when using "[["
+   bracket escape in comment */
+/* 0.173 3-Feb-2019 nm mmwtex.c - fix infinite loop when "[" was the first
+   character in a comment */
+/* 0.172 25-Jan-2019 nm mmwtex.c - comment out bug 2343 trap (not a bug) */
+/* 0.171 13-Dec-2018 nm metamath.c, mmcmdl.c, mmhlpa.c, mmcmds.c,h, mmwtex.c,h
+   - add fine-grained qualifiers to MARKUP command */
+/* 0.170 12-Dec-2018 nm mmwtex.c - restore line accidentally deleted in 0.169 */
+/* 0.169 10-Dec-2018 nm metamath.c, mmcmds.c,h, mmcmdl.c, mmpars.c, mmhlpa.c,
+   mmwtex.c - Add MARKUP command.
+   9-Dec-2018 nm mmwtex.c - escape literal "[" with "[[" in comments. */
+/* 0.168 8-Dec-2018 nm metamath.c - validate that /NO_REPEATED_STEPS is used
+   only with /LEMMON.
+   8-Dec-2018 nm mmcmds.c - fix bug #256 reported by Jim Kingdon
+   (https://github.com/metamath/set.mm/issues/497). */
+/* 0.167 13-Nov-2018 nm mmcmds.c - SHOW TRACE_BACK .../COUNT now uses proof
+   the way it's stored (previously, it always uncompressed the proof).  The
+   new step count (for compressed proofs) corresponds to the step count the
+   user would see on the web pages.
+   12-Nov-2018 nm mmcmds.c - added unlimited precision arithmetic
+   for SHOW TRACE_BACK .../COUNT/ESSENTIAL */
+/* 0.166 31-Oct-2018 nm mmwtex.c - workaround Chrome anchor bug
+   30-Oct-2018 nm mmcmds.c - put "This theorem is referenced by" after
+   axioms and definitions used in HTML; use "(None)" instead of suppressing
+   line if nothing is referenced */
+/* 0.165 20-Oct-2018 nm mmwtex.c - added ~ mmtheorems#abc type anchor
+   in TOC details.  mmwtex.c - fix bug (reported by Benoit Jubin) that
+   changes "_" in labels to subscript.  mmcmdl.c - remove unused COMPLETE
+   qualifier from SHOW PROOF.  mmwtex.c - enhance special cases of web page
+   spacing identified by Benoit Jubin */
+/* 0.164 5-Sep-2018 nm mmwtex.c, mmhlpb.c - added NOTE to bib keywords
+   14-Aug-2018 nm metamath.c - added defaultScrollMode to prevent
+   SET SCROLL CONTINUOUS from reverting to PROMPTED after a SUBMIT command */
+/* 0.163 4-Aug-2018 nm mmwtex.c - removed 2nd "sandbox:bighdr" anchor
+   in mmtheorems.html; removed Firefox and IE references; changed breadcrumb
+   font to be consistent with other pages; put asterisk next to TOC entries
+   that have associated comments */
+/* FOR FUTURE REFERENCE: search for "Thierry" in mmwtex.c to modify the link
+   to tirix.org structured proof site */
+/* 0.162-thierry 3-Jun-2018 nm mmwtex.c - add link to tirix.org structured
+   proofs */
+/* 0.162 3-Jun-2018 nm mmpars.c - re-enabled error check for $c not in
+   outermost scope.  mmhlpa.c mmhlpb.c- improve some help messages.
+   mmwtex.c - added "OBSERVATION", "PROOF", AND "STATEMENT" keywords for
+   WRITE BIBLIOGRAPHY */
+/* 0.161 2-Feb-2018 nm mmpars.c,h mmcmds.c mmwtex.c - fix wrong file name
+   and line number in error messages */
+/* 0.160 24-Jan-2018 nm mmpars.c - fix bug introduced in version 0.158 */
+/* 0.159 23-Jan-2018 nm mmpars.c - fix crash due to missing include file */
+/* 0.158 22-Jan-2018 nm mminou.c - strip CRs from Windows SUBMIT files
+   run on Linux */
+/* 0.157 15-Jan-2018 nm Major rewrite of READ-related functions.
+     Added HELP MARKUP.
+   9-Jan-2018 nm Track line numbers for error messages in included files
+   1-Jan-2018 nm Changed HOME_DIRECTORY to ROOT_DIRECTORY
+   31-Dec-2017 nm metamath.c mmcmdl.c,h mmpars.c,h mmcmds.c,h mminou.c,h
+     mmwtex.c mmhlpb.c mmdata.h - add virtual includes "$( Begin $[...$] $)",
+     $( End $[...$] $)", "$( Skip $[...$] $)" */
+/* 0.156 8-Dec-2017 nm mmwtex.c - fix bug that incorrectly gave "verify markup"
+   errors when there was a mathbox statement without an "extended" section */
+/* 0.155 8-Oct-2017 nm mmcmdl.c - restore accidentally removed HELP HTML;
+   mmhlpb.c mmwtex.c mmwtex.h,c mmcmds.c metamath.c - improve HELP and make
+   other cosmetic changes per Benoit Jubin's suggestions */
+/* 0.154 2-Oct-2017 nm mmunif.h,c mmcmds.c - add 2 more variables to ERASE;
+   metamath.c mmcmdl.c - remove obsolete OPEN/CLOSE HTML; mmhlpa.c mmhlpb.c -
+   fix typos reported by Benoit Jubin */
+/* 0.153 1-Oct-2017 nm mmunif.c,h mmcmds.c - Re-initialize internal nmbrStrings
+   in unify() after 'erase' command reported by Benoit Jubin */
+/* 0.152 26-Sep-2017 nm mmcmds.c - change default links from mpegif to mpeuni;
+   metamath.c - enforce minimum screen width = 3 to prevent crash reported
+   by Benoit Jubin */
+/* 0.151 20-Sep-2017 nm mmwtex.c - better matching to insert space between
+   A and y in "E. x e. ran A y R x" to prevent spurious spaces in thms rncoeq,
+   dfiun3g as reported by Benoit Jubin */
+/* 0.150 26-Aug-2017 nm mmcmds.c,mmwtex.h - fix hyperlink for Distinct variable
+   etc. lists so that it will point to mmset.html on other Explorers like NF.
+   Move the "Dummy variables..." to print after the "Proof of Theorem..."
+   line. */
+/* 0.149 21-Aug-2017 nm mmwtex.c,h mmcmds.c mmhlpb.c - add a subsubsection
+     "tiny" header with separator "-.-." to table of contents and theorem list;
+     see HELP WRITE THEOREM_LIST
+   21-Aug-2017 nm mmcmds.c - remove bug check 255
+   19-Aug-2017 nm mmcmds.c - change mmset.html links to
+     ../mpeuni/mmset.html so they will work in NF Explorer etc. */
+/* 0.148 14-Aug-2017 nm mmcmds.c - hyperlink "Dummy variable(s)" */
+/* 0.147 13-Aug-2017 nm mmcmds.c,h - add "Dummy variable x is distinct from all
+   other variables." to proof web page */
+/* 0.146 26-Jun-2017 nm mmwtex.c - fix handling of local labels in
+     'show proof.../tex' (bug 2341 reported by Eric Parfitt) */
+/* 0.145 16-Jun-2017 nm metamath.c mmpars.c - fix bug 1741 during
+     MINIMIZE_WITH; mmpfas.c - make duplicate bug numbers unique; mmhlpa.c
+     mmhlpb.c - adjust to prevent lcc compiler "Function too big for the
+     optimizer"
+   29-May-2017 nm mmwtex.c mmhlpa.c - take out extraneous  <HTML>...</HTML>
+     markup tags in HTML output so w3c validator will pass */
+/* 0.144 15-May-2017 nm metamath.c mmcmds.c - add "(Revised by..." tag for
+     conversion of legacy .mm's if there is a 2nd date under the proof */
+/* 0.143 14-May-2017 nm metamath.c mmdata.c,h mmcmdl.c mmcmds.c mmhlpb.c -
+     added SET CONTRIBUTOR; for missing "(Contributed by..." use date
+     below proof if it exists, otherwise use today's date, in order to update
+     old .mm files.
+   14-May-2017 Ari Ferrera - mmcmds.c - fix memory leaks in ERASE */
+/* 0.142 12-May-2017 nm metamath.c mmdata.c,h mmcmds.c - added
+     "#define DATE_BELOW_PROOF" in mmdata.h that if uncommented, will enable
+     use of the (soon-to-be obsolete) date below the proof
+   4-May-2017 Ari Ferrera - mmcmds.c metamath.c mmdata.c mmcmdl.c
+     mminou.c mminou.h mmcmdl.h mmdata.h - fixed memory leaks and warnings
+     found by valgrind.
+   3-May-2017 nm - metamath.c mmdata.c,h mmcmds.c,h mmpars.c,h mmhlpb.c
+     mmcmdl.c mmwtex.c - added xxChanged flags to statement structure so
+     that any part of the source can be changed;  removed /CLEAN qualifier
+     of WRITE SOURCE; automatically put "(Contributed by ?who?..." during
+     SAVE NEW_PROOF or SAVE PROOF when it is missing; more VERIFY MARKUP
+     checking. */
+/* 0.141 2-May-2017 nm mmdata.c, metamath.c, mmcmds.c, mmhlpb.c - use
+   getContrib() date for WRITE RECENT instead of date below proof.  This lets
+   us list recent $a's as well as $p's.  Also, add caching to getContrib() for
+   speedup. */
+/* 0.140 1-May-2017 nm mmwtex.c, mmcmds.c, metamath.c - fix some LaTeX issues
+   reported by Ari Ferrera */
+/* 0.139 2-Jan-2017 nm metamath.c - print only one line for
+     'save proof * /compressed/fast' */
+/* 0.138 26-Dec-2016 nm mmwtex.c - remove extraneous </TD> causing w3c
+   validation failure; put space after 1st x in "F/ x x = x";
+   mmcmds.c - added checking for lines > 79 chars in VERIFY MARKUP;
+   mmcmds.c, mmcmdl.c, metamath.c, mmhlpb.c, mmcmds.h - added /VERBOSE to
+   VERIFY MARKUP */
+/* 0.137 20-Dec-2016 nm mmcmds.c - check ax-XXX $a vs axXXX $p label convention
+     in 'verify markup'
+   18-Dec-2016 nm mmwtex.c, mmpars.c, mmdata.h - use true "header area"
+     between successive $a/$p for getSectionHeadings()  mmcmds.c - add
+     header comment checking
+   13-Dec-2016 nm mmdata.c,h - enhanced compareDates() to treat empty string as
+     older date.
+   13-Dec-2016 nm metamath.c, mmcmds.c - moved mm* and Microsoft illegal file
+     name label check to verifyMarkup() (the VERIFY MARKUP command) instead of
+     checking on READ; added check of set.mm Version date to verifyMarkup().
+   13-Dec-2016 nm mmwtex.c,h - don't treat bracketed description text with
+     space as a bib label; add labelMatch parameter to writeBibliography() */
+/* 0.136 10-Oct-2016 mminou.c - fix resource leak bug reported by David
+   Binderman */
+/* 0.135 11-Sep-2016, 14-Sep-2016 metamath.c, mmpfas.c,h, mmdata.c,h,
+   mmpars.c,h mmcmds.c, mmcmdl.c, mmhlpb.c - added EXPAND command */
+/* 0.134 16-Aug-2016 mmwtex.c - added breadcrumbs to theorem pages;
+   metamath.c, mmcmdl.c, mmhlpb.c, mminou.c,.h - added /TIME to SAVE PROOF,
+   SHOW STATEMENT.../[ALT}HTML, MINIMIZE_WITH */
+/* 0.133 13-Aug-2016 mmwtex.c - improve mobile display with <head> tag
+   mmpars.c - use updated Macintosh information */
+/* 0.132 10-Jul-2016 metamath.c, mmcmdl.c, mmcmds.c,.h, mmdata.c,.h, mmhlpb.c,
+   mmpfas.c - change "restricted" to "discouraged" to match set.mm markup
+   tags; add SET DISCOURAGEMENT OFF|ON (default ON) to turn off blocking for
+   convenience of advanced users
+   6-Jul-2016 metamath.c - add "(void)" in front of "system(...)" to
+   suppress -Wunused-result warning */
+/* 0.131 10-Jun-2016 mminou.c - reverted change of 22-May-2016 because
+   'minimize_with' depends on error message in string to prevent DV violations.
+   Todo:  write a DV-checking routine for 'minimize_with', then revert
+   the 22-May-2016 fix for bug 126 (which only occurs when writing web
+   pages for .mm file with errors).
+   9-Jun-2016 mmcmdl.c, metamath.c - added _EXIT_PA for use with
+   scripts that will give an error message outside of MM-PA> rather
+   than exiting metamath */
+/* 0.130 25-May-2016 mmpars.c - workaround clang warning about j = j;
+      mmvstr.c - workaround gcc -Wstrict-overflow warning */
+/* 0.129 24-May-2016 mmdata.c - fix bug 1393 */
+/* 0.128 22-May-2016 mminou.c - fixed error message going to html page
+      instead of to screen, triggering bug 126. */
+/* 0.127 10-May-2016 metamath.c, mmcmdl.c, mmhlpb.c - added /OVERRIDE to
+      PROVE */
+/* 0.126 3-May-2016 metamath.c, mmdata.h, mmdata.c, mmcmds.h, mmcmds.c,
+      mmcmdl.c, mmhlpb.c, mmpars.c - added getMarkupFlag() in mmdata.c;
+      Added /OVERRIDE added to ASSIGN, REPLACE, IMPROVE, MINIMIZE_WITH,
+      SAVE NEW_PROOF;  PROVE gives warning about SAVE NEW_PROOF for locked
+      proof.  Added SHOW RESTRICTED command.
+   3-May-2016 m*.c - fix numerous conversion warnings provided by gcc 5.3.0 */
+/* 0.125 10-Mar-2016 mmpars.c - fixed bug parsing /EXPLICIT/PACKED format
+   8-Mar-2016 nm mmdata.c - added "#nnn" to SHOW STATEMENT etc. to reference
+      statement number e.g. SHOW STATEMENT #58 shows a1i in set.mm.
+   7-Mar-2016 nm mmwtex.c - added space between } and { in HTML output
+   6-Mar-2016 nm mmpars.c - disabled wrapping of formula lines in
+       WRITE SOURCE.../REWRAP
+   2-Mar-2016 nm metamath.c, mmcmdl.c, mmhlpb.c - added /FAST to
+       SAVE PROOF, SHOW PROOF */
+/* 0.123 25-Jan-2016 nm mmpars.c, mmdata.h, mmdata.c, mmpfas.c, mmcmds.,
+   metamath.c, mmcmdl.c, mmwtex.c - unlocked SHOW PROOF.../PACKED,
+   added SHOW PROOF.../EXPLICIT */
+/* 0.122 14-Jan-2016 nm metamath.c, mmcmds.c, mmwtex.c, mmwtex.h - surrounded
+      math HTML output with "<SPAN [g_htmlFont]>...</SPAN>; added htmlcss and
+      htmlfont $t commands
+   10-Jan-2016 nm mmwtex.c - delete duplicate -4px style; metamath.c -
+     add &nbsp; after char on mmascii.html
+   3-Jan-2016 nm mmwtex.c - fix bug when doing SHOW STATEMENT * /ALT_HTML after
+   VERIFY MARKUP */
+/* 0.121 17-Nov-2015 nm metamath.c, mmcmdl.h, mmcmdl.c, mmcmds.h, mmcmds.c,
+       mmwtex.h, mmwtex.c, mmdata.h, mmdata.c -
+   1. Moved WRITE BIBLIOGRAPHY code from metamath.c to its own function in
+      mmwtex.c; moved qsortStringCmp() from metamath.c to mmdata.c
+   2. Added $t, comment markup, and bibliography checking to VERIFY MARKUP
+   3. Added options to bug() bug-check interception to select aborting,
+      stepping to next bug, or ignoring subsequent bugs
+   4. SHOW STATEMENT can now use both /HTML and /ALT_HTML in same session
+   5. Added /HTML, /ALT_HTML to WRITE THEOREM_LIST and
+      WRITE RECENT_ADDITIONS */
+/* 0.120 7-Nov-2015 nm metamath.c, mmcmdl.c, mmpars.c - add VERIFY MARKUP
+   4-Nov-2015 nm metamath.c, mmcmds.c/h, mmdata.c/h - move getDescription,
+       getSourceIndentation from mmcmds.c to mmdata.c.
+       metamath.c, mmdata.c - add and call parseDate() instead of in-line
+       code; add getContrib(), getProofDate(), buildDate(), compareDates(). */
+/* 0.119 18-Oct-2015 nm mmwtex.c - add summary TOC to Theorem List; improve
+       math symbol GIF image alignment
+   2-Oct-2015 nm metamath.c, mmpfas.c, mmwtex.c - fix miscellaneous small
+       bugs or quirks */
+/* 0.118 18-Jul-2015 nm metamath.c, mmcmds.h, mmcmds.c, mmcmdl.c, mmhlpb.h,
+   mmhlpb.c - added /TO qualifier to SHOW TRACE_BACK.  See
+   HELP SHOW TRACE_BACK. */
+/* 0.117 30-May-2015
+   1. nm mmwtex.c - move <A NAME... tag to math symbol cell in proof pages so
+      hyperlink will jump to top of cell (reported by Alan Sare)
+   2. daw mmpfas.c - add INLINE speedup if compiler permits
+   3. nm metamath.c, mminou.c, mmwtex.c, mmpfas.c - fix clang -Wall warnings
+      (reported by David A. Wheeler) */
+/* 0.116 9-May-2015 nm mmwtex.c - adjust paragraph break in 'write th';
+   Statement List renamed Theorem List;  prevent space in after paragraph
+   in Theorem List; remove stray "(";  put header and header comment
+   in same table cell; fix <TITLE> of Theorem List pages */
+/* 0.115 8-May-2015 nm mmwtex.c - added section header comments to
+       WRITE THEOREM_LIST and broke out Table of Contents page
+   24-Apr-2015 nm metamath.c - add # bytes to end of "---Clip out the proof";
+       reverted to no blank lines there (see 0.113 item 3) */
+/* 0.114 22-Apr-2015 nm mmcmds.c - put [-1], [-2],... offsets on 'show
+   new_proof/unknown' */
+/* 0.113 19-Apr-2015 so, nm metamath.c, mmdata.c
+   1. SHOW LABEL % will show statements with changed proofs
+   2. SHOW LABEL = will show the statement being proved in MM-PA
+   3. Added blank lines before, after "---------Clip out the proof" proof
+   4. Generate date only if the proof is complete */
+/* 0.112 15-Apr-2015 nm metamath.c - fix bug 1121 (reported by S. O'Rear);
+   mmwtex.c - add "img { margin-bottom: -4px }" to CSS to align symbol GIFs;
+   mmwtex.c - remove some hard coding for set.mm, for use with new nf.mm;
+   metamath.c - fix comment parsing in WRITE BIBLIOGRAPHY to ignore
+   math symbols  */
+/* 0.111 22-Nov-2014 nm metamath.c, mmcmds.c, mmcmdl.c, mmhlpb.c - added
+   /NO_NEW_AXIOMS_FROM qualifier to MINIMIZE_WITH; see HELP MINIMIZE_WITH.
+   21-Nov-2014 Stefan O'Rear mmdata.c, mmhlpb.c - added ~ label range specifier
+   to wildcards; see HELP SEARCH */
+/* 0.110 2-Nov-2014 nm mmcmds.c - fixed bug 1114 (reported by Stefan O'Rear);
+   metamath.c, mmhlpb.c - added "SHOW STATEMENT =" to show the statement
+   being proved in MM-PA (based on patch submitted by Stefan O'Rear) */
+/* 0.109 20-Aug-2014 nm mmwtex.c - fix corrupted HTML caused by misinterpreting
+   math symbols as comment markup (math symbols with _ [ ] or ~).  Also,
+   allow https:// as well as http:// in ~ label markup.
+   11-Jul-2014 wl mmdata.c - fix obscure crash in debugging mode db9 */
+/* 0.108 25-Jun-2014 nm
+   (1) metamath.c, mmcmdl.c, mmhlpb.c - MINIMIZE_WITH now checks the size
+   of the compressed proof, prevents $d violations, and tries forward and
+   reverse statement scanning order; /NO_DISTINCT, /BRIEF, /REVERSE
+   qualifiers were removed.
+   (2) mminou.c - prevent hard breaks (in the middle of a word) in too-long
+   lines (e.g. long URLs) in WRITE SOURCE .../REWRAP; just overflow the
+   screen width instead.
+   (3) mmpfas.c - fixed memory leak in replaceStatement()
+   (4) mmpfas.c - suppress inf. growth with MINIMIZE_WITH idi/ALLOW_GROWTH */
+/* 0.107 21-Jun-2014 nm metamath.c, mmcmdl.c, mmhlpb.c - added /SIZE qualifier
+   to SHOW PROOF; added SHOW ELAPSED_TIME; mmwtex.c - reformatted WRITE
+   THEOREM_LIST output; now "$(", newline, "######" starts a "part" */
+/* 0.106 30-Mar-2014 nm mmwtex.c - fix bug introduced by 0.105 that disabled
+   hyperlinks on literature refs in HTML comment.  metamath.c - improve
+   messages */
+/* 0.105 15-Feb-2014 nm mmwtex.c - prevented illegal LaTeX output for certain
+   special characters in comments. */
+/* 0.104 14-Feb-2014 nm mmwtex.c - fixed bug 2312, mmcmds.c - enhanced ASSIGN
+   error message. */
+/* 0.103 4-Jan-2014 nm mmcmds.c,h - added "Allowed substitution hints" below
+   the "Distinct variable groups" list on generated web pages
+   mmwtex.c - added "*" to indicate DV's occur in Statement List entries. */
+/* 0.102 2-Jan-2014 nm mminou.c - made compressed proof line wrapping more
+   uniform at start of compressed part of proof */
+/* 0.101 27-Dec-2013 nm mmdata.h,c, mminou.c, mmcmdl.c, mmhlpb.c, mmvstr.c -
+   Improved storage efficiency of /COMPRESSED proofs (but with 20% slower run
+   time); added /OLD_COMPRESSION to specify old algorithm; removed end-of-line
+   space after label list in old algorithm; fixed linput() bug */
+/* 0.100 30-Nov-2013 nm mmpfas.c - reversed statement scan order in
+   proveFloating(), to speed up SHOW STATEMENT df-* /HTML; metamath.c - remove
+   the unknown date place holder in SAVE NEW_PROOF; Wolf Lammen mmvstr.c -
+   some cleanup */
+/* 0.07.99 1-Nov-2013 nm metamath.c, mmpfas.h,c, mmcmdl.h,c, mmhlpa.c,
+   mmhlpb.c - added UNDO, REDO, SET UNDO commands (see HELP UNDO) */
+/* 0.07.98 30-Oct-2013 Wolf Lammen mmvstr.c,h, mminou.c, mmpars.c,
+   mmdata.c  - improve code style and program structure */
+/* 0.07.97 20-Oct-2013 Wolf Lammen mmvstr.c,h, metamath.c - improved linput();
+   nm mmcmds.c, mmdata.c - tolerate bad proofs in SHOW TRACE_BACK etc. */
+/* 0.07.96 20-Sep-2013 Wolf Lammen mmvstr.c - revised cat();
+   nm mmwtex.c, mminou.c - change a print2 to printLongLine to fix bug 1150 */
+/* 0.07.95 18-Sep-2013 Wolf Lammen mmvstr.c - optimized cat();
+   nm metamath.c, mmcmds.c, mmdata.c, mmpars.c, mmpfas.c, mmvstr.c,
+   mmwtex.c - suppress some clang warnings */
+/* 0.07.94 28-Aug-2013 Alexey Merkulov mmcmds.c, mmpars.c - fixed several
+   memory leaks found by valgrind --leak-check=full --show-possibly-lost=no */
+/* 0.07.93 8-Jul-2013 Wolf Lammen mmvstr.c - simplified let() function;
+   also many minor changes in m*.c and m*.h to assist future refactoring */
+/* 0.07.92 28-Jun-2013 nm metamath.c mmcmds.c,h mmcmdl.c mmhlpb.c- added
+   /NO_REPEATED_STEPS for /LEMMON mode of SHOW PROOF, SHOW NEW_PROOF.
+   This reverts the /LEMMON mode default display change of 31-Jan-2010
+   and invokes it when desired via /NO_REPEATED_STEPS. */
+/* 0.07.91 20-May-2013 nm metamath.c mmpfas.c,h mmcmds.c,h mmcmdl.c
+   mmhlpb.c- added /FORBID qualifier to MINIMIZE_WITH */
+/* 0.07.90 19-May-2013 nm metamath.c mmcmds.c mmcmdl.c mmhlpb.c - added /MATCH
+   qualifier to SHOW TRACE_BACK */
+/* 0.07.88 18-Nov-2012 nm mmcmds.c - fixed bug 243 */
+/* 0.07.87 17-Nov-2012 nm mmwtex.c - fixed formatting problem when label
+   markup ends a comment in SHOW PROOF ... /HTML */
+/* 0.07.86 27-Oct-2012 nm mmcmds.c, mmwtex.c, mmwtex.h - fixed ERASE bug
+   caused by imperfect re-initialization; reported by Wolf Lammen */
+/* 0.07.85 10-Oct-2012 nm metamath.c, mmcmdl.c, mmwtex.c, mmwtex.h, mmhlpb.c -
+   added /SHOW_LEMMAS to WRITE THEOREM_LIST to bypass lemma math suppression */
+/* 0.07.84 9-Oct-2012 nm mmcmds.c - fixed bug in getStatementNum() */
+/* 0.07.83 19-Sep-2012 nm mmwtex.c - fixed bug reported by Wolf Lammen */
+/* 0.07.82 16-Sep-2012 nm metamath.c, mmpfas.c - fixed REPLACE infinite loop;
+   improved REPLACE message for shared dummy variables */
+/* 0.07.81 14-Sep-2012 nm metamath.c, mmcmds.c, mmcmds.h, mmcmdl.c, mmhlpb.c
+   - added FIRST, LAST, +nn, -nn where missing from ASSIGN, REPLACE,
+   IMPROVE, LET STEP.  Wildcards are allowed for PROVE, ASSIGN, REPLACE
+   labels provided there is a unique match. */
+/* 0.07.80 4-Sep-2012 nm metamath.c, mmpfas.c, mmpfas.h, mmcmdl.c, mmhlpb.c
+   - added / 1, / 2, / 3, / SUBPROOFS to IMPROVE to specify search level */
+/* 0.07.79 31-Aug-2012 nm m*.c - clean up some gcc warnings */
+/* 0.07.78 28-Aug-2012 nm mmpfas.c - fix bug in 0.07.77. */
+/* 0.07.77 25-Aug-2012 nm metamath.c, mmpfas.c - Enhanced IMPROVE algorithm to
+   allow non-shared dummy variables in unknown steps */
+/* 0.07.76 22-Aug-2012 nm metamath.c, mmpfas.c, mmcmdl.c, mmhlpb.c -
+   Enhanced IMPROVE algorithm to also try REPLACE algorithm */
+/* 0.07.75 14-Aug-2012 nm metamath.c - MINIMIZE_WITH now checks current
+   mathbox (but not other mathboxes) even if /INCLUDE_MATHBOXES is omitted */
+/* 0.07.74 18-Mar-2012 nm mmwtex.c, mmcmds.c, metamath.c - improved texToken()
+   error message */
+/* 0.07.73 26-Dec-2011 nm mmwtex.c, mmpars.c - added <HTML>...</HTML> in
+   comments for passing through raw HTML code into HTML files generated with
+   SHOw STATEMENT xxx / HTML */
+/* 0.07.72 25-Dec-2011 nm (obsolete) */
+/* 0.07.71 10-Nov-2011 nm metamath.c, mmcmdl.c - added /REV to MINIMIZE_WITH */
+/* 0.07.70 6-Aug-2011 nm mmwtex.c - fix handling of double quotes inside
+   of htmldef strings to match spec in Metamath book Appendix A p. 156 */
+/* 0.07.69 9-Jul-2011 nm mmpars.c, mmvstr.c - Untab file in WRITE SOURCE
+   ... /REWRAP */
+/* 0.07.68 3-Jul-2011 nm metamath.c, mminou.h, mminou.c - Nested SUBMIT calls
+   (SUBMIT calls inside of a SUBMIT command file) are now allowed.
+   Also, mmdata.c - fix memory leak. */
+/* 0.07.67 2-Jul-2011 nm metamath.c, mmcmdl.c, mmhlpa.c - Added TAG command
+   to TOOLS.  See HELP TAG under TOOLS.  (The old special-purpose TAG command
+   was renamed to UPDATE.) */
+/* 0.07.66 1-Jul-2011 nm metamath.c, mmcmds.c, mmpars.c, mmpars.h - Added code
+   to strip spurious "$( [?] $)" in WRITE SOURCE ... /CLEAN output */
+/* 0.07.65 30-Jun-2011 nm mmwtex.c - Prevent processing [...] bibliography
+   brackets inside of `...` math strings in comments. */
+/* 0.07.64 28-Jun-2011 nm metamath.c, mmcmdl.c - Added /INCLUDE_MATHBOXES
+   qualifier to MINIMIZE_WITH; without it, MINIMIZE_WITH * will skip
+   checking user mathboxes. */
+/* 0.07.63 26-Jun-2011 nm mmwtex.c - check that .gifs exist for htmldefs */
+/* 0.07.62 18-Jun-2011 nm mmpars.c - fixed bug where redeclaration of active
+   $v was not detected */
+/* 0.07.61 12-Jun-2011 nm mmpfas.c, mmcmds.c, metamath.c, mmhlpb.c - added
+   /FORMAT and /REWRAP qualifiers to WRITE SOURCE to format according to set.mm
+   conventions - set HELP WRITE SOURCE */
+/* 0.07.60 7-Jun-2011 nm mmpfas.c - fixed bug 1805 which occurred when doing
+   MINIMIZE_WITH weq/ALLOW_GROWTH after DELETE DELETE FLOATING_HYPOTHESES */
+/* 0.07.59 11-Dec-2010 nm mmpfas.c - increased default SET SEARCH_LIMIT from
+   10000 to 25000 to accommodate df-plig web page in set.mm */
+/* 0.07.58 9-Dec-2010 nm mmpars.c - detect if same symbol is used with both
+   $c and $v, in order to conform with Metamath spec */
+/* 0.07.57 19-Oct-2010 nm mmpars.c - fix bug causing incorrect line count
+   for error messages when non-ASCII character was found; mminou.h -
+   increase SET WIDTH maximum from 999 to 9999 */
+/* 0.07.56 27-Sep-2010 nm mmpars.c, mmpfas.c - check for $a's with
+   one token e.g. "$a wff $."; if found, turn SET EMPTY_SUBSTITUTION ON
+   automatically.  (Suggested by Mel O'Cat; patent pending.) */
+/* 0.07.55 26-Sep-2010 nm mmunif.c, mmcmds.c, mmunif.h - check for mismatched
+   brackets in all $a's, so that if there are any, the bracket matching
+   algorithm (for fewer ambiguous unifications) in mmunif.c will be turned
+   off. */
+/* 0.07.54 25-Sep-2010 nm mmpars.c - added $f checking to conform to the
+   current Metamath spec, so footnote 2 on p. 92 of Metamath book is
+   no longer applicable. */
+/* 0.07.53 24-Sep-2010 nm mmveri.c - fixed bug(2106), reported by Michal
+   Burger */
+/* 0.07.52 14-Sep-2010 nm metamath.c, mmwtex.h, mmwtex.c, mmcmds.c,
+   mmcmdl.c, mmhlpb.c - rewrote the LaTeX output for easier hand-editing
+   and embedding in LaTeX documents.  The old LaTeX output is still
+   available with /OLD_TEX on OPEN TEX, SHOW STATEMENT, and SHOW PROOF,
+   but it is obsolete and will be deleted eventually if no one objects.  The
+   new /TEX output also replaces the old /SIMPLE_TEX, which was removed. */
+/* 0.07.51 9-Sep-2010 Stefan Allen mmwtex.c - put hyperlinks on hypothesis
+   label references in SHOW STATEMENT * /HTML, ALT_HTML output */
+/* 0.07.50 21-Feb-2010 nm mminou.c - "./metamath < empty", where "empty" is a
+   0-byte file, now exits metamath instead of producing an infinite loop.
+   Also, ^D now exits metamath.  Reported by Cai Yufei */
+/* 0.07.49 31-Jan-2010 nm mmcmds.c - Lemmon-style proofs (SHOW PROOF xxx
+   /LEMON/RENUMBER) no longer have steps with dummy labels; instead, steps
+   are now the same as in HTML page proofs.  (There is a line to comment
+   out if you want to revert to old behavior.) */
+/* 0.07.48 11-Sep-2009 nm mmpars.c, mm, mmvstr.c, mmdata.c - Added detection of
+   non-whitespace around keywords (mmpars.c); small changes to eliminate
+   warnings in gcc 3.4.4 (mmvstr.c, mmdata.c) */
+/* 0.07.47 2-Aug-2009 nm mmwtex.c, mmwtex.h - added user name to mathbox
+   pages */
+/* 0.07.46 24-Jul-2009 nm metamath.c, mmwtex.c - changed name of sandbox
+   to "mathbox" */
+/* 0.07.45 15-Jun-2009 nm metamath.c, mmhlpb.c - put "!" before each line of
+   SET ECHO ON output to make them easy to identity for creating scripts */
+/* 0.07.44 12-May-2009 Stefan Allan, nm metamath.c, mmcmdl.c, mmwtex.c -
+   added SHOW STATEMENT / MNEMONICS - see HELP SHOW STATEMENT */
+/* 0.07.43 29-Aug-2008 nm mmwtex.c - workaround for Unicode huge font bug in
+   FireFox 3 */
+/* 0.07.42 8-Aug-2008 nm metamath.c - added sandbox, Hilbert Space colors to
+   Definition List */
+/* 0.07.41 29-Jul-2008 nm metamath.c, mmwtex.h, mmwtex.c - Added handling of
+   sandbox section of Metamath Proof Explorer web pages */
+/* 0.07.40 6-Jul-2008 nm metamath.c, mmcmdl.c, mmhlpa.c, mmhlpb.c - Added
+   / NO_VERSIONING qualifier for SHOW STATEMENT, so website can be regenerated
+   in place with less temporary space required.  Also, the wildcard trigger
+   for mmdefinitions.html, etc. is more flexible (see HELP HTML). */
+/* 0.07.39 21-May-2008 nm metamath.c, mmhlpb.c - Added wildcard handling to
+   statement label in SHOW TRACE_BACK.  All wildcards now allow
+   comma-separated lists [i.e. matchesList() instead of matches()] */
+/* 0.07.38 26-Apr-2008 nm metamath.c, mmdata.h, mmdata.c, mmvstr.c, mmhlpb.c -
+   Enhanced / EXCEPT qualifier for MINIMIZE_WITH to handle comma-separated
+   wildcard list. */
+/* 0.07.37 14-Apr-2008 nm metamath.c, mmcmdl.c, mmhlpb.c - Added / JOIN
+   qualifier to SEARCH. */
+/* 0.07.36 7-Jan-2008 nm metamath.c, mmcmdl.c, mmhlpb.c - Added wildcard
+   handling for labels in SHOW USAGE. */
+/* 0.07.35 2-Jan-2008 nm mmcmdl.c, metamath.c, mmhlpb.c - Changed keywords
+   COMPACT to PACKED and COLUMN to START_COLUMN so that SAVE/SHOW proof can use
+   C to abbreviate COMPRESSED.  (PACKED format is supported but "unofficial,"
+   used mainly for debugging purposes, and is not listed in HELP SAVE
+   PROOF.) */
+/* 0.07.34 19-Nov-2007 nm mmwtex.c, mminou.c - Added tooltips to proof step
+   hyperlinks in SHOW STATEMENT.../HTML,ALT_HTML output (suggested by Reinder
+   Verlinde) */
+/* 0.07.33 19-Jul-2007 nm mminou.c, mmvstr.c, mmdata.c, mmword.c - added fflush
+   after each printf() call for proper behavior inside emacs (suggested by
+   Frederic Line) */
+/* 0.07.32 29-Apr-2007 nm mminou.c - fSafeOpen now stops at gap; e.g. if ~2
+   doesn't exist, ~1 will be renamed to ~2, but any ~3, etc. are not touched */
+/* 0.07.31 5-Apr-2007 nm mmwtex.c - Don't make "_" in hyperlink a subscript */
+/* 0.07.30 8-Feb-2007 nm mmcmds.c, mmwtex.c Added HTML statement number info to
+   SHOW STATEMENT.../FULL; friendlier "Contents+1" link in mmtheorems*.html */
+/* 0.07.29 6-Feb-2007 Jason Orendorff mmpfas.c - Patch to eliminate the
+   duplicate "Exceeded trial limit at step n" messages */
+/* 0.07.28 22-Dec-2006 nm mmhlpb.c - Added info on quotes to HELP LET */
+/* 0.07.27 23-Oct-2006 nm metamath.c, mminou.c, mmhlpa.c, mmhlpb.c - Added
+   / SILENT qualifier to SUBMIT command */
+/* 0.07.26 12-Oct-2006 nm mminou.c - Fixed bug when SUBMIT file was missing
+   a new-line at end of file (reported by Marnix Klooster) */
+/* 0.07.25 10-Oct-2006 nm metamath.c - Fixed bug invoking TOOLS as a ./metamath
+   command-line argument */
+/* 0.07.24 25-Sep-2006 nm mmcmdl.c Fixed bug in
+   SHOW NEW_PROOF/START_COLUMN nn/LEM */
+/* 0.07.23 31-Aug-2006 nm mmwtex.c - Added Home and Contents links to bottom of
+   WRITE THEOREM_LIST pages */
+/* 0.07.22 26-Aug-2006 nm metamath.c, mmcmdl.c, mmhlpb.c - Changed 'IMPROVE
+   STEP <step>' to 'IMPROVE <step>' for user convenience and to be consistent
+   with ASSIGN <step> */
+/* 0.07.21 20-Aug-2006 nm mmwtex.c - Revised small colored numbers so that all
+   colors have the same grayscale brightness.. */
+/* 0.07.20 19-Aug-2006 nm mmpars.c - Made the error "Required hypotheses may
+   not be explicitly declared" in a compressed proof non-severe, so that we
+   can still SAVE the proof to reformat and recover it. */
+/* 0.07.19 11-Aug-06 nm mmcmds.c - "Distinct variable group(s)" is now
+   "group" or "groups" as appropriate. */
+/* 0.07.18 31-Jul-06 nm mmwtex.c - added table to contents to p.1 of output of
+   WRITE THEOREM_LIST command. */
+/* 0.07.17 4-Jun-06 nm mmpars.c - do not allow labels to match math symbols
+   (new spec proposed by O'Cat).   mmwtex.c - made theorem name 1st in title,
+   for readability in Firefox tabs. */
+/* 0.07.16 16-Apr-06 nm metamath.c, mmcmdl.c, mmpfas.c, mmhlpb.c - allow step
+   to be negative (relative to end of proof) for ASSIGN, UNIFY, and LET STEP
+   (see their HELPs).  Added INITIALIZE USER to delete LET STEP assignments
+   (see HELP INITIALIZE).  Fixed bug in LET STEP (mmpfas.c). */
+/* 0.07.15 10-Apr-06 nm metamath.c, mmvstr.c - change dates from 2-digit to
+   4-digit year; make compatible with older 2-digit year. */
+/* 0.07.14 21-Mar-06 nm mmpars.c - fix bug 1722 when compressed proof has
+   "Z" at beginning of proof instead of after a proof step. */
+/* 0.07.13 3-Feb-06 nm mmpfas.c - minor improvement to MINIMIZE_WITH */
+/* 0.07.12 30-Jan-06 nm metamath.c, mmcmds.c, mmdata.c, mmdata.h, mmhlpa.c,
+   mmhlpb.c - added "?" wildcard to match single character. See HELP SEARCH. */
+/* 0.07.11 7-Jan-06 nm metamath.c, mmcmdl.c, mmhlpb.c - added EXCEPT qualifier
+   to MINIMIZE_WITH */
+/* 0.07.10 28-Dec-05 nm metamath.c, mmcmds.c - cosmetic tweaks */
+/* 0.07.10 11-Dec-05 nm metamath.c, mmcmdl.c, mmhlpb.c - added ASSIGN FIRST
+   and IMPROVE FIRST commands.  Also enhanced READ error message */
+/* 0.07.9 1-Dec-05 nm mmvstr.c - added comment on how to make portable */
+/* 0.07.9 18-Nov-05 nm metamath.c, mminou.c, mminou.h, mmcmdl.c, mmhlpb.c -
+   added SET HEIGHT command; changed SET SCREEN_WIDTH to SET WIDTH; changed
+   SET HENTY_FILTER to SET JEREMY_HENTY_FILTER (to make H for HEIGHT
+   unambiguous); added HELP for SET JEREMY_HENTY_FILTER */
+/* 0.07.8 15-Nov-05 nm mmunif.c - now detects wrong order in bracket matching
+   heuristic to further reduce ambiguous unifications in Proof Assistant */
+/* 0.07.7 12-Nov-05 nm mmunif.c - add "[","]" and "[_","]_" bracket matching
+   heuristic to reduce ambiguous unifications in Proof Assistant.
+   mmwtex.c - added heuristic for HTML spacing after "sum_" token. */
+/* 0.07.6 15-Oct-05 nm mmcmds.c,mmpars.c - fixed compressed proof algorithm
+   to match spec in book (with new algorithm due to Marnix Klooster).
+   Users are warned to convert proofs when the old compression is found. */
+/* 0.07.5 6-Oct-05 nm mmpars.c - fixed bug that reset "severe error in
+   proof" flag when a proof with severe error also had unknown steps */
+/* 0.07.4 1-Oct-05 nm mmcmds.c - ignored bug 235, which could happen for
+   non-standard logics */
+/* 0.07.3 17-Sep-05 nm mmpars.c - reinstated duplicate local label checking to
+   conform to strict spec */
+/* 0.07.2 19-Aug-05 nm mmwtex.c - suppressed math content for lemmas in
+   WRITE THEOREMS output */
+/* 0.07.1 28-Jul-05 nm Added SIMPLE_TEX qualifier to SHOW STATEMENT */
+/* 0.07:  Official 0.07 22-Jun-05 corresponding to Metamath book */
+/* 0.07x:  Fixed to work with AMD64 with 64-bit longs by
+   Waldek Hebisch; deleted unused stuff in mmdata.c */
+/* 0.07w:  .mm date format like "$( [7-Sep-04] $)" is now
+   generated and permitted (old one is tolerated too for compatibility) */
+/* Metamath Proof Verifier - main program */
+/* See the book "Metamath" for description of Metamath and run instructions */
+
+/*****************************************************************************/
+
+
+/*----------------------------------------------------------------------*/
+
+
+#include <string.h>
+#include <stdlib.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mmcmdl.h"
+#include "mmcmds.h"
+#include "mmhlpa.h"
+#include "mmhlpb.h"
+#include "mminou.h"
+#include "mmpars.h"
+#include "mmveri.h"
+#include "mmpfas.h"
+#include "mmunif.h"
+#include "mmword.h"
+#include "mmwtex.h"
+
+void command(int argc, char *argv[]);
+
+/*! \fn int main(int argc, char *argv[])
+ * \brief entry point of the metamath program
+ * \param argc int number of command line parameters
+ * \param argv (char*)[] array of \p argc command line parameters, followed by NULL
+ * \return success 0 else failure
+ *
+ * Running metamath
+ *   ./metamath 'read set.mm' 'verify proof *'
+ * will start main with \p argc set to 2, argv[0] to "read set.mm", argv[1]
+ * to "verify proof *" (both without quotes) and argv[2] to NULL.
+ * Returning 0 indicates successful completion, anything else some kind of
+ * failure.
+ * For details see https://en.cppreference.com/w/cpp/language/main_function.
+ */
+int main(int argc, char *argv[]) {
+
+/* argc is the number of arguments; argv points to an array containing them */
+
+  /****** If g_listMode is set to 1 here, the startup will be Text Tools
+          utilities, and Metamath will be disabled ***************************/
+  /* (Historically, this mode was used for the creation of a stand-alone
+     "TOOLS>" utility for people not interested in Metamath.  This utility
+     was named "LIST.EXE", "tools.exe", and "tools" on VMS, DOS, and Unix
+     platforms respectively.  The UPDATE command of TOOLS (mmword.c) was
+     custom-written in accordance with the version control requirements of a
+     company that used it; it documents the differences between two versions
+     of a program as C-style comments embedded in the newer version.) */
+  g_listMode = 0; /* Force Metamath mode as startup */
+
+
+  g_toolsMode = g_listMode;
+
+  if (!g_listMode) {
+    /*print2("Metamath - Version %s\n", MVERSION);*/
+    print2("Metamath - Version %s%s", MVERSION, space(27 - (long)strlen(MVERSION)));
+  }
+  print2("Type HELP for help, EXIT to exit.\n");
+
+  /* Allocate big arrays */
+  initBigArrays();
+
+  /* Set the default contributor */
+  let(&g_contributorName, DEFAULT_CONTRIBUTOR);
+
+  /* Process a command line until EXIT */
+  command(argc, argv);
+
+  /* Close logging command file */
+  if (g_listMode && g_listFile_fp != NULL) {
+    fclose(g_listFile_fp);
+  }
+
+  return 0;
+
+}
+
+
+
+
+void command(int argc, char *argv[]) {
+  /* Command line user interface -- this is an infinite loop; it fetches and
+     processes a command; returns only if the command is 'EXIT' or 'QUIT' and
+     never returns otherwise. */
+  long argsProcessed = 0;  /* Number of argv initial command-line
+                                     arguments processed so far */
+
+  long /*c,*/ i, j, k, m, l, n, p, q, r, s /*,tokenNum*/;
+  long stmt, step;
+  int subType = 0;
+#define SYNTAX 4
+  vstring_def(str1);
+  vstring_def(str2);
+  vstring_def(str3);
+  vstring_def(str4);
+  vstring_def(str5);
+  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated directly */
+  nmbrString_def(nmbrTmp);
+  nmbrString_def(nmbrSaveProof);
+  /*pntrString *pntrTmpPtr;*/ /* Pointer only; not allocated directly */
+  pntrString_def(pntrTmp);
+  pntrString_def(expandedProof);
+  flag tmpFlag;
+
+  /* proofSavedFlag tells us there was at least one
+     SAVE NEW_PROOF during the MM-PA session while the UNDO stack wasn't
+     empty, meaning that "UNDO stack empty" is no longer a reliable indication
+     that the proof wasn't changed.  It is cleared upon entering MM-PA, and
+     set by SAVE NEW_PROOF. */
+  flag proofSavedFlag = 0;
+
+  /* Variables for SHOW PROOF */
+  flag pipFlag; /* Proof-in-progress flag */
+  long outStatement; /* Statement for SHOW PROOF or SHOW NEW_PROOF */
+  flag explicitTargets; /* For SAVE PROOF /EXPLICIT */
+  long startStep; long endStep;
+  /* long startIndent; */
+  long endIndent; /* Also for SHOW TRACE_BACK */
+  flag essentialFlag; /* Also for SHOW TRACE_BACK */
+  flag renumberFlag; /* Flag to use essential step numbering */
+  flag unknownFlag;
+  flag notUnifiedFlag;
+  flag reverseFlag;
+  long detailStep;
+  flag noIndentFlag; /* Flag to use non-indented display */
+  long splitColumn; /* Column at which formula starts in non-indented display */
+  flag skipRepeatedSteps; /* NO_REPEATED_STEPS qualifier */
+  flag texFlag; /* Flag for TeX */
+  flag saveFlag; /* Flag to save in source */
+  flag fastFlag; /* Flag for SAVE PROOF.../FAST */
+  long indentation; /* Number of spaces to indent proof */
+  vstring_def(labelMatch); /* SHOW PROOF <label> argument */
+
+  flag axiomFlag; /* For SHOW TRACE_BACK */
+  flag treeFlag; /* For SHOW TRACE_BACK */
+  flag countStepsFlag; /* For SHOW TRACE_BACK */
+  flag matchFlag; /* For SHOW TRACE_BACK */
+  vstring_def(matchList);  /* For SHOW TRACE_BACK */
+  vstring_def(traceToList); /* For SHOW TRACE_BACK */
+  flag recursiveFlag; /* For SHOW USAGE */
+  long fromLine, toLine; /* For TYPE, SEARCH */
+  flag joinFlag; /* For SEARCH */
+  long searchWindow; /* For SEARCH */
+  FILE *type_fp; /* For TYPE, SEARCH */
+  long maxEssential; /* For MATCH */
+  nmbrString_def(essentialFlags);
+                                            /* For ASSIGN/IMPROVE FIRST/LAST */
+  long improveDepth; /* For IMPROVE */
+  flag searchAlg; /* For IMPROVE */
+  flag searchUnkSubproofs;  /* For IMPROVE */
+  flag dummyVarIsoFlag; /* For IMPROVE */
+  long improveAllIter; /* For IMPROVE ALL */
+  flag proofStepUnk; /* For IMPROVE ALL */
+
+  flag texHeaderFlag; /* For OPEN TEX, CLOSE TEX */
+  flag commentOnlyFlag; /* For SHOW STATEMENT */
+  flag briefFlag; /* For SHOW STATEMENT */
+  flag linearFlag; /* For SHOW LABELS */
+  vstring_def(bgcolor); /* For SHOW STATEMENT definition list */
+
+  flag verboseMode, mayGrowFlag /*, noDistinctFlag*/; /* For MINIMIZE_WITH */
+  long prntStatus; /* For MINIMIZE_WITH */
+  flag hasWildCard; /* For MINIMIZE_WITH */
+  long exceptPos; /* For MINIMIZE_WITH */
+  flag mathboxFlag; /* For MINIMIZE_WITH */
+  long thisMathboxStartStmt; /* For MINIMIZE_WITH */
+  flag forwFlag; /* For MINIMIZE_WITH */
+  long forbidMatchPos;  /* For MINIMIZE_WITH */
+  vstring_def(forbidMatchList);  /* For MINIMIZE_WITH */
+  long noNewAxiomsMatchPos;  /* For NO_NEW_AXIOMS_FROM */
+  vstring_def(noNewAxiomsMatchList);  /* For NO_NEW_AXIOMS_FROM */
+  long allowNewAxiomsMatchPos;  /* For NO_NEW_AXIOMS_FROM */
+  vstring_def(allowNewAxiomsMatchList);  /* For NO_NEW_AXIOMS_FROM */
+  vstring_def(traceProofFlags); /* For NO_NEW_AXIOMS_FROM */
+  vstring_def(traceTrialFlags); /* For NO_NEW_AXIOMS_FROM */
+  flag overrideFlag; /* For discouraged statement /OVERRIDE */
+
+  struct pip_struct saveProofForReverting = {
+       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
+                                   /* For MINIMIZE_WITH */
+  long origCompressedLength; /* For MINIMIZE_WITH */
+  long oldCompressedLength = 0; /* For MINIMIZE_WITH */
+  long newCompressedLength = 0; /* For MINIMIZE_WITH */
+  long forwardCompressedLength = 0; /* For MINIMIZE_WITH */
+  long forwardLength = 0; /* For MINIMIZE_WITH */
+  vstring saveZappedProofSectionPtr; /* Pointer only */ /* For MINIMIZE_WITH */
+  long saveZappedProofSectionLen; /* For MINIMIZE_WITH */
+  flag saveZappedProofSectionChanged; /* For MINIMIZE_WITH */
+
+  struct pip_struct saveOrigProof = {
+       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
+                                   /* For MINIMIZE_WITH */
+  struct pip_struct save1stPassProof = {
+       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
+                                   /* For MINIMIZE_WITH */
+  long forwRevPass; /* 1 = forward pass */
+
+  long sourceStatement; /* For EXPAND */
+
+  flag showLemmas; /* For WRITE THEOREM_LIST */
+  flag noVersioning; /* For WRITE THEOREM_LIST & others */
+  long theoremsPerPage; /* For WRITE THEOREM_LIST */
+
+  /* g_toolsMode-specific variables */
+  flag commandProcessedFlag = 0; /* Set when the first command line processed;
+                                    used to exit shell command line mode */
+  FILE *list1_fp;
+  FILE *list2_fp;
+  FILE *list3_fp;
+  vstring_def(list2_fname);
+  vstring_def(list2_ftmpname);
+  vstring_def(list3_ftmpname);
+  vstring_def(oldstr);
+  vstring_def(newstr);
+  long lines, changedLines, oldChangedLines, twoMatches, p1, p2;
+  long firstChangedLine;
+  flag cmdMode, changedFlag, outMsgFlag;
+  double sum;
+  vstring_def(bufferedLine);
+  vstring_def(tagStartMatch);  /* For TAG command */
+  long tagStartCount = 0;      /* For TAG command */
+  vstring_def(tagEndMatch);    /* For TAG command */
+  long tagEndCount = 0;        /* For TAG command */
+  long tagStartCounter = 0;    /* For TAG command */
+  long tagEndCounter = 0;      /* For TAG command */
+
+  double timeTotal = 0;
+  double timeIncr = 0;
+  flag printTime;  /* Set by "/ TIME" in SAVE PROOF and others */
+
+  flag defaultScrollMode = 1; /* Default to prompted mode */
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  p = 0;
+  q = 0;
+  s = 0;
+  texHeaderFlag = 0;
+  firstChangedLine = 0;
+  tagStartCount = 0;           /* For TAG command */
+  tagEndCount = 0;             /* For TAG command */
+  tagStartCounter = 0;         /* For TAG command */
+  tagEndCounter = 0;           /* For TAG command */
+
+  while (1) {
+
+    if (g_listMode) {
+      /* If called from the OS shell with arguments, do one command
+         then exit program. */
+      /* (However, let a SUBMIT job complete) */
+      if (argc > 1 && commandProcessedFlag &&
+             g_commandFileNestingLevel == 0) return;
+    }
+
+    g_errorCount = 0; /* Reset error count before each read or proof parse. */
+
+    /* Deallocate stuff that may have been used in previous pass */
+    free_vstring(str1);
+    free_vstring(str2);
+    free_vstring(str3);
+    free_vstring(str4);
+    free_vstring(str5);
+    free_nmbrString(nmbrTmp);
+    free_pntrString(pntrTmp);
+    free_nmbrString(nmbrSaveProof);
+    free_nmbrString(essentialFlags);
+    j = nmbrLen(g_rawArgNmbr);
+    if (j != g_rawArgs) bug(1110);
+    j = pntrLen(g_rawArgPntr);
+    if (j != g_rawArgs) bug(1111);
+    g_rawArgs = 0;
+    for (i = 0; i < j; i++) let((vstring *)(&g_rawArgPntr[i]), "");
+    free_pntrString(g_rawArgPntr);
+    free_nmbrString(g_rawArgNmbr);
+    j = pntrLen(g_fullArg);
+    for (i = 0; i < j; i++) let((vstring *)(&g_fullArg[i]),"");
+    free_pntrString(g_fullArg);
+    j = pntrLen(expandedProof);
+    if (j) {
+      for (i = 0; i < j; i++) {
+        let((vstring *)(&expandedProof[i]),"");
+      }
+     free_pntrString(expandedProof);
+    }
+
+    free_vstring(list2_fname);
+    free_vstring(list2_ftmpname);
+    free_vstring(list3_ftmpname);
+    free_vstring(oldstr);
+    free_vstring(newstr);
+    free_vstring(labelMatch);
+    /* (End of space deallocation) */
+
+    g_midiFlag = 0; /* Initialize here in case SHOW PROOF exits early */
+
+    if (g_memoryStatus) {
+      /*??? Change to user-friendly message */
+      print2("Memory:  string %ld xxxString %ld\n",db,db3);
+      getPoolStats(&i, &j, &k);
+      print2("Pool:  free alloc %ld  used alloc %ld  used actual %ld\n",i,j,k);
+    }
+
+    if (!g_toolsMode) {
+      if (g_PFASmode) {
+        let(&g_commandPrompt,"MM-PA> ");
+      } else {
+        let(&g_commandPrompt,"MM> ");
+      }
+    } else {
+      if (g_listMode) {
+        let(&g_commandPrompt,"Tools> ");
+      } else {
+        let(&g_commandPrompt,"TOOLS> ");
+      }
+    }
+
+    free_vstring(g_commandLine); /* Deallocate previous contents */
+
+    if (!commandProcessedFlag && argc > 1 && argsProcessed < argc - 1
+        && g_commandFileNestingLevel == 0) {
+      if (g_listMode) {
+        /* If program was compiled in TOOLS mode, the command-line argument
+           is assumed to be a single TOOLS command; build the equivalent
+           TOOLS command */
+        for (i = 1; i < argc; i++) {
+          argsProcessed++;
+          /* Put quotes around an argument with spaces or tabs or quotes
+             or empty string */
+          if (instr(1, argv[i], " ") || instr(1, argv[i], "\t")
+              || instr(1, argv[i], "\"") || instr(1, argv[i], "'")
+              || (argv[i])[0] == 0) {
+            /* If it contains a double quote, use a single quote */
+            if (instr(1, argv[i], "\"")) {
+              let(&str1, cat("'", argv[i], "'", NULL));
+            } else {
+              /* (??? (TODO)Case of both ' and " is not handled) */
+              let(&str1, cat("\"", argv[i], "\"", NULL));
+            }
+          } else {
+            let(&str1, argv[i]);
+          }
+          let(&g_commandLine, cat(g_commandLine, (i == 1) ? "" : " ", str1, NULL));
+        }
+      } else {
+        /* If program was compiled in default (Metamath) mode, each command-line
+           argument is considered a full Metamath command.  User is responsible
+           for ensuring necessary quotes around arguments are passed in. */
+        argsProcessed++;
+        g_scrollMode = 0; /* Set continuous scrolling until completed */
+        let(&g_commandLine, cat(g_commandLine, argv[argsProcessed], NULL));
+        if (argc == 2 && instr(1, argv[1], " ") == 0) {
+          /* Assume the user intended a READ command.  This special mode allows
+             invocation via "metamath xxx.mm". */
+          if (instr(1, g_commandLine, "\"") || instr(1, g_commandLine, "'")) {
+            /* If it already has quotes don't put quotes */
+            let(&g_commandLine, cat("READ ", g_commandLine, NULL));
+          } else {
+            /* Put quotes so / won't be interpreted as qualifier separator */
+            let(&g_commandLine, cat("READ \"", g_commandLine, "\"", NULL));
+          }
+        }
+      }
+      print2("%s\n", cat(g_commandPrompt, g_commandLine, NULL));
+    } else {
+      /* Get command from user input or SUBMIT script file */
+      g_commandLine = cmdInput1(g_commandPrompt);
+    }
+    if (argsProcessed == argc && !commandProcessedFlag) {
+      commandProcessedFlag = 1;
+      g_scrollMode = defaultScrollMode; /* Set prompted (default) scroll mode */
+    }
+    if (argsProcessed == argc - 1) {
+      argsProcessed++; /* Indicates restore scroll mode next time around */
+      if (g_toolsMode) {
+        /* If program was compiled in TOOLS mode, we're only going to execute
+           one command; set flag to exit next time around */
+        commandProcessedFlag = 1;
+      }
+    }
+
+    /* See if it's an operating system command */
+    /* (This is a command line that begins with a quote) */
+    if (g_commandLine[0] == '\'' || g_commandLine[0] == '\"') {
+      /* See if this computer has this feature */
+      if (!system(NULL)) {
+        print2("?This computer does not accept an operating system command.\n");
+        continue;
+      } else {
+        /* Strip off quote and trailing quote if any */
+        let(&str1, right(g_commandLine, 2));
+        if (g_commandLine[0]) { /* (Prevent stray pointer if empty string) */
+          if (g_commandLine[0] == g_commandLine[strlen(g_commandLine) - 1]) {
+            let(&str1, left(str1, (long)(strlen(str1)) - 1));
+          }
+        }
+        /* Do the operating system command */
+        /* The use of (void)!f() is to ignore the value on both
+          clang (which takes (void) as an ignore indicator)
+          and gcc (which doesn't but is fooled by the ! operator). */
+        (void)!system(str1);
+#ifdef VAXC
+        printf("\n"); /* Last line from VAX doesn't have new line */
+#endif
+        continue;
+      }
+    }
+
+    parseCommandLine(g_commandLine);
+    if (g_rawArgs == 0) {
+      continue; /* Empty or comment line */
+    }
+    if (!processCommandLine()) {
+      continue;
+    }
+
+    if (g_commandEcho || (g_toolsMode && g_listFile_fp != NULL)) {
+      /* Build the complete command and print it for the user */
+      k = pntrLen(g_fullArg);
+      let(&str1,"");
+      for (i = 0; i < k; i++) {
+        if (instr(1, g_fullArg[i], " ") || instr(1, g_fullArg[i], "\t")
+            || instr(1, g_fullArg[i], "\"") || instr(1, g_fullArg[i], "'")
+            || ((char *)(g_fullArg[i]))[0] == 0) {
+          /* If the argument has spaces or tabs or quotes
+             or is empty string, put quotes around it */
+          if (instr(1, g_fullArg[i], "\"")) {
+            let(&str1, cat(str1, "'", g_fullArg[i], "' ", NULL));
+          } else {
+            /* (???Case of both ' and " is not handled) */
+            let(&str1, cat(str1, "\"", g_fullArg[i], "\" ", NULL));
+          }
+        } else {
+          let(&str1, cat(str1, g_fullArg[i], " ", NULL));
+        }
+      }
+      let(&str1, left(str1, (long)(strlen(str1)) - 1)); /* Trim trailing space */
+      if (g_toolsMode && g_listFile_fp != NULL) {
+        /* Put line in list.tmp as command */
+        fprintf(g_listFile_fp, "%s\n", str1);  /* Print to list command file */
+      }
+      if (g_commandEcho) {
+        /* Put special character "!" before line for easier extraction to
+           build SUBMIT files; see also SET ECHO ON output below */
+        let(&str1, cat("!", str1, NULL));
+        /* The tilde is a special flag for printLongLine to print a
+           tilde before the carriage return in a split line, not after */
+        printLongLine(str1, "~", " ");
+      }
+    }
+
+    if (cmdMatches("BEEP") || cmdMatches("B")) {
+      /* Print a bell (if user types ahead "B", the bell lets him know when
+         his command is finished - useful for long-running commands */
+      print2("%c",7);
+      continue;
+    }
+
+    if (cmdMatches("HELP")) {
+      /* Build the complete command */
+      k = pntrLen(g_fullArg);
+      let(&str1,"");
+      for (i = 0; i < k; i++) {
+        let(&str1, cat(str1, g_fullArg[i], " ", NULL));
+      }
+      let(&str1, left(str1, (long)(strlen(str1)) - 1));
+      if (g_toolsMode) {
+        help0(str1);
+        help1(str1);
+      } else {
+        help1(str1);
+        help2(str1);
+        help3(str1);
+      }
+      continue;
+    }
+
+
+    if (cmdMatches("SET SCROLL")) {
+      if (cmdMatches("SET SCROLL CONTINUOUS")) {
+        defaultScrollMode = 0;
+        g_scrollMode = 0;
+        print2("Continuous scrolling is now in effect.\n");
+      } else {
+        defaultScrollMode = 1;
+        g_scrollMode = 1;
+        print2("Prompted scrolling is now in effect.\n");
+      }
+      continue;
+    }
+
+    if (cmdMatches("EXIT") || cmdMatches("QUIT") || cmdMatches("_EXIT_PA")) {
+      /* for MM-PA> exit in scripts, so it will error out in MM> (if for some reason
+        MM-PA wasn't entered) instead of exiting metamath */
+      if (cmdMatches("_EXIT_PA")) {
+        if (!g_PFASmode || (g_toolsMode && !g_listMode)) bug(1127);
+                 /* mmcmdl.c should have caught this */
+      }
+
+      if (g_toolsMode && !g_listMode) {
+        /* Quitting tools command from within Metamath */
+        if (!g_PFASmode) {
+          print2(
+ "Exiting the Text Tools.  Type EXIT again to exit Metamath.\n");
+        } else {
+          print2(
+ "Exiting the Text Tools.  Type EXIT again to exit the Proof Assistant.\n");
+        }
+        g_toolsMode = 0;
+        continue;
+      }
+
+      if (g_PFASmode) {
+
+        if (g_proofChanged &&
+              /* If g_proofChanged, but the UNDO stack is empty (and
+                 there were no other conditions such as stack overflow),
+                 the proof didn't really change, so it is safe to
+                 exit MM-PA without warning */
+              (processUndoStack(NULL, PUS_GET_STATUS, "", 0)
+                 /* However, if the proof was saved earlier, UNDO stack
+                    empty no longer indicates proof didn't change */
+                 || proofSavedFlag)) {
+          print2(
+              "Warning:  You have not saved changes to the proof of \"%s\".\n",
+              g_Statement[g_proveStatement].labelName);
+          if (switchPos("/ FORCE") == 0) {
+            str1 = cmdInput1("Do you want to EXIT anyway (Y, N) <N>? ");
+            if (str1[0] != 'y' && str1[0] != 'Y') {
+              print2("Use SAVE NEW_PROOF to save the proof.\n");
+              continue;
+            }
+          } else {
+            /* User specified / FORCE, so answer question automatically */
+            print2("Do you want to EXIT anyway (Y, N) <N>? Y\n");
+          }
+        }
+
+        g_proofChanged = 0;
+        processUndoStack(NULL, PUS_INIT, "", 0);
+        proofSavedFlag = 0; /* Will become 1 if proof is ever saved */
+
+        print2("Exiting the Proof Assistant.  Type EXIT again to exit Metamath.\n");
+
+        /* Deallocate proof structure */
+        deallocProofStruct(&g_ProofInProgress);
+
+        g_PFASmode = 0;
+        continue;
+      } else {
+        if (g_sourceChanged) {
+          print2("Warning:  You have not saved changes to the source.\n");
+          if (switchPos("/ FORCE") == 0) {
+            str1 = cmdInput1("Do you want to EXIT anyway (Y, N) <N>? ");
+            if (str1[0] != 'y' && str1[0] != 'Y') {
+              print2("Use WRITE SOURCE to save the changes.\n");
+              continue;
+            }
+          } else {
+            /* User specified / FORCE, so answer question automatically */
+            print2("Do you want to EXIT anyway (Y, N) <N>? Y\n");
+          }
+          g_sourceChanged = 0;
+        }
+
+        if (g_texFileOpenFlag) {
+          print2("The %s file \"%s\" was closed.\n",
+              g_htmlFlag ? "HTML" : "LaTeX", g_texFileName);
+          printTexTrailer(texHeaderFlag);
+          fclose(g_texFilePtr);
+          g_texFileOpenFlag = 0;
+        }
+        if (g_logFileOpenFlag) {
+          print2("The log file \"%s\" was closed %s %s.\n",g_logFileName,
+              date(),time_());
+          fclose(g_logFilePtr);
+          g_logFileOpenFlag = 0;
+        }
+
+        /* Free remaining allocations before exiting */
+        freeCommandLine();
+        freeInOu();
+        memFreePoolPurge(0);
+        eraseSource();
+        freeData(); /* Call AFTER eraseSource()(->initBigArrays->malloc) */
+        free_vstring(g_commandPrompt);
+        free_vstring(g_commandLine);
+        free_vstring(g_input_fn);
+        free_vstring(g_contributorName);
+
+        return; /* Exit from program */
+      }
+    }
+
+    if (cmdMatches("SUBMIT")) {
+      if (g_commandFileNestingLevel == MAX_COMMAND_FILE_NESTING) {
+        printf("?The SUBMIT nesting level has been exceeded.\n");
+        continue;
+      }
+      g_commandFilePtr[g_commandFileNestingLevel + 1] = fSafeOpen(g_fullArg[1], "r",
+          0/*noVersioningFlag*/);
+      if (!g_commandFilePtr[g_commandFileNestingLevel + 1]) continue;
+                                      /* Couldn't open (err msg was provided) */
+      g_commandFileNestingLevel++;
+      g_commandFileName[g_commandFileNestingLevel] = ""; /* Initialize if necessary */
+      let(&g_commandFileName[g_commandFileNestingLevel], g_fullArg[1]);
+
+      g_commandFileSilent[g_commandFileNestingLevel] = 0;
+      if (switchPos("/ SILENT")
+          || g_commandFileSilentFlag /* Propagate silence from outer level */) {
+        g_commandFileSilent[g_commandFileNestingLevel] = 1;
+      } else {
+        g_commandFileSilent[g_commandFileNestingLevel] = 0;
+      }
+      g_commandFileSilentFlag = g_commandFileSilent[g_commandFileNestingLevel];
+      if (!g_commandFileSilentFlag)
+        print2("Taking command lines from file \"%s\"...\n",
+            g_commandFileName[g_commandFileNestingLevel]);
+
+      continue;
+    }
+
+    if (g_toolsMode) {
+      /* Start of g_toolsMode-specific commands */
+#define ADD_MODE 1
+#define DELETE_MODE 2
+#define CLEAN_MODE 3
+#define SUBSTITUTE_MODE 4
+#define SWAP_MODE 5
+#define INSERT_MODE 6
+#define BREAK_MODE 7
+#define BUILD_MODE 8
+#define MATCH_MODE 9
+#define RIGHT_MODE 10
+#define TAG_MODE 11  /* Added TAG command */
+      cmdMode = 0;
+      if (cmdMatches("ADD")) cmdMode = ADD_MODE;
+      else if (cmdMatches("DELETE")) cmdMode = DELETE_MODE;
+      else if (cmdMatches("CLEAN")) cmdMode = CLEAN_MODE;
+      else if (cmdMatches("SUBSTITUTE") || cmdMatches("S"))
+        cmdMode = SUBSTITUTE_MODE;
+      else if (cmdMatches("SWAP")) cmdMode = SWAP_MODE;
+      else if (cmdMatches("INSERT")) cmdMode = INSERT_MODE;
+      else if (cmdMatches("BREAK")) cmdMode = BREAK_MODE;
+      else if (cmdMatches("BUILD")) cmdMode = BUILD_MODE;
+      else if (cmdMatches("MATCH")) cmdMode = MATCH_MODE;
+      else if (cmdMatches("RIGHT")) cmdMode = RIGHT_MODE;
+      else if (cmdMatches("TAG")) cmdMode = TAG_MODE;
+      if (cmdMode) {
+        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
+        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
+        if (cmdMode == RIGHT_MODE) {
+          /* Find the longest line */
+          p = 0;
+          while (linput(list1_fp, NULL, &str1)) {
+            if (p < (signed)(strlen(str1))) p = (long)(strlen(str1));
+          }
+          rewind(list1_fp);
+        }
+        let(&list2_fname, g_fullArg[1]);
+        if (list2_fname[strlen(list2_fname) - 2] == '~') {
+          let(&list2_fname, left(list2_fname, (long)(strlen(list2_fname)) - 2));
+          print2("The output file will be called %s.\n", list2_fname);
+        }
+        free_vstring(list2_ftmpname);
+        list2_ftmpname = fGetTmpName("zz~tools");
+        list2_fp = fSafeOpen(list2_ftmpname, "w", 0/*noVersioningFlag*/);
+        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
+        lines = 0;
+        changedLines = 0;
+        twoMatches = 0;
+        changedFlag = 0;
+        outMsgFlag = 0;
+        switch (cmdMode) {
+          case ADD_MODE:
+            break;
+          case TAG_MODE:
+            let(&tagStartMatch, g_fullArg[4]);
+            tagStartCount = (long)val(g_fullArg[5]);
+            if (tagStartCount == 0) tagStartCount = 1; /* Default */
+            let(&tagEndMatch, g_fullArg[6]);
+            tagEndCount = (long)val(g_fullArg[7]);
+            if (tagEndCount == 0) tagEndCount = 1; /* Default */
+            tagStartCounter = 0;
+            tagEndCounter = 0;
+            break;
+          case DELETE_MODE:
+            break;
+          case CLEAN_MODE:
+            let(&str4, edit(g_fullArg[2], 32));
+            q = 0;
+            if (instr(1, str4, "P") > 0) q = q + 1;
+            if (instr(1, str4, "D") > 0) q = q + 2;
+            if (instr(1, str4, "G") > 0) q = q + 4;
+            if (instr(1, str4, "B") > 0) q = q + 8;
+            if (instr(1, str4, "R") > 0) q = q + 16;
+            if (instr(1, str4, "C") > 0) q = q + 32;
+            if (instr(1, str4, "E") > 0) q = q + 128;
+            if (instr(1, str4, "Q") > 0) q = q + 256;
+            if (instr(1, str4, "L") > 0) q = q + 512;
+            if (instr(1, str4, "T") > 0) q = q + 1024;
+            if (instr(1, str4, "U") > 0) q = q + 2048;
+            if (instr(1, str4, "V") > 0) q = q + 4096;
+            break;
+          case SUBSTITUTE_MODE:
+            let(&newstr, g_fullArg[3]); /* The replacement string */
+            if (((vstring)(g_fullArg[4]))[0] == 'A' ||
+                ((vstring)(g_fullArg[4]))[0] == 'a') { /* ALL */
+              q = -1;
+            } else {
+              q = (long)val(g_fullArg[4]);
+              if (q == 0) q = 1;    /* The occurrence # of string to subst */
+            }
+            s = instr(1, g_fullArg[2], "\\n");
+            if (s) {
+              /*s = 1;*/ /* Replace lf flag */
+              q = 1; /* Only 1st occurrence makes sense in this mode */
+            }
+            if (!strcmp(g_fullArg[3], "\\n")) {
+              let(&newstr, "\n"); /* Replace with lf */
+            }
+            break;
+          case SWAP_MODE:
+            break;
+          case INSERT_MODE:
+            p = (long)val(g_fullArg[3]);
+            break;
+          case BREAK_MODE:
+            outMsgFlag = 1;
+            break;
+          case BUILD_MODE:
+            free_vstring(str4);
+            outMsgFlag = 1;
+            break;
+          case MATCH_MODE:
+            outMsgFlag = 1;
+        } /* End switch */
+        free_vstring(bufferedLine);
+        /*
+        while (linput(list1_fp, NULL, &str1)) {
+        */
+        while (1) {
+          if (bufferedLine[0]) {
+            /* Get input from buffered line (from rejected \n replacement) */
+            let(&str1, bufferedLine);
+            free_vstring(bufferedLine);
+          } else {
+            if (!linput(list1_fp, NULL, &str1)) break;
+          }
+          lines++;
+          oldChangedLines = changedLines;
+          let(&str2, str1);
+          switch (cmdMode) {
+            case ADD_MODE:
+              let(&str2, cat(g_fullArg[2], str1, g_fullArg[3], NULL));
+              if (strcmp(str1, str2)) changedLines++;
+              break;
+            case TAG_MODE:
+              if (tagStartCounter < tagStartCount) {
+                if (instr(1, str1, tagStartMatch)) tagStartCounter++;
+              }
+              if (tagStartCounter == tagStartCount &&
+                  tagEndCounter < tagEndCount) { /* We're in tagging range */
+                let(&str2, cat(g_fullArg[2], str1, g_fullArg[3], NULL));
+                if (strcmp(str1, str2)) changedLines++;
+                if (instr(1, str1, tagEndMatch)) tagEndCounter++;
+              }
+              break;
+            case DELETE_MODE:
+              p1 = instr(1, str1, g_fullArg[2]);
+              if (strlen(g_fullArg[2]) == 0) p1 = 1;
+              p2 = instr(p1, str1, g_fullArg[3]);
+              if (strlen(g_fullArg[3]) == 0) p2 = (long)strlen(str1) + 1;
+              if (p1 != 0 && p2 != 0) {
+                let(&str2, cat(left(str1, p1 - 1), right(str1, p2
+                    + (long)strlen(g_fullArg[3])), NULL));
+                changedLines++;
+              }
+              break;
+            case CLEAN_MODE:
+              if (q) {
+                let(&str2, edit(str1, q));
+                if (strcmp(str1, str2)) changedLines++;
+              }
+              break;
+            case SUBSTITUTE_MODE:
+              let(&str2, str1);
+              p = 0;
+              p1 = 0;
+
+              k = 1;
+              /* See if an additional match on line is required */
+              if (((vstring)(g_fullArg[5]))[0] != 0) {
+                if (!instr(1, str2, g_fullArg[5])) {
+                  /* No match on line; prevent any substitution */
+                  k = 0;
+                }
+              }
+
+              if (s && k) { /* We're asked to replace a newline char */
+                /* Read in the next line */
+                /*
+                if (linput(list1_fp, NULL, &str4)) {
+                  let(&str2, cat(str1, "\\n", str4, NULL));
+                */
+                if (linput(list1_fp, NULL, &bufferedLine)) {
+                  /* Join the next line and see if the string matches */
+                  if (instr(1, cat(str1, "\\n", bufferedLine, NULL),
+                      g_fullArg[2])) {
+                    let(&str2, cat(str1, "\\n", bufferedLine, NULL));
+                    free_vstring(bufferedLine);
+                  } else {
+                    k = 0; /* No match - leave bufferedLine for next pass */
+                  }
+                } else { /* EOF reached */
+                  print2("Warning: file %s has an odd number of lines\n",
+                      g_fullArg[1]);
+                }
+              }
+
+              while (k) {
+                p1 = instr(p1 + 1, str2, g_fullArg[2]);
+                if (!p1) break;
+                p++;
+                if (p == q || q == -1) {
+                  let(&str2, cat(left(str2, p1 - 1), newstr,
+                      right(str2, p1 + (long)strlen(g_fullArg[2])), NULL));
+                  if (newstr[0] == '\n') {
+                    /* Replacement string is an lf */
+                    lines++;
+                    changedLines++;
+                  }
+                  /* Continue the search after the replacement
+                     string, so that "SUBST 1.tmp abbb ab a ''" will change
+                     "abbbab" to "abba" rather than "aa" */
+                  p1 = p1 + (long)strlen(newstr) - 1;
+                  /* p1 = p1 - (long)strlen(g_fullArg[2]) + (long)strlen(newstr); */ /* bad */
+                  if (q != -1) break;
+                }
+              }
+              if (strcmp(str1, str2)) changedLines++;
+              break;
+            case SWAP_MODE:
+              p1 = instr(1, str1, g_fullArg[2]);
+              if (p1) {
+                p2 = instr(p1 + 1, str1, g_fullArg[2]);
+                if (p2) twoMatches++;
+                let(&str2, cat(right(str1, p1) + (long)strlen(g_fullArg[2]),
+                    g_fullArg[2], left(str1, p1 - 1), NULL));
+                if (strcmp(str1, str2)) changedLines++;
+              }
+              break;
+            case INSERT_MODE:
+              if ((signed)(strlen(str2)) < p - 1)
+                let(&str2, cat(str2, space(p - 1 - (long)strlen(str2)), NULL));
+              let(&str2, cat(left(str2, p - 1), g_fullArg[2],
+                  right(str2, p), NULL));
+              if (strcmp(str1, str2)) changedLines++;
+              break;
+            case BREAK_MODE:
+              let(&str2, str1);
+              changedLines++;
+              for (i = 0; i < (signed)(strlen(g_fullArg[2])); i++) {
+                p = 0;
+                while (1) {
+                  p = instr(p + 1, str2, chr(((vstring)(g_fullArg[2]))[i]));
+                  if (!p) break;
+                  /* Put spaces around special one-char tokens */
+                  let(&str2, cat(left(str2, p - 1), " ",
+                      mid(str2, p, 1),
+                      " ", right(str2, p + 1), NULL));
+                  /*p++;*/
+                  /* Even though space is always a separator, it can be used
+                     to suppress all default tokens.  Go past 2nd space to prevent
+                     infinite loop in that case. */
+                  p += 2;
+                }
+              }
+              let(&str2, edit(str2, 8 + 16 + 128)); /* Reduce & trim spaces */
+              for (p = (long)strlen(str2) - 1; p >= 0; p--) {
+                if (str2[p] == ' ') {
+                  str2[p] = '\n';
+                  changedLines++;
+                }
+              }
+              if (!str2[0]) changedLines--; /* Don't output blank line */
+              break;
+            case BUILD_MODE:
+              if (str2[0] != 0) { /* Ignore blank lines */
+                if (str4[0] == 0) {
+                  let(&str4, str2);
+                } else {
+                  if ((long)strlen(str4) + (long)strlen(str2) > 72) {
+                    let(&str4, cat(str4, "\n", str2, NULL));
+                    changedLines++;
+                  } else {
+                    let(&str4, cat(str4, " ", str2, NULL));
+                  }
+                }
+                p = instr(1, str4, "\n");
+                if (p) {
+                  let(&str2, left(str4, p - 1));
+                  let(&str4, right(str4, p + 1));
+                } else {
+                  let(&str2, "");
+                }
+              }
+              break;
+            case MATCH_MODE:
+              if (((vstring)(g_fullArg[2]))[0] == 0) {
+                /* Match any non-blank line */
+                p = str1[0];
+              } else {
+                p = instr(1, str1, g_fullArg[2]);
+              }
+              if (((vstring)(g_fullArg[3]))[0] == 'n' ||
+                  ((vstring)(g_fullArg[3]))[0] == 'N') {
+                p = !p;
+              }
+              if (p) changedLines++;
+              break;
+            case RIGHT_MODE:
+              let(&str2, cat(space(p - (long)strlen(str2)), str2, NULL));
+              if (strcmp(str1, str2)) changedLines++;
+              break;
+          } /* End switch(cmdMode) */
+          if (lines == 1) let(&str3, left(str2, 79)); /* For msg */
+          if (oldChangedLines != changedLines && !changedFlag) {
+            changedFlag = 1;
+            let(&str3, left(str2, 79)); /* For msg */
+            firstChangedLine = lines;
+            if ((cmdMode == SUBSTITUTE_MODE && newstr[0] == '\n')
+                || cmdMode == BUILD_MODE) /* Joining lines */ {
+              firstChangedLine = 1; /* Better message */
+            }
+          }
+          if (((cmdMode != BUILD_MODE && cmdMode != BREAK_MODE)
+              || str2[0] != 0)
+              && (cmdMode != MATCH_MODE || p))
+            fprintf(list2_fp, "%s\n", str2);
+        } /* Next input line */
+        if (cmdMode == BUILD_MODE) {
+          if (str4[0]) {
+            /* Output last partial line */
+            fprintf(list2_fp, "%s\n", str4);
+            changedLines++;
+            if (!str3[0]) {
+              let(&str3, str4); /* For msg */
+            }
+          }
+        }
+        /* Remove any lines after lf for readability of msg */
+        p = instr(1, str3, "\n");
+        if (p) let(&str3, left(str3, p - 1));
+        if (!outMsgFlag) {
+          /* Make message depend on line counts */
+          if (!changedFlag) {
+            if (!lines) {
+              print2("The file %s has no lines.\n", g_fullArg[1]);
+            } else {
+              print2(
+"The file %s has %ld line%s; none were changed.  First line:\n",
+                list2_fname, lines, (lines == 1) ? "" : "s");
+              print2("%s\n", str3);
+            }
+          } else {
+            print2(
+"The file %s has %ld line%s; %ld w%s changed.  First changed line is %ld:\n",
+                list2_fname,
+                lines,  (lines == 1) ? "" : "s",
+                changedLines,  (changedLines == 1) ? "as" : "ere",
+                firstChangedLine);
+            print2("%s\n", str3);
+          }
+          if (twoMatches > 0) {
+            /* For SWAP command */
+            print2(
+"Warning:  %ld line%s more than one \"%s\".  The first one was used.\n",
+                twoMatches, (twoMatches == 1) ? " has" : "s have", g_fullArg[2]);
+          }
+        } else {
+          /* if (changedLines == 0) let(&str3, ""); */
+          print2(
+"The input had %ld line%s, the output has %ld line%s.%s\n",
+              lines, (lines == 1) ? "" : "s",
+              changedLines, (changedLines == 1) ? "" : "s",
+              (changedLines == 0) ? "" : " First output line:");
+          if (changedLines != 0) print2("%s\n", str3);
+        }
+        fclose(list1_fp);
+        fclose(list2_fp);
+        fSafeRename(list2_ftmpname, list2_fname);
+        /* Deallocate string memory */
+        free_vstring(tagStartMatch);
+        free_vstring(tagEndMatch);
+        continue;
+      } /* end if cmdMode for ADD, etc. */
+
+#define SORT_MODE 1
+#define UNDUPLICATE_MODE 2
+#define DUPLICATE_MODE 3
+#define UNIQUE_MODE 4
+#define REVERSE_MODE 5
+      cmdMode = 0;
+      if (cmdMatches("SORT")) cmdMode = SORT_MODE;
+      else if (cmdMatches("UNDUPLICATE")) cmdMode = UNDUPLICATE_MODE;
+      else if (cmdMatches("DUPLICATE")) cmdMode = DUPLICATE_MODE;
+      else if (cmdMatches("UNIQUE")) cmdMode = UNIQUE_MODE;
+      else if (cmdMatches("REVERSE")) cmdMode = REVERSE_MODE;
+      if (cmdMode) {
+        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
+        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
+        let(&list2_fname, g_fullArg[1]);
+        if (list2_fname[strlen(list2_fname) - 2] == '~') {
+          let(&list2_fname, left(list2_fname, (long)strlen(list2_fname) - 2));
+          print2("The output file will be called %s.\n", list2_fname);
+        }
+        free_vstring(list2_ftmpname);
+        list2_ftmpname = fGetTmpName("zz~tools");
+        list2_fp = fSafeOpen(list2_ftmpname, "w", 0/*noVersioningFlag*/);
+        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
+
+        /* Count the lines */
+        lines = 0;
+        while (linput(list1_fp, NULL, &str1)) lines++;
+        if (cmdMode != SORT_MODE  && cmdMode != REVERSE_MODE) {
+          print2("The input file has %ld lines.\n", lines);
+        }
+
+        /* Close and reopen the input file */
+        fclose(list1_fp);
+        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
+        /* Allocate memory */
+        pntrLet(&pntrTmp, pntrSpace(lines));
+        /* Assign the lines to string array */
+        for (i = 0; i < lines; i++) linput(list1_fp, NULL,
+            (vstring *)(&pntrTmp[i]));
+
+        /* Sort */
+        if (cmdMode != REVERSE_MODE) {
+          if (cmdMode == SORT_MODE) {
+            g_qsortKey = g_fullArg[2]; /* Do not deallocate! */
+          } else {
+            g_qsortKey = "";
+          }
+          qsort(pntrTmp, (size_t)lines, sizeof(void *), qsortStringCmp);
+        } else { /* Reverse the lines */
+          for (i = lines / 2; i < lines; i++) {
+            g_qsortKey = pntrTmp[i]; /* Use g_qsortKey as handy tmp var here */
+            pntrTmp[i] = pntrTmp[lines - 1 - i];
+            pntrTmp[lines - 1 - i] = g_qsortKey;
+          }
+        }
+
+        /* Output sorted lines */
+        changedLines = 0;
+        let(&str3, "");
+        for (i = 0; i < lines; i++) {
+          j = 0; /* Flag that line should be printed */
+          switch (cmdMode) {
+            case SORT_MODE:
+            case REVERSE_MODE:
+              j = 1;
+              break;
+            case UNDUPLICATE_MODE:
+              if (i == 0) {
+                j = 1;
+              } else {
+                if (strcmp((vstring)(pntrTmp[i - 1]), (vstring)(pntrTmp[i]))) {
+                  j = 1;
+                }
+              }
+              break;
+            case DUPLICATE_MODE:
+              if (i > 0) {
+                if (!strcmp((vstring)(pntrTmp[i - 1]), (vstring)(pntrTmp[i]))) {
+                  if (i == lines - 1) {
+                    j = 1;
+                  } else {
+                    if (strcmp((vstring)(pntrTmp[i]),
+                        (vstring)(pntrTmp[i + 1]))) {
+                      j = 1;
+                    }
+                  }
+                }
+              }
+              break;
+            case UNIQUE_MODE:
+              if (i < lines - 1) {
+                if (strcmp((vstring)(pntrTmp[i]), (vstring)(pntrTmp[i + 1]))) {
+                  if (i == 0) {
+                    j = 1;
+                  } else {
+                    if (strcmp((vstring)(pntrTmp[i - 1]),
+                        (vstring)(pntrTmp[i]))) {
+                      j = 1;
+                    }
+                  }
+                }
+              } else {
+                if (i == 0) {
+                  j = 1;
+                } else {
+                  if (strcmp((vstring)(pntrTmp[i - 1]),
+                        (vstring)(pntrTmp[i]))) {
+                      j = 1;
+                  }
+                }
+              }
+              break;
+          } /* end switch (cmdMode) */
+          if (j) {
+            fprintf(list2_fp, "%s\n", (vstring)(pntrTmp[i]));
+            changedLines++;
+            if (changedLines == 1)
+              let(&str3, left((vstring)(pntrTmp[i]), 79));
+          }
+        } /* next i */
+        print2("The output file has %ld lines.  The first line is:\n",
+            changedLines);
+        print2("%s\n", str3);
+
+        /* Deallocate memory */
+        for (i = 0; i < lines; i++) let((vstring *)(&pntrTmp[i]), "");
+        free_pntrString(pntrTmp);
+
+        fclose(list1_fp);
+        fclose(list2_fp);
+        fSafeRename(list2_ftmpname, list2_fname);
+        continue;
+      } /* end if cmdMode for SORT, etc. */
+
+      if (cmdMatches("PARALLEL")) {
+        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
+        list2_fp = fSafeOpen(g_fullArg[2], "r", 0/*noVersioningFlag*/);
+        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
+        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
+        free_vstring(list3_ftmpname);
+        list3_ftmpname = fGetTmpName("zz~tools");
+        list3_fp = fSafeOpen(list3_ftmpname, "w", 0/*noVersioningFlag*/);
+        if (!list3_fp) continue; /* Couldn't open it (error msg was provided) */
+
+        p1 = 1; p2 = 1; /* not eof */
+        p = 0; q = 0; /* lines */
+        j = 0; /* 1st line flag */
+        let(&str3, "");
+        while (1) {
+          free_vstring(str1);
+          if (p1) {
+            p1 = linput(list1_fp, NULL, &str1);
+            if (p1) p++;
+            else let(&str1, "");
+          }
+          free_vstring(str2);
+          if (p2) {
+            p2 = linput(list2_fp, NULL, &str2);
+            if (p2) q++;
+            else let(&str2, "");
+          }
+          if (!p1 && !p2) break;
+          let(&str4, cat(str1, g_fullArg[4], str2, NULL));
+          fprintf(list3_fp, "%s\n", str4);
+          if (!j) {
+            let(&str3, str4); /* Save 1st line for msg */
+            j = 1;
+          }
+        }
+        if (p == q) {
+          print2(
+"The input files each had %ld lines.  The first output line is:\n", p);
+        } else {
+          print2(
+"Warning: file \"%s\" had %ld lines while file \"%s\" had %ld lines.\n",
+              g_fullArg[1], p, g_fullArg[2], q);
+          if (p < q) p = q;
+          print2("The output file \"%s\" has %ld lines.  The first line is:\n",
+              g_fullArg[3], p);
+        }
+        print2("%s\n", str3);
+
+        fclose(list1_fp);
+        fclose(list2_fp);
+        fclose(list3_fp);
+        fSafeRename(list3_ftmpname, g_fullArg[3]);
+        continue;
+      }
+
+
+      if (cmdMatches("NUMBER")) {
+        list1_fp = fSafeOpen(g_fullArg[1], "w", 0/*noVersioningFlag*/);
+        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
+        j = (long)strlen(str(val(g_fullArg[2])));
+        k = (long)strlen(str(val(g_fullArg[3])));
+        if (k > j) j = k;
+        for (i = (long)val(g_fullArg[2]); i <= val(g_fullArg[3]);
+            i = i + (long)val(g_fullArg[4])) {
+          let(&str1, str((double)i));
+          fprintf(list1_fp, "%s\n", str1);
+        }
+        fclose(list1_fp);
+        continue;
+      }
+
+      if (cmdMatches("COUNT")) {
+        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
+        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
+        p1 = 0;
+        p2 = 0;
+        lines = 0;
+        q = 0; /* Longest line length */
+        i = 0; /* First longest line */
+        j = 0; /* Number of longest lines */
+        sum = 0.0; /* Sum of numeric content of lines */
+        firstChangedLine = 0;
+        while (linput(list1_fp, NULL, &str1)) {
+          lines++;
+
+          /* Longest line */
+          if (q < (signed)(strlen(str1))) {
+            q = (long)strlen(str1);
+            let(&str4, str1);
+            i = lines;
+            j = 0;
+          }
+
+          if (q == (signed)(strlen(str1))) {
+            j++;
+          }
+
+          if (instr(1, str1, g_fullArg[2])) {
+            if (!firstChangedLine) {
+              firstChangedLine = lines;
+              let(&str3, str1);
+            }
+            p1++;
+            p = 0;
+            while (1) {
+              p = instr(p + 1, str1, g_fullArg[2]);
+              if (!p) break;
+              p2++;
+            }
+          }
+          sum = sum + val(str1);
+        }
+        print2(
+"The file has %ld lines.  The string \"%s\" occurs %ld times on %ld lines.\n",
+            lines, g_fullArg[2], p2, p1);
+        if (firstChangedLine) {
+          print2("The first occurrence is on line %ld:\n", firstChangedLine);
+          print2("%s\n", str3);
+        }
+        print2(
+"The first longest line (out of %ld) is line %ld and has %ld characters:\n",
+            j, i, q);
+        printLongLine(str4, "    "/*startNextLine*/, ""/*breakMatch*/);
+            /* breakMatch empty means break line anywhere */
+        printLongLine(cat(
+            "Stripping all but digits, \".\", and \"-\", the sum of lines is ",
+            str((double)sum), NULL), "    ", " ");
+        fclose(list1_fp);
+        continue;
+      }
+
+      if (cmdMatches("TYPE") || cmdMatches("T")) {
+        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
+        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
+        if (g_rawArgs == 2) {
+          n = 10;
+        } else {
+          if (((vstring)(g_fullArg[2]))[0] == 'A' ||
+              ((vstring)(g_fullArg[2]))[0] == 'a') { /* ALL */
+            n = -1;
+          } else {
+            n = (long)val(g_fullArg[2]);
+          }
+        }
+        for (i = 0; i < n || n == -1; i++) {
+          if (!linput(list1_fp, NULL, &str1)) break;
+          if (!print2("%s\n", str1)) break;
+        }
+        fclose(list1_fp);
+        continue;
+      } /* end TYPE */
+
+      if (cmdMatches("UPDATE")) {
+        list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
+        list2_fp = fSafeOpen(g_fullArg[2], "r", 0/*noVersioningFlag*/);
+        if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
+        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
+        if (!getRevision(g_fullArg[4])) {
+          print2(
+"?The revision tag must be of the form /*nn*/ or /*#nn*/.  Please try again.\n");
+          continue;
+        }
+        free_vstring(list3_ftmpname);
+        list3_ftmpname = fGetTmpName("zz~tools");
+        list3_fp = fSafeOpen(list3_ftmpname, "w", 0/*noVersioningFlag*/);
+        if (!list3_fp) continue; /* Couldn't open it (error msg was provided) */
+
+        revise(list1_fp, list2_fp, list3_fp, g_fullArg[4],
+            (long)val(g_fullArg[5]));
+
+        fSafeRename(list3_ftmpname, g_fullArg[3]);
+        continue;
+      }
+
+      if (cmdMatches("COPY") || cmdMatches("C")) {
+        free_vstring(list2_ftmpname);
+        list2_ftmpname = fGetTmpName("zz~tools");
+        list2_fp = fSafeOpen(list2_ftmpname, "w", 0/*noVersioningFlag*/);
+        if (!list2_fp) continue; /* Couldn't open it (error msg was provided) */
+        let(&str4, cat(g_fullArg[1], ",", NULL));
+        lines = 0;
+        j = 0; /* Error flag */
+        while (1) {
+          if (!str4[0]) break; /* Done scanning list */
+          p = instr(1, str4, ",");
+          let(&str3, left(str4, p - 1));
+          let(&str4, right(str4, p + 1));
+          list1_fp = fSafeOpen((str3), "r", 0/*noVersioningFlag*/);
+          if (!list1_fp) { /* Couldn't open it (error msg was provided) */
+            j = 1; /* Error flag */
+            break;
+          }
+          n = 0;
+          while (linput(list1_fp, NULL, &str1)) {
+            lines++; n++;
+            fprintf(list2_fp, "%s\n", str1);
+          }
+          if (instr(1, g_fullArg[1], ",")) { /* More than 1 input file */
+            print2("The input file \"%s\" has %ld lines.\n", str3, n);
+          }
+          fclose(list1_fp);
+        }
+        if (j) continue; /* One of the input files couldn't be opened */
+        fclose(list2_fp);
+        print2("The output file \"%s\" has %ld lines.\n", g_fullArg[2], lines);
+        fSafeRename(list2_ftmpname, g_fullArg[2]);
+        continue;
+      }
+
+      print2("?This command has not been implemented yet.\n");
+      continue;
+    } /* End of g_toolsMode-specific commands */
+
+    if (cmdMatches("TOOLS")) {
+      print2(
+"Entering the Text Tools utilities.  Type HELP for help, EXIT to exit.\n");
+      g_toolsMode = 1;
+      continue;
+    }
+
+    if (cmdMatches("READ")) {
+      /* We can't use 'statements > 0' for the test since the source
+         could be just a comment */
+      if (g_sourceHasBeenRead == 1) {
+        printLongLine(cat(
+            "?Sorry, reading of more than one source file is not allowed.  ",
+            "The file \"", g_input_fn, "\" has already been READ in.  ",
+            "You may type ERASE to start over.  Note that additional source ",
+            "files may be included in the source file with \"$[ <filename> $]\".",
+            NULL),"  "," ");
+        continue;
+      }
+
+      let(&g_input_fn, g_fullArg[1]);
+
+      let(&str1, cat(g_rootDirectory, g_input_fn, NULL));
+      g_input_fp = fSafeOpen(str1, "r", 0/*noVersioningFlag*/);
+      if (!g_input_fp) continue; /* Couldn't open it (error msg was provided
+                                     by fSafeOpen) */
+      fclose(g_input_fp);
+
+      readInput();
+
+      if (switchPos("/ VERIFY")) {
+        verifyProofs("*",1); /* Parse and verify */
+      } else {
+        /* verifyProofs("*",0); */ /* Parse only (for gross error checking) */
+      }
+
+      if (g_sourceHasBeenRead == 1) {
+        if (!g_errorCount) {
+          let(&str1, "No errors were found.");
+          if (!switchPos("/ VERIFY")) {
+              let(&str1, cat(str1,
+         "  However, proofs were not checked.  Type VERIFY PROOF *",
+         " if you want to check them.",
+              NULL));
+          }
+          printLongLine(str1, "", " ");
+        } else {
+          print2("\n");
+          if (g_errorCount == 1) {
+            print2("One error was found.\n");
+          } else {
+            print2("%ld errors were found.\n", (long)g_errorCount);
+          }
+        }
+      } /* g_sourceHasBeenRead == 1 */
+
+      continue;
+    }
+
+    if (cmdMatches("WRITE SOURCE")) {
+      let(&g_output_fn, g_fullArg[2]);
+
+      if (switchPos("/ REWRAP") > 0) {
+        r = 2; /* Re-wrap then format (more aggressive than / FORMAT) */
+      } else if (switchPos("/ FORMAT") > 0) {
+        r = 1; /* Format output according to set.mm standard */
+      } else {
+        r = 0; /* Keep formatting as-is */
+      }
+
+      i = switchPos("/ EXTRACT");
+      if (i > 0) {
+        let(&str1, g_fullArg[i + 1]); /* List of labels */
+        if (r > 0
+            || switchPos("/ SPLIT") > 0
+            || switchPos("/ KEEP_INCLUDES") > 0) {
+          print2(
+"?You may not use / SPLIT, / REWRAP, or / KEEP_INCLUDES with / EXTRACT.\n");
+          continue;
+        }
+      } else {
+        let(&str1, ""); /* Empty string means full db */
+      }
+
+      writeSource((char)r, /* Rewrap type */
+        ((switchPos("/ SPLIT") > 0) ? 1 : 0),
+        ((switchPos("/ NO_VERSIONING") > 0) ? 1 : 0),
+        ((switchPos("/ KEEP_INCLUDES") > 0) ? 1 : 0),
+        str1 /* Label list to extract */
+          );
+      g_sourceChanged = 0;
+
+      free_vstring(str1); /* Deallocate */
+      continue;
+    } /* End of WRITE SOURCE */
+
+
+    if (cmdMatches("WRITE THEOREM_LIST")) {
+      /* Write out an HTML summary of the theorems to
+         mmtheorems.html, mmtheorems1.html,... */
+      /* THEOREMS_PER_PAGE is the default number of proof descriptions to output. */
+#define THEOREMS_PER_PAGE 100
+      /* theoremsPerPage is the actual number of proof descriptions to output. */
+      /* See if the user overrode the default. */
+      i = switchPos("/ THEOREMS_PER_PAGE");
+      if (i != 0) {
+        theoremsPerPage = (long)val(g_fullArg[i + 1]); /* Use user's value */
+      } else {
+        theoremsPerPage = THEOREMS_PER_PAGE; /* Use the default value */
+      }
+      showLemmas = (switchPos("/ SHOW_LEMMAS") != 0);
+      noVersioning = (switchPos("/ NO_VERSIONING") != 0);
+
+      g_htmlFlag = 1;
+      /* If not specified, for backwards compatibility in scripts
+         leave g_altHtmlFlag at current value */
+      if (switchPos("/ HTML") != 0) {
+        if (switchPos("/ ALT_HTML") != 0) {
+          print2("?Please specify only one of / HTML and / ALT_HTML.\n");
+          continue;
+        }
+        g_altHtmlFlag = 0;
+      } else {
+        if (switchPos("/ ALT_HTML") != 0) g_altHtmlFlag = 1;
+      }
+
+      if (2/*error*/ == readTexDefs(0 /* 1 = check errors only */,
+          1 /* 1 = GIF file existence check */ )) {
+        continue; /* An error occurred */
+      }
+
+      /* Output the theorem list */
+      writeTheoremList(theoremsPerPage, showLemmas, noVersioning);
+      continue;
+    }  /* End of "WRITE THEOREM_LIST" */
+
+
+    if (cmdMatches("WRITE BIBLIOGRAPHY")) {
+      /* This command builds the bibliographical cross-references to various
+         textbooks and updates the user-specified file normally called
+         mmbiblio.html. */
+
+      writeBibliography(g_fullArg[2],
+          "*", /* labelMatch - all labels */
+          0,  /* 1 = no output, just warning msgs if any */
+          1); /* 1 = check external files (gifs and bib) */
+      continue;
+    }  /* End of "WRITE BIBLIOGRAPHY" */
+
+
+    if (cmdMatches("WRITE RECENT_ADDITIONS")) {
+      /* This utility creates a list of recent proof descriptions and updates
+         the user-specified file normally called mmrecent.html.
+       */
+
+      /* RECENT_COUNT is the default number of proof descriptions to output. */
+#define RECENT_COUNT 100
+      /* i is the actual number of proof descriptions to output. */
+      /* See if the user overrode the default. */
+      i = switchPos("/ LIMIT");
+      if (i) {
+        i = (long)val(g_fullArg[i + 1]); /* Use user's value */
+      } else {
+        i = RECENT_COUNT; /* Use the default value */
+      }
+
+      g_htmlFlag = 1;
+      /* If not specified, for backwards compatibility in scripts
+         leave g_altHtmlFlag at current value */
+      if (switchPos("/ HTML") != 0) {
+        if (switchPos("/ ALT_HTML") != 0) {
+          print2("?Please specify only one of / HTML and / ALT_HTML.\n");
+          continue;
+        }
+        g_altHtmlFlag = 0;
+      } else {
+        if (switchPos("/ ALT_HTML") != 0) g_altHtmlFlag = 1;
+      }
+
+      /* readTexDefs() rereads based on changed in g_htmlFlag, g_altHtmlFlag */
+      if (2/*error*/ == readTexDefs(0 /* 1 = check errors only */,
+          1 /* 1 = GIF file existence check */  )) {
+        continue; /* An error occurred */
+      }
+
+      tmpFlag = 0; /* Error flag to recover input file */
+      list1_fp = fSafeOpen(g_fullArg[2], "r", 0/*noVersioningFlag*/);
+      if (list1_fp == NULL) {
+        /* Couldn't open it (error msg was provided)*/
+        continue;
+      }
+      fclose(list1_fp);
+      /* This will rename the input mmrecent.html as mmrecent.html~1 */
+      list2_fp = fSafeOpen(g_fullArg[2], "w", 0/*noVersioningFlag*/);
+      if (list2_fp == NULL) {
+          /* Couldn't open it (error msg was provided)*/
+        continue;
+      }
+      /* Note: in older versions the "~1" string was OS-dependent, but we
+         don't support VAX or THINK C anymore...  Anyway we reopen it
+         here with the renamed file in case the OS won't let us rename
+         an opened file during the fSafeOpen for write above. */
+      list1_fp = fSafeOpen(cat(g_fullArg[2], "~1", NULL), "r",
+          0/*noVersioningFlag*/);
+      if (list1_fp == NULL) bug(1117);
+
+      /* Transfer the input file up to the special "<!-- #START# -->" comment */
+      while (1) {
+        if (!linput(list1_fp, NULL, &str1)) {
+          print2(
+"?Error: Could not find \"<!-- #START# -->\" line in input file \"%s\".\n",
+              g_fullArg[2]);
+          tmpFlag = 1; /* Error flag to recover input file */
+          break;
+        }
+        if (!strcmp(left(str1, 21), "<!-- last updated -->")) {
+          let(&str1, cat(left(str1, 21), " <I>Last updated on ", date(),
+          /* ??Future: use timezones properly */
+          /* Just make it "ET" for "Eastern Time" */
+            " at ", time_(), " ET.</I>", NULL));
+        }
+        fprintf(list2_fp, "%s\n", str1);
+        if (!strcmp(str1, "<!-- #START# -->")) break;
+      }
+      if (tmpFlag) goto wrrecent_error;
+
+      /* Get and parse today's date */
+      parseDate(date(), &k /*dd*/, &l /*mmm*/, &m /*yyyy*/);
+
+#define START_YEAR 93 /* Earliest 19xx year in set.mm database */
+      n = 0; /* Count of how many output so far */
+      while (n < i /*RECENT_COUNT*/ && m > START_YEAR + 1900 - 1) {
+
+        /* Build date string to match */
+        buildDate(k, l, m, &str1);
+
+        for (stmt = g_statements; stmt >= 1; stmt--) {
+
+          if (g_Statement[stmt].type != (char)p_
+                && g_Statement[stmt].type != (char)a_) {
+            continue;
+          }
+
+          free_vstring(str2);
+          str2 = getContrib(stmt, MOST_RECENT_DATE);
+
+          /* See if the date comment matches */
+          if (!strcmp(str2, str1)) {
+            /* We have a match, so increment the match count */
+            n++;
+            free_vstring(str3);
+            str3 = getDescription(stmt);
+            free_vstring(str4);
+            str4 = pinkHTML(stmt); /* Get little pink number */
+            /* Output the description comment */
+            /* Break up long lines for text editors with printLongLine */
+            free_vstring(g_printString);
+            g_outputToString = 1;
+            print2("\n"); /* Blank line for HTML human readability */
+            printLongLine(cat(
+
+                /*
+                (stmt < g_extHtmlStmt) ?
+                     "<TR>" :
+                     cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
+                */
+
+                (stmt < g_extHtmlStmt)
+                   ? "<TR>"
+                   : (stmt < g_mathboxStmt)
+                       ? cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">",
+                           NULL)
+                       : cat("<TR BGCOLOR=", SANDBOX_COLOR, ">", NULL),
+
+                "<TD NOWRAP>",  /* IE breaks up the date */
+                str2, /* Date */
+                "</TD><TD ALIGN=CENTER><A HREF=\"",
+                g_Statement[stmt].labelName, ".html\">",
+                g_Statement[stmt].labelName, "</A>",
+                str4, "</TD><TD ALIGN=LEFT>", NULL),  /* Description */
+              " ",  /* Start continuation line with space */
+              "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+            g_showStatement = stmt; /* For printTexComment */
+            g_outputToString = 0; /* For printTexComment */
+            g_texFilePtr = list2_fp;
+            printTexComment(str3,              /* Sends result to g_texFilePtr */
+                0, /* 1 = htmlCenterFlag */
+                PROCESS_EVERYTHING, /* actionBits */
+                1  /* 1 = fileCheck */ );
+            g_texFilePtr = NULL;
+            g_outputToString = 1; /* Restore after printTexComment */
+
+            /* Get HTML hypotheses => assertion */
+            free_vstring(str4);
+            str4 = getTexOrHtmlHypAndAssertion(stmt);
+            printLongLine(cat("</TD></TR><TR",
+
+                  /*
+                  (s < g_extHtmlStmt) ?
+                       ">" :
+                       cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
+                  */
+
+                  (stmt < g_extHtmlStmt)
+                     ? ">"
+                     : (stmt < g_mathboxStmt)
+                         ? cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">",
+                             NULL)
+                         : cat(" BGCOLOR=", SANDBOX_COLOR, ">", NULL),
+
+                "<TD COLSPAN=3 ALIGN=CENTER>",
+                str4, "</TD></TR>", NULL),
+
+                " ",  /* Start continuation line with space */
+                "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+            g_outputToString = 0;
+            fprintf(list2_fp, "%s", g_printString);
+            free_vstring(g_printString);
+
+            if (n >= i /*RECENT_COUNT*/) break; /* We're done */
+
+            /* Put separator row if not last theorem */
+            g_outputToString = 1;
+            printLongLine(cat("<TR BGCOLOR=white><TD COLSPAN=3>",
+                "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>", NULL),
+                " ",  /* Start continuation line with space */
+                "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+            /* Put the previous, current, and next statement
+               labels in HTML comments so a script can use them to update
+               web site incrementally.  This would be done by searching
+               for "For script" and gather label between = and --> then
+               regenerate just those statements.  Previous and next labels
+               are included to prevent dead links if they don't exist yet. */
+            /* This section can be deleted without side effects */
+            /* Find the previous statement with a web page */
+            j = 0;
+            for (q = stmt - 1; q >= 1; q--) {
+              if (g_Statement[q].type == (char)p_ ||
+                  g_Statement[q].type == (char)a_ ) {
+                j = q;
+                break;
+              }
+            }
+            /* 13-Dec-2018 nm This isn't used anywhere yet.  But fix error
+               in current label and also identify previous, current, next */
+            if (j) print2("<!-- For script: previous = %s -->\n",
+                g_Statement[j].labelName);
+            /* Current statement */
+            print2("<!-- For script: current = %s -->\n",
+                g_Statement[stmt].labelName);
+            /* Find the next statement with a web page */
+            j = 0;
+            for (q = stmt + 1; q <= g_statements; q++) {
+              if (g_Statement[q].type == (char)p_ ||
+                  g_Statement[q].type == (char)a_ ) {
+                j = q;
+                break;
+              }
+            }
+            if (j) print2("<!-- For script: next = %s -->\n",
+                g_Statement[j].labelName);
+
+            g_outputToString = 0;
+            fprintf(list2_fp, "%s", g_printString);
+            free_vstring(g_printString);
+
+          }
+        } /* Next stmt - statement number */
+        /* Decrement date */
+        if (k > 1) {
+          k--; /* Decrement day */
+        } else {
+          k = 31; /* Non-existent day 31's will never match, which is OK */
+          if (l > 1) {
+            l--; /* Decrement month */
+          } else {
+            l = 12; /* Dec */
+            m --; /* Decrement year */
+          }
+        }
+      } /* next while - Scan next date */
+
+
+      /* Discard the input file up to the special "<!-- #END# -->" comment */
+      while (1) {
+        if (!linput(list1_fp, NULL, &str1)) {
+          print2(
+"?Error: Could not find \"<!-- #END# -->\" line in input file \"%s\".\n",
+              g_fullArg[2]);
+          tmpFlag = 1; /* Error flag to recover input file */
+          break;
+        }
+        if (!strcmp(str1, "<!-- #END# -->")) {
+          fprintf(list2_fp, "%s\n", str1);
+          break;
+        }
+      }
+      if (tmpFlag) goto wrrecent_error;
+
+      /* Transfer the rest of the input file */
+      while (1) {
+        if (!linput(list1_fp, NULL, &str1)) {
+          break;
+        }
+
+        /* Update the date stamp at the bottom of the HTML page. */
+        /* This is just a nicety; no error check is done. */
+        if (!strcmp("This page was last updated on ", left(str1, 30))) {
+          let(&str1, cat(left(str1, 30), date(), ".", NULL));
+        }
+
+        fprintf(list2_fp, "%s\n", str1);
+      }
+
+      print2("The %ld most recent theorem(s) were written.\n", n);
+
+     wrrecent_error:
+      fclose(list1_fp);
+      fclose(list2_fp);
+      if (tmpFlag) {
+        /* Recover input files in case of error */
+        remove(g_fullArg[2]);  /* Delete output file */
+        rename(cat(g_fullArg[2], "~1", NULL), g_fullArg[2]);
+            /* Restore input file name */
+        print2("?The file \"%s\" was not modified.\n", g_fullArg[2]);
+      }
+      continue;
+    }  /* End of "WRITE RECENT_ADDITIONS" */
+
+
+    if (cmdMatches("SHOW LABELS")) {
+      linearFlag = 0;
+      if (switchPos("/ LINEAR")) linearFlag = 1;
+      if (switchPos("/ ALL")) {
+        m = 1;  /* Include $e, $f statements */
+        print2(
+"The labels that match are shown with statement number, label, and type.\n");
+      } else {
+        m = 0;  /* Show $a, $p only */
+        print2(
+"The assertions that match are shown with statement number, label, and type.\n");
+      }
+      j = 0;
+      k = 0;
+      let(&str2, ""); /* Line so far */
+#define COL 20 /* Column width */
+#define MIN_SPACE 2 /* At least this many spaces between columns */
+      for (i = 1; i <= g_statements; i++) {
+        if (!g_Statement[i].labelName[0]) continue; /* No label */
+        if (!m && g_Statement[i].type != (char)p_ &&
+            g_Statement[i].type != (char)a_) continue; /* No /ALL switch */
+        if (!matchesList(g_Statement[i].labelName, g_fullArg[2], '*', '?')) {
+          continue;
+        }
+
+        let(&str1, cat(str((double)i), " ",
+            g_Statement[i].labelName, " $", chr(g_Statement[i].type),
+            NULL));
+        if (!str2[0]) {
+          j = 0; /* # of fields on line so far */
+        }
+        k = ((long)strlen(str2) + MIN_SPACE > j * COL)
+            ? (long)strlen(str2) + MIN_SPACE : j * COL;
+                /* Position before new str1 starts */
+        if (k + (long)strlen(str1) > g_screenWidth || linearFlag) {
+          if (j == 0) {
+            /* In case of huge label, force it out anyway */
+            printLongLine(str1, "", " ");
+          } else {
+            /* Line width exceeded, postpone adding str1 */
+            print2("%s\n", str2);
+            let(&str2, str1);
+            j = 1;
+          }
+        } else {
+          /* Add new field to line */
+          if (j == 0) {
+            let(&str2, str1); /* Don't put space before 1st label on line */
+          } else {
+            let(&str2, cat(str2, space(k - (long)strlen(str2)), str1, NULL));
+          }
+          j++;
+        }
+      } /* next i */
+      if (str2[0]) {
+        print2("%s\n", str2);
+        free_vstring(str2);
+      }
+      free_vstring(str1);
+      continue;
+    }
+
+    if (cmdMatches("SHOW DISCOURAGED")) {
+      showDiscouraged();
+      continue;
+    }
+
+    if (cmdMatches("SHOW SOURCE")) {
+      /* Currently, SHOW SOURCE only handles one statement at a time,
+         so use getStatementNum().  Eventually, SHOW SOURCE may become
+         obsolete; I don't think anyone uses it. */
+      s = getStatementNum(g_fullArg[2],
+          1/*startStmt*/,
+          g_statements + 1  /*maxStmt*/,
+          1/*aAllowed*/,
+          1/*pAllowed*/,
+          1/*eAllowed*/,
+          1/*fAllowed*/,
+          0/*efOnlyForMaxStmt*/,
+          1/*uniqueFlag*/);
+      if (s == -1) {
+        continue; /* Error msg was provided */
+      }
+      g_showStatement = s; /* Update for future defaults */
+
+      free_vstring(str1);
+      str1 = outputStatement(g_showStatement, /* cleanFlag */
+          0 /* reformatFlag */);
+      let(&str1,edit(str1,128)); /* Trim trailing spaces */
+      if (str1[strlen(str1)-1] == '\n') let(&str1, left(str1,
+          (long)strlen(str1) - 1));
+      printLongLine(str1, "", "");
+      free_vstring(str1); /* Deallocate vstring */
+      continue;
+    } /* if (cmdMatches("SHOW SOURCE")) */
+
+
+    if (cmdMatches("SHOW STATEMENT") && (
+        switchPos("/ HTML")
+        || switchPos("/ BRIEF_HTML")
+        || switchPos("/ ALT_HTML")
+        || switchPos("/ BRIEF_ALT_HTML"))) {
+      /* Special processing for the / HTML qualifiers - for each matching
+         statement, a .html file is opened, the statement is output,
+         and depending on statement type a proof or other information
+         is output. */
+
+      noVersioning = (switchPos("/ NO_VERSIONING") != 0);
+      i = 5;  /* # arguments with only / HTML or / ALT_HTML */
+      if (noVersioning) i = i + 2;
+      if (switchPos("/ TIME")) i = i + 2;
+      if (g_rawArgs != i) {
+        printLongLine(cat("?The HTML qualifiers may not be combined with",
+            " others except / NO_VERSIONING and / TIME.\n", NULL), "  ", " ");
+        continue;
+      }
+
+      printTime = 0;
+      if (switchPos("/ TIME") != 0) {
+        printTime = 1;
+      }
+
+      g_htmlFlag = 1;
+
+      if (switchPos("/ BRIEF_HTML") || switchPos("/ BRIEF_ALT_HTML")) {
+        if (strcmp(g_fullArg[2], "*")) {
+          print2(
+              "?For BRIEF_HTML or BRIEF_ALT_HTML, the label must be \"*\"\n");
+          goto htmlDone;
+        }
+        g_briefHtmlFlag = 1;
+      } else {
+        g_briefHtmlFlag = 0;
+      }
+
+      if (switchPos("/ ALT_HTML") || switchPos("/ BRIEF_ALT_HTML")) {
+        g_altHtmlFlag = 1;
+      } else {
+        g_altHtmlFlag = 0;
+      }
+
+      q = 0;
+
+      /* Special feature:  if the match statement starts with "*", we
+         will also output mmascii.html, mmtheoremsall.html, and
+         mmdefinitions.html.  So, with
+                 SHOW STATEMENT * / HTML
+         these will be output plus all statements; with
+                 SHOW STATEMENT *! / HTML
+         these will be output with no statements (since ! is illegal in a
+         statement label); with
+                 SHOW STATEMENT ?* / HTML
+         all statements will be output, but without mmascii.html etc. */
+      if (((char *)(g_fullArg[2]))[0] == '*' || g_briefHtmlFlag) {
+        s = -2; /* -2 is for ASCII table; -1 is for theorems;
+                    0 is for definitions */
+      } else {
+        s = 1;
+      }
+
+      for (s = s + 0; s <= g_statements; s++) {
+
+        if (s > 0 && g_briefHtmlFlag) break; /* Only do summaries */
+
+        /*
+           s = -2:  mmascii.html
+           s = -1:  mmtheoremsall.html (used to be mmtheorems.html)
+           s = 0:   mmdefinitions.html
+           s > 0:   normal statement
+        */
+
+        if (s > 0) {
+          if (!g_Statement[s].labelName[0]) continue; /* No label */
+          if (!matchesList(g_Statement[s].labelName, g_fullArg[2], '*', '?'))
+            continue;
+          if (g_Statement[s].type != (char)a_
+              && g_Statement[s].type != (char)p_) continue;
+        }
+
+        q = 1; /* Flag that at least one matching statement was found */
+
+        if (s > 0) {
+          g_showStatement = s;
+        } else {
+          /* We set it to 1 here so we will output the Metamath Proof
+             Explorer and not the Hilbert Space Explorer header for
+             definitions and theorems lists, when g_showStatement is
+             compared to g_extHtmlStmt in printTexHeader in mmwtex.c */
+          g_showStatement = 1;
+        }
+
+
+        /*** Open the html file ***/
+        g_htmlFlag = 1;
+        /* Open the html output file */
+        switch (s) {
+          case -2:
+            let(&g_texFileName, "mmascii.html");
+            break;
+          case -1:
+            let(&g_texFileName, "mmtheoremsall.html");
+            break;
+          case 0:
+            let(&g_texFileName, "mmdefinitions.html");
+            break;
+          default:
+            let(&g_texFileName, cat(g_Statement[g_showStatement].labelName, ".html",
+                NULL));
+        }
+        print2("Creating HTML file \"%s\"...\n", g_texFileName);
+        g_texFilePtr = fSafeOpen(g_texFileName, "w", noVersioning);
+        if (!g_texFilePtr) goto htmlDone; /* Couldn't open it (err msg was
+            provided) */
+        g_texFileOpenFlag = 1;
+        printTexHeader((s > 0) ? 1 : 0 /*texHeaderFlag*/);
+        if (!g_texDefsRead) {
+          /* If there was an error reading the $t xx.mm statement,
+             g_texDefsRead won't be set, and we should close out file and skip
+             further processing.  Otherwise we will be attempting to process
+             uninitialized htmldef arrays and such. */
+          print2("?HTML generation was aborted due to the error above.\n");
+          s = g_statements + 1; /* To force loop to exit */
+          goto ABORT_S; /* Go to end of loop where file is closed out */
+        }
+
+        if (s <= 0) {
+          g_outputToString = 1;
+          if (s == -2) {
+            printLongLine(cat("<CENTER><FONT COLOR=", GREEN_TITLE_COLOR,
+                "><B>",
+                "Symbol to ASCII Correspondence for Text-Only Browsers",
+                " (in order of appearance in $c and $v statements",
+                     " in the database)",
+                "</B></FONT></CENTER><P>", NULL), "", "\"");
+          }
+          /* 13-Oct-2006 nm todo - </CENTER> still appears - where is it? */
+          if (!g_briefHtmlFlag) print2("<CENTER>\n");
+          print2("<TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
+              MINT_BACKGROUND_COLOR);
+          /* For bobby.cast.org approval */
+          switch (s) {
+            case -2:
+              print2("SUMMARY=\"Symbol to ASCII correspondences\">\n");
+              break;
+            case -1:
+              print2("SUMMARY=\"List of theorems\">\n");
+              break;
+            case 0:
+              print2("SUMMARY=\"List of syntax, axioms and definitions\">\n");
+              break;
+          }
+          switch (s) {
+            case -2:
+              print2("<TR ALIGN=LEFT><TD><B>\n");
+              break;
+            case -1:
+              print2(
+         "<CAPTION><B>List of Theorems</B></CAPTION><TR ALIGN=LEFT><TD><B>\n");
+              break;
+            case 0:
+              printLongLine(cat(
+                  /* (in case |- suppressed) */
+                  "<CAPTION><B>List of Syntax, ",
+                  "Axioms (<FONT COLOR=", GREEN_TITLE_COLOR, ">ax-</FONT>) and",
+                  " Definitions (<FONT COLOR=", GREEN_TITLE_COLOR,
+                  ">df-</FONT>)", "</B></CAPTION><TR ALIGN=LEFT><TD><B>",
+                  NULL), "", "\"");
+              break;
+          }
+          switch (s) {
+            case -2:
+              print2("Symbol</B></TD><TD><B>ASCII\n");
+              break;
+            case -1:
+              print2(
+                  "Ref</B></TD><TD><B>Description\n");
+              break;
+            case 0:
+              printLongLine(cat(
+                  "Ref</B></TD><TD><B>",
+                "Expression (see link for any distinct variable requirements)",
+                NULL), "", "\"");
+              break;
+          }
+          print2("</B></TD></TR>\n");
+          m = 0; /* Statement number map */
+          let(&str3, ""); /* For storing ASCII token list in s=-2 mode */
+          let(&bgcolor, MINT_BACKGROUND_COLOR);
+          for (i = 1; i <= g_statements; i++) {
+            if (s != -2 && (i == g_extHtmlStmt || i == g_mathboxStmt)) {
+              /* Print a row that identifies the start of the extended
+                 database (e.g. Hilbert Space Explorer) or the user
+                 sandboxes */
+              if (i == g_extHtmlStmt) {
+                let(&bgcolor, PURPLISH_BIBLIO_COLOR);
+              } else {
+                let(&bgcolor, SANDBOX_COLOR);
+              }
+              printLongLine(cat("<TR BGCOLOR=", bgcolor,
+                  "><TD COLSPAN=2 ALIGN=CENTER><A NAME=\"startext\"></A>",
+                  "The list of syntax, axioms (ax-) and definitions (df-) for",
+                  " the <B><FONT COLOR=", GREEN_TITLE_COLOR, ">",
+                  (i == g_extHtmlStmt) ?
+                    g_extHtmlTitle :
+                    "User Mathboxes",
+                  "</FONT></B> starts here</TD></TR>", NULL), "", "\"");
+            }
+
+            if (g_Statement[i].type == (char)p_ ||
+                g_Statement[i].type == (char)a_ ) m++;
+            if ((s == -1 && g_Statement[i].type != (char)p_)
+                || (s == 0 && g_Statement[i].type != (char)a_)
+                || (s == -2 && g_Statement[i].type != (char)c_
+                    && g_Statement[i].type != (char)v_)
+                ) continue;
+            switch (s) {
+              case -2:
+                /* Print symbol to ASCII table entry */
+                /* It's a $c or $v statement, so each token generates a
+                   table row */
+                for (j = 0; j < g_Statement[i].mathStringLen; j++) {
+                  let(&str1, g_MathToken[(g_Statement[i].mathString)[j]].tokenName);
+                  /* Output each token only once in case of multiple decl. */
+                  if (!instr(1, str3, cat(" ", str1, " ", NULL))) {
+                    let(&str3, cat(str3, " ", str1, " ", NULL));
+                    free_vstring(str2);
+                    str2 = tokenToTex(g_MathToken[(g_Statement[i].mathString)[j]
+                        ].tokenName, i/*stmt# for error msgs*/);
+                    /* Skip any tokens (such as |- in QL Explorer) that may be suppressed */
+                    if (!str2[0]) continue;
+                    /* Convert special characters to HTML entities */
+                    for (k = 0; k < (signed)(strlen(str1)); k++) {
+                      if (str1[k] == '&') {
+                        let(&str1, cat(left(str1, k), "&amp;",
+                            right(str1, k + 2), NULL));
+                        k = k + 4;
+                      }
+                      if (str1[k] == '<') {
+                        let(&str1, cat(left(str1, k), "&lt;",
+                            right(str1, k + 2), NULL));
+                        k = k + 3;
+                      }
+                      if (str1[k] == '>') {
+                        let(&str1, cat(left(str1, k), "&gt;",
+                            right(str1, k + 2), NULL));
+                        k = k + 3;
+                      }
+                    } /* next k */
+                    printLongLine(cat("<TR ALIGN=LEFT><TD>",
+                        (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
+                        str2,
+                        (g_altHtmlFlag ? "</SPAN>" : ""),
+                        "&nbsp;", /* This will prevent a
+                                     -4px shifted image from overlapping the
+                                     lower border of the table cell */
+                        "</TD><TD><TT>",
+                        str1,
+                        "</TT></TD></TR>", NULL), "", "\"");
+                  }
+                } /* next j */
+                /* Close out the string now to prevent memory overflow */
+                fprintf(g_texFilePtr, "%s", g_printString);
+                free_vstring(g_printString);
+                break;
+              case -1: /* Falls through to next case */
+              case 0:
+                free_vstring(str1);
+                if (s == 0 || g_briefHtmlFlag) {
+                  free_vstring(str1);
+                  /* Get HTML hypotheses => assertion */
+                  str1 = getTexOrHtmlHypAndAssertion(i);
+                  let(&str1, cat(str1, "</TD></TR>", NULL));
+                }
+
+                if (g_briefHtmlFlag) {
+                  /* Get page number in mmtheorems*.html of WRITE THEOREMS */
+                  k = ((g_Statement[i].pinkNumber - 1) /
+                      THEOREMS_PER_PAGE) + 1; /* Page # */
+                  let(&str2, cat("<TR ALIGN=LEFT><TD ALIGN=LEFT>",
+                      /*"<FONT COLOR=\"#FA8072\">",*/
+                      "<FONT COLOR=ORANGE>",
+                      str((double)(g_Statement[i].pinkNumber)), "</FONT> ",
+                      "<FONT COLOR=GREEN><A HREF=\"",
+                      "mmtheorems", (k == 1) ? "" : str((double)k), ".html#",
+                      g_Statement[i].labelName,
+                      "\">", g_Statement[i].labelName,
+                      "</A></FONT>", NULL));
+                  let(&str1, cat(str2, " ", str1, NULL));
+                } else {
+                  /* Get little pink (or rainbow-colored) number */
+                  free_vstring(str4);
+                  str4 = pinkHTML(i);
+                  let(&str2, cat("<TR BGCOLOR=", bgcolor,
+                      " ALIGN=LEFT><TD><A HREF=\"",
+                      g_Statement[i].labelName,
+                      ".html\">", g_Statement[i].labelName,
+                      "</A>", str4, NULL));
+                  let(&str1, cat(str2, "</TD><TD>", str1, NULL));
+                }
+
+                print2("\n");  /* New line for HTML source readability */
+                printLongLine(str1, "", "\"");
+
+                if (s == 0 || g_briefHtmlFlag) {
+                              /* Set s == 0 here for Web site version,
+                                 s == s for symbol version of theorem list */
+                  /* The below has been replaced by
+                     getTexOrHtmlHypAndAssertion(i) above. */
+                  /*printTexLongMath(g_Statement[i].mathString, "", "", 0, 0);*/
+                  /*g_outputToString = 1;*/ /* Is reset by printTexLongMath */
+                } else {
+                  /* Theorems are listed w/ description; otherwise file is too
+                     big for convenience */
+                  free_vstring(str1);
+                  str1 = getDescription(i);
+                  if (strlen(str1) > 29)
+                    let(&str1, cat(left(str1, 26), "...", NULL));
+                  let(&str1, cat(str1, "</TD></TR>", NULL));
+                  printLongLine(str1, "", "\"");
+                }
+                /* Close out the string now to prevent overflow */
+                fprintf(g_texFilePtr, "%s", g_printString);
+                free_vstring(g_printString);
+                break;
+            } /* end switch */
+          } /* next i (statement number) */
+          g_outputToString = 0;  /* closing will write out the string */
+          free_vstring(bgcolor); /* Deallocate (to improve fragmentation) */
+
+        } else { /* s > 0 */
+
+          if (printTime == 1) {
+            getRunTime(&timeIncr);  /* This call just resets the time */
+          }
+
+          /*** Output the html statement body ***/
+          typeStatement(g_showStatement,
+              0 /*briefFlag*/,
+              0 /*commentOnlyFlag*/,
+              1 /*texFlag*/,   /* means latex or html */
+              1 /*g_htmlFlag*/);
+
+          if (printTime == 1) {
+            getRunTime(&timeIncr);
+            print2("SHOW STATEMENT run time = %6.2f sec for \"%s\"\n",
+                timeIncr,
+                g_texFileName);
+          }
+
+        } /* if s <= 0 */
+
+       ABORT_S:
+        /*** Close the html file ***/
+        printTexTrailer(1 /*texHeaderFlag*/);
+        fclose(g_texFilePtr);
+        g_texFileOpenFlag = 0;
+        free_vstring(g_texFileName);
+
+      } /* next s */
+
+      if (!q) {
+        /* No matching statement was found */
+        printLongLine(cat("?There is no statement whose label matches \"",
+            g_fullArg[2], "\".  ",
+            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
+        continue;
+      }
+
+      /* Complete the command processing to bypass normal SHOW STATEMENT
+         (non-html) below. */
+     htmlDone:
+      continue;
+    } /* if (cmdMatches("SHOW STATEMENT") && switchPos("/ HTML")...) */
+
+
+    /* Write mnemosyne.txt */
+    if (cmdMatches("SHOW STATEMENT") && switchPos("/ MNEMONICS")) {
+      g_htmlFlag = 1;     /* Use HTML, not TeX section */
+      g_altHtmlFlag = 1;  /* Use Unicode, not GIF */
+      /* readTexDefs() rereads based on changes to g_htmlFlag, g_altHtmlFlag */
+      if (2/*error*/ == readTexDefs(0 /* 1 = check errors only */,
+          1 /* 1 = GIF file existence check */  )) {
+        continue; /* An error occurred */
+      }
+
+      let(&g_texFileName, "mnemosyne.txt");
+      g_texFilePtr = fSafeOpen(g_texFileName, "w", 0/*noVersioningFlag*/);
+      if (!g_texFilePtr) {
+        /* Couldn't open file; error message was provided by fSafeOpen */
+        continue;
+      }
+      print2("Creating Mnemosyne file \"%s\"...\n", g_texFileName);
+
+      for (s = 1; s <= g_statements; s++) {
+        g_showStatement = s;
+
+        if (!g_Statement[s].labelName[0]) continue; /* No label */
+        if (!matchesList(g_Statement[s].labelName, g_fullArg[2], '*', '?'))
+          continue;
+        if (g_Statement[s].type != (char)a_
+            && g_Statement[s].type != (char)p_)
+          continue;
+
+        let(&str1, cat("<CENTER><B><FONT SIZE=\"+1\">",
+            " <FONT COLOR=", GREEN_TITLE_COLOR,
+            " SIZE = \"+3\">", g_Statement[g_showStatement].labelName,
+            "</FONT></FONT></B>", "</CENTER>", NULL));
+        fprintf(g_texFilePtr, "%s", str1);
+
+        let(&str1, cat("<TABLE>",NULL));
+        fprintf(g_texFilePtr, "%s", str1);
+
+        j = nmbrLen(g_Statement[g_showStatement].reqHypList);
+        for (i = 0; i < j; i++) {
+          k = g_Statement[g_showStatement].reqHypList[i];
+          if (g_Statement[k].type != (char)e_
+              && !(subType == SYNTAX
+              && g_Statement[k].type == (char)f_))
+            continue;
+
+          let(&str1, cat("<TR ALIGN=LEFT><TD><FONT SIZE=\"+2\">",
+              g_Statement[k].labelName, "</FONT></TD><TD><FONT SIZE=\"+2\">",
+              NULL));
+          fprintf(g_texFilePtr, "%s", str1);
+
+          /* Print hypothesis */
+          free_vstring(str1); /* Free any previous allocation to str1 */
+          /* getTexLongMath does not return a temporary allocation; must
+             assign str1 directly, not with let().  It will be deallocated
+             with the next let(&str1,...). */
+          str1 = getTexLongMath(g_Statement[k].mathString,
+              k/*stmt# for err msgs*/);
+          fprintf(g_texFilePtr, "%s</FONT></TD>", str1);
+        }
+
+
+        let(&str1, "</TABLE>");
+        fprintf(g_texFilePtr, "%s", str1);
+
+        let(&str1, "<BR><FONT SIZE=\"+2\">What is the conclusion?</FONT>");
+        fprintf(g_texFilePtr, "%s\n", str1);
+
+        let(&str1, "<FONT SIZE=\"+3\">");
+        fprintf(g_texFilePtr, "%s", str1);
+
+        free_vstring(str1); /* Free any previous allocation to str1 */
+        /* getTexLongMath does not return a temporary allocation */
+        str1 = getTexLongMath(g_Statement[s].mathString, s);
+        fprintf(g_texFilePtr, "%s", str1);
+
+        let(&str1, "</FONT>");
+        fprintf(g_texFilePtr, "%s\n",str1);
+      } /*  for(s=1;s<g_statements;++s) */
+
+      fclose(g_texFilePtr);
+      g_texFileOpenFlag = 0;
+      free_vstring(g_texFileName);
+      free_vstring(str1);
+      free_vstring(str2);
+
+      continue;
+    } /* if (cmdMatches("SHOW STATEMENT") && switchPos("/ MNEMONICS")) */
+
+    /* If we get here, the user did not specify one of the qualifiers /HTML,
+       /BRIEF_HTML, /ALT_HTML, or /BRIEF_ALT_HTML */
+    if (cmdMatches("SHOW STATEMENT") && !switchPos("/ HTML")) {
+
+      texFlag = 0;
+      if (switchPos("/ TEX") || switchPos("/ OLD_TEX")
+          || switchPos("/ HTML"))
+        texFlag = 1;
+
+      briefFlag = 1;
+      g_oldTexFlag = 0;
+      if (switchPos("/ TEX")) briefFlag = 0;
+      if (switchPos("/ OLD_TEX")) briefFlag = 0;
+      if (switchPos("/ OLD_TEX")) g_oldTexFlag = 1;
+      if (switchPos("/ FULL")) briefFlag = 0;
+
+      commentOnlyFlag = 0;
+      if (switchPos("/ COMMENT")) {
+        commentOnlyFlag = 1;
+        briefFlag = 1;
+      }
+
+
+      if (switchPos("/ FULL")) {
+        briefFlag = 0;
+        commentOnlyFlag = 0;
+      }
+
+      if (texFlag) {
+        if (!g_texFileOpenFlag) {
+          print2(
+      "?You have not opened a %s file.  Use the OPEN TEX command first.\n",
+              g_htmlFlag ? "HTML" : "LaTeX");
+          continue;
+        }
+      }
+
+      if (texFlag && (commentOnlyFlag || briefFlag)) {
+        print2("?TEX qualifier should be used alone\n");
+        continue;
+      }
+
+      q = 0;
+
+      for (s = 1; s <= g_statements; s++) {
+        if (!g_Statement[s].labelName[0]) continue; /* No label */
+        /* We are not in MM-PA mode, or the statement isn't "=" */
+        if (!matchesList(g_Statement[s].labelName, g_fullArg[2], '*', '?'))
+          continue;
+
+        if (briefFlag || commentOnlyFlag || texFlag) {
+          /* For brief or comment qualifier, if label has wildcards,
+             show only $p and $a's */
+          if (g_Statement[s].type != (char)p_
+              && g_Statement[s].type != (char)a_ && (instr(1, g_fullArg[2], "*")
+                  || instr(1, g_fullArg[2], "?")))
+            continue;
+        }
+
+        if (q && !texFlag) {
+          if (!print2("%s\n", string(g_screenWidth, '-'))) /* Put line between
+                                                   statements */
+            break; /* Break for speedup if user quit */
+        }
+        if (texFlag) print2("Outputting statement \"%s\"...\n",
+            g_Statement[s].labelName);
+
+        q = 1; /* Flag that at least one matching statement was found */
+
+        g_showStatement = s;
+
+
+        typeStatement(g_showStatement,
+            briefFlag,
+            commentOnlyFlag,
+            texFlag,
+            g_htmlFlag);
+      } /* Next s */
+
+      if (!q) {
+        /* No matching statement was found */
+        printLongLine(cat("?There is no statement whose label matches \"",
+            g_fullArg[2], "\".  ",
+            "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
+        continue;
+      }
+
+      if (texFlag && !g_htmlFlag) {
+        print2("The LaTeX source was written to \"%s\".\n", g_texFileName);
+        g_oldTexFlag = 0;
+      }
+      continue;
+    } /* (cmdMatches("SHOW STATEMENT") && !switchPos("/ HTML")) */
+
+    if (cmdMatches("SHOW SETTINGS")) {
+      print2("Metamath settings on %s at %s:\n",date(),time_());
+      if (g_commandEcho) {
+        print2("(SET ECHO...) Command ECHO is ON.\n");
+      } else {
+        print2("(SET ECHO...) Command ECHO is OFF.\n");
+      }
+      if (defaultScrollMode == 1) {
+        print2("(SET SCROLL...) SCROLLing mode is PROMPTED.\n");
+      } else {
+        print2("(SET SCROLL...) SCROLLing mode is CONTINUOUS.\n");
+      }
+      print2("(SET WIDTH...) Screen display WIDTH is %ld.\n", g_screenWidth);
+      print2("(SET HEIGHT...) Screen display HEIGHT is %ld.\n",
+          g_screenHeight + 1);
+      if (g_sourceHasBeenRead == 1) {
+        print2("(READ...) %ld statements have been read from \"%s\".\n",
+            g_statements, g_input_fn);
+      } else {
+        print2("(READ...) No source file has been read in yet.\n");
+      }
+      print2("(SET ROOT_DIRECTORY...) Root directory is \"%s\".\n",
+          g_rootDirectory);
+      print2(
+     "(SET DISCOURAGEMENT...) Blocking based on \"discouraged\" tags is %s.\n",
+          (g_globalDiscouragement ? "ON" : "OFF"));
+      print2(
+     "(SET CONTRIBUTOR...) The current contributor is \"%s\".\n",
+          g_contributorName);
+      if (g_PFASmode) {
+        print2("(PROVE...) The statement you are proving is \"%s\".\n",
+            g_Statement[g_proveStatement].labelName);
+      }
+      print2("(SET UNDO...) The maximum number of UNDOs is %ld.\n",
+          processUndoStack(NULL, PUS_GET_SIZE, "", 0));
+      print2(
+    "(SET UNIFICATION_TIMEOUT...) The unification timeout parameter is %ld.\n",
+          g_userMaxUnifTrials);
+      print2(
+    "(SET SEARCH_LIMIT...) The SEARCH_LIMIT for the IMPROVE command is %ld.\n",
+          g_userMaxProveFloat);
+      if (g_minSubstLen) {
+        print2(
+     "(SET EMPTY_SUBSTITUTION...) EMPTY_SUBSTITUTION is not allowed (OFF).\n");
+      } else {
+        print2(
+          "(SET EMPTY_SUBSTITUTION...) EMPTY_SUBSTITUTION is allowed (ON).\n");
+      }
+      if (g_hentyFilter) {
+        print2(
+              "(SET JEREMY_HENTY_FILTER...) The Henty filter is turned ON.\n");
+      } else {
+        print2(
+             "(SET JEREMY_HENTY_FILTER...) The Henty filter is turned OFF.\n");
+      }
+      if (g_showStatement) {
+        print2("(SHOW...) The default statement for SHOW commands is \"%s\".\n",
+            g_Statement[g_showStatement].labelName);
+      }
+      if (g_logFileOpenFlag) {
+        print2("(OPEN LOG...) The log file \"%s\" is open.\n", g_logFileName);
+      } else {
+        print2("(OPEN LOG...) No log file is currently open.\n");
+      }
+      if (g_texFileOpenFlag) {
+        print2("The %s file \"%s\" is open.\n", g_htmlFlag ? "HTML" : "LaTeX",
+            g_texFileName);
+      }
+      print2(
+  "(SHOW STATEMENT.../[TEX,HTML,ALT_HTML]) Current output mode is %s.\n",
+          g_htmlFlag
+             ? (g_altHtmlFlag ? "ALT_HTML" : "HTML")
+             : "TEX (LaTeX)");
+      print2("The program is compiled for a %ld-bit CPU.\n",
+          (long)(8 * sizeof(long)));
+      print2(
+ "sizeof(short)=%ld, sizeof(int)=%ld, sizeof(long)=%ld, sizeof(size_t)=%ld.\n",
+        (long)(sizeof(short)),
+        (long)(sizeof(int)), (long)(sizeof(long)), (long)(sizeof(size_t)));
+      continue;
+    }
+
+    if (cmdMatches("SHOW MEMORY")) {
+      j = 32000000; /* The largest we'd ever look for */
+      i = getFreeSpace(j);
+      if (i > j-3) {
+        print2("At least %ld bytes of memory are free.\n",j);
+      } else {
+        print2("%ld bytes of memory are free.\n",i);
+      }
+      continue;
+    }
+
+    if (cmdMatches("SHOW ELAPSED_TIME")) {
+      timeTotal = getRunTime(&timeIncr);
+      print2(
+      "Time since last SHOW ELAPSED_TIME command = %6.2f s; total = %6.2f s\n",
+          timeIncr, timeTotal);
+      continue;
+    } /* if (cmdMatches("SHOW ELAPSED_TIME")) */
+
+
+    if (cmdMatches("SHOW TRACE_BACK")) {
+      essentialFlag = 0;
+      axiomFlag = 0;
+      endIndent = 0;
+      i = switchPos("/ ESSENTIAL");
+      if (i) essentialFlag = 1; /* Limit trace to essential steps only */
+      i = switchPos("/ ALL");
+      if (i) essentialFlag = 0;
+      i = switchPos("/ AXIOMS");
+      if (i) axiomFlag = 1; /* Limit trace printout to axioms */
+      i = switchPos("/ DEPTH"); /* Limit depth of printout */
+      if (i) endIndent = (long)val(g_fullArg[i + 1]);
+
+      i = switchPos("/ COUNT_STEPS");
+      countStepsFlag = (i != 0 ? 1 : 0);
+      i = switchPos("/ TREE");
+      treeFlag = (i != 0 ? 1 : 0);
+      i = switchPos("/ MATCH");
+      matchFlag = (i != 0 ? 1 : 0);
+      if (matchFlag) {
+        let(&matchList, g_fullArg[i + 1]);
+      } else {
+        let(&matchList, "");
+      }
+      i = switchPos("/ TO");
+      if (i != 0) {
+        let(&traceToList, g_fullArg[i + 1]);
+      } else {
+        let(&traceToList, "");
+      }
+      if (treeFlag) {
+        if (axiomFlag) {
+          print2(
+              "(Note:  The AXIOMS switch is ignored in TREE mode.)\n");
+        }
+        if (countStepsFlag) {
+          print2(
+              "(Note:  The COUNT_STEPS switch is ignored in TREE mode.)\n");
+        }
+        if (matchFlag) {
+          print2(
+  "(Note: The MATCH list is ignored in TREE mode.)\n");
+        }
+      } else {
+        if (endIndent != 0) {
+          print2(
+  "(Note:  The DEPTH is ignored if the TREE switch is not used.)\n");
+        }
+        if (countStepsFlag) {
+          if (matchFlag) {
+            print2(
+  "(Note: The MATCH list is ignored in COUNT_STEPS mode.)\n");
+          }
+        }
+      }
+
+      g_showStatement = 0;
+      for (i = 1; i <= g_statements; i++) {
+        if (g_Statement[i].type != (char)p_)
+          continue; /* Not a $p statement; skip it */
+        /* Wildcard matching */
+        if (!matchesList(g_Statement[i].labelName, g_fullArg[2], '*', '?'))
+          continue;
+
+        g_showStatement = i;
+
+        if (treeFlag) {
+          traceProofTree(g_showStatement, essentialFlag, endIndent);
+        } else {
+          if (countStepsFlag) {
+            countSteps(g_showStatement, essentialFlag);
+          } else {
+            traceProof(g_showStatement,
+                essentialFlag,
+                axiomFlag,
+                matchList,
+                traceToList,
+                0 /* testOnlyFlag */);
+          }
+        }
+
+      } /* next i */
+      if (g_showStatement == 0) {
+        printLongLine(cat("?There are no $p labels matching \"",
+            g_fullArg[2], "\".  ",
+            "See HELP SHOW TRACE_BACK for matching rules.", NULL), "", " ");
+      }
+
+      free_vstring(matchList); /* Deallocate memory */
+      free_vstring(traceToList); /* Deallocate memory */
+      continue;
+    } /* if (cmdMatches("SHOW TRACE_BACK")) */
+
+
+    if (cmdMatches("SHOW USAGE")) {
+
+      if (switchPos("/ ALL")) {
+        m = 1;  /* Always include $e, $f statements */
+      } else {
+        m = 0;  /* If wildcards are used, show $a, $p only */
+      }
+
+      g_showStatement = 0;
+      for (i = 1; i <= g_statements; i++) {
+        if (!g_Statement[i].labelName[0]) continue; /* No label */
+        if (!m && g_Statement[i].type != (char)p_ &&
+            g_Statement[i].type != (char)a_
+            /* A wildcard-free user-specified statement is always matched even
+               if it's a $e, i.e. it overrides omission of / ALL */
+            && (instr(1, g_fullArg[2], "*")
+              || instr(1, g_fullArg[2], "?")))
+          continue; /* No /ALL switch and wildcard and not $p, $a */
+        /* Wildcard matching */
+        if (!matchesList(g_Statement[i].labelName, g_fullArg[2], '*', '?'))
+          continue;
+
+        g_showStatement = i;
+        recursiveFlag = 0;
+        j = switchPos("/ RECURSIVE");
+        if (j) recursiveFlag = 1; /* Recursive (indirect) usage */
+        j = switchPos("/ DIRECT");
+        if (j) recursiveFlag = 0; /* Direct references only */
+
+        free_vstring(str1);
+        str1 = traceUsage(g_showStatement,
+            recursiveFlag,
+            0 /* cutoffStmt */);
+
+        /* str1[0] will be 'Y' or 'N' depending on whether there are any
+           statements.  str1[i] will be 'Y' or 'N' depending on whether
+           g_Statement[i] uses g_showStatement. */
+        /* Count the number of statements k = # of 'Y' */
+        k = 0;
+        if (str1[0] == 'Y') {
+          /* There is at least one statement using g_showStatement */
+          for (j = g_showStatement + 1; j <= g_statements; j++) {
+            if (str1[j] == 'Y') {
+              k++;
+            } else {
+              if (str1[j] != 'N') bug(1124); /* Must be 'Y' or 'N' */
+            }
+          }
+        } else {
+          if (str1[0] != 'N') bug(1125); /* Must be 'Y' or 'N' */
+        }
+
+        if (k == 0) {
+          printLongLine(cat("Statement \"",
+              g_Statement[g_showStatement].labelName,
+              "\" is not referenced in the proof of any statement.", NULL),
+              "", " ");
+        } else {
+          if (recursiveFlag) {
+            let(&str2, "\" directly or indirectly affects");
+          } else {
+            let(&str2, "\" is directly referenced in");
+          }
+          if (k == 1) {
+            printLongLine(cat("Statement \"",
+                g_Statement[g_showStatement].labelName,
+                str2, " the proof of ",
+                str((double)k), " statement:", NULL), "", " ");
+          } else {
+            printLongLine(cat("Statement \"",
+                g_Statement[g_showStatement].labelName,
+                str2, " the proofs of ",
+                str((double)k), " statements:", NULL), "", " ");
+          }
+        }
+
+        if (k != 0) {
+          let(&str3, " "); /* Line buffer */
+          for (j = g_showStatement + 1; j <= g_statements; j++) {
+            if (str1[j] == 'Y') {
+              /* Since the output list could be huge, don't build giant
+                 string (very slow) but output it line by line */
+              if ((long)strlen(str3) + 1 +
+                  (long)strlen(g_Statement[j].labelName) > g_screenWidth) {
+                /* Output and reset the line buffer */
+                print2("%s\n", str3);
+                let(&str3, " ");
+              }
+              let(&str3, cat(str3, " ", g_Statement[j].labelName, NULL));
+            }
+          }
+          if (strlen(str3) > 1) print2("%s\n", str3);
+          free_vstring(str3);
+        } else {
+          print2("  (None)\n");
+        } /* if (k != 0) */
+      } /* next i (statement matching wildcard list) */
+
+      if (g_showStatement == 0) {
+        printLongLine(cat("?There are no labels matching \"",
+            g_fullArg[2], "\".  ",
+            "See HELP SHOW USAGE for matching rules.", NULL), "", " ");
+      }
+      continue;
+    } /* if cmdMatches("SHOW USAGE") */
+
+
+    if (cmdMatches("SHOW PROOF")
+        || cmdMatches("SHOW NEW_PROOF")
+        || cmdMatches("SAVE PROOF")
+        || cmdMatches("SAVE NEW_PROOF")
+        || cmdMatches("MIDI")) {
+      if (switchPos("/ HTML")) {
+        print2("?HTML qualifier is obsolete - use SHOW STATEMENT * / HTML\n");
+        continue;
+      }
+
+      if (cmdMatches("SAVE NEW_PROOF")
+          && getMarkupFlag(g_proveStatement, PROOF_DISCOURAGED)) {
+        if (switchPos("/ OVERRIDE") == 0 && g_globalDiscouragement == 1) {
+          /* print2("\n"); */ /* Enable for more emphasis */
+          print2(
+">>> ?Error: Attempt to overwrite a proof whose modification is discouraged.\n");
+          print2(
+   ">>> Use SAVE NEW_PROOF ... / OVERRIDE if you really want to do this.\n");
+          /* print2("\n"); */ /* Enable for more emphasis */
+          continue;
+        } else {
+          /* print2("\n"); */ /* Enable for more emphasis */
+          print2(
+">>> ?Warning: You are overwriting a proof whose modification is discouraged.\n");
+          /* print2("\n"); */ /* Enable for more emphasis */
+        }
+      }
+
+      if (cmdMatches("SHOW PROOF") || cmdMatches("SAVE PROOF")) {
+        pipFlag = 0;
+      } else {
+        pipFlag = 1; /* Proof-in-progress (NEW_PROOF) flag */
+      }
+      if (cmdMatches("SHOW")) {
+        saveFlag = 0;
+      } else {
+        saveFlag = 1; /* The command is SAVE PROOF */
+      }
+
+      printTime = 0;
+      if (switchPos("/ TIME") != 0) {  /* / TIME legal in SAVE mode only */
+        printTime = 1;
+      }
+
+      i = switchPos("/ OLD_COMPRESSION");
+      if (i) {
+        if (!switchPos("/ COMPRESSED")) {
+          print2("?/ OLD_COMPRESSION must be accompanied by / COMPRESSED.\n");
+          continue;
+        }
+      }
+
+      i = switchPos("/ FAST");
+      if (i) {
+        if (!switchPos("/ COMPRESSED") && !switchPos("/ PACKED")) {
+          print2("?/ FAST must be accompanied by / COMPRESSED or / PACKED.\n");
+          continue;
+        }
+        fastFlag = 1;
+      } else {
+        fastFlag = 0;
+      }
+
+      if (switchPos("/ EXPLICIT")) {
+        if (switchPos("/ COMPRESSED")) {
+          print2("?/ COMPRESSED and / EXPLICIT may not be used together.\n");
+          continue;
+        } else if (switchPos("/ NORMAL")) {
+          print2("?/ NORMAL and / EXPLICIT may not be used together.\n");
+          continue;
+        }
+      }
+      if (switchPos("/ NORMAL")) {
+        if (switchPos("/ COMPRESSED")) {
+          print2("?/ NORMAL and / COMPRESSED may not be used together.\n");
+          continue;
+        }
+      }
+
+
+      /* Establish defaults for omitted qualifiers */
+      startStep = 0;
+      endStep = 0;
+      endIndent = 0;
+      essentialFlag = 1;
+      renumberFlag = 0;
+      unknownFlag = 0;
+      notUnifiedFlag = 0;
+      reverseFlag = 0;
+      detailStep = 0;
+      noIndentFlag = 0;
+      splitColumn = DEFAULT_COLUMN;
+      skipRepeatedSteps = 0;
+      texFlag = 0;
+
+      i = switchPos("/ FROM_STEP");
+      if (i) startStep = (long)val(g_fullArg[i + 1]);
+      i = switchPos("/ TO_STEP");
+      if (i) endStep = (long)val(g_fullArg[i + 1]);
+      i = switchPos("/ DEPTH");
+      if (i) endIndent = (long)val(g_fullArg[i + 1]);
+      /* ESSENTIAL is retained for downwards compatibility, but is
+         now the default, so we ignore it. */
+      /*
+      i = switchPos("/ ESSENTIAL");
+      if (i) essentialFlag = 1;
+      */
+      i = switchPos("/ ALL");
+      if (i) essentialFlag = 0;
+      if (i && switchPos("/ ESSENTIAL")) {
+        print2("?You may not specify both / ESSENTIAL and / ALL.\n");
+        continue;
+      }
+      i = switchPos("/ RENUMBER");
+      if (i) renumberFlag = 1;
+      i = switchPos("/ UNKNOWN");
+      if (i) unknownFlag = 1;
+      i = switchPos("/ NOT_UNIFIED"); /* pip mode only */
+      if (i) notUnifiedFlag = 1;
+      i = switchPos("/ REVERSE");
+      if (i) reverseFlag = 1;
+      i = switchPos("/ LEMMON");
+      if (i) noIndentFlag = 1;
+      i = switchPos("/ START_COLUMN");
+      if (i) splitColumn = (long)val(g_fullArg[i + 1]);
+      i = switchPos("/ NO_REPEATED_STEPS");
+      if (i) skipRepeatedSteps = 1;
+
+      /* If NO_REPEATED_STEPS is specified, indentation (tree) mode will be
+         misleading because a hypothesis assignment will be suppressed if the
+         same assignment occurred earlier, i.e. it is no longer a "tree". */
+      if (skipRepeatedSteps == 1 && noIndentFlag == 0) {
+        print2("?You must specify / LEMMON with / NO_REPEATED_STEPS\n");
+        continue;
+      }
+
+      i = switchPos("/ TEX") || switchPos("/ HTML")
+          || switchPos("/ OLD_TEX");
+      if (i) texFlag = 1;
+
+      g_oldTexFlag = 0;
+      if (switchPos("/ OLD_TEX")) g_oldTexFlag = 1;
+
+      if (cmdMatches("MIDI")) {
+        g_midiFlag = 1;
+        pipFlag = 0;
+        saveFlag = 0;
+        let(&labelMatch, g_fullArg[1]);
+        i = switchPos("/ PARAMETER"); /* MIDI only */
+        if (i) {
+          let(&g_midiParam, g_fullArg[i + 1]);
+        } else {
+          let(&g_midiParam, "");
+        }
+      } else {
+        g_midiFlag = 0;
+        if (!pipFlag) let(&labelMatch, g_fullArg[2]);
+      }
+
+
+      if (texFlag) {
+        if (!g_texFileOpenFlag) {
+          print2(
+     "?You have not opened a %s file.  Use the OPEN %s command first.\n",
+              g_htmlFlag ? "HTML" : "LaTeX",
+              g_htmlFlag ? "HTML" : "TEX");
+          continue;
+        }
+      }
+
+      i = switchPos("/ DETAILED_STEP"); /* non-pip mode only */
+      if (i) {
+        detailStep = (long)val(g_fullArg[i + 1]);
+        if (!detailStep) detailStep = -1; /* To use as flag; error message
+                                             will occur in showDetailStep() */
+      }
+
+/*??? Need better warnings for switch combinations that don't make sense */
+
+      /* Print a single message for "/compressed/fast" */
+      if (switchPos("/ COMPRESSED") && fastFlag
+          && !strcmp("*", labelMatch)) {
+        print2(
+            "Reformatting and saving (but not recompressing) all proofs...\n");
+      }
+
+
+      q = 0;  /* Flag that at least one matching statement was found */
+      for (stmt = 1; stmt <= g_statements; stmt++) {
+        /* If pipFlag (NEW_PROOF), we will iterate exactly once.  This
+           loop of course will be entered because there is a least one
+           statement, and at the end of the s loop we break out of it. */
+        /* If !pipFlag, get the next statement: */
+        if (!pipFlag) {
+          if (g_Statement[stmt].type != (char)p_) continue; /* Not $p */
+          if (!matchesList(g_Statement[stmt].labelName, labelMatch, '*', '?'))
+            continue;
+          g_showStatement = stmt;
+        }
+
+        q = 1; /* Flag that at least one matching statement was found */
+
+        if (detailStep) {
+          /* Show the details of just one step */
+          showDetailStep(g_showStatement, detailStep);
+          continue;
+        }
+
+        if (switchPos("/ STATEMENT_SUMMARY")) { /* non-pip mode only */
+          /* Just summarize the statements used in the proof */
+          proofStmtSumm(g_showStatement, essentialFlag, texFlag);
+          continue;
+        }
+
+        if (switchPos("/ SIZE")) { /* non-pip mode only */
+          /* Just print the size of the stored proof and continue */
+          let(&str1, space(g_Statement[g_showStatement].proofSectionLen));
+          memcpy(str1, g_Statement[g_showStatement].proofSectionPtr,
+              (size_t)(g_Statement[g_showStatement].proofSectionLen));
+          n = instr(1, str1, "$.");
+          if (n == 0) {
+            /* The original source truncates the proof before $. */
+            n = g_Statement[g_showStatement].proofSectionLen;
+          } else {
+            /* If a proof is saved, it includes the $. (Should we
+               revisit or document better how/why this is done?) */
+            n = n - 1;
+          }
+          print2("The proof source for \"%s\" has %ld characters.\n",
+              g_Statement[g_showStatement].labelName, n);
+          continue;
+        }
+
+        if (switchPos("/ PACKED") || switchPos("/ NORMAL") ||
+            switchPos("/ COMPRESSED") || switchPos("/ EXPLICIT") || saveFlag) {
+          /*??? Add error msg if other switches were specified. (Ignore them.)*/
+
+          if (saveFlag) {
+            if (printTime == 1) {
+              getRunTime(&timeIncr);  /* This call just resets the time */
+            }
+          }
+
+          if (!pipFlag) {
+            outStatement = g_showStatement;
+          } else {
+            outStatement = g_proveStatement;
+          }
+
+          explicitTargets = (switchPos("/ EXPLICIT") != 0) ? 1 : 0;
+
+          /* Get the amount to indent the proof by */
+          indentation = 2 + getSourceIndentation(outStatement);
+
+          if (!pipFlag) {
+            parseProof(g_showStatement);  /* Prints message if severe error */
+            if (g_WrkProof.errorSeverity > 1) {
+              /* Prevent bug trap in nmbrSquishProof -> nmbrGetSubProofLen
+                 if proof corrupted */
+              print2(
+          "?The proof has a severe error and cannot be displayed or saved.\n");
+              continue;
+            }
+            if (fastFlag) {
+              /* Use the proof as is */
+              nmbrLet(&nmbrSaveProof, g_WrkProof.proofString);
+            } else {
+              /* Make sure the proof is uncompressed */
+              nmbrLet(&nmbrSaveProof, nmbrUnsquishProof(g_WrkProof.proofString));
+            }
+          } else {
+            nmbrLet(&nmbrSaveProof, g_ProofInProgress.proof);
+          }
+          if (switchPos("/ PACKED")  || switchPos("/ COMPRESSED")) {
+            if (!fastFlag) {
+              nmbrLet(&nmbrSaveProof, nmbrSquishProof(nmbrSaveProof));
+            }
+          }
+
+          if (switchPos("/ COMPRESSED")) {
+            let(&str1, compressProof(nmbrSaveProof,
+                outStatement, /* g_showStatement or g_proveStatement based on pipFlag */
+                (switchPos("/ OLD_COMPRESSION")) ? 1 : 0));
+          } else {
+            let(&str1, nmbrCvtRToVString(nmbrSaveProof,
+                explicitTargets,
+                outStatement /*statemNum, used only if explicitTargets*/));
+          }
+
+
+          if (saveFlag) {
+            /* ??? This is a problem when mixing html and save proof */
+            if (g_printString[0]) bug(1114);
+            let(&g_printString, "");
+
+            /* Set flag for print2() to put output in g_printString instead
+               of displaying it on the screen */
+            g_outputToString = 1;
+
+          } else {
+            if (!print2("Proof of \"%s\":\n", g_Statement[outStatement].labelName))
+              break; /* Break for speedup if user quit */
+            print2(
+"---------Clip out the proof below this line to put it in the source file:\n");
+          }
+          if (switchPos("/ COMPRESSED")) {
+            printLongLine(cat(space(indentation), str1, " $.", NULL),
+              space(indentation), "& "); /* "&" is special flag to break
+                  compressed part of proof anywhere */
+          } else {
+            printLongLine(cat(space(indentation), str1," $.", NULL),
+              space(indentation), " ");
+          }
+
+          l = (long)(strlen(str1)); /* Save length for printout below */
+
+          /* SOREAR Only generate date if the proof looks complete.
+            This is not intended as a grading mechanism, just trying
+            to avoid premature output */
+          if (!nmbrElementIn(1, nmbrSaveProof, -(long)'?')) {
+            /* Add a "(Contributed by...)" date if it isn't there */
+            free_vstring(str2);
+            str2 = getContrib(outStatement, CONTRIBUTOR);
+            if (str2[0] == 0) { /* The is no contributor, so add one */
+
+              /* See if there is a date below the proof (for converting old
+                 .mm files).  Someday this will be obsolete, with str3 and
+                 str4 always returned as "". */
+              getProofDate(outStatement, &str3, &str4);
+              /* If there are two dates below the proof, the first on is
+                 the revision date and the second the "Contributed by" date. */
+              if (str4[0] != 0) { /* There are 2 dates below the proof */
+                let(&str5, str3); /* 1st date is Revised by... */
+                let(&str3, str4); /* 2nd date is Contributed by... */
+              } else {
+                let(&str5, "");
+              }
+              /* If there is no date below proof, use today's date */
+              if (str3[0] == 0) let(&str3, date());
+              let(&str4, cat("\n", space(indentation + 1),
+                  "(Contributed by ", g_contributorName,
+                      ", ", str3, ".) ", NULL));
+              /* If there is a 2nd date below proof, add a "(Revised by..."
+                 tag */
+              if (str5[0] != 0) {
+                /* Use the DEFAULT_CONTRIBUTOR ?who? because we don't
+                   know the reviser name (using the contributor name may
+                   be incorrect).  Also, this will trigger a warning in
+                   VERIFY MARKUP since it may be a proof shortener rather than
+                   a reviser. */
+                let(&str4, cat(str4, "\n", space(indentation + 1),
+                    "(Revised by ", DEFAULT_CONTRIBUTOR,
+                        ", ", str5, ".) ", NULL));
+              }
+
+              let(&str3, space(g_Statement[outStatement].labelSectionLen));
+              /* str3 will have the statement's label section w/ comment */
+              memcpy(str3, g_Statement[outStatement].labelSectionPtr,
+                  (size_t)(g_Statement[outStatement].labelSectionLen));
+              i = rinstr(str3, "$)");  /* The last "$)" occurrence */
+              if (i != 0    /* A description comment exists */
+                  && saveFlag) { /* and we are saving the proof */
+                /* This isn't a perfect wrapping but we assume
+                   'write source .../rewrap' will be done eventually. */
+                /* str3 will have the updated comment */
+                let(&str3, cat(left(str3, i - 1), str4, right(str3, i), NULL));
+                if (g_Statement[outStatement].labelSectionChanged == 1) {
+                  /* Deallocate old comment if not original source */
+                  free_vstring(str4); /* Deallocate any previous str4 content */
+                  str4 = g_Statement[outStatement].labelSectionPtr;
+                  free_vstring(str4); /* Deallocate the old content */
+                }
+                /* Set flag that this is not the original source */
+                g_Statement[outStatement].labelSectionChanged = 1;
+                g_Statement[outStatement].labelSectionLen = (long)strlen(str3);
+                /* We do a direct assignment instead of let(&...) because
+                   labelSectionPtr may point to the middle of the giant input
+                   file buffer, which we don't want to deallocate */
+                g_Statement[outStatement].labelSectionPtr = str3;
+                /* Reset str3 without deallocating with let(), since it
+                   was assigned to labelSectionPtr */
+                str3 = "";
+                /* Reset the cache for this statement in getContrib() */
+                str3 = getContrib(outStatement, GC_RESET_STMT);
+              } /* if i != 0 */
+            } /* if str2[0] == 0 */
+          } /* if (!nmbrElementIn(1, nmbrSaveProof, -(long)'?')) */
+
+          if (saveFlag) {
+            g_sourceChanged = 1;
+            g_proofChanged = 0;
+            if (processUndoStack(NULL, PUS_GET_STATUS, "", 0)) {
+              /* The UNDO stack may not be empty */
+              proofSavedFlag = 1; /* UNDO stack empty no longer reliably
+                             indicates that proof hasn't changed */
+            }
+
+            /* Add an initial \n which will go after the "$=" and the
+               beginning of the proof */
+            let(&g_printString, cat("\n", g_printString, NULL));
+            if (g_Statement[outStatement].proofSectionChanged == 1) {
+              /* Deallocate old proof if not original source */
+              free_vstring(str1); /* Deallocate any previous str1 content */
+              str1 = g_Statement[outStatement].proofSectionPtr;
+              free_vstring(str1); /* Deallocate the proof section */
+            }
+            /* Set flag that this is not the original source */
+            g_Statement[outStatement].proofSectionChanged = 1;
+            if (strcmp(" $.\n",
+                right(g_printString, (long)strlen(g_printString) - 3))) {
+              bug(1128);
+            }
+            /* Note that g_printString ends with "$.\n", but those 3 characters
+               should not be in the proofSection.  (The "$." keyword is
+               added between proofSection and next labelSection when the
+               output is written by writeOutput.)  Thus we subtract 3
+               from the length.  But there is no need to truncate the
+               string; later deallocation will take care of the whole
+               string. */
+            g_Statement[outStatement].proofSectionLen
+                = (long)strlen(g_printString) - 3;
+            /* We do a direct assignment instead of let(&...) because
+               proofSectionPtr may point to the middle of the giant input
+               file string, which we don't want to deallocate */
+            g_Statement[outStatement].proofSectionPtr = g_printString;
+            /* Reset g_printString without deallocating with let(), since it
+               was assigned to proofSectionPtr */
+            g_printString = "";
+            g_outputToString = 0;
+
+
+            if (!pipFlag) {
+              if (!(fastFlag && !strcmp("*", labelMatch))) {
+                printLongLine(
+                    cat("The proof of \"", g_Statement[outStatement].labelName,
+                    "\" has been reformatted and saved internally.",
+                    NULL), "", " ");
+              }
+            } else {
+              printLongLine(cat("The new proof of \"", g_Statement[outStatement].labelName,
+                  "\" has been saved internally.",
+                  NULL), "", " ");
+            }
+
+            if (printTime == 1) {
+              getRunTime(&timeIncr);
+              print2("SAVE PROOF run time = %6.2f sec for \"%s\"\n", timeIncr,
+                  g_Statement[outStatement].labelName);
+            }
+
+          } else {
+            /*print2("\n");*/ /* Add a blank line to make clipping easier */
+            print2(cat(
+                "---------The proof of \"", g_Statement[outStatement].labelName,
+                /* "\" to clip out ends above this line.\n",NULL)); */
+                "\" (", str((double)l), " bytes) ends above this line.\n", NULL));
+          } /* End if saveFlag */
+          free_nmbrString(nmbrSaveProof);
+          if (pipFlag) break; /* Only one iteration for NEW_PROOF stuff */
+          continue;  /* to next s iteration */
+        } /* end if (switchPos("/ PACKED") || switchPos("/ NORMAL") ||
+            switchPos("/ COMPRESSED") || switchPos("/ EXPLICIT") || saveFlag) */
+
+        if (saveFlag) bug(1112); /* Shouldn't get here */
+
+        if (!pipFlag) {
+          outStatement = g_showStatement;
+        } else {
+          outStatement = g_proveStatement;
+        }
+        if (texFlag) {
+          g_outputToString = 1; /* Flag for print2 to add to g_printString */
+          if (!g_htmlFlag) {
+            if (!g_oldTexFlag) {
+              print2("\\begin{proof}\n");
+              print2("\\begin{align}\n");
+            } else {
+              print2("\n");
+              print2("\\vspace{1ex} %%1\n");
+              printLongLine(cat("Proof of ",
+                  "{\\tt ",
+                  asciiToTt(g_Statement[outStatement].labelName),
+                  "}:", NULL), "", " ");
+              print2("\n");
+              print2("\n");
+            }
+          } else { /* g_htmlFlag */
+            bug(1118);
+          }
+          g_outputToString = 0;
+          /* printTeXLongMath clears g_printString in LaTeX
+             mode before starting its output, so we must put out the
+             g_printString ourselves here */
+          fprintf(g_texFilePtr, "%s", g_printString);
+          free_vstring(g_printString); /* We'll clear it anyway */
+        } else { /* !texFlag */
+          /* Terminal output - display the statement if wildcard is used */
+          if (!pipFlag) {
+            if (instr(1, labelMatch, "*") || instr(1, labelMatch, "?")) {
+              if (!print2("Proof of \"%s\":\n", g_Statement[outStatement].labelName))
+                break; /* Break for speedup if user quit */
+            }
+          }
+        }
+
+
+        if (texFlag) print2("Outputting proof of \"%s\"...\n",
+            g_Statement[outStatement].labelName);
+
+        typeProof(outStatement,
+            pipFlag,
+            startStep,
+            endStep,
+            endIndent,
+            essentialFlag,
+
+            /* In SHOW PROOF xxx /TEX, we use renumber steps mode so that
+               the first step is step 1.  The step number is checked for
+               step 1 in mmwtex.c to prevent a spurious \\ (newline) at the
+               start of the proof.  Note that
+               SHOW PROOF is not available in HTML mode, so texFlag will
+               always mean LaTeX mode here. */
+            (texFlag ? 1 : renumberFlag),
+
+            unknownFlag,
+            notUnifiedFlag,
+            reverseFlag,
+
+            /* In SHOW PROOF xxx /TEX, we use Lemmon mode so that the
+               hypothesis reference list will be available.  Note that
+               SHOW PROOF is not available in HTML mode, so texFlag will
+               always mean LaTeX mode here. */
+            (texFlag ? 1 : noIndentFlag),
+
+            splitColumn,
+            skipRepeatedSteps,
+            texFlag,
+            g_htmlFlag);
+        if (texFlag) {
+          if (!g_htmlFlag) {
+            if (!g_oldTexFlag) {
+              g_outputToString = 1;
+              print2("\\end{align}\n");
+              print2("\\end{proof}\n");
+              print2("\n");
+              g_outputToString = 0;
+              fprintf(g_texFilePtr, "%s", g_printString);
+              free_vstring(g_printString);
+            } else {
+            }
+          } else { /* g_htmlFlag */
+            g_outputToString = 1;
+            print2("</TABLE>\n");
+            print2("</CENTER>\n");
+            /* print trailer will close out string later */
+            g_outputToString = 0;
+          }
+        }
+
+        /*E*/ if (0) { /* for debugging: */
+          printLongLine(nmbrCvtRToVString(g_WrkProof.proofString,
+                0, /*explicitTargets*/
+                0 /*statemNum, used only if explicitTargets*/)," "," ");
+          print2("\n");
+
+          nmbrLet(&nmbrSaveProof, nmbrSquishProof(g_WrkProof.proofString));
+          printLongLine(nmbrCvtRToVString(nmbrSaveProof,
+                0, /*explicitTargets*/
+                0 /*statemNum, used only if explicitTargets*/)," "," ");
+          print2("\n");
+
+          nmbrLet(&nmbrTmp, nmbrUnsquishProof(nmbrSaveProof));
+          printLongLine(nmbrCvtRToVString(nmbrTmp,
+                0, /*explicitTargets*/
+                0 /*statemNum, used only if explicitTargets*/)," "," ");
+
+          nmbrLet(&nmbrTmp, nmbrGetTargetHyp(nmbrSaveProof,g_showStatement));
+          printLongLine(nmbrCvtAnyToVString(nmbrTmp)," "," "); print2("\n");
+
+          nmbrLet(&nmbrTmp, nmbrGetEssential(nmbrSaveProof));
+          printLongLine(nmbrCvtAnyToVString(nmbrTmp)," "," "); print2("\n");
+
+          cleanWrkProof(); /* Deallocate verifyProof storage */
+        } /* end debugging */
+
+        if (pipFlag) break; /* Only one iteration for NEW_PROOF stuff */
+      } /* Next stmt */
+      if (!q) {
+        /* No matching statement was found */
+        printLongLine(cat("?There is no $p statement whose label matches \"",
+            (cmdMatches("MIDI")) ? g_fullArg[1] : g_fullArg[2],
+            "\".  ",
+            "Use SHOW LABELS to see list of valid labels.", NULL), "", " ");
+      } else {
+        if (saveFlag) {
+          print2("Remember to use WRITE SOURCE to save changes permanently.\n");
+        }
+        if (texFlag) {
+          print2("The LaTeX source was written to \"%s\".\n", g_texFileName);
+         g_oldTexFlag = 0;
+        }
+      }
+
+      continue;
+    } /* if (cmdMatches("SHOW/SAVE [NEW_]PROOF" or" MIDI") */
+
+
+/*E*/ /*???????? DEBUG command for debugging only */
+    if (cmdMatches("DBG")) {
+      print2("DEBUGGING MODE IS FOR DEVELOPER'S USE ONLY!\n");
+      print2("Argument:  %s\n", g_fullArg[1]);
+      nmbrLet(&nmbrTmp, parseMathTokens(g_fullArg[1], g_proveStatement));
+      for (j = 0; j < 3; j++) {
+        print2("Trying depth %ld\n", j);
+        nmbrTmpPtr = proveFloating(nmbrTmp, g_proveStatement, j, 0, 0,
+            1/*overrideFlag*/, 1/*mathboxFlag*/);
+        if (nmbrLen(nmbrTmpPtr)) break;
+      }
+
+      print2("Result:  %s\n", nmbrCvtRToVString(nmbrTmpPtr,
+                0, /*explicitTargets*/
+                0 /*statemNum, used only if explicitTargets*/));
+      free_nmbrString(nmbrTmpPtr);
+
+      continue;
+    }
+/*E*/ /*???????? DEBUG command for debugging only */
+
+    if (cmdMatches("PROVE")) {
+
+      /* Get the unique statement matching the g_fullArg[1] pattern */
+      i = getStatementNum(g_fullArg[1],
+          1/*startStmt*/,
+          g_statements + 1  /*maxStmt*/,
+          0/*aAllowed*/,
+          1/*pAllowed*/,
+          0/*eAllowed*/,
+          0/*fAllowed*/,
+          0/*efOnlyForMaxStmt*/,
+          1/*uniqueFlag*/);
+      if (i == -1) {
+        continue; /* Error msg was provided if not unique */
+      }
+      g_proveStatement = i;
+
+
+      /* 1 means to override usage locks */
+      overrideFlag = ( (switchPos("/ OVERRIDE")) ? 1 : 0)
+         || g_globalDiscouragement == 0;
+      if (getMarkupFlag(g_proveStatement, PROOF_DISCOURAGED)) {
+        if (overrideFlag == 0) {
+          /* print2("\n"); */ /* Enable for more emphasis */
+          print2(">>> ?Error: "
+            "Modification of this statement's proof is discouraged.\n");
+          print2(">>> You must use PROVE ... / OVERRIDE to work on it.\n");
+          /* print2("\n"); */ /* Enable for more emphasis */
+          continue;
+        }
+      }
+
+      print2("Entering the Proof Assistant.  "
+        "HELP PROOF_ASSISTANT for help, EXIT to exit.\n");
+
+      g_PFASmode = 1; /* Set mode for commands here and in mmcmdl.c */
+      /* Note:  Proof Assistant mode can equivalently be determined by:
+            nmbrLen(g_ProofInProgress.proof) != 0  */
+
+      parseProof(g_proveStatement);
+      verifyProof(g_proveStatement); /* Necessary to set RPN stack ptrs
+                                      before calling cleanWrkProof() */
+      if (g_WrkProof.errorSeverity > 1) {
+        print2(
+             "The starting proof has a severe error.  It will not be used.\n");
+        nmbrLet(&nmbrSaveProof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
+      } else {
+        nmbrLet(&nmbrSaveProof, g_WrkProof.proofString);
+      }
+      cleanWrkProof(); /* Deallocate verifyProof storage */
+
+      /* Initialize the structure needed for the Proof Assistant */
+      initProofStruct(&g_ProofInProgress, nmbrSaveProof, g_proveStatement);
+
+      /* Show the user the statement to be proved */
+      print2("You will be working on statement (from \"SHOW STATEMENT %s\"):\n",
+          g_Statement[g_proveStatement].labelName);
+      typeStatement(g_proveStatement /*g_showStatement*/,
+          1 /*briefFlag*/,
+          0 /*commentOnlyFlag*/,
+          0 /*texFlag*/,
+          0 /*g_htmlFlag*/);
+
+      if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
+        print2(
+        "Note:  The proof you are starting with is already complete.\n");
+      } else {
+
+        print2(
+     "Unknown step summary (from \"SHOW NEW_PROOF / UNKNOWN\"):\n");
+        /* Automatically display new unknown steps
+           ???Future - add switch to enable/defeat this */
+        typeProof(g_proveStatement,
+            1 /*pipFlag*/,
+            0 /*startStep*/,
+            0 /*endStep*/,
+            0 /*endIndent*/,
+            1 /*essentialFlag*/,
+            0 /*renumberFlag*/,
+            1 /*unknownFlag*/,
+            0 /*notUnifiedFlag*/,
+            0 /*reverseFlag*/,
+            0 /*noIndentFlag*/,
+            0 /*splitColumn*/,
+            0 /*skipRepeatedSteps*/,
+            0 /*texFlag*/,
+            0 /*g_htmlFlag*/);
+      }
+
+      if (getMarkupFlag(g_proveStatement, PROOF_DISCOURAGED)) {
+        /* print2("\n"); */ /* Enable for more emphasis */
+        print2(
+">>> ?Warning: Modification of this statement's proof is discouraged.\n"
+            );
+        /* print2("\n"); */ /* Enable for more emphasis */
+      }
+
+      processUndoStack(NULL, PUS_INIT, "", 0); /* Optional? */
+      /* Put the initial proof into the UNDO stack; we don't need
+         the info string since it won't be undone */
+      processUndoStack(&g_ProofInProgress, PUS_PUSH, "", 0);
+      continue;
+    }
+
+
+    if (cmdMatches("UNDO")) {
+      processUndoStack(&g_ProofInProgress, PUS_UNDO, "", 0);
+      g_proofChanged = 1; /* Maybe make this more intelligent some day */
+      /* Automatically display new unknown steps
+         ???Future - add switch to enable/defeat this */
+      typeProof(g_proveStatement,
+          1 /*pipFlag*/,
+          0 /*startStep*/,
+          0 /*endStep*/,
+          0 /*endIndent*/,
+          1 /*essentialFlag*/,
+          0 /*renumberFlag*/,
+          1 /*unknownFlag*/,
+          0 /*notUnifiedFlag*/,
+          0 /*reverseFlag*/,
+          0 /*noIndentFlag*/,
+          0 /*splitColumn*/,
+          0 /*skipRepeatedSteps*/,
+          0 /*texFlag*/,
+          0 /*g_htmlFlag*/);
+      continue;
+    }
+
+    if (cmdMatches("REDO")) {
+      processUndoStack(&g_ProofInProgress, PUS_REDO, "", 0);
+      g_proofChanged = 1; /* Maybe make this more intelligent some day */
+      /* Automatically display new unknown steps
+         ???Future - add switch to enable/defeat this */
+      typeProof(g_proveStatement,
+          1 /*pipFlag*/,
+          0 /*startStep*/,
+          0 /*endStep*/,
+          0 /*endIndent*/,
+          1 /*essentialFlag*/,
+          0 /*renumberFlag*/,
+          1 /*unknownFlag*/,
+          0 /*notUnifiedFlag*/,
+          0 /*reverseFlag*/,
+          0 /*noIndentFlag*/,
+          0 /*splitColumn*/,
+          0 /*skipRepeatedSteps*/,
+          0 /*texFlag*/,
+          0 /*g_htmlFlag*/);
+      continue;
+    }
+
+    if (cmdMatches("UNIFY")) {
+      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+      g_proofChangedFlag = 0;
+      if (cmdMatches("UNIFY STEP")) {
+
+        s = (long)val(g_fullArg[2]); /* Step number */
+        if (s > m || s < 1) {
+          print2("?The step must be in the range from 1 to %ld.\n", m);
+          continue;
+        }
+
+        interactiveUnifyStep(s - 1, 1); /* 2nd arg. means print msg if
+                                           already unified */
+
+        /* (The interactiveUnifyStep handles all messages.) */
+        /* print2("... */
+
+        autoUnify(1);
+        if (g_proofChangedFlag) {
+          g_proofChanged = 1; /* Cumulative flag */
+          processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+        }
+        continue;
+      }
+
+      /* "UNIFY ALL" */
+      if (!switchPos("/ INTERACTIVE")) {
+        autoUnify(1);
+        if (!g_proofChangedFlag) {
+          print2("No new unifications were made.\n");
+        } else {
+          print2(
+  "Steps were unified.  SHOW NEW_PROOF / NOT_UNIFIED to see any remaining.\n");
+          g_proofChanged = 1; /* Cumulative flag */
+          processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+        }
+      } else {
+        q = 0;
+        while (1) {
+          /* Repeat the unifications over and over until done, since
+             a later unification may improve the ability of an aborted earlier
+             one to be done without timeout */
+          g_proofChangedFlag = 0; /* This flag is set by autoUnify() and
+                                   interactiveUnifyStep() */
+          autoUnify(0);
+          for (s = m - 1; s >= 0; s--) {
+            interactiveUnifyStep(s, 0); /* 2nd arg. means no msg if already
+                                           unified */
+          }
+          autoUnify(1); /* 1 means print congratulations if complete */
+          if (!g_proofChangedFlag) {
+            if (!q) {
+              print2("No new unifications were made.\n");
+            } else {
+              /* If q=1, then we are in the 2nd or later pass, which means
+                 there was a change in the 1st pass. */
+              print2(
+  "Steps were unified.  SHOW NEW_PROOF / NOT_UNIFIED to see any remaining.\n");
+              g_proofChanged = 1; /* Cumulative flag */
+              processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+            }
+            break; /* while (1) */
+          } else {
+            q = 1; /* Flag that we're starting a 2nd or later pass */
+          }
+        } /* End while (1) */
+      }
+      /* Automatically display new unknown steps
+         ???Future - add switch to enable/defeat this */
+      typeProof(g_proveStatement,
+          1 /*pipFlag*/,
+          0 /*startStep*/,
+          0 /*endStep*/,
+          0 /*endIndent*/,
+          1 /*essentialFlag*/,
+          0 /*renumberFlag*/,
+          1 /*unknownFlag*/,
+          0 /*notUnifiedFlag*/,
+          0 /*reverseFlag*/,
+          0 /*noIndentFlag*/,
+          0 /*splitColumn*/,
+          0 /*skipRepeatedSteps*/,
+          0 /*texFlag*/,
+          0 /*g_htmlFlag*/);
+      continue;
+    }
+
+    if (cmdMatches("MATCH")) {
+
+      maxEssential = -1; /* Default:  no maximum */
+      i = switchPos("/ MAX_ESSENTIAL_HYP");
+      if (i) maxEssential = (long)val(g_fullArg[i + 1]);
+
+      if (cmdMatches("MATCH STEP")) {
+
+        s = (long)val(g_fullArg[2]); /* Step number */
+        m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+        if (s > m || s < 1) {
+          print2("?The step must be in the range from 1 to %ld.\n", m);
+          continue;
+        }
+        if ((g_ProofInProgress.proof)[s - 1] != -(long)'?') {
+          print2(
+    "?Step %ld is already assigned.  Only unknown steps can be matched.\n", s);
+          continue;
+        }
+
+        interactiveMatch(s - 1, maxEssential);
+        n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
+        if (n != m) {
+          if (s != m) {
+            printLongLine(cat("Steps ", str((double)s), ":",
+                str((double)m), " are now ", str((double)(s - m + n)), ":",
+                str((double)n), ".",
+                NULL),
+                "", " ");
+          } else {
+            printLongLine(cat("Step ", str((double)m), " is now step ", str((double)n), ".",
+                NULL),
+                "", " ");
+          }
+        }
+
+        autoUnify(1);
+        g_proofChanged = 1; /* Cumulative flag */
+        /* 1-Nov-2013 nm Why is g_proofChanged set unconditionally above?
+           Do we need the processUndoStack() call? */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+
+        continue;
+      } /* End if MATCH STEP */
+
+      if (cmdMatches("MATCH ALL")) {
+
+        m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+
+        k = 0;
+        g_proofChangedFlag = 0;
+
+        if (switchPos("/ ESSENTIAL")) {
+          nmbrLet(&nmbrTmp, nmbrGetEssential(g_ProofInProgress.proof));
+        }
+
+        for (s = m; s > 0; s--) {
+          /* Match only unknown steps */
+          if ((g_ProofInProgress.proof)[s - 1] != -(long)'?') continue;
+          /* Match only essential steps if specified */
+          if (switchPos("/ ESSENTIAL")) {
+            if (!nmbrTmp[s - 1]) continue;
+          }
+
+          interactiveMatch(s - 1, maxEssential);
+          if (g_proofChangedFlag) {
+            k = s; /* Save earliest step changed */
+            g_proofChangedFlag = 0;
+          }
+          print2("\n");
+        }
+        if (k) {
+          g_proofChangedFlag = 1; /* Restore it */
+          g_proofChanged = 1; /* Cumulative flag */
+          processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+          print2("Steps %ld and above have been renumbered.\n", k);
+        }
+        autoUnify(1);
+
+        continue;
+      } /* End if MATCH ALL */
+    }
+
+    if (cmdMatches("LET")) {
+
+      g_errorCount = 0;
+      nmbrLet(&nmbrTmp, parseMathTokens(g_fullArg[4], g_proveStatement));
+      if (g_errorCount) {
+        /* Parsing issued error message(s) */
+        g_errorCount = 0;
+        continue;
+      }
+
+      if (cmdMatches("LET VARIABLE")) {
+        if (((vstring)(g_fullArg[2]))[0] != '$') {
+          print2(
+   "?The target variable must be of the form \"$<integer>\", e.g. \"$23\".\n");
+          continue;
+        }
+        n = (long)val(right(g_fullArg[2], 2));
+        if (n < 1 || n > g_pipDummyVars) {
+          print2("?The target variable must be between $1 and $%ld.\n",
+              g_pipDummyVars);
+          continue;
+        }
+
+        replaceDummyVar(n, nmbrTmp);
+
+        autoUnify(1);
+
+
+        g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
+        g_proofChanged = 1; /* Cumulative flag */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+
+      }
+
+      if (cmdMatches("LET STEP")) {
+
+        s = getStepNum(g_fullArg[2], g_ProofInProgress.proof,
+            0 /* ALL not allowed */);
+        if (s == -1) continue;  /* Error; message was provided already */
+
+        /* Check to see if the statement selected is allowed */
+        if (!checkMStringMatch(nmbrTmp, s - 1)) {
+          printLongLine(cat("?Step ", str((double)s), " cannot be unified with \"",
+              nmbrCvtMToVString(nmbrTmp),"\".", NULL), " ", " ");
+          continue;
+        }
+
+        /* Assign the user string */
+        nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[s - 1])), nmbrTmp);
+
+        autoUnify(1);
+        g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
+        g_proofChanged = 1; /* Cumulative flag */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+      }
+      /* Automatically display new unknown steps
+         ???Future - add switch to enable/defeat this */
+      typeProof(g_proveStatement,
+          1 /*pipFlag*/,
+          0 /*startStep*/,
+          0 /*endStep*/,
+          0 /*endIndent*/,
+          1 /*essentialFlag*/,
+          0 /*renumberFlag*/,
+          1 /*unknownFlag*/,
+          0 /*notUnifiedFlag*/,
+          0 /*reverseFlag*/,
+          0 /*noIndentFlag*/,
+          0 /*splitColumn*/,
+          0 /*skipRepeatedSteps*/,
+          0 /*texFlag*/,
+          0 /*g_htmlFlag*/);
+      continue;
+    }
+
+
+    if (cmdMatches("ASSIGN")) {
+      s = getStepNum(g_fullArg[1], g_ProofInProgress.proof,
+          0 /* ALL not allowed */);
+      if (s == -1) continue;  /* Error; message was provided already */
+
+      /* 1 means to override usage locks */
+      overrideFlag = ( (switchPos("/ OVERRIDE")) ? 1 : 0)
+         || g_globalDiscouragement == 0;
+
+      /* Get the unique statement matching the g_fullArg[2] pattern */
+      k = getStatementNum(g_fullArg[2],
+          1/*startStmt*/,
+          g_proveStatement  /*maxStmt*/,
+          1/*aAllowed*/,
+          1/*pAllowed*/,
+          1/*eAllowed*/,
+          1/*fAllowed*/,
+          1/*efOnlyForMaxStmt*/,
+          1/*uniqueFlag*/);
+      if (k == -1) {
+        continue; /* Error msg was provided if not unique */
+      }
+
+      if (getMarkupFlag(k, USAGE_DISCOURAGED)) {
+        if (overrideFlag == 0) {
+          /* print2("\n"); */ /* Enable for more emphasis */
+          print2(
+">>> ?Error: Attempt to assign a statement whose usage is discouraged.\n");
+          print2(
+       ">>> Use ASSIGN ... / OVERRIDE if you really want to do this.\n");
+          /* print2("\n"); */ /* Enable for more emphasis */
+          continue;
+        } else {
+          /* print2("\n"); */ /* Enable for more emphasis */
+          print2(
+">>> ?Warning: You are assigning a statement whose usage is discouraged.\n");
+          /* print2("\n"); */ /* Enable for more emphasis */
+        }
+      }
+
+      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+
+      /* Check to see that the step is an unknown step */
+      if ((g_ProofInProgress.proof)[s - 1] != -(long)'?') {
+        print2(
+        "?Step %ld is already assigned.  You can only assign unknown steps.\n"
+            , s);
+        continue;
+      }
+
+      /* Check to see if the statement selected is allowed */
+      if (!checkStmtMatch(k, s - 1)) {
+        print2("?Statement \"%s\" cannot be unified with step %ld.\n",
+          g_Statement[k].labelName, s);
+        continue;
+      }
+
+      assignStatement(k /*statement#*/, s - 1 /*step*/);
+
+      n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
+      autoUnify(1);
+
+      /* Automatically interact with user if step not unified */
+      /* ???We might want to add a setting to defeat this if user doesn't
+         like it */
+      /* Since ASSIGN LAST is often run from a command file, don't
+         interact if / NO_UNIFY is specified, so response is predictable */
+      if (switchPos("/ NO_UNIFY") == 0) {
+        interactiveUnifyStep(s - m + n - 1, 2); /* 2nd arg. means print msg if
+                                                 already unified */
+      } /* if NO_UNIFY flag not set */
+
+      /* See if it's in another mathbox; if so, let user know */
+      assignMathboxInfo();
+      if (k > g_mathboxStmt && g_proveStatement > g_mathboxStmt) {
+        if (k < g_mathboxStart[getMathboxNum(g_proveStatement) - 1]) {
+          printLongLine(cat("\"", g_Statement[k].labelName,
+                "\" is in the mathbox for ",
+                g_mathboxUser[getMathboxNum(k) - 1], ".",
+                NULL),
+              "", " ");
+        }
+      }
+
+      /* Automatically display new unknown steps
+         ???Future - add switch to enable/defeat this */
+      typeProof(g_proveStatement,
+          1 /*pipFlag*/,
+          0 /*startStep*/,
+          0 /*endStep*/,
+          0 /*endIndent*/,
+          1 /*essentialFlag*/,
+          0 /*renumberFlag*/,
+          1 /*unknownFlag*/,
+          0 /*notUnifiedFlag*/,
+          0 /*reverseFlag*/,
+          0 /*noIndentFlag*/,
+          0 /*splitColumn*/,
+          0 /*skipRepeatedSteps*/,
+          0 /*texFlag*/,
+          0 /*g_htmlFlag*/);
+
+      g_proofChangedFlag = 1; /* Flag to push 'undo' stack (future) */
+      g_proofChanged = 1; /* Cumulative flag */
+      processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+      continue;
+
+    } /* cmdMatches("ASSIGN") */
+
+
+    if (cmdMatches("REPLACE")) {
+      /* 1 means to override usage locks */
+      overrideFlag = ( (switchPos("/ OVERRIDE")) ? 1 : 0)
+         || g_globalDiscouragement == 0;
+
+      step = getStepNum(g_fullArg[1], g_ProofInProgress.proof,
+          0 /* ALL not allowed */);
+      if (step == -1) continue;  /* Error; message was provided already */
+
+      /* Get the unique statement matching the g_fullArg[2] pattern */
+      stmt = getStatementNum(g_fullArg[2],
+          1/*startStmt*/,
+          g_proveStatement  /*maxStmt*/,
+          1/*aAllowed*/,
+          1/*pAllowed*/,
+          0/*eAllowed*/, /* Not implemented (yet?) */
+          0/*fAllowed*/, /* Not implemented (yet?) */
+          1/*efOnlyForMaxStmt*/,
+          1/*uniqueFlag*/);
+      if (stmt == -1) {
+        continue; /* Error msg was provided if not unique */
+      }
+
+      if (getMarkupFlag(stmt, USAGE_DISCOURAGED)) {
+        if (overrideFlag == 0) {
+          /* print2("\n"); */ /* Enable for more emphasis */
+          print2(
+">>> ?Error: Attempt to assign a statement whose usage is discouraged.\n");
+          print2(
+       ">>> Use REPLACE ... / OVERRIDE if you really want to do this.\n");
+          /* print2("\n"); */ /* Enable for more emphasis */
+          continue;
+        } else {
+          /* print2("\n"); */ /* Enable for more emphasis */
+          print2(
+">>> ?Warning: You are assigning a statement whose usage is discouraged.\n");
+          /* print2("\n"); */ /* Enable for more emphasis */
+        }
+      }
+
+      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+
+      /* Set a flag that proof has unknown steps (for autoUnify() call below) */
+      if (nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
+        p = 1;
+      } else {
+        p = 0;
+      }
+
+      /* Check to see if the statement selected is allowed */
+      if (!checkStmtMatch(stmt, step - 1)) {
+        print2("?Statement \"%s\" cannot be unified with step %ld.\n",
+          g_Statement[stmt].labelName, step);
+        continue;
+      }
+
+      /* Check dummy variable status of step */
+      /* For use in message later */
+      dummyVarIsoFlag = checkDummyVarIsolation(step - 1);
+            /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/
+
+      /* Do the replacement */
+      nmbrTmpPtr = replaceStatement(stmt /*statement#*/,
+          step - 1 /*step*/,
+          g_proveStatement,
+          0,/*scan whole proof to maximize chance of a match*/
+          0/*noDistinct*/,
+          1/* try to prove $e's */,
+          1/*improveDepth*/,
+          overrideFlag,
+          /* Currently REPLACE (not often used) allows other mathboxes
+             silently; TODO: we may want to notify user like for ASSIGN */
+          1/*mathboxFlag*/);
+      if (!nmbrLen(nmbrTmpPtr)) {
+        print2(
+           "?Hypotheses of statement \"%s\" do not match known proof steps.\n",
+            g_Statement[stmt].labelName);
+        continue;
+      }
+
+      /* Get the subproof at step s */
+      q = subproofLen(g_ProofInProgress.proof, step - 1);
+      deleteSubProof(step - 1);
+      addSubProof(nmbrTmpPtr, step - q);
+
+      /* Assign known subproofs */
+      assignKnownSubProofs();
+      /* Initialize remaining steps */
+      i = nmbrLen(g_ProofInProgress.proof);
+      for (j = 0; j < i; j++) {
+        if (!nmbrLen((g_ProofInProgress.source)[j])) {
+          initStep(j);
+        }
+      }
+      /* Unify whatever can be unified */
+      /* If proof wasn't complete before (p = 1), but is now, print congrats
+         for user */
+      autoUnify((char)p); /* 0 means no "congrats" message */
+
+      free_nmbrString(nmbrTmpPtr); /* Deallocate memory */
+
+      n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
+      if (nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
+        /* The proof is not complete, so print step numbers that changed */
+        if (m == n) {
+          print2("Step %ld was replaced with statement %s.\n",
+            step, g_Statement[stmt].labelName);
+        } else {
+          if (step != m) {
+            printLongLine(cat("Step ", str((double)step),
+                " was replaced with statement ", g_Statement[stmt].labelName,
+                ".  Steps ", str((double)step), ":",
+                str((double)m), " are now ", str((double)(step - m + n)), ":",
+                str((double)n), ".",
+                NULL),
+                "", " ");
+          } else {
+            printLongLine(cat("Step ", str((double)step),
+                " was replaced with statement ", g_Statement[stmt].labelName,
+                ".  Step ", str((double)m), " is now step ", str((double)n), ".",
+                NULL),
+                "", " ");
+          }
+        }
+      }
+
+      g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
+      g_proofChanged = 1; /* Cumulative flag */
+      processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+
+      if (dummyVarIsoFlag == 2 && g_proofChangedFlag) {
+        printLongLine(cat(
+     "Assignments to shared working variables ($nn) are guesses.  If "
+     "incorrect, UNDO then assign them manually with LET ",
+      "and try REPLACE again.",
+              NULL),
+              "", " ");
+      }
+
+
+      /* Automatically display new unknown steps
+         ???Future - add switch to enable/defeat this */
+      if (g_proofChangedFlag)
+        typeProof(g_proveStatement,
+            1 /*pipFlag*/,
+            0 /*startStep*/,
+            0 /*endStep*/,
+            0 /*endIndent*/,
+            1 /*essentialFlag*/,
+            0 /*renumberFlag*/,
+            1 /*unknownFlag*/,
+            0 /*notUnifiedFlag*/,
+            0 /*reverseFlag*/,
+            0 /*noIndentFlag*/,
+            0 /*splitColumn*/,
+            0 /*skipRepeatedSteps*/,
+            0 /*texFlag*/,
+            0 /*g_htmlFlag*/);
+
+      continue;
+
+    } /* REPLACE */
+
+
+    if (cmdMatches("IMPROVE")) {
+
+      improveDepth = 0; /* Depth */
+      i = switchPos("/ DEPTH");
+      if (i) improveDepth = (long)val(g_fullArg[i + 1]);
+      if (switchPos("/ NO_DISTINCT")) p = 1; else p = 0;
+                        /* p = 1 means don't try to use statements with $d's */
+      searchAlg = 1; /* Default */
+      if (switchPos("/ 1")) searchAlg = 1;
+      if (switchPos("/ 2")) searchAlg = 2;
+      if (switchPos("/ 3")) searchAlg = 3;
+      searchUnkSubproofs = 0;
+      if (switchPos("/ SUBPROOFS")) searchUnkSubproofs = 1;
+
+      mathboxFlag = (switchPos("/ INCLUDE_MATHBOXES") != 0);
+      assignMathboxInfo(); /* In case it hasn't been assigned yet */
+      if (g_proveStatement > g_mathboxStmt) {
+        /* We're in a mathbox */
+        i = getMathboxNum(g_proveStatement);
+        if (i <= 0) bug(1130);
+        thisMathboxStartStmt = g_mathboxStart[i - 1];
+      } else {
+        thisMathboxStartStmt = g_mathboxStmt;
+      }
+
+      /* 1 means to override usage locks */
+      overrideFlag = ( (switchPos("/ OVERRIDE")) ? 1 : 0)
+         || g_globalDiscouragement == 0;
+
+      s = getStepNum(g_fullArg[1], g_ProofInProgress.proof,
+          1 /* 1 = "ALL" is permissible; returns 0 */);
+      if (s == -1) continue;  /* Error; message was provided already */
+
+      if (s != 0) {  /* s=0 means ALL */
+        m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+
+        /* Get the subproof at step s */
+        q = subproofLen(g_ProofInProgress.proof, s - 1);
+        nmbrLet(&nmbrTmp, nmbrSeg(g_ProofInProgress.proof, s - q + 1, s));
+
+        /*???Shouldn't this be just known?*/
+        /* Check to see that the subproof has an unknown step. */
+        if (!nmbrElementIn(1, nmbrTmp, -(long)'?')) {
+          print2(
+              "?Step %ld already has a proof and cannot be improved.\n",
+              s);
+          continue;
+        }
+
+        /* Check dummy variable status of step */
+        dummyVarIsoFlag = checkDummyVarIsolation(s - 1);
+              /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/
+        if (dummyVarIsoFlag == 2) {
+          print2(
+  "?Step %ld target has shared dummy variables and cannot be improved.\n", s);
+          continue; /* Don't try to improve
+                                 dummy variables that aren't isolated */
+        }
+
+        if (dummyVarIsoFlag == 0) { /* No dummy vars */
+          /* Only use proveFloating if no dummy vars */
+          nmbrTmpPtr = proveFloating((g_ProofInProgress.target)[s - 1],
+              g_proveStatement, improveDepth, s - 1, (char)p/*NO_DISTINCT*/,
+              overrideFlag,
+              mathboxFlag);
+        } else {
+          nmbrTmpPtr = NULL_NMBRSTRING; /* Initialize */
+        }
+        if (!nmbrLen(nmbrTmpPtr)) {
+          /* A proof for the step was not found with proveFloating(). */
+
+          /* Next, try REPLACE algorithm */
+          if (searchAlg == 2 || searchAlg == 3) {
+            nmbrTmpPtr = proveByReplacement(g_proveStatement,
+              s - 1/*prfStep*/, /* 0 means step 1 */
+              (char)p/*NO_DISTINCT*/, /* 1 means don't try stmts with $d's */
+              dummyVarIsoFlag,
+              (char)(searchAlg - 2), /*0=proveFloat for $fs, 1=$e's also */
+              improveDepth,
+              overrideFlag,
+              mathboxFlag);
+          }
+          if (!nmbrLen(nmbrTmpPtr)) {
+            print2("A proof for step %ld was not found.\n", s);
+            /* REPLACE algorithm also failed */
+            continue;
+          }
+        }
+
+        /* If q=1, subproof must be an unknown step, so don't bother to
+           delete it */
+        /*???Won't q always be 1 here?*/
+        if (q > 1) deleteSubProof(s - 1);
+        addSubProof(nmbrTmpPtr, s - q);
+        assignKnownSteps(s - q, nmbrLen(nmbrTmpPtr));
+        free_nmbrString(nmbrTmpPtr);
+
+        n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
+        if (m == n) {
+          print2("A 1-step proof was found for step %ld.\n", s);
+        } else {
+          if (s != m || q != 1) {
+            printLongLine(cat("A ", str((double)(n - m + 1)),
+                "-step proof was found for step ", str((double)s),
+                ".  Steps ", str((double)s), ":",
+                str((double)m), " are now ", str((double)(s - q + 1 - m + n)),
+                ":", str((double)n), ".",
+                NULL),
+                "", " ");
+          } else {
+            printLongLine(cat("A ", str((double)(n - m + 1)),
+                "-step proof was found for step ", str((double)s),
+                ".  Step ", str((double)m), " is now step ", str((double)n), ".",
+                NULL),
+                "", " ");
+          }
+        }
+
+        autoUnify(1); /* To get 'congrats' message if proof complete */
+        g_proofChanged = 1; /* Cumulative flag */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+
+        /* End if s != 0 i.e. not IMPROVE ALL */
+      } else {
+        /* Here, getStepNum() returned 0, meaning ALL */
+
+        if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
+          print2("The proof is already complete.\n");
+          continue;
+        }
+
+        n = 0; /* Earliest step that changed */
+
+        g_proofChangedFlag = 0;
+
+        for (improveAllIter = 1; improveAllIter <= 4; improveAllIter++) {
+          if (improveAllIter == 1 && (searchAlg == 2 || searchAlg == 3))
+            print2("Pass 1:  Trying to match cut-free statements...\n");
+          if (improveAllIter == 2) {
+            if (searchAlg == 2) {
+              print2("Pass 2:  Trying to match all statements...\n");
+            } else {
+              print2(
+"Pass 2:  Trying to match all statements, with cut-free hypothesis matches...\n"
+                  );
+            }
+          }
+          if (improveAllIter == 3 && searchUnkSubproofs)
+            print2("Pass 3:  Trying to replace incomplete subproofs...\n");
+          if (improveAllIter == 4) {
+            if (searchUnkSubproofs) {
+              print2("Pass 4:  Repeating pass 1...\n");
+            } else {
+              print2("Pass 3:  Repeating pass 1...\n");
+            }
+          }
+          /* improveAllIter = 1: run proveFloating only */
+          /* improveAllIter = 2: run proveByReplacement on unknown steps */
+          /* improveAllIter = 3: run proveByReplacement on steps with
+                                   incomplete subproofs */
+          /* improveAllIter = 4: if something changed, run everything again */
+
+          if (improveAllIter == 3 && !searchUnkSubproofs) continue;
+
+          m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+
+          for (s = m; s > 0; s--) {
+
+            proofStepUnk = ((g_ProofInProgress.proof)[s - 1] == -(long)'?')
+                ? 1 : 0;
+
+            /* I think this is really too conservative, even
+               with the old algorithm, but keep it to imitate the old one */
+            if (improveAllIter == 1 || searchAlg == 1) {
+              /* If the step is known and unified, don't do it, since nothing
+                 would be accomplished. */
+              if (!proofStepUnk) {
+                if (nmbrEq((g_ProofInProgress.target)[s - 1],
+                    (g_ProofInProgress.source)[s - 1])) continue;
+              }
+            }
+
+            /* Get the subproof at step s */
+            q = subproofLen(g_ProofInProgress.proof, s - 1);
+            if (proofStepUnk && q != 1) bug(1120); /* Consistency check */
+            nmbrLet(&nmbrTmp, nmbrSeg(g_ProofInProgress.proof, s - q + 1, s));
+
+            /* Improve only subproofs with unknown steps */
+            if (!nmbrElementIn(1, nmbrTmp, -(long)'?')) continue;
+
+            free_nmbrString(nmbrTmp); /* No longer needed - dealloc */
+
+            /* Check dummy variable status of step */
+            dummyVarIsoFlag = checkDummyVarIsolation(s - 1);
+                  /* 0=no dummy vars, 1=isolated dummy vars, 2=not isolated*/
+            if (dummyVarIsoFlag == 2) continue; /* Don't try to improve
+                                     dummy variables that aren't isolated */
+
+            if (dummyVarIsoFlag == 0
+                && (improveAllIter == 1
+                  || improveAllIter == 4)) {
+                /* No dummy vars */
+              /* Only use proveFloating if no dummy vars */
+              nmbrTmpPtr = proveFloating((g_ProofInProgress.target)[s - 1],
+                  g_proveStatement, improveDepth, s - 1,
+                  (char)p/*NO_DISTINCT*/,
+                  overrideFlag,
+                  mathboxFlag);
+            } else {
+              nmbrTmpPtr = NULL_NMBRSTRING; /* Init */
+            }
+            if (!nmbrLen(nmbrTmpPtr)) {
+              /* A proof for the step was not found with proveFloating(). */
+
+              /* Next, try REPLACE algorithm */
+              if ((searchAlg == 2 || searchAlg == 3)
+                  && ((improveAllIter == 2 && proofStepUnk)
+                    || (improveAllIter == 3 && !proofStepUnk)
+                    /*|| improveAllIter == 4*/)) {
+                nmbrTmpPtr = proveByReplacement(g_proveStatement,
+                  s - 1/*prfStep*/, /* 0 means step 1 */
+                  (char)p/*NO_DISTINCT*/, /* 1 means don't try stmts w/ $d's */
+                  dummyVarIsoFlag,
+                  (char)(searchAlg - 2),/*searchMethod: 0 or 1*/
+                  improveDepth,
+                  overrideFlag,
+                  mathboxFlag);
+
+              }
+              if (!nmbrLen(nmbrTmpPtr)) {
+                /* REPLACE algorithm also failed */
+                continue;
+              }
+            }
+
+            /* If q=1, subproof must be an unknown step, so don't bother to
+               delete it */
+            if (q > 1) deleteSubProof(s - 1);
+            addSubProof(nmbrTmpPtr, s - q);
+            assignKnownSteps(s - q, nmbrLen(nmbrTmpPtr));
+            print2("A proof of length %ld was found for step %ld.\n",
+                nmbrLen(nmbrTmpPtr), s);
+            if (nmbrLen(nmbrTmpPtr) || q != 1) n = s - q + 1;
+                                               /* Save earliest step changed */
+            free_nmbrString(nmbrTmpPtr);
+            g_proofChangedFlag = 1;
+            s = s - q + 1; /* Adjust step position to account for deleted subpr */
+          } /* Next step s */
+
+          if (g_proofChangedFlag) {
+            autoUnify(0); /* 0 = No 'Congrats' if done */
+          }
+
+          if (!g_proofChangedFlag
+              && ( (improveAllIter == 2 && !searchUnkSubproofs)
+                 || improveAllIter == 3
+                 || searchAlg == 1)) {
+            print2("No new subproofs were found.\n");
+            break; /* out of improveAllIter loop */
+          }
+          if (g_proofChangedFlag) {
+            g_proofChanged = 1; /* Cumulative flag */
+          }
+
+          if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
+            break; /* Proof is complete */
+          }
+
+          if (searchAlg == 1) break; /* Old algorithm does just 1st pass */
+
+        } /* Next improveAllIter */
+
+        if (g_proofChangedFlag) {
+          if (n > 0) {
+            /* n is the first step number changed.  It will be 0 if
+               the numbering didn't change e.g. a $e was assigned to
+               an unknown step. */
+            print2("Steps %ld and above have been renumbered.\n", n);
+          }
+          processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+        }
+        if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
+          /* This is a redundant call; its purpose is just to give
+             the message if the proof is complete */
+          autoUnify(1); /* 1 = 'Congrats' if done */
+        }
+
+      } /* End if IMPROVE ALL */
+
+      /* Automatically display new unknown steps
+         ???Future - add switch to enable/defeat this */
+      if (g_proofChangedFlag)
+        typeProof(g_proveStatement,
+            1 /*pipFlag*/,
+            0 /*startStep*/,
+            0 /*endStep*/,
+            0 /*endIndent*/,
+            1 /*essentialFlag*/,
+            0 /*renumberFlag*/,
+            1 /*unknownFlag*/,
+            0 /*notUnifiedFlag*/,
+            0 /*reverseFlag*/,
+            0 /*noIndentFlag*/,
+            0 /*splitColumn*/,
+            0 /*skipRepeatedSteps*/,
+            0 /*texFlag*/,
+            0 /*g_htmlFlag*/);
+
+      continue;
+
+    } /* cmdMatches("IMPROVE") */
+
+
+    if (cmdMatches("MINIMIZE_WITH")) {
+
+      printTime = 0;
+      if (switchPos("/ TIME") != 0) {
+        printTime = 1;
+      }
+      if (printTime == 1) {
+        getRunTime(&timeIncr);  /* This call just resets the time */
+      }
+
+      prntStatus = 0; /* Status flag to help determine messages
+                         0 = no statement was matched during scan (mainly for
+                             error message if user typo in label field)
+                         1 = a statement was matched but no shorter proof
+                         2 = shorter proof found */
+      verboseMode = (switchPos("/ VERBOSE") != 0); /* Verbose mode */
+
+      /* If no wildcard was used, switch to non-verbose mode
+        since there is no point to it and an annoying extra blank line
+        results */
+      if (!(instr(1, g_fullArg[1], "*") || instr(1, g_fullArg[1], "?"))) i = 1;
+
+      mayGrowFlag = (switchPos("/ MAY_GROW") != 0);
+                  /* Mode to replace even if it doesn't reduce proof length */
+      exceptPos = switchPos("/ EXCEPT"); /* Statement match to skip */
+
+      allowNewAxiomsMatchPos = switchPos("/ ALLOW_NEW_AXIOMS");
+      if (allowNewAxiomsMatchPos != 0) {
+        let(&allowNewAxiomsMatchList, g_fullArg[allowNewAxiomsMatchPos + 1]);
+      } else {
+        let(&allowNewAxiomsMatchList, "");
+      }
+
+      noNewAxiomsMatchPos = switchPos("/ NO_NEW_AXIOMS_FROM");
+      if (noNewAxiomsMatchPos != 0) {
+        let(&noNewAxiomsMatchList, g_fullArg[noNewAxiomsMatchPos + 1]);
+      } else {
+        let(&noNewAxiomsMatchList, "");
+      }
+
+      forbidMatchPos = switchPos("/ FORBID");
+      if (forbidMatchPos != 0) {
+        let(&forbidMatchList, g_fullArg[forbidMatchPos + 1]);
+      } else {
+        let(&forbidMatchList, "");
+      }
+
+      mathboxFlag = (switchPos("/ INCLUDE_MATHBOXES") != 0);
+
+      /* Flag to override any "usage locks" placed in the comment markup */
+      overrideFlag = (switchPos("/ OVERRIDE") != 0)
+           || g_globalDiscouragement == 0;
+
+      /* If a single statement is specified, don't bother to do certain
+         actions or print some of the messages */
+      hasWildCard = 0;
+      /* Set hasWildCard only when there are potentially > 1 matches */
+      if (strpbrk(g_fullArg[1], "*?~%,") != NULL) {
+        /* (See matches() function for processing of these)
+           "*" 0 or more char match
+           "?" 1 char match
+           "=" Most recent PROVE command statement  = one statement match
+           "~" Statement range
+           "%" List of modified statements
+           "#" Internal statement number            = one statement match
+           "@" Web page statement number            = one statement match
+           "," Comma-separated fields */
+        hasWildCard = 1;
+      }
+
+      g_proofChangedFlag = 0;
+
+      /* Always scan statements in current mathbox, even if
+         "/ INCLUDE_MATHBOXES" is omitted */
+
+      assignMathboxInfo(); /* In case it hasn't been assigned yet */
+      if (g_proveStatement > g_mathboxStmt) {
+        /* We're in a mathbox */
+        i = getMathboxNum(g_proveStatement);
+        if (i <= 0) bug(1130);
+        thisMathboxStartStmt = g_mathboxStart[i - 1];
+      } else {
+        thisMathboxStartStmt = g_mathboxStmt;
+      }
+
+      copyProofStruct(&saveOrigProof, g_ProofInProgress);
+
+      /* 12-Sep-2016 nm TODO replace this w/ compressedProofSize */
+      /* Get the current (original) compressed proof length
+         to compare it when a shorter non-compressed proof is found, to see
+         if the compressed proof also decreased in size */
+      nmbrLet(&nmbrSaveProof, g_ProofInProgress.proof);   /* Redundant? */
+      nmbrLet(&nmbrSaveProof, nmbrSquishProof(g_ProofInProgress.proof));
+      /* We only care about length; str1 will be discarded */
+      let(&str1, compressProof(nmbrSaveProof,
+          g_proveStatement, /* statement being proved */
+          0 /* Normal (not "fast") compression */
+          ));
+      origCompressedLength = (long)strlen(str1);
+      print2("Bytes refer to compressed proof size, "
+        "steps to uncompressed length.\n");
+
+      /* Scan forward, then reverse, then pick best result */
+      for (forwRevPass = 1; forwRevPass <= 2; forwRevPass++) {
+
+        if (forwRevPass == 1) {
+          if (hasWildCard) print2("Scanning forward through statements...\n");
+          forwFlag = 1;
+        } else {
+          /* If growth allowed, don't bother with reverse pass */
+          if (mayGrowFlag) break;
+          /* If nothing was found on forward pass, don't bother with rev pass */
+          if (!g_proofChangedFlag) break;
+          /* If only one statement was specified, don't bother with rev pass */
+          if (!hasWildCard) break;
+          print2("Scanning backward through statements...\n");
+          forwFlag = 0;
+          /* Save proof and length from 1st pass; re-initialize */
+          copyProofStruct(&save1stPassProof, g_ProofInProgress);
+          forwardLength = nmbrLen(g_ProofInProgress.proof);
+          forwardCompressedLength = oldCompressedLength;
+          /* Start over from original proof */
+          copyProofStruct(&g_ProofInProgress, saveOrigProof);
+        }
+
+        copyProofStruct(&saveProofForReverting, g_ProofInProgress);
+
+        oldCompressedLength = origCompressedLength;
+
+        /* If forwFlag is 0, scan from g_proveStatement-1 to 1
+           If forwFlag is 1, scan from 1 to g_proveStatement-1 */
+        for (k = forwFlag ? 1 : (g_proveStatement - 1);
+             k * (forwFlag ? 1 : -1) < (forwFlag ? g_proveStatement : 0);
+             k = k + (forwFlag ? 1 : -1)) {
+          if (!mathboxFlag && k >= g_mathboxStmt && k < thisMathboxStartStmt) {
+            continue;
+          }
+
+          if (g_Statement[k].type != (char)p_ && g_Statement[k].type != (char)a_)
+            continue;
+          if (!matchesList(g_Statement[k].labelName, g_fullArg[1], '*', '?'))
+            continue;
+
+          if (exceptPos != 0) {
+            /* Skip any match to the EXCEPT argument */
+            if (matchesList(g_Statement[k].labelName, g_fullArg[exceptPos + 1],
+                '*', '?'))
+              continue;
+          }
+
+          if (forbidMatchList[0]) { /* User provided a /FORBID list */
+            /* First, we check to make sure we're not trying a statement
+               in the forbidMatchList directly (traceProof() won't find
+               this) */
+            if (matchesList(g_Statement[k].labelName, forbidMatchList, '*', '?'))
+              continue;
+          }
+
+          /* Check to see if statement comment specified a usage
+             restriction */
+          if (!overrideFlag) {
+            if (getMarkupFlag(k, USAGE_DISCOURAGED)) {
+              continue;
+            }
+          }
+
+          /* Print individual labels */
+          if (prntStatus == 0) prntStatus = 1; /* Matched at least one */
+
+          m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+          nmbrLet(&nmbrTmp, g_ProofInProgress.proof);
+          minimizeProof(k /* trial statement */,
+              g_proveStatement /* statement being proved in MM-PA */,
+              (char)mayGrowFlag /* mayGrowFlag */);
+
+          n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
+          if (!nmbrEq(nmbrTmp, g_ProofInProgress.proof)) {
+            /* The proof got shorter (or it changed if MAY_GROW) */
+
+            /* Because of the slow speed of traceBack(),
+               we only want to check the /FORBID list in the relatively
+               rare case where a minimization occurred.  If the FORBID
+               list is matched, we then need to revert back to the
+               original proof. */
+            if (forbidMatchList[0]) { /* User provided a /FORBID list */
+              if (g_Statement[k].type == (char)p_) {
+                /* We only care about tracing $p statements */
+                /* See if the TRACE_BACK list includes a match to the
+                   /FORBID argument */
+                if (traceProof(k,
+                    0, /*essentialFlag*/
+                    0, /*axiomFlag*/
+                    forbidMatchList,
+                    "", /*traceToList*/
+                    1 /* testOnlyFlag */)) {
+                  /* Yes, a forbidden statement occurred in traceProof() */
+                  /* Revert the proof to before minimization */
+                  copyProofStruct(&g_ProofInProgress, saveProofForReverting);
+                  /* Skip further printout and flag setting */
+                  continue; /* Continue at 'Next k' loop end below */
+                }
+              }
+            }
+
+
+            /* Because of the slow speed of traceBack(),
+               we only want to check the /NO_NEW_AXIOMS_FROM list in the
+               relatively rare case where a minimization occurred.  If the
+               NO_NEW_AXIOMS_FROM condition applies, we then need to revert
+               back to the original proof. */
+            if (n == n + 0) {  /* By default, no new axioms are permitted */
+            /*if (noNewAxiomsMatchList[0]) {*/ /* User provided /NO_NEW_AXIOMS_FROM */
+              /* If we haven't called trace yet for the theorem being proved,
+                 do it now. */
+              if (traceProofFlags[0] == 0) {
+
+                /* traceProofWork() was written to use the SAVEd proof and
+                   not the proof in progress.  In order to use the proof in
+                   progress, we temporarily put the proof in progress into the
+                   (SAVEd) statement structure to trick traceProofWork() into using
+                   the proof in progress instead of the SAVEd proof */
+                /* Use the version of the proof in progress that existed *before* the
+                   MINIMIZE_WITH command was invoked */
+                nmbrLet(&nmbrSaveProof, nmbrSquishProof(saveProofForReverting.proof));
+                let(&str1, compressProof(nmbrSaveProof,
+                    g_proveStatement, /* statement being proved in MM-PA */
+                    0 /* Normal (not "fast") compression */
+                    ));
+                saveZappedProofSectionPtr
+                    = g_Statement[g_proveStatement].proofSectionPtr;
+                saveZappedProofSectionLen
+                    = g_Statement[g_proveStatement].proofSectionLen;
+                saveZappedProofSectionChanged
+                    = g_Statement[g_proveStatement].proofSectionChanged;
+                /* Set flag that this is not the original source */
+                g_Statement[g_proveStatement].proofSectionChanged = 1;
+                /* str1 has the new compressed trial proof after minimization */
+                /* Put space before and after to satisfy "space around token"
+                   requirement, to prevent possible error messages, and also
+                   add "$." since parseCompressedProof() expects it */
+                let(&str1, cat(" ", str1, " $.", NULL));
+                /* Don't include the "$." in the length */
+                g_Statement[g_proveStatement].proofSectionLen
+                    = (long)strlen(str1) - 2;
+                /* We don't deallocate previous proofSectionPtr content because
+                   we will recover it after the verifyProof() */
+                g_Statement[g_proveStatement].proofSectionPtr = str1;
+
+                traceProofWork(g_proveStatement,
+                    1 /*essentialFlag*/,
+                    "", /*traceToList*/
+                    &traceProofFlags, /* y/n list of flags */
+                    &nmbrTmp /* unproved list - ignored */);
+                free_nmbrString(nmbrTmp); /* Discard */
+
+                /* Restore the SAVEd proof */
+                g_Statement[g_proveStatement].proofSectionPtr
+                    = saveZappedProofSectionPtr;
+                g_Statement[g_proveStatement].proofSectionLen
+                    = saveZappedProofSectionLen;
+                g_Statement[g_proveStatement].proofSectionChanged
+                    = saveZappedProofSectionChanged;
+              }
+              free_vstring(traceTrialFlags);
+              traceProofWork(k, /* The trial statement */
+                  1 /*essentialFlag*/,
+                  "", /*traceToList*/
+                  &traceTrialFlags, /* Y/N list of flags */
+                  &nmbrTmp /* unproved list - ignored */);
+              free_nmbrString(nmbrTmp); /* Discard */
+              j = 1; /* 1 = ok to use trial statement */
+              for (i = 1; i < g_proveStatement; i++) {
+                if (g_Statement[i].type != (char)a_) continue; /* Not $a */
+                if (traceProofFlags[i] == 'Y') continue;
+                         /* If the axiom is already used by the proof, we
+                            don't care if the trial statement depends on it */
+                if (matchesList(g_Statement[i].labelName, allowNewAxiomsMatchList,
+                    '*', '?') == 1
+                      &&
+                    matchesList(g_Statement[i].labelName, noNewAxiomsMatchList,
+                    '*', '?') != 1) {
+                  /* If the axiom is in the list to allow and not in the list
+                     to disallow, we don't care if the trial statement depends
+                     on it */
+                  continue;
+                }
+                if (traceTrialFlags[i] == 'Y') {
+                  /* The trial statement uses an axiom that the current
+                     proof should avoid, so we abort it */
+                  j = 0; /* 0 = don't use trial statement */
+                  break;
+                }
+              } /* next i */
+              if (j == 0) {
+                /* A forbidden axiom is used by the trial proof */
+                /* Revert the proof to before minimization */
+                copyProofStruct(&g_ProofInProgress, saveProofForReverting);
+                /* Skip further printout and flag setting */
+                continue; /* Continue at 'Next k' loop end below */
+              }
+            } /* end if (true) */
+
+
+            /* Make sure the compressed proof length
+               decreased, otherwise revert.  Also, we will use the
+               compressed proof for the $d check next */
+            if (nmbrLen(g_Statement[k].reqDisjVarsA) || !mayGrowFlag) {
+              nmbrLet(&nmbrSaveProof, g_ProofInProgress.proof);
+              nmbrLet(&nmbrSaveProof, nmbrSquishProof(g_ProofInProgress.proof));
+              let(&str1, compressProof(nmbrSaveProof,
+                  g_proveStatement, /* statement being proved in MM-PA */
+                  0 /* Normal (not "fast") compression */
+                  ));
+              newCompressedLength = (long)strlen(str1);
+              if (!mayGrowFlag && newCompressedLength > oldCompressedLength) {
+                /* The compressed proof length increased, so don't use it.
+                   (If it stayed the same, we will use it because the uncompressed
+                   length did decrease.) */
+                /* Revert the proof to before minimization */
+                if (verboseMode) {
+                  print2(
+ "Reverting \"%s\": Uncompressed steps:  old = %ld, new = %ld\n",
+                      g_Statement[k].labelName,
+                      m, n );
+                  print2(
+ "    but compressed size:  old = %ld bytes, new = %ld bytes\n",
+                      oldCompressedLength, newCompressedLength);
+                }
+                copyProofStruct(&g_ProofInProgress, saveProofForReverting);
+                /* Skip further printout and flag setting */
+                continue; /* Continue at 'Next k' loop end below */
+              }
+            } /* if (nmbrLen(g_Statement[k].reqDisjVarsA) || !mayGrowFlag) */
+
+            /* Make sure there are no $d violations, otherwise revert */
+            /* This requires the str1 from above */
+            if (nmbrLen(g_Statement[k].reqDisjVarsA)) {
+              /* There is currently no way to verify a proof that doesn't
+                 read and parse the source directly.  This should be
+                 changed in the future to make the program more modular.  But
+                 for now, we temporarily zap the source with new compressed
+                 proof and see if there are any $d violations by looking at
+                 the error message output */
+              saveZappedProofSectionPtr
+                  = g_Statement[g_proveStatement].proofSectionPtr;
+              saveZappedProofSectionLen
+                  = g_Statement[g_proveStatement].proofSectionLen;
+
+              saveZappedProofSectionChanged
+                  = g_Statement[g_proveStatement].proofSectionChanged;
+              /* Set flag that this is not the original source */
+              g_Statement[g_proveStatement].proofSectionChanged = 1;
+              /* str1 has the new compressed trial proof after minimization */
+              /* Put space before and after to satisfy "space around token"
+                 requirement, to prevent possible error messages, and also
+                 add "$." since parseCompressedProof() expects it */
+              let(&str1, cat(" ", str1, " $.", NULL));
+              /* Don't include the "$." in the length */
+              g_Statement[g_proveStatement].proofSectionLen = (long)strlen(str1) - 2;
+              /* We don't deallocate previous proofSectionPtr content because
+                 we will recover it after the verifyProof() */
+              g_Statement[g_proveStatement].proofSectionPtr = str1;
+
+              g_outputToString = 1; /* Suppress error messages */
+              /* parseProof, verifyProof, cleanWrkProof must be
+                 called in sequence to assign the g_WrkProof structure, verify
+                 the proof, and deallocate the g_WrkProof structure.  Either none
+                 of them or all of them must be called. */
+              parseProof(g_proveStatement);
+              verifyProof(g_proveStatement); /* Must be called even if error
+                                  occurred in parseProof, to init RPN stack
+                                  for cleanWrkProof() */
+              /* don't change proof if there is an error
+                 (which could be pre-existing). */
+              i = (g_WrkProof.errorSeverity > 1);
+              /**** Here we look at the screen output sent to a string.
+                    This is rather crude, and someday the ability to
+                    check proofs and $d violations should be modularized *****/
+              j = instr(1, g_printString,
+                  "There is a disjoint variable ($d) violation");
+              g_outputToString = 0; /* Restore to normal output */
+              free_vstring(g_printString); /* Clear out the stored error messages */
+              cleanWrkProof(); /* Deallocate verifyProof storage */
+              g_Statement[g_proveStatement].proofSectionPtr
+                  = saveZappedProofSectionPtr;
+              g_Statement[g_proveStatement].proofSectionLen
+                  = saveZappedProofSectionLen;
+              g_Statement[g_proveStatement].proofSectionChanged
+                  = saveZappedProofSectionChanged;
+              if (i != 0 || j != 0) {
+                /* There was a verify proof error (j!=0) or $d violation (i!=0)
+                   so don't used minimized proof */
+                /* Revert the proof to before minimization */
+                copyProofStruct(&g_ProofInProgress, saveProofForReverting);
+                /* Skip further printout and flag setting */
+                continue; /* Continue at 'Next k' loop end below */
+              }
+            } /* if (nmbrLen(g_Statement[k].reqDisjVarsA)) */
+
+            /* Warn user if a discouraged statement is overridden */
+            if (getMarkupFlag(k, USAGE_DISCOURAGED)) {
+              if (!overrideFlag) bug(1126);
+              /* print2("\n"); */ /* Enable for more emphasis */
+              print2(
+                  ">>> ?Warning: Overriding discouraged usage of \"%s\".\n",
+                  g_Statement[k].labelName);
+              /* print2("\n"); */ /* Enable for more emphasis */
+            }
+
+            if (!mayGrowFlag) {
+              /* Note:  this is the length BEFORE indentation and wrapping,
+                 so it is less than SHOW PROOF ... /SIZE */
+              if (newCompressedLength < oldCompressedLength) {
+                print2(
+     "Proof of \"%s\" decreased from %ld to %ld bytes using \"%s\".\n",
+                    g_Statement[g_proveStatement].labelName,
+                    oldCompressedLength, newCompressedLength,
+                    g_Statement[k].labelName);
+              } else {
+                if (newCompressedLength > oldCompressedLength) bug(1123);
+                print2(
+     "Proof of \"%s\" stayed at %ld bytes using \"%s\".\n",
+                    g_Statement[g_proveStatement].labelName,
+                    oldCompressedLength,
+                    g_Statement[k].labelName);
+                print2(
+    "    (Uncompressed steps decreased from %ld to %ld).\n",
+                    m, n );
+              }
+              /* (We don't care about compressed length if MAY_GROW) */
+              oldCompressedLength = newCompressedLength;
+            }
+
+            if (n < m && (mayGrowFlag || verboseMode)) {
+              print2(
+      "%sProof of \"%s\" decreased from %ld to %ld steps using \"%s\".\n",
+                (mayGrowFlag ? "" : "    "),
+                g_Statement[g_proveStatement].labelName,
+                m, n, g_Statement[k].labelName);
+            }
+            /* MAY_GROW possibility */
+            if (m < n) print2(
+      "Proof of \"%s\" increased from %ld to %ld steps using \"%s\".\n",
+                g_Statement[g_proveStatement].labelName,
+                m, n, g_Statement[k].labelName);
+            /* MAY_GROW possibility */
+            if (m == n) print2(
+                "Proof of \"%s\" remained at %ld steps using \"%s\".\n",
+                g_Statement[g_proveStatement].labelName,
+                m, g_Statement[k].labelName);
+
+            /* See if it's in another mathbox; if so, let user know */
+            assignMathboxInfo();
+            if (k > g_mathboxStmt && g_proveStatement > g_mathboxStmt) {
+              if (k < g_mathboxStart[getMathboxNum(g_proveStatement) - 1]) {
+                printLongLine(cat("\"", g_Statement[k].labelName,
+                      "\" is in the mathbox for ",
+                      g_mathboxUser[getMathboxNum(k) - 1], ".",
+                      NULL),
+                    "  ", " ");
+              }
+            }
+
+            prntStatus = 2; /* Found one */
+            g_proofChangedFlag = 1;
+
+            /* Save the changed proof in case we have to restore
+               it later */
+            copyProofStruct(&saveProofForReverting, g_ProofInProgress);
+
+          }
+
+        } /* Next k (statement) */
+
+        if (g_proofChangedFlag && forwRevPass == 2) {
+          /* Check whether the reverse pass found a better proof than the
+             forward pass */
+          if (verboseMode) {
+            print2(
+"Forward vs. backward: %ld vs. %ld bytes; %ld vs. %ld steps\n",
+                      forwardCompressedLength,
+                      oldCompressedLength,
+                      forwardLength,
+                      nmbrLen(g_ProofInProgress.proof));
+          }
+          if (oldCompressedLength < forwardCompressedLength
+               || (oldCompressedLength == forwardCompressedLength &&
+                   nmbrLen(g_ProofInProgress.proof) < forwardLength)) {
+            /* The reverse pass was better */
+            print2("The backward scan results were used.\n");
+          } else {
+            copyProofStruct(&g_ProofInProgress, save1stPassProof);
+            print2("The forward scan results were used.\n");
+          }
+        }
+
+      } /* next forwRevPass */
+
+      if (prntStatus == 1 && !mayGrowFlag)
+        print2("No shorter proof was found.\n");
+      if (prntStatus == 1 && mayGrowFlag)
+        print2("The proof was not changed.\n");
+      if (!prntStatus /* && !noDistinctFlag */)
+        print2("?No earlier %s$p or $a label matches \"%s\".\n",
+            (overrideFlag ? "" : "(allowed) "),
+            g_fullArg[1]);
+      if (!mathboxFlag && g_proveStatement >= g_mathboxStmt) {
+        print2(
+  "(Other mathboxes were not checked.  Use / INCLUDE_MATHBOXES to include them.)\n");
+      }
+
+      if (printTime == 1) {
+        getRunTime(&timeIncr);
+        print2("MINIMIZE_WITH run time = %7.2f sec for \"%s\"\n", timeIncr,
+            g_Statement[g_proveStatement].labelName);
+      }
+
+      free_vstring(str1); /* Deallocate memory */
+      free_nmbrString(nmbrSaveProof); /* Deallocate memory */
+
+      /* Clear these Y/N trace strings unconditionally since new axioms are no
+        longer  allowed by default, so they may become set regardless of
+        qualifiers */
+      free_vstring(traceProofFlags); /* Deallocate memory */
+      free_vstring(traceTrialFlags); /* Deallocate memory */
+
+      if (allowNewAxiomsMatchList[0]) { /* User provided /NO_NEW_AXIOMS_FROM list */
+        free_vstring(allowNewAxiomsMatchList); /* Deallocate memory */
+      }
+
+      if (noNewAxiomsMatchList[0]) { /* User provided /ALLOW_NEW_AXIOMS list */
+        free_vstring(noNewAxiomsMatchList); /* Deallocate memory */
+      }
+
+      if (forbidMatchList[0]) { /* User provided a /FORBID list */
+        free_vstring(forbidMatchList); /* Deallocate memory */
+      }
+
+      deallocProofStruct(&saveProofForReverting); /* Deallocate memory */
+      deallocProofStruct(&saveOrigProof); /* Deallocate memory */
+      deallocProofStruct(&save1stPassProof); /* Deallocate memory */
+
+      if (g_proofChangedFlag) {
+        g_proofChanged = 1; /* Cumulative flag */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+      }
+      continue;
+
+    } /* End if MINIMIZE_WITH */
+
+
+    if (cmdMatches("EXPAND")) {
+
+      g_proofChangedFlag = 0;
+      nmbrLet(&nmbrSaveProof, g_ProofInProgress.proof);
+      s = compressedProofSize(nmbrSaveProof, g_proveStatement);
+
+      for (i = g_proveStatement - 1; i >= 1; i--) {
+        if (g_Statement[i].type != (char)p_) continue; /* Not a $p */
+        if (!matchesList(g_Statement[i].labelName, g_fullArg[1], '*', '?')) {
+          continue;
+        }
+        sourceStatement = i;
+
+        nmbrTmp = expandProof(nmbrSaveProof, sourceStatement);
+
+        if (!nmbrEq(nmbrTmp, nmbrSaveProof)) {
+          g_proofChangedFlag = 1;
+          n = compressedProofSize(nmbrTmp, g_proveStatement);
+          printLongLine(cat("Proof of \"",
+            g_Statement[g_proveStatement].labelName, "\" ",
+            (s == n ? cat("stayed at ", str((double)s), NULL)
+                : cat((s < n ? "increased from " : " decreased from "),
+                    str((double)s), " to ", str((double)n), NULL)),
+            " bytes after expanding \"",
+            g_Statement[sourceStatement].labelName, "\".", NULL), " ", " ");
+          s = n;
+          nmbrLet(&nmbrSaveProof, nmbrTmp);
+        }
+      }
+
+      if (g_proofChangedFlag) {
+        g_proofChanged = 1; /* Cumulative flag */
+        /* Clear the existing proof structure */
+        deallocProofStruct(&g_ProofInProgress);
+        /* Then rebuild proof structure from new proof nmbrTmp */
+        initProofStruct(&g_ProofInProgress, nmbrTmp, g_proveStatement);
+        /* Save the new proof structure on the undo stack */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+      } else {
+        print2("No expansion occurred.  The proof was not changed.\n");
+      }
+      free_nmbrString(nmbrSaveProof);
+      free_nmbrString(nmbrTmp);
+      continue;
+    } /* EXPAND */
+
+
+    if (cmdMatches("DELETE STEP") || (cmdMatches("DELETE ALL"))) {
+
+      if (cmdMatches("DELETE STEP")) {
+        s = (long)val(g_fullArg[2]); /* Step number */
+      } else {
+        s = nmbrLen(g_ProofInProgress.proof);
+      }
+      if ((g_ProofInProgress.proof)[s - 1] == -(long)'?') {
+        print2("?Step %ld is unknown and cannot be deleted.\n", s);
+        continue;
+      }
+      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+      if (s > m || s < 1) {
+        print2("?The step must be in the range from 1 to %ld.\n", m);
+        continue;
+      }
+
+      deleteSubProof(s - 1);
+      n = nmbrLen(g_ProofInProgress.proof); /* New proof length */
+      if (m == n) {
+        print2("Step %ld was deleted.\n", s);
+      } else {
+        if (n > 1) {
+          printLongLine(cat("A ", str((double)(m - n + 1)),
+              "-step subproof at step ", str((double)s),
+              " was deleted.  Steps ", str((double)s), ":",
+              str((double)m), " are now ", str((double)(s - m + n)), ":",
+              str((double)n), ".",
+              NULL),
+              "", " ");
+        } else {
+          print2("The entire proof was deleted.\n");
+        }
+      }
+
+      /* Automatically display new unknown steps
+         ???Future - add switch to enable/defeat this */
+      typeProof(g_proveStatement,
+          1 /*pipFlag*/,
+          0 /*startStep*/,
+          0 /*endStep*/,
+          0 /*endIndent*/,
+          1 /*essentialFlag*/,
+          0 /*renumberFlag*/,
+          1 /*unknownFlag*/,
+          0 /*notUnifiedFlag*/,
+          0 /*reverseFlag*/,
+          0 /*noIndentFlag*/,
+          0 /*splitColumn*/,
+          0 /*skipRepeatedSteps*/,
+          0 /*texFlag*/,
+          0 /*g_htmlFlag*/);
+
+      g_proofChanged = 1; /* Cumulative flag */
+      processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+
+      continue;
+
+    }
+
+    if (cmdMatches("DELETE FLOATING_HYPOTHESES")) {
+
+      /* Get the essential step flags */
+      nmbrLet(&nmbrTmp, nmbrGetEssential(g_ProofInProgress.proof));
+
+      m = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+
+      n = 0; /* Earliest step that changed */
+      g_proofChangedFlag = 0;
+
+      for (s = m; s > 0; s--) {
+
+        /* Skip essential steps and unknown steps */
+        if (nmbrTmp[s - 1] == 1) continue; /* Not floating */
+        if ((g_ProofInProgress.proof)[s - 1] == -(long)'?') continue; /* Unknown */
+
+        /* Get the subproof length at step s */
+        q = subproofLen(g_ProofInProgress.proof, s - 1);
+
+        deleteSubProof(s - 1);
+
+        n = s - q + 1; /* Save earliest step changed */
+        g_proofChangedFlag = 1;
+        s = s - q + 1; /* Adjust step position to account for deleted subpr */
+      } /* Next step s */
+
+      if (g_proofChangedFlag) {
+        print2("All floating-hypothesis steps were deleted.\n");
+
+        if (n) {
+          print2("Steps %ld and above have been renumbered.\n", n);
+        }
+
+        /* Automatically display new unknown steps
+           ???Future - add switch to enable/defeat this */
+        typeProof(g_proveStatement,
+            1 /*pipFlag*/,
+            0 /*startStep*/,
+            0 /*endStep*/,
+            0 /*endIndent*/,
+            1 /*essentialFlag*/,
+            0 /*renumberFlag*/,
+            1 /*unknownFlag*/,
+            0 /*notUnifiedFlag*/,
+            0 /*reverseFlag*/,
+            0 /*noIndentFlag*/,
+            0 /*splitColumn*/,
+            0 /*skipRepeatedSteps*/,
+            0 /*texFlag*/,
+            0 /*g_htmlFlag*/);
+
+        g_proofChanged = 1; /* Cumulative flag */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+      } else {
+        print2("?There are no floating-hypothesis steps to delete.\n");
+      }
+
+      continue;
+
+    } /* End if DELETE FLOATING_HYPOTHESES */
+
+    if (cmdMatches("INITIALIZE")) {
+
+      if (cmdMatches("INITIALIZE ALL")) {
+        i = nmbrLen(g_ProofInProgress.proof);
+
+        /* Reset the dummy variable counter (all will be refreshed) */
+        g_pipDummyVars = 0;
+
+        /* Initialize all steps */
+        for (j = 0; j < i; j++) {
+          initStep(j);
+        }
+
+        /* Assign known subproofs */
+        assignKnownSubProofs();
+
+        print2("All steps have been initialized.\n");
+        g_proofChanged = 1; /* Cumulative flag */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+        continue;
+      }
+
+      if (cmdMatches("INITIALIZE USER")) {
+        i = nmbrLen(g_ProofInProgress.proof);
+        /* Delete all LET STEP assignments */
+        for (j = 0; j < i; j++) {
+          nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[j])),
+              NULL_NMBRSTRING);
+        }
+        print2(
+      "All LET STEP user assignments have been initialized (i.e. deleted).\n");
+        g_proofChanged = 1; /* Cumulative flag */
+        processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+        continue;
+      }
+
+      s = (long)val(g_fullArg[2]); /* Step number */
+      if (s > nmbrLen(g_ProofInProgress.proof) || s < 1) {
+        print2("?The step must be in the range from 1 to %ld.\n",
+            nmbrLen(g_ProofInProgress.proof));
+        continue;
+      }
+
+      initStep(s - 1);
+
+      /* Also delete LET STEPs, per HELP INITIALIZE */
+      nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[s - 1])),
+              NULL_NMBRSTRING);
+
+      print2("Step %ld and its hypotheses have been initialized.\n", s);
+
+      g_proofChanged = 1; /* Cumulative flag */
+      processUndoStack(&g_ProofInProgress, PUS_PUSH, g_fullArgString, 0);
+      continue;
+
+    }
+
+
+    if (cmdMatches("SEARCH")) {
+      if (switchPos("/ ALL")) {
+        m = 1;  /* Include $e, $f statements */
+      } else {
+        m = 0;  /* Show $a, $p only */
+      }
+
+      if (switchPos("/ JOIN")) {
+        joinFlag = 1;  /* Join $e's to $a,$p for matching */
+      } else {
+        joinFlag = 0;  /* Search $a,$p by themselves */
+      }
+
+      if (switchPos("/ COMMENTS")) {
+        n = 1;  /* Search comments */
+      } else {
+        n = 0;  /* Search statement math symbols */
+      }
+
+      let(&str1, g_fullArg[2]); /* String to match */
+
+      if (n) { /* COMMENTS switch */
+        /* Trim leading, trailing spaces; reduce white space to space;
+           convert to upper case */
+        let(&str1, edit(str1, 8 + 16 + 128 + 32));
+      } else { /* No COMMENTS switch */
+        /* Trim leading, trailing spaces; reduce white space to space */
+        let(&str1, edit(str1, 8 + 16 + 128));
+
+        /* Change all spaces to double spaces */
+        q = (long)strlen(str1);
+        let(&str3, space(q + q));
+        s = 0;
+        for (p = 0; p < q; p++) {
+          str3[p + s] = str1[p];
+          if (str1[p] == ' ') {
+            s++;
+            str3[p + s] = str1[p];
+          }
+        }
+        let(&str1, left(str3, q + s));
+
+        /* Find single-character-match wildcard argument "$?"
+           (or "?" for convenience).  Use ASCII 3 for the exactly-1-char
+           wildcard character.  This is a single-character
+           match, not a single-token match:  we need "??" to match "ph". */
+        while (1) {
+          p = instr(1, str1, "$?");
+          if (!p) break;
+          let(&str1, cat(left(str1, p - 1), chr(3), right(str1, p + 2), NULL));
+        }
+        /* Allow just "?" for convenience. */
+        while (1) {
+          p = instr(1, str1, "?");
+          if (!p) break;
+          let(&str1, cat(left(str1, p - 1), chr(3), right(str1, p + 1), NULL));
+        }
+
+        /* Change wildcard to ASCII 2 (to be different from printable chars) */
+        /* 1-Mar-02 nm - (Why are we matching with and without space? I'm not sure.) */
+        /* 30-Jan-06 nm Answer:  We need the double-spacing, and the removal
+           of space in the "with spaces" case, so that "ph $ ph" will match
+           "ph  ph" (0-token case) - "ph  $  ph" won't match this in the
+           (character-based, not token-based) matches().  The "with spaces"
+           case is for matching whole tokens, whereas the "without spaces"
+           case is for matching part of a token. */
+        while (1) {
+          p = instr(1, str1, " $* ");
+          if (!p) break;
+          /* This removes the space before and after the $* */
+          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 4), NULL));
+        }
+        while (1) {
+          p = instr(1, str1, "$*");
+          if (!p) break;
+          /* This simply replaces $* with chr(2) */
+          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 2), NULL));
+        }
+        /* Also allow a plain $ as a wildcard, for convenience. */
+        while (1) {
+          p = instr(1, str1, " $ ");
+          if (!p) break;
+          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 3), NULL));
+        }
+        while (1) {
+          /* Note: the "$" shortcut must be done last to avoid picking up
+             "$*" and "$?". */
+          p = instr(1, str1, "$");
+          if (!p) break;
+          let(&str1, cat(left(str1, p - 1), chr(2), right(str1, p + 1), NULL));
+        }
+
+        /* Add wildcards to beginning and end to match middle of any string */
+        let(&str1, cat(chr(2), " ", str1, " ", chr(2), NULL));
+      } /* End no COMMENTS switch */
+
+      for (i = 1; i <= g_statements; i++) {
+        if (!g_Statement[i].labelName[0]) continue; /* No label */
+        if (!m && g_Statement[i].type != (char)p_ &&
+            g_Statement[i].type != (char)a_) {
+          continue; /* No /ALL switch */
+        }
+        if (!matchesList(g_Statement[i].labelName, g_fullArg[1], '*', '?'))
+          continue;
+        if (n) { /* COMMENTS switch */
+          free_vstring(str2);
+          str2 = getDescription(i); /* str2 must be deallocated here */
+          /* Strip linefeeds and reduce spaces; cvt to uppercase */
+          j = instr(1, edit(str2, 4 + 8 + 16 + 128 + 32), str1);
+          if (!j) { /* No match */
+            free_vstring(str2);
+            continue;
+          }
+          /* Strip linefeeds and reduce spaces */
+          let(&str2, edit(str2, 4 + 8 + 16 + 128));
+          j = j + ((long)strlen(str1) / 2); /* Center of match location */
+          p = g_screenWidth - 7 - (long)strlen(str((double)i)) -
+            (long)strlen(g_Statement[i].labelName);
+                        /* Longest comment portion that will fit in one line */
+          q = (long)strlen(str2); /* Length of comment */
+          if (q <= p) { /* Use entire comment */
+            let(&str3, str2);
+          } else {
+            if (q - j <= p / 2) { /* Use right part of comment */
+              let(&str3, cat("...", right(str2, q - p + 4), NULL));
+            } else {
+              if (j <= p / 2) { /* Use left part of comment */
+                let(&str3, cat(left(str2, p - 3), "...", NULL));
+              } else { /* Use middle part of comment */
+                let(&str3, cat("...", mid(str2, j - p / 2, p - 6), "...",
+                    NULL));
+              }
+            }
+          }
+          print2("%s\n", cat(str((double)i), " ", g_Statement[i].labelName, " $",
+              chr(g_Statement[i].type), " \"", str3, "\"", NULL));
+          free_vstring(str2);
+        } else { /* No COMMENTS switch */
+          let(&str2,nmbrCvtMToVString(g_Statement[i].mathString));
+
+          tmpFlag = 0; /* Flag that $p or $a is already in string */
+          if (joinFlag && (g_Statement[i].type == (char)p_ ||
+              g_Statement[i].type == (char)a_)) {
+            /* If $a or $p, prepend $e's to string to match */
+            k = nmbrLen(g_Statement[i].reqHypList);
+            for (j = k - 1; j >= 0; j--) {
+              p = g_Statement[i].reqHypList[j];
+              if (g_Statement[p].type == (char)e_) {
+                let(&str2, cat("$e ",
+                    nmbrCvtMToVString(g_Statement[p].mathString),
+                    tmpFlag ? "" : cat(" $", chr(g_Statement[i].type), NULL),
+                    " ", str2, NULL));
+                tmpFlag = 1; /* Flag that a $p or $a was added */
+              }
+            }
+          }
+
+          /* Change all spaces to double spaces */
+          q = (long)strlen(str2);
+          let(&str3, space(q + q));
+          s = 0;
+          for (p = 0; p < q; p++) {
+            str3[p + s] = str2[p];
+            if (str2[p] == ' ') {
+              s++;
+              str3[p + s] = str2[p];
+            }
+          }
+          let(&str2, left(str3, q + s));
+
+          let(&str2, cat(" ", str2, " ", NULL));
+          /* We should use matches() and not matchesList() here, because
+             commas can be legal token characters in math symbols */
+          if (!matches(str2, str1, 2/* ascii 2 0-or-more-token match char*/,
+              3/* ascii 3 single-token-match char*/))
+            continue;
+          let(&str2, edit(str2, 8 + 16 + 128)); /* Trim leading, trailing
+              spaces; reduce white space to space */
+          printLongLine(cat(str((double)i)," ",
+              g_Statement[i].labelName,
+              tmpFlag ? "" : cat(" $", chr(g_Statement[i].type), NULL),
+              " ", str2,
+              NULL), "    ", " ");
+        } /* End no COMMENTS switch */
+      } /* Next i */
+      continue;
+    }
+
+
+    if (cmdMatches("SET ECHO")) {
+      if (cmdMatches("SET ECHO ON")) {
+        g_commandEcho = 1;
+        print2("!SET ECHO ON\n");
+        print2("Command line echoing is now turned on.\n");
+      } else {
+        g_commandEcho = 0;
+        print2("Command line echoing is now turned off.\n");
+      }
+      continue;
+    }
+
+    if (cmdMatches("SET MEMORY_STATUS")) {
+      if (cmdMatches("SET MEMORY_STATUS ON")) {
+        print2("Memory status display has been turned on.\n");
+        print2("This command is intended for debugging purposes only.\n");
+        g_memoryStatus = 1;
+      } else {
+        g_memoryStatus = 0;
+        print2("Memory status display has been turned off.\n");
+      }
+      continue;
+    }
+
+
+    if (cmdMatches("SET JEREMY_HENTY_FILTER")) {
+      if (cmdMatches("SET JEREMY_HENTY_FILTER ON")) {
+        print2("The unification equivalence filter has been turned on.\n");
+        print2("This command is intended for debugging purposes only.\n");
+        g_hentyFilter = 1;
+      } else {
+        print2("This command is intended for debugging purposes only.\n");
+        print2("The unification equivalence filter has been turned off.\n");
+        g_hentyFilter = 0;
+      }
+      continue;
+    }
+
+
+    if (cmdMatches("SET EMPTY_SUBSTITUTION")) {
+      if (cmdMatches("SET EMPTY_SUBSTITUTION ON")) {
+        g_minSubstLen = 0;
+        print2("Substitutions with empty symbol sequences is now allowed.\n");
+        continue;
+      }
+      if (cmdMatches("SET EMPTY_SUBSTITUTION OFF")) {
+        g_minSubstLen = 1;
+        printLongLine(cat("The ability to substitute empty expressions",
+            " for variables  has been turned off.  Note that this may",
+            " make the Proof Assistant too restrictive in some cases.",
+            NULL),
+            "", " ");
+        continue;
+      }
+    }
+
+
+    if (cmdMatches("SET SEARCH_LIMIT")) {
+      s = (long)val(g_fullArg[2]); /* Timeout value */
+      print2("IMPROVE search limit has been changed from %ld to %ld\n",
+          g_userMaxProveFloat, s);
+      g_userMaxProveFloat = s;
+      continue;
+    }
+
+    if (cmdMatches("SET WIDTH")) {
+      s = (long)val(g_fullArg[2]); /* Screen width value */
+
+      /* TODO: figure out why s=2 crashes program! */
+      if (s < 3) s = 3; /* Less than 3 may cause a segmentation fault */
+      i = g_screenWidth;
+      g_screenWidth = s;
+      print2("Screen width has been changed from %ld to %ld.\n",
+          i, s);
+      continue;
+    }
+
+
+    if (cmdMatches("SET HEIGHT")) {
+      s = (long)val(g_fullArg[2]); /* Screen height value */
+      if (s < 2) s = 2;  /* Less than 2 makes no sense */
+      i = g_screenHeight;
+      g_screenHeight = s - 1;
+      print2("Screen height has been changed from %ld to %ld.\n",
+          i + 1, s);
+      /* g_screenHeight is one less than the physical screen to account for the
+         prompt line after pausing. */
+      continue;
+    }
+
+
+    if (cmdMatches("SET DISCOURAGEMENT")) {
+      if (!strcmp(g_fullArg[2], "ON")) {
+        g_globalDiscouragement = 1;
+        print2("\"(...is discouraged.)\" markup tags are now honored.\n");
+      } else if (!strcmp(g_fullArg[2], "OFF")) {
+        print2(
+    "\"(...is discouraged.)\" markup tags are no longer honored.\n");
+        /* print2("\n"); */ /* Enable for more emphasis */
+        print2(
+">>> ?Warning: This setting is intended for advanced users only.  Please turn\n");
+        print2(
+">>> it back ON if you are not intimately familiar with this database.\n");
+        /* print2("\n"); */ /* Enable for more emphasis */
+        g_globalDiscouragement = 0;
+      } else {
+        bug(1129);
+      }
+      continue;
+    }
+
+
+    if (cmdMatches("SET CONTRIBUTOR")) {
+      print2("\"Contributed by...\" name was changed from \"%s\" to \"%s\"\n",
+          g_contributorName, g_fullArg[2]);
+      let(&g_contributorName, g_fullArg[2]);
+      continue;
+    }
+
+
+    if (cmdMatches("SET ROOT_DIRECTORY")) {
+      let(&str1, g_rootDirectory); /* Save previous one */
+      let(&g_rootDirectory, edit(g_fullArg[2], 2/*discard spaces,tabs*/));
+      if (g_rootDirectory[0] != 0) {  /* Not an empty directory path */
+        /* Add trailing "/" to g_rootDirectory if missing */
+        if (instr(1, g_rootDirectory, "\\") != 0
+            || instr(1, g_input_fn, "\\") != 0
+            || instr(1, g_output_fn, "\\") != 0 ) {
+          /* Using Windows-style path (not really supported, but at least
+             make full path consistent) */
+          if (g_rootDirectory[strlen(g_rootDirectory) - 1] != '\\') {
+            let(&g_rootDirectory, cat(g_rootDirectory, "\\", NULL));
+          }
+        } else {
+          if (g_rootDirectory[strlen(g_rootDirectory) - 1] != '/') {
+            let(&g_rootDirectory, cat(g_rootDirectory, "/", NULL));
+          }
+        }
+      }
+      if (strcmp(str1, g_rootDirectory)) {
+        print2("Root directory was changed from \"%s\" to \"%s\"\n",
+            str1, g_rootDirectory);
+      }
+      free_vstring(str1);
+      continue;
+    }
+
+
+    if (cmdMatches("SET UNDO")) {
+      s = (long)val(g_fullArg[2]); /* Maximum UNDOs */
+      if (s < 0) s = 0;  /* Less than 0 UNDOs makes no sense */
+      /* Reset the stack size if it changed */
+      if (processUndoStack(NULL, PUS_GET_SIZE, "", 0) != s) {
+        print2(
+            "The maximum number of UNDOs was changed from %ld to %ld\n",
+            processUndoStack(NULL, PUS_GET_SIZE, "", 0), s);
+        processUndoStack(NULL, PUS_NEW_SIZE, "", s);
+        if (g_PFASmode == 1) {
+          /* If we're in the Proof Assistant, assign the first stack
+             entry with the current proof (the stack was erased) */
+          processUndoStack(&g_ProofInProgress, PUS_PUSH, "", 0);
+        }
+      } else {
+        print2("The maximum number of UNDOs was not changed.\n");
+      }
+      continue;
+    }
+
+
+    if (cmdMatches("SET UNIFICATION_TIMEOUT")) {
+      s = (long)val(g_fullArg[2]); /* Timeout value */
+      print2("Unification timeout has been changed from %ld to %ld\n",
+          g_userMaxUnifTrials,s);
+      g_userMaxUnifTrials = s;
+      continue;
+    }
+
+
+    if (cmdMatches("OPEN LOG")) {
+        /* Open a log file */
+        let(&g_logFileName, g_fullArg[2]);
+        g_logFilePtr = fSafeOpen(g_logFileName, "w", 0/*noVersioningFlag*/);
+        if (!g_logFilePtr) continue; /* Couldn't open it (err msg was provided) */
+        g_logFileOpenFlag = 1;
+        print2("The log file \"%s\" was opened %s %s.\n",g_logFileName,
+            date(),time_());
+        continue;
+    }
+
+    if (cmdMatches("CLOSE LOG")) {
+        /* Close the log file */
+        if (!g_logFileOpenFlag) {
+          print2("?Sorry, there is no log file currently open.\n");
+        } else {
+          print2("The log file \"%s\" was closed %s %s.\n",g_logFileName,
+              date(),time_());
+          fclose(g_logFilePtr);
+          g_logFileOpenFlag = 0;
+        }
+        free_vstring(g_logFileName);
+        continue;
+    }
+
+    if (cmdMatches("OPEN TEX")) {
+      if (g_texDefsRead) {
+        if (g_htmlFlag) {
+          /* Actually it isn't clear to me this is still the case, but
+             to be safe I left it in */
+          print2("?You cannot use both LaTeX and HTML in the same session.\n");
+          print2(
+              "?You must EXIT and restart Metamath to switch to the other.\n");
+          continue;
+        }
+      }
+
+      /* Open a TeX file */
+      let(&g_texFileName,g_fullArg[2]);
+      if (switchPos("/ NO_HEADER")) {
+        texHeaderFlag = 0;
+      } else {
+        texHeaderFlag = 1;
+      }
+
+      if (switchPos("/ OLD_TEX")) {
+        g_oldTexFlag = 1;
+      } else {
+        g_oldTexFlag = 0;
+      }
+      g_texFilePtr = fSafeOpen(g_texFileName, "w", 0/*noVersioningFlag*/);
+      if (!g_texFilePtr) continue; /* Couldn't open it (err msg was provided) */
+      g_texFileOpenFlag = 1;
+      print2("Created %s output file \"%s\".\n",
+          g_htmlFlag ? "HTML" : "LaTeX", g_texFileName);
+      printTexHeader(texHeaderFlag);
+      g_oldTexFlag = 0;
+      continue;
+    }
+
+    if (cmdMatches("CLOSE TEX")) {
+      /* Close the TeX file */
+      if (!g_texFileOpenFlag) {
+        print2("?Sorry, there is no LaTeX file currently open.\n");
+      } else {
+        print2("The LaTeX output file \"%s\" has been closed.\n",
+            g_texFileName);
+        printTexTrailer(texHeaderFlag);
+        fclose(g_texFilePtr);
+        g_texFileOpenFlag = 0;
+      }
+      free_vstring(g_texFileName);
+      continue;
+    }
+
+    /* Similar to Unix 'more' */
+    if (cmdMatches("MORE")) {
+      list1_fp = fSafeOpen(g_fullArg[1], "r", 0/*noVersioningFlag*/);
+      if (!list1_fp) continue; /* Couldn't open it (error msg was provided) */
+      while (1) {
+        if (!linput(list1_fp, NULL, &str1)) break; /* End of file */
+        /* Print a line on the screen */
+        if (!print2("%s\n", str1)) break; /* User typed Q */
+      }
+      fclose(list1_fp);
+      continue;
+    } /* end MORE */
+
+
+    if (cmdMatches("FILE SEARCH")) {
+      /* Search the contents of a file and type on the screen */
+
+      type_fp = fSafeOpen(g_fullArg[2], "r", 0/*noVersioningFlag*/);
+      if (!type_fp) continue; /* Couldn't open it (error msg was provided) */
+      fromLine = 0;
+      toLine = 0;
+      searchWindow = 0;
+      i = switchPos("/ FROM_LINE");
+      if (i) fromLine = (long)val(g_fullArg[i + 1]);
+      i = switchPos("/ TO_LINE");
+      if (i) toLine = (long)val(g_fullArg[i + 1]);
+      i = switchPos("/ WINDOW");
+      if (i) searchWindow = (long)val(g_fullArg[i + 1]);
+      /*??? Implement SEARCH /WINDOW */
+      if (i) print2("Sorry, WINDOW has not be implemented yet.\n");
+
+      let(&str2, g_fullArg[3]); /* Search string */
+      let(&str2, edit(str2, 32)); /* Convert to upper case */
+
+      tmpFlag = 0;
+
+      /* Search window buffer */
+      pntrLet(&pntrTmp, pntrSpace(searchWindow));
+
+      j = 0; /* Line # */
+      m = 0; /* # matches */
+      while (linput(type_fp, NULL, &str1)) {
+        j++;
+        if (j > toLine && toLine != 0) break;
+        if (j >= fromLine || fromLine == 0) {
+          let(&str3, edit(str1, 32)); /* Convert to upper case */
+          if (instr(1, str3, str2)) { /* Match occurred */
+            if (!tmpFlag) {
+              tmpFlag = 1;
+              print2(
+                    "The line number in the file is shown before each line.\n");
+            }
+            m++;
+            if (!print2("%ld:  %s\n", j, left(str1,
+                MAX_LEN - (long)strlen(str((double)j)) - 3))) break;
+          }
+        }
+        for (k = 1; k < searchWindow; k++) {
+          let((vstring *)(&pntrTmp[k - 1]), pntrTmp[k]);
+        }
+        if (searchWindow > 0)
+            let((vstring *)(&pntrTmp[searchWindow - 1]), str1);
+      }
+      if (!tmpFlag) {
+        print2("There were no matches.\n");
+      } else {
+        if (m == 1) {
+          print2("There was %ld matching line in the file %s.\n", m,
+              g_fullArg[2]);
+        } else {
+          print2("There were %ld matching lines in the file %s.\n", m,
+              g_fullArg[2]);
+        }
+      }
+
+      fclose(type_fp);
+
+      /* Deallocate search window buffer */
+      for (i = 0; i < searchWindow; i++) {
+        let((vstring *)(&pntrTmp[i]), "");
+      }
+      free_pntrString(pntrTmp);
+
+
+      continue;
+    }
+
+
+    if (cmdMatches("SET UNIVERSE") || cmdMatches("ADD UNIVERSE") ||
+        cmdMatches("DELETE UNIVERSE")) {
+
+      /*continue;*/ /* ???Not implemented */
+    } /* end if xxx UNIVERSE */
+
+
+
+    if (cmdMatches("SET DEBUG FLAG")) {
+      print2("Notice:  The DEBUG mode is intended for development use only.\n");
+      print2("The printout will not be meaningful to the user.\n");
+      i = (long)val(g_fullArg[3]);
+      if (i == 4) db4 = 1;  /* Not used */
+      if (i == 5) db5 = 1;  /* mmpars.c statistics; mmunif.c overview */
+      if (i == 6) db6 = 1;  /* mmunif.c details */
+      if (i == 7) db7 = 1;  /* mmunif.c more details; mmveri.c */
+      if (i == 8) db8 = 1;  /* mmpfas.c unification calls */
+      if (i == 9) db9 = 1;  /* memory */ /* use SET MEMORY_STATUS ON instead */
+      continue;
+    }
+    if (cmdMatches("SET DEBUG OFF")) {
+      db4 = 0;
+      db5 = 0;
+      db6 = 0;
+      db7 = 0;
+      db8 = 0;
+      db9 = 0;
+      print2("The DEBUG mode has been turned off.\n");
+      continue;
+    }
+
+    if (cmdMatches("ERASE")) {
+      if (g_sourceChanged) {
+        print2("Warning:  You have not saved changes to the source.\n");
+        str1 = cmdInput1("Do you want to ERASE anyway (Y, N) <N>? ");
+        if (str1[0] != 'y' && str1[0] != 'Y') {
+          print2("Use WRITE SOURCE to save the changes.\n");
+          continue;
+        }
+        g_sourceChanged = 0;
+      }
+      eraseSource();
+      g_sourceHasBeenRead = 0; /* Global variable */
+      g_showStatement = 0;
+      g_proveStatement = 0;
+      print2("Metamath has been reset to the starting state.\n");
+      continue;
+    }
+
+    if (cmdMatches("VERIFY PROOF")) {
+      if (switchPos("/ SYNTAX_ONLY")) {
+        verifyProofs(g_fullArg[2],0); /* Parse only */
+      } else {
+        verifyProofs(g_fullArg[2],1); /* Parse and verify */
+      }
+      continue;
+    }
+
+    if (cmdMatches("VERIFY MARKUP")) {
+      i = switchPos("/ DATE_SKIP") == 0;
+      j = switchPos("/ TOP_DATE_CHECK") != 0;
+      k = switchPos("/ FILE_CHECK") != 0;
+      l = switchPos("/ UNDERSCORE_SKIP") == 0;
+      m = switchPos("/ MATHBOX_SKIP") == 0;
+      n = switchPos("/ VERBOSE") != 0;
+      verifyMarkup(g_fullArg[2],
+          (flag)i, /* 1 = check date consistency */
+          (flag)j, /* 1 = check top date */
+          (flag)k, /* 1 = check external files (gifs and bib) */
+          (flag)l, /* 1 = check labels for underscores */
+          (flag)m, /* 1 = check mathbox cross-references */
+          (flag)n); /* 1 = verbose mode */
+      continue;
+    }
+
+    if (cmdMatches("MARKUP")) {
+      g_htmlFlag = 1;
+      g_altHtmlFlag = (switchPos("/ ALT_HTML") != 0);
+      if ((switchPos("/ HTML") != 0) == (switchPos("/ ALT_HTML") != 0)) {
+        print2("?Please specify exactly one of / HTML and / ALT_HTML.\n");
+        continue;
+      }
+      i = 0;
+      i = ((switchPos("/ SYMBOLS") != 0) ? PROCESS_SYMBOLS : 0)
+          + ((switchPos("/ LABELS") != 0) ? PROCESS_LABELS : 0)
+          + ((switchPos("/ NUMBER_AFTER_LABEL") != 0) ? ADD_COLORED_LABEL_NUMBER : 0)
+          + ((switchPos("/ BIB_REFS") != 0) ? PROCESS_BIBREFS : 0)
+          + ((switchPos("/ UNDERSCORES") != 0) ? PROCESS_UNDERSCORES : 0);
+      processMarkup(g_fullArg[1], /* Input file */
+          g_fullArg[2],  /* Output file */
+          (switchPos("/ CSS") != 0),
+          i); /* Action bits */
+      continue;
+    }
+
+    print2("?This command has not been implemented.\n");
+    continue;
+
+  }
+} /* command */
+
+
diff --git a/mmcmdl.c b/src/mmcmdl.c
similarity index 71%
rename from mmcmdl.c
rename to src/mmcmdl.c
index c81765b..46e3b3f 100644
--- a/mmcmdl.c
+++ b/src/mmcmdl.c
@@ -1,2480 +1,2253 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* Command line syntax specification for Metamath */
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mmcmdl.h"
-#include "mminou.h"
-#include "mmpfas.h"
-#include "mmunif.h" /* For g_hentyFilter, g_userMaxUnifTrials, g_minSubstLen */
-#include "mmwtex.h"
-#include "mmword.h"
-
-/* Global variables */
-pntrString *g_rawArgPntr = NULL_PNTRSTRING;
-nmbrString *g_rawArgNmbr = NULL_NMBRSTRING;
-long g_rawArgs = 0;
-pntrString *g_fullArg = NULL_PNTRSTRING;
-vstring g_fullArgString = ""; /* 1-Nov-2013 nm g_fullArg as one string */
-vstring g_commandPrompt = "";
-vstring g_commandLine = "";
-long g_showStatement = 0;
-vstring g_logFileName = "";
-vstring g_texFileName = "";
-flag g_PFASmode = 0; /* Proof assistant mode, invoked by PROVE command */
-flag g_queryMode = 0; /* If 1, explicit questions will be asked even if
-    a field in the input command line is optional */
-flag g_sourceChanged = 0; /* Flag that user made some change to the source file*/
-flag g_proofChanged = 0; /* Flag that user made some change to proof in progress*/
-flag g_commandEcho = 0; /* Echo full command */
-flag g_memoryStatus = 0; /* Always show memory */
-/* 31-Dec-2017 nm */
-flag g_sourceHasBeenRead = 0; /* 1 if a source file has been read in */
-/* 31-Dec-2017 nm */
-vstring g_rootDirectory = ""; /* Directory prefix to use for included files */
-
-
-
-flag processCommandLine(void)
-{
-  vstring defaultArg = "";
-  vstring tmpStr = "";
-  long i;
-  g_queryMode = 0; /* If 1, explicit questions will be asked even if
-    a field is optional */
-  pntrLet(&g_fullArg, NULL_PNTRSTRING);
-
-  if (!g_toolsMode) {
-
-    if (!g_PFASmode) {
-      /* Normal mode */
-      let(&tmpStr, cat("DBG|",
-          "HELP|READ|WRITE|PROVE|SHOW|SEARCH|SAVE|SUBMIT|OPEN|CLOSE|",
-          /* 10-Dec-2018 nm Added MARKUP */
-          "SET|FILE|BEEP|EXIT|QUIT|ERASE|VERIFY|MARKUP|MORE|TOOLS|",
-          "MIDI|<HELP>",
-          NULL));
-    } else {
-      /* Proof assistant mode */
-      let(&tmpStr, cat("DBG|",
-          "HELP|WRITE|SHOW|SEARCH|SAVE|SUBMIT|OPEN|CLOSE|",
-          /* 9-Jun-2016 nm Added _EXIT_PA */
-          "SET|FILE|BEEP|EXIT|_EXIT_PA|QUIT|VERIFY|INITIALIZE|ASSIGN|REPLACE|",
-          /* 11-Sep-2016 nm Added EXPAND */
-          "LET|UNIFY|IMPROVE|MINIMIZE_WITH|EXPAND|MATCH|DELETE|UNDO|REDO|",
-          /* 10-Dec-2018 nm Added MARKUP */
-          "MARKUP|MORE|TOOLS|MIDI|<HELP>",
-          NULL));
-    }
-    if (!getFullArg(0,tmpStr)) {
-      goto pclbad;
-    }
-
-    if (cmdMatches("HELP")) {
-          /* 15-Jan-2018 nm Added MARKUP */
-      if (!getFullArg(1, cat("LANGUAGE|PROOF_ASSISTANT|MM-PA|",
-          "BEEP|EXIT|QUIT|READ|ERASE|",
-          "OPEN|CLOSE|SHOW|SEARCH|SET|VERIFY|SUBMIT|SYSTEM|PROVE|FILE|WRITE|",
-          /* 10-Dec-2018 nm Added MARKUP */
-          "MARKUP|ASSIGN|REPLACE|MATCH|UNIFY|LET|INITIALIZE|DELETE|IMPROVE|",
-          /* 11-Sep-2016 nm Added EXPAND */
-          "MINIMIZE_WITH|EXPAND|UNDO|REDO|SAVE|DEMO|INVOKE|CLI|EXPLORE|TEX|",
-          "LATEX|HTML|COMMENTS|BIBLIOGRAPHY|MORE|",
-          "TOOLS|MIDI|$|<$>", NULL))) goto pclbad;
-      if (cmdMatches("HELP OPEN")) {
-        /*if (!getFullArg(2, "LOG|TEX|HTML|<LOG>")) goto pclbad;*/
-        /* 2-Oct-2017 nm Removed HTML */
-        if (!getFullArg(2, "LOG|TEX|<LOG>")) goto pclbad;
-        goto pclgood;
-      }
-      if (cmdMatches("HELP CLOSE")) {
-        /*if (!getFullArg(2, "LOG|TEX|HTML|<LOG>")) goto pclbad;*/
-        /* 2-Oct-2017 nm Removed HTML */
-        if (!getFullArg(2, "LOG|TEX|<LOG>")) goto pclbad;
-        goto pclgood;
-      }
-      if (cmdMatches("HELP SHOW")) {
-        if (!getFullArg(2, cat("MEMORY|SETTINGS|LABELS|SOURCE|STATEMENT|",
-            "PROOF|NEW_PROOF|USAGE|TRACE_BACK|ELAPSED_TIME|",
-            "DISCOURAGED|<MEMORY>",
-            NULL)))
-            goto pclbad;
-        goto pclgood;
-      }
-      if (cmdMatches("HELP SET")) {
-        if (!getFullArg(2, cat(
-            "ECHO|SCROLL|WIDTH|HEIGHT|UNDO|UNIFICATION_TIMEOUT|",
-            "DISCOURAGEMENT|",
-            "CONTRIBUTOR|",   /* 14-May-2017 nm */
-            "ROOT_DIRECTORY|",   /* 31-Dec-2017 nm */
-            "EMPTY_SUBSTITUTION|SEARCH_LIMIT|JEREMY_HENTY_FILTER|<ECHO>",
-            NULL)))
-            goto pclbad;
-        goto pclgood;
-      }
-      if (cmdMatches("HELP VERIFY")) {
-        if (!getFullArg(2, "PROOF|MARKUP|<PROOF>"))
-            goto pclbad;
-        goto pclgood;
-      }
-      if (cmdMatches("HELP WRITE")) {
-        if (!getFullArg(2,
-            "SOURCE|THEOREM_LIST|BIBLIOGRAPHY|RECENT_ADDITIONS|<SOURCE>"))
-            goto pclbad;
-        goto pclgood;
-      }
-      if (cmdMatches("HELP FILE")) {
-        if (!getFullArg(2, "SEARCH"))
-            goto pclbad;
-        goto pclgood;
-      }
-      if (cmdMatches("HELP SAVE")) {
-        if (!getFullArg(2,
-            "PROOF|NEW_PROOF|<PROOF>"))
-            goto pclbad;
-        goto pclgood;
-      }
-      goto pclgood;
-    } /* cmdMatches("HELP") */
-
-    if (cmdMatches("READ")) {
-      if (!getFullArg(1, "& What is the name of the source input file? "))
-          goto pclbad;
-      /* Get any switches */
-      i = 1;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, "VERIFY|<VERIFY>")) goto pclbad;
-        } else {
-          break;
-        }
-        break; /* Break if only 1 switch is allowed */
-      } /* End while for switch loop */
-      goto pclgood;
-    }
-
-    if (cmdMatches("WRITE")) {
-      if (!getFullArg(1,
-          "SOURCE|THEOREM_LIST|BIBLIOGRAPHY|RECENT_ADDITIONS|<SOURCE>"))
-        goto pclbad;
-      if (cmdMatches("WRITE SOURCE")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2, cat(
-            "* What is the name of the source output file <",
-            g_input_fn, ">? ", NULL)))
-          goto pclbad;
-        if (!strcmp(g_input_fn, g_fullArg[2])) {
-          print2(
-          "The input file will be renamed %s~1.\n", g_input_fn);
-        }
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                /* 3-May-2017 nm Removed CLEAN */
-                "FORMAT|REWRAP",
-                /* 31-Dec-2017 nm Added SPLIT, NO_VERSIONING, KEEP_INCLUDES */
-                /* 24-Aug-2020 nm Added EXTRACT */
-                "|SPLIT|NO_VERSIONING|KEEP_INCLUDES|EXTRACT",
-                "|<REWRAP>", NULL)))
-              goto pclbad;
-            /* 24-Aug-2020 nm Added EXTRACT */
-            if (lastArgMatches("EXTRACT")) {
-              i++;
-              if (!getFullArg(i, "* What statement label? "))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        } /* End while for switch loop */
-
-        goto pclgood;
-      }
-      if (cmdMatches("WRITE THEOREM_LIST")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        /* Get any switches */
-        i = 1;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "THEOREMS_PER_PAGE|SHOW_LEMMAS|HTML|ALT_HTML|NO_VERSIONING",
-                "|<THEOREMS_PER_PAGE>", NULL)))
-              goto pclbad;
-            if (lastArgMatches("THEOREMS_PER_PAGE")) {
-              i++;
-              if (!getFullArg(i, "# How many theorems per page <100>? "))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      }
-      if (cmdMatches("WRITE BIBLIOGRAPHY")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2, cat(
-            "* What is the bibliography HTML input/output file <",
-            "mmbiblio.html", ">? ", NULL)))
-          goto pclbad;
-        print2(
-          "The old file will be renamed %s~1.\n", g_fullArg[2]);
-        goto pclgood;
-      }
-      if (cmdMatches("WRITE RECENT_ADDITIONS")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2, cat(
-            "* What is the Recent Additions HTML input/output file <",
-            "mmrecent.html", ">? ", NULL)))
-          goto pclbad;
-        print2(
-          "The old file will be renamed %s~1.\n", g_fullArg[2]);
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "LIMIT|HTML|ALT_HTML",
-                "|<LIMIT>", NULL)))
-              goto pclbad;
-            if (lastArgMatches("LIMIT")) {
-              i++;
-              if (!getFullArg(i, "# How many most recent theorems <100>? "))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /*break;*/ /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      }
-    }
-
-    if (cmdMatches("OPEN")) {
-      /*if (!getFullArg(1, "LOG|TEX|HTML|<LOG>")) goto pclbad;*/
-      /* 2-Oct-2017 nm Removed HTML */
-      if (!getFullArg(1, "LOG|TEX|<LOG>")) goto pclbad;
-      if (cmdMatches("OPEN LOG")) {
-        if (g_logFileOpenFlag) {
-          printLongLine(cat(
-              "?Sorry, the log file \"", g_logFileName, "\" is currently open.  ",
-  "Type CLOSE LOG to close the current log if you want to open another one."
-              , NULL), "", " ");
-          goto pclbad;
-        }
-        if (!getFullArg(2, "* What is the name of logging output file? "))
-          goto pclbad;
-      }
-      if (cmdMatches("OPEN TEX")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (g_texFileOpenFlag) {
-          printLongLine(cat(
-              "?Sorry, the LaTeX file \"", g_texFileName, "\" is currently open.  ",
-              "Type CLOSE TEX to close the current LaTeX file",
-              " if you want to open another one."
-              , NULL), "", " ");
-          goto pclbad;
-        }
-        if (!getFullArg(2, "* What is the name of LaTeX output file? "))
-          goto pclbad;
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "NO_HEADER|OLD_TEX|<NO_HEADER>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        } /* End while for switch loop */
-
-      }
-
-      /* 2-Oct-2017 nm OPEN HTML is obsolete */
-      /*******
-      if (cmdMatches("OPEN HTML")) {
-        if (g_texFileOpenFlag) {
-          printLongLine(cat(
-              "?Sorry, the HTML file \"",g_texFileName, "\" is currently open.  ",
-              "Type CLOSE HTML to close the current HTML file",
-              " if you want to open another one."
-              , NULL), "", " ");
-          goto pclbad;
-        }
-        if (!getFullArg(2, "@ What is the name of HTML output file? "))
-          goto pclbad;
-
-        /@ Get any switches @/
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "NO_HEADER|<NO_HEADER>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          break; /@ Break if only 1 switch is allowed @/
-        } /@ End while for switch loop @/
-
-      }
-      ****/
-      goto pclgood;
-    }
-
-    if (cmdMatches("CLOSE")) {
-      /*if (!getFullArg(1, "LOG|TEX|HTML|<LOG>")) goto pclbad;*/
-      /* 2-Oct-2017 nm Removed HTML */
-      if (!getFullArg(1, "LOG|TEX|<LOG>")) goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("FILE")) {
-      if (!getFullArg(1, cat("SEARCH", NULL))) goto pclbad;
-
-      if (cmdMatches("FILE SEARCH")) {
-        if (!getFullArg(2, "& What is the name of the file to search? "))
-          goto pclbad;
-        if (!getFullArg(3, "* What is the string to search for? "))
-          goto pclbad;
-
-
-        /* Get any switches */
-        i = 3;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (i == 4) {
-              if (!getFullArg(i, cat(
-                  "FROM_LINE|TO_LINE|<FROM_LINE>", NULL)))
-                goto pclbad;
-            } else {
-              if (!getFullArg(i, cat(
-                  "FROM_LINE|TO_LINE|<TO_LINE>", NULL)))
-                goto pclbad;
-            }
-            if (lastArgMatches("FROM_LINE")) {
-              i++;
-              if (!getFullArg(i, "# From what line number <1>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("TO_LINE")) {
-              i++;
-              if (!getFullArg(i, "# To what line number <999999>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("WINDOW")) { /* ???Not implemented yet */
-              i++;
-              if (!getFullArg(i, "# How big a window around matched lines <0>? "))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        } /* End while for switch loop */
-
-
-        goto pclgood;
-      } /* End if (cmdMatches("FILE SEARCH")) */
-      goto pclgood;
-    }
-
-    if (cmdMatches("SHOW")) {
-      if (!g_PFASmode) {
-        if (!getFullArg(1, cat(
-     "SETTINGS|LABELS|STATEMENT|SOURCE|PROOF|MEMORY|TRACE_BACK|",
-     "USAGE|ELAPSED_TIME|DISCOURAGED|<SETTINGS>", NULL)))
-            goto pclbad;
-      } else {
-        if (!getFullArg(1, cat("NEW_PROOF|",
-     "SETTINGS|LABELS|STATEMENT|SOURCE|PROOF|MEMORY|TRACE_BACK|",
-     "USAGE|ELAPSED_TIME|DISCOURAGED|<SETTINGS>",
-            NULL)))
-            goto pclbad;
-      }
-      if (g_showStatement) {
-        if (g_showStatement < 1 || g_showStatement > g_statements) bug(1110);
-        let(&defaultArg, cat(" <",g_Statement[g_showStatement].labelName, ">",
-            NULL));
-      } else {
-        let(&defaultArg, "");
-      }
-
-
-      if (cmdMatches("SHOW TRACE_BACK")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            cat("* What is the statement label", defaultArg, "? ", NULL)))
-          goto pclbad;
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                /* 19-May-2013 nm Added MATCH */
-                "ALL|ESSENTIAL|AXIOMS|TREE|DEPTH|COUNT_STEPS|MATCH|TO",
-                "|<ALL>", NULL)))
-              goto pclbad;
-            if (lastArgMatches("DEPTH")) {
-              i++;
-              if (!getFullArg(i, "# How many indentation levels <999>? "))
-                goto pclbad;
-            }
-            /* 13-May-2013 nm Added MATCH */
-            if (lastArgMatches("MATCH")) {
-              i++;
-              if (!getFullArg(i, "* What statement label? "))
-                goto pclbad;
-            }
-            /* 18-Jul-2015 nm Added TO */
-            if (lastArgMatches("TO")) {
-              i++;
-              if (!getFullArg(i, "* What statement label? "))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        }
-
-        goto pclgood;
-      } /* End if (cmdMatches("SHOW TRACE_BACK")) */
-
-      if (cmdMatches("SHOW USAGE")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            cat("* What is the statement label", defaultArg, "? ", NULL)))
-          goto pclbad;
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "DIRECT|RECURSIVE|ALL",
-                "|<DIRECT>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          /* break; */  /* Break if only 1 switch is allowed */
-        }
-
-        goto pclgood;
-      } /* End if (cmdMatches("SHOW USAGE")) */
-
-
-      if (cmdMatches("SHOW LABELS")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            "* What are the labels to match (* = wildcard) <*>?"))
-          goto pclbad;
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat("ALL|LINEAR|<ALL>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          /*break;*/ /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      }
-      if (cmdMatches("SHOW STATEMENT")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            cat("* What is the statement label", defaultArg, "? ", NULL)))
-          goto pclbad;
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "FULL|COMMENT|TEX|OLD_TEX|HTML|ALT_HTML|TIME|BRIEF_HTML",
-                /* 12-May-2009 sa Added MNEMONICS */
-                "|BRIEF_ALT_HTML|MNEMONICS|NO_VERSIONING|<FULL>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      }
-      if (cmdMatches("SHOW SOURCE")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            cat("* What is the statement label", defaultArg, "? ", NULL))) {
-          goto pclbad;
-        }
-        goto pclgood;
-      }
-
-
-      if (cmdMatches("SHOW PROOF")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            cat("* What is the statement label", defaultArg, "? ", NULL)))
-          goto pclbad;
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "ESSENTIAL|ALL|UNKNOWN|FROM_STEP|TO_STEP|DEPTH",
-                /*"|REVERSE|VERBOSE|NORMAL|COMPRESSED",*/
-                "|REVERSE|VERBOSE|NORMAL|PACKED|COMPRESSED|EXPLICIT",
-                "|FAST",   /* 2-Mar-2016 nm */
-                "|OLD_COMPRESSION",   /* 27-Dec-2013 nm */
-                /* 14-Sep-2010 nm Added OLD_TEX */
-                "|STATEMENT_SUMMARY|DETAILED_STEP|TEX|OLD_TEX|HTML",
-                "|LEMMON|START_COLUMN|NO_REPEATED_STEPS",
-                "|RENUMBER|SIZE|<ESSENTIAL>", NULL)))
-              goto pclbad;
-            if (lastArgMatches("FROM_STEP")) {
-              i++;
-              if (!getFullArg(i, "# From what step <1>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("TO_STEP")) {
-              i++;
-              if (!getFullArg(i, "# To what step <9999>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("DEPTH")) {
-              i++;
-              if (!getFullArg(i, "# How many indentation levels <999>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("DETAILED_STEP")) {
-              i++;
-              if (!getFullArg(i, "# Display details of what step <1>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("START_COLUMN")) {
-              i++;
-              if (!getFullArg(i, cat(
-                  "# At what column should the formula start <",
-                  str((double)DEFAULT_COLUMN), ">? ", NULL)))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      } /* End if (cmdMatches("SHOW PROOF")) */
-
-
-      if (cmdMatches("SHOW NEW_PROOF")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-
-        /* Get any switches */
-        i = 1;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "ESSENTIAL|ALL|UNKNOWN|FROM_STEP|TO_STEP|DEPTH",
-                /*"|REVERSE|VERBOSE|NORMAL|COMPRESSED",*/
-                "|REVERSE|VERBOSE|NORMAL|PACKED|COMPRESSED|EXPLICIT",
-                "|OLD_COMPRESSION",   /* 27-Dec-2013 nm */
-                "|NOT_UNIFIED|TEX|HTML",
-                "|LEMMON|START_COLUMN|NO_REPEATED_STEPS",
-                "|RENUMBER|<ESSENTIAL>", NULL)))
-              goto pclbad;
-            if (lastArgMatches("FROM_STEP")) {
-              i++;
-              if (!getFullArg(i, "# From what step <1>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("TO_STEP")) {
-              i++;
-              if (!getFullArg(i, "# To what step <9999>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("DEPTH")) {
-              i++;
-              if (!getFullArg(i, "# How many indentation levels <999>? "))
-                goto pclbad;
-            }
-            if (lastArgMatches("START_COLUMN")) {
-              i++;
-              if (!getFullArg(i, cat(
-                  "# At what column should the formula start <",
-                  str((double)DEFAULT_COLUMN), ">? ", NULL)))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      } /* End if (cmdMatches("SHOW NEW_PROOF")) */
-
-
-      goto pclgood;
-    } /* End of SHOW */
-
-    if (cmdMatches("SEARCH")) {
-      if (g_sourceHasBeenRead == 0) {
-        print2("?No source file has been read in.  Use READ first.\n");
-        goto pclbad;
-      }
-      if (!getFullArg(1,
-          "* What are the labels to match (* = wildcard) <*>?"))
-        goto pclbad;
-      if (!getFullArg(2, "* Search for what math symbol string? "))
-          goto pclbad;
-      /* Get any switches */
-      i = 2;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, cat("ALL|COMMENTS|JOIN|<ALL>", NULL)))
-            goto pclbad;
-        } else {
-          break;
-        }
-        /*break;*/ /* Break if only 1 switch is allowed */
-      }
-      goto pclgood;
-
-    } /* End of SEARCH */
-
-
-    if (cmdMatches("SAVE")) {
-      if (!g_PFASmode) {
-        if (!getFullArg(1,
-            "PROOF|<PROOF>"))
-            goto pclbad;
-      } else {
-        if (!getFullArg(1, cat("NEW_PROOF|",
-            "PROOF|<NEW_PROOF>",
-            NULL)))
-            goto pclbad;
-      }
-      if (g_showStatement) {
-        if (g_showStatement < 0) bug(1111);
-        let(&defaultArg, cat(" <",g_Statement[g_showStatement].labelName, ">", NULL));
-      } else {
-        let(&defaultArg, "");
-      }
-
-
-      if (cmdMatches("SAVE PROOF")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            cat("* What is the statement label", defaultArg, "? ", NULL)))
-          goto pclbad;
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "NORMAL|PACKED|COMPRESSED|EXPLICIT",
-                "|FAST|OLD_COMPRESSION",   /* 27-Dec-2013 nm */
-                "|TIME|<NORMAL>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          /* break; */ /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      } /* End if (cmdMatches("SAVE PROOF")) */
-
-
-      if (cmdMatches("SAVE NEW_PROOF")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-
-        /* Get any switches */
-        i = 1;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "NORMAL|PACKED|COMPRESSED|EXPLICIT",
-                /* 27-Dec-2013 nm Added / OLD_COMPRESSION */
-                /* 3-May-2016 nm Added / OVERRIDE */
-                "|OLD_COMPRESSION|OVERRIDE",
-                "|<NORMAL>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          /*break;*/ /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      } /* End if (cmdMatches("SAVE NEW_PROOF")) */
-
-
-      goto pclgood;
-    } /* End of SAVE */
-
-
-    if (cmdMatches("PROVE")) {
-      if (g_sourceHasBeenRead == 0) {
-        print2("?No source file has been read in.  Use READ first.\n");
-        goto pclbad;
-      }
-      if (!g_proveStatement) g_proveStatement = g_showStatement;
-      if (g_proveStatement) {
-        let(&defaultArg, cat(" <",g_Statement[g_proveStatement].labelName, ">", NULL));
-      } else {
-        let(&defaultArg, "");
-      }
-      if (!getFullArg(1,
-          cat("* What is the label of the statement you want to try proving",
-          defaultArg, "? ", NULL)))
-        goto pclbad;
-
-      /* 10-May-2016 nm */
-      /* Get any switches */
-      i = 1;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, "OVERRIDE|<OVERRIDE>")) goto pclbad;
-        } else {
-          break;
-        }
-        break; /* Break if only 1 switch is allowed */
-      } /* End while for switch loop */
-
-      goto pclgood;
-    }
-
-    /* Commands in Proof Assistant mode */
-
-    if (cmdMatches("MATCH")) {
-      if (!getFullArg(1,
-          "STEP|ALL|<ALL>")) goto pclbad;
-      if (cmdMatches("MATCH STEP")) {
-        if (!getFullArg(2, "# What step number? ")) goto pclbad;
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "MAX_ESSENTIAL_HYP|<MAX_ESSENTIAL_HYP>", NULL)))
-              goto pclbad;
-            if (lastArgMatches("MAX_ESSENTIAL_HYP")) {
-              i++;
-              if (!getFullArg(i,
-  "# Maximum number of essential hypotheses to allow for a match <0>? "))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          break;  /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      }
-      if (cmdMatches("MATCH ALL")) {
-        /* Get any switches */
-        i = 1;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "ESSENTIAL|MAX_ESSENTIAL_HYP|<ESSENTIAL>", NULL)))
-              goto pclbad;
-            if (lastArgMatches("MAX_ESSENTIAL_HYP")) {
-              i++;
-              if (!getFullArg(i,
-  "# Maximum number of essential hypotheses to allow for a match <0>? "))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /*break;*/  /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      } /* End if (cmdMatches("MATCH ALL")) */
-      goto pclgood;
-    }
-
-    if (cmdMatches("INITIALIZE")) {
-      if (!getFullArg(1,
-          "STEP|ALL|USER|<ALL>")) goto pclbad;  /* 16-Apr-06 nm Added USER */
-      if (cmdMatches("INITIALIZE STEP")) {
-        if (!getFullArg(2, "# What step number? ")) goto pclbad;
-      }
-      goto pclgood;
-    }
-
-    /* 26-Aug-2006 nm Changed "IMPROVE STEP <step>" to just "IMPROVE <step>"
-       for user convenience (and consistency with "ASSIGN" command) */
-    if (cmdMatches("IMPROVE")) {
-      if (!getFullArg(1,
-        "* What step number, or FIRST, or LAST, or ALL <ALL>? ")) goto pclbad;
-                                                             /* 11-Dec-05 nm */
-      /* Get any switches */
-      i = 1;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i,
-              /* 3-May-2016 nm Added / OVERRIDE */
-              /* 5-Aug-2020 nm Added / INCLUDE_MATHBOXES */
-         "DEPTH|NO_DISTINCT|1|2|3|SUBPROOFS|OVERRIDE|INCLUDE_MATHBOXES|<DEPTH>")
-              ) goto pclbad;
-          if (lastArgMatches("DEPTH")) {
-            i++;
-            if (!getFullArg(i,
-  "# What is maximum depth for searching statements with $e hypotheses <0>? "))
-              goto pclbad;
-          }
-        } else {
-          break;
-        }
-        /*break;*/ /* Do this if only 1 switch is allowed */
-      } /* end while */
-      goto pclgood;
-    } /* end if IMPROVE */
-
-    /* ------- Old version before 26-Aug-2006 -------
-    if (cmdMatches("IMPROVE")) {
-      if (!getFullArg(1,
-          "STEP|ALL|FIRST|LAST|<ALL>")) goto pclbad;
-
-      if (cmdMatches("IMPROVE STEP")) {
-        if (!getFullArg(2, "# What step number? ")) goto pclbad;
-      }
-      if (cmdMatches("IMPROVE STEP") || cmdMatches("IMPROVE ALL")
-          || cmdMatches("IMPROVE LAST") || cmdMatches("IMPROVE FIRST")) {
-                                                            /@ 11-Dec-05 nm @/
-        /@ Get switches @/
-        if (cmdMatches("IMPROVE STEP")) {
-          i = 2;
-        } else {
-          i = 1;
-        }
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i,
-                "DEPTH|NO_DISTINCT|<DEPTH>")
-                ) goto pclbad;
-            if (lastArgMatches("DEPTH")) {
-              i++;
-              if (!getFullArg(i,
-  "# What is maximum depth for searching statements with $e hypotheses <0>? "))
-                goto pclbad;
-            }
-          } else {
-            break;
-          }
-          /@break;@/ /@ Do this if only 1 switch is allowed @/
-        } /@ end while @/
-        goto pclgood;
-      } /@ end if IMPROVE STEP or IMPROVE ALL @/
-    }
-    ------- End of old version ------- */
-
-
-    if (cmdMatches("MINIMIZE_WITH")) {
-      if (!getFullArg(1, "* What statement label? ")) goto pclbad;
-      /* Get any switches */
-      i = 1;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, cat(
-              /*
-              "BRIEF|VERBOSE|ALLOW_GROWTH|NO_DISTINCT|EXCEPT|",
-              "REVERSE|INCLUDE_MATHBOXES|FORBID|<BRIEF>", NULL)))
-              */
-              "VERBOSE|MAY_GROW|EXCEPT|OVERRIDE|INCLUDE_MATHBOXES|",
-              "ALLOW_NEW_AXIOMS|NO_NEW_AXIOMS_FROM|FORBID|TIME|<VERBOSE>",
-              NULL)))
-                              /* 7-Jan-06 nm Added EXCEPT */
-                              /* 28-Jun-2011 nm Added INCLUDE_MATHBOXES */
-                              /* 10-Nov-2011 nm Added REVERSE */
-                              /* 25-Jun-2014 nm Removed REVERSE, NO_DISTINCT */
-                              /* 22-Nov-2014 nm Added NO_NEW_AXIOMS_FROM */
-                              /* 3-May-2016 nm Added / OVERRIDE */
-                              /* 4-Aug-2019 nm Added ALLOW_NEW_AXIOMS; changed
-                                     ALLOW_GROWTH to MAY_GROW */
-            goto pclbad;
-
-          /* 7-Jan-06 nm Added EXCEPT */
-          if (lastArgMatches("EXCEPT")) {
-            i++;
-            if (!getFullArg(i, "* What statement label match pattern? "))
-              goto pclbad;
-          }
-          /* 4-Aug-2019 nm Added ALLOW_NEW_AXIOMS */
-          if (lastArgMatches("ALLOW_NEW_AXIOMS")) {
-            i++;
-            if (!getFullArg(i, "* What statement label match pattern? "))
-              goto pclbad;
-          }
-          /* 22-Nov-2014 nm Added NO_NEW_AXIOMS_FROM */
-          if (lastArgMatches("NO_NEW_AXIOMS_FROM")) {
-            i++;
-            if (!getFullArg(i, "* What statement label match pattern? "))
-              goto pclbad;
-          }
-          /* 20-May-2013 nm Added FORBID */
-          if (lastArgMatches("FORBID")) {
-            i++;
-            if (!getFullArg(i, "* What statement label match pattern? "))
-              goto pclbad;
-          }
-        } else {
-          break;
-        }
-        /*break;*/  /* Break if only 1 switch is allowed */
-      }
-      goto pclgood;
-    } /* end of MINIMIZE_WITH */
-
-    /* 11-Sep-2016 nm */
-    if (cmdMatches("EXPAND")) {
-      if (!getFullArg(1, "* What statement label? ")) goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("UNIFY")) {
-      if (!getFullArg(1,
-          "STEP|ALL|<ALL>")) goto pclbad;
-      if (cmdMatches("UNIFY STEP")) {
-        if (!getFullArg(2, "# What step number? ")) goto pclbad;
-        goto pclgood;
-      }
-      if (cmdMatches("UNIFY ALL")) {
-        /* Get any switches */
-        i = 1;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "INTERACTIVE|<INTERACTIVE>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          break;  /* Break if only 1 switch is allowed */
-        }
-        goto pclgood;
-      } /* End if (cmdMatches("UNIFY ALL")) */
-    }
-
-    if (cmdMatches("DELETE")) {
-      if (!getFullArg(1,
-          "STEP|ALL|FLOATING_HYPOTHESES|<STEP>")) goto pclbad;
-      if (lastArgMatches("STEP")) {
-        if (!getFullArg(2, "# What step number? ")) goto pclbad;
-        goto pclgood;
-      }
-      goto pclgood;
-    }
-
-    /*???OBSOL???*/
-    if (cmdMatches("ADD")) {
-      if (!getFullArg(1,
-          "UNIVERSE|<UNIVERSE>")) goto pclbad;
-      /* Note:  further parsing below */
-    }
-
-    if (cmdMatches("REPLACE")) {
-      /* 14-Sep-2012 nm Added FIRST, LAST */
-      if (!getFullArg(1, "* What step number, or FIRST, or LAST <LAST>? "))
-          goto pclbad;
-      if (!getFullArg(2, "* With what statement label? ")) goto pclbad;
-      /* Get any switches */
-      i = 2;
-
-      /* 3-May-2016 nm Added / OVERRIDE */
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, cat(
-              "OVERRIDE|<OVERRIDE>", NULL)))
-            goto pclbad;
-        } else {
-          break;
-        }
-        break; /* Break if only 1 switch is allowed */
-      }
-
-      goto pclgood;
-    }
-
-    if (cmdMatches("LET")) {
-      if (!getFullArg(1, "STEP|VARIABLE|<STEP>")) goto pclbad;
-      if (cmdMatches("LET STEP")) {
-        if (!getFullArg(2, "* What step number, or FIRST, or LAST <LAST>? "))
-          goto pclbad;
-      }
-      if (cmdMatches("LET VARIABLE")) {
-        if (!getFullArg(2, "* Assign what variable (format $nn)? ")) goto pclbad;
-      }
-      if (!getFullArg(3, "=|<=>")) goto pclbad;
-      if (!getFullArg(4, "* With what math symbol string? "))
-          goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("ASSIGN")) {
-      if (!getFullArg(1,
-          "* What step number, or FIRST, or LAST <LAST>? ")) goto pclbad;
-                                                             /* 11-Dec-05 nm */
-      if (!getFullArg(2, "* With what statement label? ")) goto pclbad;
-      /* Get any switches */
-      i = 2;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, cat(    /* 3-May-2016 nm Added / OVERRIDE */
-              "NO_UNIFY|OVERRIDE|<NO_UNIFY>", NULL)))
-            goto pclbad;
-        } else {
-          break;
-        }
-        /*break;*/  /* Break if only 1 switch is allowed */
-      }
-      goto pclgood;
-    }
-
-    if (cmdMatches("UNDO")) {
-      goto pclgood;
-    }
-
-    if (cmdMatches("REDO")) {
-      goto pclgood;
-    }
-
-    if (cmdMatches("SET")) {
-      let(&tmpStr, cat(
-          /*"ECHO|SCROLL|UNIVERSE|",*/
-          "WIDTH|HEIGHT|UNDO|ECHO|SCROLL|",
-          "DEBUG|MEMORY_STATUS|SEARCH_LIMIT|UNIFICATION_TIMEOUT|",
-          "DISCOURAGEMENT|",  /* 10-Jul-2016 nm */
-          "CONTRIBUTOR|",  /* 14-May-2017 nm */
-          "ROOT_DIRECTORY|",  /* 31-Dec-2017 nm */
-          "EMPTY_SUBSTITUTION|JEREMY_HENTY_FILTER|<WIDTH>", NULL));
-      if (!getFullArg(1,tmpStr)) goto pclbad;
-      if (cmdMatches("SET DEBUG")) {
-        if (!getFullArg(2, "FLAG|OFF|<OFF>")) goto pclbad;
-        if (lastArgMatches("FLAG")) {
-          if (!getFullArg(3, "4|5|6|7|8|9|<5>")) goto pclbad;
-        }
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET ECHO")) {
-        if (g_commandEcho) {
-          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
-        } else {
-          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
-        }
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET SCROLL")) {
-        if (g_scrollMode == 1) {
-          if (!getFullArg(2, "CONTINUOUS|PROMPTED|<CONTINUOUS>")) goto pclbad;
-        } else {
-          if (!getFullArg(2, "CONTINUOUS|PROMPTED|<PROMPTED>")) goto pclbad;
-        }
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET DISCOURAGEMENT")) {  /* 10-Jul-2016 nm */
-        if (g_globalDiscouragement) {
-          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
-        } else {
-          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
-        }
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET MEMORY_STATUS")) {
-        if (g_memoryStatus) {
-          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
-        } else {
-          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
-        }
-        goto pclgood;
-      }
-
-
-      if (cmdMatches("SET JEREMY_HENTY_FILTER")) {
-        if (g_hentyFilter) {
-          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
-        } else {
-          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
-        }
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET CONTRIBUTOR")) {
-        if (!getFullArg(2, cat(
-            "* What is the contributor name for SAVE (NEW_)PROOF <",
-            g_contributorName, ">? ", NULL)))
-          goto pclbad;
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET ROOT_DIRECTORY")) {
-        if (!getFullArg(2, cat(
-            "* What is the root directory path (use space if none) <",
-            g_rootDirectory, ">? ", NULL)))
-          goto pclbad;
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET SEARCH_LIMIT")) {
-        if (!getFullArg(2, cat(
-            "# What is search limit for IMPROVE command <",
-            str((double)g_userMaxProveFloat), ">? ", NULL)))
-          goto pclbad;
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET UNIFICATION_TIMEOUT")) {
-        if (!getFullArg(2, cat(
-           "# What is maximum number of unification trials <",
-            str((double)g_userMaxUnifTrials), ">? ", NULL)))
-          goto pclbad;
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET WIDTH")) {
-        if (!getFullArg(2, cat(
-           "# What is maximum line length on your screen <",
-            str((double)g_screenHeight), ">? ", NULL)))
-          goto pclbad;
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET HEIGHT")) {
-        if (!getFullArg(2, cat(
-           "# What is number of lines your screen displays <",
-            str((double)g_screenHeight), ">? ", NULL)))
-          goto pclbad;
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET UNDO")) {
-        if (!getFullArg(2, cat(
-           "# What is the maximum number of UNDOs <",
-            str((double)(processUndoStack(NULL, PUS_GET_SIZE, "", 0))),
-            ">? ", NULL)))
-          goto pclbad;
-        goto pclgood;
-      }
-
-      if (cmdMatches("SET EMPTY_SUBSTITUTION")) {
-        if (g_minSubstLen == 0) {
-          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
-        } else {
-          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
-        }
-        goto pclgood;
-      }
-
-    } /* end if SET */
-
-    /************** 31-Dec-2017 nm Delete unused stuff
-    if (cmdMatches("INPUT")) {
-      if (!getFullArg(1, "PROOF|<PROOF>")) goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("SET UNIVERSE") || cmdMatches("ADD UNIVERSE") ||
-        cmdMatches("DELETE UNIVERSE")) {
-      /@ Get a list of statement labels @/
-      i = 1;
-      while (1) {
-        i++;
-        /@??? The user will never be asked this. @/
-        if (!getFullArg(i, "@ Statement label or '@' or '$f'|$<$>? "))
-            goto pclbad;
-        if (lastArgMatches("")) goto pclgood; /@ End of argument list @/
-      } /@ end while @/
-    } /@ end if xxx UNIVERSE @/
-    ************/
-
-    if (cmdMatches("ERASE")) {
-      goto pclgood;
-    }
-
-    if (cmdMatches("MORE")) {
-      if (!getFullArg(1,
-         "* What is the name of the file to display? "))
-        goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("TOOLS")) {
-      goto pclgood;
-    }
-
-    if (cmdMatches("VERIFY")) {
-      if (!getFullArg(1,
-          "PROOF|MARKUP|<PROOF>"))
-        goto pclbad;
-      if (cmdMatches("VERIFY PROOF")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            "* What are the labels to match (* = wildcard) <*>?"))
-          goto pclbad;
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "SYNTAX_ONLY",
-                "|<SYNTAX_ONLY>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          break;  /* Break if only 1 switch is allowed */
-        }
-
-        goto pclgood;
-      }
-
-      /* 7-Nov-2015 nm */
-      if (cmdMatches("VERIFY MARKUP")) {
-        if (g_sourceHasBeenRead == 0) {
-          print2("?No source file has been read in.  Use READ first.\n");
-          goto pclbad;
-        }
-        if (!getFullArg(2,
-            "* What are the labels to match (* = wildcard) <*>?"))
-          goto pclbad;
-
-        /* Get any switches */
-        i = 2;
-        while (1) {
-          i++;
-          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-          if (lastArgMatches("/")) {
-            i++;
-            if (!getFullArg(i, cat(
-                "DATE_SKIP|FILE_SKIP|TOP_DATE_SKIP|VERBOSE",
-                "|UNDERSCORE_SKIP|MATHBOX_SKIP|<DATE_SKIP>", NULL)))
-              goto pclbad;
-          } else {
-            break;
-          }
-          /* break; */  /* Break if only 1 switch is allowed */
-        }
-
-        goto pclgood;
-      }
-    }
-
-    if (cmdMatches("DBG")) {
-      /* The debug command fetches an arbitrary 2nd arg in quotes, to be handled
-         in whatever way is needed for debugging. */
-      if (!getFullArg(1, "* What is the debugging string? "))
-        goto pclbad;
-      goto pclgood;
-    }
-
-    /* 10-Dec-2018 nm Added MARKUP command*/
-    if (cmdMatches("MARKUP")) {
-      if (g_sourceHasBeenRead == 0) {
-        print2("?No source file has been read in.  Use READ first.\n");
-        goto pclbad;
-      }
-      if (!getFullArg(1,
-          "* What is the name of the input file with markup? "))
-        goto pclbad;
-      if (!getFullArg(2,
-          "* What is the name of the HTML output file? "))
-        goto pclbad;
-
-      /* Get any switches */
-      i = 2;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, cat(
-              "HTML|ALT_HTML|SYMBOLS|LABELS|NUMBER_AFTER_LABEL|BIB_REFS",
-              "|UNDERSCORES|CSS|<ALT_HTML>", NULL)))
-            goto pclbad;
-        } else {
-          break;
-        }
-        /*break;*/ /* Break if only 1 switch is allowed */
-      }
-      goto pclgood;
-    }
-
-    if (cmdMatches("MIDI")) {
-      if (g_sourceHasBeenRead == 0) {
-        print2("?No source file has been read in.  Use READ first.\n");
-        goto pclbad;
-      }
-      if (!getFullArg(1,
-         "* Statement label to create MIDI for (* matches any substring) <*>?"))
-        goto pclbad;
-      /* Get any switches */
-      i = 1;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, cat("PARAMETER|<PARAMETER>", NULL)))
-            goto pclbad;
-          i++;
-          if (!getFullArg(i,
-              "* What is the parameter string <FSH>?"))
-            goto pclbad;
-        } else {
-          break;
-        }
-        break; /* Break if only 1 switch is allowed */
-      }
-      goto pclgood;
-    }
-
-    if (cmdMatches("EXIT") || cmdMatches("QUIT")
-        || cmdMatches("_EXIT_PA")) { /* 9-Jun-2016 nm */
-
-      /* Get any switches */
-      i = 0;
-      while (1) {
-        i++;
-        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-        if (lastArgMatches("/")) {
-          i++;
-          if (!getFullArg(i, cat(
-              "FORCE|<FORCE>", NULL)))
-            goto pclbad;
-        } else {
-          break;
-        }
-        break; /* Break if only 1 switch is allowed */
-      } /* End while for switch loop */
-
-      goto pclgood;
-    }
-
-  } else { /* g_toolsMode */
-    /* Text tools mode */
-    let(&tmpStr, cat(
-          "HELP|SUBMIT|",
-          "ADD|DELETE|SUBSTITUTE|S|SWAP|CLEAN|INSERT|BREAK|BUILD|MATCH|SORT|",
-          "UNDUPLICATE|DUPLICATE|UNIQUE|REVERSE|RIGHT|PARALLEL|NUMBER|COUNT|",
-          "COPY|C|TYPE|T|TAG|UPDATE|BEEP|B|EXIT|QUIT|<HELP>", NULL));
-    if (!getFullArg(0,tmpStr))
-      goto pclbad;
-
-    if (cmdMatches("HELP")) {
-      if (!getFullArg(1, cat(
-          "ADD|DELETE|SUBSTITUTE|S|SWAP|CLEAN|INSERT|BREAK|BUILD|MATCH|SORT|",
-          "UNDUPLICATE|DUPLICATE|UNIQUE|REVERSE|RIGHT|PARALLEL|NUMBER|COUNT|",
-          "TYPE|T|TAG|UPDATE|BEEP|B|EXIT|QUIT|",
-          "COPY|C|SUBMIT|SYSTEM|CLI|",
-          "$|<$>", NULL))) goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("ADD") || cmdMatches("TAG")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2, "* String to add to beginning of each line <>? "))
-        goto pclbad;
-      if (!getFullArg(3, "* String to add to end of each line <>? "))
-        goto pclbad;
-      if (cmdMatches("TAG")) {
-        if (!getFullArg(4,
-            "* String to match to start range (null = any line) <>? "))
-          goto pclbad;
-        if (!getFullArg(5,
-            "# Which occurrence of start match to start range <1>? "))
-          goto pclbad;
-        if (!getFullArg(6,
-            "* String to match to end range (null = any line) <>? "))
-          goto pclbad;
-        if (!getFullArg(7,
-            "# Which occurrence of end match to end range <1>? "))
-          goto pclbad;
-      }
-      goto pclgood;
-    }
-    if (cmdMatches("DELETE")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2,
-"* String from which to start deleting (CR = beginning of line) <>? "))
-        goto pclbad;
-      if (!getFullArg(3,
-"* String at which to stop deleting (CR = end of line) <>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("CLEAN")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2,
-          "* Subcommand(s) (D,B,E,R,Q,T,U,P,G,C,L,V) <B,E,R>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("SWAP")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2,
-"* Character string to match between the halves to be swapped? "))
-        goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("SUBSTITUTE") || cmdMatches("S")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2, "* String to replace? "))
-        goto pclbad;
-      if (!getFullArg(3, "* Replace it with <>? "))
-        goto pclbad;
-      if (!getFullArg(4,
-"* Which occurrence in the line (1,2,... or ALL or EACH) <1>? "))
-        goto pclbad;
-      if (!getFullArg(5,
-"* Additional match required on line (null = match all) <>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("INSERT")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2, "* String to insert in each line <!>? "))
-        goto pclbad;
-      if (!getFullArg(3, "# Column at which to insert the string <1>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("BREAK")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2,
-          "* Special characters to use as token delimiters <()[],=:;{}>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("MATCH")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2,
-"* String to match on each line (null = any non-blank line) <>? "))
-        goto pclbad;
-      if (!getFullArg(3,
-"* Output those lines containing the string (Y) or those not (N) <Y>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("SORT")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      if (!getFullArg(2,
-          "* String to start key on each line (null string = column 1) <>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("UNDUPLICATE") || cmdMatches("DUPLICATE") ||
-        cmdMatches("UNIQUE") || cmdMatches("REVERSE") || cmdMatches("BUILD")
-        || cmdMatches("RIGHT")) {
-      if (!getFullArg(1, "& Input/output file? "))
-        goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("COUNT")) {
-      if (!getFullArg(1, "& Input file? "))
-        goto pclbad;
-      if (!getFullArg(2,
-"* String to count <;>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("COPY") || cmdMatches("C")) {
-      if (!getFullArg(1, "* Comma-separated list of input files? "))
-        goto pclbad;
-      if (!getFullArg(2, "* Output file? "))
-        goto pclbad;
-      goto pclgood;
-    }
-
-
-    if (cmdMatches("NUMBER")) {
-      if (!getFullArg(1, "* Output file <n.tmp>? "))
-        goto pclbad;
-      if (!getFullArg(2, "# First number <1>? "))
-        goto pclbad;
-      if (!getFullArg(3, "# Last number <10>? "))
-        goto pclbad;
-      if (!getFullArg(4, "# Increment <1>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-    if (cmdMatches("TYPE") || cmdMatches("T")) {
-      if (!getFullArg(1, "& File to display on the screen? "))
-        goto pclbad;
-      if (!getFullArg(2, "* Num. lines to type or ALL (nothing = 10) <$>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-
-
-    if (cmdMatches("UPDATE")) {
-      print2(
-"Warning: Do not comment out code - delete it before running UPDATE!  If\n");
-      print2(
-"rerunning UPDATE, do not tamper with \"start/end of deleted section\" comments!\n");
-      print2(
-"Edit out tag on header comment line!  Review the output file!\n");
-      if (!getFullArg(1, "& Original (reference) program input file? "))
-        goto pclbad;
-      if (!getFullArg(2, "& Edited program input file? "))
-        goto pclbad;
-      if (!getFullArg(3, cat(
-"* Edited program output file with revisions tagged <",
-          g_fullArg[2], ">? ", NULL)))
-        goto pclbad;
-      if (!strcmp(g_fullArg[2], g_fullArg[3])) {
-        print2(
-"The input file will be renamed %s~1.\n", g_fullArg[2]);
-      }
-      if (!getFullArg(4,
-          cat("* Revision tag for added lines </* #",
-          str((double)(highestRevision(g_fullArg[1]) + 1)), " */>? ", NULL)))
-        goto pclbad;
-      if (!getFullArg(5,
-"# Successive lines required for match (more = better sync) <3>? "))
-        goto pclbad;
-      goto pclgood;
-    }
-
-    if (cmdMatches("PARALLEL")) {
-      if (!getFullArg(1, "& Left file? "))
-        goto pclbad;
-      if (!getFullArg(2, "& Right file? "))
-        goto pclbad;
-      if (!getFullArg(3, cat("* Output file <",
-          g_fullArg[1], ">? ", NULL)))
-        goto pclbad;
-      if (!getFullArg(4,
-          cat("* String to insert between the 2 input lines <>? ", NULL)))
-        goto pclbad;
-      goto pclgood;
-    }
-
-    /* g_toolsMode - no qualifiers for EXIT */
-    if (cmdMatches("EXIT") || cmdMatches("QUIT")) {
-      goto pclgood;
-    }
-
-
-  } /* if !g_toolsMode ... else ... */
-
-  if (cmdMatches("SUBMIT")) {
-    if (g_toolsMode) {
-      let(&tmpStr, " <tools.cmd>");
-    } else {
-      let(&tmpStr, " <mm.cmd>");
-    }
-    if (!getFullArg(1, cat("& What is the name of command file to run",
-        tmpStr, "? ", NULL))) {
-      goto pclbad;
-    }
-
-    /* 23-Oct-2006 nm Added / SILENT qualifier */
-    /* Get any switches */
-    i = 1; /* Number of command words before switch */
-    while (1) {
-      i++;
-      if (!getFullArg(i, "/|$|<$>")) goto pclbad;
-      if (lastArgMatches("/")) {
-        i++;
-        if (!getFullArg(i, cat(
-            "SILENT",
-            "|<SILENT>", NULL)))
-          goto pclbad;
-      } else {
-        break;
-      }
-      break; /* Break if only 1 switch is allowed */
-    } /* End while for switch loop */
-
-    goto pclgood;
-  }
-
-  if (cmdMatches("BEEP") || cmdMatches("B")) {
-    goto pclgood;
-  }
-
-  /* Command in master list but not intercepted -- really a bug */
-  print2("?This command has not been implemented yet.\n");
-  print2("(This is really a bug--please report it.)\n");
-  goto pclbad;
-
-  /* Should never get here */
-
-
-
- pclgood:
-
-  /* Strip off the last g_fullArg if a null argument was added by getFullArg
-     in the case when "$" (nothing) is allowed */
-  if (!strcmp(g_fullArg[pntrLen(g_fullArg) - 1], chr(3))) {
-    let((vstring *)(&g_fullArg[pntrLen(g_fullArg) - 1]), ""); /* Deallocate */
-    pntrLet(&g_fullArg, pntrLeft(g_fullArg, pntrLen(g_fullArg) - 1));
-  }
-
-  if (pntrLen(g_fullArg) > g_rawArgs) bug(1102);
-  if (pntrLen(g_fullArg) < g_rawArgs) {
-    let(&tmpStr, cat("?Too many arguments.  Use quotes around arguments with special",
-        " characters and around Unix file names with \"/\"s.", NULL));
-    printCommandError(cat(g_commandPrompt, g_commandLine, NULL), pntrLen(g_fullArg),
-        tmpStr);
-    goto pclbad;
-  }
-
-  /* 1-Nov-2013 nm Create a single string containing the g_fullArg tokens */
-  let(&g_fullArgString, "");
-  for (i = 0; i < pntrLen(g_fullArg); i++) {
-    let(&g_fullArgString, cat(g_fullArgString, " ", g_fullArg[i], NULL));
-  }
-  let(&g_fullArgString, right(g_fullArgString, 2)); /* Strip leading space */
-
-
-  /* Deallocate memory */
-  let(&defaultArg, "");
-  let(&tmpStr, "");
-  return (1);
-
- pclbad:
-  /* Deallocate memory */
-  let(&defaultArg, "");
-  let(&tmpStr, "");
-  return (0);
-
-} /* processCommandLine */
-
-
-
-flag getFullArg(long arg, vstring cmdList1)
-{
-  /* This function converts the user's abbreviated keyword in
-     g_rawArgPntr[arg] to a full, upper-case keyword,
-     in g_fullArg[arg], matching
-     the available choices in cmdList. */
-  /* Special cases:  cmdList = "# xxx <yyy>?" - get an integer */
-  /*                 cmdList = "* xxx <yyy>?" - get any string;
-                       don't convert to upper case
-                     cmdList = "& xxx <yyy>?" - same as * except
-                       verify it is a file that exists */
-  /* "$" means a null argument is acceptable; put it in as
-     special character chr(3) so it can be recognized */
-
-  pntrString *possCmd = NULL_PNTRSTRING;
-  long possCmds, i, j, k, m, p, q;
-  vstring defaultCmd = "";
-  vstring infoStr = "";
-  vstring tmpStr = "";
-  vstring tmpArg = "";
-  vstring errorLine = "";
-  vstring keyword = "";
-  vstring cmdList = "";
-  FILE *tmpFp;
-
-  let(&cmdList,cmdList1); /* In case cmdList1 gets deallocated when it comes
-                             directly from a vstring function such as cat() */
-
-  let(&errorLine, cat(g_commandPrompt,g_commandLine, NULL));
-
-  /* Handle special case - integer expected */
-  if (cmdList[0] == '#') {
-    let(&defaultCmd,
-        seg(cmdList,instr(1,cmdList, "<"),instr(1,cmdList, ">")));
-
-    /* If the argument has not been entered, prompt the user for it */
-    if (g_rawArgs <= arg) {
-      pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-      nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, 0));
-      g_rawArgs++;
-      if (g_rawArgs <= arg) bug(1103);
-
-      g_queryMode = 1;
-      tmpArg = cmdInput1(right(cmdList,3));
-      let(&errorLine,right(cmdList,3));
-      if (tmpArg[0] == 0) { /* Use default argument */
-        let(&tmpArg, seg(defaultCmd,2,len(defaultCmd) - 1));
-      }
-      let((vstring *)(&g_rawArgPntr[arg]), tmpArg);
-      g_rawArgNmbr[arg] = len(cmdList) - 1;/* Line position for error msgs */
-
-    } /* End of asking user for additional argument */
-
-    /* Make sure that the argument is a non-negative integer */
-    let(&tmpArg,g_rawArgPntr[arg]);
-    if (tmpArg[0] == 0) { /* Use default argument */
-      /* (This code is needed in case of null string passed directly) */
-      let(&tmpArg, seg(defaultCmd,2,len(defaultCmd) - 1));
-    }
-    let(&tmpStr, str(val(tmpArg)));
-    let(&tmpStr, cat(string(len(tmpArg)-len(tmpStr),'0'), tmpStr, NULL));
-    if (strcmp(tmpStr, tmpArg)) {
-      printCommandError(errorLine, arg,
-          "?A number was expected here.");
-      goto return0;
-    }
-
-    let(&keyword, str(val(tmpArg)));
-    goto return1;
-  }
-
-
-
-  /* Handle special case - any arbitrary string is OK */
-  /* '*' means any string, '&' means a file */
-  /* However, "|$<$>" also allows null string (no argument) */
-  if (cmdList[0] == '*' || cmdList[0] == '&') {
-    let(&defaultCmd,
-        seg(cmdList,instr(1,cmdList, "<"),instr(1,cmdList, ">")));
-
-    /* If the argument has not been entered, prompt the user for it */
-    if (g_rawArgs <= arg) {
-      if (!strcmp(defaultCmd, "<$>")) { /* End of command acceptable */
-        /* Note:  in this case, user will never be prompted for anything. */
-        let(&keyword,chr(3));
-        goto return1;
-      }
-      g_rawArgs++;
-      pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-      nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, 0));
-      if (g_rawArgs <= arg) bug(1104);
-      g_queryMode = 1;
-      tmpArg = cmdInput1(right(cmdList,3));
-
-      /* Strip off any quotes around it
-         and tolerate lack of trailing quote */
-      /******* (This is no longer done - it is confusing to the user.)
-      if (tmpArg[0] == '\'' || tmpArg[0] == '\"') {
-        if (tmpArg[0] == tmpArg[len(tmpArg) - 1]) {
-          let(&tmpArg, right(left(tmpArg, len(tmpArg) - 1), 2));
-        } else {
-          let(&tmpArg, right(tmpArg, 2));
-        }
-      }
-      *******/
-
-      let(&errorLine,right(cmdList,3));
-      if (tmpArg[0] == 0) { /* Use default argument */
-        let(&tmpArg, seg(defaultCmd,2,len(defaultCmd) - 1));
-      }
-      let((vstring *)(&g_rawArgPntr[arg]), tmpArg);
-      g_rawArgNmbr[arg] = len(cmdList) - 1; /* Line position for error msgs */
-
-    } /* End of asking user for additional argument */
-
-    let(&keyword,g_rawArgPntr[arg]);
-
-    /* 1-Nov-2013 nm */
-    /* Convert abbreviations of FIRST, LAST, ALL to
-       full keywords.  The rest of the program works fine without doing this,
-       but it provides better cosmetic appearance when the command is echoed
-       such as in during the UNDO command. */
-    if (cmdList[0] == '*') {
-      if ((keyword[0] == 'f' || keyword[0] == 'F')
-          && instr(1, cmdList, " FIRST") != 0)
-        let(&keyword, "FIRST");
-      if ((keyword[0] == 'l' || keyword[0] == 'L')
-          && instr(1, cmdList, " LAST") != 0)
-        let(&keyword, "LAST");
-      if ((keyword[0] == 'a' || keyword[0] == 'A')
-          && instr(1, cmdList, " ALL") != 0)
-        let(&keyword, "ALL");
-    }
-
-    if (keyword[0] == 0) { /* Use default argument */
-      /* This case handles blank arguments on completely input command line */
-      let(&keyword, seg(defaultCmd,2,len(defaultCmd) - 1));
-    }
-    if (cmdList[0] == '&') {
-      /* See if file exists */
-      let(&tmpStr, cat(g_rootDirectory, keyword, NULL)); /* 9-Jan-2018 nm */
-      tmpFp = fopen(tmpStr, "r");
-      if (!tmpFp) {
-        let(&tmpStr,  cat(
-/*"?Sorry, couldn't open the file \"", keyword, "\".", NULL));*/
-  "?Sorry, couldn't open the file \"", tmpStr, "\".", NULL)); /* 9-Jan-2018 nm */
-        printCommandError(errorLine, arg, tmpStr);
-        goto return0;
-      }
-      fclose(tmpFp);
-    }
-    goto return1;
-  }
-
-
-
-  /* Parse the choices available */
-  possCmds = 0;
-  p = 0;
-  while (1) {
-    q = p;
-    p = instr(p + 1, cat(cmdList, "|", NULL), "|");
-    if (!p) break;
-    pntrLet(&possCmd,pntrAddElement(possCmd));
-    let((vstring *)(&possCmd[possCmds]),seg(cmdList,q+1,p-1));
-    possCmds++;
-  }
-  if (!strcmp(left(possCmd[possCmds - 1],1), "<")) {
-    /* Get default argument, if any */
-    defaultCmd = possCmd[possCmds - 1]; /* re-use old allocation */
-    if (!strcmp(defaultCmd, "<$>")) {
-      let(&defaultCmd, "<nothing>");
-    }
-    pntrLet(&possCmd,pntrLeft(possCmd,possCmds - 1));
-    possCmds--;
-  }
-  if (!strcmp(possCmd[possCmds - 1], "$")) {
-    /* Change "$" to "nothing" for printouts */
-    let((vstring *)(&possCmd[possCmds - 1]), "nothing");
-  }
-
-  /* Create a string used for queries and error messages */
-  if (possCmds < 1) bug(1105);
-  if (possCmds == 1) {
-    let(&infoStr,possCmd[0]);
-  }
-  if (possCmds == 2) {
-    let(&infoStr, cat(possCmd[0], " or ",
-        possCmd[1], NULL));
-  }
-  if (possCmds > 2) {
-    let(&infoStr, "");
-    for (i = 0; i < possCmds - 1; i++) {
-      let(&infoStr, cat(infoStr,possCmd[i], ", ", NULL));
-    }
-    let(&infoStr, cat(infoStr, "or ",possCmd[possCmds - 1], NULL));
-  }
-
-  /* If the argument has not been entered, prompt the user for it */
-  if (g_rawArgs <= arg && (strcmp(possCmd[possCmds - 1], "nothing")
-      || g_queryMode == 1)) {
-
-    let(&tmpStr, infoStr);
-    if (defaultCmd[0] != 0) {
-      let(&tmpStr, cat(tmpStr, " ",defaultCmd, NULL));
-    }
-    let(&tmpStr, cat(tmpStr, "? ", NULL));
-    g_queryMode = 1;
-    if (possCmds != 1) {
-      tmpArg = cmdInput1(tmpStr);
-    } else {
-      /* There is only one possibility, so don't ask user */
-      /* Don't print the message when "end-of-list" is the only possibility. */
-      if (!strcmp(cmdList, "$|<$>")) {
-        let(&tmpArg, possCmd[0]);
-        print2("The command so far is:  ");
-        for (i = 0; i < arg; i++) {
-          print2("%s ", g_fullArg[i]);
-        }
-        print2("%s\n", tmpArg);
-      }
-    }
-    let(&errorLine,tmpStr);
-    if (tmpArg[0] == 0) { /* Use default argument */
-      let(&tmpArg, seg(defaultCmd,2,len(defaultCmd) - 1));
-    }
-
-    if (strcmp(tmpArg, "nothing")) {
-      pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-      nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, 0));
-      g_rawArgs++;
-      if (g_rawArgs <= arg) bug(1106);
-      let((vstring *)(&g_rawArgPntr[arg]), tmpArg);
-      g_rawArgNmbr[arg] = len(tmpStr) + 1; /* Line position for error msgs */
-    }
-
-  } /* End of asking user for additional argument */
-
-  if (g_rawArgs <= arg) {
-    /* No argument was specified, and "nothing" is a valid argument */
-    let(&keyword,chr(3));
-    goto return1;
-  }
-
-
-  let(&tmpArg,edit(g_rawArgPntr[arg], 32)); /* Convert to upper case */
-  j = 0;
-  k = 0;
-  m = len(tmpArg);
-  let(&tmpStr, "");
-  /* Scan the possible arguments for a match */
-  for (i = 0; i < possCmds; i++) {
-    if (!strcmp(possCmd[i], tmpArg)) {
-      /* An exact match was found, so ignore any other matches
-         and use this one */
-      k = 1;
-      j = i;
-      break;
-    }
-    if (!strcmp(left(possCmd[i], m), tmpArg)) {
-      if (!k) {
-        let(&tmpStr, possCmd[i]);
-      } else {
-        let(&tmpStr, cat(tmpStr, ", ", possCmd[i], NULL));
-      }
-      j = i; /* Save match position */
-      k++; /* Number of matches */
-    }
-  }
-  if (k < 1 || k > 1) {
-    if (k < 1) {
-      let(&tmpStr, cat("?Expected ", infoStr, ".", NULL));
-    } else {
-      if (k == 2) {
-        p = instr(1,tmpStr, ", ");
-        let(&tmpStr, cat(left(tmpStr,p-1), " or",right(tmpStr,p+1), NULL));
-      } else {
-        p = len(tmpStr) - 1;
-        while (tmpStr[p] != ',') p--;
-        let(&tmpStr, cat(left(tmpStr,p+1), " or",right(tmpStr,p+2), NULL));
-      }
-      let(&tmpStr, cat("?Ambiguous keyword - please specify ",tmpStr, ".", NULL));
-    }
-    printCommandError(errorLine, arg, tmpStr);
-    goto return0;
-  }
-
-  let(&keyword,possCmd[j]);
-  goto return1;
-
- return1:
-  if (keyword[0] == 0) {
-    if (g_rawArgs > arg && strcmp(defaultCmd, "<>")) {
-      /* otherwise, "nothing" was specified */
-      printCommandError("", arg,
-          "?No default answer is available - please be explicit.");
-      goto return0;
-    }
-  }
-  /* Add new field to g_fullArg */
-  pntrLet(&g_fullArg,pntrAddElement(g_fullArg));
-  if (pntrLen(g_fullArg) != arg + 1) bug(1107);
-  let((vstring *)(&g_fullArg[arg]),keyword);
-
-  /* Deallocate memory */
-  j = pntrLen(possCmd);
-  for (i = 0; i < j; i++) let((vstring *)(&possCmd[i]), "");
-  pntrLet(&possCmd, NULL_PNTRSTRING);
-  let(&defaultCmd, "");
-  let(&infoStr, "");
-  let(&tmpStr, "");
-  let(&tmpArg, "");
-  let(&errorLine, "");
-  let(&keyword, "");
-  let(&cmdList, "");
-  return(1);
-
- return0:
-  /* Deallocate memory */
-  j = pntrLen(possCmd);
-  for (i = 0; i < j; i++) let((vstring *)(&possCmd[i]), "");
-  pntrLet(&possCmd, NULL_PNTRSTRING);
-  let(&defaultCmd, "");
-  let(&infoStr, "");
-  let(&tmpStr, "");
-  let(&tmpArg, "");
-  let(&errorLine, "");
-  let(&keyword, "");
-  let(&cmdList, "");
-  return(0);
-
-} /* getFullArg */
-
-
-
-void parseCommandLine(vstring line)
-{
-  /* This function breaks up line into individual tokens
-     and puts them into g_rawArgPntr[].  g_rawArgs is the number of tokens.
-     g_rawArgPntr[] is the starting position of each token on the line;
-     the first character on the line has position 1, not 0.
-
-     Spaces, tabs, and newlines are considered white space.  Special
-     one-character
-     tokens don't have to be surrounded by white space.  Characters
-     inside quotes are considered to be one token, and the quotes are
-     removed.
-
-  */
-  /* Warning:  Don't deallocate these vstring constants */
-  /*vstring specialOneCharTokens = "()/,=:";*/
-  vstring tokenWhiteSpace = " \t\n";
-  vstring tokenComment = "!";
-
-
-  vstring tmpStr = ""; /* Dummy vstring to clean up temp alloc stack */
-  flag mode;
-  long tokenStart, i, p, lineLen;
-
-  vstring specialOneCharTokens = "";
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  tokenStart = 0;
-
-  if (!g_toolsMode) {
-    /* 5-Nov-99:  We only really need / and =
-       Took out the others so user will have less need to put quotes around
-       e.g. single tokens in SEARCH command
-    let(&specialOneCharTokens, "()/,=:");
-    */
-    let(&specialOneCharTokens, "/="); /* List of special one-char tokens */
-  } else {
-    let(&specialOneCharTokens, "");
-  }
-
-  lineLen = len(line);
-  /* mode: 0 means look for start of token, 1 means look for end of
-     token, 2 means look for trailing single quote, 3 means look for
-     trailing double quote */
-  /* 2/20/99 - only "!" at beginning of line now acts as comment
-     - This was done because sometimes ! might be legal as part of a command */
-  mode = 0;
-  for (p = 0; p < lineLen; p++) {
-    let(&tmpStr, ""); /* Clean up temp alloc stack to prevent overflow */
-    if (mode == 0) {
-      /* If character is white space, ignore it */
-      if (instr(1,tokenWhiteSpace,chr(line[p]))) {
-        continue;
-      }
-      /* If character is comment, we're done */
-      if (p == 0 && /* 2/20/99 */ instr(1,tokenComment,chr(line[p]))) {
-        break;
-      }
-      /* If character is a special token, get it but don't change mode */
-      if (instr(1,specialOneCharTokens,chr(line[p]))) {
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, p+1));
-                                                          /* Save token start */
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]), chr(line[p]));
-        g_rawArgs++;
-        continue;
-      }
-      /* If character is a quote, set start and change mode */
-      if (line[p] == '\'') {
-        mode = 2;
-        tokenStart = p + 2;
-        continue;
-      }
-      if (line[p] == '\"') {
-        mode = 3;
-        tokenStart = p + 2;
-        continue;
-      }
-      /* Character must be start of a token */
-      mode = 1;
-      tokenStart = p + 1;
-      continue;
-    }
-    if (mode == 1) {
-      /* If character is white space, end token and change mode */
-      if (instr(1,tokenWhiteSpace,chr(line[p]))) {
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
-                                                          /* Save token start */
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]), seg(line, tokenStart, p));
-        g_rawArgs++;
-        mode = 0;
-        continue;
-      }
-      /* If character is comment, we're done */
-      /* 2/20/99 - see above
-      if (instr(1,tokenComment,chr(line[p]))) {
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]), seg(line,tokenStart,p));
-        g_rawArgs++;
-        mode = 0;
-        break;
-      }
-      2/20/99 */
-      /* If character is a special token, get it and change mode */
-      if (instr(1,specialOneCharTokens,chr(line[p]))) {
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
-                                                          /* Save token start */
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart, p));
-        g_rawArgs++;
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, p + 1));
-                                                          /* Save token start */
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]), chr(line[p]));
-        g_rawArgs++;
-        mode = 0;
-        continue;
-      }
-      /* If character is a quote, set start and change mode */
-      if (line[p] == '\'') {
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
-                                                          /* Save token start */
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart,p));
-        g_rawArgs++;
-        mode = 2;
-        tokenStart = p + 2;
-        continue;
-      }
-      if (line[p] == '\"') {
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
-                                                          /* Save token start */
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart,p));
-        g_rawArgs++;
-        mode = 3;
-        tokenStart = p + 2;
-        continue;
-      }
-      /* Character must be continuation of the token */
-      continue;
-    }
-    if (mode == 2 || mode == 3) {
-      /* If character is a quote, end quote and change mode */
-      if (line[p] == '\'' && mode == 2) {
-        mode = 0;
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
-                                                          /* Save token start */
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]), seg(line,tokenStart,p));
-        g_rawArgs++;
-        continue;
-      }
-      if (line[p] == '\"' && mode == 3) {
-        mode = 0;
-        pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-        nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
-                                                          /* Save token start */
-        let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart,p));
-        g_rawArgs++;
-        continue;
-      }
-      /* Character must be continuation of quoted token */
-      continue;
-    }
-  }
-
-  /* Finished scanning the line.  Finish processing last token. */
-  if (mode != 0) {
-    pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
-    nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
-                                                          /* Save token start */
-    let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line,tokenStart,p));
-    g_rawArgs++;
-  }
-
-  /* Add length of command line prompt to each argument, to
-     align the error message pointer */
-  for (i = 0; i < g_rawArgs; i++) {
-    g_rawArgNmbr[i] = g_rawArgNmbr[i] + len(g_commandPrompt);
-  }
-
-  /* Deallocate */
-  let(&specialOneCharTokens, "");
-} /* parseCommandLine */
-
-
-flag lastArgMatches(vstring argString)
-{
-  /* This functions checks to see if the last field was argString */
-  if (!strcmp(argString, g_fullArg[pntrLen(g_fullArg)-1])) {
-    return (1);
-  } else {
-    return (0);
-  }
-} /* lastArgMatches */
-
-flag cmdMatches(vstring cmdString)
-{
-  /* This function checks that fields 0 through n of g_fullArg match
-     cmdString (separated by spaces). */
-  long i, j, k;
-  vstring tmpStr = "";
-  /* Count the number of spaces */
-  k = len(cmdString);
-  j = 0;
-  for (i = 0; i < k; i++) {
-    if (cmdString[i] == ' ') j++;
-  }
-  k = pntrLen(g_fullArg);
-  for (i = 0; i <= j; i++) {
-    if (j >= k) {
-      /* Command to match is longer than the user's command; assume no match */
-      let(&tmpStr, "");
-      return (0);
-    }
-    let(&tmpStr, cat(tmpStr, " ", g_fullArg[i], NULL));
-  }
-  if (!strcmp(cat(" ", cmdString, NULL), tmpStr)) {
-    let(&tmpStr, "");
-    return (1);
-  } else {
-    let(&tmpStr, "");
-    return (0);
-  }
-} /* cmdMatches */
-
-
-long switchPos(vstring swString)
-{
-  /* This function checks that fields i through j of g_fullArg match
-     swString (separated by spaces).  The first character of swString
-     should be "/" and must be separated from the first field
-     of swString with a space.  The position of the "/" in g_fullArg
-     is returned if swString is there, otherwise 0 is returned (the first
-     position in g_fullArg is considered 1, not 0). */
-  /* Example:  if g_fullArg (combined into one string) is
-     "DISPLAY PROOF / UNKNOWN / START_STEP = 10 / ESSENTIAL"
-     and swString is "/ START_STEP", switchPos will return 5. */
-  long i, j, k;
-  vstring tmpStr = "";
-  vstring swString1 = "";
-
-  if (swString[0] != '/') bug(1108);
-
-  /* Add a space after the "/" if there is none */
-  if (swString[1] != ' ') {
-    let(&swString1, cat("/ ", right(swString,2), " ", NULL));
-  } else {
-    let(&swString1,swString);
-  }
-
-  /* Build the complete command */
-  k = pntrLen(g_fullArg);
-  for (i = 0; i < k; i++) {
-    let(&tmpStr, cat(tmpStr,g_fullArg[i], " ", NULL));
-  }
-
-  k = instr(1,tmpStr,swString1);
-  if (!k) {
-    let(&swString1, "");
-    let(&tmpStr, "");
-    return (0);
-  }
-
-  let(&tmpStr,left(tmpStr,k));
-  /* Count the number of spaces - it will be the g_fullArg position */
-  k = len(tmpStr);
-  j = 0;
-  for (i = 0; i < k; i++) {
-    if (tmpStr[i] == ' ') j++;
-  }
-  let(&tmpStr, "");
-  let(&swString1, "");
-  return (j + 1);
-} /* switchPos */
-
-
-void printCommandError(vstring line1, long arg, vstring errorMsg)
-{
-  /* Warning: errorMsg should not a temporarily allocated string such
-     as the direct output of cat() */
-  vstring errorPointer = "";
-  vstring line = "";
-  long column, tokenLength, j;
-
-  let(&line,line1); /* Prevent deallocation in case line1 is
-                       direct return from string function such as cat() */
-  if (!line[0]) {
-    /* Empty line - don't print an error pointer */
-    print2("%s\n", errorMsg);
-    let(&line, "");
-    return;
-  }
-  column = g_rawArgNmbr[arg];
-  tokenLength = len(g_rawArgPntr[arg]);
-  for (j = 0; j < column - 1; j++) {
-    /* Make sure that tabs on the line with the error are accounted for so
-       that the error pointer lines up correctly */
-    if (j >= len(line)) bug(1109);
-    if (line[j] == '\t') {
-      let(&errorPointer, cat(errorPointer, "\t", NULL));
-    } else {
-      if (line[j] == '\n') {
-        let(&errorPointer, "");
-      } else {
-        let(&errorPointer, cat(errorPointer, " ", NULL));
-      }
-    }
-  }
-  for (j = 0; j < tokenLength; j++)
-    let(&errorPointer, cat(errorPointer, "^", NULL));
-  print2("%s\n", errorPointer);
-  printLongLine(errorMsg, "", " ");
-  let(&errorPointer, "");
-  let(&line, "");
-} /* printCommandError */
-
-/* 4-May-2017 Ari Ferrera */
-void freeCommandLine() {
-  long i, j;
-  j = pntrLen(g_rawArgPntr);
-  for (i = 0; i < j; i++) let((vstring *)(&g_rawArgPntr[i]), "");
-  j = pntrLen(g_fullArg);
-  for (i = 0; i < j; i++) let((vstring *)(&g_fullArg[i]), "");
-  pntrLet(&g_fullArg, NULL_PNTRSTRING);
-  pntrLet(&g_rawArgPntr, NULL_PNTRSTRING);
-  nmbrLet(&g_rawArgNmbr, NULL_NMBRSTRING);
-  let(&g_fullArgString, "");
-}
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* Command line syntax specification for Metamath */
+
+#include <string.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mmcmdl.h"
+#include "mminou.h"
+#include "mmpfas.h"
+#include "mmunif.h" /* For g_hentyFilter, g_userMaxUnifTrials, g_minSubstLen */
+#include "mmwtex.h"
+#include "mmword.h"
+
+/* Global variables */
+pntrString_def(g_rawArgPntr);
+nmbrString_def(g_rawArgNmbr);
+long g_rawArgs = 0;
+pntrString_def(g_fullArg);
+vstring_def(g_fullArgString); /* g_fullArg as one string */
+vstring_def(g_commandPrompt);
+vstring_def(g_commandLine);
+long g_showStatement = 0;
+vstring_def(g_logFileName);
+vstring_def(g_texFileName);
+flag g_PFASmode = 0; /* Proof assistant mode, invoked by PROVE command */
+flag g_queryMode = 0; /* If 1, explicit questions will be asked even if
+    a field in the input command line is optional */
+flag g_sourceChanged = 0; /* Flag that user made some change to the source file*/
+flag g_proofChanged = 0; /* Flag that user made some change to proof in progress*/
+flag g_commandEcho = 0; /* Echo full command */
+flag g_memoryStatus = 0; /* Always show memory */
+flag g_sourceHasBeenRead = 0; /* 1 if a source file has been read in */
+vstring_def(g_rootDirectory); /* Directory prefix to use for included files */
+
+
+static flag getFullArg(long arg, const char *cmdList);
+
+flag processCommandLine(void) {
+  vstring_def(defaultArg);
+  vstring_def(tmpStr);
+  long i;
+  g_queryMode = 0; /* If 1, explicit questions will be asked even if
+    a field is optional */
+  free_pntrString(g_fullArg);
+
+  if (!g_toolsMode) {
+
+    if (!g_PFASmode) {
+      /* Normal mode */
+      let(&tmpStr, cat("DBG|",
+          "HELP|READ|WRITE|PROVE|SHOW|SEARCH|SAVE|SUBMIT|OPEN|CLOSE|",
+          "SET|FILE|BEEP|EXIT|QUIT|ERASE|VERIFY|MARKUP|MORE|TOOLS|",
+          "MIDI|<HELP>",
+          NULL));
+    } else {
+      /* Proof assistant mode */
+      let(&tmpStr, cat("DBG|",
+          "HELP|WRITE|SHOW|SEARCH|SAVE|SUBMIT|OPEN|CLOSE|",
+          "SET|FILE|BEEP|EXIT|_EXIT_PA|QUIT|VERIFY|INITIALIZE|ASSIGN|REPLACE|",
+          "LET|UNIFY|IMPROVE|MINIMIZE_WITH|EXPAND|MATCH|DELETE|UNDO|REDO|",
+          "MARKUP|MORE|TOOLS|MIDI|<HELP>",
+          NULL));
+    }
+    if (!getFullArg(0, tmpStr)) {
+      goto pclbad;
+    }
+
+    if (cmdMatches("HELP")) {
+      if (!getFullArg(1, cat("LANGUAGE|PROOF_ASSISTANT|MM-PA|",
+          "BEEP|EXIT|QUIT|READ|ERASE|",
+          "OPEN|CLOSE|SHOW|SEARCH|SET|VERIFY|SUBMIT|SYSTEM|PROVE|FILE|WRITE|",
+          "MARKUP|ASSIGN|REPLACE|MATCH|UNIFY|LET|INITIALIZE|DELETE|IMPROVE|",
+          "MINIMIZE_WITH|EXPAND|UNDO|REDO|SAVE|DEMO|INVOKE|CLI|EXPLORE|TEX|",
+          "LATEX|HTML|COMMENTS|BIBLIOGRAPHY|MORE|",
+          "TOOLS|MIDI|$|<$>", NULL))) goto pclbad;
+      if (cmdMatches("HELP OPEN")) {
+        if (!getFullArg(2, "LOG|TEX|<LOG>")) goto pclbad;
+        goto pclgood;
+      }
+      if (cmdMatches("HELP CLOSE")) {
+        if (!getFullArg(2, "LOG|TEX|<LOG>")) goto pclbad;
+        goto pclgood;
+      }
+      if (cmdMatches("HELP SHOW")) {
+        if (!getFullArg(2, cat("MEMORY|SETTINGS|LABELS|SOURCE|STATEMENT|",
+            "PROOF|NEW_PROOF|USAGE|TRACE_BACK|ELAPSED_TIME|",
+            "DISCOURAGED|<MEMORY>",
+            NULL)))
+            goto pclbad;
+        goto pclgood;
+      }
+      if (cmdMatches("HELP SET")) {
+        if (!getFullArg(2, cat(
+            "ECHO|SCROLL|WIDTH|HEIGHT|UNDO|UNIFICATION_TIMEOUT|",
+            "DISCOURAGEMENT|",
+            "CONTRIBUTOR|",
+            "ROOT_DIRECTORY|",
+            "EMPTY_SUBSTITUTION|SEARCH_LIMIT|JEREMY_HENTY_FILTER|<ECHO>",
+            NULL)))
+            goto pclbad;
+        goto pclgood;
+      }
+      if (cmdMatches("HELP VERIFY")) {
+        if (!getFullArg(2, "PROOF|MARKUP|<PROOF>"))
+            goto pclbad;
+        goto pclgood;
+      }
+      if (cmdMatches("HELP WRITE")) {
+        if (!getFullArg(2,
+            "SOURCE|THEOREM_LIST|BIBLIOGRAPHY|RECENT_ADDITIONS|<SOURCE>"))
+            goto pclbad;
+        goto pclgood;
+      }
+      if (cmdMatches("HELP FILE")) {
+        if (!getFullArg(2, "SEARCH"))
+            goto pclbad;
+        goto pclgood;
+      }
+      if (cmdMatches("HELP SAVE")) {
+        if (!getFullArg(2,
+            "PROOF|NEW_PROOF|<PROOF>"))
+            goto pclbad;
+        goto pclgood;
+      }
+      goto pclgood;
+    } /* cmdMatches("HELP") */
+
+    if (cmdMatches("READ")) {
+      if (!getFullArg(1, "& What is the name of the source input file? "))
+          goto pclbad;
+      /* Get any switches */
+      i = 1;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, "VERIFY|<VERIFY>")) goto pclbad;
+        } else {
+          break;
+        }
+        break; /* Break if only 1 switch is allowed */
+      } /* End while for switch loop */
+      goto pclgood;
+    }
+
+    if (cmdMatches("WRITE")) {
+      if (!getFullArg(1,
+          "SOURCE|THEOREM_LIST|BIBLIOGRAPHY|RECENT_ADDITIONS|<SOURCE>"))
+        goto pclbad;
+      if (cmdMatches("WRITE SOURCE")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2, cat(
+            "* What is the name of the source output file <",
+            g_input_fn, ">? ", NULL)))
+          goto pclbad;
+        if (!strcmp(g_input_fn, g_fullArg[2])) {
+          print2("The input file will be renamed %s~1.\n", g_input_fn);
+        }
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "FORMAT|REWRAP",
+                "|SPLIT|NO_VERSIONING|KEEP_INCLUDES|EXTRACT",
+                "|<REWRAP>", NULL)))
+              goto pclbad;
+            if (lastArgMatches("EXTRACT")) {
+              i++;
+              if (!getFullArg(i, "* What statement label? "))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        } /* End while for switch loop */
+
+        goto pclgood;
+      }
+      if (cmdMatches("WRITE THEOREM_LIST")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        /* Get any switches */
+        i = 1;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "THEOREMS_PER_PAGE|SHOW_LEMMAS|HTML|ALT_HTML|NO_VERSIONING",
+                "|<THEOREMS_PER_PAGE>", NULL)))
+              goto pclbad;
+            if (lastArgMatches("THEOREMS_PER_PAGE")) {
+              i++;
+              if (!getFullArg(i, "# How many theorems per page <100>? "))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      }
+      if (cmdMatches("WRITE BIBLIOGRAPHY")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2, cat(
+            "* What is the bibliography HTML input/output file <",
+            "mmbiblio.html", ">? ", NULL)))
+          goto pclbad;
+        print2(
+          "The old file will be renamed %s~1.\n", g_fullArg[2]);
+        goto pclgood;
+      }
+      if (cmdMatches("WRITE RECENT_ADDITIONS")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2, cat(
+            "* What is the Recent Additions HTML input/output file <",
+            "mmrecent.html", ">? ", NULL)))
+          goto pclbad;
+        print2(
+          "The old file will be renamed %s~1.\n", g_fullArg[2]);
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "LIMIT|HTML|ALT_HTML",
+                "|<LIMIT>", NULL)))
+              goto pclbad;
+            if (lastArgMatches("LIMIT")) {
+              i++;
+              if (!getFullArg(i, "# How many most recent theorems <100>? "))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          /*break;*/ /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      }
+    }
+
+    if (cmdMatches("OPEN")) {
+      if (!getFullArg(1, "LOG|TEX|<LOG>")) goto pclbad;
+      if (cmdMatches("OPEN LOG")) {
+        if (g_logFileOpenFlag) {
+          printLongLine(cat(
+              "?Sorry, the log file \"", g_logFileName, "\" is currently open.  ",
+              "Type CLOSE LOG to close the current log if you want to open another one.",
+              NULL), "", " ");
+          goto pclbad;
+        }
+        if (!getFullArg(2, "* What is the name of logging output file? "))
+          goto pclbad;
+      }
+      if (cmdMatches("OPEN TEX")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (g_texFileOpenFlag) {
+          printLongLine(cat(
+              "?Sorry, the LaTeX file \"", g_texFileName, "\" is currently open.  ",
+              "Type CLOSE TEX to close the current LaTeX file",
+              " if you want to open another one."
+              , NULL), "", " ");
+          goto pclbad;
+        }
+        if (!getFullArg(2, "* What is the name of LaTeX output file? "))
+          goto pclbad;
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "NO_HEADER|OLD_TEX|<NO_HEADER>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        } /* End while for switch loop */
+
+      }
+      goto pclgood;
+    }
+
+    if (cmdMatches("CLOSE")) {
+      if (!getFullArg(1, "LOG|TEX|<LOG>")) goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("FILE")) {
+      if (!getFullArg(1, cat("SEARCH", NULL))) goto pclbad;
+
+      if (cmdMatches("FILE SEARCH")) {
+        if (!getFullArg(2, "& What is the name of the file to search? "))
+          goto pclbad;
+        if (!getFullArg(3, "* What is the string to search for? "))
+          goto pclbad;
+
+
+        /* Get any switches */
+        i = 3;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (i == 4) {
+              if (!getFullArg(i, cat(
+                  "FROM_LINE|TO_LINE|<FROM_LINE>", NULL)))
+                goto pclbad;
+            } else {
+              if (!getFullArg(i, cat(
+                  "FROM_LINE|TO_LINE|<TO_LINE>", NULL)))
+                goto pclbad;
+            }
+            if (lastArgMatches("FROM_LINE")) {
+              i++;
+              if (!getFullArg(i, "# From what line number <1>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("TO_LINE")) {
+              i++;
+              if (!getFullArg(i, "# To what line number <999999>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("WINDOW")) { /* ???Not implemented yet */
+              i++;
+              if (!getFullArg(i, "# How big a window around matched lines <0>? "))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        } /* End while for switch loop */
+
+
+        goto pclgood;
+      } /* End if (cmdMatches("FILE SEARCH")) */
+      goto pclgood;
+    }
+
+    if (cmdMatches("SHOW")) {
+      if (!g_PFASmode) {
+        if (!getFullArg(1, cat(
+     "SETTINGS|LABELS|STATEMENT|SOURCE|PROOF|MEMORY|TRACE_BACK|",
+     "USAGE|ELAPSED_TIME|DISCOURAGED|<SETTINGS>", NULL)))
+            goto pclbad;
+      } else {
+        if (!getFullArg(1, cat("NEW_PROOF|",
+     "SETTINGS|LABELS|STATEMENT|SOURCE|PROOF|MEMORY|TRACE_BACK|",
+     "USAGE|ELAPSED_TIME|DISCOURAGED|<SETTINGS>",
+            NULL)))
+            goto pclbad;
+      }
+      if (g_showStatement) {
+        if (g_showStatement < 1 || g_showStatement > g_statements) bug(1110);
+        let(&defaultArg, cat(" <",g_Statement[g_showStatement].labelName, ">",
+            NULL));
+      } else {
+        let(&defaultArg, "");
+      }
+
+
+      if (cmdMatches("SHOW TRACE_BACK")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            cat("* What is the statement label", defaultArg, "? ", NULL)))
+          goto pclbad;
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "ALL|ESSENTIAL|AXIOMS|TREE|DEPTH|COUNT_STEPS|MATCH|TO",
+                "|<ALL>", NULL)))
+              goto pclbad;
+            if (lastArgMatches("DEPTH")) {
+              i++;
+              if (!getFullArg(i, "# How many indentation levels <999>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("MATCH")) {
+              i++;
+              if (!getFullArg(i, "* What statement label? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("TO")) {
+              i++;
+              if (!getFullArg(i, "* What statement label? "))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        }
+
+        goto pclgood;
+      } /* End if (cmdMatches("SHOW TRACE_BACK")) */
+
+      if (cmdMatches("SHOW USAGE")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            cat("* What is the statement label", defaultArg, "? ", NULL)))
+          goto pclbad;
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "DIRECT|RECURSIVE|ALL",
+                "|<DIRECT>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          /* break; */  /* Break if only 1 switch is allowed */
+        }
+
+        goto pclgood;
+      } /* End if (cmdMatches("SHOW USAGE")) */
+
+
+      if (cmdMatches("SHOW LABELS")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            "* What are the labels to match (* = wildcard) <*>?"))
+          goto pclbad;
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat("ALL|LINEAR|<ALL>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          /*break;*/ /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      }
+      if (cmdMatches("SHOW STATEMENT")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            cat("* What is the statement label", defaultArg, "? ", NULL)))
+          goto pclbad;
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "FULL|COMMENT|TEX|OLD_TEX|HTML|ALT_HTML|TIME|BRIEF_HTML",
+                "|BRIEF_ALT_HTML|MNEMONICS|NO_VERSIONING|<FULL>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      }
+      if (cmdMatches("SHOW SOURCE")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            cat("* What is the statement label", defaultArg, "? ", NULL))) {
+          goto pclbad;
+        }
+        goto pclgood;
+      }
+
+
+      if (cmdMatches("SHOW PROOF")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            cat("* What is the statement label", defaultArg, "? ", NULL)))
+          goto pclbad;
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "ESSENTIAL|ALL|UNKNOWN|FROM_STEP|TO_STEP|DEPTH",
+                "|REVERSE|VERBOSE|NORMAL|PACKED|COMPRESSED|EXPLICIT",
+                "|FAST|OLD_COMPRESSION",
+                "|STATEMENT_SUMMARY|DETAILED_STEP|TEX|OLD_TEX|HTML",
+                "|LEMMON|START_COLUMN|NO_REPEATED_STEPS",
+                "|RENUMBER|SIZE|<ESSENTIAL>", NULL)))
+              goto pclbad;
+            if (lastArgMatches("FROM_STEP")) {
+              i++;
+              if (!getFullArg(i, "# From what step <1>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("TO_STEP")) {
+              i++;
+              if (!getFullArg(i, "# To what step <9999>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("DEPTH")) {
+              i++;
+              if (!getFullArg(i, "# How many indentation levels <999>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("DETAILED_STEP")) {
+              i++;
+              if (!getFullArg(i, "# Display details of what step <1>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("START_COLUMN")) {
+              i++;
+              if (!getFullArg(i, cat(
+                  "# At what column should the formula start <",
+                  str((double)DEFAULT_COLUMN), ">? ", NULL)))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      } /* End if (cmdMatches("SHOW PROOF")) */
+
+
+      if (cmdMatches("SHOW NEW_PROOF")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+
+        /* Get any switches */
+        i = 1;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "ESSENTIAL|ALL|UNKNOWN|FROM_STEP|TO_STEP|DEPTH",
+                "|REVERSE|VERBOSE|NORMAL|PACKED|COMPRESSED|EXPLICIT",
+                "|OLD_COMPRESSION",
+                "|NOT_UNIFIED|TEX|HTML",
+                "|LEMMON|START_COLUMN|NO_REPEATED_STEPS",
+                "|RENUMBER|<ESSENTIAL>", NULL)))
+              goto pclbad;
+            if (lastArgMatches("FROM_STEP")) {
+              i++;
+              if (!getFullArg(i, "# From what step <1>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("TO_STEP")) {
+              i++;
+              if (!getFullArg(i, "# To what step <9999>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("DEPTH")) {
+              i++;
+              if (!getFullArg(i, "# How many indentation levels <999>? "))
+                goto pclbad;
+            }
+            if (lastArgMatches("START_COLUMN")) {
+              i++;
+              if (!getFullArg(i, cat(
+                  "# At what column should the formula start <",
+                  str((double)DEFAULT_COLUMN), ">? ", NULL)))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      } /* End if (cmdMatches("SHOW NEW_PROOF")) */
+
+
+      goto pclgood;
+    } /* End of SHOW */
+
+    if (cmdMatches("SEARCH")) {
+      if (g_sourceHasBeenRead == 0) {
+        print2("?No source file has been read in.  Use READ first.\n");
+        goto pclbad;
+      }
+      if (!getFullArg(1,
+          "* What are the labels to match (* = wildcard) <*>?"))
+        goto pclbad;
+      if (!getFullArg(2, "* Search for what math symbol string? "))
+          goto pclbad;
+      /* Get any switches */
+      i = 2;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, cat("ALL|COMMENTS|JOIN|<ALL>", NULL)))
+            goto pclbad;
+        } else {
+          break;
+        }
+        /*break;*/ /* Break if only 1 switch is allowed */
+      }
+      goto pclgood;
+
+    } /* End of SEARCH */
+
+    if (cmdMatches("SAVE")) {
+      if (!g_PFASmode) {
+        if (!getFullArg(1,
+            "PROOF|<PROOF>"))
+            goto pclbad;
+      } else {
+        if (!getFullArg(1, cat("NEW_PROOF|",
+            "PROOF|<NEW_PROOF>",
+            NULL)))
+            goto pclbad;
+      }
+      if (g_showStatement) {
+        if (g_showStatement < 0) bug(1111);
+        let(&defaultArg, cat(" <",g_Statement[g_showStatement].labelName, ">", NULL));
+      } else {
+        let(&defaultArg, "");
+      }
+
+
+      if (cmdMatches("SAVE PROOF")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            cat("* What is the statement label", defaultArg, "? ", NULL)))
+          goto pclbad;
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "NORMAL|PACKED|COMPRESSED|EXPLICIT",
+                "|FAST|OLD_COMPRESSION",
+                "|TIME|<NORMAL>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          /* break; */ /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      } /* End if (cmdMatches("SAVE PROOF")) */
+
+
+      if (cmdMatches("SAVE NEW_PROOF")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+
+        /* Get any switches */
+        i = 1;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "NORMAL|PACKED|COMPRESSED|EXPLICIT",
+                "|OLD_COMPRESSION|OVERRIDE",
+                "|<NORMAL>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          /*break;*/ /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      } /* End if (cmdMatches("SAVE NEW_PROOF")) */
+
+
+      goto pclgood;
+    } /* End of SAVE */
+
+    if (cmdMatches("PROVE")) {
+      if (g_sourceHasBeenRead == 0) {
+        print2("?No source file has been read in.  Use READ first.\n");
+        goto pclbad;
+      }
+      if (!g_proveStatement) g_proveStatement = g_showStatement;
+      if (g_proveStatement) {
+        let(&defaultArg, cat(" <",g_Statement[g_proveStatement].labelName, ">", NULL));
+      } else {
+        let(&defaultArg, "");
+      }
+      if (!getFullArg(1,
+          cat("* What is the label of the statement you want to try proving",
+          defaultArg, "? ", NULL)))
+        goto pclbad;
+
+      /* Get any switches */
+      i = 1;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, "OVERRIDE|<OVERRIDE>")) goto pclbad;
+        } else {
+          break;
+        }
+        break; /* Break if only 1 switch is allowed */
+      } /* End while for switch loop */
+
+      goto pclgood;
+    }
+
+    /* Commands in Proof Assistant mode */
+
+    if (cmdMatches("MATCH")) {
+      if (!getFullArg(1,
+          "STEP|ALL|<ALL>")) goto pclbad;
+      if (cmdMatches("MATCH STEP")) {
+        if (!getFullArg(2, "# What step number? ")) goto pclbad;
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "MAX_ESSENTIAL_HYP|<MAX_ESSENTIAL_HYP>", NULL)))
+              goto pclbad;
+            if (lastArgMatches("MAX_ESSENTIAL_HYP")) {
+              i++;
+              if (!getFullArg(i,
+  "# Maximum number of essential hypotheses to allow for a match <0>? "))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          break;  /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      }
+      if (cmdMatches("MATCH ALL")) {
+        /* Get any switches */
+        i = 1;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "ESSENTIAL|MAX_ESSENTIAL_HYP|<ESSENTIAL>", NULL)))
+              goto pclbad;
+            if (lastArgMatches("MAX_ESSENTIAL_HYP")) {
+              i++;
+              if (!getFullArg(i,
+  "# Maximum number of essential hypotheses to allow for a match <0>? "))
+                goto pclbad;
+            }
+          } else {
+            break;
+          }
+          /*break;*/  /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      } /* End if (cmdMatches("MATCH ALL")) */
+      goto pclgood;
+    }
+
+    if (cmdMatches("INITIALIZE")) {
+      if (!getFullArg(1,
+          "STEP|ALL|USER|<ALL>")) goto pclbad;
+      if (cmdMatches("INITIALIZE STEP")) {
+        if (!getFullArg(2, "# What step number? ")) goto pclbad;
+      }
+      goto pclgood;
+    }
+
+    if (cmdMatches("IMPROVE")) {
+      if (!getFullArg(1,
+        "* What step number, or FIRST, or LAST, or ALL <ALL>? ")) goto pclbad;
+      /* Get any switches */
+      i = 1;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, "DEPTH|NO_DISTINCT|1|2|3"
+            "|SUBPROOFS|OVERRIDE|INCLUDE_MATHBOXES|<DEPTH>")) goto pclbad;
+          if (lastArgMatches("DEPTH")) {
+            i++;
+            if (!getFullArg(i, "# What is maximum depth for "
+              "searching statements with $e hypotheses <0>? ")) goto pclbad;
+          }
+        } else {
+          break;
+        }
+        /*break;*/ /* Do this if only 1 switch is allowed */
+      } /* end while */
+      goto pclgood;
+    } /* end if IMPROVE */
+
+    if (cmdMatches("MINIMIZE_WITH")) {
+      if (!getFullArg(1, "* What statement label? ")) goto pclbad;
+      /* Get any switches */
+      i = 1;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, cat(
+              "VERBOSE|MAY_GROW|EXCEPT|OVERRIDE|INCLUDE_MATHBOXES|",
+              "ALLOW_NEW_AXIOMS|NO_NEW_AXIOMS_FROM|FORBID|TIME|<VERBOSE>",
+              NULL)))
+            goto pclbad;
+
+          if (lastArgMatches("EXCEPT")) {
+            i++;
+            if (!getFullArg(i, "* What statement label match pattern? "))
+              goto pclbad;
+          }
+          if (lastArgMatches("ALLOW_NEW_AXIOMS")) {
+            i++;
+            if (!getFullArg(i, "* What statement label match pattern? "))
+              goto pclbad;
+          }
+          if (lastArgMatches("NO_NEW_AXIOMS_FROM")) {
+            i++;
+            if (!getFullArg(i, "* What statement label match pattern? "))
+              goto pclbad;
+          }
+          if (lastArgMatches("FORBID")) {
+            i++;
+            if (!getFullArg(i, "* What statement label match pattern? "))
+              goto pclbad;
+          }
+        } else {
+          break;
+        }
+        /*break;*/  /* Break if only 1 switch is allowed */
+      }
+      goto pclgood;
+    } /* end of MINIMIZE_WITH */
+
+    if (cmdMatches("EXPAND")) {
+      if (!getFullArg(1, "* What statement label? ")) goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("UNIFY")) {
+      if (!getFullArg(1,
+          "STEP|ALL|<ALL>")) goto pclbad;
+      if (cmdMatches("UNIFY STEP")) {
+        if (!getFullArg(2, "# What step number? ")) goto pclbad;
+        goto pclgood;
+      }
+      if (cmdMatches("UNIFY ALL")) {
+        /* Get any switches */
+        i = 1;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "INTERACTIVE|<INTERACTIVE>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          break;  /* Break if only 1 switch is allowed */
+        }
+        goto pclgood;
+      } /* End if (cmdMatches("UNIFY ALL")) */
+    }
+
+    if (cmdMatches("DELETE")) {
+      if (!getFullArg(1,
+          "STEP|ALL|FLOATING_HYPOTHESES|<STEP>")) goto pclbad;
+      if (lastArgMatches("STEP")) {
+        if (!getFullArg(2, "# What step number? ")) goto pclbad;
+        goto pclgood;
+      }
+      goto pclgood;
+    }
+
+    /*???OBSOLETE???*/
+    if (cmdMatches("ADD")) {
+      if (!getFullArg(1,
+          "UNIVERSE|<UNIVERSE>")) goto pclbad;
+      /* Note:  further parsing below */
+    }
+
+    if (cmdMatches("REPLACE")) {
+      if (!getFullArg(1, "* What step number, or FIRST, or LAST <LAST>? "))
+          goto pclbad;
+      if (!getFullArg(2, "* With what statement label? ")) goto pclbad;
+      /* Get any switches */
+      i = 2;
+
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, cat(
+              "OVERRIDE|<OVERRIDE>", NULL)))
+            goto pclbad;
+        } else {
+          break;
+        }
+        break; /* Break if only 1 switch is allowed */
+      }
+
+      goto pclgood;
+    }
+
+    if (cmdMatches("LET")) {
+      if (!getFullArg(1, "STEP|VARIABLE|<STEP>")) goto pclbad;
+      if (cmdMatches("LET STEP")) {
+        if (!getFullArg(2, "* What step number, or FIRST, or LAST <LAST>? "))
+          goto pclbad;
+      }
+      if (cmdMatches("LET VARIABLE")) {
+        if (!getFullArg(2, "* Assign what variable (format $nn)? ")) goto pclbad;
+      }
+      if (!getFullArg(3, "=|<=>")) goto pclbad;
+      if (!getFullArg(4, "* With what math symbol string? "))
+          goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("ASSIGN")) {
+      if (!getFullArg(1, "* What step number, or FIRST, or LAST <LAST>? ")) goto pclbad;
+      if (!getFullArg(2, "* With what statement label? ")) goto pclbad;
+      /* Get any switches */
+      i = 2;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, "NO_UNIFY|OVERRIDE|<NO_UNIFY>")) goto pclbad;
+        } else {
+          break;
+        }
+        /*break;*/  /* Break if only 1 switch is allowed */
+      }
+      goto pclgood;
+    }
+
+    if (cmdMatches("UNDO")) {
+      goto pclgood;
+    }
+
+    if (cmdMatches("REDO")) {
+      goto pclgood;
+    }
+
+    if (cmdMatches("SET")) {
+      let(&tmpStr, cat(
+          "WIDTH|HEIGHT|UNDO|ECHO|SCROLL|",
+          "DEBUG|MEMORY_STATUS|SEARCH_LIMIT|UNIFICATION_TIMEOUT|",
+          "DISCOURAGEMENT|",
+          "CONTRIBUTOR|",
+          "ROOT_DIRECTORY|",
+          "EMPTY_SUBSTITUTION|JEREMY_HENTY_FILTER|<WIDTH>", NULL));
+      if (!getFullArg(1,tmpStr)) goto pclbad;
+      if (cmdMatches("SET DEBUG")) {
+        if (!getFullArg(2, "FLAG|OFF|<OFF>")) goto pclbad;
+        if (lastArgMatches("FLAG")) {
+          if (!getFullArg(3, "4|5|6|7|8|9|<5>")) goto pclbad;
+        }
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET ECHO")) {
+        if (g_commandEcho) {
+          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
+        } else {
+          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
+        }
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET SCROLL")) {
+        if (g_scrollMode == 1) {
+          if (!getFullArg(2, "CONTINUOUS|PROMPTED|<CONTINUOUS>")) goto pclbad;
+        } else {
+          if (!getFullArg(2, "CONTINUOUS|PROMPTED|<PROMPTED>")) goto pclbad;
+        }
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET DISCOURAGEMENT")) {
+        if (g_globalDiscouragement) {
+          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
+        } else {
+          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
+        }
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET MEMORY_STATUS")) {
+        if (g_memoryStatus) {
+          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
+        } else {
+          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
+        }
+        goto pclgood;
+      }
+
+
+      if (cmdMatches("SET JEREMY_HENTY_FILTER")) {
+        if (g_hentyFilter) {
+          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
+        } else {
+          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
+        }
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET CONTRIBUTOR")) {
+        if (!getFullArg(2, cat(
+            "* What is the contributor name for SAVE (NEW_)PROOF <",
+            g_contributorName, ">? ", NULL)))
+          goto pclbad;
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET ROOT_DIRECTORY")) {
+        if (!getFullArg(2, cat(
+            "* What is the root directory path (use space if none) <",
+            g_rootDirectory, ">? ", NULL)))
+          goto pclbad;
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET SEARCH_LIMIT")) {
+        if (!getFullArg(2, cat(
+            "# What is search limit for IMPROVE command <",
+            str((double)g_userMaxProveFloat), ">? ", NULL)))
+          goto pclbad;
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET UNIFICATION_TIMEOUT")) {
+        if (!getFullArg(2, cat(
+           "# What is maximum number of unification trials <",
+            str((double)g_userMaxUnifTrials), ">? ", NULL)))
+          goto pclbad;
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET WIDTH")) {
+        if (!getFullArg(2, cat(
+           "# What is maximum line length on your screen <",
+            str((double)g_screenHeight), ">? ", NULL)))
+          goto pclbad;
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET HEIGHT")) {
+        if (!getFullArg(2, cat(
+           "# What is number of lines your screen displays <",
+            str((double)g_screenHeight), ">? ", NULL)))
+          goto pclbad;
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET UNDO")) {
+        if (!getFullArg(2, cat(
+           "# What is the maximum number of UNDOs <",
+            str((double)(processUndoStack(NULL, PUS_GET_SIZE, "", 0))),
+            ">? ", NULL)))
+          goto pclbad;
+        goto pclgood;
+      }
+
+      if (cmdMatches("SET EMPTY_SUBSTITUTION")) {
+        if (g_minSubstLen == 0) {
+          if (!getFullArg(2, "ON|OFF|<OFF>")) goto pclbad;
+        } else {
+          if (!getFullArg(2, "ON|OFF|<ON>")) goto pclbad;
+        }
+        goto pclgood;
+      }
+
+    } /* end if SET */
+
+    if (cmdMatches("ERASE")) {
+      goto pclgood;
+    }
+
+    if (cmdMatches("MORE")) {
+      if (!getFullArg(1,
+         "* What is the name of the file to display? "))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("TOOLS")) {
+      goto pclgood;
+    }
+
+    if (cmdMatches("VERIFY")) {
+      if (!getFullArg(1,
+          "PROOF|MARKUP|<PROOF>"))
+        goto pclbad;
+      if (cmdMatches("VERIFY PROOF")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            "* What are the labels to match (* = wildcard) <*>?"))
+          goto pclbad;
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "SYNTAX_ONLY",
+                "|<SYNTAX_ONLY>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          break;  /* Break if only 1 switch is allowed */
+        }
+
+        goto pclgood;
+      }
+
+      if (cmdMatches("VERIFY MARKUP")) {
+        if (g_sourceHasBeenRead == 0) {
+          print2("?No source file has been read in.  Use READ first.\n");
+          goto pclbad;
+        }
+        if (!getFullArg(2,
+            "* What are the labels to match (* = wildcard) <*>?"))
+          goto pclbad;
+
+        /* Get any switches */
+        i = 2;
+        while (1) {
+          i++;
+          if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+          if (lastArgMatches("/")) {
+            i++;
+            if (!getFullArg(i, cat(
+                "DATE_SKIP|FILE_SKIP|TOP_DATE_SKIP|VERBOSE",
+                "|FILE_CHECK|TOP_DATE_CHECK",
+                "|UNDERSCORE_SKIP|MATHBOX_SKIP|<DATE_SKIP>", NULL)))
+              goto pclbad;
+          } else {
+            break;
+          }
+          /* break; */  /* Break if only 1 switch is allowed */
+        }
+
+        goto pclgood;
+      }
+    }
+
+    if (cmdMatches("DBG")) {
+      /* The debug command fetches an arbitrary 2nd arg in quotes, to be handled
+         in whatever way is needed for debugging. */
+      if (!getFullArg(1, "* What is the debugging string? "))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("MARKUP")) {
+      if (g_sourceHasBeenRead == 0) {
+        print2("?No source file has been read in.  Use READ first.\n");
+        goto pclbad;
+      }
+      if (!getFullArg(1,
+          "* What is the name of the input file with markup? "))
+        goto pclbad;
+      if (!getFullArg(2,
+          "* What is the name of the HTML output file? "))
+        goto pclbad;
+
+      /* Get any switches */
+      i = 2;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, cat(
+              "HTML|ALT_HTML|SYMBOLS|LABELS|NUMBER_AFTER_LABEL|BIB_REFS",
+              "|UNDERSCORES|CSS|<ALT_HTML>", NULL)))
+            goto pclbad;
+        } else {
+          break;
+        }
+        /*break;*/ /* Break if only 1 switch is allowed */
+      }
+      goto pclgood;
+    }
+
+    if (cmdMatches("MIDI")) {
+      if (g_sourceHasBeenRead == 0) {
+        print2("?No source file has been read in.  Use READ first.\n");
+        goto pclbad;
+      }
+      if (!getFullArg(1,
+         "* Statement label to create MIDI for (* matches any substring) <*>?"))
+        goto pclbad;
+      /* Get any switches */
+      i = 1;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, cat("PARAMETER|<PARAMETER>", NULL)))
+            goto pclbad;
+          i++;
+          if (!getFullArg(i,
+              "* What is the parameter string <FSH>?"))
+            goto pclbad;
+        } else {
+          break;
+        }
+        break; /* Break if only 1 switch is allowed */
+      }
+      goto pclgood;
+    }
+
+    if (cmdMatches("EXIT") || cmdMatches("QUIT") || cmdMatches("_EXIT_PA")) {
+
+      /* Get any switches */
+      i = 0;
+      while (1) {
+        i++;
+        if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+        if (lastArgMatches("/")) {
+          i++;
+          if (!getFullArg(i, cat(
+              "FORCE|<FORCE>", NULL)))
+            goto pclbad;
+        } else {
+          break;
+        }
+        break; /* Break if only 1 switch is allowed */
+      } /* End while for switch loop */
+
+      goto pclgood;
+    }
+
+  } else { /* g_toolsMode */
+    /* Text tools mode */
+    let(&tmpStr, cat(
+          "HELP|SUBMIT|",
+          "ADD|DELETE|SUBSTITUTE|S|SWAP|CLEAN|INSERT|BREAK|BUILD|MATCH|SORT|",
+          "UNDUPLICATE|DUPLICATE|UNIQUE|REVERSE|RIGHT|PARALLEL|NUMBER|COUNT|",
+          "COPY|C|TYPE|T|TAG|UPDATE|BEEP|B|EXIT|QUIT|<HELP>", NULL));
+    if (!getFullArg(0, tmpStr))
+      goto pclbad;
+
+    if (cmdMatches("HELP")) {
+      if (!getFullArg(1, cat(
+          "ADD|DELETE|SUBSTITUTE|S|SWAP|CLEAN|INSERT|BREAK|BUILD|MATCH|SORT|",
+          "UNDUPLICATE|DUPLICATE|UNIQUE|REVERSE|RIGHT|PARALLEL|NUMBER|COUNT|",
+          "TYPE|T|TAG|UPDATE|BEEP|B|EXIT|QUIT|",
+          "COPY|C|SUBMIT|SYSTEM|CLI|",
+          "$|<$>", NULL))) goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("ADD") || cmdMatches("TAG")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2, "* String to add to beginning of each line <>? "))
+        goto pclbad;
+      if (!getFullArg(3, "* String to add to end of each line <>? "))
+        goto pclbad;
+      if (cmdMatches("TAG")) {
+        if (!getFullArg(4,
+            "* String to match to start range (null = any line) <>? "))
+          goto pclbad;
+        if (!getFullArg(5,
+            "# Which occurrence of start match to start range <1>? "))
+          goto pclbad;
+        if (!getFullArg(6,
+            "* String to match to end range (null = any line) <>? "))
+          goto pclbad;
+        if (!getFullArg(7,
+            "# Which occurrence of end match to end range <1>? "))
+          goto pclbad;
+      }
+      goto pclgood;
+    }
+    if (cmdMatches("DELETE")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2,
+"* String from which to start deleting (CR = beginning of line) <>? "))
+        goto pclbad;
+      if (!getFullArg(3,
+"* String at which to stop deleting (CR = end of line) <>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("CLEAN")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2,
+          "* Subcommand(s) (D,B,E,R,Q,T,U,P,G,C,L,V) <B,E,R>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("SWAP")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2,
+"* Character string to match between the halves to be swapped? "))
+        goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("SUBSTITUTE") || cmdMatches("S")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2, "* String to replace? "))
+        goto pclbad;
+      if (!getFullArg(3, "* Replace it with <>? "))
+        goto pclbad;
+      if (!getFullArg(4,
+"* Which occurrence in the line (1,2,... or ALL or EACH) <1>? "))
+        goto pclbad;
+      if (!getFullArg(5,
+"* Additional match required on line (null = match all) <>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("INSERT")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2, "* String to insert in each line <!>? "))
+        goto pclbad;
+      if (!getFullArg(3, "# Column at which to insert the string <1>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("BREAK")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2,
+          "* Special characters to use as token delimiters <()[],=:;{}>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("MATCH")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2,
+"* String to match on each line (null = any non-blank line) <>? "))
+        goto pclbad;
+      if (!getFullArg(3,
+"* Output those lines containing the string (Y) or those not (N) <Y>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("SORT")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      if (!getFullArg(2,
+          "* String to start key on each line (null string = column 1) <>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("UNDUPLICATE") || cmdMatches("DUPLICATE") ||
+        cmdMatches("UNIQUE") || cmdMatches("REVERSE") || cmdMatches("BUILD")
+        || cmdMatches("RIGHT")) {
+      if (!getFullArg(1, "& Input/output file? "))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("COUNT")) {
+      if (!getFullArg(1, "& Input file? "))
+        goto pclbad;
+      if (!getFullArg(2,
+"* String to count <;>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("COPY") || cmdMatches("C")) {
+      if (!getFullArg(1, "* Comma-separated list of input files? "))
+        goto pclbad;
+      if (!getFullArg(2, "* Output file? "))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("NUMBER")) {
+      if (!getFullArg(1, "* Output file <n.tmp>? "))
+        goto pclbad;
+      if (!getFullArg(2, "# First number <1>? "))
+        goto pclbad;
+      if (!getFullArg(3, "# Last number <10>? "))
+        goto pclbad;
+      if (!getFullArg(4, "# Increment <1>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+    if (cmdMatches("TYPE") || cmdMatches("T")) {
+      if (!getFullArg(1, "& File to display on the screen? "))
+        goto pclbad;
+      if (!getFullArg(2, "* Num. lines to type or ALL (nothing = 10) <$>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("UPDATE")) {
+      print2("Warning: Do not comment out code - delete it before running UPDATE!  If\n");
+      print2("rerunning UPDATE, do not tamper with \"start/end of deleted section\" comments!\n");
+      print2("Edit out tag on header comment line!  Review the output file!\n");
+      if (!getFullArg(1, "& Original (reference) program input file? "))
+        goto pclbad;
+      if (!getFullArg(2, "& Edited program input file? "))
+        goto pclbad;
+      if (!getFullArg(3, cat("* Edited program output file with revisions tagged <",
+          g_fullArg[2], ">? ", NULL)))
+        goto pclbad;
+      if (!strcmp(g_fullArg[2], g_fullArg[3])) {
+        print2("The input file will be renamed %s~1.\n", g_fullArg[2]);
+      }
+      if (!getFullArg(4,
+          cat("* Revision tag for added lines </* #",
+          str((double)(highestRevision(g_fullArg[1]) + 1)), " */>? ", NULL)))
+        goto pclbad;
+      if (!getFullArg(5, "# Successive lines required for match (more = better sync) <3>? "))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    if (cmdMatches("PARALLEL")) {
+      if (!getFullArg(1, "& Left file? "))
+        goto pclbad;
+      if (!getFullArg(2, "& Right file? "))
+        goto pclbad;
+      if (!getFullArg(3, cat("* Output file <",
+          g_fullArg[1], ">? ", NULL)))
+        goto pclbad;
+      if (!getFullArg(4,
+          cat("* String to insert between the 2 input lines <>? ", NULL)))
+        goto pclbad;
+      goto pclgood;
+    }
+
+    /* g_toolsMode - no qualifiers for EXIT */
+    if (cmdMatches("EXIT") || cmdMatches("QUIT")) {
+      goto pclgood;
+    }
+  } /* if !g_toolsMode ... else ... */
+
+  if (cmdMatches("SUBMIT")) {
+    if (g_toolsMode) {
+      let(&tmpStr, " <tools.cmd>");
+    } else {
+      let(&tmpStr, " <mm.cmd>");
+    }
+    if (!getFullArg(1, cat("& What is the name of command file to run",
+        tmpStr, "? ", NULL))) {
+      goto pclbad;
+    }
+
+    /* Get any switches */
+    i = 1; /* Number of command words before switch */
+    while (1) {
+      i++;
+      if (!getFullArg(i, "/|$|<$>")) goto pclbad;
+      if (lastArgMatches("/")) {
+        i++;
+        if (!getFullArg(i, cat(
+            "SILENT",
+            "|<SILENT>", NULL)))
+          goto pclbad;
+      } else {
+        break;
+      }
+      break; /* Break if only 1 switch is allowed */
+    } /* End while for switch loop */
+
+    goto pclgood;
+  }
+
+  if (cmdMatches("BEEP") || cmdMatches("B")) {
+    goto pclgood;
+  }
+
+  /* Command in master list but not intercepted -- really a bug */
+  print2("?This command has not been implemented yet.\n");
+  print2("(This is really a bug--please report it.)\n");
+  goto pclbad;
+
+  /* Should never get here */
+
+
+ pclgood:
+  /* Strip off the last g_fullArg if a null argument was added by getFullArg
+     in the case when "$" (nothing) is allowed */
+  if (!strcmp(g_fullArg[pntrLen(g_fullArg) - 1], chr(3))) {
+    free_vstring(*(vstring *)(&g_fullArg[pntrLen(g_fullArg) - 1])); /* Deallocate */
+    pntrLet(&g_fullArg, pntrLeft(g_fullArg, pntrLen(g_fullArg) - 1));
+  }
+
+  if (pntrLen(g_fullArg) > g_rawArgs) bug(1102);
+  if (pntrLen(g_fullArg) < g_rawArgs) {
+    let(&tmpStr, cat("?Too many arguments.  Use quotes around arguments with special",
+        " characters and around Unix file names with \"/\"s.", NULL));
+    printCommandError(cat(g_commandPrompt, g_commandLine, NULL), pntrLen(g_fullArg),
+        tmpStr);
+    goto pclbad;
+  }
+
+  /* Create a single string containing the g_fullArg tokens */
+  let(&g_fullArgString, "");
+  for (i = 0; i < pntrLen(g_fullArg); i++) {
+    let(&g_fullArgString, cat(g_fullArgString, " ", g_fullArg[i], NULL));
+  }
+  let(&g_fullArgString, right(g_fullArgString, 2)); /* Strip leading space */
+
+
+  /* Deallocate memory */
+  free_vstring(defaultArg);
+  free_vstring(tmpStr);
+  return 1;
+
+ pclbad:
+  /* Deallocate memory */
+  free_vstring(defaultArg);
+  free_vstring(tmpStr);
+  return 0;
+} /* processCommandLine */
+
+
+
+/* This function converts the user's abbreviated keyword in
+   g_rawArgPntr[arg] to a full, upper-case keyword,
+   in g_fullArg[arg], matching
+   the available choices in cmdList. */
+/* Special cases:  cmdList = "# xxx <yyy>?" - get an integer */
+/*                 cmdList = "* xxx <yyy>?" - get any string;
+                     don't convert to upper case
+                   cmdList = "& xxx <yyy>?" - same as * except
+                     verify it is a file that exists */
+/* "$" means a null argument is acceptable; put it in as
+   special character chr(3) so it can be recognized */
+static flag getFullArg(long arg, const char *cmdList1) {
+  pntrString_def(possCmd);
+  flag ret = 1;
+  vstring_def(defaultCmd);
+  vstring_def(infoStr);
+  vstring_def(errorLine);
+  vstring_def(keyword);
+
+  vstring_def(cmdList);
+  let(&cmdList, cmdList1); /* In case cmdList1 gets deallocated when it comes
+                             directly from a vstring function such as cat() */
+
+  let(&errorLine, cat(g_commandPrompt, g_commandLine, NULL));
+
+  /* Handle special case - integer expected */
+  if (cmdList[0] == '#') {
+    let(&defaultCmd, seg(cmdList, instr(1, cmdList, "<"), instr(1, cmdList, ">")));
+
+    /* If the argument has not been entered, prompt the user for it */
+    if (g_rawArgs <= arg) {
+      pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+      nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, 0));
+      g_rawArgs++;
+      if (g_rawArgs <= arg) bug(1103);
+
+      g_queryMode = 1;
+      vstring argLine = cmdInput1(right(cmdList, 3));
+      let(&errorLine, right(cmdList, 3));
+      if (argLine[0] == 0) { /* Use default argument */
+        let(&argLine, seg(defaultCmd, 2, len(defaultCmd) - 1));
+      }
+      let((vstring *)(&g_rawArgPntr[arg]), argLine);
+      free_vstring(argLine);
+      g_rawArgNmbr[arg] = len(cmdList) - 1;/* Line position for error msgs */
+
+    } /* End of asking user for additional argument */
+
+    /* Make sure that the argument is a non-negative integer */
+    vstring_def(tmpArg);
+    let(&tmpArg, g_rawArgPntr[arg]);
+    if (tmpArg[0] == 0) { /* Use default argument */
+      /* (This code is needed in case of null string passed directly) */
+      let(&tmpArg, seg(defaultCmd, 2, len(defaultCmd) - 1));
+    }
+    vstring_def(tmpStr);
+    let(&tmpStr, str(val(tmpArg)));
+    let(&tmpStr, cat(string(len(tmpArg)-len(tmpStr),'0'), tmpStr, NULL));
+    if (strcmp(tmpStr, tmpArg)) {
+      printCommandError(errorLine, arg, "?A number was expected here.");
+      ret = 0;
+    } else {
+      let(&keyword, str(val(tmpArg)));
+    }
+
+    free_vstring(tmpArg);
+    free_vstring(tmpStr);
+    goto getFullArg_ret;
+  }
+
+
+
+  /* Handle special case - any arbitrary string is OK */
+  /* '*' means any string, '&' means a file */
+  /* However, "|$<$>" also allows null string (no argument) */
+  if (cmdList[0] == '*' || cmdList[0] == '&') {
+    let(&defaultCmd, seg(cmdList,instr(1, cmdList, "<"), instr(1, cmdList, ">")));
+
+    /* If the argument has not been entered, prompt the user for it */
+    if (g_rawArgs <= arg) {
+      if (!strcmp(defaultCmd, "<$>")) { /* End of command acceptable */
+        /* Note:  in this case, user will never be prompted for anything. */
+        let(&keyword, chr(3));
+        goto getFullArg_ret;
+      }
+      g_rawArgs++;
+      pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+      nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, 0));
+      if (g_rawArgs <= arg) bug(1104);
+      g_queryMode = 1;
+      vstring tmpArg = cmdInput1(right(cmdList,3));
+
+      /* Strip off any quotes around it
+         and tolerate lack of trailing quote */
+      /******* (This is no longer done - it is confusing to the user.)
+      if (tmpArg[0] == '\'' || tmpArg[0] == '\"') {
+        if (tmpArg[0] == tmpArg[len(tmpArg) - 1]) {
+          let(&tmpArg, right(left(tmpArg, len(tmpArg) - 1), 2));
+        } else {
+          let(&tmpArg, right(tmpArg, 2));
+        }
+      }
+      *******/
+
+      let(&errorLine, right(cmdList,3));
+      if (tmpArg[0] == 0) { /* Use default argument */
+        let(&tmpArg, seg(defaultCmd, 2, len(defaultCmd) - 1));
+      }
+      let((vstring *)(&g_rawArgPntr[arg]), tmpArg);
+      g_rawArgNmbr[arg] = len(cmdList) - 1; /* Line position for error msgs */
+      free_vstring(tmpArg);
+    } /* End of asking user for additional argument */
+
+    let(&keyword, g_rawArgPntr[arg]);
+
+    /* Convert abbreviations of FIRST, LAST, ALL to
+       full keywords.  The rest of the program works fine without doing this,
+       but it provides better cosmetic appearance when the command is echoed
+       such as in during the UNDO command. */
+    if (cmdList[0] == '*') {
+      if ((keyword[0] == 'f' || keyword[0] == 'F')
+          && instr(1, cmdList, " FIRST") != 0)
+        let(&keyword, "FIRST");
+      if ((keyword[0] == 'l' || keyword[0] == 'L')
+          && instr(1, cmdList, " LAST") != 0)
+        let(&keyword, "LAST");
+      if ((keyword[0] == 'a' || keyword[0] == 'A')
+          && instr(1, cmdList, " ALL") != 0)
+        let(&keyword, "ALL");
+    }
+
+    if (keyword[0] == 0) { /* Use default argument */
+      /* This case handles blank arguments on completely input command line */
+      let(&keyword, seg(defaultCmd,2,len(defaultCmd) - 1));
+    }
+    if (cmdList[0] == '&') {
+      /* See if file exists */
+      vstring_def(tmpStr);
+      let(&tmpStr, cat(g_rootDirectory, keyword, NULL));
+      FILE *tmpFp = fopen(tmpStr, "r");
+      if (!tmpFp) {
+        let(&tmpStr, cat("?Sorry, couldn't open the file \"", tmpStr, "\".", NULL));
+        printCommandError(errorLine, arg, tmpStr);
+        free_vstring(tmpStr);
+        ret = 0;
+      } else {
+        fclose(tmpFp);
+      }
+    }
+    goto getFullArg_ret;
+  }
+
+
+
+  /* Parse the choices available */
+  long possCmds = 0;
+  long p = 0;
+  long q = 0;
+  while (1) {
+    p = instr(p + 1, cat(cmdList, "|", NULL), "|");
+    if (!p) break;
+    pntrLet(&possCmd, pntrAddElement(possCmd));
+    let((vstring *)(&possCmd[possCmds]), seg(cmdList, q+1, p-1));
+    possCmds++;
+    q = p;
+  }
+  if (!strcmp(left(possCmd[possCmds - 1],1), "<")) {
+    // free_vstring(defaultCmd); // Not needed because defaultCmd is already empty
+    /* Get default argument, if any */
+    defaultCmd = possCmd[possCmds - 1]; /* re-use old allocation */
+
+    if (!strcmp(defaultCmd, "<$>")) {
+      let(&defaultCmd, "<nothing>");
+    }
+    pntrLet(&possCmd, pntrLeft(possCmd, possCmds - 1));
+    possCmds--;
+  }
+  if (!strcmp(possCmd[possCmds - 1], "$")) {
+    /* Change "$" to "nothing" for printouts */
+    let((vstring *)(&possCmd[possCmds - 1]), "nothing");
+  }
+
+  /* Create a string used for queries and error messages */
+  if (possCmds < 1) {
+    bug(1105);
+    ret = 0;
+    goto getFullArg_ret;
+  }
+  if (possCmds == 1) {
+    let(&infoStr,possCmd[0]);
+  } else if (possCmds == 2) {
+    let(&infoStr, cat(possCmd[0], " or ",
+        possCmd[1], NULL));
+  } else /* possCmds > 2 */ {
+    let(&infoStr, "");
+    for (long i = 0; i < possCmds - 1; i++) {
+      let(&infoStr, cat(infoStr, possCmd[i], ", ", NULL));
+    }
+    let(&infoStr, cat(infoStr, "or ", possCmd[possCmds - 1], NULL));
+  }
+
+  /* If the argument has not been entered, prompt the user for it */
+  if (g_rawArgs <= arg && (strcmp(possCmd[possCmds - 1], "nothing")
+      || g_queryMode == 1)) {
+
+    vstring_def(tmpStr);
+    let(&tmpStr, infoStr);
+    if (defaultCmd[0] != 0) {
+      let(&tmpStr, cat(tmpStr, " ",defaultCmd, NULL));
+    }
+    let(&tmpStr, cat(tmpStr, "? ", NULL));
+    g_queryMode = 1;
+    vstring_def(tmpArg);
+    if (possCmds != 1) {
+      tmpArg = cmdInput1(tmpStr);
+    } else {
+      /* There is only one possibility, so don't ask user */
+      /* Don't print the message when "end-of-list" is the only possibility. */
+      if (!strcmp(cmdList, "$|<$>")) {
+        let(&tmpArg, possCmd[0]);
+        print2("The command so far is:  ");
+        for (long i = 0; i < arg; i++) {
+          print2("%s ", g_fullArg[i]);
+        }
+        print2("%s\n", tmpArg);
+      }
+    }
+    let(&errorLine, tmpStr);
+    if (tmpArg[0] == 0) { /* Use default argument */
+      let(&tmpArg, seg(defaultCmd, 2, len(defaultCmd) - 1));
+    }
+
+    if (strcmp(tmpArg, "nothing")) {
+      pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+      nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, 0));
+      g_rawArgs++;
+      if (g_rawArgs <= arg) bug(1106);
+      let((vstring *)(&g_rawArgPntr[arg]), tmpArg);
+      g_rawArgNmbr[arg] = len(tmpStr) + 1; /* Line position for error msgs */
+    }
+
+    free_vstring(tmpStr);
+    free_vstring(tmpArg);
+  } /* End of asking user for additional argument */
+
+  if (g_rawArgs <= arg) {
+    /* No argument was specified, and "nothing" is a valid argument */
+    let(&keyword, chr(3));
+    goto getFullArg_ret;
+  }
+
+  vstring_def(tmpArg);
+  let(&tmpArg, edit(g_rawArgPntr[arg], 32)); /* Convert to upper case */
+  long j = 0;
+  long k = 0;
+  long m = len(tmpArg);
+  vstring_def(tmpStr);
+  /* Scan the possible arguments for a match */
+  for (long i = 0; i < possCmds; i++) {
+    if (!strcmp(possCmd[i], tmpArg)) {
+      /* An exact match was found, so ignore any other matches
+         and use this one */
+      k = 1;
+      j = i;
+      break;
+    }
+    if (!strcmp(left(possCmd[i], m), tmpArg)) {
+      if (!k) {
+        let(&tmpStr, possCmd[i]);
+      } else {
+        let(&tmpStr, cat(tmpStr, ", ", possCmd[i], NULL));
+      }
+      j = i; /* Save match position */
+      k++; /* Number of matches */
+    }
+  }
+  free_vstring(tmpArg);
+  if (k < 1 || k > 1) {
+    if (k < 1) {
+      let(&tmpStr, cat("?Expected ", infoStr, ".", NULL));
+    } else {
+      if (k == 2) {
+        p = instr(1, tmpStr, ", ");
+        let(&tmpStr, cat(left(tmpStr, p-1), " or", right(tmpStr, p+1), NULL));
+      } else {
+        p = len(tmpStr) - 1;
+        while (tmpStr[p] != ',') p--;
+        let(&tmpStr, cat(left(tmpStr, p+1), " or", right(tmpStr, p+2), NULL));
+      }
+      let(&tmpStr, cat("?Ambiguous keyword - please specify ", tmpStr, ".", NULL));
+    }
+    printCommandError(errorLine, arg, tmpStr);
+    free_vstring(tmpStr);
+    ret = 0;
+    goto getFullArg_ret;
+  }
+  free_vstring(tmpStr);
+
+  let(&keyword, possCmd[j]);
+
+getFullArg_ret:
+  if (ret) {
+    if (keyword[0] == 0) {
+      if (g_rawArgs > arg && strcmp(defaultCmd, "<>")) {
+        /* otherwise, "nothing" was specified */
+        printCommandError("", arg, "?No default answer is available - please be explicit.");
+        ret = 0;
+        goto getFullArg_ret;
+      }
+    }
+    /* Add new field to g_fullArg */
+    pntrLet(&g_fullArg, pntrAddElement(g_fullArg));
+    if (pntrLen(g_fullArg) != arg + 1) bug(1107);
+    else let((vstring *)(&g_fullArg[arg]), keyword);
+  }
+
+  /* Deallocate memory */
+  long len = pntrLen(possCmd);
+  for (long i = 0; i < len; i++) free_vstring(*(vstring *)(&possCmd[i]));
+  free_pntrString(possCmd);
+  free_vstring(defaultCmd);
+  free_vstring(infoStr);
+  free_vstring(errorLine);
+  free_vstring(keyword);
+  free_vstring(cmdList);
+  return ret;
+} /* getFullArg */
+
+
+
+/* This function breaks up line into individual tokens
+   and puts them into g_rawArgPntr[].  g_rawArgs is the number of tokens.
+   g_rawArgPntr[] is the starting position of each token on the line;
+   the first character on the line has position 1, not 0.
+
+   Spaces, tabs, and newlines are considered white space.  Special
+   one-character
+   tokens don't have to be surrounded by white space.  Characters
+   inside quotes are considered to be one token, and the quotes are
+   removed.
+*/
+void parseCommandLine(vstring line) {
+  /*const char *specialOneCharTokens = "()/,=:";*/
+  const char *tokenWhiteSpace = " \t\n";
+  const char *tokenComment = "!";
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  long tokenStart = 0;
+
+  /* List of special one-char tokens */
+  const char* specialOneCharTokens = g_toolsMode ? "" : "/=";
+
+  long lineLen = len(line);
+  /* only "!" at beginning of line acts as comment.
+     This is done because sometimes ! might be legal as part of a command */
+  enum mode_t {
+    MODE_START, // look for start of token
+    MODE_END, // look for end of token
+    MODE_SQUOTE, // look for trailing single quote
+    MODE_DQUOTE, // look for trailing double quote
+  } mode = MODE_START;
+  long p = 0;
+  for (; p < lineLen; p++) {
+    freeTempAlloc(); /* Clean up temp alloc stack to prevent overflow */
+    switch (mode) {
+      case MODE_START: {
+        /* If character is white space, ignore it */
+        if (instr(1, tokenWhiteSpace, chr(line[p]))) continue;
+        /* If character is comment, we're done */
+        if (p == 0 && instr(1, tokenComment, chr(line[p]))) goto parseCommandLine_ret;
+
+        /* If character is a special token, get it but don't change mode */
+        if (instr(1, specialOneCharTokens, chr(line[p]))) {
+          pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+          nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, p+1));
+                                                            /* Save token start */
+          let((vstring *)(&g_rawArgPntr[g_rawArgs]), chr(line[p]));
+          g_rawArgs++;
+          continue;
+        }
+        /* If character is a quote, set start and change mode */
+        if (line[p] == '\'') {
+          mode = MODE_SQUOTE;
+          tokenStart = p + 2;
+          continue;
+        }
+        if (line[p] == '\"') {
+          mode = MODE_DQUOTE;
+          tokenStart = p + 2;
+          continue;
+        }
+        /* Character must be start of a token */
+        mode = MODE_END;
+        tokenStart = p + 1;
+      } break;
+
+      case MODE_END: {
+        /* If character is white space, end token and change mode */
+        if (instr(1, tokenWhiteSpace, chr(line[p]))) {
+          pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+          nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
+                                                            /* Save token start */
+          let((vstring *)(&g_rawArgPntr[g_rawArgs]), seg(line, tokenStart, p));
+          g_rawArgs++;
+          mode = MODE_START;
+          continue;
+        }
+
+        /* If character is a special token, get it and change mode */
+        if (instr(1, specialOneCharTokens, chr(line[p]))) {
+          pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+          nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
+                                                            /* Save token start */
+          let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart, p));
+          g_rawArgs++;
+          pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+          nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, p + 1));
+                                                            /* Save token start */
+          let((vstring *)(&g_rawArgPntr[g_rawArgs]), chr(line[p]));
+          g_rawArgs++;
+          mode = MODE_START;
+          continue;
+        }
+
+        /* If character is a quote, set start and change mode */
+        if (line[p] == '\'') {
+          pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+          nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
+                                                            /* Save token start */
+          let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart, p));
+          g_rawArgs++;
+          mode = MODE_SQUOTE;
+          tokenStart = p + 2;
+          continue;
+        }
+        if (line[p] == '\"') {
+          pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+          nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
+                                                            /* Save token start */
+          let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart, p));
+          g_rawArgs++;
+          mode = MODE_DQUOTE;
+          tokenStart = p + 2;
+          continue;
+        }
+        /* Character must be continuation of the token */
+      } break;
+
+      case MODE_SQUOTE:
+      case MODE_DQUOTE: {
+        /* If character is a quote, end quote and change mode */
+        if (line[p] == (mode == MODE_SQUOTE ? '\'' : '\"')) {
+          mode = MODE_START;
+          pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+          nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
+                                                            /* Save token start */
+          let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart, p));
+          g_rawArgs++;
+          continue;
+        }
+        /* Character must be continuation of quoted token */
+      } break;
+    }
+  }
+
+  /* Finished scanning the line.  Finish processing last token. */
+  if (mode != MODE_START) {
+    pntrLet(&g_rawArgPntr, pntrAddElement(g_rawArgPntr));
+    nmbrLet(&g_rawArgNmbr, nmbrAddElement(g_rawArgNmbr, tokenStart));
+                                                          /* Save token start */
+    let((vstring *)(&g_rawArgPntr[g_rawArgs]),seg(line, tokenStart, p));
+    g_rawArgs++;
+  }
+
+parseCommandLine_ret:
+  /* Add length of command line prompt to each argument, to
+     align the error message pointer */
+  for (long i = 0; i < g_rawArgs; i++) {
+    g_rawArgNmbr[i] = g_rawArgNmbr[i] + len(g_commandPrompt);
+  }
+} /* parseCommandLine */
+
+
+flag lastArgMatches(vstring argString) {
+  /* This functions checks to see if the last field was argString */
+  if (!strcmp(argString, g_fullArg[pntrLen(g_fullArg)-1])) {
+    return (1);
+  } else {
+    return (0);
+  }
+} /* lastArgMatches */
+
+flag cmdMatches(vstring cmdString) {
+  /* This function checks that fields 0 through n of g_fullArg match
+     cmdString (separated by spaces). */
+  long i, j, k;
+  /* Count the number of spaces */
+  k = len(cmdString);
+  j = 0;
+  for (i = 0; i < k; i++) {
+    if (cmdString[i] == ' ') j++;
+  }
+  k = pntrLen(g_fullArg);
+  vstring_def(tmpStr);
+  for (i = 0; i <= j; i++) {
+    if (j >= k) {
+      /* Command to match is longer than the user's command; assume no match */
+      free_vstring(tmpStr);
+      return 0;
+    }
+    let(&tmpStr, cat(tmpStr, " ", g_fullArg[i], NULL));
+  }
+  if (!strcmp(cat(" ", cmdString, NULL), tmpStr)) {
+    free_vstring(tmpStr);
+    return 1;
+  } else {
+    free_vstring(tmpStr);
+    return 0;
+  }
+} /* cmdMatches */
+
+
+long switchPos(vstring swString) {
+  /* This function checks that fields i through j of g_fullArg match
+     swString (separated by spaces).  The first character of swString
+     should be "/" and must be separated from the first field
+     of swString with a space.  The position of the "/" in g_fullArg
+     is returned if swString is there, otherwise 0 is returned (the first
+     position in g_fullArg is considered 1, not 0). */
+  /* Example:  if g_fullArg (combined into one string) is
+     "DISPLAY PROOF / UNKNOWN / START_STEP = 10 / ESSENTIAL"
+     and swString is "/ START_STEP", switchPos will return 5. */
+  long i, j, k;
+  vstring_def(tmpStr);
+  vstring_def(swString1);
+
+  if (swString[0] != '/') bug(1108);
+
+  /* Add a space after the "/" if there is none */
+  if (swString[1] != ' ') {
+    let(&swString1, cat("/ ", right(swString, 2), " ", NULL));
+  } else {
+    let(&swString1, swString);
+  }
+
+  /* Build the complete command */
+  k = pntrLen(g_fullArg);
+  for (i = 0; i < k; i++) {
+    let(&tmpStr, cat(tmpStr, g_fullArg[i], " ", NULL));
+  }
+
+  k = instr(1, tmpStr, swString1);
+  if (!k) {
+    free_vstring(swString1);
+    free_vstring(tmpStr);
+    return 0;
+  }
+
+  let(&tmpStr, left(tmpStr, k));
+  /* Count the number of spaces - it will be the g_fullArg position */
+  k = len(tmpStr);
+  j = 0;
+  for (i = 0; i < k; i++) {
+    if (tmpStr[i] == ' ') j++;
+  }
+  free_vstring(tmpStr);
+  free_vstring(swString1);
+  return j + 1;
+} /* switchPos */
+
+
+void printCommandError(vstring line1, long arg, vstring errorMsg)
+{
+  /* Warning: errorMsg should not a temporarily allocated string such
+     as the direct output of cat() */
+  vstring_def(errorPointer);
+  vstring_def(line);
+  long column, tokenLength, j;
+
+  let(&line,line1); /* Prevent deallocation in case line1 is
+                       direct return from string function such as cat() */
+  if (!line[0]) {
+    /* Empty line - don't print an error pointer */
+    print2("%s\n", errorMsg);
+    free_vstring(line);
+    return;
+  }
+  column = g_rawArgNmbr[arg];
+  tokenLength = len(g_rawArgPntr[arg]);
+  for (j = 0; j < column - 1; j++) {
+    /* Make sure that tabs on the line with the error are accounted for so
+       that the error pointer lines up correctly */
+    if (j >= len(line)) bug(1109);
+    if (line[j] == '\t') {
+      let(&errorPointer, cat(errorPointer, "\t", NULL));
+    } else {
+      if (line[j] == '\n') {
+        let(&errorPointer, "");
+      } else {
+        let(&errorPointer, cat(errorPointer, " ", NULL));
+      }
+    }
+  }
+  for (j = 0; j < tokenLength; j++)
+    let(&errorPointer, cat(errorPointer, "^", NULL));
+  print2("%s\n", errorPointer);
+  printLongLine(errorMsg, "", " ");
+  free_vstring(errorPointer);
+  free_vstring(line);
+} /* printCommandError */
+
+void freeCommandLine() {
+  long i, j;
+  j = pntrLen(g_rawArgPntr);
+  for (i = 0; i < j; i++) free_vstring(*(vstring *)(&g_rawArgPntr[i]));
+  j = pntrLen(g_fullArg);
+  for (i = 0; i < j; i++) free_vstring(*(vstring *)(&g_fullArg[i]));
+  free_pntrString(g_fullArg);
+  free_pntrString(g_rawArgPntr);
+  free_nmbrString(g_rawArgNmbr);
+  free_vstring(g_fullArgString);
+}
diff --git a/src/mmcmdl.h b/src/mmcmdl.h
new file mode 100644
index 0000000..b34fb44
--- /dev/null
+++ b/src/mmcmdl.h
@@ -0,0 +1,61 @@
+/*****************************************************************************/
+/*        Copyright (C) 2018  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMCMDL_H_
+#define METAMATH_MMCMDL_H_
+
+/*!
+ * \file mmcmdl.h
+ * \brief includes for accessing the command line interpreter
+ */
+
+#include "mmvstr.h"
+#include "mmdata.h"
+
+flag processCommandLine(void);
+void parseCommandLine(vstring line);
+flag lastArgMatches(vstring argString);
+flag cmdMatches(vstring cmdString);
+long switchPos(vstring swString);
+void printCommandError(vstring line, long arg, vstring errorMsg);
+void freeCommandLine(void);
+
+#define DEFAULT_COLUMN 16
+extern pntrString *g_rawArgPntr;
+extern nmbrString *g_rawArgNmbr;
+extern long g_rawArgs;
+extern pntrString *g_fullArg;
+extern vstring g_fullArgString; /*!< g_fullArg as one string */
+/*!
+ * \var vstring g_commandPrompt
+ * text displayed at the beginning of the line where a user is supposed to
+ * enter a new command.  For example 'MM>'.
+ */
+extern vstring g_commandPrompt;
+extern vstring g_commandLine;
+extern long g_showStatement;
+extern vstring g_logFileName;
+extern vstring g_texFileName;
+extern flag g_PFASmode; /*!< Proof assistant mode, invoked by PROVE command */
+extern flag g_sourceChanged; /*!< Flag that user made some change to the source file*/
+extern flag g_proofChanged; /*!< Flag that user made some change to proof in progress*/
+extern flag g_commandEcho; /*!< Echo full command */
+/*!
+ * \brief `MEMORY_STATUS` option: Always show memory
+ *
+ * Indicates whether the user has turned MEMORY_STATUS on.
+ *
+ * If the user issues SET MEMORY_STATUS ON this \ref flag is set to 1.  It is
+ * reset to 0 again on a SET MEMORY_STATUS OFF command.  When 1, certain
+ * memory de/allocations are monitored - see \ref db3.
+ */
+extern flag g_memoryStatus;
+
+extern flag g_sourceHasBeenRead; /*!< 1 if a source file has been read in */
+extern vstring g_rootDirectory; /*!< Directory to use for included files */
+
+
+#endif /* METAMATH_MMCMDL_H_ */
diff --git a/mmcmds.c b/src/mmcmds.c
similarity index 76%
rename from mmcmds.c
rename to src/mmcmds.c
index c3e4822..bee7115 100644
--- a/mmcmds.c
+++ b/src/mmcmds.c
@@ -1,7007 +1,6270 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* mmcmds.c - assorted user commands */
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <time.h>  /* 28-May-04 nm For clock() */
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mmcmdl.h" /* For g_texFileName */
-#include "mmcmds.h"
-#include "mminou.h"
-#include "mmpars.h"
-#include "mmveri.h"
-#include "mmwtex.h" /* For g_htmlVarColor,... */
-#include "mmpfas.h"
-#include "mmunif.h" /* 26-Sep-2010 nm For g_bracketMatchInit, g_minSubstLen */
-                    /* 1-Oct-2017 nm ...and g_firstConst */
-
-/* 12-Nov-2018 nm */
-/* Local prototypes */
-vstring bigAdd(vstring bignum1, vstring bignum2);
-vstring bigSub(vstring bignum1, vstring bignum2);
-
-/* vstring mainFileName = ""; */ /* 28-Dec-05 nm Obsolete */
-flag g_printHelp = 0;
-
-/* For HTML output */
-vstring g_printStringForReferencedBy = "";
-
-/* For MIDI */
-flag g_midiFlag = 0;
-vstring g_midiParam = "";
-
-/* Type (i.e. print) a statement */
-void typeStatement(long showStmt,
-  flag briefFlag,
-  flag commentOnlyFlag,
-  flag texFlag,
-  flag htmlFlg)
-{
-  /* From HELP SHOW STATEMENT: Optional qualifiers:
-    / TEX - This qualifier will write the statement information to the
-        LaTeX file previously opened with OPEN TEX.
-            [Note:  texFlag=1 and htmlFlg=0 with this qualifier.]
-
-    / HTML - This qualifier will write the statement information to the
-        HTML file previously opened with OPEN HTML.
-            [Note:  texFlag=1 and htmlFlg=1 with this qualifier.]
-
-    / COMMENT_ONLY - This qualifier will show only the comment that
-        immediatley precedes the statement.  This is useful when you are
-        using Metamath to preprocess LaTeX source you have created (see
-        HELP TEX)
-
-    / BRIEF - This qualifier shows the statement and its $e hypotheses
-        only.
-  */
-  long i, j, k, m, n;
-  vstring str1 = "", str2 = "", str3 = "";
-  nmbrString *nmbrTmpPtr1; /* Pointer only; not allocated directly */
-  nmbrString *nmbrTmpPtr2; /* Pointer only; not allocated directly */
-  nmbrString *nmbrDDList = NULL_NMBRSTRING;
-  flag q1, q2;
-  flag type;
-  flag subType;
-  vstring htmlDistinctVars = ""; /* 12/23/01 */
-  char htmlDistinctVarsCommaFlag = 0; /* 12/23/01 */
-  vstring str4 = ""; /* 10/10/02 */
-  vstring str5 = ""; /* 19-Sep-2012 nm */
-  long distVarGrps = 0;  /* 11-Aug-2006 nm */
-
-  /* For syntax breakdown of definitions in HTML page */
-  long zapStatement1stToken;
-  static long wffToken = -1; /* array index of the hard-coded token "wff" -
-      static so we only have to look it up once - set to -2 if not found */
-
-  subType = 0; /* Assign to prevent compiler warnings - not theor. necessary */
-
-  if (!showStmt) bug(225); /* Must be 1 or greater */
-
-  if (!commentOnlyFlag && !briefFlag) {
-    assignStmtFileAndLineNum(showStmt); /* 9-Jan-2018 nm */
-    let(&str1, cat("Statement ", str((double)showStmt),
-        " is located on line ", str((double)(g_Statement[showStmt].lineNum)),
-        " of the file ", NULL));
-    if (!texFlag) {
-      assignMathboxInfo(); /* In case it hasn't been assigned yet */
-          /* We need to call assignMathboxInfo() to do the initial assignment,
-             in order to prevent "let(&" from executing during the
-             getMathboxUser() call below, which would corrupt the "cat()"
-             temporary stack (since "let(&" empties the stack). */
-      printLongLine(cat(str1,
-        "\"", g_Statement[showStmt].fileName,
-        "\".",
-        /* 8-Feb-2007 nm Added HTML page info to SHOW STATEMENT ... /FULL */
-        (g_Statement[showStmt].pinkNumber == 0) ?   /* !=0 means $a or $p */
-           "" :
-           cat("  Its statement number for HTML pages is ",
-               str((double)(g_Statement[showStmt].pinkNumber)), ".", NULL),
-        /* 5-Aug-2020 nm Added mathbox user to SHOW STATEMENT ... /FULL */
-        ((getMathboxUser(showStmt))[0] == 0) ?
-           "" :
-           cat("  It is in the mathbox for ", getMathboxUser(showStmt), ".",
-               NULL),
-        NULL), "", " ");
-    } else {
-      if (!htmlFlg) let(&g_printString, "");
-      g_outputToString = 1; /* Flag for print2 to add to g_printString */
-                     /* Note that printTexLongMathString resets it */
-      if (!(htmlFlg && texFlag)) {
-        /*
-        printLongLine(cat(str1, "{\\tt ",
-            asciiToTt(g_Statement[showStmt].fileName),
-            "}.", NULL), "", " ");
-        */
-      } else {
-        /* For categorizing html pages, we use the source file convention
-           that syntax statements don't start with "|-" and that axioms
-           have labels starting with "ax-".  It is up to the database
-           creator to follow this standard, which is not enforced. */
-#define SYNTAX 1
-#define DEFINITION 2
-#define AXIOM 3
-#define THEOREM 4
-        if (g_Statement[showStmt].type == (char)p_) {
-          subType = THEOREM;
-        } else {
-          /* Must be a_ due to filter in main() */
-          if (g_Statement[showStmt].type != (char)a_) bug(228);
-          if (strcmp("|-", g_MathToken[
-              (g_Statement[showStmt].mathString)[0]].tokenName)) {
-            subType = SYNTAX;
-          } else {
-            if (!strcmp("ax-", left(g_Statement[showStmt].labelName, 3))) {
-              subType = AXIOM;
-            } else {
-              subType = DEFINITION;
-            }
-          }
-        }
-        switch (subType) {
-          case SYNTAX:
-            let(&str1, "Syntax Definition"); break;
-          case DEFINITION:
-            let(&str1, "Definition"); break;
-          case AXIOM:
-            let(&str1, "Axiom"); break;
-          case THEOREM:
-            let(&str1, "Theorem"); break;
-          default:
-            bug(229);
-        }
-
-        /* Print a small pink statement number after the statement */
-        let(&str2, "");
-        str2 = pinkHTML(showStmt);
-        printLongLine(cat("<CENTER><B><FONT SIZE=\"+1\">", str1,
-            " <FONT COLOR=", GREEN_TITLE_COLOR,
-            ">", g_Statement[showStmt].labelName,
-            "</FONT></FONT></B>", str2, "</CENTER>", NULL), "", "\"");
-      } /* (htmlFlg && texFlag) */
-      g_outputToString = 0;
-    } /* texFlag */
-  }
-
-  if (!briefFlag || commentOnlyFlag) {
-    let(&str1, "");
-    str1 = getDescription(showStmt);
-    if (!str1[0]    /* No comment */
-
-#ifdef DATE_BELOW_PROOF /* 12-May-2017 nm */
-
-        || (str1[0] == '[' && str1[strlen(str1) - 1] == ']')
-        /* 7-Sep-04 Allow both "$([<date>])$" and "$( [<date>] )$" */
-        || (strlen(str1) > 1 &&
-            str1[1] == '[' && str1[strlen(str1) - 2] == ']') /* Make sure
-            getDescription() didn't pick up date stamp from previous proof */
-
-#endif /* 12-May-2017 nm */
-
-        ) {
-      print2("?Warning: Statement \"%s\" has no comment\n",
-          g_Statement[showStmt].labelName);
-      /* 14-Sep-2010 nm We must print a blank comment to have \begin{lemma} */
-      if (texFlag && !htmlFlg && !g_oldTexFlag) {
-        let(&str1, "TO DO: PUT DESCRIPTION HERE");
-      }
-    }
-    if (str1[0]) {
-      if (!texFlag) {
-        printLongLine(cat("\"", str1, "\"", NULL), "", " ");
-      } else {
-        /* 10/10/02 This is now done in mmwtex.c printTexComment */
-        /* Although it will affect the 2nd (and only other) call to
-           printTexComment below, that call is obsolete and there should
-           be no side effect. */
-        /*******
-        if (htmlFlg && texFlag) {
-          let(&str1, cat("<CENTER><B>Description: </B>", str1,
-              "</CENTER><BR>", NULL));
-        }
-        *******/
-        if (!htmlFlg) {  /* LaTeX */
-          if (!g_oldTexFlag) {
-            /* 14-Sep-2010 */
-
-            /* 1-May-2017 nm */
-            /* Distinguish axiom, definition, theorem */
-            /* Note: changes here must be mirrored in the \end{...} below */
-            if (g_Statement[showStmt].type == a_) {
-              if (!strcmp(left(g_Statement[showStmt].labelName, 3), "ax-")) {
-                let(&str3, "axiom");
-              } else {
-                let(&str3, "definition");
-              }
-            } else {
-              let(&str3, "theorem");
-            }
-            let(&str1, cat("\\begin{", str3, "}\\label{",
-                left(str3, 3), ":",
-                g_Statement[showStmt].labelName, "} ", str1, NULL));
-            /* old code before 1-May-2017:
-            let(&str1, cat("\\begin{lemma}\\label{lem:",
-                g_Statement[showStmt].labelName, "} ", str1, NULL));
-            */
-
-          } else {
-            /* 6-Dec-03 Add separation space between theorems */
-            let(&str1, cat("\n\\vspace{1ex} %2\n\n", str1, NULL));
-          }
-        }
-        /* printTexComment(str1, 1); */
-        /* 17-Nov-2015 nm Add 3rd & 4th arguments */
-        printTexComment(str1,              /* Sends result to g_texFilePtr */
-            1, /* 1 = htmlCenterFlag */
-            PROCESS_EVERYTHING, /* actionBits */ /* 13-Dec-2018 nm */
-            0  /* 1 = noFileCheck */);
-      }
-    }
-  }
-  if (commentOnlyFlag && !briefFlag) goto returnPoint;
-
-  if ((briefFlag && !texFlag) ||
-       (htmlFlg && texFlag) /* HTML page 12/23/01 */) {
-    /* In BRIEF mode screen output, show $d's */
-    /* This section was added 8/31/99 */
-
-    /* 12/23/01 - added algorithm to HTML pages also; the string to print out
-       is stored in htmlDistinctVars for later printing */
-
-    /* 12/23/01 */
-    if (htmlFlg && texFlag) {
-      let(&htmlDistinctVars, "");
-      htmlDistinctVarsCommaFlag = 0;
-    }
-
-    /* Note added 22-Aug-04:  This algorithm is used to re-merge $d pairs
-       into groups of 3 or more when possible, for a more compact display.
-       The algorithm does not merge groups optimally, but it should be
-       adequate.  For example, in set.mm (e.g. old r19.23aivv):
-         $d x ps $.  $d y ps $.  $d y A $.  $d x y $.
-       produces in SHOW STATEMENT (note redundant 3rd $d):
-         $d ps x y $.  $d y A $.  $d x y $.
-       However, in set.mm the equivalent (and better anyway):
-         $d x y ps $.  $d y A $.
-       produces the same thing when remerged in SHOW STATEMENT. */
-    let(&str1, "");
-    nmbrTmpPtr1 = g_Statement[showStmt].reqDisjVarsA;
-    nmbrTmpPtr2 = g_Statement[showStmt].reqDisjVarsB;
-    i = nmbrLen(nmbrTmpPtr1);
-    if (i /* Number of mandatory $d pairs */) {
-      nmbrLet(&nmbrDDList, NULL_NMBRSTRING);
-      for (k = 0; k < i; k++) {
-        /* Is one of the variables in the current list? */
-        if (!nmbrElementIn(1, nmbrDDList, nmbrTmpPtr1[k]) &&
-            !nmbrElementIn(1, nmbrDDList, nmbrTmpPtr2[k])) {
-          /* No, so close out the current list */
-          if (!(htmlFlg && texFlag)) { /* 12/23/01 */
-            if (k == 0) let(&str1, "$d");
-            else let(&str1, cat(str1, " $.  $d", NULL));
-          } else {
-
-            /* 12/23/01 */
-            let(&htmlDistinctVars, cat(htmlDistinctVars, " &nbsp; ",
-                NULL));
-            htmlDistinctVarsCommaFlag = 0;
-            distVarGrps++;  /* 11-Aug-2006 nm */
-
-          }
-          nmbrLet(&nmbrDDList, NULL_NMBRSTRING);
-        }
-        /* Are both variables required to be distinct from all others
-           in current list? */
-        for (n = 0; n < nmbrLen(nmbrDDList); n++) {
-          if (nmbrDDList[n] != nmbrTmpPtr1[k] &&
-              nmbrDDList[n] != nmbrTmpPtr2[k]) {
-            q1 = 0; q2 = 0;
-            for (m = 0; m < i; m++) {
-              if ((nmbrTmpPtr1[m] == nmbrDDList[n] &&
-                      nmbrTmpPtr2[m] == nmbrTmpPtr1[k]) ||
-                  (nmbrTmpPtr2[m] == nmbrDDList[n] &&
-                      nmbrTmpPtr1[m] == nmbrTmpPtr1[k])) {
-                q1 = 1; /* 1st var is required to be distinct */
-              }
-              if ((nmbrTmpPtr1[m] == nmbrDDList[n] &&
-                      nmbrTmpPtr2[m] == nmbrTmpPtr2[k]) ||
-                  (nmbrTmpPtr2[m] == nmbrDDList[n] &&
-                      nmbrTmpPtr1[m] == nmbrTmpPtr2[k])) {
-                q2 = 1;  /* 2nd var is required to be distinct */
-              }
-              if (q1 && q2) break; /* Found both */
-            }  /* Next m */
-            if (!q1 || !q2) {
-              /* One of the variables is not required to be distinct from
-                all others in the current list, so close out current list */
-              if (!(htmlFlg && texFlag)) {
-                if (k == 0) let(&str1, "$d");
-                else let(&str1, cat(str1, " $.  $d", NULL));
-              } else {
-
-                /* 12/23/01 */
-                let(&htmlDistinctVars, cat(htmlDistinctVars, " &nbsp; ",
-                    NULL));
-                htmlDistinctVarsCommaFlag = 0;
-                distVarGrps++;  /* 11-Aug-2006 nm */
-
-              }
-              nmbrLet(&nmbrDDList, NULL_NMBRSTRING);
-              break; /* Out of n loop */
-            }
-          } /* If $d var in current list is not same as one we're adding */
-        } /* Next n */
-        /* If the variable is not already in current list, add it */
-        if (!nmbrElementIn(1, nmbrDDList, nmbrTmpPtr1[k])) {
-          if (!(htmlFlg && texFlag)) {
-            let(&str1, cat(str1, " ", g_MathToken[nmbrTmpPtr1[k]].tokenName,
-                NULL));
-          } else {
-
-            /* 12/23/01 */
-            if (htmlDistinctVarsCommaFlag) {
-              let(&htmlDistinctVars, cat(htmlDistinctVars, ",", NULL));
-            }
-            htmlDistinctVarsCommaFlag = 1;
-            let(&str2, "");
-            str2 = tokenToTex(g_MathToken[nmbrTmpPtr1[k]].tokenName, showStmt);
-                 /* tokenToTex allocates str2; we must deallocate it */
-            let(&htmlDistinctVars, cat(htmlDistinctVars, str2, NULL));
-
-          }
-          nmbrLet(&nmbrDDList, nmbrAddElement(nmbrDDList, nmbrTmpPtr1[k]));
-        }
-        if (!nmbrElementIn(1, nmbrDDList, nmbrTmpPtr2[k])) {
-          if (!(htmlFlg && texFlag)) {
-            let(&str1, cat(str1, " ", g_MathToken[nmbrTmpPtr2[k]].tokenName,
-                NULL));
-          } else {
-
-            /* 12/23/01 */
-            if (htmlDistinctVarsCommaFlag) {
-              let(&htmlDistinctVars, cat(htmlDistinctVars, ",", NULL));
-            }
-            htmlDistinctVarsCommaFlag = 1;
-            let(&str2, "");
-            str2 = tokenToTex(g_MathToken[nmbrTmpPtr2[k]].tokenName, showStmt);
-                 /* tokenToTex allocates str2; we must deallocate it */
-            let(&htmlDistinctVars, cat(htmlDistinctVars, str2, NULL));
-
-          }
-          nmbrLet(&nmbrDDList, nmbrAddElement(nmbrDDList, nmbrTmpPtr2[k]));
-        }
-      } /* Next k */
-      /* Close out entire list */
-      if (!(htmlFlg && texFlag)) {
-        let(&str1, cat(str1, " $.", NULL));
-        printLongLine(str1, "  ", " ");
-      } else {
-
-        /* 12/23/01 */
-        /* (do nothing) */
-        /*let(&htmlDistinctVars, cat(htmlDistinctVars, "<BR>", NULL));*/
-
-      }
-    } /* if i(#$d's) > 0 */
-  }
-
-  if (briefFlag || texFlag /*(texFlag && htmlFlg)*/) { /* 6-Dec-03 */
-    /* For BRIEF mode, print $e hypotheses (only) before statement */
-    /* Also do it for HTML output */
-    /* 6-Dec-03  For the LaTeX output, now print hypotheses before statement */
-    j = nmbrLen(g_Statement[showStmt].reqHypList);
-    k = 0;
-    for (i = 0; i < j; i++) {
-      /* Count the number of essential hypotheses */
-      if (g_Statement[g_Statement[showStmt].reqHypList[i]].type
-        == (char)e_) k++;
-
-      /* Added 5/26/03 */
-      /* For syntax definitions, also include $f hypotheses so user can more
-         easily match them in syntax breakdowns of axioms and definitions */
-      if (subType == SYNTAX && (texFlag && htmlFlg)) {
-        if (g_Statement[g_Statement[showStmt].reqHypList[i]].type
-          == (char)f_) k++;
-      }
-
-    }
-    if (k) {
-      if (texFlag) {
-        /* Note that printTexLongMath resets it to 0 */
-        g_outputToString = 1;
-      }
-      if (texFlag && htmlFlg) {
-        print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
-            MINT_BACKGROUND_COLOR);
-        /* For bobby.cast.org approval */
-        print2("SUMMARY=\"%s\">\n", (k == 1) ? "Hypothesis" : "Hypotheses");
-        print2("<CAPTION><B>%s</B></CAPTION>\n",
-            (k == 1) ? "Hypothesis" : "Hypotheses");
-        print2("<TR><TH>Ref\n");
-        print2("</TH><TH>Expression</TH></TR>\n");
-      }
-      for (i = 0; i < j; i++) {
-        k = g_Statement[showStmt].reqHypList[i];
-        if (g_Statement[k].type != (char)e_
-
-            /* Added 5/26/03 */
-            /* For syntax definitions, include $f hypotheses so user can more
-               easily match them in syntax breakdowns of axioms & definitions */
-            && !(subType == SYNTAX && (texFlag && htmlFlg)
-                && g_Statement[k].type == (char)f_)
-
-            ) continue;
-
-        if (!texFlag) {
-          let(&str2, cat(str((double)k), " ", NULL));
-        } else {
-          let(&str2, "  ");
-        }
-        let(&str2, cat(str2, g_Statement[k].labelName,
-            " $", chr(g_Statement[k].type), " ", NULL));
-        if (!texFlag) {
-          printLongLine(cat(str2,
-              nmbrCvtMToVString(g_Statement[k].mathString), " $.", NULL),
-              "      "," ");
-        } else { /* if texFlag */
-          /* texFlag was (misleadingly) included below to facilitate search
-             for "htmlFlg && texFlag". */
-          if (!(htmlFlg && texFlag)) {
-            if (!g_oldTexFlag) {  /* 14-Sep-2010 nm */
-              /* Do nothing */
-            } else {
-              let(&str3, space((long)strlen(str2)));
-              printTexLongMath(g_Statement[k].mathString,
-                  str2, str3, 0, 0);
-            }
-          } else {
-            g_outputToString = 1;
-            print2("<TR ALIGN=LEFT><TD>%s</TD><TD>\n",
-                g_Statement[k].labelName);
-            /* Print hypothesis */
-            printTexLongMath(g_Statement[k].mathString, "", "", 0, 0);
-          }
-        }
-      } /* next i */
-      if (texFlag && htmlFlg) {
-        g_outputToString = 1;
-        print2("</TABLE></CENTER>\n");
-      }
-    } /* if k (#essential hyp) */
-  }
-
-  let(&str1, "");
-  type = g_Statement[showStmt].type;
-  if (type == p_) let(&str1, " $= ...");
-  if (!texFlag)
-    let(&str2, cat(str((double)showStmt), " ", NULL));
-  else
-    let(&str2, "  ");
-  let(&str2, cat(str2, g_Statement[showStmt].labelName,
-      " $",chr(type), " ", NULL));
-  if (!texFlag) {
-    printLongLine(cat(str2,
-        nmbrCvtMToVString(g_Statement[showStmt].mathString),
-        str1, " $.", NULL), "      ", " ");
-  } else {
-    if (!(htmlFlg && texFlag)) {  /* really !htmlFlg & texFlag */
-      if (!g_oldTexFlag) {
-        /* 14-Sep-2010 nm new LaTeX code: */
-        g_outputToString = 1;
-        print2("\\begin{align}\n");
-        let(&str3, "");
-        /* Get HTML hypotheses => assertion */
-        str3 = getTexOrHtmlHypAndAssertion(showStmt); /* In mmwtex.c */
-        printLongLine(cat(str3,
-              /* No space before \label to make it easier to find last
-                 parenthesis in a post-processing script */
-              "\\label{eq:",
-              g_Statement[showStmt].labelName,
-              "}",
-
-              /* 1-May-2017 nm */
-              /* Add "\tag{..}" to use .mm labels instead of equation numbers */
-              /* (Suggested by Ari Ferrera) */
-              "\\tag{",
-              g_Statement[showStmt].labelName,
-              "}",
-
-              NULL), "    ", " ");
-        /* print2("    \\label{eq:%s}\n",g_Statement[showStmt].labelName); */
-        print2("\\end{align}\n");
-
-
-        /* 1-May-2017 nm */
-        /* Distinguish axiom, definition, theorem for LaTeX */
-        /* Note: changes here must be mirrored in the \begin{...} above */
-        if (g_Statement[showStmt].type == a_) {
-          if (!strcmp(left(g_Statement[showStmt].labelName, 3), "ax-")) {
-            let(&str3, "axiom");
-          } else {
-            let(&str3, "definition");
-          }
-        } else {
-          let(&str3, "theorem");
-        }
-        print2("%s\n", cat("\\end{", str3, "}", NULL));
-        /* old code before 1-May-2017:
-        print2("\\end{lemma}\n");
-        */
-
-        fprintf(g_texFilePtr, "%s", g_printString);
-        let(&g_printString, "");
-        g_outputToString = 0;
-
-      } else { /* old TeX code */
-        let(&str3, space((long)strlen(str2))); /* 3rd argument of printTexLongMath
-            cannot be temp allocated */
-        printTexLongMath(g_Statement[showStmt].mathString,
-            str2, str3, 0, 0);
-      }
-    } else { /* (htmlFlg && texFlag) */
-      g_outputToString = 1;
-      print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
-          MINT_BACKGROUND_COLOR);
-      /* For bobby.cast.org approval */
-      print2("SUMMARY=\"Assertion\">\n");
-      print2("<CAPTION><B>Assertion</B></CAPTION>\n");
-      print2("<TR><TH>Ref\n");
-      print2("</TH><TH>Expression</TH></TR>\n");
-      printLongLine(cat(
-       "<TR ALIGN=LEFT><TD><FONT COLOR=",
-          GREEN_TITLE_COLOR, "><B>", g_Statement[showStmt].labelName,
-          "</B></FONT></TD><TD>", NULL), "      ", " ");
-      printTexLongMath(g_Statement[showStmt].mathString, "", "", 0, 0);
-      g_outputToString = 1;
-      print2("</TABLE></CENTER>\n");
-    }
-  }
-
-  if (briefFlag) goto returnPoint;
-
-  /* 6-Dec-03 In the LaTeX output, the hypotheses used to be printed after the
-     statement.  Now they are printed before (see above 6-Dec-03 comments),
-     so some code below is commented out. */
-  switch (type) {
-    case a_:
-    case p_:
-      /* 6-Dec-03  This is not really needed but keeps output consistent
-         with previous version.  It puts a blank line before the HTML
-         "distinct variable" list. */
-      if (texFlag && htmlFlg) { /* 6-Dec-03 */
-        g_outputToString = 1;
-        print2("\n");
-        g_outputToString = 0;
-      }
-
-      /*if (!(htmlFlg && texFlag)) {*/
-      if (!texFlag) {  /* 6-Dec-03 fix */
-        print2("Its mandatory hypotheses in RPN order are:\n");
-      }
-      /*if (texFlag) g_outputToString = 0;*/ /* 6-Dec-03 */
-      j = nmbrLen(g_Statement[showStmt].reqHypList);
-      for (i = 0; i < j; i++) {
-        k = g_Statement[showStmt].reqHypList[i];
-        if (g_Statement[k].type != (char)e_ && (!htmlFlg && texFlag))
-          continue; /* 9/2/99 Don't put $f's in LaTeX output */
-        let(&str2, cat("  ",g_Statement[k].labelName,
-            " $", chr(g_Statement[k].type), " ", NULL));
-        if (!texFlag) {
-          printLongLine(cat(str2,
-              nmbrCvtMToVString(g_Statement[k].mathString), " $.", NULL),
-              "      "," ");
-        } else {
-          if (!(htmlFlg && texFlag)) {  /* LaTeX */
-            /*let(&str3, space((long)strlen(str2)));*/ /* 6-Dec-03 */
-            /* This clears out g_printString */
-            /*printTexLongMath(g_Statement[k].mathString,
-                str2, str3, 0, 0);*/ /* 6-Dec-03 */
-          }
-        }
-      }
-      /* 6-Dec-03  This is not really needed but keeps output consistent
-         with previous version.  It puts a blank line before the HTML
-         "distinct variable" list. */
-      if (texFlag && htmlFlg) { /* 6-Dec-03 */
-        g_outputToString = 1;
-        print2("\n");
-        g_outputToString = 0;
-      }
-      /*if (j == 0 && !(htmlFlg && texFlag)) print2("  (None)\n");*/
-      if (j == 0 && !texFlag) print2("  (None)\n"); /* 6-Dec-03 fix */
-      let(&str1, "");
-      nmbrTmpPtr1 = g_Statement[showStmt].reqDisjVarsA;
-      nmbrTmpPtr2 = g_Statement[showStmt].reqDisjVarsB;
-      i = nmbrLen(nmbrTmpPtr1);
-      if (i) {
-        for (k = 0; k < i; k++) {
-          if (!texFlag) {
-            let(&str1, cat(str1, ", <",
-                g_MathToken[nmbrTmpPtr1[k]].tokenName, ",",
-                g_MathToken[nmbrTmpPtr2[k]].tokenName, ">", NULL));
-          } else {
-            if (htmlFlg && texFlag) {
-              let(&str2, "");
-              str2 = tokenToTex(g_MathToken[nmbrTmpPtr1[k]].tokenName, showStmt);
-                   /* tokenToTex allocates str2; we must deallocate it */
-              let(&str1, cat(str1, " &nbsp; ", str2, NULL));
-              let(&str2, "");
-              str2 = tokenToTex(g_MathToken[nmbrTmpPtr2[k]].tokenName, showStmt);
-              let(&str1, cat(str1, ",", str2, NULL));
-            }
-          }
-        }
-        if (!texFlag)
-          printLongLine(cat(
-              "Its mandatory disjoint variable pairs are:  ",
-              right(str1,3),NULL),"  "," ");
-      }
-      if (type == p_ &&
-          nmbrLen(g_Statement[showStmt].optHypList)
-          && !texFlag) {
-        printLongLine(cat(
-           "Its optional hypotheses are:  ",
-            nmbrCvtRToVString(
-            g_Statement[showStmt].optHypList,
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum, used only if explicitTargets*/), NULL),
-            "      "," ");
-      }
-      nmbrTmpPtr1 = g_Statement[showStmt].optDisjVarsA;
-      nmbrTmpPtr2 = g_Statement[showStmt].optDisjVarsB;
-      i = nmbrLen(nmbrTmpPtr1);
-      if (i && type == p_) {
-        if (!texFlag) {
-          let(&str1, "");
-        } else {
-          if (htmlFlg && texFlag) {
-            /*  12/1/01 don't output dummy variables
-            let(&str1, cat(str1,
-            " &nbsp; (Dummy variables for use in proof:) ", NULL));
-            */
-          }
-        }
-        for (k = 0; k < i; k++) {
-          if (!texFlag) {
-            let(&str1, cat(str1, ", <",
-                g_MathToken[nmbrTmpPtr1[k]].tokenName, ",",
-                g_MathToken[nmbrTmpPtr2[k]].tokenName, ">", NULL));
-          } /* if !texFlag */
-        } /* next k */
-        if (!texFlag) {
-          printLongLine(cat(
-              "Its optional disjoint variable pairs are:  ",
-              right(str1,3),NULL),"  "," ");
-        }
-      } /* if (i && type == p_) */
-
-      /* Before 12/23/01 **********
-           Future: once stable, take out redundant code producing str1
-      if (texFlag && htmlFlg && str1[0]) {
-        g_outputToString = 1;
-        printLongLine(cat("<CENTER>Substitutions into these variable",
-            " pairs may not have variables in common: ",
-            str1, "</CENTER>", NULL), "", " ");
-        g_outputToString = 0;
-      }
-      ***********/
-
-
-      /* 12/23/01 */
-      if (texFlag && htmlFlg) { /* It's a web page */
-
-        if (htmlDistinctVars[0] != 0) {
-          g_outputToString = 1;
-          printLongLine(cat(
-              "<CENTER>",
-              "<A HREF=\"",
-
-              /* 26-Aug-2017 nm */
-              /* g_htmlHome is set by htmlhome in $t comment */
-              (instr(1, g_htmlHome, "mmset.html") > 0) ?
-                  "mmset.html" :
-                  /* The following link will work in the NF and other
-                     "Proof Explorers" */
-                  "../mpeuni/mmset.html",  /* 19-Aug-2017, 26-Sep-2017 nm */
-
-              "#distinct\">Distinct variable</A> group",
-              /* 11-Aug-2006 nm Determine whether "group" or "groups". */
-              distVarGrps > 1 ? "s" : "",  /* 11-Aug-2006 */
-              ": ",
-              /* 14-Jan-2016 nm Put a span around the variable list to localize
-                 the use of the special math font for ALT_HTML */
-              (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
-                                                 /* 14-Jan-2016 nm */
-              htmlDistinctVars,
-              (g_altHtmlFlag ? "</SPAN>" : ""),
-              "</CENTER>",
-              NULL), "", "\"");
-          g_outputToString = 0;
-        }
-
-        /* 26-Aug-2017 nm Moved this down into proof section */
-        /*
-        /@ 13-Aug-2017 nm @/
-        let(&str3, "");
-        if (type == p_) {
-          str3 = htmlDummyVars(showStmt);
-          if (str3[0] != 0) {
-            g_outputToString = 1;
-            /@ We don't need <BR> if code is surrounded by <CENTER>...</CENTER>
-            if (htmlDistinctVars[0] != 0) print2("<BR>\n");
-            @/
-            /@ Print the list of dummy variables @/
-            printLongLine(str3, "", "\"");
-            g_outputToString = 0;
-          }
-        }
-        /@ (end of 13-Aug-2017) @/
-        */
-
-        /* 4-Jan-2014 nm */
-        let(&str2, "");
-        str2 = htmlAllowedSubst(showStmt);
-        if (str2[0] != 0) {
-          g_outputToString = 1;
-          /* We don't need <BR> if code is surrounded by <CENTER>...</CENTER>
-          if (htmlDistinctVars[0] != 0 || str3[0] != 0) print2("<BR>\n");
-          */
-          /* Print the list of allowed free variables */
-          printLongLine(str2, "", "\"");
-          g_outputToString = 0;
-        }
-
-      } /* if (texFlag && htmlFlg) */
-
-      if (texFlag) {
-        g_outputToString = 1;
-        if (htmlFlg && texFlag) print2("<HR NOSHADE SIZE=1>\n");
-        g_outputToString = 0; /* Restore normal output */
-        /* will be done automatically at closing
-        fprintf(g_texFilePtr, "%s", g_printString);
-        let(&g_printString, "");
-        */
-        break; /* case a_ or p_ */
-      }
-      let(&str1, nmbrCvtMToVString(
-          g_Statement[showStmt].reqVarList));
-      if (!strlen(str1)) let(&str1, "(None)");
-      printLongLine(cat(
-          "The statement and its hypotheses require the variables:  ",
-          str1, NULL), "      ", " ");
-      if (type == p_ &&
-          nmbrLen(g_Statement[showStmt].optVarList)) {
-        printLongLine(cat(
-            "These additional variables are allowed in its proof:  "
-            ,nmbrCvtMToVString(
-            g_Statement[showStmt].optVarList),NULL),"      ",
-            " ");
-        /*??? Add variables required by proof */
-      }
-      /* Note:  g_Statement[].reqVarList is only stored for $a and $p
-         statements, not for $e or $f. */
-      let(&str1, nmbrCvtMToVString(
-          g_Statement[showStmt].reqVarList));
-      if (!strlen(str1)) let(&str1, "(None)");
-      printLongLine(cat("The variables it contains are:  ",
-          str1, NULL),
-          "      ", " ");
-      break; /* case a_ or p_ */
-    default:
-      break;
-  } /* End switch(type) */
-  if (texFlag) {
-    g_outputToString = 0;
-    /* will be done automatically at closing
-    fprintf(g_texFilePtr, "%s", g_printString);
-    let(&g_printString, "");
-    */
-  }
-
-  /* Start of finding definition for syntax statement */
-  if (htmlFlg && texFlag) {
-
-    /* For syntax declarations, find the first definition that follows
-       it.  It is up to the user to arrange the database so that a
-       meaningful definition is picked. */
-    if (subType == SYNTAX) {
-      for (i = showStmt + 1; i <= g_statements; i++) {
-        if (g_Statement[i].type == (char)a_) {
-          if (!strcmp("|-", g_MathToken[
-              (g_Statement[i].mathString)[0]].tokenName)) {
-            /* It's a definition or axiom */
-            /* See if each constant token in the syntax statement
-               exists in the definition; if not don't use the definition */
-            j = 1;
-            /* We start with k=1 for 2nd token (1st is wff, class, etc.) */
-            for (k = 1; k < g_Statement[showStmt].mathStringLen; k++) {
-              if (g_MathToken[(g_Statement[showStmt].mathString)[k]].
-                  tokenType == (char)con_) {
-                if (!nmbrElementIn(1, g_Statement[i].mathString,
-                    (g_Statement[showStmt].mathString)[k])) {
-                  /* The definition being considered doesn't have one of
-                     the constant symbols in the syntax statement, so
-                     reject it */
-                  j = 0;
-                  break; /* Out of k loop */
-                }
-              }
-            } /* Next k */
-            if (j) {
-              /* Successful - use this definition or axiom as the reference */
-              g_outputToString = 1;
-              let(&str1, left(g_Statement[i].labelName, 3));
-              let(&str2, "");
-              str2 = pinkHTML(i);
-              if (!strcmp(str1, "ax-")) {
-                printLongLine(cat(
-                    "<CENTER>This syntax is primitive.",
-                    "  The first axiom using it is <A HREF=\"",
-                    g_Statement[i].labelName, ".html\">",
-                    g_Statement[i].labelName,
-                    "</A>", str2, ".</CENTER><HR NOSHADE SIZE=1>",
-                    NULL), "", "\"");
-              } else {
-                printLongLine(cat(
-                    "<CENTER>See definition <A HREF=\"",
-                    g_Statement[i].labelName, ".html\">",
-                    g_Statement[i].labelName, "</A>", str2,
-                    " for more information.</CENTER><HR NOSHADE SIZE=1>",
-                    NULL), "", "\"");
-              }
-
-              /* 10/10/02 Moved here from mmwtex.c */
-              /*print2("<FONT SIZE=-1 FACE=sans-serif>Colors of variables:\n");*/
-              printLongLine(cat(
-                  "<CENTER><TABLE CELLSPACING=7><TR><TD ALIGN=LEFT><FONT SIZE=-1>",
-                  "<B>Colors of variables:</B> ",
-                  g_htmlVarColor, "</FONT></TD></TR>",
-                  NULL), "", "\"");
-              g_outputToString = 0;
-              break; /* Out of i loop */
-            }
-          }
-        }
-      } /* Next i */
-    } /* if (subType == SYNTAX) */
-
-
-    /* For definitions, we pretend that the definition is a "wff" (hard-coded
-       here; the .mm database provided by the user must use this convention).
-       We use the proof assistant tools to prove that the statement is
-       a wff, then we print the wff construction proof to the HTML file. */
-    if (subType == DEFINITION || subType == AXIOM) {
-
-      /* Look up the token "wff" if we haven't found it before */
-      if (wffToken == -1) { /* First time */
-        wffToken = -2; /* In case it's not found because the user's source
-            used a convention different for "wff" for wffs */
-        for (i = 0; i < g_mathTokens; i++) {
-          if (!strcmp("wff", g_MathToken[i].tokenName)) {
-            wffToken = i;
-            break;
-          }
-        }
-      }
-
-      if (wffToken >= 0) {
-        /* Temporarily zap statement type from $a to $p */
-        if (g_Statement[showStmt].type != (char)a_) bug(231);
-        g_Statement[showStmt].type = (char)p_;
-        /* Temporarily zap statement with "wff" token in 1st position
-           so parseProof will not give errors (in typeProof() call) */
-        zapStatement1stToken = (g_Statement[showStmt].mathString)[0];
-        (g_Statement[showStmt].mathString)[0] = wffToken;
-        if (strcmp("|-", g_MathToken[zapStatement1stToken].tokenName)) bug(230);
-
-        nmbrTmpPtr1 = NULL_NMBRSTRING;
-        nmbrLet(&nmbrTmpPtr1, g_Statement[showStmt].mathString);
-
-        /* Find proof of formula or simple theorem (no new vars in $e's) */
-        /* maxEDepth is the maximum depth at which statements with $e
-           hypotheses are
-           considered.  A value of 0 means none are considered. */
-        nmbrTmpPtr2 = proveFloating(nmbrTmpPtr1 /*mString*/,
-            showStmt /*statemNum*/, 0 /*maxEDepth*/,
-            0, /*step:  0 = step 1 */ /*For messages*/
-            0,  /*not noDistinct*/
-            /* 3-May-2016 nm */
-            2, /* override discouraged-usage statements silently */
-            1 /* Always allow other mathboxes */ /* 5-Aug-2020 nm */
-            );
-
-        if (nmbrLen(nmbrTmpPtr2)) {
-          /* A proof for the step was found. */
-          /* Get packed form of proof for shorter display */
-          nmbrLet(&nmbrTmpPtr2, nmbrSquishProof(nmbrTmpPtr2));
-          /* Temporarily zap proof into statement structure */
-          /* (The bug check makes sure there is no proof attached to the
-              definition - this would be impossible) */
-          if (strcmp(g_Statement[showStmt].proofSectionPtr, "")) bug(231);
-          if (g_Statement[showStmt].proofSectionLen != 0) bug(232);
-          let(&str1, nmbrCvtRToVString(nmbrTmpPtr2,
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum, used only if explicitTargets*/));
-          /* Temporarily zap proof into the $a statement */
-          g_Statement[showStmt].proofSectionPtr = str1;
-          g_Statement[showStmt].proofSectionLen = (long)strlen(str1) - 1;
-
-          /* Display the HTML proof of syntax breakdown */
-          typeProof(showStmt,
-              0 /*pipFlag*/,
-              0 /*startStep*/,
-              0 /*endStep*/,
-              0 /*endIndent*/,
-              0 /*essentialFlag*/, /* <- also used as def flag in typeProof */
-              1 /*renumberFlag*/,
-              0 /*unknownFlag*/,
-              0 /*notUnifiedFlag*/,
-              0 /*reverseFlag*/,
-              1 /*noIndentFlag*/,
-              0 /*splitColumn*/,
-              0 /*skipRepeatedSteps*/,  /* 28-Jun-2013 nm */
-              1 /*texFlag*/,  /* Means either latex or html */
-              1 /*htmlFlg*/);
-
-          /* Restore the zapped statement structure */
-          g_Statement[showStmt].proofSectionPtr = "";
-          g_Statement[showStmt].proofSectionLen = 0;
-
-          /* Deallocate storage */
-          let(&str1, "");
-          nmbrLet(&nmbrTmpPtr2, NULL_NMBRSTRING);
-
-        } else { /* if (nmbrLen(nmbrTmpPtr2)) else */
-          /* 5-Aug-2011 nm */
-          /* Proof was not found - probable syntax error */
-          if (g_outputToString != 0) bug(246);
-          printLongLine(cat(
-              "?Warning: Unable to generate syntax breakdown for \"",
-              g_Statement[showStmt].labelName,
-              "\".", NULL), "    ", " ");
-        }
-
-
-        /* Restore the zapped statement structure */
-        g_Statement[showStmt].type = (char)a_;
-        (g_Statement[showStmt].mathString)[0] = zapStatement1stToken;
-
-        /* Deallocate storage */
-        nmbrLet(&nmbrTmpPtr1, NULL_NMBRSTRING);
-
-      } /* if (wffToken >= 0) */
-
-    } /* if (subType == DEFINITION) */
-
-
-  } /* if (htmlFlg && texFlag) */
-  /* End of finding definition for syntax statement */
-
-
-  /* 10/6/99 - Start of creating used-by list for html page */
-  if (htmlFlg && texFlag) {
-    /* 10/25/02 Clear out any previous g_printString accumulation
-       for g_printStringForReferencedBy case below */
-    fprintf(g_texFilePtr, "%s", g_printString);
-    let(&g_printString, "");
-    /* Start outputting to g_printString */
-    if (g_outputToString != 0) bug(242);
-    g_outputToString = 1;
-    if (subType != SYNTAX) { /* Only do this for
-        definitions, axioms, and theorems, not syntax statements */
-      let(&str1, "");
-      g_outputToString = 0; /* Switch output to console in case
-            traceUsage reports an error */ /* 8-Dec-2018 nm */
-      str1 = traceUsage(showStmt,
-          0, /* recursiveFlag */
-          0 /* cutoffStmt */);
-      g_outputToString = 1; /* Restore output to string */ /* 8-Dec-2018 nm */
-      /* if (str1[0]) { */ /* Used by at least one */
-      /* 18-Jul-2015 nm */
-
-      /* 30-Oct-2018 nm Commented out, and block unindented: */
-      /*if (str1[0] == 'Y') {*/ /* Used by at least one */
-
-      /* 30-Oct-2018 nm */
-      /* We now output this all the time, using "(None)" if none, per
-         request of Benoit Jubin */
-
-      /* str1[i] will be 'Y' if used by showStmt */
-      /* Convert usage list str1 to html links */
-      switch (subType) {
-        case AXIOM:  let(&str3, "axiom"); break;
-        case DEFINITION: let(&str3, "definition"); break;
-        case THEOREM: let(&str3, "theorem"); break;
-        default: bug(233);
-      }
-      /******* pre 10/10/02
-      let(&str2, cat("<FONT SIZE=-1 FACE=sans-serif>This ", str3,
-          " is referenced by: ", NULL));
-      *******/
-      /* 10/10/02 */
-      let(&str2, cat("<TR><TD ALIGN=LEFT><FONT SIZE=-1><B>This ", str3,
-          " is referenced by:</B>", NULL));
-
-      if (str1[0] == 'Y') { /* Used by at least one */
-        /********* 18-Jul-2015 Deleted code *********************/
-        /*
-        /@ Convert str1 to trailing space after each label @/
-        let(&str1, cat(right(str1, 2), " ", NULL));
-        let(&str5, ""); /@ Buffer for very long strings @/ /@ 19-Sep-2012 nm @/
-        i = 0;
-        while (1) {
-          j = i + 1;
-          i = instr(j, str1, " ");
-          if (!i) break;
-          /@ Extract the label @/
-          let(&str3, seg(str1, j, i - 1));
-          /@ Find the statement number @/
-          m = lookupLabel(str3);
-          if (m < 0) {
-            /@ The lookup should never fail @/
-            bug(240);
-            continue;
-          }
-          /@ It should be a $p @/
-          if (g_Statement[m].type != p_) bug(241);
-          /@ Get the pink number @/
-          let(&str4, "");
-          str4 = pinkHTML(m);
-          /@ Assemble the href @/
-          let(&str2, cat(str2, " &nbsp;<A HREF=\"",
-              str3, ".html\">",
-              str3, "</A>", str4, NULL));
-          /@ 8-Aug-2008 nm If line is very long, print it out and reset
-             it to speed up program (SHOW STATEMENT syl/HTML is very slow) @/
-          /@ 8-Aug-2008 nm This doesn't solve problem, because the bottleneck
-             is printing g_printStringForReferencedBy below.  This whole
-             code section needs to be redesigned to solve the speed problem. @/
-          /@
-          if (strlen(str2) > 500) {
-            printLongLine(str2, "", "\"");
-            let(&str2, "");
-          }
-          @/
-          /@ 19-Sep-2012 nm Try again to fix SHOW STATEMENT syl/HTML speed
-             without a major rewrite @/
-          /@
-          Unfortunately, makes little difference.  Using lcc:
-          orig    real    1m6.676s
-          500     real    1m2.285s
-          3000    real    1m2.181s
-          3000    real    1m1.663s
-          3000    real    1m1.785s
-          5000    real    1m0.678s
-          5000    real    1m0.169s
-          5000    real    1m1.951s
-          5000    real    1m2.307s
-          5000    real    1m1.717s
-          7000    real    1m2.048s
-          7000    real    1m2.012s
-          7000    real    1m1.817s
-          10000   real    1m2.779s
-          10000   real    1m1.830s
-          20000   real    1m1.431s
-          50000   real    1m1.325s
-          100000  real    1m3.172s
-          100000  real    1m4.657s
-          (Added 17-Jul-2015: The 1 minute is probably due to the old
-              traceUsage algorithm that built a huge string of labels; should
-              be faster now.)
-          @/
-          /@ Accumulate large cat buffer when small cats exceed certain size @/
-          if (strlen(str2) > 5000) {
-            let(&str5, cat(str5, str2, NULL));
-            let(&str2, "");
-          }
-          /@ End 19-Sep-2012 @/
-        }
-        /@ let(&str2, cat(str2, "</FONT></TD></TR>", NULL)); @/ /@ old @/
-         /@ 19-Sep-2012 nm Include buffer in output string@/
-        let(&str2, cat(str5, str2, "</FONT></TD></TR>", NULL));
-        printLongLine(str2, "", "\"");
-        */
-        /**************** 18-Jul-2015 End of deleted code *********/
-
-
-        let(&str5, ""); /* Buffer for very long strings */ /* 19-Sep-2012 nm */
-        /* Scan all future statements in str1 Y/N list */
-        for (m = showStmt + 1; m <= g_statements; m++) {
-          /* Scan the used-by map */
-          if (str1[m] != 'Y') continue;
-          /* Get the label */
-          let(&str3, g_Statement[m].labelName);
-          /* It should be a $p */
-          if (g_Statement[m].type != p_) bug(241);
-          /* Get the pink number */
-          let(&str4, "");
-          str4 = pinkHTML(m);
-          /* Assemble the href */
-          let(&str2, cat(str2, " &nbsp;<A HREF=\"",
-              str3, ".html\">",
-              /*str3, "</A>", str4, NULL));*/
-              str3, "</A>\n", str4, NULL));  /* 18-Jul-2015 nm */
-          /* 8-Aug-2008 nm If line is very long, print it out and reset
-             it to speed up program (SHOW STATEMENT syl/HTML is very slow) */
-          /* 8-Aug-2008 nm This doesn't solve problem, because the bottleneck
-             is printing g_printStringForReferencedBy below.  This whole
-             code section needs to be redesigned to solve the speed problem. */
-          /* 19-Sep-2012 nm Try again to fix SHOW STATEMENT syl/HTML speed
-             without a major rewrite.  Unfortunately, made little difference. */
-          /* 18-Jul-2015: Part of slowdown was due to the old
-              traceUsage algorithm that built a huge string of labels.  Improved
-             from 313 sec to 280 sec for 'sh st syl/a'; still a problem. */
-          /* Accumulate large cat buffer when small cats exceed certain size */
-          if (strlen(str2) > 5000) {
-            let(&str5, cat(str5, str2, NULL));
-            let(&str2, "");
-          }
-          /* End 19-Sep-2012 */
-        } /* next m (statement number) */
-
-
-      /* 30-Oct-2018 nm Added "else" clause to print "(None)" if no refs */
-      } else {
-        /* There is no usage of this statement; print "(None)" */
-        let(&str5, "");
-        let(&str2, cat(str2, " (None)", NULL));
-
-      } /* if (str1[0] == 'Y') */
-      /* let(&str2, cat(str2, "</FONT></TD></TR>", NULL)); */ /* old */
-       /* 19-Sep-2012 nm Include buffer in output string*/
-      let(&str2, cat(str5, str2, "</FONT></TD></TR>", NULL));
-      /*printLongLine(str2, "", "\"");*/ /* 18-Jul-2015 nm Deleted */
-      if (g_printString[0]) {
-        bug(256);  /* 18-Jul-2015 nm */
-      }
-      let(&g_printString, str2); /* 18-Jul-2015 nm */
-    } /* if (subType != SYNTAX) */
-    if (subType == THEOREM) {
-      /* 10/25/02 The "referenced by" does not show up after the proof
-         because we moved the typeProof() to below.  Therefore, we save
-         g_printString into a temporary global holding variable to print
-         at the proper place inside of typeProof().  Ugly but necessary
-         with present design. */
-      /* In the case of THEOREM, we save and reset the g_printString.  In the
-         case of != THEOREM (i.e. AXIOM and DEFINITION), g_printString will
-         be printed and cleared below. */
-      let(&g_printStringForReferencedBy, g_printString);
-      let(&g_printString, "");
-    }
-
-    /* Printing of the trailer in mmwtex.c will close out string later */
-    g_outputToString = 0;
-  } /* if (htmlFlg && texFlag) */
-  /* 10/6/99 - End of used-by list for html page */
-
-
-  /* 10/25/02  Moved this to after the block above, so referenced statements
-     show up first for convenience */
-  if (htmlFlg && texFlag) {
-    /*** Output the html proof for $p statements ***/
-    /* Note that we also output the axiom and definition usage
-       lists inside this function */
-    if (g_Statement[showStmt].type == (char)p_) {
-      typeProof(showStmt,
-          0 /*pipFlag*/,
-          0 /*startStep*/,
-          0 /*endStep*/,
-          0 /*endIndent*/,
-          1 /*essentialFlag*/,
-          1 /*renumberFlag*/,
-          0 /*unknownFlag*/,
-          0 /*notUnifiedFlag*/,
-          0 /*reverseFlag*/,
-          1 /*noIndentFlag*/,
-          0 /*splitColumn*/,
-          0 /*skipRepeatedSteps*/,  /* 28-Jun-2013 nm */
-          1 /*texFlag*/,  /* Means either latex or html */
-          1 /*htmlFlg*/);
-    } /* if (g_Statement[showStmt].type == (char)p_) */
-  } /* if (htmlFlg && texFlag) */
-  /* End of html proof for $p statements */
-
-  /* typeProof should have cleared this out */
-  if (g_printStringForReferencedBy[0]) bug(243);
-
- returnPoint:
-  /* Deallocate strings */
-  nmbrLet(&nmbrDDList, NULL_NMBRSTRING);
-  let(&str1, "");
-  let(&str2, "");
-  let(&str3, "");
-  let(&str4, "");
-  let(&str5, "");
-  let(&htmlDistinctVars, "");
-} /* typeStatement */
-
-
-
-/* 13-Aug-2017 nm */
-/* Get the HTML string of dummy variables used by a proof for the
-   theorem's web page.  It should be called only if we're in
-   HTML output mode i.e.  SHOW STATEMENT .../HTML or /ALT_HTML */
-/* This is HARD-CODED FOR SET.MM and will not produce meaningful
-   output for other databases (so far none) with $d's */
-/* Caller must deallocate returned string */
-vstring htmlDummyVars(long showStmt)
-{
-  nmbrString *optDVA; /* Pointer only; not allocated directly */
-  nmbrString *optDVB; /* Pointer only; not allocated directly */
-  long numDVs;
-  nmbrString *optHyp; /* Pointer only; not allocated directly */
-  long numOptHyps;
-  vstring str1 = "";
-  long k, l, n, hypStmt;
-
-  /* Variables used while collecting a statement's dummy variables in $d's */
-  long dummyVarCount; /* # of (different) dummy vars found in $d statements */
-  vstring dummyVarUsed = ""; /* 'Y'/'N' indicators that we found that var */
-  vstring htmlDummyVarList = ""; /* Output HTML string */
-  long dummyVar; /* Current variable in a $d; test if it's a dummy variable */
-
-  /* This function should be called only for web page generation */
-  /*if (!(g_htmlFlag && texFlag)) bug(261);*/  /* texFlag is not global */
-  if (!g_htmlFlag) bug(261);
-
-  if (g_Statement[showStmt].type != p_) bug(262);
-  if (strcmp("|-", g_MathToken[
-            (g_Statement[showStmt].mathString)[0]].tokenName)) {
-    /* Don't process syntax statements */
-    goto RETURN_POINT;
-  }
-
-  optDVA = g_Statement[showStmt].optDisjVarsA;
-  optDVB = g_Statement[showStmt].optDisjVarsB;
-  numDVs = nmbrLen(optDVA);
-  optHyp = g_Statement[showStmt].optHypList;
-  numOptHyps = nmbrLen(optHyp);
-
-  if (numDVs == 0) {  /* Don't create a hint list if no $d's */
-    /*let(&htmlDummyVarList, "(no restrictions)");*/
-    goto RETURN_POINT;
-  }
-
-  dummyVarCount = 0;
-  if (numDVs != 0) {
-
-    /* Update g_WrkProof.proofString with current proof so we can
-       search it later to see if it uses the dummy variable */
-    parseProof(showStmt); /* Prints message if severe error */
-
-    /* Create an array of Y/N indicators that variable is occurs in a
-       $d statement as a dummy variable */
-    let(&dummyVarUsed, string(g_mathTokens, 'N'));
-    for (k = 0; k < numDVs; k++) {
-      for (l = 1; l <= 2; l++) {
-        if (l == 1) {
-          dummyVar = optDVA[k];
-        } else {
-          dummyVar = optDVB[k];
-        }
-        /* At this point, dummyVar is just a var in the $d; we
-           must still check that it is in the optHypList */
-        /* See if potential dummyVar is in optHypList */
-        if (dummyVarUsed[dummyVar] == 'N') {
-          for (n = 0; n < numOptHyps; n++) {
-            /* Check whether dummyVar matches the 2nd token of an
-               optional hypothesis list entry e.g. "x" in "set x" */
-            hypStmt = g_Statement[showStmt].optHypList[n];
-            if (g_Statement[hypStmt].mathString[1] == dummyVar) {
-              /* dummyVar is a dummy variable */
-
-              /* See if it is used by the proof */
-              /* g_WrkProof.proofString was updated by parseProof(showStmt)
-                 above */
-              if (nmbrElementIn(1, g_WrkProof.proofString, hypStmt) == 0) {
-                break; /* It's not used by the proof; stop hyp scan */
-              }
-
-              dummyVarUsed[dummyVar] = 'Y';
-              dummyVarCount++;
-              /* tokenToTex allocates str1; must deallocate it first */
-              let(&str1, "");
-              /* Convert token to htmldef/althtmldef string */
-              str1 = tokenToTex(g_MathToken[dummyVar].tokenName,
-                  showStmt);
-              let(&htmlDummyVarList, cat(htmlDummyVarList, " ", str1, NULL));
-              break; /* Found a match, so stop further checking */
-            }
-          } /* next n, 0 to numOptHyps-1*/
-        } /* if dummy var not used (yet) */
-      } /* next l */
-    } /* next k */
-  } /* if (numDVs != 0) */
-
-  if (dummyVarCount > 0) {
-    let(&htmlDummyVarList, cat(
-        "<CENTER>",
-         "<A HREF=\"",
-
-         /* 26-Aug-2017 nm */
-         /* g_htmlHome is set by htmlhome in $t comment */
-         (instr(1, g_htmlHome, "mmset.html") > 0) ?
-             "mmset.html" :
-             /* The following link will work in the NF and other
-                "Proof Explorers" */
-             "../mpeuni/mmset.html",  /* 19-Aug-2017, 26-Sep-2017 nm */
-
-        "#dvnote1\">Dummy variable",
-        /* Determine whether singular or plural */
-        dummyVarCount > 1 ? "s" : "",
-        "</A> ",  /* 14-Aug-2017 nm */
-        /* 14-Jan-2016 nm Put a span around the variable list to localize
-           the use of the special math font for ALT_HTML */
-        (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
-                                           /* 14-Jan-2016 nm */
-        htmlDummyVarList,
-        (g_altHtmlFlag ? "</SPAN>" : ""),
-        /*
-        dummyVarCount > 1 ? " are assumed to be mutually distinct and"
-            : " is assumed to be",
-        */
-        dummyVarCount > 1 ? " are mutually distinct and"
-            : " is",
-        " distinct from all other variables.",
-        "</CENTER>",
-        NULL));
-  } /* htmlDummyVars */
-
-
- RETURN_POINT:
-  /* Deallocate strings */
-  let(&dummyVarUsed, "");
-  let(&str1, "");
-
-  return htmlDummyVarList;
-} /* htmlDummyVars */
-
-
-
-/* 4-Jan-2014 nm */
-/* Get the HTML string of "allowed substitutions" list for an axiom
-   or theorem's web page.  It should be called only if we're in
-   HTML output mode i.e.  SHOW STATEMENT .../HTML or /ALT_HTML */
-/* This is HARD-CODED FOR SET.MM and will not produce meaningful
-   output for other databases (so far none) with $d's */
-/* Caller must deallocate returned string */
-vstring htmlAllowedSubst(long showStmt)
-{
-  nmbrString *reqHyp; /* Pointer only; not allocated directly */
-  long numReqHyps;
-  nmbrString *reqDVA; /* Pointer only; not allocated directly */
-  nmbrString *reqDVB; /* Pointer only; not allocated directly */
-  long numDVs;
-  nmbrString *setVar = NULL_NMBRSTRING; /* set (individual) variables */
-  char *strptr;
-  vstring str1 = "";
-  long setVars;
-  long wffOrClassVar;
-  vstring setVarDVFlag = "";
-  flag found, first;
-  long i, j, k;
-  vstring htmlAllowedList = "";
-  long countInfo = 0;
-
-  reqDVA = g_Statement[showStmt].reqDisjVarsA;
-  reqDVB = g_Statement[showStmt].reqDisjVarsB;
-  numDVs = nmbrLen(reqDVA);
-
-  reqHyp = g_Statement[showStmt].reqHypList;
-  numReqHyps = nmbrLen(reqHyp);
-
-  /* This function should be called only for web page generation */
-  /*if (!(g_htmlFlag && texFlag)) bug(250);*/  /* texFlag is not global */
-  if (!g_htmlFlag) bug(250);
-
-  if (g_Statement[showStmt].mathStringLen < 1) bug(254);
-  if (strcmp("|-", g_MathToken[
-            (g_Statement[showStmt].mathString)[0]].tokenName)) {
-    /* Don't process syntax statements */
-    goto RETURN_POINT;
-  }
-
-  if (numDVs == 0) {  /* Don't create a hint list if no $d's */
-    /*let(&htmlAllowedList, "(no restrictions)");*/
-    goto RETURN_POINT;
-  }
-
-  /* Collect list of all set variables in the theorem */
-  /* First, count the number of set variables */
-  setVars = 0;
-  for (i = 0; i < numReqHyps; i++) {
-    /* Scan "setvar" variables */
-    if (g_Statement[reqHyp[i]].type == (char)e_) continue;
-    if (g_Statement[reqHyp[i]].type != (char)f_) bug(251);
-    if (g_Statement[reqHyp[i]].mathStringLen != 2)
-      bug(252); /* $f must have 2 tokens */
-    strptr = g_MathToken[
-              (g_Statement[reqHyp[i]].mathString)[0]].tokenName;
-    /* THE FOLLOWING IS SPECIFIC TO set.mm */
-    if (strcmp("setvar", strptr)) continue;
-                                  /* Not a set variable */
-    setVars++;
-  }
-  /* Next, create a list of them in setVar[] */
-  j = 0;
-  nmbrLet(&setVar, nmbrSpace(setVars));
-  for (i = 0; i < numReqHyps; i++) {
-    /* Scan "setvar" variables */
-    if (g_Statement[reqHyp[i]].type == (char)e_) continue;
-    strptr = g_MathToken[
-              (g_Statement[reqHyp[i]].mathString)[0]].tokenName;
-    if (strcmp("setvar", strptr)) continue;
-                                  /* Not a set variable */
-    setVar[j] = (g_Statement[reqHyp[i]].mathString)[1];
-    j++;
-  }
-  if (j != setVars) bug(253);
-
-  /* Scan "wff" and "class" variables for attached $d's */
-  for (i = 0; i < numReqHyps; i++) {
-    /* Look for a "wff" and "class" variable */
-    if (g_Statement[reqHyp[i]].type == (char)e_) continue;
-    strptr = g_MathToken[
-              (g_Statement[reqHyp[i]].mathString)[0]].tokenName;
-    if (strcmp("wff", strptr) && strcmp("class", strptr)) continue;
-                                  /* Not a wff or class variable */
-    wffOrClassVar = (g_Statement[reqHyp[i]].mathString)[1];
-    let(&setVarDVFlag, string(setVars, 'N')); /* No $d yet */
-    /* Scan for attached $d's */
-    for (j = 0; j < numDVs; j++) {
-      found = 0;
-      if (wffOrClassVar == reqDVA[j]) {
-        for (k = 0; k < setVars; k++) {
-          if (setVar[k] == reqDVB[j]) {
-            setVarDVFlag[k] = 'Y';
-            found = 1;
-            break;
-          }
-        }
-      }
-      if (found) continue;
-      /* Repeat with swapped $d arguments */
-      if (wffOrClassVar == reqDVB[j]) {
-        for (k = 0; k < setVars; k++) {
-          if (setVar[k] == reqDVA[j]) {
-            setVarDVFlag[k] = 'Y';
-            break;
-          }
-        }
-      }
-    } /* next $d */
-
-    /* Collect set vars that don't have $d's with this wff or class var */
-    /* First, if there aren't any, then omit this wff or class var */
-    found = 0;
-    for (j = 0; j < setVars; j++) {
-      if (setVarDVFlag[j] == 'N') {
-        found = 1;
-        break;
-      }
-    }
-    if (found == 0) continue; /* All set vars have $d with this wff or class */
-
-    let(&str1, "");
-    str1 = tokenToTex(g_MathToken[wffOrClassVar].tokenName, showStmt);
-         /* tokenToTex allocates str1; we must deallocate it eventually */
-    countInfo++;
-    let(&htmlAllowedList, cat(htmlAllowedList, " &nbsp; ",
-        str1, "(", NULL));
-    first = 1;
-    for (j = 0; j < setVars; j++) {
-      if (setVarDVFlag[j] == 'N') {
-        let(&str1, "");
-        str1 = tokenToTex(g_MathToken[setVar[j]].tokenName, showStmt);
-        let(&htmlAllowedList, cat(htmlAllowedList,
-            (first == 0) ? "," : "", str1, NULL));
-        if (first == 0) countInfo++;
-        first = 0;
-      }
-    }
-    let(&htmlAllowedList, cat(htmlAllowedList, ")", NULL));
-
-  } /* next i (wff or class var) */
-
- RETURN_POINT:
-
-  if (htmlAllowedList[0] != 0) {
-    let(&htmlAllowedList, cat("<CENTER>",
-        "<A HREF=\"",
-
-        /* 26-Aug-2017 nm */
-        /* g_htmlHome is set by htmlhome in $t comment */
-        (instr(1, g_htmlHome, "mmset.html") > 0) ?
-            "mmset.html" :
-            /* The following link will work in the NF and other
-               "Proof Explorers" */
-            "../mpeuni/mmset.html",  /* 19-Aug-2017, 26-Sep-2017 nm */
-
-        "#allowedsubst\">Allowed substitution</A> hint",
-        ((countInfo != 1) ? "s" : ""), ": ",
-        (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
-                                           /* 14-Jan-2016 nm */
-        htmlAllowedList,
-        (g_altHtmlFlag ? "</SPAN>" : ""),    /* 14-Jan-2016 nm */
-        "</CENTER>", NULL));
-  }
-
-  /* Deallocate strings */
-  nmbrLet(&setVar, NULL_NMBRSTRING);
-  let(&str1, "");
-  let(&setVarDVFlag, "");
-
-  return htmlAllowedList;
-} /* htmlAllowedSubst */
-
-
-
-/* Displays a proof (or part of a proof, depending on arguments). */
-/* Note that parseProof() and verifyProof() are assumed to have been called,
-   so that the g_WrkProof structure elements are assigned for the current
-   statement. */
-/* 8/28/00 - this is also used for the MIDI output, since we conveniently
-   have the necessary proof information here.  The function outputMidi()
-   is called from within. */
-void typeProof(long statemNum,
-  flag pipFlag, /* Means use g_ProofInProgress; statemNum must be proveStatement*/
-  long startStep, long endStep,
-  long endIndent,
-  flag essentialFlag, /* <- also used as definition/axiom flag for HTML
-      syntax breakdown when called from typeStatement() */
-  flag renumberFlag,
-  flag unknownFlag,
-  flag notUnifiedFlag,
-  flag reverseFlag,
-  flag noIndentFlag, /* Means Lemmon-style proof */
-  long splitColumn, /* START_COLUMN */
-  flag skipRepeatedSteps, /* NO_REPEATED_STEPS */  /* 28-Jun-2013 nm */
-  flag texFlag,
-  flag htmlFlg /* htmlFlg added 6/27/99 */
-  /* flag g_midiFlag - global to avoid changing many calls to typeProof() */
-  )
-{
-  /* From HELP SHOW PROOF: Optional qualifiers:
-    / ESSENTIAL - the proof tree is trimmed of all $f hypotheses before
-        being displayed.
-    / FROM_STEP <step> - the display starts at the specified step.  If
-        this qualifier is omitted, the display starts at the first step.
-    / TO_STEP <step> - the display ends at the specified step.  If this
-        qualifier is omitted, the display ends at the last step.
-    / TREE_DEPTH <number> - Only steps at less than the specified proof
-        tree depth are displayed.  Useful for obtaining an overview of
-        the proof.
-    / REVERSE - the steps are displayed in reverse order.
-    / RENUMBER - when used with / ESSENTIAL, the steps are renumbered
-        to correspond only to the essential steps.
-    / TEX - the proof is converted to LaTeX and stored in the file opened
-        with OPEN TEX.
-    / HTML - the proof is converted to HTML and stored in the file opened
-        with OPEN HTML.
-    / LEMMON - The proof is displayed in a non-indented format known
-        as Lemmon style, with explicit previous step number references.
-        If this qualifier is omitted, steps are indented in a tree format.
-    / START_COLUMN <number> - Overrides the default column at which
-        the formula display starts in a Lemmon style display.  May be
-        used only in conjuction with / LEMMON.
-    / NO_REPEATED_STEPS - When a proof step is identical to an earlier
-        step, it will not be repeated.  Instead, a reference to it will be
-        changed to a reference to the earlier step.  In particular,
-        SHOW PROOF <label> / LEMMON / RENUMBER / NO_REPEATED_STEPS
-        will have the same proof step numbering as the web page proof
-        generated by SHOW STATEMENT  <label> / HTML, rather than
-        the proof step numbering of the indented format
-        SHOW PROOF <label> / RENUMBER.  This qualifier affects only
-        displays also using the / LEMMON qualifier.
-    / NORMAL - The proof is displayed in normal format suitable for
-        inclusion in a source file.  May not be used with any other
-        qualifier.
-    / COMPRESSED - The proof is displayed in compressed format
-        suitable for inclusion in a source file.  May not be used with
-        any other qualifier.
-    / STATEMENT_SUMMARY - Summarizes all statements (like a brief SHOW
-        STATEMENT) used by the proof.  May not be used with any other
-        qualifier except / ESSENTIAL.
-    / DETAILED_STEP <step> - Shows the details of what is happening at
-        a specific proof step.  May not be used with any other qualifier.
-    / MIDI - 8/28/00 - puts out a midi sound file instead of a proof
-        - determined by the global variable g_midiFlag, not by a parameter to
-        typeProof()
-  */
-  long i, j, plen, step, stmt, lens, lent, maxStepNum;
-  vstring tmpStr = "";
-  vstring tmpStr1 = "";
-  vstring locLabDecl = "";
-  vstring tgtLabel = "";
-  vstring srcLabel = "";
-  vstring startPrefix = "";
-  vstring tgtPrefix = "";
-  vstring srcPrefix = "";
-  vstring userPrefix = "";
-  vstring contPrefix = "";
-  vstring statementUsedFlags = "";
-  vstring startStringWithNum = ""; /* 22-Apr-2015 nm */
-  vstring startStringWithoutNum = ""; /* 22-Apr-2015 nm */
-  nmbrString *proof = NULL_NMBRSTRING;
-  nmbrString *localLabels = NULL_NMBRSTRING;
-  nmbrString *localLabelNames = NULL_NMBRSTRING;
-  nmbrString *indentationLevel = NULL_NMBRSTRING;
-  nmbrString *targetHyps = NULL_NMBRSTRING;
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-  nmbrString *stepRenumber = NULL_NMBRSTRING;
-  nmbrString *notUnifiedFlags = NULL_NMBRSTRING;
-  nmbrString *unprovedList = NULL_NMBRSTRING; /* For traceProofWork() */
-  nmbrString *relativeStepNums = NULL_NMBRSTRING; /* For unknownFlag */
-  long maxLabelLen = 0;
-  long maxStepNumLen = 1;
-  long maxStepNumOffsetLen = 0; /* 22-Apr-2015 nm */
-  char type;
-  flag stepPrintFlag;
-  long fromStep, toStep, byStep;
-  vstring hypStr = "";
-  nmbrString *hypPtr;
-  long hyp, hypStep;
-
-  /* For statement syntax breakdown (see section below added 2/5/02 for better
-     syntax hints), we declare the following 3 variables. */
-  static long wffToken = -1; /* array index of the hard-coded token "wff" -
-      static so we only have to look it up once - set to -2 if not found */
-  nmbrString *nmbrTmpPtr1; /* Pointer only; not allocated directly */
-  nmbrString *nmbrTmpPtr2; /* Pointer only; not allocated directly */
-
-  /* 31-Jan-2010 nm */
-  /* skipRepeatedSteps = 0; */ /* 28-Jun-2010 nm Now set by parameter */
-  if (htmlFlg && texFlag) skipRepeatedSteps = 1; /* Keep old behavior */
-  /* Comment out the following line if you want to revert to the old
-     Lemmon-style behavior with local label references for reused compressed
-     proof steps is desired.  The only reason for doing this is to obtain
-     the same steps and step numbers as the indented proof, rather than
-     those on the HTML pages. */
-  /* if (noIndentFlag) skipRepeatedSteps = 1; */
-           /* 28-Jun-2013 nm Now set by parameter */
-
-  if (htmlFlg && texFlag) {
-
-
-
-
-    g_outputToString = 1; /* Flag for print2 to add to g_printString */
-    if (essentialFlag) {
-
-      /* 26-Aug-2017 nm */
-      /* See if there are dummy variables.  If so, print them below
-         "Proof of Theorem", which means we have to make the "Proof of
-         Theorem" line separate and not the table caption, so that the
-         "Distinct variables..." line does not become part of the table. */
-      let(&tmpStr, "");
-      tmpStr = htmlDummyVars(statemNum);
-      if (tmpStr[0] != 0) {
-        print2("<CENTER><B>Proof of Theorem <FONT\n");
-        printLongLine(cat("   COLOR=", GREEN_TITLE_COLOR, ">",
-            asciiToTt(g_Statement[statemNum].labelName),
-            "</FONT></B></CENTER>", NULL), "", "\"");
-        /* Print the list of dummy variables */
-        printLongLine(tmpStr, "", "\"");
-        let(&tmpStr, "");
-        print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
-            MINT_BACKGROUND_COLOR);
-        print2("SUMMARY=\"Proof of theorem\">\n");
-      } else {
-      /* End of 26-Aug-2017 */
-
-        /* For bobby.cast.org approval */
-        print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
-            MINT_BACKGROUND_COLOR);
-        print2("SUMMARY=\"Proof of theorem\">\n");
-        print2("<CAPTION><B>Proof of Theorem <FONT\n");
-        printLongLine(cat("   COLOR=", GREEN_TITLE_COLOR, ">",
-            asciiToTt(g_Statement[statemNum].labelName),
-            "</FONT></B></CAPTION>", NULL), "", "\"");
-      }
-    } else {
-      /* This is a syntax breakdown "proof" of a definition called
-         from typeStatement */
-      print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
-          MINT_BACKGROUND_COLOR);
-      if (!strcmp("ax-", left(g_Statement[g_showStatement].labelName, 3))) {
-        /* For bobby.cast.org approval */
-        print2("SUMMARY=\"Detailed syntax breakdown of axiom\">\n");
-        print2("<CAPTION><B>Detailed syntax breakdown of Axiom <FONT\n");
-      } else {
-        /* For bobby.cast.org approval */
-        print2("SUMMARY=\"Detailed syntax breakdown of definition\">\n");
-        print2("<CAPTION><B>Detailed syntax breakdown of Definition <FONT\n");
-      }
-      printLongLine(cat("   COLOR=", GREEN_TITLE_COLOR, ">",
-          asciiToTt(g_Statement[statemNum].labelName),
-          "</FONT></B></CAPTION>", NULL), "", "\"");
-    }
-    print2(
-        "<TR><TH>Step</TH><TH>Hyp</TH><TH>Ref\n");
-    print2("</TH><TH>Expression</TH></TR>\n");
-    g_outputToString = 0;
-    /* printTexLongMath in typeProof will do this
-    fprintf(g_texFilePtr, "%s", g_printString);
-    let(&g_printString, "");
-    */
-  }
-
-  if (!pipFlag) {
-    parseProof(g_showStatement);
-    if (g_WrkProof.errorSeverity > 1) {
-      /* 2-Nov-2014 nm Prevent population of g_printString outside of web
-         page generation to fix bug 1114 (reported by Sefan O'Rear). */
-      if (htmlFlg && texFlag) {
-        /* Print warning and close out proof table */
-        g_outputToString = 1;
-        print2(
-      "<TD COLSPAN=4><B><FONT COLOR=RED>WARNING: Proof has a severe error.\n");
-        print2("</FONT></B></TD></TR>\n");
-        g_outputToString = 0;
-        /* 18-Nov-2012 nm Fix bug 243 */
-        /* Clear out g_printStringForReferencedBy to prevent bug 243 above */
-        let(&g_printStringForReferencedBy, "");
-      }
-      return; /* verifyProof() could crash */
-    }
-    verifyProof(g_showStatement);
-  }
-
-  if (!pipFlag) {
-    nmbrLet(&proof, g_WrkProof.proofString); /* The proof */
-    if (g_midiFlag) { /* 8/28/00 */
-      /* Get the uncompressed version of the proof */
-      nmbrLet(&proof, nmbrUnsquishProof(proof));
-    }
-  } else {
-    nmbrLet(&proof, g_ProofInProgress.proof); /* The proof */
-  }
-  plen = nmbrLen(proof);
-
-  /* 6/27/99 - to reduce the number of steps displayed in an html proof,
-     we will use a local label to reference the 2nd or later reference to a
-     hypothesis, so the hypothesis won't have to be shown multiple times
-     in the proof.
-     31-Jan-2010 - do this for all Lemmon-style proofs */
-  if (htmlFlg && texFlag && !noIndentFlag /* Lemmon */) {
-    /* Only Lemmon-style proofs are implemented for html */
-    bug(218);
-  }
-  /*if (htmlFlg && texFlag) {*/   /* pre-31-Jan-2010 */
-  /* 31-Jan-2010 nm Do this for all Lemmon-style proofs */
-  if (skipRepeatedSteps) {
-    for (step = 0; step < plen; step++) {
-      stmt = proof[step];
-      if (stmt < 0) continue;  /* Unknown or label ref */
-      type = g_Statement[stmt].type;
-      if (type == f_ || type == e_  /* It's a hypothesis */
-          || g_Statement[stmt].numReqHyp == 0) { /* A statement w/ no hyp */
-        for (i = 0; i < step; i++) {
-          if (stmt == proof[i]) {
-            /* The hypothesis at 'step' matches an earlier hypothesis at i,
-               so we will backreference 'step' to i with a local label */
-            proof[step] = -1000 - i;
-            break;
-          }
-        } /* next i */
-      }
-    } /* next step */
-  }
-
-
-  /* Collect local labels */
-  for (step = 0; step < plen; step++) {
-    stmt = proof[step];
-    if (stmt <= -1000) {
-      stmt = -1000 - stmt;
-      if (!nmbrElementIn(1, localLabels, stmt)) {
-        nmbrLet(&localLabels, nmbrAddElement(localLabels, stmt));
-      }
-    }
-  }
-
-  /* localLabelNames[] hold an integer which, when converted to string,
-    is the local label name. */
-  nmbrLet(&localLabelNames, nmbrSpace(plen));
-
-  /* Get the indentation level */
-  nmbrLet(&indentationLevel, nmbrGetIndentation(proof, 0));
-
-  /* Get the target hypotheses */
-  nmbrLet(&targetHyps, nmbrGetTargetHyp(proof, statemNum));
-
-  /* Get the essential step flags, if required */
-  if (essentialFlag || g_midiFlag) {
-    nmbrLet(&essentialFlags, nmbrGetEssential(proof));
-  } else {
-    nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-  }
-
-  /* 8/28/00 We now have enough information for the MIDI output, so
-     do it */
-  if (g_midiFlag) {
-    outputMidi(plen, indentationLevel,
-        essentialFlags, g_midiParam, g_Statement[statemNum].labelName);
-    goto typeProof_return;
-  }
-
-  /* Get the step renumbering */
-  nmbrLet(&stepRenumber, nmbrSpace(plen)); /* This initializes all step
-      renumbering to step 0.  Later, we will use (for html) the fact that
-      a step renumbered to 0 is a step to be skipped (6/27/99). */
-  i = 0;
-  maxStepNum = 0;
-  for (step = 0; step < plen; step++) {
-    stepPrintFlag = 1; /* Note: stepPrintFlag is reused below with a
-        slightly different meaning (i.e. it will be printed after
-        a filter such as notUnified is applied) */
-    if (renumberFlag && essentialFlag) {
-      if (!essentialFlags[step]) stepPrintFlag = 0;
-    }
-    /* if (htmlFlg && texFlag && proof[step] < 0) stepPrintFlag = 0; */
-    /* 31-Jan-2010 nm changed to: */
-    if (skipRepeatedSteps && proof[step] < 0) stepPrintFlag = 0;
-    /* For standard numbering, stepPrintFlag will be always be 1 here */
-    if (stepPrintFlag) {
-      i++;
-      stepRenumber[step] = i; /* Numbering for step to be printed */
-      maxStepNum = i; /* To compute maxStepNumLen below */
-    }
-  }
-
-  /* 22-Apr-2015 nm */
-  /* Get the relative offset (0, -1, -2,...) for unknown steps */
-  if (unknownFlag) {
-    /* 21-Aug-2017 nm There could be unknown steps outside of MM-PA
-       So remove this bugcheck, which seems spurious.  I can't see that
-       getRelStepNums() cares whether we are in MM-PA. */
-    /*
-    if (!pipFlag) {
-      bug(255);
-    }
-    */
-    relativeStepNums = getRelStepNums(g_ProofInProgress.proof);
-  }
-
-  /* Get steps not unified (pipFlag only) */
-  if (notUnifiedFlag) {
-    if (!pipFlag) bug(205);
-    nmbrLet(&notUnifiedFlags, nmbrSpace(plen));
-    for (step = 0; step < plen; step++) {
-      notUnifiedFlags[step] = 0;
-      if (nmbrLen(g_ProofInProgress.source[step])) {
-        if (!nmbrEq(g_ProofInProgress.target[step],
-            g_ProofInProgress.source[step])) notUnifiedFlags[step] = 1;
-      }
-      if (nmbrLen(g_ProofInProgress.user[step])) {
-        if (!nmbrEq(g_ProofInProgress.target[step],
-            g_ProofInProgress.user[step])) notUnifiedFlags[step] = 1;
-      }
-    }
-  }
-
-  /* Get the printed character length of the largest step number */
-  i = maxStepNum;
-  while (i >= 10) {
-    i = i/10; /* The number is printed in base 10 */
-    maxStepNumLen++;
-  }
-  /* 22-Apr-2015 nm */
-  /* Add extra space for negative offset numbers e.g. "3:-1" */
-  if (unknownFlag) {
-    maxStepNumOffsetLen = 3; /* :, -, # */
-    j = 0;
-    for (i = 0; i < plen; i++) {
-      j = relativeStepNums[i];
-      if (j <= 0) break; /* Found first unknown step (largest offset) */
-    }
-    while (j <= -10) {
-      j = j/10; /* The number is printed in base 10 */
-      maxStepNumOffsetLen++;
-    }
-  }
-
-
-
-  /* Get local labels and maximum label length */
-  /* lent = target length, lens = source length */
-  for (step = 0; step < plen; step++) {
-    lent = (long)strlen(g_Statement[targetHyps[step]].labelName);
-    stmt = proof[step];
-    if (stmt < 0) {
-      if (stmt <= -1000) {
-        stmt = -1000 - stmt;
-        /* stmt is now the step number a local label refers to */
-        lens = (long)strlen(str((double)(localLabelNames[stmt])));
-        let(&tmpStr1, ""); /* Clear temp alloc stack for str function */
-      } else {
-        if (stmt != -(long)'?') bug (219); /* the only other possibility */
-        lens = 1; /* '?' (unknown step) */
-      }
-    } else {
-      if (nmbrElementIn(1, localLabels, step)) {
-
-        /* 6/27/99 The new philosophy is to number all local labels with the
-           actual step number referenced, for better readability.  This means
-           that if a *.mm label is a pure number, there may be ambiguity in
-           the proof display, but this is felt to be too rare to be a serious
-           drawback. */
-        localLabelNames[step] = stepRenumber[step];
-
-      }
-      lens = (long)strlen(g_Statement[stmt].labelName);
-    }
-    /* Find longest label assignment, excluding local label declaration */
-    if (maxLabelLen < lent + 1 + lens) {
-      maxLabelLen = lent + 1 + lens; /* Target, =, source */
-    }
-  } /* Next step */
-
-  /* Print the steps */
-  if (reverseFlag
-      && !g_midiFlag /* 8/28/00 */
-      ) {
-    fromStep = plen - 1;
-    toStep = -1;
-    byStep = -1;
-  } else {
-    fromStep = 0;
-    toStep = plen;
-    byStep = 1;
-  }
-  for (step = fromStep; step != toStep; step = step + byStep) {
-
-    /* Filters to decide whether to print the step */
-    stepPrintFlag = 1;
-    if (startStep > 0) { /* The user's FROM_STEP */
-      if (step + 1 < startStep) stepPrintFlag = 0;
-    }
-    if (endStep > 0) { /* The user's TO_STEP */
-      if (step + 1 > endStep) stepPrintFlag = 0;
-    }
-    if (endIndent > 0) { /* The user's INDENTATION_DEPTH */
-      if (indentationLevel[step] + 1 > endIndent) stepPrintFlag = 0;
-    }
-    if (essentialFlag) {
-      if (!essentialFlags[step]) stepPrintFlag = 0;
-    }
-    if (notUnifiedFlag) {
-      if (!notUnifiedFlags[step]) stepPrintFlag = 0;
-    }
-    if (unknownFlag) {
-      if (proof[step] != -(long)'?') stepPrintFlag = 0;
-    }
-
-    /* 6/27/99 Skip steps that are local label references for html */
-    /* if (htmlFlg && texFlag) { */
-    /* 31-Jan-2010 nm changed to: */
-    if (skipRepeatedSteps) {
-      if (stepRenumber[step] == 0) stepPrintFlag = 0;
-    }
-
-    /* 8/28/00 For MIDI files, ignore all qualifiers and process all steps */
-    if (g_midiFlag) stepPrintFlag = 1;
-
-    if (!stepPrintFlag) continue;
-
-    if (noIndentFlag) {
-      let(&tgtLabel, "");
-    } else {
-      let(&tgtLabel, g_Statement[targetHyps[step]].labelName);
-    }
-    let(&locLabDecl, ""); /* Local label declaration */
-    stmt = proof[step];
-    if (stmt < 0) {
-      if (stmt <= -1000) {
-        stmt = -1000 - stmt;
-        /* if (htmlFlg && texFlag) bug(220); */
-        /* 31-Jan-2010 nm Changed to: */
-        if (skipRepeatedSteps) bug(220); /* If html, a step referencing a
-            local label will never be printed since it will be skipped above */
-        /* stmt is now the step number a local label refers to */
-        if (noIndentFlag) {
-          let(&srcLabel, cat("@", str((double)(localLabelNames[stmt])), NULL));
-        } else {
-          let(&srcLabel, cat("=", str((double)(localLabelNames[stmt])), NULL));
-        }
-        type = g_Statement[proof[stmt]].type;
-      } else {
-        if (stmt != -(long)'?') bug(206);
-        if (noIndentFlag) {
-          let(&srcLabel, chr(-stmt)); /* '?' */
-        } else {
-          let(&srcLabel, cat("=", chr(-stmt), NULL)); /* '?' */
-        }
-        type = '?';
-      }
-    } else {
-      if (nmbrElementIn(1, localLabels, step)) {
-        /* This statement declares a local label */
-        if (noIndentFlag) {
-          /* if (!(htmlFlg && texFlag)) { */
-          /* 31-Jan-2010 nm Changed to: */
-          if (!(skipRepeatedSteps)) { /* No local label declaration is
-              shown for html */
-            let(&locLabDecl, cat("@", str((double)(localLabelNames[step])), ":", NULL));
-          }
-        } else {
-          let(&locLabDecl, cat(str((double)(localLabelNames[step])), ":", NULL));
-        }
-      }
-
-      if (noIndentFlag) {
-        let(&srcLabel, g_Statement[stmt].labelName);
-
-        /* For non-indented mode, add step numbers of hypotheses after label */
-        let(&hypStr, "");
-        hypStep = step - 1;
-        hypPtr = g_Statement[stmt].reqHypList;
-        for (hyp = g_Statement[stmt].numReqHyp - 1; hyp >=0; hyp--) {
-          if (!essentialFlag || g_Statement[hypPtr[hyp]].type == (char)e_) {
-            i = stepRenumber[hypStep];
-            if (i == 0) {
-              /* if (!(htmlFlg && texFlag)) bug(221); */
-              /* 31-Jan-2010 nm Changed to: */
-              if (!(skipRepeatedSteps)) bug(221);
-              if (proof[hypStep] != -(long)'?') {
-                if (proof[hypStep] > -1000) bug(222);
-                if (localLabelNames[-1000 - proof[hypStep]] == 0) bug(223);
-                if (localLabelNames[-1000 - proof[hypStep]] !=
-                    stepRenumber[-1000 - proof[hypStep]]) bug(224);
-                /* Get the step number the hypothesis refers to */
-                i = stepRenumber[-1000 - proof[hypStep]];
-              } else {
-                /* The hypothesis refers to an unknown step - use i as flag */
-                i = -(long)'?';
-              }
-            }
-            if (!hypStr[0]) {
-              if (i != -(long)'?') {
-                let(&hypStr, str((double)i));
-              } else {
-                let(&hypStr, "?");
-              }
-            } else {
-              /* Put comma between more than one hypothesis reference */
-              if (i != -(long)'?') {
-                let(&hypStr, cat(str((double)i), ",", hypStr, NULL));
-              } else {
-                let(&hypStr, cat("?", ",", hypStr, NULL));
-              }
-            }
-          }
-          if (hyp < g_Statement[stmt].numReqHyp) {
-            /* Move down to previous hypothesis */
-            hypStep = hypStep - subproofLen(proof, hypStep);
-          }
-        } /* Next hyp */
-
-        if (hypStr[0]) {
-          /* Add hypothesis list after label */
-          let(&srcLabel, cat(hypStr, " ", srcLabel, NULL));
-        }
-
-      } else {
-        let(&srcLabel, cat("=", g_Statement[stmt].labelName, NULL));
-      }
-      type = g_Statement[stmt].type;
-    }
-
-
-#define PF_INDENT_INC 2
-    /* Print the proof line */
-    if (stepPrintFlag) {
-
-      if (noIndentFlag) {
-        let(&startPrefix, cat(
-            space(maxStepNumLen - (long)strlen(str((double)(stepRenumber[step])))),
-            str((double)(stepRenumber[step])),
-            " ",
-            srcLabel,
-            space(splitColumn - (long)strlen(srcLabel) - (long)strlen(locLabDecl) - 1
-                - maxStepNumLen - 1),
-            " ", locLabDecl,
-            NULL));
-        if (pipFlag) {
-          let(&tgtPrefix, startPrefix);
-          let(&srcPrefix, cat(
-              space(maxStepNumLen - (long)strlen(str((double)(stepRenumber[step])))),
-              space((long)strlen(str((double)(stepRenumber[step])))),
-              " ",
-              space(splitColumn - 1
-                  - maxStepNumLen),
-              NULL));
-          let(&userPrefix, cat(
-              space(maxStepNumLen - (long)strlen(str((double)(stepRenumber[step])))),
-              space((long)strlen(str((double)(stepRenumber[step])))),
-              " ",
-              "(User)",
-              space(splitColumn - (long)strlen("(User)") - 1
-                  - maxStepNumLen),
-              NULL));
-        }
-        let(&contPrefix, space((long)strlen(startPrefix) + 4));
-      } else {  /* not noIndentFlag */
-
-        /* 22-Apr-2015 nm */
-        /* Compute prefix with and without step number.  For 'show new_proof
-           /unknown', unknownFlag is set, and we add the negative offset. */
-        let(&tmpStr, "");
-        if (unknownFlag) {
-          if (relativeStepNums[step] < 0) {
-            let(&tmpStr, cat(" ", str((double)(relativeStepNums[step])), NULL));
-          }
-          let(&tmpStr, cat(tmpStr, space(maxStepNumOffsetLen
-              - (long)(strlen(tmpStr))), NULL));
-        }
-
-        let(&startStringWithNum, cat(
-            space(maxStepNumLen - (long)strlen(str((double)(stepRenumber[step])))),
-            str((double)(stepRenumber[step])),
-            tmpStr,
-            " ", NULL));
-        let(&startStringWithoutNum, space(maxStepNumLen + 1));
-
-
-        let(&startPrefix, cat(
-            startStringWithNum,
-            space(indentationLevel[step] * PF_INDENT_INC
-                - (long)strlen(locLabDecl)),
-            locLabDecl,
-            tgtLabel,
-            srcLabel,
-            space(maxLabelLen - (long)strlen(tgtLabel)
-                - (long)strlen(srcLabel)),
-            NULL));
-        if (pipFlag) {
-          let(&tgtPrefix, cat(
-              startStringWithNum,
-              space(indentationLevel[step] * PF_INDENT_INC - (long)strlen(locLabDecl)),
-              locLabDecl,
-              tgtLabel,
-              space((long)strlen(srcLabel)),
-              space(maxLabelLen - (long)strlen(tgtLabel) - (long)strlen(srcLabel)),
-              NULL));
-          let(&srcPrefix, cat(
-              startStringWithoutNum,
-              space(indentationLevel[step] * PF_INDENT_INC - (long)strlen(locLabDecl)),
-              space((long)strlen(locLabDecl)),
-              space((long)strlen(tgtLabel)),
-              srcLabel,
-              space(maxLabelLen - (long)strlen(tgtLabel) - (long)strlen(srcLabel)),
-              NULL));
-          let(&userPrefix, cat(
-              startStringWithoutNum,
-              space(indentationLevel[step] * PF_INDENT_INC - (long)strlen(locLabDecl)),
-              space((long)strlen(locLabDecl)),
-              space((long)strlen(tgtLabel)),
-              "=(User)",
-              space(maxLabelLen - (long)strlen(tgtLabel) - (long)strlen("=(User)")),
-              NULL));
-        }
-        /*
-        let(&contPrefix, space(maxStepNumLen + 1 + indentationLevel[step] *
-            PF_INDENT_INC
-            + maxLabelLen + 4));
-        */
-        let(&contPrefix, ""); /* Continuation lines use whole screen width */
-      }
-
-      if (!pipFlag) {
-
-        if (!texFlag) {
-          if (!g_midiFlag) { /* 8/28/00 */
-            printLongLine(cat(startPrefix," $", chr(type), " ",
-                nmbrCvtMToVString(g_WrkProof.mathStringPtrs[step]),
-                NULL),
-                contPrefix,
-                chr(1));
-                /* chr(1) is right-justify flag for printLongLine */
-          }
-        } else {  /* TeX or HTML */
-          printTexLongMath(g_WrkProof.mathStringPtrs[step],
-              cat(startPrefix, " $", chr(type), " ", NULL),
-              contPrefix, stmt, indentationLevel[step]);
-        }
-
-      } else { /* pipFlag */
-        if (texFlag) {
-          /* nm 3-Feb-04  Added this bug check - it doesn't make sense to
-             do this and it hasn't been tested anyway */
-          print2("?Unsupported:  HTML or LaTeX proof for NEW_PROOF.\n");
-          bug(244);
-        }
-
-        if (!nmbrEq(g_ProofInProgress.target[step], g_ProofInProgress.source[step])
-            && nmbrLen(g_ProofInProgress.source[step])) {
-
-          if (!texFlag) {
-            printLongLine(cat(tgtPrefix, " $", chr(type), " ",
-                nmbrCvtMToVString(g_ProofInProgress.target[step]),
-                NULL),
-                contPrefix,
-                chr(1)); /* chr(1) is right-justify flag for printLongLine */
-            printLongLine(cat(srcPrefix,"  = ",
-                nmbrCvtMToVString(g_ProofInProgress.source[step]),
-                NULL),
-                contPrefix,
-                chr(1)); /* chr(1) is right-justify flag for printLongLine */
-          } else { /* TeX or HTML */
-            printTexLongMath(g_ProofInProgress.target[step],
-                cat(tgtPrefix, " $", chr(type), " ", NULL),
-                contPrefix, 0, 0);
-            printTexLongMath(g_ProofInProgress.source[step],
-                cat(srcPrefix, "  = ", NULL),
-                contPrefix, 0, 0);
-          }
-        } else {
-          if (!texFlag) {
-            printLongLine(cat(startPrefix, " $", chr(type), " ",
-                nmbrCvtMToVString(g_ProofInProgress.target[step]),
-                NULL),
-                contPrefix,
-                chr(1)); /* chr(1) is right-justify flag for printLongLine */
-          } else {  /* TeX or HTML */
-            printTexLongMath(g_ProofInProgress.target[step],
-                cat(startPrefix, " $", chr(type), " ", NULL),
-                contPrefix, 0, 0);
-          }
-
-        }
-        if (nmbrLen(g_ProofInProgress.user[step])) {
-
-          if (!texFlag) {
-            printLongLine(cat(userPrefix, "  = ",
-                nmbrCvtMToVString(g_ProofInProgress.user[step]),
-                NULL),
-                contPrefix,
-                chr(1)); /* chr(1) is right-justify flag for printLongLine */
-          } else {
-            printTexLongMath(g_ProofInProgress.user[step],
-                cat(userPrefix, "  = ", NULL),
-                contPrefix, 0, 0);
-          }
-
-        }
-      }
-    }
-
-
-  } /* Next step */
-
-  if (!pipFlag) {
-    cleanWrkProof(); /* Deallocate verifyProof storage */
-  }
-
-  if (htmlFlg && texFlag) {
-    g_outputToString = 1;
-    print2("</TABLE></CENTER>\n");
-
-    /* 10/10/02 Moved here from mmwtex.c */
-    /*print2("<FONT SIZE=-1 FACE=sans-serif>Colors of variables:\n");*/
-    printLongLine(cat(
-        "<CENTER><TABLE CELLSPACING=5><TR><TD ALIGN=LEFT><FONT SIZE=-1>",
-        "<B>Colors of variables:</B> ",
-        g_htmlVarColor, "</FONT></TD></TR>",
-        NULL), "", "\"");
-
-    if (essentialFlag) {  /* Means this is not a syntax breakdown of a
-        definition which is called from typeStatement() */
-
-      /* Create list of syntax statements used */
-      let(&statementUsedFlags, string(g_statements + 1, 'N')); /* Init. to 'no' */
-      for (step = 0; step < plen; step++) {
-        stmt = proof[step];
-        /* Convention: collect all $a's that don't begin with "|-" */
-        if (stmt > 0) {
-          if (g_Statement[stmt].type == a_) {
-            if (strcmp("|-", g_MathToken[
-                (g_Statement[stmt].mathString)[0]].tokenName)) {
-              statementUsedFlags[stmt] = 'Y'; /* Flag to use it */
-            }
-          }
-        }
-      }
-
-      /******************************************************************/
-      /* Start of section added 2/5/02 - for a more complete syntax hints
-         list in the HTML pages, parse the wffs comprising the hypotheses
-         and the statement, and add their syntax to the hints list. */
-
-      /* Look up the token "wff" (hard-coded) if we haven't found it before */
-      if (wffToken == -1) { /* First time */
-        wffToken = -2; /* In case it's not found because the user's source
-            used a convention different for "wff" for wffs */
-        for (i = 0; i < g_mathTokens; i++) {
-          if (!strcmp("wff", g_MathToken[i].tokenName)) {
-            wffToken = i;
-            break;
-          }
-        }
-      }
-
-      if (wffToken >= 0) {
-
-        /* Scan the statement being proved and its essential hypotheses,
-           and find a proof for each of them expressed as a wff */
-        for (i = -1; i < g_Statement[statemNum].numReqHyp; i++) {
-          /* i = -1 is the statement itself; i >= 0 is hypotheses i */
-          if (i == -1) {
-            /* If it's not a $p we shouldn't be here */
-            if (g_Statement[statemNum].type != (char)p_) bug(245);
-            nmbrTmpPtr1 = NULL_NMBRSTRING;
-            nmbrLet(&nmbrTmpPtr1, g_Statement[statemNum].mathString);
-          } else {
-            /* Ignore $f */
-            if (g_Statement[g_Statement[statemNum].reqHypList[i]].type
-                == (char)f_) continue;
-            /* Must therefore be a $e */
-            if (g_Statement[g_Statement[statemNum].reqHypList[i]].type
-                != (char)e_) bug(234);
-            nmbrTmpPtr1 = NULL_NMBRSTRING;
-            nmbrLet(&nmbrTmpPtr1,
-                g_Statement[g_Statement[statemNum].reqHypList[i]].mathString);
-          }
-          if (strcmp("|-", g_MathToken[nmbrTmpPtr1[0]].tokenName)) {
-            /* 1-Oct-05 nm Since non-standard logics may not have this,
-               just break out of this section gracefully */
-            nmbrTmpPtr2 = NULL_NMBRSTRING; /* To be known after break */
-            break;
-            /* bug(235); */  /* 1-Oct-05 nm No longer a bug */
-          }
-          /* Turn "|-" assertion into a "wff" assertion */
-          nmbrTmpPtr1[0] = wffToken;
-
-          /* Find proof of formula or simple theorem (no new vars in $e's) */
-          /* maxEDepth is the maximum depth at which statements with $e
-             hypotheses are
-             considered.  A value of 0 means none are considered. */
-          nmbrTmpPtr2 = proveFloating(nmbrTmpPtr1 /*mString*/,
-              statemNum /*statemNum*/, 0 /*maxEDepth*/,
-              0, /* step; 0 = step 1 */ /*For messages*/
-              0,  /*not noDistinct*/
-              /* 3-May-2016 nm */
-              2, /* override discouraged-usage statements silently */
-              1 /* Always allow other mathboxes */ /* 5-Aug-2020 nm */
-              );
-          if (!nmbrLen(nmbrTmpPtr2)) {
-            /* 1-Oct-05 nm Since a proof may not be found for non-standard
-               logics, just break out of this section gracefully */
-            break;
-            /* bug(236); */ /* Didn't find syntax proof */
-          }
-
-          /* Add to list of syntax statements used */
-          for (step = 0; step < nmbrLen(nmbrTmpPtr2); step++) {
-            stmt = nmbrTmpPtr2[step];
-            /* Convention: collect all $a's that don't begin with "|-" */
-            if (stmt > 0) {
-              if (statementUsedFlags[stmt] == 'N') { /* For slight speedup */
-                if (g_Statement[stmt].type == a_) {
-                  if (strcmp("|-", g_MathToken[
-                      (g_Statement[stmt].mathString)[0]].tokenName)) {
-                    statementUsedFlags[stmt] = 'Y'; /* Flag to use it */
-                  } else {
-                    /* In a syntax proof there should be no |- */
-                    /* (In the future, we may want to break instead of
-                       calling it a bug, if it's a problem for non-standard
-                       logics.) */
-                    bug(237);
-                  }
-                }
-              }
-            } else {
-              /* proveFloating never returns a compressed proof */
-              bug(238);
-            }
-          }
-
-          /* Deallocate memory */
-          nmbrLet(&nmbrTmpPtr2, NULL_NMBRSTRING);
-          nmbrLet(&nmbrTmpPtr1, NULL_NMBRSTRING);
-        } /* next i */
-        /* 1-Oct-05 nm Deallocate memory in case we broke out above */
-        nmbrLet(&nmbrTmpPtr2, NULL_NMBRSTRING);
-        nmbrLet(&nmbrTmpPtr1, NULL_NMBRSTRING);
-      } /* if (wffToken >= 0) */
-      /* End of section added 2/5/02 */
-      /******************************************************************/
-
-      let(&tmpStr, "");
-      for (stmt = 1; stmt <= g_statements; stmt++) {
-        if (statementUsedFlags[stmt] == 'Y') {
-          if (!tmpStr[0]) {
-            let(&tmpStr,
-               /* 10/10/02 */
-               /*"<FONT SIZE=-1><FONT FACE=sans-serif>Syntax hints:</FONT> ");*/
-               "<TR><TD ALIGN=LEFT><FONT SIZE=-1><B>Syntax hints:</B> ");
-          }
-
-          /* 10/6/99 - Get the main symbol in the syntax */
-          /* This section can be deleted if not wanted - it is custom
-             for set.mm and might not work with other .mm's */
-          let(&tmpStr1, "");
-          for (i = 1 /* Skip |- */; i < g_Statement[stmt].mathStringLen; i++) {
-            if (g_MathToken[(g_Statement[stmt].mathString)[i]].tokenType ==
-                (char)con_) {
-              /* Skip parentheses, commas, etc. */
-              if (strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
-                      ].tokenName, "(")
-                  && strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
-                      ].tokenName, ",")
-                  && strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
-                      ].tokenName, ")")
-                  && strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
-                      ].tokenName, ":")
-                  /* 14-Oct-2019 nm Use |-> rather than e. for cmpt, cmpt2 */
-                  && !(!strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
-                      ].tokenName, "e.")
-                      && (!strcmp(g_Statement[stmt].labelName, "cmpt")
-                          || !strcmp(g_Statement[stmt].labelName, "cmpt2")))
-                  ) {
-                tmpStr1 =
-                    tokenToTex(g_MathToken[(g_Statement[stmt].mathString)[i]
-                    ].tokenName, stmt);
-                /* 14-Jan-2016 nm */
-                let(&tmpStr1, cat(
-                    (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
-                                           /* 14-Jan-2016 nm */
-                    tmpStr1,
-                    (g_altHtmlFlag ? "</SPAN>" : ""),
-                    NULL));
-                break;
-              }
-            }
-          } /* Next i */
-          /* Special cases hard-coded for set.mm */
-          if (!strcmp(g_Statement[stmt].labelName, "wbr")) /* binary relation */
-            let(&tmpStr1, "<i> class class class </i>");
-          if (!strcmp(g_Statement[stmt].labelName, "cv"))
-            let(&tmpStr1, "[set variable]");
-          /* 10/10/02 Let's don't do cv - confusing to reader */
-          if (!strcmp(g_Statement[stmt].labelName, "cv"))
-            continue;
-          if (!strcmp(g_Statement[stmt].labelName, "co")) /* operation */
-            let(&tmpStr1, "(<i>class class class</i>)");
-          let(&tmpStr, cat(tmpStr, " &nbsp;", tmpStr1, NULL));
-          /* End of 10/6/99 section - Get the main symbol in the syntax */
-
-          let(&tmpStr1, "");
-          tmpStr1 = pinkHTML(stmt);
-          /******* 10/10/02
-          let(&tmpStr, cat(tmpStr, "<FONT FACE=sans-serif><A HREF=\"",
-              g_Statement[stmt].labelName, ".html\">",
-              g_Statement[stmt].labelName, "</A></FONT> &nbsp; ", NULL));
-          *******/
-          let(&tmpStr, cat(tmpStr, "<A HREF=\"",
-              g_Statement[stmt].labelName, ".html\">",
-              g_Statement[stmt].labelName, "</A>", tmpStr1, NULL));
-
-
-        }
-      }
-      if (tmpStr[0]) {
-        let(&tmpStr, cat(tmpStr,
-            "</FONT></TD></TR>", NULL));
-        printLongLine(tmpStr, "", "\"");
-      }
-      /* End of syntax hints list */
-
-
-      /* 10/25/02 Output "referenced by" list here */
-      /* 30-Oct-2018 nm Moved this block to after axioms/defs lists */
-      /*
-      if (g_printStringForReferencedBy[0]) {
-        /@ printLongLine takes 130 sec for 'sh st syl/a' @/
-        /@printLongLine(g_printStringForReferencedBy, "", "\"");@/
-        /@ 18-Jul-2015 nm Speedup for 'sh st syl/a' @/
-        if (g_outputToString != 1) bug(257);
-        let(&g_printString, cat(g_printString, g_printStringForReferencedBy, NULL));
-        let(&g_printStringForReferencedBy, "");
-      }
-      */
-
-
-      /* Get list of axioms and definitions assumed by proof */
-      let(&statementUsedFlags, "");
-      traceProofWork(statemNum,
-          1, /*essentialFlag*/
-          "", /*traceToList*/ /* 18-Jul-2015 nm */
-          &statementUsedFlags, /*&statementUsedFlags*/
-          &unprovedList /* &unprovedList */);
-      if ((signed)(strlen(statementUsedFlags)) != g_statements + 1) bug(227);
-
-      /* First get axioms */
-      let(&tmpStr, "");
-      for (stmt = 1; stmt <= g_statements; stmt++) {
-        if (statementUsedFlags[stmt] == 'Y' && g_Statement[stmt].type == a_) {
-          let(&tmpStr1, left(g_Statement[stmt].labelName, 3));
-          if (!strcmp(tmpStr1, "ax-")) {
-            if (!tmpStr[0]) {
-              let(&tmpStr,
- /******* 10/10/02
- "<FONT SIZE=-1 FACE=sans-serif>The theorem was proved from these axioms: ");
- *******/
- "<TR><TD ALIGN=LEFT><FONT SIZE=-1><B>This theorem was proved from axioms:</B>");
-            }
-            let(&tmpStr1, "");
-            tmpStr1 = pinkHTML(stmt);
-            let(&tmpStr, cat(tmpStr, " &nbsp;<A HREF=\"",
-                g_Statement[stmt].labelName, ".html\">",
-                g_Statement[stmt].labelName, "</A>", tmpStr1, NULL));
-          }
-        }
-      } /* next stmt */
-      if (tmpStr[0]) {
-        let(&tmpStr, cat(tmpStr, "</FONT></TD></TR>", NULL));
-        printLongLine(tmpStr, "", "\"");
-      }
-
-      /* 10/10/02 Then get definitions */
-      let(&tmpStr, "");
-      for (stmt = 1; stmt <= g_statements; stmt++) {
-        if (statementUsedFlags[stmt] == 'Y' && g_Statement[stmt].type == a_) {
-          let(&tmpStr1, left(g_Statement[stmt].labelName, 3));
-          if (!strcmp(tmpStr1, "df-")) {
-            if (!tmpStr[0]) {
-              let(&tmpStr,
- "<TR><TD ALIGN=LEFT><FONT SIZE=-1><B>This theorem depends on definitions:</B>");
-            }
-            let(&tmpStr1, "");
-            tmpStr1 = pinkHTML(stmt);
-            let(&tmpStr, cat(tmpStr, " &nbsp;<A HREF=\"",
-                g_Statement[stmt].labelName, ".html\">",
-                g_Statement[stmt].labelName, "</A>", tmpStr1, NULL));
-          }
-        }
-      } /* next stmt */
-      if (tmpStr[0]) {
-        let(&tmpStr, cat(tmpStr, "</FONT></TD></TR>", NULL));
-        printLongLine(tmpStr, "", "\"");
-      }
-
-      /* Print any unproved statements */
-      if (nmbrLen(unprovedList)) {
-        if (nmbrLen(unprovedList) == 1 &&
-            !strcmp(g_Statement[unprovedList[0]].labelName,
-            g_Statement[statemNum].labelName)) {
-          /* When the unproved list consists only of the statement that
-             was traced, it means the statement traced has no
-             proof (or it has a proof, but is incomplete and all earlier
-             ones do have complete proofs). */
-          printLongLine(cat(
-"<TR><TD ALIGN=left >&nbsp;<B><FONT COLOR=\"#FF6600\">",
-"WARNING: This theorem has an incomplete proof.</FONT></B><BR></TD></TR>",
-              NULL), "", "\"");
-
-        } else {
-          printLongLine(cat(
-"<TR><TD ALIGN=left >&nbsp;</TD><TD><B><FONT COLOR=\"#FF6600\">",
-"WARNING: This proof depends on the following unproved theorem(s): ",
-              NULL), "", "\"");
-          let(&tmpStr, "");
-          for (i = 0; i < nmbrLen(unprovedList); i++) {
-            let(&tmpStr, cat(tmpStr, " <A HREF=\"",
-                g_Statement[unprovedList[i]].labelName, ".html\">",
-                g_Statement[unprovedList[i]].labelName, "</A>",
-                NULL));
-          }
-          printLongLine(cat(tmpStr, "</B></FONT></TD></TR>", NULL), "", "\"");
-        }
-      }
-
-      /* End of axiom list */
-
-      /* 30-Oct-2018 nm Moved down from above to put referenced by list last */
-      if (g_printStringForReferencedBy[0]) {
-        /* printLongLine takes 130 sec for 'sh st syl/a' */
-        /*printLongLine(g_printStringForReferencedBy, "", "\"");*/
-        /* 18-Jul-2015 nm Speedup for 'sh st syl/a' */
-        if (g_outputToString != 1) bug(257);
-        /* 30-Oct-2018 nm Deleted line: */
-        /*let(&g_printString, cat(g_printString, g_printStringForReferencedBy, NULL));*/
-        /* 30-Oct-2018 nm Added line: */
-        printLongLine(g_printStringForReferencedBy, "", "\"");
-        let(&g_printStringForReferencedBy, "");
-
-      /* 30-Oct-2018 nm */
-      } else {
-        /* Since we now always print ref-by list even if "(None)",
-           g_printStringForReferencedBy should never be empty */
-        bug(263);
-      }
-
-    }  /* if essentialFlag */
-
-
-    /* Printing of the trailer in mmwtex.c will close out string later */
-    g_outputToString = 0;
-  }
-
- typeProof_return:
-  let(&tmpStr, "");
-  let(&tmpStr1, "");
-  let(&statementUsedFlags, "");
-  let(&locLabDecl, "");
-  let(&tgtLabel, "");
-  let(&srcLabel, "");
-  let(&startPrefix, "");
-  let(&tgtPrefix, "");
-  let(&srcPrefix, "");
-  let(&userPrefix, "");
-  let(&contPrefix, "");
-  let(&hypStr, "");
-  let(&startStringWithNum, ""); /* 22-Apr-2015 nm */
-  let(&startStringWithoutNum, ""); /* 22-Apr-2015 nm */
-  nmbrLet(&unprovedList, NULL_NMBRSTRING);
-  nmbrLet(&localLabels, NULL_NMBRSTRING);
-  nmbrLet(&localLabelNames, NULL_NMBRSTRING);
-  nmbrLet(&proof, NULL_NMBRSTRING);
-  nmbrLet(&targetHyps, NULL_NMBRSTRING);
-  nmbrLet(&indentationLevel, NULL_NMBRSTRING);
-  nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-  nmbrLet(&stepRenumber, NULL_NMBRSTRING);
-  nmbrLet(&notUnifiedFlags, NULL_NMBRSTRING);
-  nmbrLet(&relativeStepNums, NULL_NMBRSTRING); /* 22-Apr-2015 nm */
-} /* typeProof() */
-
-/* Show details of one proof step */
-/* Note:  detailStep is the actual step number (starting with 1), not
-   the actual step - 1. */
-void showDetailStep(long statemNum, long detailStep) {
-
-  long i, j, plen, step, stmt, sourceStmt, targetStmt;
-  vstring tmpStr = "";
-  vstring tmpStr1 = "";
-  nmbrString *proof = NULL_NMBRSTRING;
-  nmbrString *localLabels = NULL_NMBRSTRING;
-  nmbrString *localLabelNames = NULL_NMBRSTRING;
-  nmbrString *targetHyps = NULL_NMBRSTRING;
-  long nextLocLabNum = 1; /* Next number to be used for a local label */
-  void *voidPtr; /* bsearch result */
-  char type;
-
-  /* Error check */
-  i = parseProof(statemNum);
-  if (i) {
-    printLongLine("?The proof is incomplete or has an error", "", " ");
-    return;
-  }
-  plen = nmbrLen(g_WrkProof.proofString);
-  if (plen < detailStep || detailStep < 1) {
-    printLongLine(cat("?The step number should be from 1 to ",
-        str((double)plen), NULL), "", " ");
-    return;
-  }
-
-  /* Structure getStep is declared in mmveri.h. */
-  getStep.stepNum = detailStep; /* Non-zero is flag for verifyProof */
-  parseProof(statemNum); /* ???Do we need to do this again? */
-  verifyProof(statemNum);
-
-
-  nmbrLet(&proof, g_WrkProof.proofString); /* The proof */
-  plen = nmbrLen(proof);
-
-  /* Collect local labels */
-  for (step = 0; step < plen; step++) {
-    stmt = proof[step];
-    if (stmt <= -1000) {
-      stmt = -1000 - stmt;
-      if (!nmbrElementIn(1, localLabels, stmt)) {
-        nmbrLet(&localLabels, nmbrAddElement(localLabels, stmt));
-      }
-    }
-  }
-
-  /* localLabelNames[] hold an integer which, when converted to string,
-    is the local label name. */
-  nmbrLet(&localLabelNames, nmbrSpace(plen));
-
-  /* Get the target hypotheses */
-  nmbrLet(&targetHyps, nmbrGetTargetHyp(proof, statemNum));
-
-  /* Get local labels */
-  for (step = 0; step < plen; step++) {
-    stmt = proof[step];
-    if (stmt >= 0) {
-      if (nmbrElementIn(1, localLabels, step)) {
-        /* This statement declares a local label */
-        /* First, get a name for the local label, using the next integer that
-           does not match any integer used for a statement label. */
-        let(&tmpStr1, str((double)nextLocLabNum));
-        while (1) {
-          voidPtr = (void *)bsearch(tmpStr,
-              g_allLabelKeyBase, (size_t)g_numAllLabelKeys,
-              sizeof(long), labelSrchCmp);
-          if (!voidPtr) break; /* It does not conflict */
-          nextLocLabNum++; /* Try the next one */
-          let(&tmpStr1, str((double)nextLocLabNum));
-        }
-        localLabelNames[step] = nextLocLabNum;
-        nextLocLabNum++; /* Prepare for next local label */
-      }
-    }
-  } /* Next step */
-
-  /* Print the step */
-  let(&tmpStr, g_Statement[targetHyps[detailStep - 1]].labelName);
-  let(&tmpStr1, ""); /* Local label declaration */
-  stmt = proof[detailStep - 1];
-  if (stmt < 0) {
-    if (stmt <= -1000) {
-      stmt = -1000 - stmt;
-      /* stmt is now the step number a local label refers to */
-      let(&tmpStr, cat(tmpStr,"=", str((double)(localLabelNames[stmt])), NULL));
-      type = g_Statement[proof[stmt]].type;
-    } else {
-      if (stmt != -(long)'?') bug(207);
-      let(&tmpStr, cat(tmpStr,"=",chr(-stmt), NULL)); /* '?' */
-      type = '?';
-    }
-  } else {
-    if (nmbrElementIn(1, localLabels, detailStep - 1)) {
-      /* This statement declares a local label */
-      let(&tmpStr1, cat(str((double)(localLabelNames[detailStep - 1])), ":",
-          NULL));
-    }
-    let(&tmpStr, cat(tmpStr, "=", g_Statement[stmt].labelName, NULL));
-    type = g_Statement[stmt].type;
-  }
-
-  /* Print the proof line */
-  printLongLine(cat("Proof step ",
-      str((double)detailStep),
-      ":  ",
-      tmpStr1,
-      tmpStr,
-      " $",
-      chr(type),
-      " ",
-      nmbrCvtMToVString(g_WrkProof.mathStringPtrs[detailStep - 1]),
-      NULL),
-      "  ",
-      " ");
-
-  /* Print details about the step */
-  let(&tmpStr, cat("This step assigns ", NULL));
-  let(&tmpStr1, "");
-  stmt = proof[detailStep - 1];
-  sourceStmt = stmt;
-  if (stmt < 0) {
-    if (stmt <= -1000) {
-      stmt = -1000 - stmt;
-      /* stmt is now the step number a local label refers to */
-      let(&tmpStr, cat(tmpStr, "step ", str((double)stmt),
-          " (via local label reference \"",
-          str((double)(localLabelNames[stmt])), "\") to ", NULL));
-    } else {
-      if (stmt != -(long)'?') bug(208);
-      let(&tmpStr, cat(tmpStr, "an unknown statement to ", NULL));
-    }
-  } else {
-    let(&tmpStr, cat(tmpStr, "source \"", g_Statement[stmt].labelName,
-        "\" ($", chr(g_Statement[stmt].type), ") to ", NULL));
-    if (nmbrElementIn(1, localLabels, detailStep - 1)) {
-      /* This statement declares a local label */
-      let(&tmpStr1, cat("  This step also declares the local label ",
-          str((double)(localLabelNames[detailStep - 1])),
-          ", which is used later on.",
-          NULL));
-    }
-  }
-  targetStmt = targetHyps[detailStep - 1];
-  if (detailStep == plen) {
-    let(&tmpStr, cat(tmpStr, "the final assertion being proved.", NULL));
-  } else {
-    let(&tmpStr, cat(tmpStr, "target \"", g_Statement[targetStmt].labelName,
-    "\" ($", chr(g_Statement[targetStmt].type), ").", NULL));
-  }
-
-  let(&tmpStr, cat(tmpStr, tmpStr1, NULL));
-
-  if (sourceStmt >= 0) {
-    if (g_Statement[sourceStmt].type == a_
-        || g_Statement[sourceStmt].type == p_) {
-      j = nmbrLen(g_Statement[sourceStmt].reqHypList);
-      if (j != nmbrLen(getStep.sourceHyps)) bug(209);
-      if (!j) {
-        let(&tmpStr, cat(tmpStr,
-            "  The source assertion requires no hypotheses.", NULL));
-      } else {
-        if (j == 1) {
-          let(&tmpStr, cat(tmpStr,
-              "  The source assertion requires the hypothesis ", NULL));
-        } else {
-          let(&tmpStr, cat(tmpStr,
-              "  The source assertion requires the hypotheses ", NULL));
-        }
-        for (i = 0; i < j; i++) {
-          let(&tmpStr, cat(tmpStr, "\"",
-              g_Statement[g_Statement[sourceStmt].reqHypList[i]].labelName,
-              "\" ($",
-              chr(g_Statement[g_Statement[sourceStmt].reqHypList[i]].type),
-              ", step ", str((double)(getStep.sourceHyps[i] + 1)), ")", NULL));
-          if (i == 0 && j == 2) {
-            let(&tmpStr, cat(tmpStr, " and ", NULL));
-          }
-          if (i < j - 2 && j > 2) {
-            let(&tmpStr, cat(tmpStr, ", ", NULL));
-          }
-          if (i == j - 2 && j > 2) {
-            let(&tmpStr, cat(tmpStr, ", and ", NULL));
-          }
-        }
-        let(&tmpStr, cat(tmpStr, ".", NULL));
-      }
-    }
-  }
-
-  if (detailStep < plen) {
-    let(&tmpStr, cat(tmpStr,
-         "  The parent assertion of the target hypothesis is \"",
-        g_Statement[getStep.targetParentStmt].labelName, "\" ($",
-        chr(g_Statement[getStep.targetParentStmt].type),", step ",
-        str((double)(getStep.targetParentStep)), ").", NULL));
-  } else {
-    let(&tmpStr, cat(tmpStr,
-        "  The target has no parent because it is the assertion being proved.",
-        NULL));
-  }
-
-  printLongLine(tmpStr, "", " ");
-
-  if (sourceStmt >= 0) {
-    if (g_Statement[sourceStmt].type == a_
-        || g_Statement[sourceStmt].type == p_) {
-      print2("The source assertion before substitution was:\n");
-      printLongLine(cat("    ", g_Statement[sourceStmt].labelName, " $",
-          chr(g_Statement[sourceStmt].type), " ", nmbrCvtMToVString(
-          g_Statement[sourceStmt].mathString), NULL),
-          "        ", " ");
-      j = nmbrLen(getStep.sourceSubstsNmbr);
-      if (j == 1) {
-        printLongLine(cat(
-            "The following substitution was made to the source assertion:",
-            NULL),""," ");
-      } else {
-        printLongLine(cat(
-            "The following substitutions were made to the source assertion:",
-            NULL),""," ");
-      }
-      if (!j) {
-        print2("    (None)\n");
-      } else {
-        print2("    Variable  Substituted with\n");
-        for (i = 0; i < j; i++) {
-          printLongLine(cat("     ",
-              g_MathToken[getStep.sourceSubstsNmbr[i]].tokenName," ",
-              space(9 - (long)strlen(
-                g_MathToken[getStep.sourceSubstsNmbr[i]].tokenName)),
-              nmbrCvtMToVString(getStep.sourceSubstsPntr[i]), NULL),
-              "                ", " ");
-        }
-      }
-    }
-  }
-
-  if (detailStep < plen) {
-    print2("The target hypothesis before substitution was:\n");
-    printLongLine(cat("    ", g_Statement[targetStmt].labelName, " $",
-        chr(g_Statement[targetStmt].type), " ", nmbrCvtMToVString(
-        g_Statement[targetStmt].mathString), NULL),
-        "        ", " ");
-    j = nmbrLen(getStep.targetSubstsNmbr);
-    if (j == 1) {
-      printLongLine(cat(
-          "The following substitution was made to the target hypothesis:",
-          NULL),""," ");
-    } else {
-      printLongLine(cat(
-          "The following substitutions were made to the target hypothesis:",
-          NULL),""," ");
-    }
-    if (!j) {
-      print2("    (None)\n");
-    } else {
-      print2("    Variable  Substituted with\n");
-      for (i = 0; i < j; i++) {
-        printLongLine(cat("     ",
-            g_MathToken[getStep.targetSubstsNmbr[i]].tokenName, " ",
-            space(9 - (long)strlen(
-              g_MathToken[getStep.targetSubstsNmbr[i]].tokenName)),
-            nmbrCvtMToVString(getStep.targetSubstsPntr[i]), NULL),
-            "                ", " ");
-      }
-    }
-  }
-
-  cleanWrkProof();
-  getStep.stepNum = 0; /* Zero is flag for verifyProof to ignore getStep info */
-
-  /* Deallocate getStep contents */
-  j = pntrLen(getStep.sourceSubstsPntr);
-  for (i = 0; i < j; i++) {
-    nmbrLet((nmbrString **)(&getStep.sourceSubstsPntr[i]),
-        NULL_NMBRSTRING);
-  }
-  j = pntrLen(getStep.targetSubstsPntr);
-  for (i = 0; i < j; i++) {
-    nmbrLet((nmbrString **)(&getStep.targetSubstsPntr[i]),
-        NULL_NMBRSTRING);
-  }
-  nmbrLet(&getStep.sourceHyps, NULL_NMBRSTRING);
-  pntrLet(&getStep.sourceSubstsPntr, NULL_PNTRSTRING);
-  nmbrLet(&getStep.sourceSubstsNmbr, NULL_NMBRSTRING);
-  pntrLet(&getStep.targetSubstsPntr, NULL_PNTRSTRING);
-  nmbrLet(&getStep.targetSubstsNmbr, NULL_NMBRSTRING);
-
-  /* Deallocate other strings */
-  let(&tmpStr, "");
-  let(&tmpStr1, "");
-  nmbrLet(&localLabels, NULL_NMBRSTRING);
-  nmbrLet(&localLabelNames, NULL_NMBRSTRING);
-  nmbrLet(&proof, NULL_NMBRSTRING);
-  nmbrLet(&targetHyps, NULL_NMBRSTRING);
-
-} /* showDetailStep */
-
-/* Summary of statements in proof for SHOW PROOF / STATEMENT_SUMMARY */
-void proofStmtSumm(long statemNum, flag essentialFlag, flag texFlag) {
-
-  long i, j, k, pos, stmt, plen, slen, step;
-  char type;
-  vstring statementUsedFlags = ""; /* 'Y'/'N' flag that statement is used */
-  vstring str1 = "";
-  vstring str2 = "";
-  vstring str3 = "";
-  nmbrString *statementList = NULL_NMBRSTRING;
-  nmbrString *proof = NULL_NMBRSTRING;
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-
-  /* 10/10/02 This section is never called in HTML mode anymore.  The code is
-     left in though just in case we somehow get here and the user continues
-     through the bug. */
-  if (texFlag && g_htmlFlag) bug(239);
-
-  if (!texFlag) {
-    print2("Summary of statements used in the proof of \"%s\":\n",
-        g_Statement[statemNum].labelName);
-  } else {
-    g_outputToString = 1; /* Flag for print2 to add to g_printString */
-    if (!g_htmlFlag) {
-      print2("\n");
-      print2("\\vspace{1ex} %%3\n");
-      printLongLine(cat("Summary of statements used in the proof of ",
-          "{\\tt ",
-          asciiToTt(g_Statement[statemNum].labelName),
-          "}:", NULL), "", " ");
-    } else {
-      printLongLine(cat("Summary of statements used in the proof of ",
-          "<B>",
-          asciiToTt(g_Statement[statemNum].labelName),
-          "</B>:", NULL), "", "\"");
-    }
-    g_outputToString = 0;
-    fprintf(g_texFilePtr, "%s", g_printString);
-    let(&g_printString, "");
-  }
-
-  if (g_Statement[statemNum].type != p_) {
-    print2("  This is not a provable ($p) statement.\n");
-    return;
-  }
-
-  /* 20-Oct-2013 nm Don't use bad proofs (incomplete proofs are ok) */
-  if (parseProof(statemNum) > 1) {
-    /* The proof has an error, so use the empty proof */
-    nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
-  } else {
-    nmbrLet(&proof, g_WrkProof.proofString);
-  }
-
-  plen = nmbrLen(proof);
-  /* Get the essential step flags, if required */
-  if (essentialFlag) {
-    nmbrLet(&essentialFlags, nmbrGetEssential(proof));
-  }
-
-  for (step = 0; step < plen; step++) {
-    if (essentialFlag) {
-      if (!essentialFlags[step]) continue;     /* Ignore floating hypotheses */
-    }
-    stmt = proof[step];
-    if (stmt < 0) {
-      continue; /* Ignore '?' and local labels */
-    }
-    if (1) { /* Limit list to $a and $p only */
-      if (g_Statement[stmt].type != a_ && g_Statement[stmt].type != p_) {
-        continue;
-      }
-    }
-    /* Add this statement to the statement list if not already in it */
-    if (!nmbrElementIn(1, statementList, stmt)) {
-      nmbrLet(&statementList, nmbrAddElement(statementList, stmt));
-    }
-  } /* Next step */
-
-  /* Prepare the output */
-  /* First, fill in the statementUsedFlags char array.  This allows us to sort
-     the output by statement number without calling a sort routine. */
-  slen = nmbrLen(statementList);
-  let(&statementUsedFlags, string(g_statements + 1, 'N')); /* Init. to 'no' */
-  for (pos = 0; pos < slen; pos++) {
-    stmt = statementList[pos];
-    if (stmt > statemNum || stmt < 1) bug(210);
-    statementUsedFlags[stmt] = 'Y';
-  }
-  /* Next, build the output string */
-  for (stmt = 1; stmt < statemNum; stmt++) {
-    if (statementUsedFlags[stmt] == 'Y') {
-      assignStmtFileAndLineNum(stmt); /* 9-Jan-2018 nm */
-      let(&str1, cat(" is located on line ",
-          str((double)(g_Statement[stmt].lineNum)),
-          " of the file ", NULL));
-      if (!texFlag) {
-        print2("\n");
-        printLongLine(cat("Statement ", g_Statement[stmt].labelName, str1,
-          "\"", g_Statement[stmt].fileName,
-          "\".",NULL), "", " ");
-      } else {
-        g_outputToString = 1; /* Flag for print2 to add to g_printString */
-        if (!g_htmlFlag) {
-          print2("\n");
-          print2("\n");
-          print2("\\vspace{1ex} %%4\n");
-          printLongLine(cat("Statement {\\tt ",
-              asciiToTt(g_Statement[stmt].labelName), "} ",
-              str1, "{\\tt ",
-              asciiToTt(g_Statement[stmt].fileName),
-              "}.", NULL), "", " ");
-          print2("\n");
-        } else {
-          printLongLine(cat("Statement <B>",
-              asciiToTt(g_Statement[stmt].labelName), "</B> ",
-              str1, " <B>",
-              asciiToTt(g_Statement[stmt].fileName),
-              "</B> ", NULL), "", "\"");
-        }
-        g_outputToString = 0;
-        fprintf(g_texFilePtr, "%s", g_printString);
-        let(&g_printString, "");
-      }
-
-      /* type = g_Statement[stmt].type; */ /* 18-Sep-2013 Not used */
-      let(&str1, "");
-      str1 = getDescription(stmt);
-      if (str1[0]) {
-        if (!texFlag) {
-          printLongLine(cat("\"", str1, "\"", NULL), "", " ");
-        } else {
-          /* printTexComment(str1, 1); */
-          /* 17-Nov-2015 nm Added 3rd & 4th arguments */
-          printTexComment(str1,              /* Sends result to g_texFilePtr */
-              1, /* 1 = htmlCenterFlag */
-              PROCESS_EVERYTHING, /* actionBits */ /* 13-Dec-2018 nm */
-              0 /* 1 = noFileCheck */);
-        }
-      }
-
-      /* print2("Its mandatory hypotheses in RPN order are:\n"); */
-      j = nmbrLen(g_Statement[stmt].reqHypList);
-      for (i = 0; i < j; i++) {
-        k = g_Statement[stmt].reqHypList[i];
-        if (!essentialFlag || g_Statement[k].type != f_) {
-          let(&str2, cat("  ",g_Statement[k].labelName,
-              " $", chr(g_Statement[k].type), " ", NULL));
-          if (!texFlag) {
-            printLongLine(cat(str2,
-                nmbrCvtMToVString(g_Statement[k].mathString), " $.", NULL),
-                "      "," ");
-          } else {
-            let(&str3, space((long)strlen(str2)));
-            printTexLongMath(g_Statement[k].mathString,
-                str2, str3, 0, 0);
-          }
-        }
-      }
-
-      let(&str1, "");
-      type = g_Statement[stmt].type;
-      if (type == p_) let(&str1, " $= ...");
-      let(&str2, cat("  ", g_Statement[stmt].labelName,
-          " $",chr(type), " ", NULL));
-      if (!texFlag) {
-        printLongLine(cat(str2,
-            nmbrCvtMToVString(g_Statement[stmt].mathString),
-            str1, " $.", NULL), "      ", " ");
-      } else {
-        let(&str3, space((long)strlen(str2)));
-        printTexLongMath(g_Statement[stmt].mathString,
-            str2, str3, 0, 0);
-      }
-
-    } /* End if (statementUsedFlag[stmt] == 'Y') */
-  } /* Next stmt */
-
-  let(&statementUsedFlags, ""); /* 'Y'/'N' flag that statement is used */
-  let(&str1, "");
-  let(&str2, "");
-  let(&str3, "");
-  nmbrLet(&statementList, NULL_NMBRSTRING);
-  nmbrLet(&proof, NULL_NMBRSTRING);
-  nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-
-} /* proofStmtSumm */
-
-
-/* Traces back the statements used by a proof, recursively. */
-/* Returns 1 if at least one label is printed (or would be printed in
-   case testOnlyFlag=1); otherwise, returns 0 */
-/* matchList suppresses all output except labels matching matchList */
-/* testOnlyFlag prevents any printout; it is used to determine whether
-   there is an unwanted axiom for MINIMIZE_WITH /FORBID. */
-/* void traceProof(long statemNum, */ /* before 20-May-2013 */
-flag traceProof(long statemNum, /* 20-May-2013 nm */
-  flag essentialFlag,
-  flag axiomFlag,
-  vstring matchList, /* 19-May-2013 nm */
-  vstring traceToList, /* 18-Jul-2015 nm */
-  flag testOnlyFlag /* 20-May-2013 nm */)
-{
-
-  long stmt, pos;
-  vstring statementUsedFlags = ""; /* y/n flags that statement is used */
-  vstring outputString = "";
-  nmbrString *unprovedList = NULL_NMBRSTRING;
-  flag foundFlag = 0;
-
-  /* Make sure we're calling this with $p statements only */
-  if (g_Statement[statemNum].type != (char)p_) bug(249);
-
-  if (!testOnlyFlag) {  /* 20-May-2013 nm */
-    if (axiomFlag) {
-      print2(
-  "Statement \"%s\" assumes the following axioms ($a statements):\n",
-          g_Statement[statemNum].labelName);
-    } else  if (traceToList[0] == 0) {
-      print2(
-  "The proof of statement \"%s\" uses the following earlier statements:\n",
-          g_Statement[statemNum].labelName);
-    } else {
-      print2(
-  "The proof of statement \"%s\" traces back to \"%s\" via:\n",
-          g_Statement[statemNum].labelName, traceToList);
-    }
-  }
-
-  traceProofWork(statemNum,
-      essentialFlag,
-      traceToList, /* /TO argument of SHOW TRACE_BACK */ /* 18-Jul-2015 nm */
-      &statementUsedFlags,
-      &unprovedList);
-  if ((signed)(strlen(statementUsedFlags)) != g_statements + 1) bug(226);
-
-  /* Build the output string */
-  let(&outputString, "");
-  for (stmt = 1; stmt < statemNum; stmt++) {
-    if (statementUsedFlags[stmt] == 'Y') {
-
-      /* 19-May-2013 nm - Added MATCH qualifier */
-      if (matchList[0]) {  /* There is a list to match */
-        /* Don't include unmatched labels */
-        if (!matchesList(g_Statement[stmt].labelName, matchList, '*', '?'))
-          continue;
-      }
-
-      /* 20-May-2013 nm  Skip rest of scan in testOnlyFlag mode */
-      foundFlag = 1; /* At least one label would be printed */
-      if (testOnlyFlag) {
-        goto TRACE_RETURN;
-      }
-      if (axiomFlag) {
-        if (g_Statement[stmt].type == a_) {
-          let(&outputString, cat(outputString, " ", g_Statement[stmt].labelName,
-              NULL));
-        }
-      } else {
-        let(&outputString, cat(outputString, " ", g_Statement[stmt].labelName,
-            NULL));
-        switch (g_Statement[stmt].type) {
-          case a_: let(&outputString, cat(outputString, "($a)", NULL)); break;
-          case e_: let(&outputString, cat(outputString, "($e)", NULL)); break;
-          case f_: let(&outputString, cat(outputString, "($f)", NULL)); break;
-        }
-      }
-    } /* End if (statementUsedFlag[stmt] == 'Y') */
-  } /* Next stmt */
-
-  /* 20-May-2013 nm  Skip printing in testOnlyFlag mode */
-  if (testOnlyFlag) {
-    goto TRACE_RETURN;
-  }
-
-  if (outputString[0]) {
-    let(&outputString, cat(" ", outputString, NULL));
-  } else {
-    let(&outputString, "  (None)");
-  }
-
-  /* Print the output */
-  printLongLine(outputString, "  ", " ");
-
-  /* Print any unproved statements */
-  if (nmbrLen(unprovedList)) {
-    print2("Warning: The following traced statement(s) were not proved:\n");
-    let(&outputString, "");
-    for (pos = 0; pos < nmbrLen(unprovedList); pos++) {
-      let(&outputString, cat(outputString, " ", g_Statement[unprovedList[
-          pos]].labelName, NULL));
-    }
-    let(&outputString, cat("  ", outputString, NULL));
-    printLongLine(outputString, "  ", " ");
-  }
-
- TRACE_RETURN:
-  /* Deallocate */
-  let(&outputString, "");
-  let(&statementUsedFlags, "");
-  nmbrLet(&unprovedList, NULL_NMBRSTRING);
-  return foundFlag;
-} /* traceProof */
-
-/* Traces back the statements used by a proof, recursively.  Returns
-   a nmbrString with a list of statements and unproved statements */
-void traceProofWork(long statemNum,
-  flag essentialFlag,
-  vstring traceToList, /* /TO argument of SHOW TRACE_BACK */ /* 18-Jul-2015 nm */
-  vstring *statementUsedFlagsP, /* 'Y'/'N' flag that statement is used */
-  nmbrString **unprovedListP)
-{
-
-  long pos, stmt, plen, slen, step;
-  nmbrString *statementList = NULL_NMBRSTRING;
-  nmbrString *proof = NULL_NMBRSTRING;
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-  vstring traceToFilter = ""; /* 18-Jul-2015 nm */
-  vstring str1 = ""; /* 18-Jul-2015 nm */
-  long j; /* 18-Jul-2015 nm */
-
-  /* 18-Jul-2015 nm */
-  /* Preprocess the "SHOW TRACE_BACK ... / TO" traceToList list if any */
-  if (traceToList[0] != 0) {
-    let(&traceToFilter, string(g_statements + 1, 'N')); /* Init. to 'no' */
-    /* Wildcard match scan */
-    for (stmt = 1; stmt <= g_statements; stmt++) {
-      if (g_Statement[stmt].type != (char)a_
-          && g_Statement[stmt].type != (char)p_)
-        continue; /* Not a $a or $p statement; skip it */
-      /* Wildcard matching */
-      if (!matchesList(g_Statement[stmt].labelName, traceToList, '*', '?'))
-        continue;
-      let(&str1, "");
-      str1 = traceUsage(stmt /*g_showStatement*/,
-          1, /*recursiveFlag*/
-          statemNum /* cutoffStmt */);
-      traceToFilter[stmt] = 'Y'; /* Include the statement we're showing
-                                    usage of */
-      if (str1[0] == 'Y') {  /* There is some usage */
-        for (j = stmt + 1; j <= g_statements; j++) {
-          /* OR in the usage to the filter */
-          if (str1[j] == 'Y') traceToFilter[j] = 'Y';
-        }
-      }
-    } /* Next i (statement number) */
-  } /* if (traceToList[0] != 0) */
-
-  nmbrLet(&statementList, nmbrSpace(g_statements));
-  statementList[0] = statemNum;
-  slen = 1;
-  nmbrLet(&(*unprovedListP), NULL_NMBRSTRING); /* List of unproved statements */
-  let(&(*statementUsedFlagsP), string(g_statements + 1, 'N')); /* Init. to 'no' */
-  (*statementUsedFlagsP)[statemNum] = 'Y';  /* nm 22-Nov-2014 */
-  for (pos = 0; pos < slen; pos++) {
-    if (g_Statement[statementList[pos]].type != p_) {
-      continue; /* Not a $p */
-    }
-
-    /* 20-Oct-2013 nm Don't use bad proofs (incomplete proofs are ok) */
-    if (parseProof(statementList[pos]) > 1) {
-      /* The proof has an error, so use the empty proof */
-      nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
-    } else {
-      nmbrLet(&proof, g_WrkProof.proofString);
-    }
-
-    plen = nmbrLen(proof);
-    /* Get the essential step flags, if required */
-    if (essentialFlag) {
-      nmbrLet(&essentialFlags, nmbrGetEssential(proof));
-    }
-    for (step = 0; step < plen; step++) {
-      if (essentialFlag) {
-        if (!essentialFlags[step]) continue;  /* Ignore floating hypotheses */
-      }
-      stmt = proof[step];
-      if (stmt < 0) {
-        if (stmt > -1000) {
-          /* '?' */
-          if (!nmbrElementIn(1, *unprovedListP, statementList[pos])) {
-            nmbrLet(&(*unprovedListP), nmbrAddElement(*unprovedListP,
-                statementList[pos]));  /* Add to list of unproved statements */
-          }
-        }
-        continue; /* Ignore '?' and local labels */
-      }
-      if (1) { /* Limit list to $a and $p only */
-        if (g_Statement[stmt].type != a_ && g_Statement[stmt].type != p_) {
-          continue;
-        }
-      }
-      /* Add this statement to the statement list if not already in it */
-      if ((*statementUsedFlagsP)[stmt] == 'N') {
-        /*(*statementUsedFlagsP)[stmt] = 'Y';*/  /* 18-Jul-2015 deleted */
-
-        /* 18-Jul-2015 nm */
-        if (traceToList[0] == 0) {
-          statementList[slen] = stmt;
-          slen++;
-          (*statementUsedFlagsP)[stmt] = 'Y';
-        } else { /* TRACE_BACK / TO */
-          if (traceToFilter[stmt] == 'Y') {
-            statementList[slen] = stmt;
-            slen++;
-            (*statementUsedFlagsP)[stmt] = 'Y';
-          }
-        }
-      }
-    } /* Next step */
-  } /* Next pos */
-
-  /* Deallocate */
-  nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-  nmbrLet(&proof, NULL_NMBRSTRING);
-  nmbrLet(&statementList, NULL_NMBRSTRING);
-  let(&str1, "");
-  let(&str1, "");
-  return;
-
-} /* traceProofWork */
-
-nmbrString *stmtFoundList = NULL_NMBRSTRING;
-long indentShift = 0;
-
-/* Traces back the statements used by a proof, recursively, with tree display.*/
-void traceProofTree(long statemNum,
-  flag essentialFlag, long endIndent)
-{
-  if (g_Statement[statemNum].type != p_) {
-    print2("Statement %s is not a $p statement.\n",
-        g_Statement[statemNum].labelName);
-    return;
-  }
-
-  printLongLine(cat("The proof tree traceback for statement \"",
-      g_Statement[statemNum].labelName,
-      "\" follows.  The statements used by each proof are indented one level in,",
-      " below the statement being proved.  Hypotheses are not included.",
-      NULL),
-      "", " ");
-  print2("\n");
-
-  nmbrLet(&stmtFoundList, NULL_NMBRSTRING);
-  indentShift = 0;
-  traceProofTreeRec(statemNum, essentialFlag, endIndent, 0);
-  nmbrLet(&stmtFoundList, NULL_NMBRSTRING);
-} /* traceProofTree */
-
-
-void traceProofTreeRec(long statemNum,
-  flag essentialFlag, long endIndent, long recursDepth)
-{
-  long i, pos, stmt, plen, slen, step;
-  vstring outputStr = "";
-  nmbrString *localFoundList = NULL_NMBRSTRING;
-  nmbrString *localPrintedList = NULL_NMBRSTRING;
-  flag unprovedFlag = 0;
-  nmbrString *proof = NULL_NMBRSTRING;
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-
-
-  let(&outputStr, "");
-  outputStr = getDescription(statemNum); /* Get statement comment */
-  let(&outputStr, edit(outputStr, 8 + 16 + 128)); /* Trim and reduce spaces */
-  slen = len(outputStr);
-  for (i = 0; i < slen; i++) {
-    /* Change newlines to spaces in comment */
-    if (outputStr[i] == '\n') {
-      outputStr[i] = ' ';
-    }
-  }
-
-#define INDENT_INCR 3
-#define MAX_LINE_LEN 79
-
-  if ((recursDepth * INDENT_INCR - indentShift) >
-      (g_screenWidth - MAX_LINE_LEN) + 50) {
-    indentShift = indentShift + 40 + (g_screenWidth - MAX_LINE_LEN);
-    print2("****** Shifting indentation.  Total shift is now %ld.\n",
-      (long)indentShift);
-  }
-  if ((recursDepth * INDENT_INCR - indentShift) < 1 && indentShift != 0) {
-    indentShift = indentShift - 40 - (g_screenWidth - MAX_LINE_LEN);
-    print2("****** Shifting indentation.  Total shift is now %ld.\n",
-      (long)indentShift);
-  }
-
-  let(&outputStr, cat(space(recursDepth * INDENT_INCR - indentShift),
-      g_Statement[statemNum].labelName, " $", chr(g_Statement[statemNum].type),
-      "  \"", edit(outputStr, 8 + 128), "\"", NULL));
-
-  if (len(outputStr) > MAX_LINE_LEN + (g_screenWidth - MAX_LINE_LEN)) {
-    let(&outputStr, cat(left(outputStr,
-        MAX_LINE_LEN + (g_screenWidth - MAX_LINE_LEN) - 3), "...", NULL));
-  }
-
-  if (g_Statement[statemNum].type == p_ || g_Statement[statemNum].type == a_) {
-    /* Only print assertions to reduce output bulk */
-    print2("%s\n", outputStr);
-  }
-
-  if (g_Statement[statemNum].type != p_) {
-    let(&outputStr, "");
-    return;
-  }
-
-  if (endIndent) {
-    /* An indentation level limit is set */
-    if (endIndent < recursDepth + 2) {
-      let(&outputStr, "");
-      return;
-    }
-  }
-
-  /* 20-Oct-2013 nm Don't use bad proofs (incomplete proofs are ok) */
-  if (parseProof(statemNum) > 1) {
-    /* The proof has an error, so use the empty proof */
-    nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
-  } else {
-    nmbrLet(&proof, g_WrkProof.proofString);
-  }
-
-  plen = nmbrLen(proof);
-  /* Get the essential step flags, if required */
-  if (essentialFlag) {
-    nmbrLet(&essentialFlags, nmbrGetEssential(proof));
-  }
-  nmbrLet(&localFoundList, NULL_NMBRSTRING);
-  nmbrLet(&localPrintedList, NULL_NMBRSTRING);
-  for (step = 0; step < plen; step++) {
-    if (essentialFlag) {
-      if (!essentialFlags[step]) continue;
-                                                /* Ignore floating hypotheses */
-    }
-    stmt = proof[step];
-    if (stmt < 0) {
-      if (stmt > -1000) {
-        /* '?' */
-        unprovedFlag = 1;
-      }
-      continue; /* Ignore '?' and local labels */
-    }
-    if (!nmbrElementIn(1, localFoundList, stmt)) {
-      nmbrLet(&localFoundList, nmbrAddElement(localFoundList, stmt));
-    }
-    if (!nmbrElementIn(1, stmtFoundList, stmt)) {
-      traceProofTreeRec(stmt, essentialFlag, endIndent, recursDepth + 1);
-      nmbrLet(&localPrintedList, nmbrAddElement(localPrintedList, stmt));
-      nmbrLet(&stmtFoundList, nmbrAddElement(stmtFoundList, stmt));
-    }
-  } /* Next step */
-
-  /* See if there are any old statements printed previously */
-  slen = nmbrLen(localFoundList);
-  let(&outputStr, "");
-  for (pos = 0; pos < slen; pos++) {
-    stmt = localFoundList[pos];
-    if (!nmbrElementIn(1, localPrintedList, stmt)) {
-      /* Don't include $f, $e in output */
-      if (g_Statement[stmt].type == p_ || g_Statement[stmt].type == a_) {
-        let(&outputStr, cat(outputStr, " ",
-            g_Statement[stmt].labelName, NULL));
-      }
-    }
-  }
-
-  if (len(outputStr)) {
-    printLongLine(cat(space(INDENT_INCR * (recursDepth + 1) - 1 - indentShift),
-      outputStr, " (shown above)", NULL),
-      space(INDENT_INCR * (recursDepth + 2) - indentShift), " ");
-  }
-
-  if (unprovedFlag) {
-    printLongLine(cat(space(INDENT_INCR * (recursDepth + 1) - indentShift),
-      "*** Statement ", g_Statement[statemNum].labelName, " has not been proved."
-      , NULL),
-      space(INDENT_INCR * (recursDepth + 2)), " ");
-  }
-
-  let(&outputStr, "");
-  nmbrLet(&localFoundList, NULL_NMBRSTRING);
-  nmbrLet(&localPrintedList, NULL_NMBRSTRING);
-  nmbrLet(&proof, NULL_NMBRSTRING);
-  nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-
-} /* traceProofTreeRec */
-
-
-/* Called by SHOW TRACE_BACK <label> / COUNT_STEPS */
-/* Counts the number of steps a completely exploded proof would require */
-/* (Recursive) */
-/* 0 is returned if some assertions have incomplete proofs. */
-double countSteps(long statemNum, flag essentialFlag)
-{
-  static double *stmtCount;
-  static double *stmtNodeCount;
-  static long *stmtDist;
-  static long *stmtMaxPath;
-  static double *stmtAveDist;
-  static long *stmtProofLen; /* The actual number of steps in stmt's proof */
-  static long *stmtUsage; /* The number of times the statement is used */
-  static long level = 0;
-  static flag unprovedFlag;
-
-  long stmt, plen, step, i, j, k;
-  long essentialplen;
-  nmbrString *proof = NULL_NMBRSTRING;
-  double stepCount; /* The total steps if fully expanded */
-
-  /* 12-Nov-2018 nm */
-  static vstring *stmtBigCount; /* Unlimited precision stmtCount */
-  vstring stepBigCount = ""; /* Unlimited precision stepCount */
-  vstring tmpBig1 = "";
-
-  double stepNodeCount;
-  double stepDistSum;
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-  vstring tmpStr = "";
-  long actualSteps, actualSubTheorems;
-  long actualSteps2, actualSubTheorems2;
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  essentialplen = 0;
-
-  /* If this is the top level of recursion, initialize things */
-  if (!level) {
-    stmtCount = malloc((sizeof(double) * ((size_t)g_statements + 1)));
-    stmtBigCount = malloc((sizeof(vstring) * ((size_t)g_statements + 1)));
-                                                           /* 12-Nov-2018 nm */
-    stmtNodeCount = malloc(sizeof(double) * ((size_t)g_statements + 1));
-    stmtDist = malloc(sizeof(long) * ((size_t)g_statements + 1));
-    stmtMaxPath = malloc(sizeof(long) * ((size_t)g_statements + 1));
-    stmtAveDist = malloc(sizeof(double) * ((size_t)g_statements + 1));
-    stmtProofLen = malloc(sizeof(long) * ((size_t)g_statements + 1));
-    stmtUsage = malloc(sizeof(long) * ((size_t)g_statements + 1));
-    if (!stmtCount || !stmtNodeCount || !stmtDist || !stmtMaxPath ||
-        !stmtAveDist || !stmtProofLen || !stmtUsage) {
-      print2("?Memory overflow.  Step count will be wrong.\n");
-      if (stmtCount) free(stmtCount);
-      if (stmtBigCount) free(stmtBigCount); /* 12-Nov-2018 nm */
-      if (stmtNodeCount) free(stmtNodeCount);
-      if (stmtDist) free(stmtDist);
-      if (stmtMaxPath) free(stmtMaxPath);
-      if (stmtAveDist) free(stmtAveDist);
-      if (stmtProofLen) free(stmtProofLen);
-      if (stmtUsage) free(stmtUsage);
-      return (0);
-    }
-    for (stmt = 1; stmt < g_statements + 1; stmt++) {
-      stmtCount[stmt] = 0;
-      stmtBigCount[stmt] = ""; /* 12-Nov-2018 nm */
-      stmtUsage[stmt] = 0;
-      stmtDist[stmt] = 0; /* 18-Sep-2013 Initialize */
-    }
-    unprovedFlag = 0; /* Flag that some proof wasn't complete */
-  }
-  level++;
-  stepCount = 0;
-  let(&stepBigCount, "0"); /* "" and "0" both mean 0 */ /* 12-Nov-2018 nm */
-  stepNodeCount = 0;
-  stepDistSum = 0;
-  stmtDist[statemNum] = -2; /* Forces at least one assignment */
-
-  if (g_Statement[statemNum].type != (char)p_) {
-    /* $a, $e, or $f */
-    stepCount = 1;
-    let(&stepBigCount, "1"); /* 12-Nov-2018 nm */
-    stepNodeCount = 0;
-    stmtDist[statemNum] = 0;
-    goto returnPoint;
-  }
-
-  /* 20-Oct-2013 nm Don't use bad proofs (incomplete proofs are ok) */
-  if (parseProof(statemNum) > 1) {
-    /* The proof has an error, so use the empty proof */
-    nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
-  } else {
-    /*nmbrLet(&proof, nmbrUnsquishProof(g_WrkProof.proofString));*/ /* The proof */
-    /* 13-Nov-2018 nm - Use proof as it is saved (so user can choose
-       compressed or not) */
-    nmbrLet(&proof, g_WrkProof.proofString); /* The proof */
-  }
-
-  plen = nmbrLen(proof);
-  /* Get the essential step flags, if required */
-  if (essentialFlag) {
-    nmbrLet(&essentialFlags, nmbrGetEssential(proof));
-  }
-  essentialplen = 0;
-  for (step = 0; step < plen; step++) {
-  /* 12-May-04 nm Use the following loop instead to get an alternate maximum
-     path */
-    if (essentialFlag) {
-      if (!essentialFlags[step]) continue;     /* Ignore floating hypotheses */
-    }
-    essentialplen++;
-    stmt = proof[step];
-    if (stmt < 0) {
-      if (stmt <= -1000) {
-        /* Pre-13-Nov-2010: */
-        /*bug(215);*/ /* The proof was expanded; there should be no local labels */
-        /* 13-Nov-2018 nm */
-        /* User can choose to count compressed or normal steps by saving
-           the proof that way */
-        /* A local label does not add a proof step in the web page proof */
-        continue;
-      } else {
-        /* '?' */
-        unprovedFlag = 1;
-        stepCount = stepCount + 1;
-
-        /* 12-Nov-2018 nm */
-        let(&tmpBig1, "");
-        tmpBig1 = bigAdd(stepBigCount, "1");
-        let(&stepBigCount, tmpBig1);
-
-        stepNodeCount = stepNodeCount + 1;
-        stepDistSum = stepDistSum + 1;
-      }
-    } else {
-      if (stmtCount[stmt] == 0) {
-        /* It has not been computed yet - call this function recursively */
-        stepCount = stepCount + countSteps(stmt, essentialFlag);
-      } else {
-        /* It has already been computed */
-        stepCount = stepCount + stmtCount[stmt];
-      }
-
-      /* 12-Nov-2018 nm */
-      /* In either case, stmtBigCount[stmt] will be populated now */
-      let(&tmpBig1, "");
-      tmpBig1 = bigAdd(stepBigCount, stmtBigCount[stmt]);
-      let(&stepBigCount, tmpBig1);
-
-      if (g_Statement[stmt].type == (char)p_) {
-        /*stepCount--;*/ /* -1 to account for the replacement of this step */
-        for (j = 0; j < g_Statement[stmt].numReqHyp; j++) {
-          k = g_Statement[stmt].reqHypList[j];
-          if (!essentialFlag || g_Statement[k].type == (char)e_) {
-            stepCount--;
-
-            /* 12-Nov-2018 nm */
-            /* In either case, stmtBigCount[stmt] will be populated now */
-            let(&tmpBig1, "");
-            tmpBig1 = bigSub(stepBigCount, "1");
-            let(&stepBigCount, tmpBig1);
-
-          }
-        }
-      }
-      stmtUsage[stmt]++;
-      if (stmtDist[statemNum] < stmtDist[stmt] + 1) {
-        stmtDist[statemNum] = stmtDist[stmt] + 1;
-        stmtMaxPath[statemNum] = stmt;
-      }
-      stepNodeCount = stepNodeCount + stmtNodeCount[stmt];
-      stepDistSum = stepDistSum + stmtAveDist[stmt] + 1;
-    }
-
-  } /* Next step */
-
- returnPoint:
-
-  /* Assign step count to statement list */
-  stmtCount[statemNum] = stepCount;
-
-  /* 12-Nov-2018 nm */
-  if ((stmtBigCount[statemNum])[0] != 0) bug(264);
-  let(&stmtBigCount[statemNum], stepBigCount);
-
-  stmtNodeCount[statemNum] = stepNodeCount + 1;
-  stmtAveDist[statemNum] = (double)stepDistSum / (double)essentialplen;
-  stmtProofLen[statemNum] = essentialplen;
-
-  nmbrLet(&proof, NULL_NMBRSTRING);
-  nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-
-  level--;
-  /* If this is the top level of recursion, deallocate */
-  if (!level) {
-    if (unprovedFlag) stepCount = 0; /* Don't mislead user */
-
-    /* Compute the total actual steps, total actual subtheorems */
-    actualSteps = stmtProofLen[statemNum];
-    actualSubTheorems = 0;
-    actualSteps2 = actualSteps; /* Steps w/ single-use subtheorems eliminated */
-    actualSubTheorems2 = 0; /* Multiple-use subtheorems only */
-    for (i = 1; i < statemNum; i++) {
-      if (g_Statement[i].type == (char)p_ && stmtCount[i] != 0) {
-        actualSteps = actualSteps + stmtProofLen[i];
-        actualSubTheorems++;
-        if (stmtUsage[i] > 1) {
-          actualSubTheorems2++;
-          actualSteps2 = actualSteps2 + stmtProofLen[i];
-        } else {
-          actualSteps2 = actualSteps2 + stmtProofLen[i] - 1;
-          for (j = 0; j < g_Statement[i].numReqHyp; j++) {
-            /* Subtract out hypotheses if subtheorem eliminated */
-            k = g_Statement[i].reqHypList[j];
-            if (!essentialFlag || g_Statement[k].type == (char)e_) {
-              actualSteps2--;
-            }
-          }
-        }
-      }
-    }
-
-    j = statemNum;
-    for (i = stmtDist[statemNum]; i >= 0; i--) {
-      if (stmtDist[j] != i) bug(214);
-      let(&tmpStr, cat(tmpStr, " <- ", g_Statement[j].labelName,
-          NULL));
-      j = stmtMaxPath[j];
-    }
-    printLongLine(cat(
-       "The statement's actual proof has ",
-           str((double)(stmtProofLen[statemNum])), " steps.  ",
-       "Backtracking, a total of ", str((double)actualSubTheorems),
-           " different subtheorems are used.  ",
-       "The statement and subtheorems have a total of ",
-           str((double)actualSteps), " actual steps.  ",
-       "If subtheorems used only once were eliminated,",
-           " there would be a total of ",
-           str((double)actualSubTheorems2), " subtheorems, and ",
-       "the statement and subtheorems would have a total of ",
-           str((double)actualSteps2), " steps.  ",
-       "The proof would have ",
-
-       /* This is the old stepCount; can be enabled to troubleshoot
-          the new unlimited precision algorithm */
-       /* 27-May-05 nm stepCount is inaccurate for over 16 or so digits due
-          to roundoff errors. */
-       /*stepCount > 1000000000 ? ">1000000000" : str((double)stepCount),*/
-
-       /* 12-Nov-2018 nm */
-       stepBigCount,
-       strlen(stepBigCount) < 6 ? ""
-           : cat(" =~ ",
-                 left(
-                    str((double)
-                        ((5.0 + val(left(stepBigCount, 3))) / 100.0)),
-                    3),
-                 " x 10^",
-                 str((double)strlen(stepBigCount) - 1), NULL),
-
-
-       " steps if fully expanded back to axiom references.  ",
-       /*
-       "The proof tree has ", str((double)stmtNodeCount[statemNum])," nodes.  ",
-       "A random backtrack path has an average path length of ",
-       str((double)(stmtAveDist[statemNum])),
-       ".  ",
-       */
-       "The maximum path length is ",
-       str((double)(stmtDist[statemNum])),
-       ".  A longest path is:  ", right(tmpStr, 5), " .", NULL),
-       "", " ");
-    let(&tmpStr, "");
-
-    free(stmtCount);
-    free(stmtNodeCount);
-    free(stmtDist);
-    free(stmtMaxPath);
-    free(stmtAveDist);
-    free(stmtProofLen);
-    free(stmtUsage);
-
-    /* 12-Nov-2018 nm */
-    /* Deallocate the big number strings */
-    for (stmt = 1; stmt < g_statements + 1; stmt++) {
-      let(&stmtBigCount[stmt], "");
-    }
-    free(stmtBigCount);
-
-  }
-
-  /* Deallocate local strings */ /* 12-Nov-2018 nm */
-  let(&tmpBig1, "");
-  let(&stepBigCount, "");
-
-  return(stepCount);
-
-} /* countSteps */
-
-
-/* 12-Nov-2018 nm */
-/* Add two arbitrary precision nonnegative integers represented
-   as strings of digits e.g. bigAdd("1234", "55") returns "1289".
-   Zero can be represented by "", "0", "00", etc. */
-/* This is a slow, unsophisticated algorithm intended for use by
-   countSteps() i.e. SHOW TRACE_BACK .../COUNT_STEPS */
-/* The caller must deallocate the returned string, and the string arguments
-   must not be volatile i.e. will not be freed by unrelated 'let()' */
-vstring bigAdd(vstring bignum1, vstring bignum2) {
-  long len1, len2, maxlen, p, p1, p2, p3;
-  char d1, d2, carry, dsum;
-  vstring bignum3 = "";
-  len1 = (long)strlen(bignum1);
-  len2 = (long)strlen(bignum2);
-  maxlen = (len1 < len2 ? len2 : len1);
-  let(&bignum3, space(maxlen + 1)); /* +1 to allow for final carry */
-  carry = 0;
-  for (p = 1; p <= maxlen; p++) {
-    p1 = len1 - p;
-    p2 = len2 - p;
-    d1 = (char)(p1 >= 0 ? bignum1[p1] - '0' : 0);
-    d2 = (char)(p2 >= 0 ? bignum2[p2] - '0' : 0);
-    dsum = (char)(d1 + d2 + carry);
-    if (dsum > 9) {
-      dsum = (char)(dsum - 10);
-      carry = 1;
-    } else {
-      carry = 0;
-    }
-    p3 = maxlen + 1 - p;
-    bignum3[p3] = (char)(dsum + '0');
-  }
-  bignum3[0] = (char)(carry + '0');
-  while (bignum3[0] == '0') {
-    /* Supress leading 0s */
-    let(&bignum3, right(bignum3, 2));
-  }
-  return bignum3;
-}
-
-
-/* 12-Nov-2018 nm */
-/* Subtract a nonnegative number (2nd arg) from a larger nonnegative number
-   (1st arg).  If the 1st arg is smaller than the 2nd, results are
-   not meaningful; there is no error checking for this.  */
-/* This is a slow, unsophisticated algorithm intended for use by
-   countSteps() i.e. SHOW TRACE_BACK .../COUNT_STEPS */
-/* The caller must deallocate the returned string */
-/* The arguments must be strings that will not be freed by unrelated 'let()' */
-vstring bigSub(vstring bignum1, vstring bignum2) {
-  long len1, len3, p;
-  vstring bignum3 = "";
-  vstring bignum1cmpl = "";
-  len1 = (long)strlen(bignum1);
-  let(&bignum1cmpl, space(len1));
-  for (p = 0; p <= len1 - 1; p++) {
-    /* Take 9s complement of 1st arg */
-    bignum1cmpl[p] = (char)(9 - (bignum1[p] - '0') + '0');
-  }
-  bignum3 = bigAdd(bignum1cmpl, bignum2);
-  len3 = (long)strlen(bignum3);
-  if (len3 < len1) {
-    /* We need to pad 0s before taking 9s complement */
-    let(&bignum3, cat(string(len1 - len3, '0'), bignum3, NULL));
-    len3 = len1;
-  }
-  for (p = 0; p <= len3 - 1; p++) {
-    /* Take 9s complement of result */
-    bignum3[p] = (char)(9 - (bignum3[p] - '0') + '0');
-  }
-  while (bignum3[0] == '0') {
-    /* Supress leading 0s */
-    let(&bignum3, right(bignum3, 2));
-  }
-  let(&bignum1cmpl, ""); /* Deallocate */
-  return bignum3;
-}
-
-
-/**** 12-Nov-2018 nm In case multiplication is ever needed, this
-  code will do it but is commented out because currently there
-  is no need for it.
-****************************************************************************
-/@ Multiply an arbitrary precision nonnegative integers by a positive
-   digit e.g. bigMulDigit("123", 9) returns "1107".
-   Zero can be represented by "", "0", "00", etc. @/
-/@ This is a slow, unsophisticated algorithm intended for use by
-   SHOW TRACE_BACK .../COUNT_STEPS @/
-/@ The caller must deallocate the returned string @/
-/@ The arguments must be strings that will not be freed by unrelated 'let()' @/
-vstring bigMulDigit(vstring bignum1, long digit) {
-  long len1, p, p1, p3;
-  char d1, carry, dprod;
-  vstring bignum3 = "";
-  len1 = (long)strlen(bignum1);
-  let(&bignum3, space(len1 + 1)); /@ +1 to allow for final carry @/
-  carry = 0;
-  for (p = 1; p <= len1; p++) {
-    p1 = len1 - p;
-    d1 = (char)(bignum1[p1] - '0');
-    dprod = (char)((d1 @ digit) + carry);
-    if (dprod > 9) {
-      carry = dprod / 10;
-      dprod = dprod % 10;
-    } else {
-      carry = 0;
-    }
-    p3 = len1 + 1 - p;
-    bignum3[p3] = (char)(dprod + '0');
-  }
-  bignum3[0] = (char)(carry + '0');
-  while (bignum3[0] == '0') {
-    /@ Supress leading 0s @/
-    let(&bignum3, right(bignum3, 2));
-  }
-  return bignum3;
-}
-
-/@ Multiply two arbitrary precision nonnegative integers represented
-   as strings of digits e.g. bigMul("1234", "55") returns "67870".
-   Zero can be represented by "", "0", "00", etc. @/
-/@ This is a slow, unsophisticated algorithm intended for use by
-   SHOW TRACE_BACK .../COUNT_STEPS @/
-/@ The caller must deallocate the returned string @/
-/@ The arguments must be strings that will not be freed by 'let()' @/
-vstring bigMul(vstring bignum1, vstring bignum2) {
-  long len2, p, p2;
-  char d2;
-  vstring bignum3 = "";
-  vstring bigdprod = "";
-  vstring bigpprod = "";
-  len2 = (long)strlen(bignum2);
-  for (p = 1; p <= len2; p++) {
-    p2 = len2 - p;
-    d2 = (char)(bignum2[p2] - '0');
-    if (d2 > 0) {
-      let(&bigdprod, "");
-      bigdprod = bigMulDigit(bignum1, d2);
-      if (p > 1) {
-        /@ Shift the digit product by adding trailing 0s @/
-        let(&bigdprod, cat(bigdprod, string(p - 1, '0'), NULL));
-      }
-      let(&bigpprod, "");
-      bigpprod = bigAdd(bignum3, bigdprod); /@ Accumulate partial product @/
-      let(&bignum3, bigpprod);
-    }
-  } /@ next p @/
-  let(&bigdprod, "");
-  let(&bigpprod, "");
-  return bignum3;
-}
-**** end commented out section added 12-Nov-2018 ***/
-
-
-/* Traces what statements require the use of a given statement */
-/* The output string must be deallocated by the user. */
-/* 18-Jul-2015 nm changed the meaning of the returned string as follows: */
-/* The return string [0] will be 'Y' or 'N' depending on whether there are any
-   statements that use statemNum.  Return string [i] will be 'Y' or 'N'
-   depending on whether g_Statement[i] uses statemNum.  All i will be populated
-   with 'Y'/'N' even if not $a or $p (always 'N' for non-$a,$p). */
-/* 18-Jul-2015 added optional 'cutoffStmt' parameter:  if nonzero, then
-   statements above cutoffStmt will not be scanned (for speedup) */
-vstring traceUsage(long statemNum,
-  flag recursiveFlag,
-  long cutoffStmt /* for speedup */) {
-
-  long lastPos, stmt, slen, pos;
-  flag tmpFlag;
-  vstring statementUsedFlags = ""; /* 'Y'/'N' flag that statement is used */
-  /* vstring outputString = ""; */ /* 18-Jun-2015 nm Deleted */
-  nmbrString *statementList = NULL_NMBRSTRING;
-  nmbrString *proof = NULL_NMBRSTRING;
-
-  /* For speed-up code */
-  char *fbPtr;
-  char *fbPtr2;
-  char zapSave;
-  flag notEFRec; /* Not ($e or $f or recursive) */
-
-  if (g_Statement[statemNum].type == e_ || g_Statement[statemNum].type == f_
-      || recursiveFlag) {
-    notEFRec = 0;
-  } else {
-    notEFRec = 1;
-  }
-
-  nmbrLet(&statementList, nmbrAddElement(statementList, statemNum));
-  lastPos = 1;
-
-  /* 18-Jul-2015 nm */
-  /* For speedup (in traceProofWork), scan only up to cutoffStmt if it
-     is specified, otherwise scan all statements. */
-  if (cutoffStmt == 0) cutoffStmt = g_statements;
-
-  /*for (stmt = statemNum + 1; stmt <= g_statements; stmt++) {*/ /* Scan all stmts*/
-  for (stmt = statemNum + 1; stmt <= cutoffStmt; stmt++) { /* Scan stmts*/
-    if (g_Statement[stmt].type != p_) continue; /* Ignore if not $p */
-
-    /* Speed up:  Do a character search for the statement label in the proof,
-       before parsing the proof.  Skip this if the label refers to a $e or $f
-       because these might not have their labels explicit in a compressed
-       proof.  Also, bypass speed up in case of recursive search. */
-    if (notEFRec) {
-      fbPtr = g_Statement[stmt].proofSectionPtr; /* Start of proof */
-      if (fbPtr[0] == 0) { /* The proof was never assigned */
-        continue; /* Don't bother */
-      }
-      fbPtr = fbPtr + whiteSpaceLen(fbPtr); /* Get past white space */
-      if (fbPtr[0] == '(') { /* "(" is flag for compressed proof */
-        fbPtr2 = fbPtr;
-        while (fbPtr2[0] != ')') {
-          fbPtr2++;
-          if (fbPtr2[0] == 0) bug(217); /* Didn't find closing ')' */
-        }
-      } else {
-        /* A non-compressed proof; use whole proof */
-        fbPtr2 = g_Statement[stmt].proofSectionPtr +
-            g_Statement[stmt].proofSectionLen;
-      }
-      zapSave = fbPtr2[0];
-      fbPtr2[0] = 0; /* Zap source for character string termination */
-      if (!instr(1, fbPtr, g_Statement[statemNum].labelName)) {
-        fbPtr2[0] = zapSave; /* Restore source buffer */
-        /* There is no string match for label in proof; don't bother to
-           parse. */
-        continue;
-      } else {
-        /* The label was found in the ASCII source.  Proceed with parse. */
-        fbPtr2[0] = zapSave; /* Restore source buffer */
-      }
-    } /* (End of speed-up code) */
-
-    /* 20-Oct-2013 nm Don't use bad proofs (incomplete proofs are ok) */
-    if (parseProof(stmt) > 1) {
-      /* The proof has an error, so use the empty proof */
-      nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
-    } else {
-      nmbrLet(&proof, g_WrkProof.proofString);
-    }
-
-    tmpFlag = 0;
-    for (pos = 0; pos < lastPos; pos++) {
-      if (nmbrElementIn(1, proof, statementList[pos])) {
-        tmpFlag = 1;
-        break;
-      }
-    }
-    if (!tmpFlag) continue;
-    /* The traced statement is used in this proof */
-    /* Add this statement to the statement list */
-    nmbrLet(&statementList, nmbrAddElement(statementList, stmt));
-    if (recursiveFlag) lastPos++;
-  } /* Next stmt */
-
-  slen = nmbrLen(statementList);
-
-  /* Prepare the output */
-  /* First, fill in the statementUsedFlags char array.  This allows us to sort
-     the output by statement number without calling a sort routine. */
-  let(&statementUsedFlags, string(g_statements + 1, 'N')); /* Init. to 'no' */
-  if (slen > 1) statementUsedFlags[0] = 'Y';  /* Used by at least one */
-                                              /* 18-Jul-2015 nm */
-  for (pos = 1; pos < slen; pos++) { /* Start with 1 (ignore traced statement)*/
-    stmt = statementList[pos];
-    if (stmt <= statemNum || g_Statement[stmt].type != p_ || stmt > g_statements)
-        bug(212);
-    statementUsedFlags[stmt] = 'Y';
-  }
-  return (statementUsedFlags); /* 18-Jul-2015 nm */
-
-  /********** 18-Jul-2015 nm Deleted code - this is now done by caller ******/
-  /*
-  /@ Next, build the output string @/
-  let(&outputString, "");
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    if (statementUsedFlags[stmt] == 'Y') {
-      let(&outputString, cat(outputString, " ", g_Statement[stmt].labelName,
-          NULL));
-      /@ For temporary unofficial use by NDM to help build command files: @/
-      /@
-      print2("prove %s$min %s/a$sa n/compr$q\n",
-          g_Statement[stmt].labelName,g_Statement[statemNum].labelName);
-      @/
-    } /@ End if (statementUsedFlag[stmt] == 'Y') @/
-  } /@ Next stmt @/
-
-  /@ Deallocate @/
-  let(&statementUsedFlags, "");
-  nmbrLet(&statementList, NULL_NMBRSTRING);
-  nmbrLet(&proof, NULL_NMBRSTRING);
-
-  return (outputString);
-  */
-  /*************** 18-Jul-2015 End of deleted code **************/
-
-} /* traceUsage */
-
-
-
-/* This implements the READ command (although the / VERIFY qualifier is
-   processed separately in metamath.c). */
-void readInput(void)
-{
-  vstring fullInput_fn = "";
-
-  let(&fullInput_fn, cat(g_rootDirectory, g_input_fn, NULL)); /* 31-Dec-2017 nm */
-
-  /*g_includeCalls = 0;*/ /* Initialized by readSourceAndIncludes() */
-
-  /* 31-Dec-2017 nm */
-  g_sourcePtr = readSourceAndIncludes(g_input_fn, &g_sourceLen);
-  if (g_sourcePtr == NULL) {
-    print2(
-"?Source was not read due to error(s).  Please correct and try again.\n");
-    goto RETURN_POINT;
-  }
-
-  g_sourcePtr = readRawSource(/*g_input_fn,*/  /* 2-Feb-2018 nm */
-      g_sourcePtr, &g_sourceLen);
-  parseKeywords();
-  parseLabels();
-  parseMathDecl();
-  parseStatements();
-  g_sourceHasBeenRead = 1;
-
- RETURN_POINT:
-  let(&fullInput_fn, "");
-
-} /* readInput */
-
-/* This function implements the WRITE SOURCE command. */
-/* Note that the labelSection, mathSection, and proofSection do not
-   contain keywords ($a, $p,...; $=; $.).  The keywords are added
-   by outputStatement. */
-void writeSource(/* flag cleanFlag, 3-May-2017 */ /* 1 = "/ CLEAN" qualifier was chosen */
-                flag reformatFlag /* 1 = "/ FORMAT", 2 = "/REWRAP" */,
-                /* 31-Dec-2017 nm */
-                flag splitFlag,  /* /SPLIT - write out separate $[ $] includes */
-                flag noVersioningFlag, /* /NO_VERSIONING - no ~1 backup */
-                flag keepSplitsFlag, /* /KEEP_INCLUDES - don't delete included
-                                      files when /SPIT is not specified */
-                vstring extractLabelList /* "" means /EXTRACT wasn't specified */
-                )
-{
-
-  /* Temporary variables and strings */
-  long i;
-  /* long p; */ /* deleted 3-May-2017 nm */
-  vstring buffer = "";
-  vstring fullOutput_fn = "";
-  /* long skippedCount = 0; */ /* deleted 3-May-2017 nm */
-  FILE *fp;
-
-  let(&fullOutput_fn, cat(g_rootDirectory, g_output_fn, NULL));
-
-  if (splitFlag == 0 /* If 1, it will have message from writeSplitSource() */
-      && extractLabelList[0] == 0) {  /* If non-zero, it will have messages
-                     from writeExtractedSource() */
-    print2("Writing \"%s\"...\n", fullOutput_fn);
-  }
-
-  /* 24-Aug-2020 nm */
-  if (extractLabelList[0] != 0) {
-    writeExtractedSource(
-       extractLabelList, /* EXTRACT label list argument provided by user */
-       fullOutput_fn,
-       noVersioningFlag
-       );
-    /* All the writing was done by writeExtractedSource() so just return */
-    goto RETURN_POINT;
-  }
-
-  if (reformatFlag > 0) {
-    /* 31-Dec-2017 nm */
-    /* Now the outputSource function just reformats and puts the
-       source back into the g_Statement[] array.  So we don't need
-       to do it when we're not reformatting/wrapping */
-    /* TODO: turn this into a REWRAP command */
-    /* Process statements */
-    for (i = 1; i <= g_statements + 1; i++) {
-
-      /******** deleted 3-May-2017 nm
-      /@ Added 24-Oct-03 nm @/
-      if (cleanFlag && g_Statement[i].type == (char)p_) {
-        /@ Clean out any proof-in-progress (that user has flagged with a ? in its
-           date comment field) @/
-        /@ Get the comment section after the statement @/
-        let(&str1, space(g_Statement[i + 1].labelSectionLen));
-        memcpy(str1, g_Statement[i + 1].labelSectionPtr,
-            (size_t)(g_Statement[i + 1].labelSectionLen));
-        /@ Make sure it's a date comment @/
-        let(&str1, edit(str1, 2 + 4)); /@ Discard whitespace + control chrs @/
-        p = instr(1, str1, "]$)"); /@ Get end of date comment @/
-        let(&str1, left(str1, p)); /@ Discard stuff after date comment @/
-        if (instr(1, str1, "$([") == 0) {
-          printLongLine(cat(
-              "?Warning: The proof for $p statement \"", g_Statement[i].labelName,
-              "\" does not have a date comment after it and will not be",
-              " removed by the CLEAN qualifier.", NULL), " ", " ");
-        } else {
-          /@ See if the date comment has a "?" in it @/
-          if (instr(1, str1, "?")) {
-            skippedCount++;
-            let(&str2, cat(str2, " ", g_Statement[i].labelName, NULL));
-            /@ Write at least the date from the _previous_ statement's
-               post-comment section so that WRITE RECENT can pick it up. @/
-            let(&str1, space(g_Statement[i].labelSectionLen));
-            memcpy(str1, g_Statement[i].labelSectionPtr,
-                (size_t)(g_Statement[i].labelSectionLen));
-            /@ nm 19-Jan-04 Don't discard w.s. because string will be returned to
-               the source file @/
-            /@let(&str1, edit(str1, 2 + 4));@/ /@ Discard whitespace + ctrl chrs @/
-            p = instr(1, str1, "]$)"); /@ Get end of date comment @/
-            if (p == 0) {
-              /@ In the future, "] $)" may flag date end to conform with
-                 space-around-keyword Metamath spec recommendation @/
-              /@ 7-Sep-04 The future is now @/
-              p = instr(1, str1, "] $)"); /@ Get end of date comment @/
-              if (p != 0) p++; /@ Match what it would be for old standard @/
-            }
-            if (p != 0) {  /@ The previous statement has a date comment @/
-              let(&str1, left(str1, p + 2)); /@ Discard stuff after date cmnt @/
-              if (!instr(1, str1, "?"))
-                /@ Don't bother to print $([?]$) of previous statement because
-                   the previous statement was skipped @/
-                fprintf(g_output_fp, "%s\n", str1);
-                                               /@ Output just the date comment @/
-            }
-
-            /@ Skip the normal output of this statement @/
-            continue;
-          } /@ if (instr(1, str1, "?")) @/
-        } /@ (else clause) if (instr(1, str1, "$([") == 0) @/
-      } /@ if cleanFlag @/
-      ********** end of 3-May-2017 deletion */
-
-      let(&buffer,""); /* Deallocate vstring */
-
-      /* 31-Dec-2017 nm This now only affects g_Statement[] array.  It does
-         not write any files. */
-      buffer = outputStatement(i, /* cleanFlag, 3-May-2017 */ reformatFlag);
-
-      /* fprintf(g_output_fp, "%s", str1); */ /* 31-Dec-2017 nm deleted */
-    } /* next i */
-  } /* if (reformatFlag > 0) */
-
-  /* 31-Dec-2017 nm */
-  /* Get put the g_Statement[] array into one linear buffer */
-  let(&buffer, "");
-  buffer = writeSourceToBuffer();
-  if (splitFlag == 1) { /* Write includes as separate files */
-
-    /* Make sure we aren't overwriting one of the include files */
-    for (i = 1; i <= g_includeCalls; i++) {  /* Start at 1 to skip main file */
-      if (g_IncludeCall[i].pushOrPop == 0 /* Don't include pop back to main file */
-          && !strcmp(g_output_fn, g_IncludeCall[i].included_fn)) {
-        print2(
-"?The output was not written because the main output file name is\n");
-        print2(
-"  the same as an included file.  Use a different name.\n");
-        goto RETURN_POINT;
-      }
-    }
-
-    /* Note that writeSplitSource requires a file name witout path since
-       it is called recursively for file inclusions where path is added */
-    writeSplitSource(&buffer, g_output_fn, noVersioningFlag, keepSplitsFlag);
-  } else {  /* Write a non-split version */
-    fp = fSafeOpen(fullOutput_fn, "w", noVersioningFlag);
-    if (fp == NULL) {
-      print2("?Error trying to write \"%s\".\n", fp);
-    } else {
-      fprintf(fp, "%s", buffer); /* Write the non-split output file */
-      fclose(fp);
-      if (keepSplitsFlag == 0) {
-        deleteSplits(&buffer, noVersioningFlag); /* Delete any old includes */
-      }
-    }
-  }
-
-  print2("%ld source statement(s) were written.\n", g_statements);
-
-
-  /******** deleted 3-May-2017 nm
-  /@ Added 24-Oct-03 nm @/
-  if (cleanFlag) {
-    if (skippedCount == 0) {
-      print2("No statements were deleted by the CLEAN qualifier.\n");
-    } else {
-      printLongLine(cat("The following ", str((double)skippedCount), " statement",
-          (skippedCount == 1) ? " was" : "s were",
-          " deleted by the CLEAN qualifier:", NULL), "", " ");
-      printLongLine(cat(" ", str2, NULL), "  ", " ");
-      printLongLine(cat(
-          "You should ERASE, READ the new output file, and run VERIFY PROOF",
-          " to ensure that no proof dependencies were broken.", NULL),
-          "", " ");
-    }
-  }
-  ********** end of 3-May-2017 deletion */
-
- RETURN_POINT:
-  let(&buffer,""); /* Deallocate vstring */
-  let(&fullOutput_fn,""); /* Deallocate vstring */
-  return;
-  /* let(&str2,""); */ /* Deallocate vstring */  /* deleted 3-May-2017 nm */
-} /* writeSource */
-
-
-/* 24-Aug-2020 nm */
-/* Get info for WRITE SOURCE ... / EXTRACT */
-void writeExtractedSource(
-    vstring extractLabelList, /* EXTRACT argument provided by user */
-    vstring fullOutput_fn,
-    flag noVersioningFlag)
-{
-  vstring statementUsedFlags = ""; /* Y/N flags that statement is used */
-  long stmt, stmtj, scpStmt, strtScpStmt, endScpStmt, j, p1, p2, p3, p4;
-  vstring extractNeeded = "";
-  nmbrString *unprovedList = NULL_NMBRSTRING; /* Needed for traceProofWork()
-                                                 but not used */
-  nmbrString *mstring = NULL_NMBRSTRING; /* Temporary holder for math string */
-  long maxStmt; /* The largest statement number (excluding $t) */
-  long hyp, hyps, mtkn, mtkns, dv, dvs;
-  long dollarTStmt; /* $t statement */
-  vstring dollarTCmt = ""; /* $t comment */
-  char zapChar; /* For finding $t statement */
-  char *tmpPtr; /* For finding $t statement */
-  vstring hugeHdrNeeded = ""; /* N/M/Y that output needs the huge header */
-  vstring bigHdrNeeded = "";                              /* big */
-  vstring smallHdrNeeded = "";                            /* small */
-  vstring tinyHdrNeeded = "";                             /* tiny */
-  char hdrNeeded;
-  /* The following 8 are needed for getSectionHeadings() */
-  vstring hugeHdr = "";
-  vstring bigHdr = "";
-  vstring smallHdr = "";
-  vstring tinyHdr = "";
-  vstring hugeHdrComment = "";
-  vstring bigHdrComment = "";
-  vstring smallHdrComment = "";
-  vstring tinyHdrComment = "";
-
-  vstring mathTokenDeclared = "";
-  vstring undeclaredC = "";
-  vstring undeclaredV = "";
-  long extractedStmts;
-  vstring hdrSuffix = "";
-  FILE *fp;
-  vstring buf = "";
-
-
-  /* Note that extractNeeded is 1-based to match 1-based
-     indices of the g_Statement array.  We also may need labelSection of entry
-     g_statements + 1 for text after last statement, which explains the +2. */
-  let(&extractNeeded, string(g_statements + 2, 'N'));
-
-  /* First, do the trace_backs for the statements in user's / EXTRACT argument */
-  /* By scanning backwards, we get a speedup by not having to trace
-     a statement twice, especially if we did "/ EXTRACT *" */
-  print2("Tracing back through proofs for $a and $p statements needed...\n");
-  if (!strcmp(extractLabelList, "*")) {
-    /**/
-    print2(
-       "   This may take up to 10 minutes.  (For audio alert when done,\n");
-    print2("   type ahead \"b\" then \"<enter>\".)\n");
-    /**/
-    /*
-    print2(
-       "   This may take up to 10 minutes.  (\n");
-    */
-  }
-
-  for (stmt = g_statements; stmt >= 1; stmt--) {
-    if (extractNeeded[stmt] == 'Y') {
-      /* We've already traced this back, so skip it */
-      continue;
-    }
-    /* Wildcard matching */
-    if (!matchesList(g_Statement[stmt].labelName, extractLabelList, '*', '?'))
-      continue;
-    if (g_Statement[stmt].type == (char)a_) {
-      extractNeeded[stmt] = 'Y'; /* Add in axioms but don't trace */
-      continue;
-    }
-    if (g_Statement[stmt].type != (char)p_)
-      continue; /* Not a $p statement; skip it */
-    traceProofWork(stmt,
-        0, /*essentialFlag,*/
-        "", /*traceToList,*/ /* /TO argument of SHOW TRACE_BACK */
-        &statementUsedFlags,
-        &unprovedList);
-    if ((signed)(strlen(statementUsedFlags)) != g_statements + 1) bug(268);
-    /* OR in all the statements found by the trace */
-    for (stmtj = 1; stmtj <= stmt; stmtj++) {
-      if (statementUsedFlags[stmtj] == 'Y')
-        extractNeeded[stmtj] = 'Y';
-    }
-  } /* next stmt */
-
-  /* Next, we add in all necessary  ${ $} scoping statements */
-  print2("Determining which ${ and $} scoping statements are needed...\n");
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    if (extractNeeded[stmt] == 'Y'
-        /* All flagged statements so far will be $a or $p */
-        /* Skip the forward $}'s that this loop populates (is it
-           necessary? */
-        && (g_Statement[stmt].type == a_ || g_Statement[stmt].type == p_)) {
-      scpStmt = stmt;
-      while (g_Statement[scpStmt].beginScopeStatementNum != 0) {
-        /* We're still in an inner scope */
-        strtScpStmt = g_Statement[scpStmt].beginScopeStatementNum;
-        if (g_Statement[strtScpStmt].type != lb_) bug(269);
-        if (extractNeeded[strtScpStmt] == 'Y')
-          /* We've already processed this ${ */
-          break;
-        endScpStmt = g_Statement[strtScpStmt].endScopeStatementNum;
-        if (g_Statement[endScpStmt].type != rb_) bug(270);
-        extractNeeded[strtScpStmt] = 'Y';
-        extractNeeded[endScpStmt] = 'Y';
-        scpStmt = strtScpStmt;
-      }
-    } /* if extraction needed */
-  } /* next stmt */
-
-  /* Next, we add in hypotheses and variable declarations for all $a's and $p's */
-  print2("Adding in $e and $f hypotheses and $d provisos...\n");
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    if (extractNeeded[stmt] == 'Y'
-        /* All flagged statements so far will be $a or $p */
-        /* Skip the ${'s and $}'s that earlier loop populates (is it
-           necessary? */
-        && (g_Statement[stmt].type == a_ || g_Statement[stmt].type == p_)) {
-      hyps = g_Statement[stmt].numReqHyp;
-      for (hyp = 0; hyp < hyps; hyp++) {
-        extractNeeded[g_Statement[stmt].reqHypList[hyp]] = 'Y';
-      }
-      hyps = nmbrLen(g_Statement[stmt].optHypList);
-      for (hyp = 0; hyp < hyps; hyp++) {
-        extractNeeded[g_Statement[stmt].optHypList[hyp]] = 'Y';
-      }
-      mtkns = nmbrLen(g_Statement[stmt].reqVarList);
-      for (mtkn = 0; mtkn < mtkns; mtkn++) {
-        /* Flag the $v statement for a required variable */
-        /* (This may be redundant because of next stmt loop below) */
-        extractNeeded[g_MathToken[
-            (g_Statement[stmt].reqVarList)[mtkn]].statement] = 'Y';
-      }
-      mtkns = nmbrLen(g_Statement[stmt].optVarList);
-      for (mtkn = 0; mtkn < mtkns; mtkn++) {
-        /* Flag the $v statement for an optional variable */
-        extractNeeded[g_MathToken[
-            (g_Statement[stmt].optVarList)[mtkn]].statement] = 'Y';
-      }
-      dvs = nmbrLen(g_Statement[stmt].reqDisjVarsStmt);
-      for (dv = 0; dv < dvs; dv++) {
-        /* Flag the $d statement */
-        extractNeeded[(g_Statement[stmt].reqDisjVarsStmt)[dv]] = 'Y';
-      }
-      dvs = nmbrLen(g_Statement[stmt].optDisjVarsStmt);
-      for (dv = 0; dv < dvs; dv++) {
-        /* Flag the $d statement */
-        extractNeeded[(g_Statement[stmt].optDisjVarsStmt)[dv]] = 'Y';
-      }
-    } /* if extraction needed */
-  } /* next stmt */
-
-  /* Next, add in the $c, $v required by all statements */
-  print2("Determining which $c and $v statements are needed...\n");
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    if (extractNeeded[stmt] == 'Y'
-        /* All $a, $p, $f, $e */
-        && (g_Statement[stmt].type == a_
-             || g_Statement[stmt].type == p_
-             || g_Statement[stmt].type == e_
-             || g_Statement[stmt].type == f_
-           )) {
-      nmbrLet(&mstring, g_Statement[stmt].mathString);
-      mtkns = g_Statement[stmt].mathStringLen;
-      for (mtkn = 0; mtkn < mtkns; mtkn++) {
-        /* Flag the $c or $v statement for the token */
-        extractNeeded[g_MathToken[mstring[mtkn]].statement] = 'Y';
-      }
-    } /* if extract needed */
-  } /* next stmt */
-
-  /* Get largest statement number (excluding $t comment) */
-  maxStmt = 0;
-  for (stmt = g_statements; stmt >= 1; stmt--) {
-    if (extractNeeded[stmt] == 'Y') {
-      maxStmt = stmt;
-      break;
-    }
-  }
-
-  /* Find the $t statement */  /* (Should this be done globally somewhere?) */
-  print2("Locating the $t statement if any...\n");
-  dollarTStmt = 0;
-  let(&dollarTCmt, "");
-  /* Note that g_Statement[g_statements + 1] is a special (empty) statement whose
-     labelSection holds any comment after the last statement.  It is possible
-     that the $t statement could be there. */
-  for (stmt = 1; stmt <= g_statements + 1; stmt++) {
-    /* We do low-level zapping in the xxx.mm input file buffer for speed */
-    tmpPtr = g_Statement[stmt].labelSectionPtr;
-    j = g_Statement[stmt].labelSectionLen;
-    zapChar = tmpPtr[j]; /* Save the original character */
-    tmpPtr[j] = 0; /* Create an end-of-string */
-    p1 = instr(1, tmpPtr, "$t");
-    if (p1 != 0) { /* Found the $t */
-      dollarTStmt = stmt;
-      /* Get the full $t comment */
-      p2 = instr(p1, tmpPtr, "$)");
-      let(&dollarTCmt, left(tmpPtr, p2 + 1));
-      /* We need the above because rinstr doesn't have starting arg */
-      p1 = rinstr(dollarTCmt, "$(");
-      /* Search backwards for non-space or beginning of string */
-      p1--;
-      while (p1 != 0) {
-        if (dollarTCmt[p1 - 1] != ' ') break;
-        p1--;
-      }
-      let(&dollarTCmt, cat("\n", seg(dollarTCmt, p1 + 1, p2 + 1), NULL));
-    }
-    tmpPtr[j] = zapChar; /* Restore the xxx.mm input file buffer */
-    if (dollarTStmt != 0) {
-      break; /* Found the $t, so no reason to continue */
-    }
-  }
-
-
-  /* Get header information about which headers to use */
-  print2("Analyzing scopes of section headings...\n");
-  let(&hugeHdrNeeded, string(g_statements + 2, 'N'));
-  let(&bigHdrNeeded, string(g_statements + 2, 'N'));
-  let(&smallHdrNeeded, string(g_statements + 2, 'N'));
-  let(&tinyHdrNeeded, string(g_statements + 2, 'N'));
-  /* Scan the database to determine which headers exist */
-  for (stmt = 1; stmt <= maxStmt; stmt++) {
-    getSectionHeadings(stmt, &hugeHdr, &bigHdr, &smallHdr,
-        &tinyHdr,
-        &hugeHdrComment, &bigHdrComment, &smallHdrComment,
-        &tinyHdrComment,
-        1, /* fineResolution */
-        1 /* fullComment */);
-    if (hugeHdr[0] != 0) hugeHdrNeeded[stmt] = '?'; /* Don't know yet */
-    if (bigHdr[0] != 0) bigHdrNeeded[stmt] = '?';
-    if (smallHdr[0] != 0) smallHdrNeeded[stmt] = '?';
-    if (tinyHdr[0] != 0) tinyHdrNeeded[stmt] = '?';
-  } /* next stmt */
-
-  /* For each tiny header, scan until next tiny, small, big, or huge header
-     is found (which means end of the tiny header's scope).  Set '?' to
-     'Y' if a used stmt is found along the way (meaning the header should
-     be in the output file) or to 'N' otherwise.  Then do the same starting
-     from small, then big, then huge header. */
-  for (stmt = 1; stmt <= maxStmt; stmt++) {
-    /***** deleted
-    if (g_Statement[stmt].type != a_ && g_Statement[stmt].type != p_)
-      continue;
-    *****/
-    /* We do ALL statements, not just $a, $p, so that headers will go to
-       the right place in the output file.  We called getSectionHeadings()
-       with fineResolution=1 above so that "header area" will be 1 statement
-       rather than the multiple-statement content between successive $a/$p
-       statements. */
-    if (tinyHdrNeeded[stmt] == '?') {
-      hdrNeeded = 0;
-      for (stmtj = stmt; stmtj <= maxStmt; stmtj++) {
-        if (hugeHdrNeeded[stmtj] != 'N' || bigHdrNeeded[stmtj] != 'N'
-            || smallHdrNeeded[stmtj] != 'N' || tinyHdrNeeded[stmtj] != 'N') {
-          /* The scope of header at stmt has ended with no used statement
-             found, so header is not needed; abort the scan */
-          if (stmtj > stmt) break; /* Ignore starting point of scan since we
-              are looking for a later header to end the scope */
-        }
-        if (extractNeeded[stmtj] == 'Y') {
-          hdrNeeded = 1;
-          /* We now know the header is needed, so abort the scan */
-          break;
-        }
-      }
-      if (hdrNeeded == 1) {
-        tinyHdrNeeded[stmt] = 'Y';
-      } else {
-        tinyHdrNeeded[stmt] = 'N';
-      }
-    } /* if tinyHdrNeeded[stmt] == '?' */
-    if (smallHdrNeeded[stmt] == '?') {
-      hdrNeeded = 0;
-      for (stmtj = stmt; stmtj <= maxStmt; stmtj++) {
-        if (hugeHdrNeeded[stmtj] != 'N' || bigHdrNeeded[stmtj] != 'N'
-            || smallHdrNeeded[stmtj] != 'N') {
-          /* The scope of header at stmt has ended with no used statement
-             found, so header is not needed; abort the scan */
-          if (stmtj > stmt) break; /* Ignore starting point of scan since we
-              are looking for a later header to end the scope */
-        }
-        if (extractNeeded[stmtj] == 'Y') {
-          hdrNeeded = 1;
-          /* We now know the header is needed, so abort the scan */
-          break;
-        }
-      }
-      if (hdrNeeded == 1) {
-        smallHdrNeeded[stmt] = 'Y';
-      } else {
-        smallHdrNeeded[stmt] = 'N';
-      }
-    } /* if smallHdrNeeded[stmt] == '?' */
-    if (bigHdrNeeded[stmt] == '?') {
-      hdrNeeded = 0;
-      /*for (stmtj = stmt + 1; stmtj <= maxStmt; stmtj++) {*/
-      for (stmtj = stmt; stmtj <= maxStmt; stmtj++) {
-        if (hugeHdrNeeded[stmtj] != 'N' || bigHdrNeeded[stmtj] != 'N') {
-          /* The scope of header at stmt has ended with no used statement
-             found, so header is not needed; abort the scan */
-          if (stmtj > stmt) break;
-        }
-        if (extractNeeded[stmtj] == 'Y') {
-          hdrNeeded = 1;
-          /* We now know the header is needed, so abort the scan */
-          break;
-          /*if (stmtj > stmt) break;*/ /* Ignore starting point of scan since we
-              are looking for a later header to end the scope */
-        }
-      }
-      if (hdrNeeded == 1) {
-        bigHdrNeeded[stmt] = 'Y';
-      } else {
-        bigHdrNeeded[stmt] = 'N';
-      }
-    } /* if bigHdrNeeded[stmt] == '?' */
-    if (hugeHdrNeeded[stmt] == '?') {
-      hdrNeeded = 0;
-      for (stmtj = stmt; stmtj <= maxStmt; stmtj++) {
-        if (hugeHdrNeeded[stmtj] != 'N') {
-          /* The scope of header at stmt has ended with no used statement
-             found, so header is not needed; abort the scan */
-          if (stmtj > stmt) break; /* Ignore starting point of scan since we
-              are looking for a later header to end the scope */
-        }
-        if (extractNeeded[stmtj] == 'Y') {
-          hdrNeeded = 1;
-          /* We now know the header is needed, so abort the scan */
-          break;
-        }
-      }
-      if (hdrNeeded == 1) {
-        hugeHdrNeeded[stmt] = 'Y';
-      } else {
-        hugeHdrNeeded[stmt] = 'N';
-      }
-    } /* if hugeHdrNeeded[stmt] == '?' */
-  } /* next stmt */
-
-  /* Collect all $c and $v tokens that are not declared in the
-     extract, so they can be included in the output .mm to satisfy
-     the htmldefs and for use in ` math ` comment markup */
-  print2("Building $c and $v statements for unused math tokens...\n");
-  let(&mathTokenDeclared, string(g_mathTokens, 'N'));
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    if (extractNeeded[stmt] == 'Y'
-       && (g_Statement[stmt].type == c_
-           || g_Statement[stmt].type == v_)) {
-      mtkns = g_Statement[stmt].mathStringLen;
-      for (mtkn = 0; mtkn < mtkns; mtkn++) {
-        /* Flag the math token as being declared */
-        mathTokenDeclared[(g_Statement[stmt].mathString)[mtkn]] = 'Y';
-      }
-    }
-  }
-  /* Build $c and $v statements for undeclared math tokens.  They are used to
-     make the database consistent with the htmldef's in the $t comment. */
-  let(&undeclaredC, "");
-  let(&undeclaredV, "");
-  for (mtkn = 0; mtkn < g_mathTokens; mtkn++) {
-    if (mathTokenDeclared[mtkn] == 'N') {
-      if (g_MathToken[mtkn].tokenType == con_) {
-        let(&undeclaredC, cat(undeclaredC, " ", g_MathToken[mtkn].tokenName,
-            NULL));
-      } else {
-        if (g_MathToken[mtkn].tokenType != var_) bug(271);
-        /* Before adding it to the unused var list, make sure it isn't declared
-           in another scope */
-        p1 = 0;
-        for (j = 0; j < g_mathTokens; j++) {
-          if (j == mtkn) continue;
-          if (!strcmp(g_MathToken[mtkn].tokenName, g_MathToken[j].tokenName)) {
-            /* See if it the $v was already declared in another scope */
-            if (mathTokenDeclared[j] == 'Y') {
-              p1 = 1;
-              break;
-            }
-          }
-        }
-        if (p1 == 0) {
-          let(&undeclaredV, cat(undeclaredV, " ", g_MathToken[mtkn].tokenName,
-            NULL));
-        }
-        /* 4-Sep-2020 nm */
-        /* Tag the variable as declared for use in later j loops above, so it
-           won't be added to the undeclaredV twice */
-        mathTokenDeclared[mtkn] = 'Y';
-      }
-    }
-  } /* next mtkn */
-
-/*
-/@D@/for(stmt=1;stmt<=2;stmt++)
-/@D@/printf("s=%ld t=%c cs=%ld bs=%ld es=%ld en=%c %c%c%c%c\n",
-/@D@/  stmt,g_Statement[stmt].type,(long)g_Statement[stmt].scope,
-/@D@/ g_Statement[stmt].beginScopeStatementNum,
-/@D@/ g_Statement[stmt].endScopeStatementNum,extractNeeded[stmt],
-/@D@/ hugeHdrNeeded[stmt],bigHdrNeeded[stmt],
-/@D@/ smallHdrNeeded[stmt],tinyHdrNeeded[stmt]);
-*/
-
-  /* Write the output file */
-  /* (We don't call the standard output functions because there's too
-     much customization needed) */
-  print2("Creating the final output file \"%s\"...\n", fullOutput_fn);
-
-  fp = fSafeOpen(fullOutput_fn, "w", noVersioningFlag);
-  if (fp == NULL) {
-    print2("?Error trying to write \"%s\".\n", fp);
-    goto EXTRACT_RETURN;
-  }
-
-  /* Get the first line of the .mm file that normally has version and date */
-  /* (The memcpy to buf is not really necessary, just a safety
-     measure in case the input file has garbage with no "\n".) */
-  let(&buf, space(g_Statement[1].labelSectionLen));
-  memcpy(buf, g_Statement[1].labelSectionPtr,
-      (size_t)(g_Statement[1].labelSectionLen));
-  let(&buf, left(buf, instr(1, buf, "\n") - 1)); /* This will not include the \n */
-  let(&buf, right(edit(buf, 8/*leading space*/), 3)); /* Take off $( */
-  j = (long)strlen(" Extracted from: ");
-  if (!strcmp(left(buf, j), " Extracted from: ")) {
-    /* Prevent "Extracted from:" accumulation if sent through /EXTRACT again */
-    let(&buf, right(buf, j + 1));
-  }
-  fprintf(fp, "$( Extracted from: %s", buf);
-  if (instr(1, buf, "$)") == 0) fprintf(fp, " $)");
-  fprintf(fp,
-      "\n$( Created %s %s using \"READ '%s'\" then\n",
-      date(), time_(), g_input_fn);
-  fprintf(fp,
-      "   \"WRITE SOURCE '%s' /EXTRACT %s\" $)",
-      fullOutput_fn, extractLabelList);
-
-  extractedStmts = 0; /* How many statements were extracted */
-  for (stmt = 1; stmt <= g_statements + 1; stmt++) {
-    /* If the header is part of the labelSection of an extracted stmt,
-       we don't want to add a newline in order for extractions to be
-       stable (i.e. more lines aren't added when we extract from an
-       extraction). */
-    if (extractNeeded[stmt] == 'Y') {
-      let(&hdrSuffix, "");
-    } else {
-      let(&hdrSuffix, "\n");
-    }
-    /* Output headers if needed */
-    if (hugeHdrNeeded[stmt] == 'Y'
-        || bigHdrNeeded[stmt] == 'Y'
-        || smallHdrNeeded[stmt] == 'Y'
-        || tinyHdrNeeded[stmt] == 'Y') {
-      getSectionHeadings(stmt, &hugeHdr, &bigHdr, &smallHdr, &tinyHdr,
-          &hugeHdrComment, &bigHdrComment, &smallHdrComment,
-          &tinyHdrComment,
-          1, /*fineResolution*/
-          1 /*fullComment*/ /* 12-Sep-2020 nm */
-          );
-      let(&buf, "");
-      if (hugeHdrNeeded[stmt] == 'Y') {
-        fixUndefinedLabels(extractNeeded, &hugeHdrComment);
-        /**** 12-Sep-2020 nm Deleted
-        let(&buf, "");
-        buf = buildHeader(hugeHdr, hugeHdrComment, HUGE_DECORATION);
-        fprintf(fp, "%s", buf);
-        ****/
-        /* 12-Sep-2020 nm */
-        fprintf(fp, "%s", cat(hugeHdr, hugeHdrComment, hdrSuffix, NULL));
-      }
-      if (bigHdrNeeded[stmt] == 'Y') {
-        fixUndefinedLabels(extractNeeded, &bigHdrComment);
-        /**** 12-Sep-2020 nm Deleted
-        let(&buf, "");
-        buf = buildHeader(bigHdr, bigHdrComment, HUGE_DECORATION);
-        fprintf(fp, "%s", buf);
-        ****/
-        /* 12-Sep-2020 nm */
-        fprintf(fp, "%s", cat(bigHdr, bigHdrComment, hdrSuffix, NULL));
-      }
-      if (smallHdrNeeded[stmt] == 'Y') {
-        fixUndefinedLabels(extractNeeded, &smallHdrComment);
-        /**** 12-Sep-2020 nm Deleted
-        let(&buf, "");
-        buf = buildHeader(smallHdr, smallHdrComment, SMALL_DECORATION);
-        fprintf(fp, "%s", buf);
-        ****/
-        /* 12-Sep-2020 nm */
-        fprintf(fp, "%s", cat(smallHdr, smallHdrComment, hdrSuffix, NULL));
-      }
-      if (tinyHdrNeeded[stmt] == 'Y') {
-        fixUndefinedLabels(extractNeeded, &tinyHdrComment);
-        /**** 12-Sep-2020 nm Deleted
-        let(&buf, "");
-        buf = buildHeader(tinyHdr, tinyHdrComment, TINY_DECORATION);
-        fprintf(fp, "%s", buf);
-        ****/
-        /* 12-Sep-2020 nm */
-        fprintf(fp, "%s", cat(tinyHdr, tinyHdrComment, hdrSuffix, NULL));
-      }
-    } /* if header(s) needed */
-
-    /* Output $t statement if needed */
-    if (dollarTStmt == stmt) {
-      fprintf(fp, "\n%s", dollarTCmt);
-    }
-
-    /* Output statement if needed */
-    if (extractNeeded[stmt] == 'Y') {
-      let(&buf, "");
-      buf = getDescriptionAndLabel(stmt);
-
-      fixUndefinedLabels(extractNeeded, &buf);
-
-      fprintf(fp, "%s", buf);
-      if (stmt == g_statements + 1) bug(272); /* Text below last statement
-           isn't (currently) used - do we need it? */
-      if (stmt != g_statements + 1) {
-        extractedStmts++; /* For final message */
-        fprintf(fp, "$%c", g_Statement[stmt].type);
-        if (g_Statement[stmt].type != lb_ && g_Statement[stmt].type != rb_) {
-          /* $v $c $d $e $f $a $p */
-          let(&buf, space(g_Statement[stmt].mathSectionLen));
-          memcpy(buf, g_Statement[stmt].mathSectionPtr,
-              (size_t)(g_Statement[stmt].mathSectionLen));
-          fprintf(fp, "%s", buf);
-          if (g_Statement[stmt].type != p_) {
-            fprintf(fp, "$.");
-/*D*//*fprintf(fp, "#%ld#",stmt);*/
-          } else {
-            /* $p */
-            fprintf(fp, "$=");
-            let(&buf, space(g_Statement[stmt].proofSectionLen));
-            memcpy(buf, g_Statement[stmt].proofSectionPtr,
-                (size_t)(g_Statement[stmt].proofSectionLen));
-            fprintf(fp, "%s$.", buf);
-          }
-        } /* if not ${ $} */
-        if (extractNeeded[stmt + 1] == 'N') {
-/*D*//*printf("added \\n stmt=%ld type=%c,%c\n",stmt+1,g_Statement[stmt].type,g_Statement[stmt+1].type);*/
-          /* Put a newline following end of statement since the next
-             statement's label section will be suppressed */
-          fprintf(fp, "\n");
-        }
-      } /* if (stmt != statements + 1) */
-
-    } /* if (extractNeeded[stmt] == 'Y') */
-  } /* next stmt */
-
-  /* Add in unused $c, $v at the end to satisfy htmldefs */
-  if (g_outputToString == 1) bug(273); /* Should be turned off here */
-  if (g_printString[0] != 0) bug(274);
-  g_outputToString = 1;
-  if (undeclaredC[0] != 0) {
-    print2("\n");
-    print2(  /* 4-Sep-2020 nm Can't use literal "$t"; change to $ t. */
-"  $( Unused constants to satisfy the htmldef's in the $ t comment. $)\n");
-    printLongLine(cat("  $c", undeclaredC, " $.", NULL), "    ", " ");
-  }
-  if (undeclaredV[0] != 0) {
-    print2("\n");
-    print2(  /* 4-Sep-2020 nm Can't use literal "$t"; change to $ t. */
-"  $( Unused variables to satisfy the htmldef's in the $ t comment. $)\n");
-    printLongLine(cat("  $v", undeclaredV, " $.", NULL), "    ", " ");
-  }
-  g_outputToString = 0;
-  if (g_printString[0] != 0) {
-    fprintf(fp, "%s", g_printString);
-    let(&g_printString, "");
-  }
-
-  /* Write the non-split output file */
-  fclose(fp);
-  /*print2("%ld source statement(s) were extracted.\n", extractedStmts);*/
-  /* 5-Sep-2020 nm */
-  j = 0; p1 = 0; p2 = 0; p3 = 0; p4 = 0;
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    if (extractNeeded[stmt] == 'Y') {
-      j++;
-      if (g_Statement[stmt].type == a_) {
-        p1++;
-        if (!strcmp("ax-", left(g_Statement[stmt].labelName, 3))) p3++;
-        if (!strcmp("df-", left(g_Statement[stmt].labelName, 3))) p4++;
-        let(&buf, ""); /* Deallocate stack created by left() */
-      }
-      if (g_Statement[stmt].type == p_) p2++;
-    }
-  }
-  print2(
-"Extracted %ld statements incl. %ld $a (%ld \"ax-\", %ld \"df-\"), %ld $p.\n",
-      j, p1, p3, p4, p2);
-
- EXTRACT_RETURN:
-  /* Deallocate */
-  let(&extractNeeded, "");
-  let(&statementUsedFlags, "");
-  nmbrLet(&unprovedList, NULL_NMBRSTRING);
-  nmbrLet(&mstring, NULL_NMBRSTRING);
-  let(&dollarTCmt, "");
-  let(&hugeHdrNeeded, "");
-  let(&bigHdrNeeded, "");
-  let(&smallHdrNeeded, "");
-  let(&tinyHdrNeeded, "");
-  let(&hugeHdr, "");   /* Deallocate memory */
-  let(&bigHdr, "");   /* Deallocate memory */
-  let(&smallHdr, ""); /* Deallocate memory */
-  let(&tinyHdr, ""); /* Deallocate memory */
-  let(&hugeHdrComment, "");   /* Deallocate memory */
-  let(&bigHdrComment, "");   /* Deallocate memory */
-  let(&smallHdrComment, ""); /* Deallocate memory */
-  let(&tinyHdrComment, ""); /* Deallocate memory */
-  let(&mathTokenDeclared, "");
-  let(&undeclaredC, "");
-  let(&undeclaredV, "");
-  let(&buf, "");
-  return;
-} /* getExtractionInfo */
-
-
-/* 24-Aug-2020 nm */
-/* Some labels in comments may not exist in statements extracted
-   with WRITE SOURCE ... / EXTRACT.  This function changes them
-   to external links to us.metamath.org. */
-void fixUndefinedLabels(vstring extractNeeded/*'Y'/'N' list*/,
-    vstring *buf/*header comment*/) {
-  long p1, p2, p3;
-  vstring label = "";
-  vstring newLabelWithTilde = "";
-  vstring restOfComment = "";
-  int mathMode; /* char gives Wconversion gcc warning */
-#define ASCII_4 4
-
-  /* 12-Sep-2020 nm */
-  /* Change ~ in math symbols to nonprintable ASCII 4 to prevent
-     interpretation as label indicator */
-  p1 = (long)strlen(*buf);
-  mathMode = 0;
-  for (p2 = 0; p2 < p1; p2++) {
-    if ((*buf)[p2] == '`') {
-      mathMode = 1 - mathMode;
-      continue;
-    }
-    if ((*buf)[p2] == '~' && mathMode == 1) {
-      (*buf)[p2] = ASCII_4;
-    }
-  }
-
-  p1 = 0;
-  let(&(*buf), cat(*buf, " \n", NULL)); /* Ensure white space after last label */
-  while (1) {
-    p1 = instr(p1 + 1, *buf, "~");
-    if (p1 == 0) break;
-    if (p1 - 2 >= 0) { /* Prevent out-of-bounds access */
-      if ((*buf)[p1 - 2] == '~') {
-        continue; /* it is a ~~ escape; don't process */
-      }
-    }
-    while (1) {
-      /* Get beyond any whitespace between ~ and label */
-      if ((*buf)[p1 + 1] == 0) break; /* end of string */
-      if ((*buf)[p1 + 1] != ' ' && (*buf)[p1 + 1] != '\n') {
-        /* Found a non-space character, assume it is start of label */
-        break;
-      }
-      p1++; /* Move past the whitespace */
-    }
-    if ((*buf)[p1 + 1] == 0) break; /* Ignore stray ~ at end of comment */
-    p2 = instr(p1 + 2, *buf, " ");
-    p3 = instr(p1 + 2, *buf, "\n");
-    if (p3 < p2) p2 = p3;  /* p2 is end of label */
-    let(&label, seg(*buf, p1 + 2, p2 - 1));
-    let(&restOfComment, right(*buf, p2));
-    /*** 4-Sep-2020 nm This is taken care of by lookupLabel
-    if (instr(1, label, "//" /@ http[s]:// @/) continue; /@ Ignore special case @/
-    if (!strcmp(left(label, 2)), "mm") continue; /@ Ignore special case @/
-    ***/
-    p3 = lookupLabel(label);
-    if (p3 == -1) continue; /* Not a statement label (e.g. ~ http://...) */
-    if (extractNeeded[p3] == 'Y') continue; /* Label link won't be broken */
-    /* Change label to external link */
-    let(&newLabelWithTilde, cat(label,
-        "\n ~ http://us.metamath.org/mpeuni/",
-        label, ".html", NULL));
-    let(&(*buf), cat(left(*buf, p1 - 1), newLabelWithTilde, NULL));
-    /* Adjust pointer to go past the modified label */
-    p1 = p1 + (long)strlen(newLabelWithTilde)
-          - ((long)strlen(label) + 2/*for "~ "*/);
-    /* 4-Sep-2020 nm */
-    /* Put newline if not at end of line - assumes no trailing spaces in .mm! */
-    /* We do this to prevent too-long lines.  But if we're already at end
-       of line, we don't want to create a paragraph, so we don't add newline. */
-    if (restOfComment[0] == '\n') {
-      let(&(*buf), cat(*buf, restOfComment, NULL));
-    } else {
-      let(&(*buf), cat(*buf, "\n", restOfComment, NULL));
-    }
-  }
-  let(&(*buf), left(*buf, (long)strlen(*buf) - 2));
-       /* Take off the '\n' we added at beginning of this function */
-
-  /* 12-Sep-2020 nm */
-  /* Restore ASCII 4 to ~ */
-  p1 = (long)strlen(*buf);
-  for (p2 = 0; p2 < p1; p2++) {
-    if ((*buf)[p2] == ASCII_4) (*buf)[p2] = '~';
-  }
-
-  let(&label, ""); /* Deallocate */
-  let(&newLabelWithTilde, ""); /* Deallocate */
-  let(&restOfComment, ""); /* Deallocate */
-  return;
-} /* fixUndefinedLabels */
-
-
-void writeDict(void)
-{
-  print2("This function has not been implemented yet.\n");
-  return;
-} /* writeDict */
-
-/* Free up all memory space and initialize all variables */
-void eraseSource(void)    /* ERASE command */
-{
-  long i;
-  vstring tmpStr = "";
-
-  /* 24-Jun-2014 nm */
-  /* Deallocate g_WrkProof structure if g_wrkProofMaxSize != 0 */
-  /* Assigned in parseProof() in mmpars.c */
-  if (g_wrkProofMaxSize) { /* It has been allocated */
-    free(g_WrkProof.tokenSrcPtrNmbr);
-    free(g_WrkProof.tokenSrcPtrPntr);
-    free(g_WrkProof.stepSrcPtrNmbr);
-    free(g_WrkProof.stepSrcPtrPntr);
-    free(g_WrkProof.localLabelFlag);
-    free(g_WrkProof.hypAndLocLabel);
-    free(g_WrkProof.localLabelPool);
-    poolFree(g_WrkProof.proofString);
-    free(g_WrkProof.mathStringPtrs);
-    free(g_WrkProof.RPNStack);
-    free(g_WrkProof.compressedPfLabelMap);
-    g_wrkProofMaxSize = 0;
-  }
-
-  if (g_statements == 0) {
-    /* Already called */
-    memFreePoolPurge(0);
-    return;
-  }
-
-  for (i = 0; i <= g_includeCalls; i++) {
-    let(&g_IncludeCall[i].source_fn, "");
-    let(&g_IncludeCall[i].included_fn, "");
-    let(&g_IncludeCall[i].current_includeSource, "");
-  }
-  g_includeCalls = -1;
-
-  /* Deallocate the g_Statement[] array */
-  for (i = 1; i <= g_statements + 1; i++) { /* g_statements + 1 is a dummy statement
-                                          to hold source after last statement */
-
-    /* 28-Aug-2013 am - g_Statement[stmt].reqVarList allocated in
-       parseStatements() was not always freed by eraseSource().  If reqVars==0
-       (in parseStatements()) then g_Statement[i].reqVarList[0]==-1 (in
-       eraseSource()) and so eraseSource() thought that
-       g_Statement[stmt].reqVarList was not allocated and should not be freed.
-
-       There were similar problems for reqHypList, reqDisjVarsA, reqDisjVarsB,
-       reqDisjVarsStmt, optDisjVarsA, optDisjVarsB, optDisjVarsStmt.
-
-       These were fixed by comparing to NULL_NMBRSTRING instead of -1.
-
-       I think other files (mathString, proofString, optHypList, optVarList)
-       should be also fixed, but I could not find simple example for memory
-       leak and leave it as it was.  */
-    if (g_Statement[i].labelName[0]) free(g_Statement[i].labelName);
-    /*if (g_Statement[i].mathString[0] != -1)*/
-    if (g_Statement[i].mathString != NULL_NMBRSTRING) /* 14-May-2014 nm */
-        poolFree(g_Statement[i].mathString);
-    /*if (g_Statement[i].proofString[0] != -1)*/
-    if (g_Statement[i].proofString != NULL_NMBRSTRING) /* 14-May-2014 nm */
-        poolFree(g_Statement[i].proofString);
-    if (g_Statement[i].reqHypList != NULL_NMBRSTRING)
-        poolFree(g_Statement[i].reqHypList);
-    /*if (g_Statement[i].optHypList[0] != -1)*/
-    if (g_Statement[i].optHypList != NULL_NMBRSTRING) /* 14-May-2014 nm */
-        poolFree(g_Statement[i].optHypList);
-    if (g_Statement[i].reqVarList != NULL_NMBRSTRING)
-        poolFree(g_Statement[i].reqVarList);
-    /*if (g_Statement[i].optVarList[0] != -1)*/
-    if (g_Statement[i].optVarList != NULL_NMBRSTRING) /* 14-May-2014 nm */
-        poolFree(g_Statement[i].optVarList);
-    if (g_Statement[i].reqDisjVarsA != NULL_NMBRSTRING)
-        poolFree(g_Statement[i].reqDisjVarsA);
-    if (g_Statement[i].reqDisjVarsB != NULL_NMBRSTRING)
-        poolFree(g_Statement[i].reqDisjVarsB);
-    if (g_Statement[i].reqDisjVarsStmt != NULL_NMBRSTRING)
-        poolFree(g_Statement[i].reqDisjVarsStmt);
-    if (g_Statement[i].optDisjVarsA != NULL_NMBRSTRING)
-        poolFree(g_Statement[i].optDisjVarsA);
-    if (g_Statement[i].optDisjVarsB != NULL_NMBRSTRING)
-        poolFree(g_Statement[i].optDisjVarsB);
-    if (g_Statement[i].optDisjVarsStmt != NULL_NMBRSTRING)
-        poolFree(g_Statement[i].optDisjVarsStmt);
-
-    /**** 3-May-17 deleted
-    /@ See if the proof was "saved" @/
-    if (g_Statement[i].proofSectionLen) {
-      if (g_Statement[i].proofSectionPtr[-1] == 1) {
-        /@ Deallocate proof if not original source @/
-        /@ (ASCII 1 is the flag for this) @/
-        tmpStr = g_Statement[i].proofSectionPtr - 1;
-        let(&tmpStr, "");
-      }
-    }
-    ********/
-
-    /* 3-May-2017 nm */
-    if (g_Statement[i].labelSectionChanged == 1) {
-      /* Deallocate text before label if not original source */
-      let(&(g_Statement[i].labelSectionPtr), "");
-    }
-    if (g_Statement[i].mathSectionChanged == 1) {
-      /* Deallocate math symbol text if not original source */
-      let(&(g_Statement[i].mathSectionPtr), "");
-    }
-    if (g_Statement[i].proofSectionChanged == 1) {
-      /* Deallocate proof if not original source */
-      let(&(g_Statement[i].proofSectionPtr), "");
-    }
-
-  } /* Next i (statement) */
-
-  /* 28-Aug-2013 am - g_MathToken[g_mathTokens].tokenName is assigned in
-     parseMathDecl() by let().  eraseSource() should free every g_MathToken and
-     there are (g_mathTokens + g_dummyVars) tokens. */
-  for (i = 0; i <= g_mathTokens + g_dummyVars; i++) {
-    let(&(g_MathToken[i].tokenName), "");
-  }
-
-  memFreePoolPurge(0);
-  /*g_statements = 0;*/ /* Must be done below */
-  g_errorCount = 0;
-
-  free(g_Statement);
-  free(g_IncludeCall);  /* Will be init'ed in initBigArrays */
-  free(g_MathToken);
-  g_dummyVars = 0; /* For Proof Assistant */
-  free(g_sourcePtr);
-  free(g_labelKey);
-  free(g_mathKey); /* 4-May-2017 Ari Ferrera */
-  free(g_allLabelKeyBase);
-
-  /* Deallocate the g_WrkProof structure */
-  /*???*/
-
-  /* Deallocate the texdef/htmldef storage */ /* Added 27-Oct-2012 nm */
-  eraseTexDefs(); /* 17-Nov-2015 nm */
-
-  g_extHtmlStmt = 0; /* May be used by a non-zero test; init to be safe */
-
-  /* 5-Aug-2020 nm */
-  /* Initialize and deallocate mathbox information */
-  g_mathboxStmt = 0; /* Used by a non-zero test in mmwtex.c to see if assigned */
-  nmbrLet(&g_mathboxStart, NULL_NMBRSTRING);
-  nmbrLet(&g_mathboxEnd, NULL_NMBRSTRING);
-  for (i = 1; i <= g_mathboxes; i++) {
-    let((vstring *)(&g_mathboxUser[i - 1]), "");
-  }
-  pntrLet(&g_mathboxUser, NULL_PNTRSTRING);
-  g_mathboxes = 0;
-
-  /* Allocate big arrays */
-  initBigArrays();
-
-  /* 2-Oct-2017 nm Future possibilty: add 'reset' parameter to unify() to clear
-     the 5 variables below */
-  g_bracketMatchInit = 0; /* Clear to force mmunif.c to scan $a's again */
-  g_minSubstLen = 1; /* Initialize to the default SET EMPTY_SUBSTITUTION OFF */
-  /* 1-Oct-2017 nm Fix 'erase' bug found by Benoit Jubin */
-  /* Clear g_firstConst to trigger clearing of g_lastConst and
-     g_oneConst in mmunif.c */
-  nmbrLet(&g_firstConst, NULL_NMBRSTRING);
-  /* 2-Oct-2017 nm Clear these directly so they will be truly deallocated
-     for valgrind */
-  nmbrLet(&g_lastConst, NULL_NMBRSTRING);
-  nmbrLet(&g_oneConst, NULL_NMBRSTRING);
-
-  /* 3-May-2016 nm */
-  getMarkupFlag(0, RESET); /* Erase the cached markup flag storage */
-
-  /* 2-May-2017 nm */
-  /* Erase the contributor markup cache */
-  /* 3-May-2017 nm */
-  let(&tmpStr, "");
-  tmpStr = getContrib(0 /*stmt is ignored*/, GC_RESET);
-  /****** old 3-May-2017
-  /@ The string arguments are not assigned in RESET mode. @/
-  getContrib(0, &tmpStr, &tmpStr,
-      &tmpStr, &tmpStr, &tmpStr, &tmpStr,
-      &tmpStr,
-      0/@printErrorsFlag@/,
-      RESET/@mode: 0 == RESET = reset, 1 = normal @/);
-  *********/
-
-  /* 2-May-2017 nm */
-  /* getContrib uses g_statements (global var), so don't do this earlier */
-  g_statements = 0; /* getContrib uses g_statements for loop limit */
-
-} /* eraseSource */
-
-
-/* If verify = 0, parse the proofs only for gross error checking.
-   If verify = 1, do the full verification. */
-void verifyProofs(vstring labelMatch, flag verifyFlag) {
-  vstring emptyProofList = "";
-  long i, k;
-  long lineLen = 0;
-  vstring header = "";
-  flag errorFound;
-#ifdef CLOCKS_PER_SEC
-  clock_t clockStart;  /* 28-May-04 nm */
-#endif
-
-#ifdef __WATCOMC__
-  vstring tmpStr="";
-#endif
-
-#ifdef VAXC
-  vstring tmpStr="";
-#endif
-
-#ifdef CLOCKS_PER_SEC
-  clockStart = clock();  /* 28-May-04 nm Retrieve start time */
-#endif
-  if (!strcmp("*", labelMatch) && verifyFlag) {
-    /* Use status bar */
-    let(&header, "0 10%  20%  30%  40%  50%  60%  70%  80%  90% 100%");
-#ifdef XXX /*__WATCOMC__*/
-    /* The vsprintf function discards text after "%" in 3rd argument string. */
-    /* This is a workaround. */
-    for (i = 1; i <= (long)strlen(header); i++) {
-      let(&tmpStr, mid(header, i, 1));
-      if (tmpStr[0] == '%') let(&tmpStr, "%%");
-      print2("%s", tmpStr);
-    }
-    print2("\n");
-#else
-#ifdef XXX /*VAXC*/
-    /* The vsprintf function discards text after "%" in 3rd argument string. */
-    /* This is a workaround. */
-    for (i = 1; i <= (long)strlen(header); i++) {
-      let(&tmpStr, mid(header, i, 1));
-      if (tmpStr[0] == '%') let(&tmpStr, "%%");
-      print2("%s", tmpStr);
-    }
-    print2("\n");
-#else
-    print2("%s\n", header);
-    let(&header, "");
-#endif
-#endif
-  }
-
-  errorFound = 0;
-  for (i = 1; i <= g_statements; i++) {
-    if (!strcmp("*", labelMatch) && verifyFlag) {
-      while (lineLen < (50 * i) / g_statements) {
-        print2(".");
-        lineLen++;
-      }
-    }
-
-    if (g_Statement[i].type != p_) continue;
-    /* 30-Jan-06 nm Added single-character-match argument */
-    if (!matchesList(g_Statement[i].labelName, labelMatch, '*', '?')) continue;
-    if (strcmp("*",labelMatch) && verifyFlag) {
-      /* If not *, print individual labels */
-      lineLen = lineLen + (long)strlen(g_Statement[i].labelName) + 1;
-      if (lineLen > 72) {
-        lineLen = (long)strlen(g_Statement[i].labelName) + 1;
-        print2("\n");
-      }
-      print2("%s ",g_Statement[i].labelName);
-    }
-
-    k = parseProof(i);
-    if (k >= 2) errorFound = 1;
-    if (k < 2) { /* $p with no error */
-      if (verifyFlag) {
-        if (verifyProof(i) >= 2) errorFound = 1;
-        cleanWrkProof(); /* Deallocate verifyProof storage */
-      }
-    }
-    if (k == 1) {
-      let(&emptyProofList, cat(emptyProofList, ", ", g_Statement[i].labelName,
-          NULL));
-    }
-  }
-  if (verifyFlag) {
-    print2("\n");
-  }
-
-  if (emptyProofList[0]) {
-    printLongLine(cat(
-        "Warning: The following $p statement(s) were not proved:  ",
-        right(emptyProofList,3), NULL)," ","  ");
-  }
-  if (!emptyProofList[0] && !errorFound && !strcmp("*", labelMatch)) {
-    if (verifyFlag) {
-#ifdef CLOCKS_PER_SEC    /* 28-May-04 nm */
-      print2("All proofs in the database were verified in %1.2f s.\n",
-           (double)((1.0 * (double)(clock() - clockStart)) / CLOCKS_PER_SEC));
-#else
-      print2("All proofs in the database were verified.\n");
-#endif
-
-    } else {
-      print2("All proofs in the database passed the syntax-only check.\n");
-    }
-  }
-  let(&emptyProofList, ""); /* Deallocate */
-
-} /* verifyProofs */
-
-
-/* 7-Nov-2015 nm Added this function for date consistency */
-/* 17-Nov-2015 nm Added htmldefs, bib, markup checking */
-/* 13-Dec-2016 nm Added checks for undesireable labels (mm*,
-   Microsoft conflicts) */
-/* 24-Mar-2019 nm Added topDateSkip */
-/* 25-Jun-2020 nm Added underscoreSkip */
-/* 17-Jul-2020 nm Added mathboxSkip */
-void verifyMarkup(vstring labelMatch,
-    flag dateSkip, /* 1 = don't check date consistency */
-    flag topDateSkip, /* 1 = don't check top date but check others */
-    flag fileSkip, /* 1 = don't check external files (gifs, mmset.html,...) */
-    flag underscoreSkip, /* 1 = don't check labels for "_" characters) */
-    flag mathboxSkip, /* 1 = don't check mathbox cross-references) */
-    flag verboseMode) /* 1 = more details */ {   /* 26-Dec-2016 nm */
-  flag f;
-  flag saveHtmlFlag, saveAltHtmlFlag;
-  flag errFound = 0;
-  long stmtNum, p1, p2, p3;
-  long flen, lnum, lstart; /* For line length check */ /* 26-Dec-2016 nm */
-  /***** deleted 3-May-2017 nm
-  vstring contributor = ""; vstring contribDate = "";
-  vstring reviser = ""; vstring reviseDate = "";
-  vstring shortener = ""; vstring shortenDate = "";
-  **********/
-
-  /* 13-Dec-2016 nm */
-  vstring mmVersionDate = ""; /* Version date at top of .mm file */
-  vstring mostRecentDate = ""; /* For entire .mm file */
-  long mostRecentStmt = 0; /* For error message */
-  /* 18-Dec-2016 nm For getSectionHeadings() call */
-  vstring hugeHdr = ""; /* 21-Jun-2014 nm */
-  vstring bigHdr = "";
-  vstring smallHdr = "";
-  vstring tinyHdr = ""; /* 21-Aug-2017 nm */
-  vstring hugeHdrComment = ""; /* 8-May-2015 nm */
-  vstring bigHdrComment = ""; /* 8-May-2015 nm */
-  vstring smallHdrComment = ""; /* 8-May-2015 nm */
-  vstring tinyHdrComment = ""; /* 21-Aug-2017 nm */
-
-  vstring descr = "";
-  vstring str1 = ""; vstring str2 = "";
-  /* 17-Jul-2020 nm */ /* For mathbox check */
-  long mbox, pmbox, stmt, pstmt, plen, step;
-  /**** 5-Aug-2020 nm These are now globals
-  long mathboxes;
-  nmbrString *mathboxStart = NULL_NMBRSTRING;
-  nmbrString *mathboxEnd = NULL_NMBRSTRING;
-  pntrString *mathboxUser = NULL_PNTRSTRING;
-  ****/
-  nmbrString *proof = NULL_NMBRSTRING;
-  vstring dupCheck = "";
-
-  saveHtmlFlag = g_htmlFlag;  saveAltHtmlFlag = g_altHtmlFlag;
-
-  print2("Checking statement label conventions...\n");
-
-  /* Check that labels can create acceptable file names for web pages */
-
-  /* 13-Dec-2016 nm Moved this check from metamath.c */
-  /* 10/21/02 - detect Microsoft bugs reported by several users, when the
-     HTML output files are named "con.html" etc. */
-  /* If we want a standard error message underlining token, this could go
-     in mmpars.c */
-  /* From Microsoft's site:
-     "The following reserved words cannot be used as the name of a file:
-     CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7,
-     COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
-     Also, reserved words followed by an extension - for example,
-     NUL.tx7 - are invalid file names." */
-  /* Check for labels that will lead to illegal Microsoft file names for
-     Windows users.  Don't bother checking CLOCK$ since $ is already
-     illegal */
-  let(&str1, cat(
-     ",CON,PRN,AUX,NUL,COM1,COM2,COM3,COM4,COM5,COM6,COM7,",
-     "COM8,COM9,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,", NULL));
-  for (stmtNum = 1; stmtNum <= g_statements; stmtNum++) {
-    if (!matchesList(g_Statement[stmtNum].labelName, labelMatch, '*', '?')) {
-      continue;
-    }
-
-    /* 25-Jun-2020 nm */
-    /* Check labels for "_" characters */
-    /* See discussion in https://github.com/metamath/set.mm/pull/1691 */
-    if (underscoreSkip == 0
-        && instr(1, g_Statement[stmtNum].labelName, "_") != 0) {
-      assignStmtFileAndLineNum(stmtNum);
-      printLongLine(cat("?Warning: In statement \"",
-          g_Statement[stmtNum].labelName, "\" at line ",
-          str((double)(g_Statement[stmtNum].lineNum)),
-          " in file \"", g_Statement[stmtNum].fileName,
-          "\".  Underscores in labels are not recommended per our conventions.  ",
-          "Use the / UNDERSCORE_SKIP ",
-          "qualifier to skip this check.",
-          NULL),
-          "    ", " ");
-      /* g_errorCount++; */
-      errFound = 1;
-    }
-
-    /* Only $a and $p can produce web pages, so check only them */
-    if (g_Statement[stmtNum].type != a_ && g_Statement[stmtNum].type != p_) {
-      continue;
-    }
-    let(&str2, cat(",", edit(g_Statement[stmtNum].labelName, 32/*uppercase*/),
-        ",", NULL));
-    if (instr(1, str1, str2) ||
-        /* 5-Jan-04 mm*.html is reserved for mmtheorems.html, etc. */
-        !strcmp(",MM", left(str2, 3))) {
-      /*print2("\n");*/ /* 25-Jun-2020 nm Deleted */
-      assignStmtFileAndLineNum(stmtNum); /* 9-Jan-2018 nm */
-      printLongLine(cat("?Warning: In statement \"",
-          g_Statement[stmtNum].labelName, "\" at line ",
-          str((double)(g_Statement[stmtNum].lineNum)),
-          " in file \"", g_Statement[stmtNum].fileName,
-          "\".  To workaround a Microsoft operating system limitation, the",
-          " the following reserved words cannot be used for label names:",
-          " CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5,",
-          " COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6,",
-          " LPT7, LPT8, and LPT9.  Also, \"mm*.html\" is reserved for",
-          " Metamath file names.  Use another name for this label.", NULL),
-          "    ", " ");
-      /* g_errorCount++; */
-      errFound = 1;
-    }
-
-    /* Check that $a assertions start with "ax-" or "df-" */
-    if (g_Statement[stmtNum].type == (char)a_) {
-      if (!strcmp("|-", g_MathToken[
-          (g_Statement[stmtNum].mathString)[0]].tokenName)) {
-        let(&str1, left(g_Statement[stmtNum].labelName, 3));
-        if (strcmp("ax-", str1) && strcmp("df-", str1)) {
-          assignStmtFileAndLineNum(stmtNum); /* 9-Jan-2018 nm */
-          printLongLine(cat("?Warning: In the $a statement \"",
-              g_Statement[stmtNum].labelName, "\" at line ",
-              str((double)(g_Statement[stmtNum].lineNum)),
-              " in file \"", g_Statement[stmtNum].fileName,
-              "\", the label does not start with \"ax-\" or \"df-\"",
-              " per our convention for axiomatic assertions (\"$a |- ...\").",
-              NULL), "    ", " ");
-          errFound = 1;
-        }
-      }
-    }
-
-
-  } /* next stmtNum */
-  /* 10/21/02 end */  /* 13-Dec-2016 nm end */
-
-
-  /* 5-Jul-2020 nm */
-  /* Check for math tokens containing "@" */
-  /* 18-Jul-2020 nm */
-  /* Check for math tokens containing "?" */
-  /* Note:  g_MathToken[] is 0-based, not 1-based */
-  for (p1 = 0; p1 < g_mathTokens; p1++) {
-    if (strchr(g_MathToken[p1].tokenName, '@') != NULL) {
-      stmtNum = g_MathToken[p1].statement;
-      assignStmtFileAndLineNum(stmtNum);
-      printLongLine(cat("?Warning: The token \"",
-          g_MathToken[p1].tokenName,
-          "\" declared at line ",
-          str((double)(g_Statement[stmtNum].lineNum)),
-          " in file \"", g_Statement[stmtNum].fileName,
-          "\" has an \"@\" character, which is discouraged because ",
-          "\"@\" is traditionally used to replace \"$\" in commented-out ",
-          "database source code.",
-          NULL),
-          "    ", " ");
-      /* g_errorCount++; */
-      errFound = 1;
-    }
-    if (strchr(g_MathToken[p1].tokenName, '?') != NULL) {
-      stmtNum = g_MathToken[p1].statement;
-      assignStmtFileAndLineNum(stmtNum);
-      printLongLine(cat("?Warning: The token \"",
-          g_MathToken[p1].tokenName,
-          "\" declared at line ",
-          str((double)(g_Statement[stmtNum].lineNum)),
-          " in file \"", g_Statement[stmtNum].fileName,
-          "\" has a \"?\" character, which is discouraged because ",
-          "\"?\" is sometimes used as a math token search wildcard.",
-          NULL),
-          "    ", " ");
-      /* g_errorCount++; */
-      errFound = 1;
-    }
-  }
-
-
-  /* 20-Dec-2016 nm */
-  /* Check $a ax-* vs. $p ax* */
-  /*print2("Checking ax-XXX axioms vs. axXXX theorems...\n");*/
-      /* (Don't print status - this runs very fast) */
-  for (stmtNum = 1; stmtNum <= g_statements; stmtNum++) {
-    if (g_Statement[stmtNum].type != a_) {
-      continue;
-    }
-    if (!matchesList(g_Statement[stmtNum].labelName, labelMatch, '*', '?')) {
-      continue;
-    }
-
-    let(&str1, ""); /* Prevent string stack buildup/overflow in left() below */
-    /* Look for "ax-*" axioms */
-    if (strcmp("ax-", left(g_Statement[stmtNum].labelName, 3))) {
-      continue;
-    }
-
-    let(&str1, g_Statement[stmtNum].labelName);
-    /* Convert ax-XXX to axXXX */
-    let(&str2, cat(left(str1, 2), right(str1, 4), NULL));
-
-    p1 = lookupLabel(str2);
-    if (p1 == -1) continue;  /* There is no corresponding axXXX */
-
-    /* 26-Dec-2016 nm */
-    if (verboseMode == 1) {
-      print2("Comparing \"%s\" to \"%s\"...\n", str2, str1);
-    }
-
-
-    /* Compare statements */
-    if (nmbrEq(g_Statement[stmtNum].mathString,
-        g_Statement[p1].mathString) != 1) {
-      printLongLine(cat("?Warning: The assertions for statements \"",
-          g_Statement[stmtNum].labelName, "\" and \"",
-          g_Statement[p1].labelName, "\" are different.",
-          NULL), "  ", " ");
-      errFound = 1;
-      continue;
-    }
-
-    /* Compare number of mandatory hypotheses */
-    if (g_Statement[stmtNum].numReqHyp != g_Statement[p1].numReqHyp) {
-      printLongLine(cat("?Warning: Statement \"",
-          g_Statement[stmtNum].labelName, "\" has ",
-          str((double)(g_Statement[stmtNum].numReqHyp)),
-          " mandatory hypotheses but \"",
-          g_Statement[p1].labelName, "\" has ",
-          str((double)(g_Statement[p1].numReqHyp)), ".",
-          NULL), "  ", " ");
-      errFound = 1;
-      continue;
-    }
-
-    /* Compare mandatory distinct variables */
-    if (nmbrEq(g_Statement[stmtNum].reqDisjVarsA,
-        g_Statement[p1].reqDisjVarsA) != 1) {
-      printLongLine(cat(
-          "?Warning: The mandatory distinct variable pairs for statements \"",
-          g_Statement[stmtNum].labelName, "\" and \"",
-          g_Statement[p1].labelName,
-          "\" are different or have a different order.",
-          NULL), "  ", " ");
-      errFound = 1;
-      continue;
-    } else if (nmbrEq(g_Statement[stmtNum].reqDisjVarsB,
-        g_Statement[p1].reqDisjVarsB) != 1) {
-      printLongLine(cat(
-          "?Warning: The mandatory distinct variable pairs for statements \"",
-          g_Statement[stmtNum].labelName, "\" and \"",
-          g_Statement[p1].labelName,
-          "\" are different or have a different order.",
-          NULL), "  ", " ");
-      errFound = 1;
-      continue;
-    }
-
-    /* Compare mandatory hypotheses */
-    for (p2 = 0; p2 < g_Statement[stmtNum].numReqHyp; p2++) {
-      if (nmbrEq(g_Statement[(g_Statement[stmtNum].reqHypList)[p2]].mathString,
-          g_Statement[(g_Statement[p1].reqHypList)[p2]].mathString) != 1) {
-        printLongLine(cat("?Warning: The mandatory hypotheses of statements \"",
-            g_Statement[stmtNum].labelName, "\" and \"",
-            g_Statement[p1].labelName, "\" are different.",
-            NULL), "  ", " ");
-        errFound = 1;
-        break;
-      }
-    } /* next p2 */
-
-    for (p2 = 0; p2 < nmbrLen(g_Statement[stmtNum].reqDisjVarsA); p2++) {
-      if (nmbrEq(g_Statement[stmtNum].reqDisjVarsA,
-          g_Statement[p1].reqDisjVarsA) != 1) {
-        printLongLine(cat("?Warning: The mandatory hypotheses of statements \"",
-            g_Statement[stmtNum].labelName, "\" and \"",
-            g_Statement[p1].labelName, "\" are different.",
-            NULL), "  ", " ");
-        errFound = 1;
-        break;
-      }
-    } /* next p2 */
-  } /* next stmtNum */
-
-
-  /* 26-Dec-2016 nm */
-  /* Check line lengths */
-  /*print2("Checking line lengths...\n");*/
-      /* (Don't print status - this runs very fast) */
-  let(&str1, ""); /* Prepare to use as pointer */
-  let(&str2, ""); /* Prepare to use as pointer */
-  if (g_statements >= /*1*/0
-          /* 6-Aug-2019 nm - no reason to skip empty .mm */
-      /*&& g_includeCalls == 0*/) { /* No $[...$] */ /* TODO - handle $[...$] */
-          /* 6-Aug-2019 nm - g_includeCalls is alway nonzero now - but check
-             anyway; line numbers may be off if there are >1 files. */
-    str1 = g_Statement[1].labelSectionPtr; /* Start of input file */
-    str2 = g_Statement[g_statements + 1].labelSectionPtr
-       + g_Statement[g_statements + 1].labelSectionLen; /* End of input file */
-        /* g_statements + 1 is dummy statement to hold text after last statement */
-    if (str2[0] != 0) bug(258); /* Should be end of (giant) string */
-    if (str2[-1] != '\n') bug(259); /* End of last line */
-    flen = str2 - str1;  /* Length of input file */
-    str2 = ""; /* Restore pointer for use as vstring */
-    lnum = 0; /* Line number */
-    lstart = 0; /* Line start */
-    for (p1 = 0; p1 < flen; p1++) {
-      if (str1[p1] == 0) {
-        /* print2("pl=%ld flen=%ld\n", p1, flen); */
-        bug(260); /* File shouldn't have nulls */
-      }
-      if (str1[p1] == '\n') {
-        /* End of a line found */
-        lnum++;
-
-        /* 25-Jun-2020 nm */
-        if (p1 > 0) { /* Not 1st character in file */
-          if (str1[p1 - 1] == ' ') {
-            printLongLine(cat("?Warning: Line number ",
-                str((double)lnum),
-                " ends with a space character, which is discouraged.",
-                NULL), "    ", " ");
-            errFound = 1;
-          }
-        }
-
-        if (p1 - lstart > g_screenWidth) { /* Normally 79; see mminou.c */
-          /* Put line in str2 for error message */
-          let(&str2, space(p1 - lstart));
-          memcpy(str2, str1 + lstart,
-            (size_t)(p1 - lstart));
-          printLongLine(cat("?Warning: Line number ",
-              str((double)lnum),
-              " has ",
-              str((double)(p1 - lstart)),
-              " characters (should have ",
-              str((double)g_screenWidth),
-              " or less):",
-              NULL), "    ", " ");
-          print2("    %s...\n", left(str2, g_screenWidth - 7));
-          errFound = 1;
-          let(&str2, ""); /* Deallocate string memory */
-        }
-        lstart = p1 + 1;
-      }
-
-      /* 3-May-2017 nm */
-      /* Check for tabs */
-      if (str1[p1] == '\t') {
-        printLongLine(cat("?Warning: Line number ",
-            str((double)lnum + 1),
-            " contains a tab, which is discouraged.",
-            NULL), "    ", " ");
-        errFound = 1;
-      }
-
-    } /* next p1 */
-    str1 = ""; /* Restore pointer for use as vstring */
-  } /* end of line length check - if (g_statements >= 1... */
-
-
-  /* Check $t comment content */
-
-  eraseTexDefs(); /* Force a reread regardless of previous mode */
-  /* Check latexdef statements */
-  print2("Checking latexdef, htmldef, althtmldef...\n");
-  g_htmlFlag = 0; /* 1 = HTML, not TeX */
-  g_altHtmlFlag = 0; /* 1 = Unicode, not GIFs (when g_htmlFlag = 1) */
-  f = readTexDefs(1/*errorsOnly*/,
-          fileSkip /* 1 = no GIF file existence check */  );
-  if (f != 0) errFound = 1;
-  if (f != 2) {   /* We continue if no severe errors (warnings are ok) */
-    /*print2("Checking htmldefs...\n");*/
-    /* Check htmldef statements (reread since we've switched modes) */
-    g_htmlFlag = 1; /* 1 = HTML, not TeX */
-    g_altHtmlFlag = 0; /* 1 = Unicode, not GIFs (when g_htmlFlag = 1) */
-    f = readTexDefs(1/*errorsOnly*/,
-            fileSkip /* 1 = no GIF file existence check */  );
-  }
-  if (f != 0) errFound = 1;
-  if (f != 2) {  /* We continue if no severe errors (warnings are ok) */
-    /*print2("Checking althtmldefs...\n");*/
-    /* Check althtmldef statements (reread since we've switched modes) */
-    g_htmlFlag = 1; /* 1 = HTML, not TeX */
-    g_altHtmlFlag = 1; /* 1 = Unicode, not GIFs (when g_htmlFlag = 1) */
-    f = readTexDefs(1/*errorsOnly*/,
-            fileSkip /* 1 = no GIF file existence check */  );
-  }
-  if (f != 0) errFound = 1;
-
-
-  /* Check date consistency and comment markup in all statements */
-  print2("Checking statement comments...\n");
-  let(&mostRecentDate, ""); /* 13-Dec-2016 nm */
-  for (stmtNum = 1; stmtNum <= g_statements; stmtNum++) {
-    if (g_Statement[stmtNum].type != a_ && g_Statement[stmtNum].type != p_) {
-      continue;
-    }
-    if (!matchesList(g_Statement[stmtNum].labelName, labelMatch, '*', '?')) {
-      continue;
-    }
-
-    /* 3-May-2017 nm */
-    /* Check the contributor */
-    let(&str1, "");
-    str1 = getContrib(stmtNum, CONTRIBUTOR);
-    if (!strcmp(str1, DEFAULT_CONTRIBUTOR)) {
-      printLongLine(cat(
-          "?Warning: Contributor \"", DEFAULT_CONTRIBUTOR,  /* 14-May-2017 nm */
-          "\" should be updated in statement \"",
-          g_Statement[stmtNum].labelName, "\".", NULL), "    ", " ");
-      errFound = 1;
-    }
-    /* 15-May-2017 nm */
-    let(&str1, "");
-    str1 = getContrib(stmtNum, REVISER);
-    if (!strcmp(str1, DEFAULT_CONTRIBUTOR)) {
-      printLongLine(cat(
-          "?Warning: Reviser \"", DEFAULT_CONTRIBUTOR,  /* 14-May-2017 nm */
-          "\" should be updated in statement \"",
-          g_Statement[stmtNum].labelName, "\".", NULL), "    ", " ");
-      errFound = 1;
-    }
-
-    if (dateSkip == 0) {
-
-      /* Check date consistency of the statement */
-      /* Use the error-checking feature of getContrib() extractor */
-      /* 3-May-2017 nm */
-      let(&str1, "");
-      str1 = getContrib(stmtNum, GC_ERROR_CHECK_PRINT); /* Returns P or F */
-      if (str1[0] == 'F') errFound = 1;
-      let(&str1, "");
-      str1 = getContrib(stmtNum, MOST_RECENT_DATE);
-
-      /***** deleted 3-May-2017
-      f = getContrib(stmtNum, &contributor, &contribDate,
-          &reviser, &reviseDate, &shortener, &shortenDate,
-          &str1 /@ most recent of the 3 dates @/, /@ 13-Dec-2016 nm @/
-          1/@printErrorsFlag@/,
-          1/@mode: 0 == RESET = reset, 1 = normal @/ /@ 2-May-2017 nm @/);
-      if (f == 1) errFound = 1;
-      *********/
-
-      /* Save most recent date in file - used to check Version date below */
-      if (compareDates(mostRecentDate, str1) == -1) {
-        let(&mostRecentDate, str1);
-        mostRecentStmt = stmtNum;
-      }
-
-    }
-
-    let(&descr, "");
-    descr = getDescription(stmtNum);
-
-    /* 17-Nov-2015 */
-    /* Check comment markup of the statement */
-    g_showStatement /* global */ = stmtNum; /* For printTexComment */
-    g_texFilePtr /* global */  = NULL; /* Not used, but set to something */
-    /* Use the errors-only (no output) feature of printTexComment() */
-    f = printTexComment(descr,
-        0, /* 1 = htmlCenterFlag (irrelevant for this call) */
-        PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */ /* 13-Dec-2018 nm */
-        fileSkip /* 1 = noFileCheck */);
-    if (f == 1) errFound = 1;
-
-    /* 20-Jun-2020 nm */
-    /* Check that $a has no "(Proof modification is discouraged.)" */
-    if (g_Statement[stmtNum].type == a_) {
-      if (getMarkupFlag(stmtNum, PROOF_DISCOURAGED) == 1) {
-        printLongLine(cat(
-            "?Warning: Statement \"", g_Statement[stmtNum].labelName,
-            "\" is a $a but has a \"(Proof modification is discouraged.)\" tag.",
-            NULL), "    ", " ");
-        errFound = 1;
-      }
-    }
-    /* Check that *OLD and *ALT have both discouragements */
-    /* See discussion at
-       https://groups.google.com/d/msg/metamath/NhPM9XNNh1E/otl0uskKBgAJ */
-    p1 = (long)strlen(g_Statement[stmtNum].labelName);
-    let(&str1, right(g_Statement[stmtNum].labelName, p1 - 2)); /* Last 3 chars. */
-    if (!strcmp(str1, "OLD") || !strcmp(str1, "ALT")) {
-      if (getMarkupFlag(stmtNum, PROOF_DISCOURAGED) != 1
-          && g_Statement[stmtNum].type == p_ /* Ignore $a's */
-          ) {
-        printLongLine(cat(
-            "?Warning: Statement \"", g_Statement[stmtNum].labelName,
-            "\" has suffix \"", str1,
-            "\" but has no \"(Proof modification is discouraged.)\" tag.",
-            NULL), "    ", " ");
-        errFound = 1;
-      }
-      if (getMarkupFlag(stmtNum, USAGE_DISCOURAGED) != 1) {
-        printLongLine(cat(
-            "?Warning: Statement \"", g_Statement[stmtNum].labelName,
-            "\" has suffix \"", str1,
-            "\" but has no \"(New usage is discouraged.)\" tag.",
-            NULL), "    ", " ");
-        errFound = 1;
-      }
-    }
-  } /* next stmtNum */
-
-  /* 13-Dec-2016 nm */
-  /* Check that the version date of the .mm file is g.e. all statement dates */
-  /* This code expects a version date at the top of the file such as:
-         $( set.mm - Version of 13-Dec-2016 $)
-     If we later want a different format, this code should be modified. */
-  if (dateSkip == 0  && topDateSkip == 0) {
-    /* Get the top of the .mm file */
-    let(&str1, space(g_Statement[1].labelSectionLen));
-    memcpy(str1, g_Statement[1].labelSectionPtr,
-      (size_t)(g_Statement[1].labelSectionLen));
-    /* Find the version date */
-    p1 = instr(1, str1, "Version of ");
-    if (p1 == 0) {
-      printLongLine(cat(
-          "?Warning: There is no \"Version of \" comment at the top of the",
-          " file \"", g_input_fn, "\".", NULL), "    ", " ");
-      errFound = 1;
-    } else {
-      p2 = instr(p1 + 11, str1, " ");
-      let(&str2, seg(str1, p1 + 11, p2 - 1)); /* The date */
-      f = parseDate(str2, &p1, &p2, &p3);
-      if (f == 1) {
-        printLongLine(cat(
-            "?Warning: The Version date \"", str2, "\" at the top of file \"",
-            g_input_fn, "\" is not a valid date.", NULL), "    ", " ");
-        errFound = 1;
-      } else {
-        if (compareDates(mostRecentDate, str2) == 1) {
-          printLongLine(cat(
-              "?Warning: The \"Version of\" date ", str2,
-              " at the top of file \"",
-              g_input_fn,
-              "\" is less recent than the date ", mostRecentDate,
-              " in the description of statement \"",
-              g_Statement[mostRecentStmt].labelName, "\".", NULL), "    ", " ");
-          errFound = 1;
-        }
-      }
-    }
-  } /* if (dateSkip == 0) */
-
-
-  /* 18-Dec-2016 nm */
-  print2("Checking section header comments...\n");
-  for (stmtNum = 1; stmtNum <= g_statements; stmtNum++) {
-    if (g_Statement[stmtNum].type != a_ && g_Statement[stmtNum].type != p_) {
-      continue;
-    }
-    if (!matchesList(g_Statement[stmtNum].labelName, labelMatch, '*', '?')) {
-      continue;
-    }
-
-    let(&hugeHdr, "");
-    let(&bigHdr, "");
-    let(&smallHdr, "");
-    let(&tinyHdr, ""); /* 21-Aug-2017 nm */
-    let(&hugeHdrComment, "");
-    let(&bigHdrComment, "");
-    let(&smallHdrComment, "");
-    let(&tinyHdrComment, ""); /* 21-Aug-2017 nm */
-    f = getSectionHeadings(stmtNum, &hugeHdr, &bigHdr, &smallHdr,
-        &tinyHdr, /* 21-Aug-2017 nm */
-        /* 5-May-2015 nm */
-        &hugeHdrComment, &bigHdrComment, &smallHdrComment,
-        &tinyHdrComment,
-        0, /* fineResolution */
-        0 /* fullComment */);
-    if (f != 0) errFound = 1;  /* 6-Aug-2019 nm */
-
-    g_showStatement /* global */ = stmtNum; /* For printTexComment() */
-    g_texFilePtr /* global */  = NULL; /* Not used, but set to something */
-
-    f = 0;
-    if (hugeHdrComment[0] != 0)
-      f = (char)(f + printTexComment(hugeHdrComment,
-          0, /* 1 = htmlCenterFlag (irrelevant for this call) */
-          PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */ /* 13-Dec-2018 nm */
-          fileSkip /* 1 = noFileCheck */));
-    if (bigHdrComment[0] != 0)
-      f = (char)(f + printTexComment(bigHdrComment,
-          0, /* 1 = htmlCenterFlag (irrelevant for this call) */
-          PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */ /* 13-Dec-2018 nm */
-          fileSkip /* 1 = noFileCheck */));
-    if (smallHdrComment[0] != 0)
-      f = (char)(f + printTexComment(smallHdrComment,
-          0, /* 1 = htmlCenterFlag (irrelevant for this call) */
-          PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */ /* 13-Dec-2018 nm */
-          fileSkip /* 1 = noFileCheck */));
-
-    /* Added 21-Aug-2017 nm */
-    if (tinyHdrComment[0] != 0)
-      f = (char)(f + printTexComment(tinyHdrComment,
-          0, /* 1 = htmlCenterFlag (irrelevant for this call) */
-          PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */ /* 13-Dec-2018 nm */
-          fileSkip /* 1 = noFileCheck */));
-    /* (End of 21-Aug-2017 addition) */
-
-    if (f != 0) printf(
-"    (The warning above refers to a header above the referenced statement.)\n");
-    if (f != 0) errFound = 1;
-  } /* next stmtNum */
-
-
-  /* Use the errors-only (no output) feature of writeBibliography() */
-  print2("Checking bibliographic references...\n");
-  f = writeBibliography("mmbiblio.html",
-          labelMatch,
-          1,  /* 1 = no output, just warning msgs if any */
-          fileSkip); /* 1 = ignore missing external files (gifs, bib, etc.) */
-  if (f != 0) errFound = 1;
-
-  /* 17-Jul-2020 nm */
-  /* Check mathboxes for cross-references */
-  if (mathboxSkip == 0) {
-    print2("Checking mathbox independence...\n");
-    /* 5-Aug-2020 nm Deleted; these are now globals */
-    /*mathboxes = getMathboxLoc(&mathboxStart, &mathboxEnd, &mathboxUser);*/
-    assignMathboxInfo();  /* Populate global mathbox variables */
-    /* Scan proofs in mathboxes to see if earlier mathbox is referenced */
-    for (pmbox = 2; pmbox <= g_mathboxes; pmbox++) {
-      /* Note g_mathboxStart, etc. are 0-based */
-      for (pstmt = g_mathboxStart[pmbox - 1]; pstmt <= g_mathboxEnd[pmbox - 1];
-          pstmt++) {
-        if (g_Statement[pstmt].type != (char)p_)
-          continue; /* Not a $p statement; skip it */
-        /* Don't use bad proofs (incomplete proofs are ok) */
-        if (parseProof(pstmt) > 1) {
-          /* The proof has an error, so use the empty proof */
-          nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
-        } else {
-          nmbrLet(&proof, g_WrkProof.proofString);
-        }
-        plen = nmbrLen(proof);
-        /* Get the essential step flags, if required */
-        /*
-        if (essentialFlag) {
-          nmbrLet(&essentialFlags, nmbrGetEssential(proof));
-        }
-        */
-        for (step = 0; step < plen; step++) {
-          /*
-          if (essentialFlag) {
-            if (!essentialFlags[step]) continue;  /@ Ignore floating hypotheses @/
-          }
-          */
-          stmt = proof[step];
-          if (stmt < 0) continue; /* Local step or '?' step */
-          if (stmt == 0) bug(266);
-          if (stmt > g_mathboxStmt && stmt < g_mathboxStart[pmbox - 1]) {
-            /* A statement in another mathbox is referenced */
-
-            /* Eliminate duplicate error messages: */
-            let(&str1, cat(str((double)pstmt), "-", str((double)stmt), NULL));
-            if (lookup(str1, dupCheck) != 0) {
-              continue;
-            } else {
-              let(&dupCheck, cat(dupCheck,
-                  (dupCheck[0] == 0) ? "" : ",", str1, NULL)); /* Add to list */
-            }
-
-            mbox = getMathboxNum(stmt);
-            if (mbox == 0) bug(267);
-            if (verboseMode == 0) {
-              printLongLine(cat("?Warning: The proof of \"",
-                  g_Statement[pstmt].labelName,
-                  "\" in the mathbox for ", (vstring *)(g_mathboxUser[pmbox - 1]),
-                  " references \"", g_Statement[stmt].labelName,
-                  "\" in the mathbox for ", (vstring *)(g_mathboxUser[mbox - 1]),
-                  ".",
-                  NULL),
-                  "    ", " ");
-            } else {
-              /* Verbose output experiment */
-              assignStmtFileAndLineNum(stmt);
-              assignStmtFileAndLineNum(pstmt);
-              printLongLine(cat("?Warning: The proof of statement \"",
-                  g_Statement[pstmt].labelName,
-                  "\" in the mathbox for \"", (vstring *)(g_mathboxUser[pmbox - 1]),
-                  "\" at line ", str((double)(g_Statement[pstmt].lineNum)),
-                  " in file \"", g_Statement[pstmt].fileName,
-                  "\" references statement \"", g_Statement[stmt].labelName,
-                  "\" in the mathbox for \"", (vstring *)(g_mathboxUser[mbox - 1]),
-                  "\" at line ", str((double)(g_Statement[stmt].lineNum)),
-                  " in file \"", g_Statement[stmt].fileName,
-                  "\".  ",
-                  "(Use the / MATHBOX_SKIP qualifier to skip this check.)",
-                  NULL),
-                  "    ", " ");
-            }
-            /* g_errorCount++; */
-            errFound = 1;
-          } /* if stmt in another mathbox */
-        } /* next step */
-      } /* next pstmt */
-    } /* next pmbox */
-    /* Deallocate */
-    let(&dupCheck, "");
-    /**** 5-Aug-2020 nm Deleted
-    nmbrLet(&mathboxStart, NULL_NMBRSTRING);
-    nmbrLet(&mathboxEnd, NULL_NMBRSTRING);
-    for (mbox = 1; mbox <= mathboxes; mbox++) {
-      let((vstring *)(&mathboxUser[mbox - 1]), "");
-    }
-    pntrLet(&mathboxUser, NULL_PNTRSTRING);
-    ****/
-    nmbrLet(&proof, NULL_NMBRSTRING);
-  }
-
-  if (errFound == 0) {
-    print2("No errors were found.\n");
-  }
-
-  g_htmlFlag = saveHtmlFlag;  g_altHtmlFlag = saveAltHtmlFlag;
-  /* Force reread to get current mode defaults for future user commands */
-  eraseTexDefs();
-
-  /* Deallocate string memory */
-  /**** deleted 3-May-2017 because now we use only mostRecentDate
-  let(&contributor, "");
-  let(&contribDate, "");
-  let(&reviser, "");
-  let(&reviseDate, "");
-  let(&shortener, "");
-  let(&shortenDate, "");
-  ******/
-  let(&mostRecentDate, "");
-  let(&mmVersionDate, "");
-  let(&descr, "");
-  let(&str1, "");
-  let(&str2, "");
-
-  /* Added 21-Aug-2017 nm */
-  /* (It seems that I forgot to deallocate in earlier versions,
-     although it usually wouldn't have caused leakage since the last
-     statement in the .mm file normally won't have a header.  But
-     we do it here to be safe.) */
-  let(&hugeHdr, "");
-  let(&bigHdr, "");
-  let(&smallHdr, "");
-  let(&tinyHdr, "");
-  let(&hugeHdrComment, "");
-  let(&bigHdrComment, "");
-  let(&smallHdrComment, "");
-  let(&tinyHdrComment, "");
-  /* (End of 21-Aug-2017 addition) */
-
-  return;
-
-} /* verifyMarkup */
-
-
-
-/* 10-Dec-2018 nm Added */
-/* Function to process markup in an arbitrary non-Metamath HTML file, treating
-   the file as a giant comment. */
-void processMarkup(vstring inputFileName, vstring outputFileName,
-    flag processCss, long actionBits) {
-  FILE *outputFilePtr;
-  vstring inputFileContent = "";
-  long size;
-  long p;
-
-  /* Check that globals aren't in a weird state */
-  if (g_outputToString == 1 || g_printString[0] != 0) {
-    bug(265);
-  }
-
-  /* readTexDefs() rereads based on changed in g_htmlFlag, g_altHtmlFlag */
-  if (2/*error*/ == readTexDefs(0 /* 1 = check errors only */,
-      1 /* 1 = no GIF file existence check */  )) {
-    goto PROCESS_MARKUP_RETURN; /* An error occurred */
-  }
-
-  print2("Reading \"%s\"...\n", inputFileName);
-
-  let(&inputFileContent, "");
-  inputFileContent = readFileToString(inputFileName, 1/*verbose*/, &size);
-  if (inputFileContent == NULL) {
-    /* Couldn't open the file; error msg provided by readFileToString */
-    inputFileContent = ""; /* Restore to normal vstring to prevent seg fault */
-    goto PROCESS_MARKUP_RETURN;
-  }
-
-  print2("Creating \"%s\"...\n", outputFileName);
-
-  /* Insert CSS from .mm file before "</HEAD>" if it isn't already there */
-  if (processCss != 0 && instr(1, inputFileContent, g_htmlCSS) == 0) {
-    p = instr(1, edit(inputFileContent, 32/*uppercase*/), "</HEAD>");
-    if (p != 0) {
-      let(&inputFileContent, cat(left(inputFileContent, p - 1),
-          g_htmlCSS, "\n", right(inputFileContent, p), NULL));
-    }
-  }
-
-  /* fSafeOpen() renames existing files with ~1,~2,etc.  This way
-     existing user files will not be accidentally destroyed. */
-  outputFilePtr = fSafeOpen(outputFileName, "w", 0/*noVersioningFlag*/);
-  if (outputFilePtr == NULL) {
-    /* (Error msg already provided by fSafeOpen) */
-    /* print2("?Couldn't open \"%s\"\n", outputFileName); */
-    goto PROCESS_MARKUP_RETURN;
-  }
-
-  g_outputToString = 0;
-  let(&g_printString, "");
-  g_showStatement = 0; /* For printTexComment */
-  g_texFilePtr = outputFilePtr; /* For printTexComment */
-  printTexComment(  /* Sends result to g_texFilePtr */
-      inputFileContent,
-      0, /* 1 = htmlCenterFlag */
-      actionBits, /* bit-mapped list of actions */
-      0 /* 1 = noFileCheck */);
-  fclose(g_texFilePtr);
-  g_texFilePtr = NULL;
-
- PROCESS_MARKUP_RETURN:
-  /* Deallocate */
-  let(&inputFileContent, "");
-  let(&g_printString, "");
-  return;
-}
-
-
-/* 3-May-2016 nm */
-/* List "discouraged" statements with "(Proof modification is discouraged."
-   and "(New usage is discourged.)" comment markup tags. */
-/* This function is primarily intended for use with the "travis" system
-   to identify versioning differences on GitHub. */
-void showDiscouraged(void) {   /* was: showRestricted */
-  long stmt, s, usageCount;
-  long lowStmt = 0, highStmt = 0; /* For a slight speedup */
-  flag notQuitPrint = 1; /* Goes to 0 if user typed 'q' at scroll prompt */
-  vstring str1 = "";
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-
-    /* Since this command is slow, quit immediately if user typed 'q'
-       at scrolling prompt */
-    if (notQuitPrint == 0) break;
-
-    if (g_Statement[stmt].type != p_ && g_Statement[stmt].type != a_) continue;
-    if (getMarkupFlag(stmt, PROOF_DISCOURAGED) == 1
-        && g_Statement[stmt].type == p_ /* Ignore $a's */
-        ) {
-      /* Restricted proof */
-      /* Get number of steps */
-      parseProof(stmt);
-      notQuitPrint = print2(
-"SHOW DISCOURAGED:  Proof modification of \"%s\" is discouraged (%ld steps).\n",
-          g_Statement[stmt].labelName,
-          nmbrLen(g_WrkProof.proofString));
-    } /* if discouraged proof */
-    if (getMarkupFlag(stmt, USAGE_DISCOURAGED) == 1) {
-      /* Discouraged usage */
-      usageCount = 0;
-      let(&str1, "");
-      str1 = traceUsage(stmt,
-          0, /* recursiveFlag */
-          0 /* cutoffStmt */);
-      if (str1[0] == 'Y') { /* Used by at least one */
-        /* str1[i] will be 'Y' if used by stmt */
-        lowStmt = g_statements;
-        highStmt = 0;
-        /* Scan all future statements in str1 Y/N list */
-        for (s = stmt + 1; s <= g_statements; s++) {
-          /* Scan the used-by map */
-          if (str1[s] != 'Y') continue;
-          usageCount++;
-          if (lowStmt > s) lowStmt = s;
-          if (highStmt < s) highStmt = s;
-        } /* Next s */
-      } /* if (str1[0] == 'Y') */
-      notQuitPrint = print2(
-"SHOW DISCOURAGED:  New usage of \"%s\" is discouraged (%ld uses).\n",
-          g_Statement[stmt].labelName,
-          usageCount);
-      if (str1[0] == 'Y') { /* Used by at least one */
-        /* str1[i] will be 'Y' if used by stmt */
-        /* Scan all future statements in str1 Y/N list */
-        for (s = lowStmt; s <= highStmt; s++) {
-          /* Scan the used-by map */
-          if (str1[s] != 'Y') continue;
-          notQuitPrint = print2(
-              "SHOW DISCOURAGED:  \"%s\" is used by \"%s\".\n",
-              g_Statement[stmt].labelName,
-              g_Statement[s].labelName);
-        } /* Next s */
-      } /* if (str1[0] == 'Y') */
-    } /* if discouraged usage */
-  } /* next stmt */
-  let(&str1, ""); /* Deallocate */
-} /* showDiscouraged */
-
-/* Added 14-Sep-2012 nm */
-/* Take a relative step FIRST, LAST, +nn, -nn (relative to the unknown
-   essential steps) or ALL, and return the actual step for use by ASSIGN,
-   IMPROVE, REPLACE, LET (or 0 in case of ALL, used by IMPROVE).  In case
-   stepStr is an unsigned integer nn, it is assumed to already be an actual
-   step and is returned as is.  If format is illegal, -1 is returned.  */
-long getStepNum(vstring relStep, /* User's argument */
-   nmbrString *pfInProgress, /* g_ProofInProgress.proof */
-   flag allFlag /* 1 = "ALL" is permissible */)
-{
-  long pfLen, i, j, relStepVal, actualStepVal;
-  flag negFlag = 0;
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-  vstring relStepCaps = "";
-
-  let(&relStepCaps, edit(relStep, 32/*upper case*/));
-  pfLen = nmbrLen(pfInProgress); /* Proof length */
-  relStepVal = (long)(val(relStepCaps)); /* val() tolerates ill-formed numbers */
-
-  if (relStepVal >= 0 && !strcmp(relStepCaps, str((double)relStepVal))) {
-    /* User's argument is an unsigned positive integer */
-    actualStepVal = relStepVal;
-    if (actualStepVal > pfLen || actualStepVal < 1) {
-      print2("?The step must be in the range from 1 to %ld.\n", pfLen);
-      actualStepVal = -1;  /* Flag the error */
-    }
-    goto RETURN_POINT;  /* Already actual step; just return it */
-  } else if (!strcmp(relStepCaps, left("FIRST", (long)(strlen(relStepCaps))))) {
-    negFlag = 0; /* Scan forwards */
-    relStepVal = 0;
-  } else if (!strcmp(relStepCaps, left("LAST", (long)(strlen(relStepCaps))))) {
-    negFlag = 1; /* Scan backwards */
-    relStepVal = 0;
-  } else if (relStepCaps[0] == '+') {
-    negFlag = 0;
-    if (strcmp(right(relStepCaps, 2), str((double)relStepVal))) {
-      print2("?The characters after '+' are not a number.\n");
-      actualStepVal = -1; /* Error - not a number after the '+' */
-      goto RETURN_POINT;
-    }
-  } else if (relStepCaps[0] == '-') {
-    negFlag = 1;
-    if (strcmp(right(relStepCaps, 2), str((double)(- relStepVal)))) {
-      print2("?The characters after '-' are not a number.\n");
-      actualStepVal = -1; /* Error - not a number after the '-' */
-      goto RETURN_POINT;
-    }
-    relStepVal = - relStepVal;
-  } else if (!strcmp(relStepCaps, left("ALL", (long)(strlen(relStepCaps))))) {
-    if (!allFlag) {
-      /* ALL is illegal */
-      print2("?You must specify FIRST, LAST, nn, +nn, or -nn.\n");
-      actualStepVal = -1; /* Flag that there was an error */
-      goto RETURN_POINT;
-    }
-    actualStepVal = 0; /* 0 is special, meaning "ALL" */
-    goto RETURN_POINT;
-  } else {
-    if (allFlag) {
-      print2("?You must specify FIRST, LAST, nn, +nn, -nn, or ALL.\n");
-    } else {
-      print2("?You must specify FIRST, LAST, nn, +nn, or -nn.\n");
-    }
-    actualStepVal = -1; /* Flag that there was an error */
-    goto RETURN_POINT;
-  }
-
-  nmbrLet(&essentialFlags, nmbrGetEssential(pfInProgress));
-
-  /* Get the essential step flags */
-  actualStepVal = 0; /* Use zero as flag that step wasn't found */
-  if (negFlag) {
-    /* Scan proof backwards */
-    /* Count back 'relStepVal' unknown steps */
-    j = relStepVal + 1;
-    for (i = pfLen; i >= 1; i--) {
-      if (essentialFlags[i - 1]
-          && pfInProgress[i - 1] == -(long)'?') {
-        j--;
-        if (j == 0) {
-          /* Found it */
-          actualStepVal = i;
-          break;
-        }             /* 16-Apr-06 */
-      }
-    } /* Next i */
-  } else {
-    /* Scan proof forwards */
-    /* Count forward 'relStepVal' unknown steps */
-    j = relStepVal + 1;
-    for (i = 1; i <= pfLen; i++) {
-      if (essentialFlags[i - 1]
-          && pfInProgress[i - 1] == -(long)'?') {
-        j--;
-        if (j == 0) {
-          /* Found it */
-          actualStepVal = i;
-          break;
-        }             /* 16-Apr-06 */
-      }
-    } /* Next i */
-  }
-  if (actualStepVal == 0) {
-    if (relStepVal == 0) {
-      print2("?There are no unknown essential steps.\n");
-    } else {
-      print2("?There are not at least %ld unknown essential steps.\n",
-        relStepVal + 1);
-    }
-    actualStepVal = -1; /* Flag that there was an error */
-    goto RETURN_POINT;
-  }
-
- RETURN_POINT:
-  /* Deallocate memory */
-  let(&relStepCaps, "");
-  nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-
-  return actualStepVal;
-} /* getStepNum */
-
-
-/* Added 22-Apr-2015 nm */
-/* Convert the actual step numbers of an unassigned step to the relative
-   -1, -2, etc. offset for SHOW NEW_PROOF ...  /UNKNOWN, to make it easier
-   for the user to ASSIGN the relative step number. A 0 is returned
-   for the last unknown step.  The step numbers of known steps are
-   unchanged.  */
-/* The caller must deallocate the returned nmbrString. */
-nmbrString *getRelStepNums(nmbrString *pfInProgress) {
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-  nmbrString *relSteps = NULL_NMBRSTRING;
-  long i, j, pfLen;
-
-  pfLen = nmbrLen(pfInProgress); /* Get proof length */
-  nmbrLet(&relSteps, nmbrSpace(pfLen));  /* Initialize */
-  nmbrLet(&essentialFlags, nmbrGetEssential(pfInProgress));
-  j = 0;  /* Negative offset (or 0 for last unknown step) */
-  for (i = pfLen; i >= 1; i--) {
-    if (essentialFlags[i - 1]
-        && pfInProgress[i - 1] == -(long)'?') {
-      relSteps[i - 1] = j;
-      j--; /* It's an essential unknown step; increment negative offset */
-    } else {
-      relSteps[i - 1] = i; /* Just keep the normal step number */
-    }
-  }
-
-  /* Deallocate memory */
-  nmbrLet(&essentialFlags, NULL_NMBRSTRING);
-
-  return relSteps;
-} /* getRelStepNums */
-
-
-/* 19-Sep-2012 nm */
-/* This procedure finds the next statement number whose label matches
-   stmtName.  Wildcards are allowed.  If uniqueFlag is 1,
-   there must be exactly one match, otherwise an error message is printed
-   and -1 is returned.  If uniqueFlag is 0, the next match is
-   returned, or -1 if there are no more matches.  No error messages are
-   printed when uniqueFlag is 0, except for the special case of
-   startStmt=1.  For use by PROVE, REPLACE, ASSIGN. */
-long getStatementNum(vstring stmtName, /* Possibly with wildcards */
-    long startStmt, /* Starting statement number (1 for full scan) */
-    long maxStmt, /* Matches must be LESS THAN this statement number */
-    flag aAllowed, /* 1 means $a is allowed */
-    flag pAllowed, /* 1 means $p is allowed */
-    flag eAllowed, /* 1 means $e is allowed */
-    flag fAllowed, /* 1 means $f is allowed */
-    flag efOnlyForMaxStmt, /* If 1, $e and $f must belong to maxStmt */
-    flag uniqueFlag) /* If 1, match must be unique */
-{
-  flag hasWildcard;
-  long matchesFound, matchStmt, matchStmt2, stmt;
-  char typ;
-  flag laterMatchFound = 0; /* For better error message */ /* 16-Jan-2014 nm */
-
-  hasWildcard = 0;
-  /* (Note strpbrk warning in mmpars.c) */
-  if (strpbrk(stmtName, "*?=~%#@,") != NULL) {
-    /* (See matches() function for processing of these)
-       "*" 0 or more char match
-       "?" 1 char match
-       "=" Most recent PROVE command statement
-       "~" Statement range
-       "%" List of modified statements
-       "#" Internal statement number
-       "@" Web page statement number
-       "," Comma-separated fields */
-    hasWildcard = 1; /* I.e. stmtName is not a simple label */
-  }
-  matchesFound = 0;
-  matchStmt = 1; /* Set to a legal value in case of bug */
-  matchStmt2 = 1; /* Set to a legal value in case of bug */
-
-  /**** 18-Jul-2020 nm No longer needed; done in matches() (called by
-     matchesList() below) @/
-  if (!strcmp(stmtName, "=") && proveStatement != 0) {
-     let(&stmtName, g_Statement[proveStatement].labelName);
-  }
-  *****/
-
-  for (stmt = startStmt; stmt <= g_statements; stmt++) {
-
-    /* 16-Jan-2014 nm */
-    if (stmt >= maxStmt) {
-      if (matchesFound > 0) break; /* Normal exit when a match was found */
-      if (!uniqueFlag) break; /* We only want to scan up to maxStmt anyway */
-      /* Otherwise, we continue to see if there is a later match, for
-         error message purposes */
-    }
-
-    if (!g_Statement[stmt].labelName[0]) continue; /* No label */
-    typ = g_Statement[stmt].type;
-
-    if ((!aAllowed && typ == (char)a_)
-        ||(!pAllowed && typ == (char)p_)
-        ||(!eAllowed && typ == (char)e_)
-        ||(!fAllowed && typ == (char)f_)) {
-      continue; /* Statement type is not allowed */
-    }
-
-    if (hasWildcard) {
-      if (!matchesList(g_Statement[stmt].labelName, stmtName, '*', '?')) {
-        continue;
-      }
-    } else {
-      /* When hasWildcard = 0, this code is very inefficient - all we need to
-         do is call lookupLabel(stmtName) outside of the stmt loop and take
-         actions based on whether the label exists and its statement number
-         and type compared to what's expected.  However, from a user
-         perspective, the current code has no noticeable delay, so there's no
-         pressing need to improve it at this point. */
-      if (strcmp(stmtName, g_Statement[stmt].labelName)) {
-        continue;
-      }
-    }
-
-    if (efOnlyForMaxStmt) {
-      if (maxStmt > g_statements) bug(247); /* Don't set efOnlyForMaxStmt
-                                             in case of PROVE call */
-      /* If a $e or $f, it must be a hypothesis of the statement
-         being proved */
-      if (typ == (char)e_ || typ == (char)f_){
-        if (!nmbrElementIn(1, g_Statement[maxStmt].reqHypList, stmt) &&
-            !nmbrElementIn(1, g_Statement[maxStmt].optHypList, stmt))
-            continue;
-      }
-    }
-
-    /* 16-Jan-2014 nm */
-    if (stmt >= maxStmt) {
-      /* For error messages:
-         This signals that a later match (after the statement being
-         proved, in case of ASSIGN) exists, so the user is trying to
-         reference a future statement. */
-      laterMatchFound = 1;
-      break;
-    }
-
-    if (matchesFound == 0) {
-      /* This is the first match found; save it */
-      matchStmt = stmt;
-      /* If uniqueFlag is not set, we're done (don't need to check for
-         uniqueness) */
-    }
-    if (matchesFound == 1) {
-      /* This is the 2nd match found; save it for error message */
-      matchStmt2 = stmt;
-    }
-    matchesFound++;
-    if (!uniqueFlag) break; /* We are just getting the next match, so done */
-    if (!hasWildcard) break; /* Since there can only be 1 match, don't
-                                bother to continue */
-  }
-
-  if (matchesFound == 0) {
-    if (!uniqueFlag) {
-      if (startStmt == 1) {
-        /* For non-unique scan, print only if we started from beginning */
-        print2("?No statement label matches \"%s\".\n", stmtName);
-      }
-    } else if (aAllowed && pAllowed && eAllowed && fAllowed
-               && !efOnlyForMaxStmt) {
-      print2("?No statement label matches \"%s\".\n", stmtName);
-    } else if (!aAllowed && pAllowed && !eAllowed && !fAllowed) {
-      /* This is normally the PROVE command */
-      print2("?No $p statement label matches \"%s\".\n", stmtName);
-    } else if (!eAllowed && !fAllowed) {
-      /* This is normally for REPLACE */
-      if (!laterMatchFound) {
-        print2("?No $a or $p statement label matches \"%s\".\n",
-          stmtName);
-      } else {
-        /* 16-Jan-2014 nm */
-        print2(
-   "?You must specify a statement that occurs earlier the one being proved.\n");
-      }
-    } else {
-      /* This is normally for ASSIGN */
-      if (!laterMatchFound) {
-        printLongLine(cat("?A statement label matching \"",
-            stmtName,
-            "\" was not found or is not a hypothesis of the statement ",
-            "being proved.", NULL), "", " ");
-      } else {
-        /* 16-Jan-2014 nm */
-        print2(
-   "?You must specify a statement that occurs earlier the one being proved.\n");
-      }
-    }
-  } else if (matchesFound == 2) {
-    printLongLine(cat("?This command requires a unique label, but there are ",
-        " 2 matches for \"",
-        stmtName, "\":  \"", g_Statement[matchStmt].labelName,
-        "\" and \"", g_Statement[matchStmt2].labelName, "\".",
-        NULL), "", " ");
-  } else if (matchesFound > 2) {
-    printLongLine(cat("?This command requires a unique label, but there are ",
-        str((double)matchesFound), " (allowed) matches for \"",
-        stmtName, "\".  The first 2 are \"", g_Statement[matchStmt].labelName,
-        "\" and \"", g_Statement[matchStmt2].labelName, "\".",
-        "  Use SHOW LABELS \"", stmtName, "\" to see all non-$e matches.",
-        NULL), "", " ");
-  }
-  if (!uniqueFlag && matchesFound > 1) bug(248);
-  if (matchesFound != 1) matchStmt = -1; /* Error - no (unique) match */
-  return matchStmt;
-} /* getStatementNum */
-
-
-
-
-/* Called by help() - prints a help line */
-/* THINK C gives compilation error if H() is lower-case h() -- why? */
-void H(vstring helpLine)
-{
-  if (g_printHelp) {
-    print2("%s\n", helpLine);
-  }
-} /* H */
-
-
-
-/******** 8/28/00 ***********************************************************/
-/******** The MIDI output algorithm is in this function, outputMidi(). ******/
-/*** Warning:  If you want to experiment with the MIDI output, please
-     confine changes to this function.  Changes to other code
-     in this file is not recommended. ***/
-
-void outputMidi(long plen, nmbrString *indentationLevels,
-  nmbrString *logicalFlags, vstring g_midiParameter, vstring statementLabel) {
-
-  /* The parameters have the following meanings.  You should treat them as
-     read-only input parameters and should not modify the contents of the
-     arrays or strings they point to.
-
-       plen = length of proof
-       indentationLevels[step] = indentation level in "show proof xxx /full"
-           where step varies from 0 to plen-1
-       logicalFlags[step] = 0 for formula-building step, 1 for logical step
-       g_midiParameter = string passed by user in "midi xxx /parameter <g_midiParameter>"
-       statementLabel = label of statement whose proof is being scanned */
-
-  /* This function is called when the user types "midi xxx /parameter
-     <g_midiParameter>".  The proof steps of theorem xxx are numbered successively
-     from 0 to plen-1.  The arrays indentationLevels[] and logicalFlags[]
-     have already been populated for you. */
-
-  /* The purpose of this function is to create an ASCII file called xxx.txt
-     that contains the ASCII format for a t2mf input file.  The mf2t package
-     is available at http://www.hitsquad.com/smm/programs/mf2t/download.shtml.
-     To convert xxx.txt to xxx.mid you type "t2mf xxx.txt xxx.mid". */
-
-  /* To experiment with this function, you can add your own local variables and
-     modify the algorithm as you see fit.  The algorithm is essentially
-     contained in the loop "for (step = 0; step < plen; step++)" and in the
-     initialization of the local variables that this loop uses.  No global
-     variables are used inside this function; the only data used is
-     that contained in the input parameters. */
-
-/* Larger TEMPO means faster speed */
-#define TEMPO 48
-/* The minimum and maximum notes for the dynamic range we allow: */
-/* (MIDI notes range from 1 to 127, but 28 to 103 seems reasonably pleasant) */
-/* MIDI note number 60 is middle C. */
-#define MIN_NOTE 28
-#define MAX_NOTE 103
-
-  /* Note: "flag" is just "char"; "vstring" is just "char *" - except
-     that by this program's convention vstring allocation is controlled
-     by string functions in mmvstr.c; and "nmbrString" is just "long *" */
-
-  long step; /* Proof step from 0 to plen-1 */
-  long midiKey; /* Current keyboard key to be output */
-  long midiNote; /* Current midi note to be output, mapped from midiKey */
-  long midiTime; /* Midi time stamp */
-  long midiPreviousFormulaStep; /* Note saved from previous step */
-  long midiPreviousLogicalStep; /* Note saved from previous step */
-  vstring midiFileName = ""; /* All vstrings MUST be initialized to ""! */
-  FILE *midiFilePtr; /* Output file pointer */
-  long midiBaseline; /* Baseline note */
-  long midiMaxIndent; /* Maximum indentation (to find dyn range of notes) */
-  long midiMinKey; /* Smallest keyboard key in output */
-  long midiMaxKey; /* Largest keyboard key in output */
-  long midiKeyInc; /* Keyboard key increment per proof indentation level */
-  flag midiSyncopate; /* 1 = syncopate the output */
-  flag midiHesitate; /* 1 = silence all repeated notes */
-  long midiTempo; /* larger = faster */
-  vstring midiLocalParam = ""; /* To manipulate user's parameter string */
-  vstring tmpStr = ""; /* Temporary string */
-#define ALLKEYSFLAG 1
-#define WHITEKEYSFLAG 2
-#define BLACKKEYSFLAG 3
-  flag keyboardType; /* ALLKEYSFLAG, WHITEKEYSFLAG, or BLACKKEYSFLAG */
-  long absMinKey; /* The smallest note we ever want */
-  long absMaxKey; /* The largest note we ever want */
-  long key2MidiMap[128]; /* Maps keyboard (possiblty with black or
-                    white notes missing) to midi notes */
-  long keyboardOctave; /* The number of keys spanning an octave */
-  long i;
-
-  /******** Define the keyboard to midi maps (added 5/17/04 nm) *************/
-  /* The idea here is to map the proof step to pressing "keyboard" keys
-     on keyboards that have all keys, or only the white keys, or only the
-     black keys.   The default is all keys, the parameter b means black,
-     and the parameter w means white. */
-#define ALLKEYS 128
-#define WHITEKEYS 75
-#define BLACKKEYS 53
-  long allKeys[ALLKEYS] =
-      {  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,
-        12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,
-        24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,
-        36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
-        48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
-        60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,
-        72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,
-        84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
-        96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107,
-       108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
-       120, 121, 122, 123, 124, 125, 126, 127};
-  long whiteKeys[WHITEKEYS] =
-      {  0,   2,   4,   5,   7,   9,  11,
-        12,  14,  16,  17,  19,  21,  23,
-        24,  26,  28,  29,  31,  33,  35,
-        36,  38,  40,  41,  43,  45,  47,
-        48,  50,  52,  53,  55,  57,  59,
-        60,  62,  64,  65,  67,  69,  71,
-        72,  74,  76,  77,  79,  81,  83,
-        84,  86,  88,  89,  91,  93,  95,
-        96,  98, 100, 101, 103, 105, 107,
-       108, 110, 112, 113, 115, 117, 119,
-       120, 122, 124, 125, 127};
-  long blackKeys[BLACKKEYS] =
-      {  1,   3,   6,   8,  10,
-        13,  15,  18,  20,  22,
-        25,  27,  30,  32,  34,
-        37,  39,  42,  44,  46,
-        49,  51,  54,  56,  58,
-        61,  63,  66,  68,  70,
-        73,  75,  78,  80,  82,
-        85,  87,  90,  92,  94,
-        97,  99, 102, 104, 106,
-       109, 111, 114, 116, 118,
-       121, 123, 126};
-
-  /************* Initialization ***************/
-
-  midiTime = 0; /* MIDI time stamp */
-  midiPreviousFormulaStep = 0; /* Note in previous formula-building step */
-  midiPreviousLogicalStep = 0; /* Note in previous logical step */
-  midiFilePtr = NULL; /* Output file pointer */
-
-  /* Parse the parameter string passed by the user */
-  let(&midiLocalParam, edit(g_midiParameter, 32)); /* Convert to uppercase */
-
-  /* Set syncopation */
-  if (strchr(midiLocalParam, 'S') != NULL) {
-    midiSyncopate = 1; /* Syncopation */
-  } else {
-    midiSyncopate = 0; /* No syncopation */
-  }
-  /* Set halting character of syncopation (only has effect if
-     syncopation is on) */
-  if (strchr(midiLocalParam, 'H') != NULL) {
-    midiHesitate = 1; /* Silence all repeated fast notes */
-  } else {
-    midiHesitate = 0; /* Silence only every other one in a repeated sequence */
-  }
-  /* Set the tempo: 96=fast, 72=medium, 48=slow */
-  if (strchr(midiLocalParam, 'F') != NULL) {
-    midiTempo = 2 * TEMPO;  /* Fast */
-  } else {
-    if (strchr(midiLocalParam, 'M') != NULL) {
-      midiTempo = 3 * TEMPO / 2;  /* Medium */
-    } else {
-      midiTempo = TEMPO; /* Slow */
-    }
-  }
-  /* Get the keyboard type */
-  if (strchr(midiLocalParam, 'W') != NULL) {
-    keyboardType = WHITEKEYSFLAG;
-  } else {
-    if (strchr(midiLocalParam, 'B') != NULL) {
-      keyboardType = BLACKKEYSFLAG;
-    } else {
-      keyboardType = ALLKEYSFLAG;
-    }
-  }
-  /* Set the tempo: 96=fast, 48=slow */
-  if (strchr(midiLocalParam, 'I') != NULL) {
-    /* Do not skip any notes */
-    midiKeyInc = 1 + 1;
-  } else {
-    /* Allow an increment of up to 4 */
-    midiKeyInc = 4 + 1;
-  }
-
-  /* End of parsing user's parameter string */
-
-
-  /* Map keyboard key numbers to MIDI notes */
-  absMinKey = MIN_NOTE; /* Initialize for ALLKEYSFLAG case */
-  absMaxKey = MAX_NOTE;
-  keyboardOctave = 12; /* Keyboard keys per octave with no notes skipped */
-  switch (keyboardType) {
-    case ALLKEYSFLAG:
-      for (i = 0; i < 128; i++) key2MidiMap[i] = allKeys[i];
-     break;
-    case WHITEKEYSFLAG:
-      for (i = 0; i < WHITEKEYS; i++) key2MidiMap[i] = whiteKeys[i];
-      keyboardOctave = 7;
-      /* Get keyboard key for smallest midi note we want */
-      for (i = 0; i < WHITEKEYS; i++) {
-        if (key2MidiMap[i] >= absMinKey) {
-          absMinKey = i;
-          break;
-        }
-      }
-      /* Get keyboard key for largest midi note we want */
-      for (i = WHITEKEYS - 1; i >= 0; i--) {
-        if (key2MidiMap[i] <= absMinKey) {
-          absMinKey = i;
-          break;
-        }
-      }
-      /* Redundant array bound check for safety */
-      if (absMaxKey >= WHITEKEYS) absMaxKey = WHITEKEYS - 1;
-      if (absMinKey >= WHITEKEYS) absMinKey = WHITEKEYS - 1;
-      break;
-    case BLACKKEYSFLAG:
-      for (i = 0; i < BLACKKEYS; i++) key2MidiMap[i] = blackKeys[i];
-      keyboardOctave = 5;
-      /* Get keyboard key for smallest midi note we want */
-      for (i = 0; i < BLACKKEYS; i++) {
-        if (key2MidiMap[i] >= absMinKey) {
-          absMinKey = i;
-          break;
-        }
-      }
-      /* Get keyboard key for largest midi note we want */
-      for (i = BLACKKEYS - 1; i >= 0; i--) {
-        if (key2MidiMap[i] <= absMinKey) {
-          absMinKey = i;
-          break;
-        }
-      }
-      /* Redundant array bound check for safety */
-      if (absMaxKey >= BLACKKEYS) absMaxKey = BLACKKEYS - 1;
-      if (absMinKey >= BLACKKEYS) absMinKey = BLACKKEYS - 1;
-      break;
-  }
-
-  /* Get max indentation, so we can determine the scale factor
-     to make midi output fit within dynamic range */
-  midiMaxIndent = 0;
-  for (step = 0; step < plen; step++) {
-    if (indentationLevels[step] > midiMaxIndent)
-      midiMaxIndent = indentationLevels[step];
-  }
-
-  /* We will use integral note increments multiplied by the indentation
-     level.  We pick the largest possible, with a maximum of 4, so that the
-     midi output stays within the desired dynamic range.  If the proof has
-     *too* large a maximum indentation (not seen so far), the do loop below
-     will decrease the note increment to 0, so the MIDI output will just be a
-     flat sequence of repeating notes and therefore useless, but at least it
-     won't crash the MIDI converter.  */
-
-  /*midiKeyInc = 5;*/ /* Starting note increment, plus 1 */
-        /* (This is now set with the I parameter; see above) */
-  do { /* Decrement note incr until song will fit MIDI dyn range */
-    midiKeyInc--;
-    /* Compute the baseline note to which add the proof indentation
-      times the midiKeyInc.  The "12" is to allow for the shift
-      of one octave down of the sustained notes on "essential"
-      (i.e. logical, not formula-building) steps. */
-    midiBaseline = ((absMaxKey + absMinKey) / 2) -
-      (((midiMaxIndent * midiKeyInc) - keyboardOctave/*12*/) / 2);
-    midiMinKey = midiBaseline - keyboardOctave/*12*/;
-    midiMaxKey = midiBaseline + (midiMaxIndent * midiKeyInc);
-  } while ((midiMinKey < absMinKey || midiMaxKey > absMaxKey) &&
-      midiKeyInc > 0);
-
-  /* Open the output file */
-  let(&midiFileName, cat(g_Statement[g_showStatement].labelName,
-      ".txt", NULL)); /* Create file name from statement label */
-  print2("Creating MIDI source file \"%s\"...", midiFileName);
-
-  /* fSafeOpen() renames existing files with ~1,~2,etc.  This way
-     existing user files will not be accidentally destroyed. */
-  midiFilePtr = fSafeOpen(midiFileName, "w", 0/*noVersioningFlag*/);
-  if (midiFilePtr == NULL) {
-    print2("?Couldn't open %s\n", midiFileName);
-    goto midi_return;
-  }
-
-  /* Output the MIDI header */
-  fprintf(midiFilePtr, "MFile 0 1 %ld\n", midiTempo);
-  fprintf(midiFilePtr, "MTrk\n");
-
-  /* Create a string exactly 38 characters long for the Meta Text
-     label (I'm not sure why, but they are in the t2mf examples) */
-  let(&tmpStr, cat("Theorem ", statementLabel, " ", g_midiParameter,
-      space(30), NULL));
-  let(&tmpStr, left(tmpStr, 38));
-  fprintf(midiFilePtr, "0 Meta Text \"%s\"\n", tmpStr);
-  fprintf(midiFilePtr,
-      "0 Meta Copyright \"Released to Public Domain by N. Megill\"\n");
-
-  /************** Scan the proof ************************/
-
-  for (step = 0; step < plen; step++) {
-  /* Handle the processing that takes place at each proof step */
-    if (!logicalFlags[step]) {
-
-      /*** Process the higher fast notes for formula-building steps ***/
-      /* Get the "keyboard" key number */
-      midiKey = (midiKeyInc * indentationLevels[step]) + midiBaseline;
-      /* Redundant prevention of array bound violation */
-      if (midiKey < 0) midiKey = 0;
-      if (midiKey > absMaxKey) midiKey = absMaxKey;
-      /* Map "keyboard" key to MIDI note */
-      midiNote = key2MidiMap[midiKey];
-      if (midiPreviousFormulaStep != midiNote || !midiSyncopate) {
-        /* Turn note on at the current MIDI time stamp */
-        fprintf(midiFilePtr, "%ld On ch=2 n=%ld v=75\n", midiTime, midiNote);
-        /* Turn note off at the time stamp + 18 */
-        fprintf(midiFilePtr, "%ld On ch=2 n=%ld v=0\n", midiTime + 18,
-            midiNote);
-        midiPreviousFormulaStep = midiNote;
-      } else {
-        /* Skip turning on the note to give syncopation */
-        /* To prevent skipping two notes in a row, set last note
-           to 0 so next step it will not be skipped even if the
-           note still doesn't change; this makes the syncopation
-           more rhythmic */
-        if (!midiHesitate) {
-          midiPreviousFormulaStep = 0;
-        }
-      }
-      midiTime += 24; /* Add 24 to the MIDI time stamp */
-
-    } else {
-
-      /*** Process the deeper sustained notes for logical steps ***/
-      /* The idea here is to shift the note down 1 octave before
-         outputting it, so it is distinguished from formula-
-         building notes */
-      /* Get the "keyboard" key number */
-      midiKey = (midiKeyInc * indentationLevels[step]) + midiBaseline;
-      /* Redundant prevention of array bound violation */
-      if (midiKey < 0) midiKey = 0;
-      if (midiKey > absMaxKey) midiKey = absMaxKey;
-      /* Map "keyboard" key to MIDI note */
-      midiNote = key2MidiMap[midiKey];
-      midiNote = midiNote - 12; /* Down 1 octave */
-      if (midiNote < 0) midiNote = 0; /* For safety but should be redundant */
-      if (midiPreviousLogicalStep) { /* If 0, it's the first time */
-        /* Turn off the previous sustained note */
-        fprintf(midiFilePtr, "%ld On ch=1 n=%ld v=0\n", midiTime,
-            midiPreviousLogicalStep);
-      }
-      /* Turn on the new sustained note */
-      fprintf(midiFilePtr, "%ld On ch=1 n=%ld v=100\n", midiTime,
-          midiNote);
-      midiTime += 24; /* Add 24 to the MIDI time stamp */
-      midiPreviousLogicalStep = midiNote; /* Save for next step */
-
-    }
-  } /* next step */
-
-  /****************** Clean up and close output file ****************/
-
-  /* After the last step, do the file closing stuff */
-  /* Sustain the very last note a little longer - sounds better */
-  midiTime += 72;
-  fprintf(midiFilePtr, "%ld On ch=1 n=%ld v=0\n", midiTime,
-      midiPreviousLogicalStep);
-  /* Output the MIDI file trailer */
-  fprintf(midiFilePtr, "%ld Meta TrkEnd\n", midiTime);
-  fprintf(midiFilePtr, "TrkEnd\n");
-  fclose(midiFilePtr);
-  /* Inform the user the run time of the MIDI file */
-  print2(" length = %ld sec\n", (long)(midiTime / (2 * midiTempo)));
-
- midi_return:
-  /* Important: all local vstrings must be deallocated to prevent
-     memory leakage */
-  let(&midiFileName, "");
-  let(&tmpStr, "");
-  let(&midiLocalParam, "");
-
-} /* outputMidi */
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* mmcmds.c - assorted user commands */
+
+#include <string.h>
+#include <stdlib.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mmcmdl.h" /* For g_texFileName */
+#include "mmcmds.h"
+#include "mminou.h"
+#include "mmpars.h"
+#include "mmveri.h"
+#include "mmwtex.h" /* For g_htmlVarColor,... */
+#include "mmpfas.h"
+#include "mmunif.h" /* For g_bracketMatchInit, g_minSubstLen,
+                       ...and g_firstConst */
+
+/* Local prototypes */
+vstring bigAdd(vstring bignum1, vstring bignum2);
+vstring bigSub(vstring bignum1, vstring bignum2);
+
+flag g_printHelp = 0;
+
+/* For HTML output */
+vstring_def(g_printStringForReferencedBy);
+
+/* For MIDI */
+flag g_midiFlag = 0;
+vstring_def(g_midiParam);
+
+/* Type (i.e. print) a statement */
+void typeStatement(long showStmt,
+  flag briefFlag,
+  flag commentOnlyFlag,
+  flag texFlag,
+  flag htmlFlag)
+{
+  /* From HELP SHOW STATEMENT: Optional qualifiers:
+    / TEX - This qualifier will write the statement information to the
+        LaTeX file previously opened with OPEN TEX.
+            [Note:  texFlag=1 and htmlFlag=0 with this qualifier.]
+
+    / HTML - This qualifier will write the statement information to the
+        HTML file previously opened with OPEN HTML.
+            [Note:  texFlag=1 and htmlFlag=1 with this qualifier.]
+
+    / COMMENT_ONLY - This qualifier will show only the comment that
+        immediately precedes the statement.  This is useful when you are
+        using Metamath to preprocess LaTeX source you have created (see
+        HELP TEX)
+
+    / BRIEF - This qualifier shows the statement and its $e hypotheses
+        only.
+  */
+  long i, j, k, m, n;
+  vstring_def(str1);
+  vstring_def(str2);
+  vstring_def(str3);
+  nmbrString *nmbrTmpPtr1; /* Pointer only; not allocated directly */
+  nmbrString *nmbrTmpPtr2; /* Pointer only; not allocated directly */
+  nmbrString_def(nmbrDDList);
+  flag q1, q2;
+  flag type;
+  flag subType;
+  vstring_def(htmlDistinctVars);
+  char htmlDistinctVarsCommaFlag = 0;
+  vstring_def(str4);
+  vstring_def(str5);
+  long distVarGrps = 0;
+
+  /* For syntax breakdown of definitions in HTML page */
+  long zapStatement1stToken;
+  static long wffToken = -1; /* array index of the hard-coded token "wff" -
+      static so we only have to look it up once - set to -2 if not found */
+
+  subType = 0; /* Assign to prevent compiler warnings - not theoretically necessary */
+
+  if (!showStmt) bug(225); /* Must be 1 or greater */
+
+  if (!commentOnlyFlag && !briefFlag) {
+    assignStmtFileAndLineNum(showStmt);
+    let(&str1, cat("Statement ", str((double)showStmt),
+        " is located on line ", str((double)(g_Statement[showStmt].lineNum)),
+        " of the file ", NULL));
+    if (!texFlag) {
+      assignMathboxInfo(); /* In case it hasn't been assigned yet */
+          /* We need to call assignMathboxInfo() to do the initial assignment,
+             in order to prevent "let(&" from executing during the
+             getMathboxUser() call below, which would corrupt the "cat()"
+             temporary stack (since "let(&" empties the stack). */
+      printLongLine(cat(str1,
+        "\"", g_Statement[showStmt].fileName,
+        "\".",
+        (g_Statement[showStmt].pinkNumber == 0) ?   /* !=0 means $a or $p */
+           "" :
+           cat("  Its statement number for HTML pages is ",
+               str((double)(g_Statement[showStmt].pinkNumber)), ".", NULL),
+        ((getMathboxUser(showStmt))[0] == 0) ?
+           "" :
+           cat("  It is in the mathbox for ", getMathboxUser(showStmt), ".",
+               NULL),
+        NULL), "", " ");
+    } else {
+      if (!htmlFlag) let(&g_printString, "");
+      g_outputToString = 1; /* Flag for print2 to add to g_printString */
+                     /* Note that printTexLongMathString resets it */
+      if (!(htmlFlag && texFlag)) {
+        /*
+        printLongLine(cat(str1, "{\\tt ",
+            asciiToTt(g_Statement[showStmt].fileName),
+            "}.", NULL), "", " ");
+        */
+      } else {
+        /* For categorizing html pages, we use the source file convention
+           that syntax statements don't start with "|-" and that axioms
+           have labels starting with "ax-".  It is up to the database
+           creator to follow this standard, which is not enforced. */
+#define SYNTAX 1
+#define DEFINITION 2
+#define AXIOM 3
+#define THEOREM 4
+        if (g_Statement[showStmt].type == (char)p_) {
+          subType = THEOREM;
+        } else {
+          /* Must be a_ due to filter in main() */
+          if (g_Statement[showStmt].type != (char)a_) bug(228);
+          if (strcmp("|-", g_MathToken[
+              (g_Statement[showStmt].mathString)[0]].tokenName)) {
+            subType = SYNTAX;
+          } else {
+            if (!strcmp("ax-", left(g_Statement[showStmt].labelName, 3))) {
+              subType = AXIOM;
+            } else {
+              subType = DEFINITION;
+            }
+          }
+        }
+        switch (subType) {
+          case SYNTAX:
+            let(&str1, "Syntax Definition"); break;
+          case DEFINITION:
+            let(&str1, "Definition"); break;
+          case AXIOM:
+            let(&str1, "Axiom"); break;
+          case THEOREM:
+            let(&str1, "Theorem"); break;
+          default:
+            bug(229);
+        }
+
+        /* Print a small pink statement number after the statement */
+        free_vstring(str2);
+        str2 = pinkHTML(showStmt);
+        printLongLine(cat("<CENTER><B><FONT SIZE=\"+1\">", str1,
+            " <FONT COLOR=", GREEN_TITLE_COLOR,
+            ">", g_Statement[showStmt].labelName,
+            "</FONT></FONT></B>", str2, "</CENTER>", NULL), "", "\"");
+      } /* (htmlFlag && texFlag) */
+      g_outputToString = 0;
+    } /* texFlag */
+  }
+
+  if (!briefFlag || commentOnlyFlag) {
+    free_vstring(str1);
+    str1 = getDescription(showStmt);
+    if (!str1[0] /* No comment */) {
+      print2("?Warning: Statement \"%s\" has no comment\n",
+          g_Statement[showStmt].labelName);
+      /* We must print a blank comment to have \begin{lemma} */
+      if (texFlag && !htmlFlag && !g_oldTexFlag) {
+        let(&str1, "TO DO: PUT DESCRIPTION HERE");
+      }
+    }
+    if (str1[0]) {
+      if (!texFlag) {
+        printLongLine(cat("\"", str1, "\"", NULL), "", " ");
+      } else {
+        if (!htmlFlag) {  /* LaTeX */
+          if (!g_oldTexFlag) {
+            /* Distinguish axiom, definition, theorem */
+            /* Note: changes here must be mirrored in the \end{...} below */
+            if (g_Statement[showStmt].type == a_) {
+              if (!strcmp(left(g_Statement[showStmt].labelName, 3), "ax-")) {
+                let(&str3, "axiom");
+              } else {
+                let(&str3, "definition");
+              }
+            } else {
+              let(&str3, "theorem");
+            }
+            let(&str1, cat("\\begin{", str3, "}\\label{",
+                left(str3, 3), ":",
+                g_Statement[showStmt].labelName, "} ", str1, NULL));
+          } else {
+            /* Add separation space between theorems */
+            let(&str1, cat("\n\\vspace{1ex} %2\n\n", str1, NULL));
+          }
+        }
+        printTexComment(str1,              /* Sends result to g_texFilePtr */
+            1, /* 1 = htmlCenterFlag */
+            PROCESS_EVERYTHING, /* actionBits */
+            1  /* 1 = fileCheck */);
+      }
+    }
+  }
+  if (commentOnlyFlag && !briefFlag) goto returnPoint;
+
+  if ((briefFlag && !texFlag) || (htmlFlag && texFlag)) {
+    /* In BRIEF mode screen output, show $d's */
+
+    if (htmlFlag && texFlag) {
+      let(&htmlDistinctVars, "");
+      htmlDistinctVarsCommaFlag = 0;
+    }
+
+    /* This algorithm is used to re-merge $d pairs
+       into groups of 3 or more when possible, for a more compact display.
+       The algorithm does not merge groups optimally, but it should be
+       adequate.  For example, in set.mm (e.g. old r19.23aivv):
+         $d x ps $.  $d y ps $.  $d y A $.  $d x y $.
+       produces in SHOW STATEMENT (note redundant 3rd $d):
+         $d ps x y $.  $d y A $.  $d x y $.
+       However, in set.mm the equivalent (and better anyway):
+         $d x y ps $.  $d y A $.
+       produces the same thing when re-merged in SHOW STATEMENT. */
+    let(&str1, "");
+    nmbrTmpPtr1 = g_Statement[showStmt].reqDisjVarsA;
+    nmbrTmpPtr2 = g_Statement[showStmt].reqDisjVarsB;
+    i = nmbrLen(nmbrTmpPtr1);
+    if (i /* Number of mandatory $d pairs */) {
+      nmbrLet(&nmbrDDList, NULL_NMBRSTRING);
+      for (k = 0; k < i; k++) {
+        /* Is one of the variables in the current list? */
+        if (!nmbrElementIn(1, nmbrDDList, nmbrTmpPtr1[k]) &&
+            !nmbrElementIn(1, nmbrDDList, nmbrTmpPtr2[k])) {
+          /* No, so close out the current list */
+          if (!(htmlFlag && texFlag)) {
+            if (k == 0) let(&str1, "$d");
+            else let(&str1, cat(str1, " $.  $d", NULL));
+          } else {
+            let(&htmlDistinctVars, cat(htmlDistinctVars, " &nbsp; ",
+                NULL));
+            htmlDistinctVarsCommaFlag = 0;
+            distVarGrps++;
+          }
+          nmbrLet(&nmbrDDList, NULL_NMBRSTRING);
+        }
+        /* Are both variables required to be distinct from all others
+           in current list? */
+        for (n = 0; n < nmbrLen(nmbrDDList); n++) {
+          if (nmbrDDList[n] != nmbrTmpPtr1[k] &&
+              nmbrDDList[n] != nmbrTmpPtr2[k]) {
+            q1 = 0; q2 = 0;
+            for (m = 0; m < i; m++) {
+              if ((nmbrTmpPtr1[m] == nmbrDDList[n] &&
+                      nmbrTmpPtr2[m] == nmbrTmpPtr1[k]) ||
+                  (nmbrTmpPtr2[m] == nmbrDDList[n] &&
+                      nmbrTmpPtr1[m] == nmbrTmpPtr1[k])) {
+                q1 = 1; /* 1st var is required to be distinct */
+              }
+              if ((nmbrTmpPtr1[m] == nmbrDDList[n] &&
+                      nmbrTmpPtr2[m] == nmbrTmpPtr2[k]) ||
+                  (nmbrTmpPtr2[m] == nmbrDDList[n] &&
+                      nmbrTmpPtr1[m] == nmbrTmpPtr2[k])) {
+                q2 = 1;  /* 2nd var is required to be distinct */
+              }
+              if (q1 && q2) break; /* Found both */
+            }  /* Next m */
+            if (!q1 || !q2) {
+              /* One of the variables is not required to be distinct from
+                all others in the current list, so close out current list */
+              if (!(htmlFlag && texFlag)) {
+                if (k == 0) let(&str1, "$d");
+                else let(&str1, cat(str1, " $.  $d", NULL));
+              } else {
+                let(&htmlDistinctVars, cat(htmlDistinctVars, " &nbsp; ",
+                    NULL));
+                htmlDistinctVarsCommaFlag = 0;
+                distVarGrps++;
+              }
+
+              free_nmbrString(nmbrDDList);
+              break; /* Out of n loop */
+            }
+          } /* If $d var in current list is not same as one we're adding */
+        } /* Next n */
+        /* If the variable is not already in current list, add it */
+        if (!nmbrElementIn(1, nmbrDDList, nmbrTmpPtr1[k])) {
+          if (!(htmlFlag && texFlag)) {
+            let(&str1, cat(str1, " ", g_MathToken[nmbrTmpPtr1[k]].tokenName,
+                NULL));
+          } else {
+
+            if (htmlDistinctVarsCommaFlag) {
+              let(&htmlDistinctVars, cat(htmlDistinctVars, ",", NULL));
+            }
+            htmlDistinctVarsCommaFlag = 1;
+            free_vstring(str2);
+            str2 = tokenToTex(g_MathToken[nmbrTmpPtr1[k]].tokenName, showStmt);
+                 /* tokenToTex allocates str2; we must deallocate it */
+            let(&htmlDistinctVars, cat(htmlDistinctVars, str2, NULL));
+
+          }
+          nmbrLet(&nmbrDDList, nmbrAddElement(nmbrDDList, nmbrTmpPtr1[k]));
+        }
+        if (!nmbrElementIn(1, nmbrDDList, nmbrTmpPtr2[k])) {
+          if (!(htmlFlag && texFlag)) {
+            let(&str1, cat(str1, " ", g_MathToken[nmbrTmpPtr2[k]].tokenName,
+                NULL));
+          } else {
+
+            if (htmlDistinctVarsCommaFlag) {
+              let(&htmlDistinctVars, cat(htmlDistinctVars, ",", NULL));
+            }
+            htmlDistinctVarsCommaFlag = 1;
+            free_vstring(str2);
+            str2 = tokenToTex(g_MathToken[nmbrTmpPtr2[k]].tokenName, showStmt);
+                 /* tokenToTex allocates str2; we must deallocate it */
+            let(&htmlDistinctVars, cat(htmlDistinctVars, str2, NULL));
+
+          }
+          nmbrLet(&nmbrDDList, nmbrAddElement(nmbrDDList, nmbrTmpPtr2[k]));
+        }
+      } /* Next k */
+      /* Close out entire list */
+      if (!(htmlFlag && texFlag)) {
+        let(&str1, cat(str1, " $.", NULL));
+        printLongLine(str1, "  ", " ");
+      } else {
+        /* (do nothing) */
+      }
+    } /* if i(#$d's) > 0 */
+  }
+
+  if (briefFlag || texFlag) {
+    /* For BRIEF mode, print $e hypotheses (only) before statement */
+    /* Also do it for HTML output */
+    j = nmbrLen(g_Statement[showStmt].reqHypList);
+    k = 0;
+    for (i = 0; i < j; i++) {
+      /* Count the number of essential hypotheses */
+      if (g_Statement[g_Statement[showStmt].reqHypList[i]].type
+        == (char)e_) k++;
+
+      /* For syntax definitions, also include $f hypotheses so user can more
+         easily match them in syntax breakdowns of axioms and definitions */
+      if (subType == SYNTAX && (texFlag && htmlFlag)) {
+        if (g_Statement[g_Statement[showStmt].reqHypList[i]].type
+          == (char)f_) k++;
+      }
+
+    }
+    if (k) {
+      if (texFlag) {
+        /* Note that printTexLongMath resets it to 0 */
+        g_outputToString = 1;
+      }
+      if (texFlag && htmlFlag) {
+        print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
+            MINT_BACKGROUND_COLOR);
+        /* For bobby.cast.org approval */
+        print2("SUMMARY=\"%s\">\n", (k == 1) ? "Hypothesis" : "Hypotheses");
+        print2("<CAPTION><B>%s</B></CAPTION>\n",
+            (k == 1) ? "Hypothesis" : "Hypotheses");
+        print2("<TR><TH>Ref\n");
+        print2("</TH><TH>Expression</TH></TR>\n");
+      }
+      for (i = 0; i < j; i++) {
+        k = g_Statement[showStmt].reqHypList[i];
+        if (g_Statement[k].type != (char)e_
+
+            /* For syntax definitions, include $f hypotheses so user can more
+               easily match them in syntax breakdowns of axioms & definitions */
+            && !(subType == SYNTAX && (texFlag && htmlFlag)
+                && g_Statement[k].type == (char)f_)
+
+            ) continue;
+
+        if (!texFlag) {
+          let(&str2, cat(str((double)k), " ", NULL));
+        } else {
+          let(&str2, "  ");
+        }
+        let(&str2, cat(str2, g_Statement[k].labelName,
+            " $", chr(g_Statement[k].type), " ", NULL));
+        if (!texFlag) {
+          printLongLine(cat(str2,
+              nmbrCvtMToVString(g_Statement[k].mathString), " $.", NULL),
+              "      "," ");
+        } else { /* if texFlag */
+          /* texFlag was (misleadingly) included below to facilitate search
+             for "htmlFlag && texFlag". */
+          if (!(htmlFlag && texFlag)) {
+            if (!g_oldTexFlag) {
+              /* Do nothing */
+            } else {
+              let(&str3, space((long)strlen(str2)));
+              printTexLongMath(g_Statement[k].mathString,
+                  str2, str3, 0, 0);
+            }
+          } else {
+            g_outputToString = 1;
+            print2("<TR ALIGN=LEFT><TD>%s</TD><TD>\n",
+                g_Statement[k].labelName);
+            /* Print hypothesis */
+            printTexLongMath(g_Statement[k].mathString, "", "", 0, 0);
+          }
+        }
+      } /* next i */
+      if (texFlag && htmlFlag) {
+        g_outputToString = 1;
+        print2("</TABLE></CENTER>\n");
+      }
+    } /* if k (#essential hyp) */
+  }
+
+  let(&str1, "");
+  type = g_Statement[showStmt].type;
+  if (type == p_) let(&str1, " $= ...");
+  if (!texFlag)
+    let(&str2, cat(str((double)showStmt), " ", NULL));
+  else
+    let(&str2, "  ");
+  let(&str2, cat(str2, g_Statement[showStmt].labelName,
+      " $",chr(type), " ", NULL));
+  if (!texFlag) {
+    printLongLine(cat(str2,
+        nmbrCvtMToVString(g_Statement[showStmt].mathString),
+        str1, " $.", NULL), "      ", " ");
+  } else {
+    if (!(htmlFlag && texFlag)) {  /* really !htmlFlag & texFlag */
+      if (!g_oldTexFlag) {
+        g_outputToString = 1;
+        print2("\\begin{align}\n");
+        free_vstring(str3);
+        /* Get HTML hypotheses => assertion */
+        str3 = getTexOrHtmlHypAndAssertion(showStmt); /* In mmwtex.c */
+        printLongLine(cat(str3,
+              /* No space before \label to make it easier to find last
+                 parenthesis in a post-processing script */
+              "\\label{eq:",
+              g_Statement[showStmt].labelName,
+              "}",
+
+              /* Add "\tag{..}" to use .mm labels instead of equation numbers */
+              /* (Suggested by Ari Ferrera) */
+              "\\tag{",
+              g_Statement[showStmt].labelName,
+              "}",
+
+              NULL), "    ", " ");
+        /* print2("    \\label{eq:%s}\n",g_Statement[showStmt].labelName); */
+        print2("\\end{align}\n");
+
+
+        /* Distinguish axiom, definition, theorem for LaTeX */
+        /* Note: changes here must be mirrored in the \begin{...} above */
+        if (g_Statement[showStmt].type == a_) {
+          if (!strcmp(left(g_Statement[showStmt].labelName, 3), "ax-")) {
+            let(&str3, "axiom");
+          } else {
+            let(&str3, "definition");
+          }
+        } else {
+          let(&str3, "theorem");
+        }
+        print2("%s\n", cat("\\end{", str3, "}", NULL));
+
+        fprintf(g_texFilePtr, "%s", g_printString);
+        let(&g_printString, "");
+        g_outputToString = 0;
+
+      } else { /* old TeX code */
+        let(&str3, space((long)strlen(str2))); /* 3rd argument of printTexLongMath
+            cannot be temp allocated */
+        printTexLongMath(g_Statement[showStmt].mathString,
+            str2, str3, 0, 0);
+      }
+    } else { /* (htmlFlag && texFlag) */
+      g_outputToString = 1;
+      print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
+          MINT_BACKGROUND_COLOR);
+      /* For bobby.cast.org approval */
+      print2("SUMMARY=\"Assertion\">\n");
+      print2("<CAPTION><B>Assertion</B></CAPTION>\n");
+      print2("<TR><TH>Ref\n");
+      print2("</TH><TH>Expression</TH></TR>\n");
+      printLongLine(cat(
+       "<TR ALIGN=LEFT><TD><FONT COLOR=",
+          GREEN_TITLE_COLOR, "><B>", g_Statement[showStmt].labelName,
+          "</B></FONT></TD><TD>", NULL), "      ", " ");
+      printTexLongMath(g_Statement[showStmt].mathString, "", "", 0, 0);
+      g_outputToString = 1;
+      print2("</TABLE></CENTER>\n");
+    }
+  }
+
+  if (briefFlag) goto returnPoint;
+
+  switch (type) {
+    case a_:
+    case p_:
+      /* This is not really needed but keeps output consistent
+         with previous version.  It puts a blank line before the HTML
+         "distinct variable" list. */
+      if (texFlag && htmlFlag) {
+        g_outputToString = 1;
+        print2("\n");
+        g_outputToString = 0;
+      }
+
+      if (!texFlag) {
+        print2("Its mandatory hypotheses in RPN order are:\n");
+      }
+      j = nmbrLen(g_Statement[showStmt].reqHypList);
+      for (i = 0; i < j; i++) {
+        k = g_Statement[showStmt].reqHypList[i];
+        if (g_Statement[k].type != (char)e_ && (!htmlFlag && texFlag))
+          continue; /* Don't put $f's in LaTeX output */
+        let(&str2, cat("  ",g_Statement[k].labelName,
+            " $", chr(g_Statement[k].type), " ", NULL));
+        if (!texFlag) {
+          printLongLine(cat(str2,
+              nmbrCvtMToVString(g_Statement[k].mathString), " $.", NULL),
+              "      "," ");
+        }
+      }
+      /* This is not really needed but keeps output consistent
+         with previous version.  It puts a blank line before the HTML
+         "distinct variable" list. */
+      if (texFlag && htmlFlag) {
+        g_outputToString = 1;
+        print2("\n");
+        g_outputToString = 0;
+      }
+      if (j == 0 && !texFlag) print2("  (None)\n");
+      let(&str1, "");
+      nmbrTmpPtr1 = g_Statement[showStmt].reqDisjVarsA;
+      nmbrTmpPtr2 = g_Statement[showStmt].reqDisjVarsB;
+      i = nmbrLen(nmbrTmpPtr1);
+      if (i) {
+        for (k = 0; k < i; k++) {
+          if (!texFlag) {
+            let(&str1, cat(str1, ", <",
+                g_MathToken[nmbrTmpPtr1[k]].tokenName, ",",
+                g_MathToken[nmbrTmpPtr2[k]].tokenName, ">", NULL));
+          } else {
+            if (htmlFlag && texFlag) {
+              free_vstring(str2);
+              str2 = tokenToTex(g_MathToken[nmbrTmpPtr1[k]].tokenName, showStmt);
+                   /* tokenToTex allocates str2; we must deallocate it */
+              let(&str1, cat(str1, " &nbsp; ", str2, NULL));
+              free_vstring(str2);
+              str2 = tokenToTex(g_MathToken[nmbrTmpPtr2[k]].tokenName, showStmt);
+              let(&str1, cat(str1, ",", str2, NULL));
+            }
+          }
+        }
+        if (!texFlag)
+          printLongLine(cat(
+              "Its mandatory disjoint variable pairs are:  ",
+              right(str1,3),NULL),"  "," ");
+      }
+      if (type == p_ &&
+          nmbrLen(g_Statement[showStmt].optHypList)
+          && !texFlag) {
+        printLongLine(cat(
+           "Its optional hypotheses are:  ",
+            nmbrCvtRToVString(
+            g_Statement[showStmt].optHypList,
+                0, /*explicitTargets*/
+                0 /*statemNum, used only if explicitTargets*/), NULL),
+            "      "," ");
+      }
+      nmbrTmpPtr1 = g_Statement[showStmt].optDisjVarsA;
+      nmbrTmpPtr2 = g_Statement[showStmt].optDisjVarsB;
+      i = nmbrLen(nmbrTmpPtr1);
+      if (i && type == p_) {
+        if (!texFlag) {
+          let(&str1, "");
+        }
+        for (k = 0; k < i; k++) {
+          if (!texFlag) {
+            let(&str1, cat(str1, ", <",
+                g_MathToken[nmbrTmpPtr1[k]].tokenName, ",",
+                g_MathToken[nmbrTmpPtr2[k]].tokenName, ">", NULL));
+          } /* if !texFlag */
+        } /* next k */
+        if (!texFlag) {
+          printLongLine(cat(
+              "Its optional disjoint variable pairs are:  ",
+              right(str1, 3), NULL), "  ", " ");
+        }
+      } /* if (i && type == p_) */
+
+
+      if (texFlag && htmlFlag) { /* It's a web page */
+
+        if (htmlDistinctVars[0] != 0) {
+          g_outputToString = 1;
+          printLongLine(cat(
+              "<CENTER>",
+              "<A HREF=\"",
+
+              /* g_htmlHome is set by htmlhome in $t comment */
+              (instr(1, g_htmlHome, "mmset.html") > 0) ?
+                  "mmset.html" :
+                  /* The following link will work in the NF and other
+                     "Proof Explorers" */
+                  "../mpeuni/mmset.html",
+
+              "#distinct\">Distinct variable</A> group",
+              distVarGrps > 1 ? "s" : "",
+              ": ",
+              /* Put a span around the variable list to localize
+                 the use of the special math font for ALT_HTML */
+              (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
+              htmlDistinctVars,
+              (g_altHtmlFlag ? "</SPAN>" : ""),
+              "</CENTER>",
+              NULL), "", "\"");
+          g_outputToString = 0;
+        }
+
+        free_vstring(str2);
+        str2 = htmlAllowedSubst(showStmt);
+        if (str2[0] != 0) {
+          g_outputToString = 1;
+          /* Print the list of allowed free variables */
+          printLongLine(str2, "", "\"");
+          g_outputToString = 0;
+        }
+
+      } /* if (texFlag && htmlFlag) */
+
+      if (texFlag) {
+        g_outputToString = 1;
+        if (htmlFlag && texFlag) print2("<HR NOSHADE SIZE=1>\n");
+        g_outputToString = 0; /* Restore normal output */
+        break; /* case a_ or p_ */
+      }
+      let(&str1, nmbrCvtMToVString(
+          g_Statement[showStmt].reqVarList));
+      if (!strlen(str1)) let(&str1, "(None)");
+      printLongLine(cat(
+          "The statement and its hypotheses require the variables:  ",
+          str1, NULL), "      ", " ");
+      if (type == p_ &&
+          nmbrLen(g_Statement[showStmt].optVarList)) {
+        printLongLine(cat(
+            "These additional variables are allowed in its proof:  "
+            ,nmbrCvtMToVString(
+            g_Statement[showStmt].optVarList),NULL),"      ",
+            " ");
+        /*??? Add variables required by proof */
+      }
+      /* Note:  g_Statement[].reqVarList is only stored for $a and $p
+         statements, not for $e or $f. */
+      let(&str1, nmbrCvtMToVString(
+          g_Statement[showStmt].reqVarList));
+      if (!strlen(str1)) let(&str1, "(None)");
+      printLongLine(cat("The variables it contains are:  ",
+          str1, NULL),
+          "      ", " ");
+      break; /* case a_ or p_ */
+    default:
+      break;
+  } /* End switch(type) */
+  if (texFlag) {
+    g_outputToString = 0;
+  }
+
+  /* Start of finding definition for syntax statement */
+  if (htmlFlag && texFlag) {
+
+    /* For syntax declarations, find the first definition that follows
+       it.  It is up to the user to arrange the database so that a
+       meaningful definition is picked. */
+    if (subType == SYNTAX) {
+      for (i = showStmt + 1; i <= g_statements; i++) {
+        if (g_Statement[i].type == (char)a_) {
+          if (!strcmp("|-", g_MathToken[
+              (g_Statement[i].mathString)[0]].tokenName)) {
+            /* It's a definition or axiom */
+            /* See if each constant token in the syntax statement
+               exists in the definition; if not don't use the definition */
+            j = 1;
+            /* We start with k=1 for 2nd token (1st is wff, class, etc.) */
+            for (k = 1; k < g_Statement[showStmt].mathStringLen; k++) {
+              if (g_MathToken[(g_Statement[showStmt].mathString)[k]].
+                  tokenType == (char)con_) {
+                if (!nmbrElementIn(1, g_Statement[i].mathString,
+                    (g_Statement[showStmt].mathString)[k])) {
+                  /* The definition being considered doesn't have one of
+                     the constant symbols in the syntax statement, so
+                     reject it */
+                  j = 0;
+                  break; /* Out of k loop */
+                }
+              }
+            } /* Next k */
+            if (j) {
+              /* Successful - use this definition or axiom as the reference */
+              g_outputToString = 1;
+              let(&str1, left(g_Statement[i].labelName, 3));
+              let(&str2, "");
+              str2 = pinkHTML(i);
+              if (!strcmp(str1, "ax-")) {
+                printLongLine(cat(
+                    "<CENTER>This syntax is primitive.",
+                    "  The first axiom using it is <A HREF=\"",
+                    g_Statement[i].labelName, ".html\">",
+                    g_Statement[i].labelName,
+                    "</A>", str2, ".</CENTER><HR NOSHADE SIZE=1>",
+                    NULL), "", "\"");
+              } else {
+                printLongLine(cat(
+                    "<CENTER>See definition <A HREF=\"",
+                    g_Statement[i].labelName, ".html\">",
+                    g_Statement[i].labelName, "</A>", str2,
+                    " for more information.</CENTER><HR NOSHADE SIZE=1>",
+                    NULL), "", "\"");
+              }
+
+              printLongLine(cat(
+                  "<CENTER><TABLE CELLSPACING=7><TR><TD ALIGN=LEFT><FONT SIZE=-1>",
+                  "<B>Colors of variables:</B> ",
+                  g_htmlVarColor, "</FONT></TD></TR>",
+                  NULL), "", "\"");
+              g_outputToString = 0;
+              break; /* Out of i loop */
+            }
+          }
+        }
+      } /* Next i */
+    } /* if (subType == SYNTAX) */
+
+
+    /* For definitions, we pretend that the definition is a "wff" (hard-coded
+       here; the .mm database provided by the user must use this convention).
+       We use the proof assistant tools to prove that the statement is
+       a wff, then we print the wff construction proof to the HTML file. */
+    if (subType == DEFINITION || subType == AXIOM) {
+
+      /* Look up the token "wff" if we haven't found it before */
+      if (wffToken == -1) { /* First time */
+        wffToken = -2; /* In case it's not found because the user's source
+            used a convention different for "wff" for wffs */
+        for (i = 0; i < g_mathTokens; i++) {
+          if (!strcmp("wff", g_MathToken[i].tokenName)) {
+            wffToken = i;
+            break;
+          }
+        }
+      }
+
+      if (wffToken >= 0) {
+        /* Temporarily zap statement type from $a to $p */
+        if (g_Statement[showStmt].type != (char)a_) bug(231);
+        g_Statement[showStmt].type = (char)p_;
+        /* Temporarily zap statement with "wff" token in 1st position
+           so parseProof will not give errors (in typeProof() call) */
+        zapStatement1stToken = (g_Statement[showStmt].mathString)[0];
+        (g_Statement[showStmt].mathString)[0] = wffToken;
+        if (strcmp("|-", g_MathToken[zapStatement1stToken].tokenName)) bug(230);
+
+        nmbrTmpPtr1 = NULL_NMBRSTRING;
+        nmbrLet(&nmbrTmpPtr1, g_Statement[showStmt].mathString);
+
+        /* Find proof of formula or simple theorem (no new vars in $e's) */
+        /* maxEDepth is the maximum depth at which statements with $e
+           hypotheses are
+           considered.  A value of 0 means none are considered. */
+        nmbrTmpPtr2 = proveFloating(nmbrTmpPtr1 /*mString*/,
+            showStmt /*statemNum*/, 0 /*maxEDepth*/,
+            0, /*step:  0 = step 1 */ /*For messages*/
+            0,  /*not noDistinct*/
+            2, /* override discouraged-usage statements silently */
+            1 /* Always allow other mathboxes */
+            );
+
+        if (nmbrLen(nmbrTmpPtr2)) {
+          /* A proof for the step was found. */
+          /* Get packed form of proof for shorter display */
+          nmbrLet(&nmbrTmpPtr2, nmbrSquishProof(nmbrTmpPtr2));
+          /* Temporarily zap proof into statement structure */
+          /* (The bug check makes sure there is no proof attached to the
+              definition - this would be impossible) */
+          if (strcmp(g_Statement[showStmt].proofSectionPtr, "")) bug(231);
+          if (g_Statement[showStmt].proofSectionLen != 0) bug(232);
+          let(&str1, nmbrCvtRToVString(nmbrTmpPtr2,
+                0, /*explicitTargets*/
+                0 /*statemNum, used only if explicitTargets*/));
+          /* Temporarily zap proof into the $a statement */
+          g_Statement[showStmt].proofSectionPtr = str1;
+          g_Statement[showStmt].proofSectionLen = (long)strlen(str1) - 1;
+
+          /* Display the HTML proof of syntax breakdown */
+          typeProof(showStmt,
+              0 /*pipFlag*/,
+              0 /*startStep*/,
+              0 /*endStep*/,
+              0 /*endIndent*/,
+              0 /*essentialFlag*/, /* <- also used as def flag in typeProof */
+              1 /*renumberFlag*/,
+              0 /*unknownFlag*/,
+              0 /*notUnifiedFlag*/,
+              0 /*reverseFlag*/,
+              1 /*noIndentFlag*/,
+              0 /*splitColumn*/,
+              0 /*skipRepeatedSteps*/,
+              1 /*texFlag*/,  /* Means either latex or html */
+              1 /*htmlFlag*/);
+
+          /* Restore the zapped statement structure */
+          g_Statement[showStmt].proofSectionPtr = "";
+          g_Statement[showStmt].proofSectionLen = 0;
+
+          /* Deallocate storage */
+          free_vstring(str1);
+          free_nmbrString(nmbrTmpPtr2);
+
+        } else { /* if (nmbrLen(nmbrTmpPtr2)) else */
+          /* Proof was not found - probable syntax error */
+          if (g_outputToString != 0) bug(246);
+          printLongLine(cat(
+              "?Warning: Unable to generate syntax breakdown for \"",
+              g_Statement[showStmt].labelName,
+              "\".", NULL), "    ", " ");
+        }
+
+
+        /* Restore the zapped statement structure */
+        g_Statement[showStmt].type = (char)a_;
+        (g_Statement[showStmt].mathString)[0] = zapStatement1stToken;
+
+        /* Deallocate storage */
+        free_nmbrString(nmbrTmpPtr1);
+
+      } /* if (wffToken >= 0) */
+
+    } /* if (subType == DEFINITION) */
+
+
+  } /* if (htmlFlag && texFlag) */
+  /* End of finding definition for syntax statement */
+
+
+  /* Start of creating used-by list for html page */
+  if (htmlFlag && texFlag) {
+    /* Clear out any previous g_printString accumulation
+       for g_printStringForReferencedBy case below */
+    fprintf(g_texFilePtr, "%s", g_printString);
+    let(&g_printString, "");
+    /* Start outputting to g_printString */
+    if (g_outputToString != 0) bug(242);
+    g_outputToString = 1;
+    if (subType != SYNTAX) { /* Only do this for
+        definitions, axioms, and theorems, not syntax statements */
+      free_vstring(str1);
+      g_outputToString = 0; /* Switch output to console in case
+            traceUsage reports an error */
+      str1 = traceUsage(showStmt,
+          0, /* recursiveFlag */
+          0 /* cutoffStmt */);
+      g_outputToString = 1; /* Restore output to string */
+
+      /* str1[i] will be 'Y' if used by showStmt */
+      /* Convert usage list str1 to html links */
+      switch (subType) {
+        case AXIOM:  let(&str3, "axiom"); break;
+        case DEFINITION: let(&str3, "definition"); break;
+        case THEOREM: let(&str3, "theorem"); break;
+        default: bug(233);
+      }
+      let(&str2, cat("<TR><TD ALIGN=LEFT><FONT SIZE=-1><B>This ", str3,
+          " is referenced by:</B>", NULL));
+
+      if (str1[0] == 'Y') { /* Used by at least one */
+        let(&str5, ""); /* Buffer for very long strings */
+        /* Scan all future statements in str1 Y/N list */
+        for (m = showStmt + 1; m <= g_statements; m++) {
+          /* Scan the used-by map */
+          if (str1[m] != 'Y') continue;
+          /* Get the label */
+          let(&str3, g_Statement[m].labelName);
+          /* It should be a $p */
+          if (g_Statement[m].type != p_) bug(241);
+          /* Get the pink number */
+          free_vstring(str4);
+          str4 = pinkHTML(m);
+          /* Assemble the href */
+          let(&str2, cat(str2, " &nbsp;<A HREF=\"",
+              str3, ".html\">",
+              /*str3, "</A>", str4, NULL));*/
+              str3, "</A>\n", str4, NULL));
+          /* 8-Aug-2008 nm If line is very long, print it out and reset
+             it to speed up program (SHOW STATEMENT syl/HTML is very slow) */
+          /* 8-Aug-2008 nm This doesn't solve problem, because the bottleneck
+             is printing g_printStringForReferencedBy below.  This whole
+             code section needs to be redesigned to solve the speed problem. */
+          /* 19-Sep-2012 nm Try again to fix SHOW STATEMENT syl/HTML speed
+             without a major rewrite.  Unfortunately, made little difference. */
+          /* 18-Jul-2015: Part of slowdown was due to the old
+              traceUsage algorithm that built a huge string of labels.  Improved
+             from 313 sec to 280 sec for 'sh st syl/a'; still a problem. */
+          /* Accumulate large cat buffer when small cats exceed certain size */
+          if (strlen(str2) > 5000) {
+            let(&str5, cat(str5, str2, NULL));
+            let(&str2, "");
+          }
+        } /* next m (statement number) */
+
+
+      } else {
+        /* There is no usage of this statement; print "(None)" */
+        let(&str5, "");
+        let(&str2, cat(str2, " (None)", NULL));
+
+      } /* if (str1[0] == 'Y') */
+      /* Include buffer in output string */
+      let(&str2, cat(str5, str2, "</FONT></TD></TR>", NULL));
+      if (g_printString[0]) {
+        bug(256);
+      }
+      let(&g_printString, str2);
+    } /* if (subType != SYNTAX) */
+    if (subType == THEOREM) {
+      /* The "referenced by" does not show up after the proof
+         because we moved the typeProof() to below.  Therefore, we save
+         g_printString into a temporary global holding variable to print
+         at the proper place inside of typeProof().  Ugly but necessary
+         with present design. */
+      /* In the case of THEOREM, we save and reset the g_printString.  In the
+         case of != THEOREM (i.e. AXIOM and DEFINITION), g_printString will
+         be printed and cleared below. */
+      let(&g_printStringForReferencedBy, g_printString);
+      let(&g_printString, "");
+    }
+
+    /* Printing of the trailer in mmwtex.c will close out string later */
+    g_outputToString = 0;
+  } /* if (htmlFlag && texFlag) */
+  /* End of used-by list for html page */
+
+
+  /* After the block above, so referenced statements
+     show up first for convenience */
+  if (htmlFlag && texFlag) {
+    /*** Output the html proof for $p statements ***/
+    /* Note that we also output the axiom and definition usage
+       lists inside this function */
+    if (g_Statement[showStmt].type == (char)p_) {
+      typeProof(showStmt,
+          0 /*pipFlag*/,
+          0 /*startStep*/,
+          0 /*endStep*/,
+          0 /*endIndent*/,
+          1 /*essentialFlag*/,
+          1 /*renumberFlag*/,
+          0 /*unknownFlag*/,
+          0 /*notUnifiedFlag*/,
+          0 /*reverseFlag*/,
+          1 /*noIndentFlag*/,
+          0 /*splitColumn*/,
+          0 /*skipRepeatedSteps*/,
+          1 /*texFlag*/,  /* Means either latex or html */
+          1 /*htmlFlag*/);
+    } /* if (g_Statement[showStmt].type == (char)p_) */
+  } /* if (htmlFlag && texFlag) */
+  /* End of html proof for $p statements */
+
+  /* typeProof should have cleared this out */
+  if (g_printStringForReferencedBy[0]) bug(243);
+
+ returnPoint:
+  /* Deallocate strings */
+  free_nmbrString(nmbrDDList);
+  free_vstring(str1);
+  free_vstring(str2);
+  free_vstring(str3);
+  free_vstring(str4);
+  free_vstring(str5);
+  free_vstring(htmlDistinctVars);
+} /* typeStatement */
+
+
+
+/* Get the HTML string of dummy variables used by a proof for the
+   theorem's web page.  It should be called only if we're in
+   HTML output mode i.e.  SHOW STATEMENT .../HTML or /ALT_HTML */
+/* This is HARD-CODED FOR SET.MM and will not produce meaningful
+   output for other databases (so far none) with $d's */
+/* Caller must deallocate returned string */
+vstring htmlDummyVars(long showStmt)
+{
+  nmbrString *optDVA; /* Pointer only; not allocated directly */
+  nmbrString *optDVB; /* Pointer only; not allocated directly */
+  long numDVs;
+  nmbrString *optHyp; /* Pointer only; not allocated directly */
+  long numOptHyps;
+  vstring_def(str1);
+  long k, l, n, hypStmt;
+
+  /* Variables used while collecting a statement's dummy variables in $d's */
+  long dummyVarCount; /* # of (different) dummy vars found in $d statements */
+  vstring_def(dummyVarUsed); /* 'Y'/'N' indicators that we found that var */
+  vstring_def(htmlDummyVarList); /* Output HTML string */
+  long dummyVar; /* Current variable in a $d; test if it's a dummy variable */
+
+  /* This function should be called only for web page generation */
+  if (!g_htmlFlag) bug(261);
+
+  if (g_Statement[showStmt].type != p_) bug(262);
+  if (strcmp("|-", g_MathToken[
+            (g_Statement[showStmt].mathString)[0]].tokenName)) {
+    /* Don't process syntax statements */
+    goto RETURN_POINT;
+  }
+
+  optDVA = g_Statement[showStmt].optDisjVarsA;
+  optDVB = g_Statement[showStmt].optDisjVarsB;
+  numDVs = nmbrLen(optDVA);
+  optHyp = g_Statement[showStmt].optHypList;
+  numOptHyps = nmbrLen(optHyp);
+
+  if (numDVs == 0) {  /* Don't create a hint list if no $d's */
+    /*let(&htmlDummyVarList, "(no restrictions)");*/
+    goto RETURN_POINT;
+  }
+
+  dummyVarCount = 0;
+  if (numDVs != 0) {
+
+    /* Update g_WrkProof.proofString with current proof so we can
+       search it later to see if it uses the dummy variable */
+    parseProof(showStmt); /* Prints message if severe error */
+
+    /* Create an array of Y/N indicators that variable is occurs in a
+       $d statement as a dummy variable */
+    let(&dummyVarUsed, string(g_mathTokens, 'N'));
+    for (k = 0; k < numDVs; k++) {
+      for (l = 1; l <= 2; l++) {
+        if (l == 1) {
+          dummyVar = optDVA[k];
+        } else {
+          dummyVar = optDVB[k];
+        }
+        /* At this point, dummyVar is just a var in the $d; we
+           must still check that it is in the optHypList */
+        /* See if potential dummyVar is in optHypList */
+        if (dummyVarUsed[dummyVar] == 'N') {
+          for (n = 0; n < numOptHyps; n++) {
+            /* Check whether dummyVar matches the 2nd token of an
+               optional hypothesis list entry e.g. "x" in "set x" */
+            hypStmt = g_Statement[showStmt].optHypList[n];
+            if (g_Statement[hypStmt].mathString[1] == dummyVar) {
+              /* dummyVar is a dummy variable */
+
+              /* See if it is used by the proof */
+              /* g_WrkProof.proofString was updated by parseProof(showStmt)
+                 above */
+              if (nmbrElementIn(1, g_WrkProof.proofString, hypStmt) == 0) {
+                break; /* It's not used by the proof; stop hyp scan */
+              }
+
+              dummyVarUsed[dummyVar] = 'Y';
+              dummyVarCount++;
+              /* tokenToTex allocates str1; must deallocate it first */
+              free_vstring(str1);
+              /* Convert token to htmldef/althtmldef string */
+              str1 = tokenToTex(g_MathToken[dummyVar].tokenName,
+                  showStmt);
+              let(&htmlDummyVarList, cat(htmlDummyVarList, " ", str1, NULL));
+              break; /* Found a match, so stop further checking */
+            }
+          } /* next n, 0 to numOptHyps-1*/
+        } /* if dummy var not used (yet) */
+      } /* next l */
+    } /* next k */
+  } /* if (numDVs != 0) */
+
+  if (dummyVarCount > 0) {
+    let(&htmlDummyVarList, cat(
+        "<CENTER>",
+         "<A HREF=\"",
+
+         /* g_htmlHome is set by htmlhome in $t comment */
+         (instr(1, g_htmlHome, "mmset.html") > 0) ?
+             "mmset.html" :
+             /* The following link will work in the NF and other
+                "Proof Explorers" */
+             "../mpeuni/mmset.html",
+
+        "#dvnote1\">Dummy variable",
+        /* Determine whether singular or plural */
+        dummyVarCount > 1 ? "s" : "",
+        "</A> ",
+        /* Put a span around the variable list to localize
+           the use of the special math font for ALT_HTML */
+        (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
+        htmlDummyVarList,
+        (g_altHtmlFlag ? "</SPAN>" : ""),
+        dummyVarCount > 1 ? " are mutually distinct and" : " is",
+        " distinct from all other variables.",
+        "</CENTER>",
+        NULL));
+  } /* htmlDummyVars */
+
+
+ RETURN_POINT:
+  /* Deallocate strings */
+  free_vstring(dummyVarUsed);
+  free_vstring(str1);
+
+  return htmlDummyVarList;
+} /* htmlDummyVars */
+
+
+
+/* Get the HTML string of "allowed substitutions" list for an axiom
+   or theorem's web page.  It should be called only if we're in
+   HTML output mode i.e.  SHOW STATEMENT .../HTML or /ALT_HTML */
+/* This is HARD-CODED FOR SET.MM and will not produce meaningful
+   output for other databases (so far none) with $d's */
+/* Caller must deallocate returned string */
+vstring htmlAllowedSubst(long showStmt)
+{
+  nmbrString *reqHyp; /* Pointer only; not allocated directly */
+  long numReqHyps;
+  nmbrString *reqDVA; /* Pointer only; not allocated directly */
+  nmbrString *reqDVB; /* Pointer only; not allocated directly */
+  long numDVs;
+  nmbrString_def(setVar); /* set (individual) variables */
+  char *strptr;
+  vstring_def(str1);
+  long setVars;
+  long wffOrClassVar;
+  vstring_def(setVarDVFlag);
+  flag found, first;
+  long i, j, k;
+  vstring_def(htmlAllowedList);
+  long countInfo = 0;
+
+  reqDVA = g_Statement[showStmt].reqDisjVarsA;
+  reqDVB = g_Statement[showStmt].reqDisjVarsB;
+  numDVs = nmbrLen(reqDVA);
+
+  reqHyp = g_Statement[showStmt].reqHypList;
+  numReqHyps = nmbrLen(reqHyp);
+
+  /* This function should be called only for web page generation */
+  if (!g_htmlFlag) bug(250);
+
+  if (g_Statement[showStmt].mathStringLen < 1) bug(254);
+  if (strcmp("|-", g_MathToken[
+            (g_Statement[showStmt].mathString)[0]].tokenName)) {
+    /* Don't process syntax statements */
+    goto RETURN_POINT;
+  }
+
+  if (numDVs == 0) {  /* Don't create a hint list if no $d's */
+    goto RETURN_POINT;
+  }
+
+  /* Collect list of all set variables in the theorem */
+  /* First, count the number of set variables */
+  setVars = 0;
+  for (i = 0; i < numReqHyps; i++) {
+    /* Scan "setvar" variables */
+    if (g_Statement[reqHyp[i]].type == (char)e_) continue;
+    if (g_Statement[reqHyp[i]].type != (char)f_) bug(251);
+    if (g_Statement[reqHyp[i]].mathStringLen != 2)
+      bug(252); /* $f must have 2 tokens */
+    strptr = g_MathToken[
+              (g_Statement[reqHyp[i]].mathString)[0]].tokenName;
+    /* THE FOLLOWING IS SPECIFIC TO set.mm */
+    if (strcmp("setvar", strptr)) continue;
+                                  /* Not a set variable */
+    setVars++;
+  }
+  /* Next, create a list of them in setVar[] */
+  j = 0;
+  nmbrLet(&setVar, nmbrSpace(setVars));
+  for (i = 0; i < numReqHyps; i++) {
+    /* Scan "setvar" variables */
+    if (g_Statement[reqHyp[i]].type == (char)e_) continue;
+    strptr = g_MathToken[
+              (g_Statement[reqHyp[i]].mathString)[0]].tokenName;
+    if (strcmp("setvar", strptr)) continue;
+                                  /* Not a set variable */
+    setVar[j] = (g_Statement[reqHyp[i]].mathString)[1];
+    j++;
+  }
+  if (j != setVars) bug(253);
+
+  /* Scan "wff" and "class" variables for attached $d's */
+  for (i = 0; i < numReqHyps; i++) {
+    /* Look for a "wff" and "class" variable */
+    if (g_Statement[reqHyp[i]].type == (char)e_) continue;
+    strptr = g_MathToken[
+              (g_Statement[reqHyp[i]].mathString)[0]].tokenName;
+    if (strcmp("wff", strptr) && strcmp("class", strptr)) continue;
+                                  /* Not a wff or class variable */
+    wffOrClassVar = (g_Statement[reqHyp[i]].mathString)[1];
+    let(&setVarDVFlag, string(setVars, 'N')); /* No $d yet */
+    /* Scan for attached $d's */
+    for (j = 0; j < numDVs; j++) {
+      found = 0;
+      if (wffOrClassVar == reqDVA[j]) {
+        for (k = 0; k < setVars; k++) {
+          if (setVar[k] == reqDVB[j]) {
+            setVarDVFlag[k] = 'Y';
+            found = 1;
+            break;
+          }
+        }
+      }
+      if (found) continue;
+      /* Repeat with swapped $d arguments */
+      if (wffOrClassVar == reqDVB[j]) {
+        for (k = 0; k < setVars; k++) {
+          if (setVar[k] == reqDVA[j]) {
+            setVarDVFlag[k] = 'Y';
+            break;
+          }
+        }
+      }
+    } /* next $d */
+
+    /* Collect set vars that don't have $d's with this wff or class var */
+    /* First, if there aren't any, then omit this wff or class var */
+    found = 0;
+    for (j = 0; j < setVars; j++) {
+      if (setVarDVFlag[j] == 'N') {
+        found = 1;
+        break;
+      }
+    }
+    if (found == 0) continue; /* All set vars have $d with this wff or class */
+
+    free_vstring(str1);
+    str1 = tokenToTex(g_MathToken[wffOrClassVar].tokenName, showStmt);
+         /* tokenToTex allocates str1; we must deallocate it eventually */
+    countInfo++;
+    let(&htmlAllowedList, cat(htmlAllowedList, " &nbsp; ",
+        str1, "(", NULL));
+    first = 1;
+    for (j = 0; j < setVars; j++) {
+      if (setVarDVFlag[j] == 'N') {
+        free_vstring(str1);
+        str1 = tokenToTex(g_MathToken[setVar[j]].tokenName, showStmt);
+        let(&htmlAllowedList, cat(htmlAllowedList,
+            (first == 0) ? "," : "", str1, NULL));
+        if (first == 0) countInfo++;
+        first = 0;
+      }
+    }
+    let(&htmlAllowedList, cat(htmlAllowedList, ")", NULL));
+
+  } /* next i (wff or class var) */
+
+ RETURN_POINT:
+
+  if (htmlAllowedList[0] != 0) {
+    let(&htmlAllowedList, cat("<CENTER>",
+        "<A HREF=\"",
+
+        /* g_htmlHome is set by htmlhome in $t comment */
+        (instr(1, g_htmlHome, "mmset.html") > 0) ?
+            "mmset.html" :
+            /* The following link will work in the NF and other
+               "Proof Explorers" */
+            "../mpeuni/mmset.html",
+
+        "#allowedsubst\">Allowed substitution</A> hint",
+        ((countInfo != 1) ? "s" : ""), ": ",
+        (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
+        htmlAllowedList,
+        (g_altHtmlFlag ? "</SPAN>" : ""),
+        "</CENTER>", NULL));
+  }
+
+  /* Deallocate strings */
+  free_nmbrString(setVar);
+  free_vstring(str1);
+  free_vstring(setVarDVFlag);
+
+  return htmlAllowedList;
+} /* htmlAllowedSubst */
+
+
+
+/* Displays a proof (or part of a proof, depending on arguments). */
+/* Note that parseProof() and verifyProof() are assumed to have been called,
+   so that the g_WrkProof structure elements are assigned for the current
+   statement. */
+/* This is also used for the MIDI output, since we conveniently
+   have the necessary proof information here.  The function outputMidi()
+   is called from within. */
+void typeProof(long statemNum,
+  flag pipFlag, /* Means use g_ProofInProgress; statemNum must be proveStatement*/
+  long startStep, long endStep,
+  long endIndent,
+  flag essentialFlag, /* <- also used as definition/axiom flag for HTML
+      syntax breakdown when called from typeStatement() */
+  flag renumberFlag,
+  flag unknownFlag,
+  flag notUnifiedFlag,
+  flag reverseFlag,
+  flag noIndentFlag, /* Means Lemmon-style proof */
+  long splitColumn, /* START_COLUMN */
+  flag skipRepeatedSteps, /* NO_REPEATED_STEPS */
+  flag texFlag,
+  flag htmlFlag
+  /* flag g_midiFlag - global to avoid changing many calls to typeProof() */
+  )
+{
+  /* From HELP SHOW PROOF: Optional qualifiers:
+    / ESSENTIAL - the proof tree is trimmed of all $f hypotheses before
+        being displayed.
+    / FROM_STEP <step> - the display starts at the specified step.  If
+        this qualifier is omitted, the display starts at the first step.
+    / TO_STEP <step> - the display ends at the specified step.  If this
+        qualifier is omitted, the display ends at the last step.
+    / TREE_DEPTH <number> - Only steps at less than the specified proof
+        tree depth are displayed.  Useful for obtaining an overview of
+        the proof.
+    / REVERSE - the steps are displayed in reverse order.
+    / RENUMBER - when used with / ESSENTIAL, the steps are renumbered
+        to correspond only to the essential steps.
+    / TEX - the proof is converted to LaTeX and stored in the file opened
+        with OPEN TEX.
+    / HTML - the proof is converted to HTML and stored in the file opened
+        with OPEN HTML.
+    / LEMMON - The proof is displayed in a non-indented format known
+        as Lemmon style, with explicit previous step number references.
+        If this qualifier is omitted, steps are indented in a tree format.
+    / START_COLUMN <number> - Overrides the default column at which
+        the formula display starts in a Lemmon style display.  May be
+        used only in conjunction with / LEMMON.
+    / NO_REPEATED_STEPS - When a proof step is identical to an earlier
+        step, it will not be repeated.  Instead, a reference to it will be
+        changed to a reference to the earlier step.  In particular,
+        SHOW PROOF <label> / LEMMON / RENUMBER / NO_REPEATED_STEPS
+        will have the same proof step numbering as the web page proof
+        generated by SHOW STATEMENT  <label> / HTML, rather than
+        the proof step numbering of the indented format
+        SHOW PROOF <label> / RENUMBER.  This qualifier affects only
+        displays also using the / LEMMON qualifier.
+    / NORMAL - The proof is displayed in normal format suitable for
+        inclusion in a source file.  May not be used with any other
+        qualifier.
+    / COMPRESSED - The proof is displayed in compressed format
+        suitable for inclusion in a source file.  May not be used with
+        any other qualifier.
+    / STATEMENT_SUMMARY - Summarizes all statements (like a brief SHOW
+        STATEMENT) used by the proof.  May not be used with any other
+        qualifier except / ESSENTIAL.
+    / DETAILED_STEP <step> - Shows the details of what is happening at
+        a specific proof step.  May not be used with any other qualifier.
+    / MIDI - puts out a midi sound file instead of a proof
+        - determined by the global variable g_midiFlag, not by a parameter to
+        typeProof()
+  */
+  long i, j, plen, step, stmt, lens, lent, maxStepNum;
+  vstring_def(tmpStr);
+  vstring_def(tmpStr1);
+  vstring_def(locLabDecl);
+  vstring_def(tgtLabel);
+  vstring_def(srcLabel);
+  vstring_def(startPrefix);
+  vstring_def(tgtPrefix);
+  vstring_def(srcPrefix);
+  vstring_def(userPrefix);
+  vstring_def(contPrefix);
+  vstring_def(statementUsedFlags);
+  vstring_def(startStringWithNum);
+  vstring_def(startStringWithoutNum);
+  nmbrString_def(proof);
+  nmbrString_def(localLabels);
+  nmbrString_def(localLabelNames);
+  nmbrString_def(indentationLevel);
+  nmbrString_def(targetHyps);
+  nmbrString_def(essentialFlags);
+  nmbrString_def(stepRenumber);
+  nmbrString_def(notUnifiedFlags);
+  nmbrString_def(unprovedList); /* For traceProofWork() */
+  nmbrString_def(relativeStepNums); /* For unknownFlag */
+  long maxLabelLen = 0;
+  long maxStepNumLen = 1;
+  long maxStepNumOffsetLen = 0;
+  char type;
+  flag stepPrintFlag;
+  long fromStep, toStep, byStep;
+  vstring_def(hypStr);
+  nmbrString *hypPtr;
+  long hyp, hypStep;
+
+  /* For statement syntax breakdown (see "syntax hints" section below),
+    we declare the following 3 variables. */
+  static long wffToken = -1; /* array index of the hard-coded token "wff" -
+      static so we only have to look it up once - set to -2 if not found */
+  nmbrString *nmbrTmpPtr1; /* Pointer only; not allocated directly */
+  nmbrString *nmbrTmpPtr2; /* Pointer only; not allocated directly */
+
+  if (htmlFlag && texFlag) skipRepeatedSteps = 1; /* Keep old behavior */
+  /* Comment out the following line if you want to revert to the old
+     Lemmon-style behavior with local label references for reused compressed
+     proof steps is desired.  The only reason for doing this is to obtain
+     the same steps and step numbers as the indented proof, rather than
+     those on the HTML pages. */
+  /* if (noIndentFlag) skipRepeatedSteps = 1; */
+
+  if (htmlFlag && texFlag) {
+
+
+
+
+    g_outputToString = 1; /* Flag for print2 to add to g_printString */
+    if (essentialFlag) {
+
+      /* See if there are dummy variables.  If so, print them below
+         "Proof of Theorem", which means we have to make the "Proof of
+         Theorem" line separate and not the table caption, so that the
+         "Distinct variables..." line does not become part of the table. */
+      free_vstring(tmpStr);
+      tmpStr = htmlDummyVars(statemNum);
+      if (tmpStr[0] != 0) {
+        print2("<CENTER><B>Proof of Theorem <FONT\n");
+        printLongLine(cat("   COLOR=", GREEN_TITLE_COLOR, ">",
+            asciiToTt(g_Statement[statemNum].labelName),
+            "</FONT></B></CENTER>", NULL), "", "\"");
+        /* Print the list of dummy variables */
+        printLongLine(tmpStr, "", "\"");
+        free_vstring(tmpStr);
+        print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
+            MINT_BACKGROUND_COLOR);
+        print2("SUMMARY=\"Proof of theorem\">\n");
+      } else {
+
+        /* For bobby.cast.org approval */
+        print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
+            MINT_BACKGROUND_COLOR);
+        print2("SUMMARY=\"Proof of theorem\">\n");
+        print2("<CAPTION><B>Proof of Theorem <FONT\n");
+        printLongLine(cat("   COLOR=", GREEN_TITLE_COLOR, ">",
+            asciiToTt(g_Statement[statemNum].labelName),
+            "</FONT></B></CAPTION>", NULL), "", "\"");
+      }
+    } else {
+      /* This is a syntax breakdown "proof" of a definition called
+         from typeStatement */
+      print2("<CENTER><TABLE BORDER CELLSPACING=0 BGCOLOR=%s\n",
+          MINT_BACKGROUND_COLOR);
+      if (!strcmp("ax-", left(g_Statement[g_showStatement].labelName, 3))) {
+        /* For bobby.cast.org approval */
+        print2("SUMMARY=\"Detailed syntax breakdown of axiom\">\n");
+        print2("<CAPTION><B>Detailed syntax breakdown of Axiom <FONT\n");
+      } else {
+        /* For bobby.cast.org approval */
+        print2("SUMMARY=\"Detailed syntax breakdown of definition\">\n");
+        print2("<CAPTION><B>Detailed syntax breakdown of Definition <FONT\n");
+      }
+      printLongLine(cat("   COLOR=", GREEN_TITLE_COLOR, ">",
+          asciiToTt(g_Statement[statemNum].labelName),
+          "</FONT></B></CAPTION>", NULL), "", "\"");
+    }
+    print2(
+        "<TR><TH>Step</TH><TH>Hyp</TH><TH>Ref\n");
+    print2("</TH><TH>Expression</TH></TR>\n");
+    g_outputToString = 0;
+  }
+
+  if (!pipFlag) {
+    parseProof(g_showStatement);
+    if (g_WrkProof.errorSeverity > 1) {
+      if (htmlFlag && texFlag) {
+        /* Print warning and close out proof table */
+        g_outputToString = 1;
+        print2(
+      "<TD COLSPAN=4><B><FONT COLOR=RED>WARNING: Proof has a severe error.\n");
+        print2("</FONT></B></TD></TR>\n");
+        g_outputToString = 0;
+        /* Clear out g_printStringForReferencedBy to prevent bug 243 above */
+        let(&g_printStringForReferencedBy, "");
+      }
+      return; /* verifyProof() could crash */
+    }
+    verifyProof(g_showStatement);
+  }
+
+  if (!pipFlag) {
+    nmbrLet(&proof, g_WrkProof.proofString); /* The proof */
+    if (g_midiFlag) {
+      /* Get the uncompressed version of the proof */
+      nmbrLet(&proof, nmbrUnsquishProof(proof));
+    }
+  } else {
+    nmbrLet(&proof, g_ProofInProgress.proof); /* The proof */
+  }
+  plen = nmbrLen(proof);
+
+  /* To reduce the number of steps displayed in an html proof,
+     we will use a local label to reference the 2nd or later reference to a
+     hypothesis, so the hypothesis won't have to be shown multiple times
+     in the proof. */
+  if (htmlFlag && texFlag && !noIndentFlag /* Lemmon */) {
+    /* Only Lemmon-style proofs are implemented for html */
+    bug(218);
+  }
+  if (skipRepeatedSteps) {
+    for (step = 0; step < plen; step++) {
+      stmt = proof[step];
+      if (stmt < 0) continue;  /* Unknown or label ref */
+      type = g_Statement[stmt].type;
+      if (type == f_ || type == e_  /* It's a hypothesis */
+          || g_Statement[stmt].numReqHyp == 0) { /* A statement w/ no hyp */
+        for (i = 0; i < step; i++) {
+          if (stmt == proof[i]) {
+            /* The hypothesis at 'step' matches an earlier hypothesis at i,
+               so we will back-reference 'step' to i with a local label */
+            proof[step] = -1000 - i;
+            break;
+          }
+        } /* next i */
+      }
+    } /* next step */
+  }
+
+
+  /* Collect local labels */
+  for (step = 0; step < plen; step++) {
+    stmt = proof[step];
+    if (stmt <= -1000) {
+      stmt = -1000 - stmt;
+      if (!nmbrElementIn(1, localLabels, stmt)) {
+        nmbrLet(&localLabels, nmbrAddElement(localLabels, stmt));
+      }
+    }
+  }
+
+  /* localLabelNames[] hold an integer which, when converted to string,
+    is the local label name. */
+  nmbrLet(&localLabelNames, nmbrSpace(plen));
+
+  /* Get the indentation level */
+  nmbrLet(&indentationLevel, nmbrGetIndentation(proof, 0));
+
+  /* Get the target hypotheses */
+  nmbrLet(&targetHyps, nmbrGetTargetHyp(proof, statemNum));
+
+  /* Get the essential step flags, if required */
+  if (essentialFlag || g_midiFlag) {
+    nmbrLet(&essentialFlags, nmbrGetEssential(proof));
+  } else {
+    nmbrLet(&essentialFlags, NULL_NMBRSTRING);
+  }
+
+  /* We now have enough information for the MIDI output, so do it */
+  if (g_midiFlag) {
+    outputMidi(plen, indentationLevel,
+        essentialFlags, g_midiParam, g_Statement[statemNum].labelName);
+    goto typeProof_return;
+  }
+
+  /* Get the step renumbering */
+  nmbrLet(&stepRenumber, nmbrSpace(plen)); /* This initializes all step
+      renumbering to step 0.  Later, we will use (for html) the fact that
+      a step renumbered to 0 is a step to be skipped. */
+  i = 0;
+  maxStepNum = 0;
+  for (step = 0; step < plen; step++) {
+    stepPrintFlag = 1; /* Note: stepPrintFlag is reused below with a
+        slightly different meaning (i.e. it will be printed after
+        a filter such as notUnified is applied) */
+    if (renumberFlag && essentialFlag) {
+      if (!essentialFlags[step]) stepPrintFlag = 0;
+    }
+    if (skipRepeatedSteps && proof[step] < 0) stepPrintFlag = 0;
+    /* For standard numbering, stepPrintFlag will be always be 1 here */
+    if (stepPrintFlag) {
+      i++;
+      stepRenumber[step] = i; /* Numbering for step to be printed */
+      maxStepNum = i; /* To compute maxStepNumLen below */
+    }
+  }
+
+  /* Get the relative offset (0, -1, -2,...) for unknown steps */
+  if (unknownFlag) {
+    /* There could be unknown steps outside of MM-PA
+       So remove this bug check, which seems spurious.  I can't see that
+       getRelStepNums() cares whether we are in MM-PA. */
+    /* if (!pipFlag) bug(255); */
+    relativeStepNums = getRelStepNums(g_ProofInProgress.proof);
+  }
+
+  /* Get steps not unified (pipFlag only) */
+  if (notUnifiedFlag) {
+    if (!pipFlag) bug(205);
+    nmbrLet(&notUnifiedFlags, nmbrSpace(plen));
+    for (step = 0; step < plen; step++) {
+      notUnifiedFlags[step] = 0;
+      if (nmbrLen(g_ProofInProgress.source[step])) {
+        if (!nmbrEq(g_ProofInProgress.target[step],
+            g_ProofInProgress.source[step])) notUnifiedFlags[step] = 1;
+      }
+      if (nmbrLen(g_ProofInProgress.user[step])) {
+        if (!nmbrEq(g_ProofInProgress.target[step],
+            g_ProofInProgress.user[step])) notUnifiedFlags[step] = 1;
+      }
+    }
+  }
+
+  /* Get the printed character length of the largest step number */
+  i = maxStepNum;
+  while (i >= 10) {
+    i = i/10; /* The number is printed in base 10 */
+    maxStepNumLen++;
+  }
+  /* Add extra space for negative offset numbers e.g. "3:-1" */
+  if (unknownFlag) {
+    maxStepNumOffsetLen = 3; /* :, -, # */
+    j = 0;
+    for (i = 0; i < plen; i++) {
+      j = relativeStepNums[i];
+      if (j <= 0) break; /* Found first unknown step (largest offset) */
+    }
+    while (j <= -10) {
+      j = j/10; /* The number is printed in base 10 */
+      maxStepNumOffsetLen++;
+    }
+  }
+
+
+
+  /* Get local labels and maximum label length */
+  /* lent = target length, lens = source length */
+  for (step = 0; step < plen; step++) {
+    lent = (long)strlen(g_Statement[targetHyps[step]].labelName);
+    stmt = proof[step];
+    if (stmt < 0) {
+      if (stmt <= -1000) {
+        stmt = -1000 - stmt;
+        /* stmt is now the step number a local label refers to */
+        lens = (long)strlen(str((double)(localLabelNames[stmt])));
+        freeTempAlloc(); /* Clear temp alloc stack for str function */
+      } else {
+        if (stmt != -(long)'?') bug (219); /* the only other possibility */
+        lens = 1; /* '?' (unknown step) */
+      }
+    } else {
+      if (nmbrElementIn(1, localLabels, step)) {
+
+        /* The philosophy is to number all local labels with the
+           actual step number referenced, for better readability.  This means
+           that if a *.mm label is a pure number, there may be ambiguity in
+           the proof display, but this is felt to be too rare to be a serious
+           drawback. */
+        localLabelNames[step] = stepRenumber[step];
+
+      }
+      lens = (long)strlen(g_Statement[stmt].labelName);
+    }
+    /* Find longest label assignment, excluding local label declaration */
+    if (maxLabelLen < lent + 1 + lens) {
+      maxLabelLen = lent + 1 + lens; /* Target, =, source */
+    }
+  } /* Next step */
+
+  /* Print the steps */
+  if (reverseFlag && !g_midiFlag) {
+    fromStep = plen - 1;
+    toStep = -1;
+    byStep = -1;
+  } else {
+    fromStep = 0;
+    toStep = plen;
+    byStep = 1;
+  }
+  for (step = fromStep; step != toStep; step = step + byStep) {
+
+    /* Filters to decide whether to print the step */
+    stepPrintFlag = 1;
+    if (startStep > 0) { /* The user's FROM_STEP */
+      if (step + 1 < startStep) stepPrintFlag = 0;
+    }
+    if (endStep > 0) { /* The user's TO_STEP */
+      if (step + 1 > endStep) stepPrintFlag = 0;
+    }
+    if (endIndent > 0) { /* The user's INDENTATION_DEPTH */
+      if (indentationLevel[step] + 1 > endIndent) stepPrintFlag = 0;
+    }
+    if (essentialFlag) {
+      if (!essentialFlags[step]) stepPrintFlag = 0;
+    }
+    if (notUnifiedFlag) {
+      if (!notUnifiedFlags[step]) stepPrintFlag = 0;
+    }
+    if (unknownFlag) {
+      if (proof[step] != -(long)'?') stepPrintFlag = 0;
+    }
+
+    /* Skip steps that are local label references for html */
+    if (skipRepeatedSteps) {
+      if (stepRenumber[step] == 0) stepPrintFlag = 0;
+    }
+
+    /* For MIDI files, ignore all qualifiers and process all steps */
+    if (g_midiFlag) stepPrintFlag = 1;
+
+    if (!stepPrintFlag) continue;
+
+    if (noIndentFlag) {
+      let(&tgtLabel, "");
+    } else {
+      let(&tgtLabel, g_Statement[targetHyps[step]].labelName);
+    }
+    let(&locLabDecl, ""); /* Local label declaration */
+    stmt = proof[step];
+    if (stmt < 0) {
+      if (stmt <= -1000) {
+        stmt = -1000 - stmt;
+        if (skipRepeatedSteps) bug(220); /* If html, a step referencing a
+            local label will never be printed since it will be skipped above */
+        /* stmt is now the step number a local label refers to */
+        if (noIndentFlag) {
+          let(&srcLabel, cat("@", str((double)(localLabelNames[stmt])), NULL));
+        } else {
+          let(&srcLabel, cat("=", str((double)(localLabelNames[stmt])), NULL));
+        }
+        type = g_Statement[proof[stmt]].type;
+      } else {
+        if (stmt != -(long)'?') bug(206);
+        if (noIndentFlag) {
+          let(&srcLabel, chr(-stmt)); /* '?' */
+        } else {
+          let(&srcLabel, cat("=", chr(-stmt), NULL)); /* '?' */
+        }
+        type = '?';
+      }
+    } else {
+      if (nmbrElementIn(1, localLabels, step)) {
+        /* This statement declares a local label */
+        if (noIndentFlag) {
+          if (!(skipRepeatedSteps)) { /* No local label declaration is
+              shown for html */
+            let(&locLabDecl, cat("@", str((double)(localLabelNames[step])), ":", NULL));
+          }
+        } else {
+          let(&locLabDecl, cat(str((double)(localLabelNames[step])), ":", NULL));
+        }
+      }
+
+      if (noIndentFlag) {
+        let(&srcLabel, g_Statement[stmt].labelName);
+
+        /* For non-indented mode, add step numbers of hypotheses after label */
+        let(&hypStr, "");
+        hypStep = step - 1;
+        hypPtr = g_Statement[stmt].reqHypList;
+        for (hyp = g_Statement[stmt].numReqHyp - 1; hyp >=0; hyp--) {
+          if (!essentialFlag || g_Statement[hypPtr[hyp]].type == (char)e_) {
+            i = stepRenumber[hypStep];
+            if (i == 0) {
+              if (!(skipRepeatedSteps)) bug(221);
+              if (proof[hypStep] != -(long)'?') {
+                if (proof[hypStep] > -1000) bug(222);
+                if (localLabelNames[-1000 - proof[hypStep]] == 0) bug(223);
+                if (localLabelNames[-1000 - proof[hypStep]] !=
+                    stepRenumber[-1000 - proof[hypStep]]) bug(224);
+                /* Get the step number the hypothesis refers to */
+                i = stepRenumber[-1000 - proof[hypStep]];
+              } else {
+                /* The hypothesis refers to an unknown step - use i as flag */
+                i = -(long)'?';
+              }
+            }
+            if (!hypStr[0]) {
+              if (i != -(long)'?') {
+                let(&hypStr, str((double)i));
+              } else {
+                let(&hypStr, "?");
+              }
+            } else {
+              /* Put comma between more than one hypothesis reference */
+              if (i != -(long)'?') {
+                let(&hypStr, cat(str((double)i), ",", hypStr, NULL));
+              } else {
+                let(&hypStr, cat("?", ",", hypStr, NULL));
+              }
+            }
+          }
+          if (hyp < g_Statement[stmt].numReqHyp) {
+            /* Move down to previous hypothesis */
+            hypStep = hypStep - subproofLen(proof, hypStep);
+          }
+        } /* Next hyp */
+
+        if (hypStr[0]) {
+          /* Add hypothesis list after label */
+          let(&srcLabel, cat(hypStr, " ", srcLabel, NULL));
+        }
+
+      } else {
+        let(&srcLabel, cat("=", g_Statement[stmt].labelName, NULL));
+      }
+      type = g_Statement[stmt].type;
+    }
+
+
+#define PF_INDENT_INC 2
+    /* Print the proof line */
+    if (stepPrintFlag) {
+
+      if (noIndentFlag) {
+        let(&startPrefix, cat(
+            space(maxStepNumLen - (long)strlen(str((double)(stepRenumber[step])))),
+            str((double)(stepRenumber[step])),
+            " ",
+            srcLabel,
+            space(splitColumn - (long)strlen(srcLabel) - (long)strlen(locLabDecl) - 1
+                - maxStepNumLen - 1),
+            " ", locLabDecl,
+            NULL));
+        if (pipFlag) {
+          let(&tgtPrefix, startPrefix);
+          let(&srcPrefix, cat(
+              space(maxStepNumLen - (long)strlen(str((double)(stepRenumber[step])))),
+              space((long)strlen(str((double)(stepRenumber[step])))),
+              " ",
+              space(splitColumn - 1
+                  - maxStepNumLen),
+              NULL));
+          let(&userPrefix, cat(
+              space(maxStepNumLen - (long)strlen(str((double)(stepRenumber[step])))),
+              space((long)strlen(str((double)(stepRenumber[step])))),
+              " ",
+              "(User)",
+              space(splitColumn - (long)strlen("(User)") - 1
+                  - maxStepNumLen),
+              NULL));
+        }
+        let(&contPrefix, space((long)strlen(startPrefix) + 4));
+      } else {  /* not noIndentFlag */
+
+        /* Compute prefix with and without step number.  For 'show new_proof
+           /unknown', unknownFlag is set, and we add the negative offset. */
+        let(&tmpStr, "");
+        if (unknownFlag) {
+          if (relativeStepNums[step] < 0) {
+            let(&tmpStr, cat(" ", str((double)(relativeStepNums[step])), NULL));
+          }
+          let(&tmpStr, cat(tmpStr, space(maxStepNumOffsetLen
+              - (long)(strlen(tmpStr))), NULL));
+        }
+
+        let(&startStringWithNum, cat(
+            space(maxStepNumLen - (long)strlen(str((double)(stepRenumber[step])))),
+            str((double)(stepRenumber[step])),
+            tmpStr,
+            " ", NULL));
+        let(&startStringWithoutNum, space(maxStepNumLen + 1));
+
+
+        let(&startPrefix, cat(
+            startStringWithNum,
+            space(indentationLevel[step] * PF_INDENT_INC
+                - (long)strlen(locLabDecl)),
+            locLabDecl,
+            tgtLabel,
+            srcLabel,
+            space(maxLabelLen - (long)strlen(tgtLabel)
+                - (long)strlen(srcLabel)),
+            NULL));
+        if (pipFlag) {
+          let(&tgtPrefix, cat(
+              startStringWithNum,
+              space(indentationLevel[step] * PF_INDENT_INC - (long)strlen(locLabDecl)),
+              locLabDecl,
+              tgtLabel,
+              space((long)strlen(srcLabel)),
+              space(maxLabelLen - (long)strlen(tgtLabel) - (long)strlen(srcLabel)),
+              NULL));
+          let(&srcPrefix, cat(
+              startStringWithoutNum,
+              space(indentationLevel[step] * PF_INDENT_INC - (long)strlen(locLabDecl)),
+              space((long)strlen(locLabDecl)),
+              space((long)strlen(tgtLabel)),
+              srcLabel,
+              space(maxLabelLen - (long)strlen(tgtLabel) - (long)strlen(srcLabel)),
+              NULL));
+          let(&userPrefix, cat(
+              startStringWithoutNum,
+              space(indentationLevel[step] * PF_INDENT_INC - (long)strlen(locLabDecl)),
+              space((long)strlen(locLabDecl)),
+              space((long)strlen(tgtLabel)),
+              "=(User)",
+              space(maxLabelLen - (long)strlen(tgtLabel) - (long)strlen("=(User)")),
+              NULL));
+        }
+        let(&contPrefix, ""); /* Continuation lines use whole screen width */
+      }
+
+      if (!pipFlag) {
+
+        if (!texFlag) {
+          if (!g_midiFlag) {
+            printLongLine(cat(startPrefix," $", chr(type), " ",
+                nmbrCvtMToVString(g_WrkProof.mathStringPtrs[step]),
+                NULL),
+                contPrefix,
+                chr(1));
+                /* chr(1) is right-justify flag for printLongLine */
+          }
+        } else {  /* TeX or HTML */
+          printTexLongMath(g_WrkProof.mathStringPtrs[step],
+              cat(startPrefix, " $", chr(type), " ", NULL),
+              contPrefix, stmt, indentationLevel[step]);
+        }
+
+      } else { /* pipFlag */
+        if (texFlag) {
+          /* It doesn't make sense to do this and it hasn't been tested anyway */
+          print2("?Unsupported:  HTML or LaTeX proof for NEW_PROOF.\n");
+          bug(244);
+        }
+
+        if (!nmbrEq(g_ProofInProgress.target[step], g_ProofInProgress.source[step])
+            && nmbrLen(g_ProofInProgress.source[step])) {
+
+          if (!texFlag) {
+            printLongLine(cat(tgtPrefix, " $", chr(type), " ",
+                nmbrCvtMToVString(g_ProofInProgress.target[step]),
+                NULL),
+                contPrefix,
+                chr(1)); /* chr(1) is right-justify flag for printLongLine */
+            printLongLine(cat(srcPrefix,"  = ",
+                nmbrCvtMToVString(g_ProofInProgress.source[step]),
+                NULL),
+                contPrefix,
+                chr(1)); /* chr(1) is right-justify flag for printLongLine */
+          } else { /* TeX or HTML */
+            printTexLongMath(g_ProofInProgress.target[step],
+                cat(tgtPrefix, " $", chr(type), " ", NULL),
+                contPrefix, 0, 0);
+            printTexLongMath(g_ProofInProgress.source[step],
+                cat(srcPrefix, "  = ", NULL),
+                contPrefix, 0, 0);
+          }
+        } else {
+          if (!texFlag) {
+            printLongLine(cat(startPrefix, " $", chr(type), " ",
+                nmbrCvtMToVString(g_ProofInProgress.target[step]),
+                NULL),
+                contPrefix,
+                chr(1)); /* chr(1) is right-justify flag for printLongLine */
+          } else {  /* TeX or HTML */
+            printTexLongMath(g_ProofInProgress.target[step],
+                cat(startPrefix, " $", chr(type), " ", NULL),
+                contPrefix, 0, 0);
+          }
+
+        }
+        if (nmbrLen(g_ProofInProgress.user[step])) {
+
+          if (!texFlag) {
+            printLongLine(cat(userPrefix, "  = ",
+                nmbrCvtMToVString(g_ProofInProgress.user[step]),
+                NULL),
+                contPrefix,
+                chr(1)); /* chr(1) is right-justify flag for printLongLine */
+          } else {
+            printTexLongMath(g_ProofInProgress.user[step],
+                cat(userPrefix, "  = ", NULL),
+                contPrefix, 0, 0);
+          }
+
+        }
+      }
+    }
+
+
+  } /* Next step */
+
+  if (!pipFlag) {
+    cleanWrkProof(); /* Deallocate verifyProof storage */
+  }
+
+  if (htmlFlag && texFlag) {
+    g_outputToString = 1;
+    print2("</TABLE></CENTER>\n");
+
+    printLongLine(cat(
+        "<CENTER><TABLE CELLSPACING=5><TR><TD ALIGN=LEFT><FONT SIZE=-1>",
+        "<B>Colors of variables:</B> ",
+        g_htmlVarColor, "</FONT></TD></TR>",
+        NULL), "", "\"");
+
+    if (essentialFlag) {  /* Means this is not a syntax breakdown of a
+        definition which is called from typeStatement() */
+
+      /* Create list of syntax statements used */
+      let(&statementUsedFlags, string(g_statements + 1, 'N')); /* Init. to 'no' */
+      for (step = 0; step < plen; step++) {
+        stmt = proof[step];
+        /* Convention: collect all $a's that don't begin with "|-" */
+        if (stmt > 0) {
+          if (g_Statement[stmt].type == a_) {
+            if (strcmp("|-", g_MathToken[
+                (g_Statement[stmt].mathString)[0]].tokenName)) {
+              statementUsedFlags[stmt] = 'Y'; /* Flag to use it */
+            }
+          }
+        }
+      }
+
+      /******************************************************************/
+      /* Start of syntax hints section - for a more complete syntax hints
+         list in the HTML pages, parse the wffs comprising the hypotheses
+         and the statement, and add their syntax to the hints list. */
+
+      /* Look up the token "wff" (hard-coded) if we haven't found it before */
+      if (wffToken == -1) { /* First time */
+        wffToken = -2; /* In case it's not found because the user's source
+            used a convention different for "wff" for wffs */
+        for (i = 0; i < g_mathTokens; i++) {
+          if (!strcmp("wff", g_MathToken[i].tokenName)) {
+            wffToken = i;
+            break;
+          }
+        }
+      }
+
+      if (wffToken >= 0) {
+
+        /* Scan the statement being proved and its essential hypotheses,
+           and find a proof for each of them expressed as a wff */
+        for (i = -1; i < g_Statement[statemNum].numReqHyp; i++) {
+          /* i = -1 is the statement itself; i >= 0 is hypotheses i */
+          if (i == -1) {
+            /* If it's not a $p we shouldn't be here */
+            if (g_Statement[statemNum].type != (char)p_) bug(245);
+            nmbrTmpPtr1 = NULL_NMBRSTRING;
+            nmbrLet(&nmbrTmpPtr1, g_Statement[statemNum].mathString);
+          } else {
+            /* Ignore $f */
+            if (g_Statement[g_Statement[statemNum].reqHypList[i]].type
+                == (char)f_) continue;
+            /* Must therefore be a $e */
+            if (g_Statement[g_Statement[statemNum].reqHypList[i]].type
+                != (char)e_) bug(234);
+            nmbrTmpPtr1 = NULL_NMBRSTRING;
+            nmbrLet(&nmbrTmpPtr1,
+                g_Statement[g_Statement[statemNum].reqHypList[i]].mathString);
+          }
+          if (strcmp("|-", g_MathToken[nmbrTmpPtr1[0]].tokenName)) {
+            /* Since non-standard logics may not have this,
+               just break out of this section gracefully */
+            nmbrTmpPtr2 = NULL_NMBRSTRING; /* To be known after break */
+            break;
+          }
+          /* Turn "|-" assertion into a "wff" assertion */
+          nmbrTmpPtr1[0] = wffToken;
+
+          /* Find proof of formula or simple theorem (no new vars in $e's) */
+          /* maxEDepth is the maximum depth at which statements with $e
+             hypotheses are
+             considered.  A value of 0 means none are considered. */
+          nmbrTmpPtr2 = proveFloating(nmbrTmpPtr1 /*mString*/,
+              statemNum /*statemNum*/, 0 /*maxEDepth*/,
+              0, /* step; 0 = step 1 */ /*For messages*/
+              0,  /*not noDistinct*/
+              2, /* override discouraged-usage statements silently */
+              1 /* Always allow other mathboxes */
+              );
+          if (!nmbrLen(nmbrTmpPtr2)) {
+            /* Didn't find syntax proof */
+            /* Since a proof may not be found for non-standard
+               logics, just break out of this section gracefully */
+            break;
+          }
+
+          /* Add to list of syntax statements used */
+          for (step = 0; step < nmbrLen(nmbrTmpPtr2); step++) {
+            stmt = nmbrTmpPtr2[step];
+            /* Convention: collect all $a's that don't begin with "|-" */
+            if (stmt > 0) {
+              if (statementUsedFlags[stmt] == 'N') { /* For slight speedup */
+                if (g_Statement[stmt].type == a_) {
+                  if (strcmp("|-", g_MathToken[
+                      (g_Statement[stmt].mathString)[0]].tokenName)) {
+                    statementUsedFlags[stmt] = 'Y'; /* Flag to use it */
+                  } else {
+                    /* In a syntax proof there should be no |- */
+                    /* (In the future, we may want to break instead of
+                       calling it a bug, if it's a problem for non-standard
+                       logics.) */
+                    bug(237);
+                  }
+                }
+              }
+            } else {
+              /* proveFloating never returns a compressed proof */
+              bug(238);
+            }
+          }
+
+          /* Deallocate memory */
+          free_nmbrString(nmbrTmpPtr2);
+          free_nmbrString(nmbrTmpPtr1);
+        } /* next i */
+        /* Deallocate memory in case we broke out above */
+        free_nmbrString(nmbrTmpPtr2);
+        free_nmbrString(nmbrTmpPtr1);
+      } /* if (wffToken >= 0) */
+      /* End of syntax hints section */
+      /******************************************************************/
+
+      let(&tmpStr, "");
+      for (stmt = 1; stmt <= g_statements; stmt++) {
+        if (statementUsedFlags[stmt] == 'Y') {
+          if (!tmpStr[0]) {
+            let(&tmpStr,
+               "<TR><TD ALIGN=LEFT><FONT SIZE=-1><B>Syntax hints:</B> ");
+          }
+
+          /* Get the main symbol in the syntax */
+          /* This section can be deleted if not wanted - it is custom
+             for set.mm and might not work with other .mm's */
+          free_vstring(tmpStr1);
+          for (i = 1 /* Skip |- */; i < g_Statement[stmt].mathStringLen; i++) {
+            if (g_MathToken[(g_Statement[stmt].mathString)[i]].tokenType ==
+                (char)con_) {
+              /* Skip parentheses, commas, etc. */
+              if (strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
+                      ].tokenName, "(")
+                  && strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
+                      ].tokenName, ",")
+                  && strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
+                      ].tokenName, ")")
+                  && strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
+                      ].tokenName, ":")
+                  /* Use |-> rather than e. for cmpt, cmpt2 */
+                  && !(!strcmp(g_MathToken[(g_Statement[stmt].mathString)[i]
+                      ].tokenName, "e.")
+                      && (!strcmp(g_Statement[stmt].labelName, "cmpt")
+                          || !strcmp(g_Statement[stmt].labelName, "cmpt2")))
+                  ) {
+                tmpStr1 =
+                    tokenToTex(g_MathToken[(g_Statement[stmt].mathString)[i]
+                    ].tokenName, stmt);
+                let(&tmpStr1, cat(
+                    (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
+                    tmpStr1,
+                    (g_altHtmlFlag ? "</SPAN>" : ""),
+                    NULL));
+                break;
+              }
+            }
+          } /* Next i */
+          /* Special cases hard-coded for set.mm */
+          if (!strcmp(g_Statement[stmt].labelName, "wbr")) /* binary relation */
+            let(&tmpStr1, "<i> class class class </i>");
+          if (!strcmp(g_Statement[stmt].labelName, "cv"))
+            let(&tmpStr1, "[set variable]");
+          /* Let's don't do cv - confusing to reader */
+          if (!strcmp(g_Statement[stmt].labelName, "cv"))
+            continue;
+          if (!strcmp(g_Statement[stmt].labelName, "co")) /* operation */
+            let(&tmpStr1, "(<i>class class class</i>)");
+          let(&tmpStr, cat(tmpStr, " &nbsp;", tmpStr1, NULL));
+          /* End section - Get the main symbol in the syntax */
+
+          free_vstring(tmpStr1);
+          tmpStr1 = pinkHTML(stmt);
+          let(&tmpStr, cat(tmpStr, "<A HREF=\"",
+              g_Statement[stmt].labelName, ".html\">",
+              g_Statement[stmt].labelName, "</A>", tmpStr1, NULL));
+
+
+        }
+      }
+      if (tmpStr[0]) {
+        let(&tmpStr, cat(tmpStr,
+            "</FONT></TD></TR>", NULL));
+        printLongLine(tmpStr, "", "\"");
+      }
+      /* End of syntax hints list */
+
+
+      /* Get list of axioms and definitions assumed by proof */
+      free_vstring(statementUsedFlags);
+      traceProofWork(statemNum,
+          1, /*essentialFlag*/
+          "", /*traceToList*/
+          &statementUsedFlags,
+          &unprovedList);
+      if ((signed)(strlen(statementUsedFlags)) != g_statements + 1) bug(227);
+
+      /* First get axioms */
+      let(&tmpStr, "");
+      for (stmt = 1; stmt <= g_statements; stmt++) {
+        if (statementUsedFlags[stmt] == 'Y' && g_Statement[stmt].type == a_) {
+          let(&tmpStr1, left(g_Statement[stmt].labelName, 3));
+          if (!strcmp(tmpStr1, "ax-")) {
+            if (!tmpStr[0]) {
+              let(&tmpStr, "<TR><TD ALIGN=LEFT><FONT SIZE=-1><B>"
+                "This theorem was proved from axioms:</B>");
+            }
+            free_vstring(tmpStr1);
+            tmpStr1 = pinkHTML(stmt);
+            let(&tmpStr, cat(tmpStr, " &nbsp;<A HREF=\"",
+                g_Statement[stmt].labelName, ".html\">",
+                g_Statement[stmt].labelName, "</A>", tmpStr1, NULL));
+          }
+        }
+      } /* next stmt */
+      if (tmpStr[0]) {
+        let(&tmpStr, cat(tmpStr, "</FONT></TD></TR>", NULL));
+        printLongLine(tmpStr, "", "\"");
+      }
+
+      /* Then get definitions */
+      let(&tmpStr, "");
+      for (stmt = 1; stmt <= g_statements; stmt++) {
+        if (statementUsedFlags[stmt] == 'Y' && g_Statement[stmt].type == a_) {
+          let(&tmpStr1, left(g_Statement[stmt].labelName, 3));
+          if (!strcmp(tmpStr1, "df-")) {
+            if (!tmpStr[0]) {
+              let(&tmpStr,
+ "<TR><TD ALIGN=LEFT><FONT SIZE=-1><B>This theorem depends on definitions:</B>");
+            }
+            free_vstring(tmpStr1);
+            tmpStr1 = pinkHTML(stmt);
+            let(&tmpStr, cat(tmpStr, " &nbsp;<A HREF=\"",
+                g_Statement[stmt].labelName, ".html\">",
+                g_Statement[stmt].labelName, "</A>", tmpStr1, NULL));
+          }
+        }
+      } /* next stmt */
+      if (tmpStr[0]) {
+        let(&tmpStr, cat(tmpStr, "</FONT></TD></TR>", NULL));
+        printLongLine(tmpStr, "", "\"");
+      }
+
+      /* Print any unproved statements */
+      if (nmbrLen(unprovedList)) {
+        if (nmbrLen(unprovedList) == 1 &&
+            !strcmp(g_Statement[unprovedList[0]].labelName,
+            g_Statement[statemNum].labelName)) {
+          /* When the unproved list consists only of the statement that
+             was traced, it means the statement traced has no
+             proof (or it has a proof, but is incomplete and all earlier
+             ones do have complete proofs). */
+          printLongLine(cat(
+"<TR><TD ALIGN=left >&nbsp;<B><FONT COLOR=\"#FF6600\">",
+"WARNING: This theorem has an incomplete proof.</FONT></B><BR></TD></TR>",
+              NULL), "", "\"");
+
+        } else {
+          printLongLine(cat(
+"<TR><TD ALIGN=left >&nbsp;</TD><TD><B><FONT COLOR=\"#FF6600\">",
+"WARNING: This proof depends on the following unproved theorem(s): ",
+              NULL), "", "\"");
+          let(&tmpStr, "");
+          for (i = 0; i < nmbrLen(unprovedList); i++) {
+            let(&tmpStr, cat(tmpStr, " <A HREF=\"",
+                g_Statement[unprovedList[i]].labelName, ".html\">",
+                g_Statement[unprovedList[i]].labelName, "</A>",
+                NULL));
+          }
+          printLongLine(cat(tmpStr, "</B></FONT></TD></TR>", NULL), "", "\"");
+        }
+      }
+
+      /* End of axiom list */
+
+      /* Put referenced by list last */
+      if (g_printStringForReferencedBy[0]) {
+        if (g_outputToString != 1) bug(257);
+        printLongLine(g_printStringForReferencedBy, "", "\"");
+        free_vstring(g_printStringForReferencedBy);
+      } else {
+        /* Since we always print ref-by list even if "(None)",
+           g_printStringForReferencedBy should never be empty */
+        bug(263);
+      }
+
+    }  /* if essentialFlag */
+
+
+    /* Printing of the trailer in mmwtex.c will close out string later */
+    g_outputToString = 0;
+  }
+
+ typeProof_return:
+  free_vstring(tmpStr);
+  free_vstring(tmpStr1);
+  free_vstring(statementUsedFlags);
+  free_vstring(locLabDecl);
+  free_vstring(tgtLabel);
+  free_vstring(srcLabel);
+  free_vstring(startPrefix);
+  free_vstring(tgtPrefix);
+  free_vstring(srcPrefix);
+  free_vstring(userPrefix);
+  free_vstring(contPrefix);
+  free_vstring(hypStr);
+  free_vstring(startStringWithNum);
+  free_vstring(startStringWithoutNum);
+  free_nmbrString(unprovedList);
+  free_nmbrString(localLabels);
+  free_nmbrString(localLabelNames);
+  free_nmbrString(proof);
+  free_nmbrString(targetHyps);
+  free_nmbrString(indentationLevel);
+  free_nmbrString(essentialFlags);
+  free_nmbrString(stepRenumber);
+  free_nmbrString(notUnifiedFlags);
+  free_nmbrString(relativeStepNums);
+} /* typeProof() */
+
+/* Show details of one proof step */
+/* Note:  detailStep is the actual step number (starting with 1), not
+   the actual step - 1. */
+void showDetailStep(long statemNum, long detailStep) {
+
+  long i, j, plen, step, stmt, sourceStmt, targetStmt;
+  vstring_def(tmpStr);
+  vstring_def(tmpStr1);
+  nmbrString_def(proof);
+  nmbrString_def(localLabels);
+  nmbrString_def(localLabelNames);
+  nmbrString_def(targetHyps);
+  long nextLocLabNum = 1; /* Next number to be used for a local label */
+  void *voidPtr; /* bsearch result */
+  char type;
+
+  /* Error check */
+  i = parseProof(statemNum);
+  if (i) {
+    printLongLine("?The proof is incomplete or has an error", "", " ");
+    return;
+  }
+  plen = nmbrLen(g_WrkProof.proofString);
+  if (plen < detailStep || detailStep < 1) {
+    printLongLine(cat("?The step number should be from 1 to ",
+        str((double)plen), NULL), "", " ");
+    return;
+  }
+
+  /* Structure getStep is declared in mmveri.h. */
+  getStep.stepNum = detailStep; /* Non-zero is flag for verifyProof */
+  parseProof(statemNum); /* ???Do we need to do this again? */
+  verifyProof(statemNum);
+
+
+  nmbrLet(&proof, g_WrkProof.proofString); /* The proof */
+  plen = nmbrLen(proof);
+
+  /* Collect local labels */
+  for (step = 0; step < plen; step++) {
+    stmt = proof[step];
+    if (stmt <= -1000) {
+      stmt = -1000 - stmt;
+      if (!nmbrElementIn(1, localLabels, stmt)) {
+        nmbrLet(&localLabels, nmbrAddElement(localLabels, stmt));
+      }
+    }
+  }
+
+  /* localLabelNames[] hold an integer which, when converted to string,
+    is the local label name. */
+  nmbrLet(&localLabelNames, nmbrSpace(plen));
+
+  /* Get the target hypotheses */
+  nmbrLet(&targetHyps, nmbrGetTargetHyp(proof, statemNum));
+
+  /* Get local labels */
+  for (step = 0; step < plen; step++) {
+    stmt = proof[step];
+    if (stmt >= 0) {
+      if (nmbrElementIn(1, localLabels, step)) {
+        /* This statement declares a local label */
+        /* First, get a name for the local label, using the next integer that
+           does not match any integer used for a statement label. */
+        let(&tmpStr1, str((double)nextLocLabNum));
+        while (1) {
+          voidPtr = (void *)bsearch(tmpStr,
+              g_allLabelKeyBase, (size_t)g_numAllLabelKeys,
+              sizeof(long), labelSrchCmp);
+          if (!voidPtr) break; /* It does not conflict */
+          nextLocLabNum++; /* Try the next one */
+          let(&tmpStr1, str((double)nextLocLabNum));
+        }
+        localLabelNames[step] = nextLocLabNum;
+        nextLocLabNum++; /* Prepare for next local label */
+      }
+    }
+  } /* Next step */
+
+  /* Print the step */
+  let(&tmpStr, g_Statement[targetHyps[detailStep - 1]].labelName);
+  let(&tmpStr1, ""); /* Local label declaration */
+  stmt = proof[detailStep - 1];
+  if (stmt < 0) {
+    if (stmt <= -1000) {
+      stmt = -1000 - stmt;
+      /* stmt is now the step number a local label refers to */
+      let(&tmpStr, cat(tmpStr,"=", str((double)(localLabelNames[stmt])), NULL));
+      type = g_Statement[proof[stmt]].type;
+    } else {
+      if (stmt != -(long)'?') bug(207);
+      let(&tmpStr, cat(tmpStr,"=",chr(-stmt), NULL)); /* '?' */
+      type = '?';
+    }
+  } else {
+    if (nmbrElementIn(1, localLabels, detailStep - 1)) {
+      /* This statement declares a local label */
+      let(&tmpStr1, cat(str((double)(localLabelNames[detailStep - 1])), ":",
+          NULL));
+    }
+    let(&tmpStr, cat(tmpStr, "=", g_Statement[stmt].labelName, NULL));
+    type = g_Statement[stmt].type;
+  }
+
+  /* Print the proof line */
+  printLongLine(cat("Proof step ",
+      str((double)detailStep),
+      ":  ",
+      tmpStr1,
+      tmpStr,
+      " $",
+      chr(type),
+      " ",
+      nmbrCvtMToVString(g_WrkProof.mathStringPtrs[detailStep - 1]),
+      NULL),
+      "  ",
+      " ");
+
+  /* Print details about the step */
+  let(&tmpStr, cat("This step assigns ", NULL));
+  let(&tmpStr1, "");
+  stmt = proof[detailStep - 1];
+  sourceStmt = stmt;
+  if (stmt < 0) {
+    if (stmt <= -1000) {
+      stmt = -1000 - stmt;
+      /* stmt is now the step number a local label refers to */
+      let(&tmpStr, cat(tmpStr, "step ", str((double)stmt),
+          " (via local label reference \"",
+          str((double)(localLabelNames[stmt])), "\") to ", NULL));
+    } else {
+      if (stmt != -(long)'?') bug(208);
+      let(&tmpStr, cat(tmpStr, "an unknown statement to ", NULL));
+    }
+  } else {
+    let(&tmpStr, cat(tmpStr, "source \"", g_Statement[stmt].labelName,
+        "\" ($", chr(g_Statement[stmt].type), ") to ", NULL));
+    if (nmbrElementIn(1, localLabels, detailStep - 1)) {
+      /* This statement declares a local label */
+      let(&tmpStr1, cat("  This step also declares the local label ",
+          str((double)(localLabelNames[detailStep - 1])),
+          ", which is used later on.",
+          NULL));
+    }
+  }
+  targetStmt = targetHyps[detailStep - 1];
+  if (detailStep == plen) {
+    let(&tmpStr, cat(tmpStr, "the final assertion being proved.", NULL));
+  } else {
+    let(&tmpStr, cat(tmpStr, "target \"", g_Statement[targetStmt].labelName,
+    "\" ($", chr(g_Statement[targetStmt].type), ").", NULL));
+  }
+
+  let(&tmpStr, cat(tmpStr, tmpStr1, NULL));
+
+  if (sourceStmt >= 0) {
+    if (g_Statement[sourceStmt].type == a_
+        || g_Statement[sourceStmt].type == p_) {
+      j = nmbrLen(g_Statement[sourceStmt].reqHypList);
+      if (j != nmbrLen(getStep.sourceHyps)) bug(209);
+      if (!j) {
+        let(&tmpStr, cat(tmpStr,
+            "  The source assertion requires no hypotheses.", NULL));
+      } else {
+        if (j == 1) {
+          let(&tmpStr, cat(tmpStr,
+              "  The source assertion requires the hypothesis ", NULL));
+        } else {
+          let(&tmpStr, cat(tmpStr,
+              "  The source assertion requires the hypotheses ", NULL));
+        }
+        for (i = 0; i < j; i++) {
+          let(&tmpStr, cat(tmpStr, "\"",
+              g_Statement[g_Statement[sourceStmt].reqHypList[i]].labelName,
+              "\" ($",
+              chr(g_Statement[g_Statement[sourceStmt].reqHypList[i]].type),
+              ", step ", str((double)(getStep.sourceHyps[i] + 1)), ")", NULL));
+          if (i == 0 && j == 2) {
+            let(&tmpStr, cat(tmpStr, " and ", NULL));
+          }
+          if (i < j - 2 && j > 2) {
+            let(&tmpStr, cat(tmpStr, ", ", NULL));
+          }
+          if (i == j - 2 && j > 2) {
+            let(&tmpStr, cat(tmpStr, ", and ", NULL));
+          }
+        }
+        let(&tmpStr, cat(tmpStr, ".", NULL));
+      }
+    }
+  }
+
+  if (detailStep < plen) {
+    let(&tmpStr, cat(tmpStr,
+         "  The parent assertion of the target hypothesis is \"",
+        g_Statement[getStep.targetParentStmt].labelName, "\" ($",
+        chr(g_Statement[getStep.targetParentStmt].type),", step ",
+        str((double)(getStep.targetParentStep)), ").", NULL));
+  } else {
+    let(&tmpStr, cat(tmpStr,
+        "  The target has no parent because it is the assertion being proved.",
+        NULL));
+  }
+
+  printLongLine(tmpStr, "", " ");
+
+  if (sourceStmt >= 0) {
+    if (g_Statement[sourceStmt].type == a_
+        || g_Statement[sourceStmt].type == p_) {
+      print2("The source assertion before substitution was:\n");
+      printLongLine(cat("    ", g_Statement[sourceStmt].labelName, " $",
+          chr(g_Statement[sourceStmt].type), " ", nmbrCvtMToVString(
+          g_Statement[sourceStmt].mathString), NULL),
+          "        ", " ");
+      j = nmbrLen(getStep.sourceSubstsNmbr);
+      if (j == 1) {
+        printLongLine(cat(
+            "The following substitution was made to the source assertion:",
+            NULL),""," ");
+      } else {
+        printLongLine(cat(
+            "The following substitutions were made to the source assertion:",
+            NULL),""," ");
+      }
+      if (!j) {
+        print2("    (None)\n");
+      } else {
+        print2("    Variable  Substituted with\n");
+        for (i = 0; i < j; i++) {
+          printLongLine(cat("     ",
+              g_MathToken[getStep.sourceSubstsNmbr[i]].tokenName," ",
+              space(9 - (long)strlen(
+                g_MathToken[getStep.sourceSubstsNmbr[i]].tokenName)),
+              nmbrCvtMToVString(getStep.sourceSubstsPntr[i]), NULL),
+              "                ", " ");
+        }
+      }
+    }
+  }
+
+  if (detailStep < plen) {
+    print2("The target hypothesis before substitution was:\n");
+    printLongLine(cat("    ", g_Statement[targetStmt].labelName, " $",
+        chr(g_Statement[targetStmt].type), " ", nmbrCvtMToVString(
+        g_Statement[targetStmt].mathString), NULL),
+        "        ", " ");
+    j = nmbrLen(getStep.targetSubstsNmbr);
+    if (j == 1) {
+      printLongLine(cat(
+          "The following substitution was made to the target hypothesis:",
+          NULL),""," ");
+    } else {
+      printLongLine(cat(
+          "The following substitutions were made to the target hypothesis:",
+          NULL),""," ");
+    }
+    if (!j) {
+      print2("    (None)\n");
+    } else {
+      print2("    Variable  Substituted with\n");
+      for (i = 0; i < j; i++) {
+        printLongLine(cat("     ",
+            g_MathToken[getStep.targetSubstsNmbr[i]].tokenName, " ",
+            space(9 - (long)strlen(
+              g_MathToken[getStep.targetSubstsNmbr[i]].tokenName)),
+            nmbrCvtMToVString(getStep.targetSubstsPntr[i]), NULL),
+            "                ", " ");
+      }
+    }
+  }
+
+  cleanWrkProof();
+  getStep.stepNum = 0; /* Zero is flag for verifyProof to ignore getStep info */
+
+  /* Deallocate getStep contents */
+  j = pntrLen(getStep.sourceSubstsPntr);
+  for (i = 0; i < j; i++) {
+    nmbrLet((nmbrString **)(&getStep.sourceSubstsPntr[i]),
+        NULL_NMBRSTRING);
+  }
+  j = pntrLen(getStep.targetSubstsPntr);
+  for (i = 0; i < j; i++) {
+    nmbrLet((nmbrString **)(&getStep.targetSubstsPntr[i]),
+        NULL_NMBRSTRING);
+  }
+  free_nmbrString(getStep.sourceHyps);
+  free_pntrString(getStep.sourceSubstsPntr);
+  free_nmbrString(getStep.sourceSubstsNmbr);
+  free_pntrString(getStep.targetSubstsPntr);
+  free_nmbrString(getStep.targetSubstsNmbr);
+
+  /* Deallocate other strings */
+  free_vstring(tmpStr);
+  free_vstring(tmpStr1);
+  free_nmbrString(localLabels);
+  free_nmbrString(localLabelNames);
+  free_nmbrString(proof);
+  free_nmbrString(targetHyps);
+
+} /* showDetailStep */
+
+/* Summary of statements in proof for SHOW PROOF / STATEMENT_SUMMARY */
+void proofStmtSumm(long statemNum, flag essentialFlag, flag texFlag) {
+
+  long i, j, k, pos, stmt, plen, slen, step;
+  char type;
+  vstring_def(statementUsedFlags); /* 'Y'/'N' flag that statement is used */
+  vstring_def(str1);
+  vstring_def(str2);
+  vstring_def(str3);
+  nmbrString_def(statementList);
+  nmbrString_def(proof);
+  nmbrString_def(essentialFlags);
+
+  /* This section is never called in HTML mode anymore.  The code is
+     left in though just in case we somehow get here and the user continues
+     through the bug. */
+  if (texFlag && g_htmlFlag) bug(239);
+
+  if (!texFlag) {
+    print2("Summary of statements used in the proof of \"%s\":\n",
+        g_Statement[statemNum].labelName);
+  } else {
+    g_outputToString = 1; /* Flag for print2 to add to g_printString */
+    if (!g_htmlFlag) {
+      print2("\n");
+      print2("\\vspace{1ex} %%3\n");
+      printLongLine(cat("Summary of statements used in the proof of ",
+          "{\\tt ",
+          asciiToTt(g_Statement[statemNum].labelName),
+          "}:", NULL), "", " ");
+    } else {
+      printLongLine(cat("Summary of statements used in the proof of ",
+          "<B>",
+          asciiToTt(g_Statement[statemNum].labelName),
+          "</B>:", NULL), "", "\"");
+    }
+    g_outputToString = 0;
+    fprintf(g_texFilePtr, "%s", g_printString);
+    free_vstring(g_printString);
+  }
+
+  if (g_Statement[statemNum].type != p_) {
+    print2("  This is not a provable ($p) statement.\n");
+    return;
+  }
+
+  /* Don't use bad proofs (incomplete proofs are ok) */
+  if (parseProof(statemNum) > 1) {
+    /* The proof has an error, so use the empty proof */
+    nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
+  } else {
+    nmbrLet(&proof, g_WrkProof.proofString);
+  }
+
+  plen = nmbrLen(proof);
+  /* Get the essential step flags, if required */
+  if (essentialFlag) {
+    nmbrLet(&essentialFlags, nmbrGetEssential(proof));
+  }
+
+  for (step = 0; step < plen; step++) {
+    if (essentialFlag) {
+      if (!essentialFlags[step]) continue;     /* Ignore floating hypotheses */
+    }
+    stmt = proof[step];
+    if (stmt < 0) {
+      continue; /* Ignore '?' and local labels */
+    }
+    if (1) { /* Limit list to $a and $p only */
+      if (g_Statement[stmt].type != a_ && g_Statement[stmt].type != p_) {
+        continue;
+      }
+    }
+    /* Add this statement to the statement list if not already in it */
+    if (!nmbrElementIn(1, statementList, stmt)) {
+      nmbrLet(&statementList, nmbrAddElement(statementList, stmt));
+    }
+  } /* Next step */
+
+  /* Prepare the output */
+  /* First, fill in the statementUsedFlags char array.  This allows us to sort
+     the output by statement number without calling a sort routine. */
+  slen = nmbrLen(statementList);
+  let(&statementUsedFlags, string(g_statements + 1, 'N')); /* Init. to 'no' */
+  for (pos = 0; pos < slen; pos++) {
+    stmt = statementList[pos];
+    if (stmt > statemNum || stmt < 1) bug(210);
+    statementUsedFlags[stmt] = 'Y';
+  }
+  /* Next, build the output string */
+  for (stmt = 1; stmt < statemNum; stmt++) {
+    if (statementUsedFlags[stmt] == 'Y') {
+      assignStmtFileAndLineNum(stmt);
+      let(&str1, cat(" is located on line ",
+          str((double)(g_Statement[stmt].lineNum)),
+          " of the file ", NULL));
+      if (!texFlag) {
+        print2("\n");
+        printLongLine(cat("Statement ", g_Statement[stmt].labelName, str1,
+          "\"", g_Statement[stmt].fileName,
+          "\".",NULL), "", " ");
+      } else {
+        g_outputToString = 1; /* Flag for print2 to add to g_printString */
+        if (!g_htmlFlag) {
+          print2("\n");
+          print2("\n");
+          print2("\\vspace{1ex} %%4\n");
+          printLongLine(cat("Statement {\\tt ",
+              asciiToTt(g_Statement[stmt].labelName), "} ",
+              str1, "{\\tt ",
+              asciiToTt(g_Statement[stmt].fileName),
+              "}.", NULL), "", " ");
+          print2("\n");
+        } else {
+          printLongLine(cat("Statement <B>",
+              asciiToTt(g_Statement[stmt].labelName), "</B> ",
+              str1, " <B>",
+              asciiToTt(g_Statement[stmt].fileName),
+              "</B> ", NULL), "", "\"");
+        }
+        g_outputToString = 0;
+        fprintf(g_texFilePtr, "%s", g_printString);
+        free_vstring(g_printString);
+      }
+
+      free_vstring(str1);
+      str1 = getDescription(stmt);
+      if (str1[0]) {
+        if (!texFlag) {
+          printLongLine(cat("\"", str1, "\"", NULL), "", " ");
+        } else {
+          printTexComment(str1,              /* Sends result to g_texFilePtr */
+              1, /* 1 = htmlCenterFlag */
+              PROCESS_EVERYTHING, /* actionBits */
+              1 /* 1 = fileCheck */);
+        }
+      }
+
+      j = nmbrLen(g_Statement[stmt].reqHypList);
+      for (i = 0; i < j; i++) {
+        k = g_Statement[stmt].reqHypList[i];
+        if (!essentialFlag || g_Statement[k].type != f_) {
+          let(&str2, cat("  ",g_Statement[k].labelName,
+              " $", chr(g_Statement[k].type), " ", NULL));
+          if (!texFlag) {
+            printLongLine(cat(str2,
+                nmbrCvtMToVString(g_Statement[k].mathString), " $.", NULL),
+                "      "," ");
+          } else {
+            let(&str3, space((long)strlen(str2)));
+            printTexLongMath(g_Statement[k].mathString,
+                str2, str3, 0, 0);
+          }
+        }
+      }
+
+      let(&str1, "");
+      type = g_Statement[stmt].type;
+      if (type == p_) let(&str1, " $= ...");
+      let(&str2, cat("  ", g_Statement[stmt].labelName,
+          " $",chr(type), " ", NULL));
+      if (!texFlag) {
+        printLongLine(cat(str2,
+            nmbrCvtMToVString(g_Statement[stmt].mathString),
+            str1, " $.", NULL), "      ", " ");
+      } else {
+        let(&str3, space((long)strlen(str2)));
+        printTexLongMath(g_Statement[stmt].mathString,
+            str2, str3, 0, 0);
+      }
+
+    } /* End if (statementUsedFlag[stmt] == 'Y') */
+  } /* Next stmt */
+
+  free_vstring(statementUsedFlags); /* 'Y'/'N' flag that statement is used */
+  free_vstring(str1);
+  free_vstring(str2);
+  free_vstring(str3);
+  free_nmbrString(statementList);
+  free_nmbrString(proof);
+  free_nmbrString(essentialFlags);
+
+} /* proofStmtSumm */
+
+
+/* Traces back the statements used by a proof, recursively. */
+/* Returns 1 if at least one label is printed (or would be printed in
+   case testOnlyFlag=1); otherwise, returns 0 */
+/* matchList suppresses all output except labels matching matchList */
+/* testOnlyFlag prevents any printout; it is used to determine whether
+   there is an unwanted axiom for MINIMIZE_WITH /FORBID. */
+flag traceProof(long statemNum,
+  flag essentialFlag,
+  flag axiomFlag,
+  vstring matchList,
+  vstring traceToList,
+  flag testOnlyFlag)
+{
+
+  long stmt, pos;
+  vstring_def(statementUsedFlags); /* y/n flags that statement is used */
+  vstring_def(outputString);
+  nmbrString_def(unprovedList);
+  flag foundFlag = 0;
+
+  /* Make sure we're calling this with $p statements only */
+  if (g_Statement[statemNum].type != (char)p_) bug(249);
+
+  if (!testOnlyFlag) {
+    if (axiomFlag) {
+      print2(
+  "Statement \"%s\" assumes the following axioms ($a statements):\n",
+          g_Statement[statemNum].labelName);
+    } else  if (traceToList[0] == 0) {
+      print2(
+  "The proof of statement \"%s\" uses the following earlier statements:\n",
+          g_Statement[statemNum].labelName);
+    } else {
+      print2(
+  "The proof of statement \"%s\" traces back to \"%s\" via:\n",
+          g_Statement[statemNum].labelName, traceToList);
+    }
+  }
+
+  traceProofWork(statemNum,
+      essentialFlag,
+      traceToList, /* /TO argument of SHOW TRACE_BACK */
+      &statementUsedFlags,
+      &unprovedList);
+  if ((signed)(strlen(statementUsedFlags)) != g_statements + 1) bug(226);
+
+  /* Build the output string */
+  let(&outputString, "");
+  for (stmt = 1; stmt < statemNum; stmt++) {
+    if (statementUsedFlags[stmt] == 'Y') {
+
+      if (matchList[0]) {  /* There is a list to match */
+        /* Don't include unmatched labels */
+        if (!matchesList(g_Statement[stmt].labelName, matchList, '*', '?'))
+          continue;
+      }
+
+      /* Skip rest of scan in testOnlyFlag mode */
+      foundFlag = 1; /* At least one label would be printed */
+      if (testOnlyFlag) {
+        goto TRACE_RETURN;
+      }
+      if (axiomFlag) {
+        if (g_Statement[stmt].type == a_) {
+          let(&outputString, cat(outputString, " ", g_Statement[stmt].labelName,
+              NULL));
+        }
+      } else {
+        let(&outputString, cat(outputString, " ", g_Statement[stmt].labelName,
+            NULL));
+        switch (g_Statement[stmt].type) {
+          case a_: let(&outputString, cat(outputString, "($a)", NULL)); break;
+          case e_: let(&outputString, cat(outputString, "($e)", NULL)); break;
+          case f_: let(&outputString, cat(outputString, "($f)", NULL)); break;
+        }
+      }
+    } /* End if (statementUsedFlag[stmt] == 'Y') */
+  } /* Next stmt */
+
+  /* Skip printing in testOnlyFlag mode */
+  if (testOnlyFlag) {
+    goto TRACE_RETURN;
+  }
+
+  if (outputString[0]) {
+    let(&outputString, cat(" ", outputString, NULL));
+  } else {
+    let(&outputString, "  (None)");
+  }
+
+  /* Print the output */
+  printLongLine(outputString, "  ", " ");
+
+  /* Print any unproved statements */
+  if (nmbrLen(unprovedList)) {
+    print2("Warning: The following traced statement(s) were not proved:\n");
+    let(&outputString, "");
+    for (pos = 0; pos < nmbrLen(unprovedList); pos++) {
+      let(&outputString, cat(outputString, " ", g_Statement[unprovedList[
+          pos]].labelName, NULL));
+    }
+    let(&outputString, cat("  ", outputString, NULL));
+    printLongLine(outputString, "  ", " ");
+  }
+
+ TRACE_RETURN:
+  /* Deallocate */
+  free_vstring(outputString);
+  free_vstring(statementUsedFlags);
+  free_nmbrString(unprovedList);
+  return foundFlag;
+} /* traceProof */
+
+/* Traces back the statements used by a proof, recursively.  Returns
+   a nmbrString with a list of statements and unproved statements */
+void traceProofWork(long statemNum,
+  flag essentialFlag,
+  vstring traceToList, /* /TO argument of SHOW TRACE_BACK */
+  vstring *statementUsedFlagsP, /* 'Y'/'N' flag that statement is used */
+  nmbrString **unprovedListP)
+{
+
+  long pos, stmt, plen, slen, step;
+  nmbrString_def(statementList);
+  nmbrString_def(proof);
+  nmbrString_def(essentialFlags);
+  vstring_def(traceToFilter);
+  vstring_def(str1);
+  long j;
+
+  /* Preprocess the "SHOW TRACE_BACK ... / TO" traceToList list if any */
+  if (traceToList[0] != 0) {
+    let(&traceToFilter, string(g_statements + 1, 'N')); /* Init. to 'no' */
+    /* Wildcard match scan */
+    for (stmt = 1; stmt <= g_statements; stmt++) {
+      if (g_Statement[stmt].type != (char)a_
+          && g_Statement[stmt].type != (char)p_)
+        continue; /* Not a $a or $p statement; skip it */
+      /* Wildcard matching */
+      if (!matchesList(g_Statement[stmt].labelName, traceToList, '*', '?'))
+        continue;
+      free_vstring(str1);
+      str1 = traceUsage(stmt /*g_showStatement*/,
+          1, /*recursiveFlag*/
+          statemNum /* cutoffStmt */);
+      traceToFilter[stmt] = 'Y'; /* Include the statement we're showing
+                                    usage of */
+      if (str1[0] == 'Y') {  /* There is some usage */
+        for (j = stmt + 1; j <= g_statements; j++) {
+          /* OR in the usage to the filter */
+          if (str1[j] == 'Y') traceToFilter[j] = 'Y';
+        }
+      }
+    } /* Next i (statement number) */
+  } /* if (traceToList[0] != 0) */
+
+  nmbrLet(&statementList, nmbrSpace(g_statements));
+  statementList[0] = statemNum;
+  slen = 1;
+  free_nmbrString(*unprovedListP); /* List of unproved statements */
+  let(&(*statementUsedFlagsP), string(g_statements + 1, 'N')); /* Init. to 'no' */
+  (*statementUsedFlagsP)[statemNum] = 'Y';
+  for (pos = 0; pos < slen; pos++) {
+    if (g_Statement[statementList[pos]].type != p_) {
+      continue; /* Not a $p */
+    }
+
+    /* Don't use bad proofs (incomplete proofs are ok) */
+    if (parseProof(statementList[pos]) > 1) {
+      /* The proof has an error, so use the empty proof */
+      nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
+    } else {
+      nmbrLet(&proof, g_WrkProof.proofString);
+    }
+
+    plen = nmbrLen(proof);
+    /* Get the essential step flags, if required */
+    if (essentialFlag) {
+      nmbrLet(&essentialFlags, nmbrGetEssential(proof));
+    }
+    for (step = 0; step < plen; step++) {
+      if (essentialFlag) {
+        if (!essentialFlags[step]) continue;  /* Ignore floating hypotheses */
+      }
+      stmt = proof[step];
+      if (stmt < 0) {
+        if (stmt > -1000) {
+          /* '?' */
+          if (!nmbrElementIn(1, *unprovedListP, statementList[pos])) {
+            nmbrLet(&(*unprovedListP), nmbrAddElement(*unprovedListP,
+                statementList[pos]));  /* Add to list of unproved statements */
+          }
+        }
+        continue; /* Ignore '?' and local labels */
+      }
+      if (1) { /* Limit list to $a and $p only */
+        if (g_Statement[stmt].type != a_ && g_Statement[stmt].type != p_) {
+          continue;
+        }
+      }
+      /* Add this statement to the statement list if not already in it */
+      if ((*statementUsedFlagsP)[stmt] == 'N') {
+        if (traceToList[0] == 0) {
+          statementList[slen] = stmt;
+          slen++;
+          (*statementUsedFlagsP)[stmt] = 'Y';
+        } else { /* TRACE_BACK / TO */
+          if (traceToFilter[stmt] == 'Y') {
+            statementList[slen] = stmt;
+            slen++;
+            (*statementUsedFlagsP)[stmt] = 'Y';
+          }
+        }
+      }
+    } /* Next step */
+  } /* Next pos */
+
+  /* Deallocate */
+  free_nmbrString(essentialFlags);
+  free_nmbrString(proof);
+  free_nmbrString(statementList);
+  free_vstring(str1);
+  return;
+
+} /* traceProofWork */
+
+nmbrString_def(stmtFoundList);
+long indentShift = 0;
+
+/* Traces back the statements used by a proof, recursively, with tree display.*/
+void traceProofTree(long statemNum,
+  flag essentialFlag, long endIndent)
+{
+  if (g_Statement[statemNum].type != p_) {
+    print2("Statement %s is not a $p statement.\n",
+        g_Statement[statemNum].labelName);
+    return;
+  }
+
+  printLongLine(cat("The proof tree traceback for statement \"",
+      g_Statement[statemNum].labelName,
+      "\" follows.  The statements used by each proof are indented one level in,",
+      " below the statement being proved.  Hypotheses are not included.",
+      NULL),
+      "", " ");
+  print2("\n");
+
+  free_nmbrString(stmtFoundList);
+  indentShift = 0;
+  traceProofTreeRec(statemNum, essentialFlag, endIndent, 0);
+  free_nmbrString(stmtFoundList);
+} /* traceProofTree */
+
+
+void traceProofTreeRec(long statemNum,
+  flag essentialFlag, long endIndent, long recursDepth)
+{
+  long i, pos, stmt, plen, slen, step;
+  vstring_def(outputStr);
+  nmbrString_def(localFoundList);
+  nmbrString_def(localPrintedList);
+  flag unprovedFlag = 0;
+  nmbrString_def(proof);
+  nmbrString_def(essentialFlags);
+
+
+  free_vstring(outputStr);
+  outputStr = getDescription(statemNum); /* Get statement comment */
+  let(&outputStr, edit(outputStr, 8 + 16 + 128)); /* Trim and reduce spaces */
+  slen = len(outputStr);
+  for (i = 0; i < slen; i++) {
+    /* Change newlines to spaces in comment */
+    if (outputStr[i] == '\n') {
+      outputStr[i] = ' ';
+    }
+  }
+
+#define INDENT_INCR 3
+#define MAX_LINE_LEN 79
+
+  if ((recursDepth * INDENT_INCR - indentShift) >
+      (g_screenWidth - MAX_LINE_LEN) + 50) {
+    indentShift = indentShift + 40 + (g_screenWidth - MAX_LINE_LEN);
+    print2("****** Shifting indentation.  Total shift is now %ld.\n",
+      (long)indentShift);
+  }
+  if ((recursDepth * INDENT_INCR - indentShift) < 1 && indentShift != 0) {
+    indentShift = indentShift - 40 - (g_screenWidth - MAX_LINE_LEN);
+    print2("****** Shifting indentation.  Total shift is now %ld.\n",
+      (long)indentShift);
+  }
+
+  let(&outputStr, cat(space(recursDepth * INDENT_INCR - indentShift),
+      g_Statement[statemNum].labelName, " $", chr(g_Statement[statemNum].type),
+      "  \"", edit(outputStr, 8 + 128), "\"", NULL));
+
+  if (len(outputStr) > MAX_LINE_LEN + (g_screenWidth - MAX_LINE_LEN)) {
+    let(&outputStr, cat(left(outputStr,
+        MAX_LINE_LEN + (g_screenWidth - MAX_LINE_LEN) - 3), "...", NULL));
+  }
+
+  if (g_Statement[statemNum].type == p_ || g_Statement[statemNum].type == a_) {
+    /* Only print assertions to reduce output bulk */
+    print2("%s\n", outputStr);
+  }
+
+  if (g_Statement[statemNum].type != p_) {
+    free_vstring(outputStr);
+    return;
+  }
+
+  if (endIndent) {
+    /* An indentation level limit is set */
+    if (endIndent < recursDepth + 2) {
+      free_vstring(outputStr);
+      return;
+    }
+  }
+
+  /* Don't use bad proofs (incomplete proofs are ok) */
+  if (parseProof(statemNum) > 1) {
+    /* The proof has an error, so use the empty proof */
+    nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
+  } else {
+    nmbrLet(&proof, g_WrkProof.proofString);
+  }
+
+  plen = nmbrLen(proof);
+  /* Get the essential step flags, if required */
+  if (essentialFlag) {
+    nmbrLet(&essentialFlags, nmbrGetEssential(proof));
+  }
+  nmbrLet(&localFoundList, NULL_NMBRSTRING);
+  nmbrLet(&localPrintedList, NULL_NMBRSTRING);
+  for (step = 0; step < plen; step++) {
+    if (essentialFlag) {
+      if (!essentialFlags[step]) continue;
+                                                /* Ignore floating hypotheses */
+    }
+    stmt = proof[step];
+    if (stmt < 0) {
+      if (stmt > -1000) {
+        /* '?' */
+        unprovedFlag = 1;
+      }
+      continue; /* Ignore '?' and local labels */
+    }
+    if (!nmbrElementIn(1, localFoundList, stmt)) {
+      nmbrLet(&localFoundList, nmbrAddElement(localFoundList, stmt));
+    }
+    if (!nmbrElementIn(1, stmtFoundList, stmt)) {
+      traceProofTreeRec(stmt, essentialFlag, endIndent, recursDepth + 1);
+      nmbrLet(&localPrintedList, nmbrAddElement(localPrintedList, stmt));
+      nmbrLet(&stmtFoundList, nmbrAddElement(stmtFoundList, stmt));
+    }
+  } /* Next step */
+
+  /* See if there are any old statements printed previously */
+  slen = nmbrLen(localFoundList);
+  let(&outputStr, "");
+  for (pos = 0; pos < slen; pos++) {
+    stmt = localFoundList[pos];
+    if (!nmbrElementIn(1, localPrintedList, stmt)) {
+      /* Don't include $f, $e in output */
+      if (g_Statement[stmt].type == p_ || g_Statement[stmt].type == a_) {
+        let(&outputStr, cat(outputStr, " ",
+            g_Statement[stmt].labelName, NULL));
+      }
+    }
+  }
+
+  if (len(outputStr)) {
+    printLongLine(cat(space(INDENT_INCR * (recursDepth + 1) - 1 - indentShift),
+      outputStr, " (shown above)", NULL),
+      space(INDENT_INCR * (recursDepth + 2) - indentShift), " ");
+  }
+
+  if (unprovedFlag) {
+    printLongLine(cat(space(INDENT_INCR * (recursDepth + 1) - indentShift),
+      "*** Statement ", g_Statement[statemNum].labelName, " has not been proved."
+      , NULL),
+      space(INDENT_INCR * (recursDepth + 2)), " ");
+  }
+
+  free_vstring(outputStr);
+  free_nmbrString(localFoundList);
+  free_nmbrString(localPrintedList);
+  free_nmbrString(proof);
+  free_nmbrString(essentialFlags);
+
+} /* traceProofTreeRec */
+
+
+/* Called by SHOW TRACE_BACK <label> / COUNT_STEPS */
+/* Counts the number of steps a completely exploded proof would require */
+/* (Recursive) */
+/* 0 is returned if some assertions have incomplete proofs. */
+double countSteps(long statemNum, flag essentialFlag)
+{
+  static double *stmtCount;
+  static double *stmtNodeCount;
+  static long *stmtDist;
+  static long *stmtMaxPath;
+  static double *stmtAveDist;
+  static long *stmtProofLen; /* The actual number of steps in stmt's proof */
+  static long *stmtUsage; /* The number of times the statement is used */
+  static long level = 0;
+  static flag unprovedFlag;
+
+  long stmt, plen, step, i, j, k;
+  long essentialplen;
+  nmbrString_def(proof);
+  double stepCount; /* The total steps if fully expanded */
+
+  static vstring *stmtBigCount; /* Unlimited precision stmtCount */
+  vstring_def(stepBigCount); /* Unlimited precision stepCount */
+  vstring_def(tmpBig1);
+
+  double stepNodeCount;
+  double stepDistSum;
+  nmbrString_def(essentialFlags);
+  vstring_def(tmpStr);
+  long actualSteps, actualSubTheorems;
+  long actualSteps2, actualSubTheorems2;
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  essentialplen = 0;
+
+  /* If this is the top level of recursion, initialize things */
+  if (!level) {
+    stmtCount = malloc((sizeof(double) * ((size_t)g_statements + 1)));
+    stmtBigCount = malloc((sizeof(vstring) * ((size_t)g_statements + 1)));
+    stmtNodeCount = malloc(sizeof(double) * ((size_t)g_statements + 1));
+    stmtDist = malloc(sizeof(long) * ((size_t)g_statements + 1));
+    stmtMaxPath = malloc(sizeof(long) * ((size_t)g_statements + 1));
+    stmtAveDist = malloc(sizeof(double) * ((size_t)g_statements + 1));
+    stmtProofLen = malloc(sizeof(long) * ((size_t)g_statements + 1));
+    stmtUsage = malloc(sizeof(long) * ((size_t)g_statements + 1));
+    if (!stmtCount || !stmtNodeCount || !stmtDist || !stmtMaxPath ||
+        !stmtAveDist || !stmtProofLen || !stmtUsage) {
+      print2("?Memory overflow.  Step count will be wrong.\n");
+      if (stmtCount) free(stmtCount);
+      if (stmtBigCount) free(stmtBigCount);
+      if (stmtNodeCount) free(stmtNodeCount);
+      if (stmtDist) free(stmtDist);
+      if (stmtMaxPath) free(stmtMaxPath);
+      if (stmtAveDist) free(stmtAveDist);
+      if (stmtProofLen) free(stmtProofLen);
+      if (stmtUsage) free(stmtUsage);
+      return (0);
+    }
+    for (stmt = 1; stmt < g_statements + 1; stmt++) {
+      stmtCount[stmt] = 0;
+      stmtBigCount[stmt] = "";
+      stmtUsage[stmt] = 0;
+      stmtDist[stmt] = 0;
+    }
+    unprovedFlag = 0; /* Flag that some proof wasn't complete */
+  }
+  level++;
+  stepCount = 0;
+  let(&stepBigCount, "0"); /* "" and "0" both mean 0 */
+  stepNodeCount = 0;
+  stepDistSum = 0;
+  stmtDist[statemNum] = -2; /* Forces at least one assignment */
+
+  if (g_Statement[statemNum].type != (char)p_) {
+    /* $a, $e, or $f */
+    stepCount = 1;
+    let(&stepBigCount, "1");
+    stepNodeCount = 0;
+    stmtDist[statemNum] = 0;
+    goto returnPoint;
+  }
+
+  /* Don't use bad proofs (incomplete proofs are ok) */
+  if (parseProof(statemNum) > 1) {
+    /* The proof has an error, so use the empty proof */
+    nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
+  } else {
+    /* Use proof as it is saved (so user can choose compressed or not) */
+    nmbrLet(&proof, g_WrkProof.proofString); /* The proof */
+  }
+
+  plen = nmbrLen(proof);
+  /* Get the essential step flags, if required */
+  if (essentialFlag) {
+    nmbrLet(&essentialFlags, nmbrGetEssential(proof));
+  }
+  essentialplen = 0;
+  for (step = 0; step < plen; step++) {
+    /* Use the following loop to get an alternate maximum path */
+    if (essentialFlag) {
+      if (!essentialFlags[step]) continue;     /* Ignore floating hypotheses */
+    }
+    essentialplen++;
+    stmt = proof[step];
+    if (stmt < 0) {
+      if (stmt <= -1000) {
+        /* User can choose to count compressed or normal steps by saving
+           the proof that way */
+        /* A local label does not add a proof step in the web page proof */
+        continue;
+      } else {
+        /* '?' */
+        unprovedFlag = 1;
+        stepCount = stepCount + 1;
+
+        free_vstring(tmpBig1);
+        tmpBig1 = bigAdd(stepBigCount, "1");
+        let(&stepBigCount, tmpBig1);
+
+        stepNodeCount = stepNodeCount + 1;
+        stepDistSum = stepDistSum + 1;
+      }
+    } else {
+      if (stmtCount[stmt] == 0) {
+        /* It has not been computed yet - call this function recursively */
+        stepCount = stepCount + countSteps(stmt, essentialFlag);
+      } else {
+        /* It has already been computed */
+        stepCount = stepCount + stmtCount[stmt];
+      }
+
+      /* In either case, stmtBigCount[stmt] will be populated now */
+      free_vstring(tmpBig1);
+      tmpBig1 = bigAdd(stepBigCount, stmtBigCount[stmt]);
+      let(&stepBigCount, tmpBig1);
+
+      if (g_Statement[stmt].type == (char)p_) {
+        /*stepCount--;*/ /* -1 to account for the replacement of this step */
+        for (j = 0; j < g_Statement[stmt].numReqHyp; j++) {
+          k = g_Statement[stmt].reqHypList[j];
+          if (!essentialFlag || g_Statement[k].type == (char)e_) {
+            stepCount--;
+
+            /* In either case, stmtBigCount[stmt] will be populated now */
+            free_vstring(tmpBig1);
+            tmpBig1 = bigSub(stepBigCount, "1");
+            let(&stepBigCount, tmpBig1);
+
+          }
+        }
+      }
+      stmtUsage[stmt]++;
+      if (stmtDist[statemNum] < stmtDist[stmt] + 1) {
+        stmtDist[statemNum] = stmtDist[stmt] + 1;
+        stmtMaxPath[statemNum] = stmt;
+      }
+      stepNodeCount = stepNodeCount + stmtNodeCount[stmt];
+      stepDistSum = stepDistSum + stmtAveDist[stmt] + 1;
+    }
+
+  } /* Next step */
+
+ returnPoint:
+
+  /* Assign step count to statement list */
+  stmtCount[statemNum] = stepCount;
+
+  if ((stmtBigCount[statemNum])[0] != 0) bug(264);
+  let(&stmtBigCount[statemNum], stepBigCount);
+
+  stmtNodeCount[statemNum] = stepNodeCount + 1;
+  stmtAveDist[statemNum] = (double)stepDistSum / (double)essentialplen;
+  stmtProofLen[statemNum] = essentialplen;
+
+  free_nmbrString(proof);
+  free_nmbrString(essentialFlags);
+
+  level--;
+  /* If this is the top level of recursion, deallocate */
+  if (!level) {
+    if (unprovedFlag) stepCount = 0; /* Don't mislead user */
+
+    /* Compute the total actual steps, total actual subtheorems */
+    actualSteps = stmtProofLen[statemNum];
+    actualSubTheorems = 0;
+    actualSteps2 = actualSteps; /* Steps w/ single-use subtheorems eliminated */
+    actualSubTheorems2 = 0; /* Multiple-use subtheorems only */
+    for (i = 1; i < statemNum; i++) {
+      if (g_Statement[i].type == (char)p_ && stmtCount[i] != 0) {
+        actualSteps = actualSteps + stmtProofLen[i];
+        actualSubTheorems++;
+        if (stmtUsage[i] > 1) {
+          actualSubTheorems2++;
+          actualSteps2 = actualSteps2 + stmtProofLen[i];
+        } else {
+          actualSteps2 = actualSteps2 + stmtProofLen[i] - 1;
+          for (j = 0; j < g_Statement[i].numReqHyp; j++) {
+            /* Subtract out hypotheses if subtheorem eliminated */
+            k = g_Statement[i].reqHypList[j];
+            if (!essentialFlag || g_Statement[k].type == (char)e_) {
+              actualSteps2--;
+            }
+          }
+        }
+      }
+    }
+
+    j = statemNum;
+    for (i = stmtDist[statemNum]; i >= 0; i--) {
+      if (stmtDist[j] != i) bug(214);
+      let(&tmpStr, cat(tmpStr, " <- ", g_Statement[j].labelName,
+          NULL));
+      j = stmtMaxPath[j];
+    }
+    printLongLine(cat(
+       "The statement's actual proof has ",
+           str((double)(stmtProofLen[statemNum])), " steps.  ",
+       "Backtracking, a total of ", str((double)actualSubTheorems),
+           " different subtheorems are used.  ",
+       "The statement and subtheorems have a total of ",
+           str((double)actualSteps), " actual steps.  ",
+       "If subtheorems used only once were eliminated,",
+           " there would be a total of ",
+           str((double)actualSubTheorems2), " subtheorems, and ",
+       "the statement and subtheorems would have a total of ",
+           str((double)actualSteps2), " steps.  ",
+       "The proof would have ",
+
+       stepBigCount,
+       strlen(stepBigCount) < 6 ? ""
+           : cat(" =~ ",
+                 left(
+                    str((double)
+                        ((5.0 + val(left(stepBigCount, 3))) / 100.0)),
+                    3),
+                 " x 10^",
+                 str((double)strlen(stepBigCount) - 1), NULL),
+
+
+       " steps if fully expanded back to axiom references.  ",
+       "The maximum path length is ",
+       str((double)(stmtDist[statemNum])),
+       ".  A longest path is:  ", right(tmpStr, 5), " .", NULL),
+       "", " ");
+    free_vstring(tmpStr);
+
+    free(stmtCount);
+    free(stmtNodeCount);
+    free(stmtDist);
+    free(stmtMaxPath);
+    free(stmtAveDist);
+    free(stmtProofLen);
+    free(stmtUsage);
+
+    /* Deallocate the big number strings */
+    for (stmt = 1; stmt < g_statements + 1; stmt++) {
+      free_vstring(stmtBigCount[stmt]);
+    }
+    free(stmtBigCount);
+
+  }
+
+  /* Deallocate local strings */
+  free_vstring(tmpBig1);
+  free_vstring(stepBigCount);
+
+  return stepCount;
+} /* countSteps */
+
+
+/* Add two arbitrary precision nonnegative integers represented
+   as strings of digits e.g. bigAdd("1234", "55") returns "1289".
+   Zero can be represented by "", "0", "00", etc. */
+/* This is a slow, unsophisticated algorithm intended for use by
+   countSteps() i.e. SHOW TRACE_BACK .../COUNT_STEPS */
+/* The caller must deallocate the returned string, and the string arguments
+   must not be volatile i.e. will not be freed by unrelated 'let()' */
+vstring bigAdd(vstring bignum1, vstring bignum2) {
+  long len1, len2, maxlen, p, p1, p2, p3;
+  char d1, d2, carry, dsum;
+  vstring_def(bignum3);
+  len1 = (long)strlen(bignum1);
+  len2 = (long)strlen(bignum2);
+  maxlen = (len1 < len2 ? len2 : len1);
+  let(&bignum3, space(maxlen + 1)); /* +1 to allow for final carry */
+  carry = 0;
+  for (p = 1; p <= maxlen; p++) {
+    p1 = len1 - p;
+    p2 = len2 - p;
+    d1 = (char)(p1 >= 0 ? bignum1[p1] - '0' : 0);
+    d2 = (char)(p2 >= 0 ? bignum2[p2] - '0' : 0);
+    dsum = (char)(d1 + d2 + carry);
+    if (dsum > 9) {
+      dsum = (char)(dsum - 10);
+      carry = 1;
+    } else {
+      carry = 0;
+    }
+    p3 = maxlen + 1 - p;
+    bignum3[p3] = (char)(dsum + '0');
+  }
+  bignum3[0] = (char)(carry + '0');
+  while (bignum3[0] == '0') {
+    /* Suppress leading 0s */
+    let(&bignum3, right(bignum3, 2));
+  }
+  return bignum3;
+}
+
+
+/* Subtract a nonnegative number (2nd arg) from a larger nonnegative number
+   (1st arg).  If the 1st arg is smaller than the 2nd, results are
+   not meaningful; there is no error checking for this.  */
+/* This is a slow, unsophisticated algorithm intended for use by
+   countSteps() i.e. SHOW TRACE_BACK .../COUNT_STEPS */
+/* The caller must deallocate the returned string */
+/* The arguments must be strings that will not be freed by unrelated 'let()' */
+vstring bigSub(vstring bignum1, vstring bignum2) {
+  long len1, len3, p;
+  vstring_def(bignum3);
+  vstring_def(bignum1cmpl);
+  len1 = (long)strlen(bignum1);
+  let(&bignum1cmpl, space(len1));
+  for (p = 0; p <= len1 - 1; p++) {
+    /* Take 9s complement of 1st arg */
+    bignum1cmpl[p] = (char)(9 - (bignum1[p] - '0') + '0');
+  }
+  bignum3 = bigAdd(bignum1cmpl, bignum2);
+  len3 = (long)strlen(bignum3);
+  if (len3 < len1) {
+    /* We need to pad 0s before taking 9s complement */
+    let(&bignum3, cat(string(len1 - len3, '0'), bignum3, NULL));
+    len3 = len1;
+  }
+  for (p = 0; p <= len3 - 1; p++) {
+    /* Take 9s complement of result */
+    bignum3[p] = (char)(9 - (bignum3[p] - '0') + '0');
+  }
+  while (bignum3[0] == '0') {
+    /* Suppress leading 0s */
+    let(&bignum3, right(bignum3, 2));
+  }
+  free_vstring(bignum1cmpl); /* Deallocate */
+  return bignum3;
+}
+
+
+/**** 12-Nov-2018 nm In case multiplication is ever needed, this
+  code will do it but is commented out because currently there
+  is no need for it.
+****************************************************************************
+/@ Multiply an arbitrary precision nonnegative integers by a positive
+   digit e.g. bigMulDigit("123", 9) returns "1107".
+   Zero can be represented by "", "0", "00", etc. @/
+/@ This is a slow, unsophisticated algorithm intended for use by
+   SHOW TRACE_BACK .../COUNT_STEPS @/
+/@ The caller must deallocate the returned string @/
+/@ The arguments must be strings that will not be freed by unrelated 'let()' @/
+vstring bigMulDigit(vstring bignum1, long digit) {
+  long len1, p, p1, p3;
+  char d1, carry, dprod;
+  vstring_def(bignum3);
+  len1 = (long)strlen(bignum1);
+  let(&bignum3, space(len1 + 1)); /@ +1 to allow for final carry @/
+  carry = 0;
+  for (p = 1; p <= len1; p++) {
+    p1 = len1 - p;
+    d1 = (char)(bignum1[p1] - '0');
+    dprod = (char)((d1 @ digit) + carry);
+    if (dprod > 9) {
+      carry = dprod / 10;
+      dprod = dprod % 10;
+    } else {
+      carry = 0;
+    }
+    p3 = len1 + 1 - p;
+    bignum3[p3] = (char)(dprod + '0');
+  }
+  bignum3[0] = (char)(carry + '0');
+  while (bignum3[0] == '0') {
+    /@ Suppress leading 0s @/
+    let(&bignum3, right(bignum3, 2));
+  }
+  return bignum3;
+}
+
+/@ Multiply two arbitrary precision nonnegative integers represented
+   as strings of digits e.g. bigMul("1234", "55") returns "67870".
+   Zero can be represented by "", "0", "00", etc. @/
+/@ This is a slow, unsophisticated algorithm intended for use by
+   SHOW TRACE_BACK .../COUNT_STEPS @/
+/@ The caller must deallocate the returned string @/
+/@ The arguments must be strings that will not be freed by 'let()' @/
+vstring bigMul(vstring bignum1, vstring bignum2) {
+  long len2, p, p2;
+  char d2;
+  vstring_def(bignum3);
+  vstring_def(bigdprod);
+  vstring_def(bigpprod);
+  len2 = (long)strlen(bignum2);
+  for (p = 1; p <= len2; p++) {
+    p2 = len2 - p;
+    d2 = (char)(bignum2[p2] - '0');
+    if (d2 > 0) {
+      free_vstring(bigdprod);
+      bigdprod = bigMulDigit(bignum1, d2);
+      if (p > 1) {
+        /@ Shift the digit product by adding trailing 0s @/
+        let(&bigdprod, cat(bigdprod, string(p - 1, '0'), NULL));
+      }
+      free_vstring(bigpprod);
+      bigpprod = bigAdd(bignum3, bigdprod); /@ Accumulate partial product @/
+      let(&bignum3, bigpprod);
+    }
+  } /@ next p @/
+  free_vstring(bigdprod);
+  free_vstring(bigpprod);
+  return bignum3;
+}
+**** end commented out section added 12-Nov-2018 ***/
+
+
+/* Traces what statements require the use of a given statement */
+/* The output string must be deallocated by the user. */
+/* The return string [0] will be 'Y' or 'N' depending on whether there are any
+   statements that use statemNum.  Return string [i] will be 'Y' or 'N'
+   depending on whether g_Statement[i] uses statemNum.  All i will be populated
+   with 'Y'/'N' even if not $a or $p (always 'N' for non-$a,$p). */
+/* Optional 'cutoffStmt' parameter:  if nonzero, then
+   statements above cutoffStmt will not be scanned (for speedup) */
+vstring traceUsage(long statemNum,
+  flag recursiveFlag,
+  long cutoffStmt /* for speedup */) {
+
+  long lastPos, stmt, slen, pos;
+  flag tmpFlag;
+  vstring_def(statementUsedFlags); /* 'Y'/'N' flag that statement is used */
+  nmbrString_def(statementList);
+  nmbrString_def(proof);
+
+  /* For speed-up code */
+  char *fbPtr;
+  char *fbPtr2;
+  char zapSave;
+  flag notEFRec; /* Not ($e or $f or recursive) */
+
+  if (g_Statement[statemNum].type == e_ || g_Statement[statemNum].type == f_
+      || recursiveFlag) {
+    notEFRec = 0;
+  } else {
+    notEFRec = 1;
+  }
+
+  nmbrLet(&statementList, nmbrAddElement(statementList, statemNum));
+  lastPos = 1;
+
+  /* For speedup (in traceProofWork), scan only up to cutoffStmt if it
+     is specified, otherwise scan all statements. */
+  if (cutoffStmt == 0) cutoffStmt = g_statements;
+
+  /*for (stmt = statemNum + 1; stmt <= g_statements; stmt++) {*/ /* Scan all stmts*/
+  for (stmt = statemNum + 1; stmt <= cutoffStmt; stmt++) { /* Scan stmts*/
+    if (g_Statement[stmt].type != p_) continue; /* Ignore if not $p */
+
+    /* Speed up:  Do a character search for the statement label in the proof,
+       before parsing the proof.  Skip this if the label refers to a $e or $f
+       because these might not have their labels explicit in a compressed
+       proof.  Also, bypass speed up in case of recursive search. */
+    if (notEFRec) {
+      fbPtr = g_Statement[stmt].proofSectionPtr; /* Start of proof */
+      if (fbPtr[0] == 0) { /* The proof was never assigned */
+        continue; /* Don't bother */
+      }
+      fbPtr = fbPtr + whiteSpaceLen(fbPtr); /* Get past white space */
+      if (fbPtr[0] == '(') { /* "(" is flag for compressed proof */
+        fbPtr2 = fbPtr;
+        while (fbPtr2[0] != ')') {
+          fbPtr2++;
+          if (fbPtr2[0] == 0) bug(217); /* Didn't find closing ')' */
+        }
+      } else {
+        /* A non-compressed proof; use whole proof */
+        fbPtr2 = g_Statement[stmt].proofSectionPtr +
+            g_Statement[stmt].proofSectionLen;
+      }
+      zapSave = fbPtr2[0];
+      fbPtr2[0] = 0; /* Zap source for character string termination */
+      if (!instr(1, fbPtr, g_Statement[statemNum].labelName)) {
+        fbPtr2[0] = zapSave; /* Restore source buffer */
+        /* There is no string match for label in proof; don't bother to
+           parse. */
+        continue;
+      } else {
+        /* The label was found in the ASCII source.  Proceed with parse. */
+        fbPtr2[0] = zapSave; /* Restore source buffer */
+      }
+    } /* (End of speed-up code) */
+
+    /* Don't use bad proofs (incomplete proofs are ok) */
+    if (parseProof(stmt) > 1) {
+      /* The proof has an error, so use the empty proof */
+      nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
+    } else {
+      nmbrLet(&proof, g_WrkProof.proofString);
+    }
+
+    tmpFlag = 0;
+    for (pos = 0; pos < lastPos; pos++) {
+      if (nmbrElementIn(1, proof, statementList[pos])) {
+        tmpFlag = 1;
+        break;
+      }
+    }
+    if (!tmpFlag) continue;
+    /* The traced statement is used in this proof */
+    /* Add this statement to the statement list */
+    nmbrLet(&statementList, nmbrAddElement(statementList, stmt));
+    if (recursiveFlag) lastPos++;
+  } /* Next stmt */
+
+  slen = nmbrLen(statementList);
+
+  /* Prepare the output */
+  /* First, fill in the statementUsedFlags char array.  This allows us to sort
+     the output by statement number without calling a sort routine. */
+  let(&statementUsedFlags, string(g_statements + 1, 'N')); /* Init. to 'no' */
+  if (slen > 1) statementUsedFlags[0] = 'Y';  /* Used by at least one */
+  for (pos = 1; pos < slen; pos++) { /* Start with 1 (ignore traced statement)*/
+    stmt = statementList[pos];
+    if (stmt <= statemNum || g_Statement[stmt].type != p_ || stmt > g_statements)
+        bug(212);
+    statementUsedFlags[stmt] = 'Y';
+  }
+  return (statementUsedFlags);
+} /* traceUsage */
+
+
+
+/* This implements the READ command (although the / VERIFY qualifier is
+   processed separately in metamath.c). */
+void readInput(void)
+{
+  vstring_def(fullInput_fn);
+
+  let(&fullInput_fn, cat(g_rootDirectory, g_input_fn, NULL));
+
+  g_sourcePtr = readSourceAndIncludes(g_input_fn, &g_sourceLen);
+  if (g_sourcePtr == NULL) {
+    print2(
+"?Source was not read due to error(s).  Please correct and try again.\n");
+    goto RETURN_POINT;
+  }
+
+  g_sourcePtr = readRawSource(g_sourcePtr, &g_sourceLen);
+  parseKeywords();
+  parseLabels();
+  parseMathDecl();
+  parseStatements();
+  g_sourceHasBeenRead = 1;
+
+ RETURN_POINT:
+  free_vstring(fullInput_fn);
+
+} /* readInput */
+
+/* This function implements the WRITE SOURCE command. */
+/* Note that the labelSection, mathSection, and proofSection do not
+   contain keywords ($a, $p,...; $=; $.).  The keywords are added
+   by outputStatement. */
+void writeSource(
+  flag reformatFlag /* 1 = "/ FORMAT", 2 = "/REWRAP" */,
+  flag splitFlag,  /* /SPLIT - write out separate $[ $] includes */
+  flag noVersioningFlag, /* /NO_VERSIONING - no ~1 backup */
+  flag keepSplitsFlag, /* /KEEP_INCLUDES - don't delete included
+                        files when /SPIT is not specified */
+  vstring extractLabelList /* "" means /EXTRACT wasn't specified */
+  )
+{
+
+  /* Temporary variables and strings */
+  long i;
+  vstring_def(buffer);
+  vstring_def(fullOutput_fn);
+  FILE *fp;
+
+  let(&fullOutput_fn, cat(g_rootDirectory, g_output_fn, NULL));
+
+  if (splitFlag == 0 /* If 1, it will have message from writeSplitSource() */
+      && extractLabelList[0] == 0) {  /* If non-zero, it will have messages
+                     from writeExtractedSource() */
+    print2("Writing \"%s\"...\n", fullOutput_fn);
+  }
+
+  if (extractLabelList[0] != 0) {
+    writeExtractedSource(
+       extractLabelList, /* EXTRACT label list argument provided by user */
+       fullOutput_fn,
+       noVersioningFlag
+       );
+    /* All the writing was done by writeExtractedSource() so just return */
+    goto RETURN_POINT;
+  }
+
+  if (reformatFlag > 0) {
+    /* Now the outputSource function just reformats and puts the
+       source back into the g_Statement[] array.  So we don't need
+       to do it when we're not reformatting/wrapping */
+    /* TODO: turn this into a REWRAP command */
+    /* Process statements */
+    for (i = 1; i <= g_statements + 1; i++) {
+      free_vstring(buffer); /* Deallocate vstring */
+
+      buffer = outputStatement(i, reformatFlag);
+    } /* next i */
+  } /* if (reformatFlag > 0) */
+
+  /* Get put the g_Statement[] array into one linear buffer */
+  free_vstring(buffer);
+  buffer = writeSourceToBuffer();
+  if (splitFlag == 1) { /* Write includes as separate files */
+
+    /* Make sure we aren't overwriting one of the include files */
+    for (i = 1; i <= g_includeCalls; i++) {  /* Start at 1 to skip main file */
+      if (g_IncludeCall[i].pushOrPop == 0 /* Don't include pop back to main file */
+          && !strcmp(g_output_fn, g_IncludeCall[i].included_fn)) {
+        print2(
+"?The output was not written because the main output file name is\n");
+        print2(
+"  the same as an included file.  Use a different name.\n");
+        goto RETURN_POINT;
+      }
+    }
+
+    /* Note that writeSplitSource requires a file name without path since
+       it is called recursively for file inclusions where path is added */
+    writeSplitSource(&buffer, g_output_fn, noVersioningFlag, keepSplitsFlag);
+  } else {  /* Write a non-split version */
+    fp = fSafeOpen(fullOutput_fn, "w", noVersioningFlag);
+    if (fp == NULL) {
+      print2("?Error trying to write \"%s\".\n", fp);
+    } else {
+      fprintf(fp, "%s", buffer); /* Write the non-split output file */
+      fclose(fp);
+      if (keepSplitsFlag == 0) {
+        deleteSplits(&buffer, noVersioningFlag); /* Delete any old includes */
+      }
+    }
+  }
+
+  print2("%ld source statement(s) were written.\n", g_statements);
+
+
+ RETURN_POINT:
+  free_vstring(buffer); /* Deallocate vstring */
+  free_vstring(fullOutput_fn); /* Deallocate vstring */
+  return;
+} /* writeSource */
+
+
+/* Get info for WRITE SOURCE ... / EXTRACT */
+void writeExtractedSource(
+    vstring extractLabelList, /* EXTRACT argument provided by user */
+    vstring fullOutput_fn,
+    flag noVersioningFlag)
+{
+  vstring_def(statementUsedFlags); /* Y/N flags that statement is used */
+  long stmt, stmtj, scpStmt, strtScpStmt, endScpStmt, j, p1, p2, p3, p4;
+  vstring_def(extractNeeded);
+  nmbrString_def(unprovedList); /* Needed for traceProofWork()
+                                                 but not used */
+  nmbrString_def(mstring); /* Temporary holder for math string */
+  long maxStmt; /* The largest statement number (excluding $t) */
+  long hyp, hyps, mtkn, mtkns, dv, dvs;
+  long dollarTStmt; /* $t statement */
+  vstring_def(dollarTCmt); /* $t comment */
+  char zapChar; /* For finding $t statement */
+  char *tmpPtr; /* For finding $t statement */
+  vstring_def(hugeHdrNeeded); /* N/M/Y that output needs the huge header */
+  vstring_def(bigHdrNeeded);                              /* big */
+  vstring_def(smallHdrNeeded);                            /* small */
+  vstring_def(tinyHdrNeeded);                             /* tiny */
+  char hdrNeeded;
+  /* The following 8 are needed for getSectionHeadings() */
+  vstring_def(hugeHdr);
+  vstring_def(bigHdr);
+  vstring_def(smallHdr);
+  vstring_def(tinyHdr);
+  vstring_def(hugeHdrComment);
+  vstring_def(bigHdrComment);
+  vstring_def(smallHdrComment);
+  vstring_def(tinyHdrComment);
+
+  vstring_def(mathTokenDeclared);
+  vstring_def(undeclaredC);
+  vstring_def(undeclaredV);
+  long extractedStmts;
+  vstring_def(hdrSuffix);
+  FILE *fp;
+  vstring_def(buf);
+
+
+  /* Note that extractNeeded is 1-based to match 1-based
+     indices of the g_Statement array.  We also may need labelSection of entry
+     g_statements + 1 for text after last statement, which explains the +2. */
+  let(&extractNeeded, string(g_statements + 2, 'N'));
+
+  /* First, do the trace_backs for the statements in user's / EXTRACT argument */
+  /* By scanning backwards, we get a speedup by not having to trace
+     a statement twice, especially if we did "/ EXTRACT *" */
+  print2("Tracing back through proofs for $a and $p statements needed...\n");
+  if (!strcmp(extractLabelList, "*")) {
+    print2(
+       "   This may take up to 10 minutes.  (For audio alert when done,\n");
+    print2("   type ahead \"b\" then \"<enter>\".)\n");
+  }
+
+  for (stmt = g_statements; stmt >= 1; stmt--) {
+    if (extractNeeded[stmt] == 'Y') {
+      /* We've already traced this back, so skip it */
+      continue;
+    }
+    /* Wildcard matching */
+    if (!matchesList(g_Statement[stmt].labelName, extractLabelList, '*', '?'))
+      continue;
+    if (g_Statement[stmt].type == (char)a_) {
+      extractNeeded[stmt] = 'Y'; /* Add in axioms but don't trace */
+      continue;
+    }
+    if (g_Statement[stmt].type != (char)p_)
+      continue; /* Not a $p statement; skip it */
+    traceProofWork(stmt,
+        0, /*essentialFlag,*/
+        "", /*traceToList,*/ /* /TO argument of SHOW TRACE_BACK */
+        &statementUsedFlags,
+        &unprovedList);
+    if ((signed)(strlen(statementUsedFlags)) != g_statements + 1) bug(268);
+    /* OR in all the statements found by the trace */
+    for (stmtj = 1; stmtj <= stmt; stmtj++) {
+      if (statementUsedFlags[stmtj] == 'Y')
+        extractNeeded[stmtj] = 'Y';
+    }
+  } /* next stmt */
+
+  /* Next, we add in all necessary  ${ $} scoping statements */
+  print2("Determining which ${ and $} scoping statements are needed...\n");
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+    if (extractNeeded[stmt] == 'Y'
+        /* All flagged statements so far will be $a or $p */
+        /* Skip the forward $}'s that this loop populates (is it
+           necessary? */
+        && (g_Statement[stmt].type == a_ || g_Statement[stmt].type == p_)) {
+      scpStmt = stmt;
+      while (g_Statement[scpStmt].beginScopeStatementNum != 0) {
+        /* We're still in an inner scope */
+        strtScpStmt = g_Statement[scpStmt].beginScopeStatementNum;
+        if (g_Statement[strtScpStmt].type != lb_) bug(269);
+        if (extractNeeded[strtScpStmt] == 'Y')
+          /* We've already processed this ${ */
+          break;
+        endScpStmt = g_Statement[strtScpStmt].endScopeStatementNum;
+        if (g_Statement[endScpStmt].type != rb_) bug(270);
+        extractNeeded[strtScpStmt] = 'Y';
+        extractNeeded[endScpStmt] = 'Y';
+        scpStmt = strtScpStmt;
+      }
+    } /* if extraction needed */
+  } /* next stmt */
+
+  /* Next, we add in hypotheses and variable declarations for all $a's and $p's */
+  print2("Adding in $e and $f hypotheses and $d provisos...\n");
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+    if (extractNeeded[stmt] == 'Y'
+        /* All flagged statements so far will be $a or $p */
+        /* Skip the ${'s and $}'s that earlier loop populates (is it
+           necessary? */
+        && (g_Statement[stmt].type == a_ || g_Statement[stmt].type == p_)) {
+      hyps = g_Statement[stmt].numReqHyp;
+      for (hyp = 0; hyp < hyps; hyp++) {
+        extractNeeded[g_Statement[stmt].reqHypList[hyp]] = 'Y';
+      }
+      hyps = nmbrLen(g_Statement[stmt].optHypList);
+      for (hyp = 0; hyp < hyps; hyp++) {
+        extractNeeded[g_Statement[stmt].optHypList[hyp]] = 'Y';
+      }
+      mtkns = nmbrLen(g_Statement[stmt].reqVarList);
+      for (mtkn = 0; mtkn < mtkns; mtkn++) {
+        /* Flag the $v statement for a required variable */
+        /* (This may be redundant because of next stmt loop below) */
+        extractNeeded[g_MathToken[
+            (g_Statement[stmt].reqVarList)[mtkn]].statement] = 'Y';
+      }
+      mtkns = nmbrLen(g_Statement[stmt].optVarList);
+      for (mtkn = 0; mtkn < mtkns; mtkn++) {
+        /* Flag the $v statement for an optional variable */
+        extractNeeded[g_MathToken[
+            (g_Statement[stmt].optVarList)[mtkn]].statement] = 'Y';
+      }
+      dvs = nmbrLen(g_Statement[stmt].reqDisjVarsStmt);
+      for (dv = 0; dv < dvs; dv++) {
+        /* Flag the $d statement */
+        extractNeeded[(g_Statement[stmt].reqDisjVarsStmt)[dv]] = 'Y';
+      }
+      dvs = nmbrLen(g_Statement[stmt].optDisjVarsStmt);
+      for (dv = 0; dv < dvs; dv++) {
+        /* Flag the $d statement */
+        extractNeeded[(g_Statement[stmt].optDisjVarsStmt)[dv]] = 'Y';
+      }
+    } /* if extraction needed */
+  } /* next stmt */
+
+  /* Next, add in the $c, $v required by all statements */
+  print2("Determining which $c and $v statements are needed...\n");
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+    if (extractNeeded[stmt] == 'Y'
+        /* All $a, $p, $f, $e */
+        && (g_Statement[stmt].type == a_
+             || g_Statement[stmt].type == p_
+             || g_Statement[stmt].type == e_
+             || g_Statement[stmt].type == f_
+           )) {
+      nmbrLet(&mstring, g_Statement[stmt].mathString);
+      mtkns = g_Statement[stmt].mathStringLen;
+      for (mtkn = 0; mtkn < mtkns; mtkn++) {
+        /* Flag the $c or $v statement for the token */
+        extractNeeded[g_MathToken[mstring[mtkn]].statement] = 'Y';
+      }
+    } /* if extract needed */
+  } /* next stmt */
+
+  /* Get largest statement number (excluding $t comment) */
+  maxStmt = 0;
+  for (stmt = g_statements; stmt >= 1; stmt--) {
+    if (extractNeeded[stmt] == 'Y') {
+      maxStmt = stmt;
+      break;
+    }
+  }
+
+  /* Find the $t statement */  /* (Should this be done globally somewhere?) */
+  print2("Locating the $t statement if any...\n");
+  dollarTStmt = 0;
+  let(&dollarTCmt, "");
+  /* Note that g_Statement[g_statements + 1] is a special (empty) statement whose
+     labelSection holds any comment after the last statement.  It is possible
+     that the $t statement could be there. */
+  for (stmt = 1; stmt <= g_statements + 1; stmt++) {
+    /* We do low-level zapping in the xxx.mm input file buffer for speed */
+    tmpPtr = g_Statement[stmt].labelSectionPtr;
+    j = g_Statement[stmt].labelSectionLen;
+    zapChar = tmpPtr[j]; /* Save the original character */
+    tmpPtr[j] = 0; /* Create an end-of-string */
+    p1 = instr(1, tmpPtr, "$t");
+    if (p1 != 0) { /* Found the $t */
+      dollarTStmt = stmt;
+      /* Get the full $t comment */
+      p2 = instr(p1, tmpPtr, "$)");
+      let(&dollarTCmt, left(tmpPtr, p2 + 1));
+      /* We need the above because rinstr doesn't have starting arg */
+      p1 = rinstr(dollarTCmt, "$(");
+      /* Search backwards for non-space or beginning of string */
+      p1--;
+      while (p1 != 0) {
+        if (dollarTCmt[p1 - 1] != ' ') break;
+        p1--;
+      }
+      let(&dollarTCmt, cat("\n", seg(dollarTCmt, p1 + 1, p2 + 1), NULL));
+    }
+    tmpPtr[j] = zapChar; /* Restore the xxx.mm input file buffer */
+    if (dollarTStmt != 0) {
+      break; /* Found the $t, so no reason to continue */
+    }
+  }
+
+
+  /* Get header information about which headers to use */
+  print2("Analyzing scopes of section headings...\n");
+  let(&hugeHdrNeeded, string(g_statements + 2, 'N'));
+  let(&bigHdrNeeded, string(g_statements + 2, 'N'));
+  let(&smallHdrNeeded, string(g_statements + 2, 'N'));
+  let(&tinyHdrNeeded, string(g_statements + 2, 'N'));
+  /* Scan the database to determine which headers exist */
+  for (stmt = 1; stmt <= maxStmt; stmt++) {
+    getSectionHeadings(stmt, &hugeHdr, &bigHdr, &smallHdr,
+        &tinyHdr,
+        &hugeHdrComment, &bigHdrComment, &smallHdrComment,
+        &tinyHdrComment,
+        1, /* fineResolution */
+        1 /* fullComment */);
+    if (hugeHdr[0] != 0) hugeHdrNeeded[stmt] = '?'; /* Don't know yet */
+    if (bigHdr[0] != 0) bigHdrNeeded[stmt] = '?';
+    if (smallHdr[0] != 0) smallHdrNeeded[stmt] = '?';
+    if (tinyHdr[0] != 0) tinyHdrNeeded[stmt] = '?';
+  } /* next stmt */
+
+  /* For each tiny header, scan until next tiny, small, big, or huge header
+     is found (which means end of the tiny header's scope).  Set '?' to
+     'Y' if a used stmt is found along the way (meaning the header should
+     be in the output file) or to 'N' otherwise.  Then do the same starting
+     from small, then big, then huge header. */
+  for (stmt = 1; stmt <= maxStmt; stmt++) {
+    /* We do ALL statements, not just $a, $p, so that headers will go to
+       the right place in the output file.  We called getSectionHeadings()
+       with fineResolution=1 above so that "header area" will be 1 statement
+       rather than the multiple-statement content between successive $a/$p
+       statements. */
+    if (tinyHdrNeeded[stmt] == '?') {
+      hdrNeeded = 0;
+      for (stmtj = stmt; stmtj <= maxStmt; stmtj++) {
+        if (hugeHdrNeeded[stmtj] != 'N' || bigHdrNeeded[stmtj] != 'N'
+            || smallHdrNeeded[stmtj] != 'N' || tinyHdrNeeded[stmtj] != 'N') {
+          /* The scope of header at stmt has ended with no used statement
+             found, so header is not needed; abort the scan */
+          if (stmtj > stmt) break; /* Ignore starting point of scan since we
+              are looking for a later header to end the scope */
+        }
+        if (extractNeeded[stmtj] == 'Y') {
+          hdrNeeded = 1;
+          /* We now know the header is needed, so abort the scan */
+          break;
+        }
+      }
+      if (hdrNeeded == 1) {
+        tinyHdrNeeded[stmt] = 'Y';
+      } else {
+        tinyHdrNeeded[stmt] = 'N';
+      }
+    } /* if tinyHdrNeeded[stmt] == '?' */
+    if (smallHdrNeeded[stmt] == '?') {
+      hdrNeeded = 0;
+      for (stmtj = stmt; stmtj <= maxStmt; stmtj++) {
+        if (hugeHdrNeeded[stmtj] != 'N' || bigHdrNeeded[stmtj] != 'N'
+            || smallHdrNeeded[stmtj] != 'N') {
+          /* The scope of header at stmt has ended with no used statement
+             found, so header is not needed; abort the scan */
+          if (stmtj > stmt) break; /* Ignore starting point of scan since we
+              are looking for a later header to end the scope */
+        }
+        if (extractNeeded[stmtj] == 'Y') {
+          hdrNeeded = 1;
+          /* We now know the header is needed, so abort the scan */
+          break;
+        }
+      }
+      if (hdrNeeded == 1) {
+        smallHdrNeeded[stmt] = 'Y';
+      } else {
+        smallHdrNeeded[stmt] = 'N';
+      }
+    } /* if smallHdrNeeded[stmt] == '?' */
+    if (bigHdrNeeded[stmt] == '?') {
+      hdrNeeded = 0;
+      /*for (stmtj = stmt + 1; stmtj <= maxStmt; stmtj++) {*/
+      for (stmtj = stmt; stmtj <= maxStmt; stmtj++) {
+        if (hugeHdrNeeded[stmtj] != 'N' || bigHdrNeeded[stmtj] != 'N') {
+          /* The scope of header at stmt has ended with no used statement
+             found, so header is not needed; abort the scan */
+          if (stmtj > stmt) break;
+        }
+        if (extractNeeded[stmtj] == 'Y') {
+          hdrNeeded = 1;
+          /* We now know the header is needed, so abort the scan */
+          break;
+          /*if (stmtj > stmt) break;*/ /* Ignore starting point of scan since we
+              are looking for a later header to end the scope */
+        }
+      }
+      if (hdrNeeded == 1) {
+        bigHdrNeeded[stmt] = 'Y';
+      } else {
+        bigHdrNeeded[stmt] = 'N';
+      }
+    } /* if bigHdrNeeded[stmt] == '?' */
+    if (hugeHdrNeeded[stmt] == '?') {
+      hdrNeeded = 0;
+      for (stmtj = stmt; stmtj <= maxStmt; stmtj++) {
+        if (hugeHdrNeeded[stmtj] != 'N') {
+          /* The scope of header at stmt has ended with no used statement
+             found, so header is not needed; abort the scan */
+          if (stmtj > stmt) break; /* Ignore starting point of scan since we
+              are looking for a later header to end the scope */
+        }
+        if (extractNeeded[stmtj] == 'Y') {
+          hdrNeeded = 1;
+          /* We now know the header is needed, so abort the scan */
+          break;
+        }
+      }
+      if (hdrNeeded == 1) {
+        hugeHdrNeeded[stmt] = 'Y';
+      } else {
+        hugeHdrNeeded[stmt] = 'N';
+      }
+    } /* if hugeHdrNeeded[stmt] == '?' */
+  } /* next stmt */
+
+  /* Collect all $c and $v tokens that are not declared in the
+     extract, so they can be included in the output .mm to satisfy
+     the htmldefs and for use in ` math ` comment markup */
+  print2("Building $c and $v statements for unused math tokens...\n");
+  let(&mathTokenDeclared, string(g_mathTokens, 'N'));
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+    if (extractNeeded[stmt] == 'Y'
+       && (g_Statement[stmt].type == c_
+           || g_Statement[stmt].type == v_)) {
+      mtkns = g_Statement[stmt].mathStringLen;
+      for (mtkn = 0; mtkn < mtkns; mtkn++) {
+        /* Flag the math token as being declared */
+        mathTokenDeclared[(g_Statement[stmt].mathString)[mtkn]] = 'Y';
+      }
+    }
+  }
+  /* Build $c and $v statements for undeclared math tokens.  They are used to
+     make the database consistent with the htmldef's in the $t comment. */
+  let(&undeclaredC, "");
+  let(&undeclaredV, "");
+  for (mtkn = 0; mtkn < g_mathTokens; mtkn++) {
+    if (mathTokenDeclared[mtkn] == 'N') {
+      if (g_MathToken[mtkn].tokenType == con_) {
+        let(&undeclaredC, cat(undeclaredC, " ", g_MathToken[mtkn].tokenName,
+            NULL));
+      } else {
+        if (g_MathToken[mtkn].tokenType != var_) bug(271);
+        /* Before adding it to the unused var list, make sure it isn't declared
+           in another scope */
+        p1 = 0;
+        for (j = 0; j < g_mathTokens; j++) {
+          if (j == mtkn) continue;
+          if (!strcmp(g_MathToken[mtkn].tokenName, g_MathToken[j].tokenName)) {
+            /* See if it the $v was already declared in another scope */
+            if (mathTokenDeclared[j] == 'Y') {
+              p1 = 1;
+              break;
+            }
+          }
+        }
+        if (p1 == 0) {
+          let(&undeclaredV, cat(undeclaredV, " ", g_MathToken[mtkn].tokenName,
+            NULL));
+        }
+        /* Tag the variable as declared for use in later j loops above, so it
+           won't be added to the undeclaredV twice */
+        mathTokenDeclared[mtkn] = 'Y';
+      }
+    }
+  } /* next mtkn */
+
+  /* Write the output file */
+  /* (We don't call the standard output functions because there's too
+     much customization needed) */
+  print2("Creating the final output file \"%s\"...\n", fullOutput_fn);
+
+  fp = fSafeOpen(fullOutput_fn, "w", noVersioningFlag);
+  if (fp == NULL) {
+    print2("?Error trying to write \"%s\".\n", fp);
+    goto EXTRACT_RETURN;
+  }
+
+  /* Get the first line of the .mm file that normally has version and date */
+  /* (The memcpy to buf is not really necessary, just a safety
+     measure in case the input file has garbage with no "\n".) */
+  let(&buf, space(g_Statement[1].labelSectionLen));
+  memcpy(buf, g_Statement[1].labelSectionPtr,
+      (size_t)(g_Statement[1].labelSectionLen));
+  let(&buf, left(buf, instr(1, buf, "\n") - 1)); /* This will not include the \n */
+  let(&buf, right(edit(buf, 8/*leading space*/), 3)); /* Take off $( */
+  j = (long)strlen(" Extracted from: ");
+  if (!strcmp(left(buf, j), " Extracted from: ")) {
+    /* Prevent "Extracted from:" accumulation if sent through /EXTRACT again */
+    let(&buf, right(buf, j + 1));
+  }
+  fprintf(fp, "$( Extracted from: %s", buf);
+  if (instr(1, buf, "$)") == 0) fprintf(fp, " $)");
+  fprintf(fp,
+      "\n$( Created %s %s using \"READ '%s'\" then\n",
+      date(), time_(), g_input_fn);
+  fprintf(fp,
+      "   \"WRITE SOURCE '%s' /EXTRACT %s\" $)",
+      fullOutput_fn, extractLabelList);
+
+  extractedStmts = 0; /* How many statements were extracted */
+  for (stmt = 1; stmt <= g_statements + 1; stmt++) {
+    /* If the header is part of the labelSection of an extracted stmt,
+       we don't want to add a newline in order for extractions to be
+       stable (i.e. more lines aren't added when we extract from an
+       extraction). */
+    if (extractNeeded[stmt] == 'Y') {
+      let(&hdrSuffix, "");
+    } else {
+      let(&hdrSuffix, "\n");
+    }
+    /* Output headers if needed */
+    if (hugeHdrNeeded[stmt] == 'Y'
+        || bigHdrNeeded[stmt] == 'Y'
+        || smallHdrNeeded[stmt] == 'Y'
+        || tinyHdrNeeded[stmt] == 'Y') {
+      getSectionHeadings(stmt, &hugeHdr, &bigHdr, &smallHdr, &tinyHdr,
+          &hugeHdrComment, &bigHdrComment, &smallHdrComment,
+          &tinyHdrComment,
+          1, /*fineResolution*/
+          1 /*fullComment*/
+          );
+      freeTempAlloc();
+      if (hugeHdrNeeded[stmt] == 'Y') {
+        fixUndefinedLabels(extractNeeded, &hugeHdrComment);
+        fprintf(fp, "%s", cat(hugeHdr, hugeHdrComment, hdrSuffix, NULL));
+      }
+      if (bigHdrNeeded[stmt] == 'Y') {
+        fixUndefinedLabels(extractNeeded, &bigHdrComment);
+        fprintf(fp, "%s", cat(bigHdr, bigHdrComment, hdrSuffix, NULL));
+      }
+      if (smallHdrNeeded[stmt] == 'Y') {
+        fixUndefinedLabels(extractNeeded, &smallHdrComment);
+        fprintf(fp, "%s", cat(smallHdr, smallHdrComment, hdrSuffix, NULL));
+      }
+      if (tinyHdrNeeded[stmt] == 'Y') {
+        fixUndefinedLabels(extractNeeded, &tinyHdrComment);
+        fprintf(fp, "%s", cat(tinyHdr, tinyHdrComment, hdrSuffix, NULL));
+      }
+    } /* if header(s) needed */
+
+    /* Output $t statement if needed */
+    if (dollarTStmt == stmt) {
+      fprintf(fp, "\n%s", dollarTCmt);
+    }
+
+    /* Output statement if needed */
+    if (extractNeeded[stmt] == 'Y') {
+      free_vstring(buf);
+      buf = getDescriptionAndLabel(stmt);
+
+      fixUndefinedLabels(extractNeeded, &buf);
+
+      fprintf(fp, "%s", buf);
+      if (stmt == g_statements + 1) bug(272); /* Text below last statement
+           isn't (currently) used - do we need it? */
+      if (stmt != g_statements + 1) {
+        extractedStmts++; /* For final message */
+        fprintf(fp, "$%c", g_Statement[stmt].type);
+        if (g_Statement[stmt].type != lb_ && g_Statement[stmt].type != rb_) {
+          /* $v $c $d $e $f $a $p */
+          let(&buf, space(g_Statement[stmt].mathSectionLen));
+          memcpy(buf, g_Statement[stmt].mathSectionPtr,
+              (size_t)(g_Statement[stmt].mathSectionLen));
+          fprintf(fp, "%s", buf);
+          if (g_Statement[stmt].type != p_) {
+            fprintf(fp, "$.");
+/*D*//*fprintf(fp, "#%ld#",stmt);*/
+          } else {
+            /* $p */
+            fprintf(fp, "$=");
+            let(&buf, space(g_Statement[stmt].proofSectionLen));
+            memcpy(buf, g_Statement[stmt].proofSectionPtr,
+                (size_t)(g_Statement[stmt].proofSectionLen));
+            fprintf(fp, "%s$.", buf);
+          }
+        } /* if not ${ $} */
+        if (extractNeeded[stmt + 1] == 'N') {
+/*D*//*printf("added \\n stmt=%ld type=%c,%c\n",stmt+1,g_Statement[stmt].type,g_Statement[stmt+1].type);*/
+          /* Put a newline following end of statement since the next
+             statement's label section will be suppressed */
+          fprintf(fp, "\n");
+        }
+      } /* if (stmt != statements + 1) */
+
+    } /* if (extractNeeded[stmt] == 'Y') */
+  } /* next stmt */
+
+  /* Add in unused $c, $v at the end to satisfy htmldefs */
+  if (g_outputToString == 1) bug(273); /* Should be turned off here */
+  if (g_printString[0] != 0) bug(274);
+  g_outputToString = 1;
+  if (undeclaredC[0] != 0) {
+    print2("\n");
+    print2("  $( Unused constants to satisfy "
+      "the htmldef's in the $ t comment. $)\n");
+    printLongLine(cat("  $c", undeclaredC, " $.", NULL), "    ", " ");
+  }
+  if (undeclaredV[0] != 0) {
+    print2("\n");
+    print2("  $( Unused variables to satisfy "
+      "the htmldef's in the $ t comment. $)\n");
+    printLongLine(cat("  $v", undeclaredV, " $.", NULL), "    ", " ");
+  }
+  g_outputToString = 0;
+  if (g_printString[0] != 0) {
+    fprintf(fp, "%s", g_printString);
+    free_vstring(g_printString);
+  }
+
+  /* Write the non-split output file */
+  fclose(fp);
+  j = 0; p1 = 0; p2 = 0; p3 = 0; p4 = 0;
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+    if (extractNeeded[stmt] == 'Y') {
+      j++;
+      if (g_Statement[stmt].type == a_) {
+        p1++;
+        if (!strcmp("ax-", left(g_Statement[stmt].labelName, 3))) p3++;
+        if (!strcmp("df-", left(g_Statement[stmt].labelName, 3))) p4++;
+        freeTempAlloc(); /* Deallocate stack created by left() */
+      }
+      if (g_Statement[stmt].type == p_) p2++;
+    }
+  }
+  print2(
+"Extracted %ld statements incl. %ld $a (%ld \"ax-\", %ld \"df-\"), %ld $p.\n",
+      j, p1, p3, p4, p2);
+
+ EXTRACT_RETURN:
+  /* Deallocate */
+  free_vstring(extractNeeded);
+  free_vstring(statementUsedFlags);
+  free_nmbrString(unprovedList);
+  free_nmbrString(mstring);
+  free_vstring(dollarTCmt);
+  free_vstring(hugeHdrNeeded);
+  free_vstring(bigHdrNeeded);
+  free_vstring(smallHdrNeeded);
+  free_vstring(tinyHdrNeeded);
+  free_vstring(hugeHdr);   /* Deallocate memory */
+  free_vstring(bigHdr);   /* Deallocate memory */
+  free_vstring(smallHdr); /* Deallocate memory */
+  free_vstring(tinyHdr); /* Deallocate memory */
+  free_vstring(hugeHdrComment);   /* Deallocate memory */
+  free_vstring(bigHdrComment);   /* Deallocate memory */
+  free_vstring(smallHdrComment); /* Deallocate memory */
+  free_vstring(tinyHdrComment); /* Deallocate memory */
+  free_vstring(mathTokenDeclared);
+  free_vstring(undeclaredC);
+  free_vstring(undeclaredV);
+  free_vstring(buf);
+  return;
+} /* getExtractionInfo */
+
+
+/* Some labels in comments may not exist in statements extracted
+   with WRITE SOURCE ... / EXTRACT.  This function changes them
+   to external links to us.metamath.org. */
+void fixUndefinedLabels(vstring extractNeeded/*'Y'/'N' list*/,
+    vstring *buf/*header comment*/) {
+  long p1, p2, p3;
+  vstring_def(label);
+  vstring_def(newLabelWithTilde);
+  vstring_def(restOfComment);
+  int mathMode; /* char gives Wconversion gcc warning */
+#define ASCII_4 4
+
+  /* Change ~ in math symbols to non-printable ASCII 4 to prevent
+     interpretation as label indicator */
+  p1 = (long)strlen(*buf);
+  mathMode = 0;
+  for (p2 = 0; p2 < p1; p2++) {
+    if ((*buf)[p2] == '`') {
+      mathMode = 1 - mathMode;
+      continue;
+    }
+    if ((*buf)[p2] == '~' && mathMode == 1) {
+      (*buf)[p2] = ASCII_4;
+    }
+  }
+
+  p1 = 0;
+  let(&(*buf), cat(*buf, " \n", NULL)); /* Ensure white space after last label */
+  while (1) {
+    p1 = instr(p1 + 1, *buf, "~");
+    if (p1 == 0) break;
+    if (p1 - 2 >= 0) { /* Prevent out-of-bounds access */
+      if ((*buf)[p1 - 2] == '~') {
+        continue; /* it is a ~~ escape; don't process */
+      }
+    }
+    while (1) {
+      /* Get beyond any whitespace between ~ and label */
+      if ((*buf)[p1 + 1] == 0) break; /* end of string */
+      if ((*buf)[p1 + 1] != ' ' && (*buf)[p1 + 1] != '\n') {
+        /* Found a non-space character, assume it is start of label */
+        break;
+      }
+      p1++; /* Move past the whitespace */
+    }
+    if ((*buf)[p1 + 1] == 0) break; /* Ignore stray ~ at end of comment */
+    p2 = instr(p1 + 2, *buf, " ");
+    p3 = instr(p1 + 2, *buf, "\n");
+    if (p3 < p2) p2 = p3;  /* p2 is end of label */
+    let(&label, seg(*buf, p1 + 2, p2 - 1));
+    let(&restOfComment, right(*buf, p2));
+    p3 = lookupLabel(label);
+    if (p3 == -1) continue; /* Not a statement label (e.g. ~ http://...) */
+    if (extractNeeded[p3] == 'Y') continue; /* Label link won't be broken */
+    /* Change label to external link */
+    let(&newLabelWithTilde, cat(label,
+        "\n ~ http://us.metamath.org/mpeuni/",
+        label, ".html", NULL));
+    let(&(*buf), cat(left(*buf, p1 - 1), newLabelWithTilde, NULL));
+    /* Adjust pointer to go past the modified label */
+    p1 = p1 + (long)strlen(newLabelWithTilde)
+          - ((long)strlen(label) + 2/*for "~ "*/);
+    /* Put newline if not at end of line - assumes no trailing spaces in .mm! */
+    /* We do this to prevent too-long lines.  But if we're already at end
+       of line, we don't want to create a paragraph, so we don't add newline. */
+    if (restOfComment[0] == '\n') {
+      let(&(*buf), cat(*buf, restOfComment, NULL));
+    } else {
+      let(&(*buf), cat(*buf, "\n", restOfComment, NULL));
+    }
+  }
+  let(&(*buf), left(*buf, (long)strlen(*buf) - 2));
+       /* Take off the '\n' we added at beginning of this function */
+
+  /* Restore ASCII 4 to ~ */
+  p1 = (long)strlen(*buf);
+  for (p2 = 0; p2 < p1; p2++) {
+    if ((*buf)[p2] == ASCII_4) (*buf)[p2] = '~';
+  }
+
+  free_vstring(label); /* Deallocate */
+  free_vstring(newLabelWithTilde); /* Deallocate */
+  free_vstring(restOfComment); /* Deallocate */
+  return;
+} /* fixUndefinedLabels */
+
+
+void writeDict(void)
+{
+  print2("This function has not been implemented yet.\n");
+  return;
+} /* writeDict */
+
+/* Free up all memory space and initialize all variables */
+void eraseSource(void)    /* ERASE command */
+{
+  long i;
+  vstring_def(tmpStr);
+
+  /* Deallocate g_WrkProof structure if g_wrkProofMaxSize != 0 */
+  /* Assigned in parseProof() in mmpars.c */
+  if (g_wrkProofMaxSize) { /* It has been allocated */
+    free(g_WrkProof.tokenSrcPtrNmbr);
+    free(g_WrkProof.tokenSrcPtrPntr);
+    free(g_WrkProof.stepSrcPtrNmbr);
+    free(g_WrkProof.stepSrcPtrPntr);
+    free(g_WrkProof.localLabelFlag);
+    free(g_WrkProof.hypAndLocLabel);
+    free(g_WrkProof.localLabelPool);
+    poolFree(g_WrkProof.proofString);
+    free(g_WrkProof.mathStringPtrs);
+    free(g_WrkProof.RPNStack);
+    free(g_WrkProof.compressedPfLabelMap);
+    g_wrkProofMaxSize = 0;
+  }
+
+  if (g_statements == 0) {
+    /* Already called */
+    memFreePoolPurge(0);
+    return;
+  }
+
+  for (i = 0; i <= g_includeCalls; i++) {
+    free_vstring(g_IncludeCall[i].source_fn);
+    free_vstring(g_IncludeCall[i].included_fn);
+    free_vstring(g_IncludeCall[i].current_includeSource);
+  }
+  g_includeCalls = -1;
+
+  /* Deallocate the g_Statement[] array */
+  for (i = 1; i <= g_statements + 1; i++) { /* g_statements + 1 is a dummy statement
+                                          to hold source after last statement */
+
+    if (g_Statement[i].labelName[0]) free(g_Statement[i].labelName);
+    if (g_Statement[i].mathString != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].mathString);
+    if (g_Statement[i].proofString != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].proofString);
+    if (g_Statement[i].reqHypList != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].reqHypList);
+    if (g_Statement[i].optHypList != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].optHypList);
+    if (g_Statement[i].reqVarList != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].reqVarList);
+    if (g_Statement[i].optVarList != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].optVarList);
+    if (g_Statement[i].reqDisjVarsA != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].reqDisjVarsA);
+    if (g_Statement[i].reqDisjVarsB != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].reqDisjVarsB);
+    if (g_Statement[i].reqDisjVarsStmt != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].reqDisjVarsStmt);
+    if (g_Statement[i].optDisjVarsA != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].optDisjVarsA);
+    if (g_Statement[i].optDisjVarsB != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].optDisjVarsB);
+    if (g_Statement[i].optDisjVarsStmt != NULL_NMBRSTRING)
+        poolFree(g_Statement[i].optDisjVarsStmt);
+
+    if (g_Statement[i].labelSectionChanged == 1) {
+      /* Deallocate text before label if not original source */
+      free_vstring(g_Statement[i].labelSectionPtr);
+    }
+    if (g_Statement[i].mathSectionChanged == 1) {
+      /* Deallocate math symbol text if not original source */
+      free_vstring(g_Statement[i].mathSectionPtr);
+    }
+    if (g_Statement[i].proofSectionChanged == 1) {
+      /* Deallocate proof if not original source */
+      free_vstring(g_Statement[i].proofSectionPtr);
+    }
+
+  } /* Next i (statement) */
+
+  /* g_MathToken[g_mathTokens].tokenName is assigned in
+     parseMathDecl() by let().  eraseSource() should free every g_MathToken and
+     there are (g_mathTokens + g_dummyVars) tokens. */
+  for (i = 0; i <= g_mathTokens + g_dummyVars; i++) {
+    free_vstring(g_MathToken[i].tokenName);
+  }
+
+  memFreePoolPurge(0);
+  g_errorCount = 0;
+
+  free(g_Statement);
+  free(g_IncludeCall);  /* Will be initialized in initBigArrays */
+  free(g_MathToken);
+  g_dummyVars = 0; /* For Proof Assistant */
+  free(g_sourcePtr);
+  free(g_labelKey);
+  free(g_mathKey);
+  free(g_allLabelKeyBase);
+
+  /* Deallocate the texdef/htmldef storage */
+  eraseTexDefs();
+
+  g_extHtmlStmt = 0; /* May be used by a non-zero test; init to be safe */
+
+  /* Initialize and deallocate mathbox information */
+  g_mathboxStmt = 0; /* Used by a non-zero test in mmwtex.c to see if assigned */
+  free_nmbrString(g_mathboxStart);
+  free_nmbrString(g_mathboxEnd);
+  for (i = 1; i <= g_mathboxes; i++) {
+    free_vstring(*(vstring *)(&g_mathboxUser[i - 1]));
+  }
+  free_pntrString(g_mathboxUser);
+  g_mathboxes = 0;
+
+  /* Allocate big arrays */
+  initBigArrays();
+
+  /* 2-Oct-2017 nm Future possibility: add 'reset' parameter to unify() to clear
+     the 5 variables below */
+  g_bracketMatchInit = 0; /* Clear to force mmunif.c to scan $a's again */
+  g_minSubstLen = 1; /* Initialize to the default SET EMPTY_SUBSTITUTION OFF */
+  /* Clear g_firstConst to trigger clearing of g_lastConst and
+     g_oneConst in mmunif.c */
+  free_nmbrString(g_firstConst);
+  /* Clear these directly so they will be truly deallocated for valgrind */
+  free_nmbrString(g_lastConst);
+  free_nmbrString(g_oneConst);
+
+  getMarkupFlag(0, RESET); /* Erase the cached markup flag storage */
+
+  /* Erase the contributor markup cache */
+  free_vstring(tmpStr);
+  tmpStr = getContrib(0 /*stmt is ignored*/, GC_RESET);
+
+  /* getContrib uses g_statements (global var), so don't do this earlier */
+  g_statements = 0; /* getContrib uses g_statements for loop limit */
+
+} /* eraseSource */
+
+
+/* If verify = 0, parse the proofs only for gross error checking.
+   If verify = 1, do the full verification. */
+void verifyProofs(vstring labelMatch, flag verifyFlag) {
+  vstring_def(emptyProofList);
+  long i, k;
+  long lineLen = 0;
+  vstring_def(header);
+  flag errorFound;
+#ifdef CLOCKS_PER_SEC
+  clock_t clockStart;
+#endif
+
+#ifdef __WATCOMC__
+  vstring_def(tmpStr);
+#endif
+
+#ifdef VAXC
+  vstring_def(tmpStr);
+#endif
+
+#ifdef CLOCKS_PER_SEC
+  clockStart = clock();  /* Retrieve start time */
+#endif
+  if (!strcmp("*", labelMatch) && verifyFlag) {
+    /* Use status bar */
+    let(&header, "0 10%  20%  30%  40%  50%  60%  70%  80%  90% 100%");
+    print2("%s\n", header);
+    free_vstring(header);
+  }
+
+  errorFound = 0;
+  for (i = 1; i <= g_statements; i++) {
+    if (!strcmp("*", labelMatch) && verifyFlag) {
+      while (lineLen < (50 * i) / g_statements) {
+        print2(".");
+        lineLen++;
+      }
+    }
+
+    if (g_Statement[i].type != p_) continue;
+    if (!matchesList(g_Statement[i].labelName, labelMatch, '*', '?')) continue;
+    if (strcmp("*",labelMatch) && verifyFlag) {
+      /* If not *, print individual labels */
+      lineLen = lineLen + (long)strlen(g_Statement[i].labelName) + 1;
+      if (lineLen > 72) {
+        lineLen = (long)strlen(g_Statement[i].labelName) + 1;
+        print2("\n");
+      }
+      print2("%s ",g_Statement[i].labelName);
+    }
+
+    k = parseProof(i);
+    if (k >= 2) errorFound = 1;
+    if (k < 2) { /* $p with no error */
+      if (verifyFlag) {
+        if (verifyProof(i) >= 2) errorFound = 1;
+        cleanWrkProof(); /* Deallocate verifyProof storage */
+      }
+    }
+    if (k == 1) {
+      let(&emptyProofList, cat(emptyProofList, ", ", g_Statement[i].labelName,
+          NULL));
+    }
+  }
+  if (verifyFlag) {
+    print2("\n");
+  }
+
+  if (emptyProofList[0]) {
+    printLongLine(cat(
+        "Warning: The following $p statement(s) were not proved:  ",
+        right(emptyProofList,3), NULL)," ","  ");
+  }
+  if (!emptyProofList[0] && !errorFound && !strcmp("*", labelMatch)) {
+    if (verifyFlag) {
+#ifdef CLOCKS_PER_SEC
+      print2("All proofs in the database were verified in %1.2f s.\n",
+           (double)((1.0 * (double)(clock() - clockStart)) / CLOCKS_PER_SEC));
+#else
+      print2("All proofs in the database were verified.\n");
+#endif
+
+    } else {
+      print2("All proofs in the database passed the syntax-only check.\n");
+    }
+  }
+  free_vstring(emptyProofList); /* Deallocate */
+
+} /* verifyProofs */
+
+
+void verifyMarkup(vstring labelMatch,
+    flag dateCheck, /* 1 = check date consistency */
+    flag topDateCheck, /* 1 = check top date */
+    flag fileCheck, /* 1 = check external files (gifs and bib) */
+    flag underscoreCheck, /* 1 = check labels for "_" characters) */
+    flag mathboxCheck, /* 1 = check mathbox cross-references) */
+    flag verboseMode) /* 1 = more details */ {
+  flag f;
+  flag saveHtmlFlag, saveAltHtmlFlag;
+  flag errFound = 0;
+  long stmtNum, p1, p2, p3;
+  long flen, lnum, lstart; /* For line length check */
+
+  vstring_def(mmVersionDate); /* Version date at top of .mm file */
+  vstring_def(mostRecentDate); /* For entire .mm file */
+  long mostRecentStmt = 0; /* For error message */
+
+  /* For getSectionHeadings() call */
+  vstring_def(hugeHdr);
+  vstring_def(bigHdr);
+  vstring_def(smallHdr);
+  vstring_def(tinyHdr);
+  vstring_def(hugeHdrComment);
+  vstring_def(bigHdrComment);
+  vstring_def(smallHdrComment);
+  vstring_def(tinyHdrComment);
+
+  vstring_def(descr);
+  vstring_def(str1);
+  vstring_def(str2);
+
+  /* For mathbox check */
+  long mbox, pmbox, stmt, pstmt, plen, step;
+
+  nmbrString_def(proof);
+  vstring_def(dupCheck);
+
+  saveHtmlFlag = g_htmlFlag;  saveAltHtmlFlag = g_altHtmlFlag;
+
+  print2("Checking statement label conventions...\n");
+
+  /* Check that labels can create acceptable file names for web pages */
+  /* Detect Microsoft bugs reported by several users, when the
+     HTML output files are named "con.html" etc. */
+  /* If we want a standard error message underlining token, this could go
+     in mmpars.c */
+  /* From Microsoft's site:
+     "The following reserved words cannot be used as the name of a file:
+     CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7,
+     COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
+     Also, reserved words followed by an extension - for example,
+     NUL.tx7 - are invalid file names." */
+  /* Check for labels that will lead to illegal Microsoft file names for
+     Windows users.  Don't bother checking CLOCK$ since $ is already
+     illegal */
+  let(&str1, cat(
+     ",CON,PRN,AUX,NUL,COM1,COM2,COM3,COM4,COM5,COM6,COM7,",
+     "COM8,COM9,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,", NULL));
+  for (stmtNum = 1; stmtNum <= g_statements; stmtNum++) {
+    if (!matchesList(g_Statement[stmtNum].labelName, labelMatch, '*', '?')) {
+      continue;
+    }
+
+    /* Check labels for "_" characters */
+    /* See discussion in https://github.com/metamath/set.mm/pull/1691 */
+    if (underscoreCheck
+        && instr(1, g_Statement[stmtNum].labelName, "_") != 0) {
+      assignStmtFileAndLineNum(stmtNum);
+      printLongLine(cat("?Warning: In statement \"",
+          g_Statement[stmtNum].labelName, "\" at line ",
+          str((double)(g_Statement[stmtNum].lineNum)),
+          " in file \"", g_Statement[stmtNum].fileName,
+          "\".  Underscores in labels are not recommended per our conventions.  ",
+          "Use the / UNDERSCORE_SKIP ",
+          "qualifier to skip this check.",
+          NULL),
+          "    ", " ");
+      errFound = 1;
+    }
+
+    /* Only $a and $p can produce web pages, so check only them */
+    if (g_Statement[stmtNum].type != a_ && g_Statement[stmtNum].type != p_) {
+      continue;
+    }
+    let(&str2, cat(",", edit(g_Statement[stmtNum].labelName, 32/*uppercase*/),
+        ",", NULL));
+    if (instr(1, str1, str2) ||
+        /* mm*.html is reserved for mmtheorems.html, etc. */
+        !strcmp(",MM", left(str2, 3))) {
+      assignStmtFileAndLineNum(stmtNum);
+      printLongLine(cat("?Warning: In statement \"",
+          g_Statement[stmtNum].labelName, "\" at line ",
+          str((double)(g_Statement[stmtNum].lineNum)),
+          " in file \"", g_Statement[stmtNum].fileName,
+          "\".  To workaround a Microsoft operating system limitation, the",
+          " the following reserved words cannot be used for label names:",
+          " CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5,",
+          " COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6,",
+          " LPT7, LPT8, and LPT9.  Also, \"mm*.html\" is reserved for",
+          " Metamath file names.  Use another name for this label.", NULL),
+          "    ", " ");
+      errFound = 1;
+    }
+
+    /* Check that $a assertions start with "ax-" or "df-" */
+    if (g_Statement[stmtNum].type == (char)a_) {
+      if (!strcmp("|-", g_MathToken[
+          (g_Statement[stmtNum].mathString)[0]].tokenName)) {
+        let(&str1, left(g_Statement[stmtNum].labelName, 3));
+        if (strcmp("ax-", str1) && strcmp("df-", str1)) {
+          assignStmtFileAndLineNum(stmtNum);
+          printLongLine(cat("?Warning: In the $a statement \"",
+              g_Statement[stmtNum].labelName, "\" at line ",
+              str((double)(g_Statement[stmtNum].lineNum)),
+              " in file \"", g_Statement[stmtNum].fileName,
+              "\", the label does not start with \"ax-\" or \"df-\"",
+              " per our convention for axiomatic assertions (\"$a |- ...\").",
+              NULL), "    ", " ");
+          errFound = 1;
+        }
+      }
+    }
+
+
+  } /* next stmtNum */
+
+
+  /* Check for math tokens containing "@" or "?" */
+  /* Note:  g_MathToken[] is 0-based, not 1-based */
+  for (p1 = 0; p1 < g_mathTokens; p1++) {
+    if (strchr(g_MathToken[p1].tokenName, '@') != NULL) {
+      stmtNum = g_MathToken[p1].statement;
+      assignStmtFileAndLineNum(stmtNum);
+      printLongLine(cat("?Warning: The token \"",
+          g_MathToken[p1].tokenName,
+          "\" declared at line ",
+          str((double)(g_Statement[stmtNum].lineNum)),
+          " in file \"", g_Statement[stmtNum].fileName,
+          "\" has an \"@\" character, which is discouraged because ",
+          "\"@\" is traditionally used to replace \"$\" in commented-out ",
+          "database source code.",
+          NULL),
+          "    ", " ");
+      errFound = 1;
+    }
+    if (strchr(g_MathToken[p1].tokenName, '?') != NULL) {
+      stmtNum = g_MathToken[p1].statement;
+      assignStmtFileAndLineNum(stmtNum);
+      printLongLine(cat("?Warning: The token \"",
+          g_MathToken[p1].tokenName,
+          "\" declared at line ",
+          str((double)(g_Statement[stmtNum].lineNum)),
+          " in file \"", g_Statement[stmtNum].fileName,
+          "\" has a \"?\" character, which is discouraged because ",
+          "\"?\" is sometimes used as a math token search wildcard.",
+          NULL),
+          "    ", " ");
+      errFound = 1;
+    }
+  }
+
+
+  /* Check $a ax-* vs. $p ax* */
+  for (stmtNum = 1; stmtNum <= g_statements; stmtNum++) {
+    if (g_Statement[stmtNum].type != a_) {
+      continue;
+    }
+    if (!matchesList(g_Statement[stmtNum].labelName, labelMatch, '*', '?')) {
+      continue;
+    }
+
+    /* Look for "ax-*" axioms */
+    if (strcmp("ax-", left(g_Statement[stmtNum].labelName, 3))) {
+      freeTempAlloc(); /* Prevent string stack buildup/overflow in left() */
+      continue;
+    }
+
+    let(&str1, g_Statement[stmtNum].labelName);
+    /* Convert ax-XXX to axXXX */
+    let(&str2, cat(left(str1, 2), right(str1, 4), NULL));
+
+    p1 = lookupLabel(str2);
+    if (p1 == -1) continue;  /* There is no corresponding axXXX */
+
+    if (verboseMode == 1) {
+      print2("Comparing \"%s\" to \"%s\"...\n", str2, str1);
+    }
+
+
+    /* Compare statements */
+    if (nmbrEq(g_Statement[stmtNum].mathString,
+        g_Statement[p1].mathString) != 1) {
+      printLongLine(cat("?Warning: The assertions for statements \"",
+          g_Statement[stmtNum].labelName, "\" and \"",
+          g_Statement[p1].labelName, "\" are different.",
+          NULL), "  ", " ");
+      errFound = 1;
+      continue;
+    }
+
+    /* Compare number of mandatory hypotheses */
+    if (g_Statement[stmtNum].numReqHyp != g_Statement[p1].numReqHyp) {
+      printLongLine(cat("?Warning: Statement \"",
+          g_Statement[stmtNum].labelName, "\" has ",
+          str((double)(g_Statement[stmtNum].numReqHyp)),
+          " mandatory hypotheses but \"",
+          g_Statement[p1].labelName, "\" has ",
+          str((double)(g_Statement[p1].numReqHyp)), ".",
+          NULL), "  ", " ");
+      errFound = 1;
+      continue;
+    }
+
+    /* Compare mandatory distinct variables */
+    if (nmbrEq(g_Statement[stmtNum].reqDisjVarsA,
+        g_Statement[p1].reqDisjVarsA) != 1) {
+      printLongLine(cat(
+          "?Warning: The mandatory distinct variable pairs for statements \"",
+          g_Statement[stmtNum].labelName, "\" and \"",
+          g_Statement[p1].labelName,
+          "\" are different or have a different order.",
+          NULL), "  ", " ");
+      errFound = 1;
+      continue;
+    } else if (nmbrEq(g_Statement[stmtNum].reqDisjVarsB,
+        g_Statement[p1].reqDisjVarsB) != 1) {
+      printLongLine(cat(
+          "?Warning: The mandatory distinct variable pairs for statements \"",
+          g_Statement[stmtNum].labelName, "\" and \"",
+          g_Statement[p1].labelName,
+          "\" are different or have a different order.",
+          NULL), "  ", " ");
+      errFound = 1;
+      continue;
+    }
+
+    /* Compare mandatory hypotheses */
+    for (p2 = 0; p2 < g_Statement[stmtNum].numReqHyp; p2++) {
+      if (nmbrEq(g_Statement[(g_Statement[stmtNum].reqHypList)[p2]].mathString,
+          g_Statement[(g_Statement[p1].reqHypList)[p2]].mathString) != 1) {
+        printLongLine(cat("?Warning: The mandatory hypotheses of statements \"",
+            g_Statement[stmtNum].labelName, "\" and \"",
+            g_Statement[p1].labelName, "\" are different.",
+            NULL), "  ", " ");
+        errFound = 1;
+        break;
+      }
+    } /* next p2 */
+
+    for (p2 = 0; p2 < nmbrLen(g_Statement[stmtNum].reqDisjVarsA); p2++) {
+      if (nmbrEq(g_Statement[stmtNum].reqDisjVarsA,
+          g_Statement[p1].reqDisjVarsA) != 1) {
+        printLongLine(cat("?Warning: The mandatory hypotheses of statements \"",
+            g_Statement[stmtNum].labelName, "\" and \"",
+            g_Statement[p1].labelName, "\" are different.",
+            NULL), "  ", " ");
+        errFound = 1;
+        break;
+      }
+    } /* next p2 */
+  } /* next stmtNum */
+
+
+  /* Check line lengths */
+  free_vstring(str1); /* Prepare to use as pointer */
+  free_vstring(str2); /* Prepare to use as pointer */
+  if (g_statements >= 0) {
+    /* TODO - handle $[...$] */
+    /* g_includeCalls is always nonzero now - but check
+        anyway; line numbers may be off if there are >1 files. */
+    str1 = g_Statement[1].labelSectionPtr; /* Start of input file */
+    str2 = g_Statement[g_statements + 1].labelSectionPtr
+       + g_Statement[g_statements + 1].labelSectionLen; /* End of input file */
+        /* g_statements + 1 is dummy statement to hold text after last statement */
+    if (str2[0] != 0) bug(258); /* Should be end of (giant) string */
+    if (str2[-1] != '\n') bug(259); /* End of last line */
+    flen = str2 - str1;  /* Length of input file */
+    str2 = ""; /* Restore pointer for use as vstring */
+    lnum = 0; /* Line number */
+    lstart = 0; /* Line start */
+    for (p1 = 0; p1 < flen; p1++) {
+      if (str1[p1] == 0) {
+        bug(260); /* File shouldn't have nulls */
+      }
+      if (str1[p1] == '\n') {
+        /* End of a line found */
+        lnum++;
+
+        if (p1 > 0) { /* Not 1st character in file */
+          if (str1[p1 - 1] == ' ') {
+            printLongLine(cat("?Warning: Line number ",
+                str((double)lnum),
+                " ends with a space character, which is discouraged.",
+                NULL), "    ", " ");
+            errFound = 1;
+          }
+        }
+
+        if (p1 - lstart > g_screenWidth) { /* Normally 79; see mminou.c */
+          /* Put line in str2 for error message */
+          let(&str2, space(p1 - lstart));
+          memcpy(str2, str1 + lstart,
+            (size_t)(p1 - lstart));
+          printLongLine(cat("?Warning: Line number ",
+              str((double)lnum),
+              " has ",
+              str((double)(p1 - lstart)),
+              " characters (should have ",
+              str((double)g_screenWidth),
+              " or less):",
+              NULL), "    ", " ");
+          print2("    %s...\n", left(str2, g_screenWidth - 7));
+          errFound = 1;
+          free_vstring(str2); /* Deallocate string memory */
+        }
+        lstart = p1 + 1;
+      }
+
+      /* Check for tabs */
+      if (str1[p1] == '\t') {
+        printLongLine(cat("?Warning: Line number ",
+            str((double)lnum + 1),
+            " contains a tab, which is discouraged.",
+            NULL), "    ", " ");
+        errFound = 1;
+      }
+
+    } /* next p1 */
+    str1 = ""; /* Restore pointer for use as vstring */
+  } /* end of line length check - if (g_statements >= 1... */
+
+
+  /* Check $t comment content */
+
+  eraseTexDefs(); /* Force a reread regardless of previous mode */
+  /* Check latexdef statements */
+  print2("Checking latexdef, htmldef, althtmldef...\n");
+  g_htmlFlag = 0; /* 1 = HTML, not TeX */
+  g_altHtmlFlag = 0; /* 1 = Unicode, not GIFs (when g_htmlFlag = 1) */
+  f = readTexDefs(1/*errorsOnly*/,
+          fileCheck /* 1 = GIF file existence check */  );
+  if (f != 0) errFound = 1;
+  if (f != 2) {   /* We continue if no severe errors (warnings are ok) */
+    /* Check htmldef statements (reread since we've switched modes) */
+    g_htmlFlag = 1; /* 1 = HTML, not TeX */
+    g_altHtmlFlag = 0; /* 1 = Unicode, not GIFs (when g_htmlFlag = 1) */
+    f = readTexDefs(1/*errorsOnly*/,
+            fileCheck /* 1 = GIF file existence check */  );
+  }
+  if (f != 0) errFound = 1;
+  if (f != 2) {  /* We continue if no severe errors (warnings are ok) */
+    /* Check althtmldef statements (reread since we've switched modes) */
+    g_htmlFlag = 1; /* 1 = HTML, not TeX */
+    g_altHtmlFlag = 1; /* 1 = Unicode, not GIFs (when g_htmlFlag = 1) */
+    f = readTexDefs(1/*errorsOnly*/,
+            fileCheck /* 1 = GIF file existence check */  );
+  }
+  if (f != 0) errFound = 1;
+
+
+  /* Check date consistency and comment markup in all statements */
+  print2("Checking statement comments...\n");
+  let(&mostRecentDate, "");
+  for (stmtNum = 1; stmtNum <= g_statements; stmtNum++) {
+    if (g_Statement[stmtNum].type != a_ && g_Statement[stmtNum].type != p_) {
+      continue;
+    }
+    if (!matchesList(g_Statement[stmtNum].labelName, labelMatch, '*', '?')) {
+      continue;
+    }
+
+    /* Check the contributor */
+    free_vstring(str1);
+    str1 = getContrib(stmtNum, CONTRIBUTOR);
+    if (!strcmp(str1, DEFAULT_CONTRIBUTOR)) {
+      printLongLine(cat(
+          "?Warning: Contributor \"", DEFAULT_CONTRIBUTOR,
+          "\" should be updated in statement \"",
+          g_Statement[stmtNum].labelName, "\".", NULL), "    ", " ");
+      errFound = 1;
+    }
+    free_vstring(str1);
+    str1 = getContrib(stmtNum, REVISER);
+    if (!strcmp(str1, DEFAULT_CONTRIBUTOR)) {
+      printLongLine(cat(
+          "?Warning: Reviser \"", DEFAULT_CONTRIBUTOR,
+          "\" should be updated in statement \"",
+          g_Statement[stmtNum].labelName, "\".", NULL), "    ", " ");
+      errFound = 1;
+    }
+
+    if (dateCheck) {
+
+      /* Check date consistency of the statement */
+      /* Use the error-checking feature of getContrib() extractor */
+      free_vstring(str1);
+      str1 = getContrib(stmtNum, GC_ERROR_CHECK_PRINT); /* Returns P or F */
+      if (str1[0] == 'F') errFound = 1;
+      free_vstring(str1);
+      str1 = getContrib(stmtNum, MOST_RECENT_DATE);
+
+      /* Save most recent date in file - used to check Version date below */
+      if (compareDates(mostRecentDate, str1) == -1) {
+        let(&mostRecentDate, str1);
+        mostRecentStmt = stmtNum;
+      }
+
+    }
+
+    free_vstring(descr);
+    descr = getDescription(stmtNum);
+
+    /* Check comment markup of the statement */
+    g_showStatement /* global */ = stmtNum; /* For printTexComment */
+    g_texFilePtr /* global */  = NULL; /* Not used, but set to something */
+    /* Use the errors-only (no output) feature of printTexComment() */
+    f = printTexComment(descr,
+        0, /* 1 = htmlCenterFlag (irrelevant for this call) */
+        PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */
+        fileCheck);
+    if (f == 1) errFound = 1;
+
+    /* Check that $a has no "(Proof modification is discouraged.)" */
+    if (g_Statement[stmtNum].type == a_) {
+      if (getMarkupFlag(stmtNum, PROOF_DISCOURAGED) == 1) {
+        printLongLine(cat(
+            "?Warning: Statement \"", g_Statement[stmtNum].labelName,
+            "\" is a $a but has a \"(Proof modification is discouraged.)\" tag.",
+            NULL), "    ", " ");
+        errFound = 1;
+      }
+    }
+    /* Check that *OLD and *ALT have both discouragements */
+    /* See discussion at
+       https://groups.google.com/d/msg/metamath/NhPM9XNNh1E/otl0uskKBgAJ */
+    p1 = (long)strlen(g_Statement[stmtNum].labelName);
+    let(&str1, right(g_Statement[stmtNum].labelName, p1 - 2)); /* Last 3 chars. */
+    if (!strcmp(str1, "OLD") || !strcmp(str1, "ALT")) {
+      if (getMarkupFlag(stmtNum, PROOF_DISCOURAGED) != 1
+          && g_Statement[stmtNum].type == p_ /* Ignore $a's */
+          ) {
+        printLongLine(cat(
+            "?Warning: Statement \"", g_Statement[stmtNum].labelName,
+            "\" has suffix \"", str1,
+            "\" but has no \"(Proof modification is discouraged.)\" tag.",
+            NULL), "    ", " ");
+        errFound = 1;
+      }
+      if (getMarkupFlag(stmtNum, USAGE_DISCOURAGED) != 1) {
+        printLongLine(cat(
+            "?Warning: Statement \"", g_Statement[stmtNum].labelName,
+            "\" has suffix \"", str1,
+            "\" but has no \"(New usage is discouraged.)\" tag.",
+            NULL), "    ", " ");
+        errFound = 1;
+      }
+    }
+  } /* next stmtNum */
+
+  /* Check that the version date of the .mm file is g.e. all statement dates */
+  /* This code expects a version date at the top of the file such as:
+         $( set.mm - Version of 13-Dec-2016 $)
+     If we later want a different format, this code should be modified. */
+  if (topDateCheck) {
+    /* Get the top of the .mm file */
+    let(&str1, space(g_Statement[1].labelSectionLen));
+    memcpy(str1, g_Statement[1].labelSectionPtr,
+      (size_t)(g_Statement[1].labelSectionLen));
+    /* Find the version date */
+    p1 = instr(1, str1, "Version of ");
+    if (p1 == 0) {
+      printLongLine(cat(
+          "?Warning: There is no \"Version of \" comment at the top of the",
+          " file \"", g_input_fn, "\".", NULL), "    ", " ");
+      errFound = 1;
+    } else {
+      p2 = instr(p1 + 11, str1, " ");
+      let(&str2, seg(str1, p1 + 11, p2 - 1)); /* The date */
+      f = parseDate(str2, &p1, &p2, &p3);
+      if (f == 1) {
+        printLongLine(cat(
+            "?Warning: The Version date \"", str2, "\" at the top of file \"",
+            g_input_fn, "\" is not a valid date.", NULL), "    ", " ");
+        errFound = 1;
+      } else if (dateCheck && compareDates(mostRecentDate, str2) == 1) {
+        printLongLine(cat(
+            "?Warning: The \"Version of\" date ", str2,
+            " at the top of file \"",
+            g_input_fn,
+            "\" is less recent than the date ", mostRecentDate,
+            " in the description of statement \"",
+            g_Statement[mostRecentStmt].labelName, "\".", NULL), "    ", " ");
+        errFound = 1;
+      }
+    }
+  } /* if (topDateCheck) */
+
+
+  print2("Checking section header comments...\n");
+  for (stmtNum = 1; stmtNum <= g_statements; stmtNum++) {
+    if (g_Statement[stmtNum].type != a_ && g_Statement[stmtNum].type != p_) {
+      continue;
+    }
+    if (!matchesList(g_Statement[stmtNum].labelName, labelMatch, '*', '?')) {
+      continue;
+    }
+
+    free_vstring(hugeHdr);
+    free_vstring(bigHdr);
+    free_vstring(smallHdr);
+    free_vstring(tinyHdr);
+    free_vstring(hugeHdrComment);
+    free_vstring(bigHdrComment);
+    free_vstring(smallHdrComment);
+    free_vstring(tinyHdrComment);
+    f = getSectionHeadings(stmtNum, &hugeHdr, &bigHdr, &smallHdr,
+        &tinyHdr,
+        &hugeHdrComment, &bigHdrComment, &smallHdrComment,
+        &tinyHdrComment,
+        0, /* fineResolution */
+        0 /* fullComment */);
+    if (f != 0) errFound = 1;
+
+    g_showStatement /* global */ = stmtNum; /* For printTexComment() */
+    g_texFilePtr /* global */  = NULL; /* Not used, but set to something */
+
+    f = 0;
+    if (hugeHdrComment[0] != 0)
+      f = (char)(f + printTexComment(hugeHdrComment,
+          0, /* 1 = htmlCenterFlag (irrelevant for this call) */
+          PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */
+          fileCheck));
+    if (bigHdrComment[0] != 0)
+      f = (char)(f + printTexComment(bigHdrComment,
+          0, /* 1 = htmlCenterFlag (irrelevant for this call) */
+          PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */
+          fileCheck));
+    if (smallHdrComment[0] != 0)
+      f = (char)(f + printTexComment(smallHdrComment,
+          0, /* 1 = htmlCenterFlag (irrelevant for this call) */
+          PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */
+          fileCheck));
+    if (tinyHdrComment[0] != 0)
+      f = (char)(f + printTexComment(tinyHdrComment,
+          0, /* 1 = htmlCenterFlag (irrelevant for this call) */
+          PROCESS_EVERYTHING + ERRORS_ONLY, /* actionBits */
+          fileCheck));
+
+    if (f != 0) printf(
+"    (The warning above refers to a header above the referenced statement.)\n");
+    if (f != 0) errFound = 1;
+  } /* next stmtNum */
+
+
+  /* Use the errors-only (no output) feature of writeBibliography() */
+  print2("Checking bibliographic references...\n");
+  f = writeBibliography("mmbiblio.html",
+          labelMatch,
+          1,  /* 1 = no output, just warning msgs if any */
+          fileCheck); /* 1 = check external files (gifs and bib) */
+  if (f != 0) errFound = 1;
+
+  /* Check mathboxes for cross-references */
+  if (mathboxCheck) {
+    print2("Checking mathbox independence...\n");
+    assignMathboxInfo();  /* Populate global mathbox variables */
+    /* Scan proofs in mathboxes to see if earlier mathbox is referenced */
+    for (pmbox = 2; pmbox <= g_mathboxes; pmbox++) {
+      /* Note g_mathboxStart, etc. are 0-based */
+      for (pstmt = g_mathboxStart[pmbox - 1]; pstmt <= g_mathboxEnd[pmbox - 1];
+          pstmt++) {
+        if (g_Statement[pstmt].type != (char)p_)
+          continue; /* Not a $p statement; skip it */
+        /* Don't use bad proofs (incomplete proofs are ok) */
+        if (parseProof(pstmt) > 1) {
+          /* The proof has an error, so use the empty proof */
+          nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
+        } else {
+          nmbrLet(&proof, g_WrkProof.proofString);
+        }
+        plen = nmbrLen(proof);
+        for (step = 0; step < plen; step++) {
+          stmt = proof[step];
+          if (stmt < 0) continue; /* Local step or '?' step */
+          if (stmt == 0) bug(266);
+          if (stmt > g_mathboxStmt && stmt < g_mathboxStart[pmbox - 1]) {
+            /* A statement in another mathbox is referenced */
+
+            /* Eliminate duplicate error messages: */
+            let(&str1, cat(str((double)pstmt), "-", str((double)stmt), NULL));
+            if (lookup(str1, dupCheck) != 0) {
+              continue;
+            } else {
+              let(&dupCheck, cat(dupCheck,
+                  (dupCheck[0] == 0) ? "" : ",", str1, NULL)); /* Add to list */
+            }
+
+            mbox = getMathboxNum(stmt);
+            if (mbox == 0) bug(267);
+            if (verboseMode == 0) {
+              printLongLine(cat("?Warning: The proof of \"",
+                  g_Statement[pstmt].labelName,
+                  "\" in the mathbox for ", (vstring *)(g_mathboxUser[pmbox - 1]),
+                  " references \"", g_Statement[stmt].labelName,
+                  "\" in the mathbox for ", (vstring *)(g_mathboxUser[mbox - 1]),
+                  ".",
+                  NULL),
+                  "    ", " ");
+            } else {
+              /* Verbose output experiment */
+              assignStmtFileAndLineNum(stmt);
+              assignStmtFileAndLineNum(pstmt);
+              printLongLine(cat("?Warning: The proof of statement \"",
+                  g_Statement[pstmt].labelName,
+                  "\" in the mathbox for \"", (vstring *)(g_mathboxUser[pmbox - 1]),
+                  "\" at line ", str((double)(g_Statement[pstmt].lineNum)),
+                  " in file \"", g_Statement[pstmt].fileName,
+                  "\" references statement \"", g_Statement[stmt].labelName,
+                  "\" in the mathbox for \"", (vstring *)(g_mathboxUser[mbox - 1]),
+                  "\" at line ", str((double)(g_Statement[stmt].lineNum)),
+                  " in file \"", g_Statement[stmt].fileName,
+                  "\".  ",
+                  "(Use the / MATHBOX_SKIP qualifier to skip this check.)",
+                  NULL),
+                  "    ", " ");
+            }
+            /* g_errorCount++; */
+            errFound = 1;
+          } /* if stmt in another mathbox */
+        } /* next step */
+      } /* next pstmt */
+    } /* next pmbox */
+    /* Deallocate */
+    free_vstring(dupCheck);
+    free_nmbrString(proof);
+  }
+
+  if (errFound == 0) {
+    print2("No errors were found.\n");
+  }
+
+  g_htmlFlag = saveHtmlFlag;  g_altHtmlFlag = saveAltHtmlFlag;
+  /* Force reread to get current mode defaults for future user commands */
+  eraseTexDefs();
+
+  /* Deallocate string memory */
+  free_vstring(mostRecentDate);
+  free_vstring(mmVersionDate);
+  free_vstring(descr);
+  free_vstring(str1);
+  free_vstring(str2);
+  free_vstring(hugeHdr);
+  free_vstring(bigHdr);
+  free_vstring(smallHdr);
+  free_vstring(tinyHdr);
+  free_vstring(hugeHdrComment);
+  free_vstring(bigHdrComment);
+  free_vstring(smallHdrComment);
+  free_vstring(tinyHdrComment);
+  return;
+
+} /* verifyMarkup */
+
+
+
+/* Function to process markup in an arbitrary non-Metamath HTML file, treating
+   the file as a giant comment. */
+void processMarkup(vstring inputFileName, vstring outputFileName,
+    flag processCss, long actionBits) {
+  FILE *outputFilePtr;
+  vstring_def(inputFileContent);
+  long size;
+  long p;
+
+  /* Check that globals aren't in a weird state */
+  if (g_outputToString == 1 || g_printString[0] != 0) {
+    bug(265);
+  }
+
+  /* readTexDefs() rereads based on changed in g_htmlFlag, g_altHtmlFlag */
+  if (2/*error*/ == readTexDefs(0 /* 1 = check errors only */,
+      0 /* 1 = GIF file existence check */  )) {
+    goto PROCESS_MARKUP_RETURN; /* An error occurred */
+  }
+
+  print2("Reading \"%s\"...\n", inputFileName);
+
+  free_vstring(inputFileContent);
+  inputFileContent = readFileToString(inputFileName, 1/*verbose*/, &size);
+  if (inputFileContent == NULL) {
+    /* Couldn't open the file; error msg provided by readFileToString */
+    inputFileContent = ""; /* Restore to normal vstring to prevent seg fault */
+    goto PROCESS_MARKUP_RETURN;
+  }
+
+  print2("Creating \"%s\"...\n", outputFileName);
+
+  /* Insert CSS from .mm file before "</HEAD>" if it isn't already there */
+  if (processCss != 0 && instr(1, inputFileContent, g_htmlCSS) == 0) {
+    p = instr(1, edit(inputFileContent, 32/*uppercase*/), "</HEAD>");
+    if (p != 0) {
+      let(&inputFileContent, cat(left(inputFileContent, p - 1),
+          g_htmlCSS, "\n", right(inputFileContent, p), NULL));
+    }
+  }
+
+  /* fSafeOpen() renames existing files with ~1,~2,etc.  This way
+     existing user files will not be accidentally destroyed. */
+  outputFilePtr = fSafeOpen(outputFileName, "w", 0/*noVersioningFlag*/);
+  if (outputFilePtr == NULL) {
+    /* (Error msg already provided by fSafeOpen) */
+    /* print2("?Couldn't open \"%s\"\n", outputFileName); */
+    goto PROCESS_MARKUP_RETURN;
+  }
+
+  g_outputToString = 0;
+  free_vstring(g_printString);
+  g_showStatement = 0; /* For printTexComment */
+  g_texFilePtr = outputFilePtr; /* For printTexComment */
+  printTexComment(  /* Sends result to g_texFilePtr */
+      inputFileContent,
+      0, /* 1 = htmlCenterFlag */
+      actionBits, /* bit-mapped list of actions */
+      1 /* 1 = fileCheck */);
+  fclose(g_texFilePtr);
+  g_texFilePtr = NULL;
+
+ PROCESS_MARKUP_RETURN:
+  /* Deallocate */
+  free_vstring(inputFileContent);
+  free_vstring(g_printString);
+  return;
+}
+
+
+/* List "discouraged" statements with "(Proof modification is discouraged."
+   and "(New usage is discouraged.)" comment markup tags. */
+/* This function is primarily intended for use with the "travis" system
+   to identify versioning differences on GitHub. */
+void showDiscouraged(void) {
+  long stmt, s, usageCount;
+  long lowStmt = 0, highStmt = 0; /* For a slight speedup */
+  flag notQuitPrint = 1; /* Goes to 0 if user typed 'q' at scroll prompt */
+  vstring_def(str1);
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+
+    /* Since this command is slow, quit immediately if user typed 'q'
+       at scrolling prompt */
+    if (notQuitPrint == 0) break;
+
+    if (g_Statement[stmt].type != p_ && g_Statement[stmt].type != a_) continue;
+    if (getMarkupFlag(stmt, PROOF_DISCOURAGED) == 1
+        && g_Statement[stmt].type == p_ /* Ignore $a's */
+        ) {
+      /* Restricted proof */
+      /* Get number of steps */
+      parseProof(stmt);
+      notQuitPrint = print2(
+"SHOW DISCOURAGED:  Proof modification of \"%s\" is discouraged (%ld steps).\n",
+          g_Statement[stmt].labelName,
+          nmbrLen(g_WrkProof.proofString));
+    } /* if discouraged proof */
+    if (getMarkupFlag(stmt, USAGE_DISCOURAGED) == 1) {
+      /* Discouraged usage */
+      usageCount = 0;
+      free_vstring(str1);
+      str1 = traceUsage(stmt,
+          0, /* recursiveFlag */
+          0 /* cutoffStmt */);
+      if (str1[0] == 'Y') { /* Used by at least one */
+        /* str1[i] will be 'Y' if used by stmt */
+        lowStmt = g_statements;
+        highStmt = 0;
+        /* Scan all future statements in str1 Y/N list */
+        for (s = stmt + 1; s <= g_statements; s++) {
+          /* Scan the used-by map */
+          if (str1[s] != 'Y') continue;
+          usageCount++;
+          if (lowStmt > s) lowStmt = s;
+          if (highStmt < s) highStmt = s;
+        } /* Next s */
+      } /* if (str1[0] == 'Y') */
+      notQuitPrint = print2(
+"SHOW DISCOURAGED:  New usage of \"%s\" is discouraged (%ld uses).\n",
+          g_Statement[stmt].labelName,
+          usageCount);
+      if (str1[0] == 'Y') { /* Used by at least one */
+        /* str1[i] will be 'Y' if used by stmt */
+        /* Scan all future statements in str1 Y/N list */
+        for (s = lowStmt; s <= highStmt; s++) {
+          /* Scan the used-by map */
+          if (str1[s] != 'Y') continue;
+          notQuitPrint = print2(
+              "SHOW DISCOURAGED:  \"%s\" is used by \"%s\".\n",
+              g_Statement[stmt].labelName,
+              g_Statement[s].labelName);
+        } /* Next s */
+      } /* if (str1[0] == 'Y') */
+    } /* if discouraged usage */
+  } /* next stmt */
+  free_vstring(str1); /* Deallocate */
+} /* showDiscouraged */
+
+/* Take a relative step FIRST, LAST, +nn, -nn (relative to the unknown
+   essential steps) or ALL, and return the actual step for use by ASSIGN,
+   IMPROVE, REPLACE, LET (or 0 in case of ALL, used by IMPROVE).  In case
+   stepStr is an unsigned integer nn, it is assumed to already be an actual
+   step and is returned as is.  If format is illegal, -1 is returned.  */
+long getStepNum(vstring relStep, /* User's argument */
+   nmbrString *pfInProgress, /* g_ProofInProgress.proof */
+   flag allFlag /* 1 = "ALL" is permissible */)
+{
+  long pfLen, i, j, relStepVal, actualStepVal;
+  flag negFlag = 0;
+  nmbrString_def(essentialFlags);
+  vstring_def(relStepCaps);
+
+  let(&relStepCaps, edit(relStep, 32/*upper case*/));
+  pfLen = nmbrLen(pfInProgress); /* Proof length */
+  relStepVal = (long)(val(relStepCaps)); /* val() tolerates ill-formed numbers */
+
+  if (relStepVal >= 0 && !strcmp(relStepCaps, str((double)relStepVal))) {
+    /* User's argument is an unsigned positive integer */
+    actualStepVal = relStepVal;
+    if (actualStepVal > pfLen || actualStepVal < 1) {
+      print2("?The step must be in the range from 1 to %ld.\n", pfLen);
+      actualStepVal = -1;  /* Flag the error */
+    }
+    goto RETURN_POINT;  /* Already actual step; just return it */
+  } else if (!strcmp(relStepCaps, left("FIRST", (long)(strlen(relStepCaps))))) {
+    negFlag = 0; /* Scan forwards */
+    relStepVal = 0;
+  } else if (!strcmp(relStepCaps, left("LAST", (long)(strlen(relStepCaps))))) {
+    negFlag = 1; /* Scan backwards */
+    relStepVal = 0;
+  } else if (relStepCaps[0] == '+') {
+    negFlag = 0;
+    if (strcmp(right(relStepCaps, 2), str((double)relStepVal))) {
+      print2("?The characters after '+' are not a number.\n");
+      actualStepVal = -1; /* Error - not a number after the '+' */
+      goto RETURN_POINT;
+    }
+  } else if (relStepCaps[0] == '-') {
+    negFlag = 1;
+    if (strcmp(right(relStepCaps, 2), str((double)(- relStepVal)))) {
+      print2("?The characters after '-' are not a number.\n");
+      actualStepVal = -1; /* Error - not a number after the '-' */
+      goto RETURN_POINT;
+    }
+    relStepVal = - relStepVal;
+  } else if (!strcmp(relStepCaps, left("ALL", (long)(strlen(relStepCaps))))) {
+    if (!allFlag) {
+      /* ALL is illegal */
+      print2("?You must specify FIRST, LAST, nn, +nn, or -nn.\n");
+      actualStepVal = -1; /* Flag that there was an error */
+      goto RETURN_POINT;
+    }
+    actualStepVal = 0; /* 0 is special, meaning "ALL" */
+    goto RETURN_POINT;
+  } else {
+    if (allFlag) {
+      print2("?You must specify FIRST, LAST, nn, +nn, -nn, or ALL.\n");
+    } else {
+      print2("?You must specify FIRST, LAST, nn, +nn, or -nn.\n");
+    }
+    actualStepVal = -1; /* Flag that there was an error */
+    goto RETURN_POINT;
+  }
+
+  nmbrLet(&essentialFlags, nmbrGetEssential(pfInProgress));
+
+  /* Get the essential step flags */
+  actualStepVal = 0; /* Use zero as flag that step wasn't found */
+  if (negFlag) {
+    /* Scan proof backwards */
+    /* Count back 'relStepVal' unknown steps */
+    j = relStepVal + 1;
+    for (i = pfLen; i >= 1; i--) {
+      if (essentialFlags[i - 1]
+          && pfInProgress[i - 1] == -(long)'?') {
+        j--;
+        if (j == 0) {
+          /* Found it */
+          actualStepVal = i;
+          break;
+        }
+      }
+    } /* Next i */
+  } else {
+    /* Scan proof forwards */
+    /* Count forward 'relStepVal' unknown steps */
+    j = relStepVal + 1;
+    for (i = 1; i <= pfLen; i++) {
+      if (essentialFlags[i - 1]
+          && pfInProgress[i - 1] == -(long)'?') {
+        j--;
+        if (j == 0) {
+          /* Found it */
+          actualStepVal = i;
+          break;
+        }
+      }
+    } /* Next i */
+  }
+  if (actualStepVal == 0) {
+    if (relStepVal == 0) {
+      print2("?There are no unknown essential steps.\n");
+    } else {
+      print2("?There are not at least %ld unknown essential steps.\n",
+        relStepVal + 1);
+    }
+    actualStepVal = -1; /* Flag that there was an error */
+    goto RETURN_POINT;
+  }
+
+ RETURN_POINT:
+  /* Deallocate memory */
+  free_vstring(relStepCaps);
+  free_nmbrString(*&essentialFlags);
+
+  return actualStepVal;
+} /* getStepNum */
+
+
+/* Convert the actual step numbers of an unassigned step to the relative
+   -1, -2, etc. offset for SHOW NEW_PROOF ...  /UNKNOWN, to make it easier
+   for the user to ASSIGN the relative step number. A 0 is returned
+   for the last unknown step.  The step numbers of known steps are
+   unchanged.  */
+/* The caller must deallocate the returned nmbrString. */
+nmbrString *getRelStepNums(nmbrString *pfInProgress) {
+  nmbrString_def(essentialFlags);
+  nmbrString_def(relSteps);
+  long i, j, pfLen;
+
+  pfLen = nmbrLen(pfInProgress); /* Get proof length */
+  nmbrLet(&relSteps, nmbrSpace(pfLen));  /* Initialize */
+  nmbrLet(&essentialFlags, nmbrGetEssential(pfInProgress));
+  j = 0;  /* Negative offset (or 0 for last unknown step) */
+  for (i = pfLen; i >= 1; i--) {
+    if (essentialFlags[i - 1]
+        && pfInProgress[i - 1] == -(long)'?') {
+      relSteps[i - 1] = j;
+      j--; /* It's an essential unknown step; increment negative offset */
+    } else {
+      relSteps[i - 1] = i; /* Just keep the normal step number */
+    }
+  }
+
+  /* Deallocate memory */
+  free_nmbrString(*&essentialFlags);
+
+  return relSteps;
+} /* getRelStepNums */
+
+
+/* This procedure finds the next statement number whose label matches
+   stmtName.  Wildcards are allowed.  If uniqueFlag is 1,
+   there must be exactly one match, otherwise an error message is printed
+   and -1 is returned.  If uniqueFlag is 0, the next match is
+   returned, or -1 if there are no more matches.  No error messages are
+   printed when uniqueFlag is 0, except for the special case of
+   startStmt=1.  For use by PROVE, REPLACE, ASSIGN. */
+long getStatementNum(vstring stmtName, /* Possibly with wildcards */
+    long startStmt, /* Starting statement number (1 for full scan) */
+    long maxStmt, /* Matches must be LESS THAN this statement number */
+    flag aAllowed, /* 1 means $a is allowed */
+    flag pAllowed, /* 1 means $p is allowed */
+    flag eAllowed, /* 1 means $e is allowed */
+    flag fAllowed, /* 1 means $f is allowed */
+    flag efOnlyForMaxStmt, /* If 1, $e and $f must belong to maxStmt */
+    flag uniqueFlag) /* If 1, match must be unique */
+{
+  flag hasWildcard;
+  long matchesFound, matchStmt, matchStmt2, stmt;
+  char typ;
+  flag laterMatchFound = 0; /* For better error message */
+
+  hasWildcard = 0;
+  /* (Note strpbrk warning in mmpars.c) */
+  if (strpbrk(stmtName, "*?=~%#@,") != NULL) {
+    /* (See matches() function for processing of these)
+       "*" 0 or more char match
+       "?" 1 char match
+       "=" Most recent PROVE command statement
+       "~" Statement range
+       "%" List of modified statements
+       "#" Internal statement number
+       "@" Web page statement number
+       "," Comma-separated fields */
+    hasWildcard = 1; /* I.e. stmtName is not a simple label */
+  }
+  matchesFound = 0;
+  matchStmt = 1; /* Set to a legal value in case of bug */
+  matchStmt2 = 1; /* Set to a legal value in case of bug */
+
+  for (stmt = startStmt; stmt <= g_statements; stmt++) {
+
+    if (stmt >= maxStmt) {
+      if (matchesFound > 0) break; /* Normal exit when a match was found */
+      if (!uniqueFlag) break; /* We only want to scan up to maxStmt anyway */
+      /* Otherwise, we continue to see if there is a later match, for
+         error message purposes */
+    }
+
+    if (!g_Statement[stmt].labelName[0]) continue; /* No label */
+    typ = g_Statement[stmt].type;
+
+    if ((!aAllowed && typ == (char)a_)
+        ||(!pAllowed && typ == (char)p_)
+        ||(!eAllowed && typ == (char)e_)
+        ||(!fAllowed && typ == (char)f_)) {
+      continue; /* Statement type is not allowed */
+    }
+
+    if (hasWildcard) {
+      if (!matchesList(g_Statement[stmt].labelName, stmtName, '*', '?')) {
+        continue;
+      }
+    } else {
+      /* When hasWildcard = 0, this code is very inefficient - all we need to
+         do is call lookupLabel(stmtName) outside of the stmt loop and take
+         actions based on whether the label exists and its statement number
+         and type compared to what's expected.  However, from a user
+         perspective, the current code has no noticeable delay, so there's no
+         pressing need to improve it at this point. */
+      if (strcmp(stmtName, g_Statement[stmt].labelName)) {
+        continue;
+      }
+    }
+
+    if (efOnlyForMaxStmt) {
+      if (maxStmt > g_statements) bug(247); /* Don't set efOnlyForMaxStmt
+                                             in case of PROVE call */
+      /* If a $e or $f, it must be a hypothesis of the statement
+         being proved */
+      if (typ == (char)e_ || typ == (char)f_){
+        if (!nmbrElementIn(1, g_Statement[maxStmt].reqHypList, stmt) &&
+            !nmbrElementIn(1, g_Statement[maxStmt].optHypList, stmt))
+            continue;
+      }
+    }
+
+    if (stmt >= maxStmt) {
+      /* For error messages:
+         This signals that a later match (after the statement being
+         proved, in case of ASSIGN) exists, so the user is trying to
+         reference a future statement. */
+      laterMatchFound = 1;
+      break;
+    }
+
+    if (matchesFound == 0) {
+      /* This is the first match found; save it */
+      matchStmt = stmt;
+      /* If uniqueFlag is not set, we're done (don't need to check for
+         uniqueness) */
+    }
+    if (matchesFound == 1) {
+      /* This is the 2nd match found; save it for error message */
+      matchStmt2 = stmt;
+    }
+    matchesFound++;
+    if (!uniqueFlag) break; /* We are just getting the next match, so done */
+    if (!hasWildcard) break; /* Since there can only be 1 match, don't
+                                bother to continue */
+  }
+
+  if (matchesFound == 0) {
+    if (!uniqueFlag) {
+      if (startStmt == 1) {
+        /* For non-unique scan, print only if we started from beginning */
+        print2("?No statement label matches \"%s\".\n", stmtName);
+      }
+    } else if (aAllowed && pAllowed && eAllowed && fAllowed
+               && !efOnlyForMaxStmt) {
+      print2("?No statement label matches \"%s\".\n", stmtName);
+    } else if (!aAllowed && pAllowed && !eAllowed && !fAllowed) {
+      /* This is normally the PROVE command */
+      print2("?No $p statement label matches \"%s\".\n", stmtName);
+    } else if (!eAllowed && !fAllowed) {
+      /* This is normally for REPLACE */
+      if (!laterMatchFound) {
+        print2("?No $a or $p statement label matches \"%s\".\n",
+          stmtName);
+      } else {
+        print2("?You must specify a statement "
+          "that occurs earlier the one being proved.\n");
+      }
+    } else {
+      /* This is normally for ASSIGN */
+      if (!laterMatchFound) {
+        printLongLine(cat("?A statement label matching \"",
+            stmtName,
+            "\" was not found or is not a hypothesis of the statement ",
+            "being proved.", NULL), "", " ");
+      } else {
+        print2("?You must specify a statement "
+          "that occurs earlier the one being proved.\n");
+      }
+    }
+  } else if (matchesFound == 2) {
+    printLongLine(cat("?This command requires a unique label, but there are ",
+        " 2 matches for \"",
+        stmtName, "\":  \"", g_Statement[matchStmt].labelName,
+        "\" and \"", g_Statement[matchStmt2].labelName, "\".",
+        NULL), "", " ");
+  } else if (matchesFound > 2) {
+    printLongLine(cat("?This command requires a unique label, but there are ",
+        str((double)matchesFound), " (allowed) matches for \"",
+        stmtName, "\".  The first 2 are \"", g_Statement[matchStmt].labelName,
+        "\" and \"", g_Statement[matchStmt2].labelName, "\".",
+        "  Use SHOW LABELS \"", stmtName, "\" to see all non-$e matches.",
+        NULL), "", " ");
+  }
+  if (!uniqueFlag && matchesFound > 1) bug(248);
+  if (matchesFound != 1) matchStmt = -1; /* Error - no (unique) match */
+  return matchStmt;
+} /* getStatementNum */
+
+
+
+
+/* Called by help() - prints a help line */
+void H(vstring helpLine)
+{
+  if (g_printHelp) {
+    print2("%s\n", helpLine);
+  }
+} /* H */
+
+
+
+/******** The MIDI output algorithm is in this function, outputMidi(). ******/
+/*** Warning:  If you want to experiment with the MIDI output, please
+     confine changes to this function.  Changes to other code
+     in this file is not recommended. ***/
+
+void outputMidi(long plen, nmbrString *indentationLevels,
+  nmbrString *logicalFlags, vstring g_midiParameter, vstring statementLabel) {
+
+  /* The parameters have the following meanings.  You should treat them as
+     read-only input parameters and should not modify the contents of the
+     arrays or strings they point to.
+
+       plen = length of proof
+       indentationLevels[step] = indentation level in "show proof xxx /full"
+           where step varies from 0 to plen-1
+       logicalFlags[step] = 0 for formula-building step, 1 for logical step
+       g_midiParameter = string passed by user in "midi xxx /parameter <g_midiParameter>"
+       statementLabel = label of statement whose proof is being scanned */
+
+  /* This function is called when the user types "midi xxx /parameter
+     <g_midiParameter>".  The proof steps of theorem xxx are numbered successively
+     from 0 to plen-1.  The arrays indentationLevels[] and logicalFlags[]
+     have already been populated for you. */
+
+  /* The purpose of this function is to create an ASCII file called xxx.txt
+     that contains the ASCII format for a t2mf input file.  The mf2t package
+     is available at http://www.hitsquad.com/smm/programs/mf2t/download.shtml.
+     To convert xxx.txt to xxx.mid you type "t2mf xxx.txt xxx.mid". */
+
+  /* To experiment with this function, you can add your own local variables and
+     modify the algorithm as you see fit.  The algorithm is essentially
+     contained in the loop "for (step = 0; step < plen; step++)" and in the
+     initialization of the local variables that this loop uses.  No global
+     variables are used inside this function; the only data used is
+     that contained in the input parameters. */
+
+/* Larger TEMPO means faster speed */
+#define TEMPO 48
+/* The minimum and maximum notes for the dynamic range we allow: */
+/* (MIDI notes range from 1 to 127, but 28 to 103 seems reasonably pleasant) */
+/* MIDI note number 60 is middle C. */
+#define MIN_NOTE 28
+#define MAX_NOTE 103
+
+  /* Note: "flag" is just "char"; "vstring" is just "char *" - except
+     that by this program's convention vstring allocation is controlled
+     by string functions in mmvstr.c; and "nmbrString" is just "long *" */
+
+  long step; /* Proof step from 0 to plen-1 */
+  long midiKey; /* Current keyboard key to be output */
+  long midiNote; /* Current midi note to be output, mapped from midiKey */
+  long midiTime; /* Midi time stamp */
+  long midiPreviousFormulaStep; /* Note saved from previous step */
+  long midiPreviousLogicalStep; /* Note saved from previous step */
+  vstring_def(midiFileName); /* All vstrings MUST be initialized to ""! */
+  FILE *midiFilePtr; /* Output file pointer */
+  long midiBaseline; /* Baseline note */
+  long midiMaxIndent; /* Maximum indentation (to find dyn range of notes) */
+  long midiMinKey; /* Smallest keyboard key in output */
+  long midiMaxKey; /* Largest keyboard key in output */
+  long midiKeyInc; /* Keyboard key increment per proof indentation level */
+  flag midiSyncopate; /* 1 = syncopate the output */
+  flag midiHesitate; /* 1 = silence all repeated notes */
+  long midiTempo; /* larger = faster */
+  vstring_def(midiLocalParam); /* To manipulate user's parameter string */
+  vstring_def(tmpStr); /* Temporary string */
+#define ALLKEYSFLAG 1
+#define WHITEKEYSFLAG 2
+#define BLACKKEYSFLAG 3
+  flag keyboardType; /* ALLKEYSFLAG, WHITEKEYSFLAG, or BLACKKEYSFLAG */
+  long absMinKey; /* The smallest note we ever want */
+  long absMaxKey; /* The largest note we ever want */
+  long key2MidiMap[128]; /* Maps keyboard (possibility with black or
+                    white notes missing) to midi notes */
+  long keyboardOctave; /* The number of keys spanning an octave */
+  long i;
+
+  /*** Define the keyboard to midi maps ***/
+  /* The idea here is to map the proof step to pressing "keyboard" keys
+     on keyboards that have all keys, or only the white keys, or only the
+     black keys.   The default is all keys, the parameter b means black,
+     and the parameter w means white. */
+#define ALLKEYS 128
+#define WHITEKEYS 75
+#define BLACKKEYS 53
+  long allKeys[ALLKEYS] =
+      {  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,
+        12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,
+        24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,
+        36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
+        48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+        60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,
+        72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,
+        84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
+        96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107,
+       108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+       120, 121, 122, 123, 124, 125, 126, 127};
+  long whiteKeys[WHITEKEYS] =
+      {  0,   2,   4,   5,   7,   9,  11,
+        12,  14,  16,  17,  19,  21,  23,
+        24,  26,  28,  29,  31,  33,  35,
+        36,  38,  40,  41,  43,  45,  47,
+        48,  50,  52,  53,  55,  57,  59,
+        60,  62,  64,  65,  67,  69,  71,
+        72,  74,  76,  77,  79,  81,  83,
+        84,  86,  88,  89,  91,  93,  95,
+        96,  98, 100, 101, 103, 105, 107,
+       108, 110, 112, 113, 115, 117, 119,
+       120, 122, 124, 125, 127};
+  long blackKeys[BLACKKEYS] =
+      {  1,   3,   6,   8,  10,
+        13,  15,  18,  20,  22,
+        25,  27,  30,  32,  34,
+        37,  39,  42,  44,  46,
+        49,  51,  54,  56,  58,
+        61,  63,  66,  68,  70,
+        73,  75,  78,  80,  82,
+        85,  87,  90,  92,  94,
+        97,  99, 102, 104, 106,
+       109, 111, 114, 116, 118,
+       121, 123, 126};
+
+  /************* Initialization ***************/
+
+  midiTime = 0; /* MIDI time stamp */
+  midiPreviousFormulaStep = 0; /* Note in previous formula-building step */
+  midiPreviousLogicalStep = 0; /* Note in previous logical step */
+  midiFilePtr = NULL; /* Output file pointer */
+
+  /* Parse the parameter string passed by the user */
+  let(&midiLocalParam, edit(g_midiParameter, 32)); /* Convert to uppercase */
+
+  /* Set syncopation */
+  if (strchr(midiLocalParam, 'S') != NULL) {
+    midiSyncopate = 1; /* Syncopation */
+  } else {
+    midiSyncopate = 0; /* No syncopation */
+  }
+  /* Set halting character of syncopation (only has effect if
+     syncopation is on) */
+  if (strchr(midiLocalParam, 'H') != NULL) {
+    midiHesitate = 1; /* Silence all repeated fast notes */
+  } else {
+    midiHesitate = 0; /* Silence only every other one in a repeated sequence */
+  }
+  /* Set the tempo: 96=fast, 72=medium, 48=slow */
+  if (strchr(midiLocalParam, 'F') != NULL) {
+    midiTempo = 2 * TEMPO;  /* Fast */
+  } else {
+    if (strchr(midiLocalParam, 'M') != NULL) {
+      midiTempo = 3 * TEMPO / 2;  /* Medium */
+    } else {
+      midiTempo = TEMPO; /* Slow */
+    }
+  }
+  /* Get the keyboard type */
+  if (strchr(midiLocalParam, 'W') != NULL) {
+    keyboardType = WHITEKEYSFLAG;
+  } else {
+    if (strchr(midiLocalParam, 'B') != NULL) {
+      keyboardType = BLACKKEYSFLAG;
+    } else {
+      keyboardType = ALLKEYSFLAG;
+    }
+  }
+  /* Set the tempo: 96=fast, 48=slow */
+  if (strchr(midiLocalParam, 'I') != NULL) {
+    /* Do not skip any notes */
+    midiKeyInc = 1 + 1;
+  } else {
+    /* Allow an increment of up to 4 */
+    midiKeyInc = 4 + 1;
+  }
+
+  /* End of parsing user's parameter string */
+
+
+  /* Map keyboard key numbers to MIDI notes */
+  absMinKey = MIN_NOTE; /* Initialize for ALLKEYSFLAG case */
+  absMaxKey = MAX_NOTE;
+  keyboardOctave = 12; /* Keyboard keys per octave with no notes skipped */
+  switch (keyboardType) {
+    case ALLKEYSFLAG:
+      for (i = 0; i < 128; i++) key2MidiMap[i] = allKeys[i];
+     break;
+    case WHITEKEYSFLAG:
+      for (i = 0; i < WHITEKEYS; i++) key2MidiMap[i] = whiteKeys[i];
+      keyboardOctave = 7;
+      /* Get keyboard key for smallest midi note we want */
+      for (i = 0; i < WHITEKEYS; i++) {
+        if (key2MidiMap[i] >= absMinKey) {
+          absMinKey = i;
+          break;
+        }
+      }
+      /* Get keyboard key for largest midi note we want */
+      for (i = WHITEKEYS - 1; i >= 0; i--) {
+        if (key2MidiMap[i] <= absMinKey) {
+          absMinKey = i;
+          break;
+        }
+      }
+      /* Redundant array bound check for safety */
+      if (absMaxKey >= WHITEKEYS) absMaxKey = WHITEKEYS - 1;
+      if (absMinKey >= WHITEKEYS) absMinKey = WHITEKEYS - 1;
+      break;
+    case BLACKKEYSFLAG:
+      for (i = 0; i < BLACKKEYS; i++) key2MidiMap[i] = blackKeys[i];
+      keyboardOctave = 5;
+      /* Get keyboard key for smallest midi note we want */
+      for (i = 0; i < BLACKKEYS; i++) {
+        if (key2MidiMap[i] >= absMinKey) {
+          absMinKey = i;
+          break;
+        }
+      }
+      /* Get keyboard key for largest midi note we want */
+      for (i = BLACKKEYS - 1; i >= 0; i--) {
+        if (key2MidiMap[i] <= absMinKey) {
+          absMinKey = i;
+          break;
+        }
+      }
+      /* Redundant array bound check for safety */
+      if (absMaxKey >= BLACKKEYS) absMaxKey = BLACKKEYS - 1;
+      if (absMinKey >= BLACKKEYS) absMinKey = BLACKKEYS - 1;
+      break;
+  }
+
+  /* Get max indentation, so we can determine the scale factor
+     to make midi output fit within dynamic range */
+  midiMaxIndent = 0;
+  for (step = 0; step < plen; step++) {
+    if (indentationLevels[step] > midiMaxIndent)
+      midiMaxIndent = indentationLevels[step];
+  }
+
+  /* We will use integral note increments multiplied by the indentation
+     level.  We pick the largest possible, with a maximum of 4, so that the
+     midi output stays within the desired dynamic range.  If the proof has
+     *too* large a maximum indentation (not seen so far), the do loop below
+     will decrease the note increment to 0, so the MIDI output will just be a
+     flat sequence of repeating notes and therefore useless, but at least it
+     won't crash the MIDI converter.  */
+
+  /*midiKeyInc = 5;*/ /* Starting note increment, plus 1 */
+        /* (This is now set with the I parameter; see above) */
+  do { /* Decrement note incr until song will fit MIDI dyn range */
+    midiKeyInc--;
+    /* Compute the baseline note to which add the proof indentation
+      times the midiKeyInc.  The "12" is to allow for the shift
+      of one octave down of the sustained notes on "essential"
+      (i.e. logical, not formula-building) steps. */
+    midiBaseline = ((absMaxKey + absMinKey) / 2) -
+      (((midiMaxIndent * midiKeyInc) - keyboardOctave/*12*/) / 2);
+    midiMinKey = midiBaseline - keyboardOctave/*12*/;
+    midiMaxKey = midiBaseline + (midiMaxIndent * midiKeyInc);
+  } while ((midiMinKey < absMinKey || midiMaxKey > absMaxKey) &&
+      midiKeyInc > 0);
+
+  /* Open the output file */
+  let(&midiFileName, cat(g_Statement[g_showStatement].labelName,
+      ".txt", NULL)); /* Create file name from statement label */
+  print2("Creating MIDI source file \"%s\"...", midiFileName);
+
+  /* fSafeOpen() renames existing files with ~1,~2,etc.  This way
+     existing user files will not be accidentally destroyed. */
+  midiFilePtr = fSafeOpen(midiFileName, "w", 0/*noVersioningFlag*/);
+  if (midiFilePtr == NULL) {
+    print2("?Couldn't open %s\n", midiFileName);
+    goto midi_return;
+  }
+
+  /* Output the MIDI header */
+  fprintf(midiFilePtr, "MFile 0 1 %ld\n", midiTempo);
+  fprintf(midiFilePtr, "MTrk\n");
+
+  /* Create a string exactly 38 characters long for the Meta Text
+     label (I'm not sure why, but they are in the t2mf examples) */
+  let(&tmpStr, cat("Theorem ", statementLabel, " ", g_midiParameter,
+      space(30), NULL));
+  let(&tmpStr, left(tmpStr, 38));
+  fprintf(midiFilePtr, "0 Meta Text \"%s\"\n", tmpStr);
+  fprintf(midiFilePtr,
+      "0 Meta Copyright \"Released to Public Domain by N. Megill\"\n");
+
+  /************** Scan the proof ************************/
+
+  for (step = 0; step < plen; step++) {
+  /* Handle the processing that takes place at each proof step */
+    if (!logicalFlags[step]) {
+
+      /*** Process the higher fast notes for formula-building steps ***/
+      /* Get the "keyboard" key number */
+      midiKey = (midiKeyInc * indentationLevels[step]) + midiBaseline;
+      /* Redundant prevention of array bound violation */
+      if (midiKey < 0) midiKey = 0;
+      if (midiKey > absMaxKey) midiKey = absMaxKey;
+      /* Map "keyboard" key to MIDI note */
+      midiNote = key2MidiMap[midiKey];
+      if (midiPreviousFormulaStep != midiNote || !midiSyncopate) {
+        /* Turn note on at the current MIDI time stamp */
+        fprintf(midiFilePtr, "%ld On ch=2 n=%ld v=75\n", midiTime, midiNote);
+        /* Turn note off at the time stamp + 18 */
+        fprintf(midiFilePtr, "%ld On ch=2 n=%ld v=0\n", midiTime + 18,
+            midiNote);
+        midiPreviousFormulaStep = midiNote;
+      } else {
+        /* Skip turning on the note to give syncopation */
+        /* To prevent skipping two notes in a row, set last note
+           to 0 so next step it will not be skipped even if the
+           note still doesn't change; this makes the syncopation
+           more rhythmic */
+        if (!midiHesitate) {
+          midiPreviousFormulaStep = 0;
+        }
+      }
+      midiTime += 24; /* Add 24 to the MIDI time stamp */
+
+    } else {
+
+      /*** Process the deeper sustained notes for logical steps ***/
+      /* The idea here is to shift the note down 1 octave before
+         outputting it, so it is distinguished from formula-
+         building notes */
+      /* Get the "keyboard" key number */
+      midiKey = (midiKeyInc * indentationLevels[step]) + midiBaseline;
+      /* Redundant prevention of array bound violation */
+      if (midiKey < 0) midiKey = 0;
+      if (midiKey > absMaxKey) midiKey = absMaxKey;
+      /* Map "keyboard" key to MIDI note */
+      midiNote = key2MidiMap[midiKey];
+      midiNote = midiNote - 12; /* Down 1 octave */
+      if (midiNote < 0) midiNote = 0; /* For safety but should be redundant */
+      if (midiPreviousLogicalStep) { /* If 0, it's the first time */
+        /* Turn off the previous sustained note */
+        fprintf(midiFilePtr, "%ld On ch=1 n=%ld v=0\n", midiTime,
+            midiPreviousLogicalStep);
+      }
+      /* Turn on the new sustained note */
+      fprintf(midiFilePtr, "%ld On ch=1 n=%ld v=100\n", midiTime,
+          midiNote);
+      midiTime += 24; /* Add 24 to the MIDI time stamp */
+      midiPreviousLogicalStep = midiNote; /* Save for next step */
+
+    }
+  } /* next step */
+
+  /****************** Clean up and close output file ****************/
+
+  /* After the last step, do the file closing stuff */
+  /* Sustain the very last note a little longer - sounds better */
+  midiTime += 72;
+  fprintf(midiFilePtr, "%ld On ch=1 n=%ld v=0\n", midiTime,
+      midiPreviousLogicalStep);
+  /* Output the MIDI file trailer */
+  fprintf(midiFilePtr, "%ld Meta TrkEnd\n", midiTime);
+  fprintf(midiFilePtr, "TrkEnd\n");
+  fclose(midiFilePtr);
+  /* Inform the user the run time of the MIDI file */
+  print2(" length = %ld sec\n", (long)(midiTime / (2 * midiTempo)));
+
+ midi_return:
+  /* Important: all local vstrings must be deallocated to prevent
+     memory leakage */
+  free_vstring(midiFileName);
+  free_vstring(tmpStr);
+  free_vstring(midiLocalParam);
+} /* outputMidi */
diff --git a/src/mmcmds.h b/src/mmcmds.h
new file mode 100644
index 0000000..7d90365
--- /dev/null
+++ b/src/mmcmds.h
@@ -0,0 +1,151 @@
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMCMDS_H_
+#define METAMATH_MMCMDS_H_
+
+/*! \file */
+
+#include "mmvstr.h"
+#include "mmdata.h"
+
+/*! Type (i.e. print) a statement */
+void typeStatement(long statemNum,
+  flag briefFlag,
+  flag commentOnlyFlag,
+  flag texFlag,
+  flag htmlFlag);
+/*! Type (i.e. print) a proof */
+void typeProof(long statemNum,
+  flag pipFlag, /*!< Type proofInProgress instead of source file proof */
+  long startStep, long endStep,
+  long endIndent,
+  flag essentialFlag,
+  flag renumberFlag,
+  flag unknownFlag,
+  flag notUnifiedFlag,
+  flag reverseFlag,
+  flag noIndentFlag,
+  long startColumn,
+  flag skipRepeatedSteps,
+  flag texFlag,
+  flag htmlFlag);
+/*! Show details of step */
+void showDetailStep(long statemNum, long detailStep);
+/*! Summary of statements in proof */
+void proofStmtSumm(long statemNum, flag essentialFlag, flag texFlag);
+/*! Traces back the statements used by a proof, recursively. */
+flag traceProof(long statemNum,
+  flag essentialFlag,
+  flag axiomFlag,
+  vstring matchList,
+  vstring traceToList,
+  flag testOnlyFlag);
+void traceProofWork(long statemNum,
+  flag essentialFlag,
+  vstring traceToList,
+  vstring *statementUsedFlagsP, /*!< 'y'/'n' flag that statement is used */
+  nmbrString **unprovedListP);
+/*! Traces back the statements used by a proof, recursively, with tree display.*/
+void traceProofTree(long statemNum,
+  flag essentialFlag, long endIndent);
+void traceProofTreeRec(long statemNum,
+  flag essentialFlag, long endIndent, long recursDepth);
+/*! Counts the number of steps a completely exploded proof would require
+  (Recursive)
+  0 is returned if some assertions have incomplete proofs. */
+double countSteps(long statemNum, flag essentialFlag);
+/*! Traces what statements require the use of a given statement */
+vstring traceUsage(long statemNum,
+  flag recursiveFlag,
+  long cutoffStmt /* if nonzero, stop scan there */);
+vstring htmlDummyVars(long showStmt);
+vstring htmlAllowedSubst(long showStmt);
+
+void readInput(void);
+/*! WRITE SOURCE command */
+void writeSource(
+  flag reformatFlag /* 1 = "/ FORMAT", 2 = "/REWRAP" */,
+  flag splitFlag,  /* /SPLIT - write out separate $[ $] includes */
+  flag noVersioningFlag, /* /NO_VERSIONING - no ~1 backup */
+  flag keepSplitsFlag, /* /KEEP_SPLITS - don't delete included files
+                        when /SPIT is not specified */
+  vstring extractLabels); /* "" means write everything */
+
+/*! Get info for WRITE SOURCE ... / EXTRACT */
+void writeExtractedSource(vstring extractLabels, /* EXTRACT argument provided by user */
+  vstring fullOutput_fn, flag noVersioningFlag);
+
+void fixUndefinedLabels(vstring extractNeeded, vstring *buf);
+
+void writeDict(void);
+void eraseSource(void);
+void verifyProofs(vstring labelMatch, flag verifyFlag);
+
+
+/*! If checkFiles = 0, do not open external files.
+   If checkFiles = 1, check for presence of gifs and biblio file */
+void verifyMarkup(vstring labelMatch, flag dateCheck, flag topDateCheck,
+    flag fileCheck,
+    flag underscoreCheck,
+    flag mathboxCheck,
+    flag verboseMode);
+
+void processMarkup(vstring inputFileName, vstring outputFileName,
+    flag processCss, long actionBits);
+
+/*! List "discouraged" statements with "(Proof modification is discouraged."
+   and "(New usage is discouraged.)" comment markup tags. */
+void showDiscouraged(void);
+
+/*! Take a relative step FIRST, LAST, +nn, -nn (relative to the unknown
+   essential steps) or ALL, and return the actual step for use by ASSIGN,
+   IMPROVE, REPLACE, LET (or 0 in case of ALL, used by IMPROVE).  In case
+   stepStr is an unsigned integer nn, it is assumed to already be an actual
+   step and is returned as is.  If format is illegal, -1 is returned.  */
+long getStepNum(vstring relStep, /*!< User's argument */
+   nmbrString *pfInProgress, /*!< proofInProgress.proof */
+   flag allFlag /*!< 1 = "ALL" is permissible */);
+
+/*! Convert the actual step numbers of an unassigned step to the relative
+   -1, -2, etc. offset for SHOW NEW_PROOF ...  /UNKNOWN, to make it easier
+   for the user to ASSIGN the relative step number. A 0 is returned
+   for the last unknown step.  The step numbers of known steps are
+   unchanged.
+   The caller must deallocate the returned nmbrString. */
+nmbrString *getRelStepNums(nmbrString *pfInProgress);
+
+/*! This procedure finds the next statement number whose label matches
+   stmtName.  Wildcards are allowed.  If uniqueFlag is 1,
+   there must be exactly one match, otherwise an error message is printed,
+   and -1 is returned.  If uniqueFlag is 0, the next match is
+   returned, or -1 if there are no more matches.  No error messages are
+   printed when uniqueFlag is 0, except for the special case of
+   startStmt=1.  For use by PROVE, REPLACE, ASSIGN. */
+long getStatementNum(
+  vstring stmtName, /*!< Possibly with wildcards */
+  long startStmt, /*!< Starting statement number (1 for full scan) */
+  long maxStmt, /*!< Must be LESS THAN this statement number */
+  flag aAllowed, /*!< 1 means $a is allowed */
+  flag pAllowed, /*!< 1 means $p is allowed */
+  flag eAllowed, /*!< 1 means $e is allowed */
+  flag fAllowed, /*!< 1 means $f is allowed */
+  flag efOnlyForMaxStmt, /*!< If 1, $e and $f must belong to maxStmt */
+  flag uniqueFlag /*!< If 1, match must be unique */
+);
+
+/*! For HELP processing */
+extern flag g_printHelp;
+void H(vstring helpLine);
+
+/*! For MIDI files */
+extern flag g_midiFlag; /*!< Set to 1 if typeProof() is to output MIDI file */
+extern vstring g_midiParam; /*!< Parameter string for MIDI file */
+void outputMidi(long plen, nmbrString *indentationLevels,
+  nmbrString *logicalFlags, vstring g_midiParameter, vstring statementLabel);
+
+
+#endif /* METAMATH_MMCMDS_H_ */
diff --git a/mmdata.c b/src/mmdata.c
similarity index 74%
rename from mmdata.c
rename to src/mmdata.c
index 95d557c..33a7fd3 100644
--- a/mmdata.c
+++ b/src/mmdata.c
@@ -1,4169 +1,3981 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/*
-mmdata.c
-*/
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <time.h>
-#include <ctype.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mminou.h"
-#include "mmpars.h"
-#include "mmcmdl.h" /* Needed for g_logFileName */
-#include "mmpfas.h" /* Needed for g_proveStatement */
-#include "mmwtex.h" /* Needed for SMALL_DECORATION etc. */
-
-#include <limits.h>
-#include <setjmp.h>
-/*E*/long db=0,db0=0,db2=0,db3=0,db4=0,db5=0,db6=0,db7=0,db8=0,db9=0;
-flag g_listMode = 0; /* 0 = metamath, 1 = list utility */
-flag g_toolsMode = 0; /* In metamath: 0 = metamath, 1 = text tools utility */
-
-
-/* 4-May-2015 nm */
-/* For use by getMarkupFlag() */
-vstring g_proofDiscouragedMarkup = "";
-vstring g_usageDiscouragedMarkup = "";
-flag g_globalDiscouragement = 1; /* SET DISCOURAGEMENT ON */
-
-/* 14-May-2017 nm */
-vstring g_contributorName = "";
-
-/* Global variables related to current statement */
-int g_currentScope = 0;
-/*long beginScopeStatementNum = 0;*/
-
-long g_MAX_STATEMENTS = 1;
-long g_MAX_MATHTOKENS = 1;
-long g_MAX_INCLUDECALLS = 2; /* Must be at least 2 (the single-file case) !!!
-                         (A dummy extra top entry is used by parseKeywords().) */
-struct statement_struct *g_Statement = NULL;
-long *g_labelKey = NULL; /* 4-May-2017 Ari Ferrera - added "= NULL" */
-struct mathToken_struct *g_MathToken;
-long *g_mathKey = NULL;
-long g_statements = 0, labels = 0, g_mathTokens = 0;
-/*long maxMathTokenLength = 0;*/ /* 15-Aug-2020 nm Not used */
-
-struct includeCall_struct *g_IncludeCall = NULL; /* 4-May-2017 Ari Ferrera
-                                                            - added "= NULL" */
-long g_includeCalls = -1;  /* For eraseSouce() in mmcmds.c */
-
-char *g_sourcePtr = NULL; /* 4-May-2017 Ari Ferrera - added "= NULL" */
-long g_sourceLen;
-
-/* 18-Jan-05 nm The structs below, and several other places, were changed
-   from hard-coded byte lengths to 'sizeof's by Waldek Hebisch
-   (hebisch at math dot uni dot wroc dot pl) so this will work on the
-   AMD64. */
-
-/* Null numString */
-struct nullNmbrStruct g_NmbrNull = {-1, sizeof(long), sizeof(long), -1};
-
-/* Null ptrString */
-struct nullPntrStruct g_PntrNull = {-1, sizeof(long), sizeof(long), NULL};
-
-nmbrString *nmbrTempAlloc(long size);
-        /* nmbrString memory allocation/deallocation */
-void nmbrCpy(nmbrString *sout, nmbrString *sin);
-void nmbrNCpy(nmbrString *s, nmbrString *t, long n);
-
-pntrString *pntrTempAlloc(long size);
-        /* pntrString memory allocation/deallocation */
-void pntrCpy(pntrString *sout, pntrString *sin);
-void pntrNCpy(pntrString *s, pntrString *t, long n);
-
-vstring g_qsortKey; /* Used by qsortStringCmp; pointer only, do not deallocate */
-
-
-/* Memory pools are used to reduce the number of malloc and alloc calls that
-   allocate arrays (strings or nmbr/pntrStrings typically).   The "free" pool
-   contains previously allocated arrays that are no longer used but that we
-   have not freed yet.  A call to allocate a new array fetches one from here
-   first.   The "used"
-   pool contains arrays that are partially used; each array has some free space
-   at the end that can be deallocated if memory gets low.   Any array that is
-   totally used (no free space) is not in any pool. */
-/* Each pool array has 3 "hidden" long elements before it, used by these
-   procedures.
-     Element -1:  actual size (bytes) of array, excluding the 3 "hidden"
-       long elements.
-     Element -2:  allocated size.  If all elements are used, allocated = actual.
-     Element -3:  location of array in memUsedPool.  If -1, it means that
-       actual = allocated and storage in memUsedPool is therefore not nec.
-   The pointer to an array always points to element 0 (recast to right size).
-*/
-
-#define MEM_POOL_GROW 1000 /* Amount that a pool grows when it overflows. */
-/*??? Let user set this from menu. */
-long poolAbsoluteMax = /*2000000*/1000000; /* Pools will be purged when this is reached */
-long poolTotalFree = 0; /* Total amount of free space allocated in pool */
-/*E*/long i1,j1_,k1; /* 11-Sep-2009 nm Fix "built-in function 'j1'" warning */
-void **memUsedPool = NULL;
-long memUsedPoolSize = 0; /* Current # of partially filled arrays in use */
-long memUsedPoolMax = 0; /* Maximum # of entries in 'in use' table (grows
-                               as nec.) */
-void **memFreePool = NULL;
-long memFreePoolSize = 0; /* Current # of available, allocated arrays */
-long memFreePoolMax = 0; /* Maximum # of entries in 'free' table (grows
-                               as nec.) */
-
-/* poolFixedMalloc should be called when the allocated array will rarely be
-   changed; a malloc or realloc with no unused array bytes will be done. */
-void *poolFixedMalloc(long size /* bytes */)
-{
-  void *ptr;
-  void *ptr2;
-/*E*/ /* 11-Jul-2014 nm Don't call print2() if db9 is set, since it will */
-/*E*/ /* recursively call the pool stuff causing a crash.  I changed */
-/*E*/ /* 41 cases of print2() to printf() below to resolve this. */
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("a0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  if (!memFreePoolSize) { /* The pool is empty; we must allocate memory */
-    ptr = malloc( 3 * sizeof(long) + (size_t)size);
-    if (!ptr) outOfMemory(
-        cat("#25 (poolFixedMalloc ", str((double)size), ")", NULL));
-
-    ptr = (long *)ptr + 3;
-    ((long *)ptr)[-1] = size; /* Actual size */
-    ((long *)ptr)[-2] = size; /* Allocated size */
-    ((long *)ptr)[-3] = -1;  /* Location in memUsedPool (-1 = none) */
-    return (ptr);
-  } else {
-    memFreePoolSize--;
-    ptr = memFreePool[memFreePoolSize];
-    poolTotalFree = poolTotalFree - ((long *)ptr)[-2];
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("a: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    if (size <= ((long *)ptr)[-2]) { /* We have enough space already */
-      ptr2 = realloc( (long *)ptr - 3, 3 * sizeof(long) + (size_t)size);
-      /* Reallocation cannot fail, since we are shrinking space */
-      if (!ptr2) bug(1382);
-      ptr = ptr2;
-    } else { /* The pool's last entry is too small; free and allocate new */
-      free((long *)ptr - 3);
-      ptr = malloc( 3 * sizeof(long) + (size_t)size);
-    }
-    if (!ptr) {
-      /* Try freeing space */
-      print2("Memory is low.  Deallocating storage pool...\n");
-      memFreePoolPurge(0);
-      ptr = malloc( 3 * sizeof(long) + (size_t)size);
-      if (!ptr) outOfMemory(
-          cat("#26 (poolMalloc ", str((double)size), ")", NULL));
-                                            /* Nothing more can be done */
-    }
-    ptr = (long *)ptr + 3;
-    ((long *)ptr)[-1] = size; /* Actual size */
-    ((long *)ptr)[-2] = size; /* Allocated size */
-    ((long *)ptr)[-3] = -1;  /* Location in memUsedPool (-1 = none) */
-    return (ptr);
-  }
-}
-
-
-
-/* poolMalloc tries first to use an array in the memFreePool before actually
-   malloc'ing */
-void *poolMalloc(long size /* bytes */)
-{
-  void *ptr;
-  long memUsedPoolTmpMax;
-  void *memUsedPoolTmpPtr;
-
-  /* Check to see if the pool total exceeds max. */
-  if (poolTotalFree > poolAbsoluteMax) {
-    memFreePoolPurge(1);
-  }
-
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("b0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  if (!memFreePoolSize) { /* The pool is empty; we must allocate memory */
-    ptr = malloc( 3 * sizeof(long) + (size_t)size);
-    if (!ptr) {
-      outOfMemory(cat("#27 (poolMalloc ", str((double)size), ")", NULL));
-    }
-    ptr = (long *)ptr + 3;
-    ((long *)ptr)[-1] = size; /* Actual size */
-    ((long *)ptr)[-2] = size; /* Allocated size */
-    ((long *)ptr)[-3] = -1;  /* Location in memUsedPool (-1 = none) */
-    return (ptr);
-  } else {
-    memFreePoolSize--;
-    ptr = memFreePool[memFreePoolSize];
-    poolTotalFree = poolTotalFree - ((long *)ptr)[-2];
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("b: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    if (size <= ((long *)ptr)[-2]) { /* We have enough space already */
-      ((long *)ptr)[-1] = size; /* Actual size */
-      ((long *)ptr)[-3] = -1; /* Not in storage pool yet */
-    } else { /* We must reallocate */
-      free((long *)ptr - 3);
-      ptr = malloc( 3 * sizeof(long) + (size_t)size);
-      if (!ptr) {
-        /* Try freeing space */
-        print2("Memory is low.  Deallocating storage pool...\n");
-        memFreePoolPurge(0);
-        ptr = malloc( 3 * sizeof(long) + (size_t)size);
-        if (!ptr) outOfMemory(
-            cat("#28 (poolMalloc ", str((double)size), ")", NULL));
-                                              /* Nothing more can be done */
-      }
-      ptr = (long *)ptr + 3;
-      ((long *)ptr)[-1] = size; /* Actual size */
-      ((long *)ptr)[-2] = size; /* Allocated size */
-      ((long *)ptr)[-3] = -1;  /* Location in memUsedPool (-1 = none) */
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("bb: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-      return (ptr);
-    }
-  }
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("bc: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  if (((long *)ptr)[-1] == ((long *)ptr)[-2]) return (ptr);
-  /* Allocated and actual sizes are different, so add this array to used pool */
-  poolTotalFree = poolTotalFree + ((long *)ptr)[-2] - ((long *)ptr)[-1];
-  if (memUsedPoolSize >= memUsedPoolMax) { /* Increase size of used pool */
-    memUsedPoolTmpMax = memUsedPoolMax + MEM_POOL_GROW;
-/*E*/if(db9)printf("Growing used pool to %ld\n",memUsedPoolTmpMax);
-    if (!memUsedPoolMax) {
-      /* The program has just started; initialize */
-      memUsedPoolTmpPtr = malloc((size_t)memUsedPoolTmpMax
-          * sizeof(void *));
-      if (!memUsedPoolTmpPtr) bug(1303); /* Shouldn't have allocation problems
-                                                    when program first starts */
-    } else {
-      /* Normal reallocation */
-      memUsedPoolTmpPtr = realloc(memUsedPool,
-          (size_t)memUsedPoolTmpMax * sizeof(void *));
-    }
-    if (!memUsedPoolTmpPtr) {
-      outOfMemory(cat("#29 (poolMalloc ", str((double)memUsedPoolTmpMax), ")", NULL));
-    } else {
-      /* Reallocation successful */
-      memUsedPool = memUsedPoolTmpPtr;
-      memUsedPoolMax = memUsedPoolTmpMax;
-    }
-  }
-  memUsedPool[memUsedPoolSize] = ptr;
-  ((long *)ptr)[-3] = memUsedPoolSize;
-  memUsedPoolSize++;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("c: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  return (ptr);
-}
-
-/* poolFree puts freed up space in memFreePool. */
-void poolFree(void *ptr)
-{
-  void *ptr1;
-  long usedLoc;
-  long memFreePoolTmpMax;
-  void *memFreePoolTmpPtr;
-
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("c0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  /* First, see if the array is in memUsedPool; if so, remove it. */
-  usedLoc = ((long *)ptr)[-3];
-  if (usedLoc >= 0) { /* It is */
-    poolTotalFree = poolTotalFree - ((long *)ptr)[-2] + ((long *)ptr)[-1];
-    memUsedPoolSize--;
-
-    /* 11-Jul-2014 WL old code deleted */
-    /*
-    memUsedPool[usedLoc] = memUsedPool[memUsedPoolSize];
-    ptr1 = memUsedPool[usedLoc];
-    ((long @)ptr1)[-3] = usedLoc;
-/@E@/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("d: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    */
-    /* 11-Jul-2014 WL new code */
-    if (usedLoc < memUsedPoolSize) {
-      memUsedPool[usedLoc] = memUsedPool[memUsedPoolSize];
-      ptr1 = memUsedPool[usedLoc];
-      ((long *)ptr1)[-3] = usedLoc;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("d: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    }
-    /* end of 11-Jul-2014 WL new code */
-  }
-
-  /* Next, add the array to the memFreePool */
-  /* First, allocate more memFreePool pointer space if needed */
-  if (memFreePoolSize >= memFreePoolMax) { /* Increase size of free pool */
-    memFreePoolTmpMax = memFreePoolMax + MEM_POOL_GROW;
-/*E*/if(db9)printf("Growing free pool to %ld\n",memFreePoolTmpMax);
-    if (!memFreePoolMax) {
-      /* The program has just started; initialize */
-      memFreePoolTmpPtr = malloc((size_t)memFreePoolTmpMax
-          * sizeof(void *));
-      if (!memFreePoolTmpPtr) bug(1304); /* Shouldn't have allocation problems
-                                                    when program first starts */
-    } else {
-      /* Normal reallocation */
-      memFreePoolTmpPtr = realloc(memFreePool,
-          (size_t)memFreePoolTmpMax * sizeof(void *));
-    }
-    if (!memFreePoolTmpPtr) {
-/*E*/if(db9)printf("Realloc failed\n");
-      outOfMemory(cat("#30 (poolFree ", str((double)memFreePoolTmpMax), ")", NULL));
-    } else {
-      /* Reallocation successful */
-      memFreePool = memFreePoolTmpPtr;
-      memFreePoolMax = memFreePoolTmpMax;
-    }
-  }
-  /* Add the free array to the free pool */
-  memFreePool[memFreePoolSize] = ptr;
-  /* In theory, [-3] should never get referenced for an entry in the
-     memFreePool. However, here we make it a definite (illegal) value in
-     case it is referenced by code with a bug. */
-  ((long *)ptr)[-3] = -2;  /* 11-Jul-2014 WL */
-  memFreePoolSize++;
-  poolTotalFree = poolTotalFree + ((long *)ptr)[-2];
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("e: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  return;
-}
-
-
-/* addToUsedPool adds a (partially used) array to the memUsedPool */
-void addToUsedPool(void *ptr)
-{
-  long memUsedPoolTmpMax;
-  void *memUsedPoolTmpPtr;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("d0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  if (((long *)ptr)[-1] == ((long *)ptr)[-2]) bug(1305); /* No need to add it
-                                 when it's not partially used */
-  if (((long *)ptr)[-1] == ((long *)ptr)[-2]) return;
-  /* Allocated and actual sizes are different, so add this array to used pool */
-  if (memUsedPoolSize >= memUsedPoolMax) { /* Increase size of used pool */
-    memUsedPoolTmpMax = memUsedPoolMax + MEM_POOL_GROW;
-/*E*/if(db9)printf("1Growing used pool to %ld\n",memUsedPoolTmpMax);
-    if (!memUsedPoolMax) {
-      /* The program has just started; initialize */
-      memUsedPoolTmpPtr = malloc((size_t)memUsedPoolTmpMax
-          * sizeof(void *));
-      if (!memUsedPoolTmpPtr) bug(1362); /* Shouldn't have allocation problems
-                                                    when program first starts */
-    } else {
-      /* Normal reallocation */
-      memUsedPoolTmpPtr = realloc(memUsedPool, (size_t)memUsedPoolTmpMax
-          * sizeof(void *));
-    }
-    if (!memUsedPoolTmpPtr) {
-      outOfMemory("#31 (addToUsedPool)");
-    } else {
-      /* Reallocation successful */
-      memUsedPool = memUsedPoolTmpPtr;
-      memUsedPoolMax = memUsedPoolTmpMax;
-    }
-  }
-  memUsedPool[memUsedPoolSize] = ptr;
-  ((long *)ptr)[-3] = memUsedPoolSize;
-  memUsedPoolSize++;
-  poolTotalFree = poolTotalFree + ((long *)ptr)[-2] - ((long *)ptr)[-1];
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("f: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  return;
-}
-
-/* Free all arrays in the free pool. */
-void memFreePoolPurge(flag untilOK)
-{
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("e0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  while (memFreePoolSize) {
-    memFreePoolSize--;
-    /* Free an array */
-    poolTotalFree = poolTotalFree -
-        ((long *)(memFreePool[memFreePoolSize]))[-2];
-    free((long *)(memFreePool[memFreePoolSize]) - 3);
-    if (untilOK) {
-      /* If pool size is OK, return. */
-      if (poolTotalFree <= poolAbsoluteMax) return;
-    }
-  }
-  /* memFreePoolSize = 0 now. */
-  if (memFreePoolMax != MEM_POOL_GROW) {
-    /* Reduce size of pool pointer array to minimum growth increment. */
-    if (memFreePool) free(memFreePool); /* Only when starting program */
-    memFreePool = malloc(MEM_POOL_GROW
-        * sizeof(void *)); /* Allocate starting increment */
-    memFreePoolMax = MEM_POOL_GROW;
-  }
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("g: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  return;
-}
-
-
-/* Get statistics for SHOW MEMORY command */
-void getPoolStats(long *freeAlloc, long *usedAlloc, long *usedActual)
-{
-  long i;
-  *freeAlloc = 0;
-  *usedAlloc = 0;
-  *usedActual = 0;
-  for (i = 0; i < memFreePoolSize; i++) {
-    *freeAlloc = *freeAlloc + /*12 +*/ ((long *)(memFreePool[i]))[-2];
-  }
-  for (i = 0; i < memUsedPoolSize; i++) {
-    *usedActual = *usedActual + 12 + ((long *)(memUsedPool[i]))[-1];
-    *usedAlloc = *usedAlloc + ((long *)(memUsedPool[i]))[-2] -
-        ((long *)(memUsedPool[i]))[-1];
-  }
-/*E*/ if (!db9)print2("poolTotalFree %ld  alloc %ld\n", poolTotalFree, *freeAlloc +
-/*E*/   *usedAlloc);
-}
-
-
-
-void initBigArrays(void)
-{
-
-/*??? This should all become obsolete. */
-  g_Statement = malloc((size_t)g_MAX_STATEMENTS * sizeof(struct statement_struct));
-/*E*//*db=db+g_MAX_STATEMENTS * sizeof(struct statement_struct);*/
-  if (!g_Statement) {
-    print2("*** FATAL ***  Could not allocate g_Statement space\n");
-    bug(1363);
-    }
-  g_MathToken = malloc((size_t)g_MAX_MATHTOKENS * sizeof(struct mathToken_struct));
-/*E*//*db=db+g_MAX_MATHTOKENS * sizeof(struct mathToken_struct);*/
-  if (!g_MathToken) {
-    print2("*** FATAL ***  Could not allocate g_MathToken space\n");
-    bug(1364);
-    }
-  g_IncludeCall = malloc((size_t)g_MAX_INCLUDECALLS * sizeof(struct includeCall_struct));
-/*E*//*db=db+g_MAX_INCLUDECALLS * sizeof(struct includeCall_struct);*/
-  if (!g_IncludeCall) {
-    print2("*** FATAL ***  Could not allocate g_IncludeCall space\n");
-    bug(1365);
-    }
-}
-
-/* Find the number of free memory bytes */
-long getFreeSpace(long max)
-{
-  long i , j, k;
-  char *s;
-  i = 0;
-  j = max + 2;
-  while (i < j - 2) {
-    k = (i + j) / 2;
-    s = malloc((size_t)k);
-    if (s) {
-      free(s);
-      i = k;
-    } else {
-      j = k;
-    }
-  }
-  return (i);
-}
-
-/* Fatal memory allocation error */
-void outOfMemory(vstring msg)
-{
-  vstring tmpStr = "";
-  print2("*** FATAL ERROR:  Out of memory.\n");
-  print2("Internal identifier (for technical support):  %s\n",msg);
-  print2(
-"To solve this problem, remove some unnecessary statements or file\n");
-  print2(
-"inclusions to reduce the size of your input source.\n");
-  print2(
-"Monitor memory periodically with SHOW MEMORY.\n");
-#ifdef THINK_C
-  print2(
-"You may also increase the \"Application Memory Size\" under \"Get Info\"\n");
-  print2(
-"under \"File\" in the Finder after clicking once on the Metamath\n");
-  print2("application icon.\n");
-#endif
-  print2("\n");
-  print2("Press <return> to exit Metamath.\n");
-  tmpStr = cmdInput1("");
-  /* let(&tmpStr, ""); */
-  let(&tmpStr, left(tmpStr, 0)); /* Prevent "not used" compiler warning */
-  /* Close the log to make sure error log is saved */
-  if (g_logFileOpenFlag) {
-    fclose(g_logFilePtr);
-    g_logFileOpenFlag = 0;
-  }
-
-  exit(1);
-}
-
-
-/* 17-Nov-2015 nm Added abort, skip, ignore options */
-/* Bug check */
-void bug(int bugNum)
-{
-  vstring tmpStr = "";
-  flag oldMode;
-  long wrongAnswerCount = 0;
-  static flag mode = 0; /* 1 = run to next bug, 2 = continue and ignore bugs */
-
-  /* 10/10/02 */
-  flag saveOutputToString = g_outputToString;
-  g_outputToString = 0; /* Make sure we print to screen and not to string */
-
-  if (mode == 2) {
-    /* If user chose to ignore bugs, print brief info and return */
-    print2("?BUG CHECK:  *** DETECTED BUG %ld, IGNORING IT...\n", (long)bugNum);
-    return;
-  }
-
-  print2("?BUG CHECK:  *** DETECTED BUG %ld\n", (long)bugNum);
-  if (mode == 0) { /* Print detailed info for first bug */
-    print2("\n");
-    print2(
-  "To get technical support, please send Norm Megill (%salum.mit.edu) the\n",
-        "nm@");
-    print2(
-  "detailed command sequence or a command file that reproduces this bug,\n");
-    print2(
-  "along with the source file that was used.  See HELP LOG for help on\n");
-    print2(
-  "recording a session.  See HELP SUBMIT for help on command files.  Search\n");
-    print2(
-  "for \"bug(%ld)\" in the m*.c source code to find its origin.\n", bugNum);
-    /* 15-Oct-2019 nm Added the next 2 info lines */
-    print2(
-  "If earlier errors were reported, try fixing them first, because they\n");
-    print2(
-  "may occasionally lead to false bug detection\n");
-    print2("\n");
-  }
-
-  let(&tmpStr, "?");
-  while (strcmp(tmpStr, "A") && strcmp(tmpStr, "a")
-      && strcmp(tmpStr, "S") && strcmp(tmpStr, "s")
-      && strcmp(tmpStr, "I") && strcmp(tmpStr, "i")
-      /* The above is actually useless because of break below, but we'll leave
-         it in case we want to re-ask after wrong answers in the future */
-      ) {
-    if (wrongAnswerCount > 6) {
-      print2(
-"Too many wrong answers; program will be aborted to exit scripting loops.\n");
-      break; /* Added 8-Nov-03 */
-    }
-    if (wrongAnswerCount > 0) {
-      let(&tmpStr, "");
-      tmpStr = cmdInput1("Please answer I, S, or A:  ");
-    } else {
-      print2(
- "Press S <return> to step to next bug, I <return> to ignore further bugs,\n");
-      let(&tmpStr, "");
-      tmpStr = cmdInput1("or A <return> to abort program:  ");
-    }
-    /******* 8-Nov-03 This loop caused an infinite loop in a cron job when bug
-      detection was triggered.  Now, when the loop breaks above,
-      the program will abort. *******/
-    wrongAnswerCount++;
-  }
-  oldMode = mode;
-  mode = 0;
-  if (!strcmp(tmpStr, "S") || !strcmp(tmpStr, "s")) mode = 1; /* Skip to next bug */
-  if (!strcmp(tmpStr, "I") || !strcmp(tmpStr, "i")) mode = 2; /* Ignore bugs */
-  if (oldMode == 0 && mode > 0) {
-    /* Print dire warning after the first bug only */
-    print2("\n");
-    print2(
-    "Warning!!!  A bug was detected, but you are continuing anyway.\n");
-    print2(
-    "The program may be corrupted, so you are proceeding at your own risk.\n");
-    print2("\n");
-    let(&tmpStr, "");
-  }
-  if (mode > 0) {
-    /* 10/10/02 */
-    g_outputToString = saveOutputToString; /* Restore for continuation */
-    return;
-  }
-  let(&tmpStr, "");
-#ifdef THINK_C
-  cmdInput1("Program has crashed.  Press <return> to leave.");
-#endif
-
-  print2("\n");
-  /* Close the log to make sure error log is saved */
-  if (g_logFileOpenFlag) {
-    print2("The log file \"%s\" was closed %s %s.\n", g_logFileName,
-        date(), time_());
-    fclose(g_logFilePtr);
-    g_logFileOpenFlag = 0;
-  }
-  print2("The program was aborted.\n");
-  exit(1); /* Use 1 instead of 0 to flag abnormal termination to scripts */
-}
-
-
-#define M_MAX_ALLOC_STACK 100
-
-/* 26-Apr-2008 nm Added */
-/* This function returns a 1 if any entry in a comma-separated list
-   matches using the matches() function. */
-flag matchesList(vstring testString, vstring pattern, char wildCard,
-    char oneCharWildCard) {
-  long entries, i;
-  flag matchVal = 0;
-  vstring entryPattern = "";
-
-  /* Done so we can use string functions like left() in call arguments */
-  long saveTempAllocStack;
-  saveTempAllocStack = g_startTempAllocStack; /* For let() stack cleanup */
-  g_startTempAllocStack = g_tempAllocStackTop;
-
-  entries = numEntries(pattern);
-  for (i = 1; i <= entries; i++) {
-    let(&entryPattern, entry(i, pattern)); /* If we didn't modify
-          g_startTempAllocStack above, this let() would corrupt string
-          functions in the matchesList() call arguments */
-    matchVal = matches(testString, entryPattern, wildCard, oneCharWildCard);
-    if (matchVal) break;
-  }
-
-  let(&entryPattern, ""); /* Deallocate */ /* 3-Jul-2011 nm Added to fix
-                                              memory leak */
-  g_startTempAllocStack = saveTempAllocStack;
-  return (matchVal);
-}
-
-
-/* This function returns a 1 if the first argument matches the pattern of
-   the second argument.  The second argument may have wildcard characters.
-   wildCard matches 0 or more characters; oneCharWildCard matches any
-   single character. */
-/* 30-Jan-06 nm Added single-character-match wildcard argument */
-/* 19-Apr-2015 so, nm - Added "=" to match statement being proved */
-/* 19-Apr-2015 so, nm - Added "%" to match changed proofs */
-/* 8-Mar-2016 nm Added "#1234" to match internal statement number */
-/* 18-Jul-2020 nm Added "@1234" to match web statement number */
-flag matches(vstring testString, vstring pattern, char wildCard,
-    char oneCharWildCard) {
-  long i, ppos, pctr, tpos, s1, s2, s3;
-  vstring tmpStr = "";
-
-  /* 21-Nov-2014 Stefan O'Rear - added label ranges - see HELP SEARCH */
-  if (wildCard == '*') {
-    /* Checking for wildCard = * meaning this is only for labels, not
-       math tokens */
-
-    /* The following special chars are handled in this block:
-       "~" Statement range
-       "=" Most recent PROVE command statement
-       "%" List of modified statements
-       "#" Internal statement number
-       "@" Web page statement number */
-
-    i = instr(1, pattern, "~");
-    if (i != 0) {
-      if (i == 1) {
-        s1 = 1; /* empty string before "~" */
-      } else {
-        s1 = lookupLabel(left(pattern, i - 1));
-      }
-      s2 = lookupLabel(testString);
-      if (i == (long)strlen(pattern)) {
-        s3 = g_statements; /* empty string after "~" */
-      } else {
-        s3 = lookupLabel(right(pattern, i + 1));
-      }
-      let(&tmpStr, ""); /* Clean up temporary allocations of left and right */
-      return ((s1 >= 1 && s2 >= 1 && s3 >= 1 && s1 <= s2 && s2 <= s3)
-          ? 1 : 0);
-    }
-
-    /* 8-Mar-2016 nm Added "#12345" to match internal statement number */
-    if (pattern[0] == '#') {
-      s1 = (long)val(right(pattern, 2));
-      if (s1 < 1 || s1 > g_statements)
-        return 0; /* # arg is out of range */
-      if (!strcmp(g_Statement[s1].labelName, testString)) {
-        return 1;
-      } else {
-        return 0;
-      }
-    }
-
-    /* 18-Jul-2020 nm Added "@12345" to match web statement number */
-    if (pattern[0] == '@') {
-      s1 = lookupLabel(testString);
-      if (s1 < 1) return 0;
-      s2 = (long)val(right(pattern, 2));
-      if (g_Statement[s1].pinkNumber == s2) {
-        return 1;
-      } else {
-        return 0;
-      }
-    }
-
-    /* 19-Apr-2015 so, nm - Added "=" to match statement being proved */
-    if (!strcmp(pattern,"=")) {
-      s1 = lookupLabel(testString);
-      /*return (PFASmode && g_proveStatement == s1);*/
-      /* 18-Jul-2020 nm */
-      /* We might as well use g_proveStatement outside of MM-PA, so =
-         can be argument to PROVE command */
-      return (g_proveStatement == s1);
-    }
-
-    /* 19-Apr-2015 so, nm - Added "%" to match changed proofs */
-    if (!strcmp(pattern,"%")) {
-      s1 = lookupLabel(testString);  /* Returns -1 if not found or (not
-                                        $a and not $p) */
-      if (s1 > 0) { /* It's a $a or $p statement */
-        /* (If it's not $p, we don't want to peek at proofSectionPtr[-1]
-           to prevent bad pointer. */
-        if (g_Statement[s1].type == (char)p_) { /* $p so it has a proof */
-          /*
-          /@ ASCII 1 is flag that proof is not from original source file @/
-          if (g_Statement[s1].proofSectionPtr[-1] == 1) {
-          */
-          /* 3-May-2017 nm */
-          /* The proof is not from the original source file */
-          if (g_Statement[s1].proofSectionChanged == 1) {
-            return 1;
-          }
-        }
-      }
-      return 0;
-      /*
-      return nmbrElementIn(1, changedStmtNmbr, s1);
-      */
-    } /* if (!strcmp(pattern,"%")) */
-  } /* if (wildCard == '*') */
-
-  /* Get to first wild card character */
-  ppos = 0;
-  /*if (wildCard!='*') printf("'%s' vs. '%s'\n", pattern, testString);*/
-  while ((pattern[ppos] == testString[ppos] ||
-          (pattern[ppos] == oneCharWildCard && testString[ppos] != 0))
-      && pattern[ppos] != 0) ppos++;
-  if (pattern[ppos] == 0) {
-    if (testString[ppos] != 0) {
-      return (0); /* No wildcards; mismatched */
-    } else {
-      return (1); /* No wildcards; matched */
-    }
-  }
-  if (pattern[ppos] != wildCard) {
-    return (0); /* Mismatched */
-  }
-  tpos = ppos;
-
-  /* Scan remainder of pattern */
-  pctr = 0;
-  i = 0;
-  while (1) {
-    if (pattern[ppos + 1 + i] == wildCard) { /* Next wildcard found */
-      tpos = tpos + pctr + i;
-      ppos = ppos + 1 + i;
-      i = 0;
-      pctr = 0;
-      continue;
-    }
-    if (pattern[ppos + 1 + i] != testString[tpos + pctr + i]
-          && (pattern[ppos + 1 + i] != oneCharWildCard
-              || testString[tpos + pctr + i] == 0)) {
-      if (testString[tpos + pctr + i] == 0) {
-        return (0);
-      }
-      pctr++;
-      i = 0;
-      continue;
-    }
-    if (pattern[ppos + 1 + i] == 0) {
-      return(1); /* Matched */
-    }
-    i++;
-  }
-  bug(1375);
-  return (0); /* Dummy return - never used */
-}
-
-
-
-
-/*******************************************************************/
-/*********** Number string functions *******************************/
-/*******************************************************************/
-
-long g_nmbrTempAllocStackTop = 0;     /* Top of stack for nmbrTempAlloc functon */
-long g_nmbrStartTempAllocStack = 0;   /* Where to start freeing temporary allocation
-                                    when nmbrLet() is called (normally 0, except in
-                                    special nested vstring functions) */
-nmbrString *nmbrTempAllocStack[M_MAX_ALLOC_STACK];
-
-
-nmbrString *nmbrTempAlloc(long size)
-                                /* nmbrString memory allocation/deallocation */
-{
-  /* When "size" is >0, "size" instances of nmbrString are allocated. */
-  /* When "size" is 0, all memory previously allocated with this */
-  /* function is deallocated, down to g_nmbrStartTempAllocStack. */
-  /* int i; */  /* 11-Jul-2014 WL old code deleted */
-  if (size) {
-    if (g_nmbrTempAllocStackTop>=(M_MAX_ALLOC_STACK-1)) {
-      /*??? Fix to allocate more */
-      outOfMemory("#105 (nmbrString stack array)");
-    }
-    if (!(nmbrTempAllocStack[g_nmbrTempAllocStackTop++]=poolMalloc(size
-        *(long)(sizeof(nmbrString)))))
-      /* outOfMemory("#106 (nmbrString stack)"); */ /*???Unnec. w/ poolMalloc*/
-/*E*/db2=db2+size*(long)(sizeof(nmbrString));
-    return (nmbrTempAllocStack[g_nmbrTempAllocStackTop-1]);
-  } else {
-    /* 11-Jul-2014 WL old code deleted */
-    /*
-    for (i=g_nmbrStartTempAllocStack; i < g_nmbrTempAllocStackTop; i++) {
-/@E@/db2=db2-(nmbrLen(nmbrTempAllocStack[i])+1)*(long)(sizeof(nmbrString));
-      poolFree(nmbrTempAllocStack[i]);
-    }
-    */
-    /* 11-Jul-2014 WL new code */
-    while(g_nmbrTempAllocStackTop != g_nmbrStartTempAllocStack) {
-/*E*/db2=db2-(nmbrLen(nmbrTempAllocStack[g_nmbrTempAllocStackTop-1])+1)
-/*E*/                                              *(long)(sizeof(nmbrString));
-      poolFree(nmbrTempAllocStack[--g_nmbrTempAllocStackTop]);
-    }
-    /* end of 11-Jul-2014 WL new code */
-    g_nmbrTempAllocStackTop=g_nmbrStartTempAllocStack;
-    return (0);
-  }
-}
-
-
-/* Make string have temporary allocation to be released by next nmbrLet() */
-/* Warning:  after nmbrMakeTempAlloc() is called, the nmbrString may NOT be
-   assigned again with nmbrLet() */
-void nmbrMakeTempAlloc(nmbrString *s)
-{
-    if (g_nmbrTempAllocStackTop>=(M_MAX_ALLOC_STACK-1)) {
-      printf(
-      "*** FATAL ERROR ***  Temporary nmbrString stack overflow in nmbrMakeTempAlloc()\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-      bug(1368);
-    }
-    if (s[0] != -1) { /* End of string */
-      /* Do it only if nmbrString is not empty */
-      nmbrTempAllocStack[g_nmbrTempAllocStackTop++] = s;
-    }
-/*E*/db2=db2+(nmbrLen(s)+1)*(long)(sizeof(nmbrString));
-/*E*/db3=db3-(nmbrLen(s)+1)*(long)(sizeof(nmbrString));
-}
-
-
-void nmbrLet(nmbrString **target,nmbrString *source)
-/* nmbrString assignment */
-/* This function must ALWAYS be called to make assignment to */
-/* a nmbrString in order for the memory cleanup routines, etc. */
-/* to work properly.  If a nmbrString has never been assigned before, */
-/* it is the user's responsibility to initialize it to NULL_NMBRSTRING (the */
-/* null string). */
-{
-  long targetLength,sourceLength;
-  long targetAllocLen;
-  long poolDiff;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  sourceLength=nmbrLen(source);  /* Save its actual length */
-  targetLength=nmbrLen(*target);  /* Save its actual length */
-  targetAllocLen=nmbrAllocLen(*target); /* Save target's allocated length */
-/*E*/if (targetLength) {
-/*E*/  /* printf("Deleting %s\n",cvtMToVString(*target,0)); */
-/*E*/  db3 = db3 - (targetLength+1)*(long)(sizeof(nmbrString));
-/*E*/}
-/*E*/if (sourceLength) {
-/*E*/  /* printf("Adding %s\n",cvtMToVString(source,0)); */
-/*E*/  db3 = db3 + (sourceLength+1)*(long)(sizeof(nmbrString));
-/*E*/}
-  if (targetAllocLen) {
-    if (sourceLength) { /* source and target are both nonzero length */
-
-      if (targetAllocLen >= sourceLength) { /* Old string has room for new one */
-        nmbrCpy(*target,source); /* Re-use the old space to save CPU time */
-
-        /* Memory pool handling */
-        /* Assign actual size of target string */
-        poolDiff = ((long *)(*target))[-1] - ((long *)source)[-1];
-        ((long *)(*target))[-1] = ((long *)source)[-1];
-        /* If actual size of target string is less than allocated size, we
-           may have to add it to the used pool */
-        if (((long *)(*target))[-1] != ((long *)(*target))[-2]) {
-          if (((long *)(*target))[-1] > ((long *)(*target))[-2]) bug(1325);
-          if (((long *)(*target))[-3] == -1) {
-            /* It's not already in the used pool, so add it */
-            addToUsedPool(*target);
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0aa: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-          } else {
-            /* Adjust free space independently */
-            poolTotalFree = poolTotalFree + poolDiff;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0ab: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-          }
-        } else {
-          if (((long *)(*target))[-3] != -1) {
-            /* It's in the pool (but all allocated space coincidentally used) */
-            /* Adjust free space independently */
-            poolTotalFree = poolTotalFree + poolDiff;
-          }
-        }
-
-
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0a: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-      } else {
-        /* Free old string space and allocate new space */
-        poolFree(*target);  /* Free old space */
-        /* *target=poolMalloc((sourceLength + 1) * sizeof(nmbrString)); */
-        *target=poolMalloc((sourceLength + 1) * (long)(sizeof(nmbrString)) * 2);
-                        /* Allocate new space --
-                            We are replacing a smaller string with a larger one;
-                            assume it is growing, and allocate twice as much as
-                            needed. */
-        /*if (!*target) outOfMemory("#107 (nmbrString)");*/ /*???Unnec. w/ poolMalloc*/
-        nmbrCpy(*target,source);
-
-        /* Memory pool handling */
-        /* Assign actual size of target string */
-        poolDiff = ((long *)(*target))[-1] - ((long *)source)[-1];
-        ((long *)(*target))[-1] = ((long *)source)[-1];
-        /* If actual size of target string is less than allocated size, we
-           may have to add it to the used pool */
-        /* (The 1st 'if' is redundant with target doubling above) */
-        if (((long *)(*target))[-1] != ((long *)(*target))[-2]) {
-          if (((long *)(*target))[-1] > ((long *)(*target))[-2]) bug(1326);
-          if (((long *)(*target))[-3] == -1) {
-            /* It's not already in the used pool, so add it */
-            addToUsedPool(*target);
-          } else {
-            /* Adjust free space independently */
-            poolTotalFree = poolTotalFree + poolDiff;
-          }
-        } else {
-          if (((long *)(*target))[-3] != -1) {
-            /* It's in the pool (but all allocated space coincidentally used) */
-            /* Adjust free space independently */
-            poolTotalFree = poolTotalFree + poolDiff;
-          }
-        }
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0b: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-
-      }
-
-    } else {    /* source is 0 length, target is not */
-      poolFree(*target);
-      *target= NULL_NMBRSTRING;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0c: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    }
-  } else {
-    if (sourceLength) { /* target is 0 length, source is not */
-      *target=poolMalloc((sourceLength + 1) * (long)(sizeof(nmbrString)));
-                        /* Allocate new space */
-      /* if (!*target) outOfMemory("#108 (nmbrString)"); */ /*???Unnec. w/ poolMalloc*/
-      nmbrCpy(*target,source);
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0d: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    } else {    /* source and target are both 0 length */
-      /* *target= NULL_NMBRSTRING; */ /* Redundant */
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0e: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    }
-  }
-
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k1: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  nmbrTempAlloc(0); /* Free up temporary strings used in expression computation*/
-
-}
-
-
-
-nmbrString *nmbrCat(nmbrString *string1,...) /* String concatenation */
-#define M_MAX_CAT_ARGS 30
-{
-  va_list ap;   /* Declare list incrementer */
-  nmbrString *arg[M_MAX_CAT_ARGS];        /* Array to store arguments */
-  long argLength[M_MAX_CAT_ARGS];       /* Array to store argument lengths */
-  int numArgs=1;        /* Define "last argument" */
-  int i;
-  long j;
-  nmbrString *ptr;
-  arg[0]=string1;       /* First argument */
-
-  va_start(ap,string1); /* Begin the session */
-  while ((arg[numArgs++]=va_arg(ap,nmbrString *)))
-        /* User-provided argument list must terminate with NULL */
-    if (numArgs>=M_MAX_CAT_ARGS-1) {
-      printf("*** FATAL ERROR ***  Too many cat() arguments\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-      bug(1369);
-    }
-  va_end(ap);           /* End var args session */
-
-  numArgs--;    /* The last argument (0) is not a string */
-
-  /* Find out the total string length needed */
-  j = 0;
-  for (i = 0; i < numArgs; i++) {
-    argLength[i]=nmbrLen(arg[i]);
-    j=j+argLength[i];
-  }
-  /* Allocate the memory for it */
-  ptr=nmbrTempAlloc(j+1);
-  /* Move the strings into the newly allocated area */
-  j = 0;
-  for (i = 0; i < numArgs; i++) {
-    nmbrCpy(ptr+j,arg[i]);
-    j=j+argLength[i];
-  }
-  return (ptr);
-
-}
-
-
-
-/* Find out the length of a nmbrString */
-long nmbrLen(nmbrString *s)
-{
-  /* Assume it's been allocated with poolMalloc. */
-  return (((long)(((long *)s)[-1] - (long)(sizeof(nmbrString))))
-              / (long)(sizeof(nmbrString)));
-}
-
-
-/* Find out the allocated length of a nmbrString */
-long nmbrAllocLen(nmbrString *s)
-{
-  /* Assume it's been allocated with poolMalloc. */
-  return (((long)(((long *)s)[-2] - (long)(sizeof(nmbrString))))
-              / (long)(sizeof(nmbrString)));
-}
-
-/* Set the actual size field in a nmbrString allocated with poolFixedMalloc() */
-/* Use this if "zapping" a nmbrString element with -1 to reduce its length. */
-/* Note that the nmbrString will not be moved to the "used pool", even if
-   zapping its length results in free space; thus the free space will never
-   get recovered unless done by the caller or poolFree is called.  (This is
-   done on purpose so the caller can know what free space is left.) */
-/* ???Note that nmbrZapLen's not moving string to used pool wastes potential
-   space when called by the routines in this module.  Effect should be minor. */
-void nmbrZapLen(nmbrString *s, long length) {
-  if (((long *)s)[-3] != -1) {
-    /* It's already in the used pool, so adjust free space tally */
-    poolTotalFree = poolTotalFree + ((long *)s)[-1]
-        - (length + 1) * (long)(sizeof(nmbrString));
-  }
-  ((long *)s)[-1] = (length + 1) * (long)(sizeof(nmbrString));
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("l: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-}
-
-
-/* Copy a string to another (pre-allocated) string */
-/* Dangerous for general purpose use */
-void nmbrCpy(nmbrString *s,nmbrString *t)
-{
-  long i;
-  i = 0;
-  while (t[i] != -1) { /* End of string -- nmbrRight depends on it!! */
-    s[i] = t[i];
-    i++;
-  }
-  s[i] = t[i]; /* End of string */
-}
-
-
-/* Copy a string to another (pre-allocated) string */
-/* Like strncpy, only the 1st n characters are copied. */
-/* Dangerous for general purpose use */
-void nmbrNCpy(nmbrString *s,nmbrString *t,long n)
-{
-  long i;
-  i = 0;
-  while (t[i] != -1) { /* End of string -- nmbrSeg, nmbrMid depend on it!! */
-    if (i >= n) break;
-    s[i] = t[i];
-    i++;
-  }
-  s[i] = t[i]; /* End of string */
-}
-
-
-/* Compare two strings */
-/* Unlike strcmp, this returns a 1 if the strings are equal
-   and 0 otherwise. */
-/* Only the token is compared.  The whiteSpace string is
-   ignored. */
-int nmbrEq(nmbrString *s,nmbrString *t)
-{
-  long i;
-  if (nmbrLen(s) != nmbrLen(t)) return 0; /* Speedup */
-  for (i = 0; s[i] == t[i]; i++)
-    if (s[i] == -1) /* End of string */
-      return 1;
-  return 0;
-}
-
-
-/* Extract sin from character position start to stop into sout */
-nmbrString *nmbrSeg(nmbrString *sin, long start, long stop)
-{
-  nmbrString *sout;
-  long length;
-  if (start < 1) start = 1;
-  if (stop < 1) stop = 0;
-  length=stop - start + 1;
-  if (length < 0) length = 0;
-  sout = nmbrTempAlloc(length + 1);
-  nmbrNCpy(sout, sin + start - 1, length);
-  sout[length] = *NULL_NMBRSTRING;
-  return (sout);
-}
-
-/* Extract sin from character position start for length len */
-nmbrString *nmbrMid(nmbrString *sin, long start, long length)
-{
-  nmbrString *sout;
-  if (start < 1) start = 1;
-  if (length < 0) length = 0;
-  sout = nmbrTempAlloc(length + 1);
-  nmbrNCpy(sout, sin + start - 1, length);
-  sout[length] = *NULL_NMBRSTRING;
-  return (sout);
-}
-
-/* Extract leftmost n characters */
-nmbrString *nmbrLeft(nmbrString *sin,long n)
-{
-  nmbrString *sout;
-  if (n < 0) n = 0;
-  sout=nmbrTempAlloc(n + 1);
-  nmbrNCpy(sout, sin, n);
-  sout[n] = *NULL_NMBRSTRING;
-  return (sout);
-}
-
-/* Extract after character n */
-nmbrString *nmbrRight(nmbrString *sin,long n)
-{
-  /*??? We could just return &sin[n-1], but this is safer for debugging. */
-  nmbrString *sout;
-  long i;
-  if (n < 1) n = 1;
-  i = nmbrLen(sin);
-  if (n > i) return (NULL_NMBRSTRING);
-  sout = nmbrTempAlloc(i - n + 2);
-  nmbrCpy(sout, &sin[n - 1]);
-  return (sout);
-}
-
-
-/* Allocate and return an "empty" string n "characters" long */
-nmbrString *nmbrSpace(long n)
-{
-  nmbrString *sout;
-  long j = 0;
-  if (n < 0) bug(1327);
-  sout = nmbrTempAlloc(n + 1);
-  while (j < n) {
-    /* Initialize all fields */
-    sout[j] = 0;
-    j++;
-  }
-  sout[j] = *NULL_NMBRSTRING; /* End of string */
-  return (sout);
-}
-
-/* Search for string2 in string1 starting at start_position */
-long nmbrInstr(long start_position,nmbrString *string1,
-  nmbrString *string2)
-{
-   long ls1, ls2, i, j;
-   if (start_position < 1) start_position = 1;
-   ls1 = nmbrLen(string1);
-   ls2 = nmbrLen(string2);
-   for (i = start_position - 1; i <= ls1 - ls2; i++) {
-     for (j = 0; j < ls2; j++) {
-       if (string1[i+j] != string2[j])
-         break;
-     }
-     if (j == ls2) return (i+1);
-   }
-   return (0);
-
-}
-
-/* Search for string2 in string 1 in reverse starting at start_position */
-/* (Reverse nmbrInstr) */
-/* Warning:  This has 'let' inside of it and is not safe for use inside
-   of 'let' statements.  (To make it safe, it must be rewritten to expand
-   the 'mid' and remove the 'let'.) */
-long nmbrRevInstr(long start_position,nmbrString *string1,
-    nmbrString *string2)
-{
-   long ls1, ls2;
-   nmbrString *tmp = NULL_NMBRSTRING;
-   ls1 = nmbrLen(string1);
-   ls2 = nmbrLen(string2);
-   if (start_position > ls1 - ls2 + 1) start_position = ls1 - ls2 + 2;
-   if (start_position<1) return 0;
-   while (!nmbrEq(string2, nmbrMid(string1, start_position, ls2))) {
-     start_position--;
-     nmbrLet(&tmp, NULL_NMBRSTRING);
-              /* Clear nmbrString buffer to prevent overflow caused by "mid" */
-     if (start_position < 1) return 0;
-   }
-   return (start_position);
-}
-
-
-/* Converts nmbrString to a vstring with one space between tokens */
-vstring nmbrCvtMToVString(nmbrString *s)
-{
-  long i, j, outputLen, mstrLen;
-  vstring tmpStr = "";
-  vstring ptr;
-  vstring ptr2;
-
-  long saveTempAllocStack;
-  saveTempAllocStack = g_startTempAllocStack; /* For let() stack cleanup */
-  g_startTempAllocStack = g_tempAllocStackTop;
-
-  mstrLen = nmbrLen(s);
-  /* Precalculate output length */
-  outputLen = -1;
-  for (i = 0; i < mstrLen; i++) {
-    outputLen = outputLen + (long)strlen(g_MathToken[s[i]].tokenName) + 1;
-  }
-  let(&tmpStr, space(outputLen)); /* Preallocate output string */
-  /* Assign output string */
-  ptr = tmpStr;
-  for (i = 0; i < mstrLen; i++) {
-    ptr2 = g_MathToken[s[i]].tokenName;
-    j = (long)strlen(ptr2);
-    memcpy(ptr, ptr2, (size_t)j);
-    ptr = ptr + j + 1;
-  }
-
-  g_startTempAllocStack = saveTempAllocStack;
-  if (tmpStr[0]) makeTempAlloc(tmpStr); /* Flag it for deallocation */
-  return (tmpStr);
-}
-
-
-/* Converts proof to a vstring with one space between tokens */
-/* 11-Sep-2016 nm Allow it to tolerate garbage entries for debugging */
-vstring nmbrCvtRToVString(nmbrString *proof,
-    /* 25-Jan-2016 */
-    flag explicitTargets, /* 1 = "target=source" for /EXPLICIT proof format */
-    long statemNum) /* used only if explicitTargets=1 */
-{
-  long i, j, plen, maxLabelLen, maxLocalLen, step, stmt;
-  long maxTargetLabelLen; /* 25-Jan-2016 nm */
-  vstring proofStr = "";
-  vstring tmpStr = "";
-  vstring ptr;
-  nmbrString *localLabels = NULL_NMBRSTRING;
-  nmbrString *localLabelNames = NULL_NMBRSTRING;
-  long nextLocLabNum = 1; /* Next number to be used for a local label */
-  void *voidPtr; /* bsearch result */
-  /* 26-Jan-2016 nm */
-  nmbrString *targetHyps = NULL_NMBRSTRING; /* Targets for /EXPLICIT format */
-
-  long saveTempAllocStack;
-  long nmbrSaveTempAllocStack;
-  saveTempAllocStack = g_startTempAllocStack; /* For let() stack cleanup */
-  g_startTempAllocStack = g_tempAllocStackTop;
-  nmbrSaveTempAllocStack = g_nmbrStartTempAllocStack;
-                                           /* For nmbrLet() stack cleanup*/
-  g_nmbrStartTempAllocStack = g_nmbrTempAllocStackTop;
-
-  plen = nmbrLen(proof);
-
-  /* 25-Jan-2016 nm */
-  if (explicitTargets == 1) {
-    /* Get the list of targets for /EXPLICIT format */
-    if (statemNum <= 0) bug(1388);
-    nmbrLet(&targetHyps, nmbrGetTargetHyp(proof, statemNum));
-  }
-
-  /* Find longest local label name */
-  maxLocalLen = 0;
-  i = plen;
-  while (i) {
-    i = i / 10;
-    maxLocalLen++;
-  }
-
-  /* Collect local labels */
-  /* Also, find longest statement label name */
-  maxLabelLen = 0;
-  maxTargetLabelLen = 0; /* 25-Jan-2016 nm */
-  for (step = 0; step < plen; step++) {
-    stmt = proof[step];
-    if (stmt <= -1000) {
-      stmt = -1000 - stmt;
-      if (!nmbrElementIn(1, localLabels, stmt)) {
-        nmbrLet(&localLabels, nmbrAddElement(localLabels, stmt));
-      }
-    } else {
-
-      /* 11-Sep-2016 nm */
-      if (stmt < 1 || stmt > g_statements) {
-        maxLabelLen = 100; /* For safety */
-        maxTargetLabelLen = 100; /* For safety */
-        continue; /* Ignore bad entry */
-      }
-
-      if (stmt > 0) {
-        if ((signed)(strlen(g_Statement[stmt].labelName)) > maxLabelLen) {
-          maxLabelLen = (long)strlen(g_Statement[stmt].labelName);
-        }
-      }
-    }
-
-    /* 25-Jan-2016 nm */
-    if (explicitTargets == 1) {
-      /* Also consider longest target label name */
-      stmt = targetHyps[step];
-      if (stmt <= 0) bug(1390);
-      if ((signed)(strlen(g_Statement[stmt].labelName)) > maxTargetLabelLen) {
-        maxTargetLabelLen = (long)strlen(g_Statement[stmt].labelName);
-      }
-    }
-
-  } /* next step */
-
-  /* localLabelNames[] holds an integer which, when converted to string,
-    is the local label name. */
-  nmbrLet(&localLabelNames, nmbrSpace(plen));
-
-  /* Build the ASCII string */
-  /* Preallocate the string for speed (the "2" accounts for a space and a
-     colon). */
-  let(&proofStr, space(plen * (2 + maxLabelLen
-      + ((explicitTargets == 1) ? maxTargetLabelLen + 1 : 0)  /* 25-Jan-2016 */
-                                          /* The "1" accounts for equal sign */
-      + maxLocalLen)));
-  ptr = proofStr;
-  for (step = 0; step < plen; step++) {
-    stmt = proof[step];
-    if (stmt < 0) {
-      if (stmt <= -1000) {
-        stmt = -1000 - stmt;
-            /* Change stmt to the step number a local label refers to */
-        let(&tmpStr, cat(
-
-            /* 25-Jan-2016 nm */
-            ((explicitTargets == 1) ? g_Statement[targetHyps[step]].labelName : ""),
-            ((explicitTargets == 1) ? "=" : ""),
-
-            str((double)(localLabelNames[stmt])), " ", NULL));
-
-      /* 11-Sep-2016 nm */
-      } else if (stmt != -(long)'?') {
-        let(&tmpStr, cat("??", str((double)stmt), " ", NULL)); /* For safety */
-
-      } else {
-        if (stmt != -(long)'?') bug(1391); /* Must be an unknown step */
-        let(&tmpStr, cat(
-
-            /* 25-Jan-2016 nm */
-            ((explicitTargets == 1) ? g_Statement[targetHyps[step]].labelName : ""),
-            ((explicitTargets == 1) ? "=" : ""),
-
-            chr(-stmt), " ", NULL));
-      }
-
-    /* 11-Sep-2016 nm */
-    } else if (stmt < 1 || stmt > g_statements) {
-      let(&tmpStr, cat("??", str((double)stmt), " ", NULL)); /* For safety */
-
-    } else {
-      let(&tmpStr,"");
-      if (nmbrElementIn(1, localLabels, step)) {
-        /* This statement declares a local label */
-        /* First, get a name for the local label, using the next integer that
-           does not match any integer used for a statement label. */
-        let(&tmpStr, str((double)nextLocLabNum));
-        while (1) {
-          voidPtr = (void *)bsearch(tmpStr,
-              g_allLabelKeyBase, (size_t)g_numAllLabelKeys,
-              sizeof(long), labelSrchCmp);
-          if (!voidPtr) break; /* It does not conflict */
-          nextLocLabNum++; /* Try the next one */
-          let(&tmpStr, str((double)nextLocLabNum));
-        }
-        localLabelNames[step] = nextLocLabNum;
-        let(&tmpStr, cat(tmpStr, ":", NULL));
-        nextLocLabNum++; /* Prepare for next local label */
-      }
-      let(&tmpStr, cat(tmpStr,
-
-          /* 25-Jan-2016 nm */
-          ((explicitTargets == 1) ? g_Statement[targetHyps[step]].labelName : ""),
-          ((explicitTargets == 1) ? "=" : ""),
-
-          g_Statement[stmt].labelName, " ", NULL));
-    }
-    j = (long)strlen(tmpStr);
-    memcpy(ptr, tmpStr, (size_t)j);
-    ptr = ptr + j;
-  } /* Next step */
-
-  if (ptr - proofStr) {
-    /* Deallocate large pool and trim trailing space */
-    let(&proofStr, left(proofStr, ptr - proofStr - 1));
-  } else {
-    let(&proofStr, "");
-  }
-  let(&tmpStr, "");
-  nmbrLet(&localLabels, NULL_NMBRSTRING);
-  nmbrLet(&localLabelNames, NULL_NMBRSTRING);
-
-  g_startTempAllocStack = saveTempAllocStack;
-  g_nmbrStartTempAllocStack = nmbrSaveTempAllocStack;
-  if (proofStr[0]) makeTempAlloc(proofStr); /* Flag it for deallocation */
-  return (proofStr);
-}
-
-
-nmbrString *nmbrGetProofStepNumbs(nmbrString *reason)
-{
-  /* This function returns a nmbrString of length of reason with
-     step numbers assigned to tokens which are steps, and 0 otherwise.
-     The returned string is allocated; THE CALLER MUST DEALLOCATE IT. */
-  nmbrString *stepNumbs = NULL_NMBRSTRING;
-  long rlen, start, end, i, step;
-
-  rlen = nmbrLen(reason);
-  nmbrLet(&stepNumbs,nmbrSpace(rlen)); /* All stepNumbs[] are initialized
-                                        to 0 by nmbrSpace() */
-  if (!rlen) return (stepNumbs);
-  if (reason[1] == -(long)'=') {
-    /* The proof is in "internal" format, with "g_proveStatement = (...)" added */
-    start = 2; /* 2, not 3, so empty proof '?' will be seen */
-    if (rlen == 3) {
-      end = rlen; /* Empty proof case */
-    } else {
-      end = rlen - 1; /* Trim off trailing ')' */
-    }
-  } else {
-    start = 1;
-    end = rlen;
-  }
-  step = 0;
-  for (i = start; i < end; i++) {
-    if (i == 0) {
-      /* i = 0 must be handled separately to prevent a reference to
-         a field outside of the nmbrString */
-      step++;
-      stepNumbs[0] = step;
-      continue;
-    }
-    if (reason[i] < 0 && reason[i] != -(long)'?') continue;
-    if (reason[i - 1] == -(long)'('
-        || reason[i - 1] == -(long)'{'
-        || reason[i - 1] == -(long)'=') {
-      step++;
-      stepNumbs[i] = step;
-    }
-  }
-  return (stepNumbs);
-}
-
-
-/* Converts any nmbrString to an ASCII string of numbers
-   -- used for debugging only. */
-vstring nmbrCvtAnyToVString(nmbrString *s)
-{
-  long i;
-  vstring tmpStr = "";
-
-  long saveTempAllocStack;
-  saveTempAllocStack = g_startTempAllocStack; /* For let() stack cleanup */
-  g_startTempAllocStack = g_tempAllocStackTop;
-
-  for (i = 1; i <= nmbrLen(s); i++) {
-    let(&tmpStr,cat(tmpStr," ", str((double)(s[i-1])),NULL));
-  }
-
-  g_startTempAllocStack = saveTempAllocStack;
-  if (tmpStr[0]) makeTempAlloc(tmpStr); /* Flag it for deallocation */
-  return (tmpStr);
-}
-
-
-/* Extract variables from a math token string */
-nmbrString *nmbrExtractVars(nmbrString *m)
-{
-  long i, j, length;
-  nmbrString *v;
-  length = nmbrLen(m);
-  v=nmbrTempAlloc(length + 1); /* Pre-allocate maximum possible space */
-  v[0] = *NULL_NMBRSTRING;
-  j = 0; /* Length of output string */
-  for (i = 0; i < length; i++) {
-    /*if (m[i] < 0 || m[i] >= g_mathTokens) {*/
-    /* Changed >= to > because tokenNum=g_mathTokens is used by mmveri.c for
-       dummy token */
-    if (m[i] < 0 || m[i] > g_mathTokens) bug(1328);
-    if (g_MathToken[m[i]].tokenType == (char)var_) {
-      if (!nmbrElementIn(1, v, m[i])) { /* Don't duplicate variable */
-        v[j] = m[i];
-        j++;
-        v[j] = *NULL_NMBRSTRING; /* Add temp. end-of-string for getElementOf() */
-      }
-    }
-  }
-  nmbrZapLen(v, j); /* Zap mem pool fields */
-/*E*/db2=db2-(length-nmbrLen(v))*(long)(sizeof(nmbrString));
-  return v;
-}
-
-
-/* Determine if an element (after start) is in a nmbrString; return position
-   if it is.  Like nmbrInstr(), but faster.  Warning:  start must NOT
-   be greater than length, otherwise results are unpredictable!!  This
-   is not checked in order to speed up search. */
-long nmbrElementIn(long start, nmbrString *g, long element)
-{
-  long i = start - 1;
-  while (g[i] != -1) { /* End of string */
-    if (g[i] == element) return(i + 1);
-    i++;
-  }
-  return(0);
-}
-
-
-/* Add a single number to end of a nmbrString - faster than nmbrCat */
-nmbrString *nmbrAddElement(nmbrString *g, long element)
-{
-  long length;
-  nmbrString *v;
-  length = nmbrLen(g);
-  v = nmbrTempAlloc(length + 2); /* Allow for end of string */
-  nmbrCpy(v, g);
-  v[length] = element;
-  v[length + 1] = *NULL_NMBRSTRING; /* End of string */
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("bbg2: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  return(v);
-}
-
-
-/* Get the set union of two math token strings (presumably
-   variable lists) */
-nmbrString *nmbrUnion(nmbrString *m1, nmbrString *m2)
-{
-  long i,j,len1,len2;
-  nmbrString *v;
-  len1 = nmbrLen(m1);
-  len2 = nmbrLen(m2);
-  v=nmbrTempAlloc(len1+len2+1); /* Pre-allocate maximum possible space */
-  nmbrCpy(v,m1);
-  nmbrZapLen(v, len1);
-  j = 0;
-  for (i = 0; i < len2; i++) {
-    if (!nmbrElementIn(1, v, m2[i])) {
-      nmbrZapLen(v, len1 + j + 1);
-      v[len1 + j] = m2[i];
-      j++;
-      v[len1 + j] = *NULL_NMBRSTRING;
-    }
-  }
-  v[len1 + j] = *NULL_NMBRSTRING;
-  nmbrZapLen(v, len1 + j);
-/*E*/db2=db2-(len1+len2-nmbrLen(v))*(long)(sizeof(nmbrString));
-  return(v);
-}
-
-
-/* Get the set intersection of two math token strings (presumably
-   variable lists) */
-nmbrString *nmbrIntersection(nmbrString *m1,nmbrString *m2)
-{
-  long i,j,len2;
-  nmbrString *v;
-  len2 = nmbrLen(m2);
-  v=nmbrTempAlloc(len2+1); /* Pre-allocate maximum possible space */
-  j = 0;
-  for (i = 0; i < len2; i++) {
-    if (nmbrElementIn(1,m1,m2[i])) {
-      v[j] = m2[i];
-      j++;
-    }
-  }
-  /* Add end-of-string */
-  v[j] = *NULL_NMBRSTRING;
-  nmbrZapLen(v, j);
-/*E*/db2=db2-(len2-nmbrLen(v))*(long)(sizeof(nmbrString));
-  return v;
-}
-
-
-/* Get the set difference m1-m2 of two math token strings (presumably
-   variable lists) */
-nmbrString *nmbrSetMinus(nmbrString *m1,nmbrString *m2)
-{
-  long i,j,len1;
-  nmbrString *v;
-  len1 = nmbrLen(m1);
-  v=nmbrTempAlloc(len1+1); /* Pre-allocate maximum possible space */
-  j = 0;
-  for (i = 0; i < len1; i++) {
-    if (!nmbrElementIn(1,m2,m1[i])) {
-      v[j] = m1[i];
-      j++;
-    }
-  }
-  /* Add end-of-string */
-  v[j] = *NULL_NMBRSTRING;
-  nmbrZapLen(v, j);
-/*E*/db2=db2-(len1-nmbrLen(v))*(long)(sizeof(nmbrString));
-  return v;
-}
-
-
-/* This is a utility function that returns the length of a subproof that
-   ends at step */
-/* 22-Aug-2012 nm - this doesn't seem to be used outside of mmdata.c -
-   should we replace it with subproofLen() in mmpfas.c? */
-long nmbrGetSubproofLen(nmbrString *proof, long step)
-{
-  long stmt, hyps, pos, i;
-  char type;
-
-  if (step < 0) bug(1329);
-  stmt = proof[step];
-  if (stmt < 0) return (1); /* Unknown or label ref */
-  type = g_Statement[stmt].type;
-  if (type == f_ || type == e_) return (1); /* Hypothesis */
-  hyps = g_Statement[stmt].numReqHyp;
-  pos = step - 1;
-  for (i = 0; i < hyps; i++) {
-    pos = pos - nmbrGetSubproofLen(proof, pos);
-  }
-  return (step - pos);
-}
-
-
-
-
-/* This function returns a packed or "squished" proof, putting in local label
-   references to previous subproofs. */
-nmbrString *nmbrSquishProof(nmbrString *proof)
-{
-  nmbrString *newProof = NULL_NMBRSTRING;
-  nmbrString *dummyProof = NULL_NMBRSTRING;
-  nmbrString *subProof = NULL_NMBRSTRING;
-  long step, dummyStep, subPrfLen, matchStep, plen;
-  flag foundFlag;
-
-  nmbrLet(&newProof,proof); /* In case of temp. alloc. of proof */
-  plen = nmbrLen(newProof);
-  dummyStep = 0;
-  nmbrLet(&dummyProof, newProof); /* Parallel proof with test subproof replaced
-                                 with a reference to itself, for matching. */
-  for (step = 0; step < plen; step++) {
-    subPrfLen = nmbrGetSubproofLen(dummyProof, dummyStep);
-    if (subPrfLen <= 1) {
-      dummyStep++;
-      continue;
-    }
-    nmbrLet(&subProof, nmbrSeg(dummyProof, dummyStep - subPrfLen + 2,
-        dummyStep + 1));
-    matchStep = step + 1;
-    foundFlag = 0;
-    while (1) {
-      matchStep = nmbrInstr(matchStep + 1, newProof, subProof);
-      if (!matchStep) break; /* No more occurrences */
-      foundFlag = 1;
-      /* Replace the found subproof with a reference to this subproof */
-      nmbrLet(&newProof,
-            nmbrCat(nmbrAddElement(nmbrLeft(newProof, matchStep - 1),
-            -1000 - step), nmbrRight(newProof, matchStep + subPrfLen), NULL));
-      matchStep = matchStep - subPrfLen + 1;
-    }
-    if (foundFlag) {
-      plen = nmbrLen(newProof); /* Update the new proof length */
-      /* Replace this subproof with a reference to itself, for later matching */
-      /* and add on rest of real proof. */
-      dummyStep = dummyStep + 1 - subPrfLen;
-      nmbrLet(&dummyProof,
-          nmbrCat(nmbrAddElement(nmbrLeft(dummyProof, dummyStep),
-          -1000 - step), nmbrRight(newProof, step + 2), NULL));
-    }
-    dummyStep++;
-  } /* Next step */
-  nmbrLet(&subProof, NULL_NMBRSTRING);
-  nmbrLet(&dummyProof, NULL_NMBRSTRING);
-  nmbrMakeTempAlloc(newProof); /* Flag it for deallocation */
-  return (newProof);
-}
-
-
-/* This function unpacks a "squished" proof, replacing local label references
-   to previous subproofs by the subproofs themselves. */
-nmbrString *nmbrUnsquishProof(nmbrString *proof)
-{
-  nmbrString *newProof = NULL_NMBRSTRING;
-  nmbrString *subProof = NULL_NMBRSTRING;
-  long step, plen, subPrfLen, stmt;
-
-  nmbrLet(&newProof, proof);
-  plen = nmbrLen(newProof);
-  for (step = plen - 1; step >= 0; step--) {
-    stmt = newProof[step];
-    if (stmt > -1000) continue;
-    /* It's a local label reference */
-    stmt = -1000 - stmt;
-    subPrfLen = nmbrGetSubproofLen(newProof, stmt);
-    nmbrLet(&newProof, nmbrCat(nmbrLeft(newProof, step),
-        nmbrSeg(newProof, stmt - subPrfLen + 2, stmt + 1),
-        nmbrRight(newProof, step + 2), NULL));
-    step = step + subPrfLen - 1;
-  }
-  nmbrLet(&subProof, NULL_NMBRSTRING);
-  nmbrMakeTempAlloc(newProof); /* Flag it for deallocation */
-  return (newProof);
-}
-
-
-/* This function returns the indentation level vs. step number of a proof
-   string.  This information is used for formatting proof displays.  The
-   function calls itself recursively, but the first call should be with
-   startingLevel = 0. */
-/* ???Optimization:  remove nmbrString calls and use static variables
-   to communicate to recursive calls */
-nmbrString *nmbrGetIndentation(nmbrString *proof,
-  long startingLevel)
-{
-  long plen, stmt, pos, splen, hyps, i, j;
-  char type;
-  nmbrString *indentationLevel = NULL_NMBRSTRING;
-  nmbrString *subProof = NULL_NMBRSTRING;
-  nmbrString *nmbrTmp = NULL_NMBRSTRING;
-
-  plen = nmbrLen(proof);
-  stmt = proof[plen - 1];
-  nmbrLet(&indentationLevel, nmbrSpace(plen));
-  indentationLevel[plen - 1] = startingLevel;
-  if (stmt < 0) { /* A local label reference or unknown */
-    if (plen != 1) bug(1330);
-    nmbrMakeTempAlloc(indentationLevel); /* Flag it for deallocation */
-    return (indentationLevel);
-  }
-  type = g_Statement[stmt].type;
-  if (type == f_ || type == e_) { /* A hypothesis */
-    if (plen != 1) bug(1331);
-    nmbrMakeTempAlloc(indentationLevel); /* Flag it for deallocation */
-    return (indentationLevel);
-  }
-  /* An assertion */
-  if (type != a_ && type != p_) bug(1332);
-  hyps = g_Statement[stmt].numReqHyp;
-  pos = plen - 2;
-  for (i = 0; i < hyps; i++) {
-    splen = nmbrGetSubproofLen(proof, pos);
-    nmbrLet(&subProof, nmbrSeg(proof, pos - splen + 2, pos + 1));
-    nmbrLet(&nmbrTmp, nmbrGetIndentation(subProof, startingLevel + 1));
-    for (j = 0; j < splen; j++) {
-      indentationLevel[j + pos - splen + 1] = nmbrTmp[j];
-    }
-    pos = pos - splen;
-  }
-  if (pos != -1) bug (333);
-
-  nmbrLet(&subProof,NULL_NMBRSTRING); /* Deallocate */
-  nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* Deallocate */
-  nmbrMakeTempAlloc(indentationLevel); /* Flag it for deallocation */
-  return (indentationLevel);
-} /* nmbrGetIndentation */
-
-
-/* This function returns essential (1) or floating (0) vs. step number of a
-   proof string.  This information is used for formatting proof displays.  The
-   function calls itself recursively. */
-/* ???Optimization:  remove nmbrString calls and use static variables
-   to communicate to recursive calls */
-nmbrString *nmbrGetEssential(nmbrString *proof)
-{
-  long plen, stmt, pos, splen, hyps, i, j;
-  char type;
-  nmbrString *essentialFlags = NULL_NMBRSTRING;
-  nmbrString *subProof = NULL_NMBRSTRING;
-  nmbrString *nmbrTmp = NULL_NMBRSTRING;
-  nmbrString *nmbrTmpPtr2;
-
-  plen = nmbrLen(proof);
-  stmt = proof[plen - 1];
-  nmbrLet(&essentialFlags, nmbrSpace(plen));
-  essentialFlags[plen - 1] = 1;
-  if (stmt < 0) { /* A local label reference or unknown */
-    if (plen != 1) bug(1334);
-    /* The only time it should get here is if the original proof has only one
-       step, which would be an unknown step */
-    if (stmt != -(long)'?' && stmt > -1000) bug(1335);
-    nmbrMakeTempAlloc(essentialFlags); /* Flag it for deallocation */
-    return (essentialFlags);
-  }
-  type = g_Statement[stmt].type;
-  if (type == f_ || type == e_) { /* A hypothesis */
-    /* The only time it should get here is if the original proof has only one
-       step */
-    if (plen != 1) bug(1336);
-    nmbrMakeTempAlloc(essentialFlags); /* Flag it for deallocation */
-    return (essentialFlags);
-  }
-  /* An assertion */
-  if (type != a_ && type != p_) bug(1337);
-  hyps = g_Statement[stmt].numReqHyp;
-  pos = plen - 2;
-  nmbrTmpPtr2 = g_Statement[stmt].reqHypList;
-  for (i = 0; i < hyps; i++) {
-    splen = nmbrGetSubproofLen(proof, pos);
-    if (g_Statement[nmbrTmpPtr2[hyps - i - 1]].type == e_) {
-      nmbrLet(&subProof, nmbrSeg(proof, pos - splen + 2, pos + 1));
-      nmbrLet(&nmbrTmp, nmbrGetEssential(subProof));
-      for (j = 0; j < splen; j++) {
-        essentialFlags[j + pos - splen + 1] = nmbrTmp[j];
-      }
-    }
-    pos = pos - splen;
-  }
-  if (pos != -1) bug (1338);
-
-  nmbrLet(&subProof,NULL_NMBRSTRING); /* Deallocate */
-  nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* Deallocate */
-  nmbrMakeTempAlloc(essentialFlags); /* Flag it for deallocation */
-  return (essentialFlags);
-} /* nmbrGetEssential */
-
-
-/* This function returns the target hypothesis vs. step number of a proof
-   string.  This information is used for formatting proof displays.  The
-   function calls itself recursively.
-   statemNum is the statement being proved. */
-/* ???Optimization:  remove nmbrString calls and use static variables
-   to communicate to recursive calls */
-nmbrString *nmbrGetTargetHyp(nmbrString *proof, long statemNum)
-{
-  long plen, stmt, pos, splen, hyps, i, j;
-  char type;
-  nmbrString *targetHyp = NULL_NMBRSTRING;
-  nmbrString *subProof = NULL_NMBRSTRING;
-  nmbrString *nmbrTmp = NULL_NMBRSTRING;
-
-  plen = nmbrLen(proof);
-  stmt = proof[plen - 1];
-  nmbrLet(&targetHyp, nmbrSpace(plen));
-  if (statemNum) { /* First (rather than recursive) call */
-    targetHyp[plen - 1] = statemNum; /* Statement being proved */
-  }
-  if (stmt < 0) { /* A local label reference or unknown */
-    if (plen != 1) bug(1339);
-    /* The only time it should get here is if the original proof has only one
-       step, which would be an unknown step */
-    if (stmt != -(long)'?') bug(1340);
-    nmbrMakeTempAlloc(targetHyp); /* Flag it for deallocation */
-    return (targetHyp);
-  }
-  type = g_Statement[stmt].type;
-  if (type == f_ || type == e_) { /* A hypothesis */
-    /* The only time it should get here is if the original proof has only one
-       step */
-    if (plen != 1) bug(1341);
-    nmbrMakeTempAlloc(targetHyp); /* Flag it for deallocation */
-    return (targetHyp);
-  }
-  /* An assertion */
-  if (type != a_ && type != p_) bug(1342);
-  hyps = g_Statement[stmt].numReqHyp;
-  pos = plen - 2;
-  for (i = 0; i < hyps; i++) {
-    splen = nmbrGetSubproofLen(proof, pos);
-    if (splen > 1) {
-      nmbrLet(&subProof, nmbrSeg(proof, pos - splen + 2, pos + 1));
-      nmbrLet(&nmbrTmp, nmbrGetTargetHyp(subProof,
-          g_Statement[stmt].reqHypList[hyps - i - 1]));
-      for (j = 0; j < splen; j++) {
-        targetHyp[j + pos - splen + 1] = nmbrTmp[j];
-      }
-    } else {
-      /* A one-step subproof; don't bother with recursive call */
-      targetHyp[pos] = g_Statement[stmt].reqHypList[hyps - i - 1];
-    }
-    pos = pos - splen;
-  }
-  if (pos != -1) bug (343);
-
-  nmbrLet(&subProof,NULL_NMBRSTRING); /* Deallocate */
-  nmbrLet(&nmbrTmp, NULL_NMBRSTRING); /* Deallocate */
-  nmbrMakeTempAlloc(targetHyp); /* Flag it for deallocation */
-  return (targetHyp);
-} /* nmbrGetTargetHyp */
-
-
-/* Converts a proof string to a compressed-proof-format ASCII string.
-   Normally, the proof string would be packed with nmbrSquishProof first,
-   although it's not a requirement (in which case the compressed proof will
-   be much longer of course). */
-/* The statement number is needed because required hypotheses are
-   implicit in the compressed proof. */
-/* The returned ASCII string isn't surrounded by spaces e.g. it
-   could be "( a1i a1d ) ACBCADEF". */
-vstring compressProof(nmbrString *proof, long statemNum,
-    flag oldCompressionAlgorithm)
-{
-  vstring output = "";
-  long outputLen;
-  long outputAllocated;
-  nmbrString *saveProof = NULL_NMBRSTRING;
-  nmbrString *labelList = NULL_NMBRSTRING;
-  nmbrString *hypList = NULL_NMBRSTRING;
-  nmbrString *assertionList = NULL_NMBRSTRING;
-  nmbrString *localList = NULL_NMBRSTRING;
-  nmbrString *localLabelFlags = NULL_NMBRSTRING;
-  long hypLabels, assertionLabels, localLabels;
-  long plen, step, stmt, labelLen, lab, numchrs;
-  /* long thresh, newnumchrs, newlab; */ /* 15-Oct-05 nm No longer used */
-  long i, j, k;
-  /* flag breakFlag; */ /* 15-Oct-05 nm No longer used */
-  /* char c; */ /* 15-Oct-05 nm No longer used */
-  long lettersLen, digitsLen;
-  static char *digits = "0123456789";
-  static char *letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  static char labelChar = ':';
-
-  /* 27-Dec-2013 nm Variables for new algorithm */
-  nmbrString *explList = NULL_NMBRSTRING;
-  long explLabels;
-  nmbrString *explRefCount = NULL_NMBRSTRING;
-  nmbrString *labelRefCount = NULL_NMBRSTRING;
-  long maxExplRefCount;
-  nmbrString *explComprLen = NULL_NMBRSTRING;
-  long explSortPosition;
-  long maxExplComprLen;
-  vstring explUsedFlag = "";
-  nmbrString *explLabelLen = NULL_NMBRSTRING;
-  nmbrString *newExplList = NULL_NMBRSTRING;
-  long newExplPosition;
-  long indentation;
-  long explOffset;
-  long explUnassignedCount;
-  nmbrString *explWorth = NULL_NMBRSTRING;
-  long explWidth;
-  vstring explIncluded = "";
-
-
-  /* Compression standard with all cap letters */
-  /* (For 500-700 step proofs, we only lose about 18% of file size --
-      but the compressed proof is more pleasing to the eye) */
-  letters = "ABCDEFGHIJKLMNOPQRST"; /* LSB is base 20 */
-  digits = "UVWXY"; /* MSB's are base 5 */
-  labelChar = 'Z'; /* Was colon */
-
-  lettersLen = (long)strlen(letters);
-  digitsLen = (long)strlen(digits);
-
-  nmbrLet(&saveProof, proof); /* In case of temp. alloc. of proof */
-
-  if (g_Statement[statemNum].type != (char)p_) bug(1344);
-  plen = nmbrLen(saveProof);
-
-  /* Create the initial label list of required hypotheses */
-  nmbrLet(&labelList, g_Statement[statemNum].reqHypList);
-
-  /* Add the other statement labels to the list */
-
-  /* Warning:  The exact union algorithm is crucial here; the required
-     hypotheses MUST remain at the beginning of the list. */
-  nmbrLet(&labelList, nmbrUnion(labelList, saveProof));
-
-  /* Break the list into hypotheses, assertions, and local labels */
-  labelLen = nmbrLen(labelList);
-  nmbrLet(&hypList, nmbrSpace(labelLen));
-  nmbrLet(&assertionList, nmbrSpace(labelLen));
-  nmbrLet(&localLabelFlags, nmbrSpace(plen)); /* Warning: nmbrSpace() must
-                                                 produce a string of 0's */
-  hypLabels = 0;
-  assertionLabels = 0;
-  localLabels = 0;
-  for (lab = 0; lab < labelLen; lab++) {
-    stmt = labelList[lab];
-    if (stmt < 0) {
-      if (stmt <= -1000) {
-        if (-1000 - stmt >= plen) bug(345);
-        localLabelFlags[-1000 - stmt] = 1;
-        localLabels++;
-      } else {
-        if (stmt != -(long)'?') bug(1346);
-      }
-    } else {
-      if (g_Statement[stmt].type != (char)a_ &&
-          g_Statement[stmt].type != (char)p_) {
-        hypList[hypLabels] = stmt;
-        hypLabels++;
-      } else {
-        assertionList[assertionLabels] = stmt;
-        assertionLabels++;
-      }
-    }
-  } /* Next lab */
-  nmbrLet(&hypList, nmbrLeft(hypList, hypLabels));
-  nmbrLet(&assertionList, nmbrLeft(assertionList, assertionLabels));
-
-  /* Get list of local labels, sorted in order of declaration */
-  nmbrLet(&localList, nmbrSpace(localLabels));
-  lab = 0;
-  for (step = 0; step < plen; step++) {
-    if (localLabelFlags[step]) {
-      localList[lab] = -1000 - step;
-      lab++;
-    }
-  }
-  if (lab != localLabels) bug(1347);
-
-  /* To obtain the old algorithm, we simply skip the new label re-ordering */
-  if (oldCompressionAlgorithm) goto OLD_ALGORITHM;
-
-
-  /* 27-Dec-2013 nm */
-  /************ New algorithm to sort labels according to usage ***********/
-
-  /* This algorithm, based on an idea proposed by Mario Carneiro, sorts
-     the explicit labels so that the most-used labels occur first, optimizing
-     the use of 1-character compressed label lengths, then 2-character
-     lengths, and so on.  Also, an attempt is made to fit the label list into
-     the exact maximum current screen width, using the 0/1-knapsack
-     algorithm, so that fewer lines will result due to wasted space at the
-     end of each line with labels.  */
-
-  /* Get the list of explicit labels */
-  nmbrLet(&explList, nmbrCat(
-      /* Trim off leading implicit required hypotheses */
-      nmbrRight(hypList, g_Statement[statemNum].numReqHyp + 1),
-      /* Add in the list of assertion ($a, $p) references */
-      assertionList, NULL));
-  explLabels = nmbrLen(explList);
-
-  /* Initialize reference counts for the explicit labels */
-  nmbrLet(&explRefCount, nmbrSpace(explLabels));
-
-  /* Count the number of references to labels in the original proof */
-  /* We allocate up to statemNum, since any earlier statement could appear */
-  nmbrLet(&labelRefCount, nmbrSpace(statemNum));  /* Warning: nmbrSpace() must
-                                                 produce a string of 0's */
-  for (step = 0; step < plen; step++) { /* Scan the proof */
-    if (saveProof[step] > 0) { /* Ignore local labels and '?' */
-      if (saveProof[step] < statemNum) {
-        labelRefCount[saveProof[step]]++;
-      } else {
-        bug(1380); /* Corrupted proof should have been caught earlier */
-      }
-    }
-  }
-  maxExplRefCount = 0;  /* Largest number of reference counts found */
-  /* Populate the explict label list with the counts */
-  for (i = 0; i < explLabels; i++) {
-    explRefCount[i] = labelRefCount[explList[i]]; /* Save the ref count */
-    if (explRefCount[i] <= 0) bug(1381);
-    if (explRefCount[i] > maxExplRefCount) {
-      maxExplRefCount = explRefCount[i]; /* Update largest count */
-    }
-  }
-  /* We're done with giant labelRefCount array; deallocate */
-  nmbrLet(&labelRefCount, NULL_NMBRSTRING);
-
-  /* Assign compressed label lengths starting from most used to least
-     used label */
-  /* Initialize compressed label lengths for the explicit labels */
-  nmbrLet(&explComprLen, nmbrSpace(explLabels));
-  explSortPosition = 0;
-  maxExplComprLen = 0;
-  /* The "sorting" below has n^2 behavior; improve if is it a problem */
-  /* explSortPosition is where the label would occur if reverse-sorted
-     by reference count, for the purpose of computing the compressed
-     label length.  No actual sorting is done, since later we're
-     only interested in groups with the same compressed label length. */
-  for (i = maxExplRefCount; i >= 1; i--) {
-    for (j = 0; j < explLabels; j++) {
-      if (explRefCount[j] == i) {
-        /* Find length, numchrs, of compressed label */
-        /* If there are no req hyps, 0 = 1st label in explict list */
-        lab = g_Statement[statemNum].numReqHyp + explSortPosition;
-
-        /* The following 7 lines are from the compressed label length
-           determination algorithm below */
-        numchrs = 1;
-        k = lab / lettersLen;
-        while (1) {
-          if (!k) break;
-          numchrs++;
-          k = (k - 1) / digitsLen;
-        }
-
-        explComprLen[j] = numchrs; /* Assign the compressed label length */
-        if (numchrs > maxExplComprLen) {
-          maxExplComprLen = numchrs; /* Update maximum length */
-        }
-        explSortPosition++;
-      }
-    }
-  }
-
-  let(&explUsedFlag, string(explLabels, 'n')); /* Mark with 'y' when placed in
-                                            output label list (newExplList) */
-  nmbrLet(&explLabelLen, nmbrSpace(explLabels));
-  /* Populate list of label lengths for knapsack01() "size" */
-  for (i = 0; i < explLabels; i++) {
-    stmt = explList[i];
-    explLabelLen[i] = (long)(strlen(g_Statement[stmt].labelName)) + 1;
-                                     /* +1 accounts for space between labels */
-  }
-
-  /* Re-distribute labels in order of compressed label length, fitted to
-     line by knapsack01() algorithm */
-
-  nmbrLet(&newExplList, nmbrSpace(explLabels)); /* List in final order */
-  nmbrLet(&explWorth, nmbrSpace(explLabels));  /* "Value" for knapsack01() */
-  let(&explIncluded, string(explLabels, '?')); /* Returned by knapsack01() */
-  newExplPosition = 0; /* Counter for position in output label list */
-
-  indentation =  2 + getSourceIndentation(statemNum); /* Proof indentation */
-  explOffset = 2; /* add 2 for "( " opening parenthesis of compressed proof */
-
-  /* Fill up the output with labels in groups of increasing compressed label
-     size */
-  for (i = 1; i <= maxExplComprLen; i++) {
-    explUnassignedCount = 0; /* Unassigned at current compressed label size */
-    /* Initialize worths for knapsack01() */
-    for (j = 0; j < explLabels; j++) {
-      if (explComprLen[j] == i) {
-        if (explUsedFlag[j] == 'y') bug(1382);
-        explWorth[j] = explLabelLen[j]; /* Make worth=size so that label
-            length does not affect whether the label is chosen by knapsack01(),
-            so the only influence is whether it fits */
-        explUnassignedCount++;
-      } else { /* Not the current compressed label size */
-        explWorth[j] = -1; /* Negative worth will make knapsack avoid it */
-      }
-    }
-    while (explUnassignedCount > 0) {
-      /* Find the the amount of space available on the remainder of the line */
-      /* The +1 accounts for space after last label, which wrapping will trim */
-      /* Note that the actual line wrapping will happen with printLongLine
-         far in the future.  Here we will just put the labels in the order
-         that will cause it to wrap at the desired place. */
-      explWidth = g_screenWidth - indentation - explOffset + 1;
-
-      /* Fill in the label list output line with labels that fit best */
-      /* The knapsack01() call below is always given the entire set of
-         explicit labels, with -1 worth assigned to the ones to be avoided.
-         It would be more efficient to give it a smaller list with -1s
-         removed, if run time becomes a problem. */
-      j = knapsack01(explLabels /*#items*/,
-          explLabelLen /*array of sizes*/,
-          explWorth /*array of worths*/,
-          explWidth /*maxSize*/,
-          explIncluded /*itemIncluded return values*/);
-      /*if (j == 0) bug(1383);*/ /* j=0 is legal when it can't fit any labels
-         on the rest of the line (such as if the line only has 1 space left
-         i.e. explWidth=1) */
-      if (j < 0) bug(1383);
-
-      /* Accumulate the labels selected by knapsack01() into the output list,
-         in the same order as they appeared in the original explicit label
-         list */
-      explUnassignedCount = 0;
-      /* Scan expIncluded y/n string returned by knapsack01() */
-      for (j = 0; j < explLabels; j++) {
-        if (explIncluded[j] == 'y') { /* was chosen by knapsack01() */
-          if (explComprLen[j] != i) bug(1384); /* Other compressed length
-             shouldn't occur because -1 worth should have been rejected by
-             knapsack01() */
-          newExplList[newExplPosition] = explList[j];
-          newExplPosition++;
-          explUsedFlag[j] = 'y';
-          if (explWorth[j] == -1) bug(1385); /* knapsack01() should
-              have rejected all previously assigned labels */
-          explWorth[j] = -1; /* Negative worth will avoid it next loop iter */
-          explOffset = explOffset + explLabelLen[j];
-        } else {
-          if (explComprLen[j] == i && explUsedFlag[j] == 'n') {
-            explUnassignedCount++; /* There are still more to be assigned
-                                      at this compressed label length */
-            if (explWorth[j] != explLabelLen[j]) bug(1386); /* Sanity check */
-          }
-        }
-      }
-      if (explUnassignedCount > 0) {
-        /* If there are labels still at this level (of compressed
-           label length), so start a new line for next knapsack01() call */
-        explOffset = 0;
-      }
-    }
-  }
-  if (newExplPosition != explLabels) bug(1387); /* Labels should be exhausted */
-
-  /* The hypList and assertionList below are artificially assigned
-     for use by the continuation of the old algorithm that follows */
-
-  /* "hypList" is truncated to have only the required hypotheses with no
-     optional ones */
-  nmbrLet(&hypList, nmbrLeft(hypList, g_Statement[statemNum].numReqHyp));
-  /* "assertionList" will have both the optional hypotheses and the assertions,
-     reordered */
-  nmbrLet(&assertionList, newExplList);
-
-  /********************** End of new algorithm ****************************/
-
-
- OLD_ALGORITHM:
-  /* Combine all label lists */
-  nmbrLet(&labelList, nmbrCat(hypList, assertionList, localList, NULL));
-
-  /* Create the compressed proof */
-  outputLen = 0;
-#define COMPR_INC 1000
-  let(&output, space(COMPR_INC));
-  outputAllocated = COMPR_INC;
-
-  plen = nmbrLen(saveProof);
-  for (step = 0; step < plen; step++) {
-
-    stmt = saveProof[step];
-
-    if (stmt == -(long)'?') {
-      /* Unknown step */
-      if (outputLen + 1 > outputAllocated) {
-        /* Increase allocation of the output string */
-        let(&output, cat(output, space(outputLen + 1 - outputAllocated +
-            COMPR_INC), NULL));
-        outputAllocated = outputLen + 1 + COMPR_INC; /* = (long)strlen(output) */
-        /* CPU-intensive bug check; enable only if required: */
-        /* if (outputAllocated != (long)strlen(output)) bug(1348); */
-        if (output[outputAllocated - 1] == 0 ||
-            output[outputAllocated] != 0) bug(1348); /* 13-Oct-05 nm */
-      }
-      output[outputLen] = '?';
-      outputLen++;
-      continue;
-    }
-
-    lab = nmbrElementIn(1, labelList, stmt);
-    if (!lab) bug(1349);
-    lab--; /* labelList array starts at 0, not 1 */
-
-    /* Determine the # of chars in the compressed label */
-    /* 15-Oct-05 nm - Obsolete (skips from YT to UVA, missing UUA) */
-    /*
-    numchrs = 1;
-    if (lab > lettersLen - 1) {
-      / * It requires a numeric prefix * /
-      i = lab / lettersLen;
-      while(i) {
-        numchrs++;
-        if (i > digitsLen) {
-          i = i / digitsLen;
-        } else {
-          i = 0; / * MSB is sort of 'mod digitsLen+1' since
-                                a blank is the MSB in the case of one
-                                fewer characters in the label * /
-        }
-      }
-    }
-    */
-
-    /* 15-Oct-05 nm - A corrected algorithm was provided by Marnix Klooster. */
-    /* For encoding we'd get (starting with n, counting from 1):
-        * start with the empty string
-        * prepend (n-1) mod 20 + 1 as character using 1->'A' .. 20->'T'
-        * n := (n-1) div 20
-        * while n > 0:
-           * prepend (n-1) mod 5 + 1 as character using 1->'U' .. 5->'Y'
-           * n := (n-1) div 5 */
-    if (lab < 0) bug(1373);
-    numchrs = 1;
-    i = lab / lettersLen;
-    while (1) {
-      if (!i) break;
-      numchrs++;
-      i = (i - 1) / digitsLen;
-    }
-
-    /* Add the compressed label to the proof */
-    if (outputLen + numchrs > outputAllocated) {
-      /* Increase allocation of the output string */
-      let(&output, cat(output, space(outputLen + numchrs - outputAllocated +
-          COMPR_INC), NULL));
-      outputAllocated = outputLen + numchrs + COMPR_INC; /* = (long)strlen(output) */
-      /* CPU-intensive bug check; enable only if required: */
-      /* if (outputAllocated != (long)strlen(output)) bug(1350); */
-      if (output[outputAllocated - 1] == 0 ||
-          output[outputAllocated] != 0) bug(1350); /* 13-Oct-05 nm */
-    }
-    outputLen = outputLen + numchrs;
-
-    /* 15-Oct-05 nm - Obsolete (skips from YT to UVA, missing UUA) */
-    /*
-    j = lab;
-    for (i = 0; i < numchrs; i++) { / * Create from LSB to MSB * /
-      if (!i) {
-        c = letters[j % lettersLen];
-        j = j / lettersLen;
-      } else {
-        if (j > digitsLen) {
-          c = digits[j % digitsLen];
-          j = j / digitsLen;
-        } else {
-          c = digits[j - 1]; / * MSB is sort of 'mod digitsLen+1' since
-                                a blank is the MSB in the case of one
-                                fewer characters in the label * /
-        }
-      }
-      output[outputLen - i - 1] = c;
-    } / * Next i * /
-    */
-
-    /* 15-Oct-05 nm - A corrected algorithm was provided by Marnix Klooster. */
-    /* For encoding we'd get (starting with n, counting from 1):
-        * start with the empty string
-        * prepend (n-1) mod 20 + 1 as character using 1->'A' .. 20->'T'
-        * n := (n-1) div 20
-        * while n > 0:
-           * prepend (n-1) mod 5 + 1 as character using 1->'U' .. 5->'Y'
-           * n := (n-1) div 5 */
-    j = lab + 1; /* lab starts at 0, not 1 */
-    i = 1;
-    output[outputLen - i] = letters[(j - 1) % lettersLen];
-    j = (j - 1) / lettersLen;
-    while (1) {
-      if (!j) break;
-      i++;
-      output[outputLen - i] = digits[(j - 1) % digitsLen];
-      j = (j - 1) / digitsLen;
-    }
-    if (i != numchrs) bug(1374);
-
-
-    /***** Local labels ******/
-    /* See if a local label is declared in this step */
-    if (!localLabelFlags[step]) continue;
-    if (outputLen + 1 > outputAllocated) {
-      /* Increase allocation of the output string */
-      let(&output, cat(output, space(outputLen + 1 - outputAllocated +
-          COMPR_INC), NULL));
-      outputAllocated = outputLen + 1 + COMPR_INC; /* = (long)strlen(output) */
-      /* CPU-intensive bug check due to strlen; enable only if required: */
-      /* if (outputAllocated != (long)strlen(output)) bug(1352); */
-      if (output[outputAllocated - 1] == 0 ||
-          output[outputAllocated] != 0) bug(1352); /* 13-Oct-05 nm */
-    }
-    output[outputLen] = labelChar;
-    outputLen++;
-
-  } /* Next step */
-
-  /* Create the final compressed proof */
-  let(&output, cat("( ", nmbrCvtRToVString(nmbrCat(
-      /* Trim off leading implicit required hypotheses */
-      nmbrRight(hypList, g_Statement[statemNum].numReqHyp + 1),
-      assertionList, NULL),
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum used only if explicitTargets*/),
-      " ) ", left(output, outputLen), NULL));
-
-  nmbrLet(&saveProof, NULL_NMBRSTRING);
-  nmbrLet(&labelList, NULL_NMBRSTRING);
-  nmbrLet(&hypList, NULL_NMBRSTRING);
-  nmbrLet(&assertionList, NULL_NMBRSTRING);
-  nmbrLet(&localList, NULL_NMBRSTRING);
-  nmbrLet(&localLabelFlags, NULL_NMBRSTRING);
-
-  /* Deallocate arrays for new algorithm */  /* 27-Dec-2013 nm */
-
-  nmbrLet(&explList, NULL_NMBRSTRING);
-  nmbrLet(&explRefCount, NULL_NMBRSTRING);
-  nmbrLet(&labelRefCount, NULL_NMBRSTRING);
-  nmbrLet(&explComprLen, NULL_NMBRSTRING);
-  let(&explUsedFlag, "");
-  nmbrLet(&explLabelLen, NULL_NMBRSTRING);
-  nmbrLet(&newExplList, NULL_NMBRSTRING);
-  nmbrLet(&explWorth, NULL_NMBRSTRING);
-  let(&explIncluded, "");
-
-  makeTempAlloc(output); /* Flag it for deallocation */
-  return(output);
-} /* compressProof */
-
-
-/* Added 11-Sep-2016 nm */
-/* Compress the input proof, create the ASCII compressed proof,
-   and return its size in bytes. */
-/* TODO: call this in MINIMIZE_WITH in metamath.c */
-long compressedProofSize(nmbrString *proof, long statemNum) {
-  vstring tmpStr = "";
-  nmbrString *tmpNmbr = NULL_NMBRSTRING;
-  long bytes;
-  nmbrLet(&tmpNmbr, nmbrSquishProof(proof));
-  let(&tmpStr, compressProof(tmpNmbr,
-          statemNum, /* statement being proved */
-          0 /* don't use old algorithm (this will become obsolete) */
-          ));
-  bytes = (long)strlen(tmpStr);
-  /* Deallocate memory */
-  let(&tmpStr, "");
-  nmbrLet(&tmpNmbr, NULL_NMBRSTRING);
-  return bytes;
-} /* compressedProofSize */
-
-
-
-/*******************************************************************/
-/*********** Pointer string functions ******************************/
-/*******************************************************************/
-
-long g_pntrTempAllocStackTop = 0;     /* Top of stack for pntrTempAlloc functon */
-long g_pntrStartTempAllocStack = 0;   /* Where to start freeing temporary allocation
-                                    when pntrLet() is called (normally 0, except in
-                                    special nested vstring functions) */
-pntrString *pntrTempAllocStack[M_MAX_ALLOC_STACK];
-
-
-pntrString *pntrTempAlloc(long size)
-                                /* pntrString memory allocation/deallocation */
-{
-  /* When "size" is >0, "size" instances of pntrString are allocated. */
-  /* When "size" is 0, all memory previously allocated with this */
-  /* function is deallocated, down to g_pntrStartTempAllocStack. */
-  /* int i; */   /* 11-Jul-2014 WL old code deleted */
-  if (size) {
-    if (g_pntrTempAllocStackTop>=(M_MAX_ALLOC_STACK-1))
-      /*??? Fix to allocate more */
-      outOfMemory("#109 (pntrString stack array)");
-    if (!(pntrTempAllocStack[g_pntrTempAllocStackTop++]=poolMalloc(size
-        *(long)(sizeof(pntrString)))))
-      /* outOfMemory("#110 (pntrString stack)"); */ /*???Unnec. w/ poolMalloc*/
-/*E*/db2=db2+(size)*(long)(sizeof(pntrString));
-    return (pntrTempAllocStack[g_pntrTempAllocStackTop-1]);
-  } else {
-    /* 11-Jul-2014 WL old code deleted */
-    /*
-    for (i=g_pntrStartTempAllocStack; i < g_pntrTempAllocStackTop; i++) {
-/@E@/db2=db2-(pntrLen(pntrTempAllocStack[i])+1)*(long)(sizeof(pntrString));
-      poolFree(pntrTempAllocStack[i]);
-    }
-    */
-    /* 11-Jul-2014 WL new code */
-    while(g_pntrTempAllocStackTop != g_pntrStartTempAllocStack) {
-/*E*/db2=db2-(pntrLen(pntrTempAllocStack[g_pntrTempAllocStackTop-1])+1)
-/*E*/                                              *(long)(sizeof(pntrString));
-      poolFree(pntrTempAllocStack[--g_pntrTempAllocStackTop]);
-    }
-    /* end of 11-Jul-2014 WL new code */
-    g_pntrTempAllocStackTop=g_pntrStartTempAllocStack;
-    return (0);
-  }
-}
-
-
-/* Make string have temporary allocation to be released by next pntrLet() */
-/* Warning:  after pntrMakeTempAlloc() is called, the pntrString may NOT be
-   assigned again with pntrLet() */
-void pntrMakeTempAlloc(pntrString *s)
-{
-    if (g_pntrTempAllocStackTop>=(M_MAX_ALLOC_STACK-1)) {
-      printf(
-      "*** FATAL ERROR ***  Temporary pntrString stack overflow in pntrMakeTempAlloc()\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-      bug(1370);
-    }
-    if (s[0] != NULL) { /* Don't do it if pntrString is empty */
-      pntrTempAllocStack[g_pntrTempAllocStackTop++] = s;
-    }
-/*E*/db2=db2+(pntrLen(s)+1)*(long)(sizeof(pntrString));
-/*E*/db3=db3-(pntrLen(s)+1)*(long)(sizeof(pntrString));
-}
-
-
-void pntrLet(pntrString **target,pntrString *source)
-/* pntrString assignment */
-/* This function must ALWAYS be called to make assignment to */
-/* a pntrString in order for the memory cleanup routines, etc. */
-/* to work properly.  If a pntrString has never been assigned before, */
-/* it is the user's responsibility to initialize it to NULL_PNTRSTRING (the */
-/* null string). */
-{
-  long targetLength,sourceLength;
-  long targetAllocLen;
-  long poolDiff;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  sourceLength=pntrLen(source);  /* Save its actual length */
-  targetLength=pntrLen(*target);  /* Save its actual length */
-  targetAllocLen=pntrAllocLen(*target); /* Save target's allocated length */
-/*E*/if (targetLength) {
-/*E*/  /* printf("Deleting %s\n",cvtMToVString(*target,0)); */
-/*E*/  db3 = db3 - (targetLength+1)*(long)(sizeof(pntrString));
-/*E*/}
-/*E*/if (sourceLength) {
-/*E*/  /* printf("Adding %s\n",cvtMToVString(source,0)); */
-/*E*/  db3 = db3 + (sourceLength+1)*(long)(sizeof(pntrString));
-/*E*/}
-  if (targetAllocLen) {
-    if (sourceLength) { /* source and target are both nonzero length */
-
-      if (targetAllocLen >= sourceLength) { /* Old string has room for new one */
-        pntrCpy(*target,source); /* Re-use the old space to save CPU time */
-
-        /* Memory pool handling */
-        /* Assign actual size of target string */
-        poolDiff = ((long *)(*target))[-1] - ((long *)source)[-1];
-        ((long *)(*target))[-1] = ((long *)source)[-1];
-        /* If actual size of target string is less than allocated size, we
-           may have to add it to the used pool */
-        if (((long *)(*target))[-1] != ((long *)(*target))[-2]) {
-          if (((long *)(*target))[-1] > ((long *)(*target))[-2]) bug(1359);
-          if (((long *)(*target))[-3] == -1) {
-            /* It's not already in the used pool, so add it */
-            addToUsedPool(*target);
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0aa: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-          } else {
-            /* Adjust free space independently */
-            poolTotalFree = poolTotalFree + poolDiff;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0ab: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-          }
-        } else {
-          if (((long *)(*target))[-3] != -1) {
-            /* It's in the pool (but all allocated space coincidentally used) */
-            /* Adjust free space independently */
-            poolTotalFree = poolTotalFree + poolDiff;
-          }
-        }
-
-
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0a: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-      } else {
-        /* Free old string space and allocate new space */
-        poolFree(*target);  /* Free old space */
-        /* *target=poolMalloc((sourceLength + 1) * sizeof(pntrString)); */
-        *target=poolMalloc((sourceLength + 1) * (long)(sizeof(pntrString)) * 2);
-                        /* Allocate new space --
-                            We are replacing a smaller string with a larger one;
-                            assume it is growing, and allocate twice as much as
-                            needed. */
-        /*if (!*target) outOfMemory("#111 (pntrString)");*/ /*???Unnec. w/ poolMalloc*/
-        pntrCpy(*target,source);
-
-        /* Memory pool handling */
-        /* Assign actual size of target string */
-        poolDiff = ((long *)(*target))[-1] - ((long *)source)[-1];
-        ((long *)(*target))[-1] = ((long *)source)[-1];
-        /* If actual size of target string is less than allocated size, we
-           may have to add it to the used pool */
-        /* (The 1st 'if' is redundant with target doubling above) */
-        if (((long *)(*target))[-1] != ((long *)(*target))[-2]) {
-          if (((long *)(*target))[-1] > ((long *)(*target))[-2]) bug(1360);
-          if (((long *)(*target))[-3] == -1) {
-            /* It's not already in the used pool, so add it */
-            addToUsedPool(*target);
-          } else {
-            /* Adjust free space independently */
-            poolTotalFree = poolTotalFree + poolDiff;
-          }
-        } else {
-          if (((long *)(*target))[-3] != -1) {
-            /* It's in the pool (but all allocated space coincidentally used) */
-            /* Adjust free space independently */
-            poolTotalFree = poolTotalFree + poolDiff;
-          }
-        }
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0b: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-
-      }
-
-    } else {    /* source is 0 length, target is not */
-      poolFree(*target);
-      *target= NULL_PNTRSTRING;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0c: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    }
-  } else {
-    if (sourceLength) { /* target is 0 length, source is not */
-      *target=poolMalloc((sourceLength + 1) * (long)(sizeof(pntrString)));
-                        /* Allocate new space */
-      /* if (!*target) outOfMemory("#112 (pntrString)"); */ /*???Unnec. w/ poolMalloc*/
-      pntrCpy(*target,source);
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0d: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    } else {    /* source and target are both 0 length */
-      /* *target= NULL_PNTRSTRING; */ /* Redundant */
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0e: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-    }
-  }
-
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k1: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  pntrTempAlloc(0); /* Free up temporary strings used in expression computation*/
-
-}
-
-
-
-pntrString *pntrCat(pntrString *string1,...) /* String concatenation */
-{
-  va_list ap;   /* Declare list incrementer */
-  pntrString *arg[M_MAX_CAT_ARGS];        /* Array to store arguments */
-  long argLength[M_MAX_CAT_ARGS];       /* Array to store argument lengths */
-  int numArgs=1;        /* Define "last argument" */
-  int i;
-  long j;
-  pntrString *ptr;
-  arg[0]=string1;       /* First argument */
-
-  va_start(ap,string1); /* Begin the session */
-  while ((arg[numArgs++]=va_arg(ap,pntrString *)))
-        /* User-provided argument list must terminate with NULL */
-    if (numArgs>=M_MAX_CAT_ARGS-1) {
-      printf("*** FATAL ERROR ***  Too many cat() arguments\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-      bug(1371);
-    }
-  va_end(ap);           /* End var args session */
-
-  numArgs--;    /* The last argument (0) is not a string */
-
-  /* Find out the total string length needed */
-  j = 0;
-  for (i = 0; i < numArgs; i++) {
-    argLength[i]=pntrLen(arg[i]);
-    j=j+argLength[i];
-  }
-  /* Allocate the memory for it */
-  ptr=pntrTempAlloc(j+1);
-  /* Move the strings into the newly allocated area */
-  j = 0;
-  for (i = 0; i < numArgs; i++) {
-    pntrCpy(ptr+j,arg[i]);
-    j=j+argLength[i];
-  }
-  return (ptr);
-
-}
-
-
-
-/* Find out the length of a pntrString */
-long pntrLen(pntrString *s)
-{
-  /* Assume it's been allocated with poolMalloc. */
-  return ((((long *)s)[-1] - (long)(sizeof(pntrString)))
-      / (long)(sizeof(pntrString)));
-}
-
-
-/* Find out the allocated length of a pntrString */
-long pntrAllocLen(pntrString *s)
-{
-  return ((((long *)s)[-2] - (long)(sizeof(pntrString)))
-    / (long)(sizeof(pntrString)));
-}
-
-/* Set the actual size field in a pntrString allocated with poolFixedMalloc() */
-/* Use this if "zapping" a pntrString element with -1 to reduce its length. */
-/* Note that the pntrString will not be moved to the "used pool", even if
-   zapping its length results in free space; thus the free space will never
-   get recovered unless done by the caller or poolFree is called.  (This is
-   done on purpose so the caller can know what free space is left.) */
-/* ???Note that pntrZapLen's not moving string to used pool wastes potential
-   space when called by the routines in this module.  Effect should be minor. */
-void pntrZapLen(pntrString *s, long length) {
-  if (((long *)s)[-3] != -1) {
-    /* It's already in the used pool, so adjust free space tally */
-    poolTotalFree = poolTotalFree + ((long *)s)[-1]
-        - (length + 1) * (long)(sizeof(pntrString));
-  }
-  ((long *)s)[-1] = (length + 1) * (long)(sizeof(pntrString));
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("l: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-}
-
-
-/* Copy a string to another (pre-allocated) string */
-/* Dangerous for general purpose use */
-void pntrCpy(pntrString *s, pntrString *t)
-{
-  long i;
-  i = 0;
-  while (t[i] != NULL) { /* End of string -- pntrRight depends on it!! */
-    s[i] = t[i];
-    i++;
-  }
-  s[i] = t[i]; /* End of string */
-}
-
-
-/* Copy a string to another (pre-allocated) string */
-/* Like strncpy, only the 1st n characters are copied. */
-/* Dangerous for general purpose use */
-void pntrNCpy(pntrString *s,pntrString *t,long n)
-{
-  long i;
-  i = 0;
-  while (t[i] != NULL) { /* End of string -- pntrSeg, pntrMid depend on it!! */
-    if (i >= n) break;
-    s[i] = t[i];
-    i++;
-  }
-  s[i] = t[i]; /* End of string */
-}
-
-
-/* Compare two strings */
-/* Unlike strcmp, this returns a 1 if the strings are equal
-   and 0 otherwise. */
-/* Only the pointers are compared.  If pointers are different,
-   0 will be returned, even if the things pointed to have same contents. */
-int pntrEq(pntrString *s,pntrString *t)
-{
-  long i;
-  for (i = 0; s[i] == t[i]; i++)
-    if (s[i] == NULL) /* End of string */
-      return 1;
-  return 0;
-}
-
-
-/* Extract sin from character position start to stop into sout */
-pntrString *pntrSeg(pntrString *sin, long start, long stop)
-{
-  pntrString *sout;
-  long length;
-  if (start < 1 ) start = 1;
-  if (stop < 1 ) stop = 0;
-  length = stop - start + 1;
-  if (length < 0) length = 0;
-  sout = pntrTempAlloc(length + 1);
-  pntrNCpy(sout, sin + start - 1, length);
-  sout[length] = *NULL_PNTRSTRING;
-  return (sout);
-}
-
-/* Extract sin from character position start for length len */
-pntrString *pntrMid(pntrString *sin, long start, long length)
-{
-  pntrString *sout;
-  if (start < 1) start = 1;
-  if (length < 0) length = 0;
-  sout = pntrTempAlloc(length + 1);
-  pntrNCpy(sout, sin + start-1, length);
-  sout[length] = *NULL_PNTRSTRING;
-  return (sout);
-}
-
-/* Extract leftmost n characters */
-pntrString *pntrLeft(pntrString *sin,long n)
-{
-  pntrString *sout;
-  if (n < 0) n = 0;
-  sout=pntrTempAlloc(n+1);
-  pntrNCpy(sout,sin,n);
-  sout[n] = *NULL_PNTRSTRING;
-  return (sout);
-}
-
-/* Extract after character n */
-pntrString *pntrRight(pntrString *sin,long n)
-{
-  /*??? We could just return &sin[n-1], but this is safer for debugging. */
-  pntrString *sout;
-  long i;
-  if (n < 1) n = 1;
-  i = pntrLen(sin);
-  if (n > i) return (NULL_PNTRSTRING);
-  sout = pntrTempAlloc(i - n + 2);
-  pntrCpy(sout, &sin[n-1]);
-  return (sout);
-}
-
-
-/* Allocate and return an "empty" string n "characters" long */
-/* Each entry in the allocated array points to an empty vString. */
-pntrString *pntrSpace(long n)
-{
-  pntrString *sout;
-  long j = 0;
-  if (n<0) bug(1360);
-  sout=pntrTempAlloc(n+1);
-  while (j<n) {
-    /* Initialize all fields */
-    sout[j] = "";
-    j++;
-  }
-  sout[j] = *NULL_PNTRSTRING; /* Flags end of string */
-  return (sout);
-}
-
-/* Allocate and return an "empty" string n "characters" long
-   initialized to nmbrStrings instead of vStrings */
-pntrString *pntrNSpace(long n)
-{
-  pntrString *sout;
-  long j = 0;
-  if (n<0) bug(1361);
-  sout=pntrTempAlloc(n+1);
-  while (j<n) {
-    /* Initialize all fields */
-    sout[j] = NULL_NMBRSTRING;
-    j++;
-  }
-  sout[j] = *NULL_PNTRSTRING; /* Flags end of string */
-  return (sout);
-}
-
-/* Allocate and return an "empty" string n "characters" long
-   initialized to pntrStrings instead of vStrings */
-pntrString *pntrPSpace(long n)
-{
-  pntrString *sout;
-  long j = 0;
-  if (n<0) bug(1372);
-  sout=pntrTempAlloc(n+1);
-  while (j<n) {
-    /* Initialize all fields */
-    sout[j] = NULL_PNTRSTRING;
-    j++;
-  }
-  sout[j] = *NULL_PNTRSTRING; /* Flags end of string */
-  return (sout);
-}
-
-/* Search for string2 in string1 starting at start_position */
-long pntrInstr(long start_position,pntrString *string1,
-  pntrString *string2)
-{
-   long ls1,ls2,i,j;
-   if (start_position<1) start_position=1;
-   ls1=pntrLen(string1);
-   ls2=pntrLen(string2);
-   for (i=start_position - 1; i <= ls1 - ls2; i++) {
-     for (j = 0; j<ls2; j++) {
-       if (string1[i+j] != string2[j])
-         break;
-     }
-     if (j == ls2) return (i+1);
-   }
-   return (0);
-
-}
-
-/* Search for string2 in string 1 in reverse starting at start_position */
-/* (Reverse pntrInstr) */
-long pntrRevInstr(long start_position,pntrString *string1,
-    pntrString *string2)
-{
-   long ls1,ls2;
-   pntrString *tmp = NULL_PNTRSTRING;
-   ls1=pntrLen(string1);
-   ls2=pntrLen(string2);
-   if (start_position>ls1-ls2+1) start_position=ls1-ls2+2;
-   if (start_position<1) return 0;
-   while (!pntrEq(string2,pntrMid(string1,start_position,ls2))) {
-     start_position--;
-     pntrLet(&tmp,NULL_PNTRSTRING);
-                /* Clear pntrString buffer to prevent overflow caused by "mid" */
-     if (start_position < 1) return 0;
-   }
-   return (start_position);
-}
-
-
-/* Add a single null string element to a pntrString - faster than pntrCat */
-pntrString *pntrAddElement(pntrString *g)
-{
-  long length;
-  pntrString *v;
-  length = pntrLen(g);
-  v = pntrTempAlloc(length + 2);
-  pntrCpy(v, g);
-  v[length] = "";
-  v[length + 1] = *NULL_PNTRSTRING;
-/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("bbg3: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
-  return(v);
-}
-
-
-/* Add a single null pntrString element to a pntrString -faster than pntrCat */
-pntrString *pntrAddGElement(pntrString *g)
-{
-  long length;
-  pntrString *v;
-  length = pntrLen(g);
-  v = pntrTempAlloc(length + 2);
-  pntrCpy(v, g);
-  v[length] = NULL_PNTRSTRING;
-  v[length + 1] = *NULL_PNTRSTRING;
-  return(v);
-}
-
-
-/*******************************************************************/
-/*********** Miscellaneous utility functions ***********************/
-/*******************************************************************/
-
-
-/* 0/1 knapsack algorithm */
-/* Returns the maximum worth (value) for items that can fit into maxSize */
-/* itemIncluded[] will be populated with 'y'/'n' if item included/excluded */
-long knapsack01(long items, /* # of items available to populate knapsack */
-    long *size, /* size of item 0,...,items-1 */
-    long *worth, /* worth (value) of item 0,...,items-1 */
-    long maxSize, /* size of knapsack (largest total size that will fit) */
-    char *itemIncluded /* output: 'y'/'n' if item 0..items-1 incl/excluded */)
-    {
-  long witem, wsize, a, b;
-
-  /* Maximum worth that can be attained for given #items and size */
-  long **maxWorth;  /* 2d matrix */
-  maxWorth = alloc2DMatrix((size_t)items + 1, (size_t)maxSize + 1);
-
-  /* This may run faster for applications that have hard-coded limits
-#define KS_MAX_ITEMS 100
-#define KS_MAX_SIZE 200
-  static long maxWorth[KS_MAX_ITEMS + 1][KS_MAX_SIZE + 1];
-  if (items > KS_MAX_ITEMS) {
-    printf("matrix item overflow\n"); exit(1);
-  }
-  if (maxSize > KS_MAX_SIZE) {
-    printf("matrix size overflow\n"); exit(1);
-  }
-  */
-
-  /* Populate the maximum worth matrix */
-  for (wsize = 0; wsize <= maxSize; wsize++) {
-    maxWorth[0][wsize] = 0;
-  }
-  for (witem = 1; witem <= items; witem++) {
-    for (wsize = 0; wsize <= maxSize; wsize++) {
-      if (wsize >= size[witem - 1]) {
-        /* Item witem can be part of the solution */
-        a = maxWorth[witem - 1][wsize];
-        b = maxWorth[witem - 1][wsize - size[witem - 1]] + worth[witem - 1];
-        /* Choose the case with greater value */
-        maxWorth[witem][wsize] = (a > b) ? a : b;  /* max(a,b) */
-      } else {
-        /* Item witem can't be part of the solution, otherwise total size
-           would exceed the intermediate size wsize. */
-        maxWorth[witem][wsize] = maxWorth[witem - 1][wsize];
-      }
-    }
-  }
-
-  /* Find the included items */
-  wsize = maxSize;
-  for (witem = items; witem > 0; witem--) {
-    itemIncluded[witem - 1] = 'n'; /* Initialize as excluded */
-    if (wsize > 0) {
-      if (maxWorth[witem][wsize] != maxWorth[witem - 1][wsize]) {
-        itemIncluded[witem - 1] = 'y'; /* Include the item */
-        wsize = wsize - size[witem - 1];
-      }
-    }
-  }
-
-  a = maxWorth[items][maxSize]; /* Final maximum worth */
-  free2DMatrix(maxWorth, (size_t)items + 1 /*, maxSize + 1*/);
-  return a;
-} /* knapsack01 */
-
-
-/* Allocate a 2-dimensional long integer matrix */
-/* Warning:  only entries 0,...,xsize-1 and 0,...,ysize-1 are allocated;
-   don't use entry xsize or ysize! */
-long **alloc2DMatrix(size_t xsize, size_t ysize)
-{
-  long **matrix;
-  long i;
-  matrix = malloc(xsize * sizeof(long *));
-  if (matrix == NULL) {
-    fprintf(stderr,"?FATAL ERROR 1376 Out of memory\n");
-    exit(1);
-  }
-  for (i = 0; i < (long)xsize; i++) {
-    matrix[i] = malloc(ysize * sizeof(long));
-    if (matrix[i] == NULL) {
-      fprintf(stderr,"?FATAL ERROR 1377 Out of memory\n");
-      exit(1);
-    }
-  }
-  return matrix;
-} /* alloc2DMatrix */
-
-
-/* Free a 2-dimensional long integer matrix */
-/* Note: the ysize argument isn't used, but is commented out as
-   a reminder so the caller doesn't confuse x and y */
-void free2DMatrix(long **matrix, size_t xsize /*, size_t ysize*/)
-{
-  long i;
-  for (i = (long)xsize - 1; i >= 0; i--) {
-    if (matrix[i] == NULL) bug(1378);
-    free(matrix[i]);
-  }
-  if (matrix == NULL) bug(1379);
-  free(matrix);
-  return;
-} /* free2DMatrix */
-
-
-/* Returns the amount of indentation of a statement label.  Used to
-   determine how much to indent a saved proof. */
-long getSourceIndentation(long statemNum) {
-  char *fbPtr; /* Source buffer pointer */
-  char *startLabel;
-  long indentation = 0;
-
-  fbPtr = g_Statement[statemNum].mathSectionPtr;
-  if (fbPtr[0] == 0) return 0;
-  startLabel = g_Statement[statemNum].labelSectionPtr;
-  if (startLabel[0] == 0) return 0;
-  while (1) { /* Go back to first line feed prior to the label */
-    if (fbPtr <= startLabel) break;
-    if (fbPtr[0] == '\n') break;
-    if (fbPtr[0] == ' ') {
-      indentation++; /* Space increments indentation */
-    } else {
-      indentation = 0; /* Non-space (i.e. a label character) resets back to 0 */
-    }
-    fbPtr--;
-  }
-  return indentation;
-} /* getSourceIndentation */
-
-
-/* Returns the last embedded comment (if any) in the label section of
-   a statement.  This is used to provide the user with information in the SHOW
-   STATEMENT command.  The caller must deallocate the result. */
-vstring getDescription(long statemNum) {
-  vstring description = "";
-  long p1, p2;
-
-  let(&description, space(g_Statement[statemNum].labelSectionLen));
-  memcpy(description, g_Statement[statemNum].labelSectionPtr,
-      (size_t)(g_Statement[statemNum].labelSectionLen));
-  p1 = rinstr(description, "$(");
-  p2 = rinstr(description, "$)");
-  if (p1 == 0 || p2 == 0 || p2 < p1) {
-    let(&description, "");
-    return description;
-  }
-  let(&description, edit(seg(description, p1 + 2, p2 - 1),
-      8 + 128 /* discard leading and trailing blanks */));
-  return description;
-
-  /* 3-May-2017 nm Old code may have been somewhat faster, but it doesn't
-     work when g_Statement[statemNum].labelSectionChanged */
-  /************* deleted *******
-  char @fbPtr; /@ Source buffer pointer @/
-  vstring description = "";
-  char @startDescription;
-  char @endDescription;
-  char @startLabel;
-
-  fbPtr = g_Statement[statemNum].mathSectionPtr;
-  if (!fbPtr[0]) return (description);
-  startLabel = g_Statement[statemNum].labelSectionPtr;
-  if (!startLabel[0]) return (description);
-  endDescription = NULL;
-  while (1) { /@ Get end of embedded comment @/
-    if (fbPtr <= startLabel) break;
-    if (fbPtr[0] == '$' && fbPtr[1] == ')') {
-      endDescription = fbPtr;
-      break;
-    }
-    fbPtr--;
-  }
-  if (!endDescription) return (description); /@ No embedded comment @/
-  while (1) { /@ Get start of embedded comment @/
-    if (fbPtr < startLabel) bug(216);
-    if (fbPtr[0] == '$' && fbPtr[1] == '(') {
-      startDescription = fbPtr + 2;
-      break;
-    }
-    fbPtr--;
-  }
-  let(&description, space(endDescription - startDescription));
-  memcpy(description, startDescription,
-      (size_t)(endDescription - startDescription));
-  if (description[endDescription - startDescription - 1] == '\n') {
-    /@ Trim trailing new line @/
-    let(&description, left(description, endDescription - startDescription - 1));
-  }
-  /@ Discard leading and trailing blanks @/
-  let(&description, edit(description, 8 + 128));
-  return (description);
-  *********** end of 3-May-2017 deletion *****/
-
-} /* getDescription */
-
-
-/* 24-Aug-2020 nm */
-/* Returns the label section of a statement with all comments except the
-   last removed.  Unlike getDescription, this function returns the comment
-   surrounded by $( and $) as well as the leading indentation space
-   and everything after this comment (such as the actual label).
-   Since this is used for arbitrary (other than $a, $p) statements by the
-   EXPAND command, we also suppress section headers if they are the last
-   comment.  The caller must deallocate the result. */
-vstring getDescriptionAndLabel(long stmt) {
-  vstring descriptionAndLabel = "";
-  long p1, p2;
-  flag dontUseComment = 0; /* 12-Sep-2020 nm */
-
-  let(&descriptionAndLabel, space(g_Statement[stmt].labelSectionLen));
-  memcpy(descriptionAndLabel, g_Statement[stmt].labelSectionPtr,
-      (size_t)(g_Statement[stmt].labelSectionLen));
-  p1 = rinstr(descriptionAndLabel, "$(");
-  p2 = rinstr(descriptionAndLabel, "$)");
-  if (p1 == 0 || p2 == 0 || p2 < p1) {
-    /* The statement has no comment; just return the label and
-       surrounding spacing if any */
-    return descriptionAndLabel;
-  }
-  /* Search backwards for non-space or beginning of string */
-  p1--;
-  while (p1 != 0) {
-    if (descriptionAndLabel[p1 - 1] != ' '
-          && descriptionAndLabel[p1 - 1] != '\n') break;
-    p1--;
-  }
-  let(&descriptionAndLabel, right(descriptionAndLabel, p1 + 1));
-  /* Ignore descriptionAndLabels that are section headers */
-  /* TODO: make this more precise here and in mmwtex.c - use 79-char decorations? */
-  if (instr(1, descriptionAndLabel, cat("\n", TINY_DECORATION, NULL)) != 0
-      || instr(1, descriptionAndLabel, cat("\n", SMALL_DECORATION, NULL)) != 0
-      || instr(1, descriptionAndLabel, cat("\n", BIG_DECORATION, NULL)) != 0
-      || instr(1, descriptionAndLabel, cat("\n", HUGE_DECORATION, NULL)) != 0) {
-    /*let(&descriptionAndLabel, "");*/
-    dontUseComment = 1; /* 12-Sep-2020 nm */
-  }
-  /* Remove comments with file inclusion markup */
-  if (instr(1, descriptionAndLabel, "$[") != 0) {
-    /*let(&descriptionAndLabel, "");*/
-    dontUseComment = 1; /* 12-Sep-2020 nm */
-  }
-
-  /* Remove comments with $j markup */
-  if (instr(1, descriptionAndLabel, "$j") != 0) {
-    /*let(&descriptionAndLabel, "");*/
-    dontUseComment = 1; /* 12-Sep-2020 nm */
-  }
-
-  /****** deleted 12-Sep-2020
-  /@ If the cleaned description is empty, e.g. ${ after a header,
-     add in spaces corresponding to the scope @/
-  if (descriptionAndLabel[0] == 0) {
-    let(&descriptionAndLabel, space(2 * (g_Statement[stmt].scope + 1)));
-  }
-  *******/
-
-  if (dontUseComment == 1) {
-    /* Get everything that follows the comment */
-    p2 = rinstr(descriptionAndLabel, "$)");
-    if (p2 == 0) bug(1401); /* Should have exited earlier if no "$)" */
-    let(&descriptionAndLabel, right(descriptionAndLabel, p2 + 2));
-  }
-
-  return descriptionAndLabel;
-} /* getDescriptionAndLabel */
-
-
-/**** Deleted 12-Sep-2020 nm
-/@ 24-Aug-2020 nm @/
-/@ Reconstruct the full header from the strings returned by
-   getSectionHeadings().  The caller should deallocate the returned string. @/
-/@ getSectionHeadings() currently return strings extracted from headers,
-   but not the full header needed for writeExtractedSource().  Maybe we
-   should have it return the full header in the future, but for now this
-   function reconstructs the full header. @/
-vstring buildHeader(vstring header, vstring hdrComment, vstring decoration) {
-  long i;
-  vstring fullDecoration = "";
-  vstring fullHeader = "";
-  /@ The HUGE_DECORATION etc. have only the 1st 4 chars of the full line.
-     Build the full line. @/
-  let(&fullDecoration, "");
-  for (i = 1; i <= 5; i++) {
-    let(&fullDecoration, cat(fullDecoration, decoration, decoration,
-        decoration, decoration, NULL));
-  }
-  let(&fullDecoration, left(fullDecoration, 79));
-  i = (long)strlen(hdrComment);
-  let(&fullHeader, cat("\n\n$(\n", fullDecoration, "\n",
-      space((79 - (long)strlen(header))/2), header, "\n", fullDecoration,
-      "\n", hdrComment,
-      (i == 0) ? "$)\n" :
-          (hdrComment[i - 1] == ' ') ? "$)\n" : "\n$)\n",
-      NULL));
-  let(&fullDecoration, "");
-  return fullHeader;
-} /@ buildHeader @/
-*******/
-
-
-/* Returns 0 or 1 to indicate absence or presence of an indicator in
-   the comment of the statement. */
-/* mode = 1 = PROOF_DISCOURAGED means get any proof modification discouraged
-                indicator
-   mode = 2 = USAGE_DISCOURAGED means get any new usage discouraged indicator
-   mode = 0 = RESET  means to reset everything (statemeNum is ignored) */
-/* TODO: add a mode to reset a single statement if in the future we add
-   the ability to change the markup within the program. */
-flag getMarkupFlag(long statemNum, flag mode) {
-  /* For speedup, the algorithm searches a statement's comment for markup
-     matches only the first time, then saves the result for subsequent calls
-     for that statement. */
-  static char init = 0;
-  static vstring commentSearchedFlags = ""; /* Y if comment was searched */
-  static vstring proofFlags = "";  /* Y if proof discouragement, else N */
-  static vstring usageFlags = "";  /* Y if usage discouragement, else N */
-  vstring str1 = "";
-  /* These are global in mmdata.h
-#define PROOF_DISCOURAGED_MARKUP "(Proof modification is discouraged.)"
-#define USAGE_DISCOURAGED_MARKUP "(New usage is discouraged.)"
-  extern vstring g_proofDiscouragedMarkup;
-  extern vstring g_usageDiscouragedMarkup;
-  */
-
-  if (mode == RESET) { /* Deallocate */ /* Should be called by ERASE command */
-    let(&commentSearchedFlags, "");
-    let(&proofFlags, "");
-    let(&usageFlags, "");
-    init = 0;
-    return 0;
-  }
-
-  if (init == 0) {
-    init = 1;
-    /* The global variables g_proofDiscouragedMarkup and g_usageDiscouragedMarkup
-       are initialized to "" like all vstrings to allow them to be reassigned
-       by a possible future SET command.  So the first time this is called
-       we need to assign them to the default markup strings. */
-    if (g_proofDiscouragedMarkup[0] == 0) {
-      let(&g_proofDiscouragedMarkup, PROOF_DISCOURAGED_MARKUP);
-    }
-    if (g_usageDiscouragedMarkup[0] == 0) {
-      let(&g_usageDiscouragedMarkup, USAGE_DISCOURAGED_MARKUP);
-    }
-    /* Initialize flag strings */
-    let(&commentSearchedFlags, string(g_statements + 1, 'N'));
-    let(&proofFlags, space(g_statements + 1));
-    let(&usageFlags, space(g_statements + 1));
-  }
-
-  if (statemNum < 1 || statemNum > g_statements) bug(1392);
-
-  if (commentSearchedFlags[statemNum] == 'N') {
-    if (g_Statement[statemNum].type == f_
-        || g_Statement[statemNum].type == e_ /* 24-May-2016 nm */ ) {
-      /* Any comment before a $f, $e statement is assumed irrelevant */
-      proofFlags[statemNum] = 'N';
-      usageFlags[statemNum] = 'N';
-    } else {
-      if (g_Statement[statemNum].type != a_ && g_Statement[statemNum].type != p_) {
-        bug(1393);
-      }
-      str1 = getDescription(statemNum);  /* str1 must be deallocated here */
-      /* Strip linefeeds and reduce spaces */
-      let(&str1, edit(str1, 4 + 8 + 16 + 128));
-      if (instr(1, str1, g_proofDiscouragedMarkup)) {
-        proofFlags[statemNum] = 'Y';
-      } else {
-        proofFlags[statemNum] = 'N';
-      }
-      if (instr(1, str1, g_usageDiscouragedMarkup)) {
-        usageFlags[statemNum] = 'Y';
-      } else {
-        usageFlags[statemNum] = 'N';
-      }
-      let(&str1, ""); /* Deallocate */
-    }
-    commentSearchedFlags[statemNum] = 'Y';
-  }
-
-  if (mode == PROOF_DISCOURAGED) return (proofFlags[statemNum] == 'Y') ? 1 : 0;
-  if (mode == USAGE_DISCOURAGED) return (usageFlags[statemNum] == 'Y') ? 1 : 0;
-  bug(1394);
-  return 0;
-} /* getMarkupFlag */
-
-
-/* 7-Nov-2015 nm */
-
-/* Extract contributor or date from statement description per the
-   following mode argument:
-
-       CONTRIBUTOR 1
-       CONTRIB_DATE 2
-       REVISER 3
-       REVISE_DATE 4
-       SHORTENER 5
-       SHORTEN_DATE 6
-       MOST_RECENT_DATE 7
-
-   When an item above is missing, the empty string is returned for that item.
-   The following utility modes are available:
-
-       GC_ERROR_CHECK_SILENT 8
-       GC_ERROR_CHECK_PRINT 9
-       GC_RESET 0
-       GC_RESET_STMT 10
-
-   For GC_ERROR_CHECK_SILENT and GC_ERROR_CHECK_PRINT, a "F" is returned if
-   error-checking fails, otherwise "P" is returned.  GC_ERROR_CHECK_PRINT also
-   prints the errors found.
-
-   GC_RESET clears the cache and returns the empty string.  It is normally
-   used by the ERASE command.  The stmtNum argument should be 0.  The
-   empty string is returned.
-
-   GC_RESET_STMT re-initializes the cache for the specified statement only.
-   It should be called whenever the labelSection is changed e.g. by
-   SAVE PROOF.  The empty string is returned.
-*/
-/* 3-May-2017 nm Changed to return a single result each call, to allow
-   expandability in the future */
-/* The caller must deallocate the returned string. */
-vstring getContrib(long stmtNum, char mode) {
-
-/******** deleted 3-May-2017
-flag getContrib(long stmtNum,
-    vstring @contributor, vstring @contribDate,
-    vstring @reviser, vstring @reviseDate,
-    vstring @shortener, vstring @shortenDate,
-    vstring @mostRecentDate, /@ The most recent of all 3 dates @/
-    flag printErrorsFlag,
-    flag mode /@ 0 == RESET = reset, 1 = normal @/ /@ 2-May-2017 nm @/) {
-*******/
-  /* 2-May-2017 nm */
-  /* For speedup, the algorithm searches a statement's comment for markup
-     matches only the first time, then saves the result for subsequent calls
-     for that statement. */
-  static char init = 0;
-
-  vstring contributor = "";
-  vstring contribDate = "";
-  vstring reviser = "";
-  vstring reviseDate = "";
-  vstring shortener = "";
-  vstring shortenDate = "";
-  vstring mostRecentDate = "";   /* The most recent of all 3 dates */
-
-  static vstring commentSearchedFlags = ""; /* Y if comment was searched */
-  static pntrString *contributorList = NULL_PNTRSTRING;
-  static pntrString *contribDateList = NULL_PNTRSTRING;
-  static pntrString *reviserList = NULL_PNTRSTRING;
-  static pntrString *reviseDateList = NULL_PNTRSTRING;
-  static pntrString *shortenerList = NULL_PNTRSTRING;
-  static pntrString *shortenDateList = NULL_PNTRSTRING;
-  static pntrString *mostRecentDateList = NULL_PNTRSTRING;
-
-  long cStart = 0, cMid = 0, cEnd = 0;
-  long rStart = 0, rMid = 0, rEnd = 0;
-  long sStart = 0, sMid = 0, sEnd = 0;
-  long firstR = 0, firstS = 0;
-  vstring description = "";
-  vstring tmpDate0 = "";
-  vstring tmpDate1 = "";
-  vstring tmpDate2 = "";
-  long stmt, p, dd, mmm, yyyy;
-  flag errorCheckFlag = 0;
-  flag err = 0;
-  vstring returnStr = ""; /* Return value */
-#define CONTRIB_MATCH " (Contributed by "
-#define REVISE_MATCH " (Revised by "
-#define SHORTEN_MATCH " (Proof shortened by "
-#define END_MATCH ".) "
-
-  /* 3-May-2017 nm */
-  if (mode == GC_ERROR_CHECK_SILENT || mode == GC_ERROR_CHECK_PRINT) {
-    errorCheckFlag = 1;
-  }
-
-  /* 2-May-2017 nm */
-  if (mode == GC_RESET) {
-    /* This is normally called by the ERASE command only */
-    if (init != 0) {
-      if ((long)strlen(commentSearchedFlags) != g_statements + 1) {
-        bug(1395);
-      }
-      if (stmtNum != 0) {
-        bug(1400);
-      }
-      for (stmt = 1; stmt <= g_statements; stmt++) {
-        if (commentSearchedFlags[stmt] == 'Y') {
-          /* Deallocate cached strings */
-          let((vstring *)(&(contributorList[stmt])), "");
-          let((vstring *)(&(contribDateList[stmt])), "");
-          let((vstring *)(&(reviserList[stmt])), "");
-          let((vstring *)(&(reviseDateList[stmt])), "");
-          let((vstring *)(&(shortenerList[stmt])), "");
-          let((vstring *)(&(shortenDateList[stmt])), "");
-          let((vstring *)(&(mostRecentDateList[stmt])), "");
-        }
-      }
-      /* Deallocate the lists of pointers to cached strings */
-      pntrLet(&contributorList, NULL_PNTRSTRING);
-      pntrLet(&contribDateList, NULL_PNTRSTRING);
-      pntrLet(&reviserList, NULL_PNTRSTRING);
-      pntrLet(&reviseDateList, NULL_PNTRSTRING);
-      pntrLet(&shortenerList, NULL_PNTRSTRING);
-      pntrLet(&shortenDateList, NULL_PNTRSTRING);
-      pntrLet(&mostRecentDateList, NULL_PNTRSTRING);
-      let(&commentSearchedFlags, "");
-      init = 0;
-    } /* if (init != 0) */
-    return "";
-  }
-
-  /* 3-May-2017 nm */
-  if (mode == GC_RESET_STMT) {
-    /* This should be called whenever the labelSection is changed e.g. by
-       SAVE PROOF. */
-    if (init != 0) {
-      if ((long)strlen(commentSearchedFlags) != g_statements + 1) {
-        bug(1398);
-      }
-      if (stmtNum < 1 || stmtNum > g_statements + 1) {
-        bug(1399);
-      }
-      if (commentSearchedFlags[stmtNum] == 'Y') {
-        /* Deallocate cached strings */
-        let((vstring *)(&(contributorList[stmtNum])), "");
-        let((vstring *)(&(contribDateList[stmtNum])), "");
-        let((vstring *)(&(reviserList[stmtNum])), "");
-        let((vstring *)(&(reviseDateList[stmtNum])), "");
-        let((vstring *)(&(shortenerList[stmtNum])), "");
-        let((vstring *)(&(shortenDateList[stmtNum])), "");
-        let((vstring *)(&(mostRecentDateList[stmtNum])), "");
-        commentSearchedFlags[stmtNum] = 'N';
-      }
-    } /* if (init != 0) */
-    return "";
-  }
-
-  /* We now check only $a and $p statements - should we do others? */
-  if (g_Statement[stmtNum].type != a_ && g_Statement[stmtNum].type != p_) {
-    goto RETURN_POINT;
-  }
-
-  if (init == 0) {
-    init = 1;
-    /* Initialize flag string */
-    let(&commentSearchedFlags, string(g_statements + 1, 'N'));
-    /* Initialize pointers to "" (null vstring) */
-    pntrLet(&contributorList, pntrSpace(g_statements + 1));
-    pntrLet(&contribDateList, pntrSpace(g_statements + 1));
-    pntrLet(&reviserList, pntrSpace(g_statements + 1));
-    pntrLet(&reviseDateList, pntrSpace(g_statements + 1));
-    pntrLet(&shortenerList, pntrSpace(g_statements + 1));
-    pntrLet(&shortenDateList, pntrSpace(g_statements + 1));
-    pntrLet(&mostRecentDateList, pntrSpace(g_statements + 1));
-  }
-
-  if (stmtNum < 1 || stmtNum > g_statements) bug(1396);
-
-  if (commentSearchedFlags[stmtNum] == 'N' /* Not in cache */
-      || errorCheckFlag == 1 /* Needed to get sStart, rStart, cStart */) {
-    /* It wasn't cached, so we extract from the statement's comment */
-
-    let(&description, "");
-    description = getDescription(stmtNum);
-    let(&description, edit(description,
-        4/*ctrl*/ + 8/*leading*/ + 16/*reduce*/ + 128/*trailing*/));
-    let(&description, cat(" ", description, " ", NULL)); /* Add for matching */
-
-    cStart = instr(1, description, CONTRIB_MATCH);
-    if (cStart != 0) {
-      cStart = cStart + (long)strlen(CONTRIB_MATCH); /* Start of contributor */
-      cEnd = instr(cStart, description, END_MATCH); /* End of date */
-      cMid = cEnd; /* After end of contributor and before start of date */
-      if (cMid != 0) {
-        while (description[cMid - 1] != ' ') {
-          cMid--;
-          if (cMid == 0) break;
-        }
-      }
-      /* We assign contributorList entry instead of contributor,
-         contribDateList entry instead of contribDate, etc. in case the
-         same string variable is used for several arguments for convenience
-         (e.g. to avoid having to declare 7 string variables if only one date
-         is needed) */
-      let((vstring *)(&(contributorList[stmtNum])),
-          seg(description, cStart, cMid - 2));
-      let((vstring *)(&(contribDateList[stmtNum])),
-          seg(description, cMid + 1, cEnd - 1));
-    } else {
-      /* The contributorList etc. are already initialized to the empty
-         string, so we don't have to assign them here. */
-      /*
-      let((vstring *)(&(contributorList[stmtNum])), "");
-      let((vstring *)(&(contribDateList[stmtNum])), "");
-      */
-    }
-
-    rStart = 0;
-    do {  /* Get the last revision entry */
-      p = instr(rStart + 1, description, REVISE_MATCH);
-      if (p != 0) {
-        rStart = p;
-        if (firstR == 0) firstR = p + (long)strlen(REVISE_MATCH);
-                               /* Add the strlen so to later compare to rStart */
-      }
-    } while (p != 0);
-    if (rStart != 0) {
-      rStart = rStart + (long)strlen(REVISE_MATCH); /* Start of reviser */
-      rEnd = instr(rStart, description, END_MATCH); /* End of date */
-      rMid = rEnd; /* After end of reviser and before start of date */
-      if (rMid != 0) {
-        while (description[rMid - 1] != ' ') {
-          rMid--;
-          if (rMid == 0) break;
-        }
-      }
-      let((vstring *)(&(reviserList[stmtNum])),
-          seg(description, rStart, rMid - 2));
-      let((vstring *)(&(reviseDateList[stmtNum])),
-          seg(description, rMid + 1, rEnd - 1));
-    } else {
-      /* redundant; already done by init
-      let((vstring *)(&(reviserList[stmtNum])), "");
-      let((vstring *)(&(reviseDateList[stmtNum])), "");
-      */
-    }
-
-    sStart = 0;
-    do {  /* Get the last shorten entry */
-      p = instr(sStart + 1, description, SHORTEN_MATCH);
-      if (p != 0) {
-        sStart = p;
-        if (firstS == 0) firstS = p + (long)strlen(SHORTEN_MATCH);
-                               /* Add the strlen so to later compare to rStart */
-      }
-    } while (p != 0);
-    if (sStart != 0) {
-      sStart = sStart + (long)strlen(SHORTEN_MATCH); /* Start of shortener */
-      sEnd = instr(sStart, description, END_MATCH); /* End of date */
-      sMid = sEnd; /* After end of shortener and before start of date */
-      if (sMid != 0) {
-        while (description[sMid - 1] != ' ') {
-          sMid--;
-          if (sMid == 0) break;
-        }
-      }
-      let((vstring *)(&(shortenerList[stmtNum])),
-          seg(description, sStart, sMid - 2));
-      let((vstring *)(&(shortenDateList[stmtNum])),
-         seg(description, sMid + 1, sEnd - 1));
-    } else {
-      /* redundant; already done by init
-      let((vstring *)(&(shortenerList[stmtNum])), "");
-      let((vstring *)(&(shortenDateList[stmtNum])), "");
-      */
-    }
-
-
-    /* 13-Dec-2016 nm Get the most recent date */
-    let((vstring *)(&(mostRecentDateList[stmtNum])),
-        (vstring)(contribDateList[stmtNum]));
-    /* Note that compareDate() treats empty string as earliest date */
-    if (compareDates((vstring)(mostRecentDateList[stmtNum]),
-        (vstring)(reviseDateList[stmtNum])) == -1) {
-      let((vstring *)(&(mostRecentDateList[stmtNum])),
-          (vstring)(reviseDateList[stmtNum]));
-    }
-    if (compareDates((vstring)(mostRecentDateList[stmtNum]),
-        (vstring)(shortenDateList[stmtNum])) == -1) {
-      let((vstring *)(&(mostRecentDateList[stmtNum])),
-          (vstring)(shortenDateList[stmtNum]));
-    }
-
-    /* 2-May-2017 nm Tag the cache entry as updated */
-    commentSearchedFlags[stmtNum] = 'Y';
-  } /* commentSearchedFlags[stmtNum] == 'N' || errorCheckFlag == 1 */
-
-  /* Assign the output strings from the cache */
-  if (errorCheckFlag == 1) {
-    let(&contributor, (vstring)(contributorList[stmtNum]));
-    let(&contribDate, (vstring)(contribDateList[stmtNum]));
-    let(&reviser, (vstring)(reviserList[stmtNum]));
-    let(&reviseDate, (vstring)(reviseDateList[stmtNum]));
-    let(&shortener, (vstring)(shortenerList[stmtNum]));
-    let(&shortenDate, (vstring)(shortenDateList[stmtNum]));
-    let(&mostRecentDate, (vstring)(mostRecentDateList[stmtNum]));
-  } else {
-    /* Assign only the requested field for faster speed */
-    switch (mode) {
-      case CONTRIBUTOR:
-        let(&returnStr, (vstring)(contributorList[stmtNum])); break;
-      case CONTRIB_DATE:
-        let(&returnStr, (vstring)(contribDateList[stmtNum])); break;
-      case REVISER:
-        let(&returnStr, (vstring)(reviserList[stmtNum])); break;
-      case REVISE_DATE:
-        let(&returnStr, (vstring)(reviseDateList[stmtNum])); break;
-      case SHORTENER:
-        let(&returnStr, (vstring)(shortenerList[stmtNum])); break;
-      case SHORTEN_DATE:
-        let(&returnStr, (vstring)(shortenDateList[stmtNum])); break;
-      case MOST_RECENT_DATE:
-        let(&returnStr, (vstring)(mostRecentDateList[stmtNum])); break;
-      default: bug(1397); /* Any future modes should be added here */
-    } /* end switch (mode) */
-  }
-
-  /* Skip error checking for speedup if we're not printing errors */
-  if (errorCheckFlag == 0) goto RETURN_POINT;
-
-  /* For error checking, we don't require dates in syntax statements
-     (**** Note that this is set.mm-specific! ****) */
-  if (g_Statement[stmtNum].type == a_   /* Don't check syntax statements */
-      && strcmp(left(g_Statement[stmtNum].labelName, 3), "df-")
-      && strcmp(left(g_Statement[stmtNum].labelName, 3), "ax-")) {
-    goto RETURN_POINT;
-  }
-
-  if (cStart == 0) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: There is no \"", edit(CONTRIB_MATCH, 8+128),
-        "...)\" in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-
-  if (instr(cStart + 1, description, CONTRIB_MATCH) != 0) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: There is more than one \"", edit(CONTRIB_MATCH, 8+128),
-        "...)\" ",
-        "in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-
-  /* 3-May-2017 nm */
-  if (cStart != 0 && description[cMid - 2] != ',') {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        "?Warning: There is no comma between contributor and date",
-        ", or period is missing,",   /* 5-Aug-2017 nm */
-        " in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (rStart != 0 && description[rMid - 2] != ',') {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        "?Warning: There is no comma between reviser and date",
-        ", or period is missing,",   /* 5-Aug-2017 nm */
-        " in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (sStart != 0 && description[sMid - 2] != ',') {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        "?Warning: There is no comma between proof shortener and date",
-        ", or period is missing,",   /* 5-Aug-2017 nm */
-        " in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (instr(1, contributor, ",") != 0) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        "?Warning: There is a comma in the contributor name \"",
-        contributor,
-        "\" in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (instr(1, reviser, ",") != 0) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        "?Warning: There is a comma in the reviser name \"",
-        reviser,
-        "\" in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (instr(1, shortener, ",") != 0) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        "?Warning: There is a comma in the proof shortener name \"",
-        shortener,
-        "\" in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-
-
-  /*********  Turn off this warning unless we decide not to allow this
-  if ((firstR != rStart) || (firstS != sStart)) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /@ convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        @contributor, "/", @reviser, "/", @shortener, "] ",
-        @/
-        "?Warning: There are multiple \"",
-        edit(REVISE_MATCH, 8+128) , "...)\" or \"",
-        edit(SHORTEN_MATCH, 8+128) ,
-        "...)\" entries in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        "  The last one of each type was used.",
-        NULL), "    ", " ");
-  }
-  *********/
-
-  if ((firstR != 0 && firstR < cStart)
-      || (firstS != 0 && firstS < cStart)) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: \"", edit(CONTRIB_MATCH, 8+128),
-        "...)\" is placed after \"",
-        edit(REVISE_MATCH, 8+128) , "...)\" or \"",
-        edit(SHORTEN_MATCH, 8+128) ,
-        "...)\" in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-
-  if ((cStart !=0 && (cMid == 0 || cEnd == 0 || cMid == cEnd
-          || contributor[0] == 0 || contribDate[0] == 0))
-      || (rStart !=0 && (rMid == 0 || rEnd == 0 || rMid == rEnd
-          || reviser[0] == 0 || reviseDate[0] == 0))
-      || (sStart !=0 && (sMid == 0 || sEnd == 0 || sMid == sEnd
-          || shortener[0] == 0 || shortenDate[0] == 0))) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: There is a formatting error in a",
-        " \"", edit(CONTRIB_MATCH, 8+128),  "...)\", \"",
-        edit(REVISE_MATCH, 8+128) , "...)\", or \"",
-        edit(SHORTEN_MATCH, 8+128),
-        "...)\" entry in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-
-  if (contribDate[0] != 0) {
-    parseDate(contribDate, &dd, &mmm, &yyyy);
-    buildDate(dd, mmm, yyyy, &tmpDate0);
-    if (strcmp(contribDate, tmpDate0)) {
-      err = 1;
-      if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-          /* convenience prefix to assist massive revisions
-          g_Statement[stmtNum].labelName, " [",
-          contributor, "/", reviser, "/", shortener, "] ",
-          */
-          "?Warning: There is a formatting error in the \"",
-          edit(CONTRIB_MATCH, 8+128),  "...)\" date \"", contribDate, "\""
-          " in the comment above statement ",
-          str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-          NULL), "    ", " ");
-    }
-  }
-
-  if (reviseDate[0] != 0) {
-    parseDate(reviseDate, &dd, &mmm, &yyyy);
-    buildDate(dd, mmm, yyyy, &tmpDate0);
-    if (strcmp(reviseDate, tmpDate0)) {
-      err = 1;
-      if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-          /* convenience prefix to assist massive revisions
-          g_Statement[stmtNum].labelName, " [",
-          contributor, "/", reviser, "/", shortener, "] ",
-          */
-          "?Warning: There is a formatting error in the \"",
-          edit(REVISE_MATCH, 8+128) , "...)\" date \"", reviseDate, "\""
-          " in the comment above statement ",
-          str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-          NULL), "    ", " ");
-    }
-  }
-
-  if (shortenDate[0] != 0) {
-    parseDate(shortenDate, &dd, &mmm, &yyyy);
-    buildDate(dd, mmm, yyyy, &tmpDate0);
-    if (strcmp(shortenDate, tmpDate0)) {
-      err = 1;
-      if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-          /* convenience prefix to assist massive revisions
-          g_Statement[stmtNum].labelName, " [",
-          contributor, "/", reviser, "/", shortener, "] ",
-          */
-          "?Warning: There is a formatting error in the \"",
-          edit(SHORTEN_MATCH, 8+128) , "...)\" date \"", shortenDate, "\""
-          " in the comment above statement ",
-          str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-          NULL), "    ", " ");
-    }
-  }
-
-  if (contribDate[0] != 0 &&
-     ((reviseDate[0] != 0
-         && compareDates(contribDate, reviseDate) != -1)
-     || (shortenDate[0] != 0
-         && compareDates(contribDate, shortenDate) != -1))) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: The \"", edit(CONTRIB_MATCH, 8+128),
-        "...)\" date is not earlier than the \"",
-        edit(REVISE_MATCH, 8+128), "...)\" or \"",
-        edit(SHORTEN_MATCH, 8+128),
-        "...)\" date in the comment above statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-
-  if (reviseDate[0] != 0 && shortenDate[0] != 0) {
-    p = compareDates(reviseDate, shortenDate);
-    if ((rStart < sStart && p == 1)
-        || (rStart > sStart && p == -1)) {
-      err = 1;
-      if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-          "?Warning: The \"", edit(REVISE_MATCH, 8+128), "...)\" and \"",
-          edit(SHORTEN_MATCH, 8+128),
-         "...)\" dates are in the wrong order in the comment above statement ",
-          str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-          NULL), "    ", " ");
-    }
-  }
-
-  #ifdef DATE_BELOW_PROOF /* 12-May-2017 nm */
-
-  /* TODO ******** The rest of the checks should be deleted if we decide
-     to drop the date after the proof */
-  if (g_Statement[stmtNum].type != p_) {
-    goto RETURN_POINT;
-  }
-  getProofDate(stmtNum, &tmpDate1, &tmpDate2);
-  if (tmpDate1[0] == 0) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: There is no date below the proof in statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (tmpDate2[0] == 0
-      && (reviseDate[0] != 0 || shortenDate[0] != 0)) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: The comment has \"",
-        edit(REVISE_MATCH, 8+128), "...)\" or \"",
-        edit(SHORTEN_MATCH, 8+128),
-        "...)\" but there is only one date below the proof",
-        " in statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (tmpDate2[0] != 0 && reviseDate[0] == 0 && shortenDate[0] == 0) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: There are two dates below the proof but no \"",
-        edit(REVISE_MATCH, 8+128), "...)\" or \"",
-        edit(SHORTEN_MATCH, 8+128),
-        "...)\" entry in statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (tmpDate2[0] != 0
-      && (reviseDate[0] != 0 || shortenDate[0] != 0)
-      && strcmp(tmpDate1, reviseDate)
-      && strcmp(tmpDate1, shortenDate)) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: Neither a \"",
-        edit(REVISE_MATCH, 8+128), "...)\" date ",
-        "nor a \"", edit(SHORTEN_MATCH, 8+128), "...)\" date ",
-        "matches the date ", tmpDate1,
-        " below the proof in statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (tmpDate2[0] != 0
-      && reviseDate[0] != 0
-      && compareDates(tmpDate1, reviseDate) == -1) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: The \"",
-        edit(REVISE_MATCH, 8+128), "...)\" date ", reviseDate,
-        " is later than the date ", tmpDate1,
-        " below the proof in statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (tmpDate2[0] != 0
-      && shortenDate[0] != 0
-      && compareDates(tmpDate1, shortenDate) == -1) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: The \"",
-        edit(SHORTEN_MATCH, 8+128), "...)\" date ", shortenDate,
-        " is later than the date ", tmpDate1,
-        " below the proof in statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (tmpDate2[0] != 0 && compareDates(tmpDate2, tmpDate1) != -1) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: The first date below the proof, ", tmpDate1,
-        ", is not newer than the second, ", tmpDate2,
-        ", in statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  if (tmpDate2[0] == 0) {
-    let(&tmpDate0, tmpDate1);
-  } else {
-    let(&tmpDate0, tmpDate2);
-  }
-  if (contribDate[0] != 0
-      && tmpDate0[0] != 0 && strcmp(contribDate, tmpDate0)) {
-    err = 1;
-    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
-        /* convenience prefix to assist massive revisions
-        g_Statement[stmtNum].labelName, " [",
-        contributor, "/", reviser, "/", shortener, "] ",
-        */
-        "?Warning: The \"", edit(CONTRIB_MATCH, 8+128), "...)\" date ",
-        contribDate,
-        " doesn't match the date ", tmpDate0,
-        " below the proof in statement ",
-        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
-        NULL), "    ", " ");
-  }
-  /***** End of section to delete if date after proof is dropped */
-#endif /* #ifdef DATE_BELOW_PROOF */
-
-  if (err == 1) {
-    let(&returnStr, "F");  /* fail */
-  } else {
-    let(&returnStr, "P");  /* pass */
-  }
-
-
- RETURN_POINT:
-
-  let(&description, "");
-
-  if (errorCheckFlag == 1) { /* Slight speedup */
-    let(&contributor, "");
-    let(&contribDate, "");
-    let(&reviser, "");
-    let(&reviseDate, "");
-    let(&shortener, "");
-    let(&shortenDate, "");
-    let(&mostRecentDate, "");
-    let(&tmpDate0, "");
-    let(&tmpDate1, "");
-    let(&tmpDate2, "");
-  }
-
-  return returnStr;
-} /* getContrib */
-
-
-/*#ifdef DATE_BELOW_PROOF*/ /* 12-May-2017 nm */
-/* 14-May-2017 nm - re-enabled (temporarily?) for converting old .mm's */
-/* 4-Nov-2015 nm */
-/* Extract up to 2 dates after a statement's proof.  If no date is present,
-   date1 will be blank.  If no 2nd date is present, date2 will be blank.
-   THIS WILL BECOME OBSOLETE WHEN WE START TO USE DATES IN THE
-   DESCRIPTION. */
-void getProofDate(long stmtNum, vstring *date1, vstring *date2) {
-  vstring textAfterProof = "";
-  long p1, p2;
-  let(&textAfterProof, space(g_Statement[stmtNum + 1].labelSectionLen));
-  memcpy(textAfterProof, g_Statement[stmtNum + 1].labelSectionPtr,
-      (size_t)(g_Statement[stmtNum + 1].labelSectionLen));
-  let(&textAfterProof, edit(textAfterProof, 2)); /* Discard spaces and tabs */
-  p1 = instr(1, textAfterProof, "$([");
-  p2 = instr(p1, textAfterProof, "]$)");
-  if (p1 && p2) {
-    let(&(*date1), seg(textAfterProof, p1 + 3, p2 - 1));  /* 1st date stamp */
-    p1 = instr(p2, textAfterProof, "$([");
-    p2 = instr(p1, textAfterProof, "]$)");
-    if (p1 && p2) {
-      let(&(*date2), seg(textAfterProof, p1 + 3, p2 - 1)); /* 2nd date stamp */
-    } else {
-      let(&(*date2), ""); /* No 2nd date stamp */
-    }
-  } else {
-    let(&(*date1), ""); /* No 1st or 2nd date stamp */
-    let(&(*date2), "");
-  }
-  let(&textAfterProof, ""); /* Deallocate */
-  return;
-} /* getProofDate */
-
-/*#endif*/ /*#ifdef DATE_BELOW_PROOF*/ /* 12-May-2017 nm */
-
-
-/* 4-Nov-2015 nm */
-/* Get date, month, year fields from a dd-mmm-yyyy date string,
-   where dd may be 1 or 2 digits, mmm is 1st 3 letters of month,
-   and yyyy is 2 or 4 digits.  A 1 is returned if an error was detected. */
-flag parseDate(vstring dateStr, long *dd, long *mmm, long *yyyy) {
-  long j;
-  flag err = 0;
-  j = instr(1, dateStr, "-");
-  *dd = (long)val(left(dateStr, j - 1)); /* Day */
-#define MONTHS "JanFebMarAprMayJunJulAugSepOctNovDec"
-  *mmm = ((instr(1, MONTHS, mid(dateStr, j + 1, 3)) - 1) / 3) + 1; /* 1 = Jan */
-  j = instr(j + 1, dateStr, "-");
-  *yyyy = (long)val(right(dateStr, j + 1));
-  if (*yyyy < 100) { /* 2-digit year (obsolete) */
-#define START_YEAR 93 /* Earliest 19xx year in set.mm database */
-    if (*yyyy < START_YEAR) {
-      *yyyy = *yyyy + 2000;
-    } else {
-      *yyyy = *yyyy + 1900;
-    }
-  }
-  if (*dd < 1 || *dd > 31 || *mmm < 1 || *mmm > 12) err = 1; /* 13-Dec-2016 nm */
-  return err;
-} /* parseDate */
-
-
-/* 4-Nov-2015 nm */
-/* Build date from numeric fields.  mmm should be a number from 1 to 12.
-   There is no error-checking. */
-void buildDate(long dd, long mmm, long yyyy, vstring *dateStr) {
-  let(&(*dateStr), cat(str((double)dd), "-", mid(MONTHS, mmm * 3 - 2, 3), "-",
-      str((double)yyyy), NULL));
-  return;
-} /* buildDate */
-
-
-/* 4-Nov-2015 nm */
-/* Compare two dates in the form dd-mmm-yyyy.  -1 = date1 < date2,
-   0 = date1 = date2,  1 = date1 > date2.  There is no error checking. */
-flag compareDates(vstring date1, vstring date2) {
-  long d1, m1, y1, d2, m2, y2, dd1, dd2;
-
-  /* 13-Dec-2016 nm */
-  /* If a date is the empty string, treat it as being _before_ any other
-     date */
-  if (date1[0] == 0 || date2[0] == 0) {
-    if (date1[0] == 0 && date2[0] == 0) {
-      return 0;
-    } else if (date1[0] == 0) {
-      return -1;
-    } else {
-      return 1;
-    }
-  }
-
-  parseDate(date1, &d1, &m1, &y1);
-  parseDate(date2, &d2, &m2, &y2);
-  /* dd1, dd2 increase monotonically but aren't true days since 1-Jan-0000 */
-  dd1 = d1 + m1 * 32 + y1 * 500;
-  dd2 = d2 + m2 * 32 + y2 * 500;
-  if (dd1 < dd2) {
-    return -1;
-  } else if (dd1 == dd2) {
-    return 0;
-  } else {
-    return 1;
-  }
-} /* compareDates */
-
-
-
-
-/* 17-Nov-2015 Moved out of metamath.c for better modularization */
-/* Compare strings via pointers for qsort */
-/* g_qsortKey is a global string key at which the sort starts; if empty,
-   start at the beginning of each line. */
-int qsortStringCmp(const void *p1, const void *p2)
-{
-  vstring tmp = "";
-  long n1, n2;
-  int r;
-  /* Returns -1 if p1 < p2, 0 if equal, 1 if p1 > p2 */
-  if (g_qsortKey[0] == 0) {
-    /* No key, use full line */
-    return strcmp(*(char * const *)p1, *(char * const *)p2);
-  } else {
-    n1 = instr(1, *(char * const *)p1, g_qsortKey);
-    n2 = instr(1, *(char * const *)p2, g_qsortKey);
-    r = strcmp(
-        right(*(char * const *)p1, n1),
-        right(*(char * const *)p2, n2));
-    let(&tmp, ""); /* Deallocate temp string stack */
-    return r;
-  }
-}
-
-/* 4-May-2017 Ari Ferrera */
-void freeData() {
-  /* 15-Aug-2020 nm TODO: are some of these called twice? (in eraseSource) */
-  free(g_IncludeCall);
-  free(g_Statement);
-  free(g_MathToken);
-  free(memFreePool);
-  free(memUsedPool);
-}
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/*
+mmdata.c
+*/
+
+/*!
+ * \file
+ * Defines global data structures and manipulates arrays with functions similar
+ * to BASIC string functions; memory management; converts between proof formats
+*/
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mminou.h"
+#include "mmpars.h"
+#include "mmcmdl.h" /* Needed for g_logFileName */
+#include "mmpfas.h" /* Needed for g_proveStatement */
+#include "mmwtex.h" /* Needed for SMALL_DECORATION etc. */
+
+/*E*/long db=0,db0=0,db2=0,db3=0,db4=0,db5=0,db6=0,db7=0,db8=0,db9=0;
+flag g_listMode = 0; /* 0 = metamath, 1 = list utility */
+flag g_toolsMode = 0; /* In metamath: 0 = metamath, 1 = text tools utility */
+
+
+/* For use by getMarkupFlag() */
+vstring_def(g_proofDiscouragedMarkup);
+vstring_def(g_usageDiscouragedMarkup);
+flag g_globalDiscouragement = 1; /* SET DISCOURAGEMENT ON */
+
+vstring_def(g_contributorName);
+
+/* Global variables related to current statement */
+int g_currentScope = 0;
+
+long g_MAX_STATEMENTS = 1;
+long g_MAX_MATHTOKENS = 1;
+long g_MAX_INCLUDECALLS = 2; /* Must be at least 2 (the single-file case) !!!
+                         (A dummy extra top entry is used by parseKeywords().) */
+struct statement_struct *g_Statement = NULL;
+long *g_labelKey = NULL;
+struct mathToken_struct *g_MathToken;
+long *g_mathKey = NULL;
+long g_statements = 0, labels = 0, g_mathTokens = 0;
+
+struct includeCall_struct *g_IncludeCall = NULL;
+long g_includeCalls = -1;  /* For eraseSource() in mmcmds.c */
+
+char *g_sourcePtr = NULL;
+long g_sourceLen;
+
+/* Null nmbrString */
+struct nullNmbrStruct g_NmbrNull = {-1, sizeof(long), sizeof(long), -1};
+
+/* Null pntrString */
+struct nullPntrStruct g_PntrNull = {-1, sizeof(long), sizeof(long), NULL};
+
+temp_nmbrString *nmbrTempAlloc(long size);
+        /* nmbrString memory allocation/deallocation */
+void nmbrCpy(nmbrString *sout, const nmbrString *sin);
+void nmbrNCpy(nmbrString *s, const nmbrString *t, long n);
+
+temp_pntrString *pntrTempAlloc(long size);
+        /* pntrString memory allocation/deallocation */
+void pntrCpy(pntrString *sout, const pntrString *sin);
+void pntrNCpy(pntrString *s, const pntrString *t, long n);
+
+vstring g_qsortKey; /* Used by qsortStringCmp; pointer only, do not deallocate */
+
+/*!
+ * \page pgSuballocator Suballocator
+ *
+ * Metamath does not free memory by returning it to the operating system again.
+ * To reduce frequent system de/allocation calls, it instead implements a
+ * suballocator.  Each chunk of memory allocated from the system (we call them
+ * \ref pgBlock "block" in this documentation) is equipped with a hidden header
+ * containing administrative information private to the suballocator.
+ *
+ * During execution chunks of memory, either complete \ref pgBlock "blocks" or
+ * \ref pgFragmentation "fragments" thereof, become free again.  The
+ * suballocator adds them then to internal **pools** for reuse, one dedicated
+ * to totally free blocks (\ref memFreePool), the other to fragmented ones
+ * (\ref memUsedPool).  We call these pools **free block array** and
+ * **used block array** in this documentation.  Fully occupied blocks are not
+ * tracked by the suballocator.
+ *
+ * Although the suballocator tries to avoid returning memory to the system, it
+ * can do so under extreme memory constraints, or when built-in limits are
+ * surpassed.
+ *
+ * The suballocator was designed with stack-like memory usage in mind, where
+ * data of the same type is pushed at, or popped off the end all the time.
+ * Each \ref pgBlock block supports this kind of usage out of the box (see
+ * \ref pgFragmentation).
+ */
+
+/*!
+ * \page pgFragmentation Fragmented blocks
+ *
+ * Memory fragmentation is kept simple in Metamath.  If a \ref pgBlock "block"
+ * contains both consumed and free space, all the free space is at the end.
+ * This scheme supports the idea of stack-like memory usage, where free space
+ * grows and shrinks behind a stack in a fixed size memory area, depending on
+ * its usage.
+ *
+ * Other types of fragmentation is not directly supported by the
+ * \ref pgSuballocator "suballocator".
+ */
+
+ /*! \page pgBlock Block of memory
+ *
+ * Each block used by the \ref pgSuballocator "suballocator" is formally´
+ * treated as an array of pointer (void*).  It is divided into an
+ * administrative header, followed by elements reserved for application data.
+ * The header is assigned elements -3 to -1 in the formal array, so that
+ * application data starts with element 0.  A **pointer to the block** always
+ * refers to element 0, so the header appears somewhat hidden.  Its **size** 
+ * is given by the bytes reserved for application data, not including the
+ * administrative header.
+ *
+ * The header elements are formally void*, but reinterpreted as long integer.
+ * The values support a stack, where data is pushed at and popped off the end
+ * during the course of execution.  The semantics of the header elements are:
+ *
+ * offset -1:\n
+ *   is the current size of the stack (in bytes, not elements!),
+ *   without header data. When interpreted as an offset into the stack, it
+ *   references the first element past the top of the stack.  (See
+ *   \ref pgFragmentation)
+ *
+ * offset -2:\n
+ *   the allocated size of the array, in bytes, not counting the
+ *   header.  When used as a stack, it marks the limit where the stack
+ *   overflows.
+ *
+ * offset -3:\n
+ *   If this block has free space at the end (is \ref pgFragmentation
+ *   "fragmented"), then this value contains its index in the used blocks
+ *   array, see \ref memUsedPool.  A value of -1 indicates it is either fully
+ *   occupied or totally free.  It is not kept in the used blocks array then.
+ *   If this block becomes full in the course of events, it is not
+ *   automatically removed from \ref memUsedPool, though.
+ */
+
+/*! \page pgPool Pool
+ * A pool is an array of pointers pointing to \ref pgBlock "blocks".  It may
+ * only be partially filled, so it is usually accompanied by two variables
+ * giving its current fill state and its capacity.
+ *
+ * In Metamath a pool has no gaps in between.
+ *
+ * The \ref pgSuballocator "suballocator" uses two pools:
+ * - the **free block array** pointed to by \ref memFreePool;
+ * - the **used block array** pointed to by \ref memUsedPool.
+ */
+
+/*! \page pgStack Temporary Allocated Memory
+ * Very often a routine needs some memory, that must live only as long as the
+ * routine is active.  Such memory is called **temporary**, or short **local**.
+ * Once a routine finishes, on return to its caller, it deallocates all its
+ * __local__ memory again.  Since routines frequently call subroutines, the
+ * same may hold for nested code, and so on.  In fact, this concept is so
+ * ubiquitous and frequent, that the processor, and all relevant program
+ * languages provide simple mechanisms for de/allocation of such __local__
+ * data.  Metamath is no exception to this.
+ *
+ * While the C compiler silently cares about __local__ variables, it must not
+ * interfere with data managed by a \ref pgSuballocator "Suballocator". Instead
+ * of tracking all __locally__ created memory individually for later
+ * deallocation, a stack like \ref pgPool "pool" is used to automate this
+ * handling.
+ *
+ * Stacks of temporary data only contain pointers to dynamically allocated
+ * memory from the heap or the \ref pgSuballocator.  This stack functions like
+ * an operand stack.  A final result depends on fragments, temporary results
+ * and similar, all pushed onto this stack.  When the final operation is
+ * executed, and its result is persisted in some variable, the dependency on
+ * its temporary operands ceases.  Consequently, they should be freed again.
+ * To automate this operation,  such a stack maintains a `start` index.  A
+ * client saves this value and sets it to the current stack top, then starts
+ * pushing dynamically allocated operands on the stack.  After the result is
+ * persisted, all entries beginning with the element at index  `start` are
+ * deallocated again, and the stack top is reset to the `start` value, while
+ * the `start` value is reset to the saved value, to accommodate nesting of
+ * this procedure.
+ *
+ * This scheme needs a few conditions to be met:
+ * - No operand is used in more than one evaluation context;
+ * - Operations are executed strictly sequential, or in a nested manner. No two
+ *   operations interleave pushing operands.
+ */
+
+/* Memory pools are used to reduce the number of malloc and alloc calls that
+   allocate arrays (strings or nmbr/pntrStrings typically).   The "free" pool
+   contains previously allocated arrays that are no longer used but that we
+   have not freed yet.  A call to allocate a new array fetches one from here
+   first.   The "used"
+   pool contains arrays that are partially used; each array has some free space
+   at the end that can be deallocated if memory gets low.   Any array that is
+   totally used (no free space) is not in any pool. */
+/* Each pool array has 3 "hidden" long elements before it, used by these
+   procedures.
+     Element -1:  actual size (bytes) of array, excluding the 3 "hidden"
+       long elements.
+     Element -2:  allocated size.  If all elements are used, allocated = actual.
+     Element -3:  location of array in memUsedPool.  If -1, it means that
+       actual = allocated and storage in memUsedPool is therefore not necessary.
+   The pointer to an array always points to element 0 (recast to right size).
+*/
+
+/*!
+ * \page doc-todo Improvements in documentation
+ * 
+ * - Revisit the \ref pgBlock "block", \ref pgStack "stack" references to check
+ *   the inserted wording.
+ * - The formatting of __p__ tags seem insufficient.  Figure out whether and
+ *   how doxygen allows assigning formats to a semantic tag.  Do not replace
+ *   a tag with direct formattings like \p aParam vs `aParam`, as some editors
+ *   recognize and highlight semantic tags.
+ *     The parameters are included in <code>aParam</code> tags.  You can change
+ *     the appearance by using your customized CSS file and let doxygen use it
+ *     with HTML-EXTRA-STYLESHEET in your own Doxyfile.
+ * - Regularly check the warning in \ref pntrString to see whether it still
+ *   holds, or can be made more precise.
+ */
+
+/*!
+ * \def MEM_POOL_GROW
+ * Amount that \ref memUsedPool and \ref memFreePool grows when it overflows.
+ */
+#define MEM_POOL_GROW 1000
+/*??? Let user set this from menu. */
+
+/*!
+ * \var long poolAbsoluteMax
+ * The value is a memory amount in bytes.
+ *
+ * The \ref pgSuballocator scheme must not hold more memory than is short term
+ * useful.  To the operating system all memory in \ref memFreePool appears as
+ * allocated, although it is not really in use.  To prevent the system from
+ * taking unnecessary action such as saving RAM to disk, a limit to the amount
+ * of free memory managed by the suballocator can be set up.  This limit is
+ * checked in frequent operations, and an automatic purge process is initiated
+ * in \ref memFreePoolPurge should \ref poolTotalFree exceed this value.
+ */
+long poolAbsoluteMax = 1000000; /* Pools will be purged when this is reached */
+
+/*!
+ * \var long poolTotalFree
+ * contains the number of free space available in bytes, in both pools
+ * \ref memFreePool and \ref memUsedPool, never counting the hidden headers at
+ * the beginning of each block, see \ref pgBlock.  Exceeding
+ * \ref poolAbsoluteMax may trigger an automatic purge process by
+ * \ref memFreePoolPurge.
+ */
+long poolTotalFree = 0; /* Total amount of free space allocated in pool */
+/*E*/long i1,j1_,k1; /* 'j1' is a built-in function */
+
+/*!
+ * \var void** memUsedPool
+ * \brief pointer to the pool of fragmented memory blocks
+ *
+ * If a \ref pgBlock "block" contains both consumed and free space, it is
+ * \ref pgFragmentation "fragmented".  All fragmented blocks are kept in the
+ * **used block array**, that memUsedPool points to.  See \ref pgSuballocator
+ * "suballocator".  Since free space appears at the end of a \ref pgBlock
+ * "block", this scheme supports in particular stack like memory, where data is
+ * pushed at and popped off the end.
+ *
+ * The used blocks array does initially not exist.  This is indicated by a
+ * null value.  Once this array is needed, space for it is allocated from the
+ * system.
+ *
+ * The used block array may only be partially occupied, in which case elements
+ * at the end of the array are unused.  Its current usage is given by
+ * \ref memUsedPoolSize.  Its capacity is given by \ref memUsedPoolMax.
+ *
+ * \attention The pool may contain full \ref pgBlock "blocks".
+ *
+ * \invariant Each block in the used blocks array has its index noted in its
+ * hidden header, for backward reference.
+ *
+ * \attention Despite the name of this variable, fully occupied blocks are never
+ * kept in the used block array.
+ */
+void **memUsedPool = NULL;
+
+/*!
+ * \var long memUsedPoolSize
+ * \attention this is the number of individual blocks, not the accumulated
+ * (unused) bytes contained.
+ *
+ * The Metamath suballocator holds fragmented blocks in a used block array.
+ * The number of occupied entries is kept in this variable.  Elements at the
+ * end of the used block array may be unused.  The fill size is given by this
+ * variable.  For further information see \ref memUsedPool.
+ *
+ * \invariant memUsedPoolSize <= \ref memUsedPoolMax.
+ */
+long memUsedPoolSize = 0; /* Current # of partially filled arrays in use */
+
+/*!
+ * \var long memUsedPoolMax
+ * \attention this is the number of individual free blocks, not the accumulated
+ * bytes contained.
+ *
+ * The Metamath suballocator holds fragmented blocks in the used block
+ * array.  This array may only partially be occupied.  Its total capacity is
+ * kept in this variable.  For further information see \ref memUsedPool.
+ *
+ * This variable may grow during a reallocation process.
+ *
+ * \invariant (memUsedPoolMax > 0) == (\ref memUsedPool != 0)
+ */
+long memUsedPoolMax = 0; /* Maximum # of entries in 'in use' table (grows
+                               as necessary) */
+
+/*!
+ * \var void** memFreePool
+ * \brief pointer to the pool of completely free memory blocks
+ *
+ * The \ref pgSuballocator "suballocator" is initially not equipped with a
+ * **free block array**, pointed to by memFreePool, indicated by a null value.
+ *
+ * Once a \ref pgBlock "memory block" is returned to the \ref pgSuballocator
+ * again, it allocates some space for the now needed array.
+ *
+ * The **free block array** contains only totally free \ref pgBlock "blocks".
+ * This array may only be partially occupied, in which case the elements at the
+ * end are the unused ones.  Its current fill size is given by
+ * \ref memFreePoolSize.  Its capacity is given by \ref memFreePoolMax.
+ *
+ * Fragmented blocks are kept in a separate \ref memUsedPool.  The suballocator
+ * never tracks fully used blocks.
+ */
+void **memFreePool = NULL;
+
+/*!
+ * \var long memFreePoolSize
+ * \attention this is the number of individual free blocks, not the accumulated
+ * bytes contained.
+ *
+ * The Metamath suballocator holds free blocks in a free block array.  The
+ * number of occupied entries is kept in this variable.  Elements at the end of
+ * the free block array may not be used.  The fill size is given by this
+ * variable.  For further information see \ref memFreePool.
+ *
+ * \invariant memFreePoolSize <= \ref memFreePoolMax.
+ */
+long memFreePoolSize = 0; /* Current # of available, allocated arrays */
+
+/*!
+ * \var long memFreePoolMax
+ * \attention this is the number of individual free blocks, not the accumulated
+ * bytes contained.
+ *
+ * The Metamath suballocator holds free blocks in a **free block array**.  It
+ * may only be partially occupied.  Its total capacity is kept in this variable.  For
+ * further information see \ref memFreePool.
+ *
+ * This variable may grow during a reallocation process.
+ *
+ * \invariant (memFreePoolMax > 0) == (\ref memFreePool != 0)
+ */
+long memFreePoolMax = 0; /* Maximum # of entries in 'free' table (grows
+                               as necessary) */
+
+/* poolFixedMalloc should be called when the allocated array will rarely be
+   changed; a malloc or realloc with no unused array bytes will be done. */
+void *poolFixedMalloc(long size /* bytes */)
+{
+  void *ptr;
+  void *ptr2;
+/*E*/ /* Don't call print2() if db9 is set, since it will */
+/*E*/ /* recursively call the pool stuff causing a crash. */
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("a0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  if (!memFreePoolSize) { /* The pool is empty; we must allocate memory */
+    ptr = malloc( 3 * sizeof(long) + (size_t)size);
+    if (!ptr) outOfMemory(
+        cat("#25 (poolFixedMalloc ", str((double)size), ")", NULL));
+
+    ptr = (long *)ptr + 3;
+    ((long *)ptr)[-1] = size; /* Actual size */
+    ((long *)ptr)[-2] = size; /* Allocated size */
+    ((long *)ptr)[-3] = -1;  /* Location in memUsedPool (-1 = none) */
+    return (ptr);
+  } else {
+    memFreePoolSize--;
+    ptr = memFreePool[memFreePoolSize];
+    poolTotalFree = poolTotalFree - ((long *)ptr)[-2];
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("a: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    if (size <= ((long *)ptr)[-2]) { /* We have enough space already */
+      ptr2 = realloc( (long *)ptr - 3, 3 * sizeof(long) + (size_t)size);
+      /* Reallocation cannot fail, since we are shrinking space */
+      if (!ptr2) bug(1382);
+      ptr = ptr2;
+    } else { /* The pool's last entry is too small; free and allocate new */
+      free((long *)ptr - 3);
+      ptr = malloc( 3 * sizeof(long) + (size_t)size);
+    }
+    if (!ptr) {
+      /* Try freeing space */
+      print2("Memory is low.  Deallocating storage pool...\n");
+      memFreePoolPurge(0);
+      ptr = malloc( 3 * sizeof(long) + (size_t)size);
+      if (!ptr) outOfMemory(
+          cat("#26 (poolMalloc ", str((double)size), ")", NULL));
+                                            /* Nothing more can be done */
+    }
+    ptr = (long *)ptr + 3;
+    ((long *)ptr)[-1] = size; /* Actual size */
+    ((long *)ptr)[-2] = size; /* Allocated size */
+    ((long *)ptr)[-3] = -1;  /* Location in memUsedPool (-1 = none) */
+    return (ptr);
+  }
+}
+
+
+
+/* poolMalloc tries first to use an array in the memFreePool before actually
+   malloc'ing */
+void *poolMalloc(long size /* bytes */)
+{
+  void *ptr;
+  long memUsedPoolTmpMax;
+  void *memUsedPoolTmpPtr;
+
+  /* Check to see if the pool total exceeds max. */
+  if (poolTotalFree > poolAbsoluteMax) {
+    memFreePoolPurge(1);
+  }
+
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("b0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  if (!memFreePoolSize) { /* The pool is empty; we must allocate memory */
+    ptr = malloc( 3 * sizeof(long) + (size_t)size);
+    if (!ptr) {
+      outOfMemory(cat("#27 (poolMalloc ", str((double)size), ")", NULL));
+    }
+    ptr = (long *)ptr + 3;
+    ((long *)ptr)[-1] = size; /* Actual size */
+    ((long *)ptr)[-2] = size; /* Allocated size */
+    ((long *)ptr)[-3] = -1;  /* Location in memUsedPool (-1 = none) */
+    return (ptr);
+  } else {
+    memFreePoolSize--;
+    ptr = memFreePool[memFreePoolSize];
+    poolTotalFree = poolTotalFree - ((long *)ptr)[-2];
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("b: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    if (size <= ((long *)ptr)[-2]) { /* We have enough space already */
+      ((long *)ptr)[-1] = size; /* Actual size */
+      ((long *)ptr)[-3] = -1; /* Not in storage pool yet */
+    } else { /* We must reallocate */
+      free((long *)ptr - 3);
+      ptr = malloc( 3 * sizeof(long) + (size_t)size);
+      if (!ptr) {
+        /* Try freeing space */
+        print2("Memory is low.  Deallocating storage pool...\n");
+        memFreePoolPurge(0);
+        ptr = malloc( 3 * sizeof(long) + (size_t)size);
+        if (!ptr) outOfMemory(
+            cat("#28 (poolMalloc ", str((double)size), ")", NULL));
+                                              /* Nothing more can be done */
+      }
+      ptr = (long *)ptr + 3;
+      ((long *)ptr)[-1] = size; /* Actual size */
+      ((long *)ptr)[-2] = size; /* Allocated size */
+      ((long *)ptr)[-3] = -1;  /* Location in memUsedPool (-1 = none) */
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("bb: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+      return (ptr);
+    }
+  }
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("bc: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  if (((long *)ptr)[-1] == ((long *)ptr)[-2]) return (ptr);
+  /* Allocated and actual sizes are different, so add this array to used pool */
+  poolTotalFree = poolTotalFree + ((long *)ptr)[-2] - ((long *)ptr)[-1];
+  if (memUsedPoolSize >= memUsedPoolMax) { /* Increase size of used pool */
+    memUsedPoolTmpMax = memUsedPoolMax + MEM_POOL_GROW;
+/*E*/if(db9)printf("Growing used pool to %ld\n",memUsedPoolTmpMax);
+    if (!memUsedPoolMax) {
+      /* The program has just started; initialize */
+      memUsedPoolTmpPtr = malloc((size_t)memUsedPoolTmpMax
+          * sizeof(void *));
+      if (!memUsedPoolTmpPtr) bug(1303); /* Shouldn't have allocation problems
+                                                    when program first starts */
+    } else {
+      /* Normal reallocation */
+      memUsedPoolTmpPtr = realloc(memUsedPool,
+          (size_t)memUsedPoolTmpMax * sizeof(void *));
+    }
+    if (!memUsedPoolTmpPtr) {
+      outOfMemory(cat("#29 (poolMalloc ", str((double)memUsedPoolTmpMax), ")", NULL));
+    } else {
+      /* Reallocation successful */
+      memUsedPool = memUsedPoolTmpPtr;
+      memUsedPoolMax = memUsedPoolTmpMax;
+    }
+  }
+  memUsedPool[memUsedPoolSize] = ptr;
+  ((long *)ptr)[-3] = memUsedPoolSize;
+  memUsedPoolSize++;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("c: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  return (ptr);
+}
+
+/* poolFree puts freed up space in memFreePool. */
+void poolFree(void *ptr)
+{
+  void *ptr1;
+  long usedLoc;
+  long memFreePoolTmpMax;
+  void *memFreePoolTmpPtr;
+
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("c0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  /* First, see if the array is in memUsedPool; if so, remove it. */
+  usedLoc = ((long *)ptr)[-3];
+  if (usedLoc >= 0) { /* It is */
+    poolTotalFree = poolTotalFree - ((long *)ptr)[-2] + ((long *)ptr)[-1];
+    memUsedPoolSize--;
+
+    if (usedLoc < memUsedPoolSize) {
+      memUsedPool[usedLoc] = memUsedPool[memUsedPoolSize];
+      ptr1 = memUsedPool[usedLoc];
+      ((long *)ptr1)[-3] = usedLoc;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("d: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    }
+  }
+
+  /* Next, add the array to the memFreePool */
+  /* First, allocate more memFreePool pointer space if needed */
+  if (memFreePoolSize >= memFreePoolMax) { /* Increase size of free pool */
+    memFreePoolTmpMax = memFreePoolMax + MEM_POOL_GROW;
+/*E*/if(db9)printf("Growing free pool to %ld\n",memFreePoolTmpMax);
+    if (!memFreePoolMax) {
+      /* The program has just started; initialize */
+      memFreePoolTmpPtr = malloc((size_t)memFreePoolTmpMax
+          * sizeof(void *));
+      if (!memFreePoolTmpPtr) bug(1304); /* Shouldn't have allocation problems
+                                                    when program first starts */
+    } else {
+      /* Normal reallocation */
+      memFreePoolTmpPtr = realloc(memFreePool,
+          (size_t)memFreePoolTmpMax * sizeof(void *));
+    }
+    if (!memFreePoolTmpPtr) {
+/*E*/if(db9)printf("Realloc failed\n");
+      outOfMemory(cat("#30 (poolFree ", str((double)memFreePoolTmpMax), ")", NULL));
+    } else {
+      /* Reallocation successful */
+      memFreePool = memFreePoolTmpPtr;
+      memFreePoolMax = memFreePoolTmpMax;
+    }
+  }
+  /* Add the free array to the free pool */
+  memFreePool[memFreePoolSize] = ptr;
+  /* In theory, [-3] should never get referenced for an entry in the
+     memFreePool. However, here we make it a definite (illegal) value in
+     case it is referenced by code with a bug. */
+  ((long *)ptr)[-3] = -2;
+  memFreePoolSize++;
+  poolTotalFree = poolTotalFree + ((long *)ptr)[-2];
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("e: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  return;
+}
+
+
+/* addToUsedPool adds a (partially used) array to the memUsedPool */
+void addToUsedPool(void *ptr)
+{
+  long memUsedPoolTmpMax;
+  void *memUsedPoolTmpPtr;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("d0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  if (((long *)ptr)[-1] == ((long *)ptr)[-2]) bug(1305); /* No need to add it
+                                 when it's not partially used */
+  if (((long *)ptr)[-1] == ((long *)ptr)[-2]) return;
+  /* Allocated and actual sizes are different, so add this array to used pool */
+  if (memUsedPoolSize >= memUsedPoolMax) { /* Increase size of used pool */
+    memUsedPoolTmpMax = memUsedPoolMax + MEM_POOL_GROW;
+/*E*/if(db9)printf("1Growing used pool to %ld\n",memUsedPoolTmpMax);
+    if (!memUsedPoolMax) {
+      /* The program has just started; initialize */
+      memUsedPoolTmpPtr = malloc((size_t)memUsedPoolTmpMax
+          * sizeof(void *));
+      if (!memUsedPoolTmpPtr) bug(1362); /* Shouldn't have allocation problems
+                                                    when program first starts */
+    } else {
+      /* Normal reallocation */
+      memUsedPoolTmpPtr = realloc(memUsedPool, (size_t)memUsedPoolTmpMax
+          * sizeof(void *));
+    }
+    if (!memUsedPoolTmpPtr) {
+      outOfMemory("#31 (addToUsedPool)");
+    } else {
+      /* Reallocation successful */
+      memUsedPool = memUsedPoolTmpPtr;
+      memUsedPoolMax = memUsedPoolTmpMax;
+    }
+  }
+  memUsedPool[memUsedPoolSize] = ptr;
+  ((long *)ptr)[-3] = memUsedPoolSize;
+  memUsedPoolSize++;
+  poolTotalFree = poolTotalFree + ((long *)ptr)[-2] - ((long *)ptr)[-1];
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("f: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  return;
+}
+
+/* Free all arrays in the free pool. */
+
+/*!
+ * \fn void memFreePoolPurge(flag untilOK)
+ * \brief returns memory held in \ref memFreePool
+ * Starting with the last entry in \ref memFreePool, memory held in that pool
+ * is returned to the system until all, or at least a sufficient amount is
+ * freed again (see \p untilOK).
+ * \param[in] untilOK
+ *   - if 1 freeing \ref pgBlock "blocks" stops the moment \ref poolTotalFree
+ *     gets within the range of \ref poolAbsoluteMax again.  Note that it is
+ *     not guaranteed that the limit \ref poolAbsoluteMax is undercut because
+ *     still too much free memory might be held in the \ref memUsedPool.
+ *   - If 0, all \ref memFreePool entries are freed, and the pool itself is
+ *     shrunk back to \ref MEM_POOL_GROW size.
+ */
+void memFreePoolPurge(flag untilOK)
+{
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("e0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  while (memFreePoolSize) {
+    memFreePoolSize--;
+    /* Free an array */
+    poolTotalFree = poolTotalFree -
+        ((long *)(memFreePool[memFreePoolSize]))[-2];
+    free((long *)(memFreePool[memFreePoolSize]) - 3);
+    if (untilOK) {
+      /* If pool size is OK, return. */
+      if (poolTotalFree <= poolAbsoluteMax) return;
+    }
+  }
+  /* memFreePoolSize = 0 now. */
+  if (memFreePoolMax != MEM_POOL_GROW) {
+    /* Reduce size of pool pointer array to minimum growth increment. */
+    if (memFreePool) free(memFreePool); /* Only when starting program */
+    memFreePool = malloc(MEM_POOL_GROW
+        * sizeof(void *)); /* Allocate starting increment */
+    memFreePoolMax = MEM_POOL_GROW;
+  }
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("g: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  return;
+}
+
+
+/* Get statistics for SHOW MEMORY command */
+void getPoolStats(long *freeAlloc, long *usedAlloc, long *usedActual)
+{
+  long i;
+  *freeAlloc = 0;
+  *usedAlloc = 0;
+  *usedActual = 0;
+  for (i = 0; i < memFreePoolSize; i++) {
+    *freeAlloc = *freeAlloc + /*12 +*/ ((long *)(memFreePool[i]))[-2];
+  }
+  for (i = 0; i < memUsedPoolSize; i++) {
+    *usedActual = *usedActual + 12 + ((long *)(memUsedPool[i]))[-1];
+    *usedAlloc = *usedAlloc + ((long *)(memUsedPool[i]))[-2] -
+        ((long *)(memUsedPool[i]))[-1];
+  }
+/*E*/ if (!db9)print2("poolTotalFree %ld  alloc %ld\n", poolTotalFree, *freeAlloc +
+/*E*/   *usedAlloc);
+}
+
+
+
+void initBigArrays(void)
+{
+
+/*??? This should all become obsolete. */
+  g_Statement = malloc((size_t)g_MAX_STATEMENTS * sizeof(struct statement_struct));
+/*E*//*db=db+g_MAX_STATEMENTS * sizeof(struct statement_struct);*/
+  if (!g_Statement) {
+    print2("*** FATAL ***  Could not allocate g_Statement space\n");
+    bug(1363);
+    }
+  g_MathToken = malloc((size_t)g_MAX_MATHTOKENS * sizeof(struct mathToken_struct));
+/*E*//*db=db+g_MAX_MATHTOKENS * sizeof(struct mathToken_struct);*/
+  if (!g_MathToken) {
+    print2("*** FATAL ***  Could not allocate g_MathToken space\n");
+    bug(1364);
+    }
+  g_IncludeCall = malloc((size_t)g_MAX_INCLUDECALLS * sizeof(struct includeCall_struct));
+/*E*//*db=db+g_MAX_INCLUDECALLS * sizeof(struct includeCall_struct);*/
+  if (!g_IncludeCall) {
+    print2("*** FATAL ***  Could not allocate g_IncludeCall space\n");
+    bug(1365);
+    }
+}
+
+/* Find the number of free memory bytes */
+long getFreeSpace(long max)
+{
+  long i , j, k;
+  char *s;
+  i = 0;
+  j = max + 2;
+  while (i < j - 2) {
+    k = (i + j) / 2;
+    s = malloc((size_t)k);
+    if (s) {
+      free(s);
+      i = k;
+    } else {
+      j = k;
+    }
+  }
+  return (i);
+}
+
+/* Fatal memory allocation error */
+void outOfMemory(const char *msg) {
+  vstring_def(tmpStr);
+  print2("*** FATAL ERROR:  Out of memory.\n");
+  print2("Internal identifier (for technical support):  %s\n", msg);
+  print2("To solve this problem, remove some unnecessary statements or file\n");
+  print2("inclusions to reduce the size of your input source.\n");
+  print2("Monitor memory periodically with SHOW MEMORY.\n");
+  print2("\n");
+  print2("Press <return> to exit Metamath.\n");
+  tmpStr = cmdInput1("");
+  free_vstring(tmpStr);
+  /* Close the log to make sure error log is saved */
+  if (g_logFileOpenFlag) {
+    fclose(g_logFilePtr);
+    g_logFileOpenFlag = 0;
+  }
+
+  exit(1);
+}
+
+
+/* Bug check */
+void bug(int bugNum)
+{
+  vstring_def(tmpStr);
+  flag oldMode;
+  long wrongAnswerCount = 0;
+  static flag mode = 0; /* 1 = run to next bug, 2 = continue and ignore bugs */
+
+  flag saveOutputToString = g_outputToString;
+  g_outputToString = 0; /* Make sure we print to screen and not to string */
+
+  if (mode == 2) {
+    /* If user chose to ignore bugs, print brief info and return */
+    print2("?BUG CHECK:  *** DETECTED BUG %ld, IGNORING IT...\n", (long)bugNum);
+    return;
+  }
+
+  print2("?BUG CHECK:  *** DETECTED BUG %ld\n", (long)bugNum);
+  if (mode == 0) { /* Print detailed info for first bug */
+    print2("\n");
+    print2("To get technical support, please open an issue \n");
+    print2("(https://github.com/metamath/metamath-exe/issues) with the\n");
+    print2("detailed command sequence or a command file that reproduces this bug,\n");
+    print2("along with the source file that was used.  See HELP LOG for help on\n");
+    print2("recording a session.  See HELP SUBMIT for help on command files.  Search\n");
+    print2("for \"bug(%ld)\" in the m*.c source code to find its origin.\n", bugNum);
+    print2("If earlier errors were reported, try fixing them first, because they\n");
+    print2("may occasionally lead to false bug detection\n");
+    print2("\n");
+  }
+
+  let(&tmpStr, "?");
+  while (strcmp(tmpStr, "A") && strcmp(tmpStr, "a")
+      && strcmp(tmpStr, "S") && strcmp(tmpStr, "s")
+      && strcmp(tmpStr, "I") && strcmp(tmpStr, "i")
+      /* The above is actually useless because of break below, but we'll leave
+         it in case we want to re-ask after wrong answers in the future */
+      ) {
+    if (wrongAnswerCount > 6) {
+      print2("Too many wrong answers; program will be aborted to exit scripting loops.\n");
+      break;
+    }
+    if (wrongAnswerCount > 0) {
+      free_vstring(tmpStr);
+      tmpStr = cmdInput1("Please answer I, S, or A:  ");
+    } else {
+      print2("Press S <return> to step to next bug, I <return> to ignore further bugs,\n");
+      free_vstring(tmpStr);
+      tmpStr = cmdInput1("or A <return> to abort program:  ");
+    }
+    wrongAnswerCount++;
+  }
+  oldMode = mode;
+  mode = 0;
+  if (!strcmp(tmpStr, "S") || !strcmp(tmpStr, "s")) mode = 1; /* Skip to next bug */
+  if (!strcmp(tmpStr, "I") || !strcmp(tmpStr, "i")) mode = 2; /* Ignore bugs */
+  if (oldMode == 0 && mode > 0) {
+    /* Print dire warning after the first bug only */
+    print2("\n");
+    print2("Warning!!!  A bug was detected, but you are continuing anyway.\n");
+    print2("The program may be corrupted, so you are proceeding at your own risk.\n");
+    print2("\n");
+    free_vstring(tmpStr);
+  }
+  if (mode > 0) {
+    g_outputToString = saveOutputToString; /* Restore for continuation */
+    return;
+  }
+  free_vstring(tmpStr);
+
+  print2("\n");
+  /* Close the log to make sure error log is saved */
+  if (g_logFileOpenFlag) {
+    print2("The log file \"%s\" was closed %s %s.\n", g_logFileName,
+        date(), time_());
+    fclose(g_logFilePtr);
+    g_logFileOpenFlag = 0;
+  }
+  print2("The program was aborted.\n");
+  exit(1); /* Use 1 instead of 0 to flag abnormal termination to scripts */
+}
+
+/*!
+ * \def M_MAX_ALLOC_STACK
+ *
+ * The number of pointers in a \ref pgStack "stack" available for data reference.
+ * Since a stack has a terminal null element, the usable count is one less than
+ * the number given here.
+ */
+#define M_MAX_ALLOC_STACK 100
+
+/* This function returns a 1 if any entry in a comma-separated list
+   matches using the matches() function. */
+flag matchesList(const char *testString, const char *pattern, char wildCard,
+    char oneCharWildCard) {
+  long entries, i;
+  flag matchVal = 0;
+  vstring_def(entryPattern);
+
+  /* Done so we can use string functions like left() in call arguments */
+  long saveTempAllocStack;
+  saveTempAllocStack = g_startTempAllocStack; /* For let() stack cleanup */
+  g_startTempAllocStack = g_tempAllocStackTop;
+
+  entries = numEntries(pattern);
+  for (i = 1; i <= entries; i++) {
+    let(&entryPattern, entry(i, pattern)); /* If we didn't modify
+          g_startTempAllocStack above, this let() would corrupt string
+          functions in the matchesList() call arguments */
+    matchVal = matches(testString, entryPattern, wildCard, oneCharWildCard);
+    if (matchVal) break;
+  }
+
+  free_vstring(entryPattern); /* Deallocate */
+  g_startTempAllocStack = saveTempAllocStack;
+  return matchVal;
+}
+
+
+/* This function returns a 1 if the first argument matches the pattern of
+   the second argument.  The second argument may have wildcard characters.
+   wildCard matches 0 or more characters; oneCharWildCard matches any
+   single character. */
+flag matches(const char *testString, const char *pattern, char wildCard,
+    char oneCharWildCard) {
+  long i, ppos, pctr, tpos, s1, s2, s3;
+  vstring_def(tmpStr);
+
+  if (wildCard == '*') {
+    /* Checking for wildCard = * meaning this is only for labels, not
+       math tokens */
+
+    /* The following special chars are handled in this block:
+       "~" Statement range
+       "=" Most recent PROVE command statement
+       "%" List of modified statements
+       "#" Internal statement number
+       "@" Web page statement number */
+
+    i = instr(1, pattern, "~");
+    if (i != 0) {
+      if (i == 1) {
+        s1 = 1; /* empty string before "~" */
+      } else {
+        s1 = lookupLabel(left(pattern, i - 1));
+      }
+      s2 = lookupLabel(testString);
+      if (i == (long)strlen(pattern)) {
+        s3 = g_statements; /* empty string after "~" */
+      } else {
+        s3 = lookupLabel(right(pattern, i + 1));
+      }
+      free_vstring(tmpStr); /* Clean up temporary allocations of left and right */
+      return ((s1 >= 1 && s2 >= 1 && s3 >= 1 && s1 <= s2 && s2 <= s3)
+          ? 1 : 0);
+    }
+
+    /* "#12345" matches internal statement number */
+    if (pattern[0] == '#') {
+      s1 = (long)val(right(pattern, 2));
+      if (s1 < 1 || s1 > g_statements)
+        return 0; /* # arg is out of range */
+      if (!strcmp(g_Statement[s1].labelName, testString)) {
+        return 1;
+      } else {
+        return 0;
+      }
+    }
+
+    /* "@12345" matches web statement number */
+    if (pattern[0] == '@') {
+      s1 = lookupLabel(testString);
+      if (s1 < 1) return 0;
+      s2 = (long)val(right(pattern, 2));
+      if (g_Statement[s1].pinkNumber == s2) {
+        return 1;
+      } else {
+        return 0;
+      }
+    }
+
+    /* "=" matches statement being proved */
+    if (!strcmp(pattern,"=")) {
+      s1 = lookupLabel(testString);
+      /* We might as well use g_proveStatement outside of MM-PA, so =
+         can be argument to PROVE command */
+      return (g_proveStatement == s1);
+    }
+
+    /* "%" matches changed proofs */
+    if (!strcmp(pattern,"%")) {
+      s1 = lookupLabel(testString);  /* Returns -1 if not found or (not
+                                        $a and not $p) */
+      if (s1 > 0) { /* It's a $a or $p statement */
+        /* (If it's not $p, we don't want to peek at proofSectionPtr[-1]
+           to prevent bad pointer. */
+        if (g_Statement[s1].type == (char)p_) { /* $p so it has a proof */
+          /* The proof is not from the original source file */
+          if (g_Statement[s1].proofSectionChanged == 1) {
+            return 1;
+          }
+        }
+      }
+      return 0;
+    } /* if (!strcmp(pattern,"%")) */
+  } /* if (wildCard == '*') */
+
+  /* Get to first wild card character */
+  ppos = 0;
+  while ((pattern[ppos] == testString[ppos] ||
+          (pattern[ppos] == oneCharWildCard && testString[ppos] != 0))
+      && pattern[ppos] != 0) ppos++;
+  if (pattern[ppos] == 0) {
+    if (testString[ppos] != 0) {
+      return (0); /* No wildcards; mismatched */
+    } else {
+      return (1); /* No wildcards; matched */
+    }
+  }
+  if (pattern[ppos] != wildCard) {
+    return (0); /* Mismatched */
+  }
+  tpos = ppos;
+
+  /* Scan remainder of pattern */
+  pctr = 0;
+  i = 0;
+  while (1) {
+    if (pattern[ppos + 1 + i] == wildCard) { /* Next wildcard found */
+      tpos = tpos + pctr + i;
+      ppos = ppos + 1 + i;
+      i = 0;
+      pctr = 0;
+      continue;
+    }
+    if (pattern[ppos + 1 + i] != testString[tpos + pctr + i]
+          && (pattern[ppos + 1 + i] != oneCharWildCard
+              || testString[tpos + pctr + i] == 0)) {
+      if (testString[tpos + pctr + i] == 0) {
+        return (0);
+      }
+      pctr++;
+      i = 0;
+      continue;
+    }
+    if (pattern[ppos + 1 + i] == 0) {
+      return(1); /* Matched */
+    }
+    i++;
+  }
+  bug(1375);
+  return (0); /* Dummy return - never used */
+}
+
+
+
+
+/*******************************************************************/
+/*********** Number string functions *******************************/
+/*******************************************************************/
+
+long g_nmbrTempAllocStackTop = 0;     /* Top of stack for nmbrTempAlloc function */
+long g_nmbrStartTempAllocStack = 0;   /* Where to start freeing temporary allocation
+                                    when nmbrLet() is called (normally 0, except in
+                                    special nested vstring functions) */
+temp_nmbrString *nmbrTempAllocStack[M_MAX_ALLOC_STACK];
+
+
+temp_nmbrString *nmbrTempAlloc(long size)
+                                /* nmbrString memory allocation/deallocation */
+{
+  /* When "size" is >0, "size" instances of nmbrString are allocated. */
+  /* When "size" is 0, all memory previously allocated with this */
+  /* function is deallocated, down to g_nmbrStartTempAllocStack. */
+  if (size) {
+    if (g_nmbrTempAllocStackTop>=(M_MAX_ALLOC_STACK-1)) {
+      /*??? Fix to allocate more */
+      outOfMemory("#105 (nmbrString stack array)");
+    }
+    if (!(nmbrTempAllocStack[g_nmbrTempAllocStackTop++]=poolMalloc(size
+        *(long)(sizeof(nmbrString)))))
+/*E*/db2=db2+size*(long)(sizeof(nmbrString));
+    return (nmbrTempAllocStack[g_nmbrTempAllocStackTop-1]);
+  } else {
+    while(g_nmbrTempAllocStackTop != g_nmbrStartTempAllocStack) {
+/*E*/db2=db2-(nmbrLen(nmbrTempAllocStack[g_nmbrTempAllocStackTop-1])+1)
+/*E*/                                              *(long)(sizeof(nmbrString));
+      poolFree(nmbrTempAllocStack[--g_nmbrTempAllocStackTop]);
+    }
+    g_nmbrTempAllocStackTop=g_nmbrStartTempAllocStack;
+    return (0);
+  }
+}
+
+
+/* Make string have temporary allocation to be released by next nmbrLet() */
+/* Warning:  after nmbrMakeTempAlloc() is called, the nmbrString may NOT be
+   assigned again with nmbrLet() */
+temp_nmbrString *nmbrMakeTempAlloc(nmbrString *s)
+{
+  if (g_nmbrTempAllocStackTop>=(M_MAX_ALLOC_STACK-1)) {
+    printf("*** FATAL ERROR ***  Temporary nmbrString stack overflow in nmbrMakeTempAlloc()\n");
+#if __STDC__
+    fflush(stdout);
+#endif
+    bug(1368);
+  }
+  if (s[0] != -1) { /* End of string */
+    /* Do it only if nmbrString is not empty */
+    nmbrTempAllocStack[g_nmbrTempAllocStackTop++] = s;
+  }
+/*E*/db2=db2+(nmbrLen(s)+1)*(long)(sizeof(nmbrString));
+/*E*/db3=db3-(nmbrLen(s)+1)*(long)(sizeof(nmbrString));
+  return s;
+}
+
+
+/* nmbrString assignment */
+/* This function must ALWAYS be called to make assignment to */
+/* a nmbrString in order for the memory cleanup routines, etc. */
+/* to work properly.  If a nmbrString has never been assigned before, */
+/* it is the user's responsibility to initialize it to NULL_NMBRSTRING (the */
+/* null string). */
+void nmbrLet(nmbrString **target, const nmbrString *source) {
+  long targetLength,sourceLength;
+  long targetAllocLen;
+  long poolDiff;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  sourceLength=nmbrLen(source);  /* Save its actual length */
+  targetLength=nmbrLen(*target);  /* Save its actual length */
+  targetAllocLen=nmbrAllocLen(*target); /* Save target's allocated length */
+/*E*/if (targetLength) {
+/*E*/  db3 = db3 - (targetLength+1)*(long)(sizeof(nmbrString));
+/*E*/}
+/*E*/if (sourceLength) {
+/*E*/  db3 = db3 + (sourceLength+1)*(long)(sizeof(nmbrString));
+/*E*/}
+  if (targetAllocLen) {
+    if (sourceLength) { /* source and target are both nonzero length */
+
+      if (targetAllocLen >= sourceLength) { /* Old string has room for new one */
+        nmbrCpy(*target,source); /* Re-use the old space to save CPU time */
+
+        /* Memory pool handling */
+        /* Assign actual size of target string */
+        poolDiff = ((long *)(*target))[-1] - ((long *)source)[-1];
+        ((long *)(*target))[-1] = ((long *)source)[-1];
+        /* If actual size of target string is less than allocated size, we
+           may have to add it to the used pool */
+        if (((long *)(*target))[-1] != ((long *)(*target))[-2]) {
+          if (((long *)(*target))[-1] > ((long *)(*target))[-2]) bug(1325);
+          if (((long *)(*target))[-3] == -1) {
+            /* It's not already in the used pool, so add it */
+            addToUsedPool(*target);
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0aa: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+          } else {
+            /* Adjust free space independently */
+            poolTotalFree = poolTotalFree + poolDiff;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0ab: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+          }
+        } else {
+          if (((long *)(*target))[-3] != -1) {
+            /* It's in the pool (but all allocated space coincidentally used) */
+            /* Adjust free space independently */
+            poolTotalFree = poolTotalFree + poolDiff;
+          }
+        }
+
+
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0a: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+      } else {
+        /* Free old string space and allocate new space */
+        poolFree(*target);  /* Free old space */
+        /* *target=poolMalloc((sourceLength + 1) * sizeof(nmbrString)); */
+        *target=poolMalloc((sourceLength + 1) * (long)(sizeof(nmbrString)) * 2);
+                        /* Allocate new space --
+                            We are replacing a smaller string with a larger one;
+                            assume it is growing, and allocate twice as much as
+                            needed. */
+        nmbrCpy(*target,source);
+
+        /* Memory pool handling */
+        /* Assign actual size of target string */
+        poolDiff = ((long *)(*target))[-1] - ((long *)source)[-1];
+        ((long *)(*target))[-1] = ((long *)source)[-1];
+        /* If actual size of target string is less than allocated size, we
+           may have to add it to the used pool */
+        /* (The 1st 'if' is redundant with target doubling above) */
+        if (((long *)(*target))[-1] != ((long *)(*target))[-2]) {
+          if (((long *)(*target))[-1] > ((long *)(*target))[-2]) bug(1326);
+          if (((long *)(*target))[-3] == -1) {
+            /* It's not already in the used pool, so add it */
+            addToUsedPool(*target);
+          } else {
+            /* Adjust free space independently */
+            poolTotalFree = poolTotalFree + poolDiff;
+          }
+        } else {
+          if (((long *)(*target))[-3] != -1) {
+            /* It's in the pool (but all allocated space coincidentally used) */
+            /* Adjust free space independently */
+            poolTotalFree = poolTotalFree + poolDiff;
+          }
+        }
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0b: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+
+      }
+
+    } else {    /* source is 0 length, target is not */
+      poolFree(*target);
+      *target= NULL_NMBRSTRING;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0c: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    }
+  } else {
+    if (sourceLength) { /* target is 0 length, source is not */
+      *target=poolMalloc((sourceLength + 1) * (long)(sizeof(nmbrString)));
+                        /* Allocate new space */
+      nmbrCpy(*target,source);
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0d: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    } else {    /* source and target are both 0 length */
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0e: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    }
+  }
+
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k1: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  nmbrTempAlloc(0); /* Free up temporary strings used in expression computation*/
+
+}
+
+
+
+temp_nmbrString *nmbrCat(const nmbrString *string1,...) /* String concatenation */
+#define M_MAX_CAT_ARGS 30
+{
+  va_list ap;   /* Declare list incrementer */
+  const nmbrString *arg[M_MAX_CAT_ARGS];        /* Array to store arguments */
+  long argLength[M_MAX_CAT_ARGS];       /* Array to store argument lengths */
+  int numArgs = 1;        /* Define "last argument" */
+  arg[0] = string1;       /* First argument */
+
+  va_start(ap,string1); /* Begin the session */
+  while ((arg[numArgs++]=va_arg(ap,nmbrString *)))
+        /* User-provided argument list must terminate with NULL */
+    if (numArgs>=M_MAX_CAT_ARGS-1) {
+      printf("*** FATAL ERROR ***  Too many cat() arguments\n");
+#if __STDC__
+      fflush(stdout);
+#endif
+      bug(1369);
+    }
+  va_end(ap);           /* End varargs session */
+
+  numArgs--;    /* The last argument (0) is not a string */
+
+  /* Find out the total string length needed */
+  long j = 0;
+  for (int i = 0; i < numArgs; i++) {
+    argLength[i]=nmbrLen(arg[i]);
+    j += argLength[i];
+  }
+  /* Allocate the memory for it */
+  temp_nmbrString *ptr = nmbrTempAlloc(j+1);
+  /* Move the strings into the newly allocated area */
+  j = 0;
+  for (int i = 0; i < numArgs; i++) {
+    nmbrCpy(ptr + j, arg[i]);
+    j += argLength[i];
+  }
+  return ptr;
+
+}
+
+
+
+/* Find out the length of a nmbrString */
+long nmbrLen(const nmbrString *s)
+{
+  /* Assume it's been allocated with poolMalloc. */
+  return (((long)(((const long *)s)[-1] - (long)(sizeof(nmbrString))))
+              / (long)(sizeof(nmbrString)));
+}
+
+
+/* Find out the allocated length of a nmbrString */
+long nmbrAllocLen(const nmbrString *s)
+{
+  /* Assume it's been allocated with poolMalloc. */
+  return (((long)(((const long *)s)[-2] - (long)(sizeof(nmbrString))))
+              / (long)(sizeof(nmbrString)));
+}
+
+/* Set the actual size field in a nmbrString allocated with poolFixedMalloc() */
+/* Use this if "zapping" a nmbrString element with -1 to reduce its length. */
+/* Note that the nmbrString will not be moved to the "used pool", even if
+   zapping its length results in free space; thus the free space will never
+   get recovered unless done by the caller or poolFree is called.  (This is
+   done on purpose so the caller can know what free space is left.) */
+/* ???Note that nmbrZapLen's not moving string to used pool wastes potential
+   space when called by the routines in this module.  Effect should be minor. */
+void nmbrZapLen(nmbrString *s, long length) {
+  if (((long *)s)[-3] != -1) {
+    /* It's already in the used pool, so adjust free space tally */
+    poolTotalFree = poolTotalFree + ((long *)s)[-1]
+        - (length + 1) * (long)(sizeof(nmbrString));
+  }
+  ((long *)s)[-1] = (length + 1) * (long)(sizeof(nmbrString));
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("l: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+}
+
+
+/* Copy a string to another (pre-allocated) string */
+/* Dangerous for general purpose use */
+void nmbrCpy(nmbrString *s, const nmbrString *t) {
+  long i;
+  i = 0;
+  while (t[i] != -1) { /* End of string -- nmbrRight depends on it!! */
+    s[i] = t[i];
+    i++;
+  }
+  s[i] = t[i]; /* End of string */
+}
+
+
+/* Copy a string to another (pre-allocated) string */
+/* Like strncpy, only the 1st n characters are copied. */
+/* Dangerous for general purpose use */
+void nmbrNCpy(nmbrString *s, const nmbrString *t, long n) {
+  long i;
+  i = 0;
+  while (t[i] != -1) { /* End of string -- nmbrSeg, nmbrMid depend on it!! */
+    if (i >= n) break;
+    s[i] = t[i];
+    i++;
+  }
+  s[i] = t[i]; /* End of string */
+}
+
+
+/* Compare two strings */
+/* Unlike strcmp, this returns a 1 if the strings are equal
+   and 0 otherwise. */
+/* Only the token is compared.  The whiteSpace string is
+   ignored. */
+flag nmbrEq(const nmbrString *s, const nmbrString *t) {
+  long i;
+  if (nmbrLen(s) != nmbrLen(t)) return 0; /* Speedup */
+  for (i = 0; s[i] == t[i]; i++)
+    if (s[i] == -1) /* End of string */
+      return 1;
+  return 0;
+}
+
+
+/* Extract sin from character position start to stop into sout */
+temp_nmbrString *nmbrSeg(const nmbrString *sin, long start, long stop) {
+  long length;
+  if (start < 1) start = 1;
+  if (stop < 1) stop = 0;
+  length=stop - start + 1;
+  if (length < 0) length = 0;
+  temp_nmbrString *sout = nmbrTempAlloc(length + 1);
+  nmbrNCpy(sout, sin + start - 1, length);
+  sout[length] = *NULL_NMBRSTRING;
+  return sout;
+}
+
+/* Extract sin from character position start for length len */
+temp_nmbrString *nmbrMid(const nmbrString *sin, long start, long length) {
+  if (start < 1) start = 1;
+  if (length < 0) length = 0;
+  temp_nmbrString *sout = nmbrTempAlloc(length + 1);
+  nmbrNCpy(sout, sin + start - 1, length);
+  sout[length] = *NULL_NMBRSTRING;
+  return sout;
+}
+
+/* Extract leftmost n characters */
+temp_nmbrString *nmbrLeft(const nmbrString *sin, long n) {
+  if (n < 0) n = 0;
+  temp_nmbrString *sout = nmbrTempAlloc(n + 1);
+  nmbrNCpy(sout, sin, n);
+  sout[n] = *NULL_NMBRSTRING;
+  return sout;
+}
+
+/* Extract after character n */
+temp_nmbrString *nmbrRight(const nmbrString *sin, long n) {
+  /*??? We could just return &sin[n-1], but this is safer for debugging. */
+  if (n < 1) n = 1;
+  long i = nmbrLen(sin);
+  if (n > i) return (NULL_NMBRSTRING);
+  temp_nmbrString *sout = nmbrTempAlloc(i - n + 2);
+  nmbrCpy(sout, &sin[n - 1]);
+  return sout;
+}
+
+
+/* Allocate and return an "empty" string n "characters" long */
+temp_nmbrString *nmbrSpace(long n) {
+  long j = 0;
+  if (n < 0) bug(1327);
+  temp_nmbrString *sout = nmbrTempAlloc(n + 1);
+  while (j < n) {
+    /* Initialize all fields */
+    sout[j] = 0;
+    j++;
+  }
+  sout[j] = *NULL_NMBRSTRING; /* End of string */
+  return sout;
+}
+
+/* Search for string2 in string1 starting at start_position */
+long nmbrInstr(long start_position, const nmbrString *string1,
+  const nmbrString *string2)
+{
+   long ls1, ls2, i, j;
+   if (start_position < 1) start_position = 1;
+   ls1 = nmbrLen(string1);
+   ls2 = nmbrLen(string2);
+   for (i = start_position - 1; i <= ls1 - ls2; i++) {
+     for (j = 0; j < ls2; j++) {
+       if (string1[i+j] != string2[j])
+         break;
+     }
+     if (j == ls2) return (i+1);
+   }
+   return 0;
+}
+
+/* Search for string2 in string 1 in reverse starting at start_position */
+/* (Reverse nmbrInstr) */
+/* Warning:  This has 'let' inside of it and is not safe for use inside
+   of 'let' statements.  (To make it safe, it must be rewritten to expand
+   the 'mid' and remove the 'let'.) */
+long nmbrRevInstr(long start_position, const nmbrString *string1,
+    const nmbrString *string2)
+{
+   long ls1, ls2;
+   ls1 = nmbrLen(string1);
+   ls2 = nmbrLen(string2);
+   if (start_position > ls1 - ls2 + 1) start_position = ls1 - ls2 + 2;
+   if (start_position<1) return 0;
+   while (!nmbrEq(string2, nmbrMid(string1, start_position, ls2))) {
+     start_position--;
+     nmbrTempAlloc(0); /* Clear temporaries to prevent overflow caused by "mid" */
+     if (start_position < 1) return 0;
+   }
+   return start_position;
+}
+
+
+/* Converts nmbrString to a vstring with one space between tokens */
+temp_vstring nmbrCvtMToVString(const nmbrString *s) {
+  long i, j, outputLen, mstrLen;
+  vstring_def(tmpStr);
+  vstring ptr;
+  vstring ptr2;
+
+  long saveTempAllocStack;
+  saveTempAllocStack = g_startTempAllocStack; /* For let() stack cleanup */
+  g_startTempAllocStack = g_tempAllocStackTop;
+
+  mstrLen = nmbrLen(s);
+  /* Precalculate output length */
+  outputLen = -1;
+  for (i = 0; i < mstrLen; i++) {
+    outputLen = outputLen + (long)strlen(g_MathToken[s[i]].tokenName) + 1;
+  }
+  let(&tmpStr, space(outputLen)); /* Preallocate output string */
+  /* Assign output string */
+  ptr = tmpStr;
+  for (i = 0; i < mstrLen; i++) {
+    ptr2 = g_MathToken[s[i]].tokenName;
+    j = (long)strlen(ptr2);
+    memcpy(ptr, ptr2, (size_t)j);
+    ptr = ptr + j + 1;
+  }
+
+  g_startTempAllocStack = saveTempAllocStack;
+  return makeTempAlloc(tmpStr); /* Flag it for deallocation */
+}
+
+
+/* Converts proof to a vstring with one space between tokens */
+temp_vstring nmbrCvtRToVString(const nmbrString *proof,
+    flag explicitTargets, /* 1 = "target=source" for /EXPLICIT proof format */
+    long statemNum) /* used only if explicitTargets=1 */
+{
+  long i, j, plen, maxLabelLen, maxLocalLen, step, stmt;
+  long maxTargetLabelLen;
+  vstring_def(proofStr);
+  vstring_def(tmpStr);
+  vstring ptr;
+  nmbrString_def(localLabels);
+  nmbrString_def(localLabelNames);
+  long nextLocLabNum = 1; /* Next number to be used for a local label */
+  void *voidPtr; /* bsearch result */
+  nmbrString_def(targetHyps); /* Targets for /EXPLICIT format */
+
+  long saveTempAllocStack;
+  long nmbrSaveTempAllocStack;
+  saveTempAllocStack = g_startTempAllocStack; /* For let() stack cleanup */
+  g_startTempAllocStack = g_tempAllocStackTop;
+  nmbrSaveTempAllocStack = g_nmbrStartTempAllocStack;
+                                           /* For nmbrLet() stack cleanup*/
+  g_nmbrStartTempAllocStack = g_nmbrTempAllocStackTop;
+
+  plen = nmbrLen(proof);
+
+  if (explicitTargets == 1) {
+    /* Get the list of targets for /EXPLICIT format */
+    if (statemNum <= 0) bug(1388);
+    nmbrLet(&targetHyps, nmbrGetTargetHyp(proof, statemNum));
+  }
+
+  /* Find longest local label name */
+  maxLocalLen = 0;
+  i = plen;
+  while (i) {
+    i = i / 10;
+    maxLocalLen++;
+  }
+
+  /* Collect local labels */
+  /* Also, find longest statement label name */
+  maxLabelLen = 0;
+  maxTargetLabelLen = 0;
+  for (step = 0; step < plen; step++) {
+    stmt = proof[step];
+    if (stmt <= -1000) {
+      stmt = -1000 - stmt;
+      if (!nmbrElementIn(1, localLabels, stmt)) {
+        nmbrLet(&localLabels, nmbrAddElement(localLabels, stmt));
+      }
+    } else {
+
+      if (stmt < 1 || stmt > g_statements) {
+        maxLabelLen = 100; /* For safety */
+        maxTargetLabelLen = 100; /* For safety */
+        continue; /* Ignore bad entry */
+      }
+
+      if (stmt > 0) {
+        if ((signed)(strlen(g_Statement[stmt].labelName)) > maxLabelLen) {
+          maxLabelLen = (long)strlen(g_Statement[stmt].labelName);
+        }
+      }
+    }
+
+    if (explicitTargets == 1) {
+      /* Also consider longest target label name */
+      stmt = targetHyps[step];
+      if (stmt <= 0) bug(1390);
+      if ((signed)(strlen(g_Statement[stmt].labelName)) > maxTargetLabelLen) {
+        maxTargetLabelLen = (long)strlen(g_Statement[stmt].labelName);
+      }
+    }
+
+  } /* next step */
+
+  /* localLabelNames[] holds an integer which, when converted to string,
+    is the local label name. */
+  nmbrLet(&localLabelNames, nmbrSpace(plen));
+
+  /* Build the ASCII string */
+  /* Preallocate the string for speed (the "2" accounts for a space and a
+     colon). */
+  let(&proofStr, space(plen * (2 + maxLabelLen
+      + ((explicitTargets == 1) ? maxTargetLabelLen + 1 : 0)
+                                          /* The "1" accounts for equal sign */
+      + maxLocalLen)));
+  ptr = proofStr;
+  for (step = 0; step < plen; step++) {
+    stmt = proof[step];
+    if (stmt < 0) {
+      if (stmt <= -1000) {
+        stmt = -1000 - stmt;
+            /* Change stmt to the step number a local label refers to */
+        let(&tmpStr, cat(
+            ((explicitTargets == 1) ? g_Statement[targetHyps[step]].labelName : ""),
+            ((explicitTargets == 1) ? "=" : ""),
+            str((double)(localLabelNames[stmt])), " ", NULL));
+
+      } else if (stmt != -(long)'?') {
+        let(&tmpStr, cat("??", str((double)stmt), " ", NULL)); /* For safety */
+
+      } else {
+        if (stmt != -(long)'?') bug(1391); /* Must be an unknown step */
+        let(&tmpStr, cat(
+            ((explicitTargets == 1) ? g_Statement[targetHyps[step]].labelName : ""),
+            ((explicitTargets == 1) ? "=" : ""),
+            chr(-stmt), " ", NULL));
+      }
+
+    } else if (stmt < 1 || stmt > g_statements) {
+      let(&tmpStr, cat("??", str((double)stmt), " ", NULL)); /* For safety */
+
+    } else {
+      free_vstring(tmpStr);
+      if (nmbrElementIn(1, localLabels, step)) {
+        /* This statement declares a local label */
+        /* First, get a name for the local label, using the next integer that
+           does not match any integer used for a statement label. */
+        let(&tmpStr, str((double)nextLocLabNum));
+        while (1) {
+          voidPtr = (void *)bsearch(tmpStr,
+              g_allLabelKeyBase, (size_t)g_numAllLabelKeys,
+              sizeof(long), labelSrchCmp);
+          if (!voidPtr) break; /* It does not conflict */
+          nextLocLabNum++; /* Try the next one */
+          let(&tmpStr, str((double)nextLocLabNum));
+        }
+        localLabelNames[step] = nextLocLabNum;
+        let(&tmpStr, cat(tmpStr, ":", NULL));
+        nextLocLabNum++; /* Prepare for next local label */
+      }
+      let(&tmpStr, cat(tmpStr,
+          ((explicitTargets == 1) ? g_Statement[targetHyps[step]].labelName : ""),
+          ((explicitTargets == 1) ? "=" : ""),
+          g_Statement[stmt].labelName, " ", NULL));
+    }
+    j = (long)strlen(tmpStr);
+    memcpy(ptr, tmpStr, (size_t)j);
+    ptr = ptr + j;
+  } /* Next step */
+
+  if (ptr - proofStr) {
+    /* Deallocate large pool and trim trailing space */
+    let(&proofStr, left(proofStr, ptr - proofStr - 1));
+  } else {
+    let(&proofStr, "");
+  }
+  free_vstring(tmpStr);
+  free_nmbrString(localLabels);
+  free_nmbrString(localLabelNames);
+
+  g_startTempAllocStack = saveTempAllocStack;
+  g_nmbrStartTempAllocStack = nmbrSaveTempAllocStack;
+  return makeTempAlloc(proofStr); /* Flag it for deallocation */
+}
+
+
+/* This function returns a nmbrString of length of reason with
+   step numbers assigned to tokens which are steps, and 0 otherwise.
+   The returned string is allocated; THE CALLER MUST DEALLOCATE IT. */
+nmbrString *nmbrGetProofStepNumbs(const nmbrString *reason) {
+  nmbrString_def(stepNumbs);
+  long rlen, start, end, i, step;
+
+  rlen = nmbrLen(reason);
+  nmbrLet(&stepNumbs, nmbrSpace(rlen)); /* All stepNumbs[] are initialized
+                                        to 0 by nmbrSpace() */
+  if (!rlen) return (stepNumbs);
+  if (reason[1] == -(long)'=') {
+    /* The proof is in "internal" format, with "g_proveStatement = (...)" added */
+    start = 2; /* 2, not 3, so empty proof '?' will be seen */
+    if (rlen == 3) {
+      end = rlen; /* Empty proof case */
+    } else {
+      end = rlen - 1; /* Trim off trailing ')' */
+    }
+  } else {
+    start = 1;
+    end = rlen;
+  }
+  step = 0;
+  for (i = start; i < end; i++) {
+    if (i == 0) {
+      /* i = 0 must be handled separately to prevent a reference to
+         a field outside of the nmbrString */
+      step++;
+      stepNumbs[0] = step;
+      continue;
+    }
+    if (reason[i] < 0 && reason[i] != -(long)'?') continue;
+    if (reason[i - 1] == -(long)'('
+        || reason[i - 1] == -(long)'{'
+        || reason[i - 1] == -(long)'=') {
+      step++;
+      stepNumbs[i] = step;
+    }
+  }
+  return stepNumbs;
+}
+
+
+/* Converts any nmbrString to an ASCII string of numbers
+   -- used for debugging only. */
+temp_vstring nmbrCvtAnyToVString(const nmbrString *s) {
+  long i;
+  vstring_def(tmpStr);
+
+  long saveTempAllocStack;
+  saveTempAllocStack = g_startTempAllocStack; /* For let() stack cleanup */
+  g_startTempAllocStack = g_tempAllocStackTop;
+
+  for (i = 1; i <= nmbrLen(s); i++) {
+    let(&tmpStr,cat(tmpStr," ", str((double)(s[i-1])),NULL));
+  }
+
+  g_startTempAllocStack = saveTempAllocStack;
+  return makeTempAlloc(tmpStr); /* Flag it for deallocation */
+}
+
+
+/* Extract variables from a math token string */
+temp_nmbrString *nmbrExtractVars(const nmbrString *m) {
+  long i, j, length;
+  length = nmbrLen(m);
+  temp_nmbrString *v = nmbrTempAlloc(length + 1); /* Pre-allocate maximum possible space */
+  v[0] = *NULL_NMBRSTRING;
+  j = 0; /* Length of output string */
+  for (i = 0; i < length; i++) {
+    /* Use > because tokenNum=g_mathTokens is used by mmveri.c for
+       dummy token */
+    if (m[i] < 0 || m[i] > g_mathTokens) bug(1328);
+    if (g_MathToken[m[i]].tokenType == (char)var_) {
+      if (!nmbrElementIn(1, v, m[i])) { /* Don't duplicate variable */
+        v[j] = m[i];
+        j++;
+        v[j] = *NULL_NMBRSTRING; /* Add temp. end-of-string for getElementOf() */
+      }
+    }
+  }
+  nmbrZapLen(v, j); /* Zap mem pool fields */
+/*E*/db2=db2-(length-nmbrLen(v))*(long)(sizeof(nmbrString));
+  return v;
+}
+
+
+/* Determine if an element (after start) is in a nmbrString; return position
+   if it is.  Like nmbrInstr(), but faster.  Warning:  start must NOT
+   be greater than length, otherwise results are unpredictable!!  This
+   is not checked in order to speed up search. */
+long nmbrElementIn(long start, const nmbrString *g, long element) {
+  long i = start - 1;
+  while (g[i] != -1) { /* End of string */
+    if (g[i] == element) return i + 1;
+    i++;
+  }
+  return 0;
+}
+
+
+/* Add a single number to end of a nmbrString - faster than nmbrCat */
+temp_nmbrString *nmbrAddElement(const nmbrString *g, long element) {
+  long length;
+  length = nmbrLen(g);
+  temp_nmbrString *v = nmbrTempAlloc(length + 2); /* Allow for end of string */
+  nmbrCpy(v, g);
+  v[length] = element;
+  v[length + 1] = *NULL_NMBRSTRING; /* End of string */
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("bbg2: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  return v;
+}
+
+
+/* Get the set union of two math token strings (presumably
+   variable lists) */
+temp_nmbrString *nmbrUnion(const nmbrString *m1, const nmbrString *m2) {
+  long i,j,len1,len2;
+  len1 = nmbrLen(m1);
+  len2 = nmbrLen(m2);
+  temp_nmbrString *v = nmbrTempAlloc(len1+len2+1); /* Pre-allocate maximum possible space */
+  nmbrCpy(v,m1);
+  nmbrZapLen(v, len1);
+  j = 0;
+  for (i = 0; i < len2; i++) {
+    if (!nmbrElementIn(1, v, m2[i])) {
+      nmbrZapLen(v, len1 + j + 1);
+      v[len1 + j] = m2[i];
+      j++;
+      v[len1 + j] = *NULL_NMBRSTRING;
+    }
+  }
+  v[len1 + j] = *NULL_NMBRSTRING;
+  nmbrZapLen(v, len1 + j);
+/*E*/db2=db2-(len1+len2-nmbrLen(v))*(long)(sizeof(nmbrString));
+  return v;
+}
+
+
+/* Get the set intersection of two math token strings (presumably
+   variable lists) */
+temp_nmbrString *nmbrIntersection(const nmbrString *m1, const nmbrString *m2)
+{
+  long i,j,len2;
+  len2 = nmbrLen(m2);
+  temp_nmbrString *v = nmbrTempAlloc(len2+1); /* Pre-allocate maximum possible space */
+  j = 0;
+  for (i = 0; i < len2; i++) {
+    if (nmbrElementIn(1,m1,m2[i])) {
+      v[j] = m2[i];
+      j++;
+    }
+  }
+  /* Add end-of-string */
+  v[j] = *NULL_NMBRSTRING;
+  nmbrZapLen(v, j);
+/*E*/db2=db2-(len2-nmbrLen(v))*(long)(sizeof(nmbrString));
+  return v;
+}
+
+
+/* Get the set difference m1-m2 of two math token strings (presumably
+   variable lists) */
+temp_nmbrString *nmbrSetMinus(const nmbrString *m1, const nmbrString *m2)
+{
+  long i,j,len1;
+  len1 = nmbrLen(m1);
+  temp_nmbrString *v = nmbrTempAlloc(len1+1); /* Pre-allocate maximum possible space */
+  j = 0;
+  for (i = 0; i < len1; i++) {
+    if (!nmbrElementIn(1,m2,m1[i])) {
+      v[j] = m1[i];
+      j++;
+    }
+  }
+  /* Add end-of-string */
+  v[j] = *NULL_NMBRSTRING;
+  nmbrZapLen(v, j);
+/*E*/db2=db2-(len1-nmbrLen(v))*(long)(sizeof(nmbrString));
+  return v;
+}
+
+
+/* This is a utility function that returns the length of a subproof that
+   ends at step */
+/* 22-Aug-2012 nm - this doesn't seem to be used outside of mmdata.c -
+   should we replace it with subproofLen() in mmpfas.c? */
+long nmbrGetSubproofLen(const nmbrString *proof, long step)
+{
+  long stmt, hyps, pos, i;
+  char type;
+
+  if (step < 0) bug(1329);
+  stmt = proof[step];
+  if (stmt < 0) return (1); /* Unknown or label ref */
+  type = g_Statement[stmt].type;
+  if (type == f_ || type == e_) return (1); /* Hypothesis */
+  hyps = g_Statement[stmt].numReqHyp;
+  pos = step - 1;
+  for (i = 0; i < hyps; i++) {
+    pos = pos - nmbrGetSubproofLen(proof, pos);
+  }
+  return (step - pos);
+}
+
+
+
+
+/* This function returns a packed or "squished" proof, putting in local label
+   references to previous subproofs. */
+temp_nmbrString *nmbrSquishProof(const nmbrString *proof) {
+  nmbrString_def(newProof);
+  nmbrString_def(dummyProof);
+  nmbrString_def(subProof);
+  long step, dummyStep, subPrfLen, matchStep, plen;
+  flag foundFlag;
+
+  nmbrLet(&newProof,proof); /* In case of temp. alloc. of proof */
+  plen = nmbrLen(newProof);
+  dummyStep = 0;
+  nmbrLet(&dummyProof, newProof); /* Parallel proof with test subproof replaced
+                                 with a reference to itself, for matching. */
+  for (step = 0; step < plen; step++) {
+    subPrfLen = nmbrGetSubproofLen(dummyProof, dummyStep);
+    if (subPrfLen <= 1) {
+      dummyStep++;
+      continue;
+    }
+    nmbrLet(&subProof, nmbrSeg(dummyProof, dummyStep - subPrfLen + 2,
+        dummyStep + 1));
+    matchStep = step + 1;
+    foundFlag = 0;
+    while (1) {
+      matchStep = nmbrInstr(matchStep + 1, newProof, subProof);
+      if (!matchStep) break; /* No more occurrences */
+      foundFlag = 1;
+      /* Replace the found subproof with a reference to this subproof */
+      nmbrLet(&newProof,
+            nmbrCat(nmbrAddElement(nmbrLeft(newProof, matchStep - 1),
+            -1000 - step), nmbrRight(newProof, matchStep + subPrfLen), NULL));
+      matchStep = matchStep - subPrfLen + 1;
+    }
+    if (foundFlag) {
+      plen = nmbrLen(newProof); /* Update the new proof length */
+      /* Replace this subproof with a reference to itself, for later matching */
+      /* and add on rest of real proof. */
+      dummyStep = dummyStep + 1 - subPrfLen;
+      nmbrLet(&dummyProof,
+          nmbrCat(nmbrAddElement(nmbrLeft(dummyProof, dummyStep),
+          -1000 - step), nmbrRight(newProof, step + 2), NULL));
+    }
+    dummyStep++;
+  } /* Next step */
+  free_nmbrString(subProof);
+  free_nmbrString(dummyProof);
+  return nmbrMakeTempAlloc(newProof); /* Flag it for deallocation */
+}
+
+
+/* This function unpacks a "squished" proof, replacing local label references
+   to previous subproofs by the subproofs themselves. */
+temp_nmbrString *nmbrUnsquishProof(const nmbrString *proof) {
+  nmbrString_def(newProof);
+  nmbrString_def(subProof);
+  long step, plen, subPrfLen, stmt;
+
+  nmbrLet(&newProof, proof);
+  plen = nmbrLen(newProof);
+  for (step = plen - 1; step >= 0; step--) {
+    stmt = newProof[step];
+    if (stmt > -1000) continue;
+    /* It's a local label reference */
+    stmt = -1000 - stmt;
+    subPrfLen = nmbrGetSubproofLen(newProof, stmt);
+    nmbrLet(&newProof, nmbrCat(nmbrLeft(newProof, step),
+        nmbrSeg(newProof, stmt - subPrfLen + 2, stmt + 1),
+        nmbrRight(newProof, step + 2), NULL));
+    step = step + subPrfLen - 1;
+  }
+  free_nmbrString(subProof);
+  return nmbrMakeTempAlloc(newProof); /* Flag it for deallocation */
+}
+
+
+/* This function returns the indentation level vs. step number of a proof
+   string.  This information is used for formatting proof displays.  The
+   function calls itself recursively, but the first call should be with
+   startingLevel = 0. */
+/* ???Optimization:  remove nmbrString calls and use static variables
+   to communicate to recursive calls */
+temp_nmbrString *nmbrGetIndentation(const nmbrString *proof, long startingLevel) {
+  long plen, stmt, pos, splen, hyps, i, j;
+  char type;
+  nmbrString_def(indentationLevel);
+  nmbrString_def(subProof);
+  nmbrString_def(nmbrTmp);
+
+  plen = nmbrLen(proof);
+  stmt = proof[plen - 1];
+  nmbrLet(&indentationLevel, nmbrSpace(plen));
+  indentationLevel[plen - 1] = startingLevel;
+  if (stmt < 0) { /* A local label reference or unknown */
+    if (plen != 1) bug(1330);
+    return nmbrMakeTempAlloc(indentationLevel); /* Flag it for deallocation */
+  }
+  type = g_Statement[stmt].type;
+  if (type == f_ || type == e_) { /* A hypothesis */
+    if (plen != 1) bug(1331);
+    return nmbrMakeTempAlloc(indentationLevel); /* Flag it for deallocation */
+  }
+  /* An assertion */
+  if (type != a_ && type != p_) bug(1332);
+  hyps = g_Statement[stmt].numReqHyp;
+  pos = plen - 2;
+  for (i = 0; i < hyps; i++) {
+    splen = nmbrGetSubproofLen(proof, pos);
+    nmbrLet(&subProof, nmbrSeg(proof, pos - splen + 2, pos + 1));
+    nmbrLet(&nmbrTmp, nmbrGetIndentation(subProof, startingLevel + 1));
+    for (j = 0; j < splen; j++) {
+      indentationLevel[j + pos - splen + 1] = nmbrTmp[j];
+    }
+    pos = pos - splen;
+  }
+  if (pos != -1) bug(333);
+
+  free_nmbrString(subProof); /* Deallocate */
+  free_nmbrString(nmbrTmp); /* Deallocate */
+  return nmbrMakeTempAlloc(indentationLevel); /* Flag it for deallocation */
+} /* nmbrGetIndentation */
+
+
+/* This function returns essential (1) or floating (0) vs. step number of a
+   proof string.  This information is used for formatting proof displays.  The
+   function calls itself recursively. */
+/* ???Optimization:  remove nmbrString calls and use static variables
+   to communicate to recursive calls */
+nmbrString *nmbrGetEssential(const nmbrString *proof) {
+  long plen, stmt, pos, splen, hyps, i, j;
+  char type;
+  nmbrString_def(essentialFlags);
+  nmbrString_def(subProof);
+  nmbrString_def(nmbrTmp);
+  nmbrString *nmbrTmpPtr2;
+
+  plen = nmbrLen(proof);
+  stmt = proof[plen - 1];
+  nmbrLet(&essentialFlags, nmbrSpace(plen));
+  essentialFlags[plen - 1] = 1;
+  if (stmt < 0) { /* A local label reference or unknown */
+    if (plen != 1) bug(1334);
+    /* The only time it should get here is if the original proof has only one
+       step, which would be an unknown step */
+    if (stmt != -(long)'?' && stmt > -1000) bug(1335);
+    return nmbrMakeTempAlloc(essentialFlags); /* Flag it for deallocation */
+  }
+  type = g_Statement[stmt].type;
+  if (type == f_ || type == e_) { /* A hypothesis */
+    /* The only time it should get here is if the original proof has only one
+       step */
+    if (plen != 1) bug(1336);
+    return nmbrMakeTempAlloc(essentialFlags); /* Flag it for deallocation */
+  }
+  /* An assertion */
+  if (type != a_ && type != p_) bug(1337);
+  hyps = g_Statement[stmt].numReqHyp;
+  pos = plen - 2;
+  nmbrTmpPtr2 = g_Statement[stmt].reqHypList;
+  for (i = 0; i < hyps; i++) {
+    splen = nmbrGetSubproofLen(proof, pos);
+    if (g_Statement[nmbrTmpPtr2[hyps - i - 1]].type == e_) {
+      nmbrLet(&subProof, nmbrSeg(proof, pos - splen + 2, pos + 1));
+      nmbrLet(&nmbrTmp, nmbrGetEssential(subProof));
+      for (j = 0; j < splen; j++) {
+        essentialFlags[j + pos - splen + 1] = nmbrTmp[j];
+      }
+    }
+    pos = pos - splen;
+  }
+  if (pos != -1) bug (1338);
+
+  free_nmbrString(subProof); /* Deallocate */
+  free_nmbrString(nmbrTmp); /* Deallocate */
+  return nmbrMakeTempAlloc(essentialFlags); /* Flag it for deallocation */
+} /* nmbrGetEssential */
+
+
+/* This function returns the target hypothesis vs. step number of a proof
+   string.  This information is used for formatting proof displays.  The
+   function calls itself recursively.
+   statemNum is the statement being proved. */
+/* ???Optimization:  remove nmbrString calls and use static variables
+   to communicate to recursive calls */
+temp_nmbrString *nmbrGetTargetHyp(const nmbrString *proof, long statemNum) {
+  long plen, stmt, pos, splen, hyps, i, j;
+  char type;
+  nmbrString_def(targetHyp);
+  nmbrString_def(subProof);
+  nmbrString_def(nmbrTmp);
+
+  plen = nmbrLen(proof);
+  stmt = proof[plen - 1];
+  nmbrLet(&targetHyp, nmbrSpace(plen));
+  if (statemNum) { /* First (rather than recursive) call */
+    targetHyp[plen - 1] = statemNum; /* Statement being proved */
+  }
+  if (stmt < 0) { /* A local label reference or unknown */
+    if (plen != 1) bug(1339);
+    /* The only time it should get here is if the original proof has only one
+       step, which would be an unknown step */
+    if (stmt != -(long)'?') bug(1340);
+    return nmbrMakeTempAlloc(targetHyp); /* Flag it for deallocation */
+  }
+  type = g_Statement[stmt].type;
+  if (type == f_ || type == e_) { /* A hypothesis */
+    /* The only time it should get here is if the original proof has only one
+       step */
+    if (plen != 1) bug(1341);
+    return nmbrMakeTempAlloc(targetHyp); /* Flag it for deallocation */
+  }
+  /* An assertion */
+  if (type != a_ && type != p_) bug(1342);
+  hyps = g_Statement[stmt].numReqHyp;
+  pos = plen - 2;
+  for (i = 0; i < hyps; i++) {
+    splen = nmbrGetSubproofLen(proof, pos);
+    if (splen > 1) {
+      nmbrLet(&subProof, nmbrSeg(proof, pos - splen + 2, pos + 1));
+      nmbrLet(&nmbrTmp, nmbrGetTargetHyp(subProof,
+          g_Statement[stmt].reqHypList[hyps - i - 1]));
+      for (j = 0; j < splen; j++) {
+        targetHyp[j + pos - splen + 1] = nmbrTmp[j];
+      }
+    } else {
+      /* A one-step subproof; don't bother with recursive call */
+      targetHyp[pos] = g_Statement[stmt].reqHypList[hyps - i - 1];
+    }
+    pos = pos - splen;
+  }
+  if (pos != -1) bug (343);
+
+  free_nmbrString(subProof); /* Deallocate */
+  free_nmbrString(nmbrTmp); /* Deallocate */
+  return nmbrMakeTempAlloc(targetHyp); /* Flag it for deallocation */
+} /* nmbrGetTargetHyp */
+
+
+/* Converts a proof string to a compressed-proof-format ASCII string.
+   Normally, the proof string would be packed with nmbrSquishProof first,
+   although it's not a requirement (in which case the compressed proof will
+   be much longer of course). */
+/* The statement number is needed because required hypotheses are
+   implicit in the compressed proof. */
+/* The returned ASCII string isn't surrounded by spaces e.g. it
+   could be "( a1i a1d ) ACBCADEF". */
+temp_vstring compressProof(const nmbrString *proof, long statemNum,
+    flag oldCompressionAlgorithm) {
+  vstring_def(output);
+  long outputLen;
+  long outputAllocated;
+  nmbrString_def(saveProof);
+  nmbrString_def(labelList);
+  nmbrString_def(hypList);
+  nmbrString_def(assertionList);
+  nmbrString_def(localList);
+  nmbrString_def(localLabelFlags);
+  long hypLabels, assertionLabels, localLabels;
+  long plen, step, stmt, labelLen, lab, numchrs;
+  long i, j, k;
+  long lettersLen, digitsLen;
+  static char *digits = "0123456789";
+  static char *letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  static char labelChar = ':';
+
+  nmbrString_def(explList);
+  long explLabels;
+  nmbrString_def(explRefCount);
+  nmbrString_def(labelRefCount);
+  long maxExplRefCount;
+  nmbrString_def(explComprLen);
+  long explSortPosition;
+  long maxExplComprLen;
+  vstring_def(explUsedFlag);
+  nmbrString_def(explLabelLen);
+  nmbrString_def(newExplList);
+  long newExplPosition;
+  long indentation;
+  long explOffset;
+  long explUnassignedCount;
+  nmbrString_def(explWorth);
+  long explWidth;
+  vstring_def(explIncluded);
+
+
+  /* Compression standard with all cap letters */
+  /* (For 500-700 step proofs, we only lose about 18% of file size --
+      but the compressed proof is more pleasing to the eye) */
+  letters = "ABCDEFGHIJKLMNOPQRST"; /* LSB is base 20 */
+  digits = "UVWXY"; /* MSB's are base 5 */
+  labelChar = 'Z'; /* Was colon */
+
+  lettersLen = (long)strlen(letters);
+  digitsLen = (long)strlen(digits);
+
+  nmbrLet(&saveProof, proof); /* In case of temp. alloc. of proof */
+
+  if (g_Statement[statemNum].type != (char)p_) bug(1344);
+  plen = nmbrLen(saveProof);
+
+  /* Create the initial label list of required hypotheses */
+  nmbrLet(&labelList, g_Statement[statemNum].reqHypList);
+
+  /* Add the other statement labels to the list */
+
+  /* Warning:  The exact union algorithm is crucial here; the required
+     hypotheses MUST remain at the beginning of the list. */
+  nmbrLet(&labelList, nmbrUnion(labelList, saveProof));
+
+  /* Break the list into hypotheses, assertions, and local labels */
+  labelLen = nmbrLen(labelList);
+  nmbrLet(&hypList, nmbrSpace(labelLen));
+  nmbrLet(&assertionList, nmbrSpace(labelLen));
+  nmbrLet(&localLabelFlags, nmbrSpace(plen)); /* Warning: nmbrSpace() must
+                                                 produce a string of 0's */
+  hypLabels = 0;
+  assertionLabels = 0;
+  localLabels = 0;
+  for (lab = 0; lab < labelLen; lab++) {
+    stmt = labelList[lab];
+    if (stmt < 0) {
+      if (stmt <= -1000) {
+        if (-1000 - stmt >= plen) bug(345);
+        localLabelFlags[-1000 - stmt] = 1;
+        localLabels++;
+      } else {
+        if (stmt != -(long)'?') bug(1346);
+      }
+    } else {
+      if (g_Statement[stmt].type != (char)a_ &&
+          g_Statement[stmt].type != (char)p_) {
+        hypList[hypLabels] = stmt;
+        hypLabels++;
+      } else {
+        assertionList[assertionLabels] = stmt;
+        assertionLabels++;
+      }
+    }
+  } /* Next lab */
+  nmbrLet(&hypList, nmbrLeft(hypList, hypLabels));
+  nmbrLet(&assertionList, nmbrLeft(assertionList, assertionLabels));
+
+  /* Get list of local labels, sorted in order of declaration */
+  nmbrLet(&localList, nmbrSpace(localLabels));
+  lab = 0;
+  for (step = 0; step < plen; step++) {
+    if (localLabelFlags[step]) {
+      localList[lab] = -1000 - step;
+      lab++;
+    }
+  }
+  if (lab != localLabels) bug(1347);
+
+  /* To obtain the old algorithm, we simply skip the new label re-ordering */
+  if (oldCompressionAlgorithm) goto OLD_ALGORITHM;
+
+
+  /* This algorithm, based on an idea proposed by Mario Carneiro, sorts
+     the explicit labels so that the most-used labels occur first, optimizing
+     the use of 1-character compressed label lengths, then 2-character
+     lengths, and so on.  Also, an attempt is made to fit the label list into
+     the exact maximum current screen width, using the 0/1-knapsack
+     algorithm, so that fewer lines will result due to wasted space at the
+     end of each line with labels.  */
+
+  /* Get the list of explicit labels */
+  nmbrLet(&explList, nmbrCat(
+      /* Trim off leading implicit required hypotheses */
+      nmbrRight(hypList, g_Statement[statemNum].numReqHyp + 1),
+      /* Add in the list of assertion ($a, $p) references */
+      assertionList, NULL));
+  explLabels = nmbrLen(explList);
+
+  /* Initialize reference counts for the explicit labels */
+  nmbrLet(&explRefCount, nmbrSpace(explLabels));
+
+  /* Count the number of references to labels in the original proof */
+  /* We allocate up to statemNum, since any earlier statement could appear */
+  nmbrLet(&labelRefCount, nmbrSpace(statemNum));  /* Warning: nmbrSpace() must
+                                                 produce a string of 0's */
+  for (step = 0; step < plen; step++) { /* Scan the proof */
+    if (saveProof[step] > 0) { /* Ignore local labels and '?' */
+      if (saveProof[step] < statemNum) {
+        labelRefCount[saveProof[step]]++;
+      } else {
+        bug(1380); /* Corrupted proof should have been caught earlier */
+      }
+    }
+  }
+  maxExplRefCount = 0;  /* Largest number of reference counts found */
+  /* Populate the explicit label list with the counts */
+  for (i = 0; i < explLabels; i++) {
+    explRefCount[i] = labelRefCount[explList[i]]; /* Save the ref count */
+    if (explRefCount[i] <= 0) bug(1381);
+    if (explRefCount[i] > maxExplRefCount) {
+      maxExplRefCount = explRefCount[i]; /* Update largest count */
+    }
+  }
+  /* We're done with giant labelRefCount array; deallocate */
+  free_nmbrString(labelRefCount);
+
+  /* Assign compressed label lengths starting from most used to least
+     used label */
+  /* Initialize compressed label lengths for the explicit labels */
+  nmbrLet(&explComprLen, nmbrSpace(explLabels));
+  explSortPosition = 0;
+  maxExplComprLen = 0;
+  /* The "sorting" below has n^2 behavior; improve if is it a problem */
+  /* explSortPosition is where the label would occur if reverse-sorted
+     by reference count, for the purpose of computing the compressed
+     label length.  No actual sorting is done, since later we're
+     only interested in groups with the same compressed label length. */
+  for (i = maxExplRefCount; i >= 1; i--) {
+    for (j = 0; j < explLabels; j++) {
+      if (explRefCount[j] == i) {
+        /* Find length, numchrs, of compressed label */
+        /* If there are no req hyps, 0 = 1st label in explicit list */
+        lab = g_Statement[statemNum].numReqHyp + explSortPosition;
+
+        /* The following 7 lines are from the compressed label length
+           determination algorithm below */
+        numchrs = 1;
+        k = lab / lettersLen;
+        while (1) {
+          if (!k) break;
+          numchrs++;
+          k = (k - 1) / digitsLen;
+        }
+
+        explComprLen[j] = numchrs; /* Assign the compressed label length */
+        if (numchrs > maxExplComprLen) {
+          maxExplComprLen = numchrs; /* Update maximum length */
+        }
+        explSortPosition++;
+      }
+    }
+  }
+
+  let(&explUsedFlag, string(explLabels, 'n')); /* Mark with 'y' when placed in
+                                            output label list (newExplList) */
+  nmbrLet(&explLabelLen, nmbrSpace(explLabels));
+  /* Populate list of label lengths for knapsack01() "size" */
+  for (i = 0; i < explLabels; i++) {
+    stmt = explList[i];
+    explLabelLen[i] = (long)(strlen(g_Statement[stmt].labelName)) + 1;
+                                     /* +1 accounts for space between labels */
+  }
+
+  /* Re-distribute labels in order of compressed label length, fitted to
+     line by knapsack01() algorithm */
+
+  nmbrLet(&newExplList, nmbrSpace(explLabels)); /* List in final order */
+  nmbrLet(&explWorth, nmbrSpace(explLabels));  /* "Value" for knapsack01() */
+  let(&explIncluded, string(explLabels, '?')); /* Returned by knapsack01() */
+  newExplPosition = 0; /* Counter for position in output label list */
+
+  indentation =  2 + getSourceIndentation(statemNum); /* Proof indentation */
+  explOffset = 2; /* add 2 for "( " opening parenthesis of compressed proof */
+
+  /* Fill up the output with labels in groups of increasing compressed label
+     size */
+  for (i = 1; i <= maxExplComprLen; i++) {
+    explUnassignedCount = 0; /* Unassigned at current compressed label size */
+    /* Initialize worths for knapsack01() */
+    for (j = 0; j < explLabels; j++) {
+      if (explComprLen[j] == i) {
+        if (explUsedFlag[j] == 'y') bug(1382);
+        explWorth[j] = explLabelLen[j]; /* Make worth=size so that label
+            length does not affect whether the label is chosen by knapsack01(),
+            so the only influence is whether it fits */
+        explUnassignedCount++;
+      } else { /* Not the current compressed label size */
+        explWorth[j] = -1; /* Negative worth will make knapsack avoid it */
+      }
+    }
+    while (explUnassignedCount > 0) {
+      /* Find the the amount of space available on the remainder of the line */
+      /* The +1 accounts for space after last label, which wrapping will trim */
+      /* Note that the actual line wrapping will happen with printLongLine
+         far in the future.  Here we will just put the labels in the order
+         that will cause it to wrap at the desired place. */
+      explWidth = g_screenWidth - indentation - explOffset + 1;
+
+      /* Fill in the label list output line with labels that fit best */
+      /* The knapsack01() call below is always given the entire set of
+         explicit labels, with -1 worth assigned to the ones to be avoided.
+         It would be more efficient to give it a smaller list with -1s
+         removed, if run time becomes a problem. */
+      j = knapsack01(explLabels /*#items*/,
+          explLabelLen /*array of sizes*/,
+          explWorth /*array of worths*/,
+          explWidth /*maxSize*/,
+          explIncluded /*itemIncluded return values*/);
+      /* j=0 is legal when it can't fit any labels
+         on the rest of the line (such as if the line only has 1 space left
+         i.e. explWidth=1) */
+      if (j < 0) bug(1383);
+
+      /* Accumulate the labels selected by knapsack01() into the output list,
+         in the same order as they appeared in the original explicit label
+         list */
+      explUnassignedCount = 0;
+      /* Scan expIncluded y/n string returned by knapsack01() */
+      for (j = 0; j < explLabels; j++) {
+        if (explIncluded[j] == 'y') { /* was chosen by knapsack01() */
+          if (explComprLen[j] != i) bug(1384); /* Other compressed length
+             shouldn't occur because -1 worth should have been rejected by
+             knapsack01() */
+          newExplList[newExplPosition] = explList[j];
+          newExplPosition++;
+          explUsedFlag[j] = 'y';
+          if (explWorth[j] == -1) bug(1385); /* knapsack01() should
+              have rejected all previously assigned labels */
+          explWorth[j] = -1; /* Negative worth will avoid it next loop iter */
+          explOffset = explOffset + explLabelLen[j];
+        } else {
+          if (explComprLen[j] == i && explUsedFlag[j] == 'n') {
+            explUnassignedCount++; /* There are still more to be assigned
+                                      at this compressed label length */
+            if (explWorth[j] != explLabelLen[j]) bug(1386); /* Sanity check */
+          }
+        }
+      }
+      if (explUnassignedCount > 0) {
+        /* If there are labels still at this level (of compressed
+           label length), so start a new line for next knapsack01() call */
+        explOffset = 0;
+      }
+    }
+  }
+  if (newExplPosition != explLabels) bug(1387); /* Labels should be exhausted */
+
+  /* The hypList and assertionList below are artificially assigned
+     for use by the continuation of the old algorithm that follows */
+
+  /* "hypList" is truncated to have only the required hypotheses with no
+     optional ones */
+  nmbrLet(&hypList, nmbrLeft(hypList, g_Statement[statemNum].numReqHyp));
+  /* "assertionList" will have both the optional hypotheses and the assertions,
+     reordered */
+  nmbrLet(&assertionList, newExplList);
+
+
+ OLD_ALGORITHM:
+  /* Combine all label lists */
+  nmbrLet(&labelList, nmbrCat(hypList, assertionList, localList, NULL));
+
+  /* Create the compressed proof */
+  outputLen = 0;
+#define COMPR_INC 1000
+  let(&output, space(COMPR_INC));
+  outputAllocated = COMPR_INC;
+
+  plen = nmbrLen(saveProof);
+  for (step = 0; step < plen; step++) {
+
+    stmt = saveProof[step];
+
+    if (stmt == -(long)'?') {
+      /* Unknown step */
+      if (outputLen + 1 > outputAllocated) {
+        /* Increase allocation of the output string */
+        let(&output, cat(output, space(outputLen + 1 - outputAllocated +
+            COMPR_INC), NULL));
+        outputAllocated = outputLen + 1 + COMPR_INC; /* = (long)strlen(output) */
+        /* CPU-intensive bug check; enable only if required: */
+        /* if (outputAllocated != (long)strlen(output)) bug(1348); */
+        if (output[outputAllocated - 1] == 0 ||
+            output[outputAllocated] != 0) bug(1348);
+      }
+      output[outputLen] = '?';
+      outputLen++;
+      continue;
+    }
+
+    lab = nmbrElementIn(1, labelList, stmt);
+    if (!lab) bug(1349);
+    lab--; /* labelList array starts at 0, not 1 */
+
+    /* Determine the # of chars in the compressed label */
+    /* A corrected algorithm was provided by Marnix Klooster. */
+    /* For encoding we'd get (starting with n, counting from 1):
+        * start with the empty string
+        * prepend (n-1) mod 20 + 1 as character using 1->'A' .. 20->'T'
+        * n := (n-1) div 20
+        * while n > 0:
+           * prepend (n-1) mod 5 + 1 as character using 1->'U' .. 5->'Y'
+           * n := (n-1) div 5 */
+    if (lab < 0) bug(1373);
+    numchrs = 1;
+    i = lab / lettersLen;
+    while (1) {
+      if (!i) break;
+      numchrs++;
+      i = (i - 1) / digitsLen;
+    }
+
+    /* Add the compressed label to the proof */
+    if (outputLen + numchrs > outputAllocated) {
+      /* Increase allocation of the output string */
+      let(&output, cat(output, space(outputLen + numchrs - outputAllocated +
+          COMPR_INC), NULL));
+      outputAllocated = outputLen + numchrs + COMPR_INC; /* = (long)strlen(output) */
+      /* CPU-intensive bug check; enable only if required: */
+      /* if (outputAllocated != (long)strlen(output)) bug(1350); */
+      if (output[outputAllocated - 1] == 0 ||
+          output[outputAllocated] != 0) bug(1350);
+    }
+    outputLen = outputLen + numchrs;
+
+    j = lab + 1; /* lab starts at 0, not 1 */
+    i = 1;
+    output[outputLen - i] = letters[(j - 1) % lettersLen];
+    j = (j - 1) / lettersLen;
+    while (1) {
+      if (!j) break;
+      i++;
+      output[outputLen - i] = digits[(j - 1) % digitsLen];
+      j = (j - 1) / digitsLen;
+    }
+    if (i != numchrs) bug(1374);
+
+
+    /***** Local labels ******/
+    /* See if a local label is declared in this step */
+    if (!localLabelFlags[step]) continue;
+    if (outputLen + 1 > outputAllocated) {
+      /* Increase allocation of the output string */
+      let(&output, cat(output, space(outputLen + 1 - outputAllocated +
+          COMPR_INC), NULL));
+      outputAllocated = outputLen + 1 + COMPR_INC; /* = (long)strlen(output) */
+      /* CPU-intensive bug check due to strlen; enable only if required: */
+      /* if (outputAllocated != (long)strlen(output)) bug(1352); */
+      if (output[outputAllocated - 1] == 0 ||
+          output[outputAllocated] != 0) bug(1352);
+    }
+    output[outputLen] = labelChar;
+    outputLen++;
+
+  } /* Next step */
+
+  /* Create the final compressed proof */
+  let(&output, cat("( ", nmbrCvtRToVString(nmbrCat(
+      /* Trim off leading implicit required hypotheses */
+      nmbrRight(hypList, g_Statement[statemNum].numReqHyp + 1),
+      assertionList, NULL),
+                0, /*explicitTargets*/
+                0 /*statemNum used only if explicitTargets*/),
+      " ) ", left(output, outputLen), NULL));
+
+  free_nmbrString(saveProof);
+  free_nmbrString(labelList);
+  free_nmbrString(hypList);
+  free_nmbrString(assertionList);
+  free_nmbrString(localList);
+  free_nmbrString(localLabelFlags);
+  free_nmbrString(explList);
+  free_nmbrString(explRefCount);
+  free_nmbrString(labelRefCount);
+  free_nmbrString(explComprLen);
+  free_vstring(explUsedFlag);
+  free_nmbrString(explLabelLen);
+  free_nmbrString(newExplList);
+  free_nmbrString(explWorth);
+  free_vstring(explIncluded);
+
+  return makeTempAlloc(output); /* Flag it for deallocation */
+} /* compressProof */
+
+
+/* Compress the input proof, create the ASCII compressed proof,
+   and return its size in bytes. */
+/* TODO: call this in MINIMIZE_WITH in metamath.c */
+long compressedProofSize(const nmbrString *proof, long statemNum) {
+  nmbrString_def(tmpNmbr);
+  vstring_def(tmpStr);
+  long bytes;
+  nmbrLet(&tmpNmbr, nmbrSquishProof(proof));
+  let(&tmpStr, compressProof(tmpNmbr,
+          statemNum, /* statement being proved */
+          0 /* don't use old algorithm (this will become obsolete) */
+          ));
+  bytes = (long)strlen(tmpStr);
+  /* Deallocate memory */
+  free_vstring(tmpStr);
+  free_nmbrString(tmpNmbr);
+  return bytes;
+} /* compressedProofSize */
+
+
+
+/*******************************************************************/
+/*********** Pointer string functions ******************************/
+/*******************************************************************/
+
+long g_pntrTempAllocStackTop = 0;     /* Top of stack for pntrTempAlloc function */
+long g_pntrStartTempAllocStack = 0;   /* Where to start freeing temporary allocation
+                                    when pntrLet() is called (normally 0, except in
+                                    special nested vstring functions) */
+
+/*!
+ * \var pntrString *pntrTempAllocStack[]
+ * \brief a \ref pgStack "stack" of \ref temp_pntrString.
+ *
+ * Holds pointers to temporarily allocated data of type \ref temp_pntrString.  Such
+ * a \ref pgStack "stack" is primarily designed to operate like one for
+ * temporary allocated ad hoc operands, as described in \ref pgStack.  The
+ * stack top index is \ref g_pntrTempAllocStackTop, always refering to the next
+ * push position.
+ * The \ref g_pntrStartTempAllocStack supports nested operations by indicating
+ * where the operands for the upcoming operation start from.
+ * \attention A \ref pntrString consists of an array of pointers.  These
+ *   pointers may themself refer data that needs a clean up, when the last
+ *   reference  to it disappears (such as deallocating memory for example).
+ *   There is no automatic procedure handling such cases when pointers are
+ *   popped off the stack to be freed.
+ * \bug The element type should be temp_pntrString, because a NULL_PNTRSTRING
+ *   must not be pushed on the stack.
+ */
+pntrString *pntrTempAllocStack[M_MAX_ALLOC_STACK];
+
+/*!
+ * \fn temp_pntrString *pntrTempAlloc(long size)
+ * \par size > 0
+ * allocates a \ref pgBlock capable of holding \p size \ref pntrString entries
+ * and pushes it onto the \ref pntrTempAllocStack.
+ * \par size == 0
+ * pops off all entries from index \ref g_pntrStartTempAllocStack on from
+ * \ref pntrTempAllocStack and adds them to the \ref memFreePool.
+ * \param[in] size count of \ref pntrString entries.  This value must include
+ *   a terminal NULL pointer if needed.
+ * \return a pointer to the allocated \ref pgBlock, or NULL if deallocation
+ *   requested
+ * \pre
+ *   \p size ==0: all entries in from \ref pntrTempAllocStack from
+ *   \ref g_pntrStartTempAllocStack do not contain relevant data any more.
+ * \post
+ *   - \p size > 0: memory for \p size entries is reserved in the \ref pgBlock
+ *     "block's" header, but the data is still random.
+ *   - updates \ref db2
+ *   - Exits on out-of-memory
+ * \bug it is unfortunate that the same function is used for opposite
+ *   operations like de-/allocation.
+ */
+temp_pntrString *pntrTempAlloc(long size) {
+                                /* pntrString memory allocation/deallocation */
+  /* When "size" is >0, "size" instances of pntrString are allocated. */
+  /* When "size" is 0, all memory previously allocated with this */
+  /* function is deallocated, down to g_pntrStartTempAllocStack. */
+  if (size) {
+    if (g_pntrTempAllocStackTop>=(M_MAX_ALLOC_STACK-1))
+      /*??? Fix to allocate more */
+      outOfMemory("#109 (pntrString stack array)");
+    if (!(pntrTempAllocStack[g_pntrTempAllocStackTop++]=poolMalloc(size
+        *(long)(sizeof(pntrString)))))
+/*E*/db2=db2+(size)*(long)(sizeof(pntrString));
+    return (pntrTempAllocStack[g_pntrTempAllocStackTop-1]);
+  } else {
+    while(g_pntrTempAllocStackTop != g_pntrStartTempAllocStack) {
+/*E*/db2=db2-(pntrLen(pntrTempAllocStack[g_pntrTempAllocStackTop-1])+1)
+/*E*/                                              *(long)(sizeof(pntrString));
+      poolFree(pntrTempAllocStack[--g_pntrTempAllocStackTop]);
+    }
+    g_pntrTempAllocStackTop=g_pntrStartTempAllocStack;
+    return (0);
+  }
+}
+
+
+temp_pntrString *pntrMakeTempAlloc(pntrString *s) {
+  if (g_pntrTempAllocStackTop>=(M_MAX_ALLOC_STACK-1)) {
+    printf(
+    "*** FATAL ERROR ***  Temporary pntrString stack overflow in pntrMakeTempAlloc()\n");
+#if __STDC__
+    fflush(stdout);
+#endif
+    bug(1370);
+  }
+  if (s[0] != NULL) { /* Don't do it if pntrString is empty */
+    pntrTempAllocStack[g_pntrTempAllocStackTop++] = s;
+  }
+/*E*/db2=db2+(pntrLen(s)+1)*(long)(sizeof(pntrString));
+/*E*/db3=db3-(pntrLen(s)+1)*(long)(sizeof(pntrString));
+  return s;
+}
+
+
+void pntrLet(pntrString **target, const pntrString *source) {
+  long targetLength,sourceLength;
+  long targetAllocLen;
+  long poolDiff;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  sourceLength=pntrLen(source);  /* Save its actual length */
+  targetLength=pntrLen(*target);  /* Save its actual length */
+  targetAllocLen=pntrAllocLen(*target); /* Save target's allocated length */
+/*E*/if (targetLength) {
+/*E*/  /* printf("Deleting %s\n",cvtMToVString(*target,0)); */
+/*E*/  db3 = db3 - (targetLength+1)*(long)(sizeof(pntrString));
+/*E*/}
+/*E*/if (sourceLength) {
+/*E*/  /* printf("Adding %s\n",cvtMToVString(source,0)); */
+/*E*/  db3 = db3 + (sourceLength+1)*(long)(sizeof(pntrString));
+/*E*/}
+  if (targetAllocLen) {
+    if (sourceLength) { /* source and target are both nonzero length */
+
+      if (targetAllocLen >= sourceLength) { /* Old string has room for new one */
+        pntrCpy(*target,source); /* Re-use the old space to save CPU time */
+
+        /* Memory pool handling */
+        /* Assign actual size of target string */
+        poolDiff = ((long *)(*target))[-1] - ((long *)source)[-1];
+        ((long *)(*target))[-1] = ((long *)source)[-1];
+        /* If actual size of target string is less than allocated size, we
+           may have to add it to the used pool */
+        if (((long *)(*target))[-1] != ((long *)(*target))[-2]) {
+          if (((long *)(*target))[-1] > ((long *)(*target))[-2]) bug(1359);
+          if (((long *)(*target))[-3] == -1) {
+            /* It's not already in the used pool, so add it */
+            addToUsedPool(*target);
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0aa: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+          } else {
+            /* Adjust free space independently */
+            poolTotalFree = poolTotalFree + poolDiff;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0ab: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+          }
+        } else {
+          if (((long *)(*target))[-3] != -1) {
+            /* It's in the pool (but all allocated space coincidentally used) */
+            /* Adjust free space independently */
+            poolTotalFree = poolTotalFree + poolDiff;
+          }
+        }
+
+
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0a: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+      } else {
+        /* Free old string space and allocate new space */
+        poolFree(*target);  /* Free old space */
+        /* *target=poolMalloc((sourceLength + 1) * sizeof(pntrString)); */
+        *target=poolMalloc((sourceLength + 1) * (long)(sizeof(pntrString)) * 2);
+                        /* Allocate new space --
+                            We are replacing a smaller string with a larger one;
+                            assume it is growing, and allocate twice as much as
+                            needed. */
+        pntrCpy(*target,source);
+
+        /* Memory pool handling */
+        /* Assign actual size of target string */
+        poolDiff = ((long *)(*target))[-1] - ((long *)source)[-1];
+        ((long *)(*target))[-1] = ((long *)source)[-1];
+        /* If actual size of target string is less than allocated size, we
+           may have to add it to the used pool */
+        /* (The 1st 'if' is redundant with target doubling above) */
+        if (((long *)(*target))[-1] != ((long *)(*target))[-2]) {
+          if (((long *)(*target))[-1] > ((long *)(*target))[-2]) bug(1360);
+          if (((long *)(*target))[-3] == -1) {
+            /* It's not already in the used pool, so add it */
+            addToUsedPool(*target);
+          } else {
+            /* Adjust free space independently */
+            poolTotalFree = poolTotalFree + poolDiff;
+          }
+        } else {
+          if (((long *)(*target))[-3] != -1) {
+            /* It's in the pool (but all allocated space coincidentally used) */
+            /* Adjust free space independently */
+            poolTotalFree = poolTotalFree + poolDiff;
+          }
+        }
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0b: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+
+      }
+
+    } else {    /* source is 0 length, target is not */
+      poolFree(*target);
+      *target= NULL_PNTRSTRING;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0c: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    }
+  } else {
+    if (sourceLength) { /* target is 0 length, source is not */
+      *target=poolMalloc((sourceLength + 1) * (long)(sizeof(pntrString)));
+                        /* Allocate new space */
+      pntrCpy(*target,source);
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0d: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    } else {    /* source and target are both 0 length */
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k0e: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+    }
+  }
+
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("k1: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  pntrTempAlloc(0); /* Free up temporary strings used in expression computation*/
+
+}
+
+
+
+/* String concatenation */
+temp_pntrString *pntrCat(const pntrString *string1,...) {
+  va_list ap;   /* Declare list incrementer */
+  const pntrString *arg[M_MAX_CAT_ARGS];        /* Array to store arguments */
+  long argLength[M_MAX_CAT_ARGS];       /* Array to store argument lengths */
+  int numArgs=1;        /* Define "last argument" */
+  int i;
+  long j;
+  arg[0] = string1;       /* First argument */
+
+  va_start(ap,string1); /* Begin the session */
+  while ((arg[numArgs++]=va_arg(ap,pntrString *)))
+        /* User-provided argument list must terminate with NULL */
+    if (numArgs>=M_MAX_CAT_ARGS-1) {
+      printf("*** FATAL ERROR ***  Too many cat() arguments\n");
+#if __STDC__
+      fflush(stdout);
+#endif
+      bug(1371);
+    }
+  va_end(ap);           /* End varargs session */
+
+  numArgs--;    /* The last argument (0) is not a string */
+
+  /* Find out the total string length needed */
+  j = 0;
+  for (i = 0; i < numArgs; i++) {
+    argLength[i]=pntrLen(arg[i]);
+    j=j+argLength[i];
+  }
+  /* Allocate the memory for it */
+  temp_pntrString *ptr = pntrTempAlloc(j+1);
+  /* Move the strings into the newly allocated area */
+  j = 0;
+  for (i = 0; i < numArgs; i++) {
+    pntrCpy(ptr + j, arg[i]);
+    j=j+argLength[i];
+  }
+  return ptr;
+
+}
+
+
+
+/* Find out the length of a pntrString */
+long pntrLen(const pntrString *s) {
+  /* Assume it's been allocated with poolMalloc. */
+  return ((((const long *)s)[-1] - (long)(sizeof(pntrString)))
+      / (long)(sizeof(pntrString)));
+}
+
+
+/* Find out the allocated length of a pntrString */
+long pntrAllocLen(const pntrString *s) {
+  return ((((long *)s)[-2] - (long)(sizeof(pntrString)))
+    / (long)(sizeof(pntrString)));
+}
+
+/* Set the actual size field in a pntrString allocated with poolFixedMalloc() */
+/* Use this if "zapping" a pntrString element with -1 to reduce its length. */
+/* Note that the pntrString will not be moved to the "used pool", even if
+   zapping its length results in free space; thus the free space will never
+   get recovered unless done by the caller or poolFree is called.  (This is
+   done on purpose so the caller can know what free space is left.) */
+/* ???Note that pntrZapLen's not moving string to used pool wastes potential
+   space when called by the routines in this module.  Effect should be minor. */
+void pntrZapLen(pntrString *s, long length) {
+  if (((long *)s)[-3] != -1) {
+    /* It's already in the used pool, so adjust free space tally */
+    poolTotalFree = poolTotalFree + ((long *)s)[-1]
+        - (length + 1) * (long)(sizeof(pntrString));
+  }
+  ((long *)s)[-1] = (length + 1) * (long)(sizeof(pntrString));
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("l: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+}
+
+
+/* Copy a string to another (pre-allocated) string */
+/* Dangerous for general purpose use */
+
+/*!
+ * \brief copies a null pointer terminated \ref pntrString to a destination
+ * \ref pntrString.
+ *
+ * This function determines the length of the source \p t by scanning for a
+ * terminal null pointer element.  The destination \p s must have enough space
+ * for receiving this amount of pointers, including the terminal null pointer.
+ * Then the source pointers are copied beginning with that at the
+ * lowest address to the destination area \p t, including the terminal null
+ * pointer.
+ *
+ * \attention make sure the destination area pointed to by \p s has enough
+ * space for the copied pointers.
+ *
+ * \param [out] s (not null) pointer to the target array receiving the copied
+ *   pointers.  This need not necessarily be the first element of the array.
+ * \param [in] t (not null) pointer to the start of the source array terminated
+ *   by a null pointer.
+ *
+ * \pre
+ *   - \p t is terminated by the first null pointer element.
+ *   - the target array \p s must have enough free space to hold the source array
+ *     \p t including the terminal null pointer.
+ *   - \p s and \p t can overlap if \p t points to a later or same element than
+ *     \p s (move left semantics).
+ * \invariant
+ *   If \p s is contained in a \ref pgBlock "block", its administrative header
+ *   is NOT updated.
+ * \warning The thoughtless use of this function has the potential to create
+ *   risks mentioned in the warning of \ref pntrString.
+ */
+void pntrCpy(pntrString *s, const pntrString *t) {
+  long i;
+  i = 0;
+  while (t[i] != NULL) { /* End of string -- pntrRight depends on it!! */
+    s[i] = t[i];
+    i++;
+  }
+  s[i] = t[i]; /* End of string */
+}
+
+
+/* Copy a string to another (pre-allocated) string */
+/* Like strncpy, only the 1st n characters are copied. */
+/* Dangerous for general purpose use */
+void pntrNCpy(pntrString *s, const pntrString *t, long n) {
+  long i;
+  i = 0;
+  while (t[i] != NULL) { /* End of string -- pntrSeg, pntrMid depend on it!! */
+    if (i >= n) break;
+    s[i] = t[i];
+    i++;
+  }
+  s[i] = t[i]; /* End of string */
+}
+
+
+/* Compare two strings */
+/* Unlike strcmp, this returns a 1 if the strings are equal
+   and 0 otherwise. */
+/* Only the pointers are compared.  If pointers are different,
+   0 will be returned, even if the things pointed to have same contents. */
+flag pntrEq(const pntrString *s, const pntrString *t) {
+  long i;
+  for (i = 0; s[i] == t[i]; i++)
+    if (s[i] == NULL) /* End of string */
+      return 1;
+  return 0;
+}
+
+
+/* Extract sin from character position start to stop into sout */
+temp_pntrString *pntrSeg(const pntrString *sin, long start, long stop) {
+  long length;
+  if (start < 1) start = 1;
+  if (stop < 1) stop = 0;
+  length = stop - start + 1;
+  if (length < 0) length = 0;
+  temp_pntrString *sout = pntrTempAlloc(length + 1);
+  pntrNCpy(sout, sin + start - 1, length);
+  sout[length] = *NULL_PNTRSTRING;
+  return sout;
+}
+
+/* Extract sin from character position start for length len */
+temp_pntrString *pntrMid(const pntrString *sin, long start, long length) {
+  if (start < 1) start = 1;
+  if (length < 0) length = 0;
+  temp_pntrString *sout = pntrTempAlloc(length + 1);
+  pntrNCpy(sout, sin + start-1, length);
+  sout[length] = *NULL_PNTRSTRING;
+  return sout;
+}
+
+/* Extract leftmost n characters */
+temp_pntrString *pntrLeft(const pntrString *sin, long n) {
+  if (n < 0) n = 0;
+  temp_pntrString *sout = pntrTempAlloc(n+1);
+  pntrNCpy(sout,sin,n);
+  sout[n] = *NULL_PNTRSTRING;
+  return sout;
+}
+
+/* Extract after character n */
+temp_pntrString *pntrRight(const pntrString *sin, long n) {
+  /*??? We could just return &sin[n-1], but this is safer for debugging. */
+  long i;
+  if (n < 1) n = 1;
+  i = pntrLen(sin);
+  if (n > i) return (NULL_PNTRSTRING);
+  temp_pntrString *sout = pntrTempAlloc(i - n + 2);
+  pntrCpy(sout, &sin[n-1]);
+  return sout;
+}
+
+
+/* Allocate and return an "empty" string n "characters" long */
+/* Each entry in the allocated array points to an empty vString. */
+temp_pntrString *pntrSpace(long n) {
+  long j = 0;
+  if (n<0) bug(1360);
+  temp_pntrString *sout = pntrTempAlloc(n+1);
+  while (j<n) {
+    /* Initialize all fields */
+    sout[j] = "";
+    j++;
+  }
+  sout[j] = *NULL_PNTRSTRING; /* Flags end of string */
+  return sout;
+}
+
+/* Allocate and return an "empty" string n "characters" long
+   initialized to nmbrStrings instead of vStrings */
+temp_pntrString *pntrNSpace(long n) {
+  long j = 0;
+  if (n<0) bug(1361);
+  temp_pntrString *sout = pntrTempAlloc(n+1);
+  while (j<n) {
+    /* Initialize all fields */
+    sout[j] = NULL_NMBRSTRING;
+    j++;
+  }
+  sout[j] = *NULL_PNTRSTRING; /* Flags end of string */
+  return sout;
+}
+
+/* Allocate and return an "empty" string n "characters" long
+   initialized to pntrStrings instead of vStrings */
+temp_pntrString *pntrPSpace(long n) {
+  long j = 0;
+  if (n<0) bug(1372);
+  temp_pntrString *sout = pntrTempAlloc(n+1);
+  while (j<n) {
+    /* Initialize all fields */
+    sout[j] = NULL_PNTRSTRING;
+    j++;
+  }
+  sout[j] = *NULL_PNTRSTRING; /* Flags end of string */
+  return sout;
+}
+
+/* Search for string2 in string1 starting at start_position */
+long pntrInstr(long start_position, const pntrString *string1,
+  const pntrString *string2)
+{
+   long ls1,ls2,i,j;
+   if (start_position<1) start_position=1;
+   ls1=pntrLen(string1);
+   ls2=pntrLen(string2);
+   for (i=start_position - 1; i <= ls1 - ls2; i++) {
+     for (j = 0; j<ls2; j++) {
+       if (string1[i+j] != string2[j])
+         break;
+     }
+     if (j == ls2) return (i+1);
+   }
+   return (0);
+
+}
+
+/* Search for string2 in string 1 in reverse starting at start_position */
+/* (Reverse pntrInstr) */
+long pntrRevInstr(long start_position, const pntrString *string1,
+    const pntrString *string2)
+{
+   long ls1,ls2;
+   ls1=pntrLen(string1);
+   ls2=pntrLen(string2);
+   if (start_position>ls1-ls2+1) start_position=ls1-ls2+2;
+   if (start_position<1) return 0;
+   while (!pntrEq(string2,pntrMid(string1,start_position,ls2))) {
+     start_position--;
+     pntrTempAlloc(0);
+        /* Clear temporaries to prevent overflow caused by "mid" */
+     if (start_position < 1) return 0;
+   }
+   return (start_position);
+}
+
+
+/* Add a single null string element to a pntrString - faster than pntrCat */
+temp_pntrString *pntrAddElement(const pntrString *g)
+{
+  long length = pntrLen(g);
+  temp_pntrString *v = pntrTempAlloc(length + 2);
+  pntrCpy(v, g);
+  v[length] = "";
+  v[length + 1] = *NULL_PNTRSTRING;
+/*E*/if(db9)getPoolStats(&i1,&j1_,&k1); if(db9)printf("bbg3: pool %ld stat %ld\n",poolTotalFree,i1+j1_);
+  return v;
+}
+
+
+/* Add a single null pntrString element to a pntrString -faster than pntrCat */
+temp_pntrString *pntrAddGElement(const pntrString *g)
+{
+  long length = pntrLen(g);
+  temp_pntrString *v = pntrTempAlloc(length + 2);
+  pntrCpy(v, g);
+  v[length] = NULL_PNTRSTRING;
+  v[length + 1] = *NULL_PNTRSTRING;
+  return v;
+}
+
+
+/*******************************************************************/
+/*********** Miscellaneous utility functions ***********************/
+/*******************************************************************/
+
+
+/* 0/1 knapsack algorithm */
+/* Returns the maximum worth (value) for items that can fit into maxSize */
+/* itemIncluded[] will be populated with 'y'/'n' if item included/excluded */
+long knapsack01(long items, /* # of items available to populate knapsack */
+    long *size, /* size of item 0,...,items-1 */
+    long *worth, /* worth (value) of item 0,...,items-1 */
+    long maxSize, /* size of knapsack (largest total size that will fit) */
+    char *itemIncluded /* output: 'y'/'n' if item 0..items-1 incl/excluded */)
+    {
+  long witem, wsize, a, b;
+
+  /* Maximum worth that can be attained for given #items and size */
+  long **maxWorth;  /* 2d matrix */
+  maxWorth = alloc2DMatrix((size_t)items + 1, (size_t)maxSize + 1);
+
+  /* This may run faster for applications that have hard-coded limits
+#define KS_MAX_ITEMS 100
+#define KS_MAX_SIZE 200
+  static long maxWorth[KS_MAX_ITEMS + 1][KS_MAX_SIZE + 1];
+  if (items > KS_MAX_ITEMS) {
+    printf("matrix item overflow\n"); exit(1);
+  }
+  if (maxSize > KS_MAX_SIZE) {
+    printf("matrix size overflow\n"); exit(1);
+  }
+  */
+
+  /* Populate the maximum worth matrix */
+  for (wsize = 0; wsize <= maxSize; wsize++) {
+    maxWorth[0][wsize] = 0;
+  }
+  for (witem = 1; witem <= items; witem++) {
+    for (wsize = 0; wsize <= maxSize; wsize++) {
+      if (wsize >= size[witem - 1]) {
+        /* Item witem can be part of the solution */
+        a = maxWorth[witem - 1][wsize];
+        b = maxWorth[witem - 1][wsize - size[witem - 1]] + worth[witem - 1];
+        /* Choose the case with greater value */
+        maxWorth[witem][wsize] = (a > b) ? a : b;  /* max(a,b) */
+      } else {
+        /* Item witem can't be part of the solution, otherwise total size
+           would exceed the intermediate size wsize. */
+        maxWorth[witem][wsize] = maxWorth[witem - 1][wsize];
+      }
+    }
+  }
+
+  /* Find the included items */
+  wsize = maxSize;
+  for (witem = items; witem > 0; witem--) {
+    itemIncluded[witem - 1] = 'n'; /* Initialize as excluded */
+    if (wsize > 0) {
+      if (maxWorth[witem][wsize] != maxWorth[witem - 1][wsize]) {
+        itemIncluded[witem - 1] = 'y'; /* Include the item */
+        wsize = wsize - size[witem - 1];
+      }
+    }
+  }
+
+  a = maxWorth[items][maxSize]; /* Final maximum worth */
+  free2DMatrix(maxWorth, (size_t)items + 1 /*, maxSize + 1*/);
+  return a;
+} /* knapsack01 */
+
+
+/* Allocate a 2-dimensional long integer matrix */
+/* Warning:  only entries 0,...,xsize-1 and 0,...,ysize-1 are allocated;
+   don't use entry xsize or ysize! */
+long **alloc2DMatrix(size_t xsize, size_t ysize)
+{
+  long **matrix;
+  long i;
+  matrix = malloc(xsize * sizeof(long *));
+  if (matrix == NULL) {
+    fprintf(stderr,"?FATAL ERROR 1376 Out of memory\n");
+    exit(1);
+  }
+  for (i = 0; i < (long)xsize; i++) {
+    matrix[i] = malloc(ysize * sizeof(long));
+    if (matrix[i] == NULL) {
+      fprintf(stderr,"?FATAL ERROR 1377 Out of memory\n");
+      exit(1);
+    }
+  }
+  return matrix;
+} /* alloc2DMatrix */
+
+
+/* Free a 2-dimensional long integer matrix */
+/* Note: the ysize argument isn't used, but is commented out as
+   a reminder so the caller doesn't confuse x and y */
+void free2DMatrix(long **matrix, size_t xsize /*, size_t ysize*/)
+{
+  long i;
+  for (i = (long)xsize - 1; i >= 0; i--) {
+    if (matrix[i] == NULL) bug(1378);
+    free(matrix[i]);
+  }
+  if (matrix == NULL) bug(1379);
+  free(matrix);
+  return;
+} /* free2DMatrix */
+
+
+/* Returns the amount of indentation of a statement label.  Used to
+   determine how much to indent a saved proof. */
+long getSourceIndentation(long statemNum) {
+  char *fbPtr; /* Source buffer pointer */
+  char *startLabel;
+  long indentation = 0;
+
+  fbPtr = g_Statement[statemNum].mathSectionPtr;
+  if (fbPtr[0] == 0) return 0;
+  startLabel = g_Statement[statemNum].labelSectionPtr;
+  if (startLabel[0] == 0) return 0;
+  while (1) { /* Go back to first line feed prior to the label */
+    if (fbPtr <= startLabel) break;
+    if (fbPtr[0] == '\n') break;
+    if (fbPtr[0] == ' ') {
+      indentation++; /* Space increments indentation */
+    } else {
+      indentation = 0; /* Non-space (i.e. a label character) resets back to 0 */
+    }
+    fbPtr--;
+  }
+  return indentation;
+} /* getSourceIndentation */
+
+
+/* Returns the last embedded comment (if any) in the label section of
+   a statement.  This is used to provide the user with information in the SHOW
+   STATEMENT command.  The caller must deallocate the result. */
+vstring getDescription(long statemNum) {
+  vstring_def(description);
+  long p1, p2;
+
+  let(&description, space(g_Statement[statemNum].labelSectionLen));
+  memcpy(description, g_Statement[statemNum].labelSectionPtr,
+      (size_t)(g_Statement[statemNum].labelSectionLen));
+  p1 = rinstr(description, "$(");
+  p2 = rinstr(description, "$)");
+  if (p1 == 0 || p2 == 0 || p2 < p1) {
+    let(&description, "");
+    return description;
+  }
+  let(&description, edit(seg(description, p1 + 2, p2 - 1),
+      8 + 128 /* discard leading and trailing blanks */));
+  return description;
+} /* getDescription */
+
+
+/* Returns the label section of a statement with all comments except the
+   last removed.  Unlike getDescription, this function returns the comment
+   surrounded by $( and $) as well as the leading indentation space
+   and everything after this comment (such as the actual label).
+   Since this is used for arbitrary (other than $a, $p) statements by the
+   EXPAND command, we also suppress section headers if they are the last
+   comment.  The caller must deallocate the result. */
+vstring getDescriptionAndLabel(long stmt) {
+  vstring_def(descriptionAndLabel);
+  long p1, p2;
+  flag dontUseComment = 0;
+
+  let(&descriptionAndLabel, space(g_Statement[stmt].labelSectionLen));
+  memcpy(descriptionAndLabel, g_Statement[stmt].labelSectionPtr,
+      (size_t)(g_Statement[stmt].labelSectionLen));
+  p1 = rinstr(descriptionAndLabel, "$(");
+  p2 = rinstr(descriptionAndLabel, "$)");
+  if (p1 == 0 || p2 == 0 || p2 < p1) {
+    /* The statement has no comment; just return the label and
+       surrounding spacing if any */
+    return descriptionAndLabel;
+  }
+  /* Search backwards for non-space or beginning of string */
+  p1--;
+  while (p1 != 0) {
+    if (descriptionAndLabel[p1 - 1] != ' '
+          && descriptionAndLabel[p1 - 1] != '\n') break;
+    p1--;
+  }
+  let(&descriptionAndLabel, right(descriptionAndLabel, p1 + 1));
+  /* Ignore descriptionAndLabels that are section headers */
+  /* TODO: make this more precise here and in mmwtex.c - use 79-char decorations? */
+  if (instr(1, descriptionAndLabel, cat("\n", TINY_DECORATION, NULL)) != 0
+      || instr(1, descriptionAndLabel, cat("\n", SMALL_DECORATION, NULL)) != 0
+      || instr(1, descriptionAndLabel, cat("\n", BIG_DECORATION, NULL)) != 0
+      || instr(1, descriptionAndLabel, cat("\n", HUGE_DECORATION, NULL)) != 0) {
+    dontUseComment = 1;
+  }
+  /* Remove comments with file inclusion markup */
+  if (instr(1, descriptionAndLabel, "$[") != 0) {
+    dontUseComment = 1;
+  }
+
+  /* Remove comments with $j markup */
+  if (instr(1, descriptionAndLabel, "$j") != 0) {
+    dontUseComment = 1;
+  }
+
+  if (dontUseComment == 1) {
+    /* Get everything that follows the comment */
+    p2 = rinstr(descriptionAndLabel, "$)");
+    if (p2 == 0) bug(1401); /* Should have exited earlier if no "$)" */
+    let(&descriptionAndLabel, right(descriptionAndLabel, p2 + 2));
+  }
+
+  return descriptionAndLabel;
+} /* getDescriptionAndLabel */
+
+
+/* Returns 0 or 1 to indicate absence or presence of an indicator in
+   the comment of the statement. */
+/* mode = 1 = PROOF_DISCOURAGED means get any proof modification discouraged
+                indicator
+   mode = 2 = USAGE_DISCOURAGED means get any new usage discouraged indicator
+   mode = 0 = RESET  means to reset everything (statemNum is ignored) */
+/* TODO: add a mode to reset a single statement if in the future we add
+   the ability to change the markup within the program. */
+flag getMarkupFlag(long statemNum, flag mode) {
+  /* For speedup, the algorithm searches a statement's comment for markup
+     matches only the first time, then saves the result for subsequent calls
+     for that statement. */
+  static char init = 0;
+  static vstring_def(commentSearchedFlags); /* Y if comment was searched */
+  static vstring_def(proofFlags);  /* Y if proof discouragement, else N */
+  static vstring_def(usageFlags);  /* Y if usage discouragement, else N */
+  vstring_def(str1);
+
+  if (mode == RESET) { /* Deallocate */ /* Should be called by ERASE command */
+    free_vstring(commentSearchedFlags);
+    free_vstring(proofFlags);
+    free_vstring(usageFlags);
+    init = 0;
+    return 0;
+  }
+
+  if (init == 0) {
+    init = 1;
+    /* The global variables g_proofDiscouragedMarkup and g_usageDiscouragedMarkup
+       are initialized to "" like all vstrings to allow them to be reassigned
+       by a possible future SET command.  So the first time this is called
+       we need to assign them to the default markup strings. */
+    if (g_proofDiscouragedMarkup[0] == 0) {
+      let(&g_proofDiscouragedMarkup, PROOF_DISCOURAGED_MARKUP);
+    }
+    if (g_usageDiscouragedMarkup[0] == 0) {
+      let(&g_usageDiscouragedMarkup, USAGE_DISCOURAGED_MARKUP);
+    }
+    /* Initialize flag strings */
+    let(&commentSearchedFlags, string(g_statements + 1, 'N'));
+    let(&proofFlags, space(g_statements + 1));
+    let(&usageFlags, space(g_statements + 1));
+  }
+
+  if (statemNum < 1 || statemNum > g_statements) bug(1392);
+
+  if (commentSearchedFlags[statemNum] == 'N') {
+    if (g_Statement[statemNum].type == f_ || g_Statement[statemNum].type == e_) {
+      /* Any comment before a $f, $e statement is assumed irrelevant */
+      proofFlags[statemNum] = 'N';
+      usageFlags[statemNum] = 'N';
+    } else {
+      if (g_Statement[statemNum].type != a_ && g_Statement[statemNum].type != p_) {
+        bug(1393);
+      }
+      str1 = getDescription(statemNum);  /* str1 must be deallocated here */
+      /* Strip linefeeds and reduce spaces */
+      let(&str1, edit(str1, 4 + 8 + 16 + 128));
+      if (instr(1, str1, g_proofDiscouragedMarkup)) {
+        proofFlags[statemNum] = 'Y';
+      } else {
+        proofFlags[statemNum] = 'N';
+      }
+      if (instr(1, str1, g_usageDiscouragedMarkup)) {
+        usageFlags[statemNum] = 'Y';
+      } else {
+        usageFlags[statemNum] = 'N';
+      }
+      free_vstring(str1); /* Deallocate */
+    }
+    commentSearchedFlags[statemNum] = 'Y';
+  }
+
+  if (mode == PROOF_DISCOURAGED) return (proofFlags[statemNum] == 'Y') ? 1 : 0;
+  if (mode == USAGE_DISCOURAGED) return (usageFlags[statemNum] == 'Y') ? 1 : 0;
+  bug(1394);
+  return 0;
+} /* getMarkupFlag */
+
+
+/* Extract contributor or date from statement description per the
+   following mode argument:
+
+       CONTRIBUTOR 1
+       CONTRIB_DATE 2
+       REVISER 3
+       REVISE_DATE 4
+       SHORTENER 5
+       SHORTEN_DATE 6
+       MOST_RECENT_DATE 7
+
+   When an item above is missing, the empty string is returned for that item.
+   The following utility modes are available:
+
+       GC_ERROR_CHECK_SILENT 8
+       GC_ERROR_CHECK_PRINT 9
+       GC_RESET 0
+       GC_RESET_STMT 10
+
+   For GC_ERROR_CHECK_SILENT and GC_ERROR_CHECK_PRINT, a "F" is returned if
+   error-checking fails, otherwise "P" is returned.  GC_ERROR_CHECK_PRINT also
+   prints the errors found.
+
+   GC_RESET clears the cache and returns the empty string.  It is normally
+   used by the ERASE command.  The stmtNum argument should be 0.  The
+   empty string is returned.
+
+   GC_RESET_STMT re-initializes the cache for the specified statement only.
+   It should be called whenever the labelSection is changed e.g. by
+   SAVE PROOF.  The empty string is returned.
+*/
+/* The caller must deallocate the returned string. */
+vstring getContrib(long stmtNum, char mode) {
+  /* For speedup, the algorithm searches a statement's comment for markup
+     matches only the first time, then saves the result for subsequent calls
+     for that statement. */
+  static char init = 0;
+
+  vstring_def(contributor);
+  vstring_def(contribDate);
+  vstring_def(reviser);
+  vstring_def(reviseDate);
+  vstring_def(shortener);
+  vstring_def(shortenDate);
+  vstring_def(mostRecentDate);   /* The most recent of all 3 dates */
+
+  static vstring_def(commentSearchedFlags); /* Y if comment was searched */
+  static pntrString_def(contributorList);
+  static pntrString_def(contribDateList);
+  static pntrString_def(reviserList);
+  static pntrString_def(reviseDateList);
+  static pntrString_def(shortenerList);
+  static pntrString_def(shortenDateList);
+  static pntrString_def(mostRecentDateList);
+
+  long cStart = 0, cMid = 0, cEnd = 0;
+  long rStart = 0, rMid = 0, rEnd = 0;
+  long sStart = 0, sMid = 0, sEnd = 0;
+  long firstR = 0, firstS = 0;
+  vstring_def(description);
+  vstring_def(tmpDate0);
+  vstring_def(tmpDate1);
+  vstring_def(tmpDate2);
+  long stmt, p, dd, mmm, yyyy;
+  flag errorCheckFlag = 0;
+  flag err = 0;
+  vstring_def(returnStr); /* Return value */
+#define CONTRIB_MATCH " (Contributed by "
+#define REVISE_MATCH " (Revised by "
+#define SHORTEN_MATCH " (Proof shortened by "
+#define END_MATCH ".) "
+
+  if (mode == GC_ERROR_CHECK_SILENT || mode == GC_ERROR_CHECK_PRINT) {
+    errorCheckFlag = 1;
+  }
+
+  if (mode == GC_RESET) {
+    /* This is normally called by the ERASE command only */
+    if (init != 0) {
+      if ((long)strlen(commentSearchedFlags) != g_statements + 1) {
+        bug(1395);
+      }
+      if (stmtNum != 0) {
+        bug(1400);
+      }
+      for (stmt = 1; stmt <= g_statements; stmt++) {
+        if (commentSearchedFlags[stmt] == 'Y') {
+          /* Deallocate cached strings */
+          free_vstring(*(vstring *)(&contributorList[stmt]));
+          free_vstring(*(vstring *)(&contribDateList[stmt]));
+          free_vstring(*(vstring *)(&reviserList[stmt]));
+          free_vstring(*(vstring *)(&reviseDateList[stmt]));
+          free_vstring(*(vstring *)(&shortenerList[stmt]));
+          free_vstring(*(vstring *)(&shortenDateList[stmt]));
+          free_vstring(*(vstring *)(&mostRecentDateList[stmt]));
+        }
+      }
+      /* Deallocate the lists of pointers to cached strings */
+      free_pntrString(contributorList);
+      free_pntrString(contribDateList);
+      free_pntrString(reviserList);
+      free_pntrString(reviseDateList);
+      free_pntrString(shortenerList);
+      free_pntrString(shortenDateList);
+      free_pntrString(mostRecentDateList);
+      free_vstring(commentSearchedFlags);
+      init = 0;
+    } /* if (init != 0) */
+    return "";
+  }
+
+  if (mode == GC_RESET_STMT) {
+    /* This should be called whenever the labelSection is changed e.g. by
+       SAVE PROOF. */
+    if (init != 0) {
+      if ((long)strlen(commentSearchedFlags) != g_statements + 1) {
+        bug(1398);
+      }
+      if (stmtNum < 1 || stmtNum > g_statements + 1) {
+        bug(1399);
+      }
+      if (commentSearchedFlags[stmtNum] == 'Y') {
+        /* Deallocate cached strings */
+        free_vstring(*(vstring *)(&contributorList[stmtNum]));
+        free_vstring(*(vstring *)(&contribDateList[stmtNum]));
+        free_vstring(*(vstring *)(&reviserList[stmtNum]));
+        free_vstring(*(vstring *)(&reviseDateList[stmtNum]));
+        free_vstring(*(vstring *)(&shortenerList[stmtNum]));
+        free_vstring(*(vstring *)(&shortenDateList[stmtNum]));
+        free_vstring(*(vstring *)(&mostRecentDateList[stmtNum]));
+        commentSearchedFlags[stmtNum] = 'N';
+      }
+    } /* if (init != 0) */
+    return "";
+  }
+
+  /* We now check only $a and $p statements - should we do others? */
+  if (g_Statement[stmtNum].type != a_ && g_Statement[stmtNum].type != p_) {
+    goto RETURN_POINT;
+  }
+
+  if (init == 0) {
+    init = 1;
+    /* Initialize flag string */
+    let(&commentSearchedFlags, string(g_statements + 1, 'N'));
+    /* Initialize pointers to "" (null vstring) */
+    pntrLet(&contributorList, pntrSpace(g_statements + 1));
+    pntrLet(&contribDateList, pntrSpace(g_statements + 1));
+    pntrLet(&reviserList, pntrSpace(g_statements + 1));
+    pntrLet(&reviseDateList, pntrSpace(g_statements + 1));
+    pntrLet(&shortenerList, pntrSpace(g_statements + 1));
+    pntrLet(&shortenDateList, pntrSpace(g_statements + 1));
+    pntrLet(&mostRecentDateList, pntrSpace(g_statements + 1));
+  }
+
+  if (stmtNum < 1 || stmtNum > g_statements) bug(1396);
+
+  if (commentSearchedFlags[stmtNum] == 'N' /* Not in cache */
+      || errorCheckFlag == 1 /* Needed to get sStart, rStart, cStart */) {
+    /* It wasn't cached, so we extract from the statement's comment */
+
+    free_vstring(description);
+    description = getDescription(stmtNum);
+    let(&description, edit(description,
+        4/*ctrl*/ + 8/*leading*/ + 16/*reduce*/ + 128/*trailing*/));
+    let(&description, cat(" ", description, " ", NULL)); /* Add for matching */
+
+    cStart = instr(1, description, CONTRIB_MATCH);
+    if (cStart != 0) {
+      cStart = cStart + (long)strlen(CONTRIB_MATCH); /* Start of contributor */
+      cEnd = instr(cStart, description, END_MATCH); /* End of date */
+      cMid = cEnd; /* After end of contributor and before start of date */
+      if (cMid != 0) {
+        while (description[cMid - 1] != ' ') {
+          cMid--;
+          if (cMid == 0) break;
+        }
+      }
+      /* We assign contributorList entry instead of contributor,
+         contribDateList entry instead of contribDate, etc. in case the
+         same string variable is used for several arguments for convenience
+         (e.g. to avoid having to declare 7 string variables if only one date
+         is needed) */
+      let((vstring *)(&(contributorList[stmtNum])),
+          seg(description, cStart, cMid - 2));
+      let((vstring *)(&(contribDateList[stmtNum])),
+          seg(description, cMid + 1, cEnd - 1));
+    } else {
+      /* The contributorList etc. are already initialized to the empty
+         string, so we don't have to assign them here. */
+    }
+
+    rStart = 0;
+    do {  /* Get the last revision entry */
+      p = instr(rStart + 1, description, REVISE_MATCH);
+      if (p != 0) {
+        rStart = p;
+        if (firstR == 0) firstR = p + (long)strlen(REVISE_MATCH);
+                               /* Add the strlen so to later compare to rStart */
+      }
+    } while (p != 0);
+    if (rStart != 0) {
+      rStart = rStart + (long)strlen(REVISE_MATCH); /* Start of reviser */
+      rEnd = instr(rStart, description, END_MATCH); /* End of date */
+      rMid = rEnd; /* After end of reviser and before start of date */
+      if (rMid != 0) {
+        while (description[rMid - 1] != ' ') {
+          rMid--;
+          if (rMid == 0) break;
+        }
+      }
+      let((vstring *)(&(reviserList[stmtNum])),
+          seg(description, rStart, rMid - 2));
+      let((vstring *)(&(reviseDateList[stmtNum])),
+          seg(description, rMid + 1, rEnd - 1));
+    }
+
+    sStart = 0;
+    do {  /* Get the last shorten entry */
+      p = instr(sStart + 1, description, SHORTEN_MATCH);
+      if (p != 0) {
+        sStart = p;
+        if (firstS == 0) firstS = p + (long)strlen(SHORTEN_MATCH);
+                               /* Add the strlen so to later compare to rStart */
+      }
+    } while (p != 0);
+    if (sStart != 0) {
+      sStart = sStart + (long)strlen(SHORTEN_MATCH); /* Start of shortener */
+      sEnd = instr(sStart, description, END_MATCH); /* End of date */
+      sMid = sEnd; /* After end of shortener and before start of date */
+      if (sMid != 0) {
+        while (description[sMid - 1] != ' ') {
+          sMid--;
+          if (sMid == 0) break;
+        }
+      }
+      let((vstring *)(&(shortenerList[stmtNum])),
+          seg(description, sStart, sMid - 2));
+      let((vstring *)(&(shortenDateList[stmtNum])),
+         seg(description, sMid + 1, sEnd - 1));
+    }
+
+
+    /* Get the most recent date */
+    let((vstring *)(&(mostRecentDateList[stmtNum])),
+        (vstring)(contribDateList[stmtNum]));
+    /* Note that compareDate() treats empty string as earliest date */
+    if (compareDates((vstring)(mostRecentDateList[stmtNum]),
+        (vstring)(reviseDateList[stmtNum])) == -1) {
+      let((vstring *)(&(mostRecentDateList[stmtNum])),
+          (vstring)(reviseDateList[stmtNum]));
+    }
+    if (compareDates((vstring)(mostRecentDateList[stmtNum]),
+        (vstring)(shortenDateList[stmtNum])) == -1) {
+      let((vstring *)(&(mostRecentDateList[stmtNum])),
+          (vstring)(shortenDateList[stmtNum]));
+    }
+
+    /* Tag the cache entry as updated */
+    commentSearchedFlags[stmtNum] = 'Y';
+  } /* commentSearchedFlags[stmtNum] == 'N' || errorCheckFlag == 1 */
+
+  /* Assign the output strings from the cache */
+  if (errorCheckFlag == 1) {
+    let(&contributor, (vstring)(contributorList[stmtNum]));
+    let(&contribDate, (vstring)(contribDateList[stmtNum]));
+    let(&reviser, (vstring)(reviserList[stmtNum]));
+    let(&reviseDate, (vstring)(reviseDateList[stmtNum]));
+    let(&shortener, (vstring)(shortenerList[stmtNum]));
+    let(&shortenDate, (vstring)(shortenDateList[stmtNum]));
+    let(&mostRecentDate, (vstring)(mostRecentDateList[stmtNum]));
+  } else {
+    /* Assign only the requested field for faster speed */
+    switch (mode) {
+      case CONTRIBUTOR:
+        let(&returnStr, (vstring)(contributorList[stmtNum])); break;
+      case CONTRIB_DATE:
+        let(&returnStr, (vstring)(contribDateList[stmtNum])); break;
+      case REVISER:
+        let(&returnStr, (vstring)(reviserList[stmtNum])); break;
+      case REVISE_DATE:
+        let(&returnStr, (vstring)(reviseDateList[stmtNum])); break;
+      case SHORTENER:
+        let(&returnStr, (vstring)(shortenerList[stmtNum])); break;
+      case SHORTEN_DATE:
+        let(&returnStr, (vstring)(shortenDateList[stmtNum])); break;
+      case MOST_RECENT_DATE:
+        let(&returnStr, (vstring)(mostRecentDateList[stmtNum])); break;
+      default: bug(1397); /* Any future modes should be added here */
+    } /* end switch (mode) */
+  }
+
+  /* Skip error checking for speedup if we're not printing errors */
+  if (errorCheckFlag == 0) goto RETURN_POINT;
+
+  /* For error checking, we don't require dates in syntax statements
+     (**** Note that this is set.mm-specific! ****) */
+  if (g_Statement[stmtNum].type == a_   /* Don't check syntax statements */
+      && strcmp(left(g_Statement[stmtNum].labelName, 3), "df-")
+      && strcmp(left(g_Statement[stmtNum].labelName, 3), "ax-")) {
+    goto RETURN_POINT;
+  }
+
+  if (cStart == 0) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        /* convenience prefix to assist massive revisions
+        g_Statement[stmtNum].labelName, " [",
+        contributor, "/", reviser, "/", shortener, "] ",
+        */
+        "?Warning: There is no \"", edit(CONTRIB_MATCH, 8+128),
+        "...)\" in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+
+  if (instr(cStart + 1, description, CONTRIB_MATCH) != 0) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        /* convenience prefix to assist massive revisions
+        g_Statement[stmtNum].labelName, " [",
+        contributor, "/", reviser, "/", shortener, "] ",
+        */
+        "?Warning: There is more than one \"", edit(CONTRIB_MATCH, 8+128),
+        "...)\" ",
+        "in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+
+  if (cStart != 0 && description[cMid - 2] != ',') {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        "?Warning: There is no comma between contributor and date",
+        ", or period is missing,",
+        " in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+  if (rStart != 0 && description[rMid - 2] != ',') {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        "?Warning: There is no comma between reviser and date",
+        ", or period is missing,",
+        " in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+  if (sStart != 0 && description[sMid - 2] != ',') {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        "?Warning: There is no comma between proof shortener and date",
+        ", or period is missing,",
+        " in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+  if (instr(1, contributor, ",") != 0) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        "?Warning: There is a comma in the contributor name \"",
+        contributor,
+        "\" in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+  if (instr(1, reviser, ",") != 0) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        "?Warning: There is a comma in the reviser name \"",
+        reviser,
+        "\" in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+  if (instr(1, shortener, ",") != 0) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        "?Warning: There is a comma in the proof shortener name \"",
+        shortener,
+        "\" in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+
+
+  /*********  Turn off this warning unless we decide not to allow this
+  if ((firstR != rStart) || (firstS != sStart)) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        /@ convenience prefix to assist massive revisions
+        g_Statement[stmtNum].labelName, " [",
+        @contributor, "/", @reviser, "/", @shortener, "] ",
+        @/
+        "?Warning: There are multiple \"",
+        edit(REVISE_MATCH, 8+128) , "...)\" or \"",
+        edit(SHORTEN_MATCH, 8+128) ,
+        "...)\" entries in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        "  The last one of each type was used.",
+        NULL), "    ", " ");
+  }
+  *********/
+
+  if ((firstR != 0 && firstR < cStart)
+      || (firstS != 0 && firstS < cStart)) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        /* convenience prefix to assist massive revisions
+        g_Statement[stmtNum].labelName, " [",
+        contributor, "/", reviser, "/", shortener, "] ",
+        */
+        "?Warning: \"", edit(CONTRIB_MATCH, 8+128),
+        "...)\" is placed after \"",
+        edit(REVISE_MATCH, 8+128) , "...)\" or \"",
+        edit(SHORTEN_MATCH, 8+128) ,
+        "...)\" in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+
+  if ((cStart !=0 && (cMid == 0 || cEnd == 0 || cMid == cEnd
+          || contributor[0] == 0 || contribDate[0] == 0))
+      || (rStart !=0 && (rMid == 0 || rEnd == 0 || rMid == rEnd
+          || reviser[0] == 0 || reviseDate[0] == 0))
+      || (sStart !=0 && (sMid == 0 || sEnd == 0 || sMid == sEnd
+          || shortener[0] == 0 || shortenDate[0] == 0))) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        /* convenience prefix to assist massive revisions
+        g_Statement[stmtNum].labelName, " [",
+        contributor, "/", reviser, "/", shortener, "] ",
+        */
+        "?Warning: There is a formatting error in a",
+        " \"", edit(CONTRIB_MATCH, 8+128),  "...)\", \"",
+        edit(REVISE_MATCH, 8+128) , "...)\", or \"",
+        edit(SHORTEN_MATCH, 8+128),
+        "...)\" entry in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+
+  if (contribDate[0] != 0) {
+    parseDate(contribDate, &dd, &mmm, &yyyy);
+    buildDate(dd, mmm, yyyy, &tmpDate0);
+    if (strcmp(contribDate, tmpDate0)) {
+      err = 1;
+      if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+          /* convenience prefix to assist massive revisions
+          g_Statement[stmtNum].labelName, " [",
+          contributor, "/", reviser, "/", shortener, "] ",
+          */
+          "?Warning: There is a formatting error in the \"",
+          edit(CONTRIB_MATCH, 8+128),  "...)\" date \"", contribDate, "\""
+          " in the comment above statement ",
+          str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+          NULL), "    ", " ");
+    }
+  }
+
+  if (reviseDate[0] != 0) {
+    parseDate(reviseDate, &dd, &mmm, &yyyy);
+    buildDate(dd, mmm, yyyy, &tmpDate0);
+    if (strcmp(reviseDate, tmpDate0)) {
+      err = 1;
+      if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+          /* convenience prefix to assist massive revisions
+          g_Statement[stmtNum].labelName, " [",
+          contributor, "/", reviser, "/", shortener, "] ",
+          */
+          "?Warning: There is a formatting error in the \"",
+          edit(REVISE_MATCH, 8+128) , "...)\" date \"", reviseDate, "\""
+          " in the comment above statement ",
+          str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+          NULL), "    ", " ");
+    }
+  }
+
+  if (shortenDate[0] != 0) {
+    parseDate(shortenDate, &dd, &mmm, &yyyy);
+    buildDate(dd, mmm, yyyy, &tmpDate0);
+    if (strcmp(shortenDate, tmpDate0)) {
+      err = 1;
+      if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+          /* convenience prefix to assist massive revisions
+          g_Statement[stmtNum].labelName, " [",
+          contributor, "/", reviser, "/", shortener, "] ",
+          */
+          "?Warning: There is a formatting error in the \"",
+          edit(SHORTEN_MATCH, 8+128) , "...)\" date \"", shortenDate, "\""
+          " in the comment above statement ",
+          str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+          NULL), "    ", " ");
+    }
+  }
+
+  if (contribDate[0] != 0 &&
+     ((reviseDate[0] != 0
+         && compareDates(contribDate, reviseDate) != -1)
+     || (shortenDate[0] != 0
+         && compareDates(contribDate, shortenDate) != -1))) {
+    err = 1;
+    if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        /* convenience prefix to assist massive revisions
+        g_Statement[stmtNum].labelName, " [",
+        contributor, "/", reviser, "/", shortener, "] ",
+        */
+        "?Warning: The \"", edit(CONTRIB_MATCH, 8+128),
+        "...)\" date is not earlier than the \"",
+        edit(REVISE_MATCH, 8+128), "...)\" or \"",
+        edit(SHORTEN_MATCH, 8+128),
+        "...)\" date in the comment above statement ",
+        str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+        NULL), "    ", " ");
+  }
+
+  if (reviseDate[0] != 0 && shortenDate[0] != 0) {
+    p = compareDates(reviseDate, shortenDate);
+    if ((rStart < sStart && p == 1)
+        || (rStart > sStart && p == -1)) {
+      err = 1;
+      if (mode == GC_ERROR_CHECK_PRINT) printLongLine(cat(
+        /* convenience prefix to assist massive revisions
+        g_Statement[stmtNum].labelName, " [",
+        contributor, "/", reviser, "/", shortener, "] ",
+        */
+          "?Warning: The \"", edit(REVISE_MATCH, 8+128), "...)\" and \"",
+          edit(SHORTEN_MATCH, 8+128),
+         "...)\" dates are in the wrong order in the comment above statement ",
+          str((double)stmtNum), ", label \"", g_Statement[stmtNum].labelName, "\".",
+          NULL), "    ", " ");
+    }
+  }
+
+  if (err == 1) {
+    let(&returnStr, "F");  /* fail */
+  } else {
+    let(&returnStr, "P");  /* pass */
+  }
+
+
+ RETURN_POINT:
+
+  free_vstring(description);
+
+  if (errorCheckFlag == 1) { /* Slight speedup */
+    free_vstring(contributor);
+    free_vstring(contribDate);
+    free_vstring(reviser);
+    free_vstring(reviseDate);
+    free_vstring(shortener);
+    free_vstring(shortenDate);
+    free_vstring(mostRecentDate);
+    free_vstring(tmpDate0);
+    free_vstring(tmpDate1);
+    free_vstring(tmpDate2);
+  }
+
+  return returnStr;
+} /* getContrib */
+
+
+/* Extract up to 2 dates after a statement's proof.  If no date is present,
+   date1 will be blank.  If no 2nd date is present, date2 will be blank.
+   THIS WILL BECOME OBSOLETE WHEN WE START TO USE DATES IN THE
+   DESCRIPTION. */
+void getProofDate(long stmtNum, vstring *date1, vstring *date2) {
+  vstring_def(textAfterProof);
+  long p1, p2;
+  let(&textAfterProof, space(g_Statement[stmtNum + 1].labelSectionLen));
+  memcpy(textAfterProof, g_Statement[stmtNum + 1].labelSectionPtr,
+      (size_t)(g_Statement[stmtNum + 1].labelSectionLen));
+  let(&textAfterProof, edit(textAfterProof, 2)); /* Discard spaces and tabs */
+  p1 = instr(1, textAfterProof, "$([");
+  p2 = instr(p1, textAfterProof, "]$)");
+  if (p1 && p2) {
+    let(&(*date1), seg(textAfterProof, p1 + 3, p2 - 1));  /* 1st date stamp */
+    p1 = instr(p2, textAfterProof, "$([");
+    p2 = instr(p1, textAfterProof, "]$)");
+    if (p1 && p2) {
+      let(&(*date2), seg(textAfterProof, p1 + 3, p2 - 1)); /* 2nd date stamp */
+    } else {
+      let(&(*date2), ""); /* No 2nd date stamp */
+    }
+  } else {
+    let(&(*date1), ""); /* No 1st or 2nd date stamp */
+    let(&(*date2), "");
+  }
+  free_vstring(textAfterProof); /* Deallocate */
+  return;
+} /* getProofDate */
+
+
+/* Get date, month, year fields from a dd-mmm-yyyy date string,
+   where dd may be 1 or 2 digits, mmm is 1st 3 letters of month,
+   and yyyy is 2 or 4 digits.  A 1 is returned if an error was detected. */
+flag parseDate(vstring dateStr, long *dd, long *mmm, long *yyyy) {
+  long j;
+  flag err = 0;
+  j = instr(1, dateStr, "-");
+  *dd = (long)val(left(dateStr, j - 1)); /* Day */
+#define MONTHS "JanFebMarAprMayJunJulAugSepOctNovDec"
+  *mmm = ((instr(1, MONTHS, mid(dateStr, j + 1, 3)) - 1) / 3) + 1; /* 1 = Jan */
+  j = instr(j + 1, dateStr, "-");
+  *yyyy = (long)val(right(dateStr, j + 1));
+  if (*yyyy < 100) { /* 2-digit year (obsolete) */
+#define START_YEAR 93 /* Earliest 19xx year in set.mm database */
+    if (*yyyy < START_YEAR) {
+      *yyyy = *yyyy + 2000;
+    } else {
+      *yyyy = *yyyy + 1900;
+    }
+  }
+  if (*dd < 1 || *dd > 31 || *mmm < 1 || *mmm > 12) err = 1;
+  return err;
+} /* parseDate */
+
+
+/* Build date from numeric fields.  mmm should be a number from 1 to 12.
+   There is no error-checking. */
+void buildDate(long dd, long mmm, long yyyy, vstring *dateStr) {
+  let(&(*dateStr), cat(str((double)dd), "-", mid(MONTHS, mmm * 3 - 2, 3), "-",
+      str((double)yyyy), NULL));
+  return;
+} /* buildDate */
+
+
+/* Compare two dates in the form dd-mmm-yyyy.  -1 = date1 < date2,
+   0 = date1 = date2,  1 = date1 > date2.  There is no error checking. */
+flag compareDates(vstring date1, vstring date2) {
+  long d1, m1, y1, d2, m2, y2, dd1, dd2;
+
+  /* If a date is the empty string, treat it as being _before_ any other
+     date */
+  if (date1[0] == 0 || date2[0] == 0) {
+    if (date1[0] == 0 && date2[0] == 0) {
+      return 0;
+    } else if (date1[0] == 0) {
+      return -1;
+    } else {
+      return 1;
+    }
+  }
+
+  parseDate(date1, &d1, &m1, &y1);
+  parseDate(date2, &d2, &m2, &y2);
+  /* dd1, dd2 increase monotonically but aren't true days since 1-Jan-0000 */
+  dd1 = d1 + m1 * 32 + y1 * 500;
+  dd2 = d2 + m2 * 32 + y2 * 500;
+  if (dd1 < dd2) {
+    return -1;
+  } else if (dd1 == dd2) {
+    return 0;
+  } else {
+    return 1;
+  }
+} /* compareDates */
+
+
+
+
+/* Compare strings via pointers for qsort */
+/* g_qsortKey is a global string key at which the sort starts; if empty,
+   start at the beginning of each line. */
+int qsortStringCmp(const void *p1, const void *p2)
+{
+  vstring_def(tmp);
+  long n1, n2;
+  int r;
+  /* Returns -1 if p1 < p2, 0 if equal, 1 if p1 > p2 */
+  if (g_qsortKey[0] == 0) {
+    /* No key, use full line */
+    return strcmp(*(char * const *)p1, *(char * const *)p2);
+  } else {
+    n1 = instr(1, *(char * const *)p1, g_qsortKey);
+    n2 = instr(1, *(char * const *)p2, g_qsortKey);
+    r = strcmp(
+        right(*(char * const *)p1, n1),
+        right(*(char * const *)p2, n2));
+    free_vstring(tmp); /* Deallocate temp string stack */
+    return r;
+  }
+}
+
+void freeData() {
+  /* 15-Aug-2020 nm TODO: are some of these called twice? (in eraseSource) */
+  free(g_IncludeCall);
+  free(g_Statement);
+  free(g_MathToken);
+  free(memFreePool);
+  free(memUsedPool);
+}
diff --git a/src/mmdata.h b/src/mmdata.h
new file mode 100644
index 0000000..8c7a316
--- /dev/null
+++ b/src/mmdata.h
@@ -0,0 +1,957 @@
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMDATA_H_
+#define METAMATH_MMDATA_H_
+
+/*!
+ * \file mmdata.h
+ * \brief includes for some principal data structures and data-string handling
+ */
+
+#include "mmvstr.h"
+
+/* debugging flags & variables */
+
+/*!
+ * \var long db
+ * \brief bytes held by vstring instances outside of the stack of temporaries
+ *
+ * monitors the number of bytes (including the terminal NUL) currently used in
+ * all \ref vstring pointer variables OUTSIDE of the \ref tempAllocStack.
+ * Note: This is NOT the number of bytes allocated, but the portion actually
+ * used!  A few memory allocations are also included:
+ * - command line buffers used to hold user input from a console;
+ * - buffers used to read file contents in.
+ *
+ * If the user has turned MEMORY_STATUS on, metamath will print out this value
+ * after each command in a message like "Memory: string < db >".
+ */
+/*E*/extern long db;
+/*E*/extern long db0;
+
+/*!
+ * \var long db1
+ * \brief bytes held by vstring instances inside of the stack of temporaries
+ *
+ * monitors the number of bytes currently pointed to by \ref vstring pointers
+ * INSIDE the \ref tempAllocStack.  Note: This is NOT their capacity, but the
+ * portion actually used!
+ *
+ * Not updated if NDEBUG (usually deactivates asserts in C code) is defined.
+ *
+ * \bug Seems never be displayed.
+ */
+/*E*/extern long db1;
+
+/*!
+ * \var long db2
+ * Bytes held in \ref pgBlock "blocks" managed in \ref tempAllocStack
+ * "temporary pointer stacks".
+ */
+/*E*/extern long db2;
+
+/*!
+ * \var db3
+ * \brief monitors the de/allocations of nmbrString and \ref pntrString outside
+ * of temporary arrays.
+ *
+ * The number of bytes held in blocks dedicated to global data.  There exist
+ * also temporary stacks, but they are not considered here.  Useful to see
+ * whether a process looses memory due to imbalanced calls to allocation/free
+ * functions.
+ *
+ * If the user has turned MEMORY_STATUS on, metamath will print out this value
+ * after each command in a message like "Memory: ... xxxString < db3 >".
+ */
+/*E*/extern long db3;
+/*E*/extern long db4,db5,db6,db7,db8;
+
+/*!
+ * \var db9
+ * \brief log memory pool usage for debugging purpose.
+ *
+ * If set to a non-zero value, the state of the memory pool is
+ * logged during execution of metamath.  This debugging feature tracks
+ * de/allocation of memory in the memory pool.
+ * This particular debug mode is controlled by the Metamath commands
+ * - SET MEMORY_STATUS ON\n
+ *   enables memory logging
+ * - SET MEMORY_STATUS OFF\n
+ *   disables memory debugging
+ * - SET DEBUG FLAG 9\n
+ *   (deprecated) enables memory logging
+ * - SET DEBUG OFF\n
+ *   disables memory logging in conjunction with other debugging aid.
+ */
+/*E*/extern long db9;
+
+/*!
+ * \typedef flag
+ * a char whose range is restricted to 0 (equivalent to false/no) and 1
+ * (equivalent to true/yes), the typical semantics of a bool (Boolean value).
+ */
+typedef char flag;
+
+/*!
+ * \var g_listMode
+ * \deprecated
+ * Obsolete.  Now fixed to 0.  Historically the metamath sources were also used
+ * for other purposes than maintaining Metamath files.  One such application, a
+ * standalone text processor, was LIST.EXE.  The sources still query this
+ * \ref flag occasionally, but its value is in fact fixed to 0 in metamath,
+ * meaning the LIST.EXE functionality is an integral part of metamath now.
+ */
+extern flag g_listMode; /* 0 = metamath, 1 = list utility */
+
+/*!
+ * \var g_toolsMode
+ * Metamath has two modes of operation: In its primary mode it handles
+ * mathematical contents like proofs.  In this mode \ref g_toolsMode  is set to
+ * 0.  This is the value assigned on startup.  A second mode is enabled after
+ * executing the 'tools' command.  In this mode text files are processed using
+ * high level commands.  It is indicated by a 1 in \ref g_toolsMode.
+ */
+extern flag g_toolsMode; /* In metamath mode:  0 = metamath, 1 = tools */
+
+typedef long nmbrString; /* String of numbers */
+
+/*!
+ * \typedef pntrString
+ * \brief an array (maybe of size 1) of untyped pointers (void*)
+ *
+ * In general this array is organized like a stack: the number of elements in
+ * the pntrString array grows and shrinks during program flow, values are
+ * pushed and popped at the end.  Such a stack is embedded in a \ref pgBlock
+ * that contains administrative information about the stack.  The stack begins
+ * with element 0, and the administrative information is accessed through
+ * negative indices, but need reinterpretation then.  To allow iterating
+ * through the tail of an array from a certain element on, an array terminates
+ * with a null pointer.  This type of usage forbids null pointer as ordinary
+ * elements, and the terminal null pointer is not part of the data in the
+ * array.
+ *
+ * The length of a pntrString array is implicitely given by a terminal NULL
+ * pointer.  If this array is held in a \ref pgBlock, its size can also be
+ * determined from its header's administrative data.  Both values must be kept
+ * synchronized.  In early phases of memory allocation, when data wasn't
+ * assigned yet, this need not hold, though.
+ *
+ * To summarize the usages of this type:
+ * - If you want to resize the array/stack you need a pointer to element 0.
+ * - You can iterate from an arbitrary element to the end.
+ * - Sometimes pntrString denotes an isolated element, not embedded in a
+ *   greater array.
+ *
+ * \warning Simply copying elements around is dangerous, if the elements point
+ *   to allocated memory.  There must be a guard that owns the instances,
+ *   survives each of the copies and finally deallocates the instances.
+ *   Otherwise you risk memory leaks, or even worse, undefined behavior, if a
+ *   copied pointer uses an instance previously freed by the original.  One way
+ *   to provide such a guard is a nested program structure.  All copied
+ *   pointers are created in subroutines, and they vanish before the caller
+ *   gains control again, and can safely deallocate the instances.  A similar
+ *   strategy is followed by \ref pntrLet, where deallocations of original and
+ *   dependent instances are deferred until an operation finally finishes.
+ *   More modern concepts employ reference counting schemes, thus avoiding
+ *   dedicated ownership and solving this problem more thoroughly, but Metamath
+ *   is not up to that level (yet).
+ */
+typedef void* pntrString; /* String of pointers */
+
+/* A nmbrString allocated in temporary storage. These strings will be deallocated
+   after the next call to `nmbrLet`.
+   See also `temp_vstring` for information on how temporaries are handled. */
+typedef nmbrString temp_nmbrString;
+
+/* A pntrString allocated in temporary storage. These strings will be deallocated
+   after the next call to `pntrLet`.
+   See also `temp_vstring` for information on how temporaries are handled. */
+
+/*!
+ * \typedef temp_pntrString
+ * \brief a single \ref pntrString element for use in a \ref pgStack "stack".
+ *
+ * These elements are pushed onto and popped off a
+ * \ref pgStack "stack of temporary data".  Pointers of this type should ONLY
+ * refer to dynamically allocated memory on the heap.  Special commands support
+ * dependency tracking and free all pointers on and after a particular one in
+ * such a stack. 
+ */
+typedef pntrString temp_pntrString;
+
+enum mTokenType { var_, con_ };
+#define lb_ '{' /* ${ */
+#define rb_ '}' /* $} */
+#define v_  'v' /* $v */
+#define c_  'c' /* $c */
+#define a_  'a' /* $a */
+#define d_  'd' /* $d */
+#define e_  'e' /* $e */
+#define f_  'f' /* $f */
+#define p_  'p' /* $p */
+#define eq_ '=' /* $= */
+#define sc_ '.' /* $. (historically, used to be $; (semicolon) ) */
+#define illegal_ '?' /* anything else */
+/* Global variables related to current statement */
+extern int g_currentScope;
+
+/*! The data associated with a statement in the file */
+struct statement_struct { /* Array index is statement number, starting at 1 */
+  long lineNum; /*!< Line number in file; 0 means not yet determined */
+  vstring fileName; /*!< File statement is in; "" means not yet determined */
+  vstring labelName; /*!< Label of statement */
+  flag uniqueLabel; /*!< Flag that label is unique (future implementations may
+                      allow duplicate labels on hypotheses) */
+  char type;    /*!< 2nd character of keyword, e.g. 'e' for $e */
+  int scope;    /*!< Block scope level, increased by ${ and decreased by $};
+       ${ has scope _before_ the increase; $} has scope _before_ the decrease */
+  long beginScopeStatementNum;  /*!< statement of previous ${ ; 0 if we're in
+                outermost block */
+  long endScopeStatementNum;  /*!< statement of next $} (populated for opening
+                                 ${ only, 0 otherwise); g_statements+1 if
+                              we're in outermost block */
+  vstring statementPtr; /*!< Pointer to end of (unmodified) label section used
+             to determine file and line number for error or info messages about
+             the statement */
+  vstring labelSectionPtr; /*!< Source code before statement keyword
+                 - will be updated if labelSection changed */
+  long labelSectionLen;
+  char labelSectionChanged; /*!< Default is 0; if 1, labelSectionPtr points to an
+                               allocated vstring that must be freed by ERASE */
+  vstring mathSectionPtr; /*!< Source code between keyword and $= or $. */
+  long mathSectionLen;
+  char mathSectionChanged; /*!< Default is 0; if 1, mathSectionPtr points to an
+                               allocated vstring that must be freed by ERASE */
+  vstring proofSectionPtr; /*!< Source code between $= and $. */
+  long proofSectionLen;
+  char proofSectionChanged; /*!< Default is 0; if 1, proofSectionPtr points to an
+                               allocated vstring that must be freed by ERASE */
+  nmbrString *mathString; /*!< Parsed mathSection */
+  long mathStringLen;
+  nmbrString *proofString; /*!< Parsed proofSection (used by $p's only */
+  nmbrString *reqHypList; /*!< Required hypotheses (excluding $d's) */
+  nmbrString *optHypList; /*!< Optional hypotheses (excluding $d's) */
+  long numReqHyp; /*!< Number of required hypotheses */
+  nmbrString *reqVarList; /*!< Required variables */
+  nmbrString *optVarList; /*!< Optional variables */
+  nmbrString *reqDisjVarsA; /*!< Required disjoint variables, 1st of pair */
+  nmbrString *reqDisjVarsB; /*!< Required disjoint variables, 2nd of pair */
+  nmbrString *reqDisjVarsStmt; /*!< Req disjoint variables, statem number */
+  nmbrString *optDisjVarsA; /*!< Optional disjoint variables, 1st of pair */
+  nmbrString *optDisjVarsB; /*!< Optional disjoint variables, 2nd of pair */
+  nmbrString *optDisjVarsStmt; /*!< Opt disjoint variables, statem number */
+  long pinkNumber; /*!< The $a/$p sequence number for web pages */
+  long headerStartStmt; /*!< # of stmt following previous $a, $p */
+};
+
+/*! Sort keys for statement labels (allocated by parseLabels) */
+extern long *g_labelKey;
+
+/*! This structure holds all information related to $[ $] (include) statements
+   in the input source files, for error message processing. */
+struct includeCall_struct {
+  vstring source_fn;  /*!< Name of the file where the
+       inclusion source is located (= parent file for $( Begin $[... etc.) */
+  vstring included_fn;  /*!< Name of the file in the
+       inclusion statement e.g. "$( Begin $[ included_fn..." */
+  long current_offset;  /*!< This is the starting
+      character position of the included file w.r.t entire source buffer */
+  long current_line; /*!< The line number
+      of the start of the included file (=1) or the continuation line of
+      the parent file */
+  flag pushOrPop; /*!< 0 means included file, 1 means continuation of parent */
+  vstring current_includeSource; /*!< (Currently) assigned
+      only if we may need it for a later Begin comparison */
+  long current_includeLength; /*!< Length of the file
+      to be included (0 if the file was previously included) */
+};
+
+/*! The data associated with a math token. */
+struct mathToken_struct {
+  vstring tokenName; /*!< may be used more than once at different scopes */
+  long length; /*!< to speed up parsing scans */
+  char tokenType; /*!< variable or constant - (char)var_ or (char)con_ */
+  flag active;  /*!< 1 if token is recognized in current scope */
+  int scope;    /*!< scope level token was declared at */
+  long tmp;     /*!< Temporary field use to speed up certain functions */
+  long statement; /*!< Statement declared in */
+  long endStatement; /*!< Statement of end of scope it was declared in */
+};
+
+/*! Sort keys for math tokens (allocated by parseMathDecl) */
+extern long *g_mathKey;
+
+extern long g_MAX_STATEMENTS;
+extern long g_MAX_MATHTOKENS;
+extern struct statement_struct *g_Statement;
+/*Obs*/ /*extern struct label_struct *label;*/
+
+/*! \warning `mathToken[i]` is 0-based, not 1-based! */
+extern struct mathToken_struct *g_MathToken;
+extern long g_statements, /*labels,*/ g_mathTokens;
+
+extern long g_MAX_INCLUDECALLS;
+extern struct includeCall_struct *g_IncludeCall;
+extern long g_includeCalls;
+
+extern char *g_sourcePtr; /*!< Pointer to buffer in memory with input source */
+extern long g_sourceLen; /*!< Number of chars. in all inputs files combined (after includes)*/
+
+/* For use by getMarkupFlag() */
+#define PROOF_DISCOURAGED_MARKUP "(Proof modification is discouraged.)"
+#define USAGE_DISCOURAGED_MARKUP "(New usage is discouraged.)"
+/* Mode argument for getMarkupFlag() */
+#define PROOF_DISCOURAGED 1
+#define USAGE_DISCOURAGED 2
+#define RESET 0
+/* Mode argument for getContrib() */
+#define GC_RESET 0
+#define GC_RESET_STMT 10
+#define CONTRIBUTOR 1
+#define CONTRIB_DATE 2
+#define REVISER 3
+#define REVISE_DATE 4
+#define SHORTENER 5
+#define SHORTEN_DATE 6
+#define MOST_RECENT_DATE 7
+#define GC_ERROR_CHECK_SILENT 8
+#define GC_ERROR_CHECK_PRINT 9
+
+/* 14-May-2017 nm - TODO: someday we should create structures to
+   hold global vars, and clear their string components in eraseSource() */
+extern vstring g_contributorName;
+#define DEFAULT_CONTRIBUTOR "?who?"
+
+extern vstring g_proofDiscouragedMarkup;
+extern vstring g_usageDiscouragedMarkup;
+extern flag g_globalDiscouragement; /* SET DISCOURAGEMENT */
+
+/* Allocation and deallocation in memory pool */
+void *poolFixedMalloc(long size /* bytes */);
+
+/*!
+ * \fn void *poolMalloc(long size)
+ * \brief allocates and initializes a new \ref pgBlock
+ *
+ * allocates a \ref pgBlock, first removing and using the last element of the
+ * \ref memFreePool.  If this block exists, but has not sufficient size, it is
+ * reallocated from the system.  If the pool is empty, a new \ref pgBlock of
+ * the given size is allocated from the system.  In any case, the header of the
+ * \ref pgBlock is properly initialized.  Exits program on out of memory
+ * condition.
+ * \param[in] size (in bytes) of the block, not including the block header.
+ * \return a \ref pgBlock with enough capacity for \p size bytes of data.
+ *  \post
+ *    - The \ref pgBlock "block's" header denotes \p size bytes are occupied,
+ *      but they yet contain random data.
+ *    - Exit on out-of-memory.
+ */
+void *poolMalloc(long size /* bytes */);
+
+/*!
+ * \fn poolFree(void *ptr)
+ *
+ * Removes \p ptr from the \ref memUsedPool, if it is listed there.  Then tries
+ * adding it to the \ref memFreePool.  If this pool is full, it is increased by
+ * \ref MEM_POOL_GROW.  If this fails, the program is exited, else \p ptr is
+ * added.
+ * \param[in] ptr pointer to a \ref pgBlock.
+ * \pre
+ *   - \p ptr refers to dynamically allocated memory on the heap.
+ *   - all memory pointed to by \p ptr is considered free.  This holds even if it
+ *     it is kept in \ref memUsedPool. 
+ * \post
+ *   - \ref poolTotalFree is updated
+ *   - Exit on out-of-memory (the \ref memFreePool overflows)
+ * \attention never submit a \p ptr referring to memory not on the heap, like
+ *   NULL_PTRSTRING.
+ */
+void poolFree(void *ptr);
+
+/*!
+ * \fn addToUsedPool(void *ptr)
+ * \brief announces a block with free capacity for further allocation
+ *
+ * This function temporarily freezes the usage of a block for the current user,
+ * and allows temporary reallocation of the free capacity to a new client.
+ *
+ * The program maintains pools of memory blocks with free capacity.  In case of
+ * demand such a \ref pgBlock can temporarily allocate this capacity for new
+ * usage.  Of course two (or more) clients share different parts of the same
+ * \ref pgBlock then, so a newer client must complete its usage before the old
+ * one resumes operation and may want to extend its usage of the \ref pgBlock.
+ *
+ * Before \p ptr is added to \ref memUsedPool, the pool size is checked and
+ * increased by \ref MEM_POOL_GROW if full.  This may lead to out-of-memory
+ * \ref outOfMemory "exit".  But if \p ptr is added to the end of the \ref memUsedPool,
+ * \ref poolTotalFree is updated.
+ * \param[in] ptr pointer to a \ref pgBlock.
+ * \pre
+ *   the block is \ref pgFragmentation "fragmented" (contains unused memory)
+ *   If it is full, \ref bugfn "bug" is called and the function returns without
+ *   further action.
+ * \post
+ *   - \ref poolTotalFree is the current free space in bytes in both pools.
+ *   - A full \ref pgBlock is not added to \ref memUsedPool by this function.
+ *   - Exit on out-of-memory (\ref memUsedPool overflows)
+ */
+void addToUsedPool(void *ptr);
+/*! Purges reset memory pool usage */
+void memFreePoolPurge(flag untilOK);
+/* Statistics */
+
+/*!
+ * \fn getPoolStats(long *freeAlloc, long *usedAlloc, long *usedActual)
+ * \brief Provide information about memory in pools at the instant of call.
+ *
+ * Return the overall statistics about the pools \ref memFreePool
+ * "free block array" and the \ref memUsedPool "used block array".  In MEMORY
+ * STATUS mode ON, a diagnostic message compares the contents of
+ * \ref poolTotalFree to the values found in this statistics.  They should not
+ * differ!
+ *
+ * \attention This is NOT full memory usage, because completely used
+ * \ref pgBlock "blocks" are not tracked!
+ *
+ * \param[out] freeAlloc (not-null) address of a long variable receiving the
+ * accumulated number of bytes in the free list.  Sizes do not include the
+ * hidden header present in each block.
+ * \param[out] usedAlloc (not-null) address of a long variable receiving the
+ * accumulated number of free bytes in partially used blocks.
+ * \param[out] usedActual (not-null) address of a long variable receiving the
+ * accumulated bytes consumed by usage so far.  This value includes the hidden
+ * header of the block.
+ * \pre Do not call within \ref bugfn "bug".\n
+ *   Submit only non-null pointers, even if not all information is needed.\n
+ *   Pointers to irrelevant information may be the same.
+ * \post Statistic data is copied to the locations the parameters point to.
+ */
+void getPoolStats(long *freeAlloc, long *usedAlloc, long *usedActual);
+
+/*! Initial memory allocation */
+void initBigArrays(void);
+
+/*! Find the number of free memory bytes */
+long getFreeSpace(long max);
+
+/*!
+ * \fn void outOfMemory(const char *msg)
+ * \brief exit after fatal memory allocation error.
+ *
+ * called when memory cannot be allocated, either because memory/address space
+ * is physically exhausted, or because administrative structures would overflow.
+ * Stops execution and wait for the user to confirm having read the message,
+ * before exiting the program raising an error condition.
+ *
+ * \param msg error message displayed to the user.
+ * \return never, but exits the program instead.
+ * \bug calls functions like print2, that in turn may call outOfMemory again
+ * under restricted memory conditions, so finally memory error messages are
+ * stacked up endlessly.
+ */
+void outOfMemory(const char *msg);
+
+/*!
+ * \anchor bugfn
+ * \fn void bug(int bugNum)
+ * \param[in] bugNum
+ */
+void bug(int bugNum);
+
+
+/*! Null nmbrString -- -1 flags the end of a nmbrString */
+struct nullNmbrStruct {
+    long poolLoc;
+    long allocSize;
+    long actualSize;
+    nmbrString nullElement; };
+extern struct nullNmbrStruct g_NmbrNull;
+#define NULL_NMBRSTRING &(g_NmbrNull.nullElement)
+#define nmbrString_def(x) nmbrString *x = NULL_NMBRSTRING
+#define free_nmbrString(x) nmbrLet(&x, NULL_NMBRSTRING)
+
+/*!
+ * \struct nullPntrStruct
+ * \brief Null pntrString -- NULL flags the end of a pntrString
+ *
+ * Describes a \ref pgBlock of \ref pntrString containing only the null
+ * pointer.  Besides this pointer it is accompanied with a header containing
+ * the hidden administrative values of such \ref pgBlock "block".
+ *
+ * The values in this administrative header are such that it is never subject to
+ * memory allocation or deallocation.
+ *
+ * \bug The C standard does not require a long having the same size as a
+ * void*.  In fact there might be **no** integer type matching a pointer in size.
+ */
+struct nullPntrStruct {
+  /*!
+   * An instance of a nullPntrStruct is always standalone and never part of a
+   * larger pool.  Indicated by the fixed value -1.
+   */
+  long poolLoc;
+  /*!
+   * allocated size of the memory block containing the \ref pntrString,
+   * excluding any hidden administrative data.
+   * Note: this is the number of bytes, not elements!  Fixed to the size of a
+   * single void* instance.
+   */
+  long allocSize;
+  /*!
+   * currently used size of the memory block containing the \ref pntrString,
+   * excluding any hidden administrative data.
+   * Note: this is the number of bytes, not elements!  Fixed to the size of a
+   * single pointer element.
+   */
+  long actualSize;
+  /*!
+   * memory for a single void* instance, set and fixed to the null pointer.
+   * A null marks the end of the array.
+   */
+  pntrString nullElement;
+};
+
+/*!
+ * \var g_PntrNull
+ * Global instance of a memory block structured like a
+ * \ref pntrString, fixed in size and containing always exactly one null pointer
+ * element, the terminating NULL.  This setup is recognized as an empty
+ * \ref pntrString.
+ *
+ * \attention mark as const
+ */
+extern struct nullPntrStruct g_PntrNull;
+
+/*!
+ * \def NULL_PNTRSTRING
+ * The address of a \ref pgBlock "block" containing an empty, not resizable
+ * \ref pntrString
+ * stack.  Used to initialize \ref pntrString variables .
+ */
+#define NULL_PNTRSTRING &(g_PntrNull.nullElement)
+
+/*!
+ * \def pntrString_def
+ *
+ * declare a new \ref pntrString variable and initialize it to point to a block
+ * with an empty, not resizable \ref pntrString.
+ *
+ * \param[in] x variable name
+ * \pre The variable does not exist in the current scope.
+ * \post The variable is initialized.
+ */
+#define pntrString_def(x) pntrString *x = NULL_PNTRSTRING
+
+/*!
+ * \def free_pntrString
+ * \param[in,out] x variable name
+ * Assigns \ref NULL_PNTRSTRING to a variable \ref pntrString \p x.  Frees all
+ * \ref pntrTempAllocStack, beginning with index 
+ * \ref g_pntrStartTempAllocStack.  See \ref pntrLet.
+ * \pre
+ *   - the \ref pgBlock assigned to \p x does not contain any valuable data.
+ *   - all \ref pntrString elements freed in \ref pntrTempAllocStack can be
+ *     discarded without losing relevant references.
+ * \post
+ *   - \p x is assigned NULL_PNTRSTRING.
+ *   - The stack pointer of \ref pntrTempAllocStack is reset to
+ *     \ref g_pntrStartTempAllocStack and all referenced
+ *     \ref pgBlock "blocks" on and beyond that are returned to the
+ *     \ref memFreePool.
+ *   - updates \ref db3 and \ref poolTotalFree.
+ *   - Exit on out-of-memory
+ */
+#define free_pntrString(x) pntrLet(&x, NULL_PNTRSTRING)
+
+
+/*! This function returns a 1 if any entry in a comma-separated list
+   matches using the matches() function. */
+flag matchesList(const char *testString, const char *pattern, char wildCard,
+    char oneCharWildCard);
+
+/*! This function returns a 1 if the first argument matches the pattern of
+   the second argument.  The second argument may have 0-or-more and
+   exactly-1 character match wildcards, typically '*' and '?'.*/
+flag matches(const char *testString, const char *pattern, char wildCard,
+    char oneCharWildCard);
+
+
+
+/*******************************************************************/
+/*********** Number string functions *******************************/
+/*******************************************************************/
+
+/******* Special purpose routines for better
+      memory allocation (use with caution) *******/
+
+extern long g_nmbrTempAllocStackTop;   /* Top of stack for nmbrTempAlloc function */
+extern long g_nmbrStartTempAllocStack; /* Where to start freeing temporary
+    allocation when nmbrLet() is called (normally 0, except for nested
+    nmbrString functions) */
+
+/*! \brief Make string have temporary allocation to be released by next nmbrLet()
+  \warning after nmbrMakeTempAlloc() is called, the nmbrString may NOT be
+    assigned again with nmbrLet() */
+temp_nmbrString *nmbrMakeTempAlloc(nmbrString *s);
+                                    /* Make string have temporary allocation to be
+                                    released by next nmbrLet() */
+
+/**************************************************/
+
+
+/*! String assignment - MUST be used to assign vstrings */
+void nmbrLet(nmbrString **target, const nmbrString *source);
+
+/*! String concatenation - last argument MUST be NULL */
+temp_nmbrString *nmbrCat(const nmbrString *string1,...);
+
+/*! Emulation of nmbrString functions similar to BASIC string functions */
+temp_nmbrString *nmbrSeg(const nmbrString *sin, long p1, long p2);
+temp_nmbrString *nmbrMid(const nmbrString *sin, long p, long l);
+temp_nmbrString *nmbrLeft(const nmbrString *sin, long n);
+temp_nmbrString *nmbrRight(const nmbrString *sin, long n);
+
+/*! Allocate and return an "empty" string n "characters" long */
+temp_nmbrString *nmbrSpace(long n);
+
+long nmbrLen(const nmbrString *s);
+long nmbrAllocLen(const nmbrString *s);
+void nmbrZapLen(nmbrString *s, long length);
+
+/*! Search for string2 in string 1 starting at start_position */
+long nmbrInstr(long start, const nmbrString *sin, const nmbrString *s);
+
+/*! Search for string2 in string 1 in reverse starting at start_position
+ * (Reverse nmbrInstr)
+ */
+long nmbrRevInstr(long start_position, const nmbrString *string1,
+   const nmbrString *string2);
+
+/*! 1 if strings are equal, 0 otherwise */
+flag nmbrEq(const nmbrString *s, const nmbrString *t);
+
+/*! Converts mString to a vstring with one space between tokens */
+temp_vstring nmbrCvtMToVString(const nmbrString *s);
+
+/*! Converts rString to a vstring with one space between tokens */
+temp_vstring nmbrCvtRToVString(const nmbrString *s,
+    flag explicitTargets,
+    long statemNum);
+
+/*! Get step numbers in an rString - needed by cvtRToVString & elsewhere */
+nmbrString *nmbrGetProofStepNumbs(const nmbrString *reason);
+
+/*! Converts any nmbrString to an ASCII string of numbers corresponding
+   to the .tokenNum field -- used for debugging only. */
+temp_vstring nmbrCvtAnyToVString(const nmbrString *s);
+
+/*! Extract variables from a math token string */
+temp_nmbrString *nmbrExtractVars(const nmbrString *m);
+
+/*! Determine if an element is in a nmbrString; return position if it is */
+long nmbrElementIn(long start, const nmbrString *g, long element);
+
+/*! Add a single number to end of a nmbrString - faster than nmbrCat */
+temp_nmbrString *nmbrAddElement(const nmbrString *g, long element);
+
+/*! Get the set union of two math token strings (presumably
+   variable lists) */
+temp_nmbrString *nmbrUnion(const nmbrString *m1, const nmbrString *m2);
+
+/*! Get the set intersection of two math token strings (presumably
+   variable lists) */
+temp_nmbrString *nmbrIntersection(const nmbrString *m1, const nmbrString *m2);
+
+/*! Get the set difference m1-m2 of two math token strings (presumably
+   variable lists) */
+temp_nmbrString *nmbrSetMinus(const nmbrString *m1,const nmbrString *m2);
+
+
+
+/*! This is a utility function that returns the length of a subproof that
+   ends at step */
+long nmbrGetSubproofLen(const nmbrString *proof, long step);
+
+/*! This function returns a "squished" proof, putting in {} references
+   to previous subproofs. */
+temp_nmbrString *nmbrSquishProof(const nmbrString *proof);
+
+/*! This function un-squishes a "squished" proof, replacing {} references
+   to previous subproofs by the subproofs themselves. */
+temp_nmbrString *nmbrUnsquishProof(const nmbrString *proof);
+
+/*! This function returns the indentation level vs. step number of a proof
+   string.  This information is used for formatting proof displays.  The
+   function calls itself recursively, but the first call should be with
+   startingLevel = 0. */
+temp_nmbrString *nmbrGetIndentation(const nmbrString *proof,
+  long startingLevel);
+
+/*! This function returns essential (1) or floating (0) vs. step number of a
+   proof string.  This information is used for formatting proof displays.  The
+   function calls itself recursively, but the first call should be with
+   startingLevel = 0. */
+temp_nmbrString *nmbrGetEssential(const nmbrString *proof);
+
+/*! This function returns the target hypothesis vs. step number of a proof
+   string.  This information is used for formatting proof displays.  The
+   function calls itself recursively.
+   statemNum is the statement being proved. */
+temp_nmbrString *nmbrGetTargetHyp(const nmbrString *proof, long statemNum);
+
+/*!
+ * Converts a proof string to a compressed-proof-format ASCII string.
+ * Normally, the proof string would be compacted with squishProof first,
+ * although it's not a requirement.
+ *
+ * The statement number is needed because required hypotheses are
+ * implicit in the compressed proof.
+ */
+temp_vstring compressProof(const nmbrString *proof, long statemNum,
+    flag oldCompressionAlgorithm);
+
+/*! Gets length of the ASCII form of a compressed proof */
+long compressedProofSize(const nmbrString *proof, long statemNum);
+
+
+/*******************************************************************/
+/*********** Pointer string functions ******************************/
+/*******************************************************************/
+
+/******* Special purpose routines for better
+      memory allocation (use with caution) *******/
+
+/*!
+ * \var long g_pntrTempAllocStackTop
+ *
+ * Index of the current top of the \ref pgStack "stack" \ref pntrTempAlloc.
+ * New data is pushed from this location on if space available.
+ *
+ * \invariant always refers the null pointer element behind the valid data.
+ */
+extern long g_pntrTempAllocStackTop;   /* Top of stack for pntrTempAlloc function */
+
+/*!
+ * \var long g_pntrStartTempAllocStack
+ *
+ * Index of the first entry of the \ref pgStack "stack" \ref pntrTempAllocStack
+ * eligible for deallocation on the next call to \ref pntrTempAlloc.  Entries
+ * below this value are considered not dependent on the value at this index,
+ * but entries above are.  So when this entry gets deallocated, dependent ones
+ * should follow suit.  A function like \ref pntrTempAlloc or \ref pntrLet
+ * manage this automatic deallocation.
+ *
+ * Nested functions using the \ref pntrTempAllocStack usually save the current
+ * value and set it to \ref g_pntrTempAllocStackTop, so they can create their
+ * local dependency chain.  On return the saved value is restored.
+ * \invariant always less or equal to \ref g_pntrTempAllocStackTop.
+ */
+extern long g_pntrStartTempAllocStack; /* Where to start freeing temporary
+    allocation when pntrLet() is called (normally 0, except for nested
+    pntrString functions) */
+
+/*!
+ * \brief Make string have temporary allocation to be released by next pntrLet()
+ * \warning after pntrMakeTempAlloc() is called, the pntrString may NOT be
+ *   assigned again with pntrLet()
+ */
+temp_pntrString *pntrMakeTempAlloc(pntrString *s);
+
+/**************************************************/
+
+
+/*!
+ * \fn void pntrLet(pntrString **target, const pntrString *source)
+ * String assignment - MUST be used to assign vstrings.
+ *
+ * Copies the \ref pntrString elements of \p source to the beginning of a
+ * \ref pgBlock referenced by \p target.  If necessary, the \p target block is
+ * reallocated, and if it is, it gets twice the needed size to account for
+ * future growing.  If the \p target block is only partially used after copy it
+ * is added to the \ref memUsedPool.  If \p source is empty, the \p target is
+ * set to \ref NULL_PNTRSTRING.
+ *
+ * It is assumed that the value persisted in \p target is in fact computed from
+ * temporary operands in \ref pntrTempAllocStack.  All blocks starting with
+ * the element at \ref g_pntrStartTempAllocStack are returned to the
+ * \ref memFreePool.
+ * \attention freed \ref pgBlock "blocks" contain \ref pntrString instances.
+ *   See \ref pntrTempAllocStack to learn how this free process can be
+ *   dangerous if insufficient precautions are taken.
+ * \param[in,out] target (not null) the address of a pointer pointing to the
+ *   first byte of a \ref pgBlock receiving the copied elements of \p source.
+ * \param[in] source (not null) a pointer to the first \ref pntrString element
+ *   in a \ref pgBlock, to be copied from.
+ * \pre
+ *   - source does not contain NULL pointer elements , but is terminated by
+ *     one.  This final NULL pointer is not part of the array, but must be present.
+ *   - the target \ref pgBlock does not contain any valuable data.
+ *   - all \ref pntrString elements freed in \ref pntrTempAllocStack can be
+ *     discarded without losing relevant references.
+ * \post
+ *   - the \ref pgBlock \p target points to is filled with a copy of
+ *     \ref pntrString elements \p source points to, padded with a terminal
+ *     NULL.
+ *   - due to a possible reallocation the pointer \p target points to may
+ *     change.
+ *   - The stack pointer \ref g_pntrTempAllocStackTop of
+ *     \ref pntrTempAllocStack is reset to \ref g_pntrStartTempAllocStack and
+ *     all referenced \ref pgBlock "blocks" on and beyond that are returned to
+ *     the \ref memFreePool.
+ *   - updates \ref db3 and \ref poolTotalFree.
+ *   - Exit on out-of-memory
+ * \bug If the \p target block is full after the copy operation, it is not
+ *   necessarily removed from the \ref memUsedPool, although other
+ *   functions like \ref addToUsedPool do not support this.
+ */
+void pntrLet(pntrString **target, const pntrString *source);
+
+/*! String concatenation - last argument MUST be NULL */
+temp_pntrString *pntrCat(const pntrString *string1,...);
+
+/* Emulation of pntrString functions similar to BASIC string functions */
+temp_pntrString *pntrSeg(const pntrString *sin, long p1, long p2);
+temp_pntrString *pntrMid(const pntrString *sin, long p, long length);
+temp_pntrString *pntrLeft(const pntrString *sin, long n);
+temp_pntrString *pntrRight(const pntrString *sin, long n);
+
+/*! Allocate and return an "empty" string n "characters" long */
+temp_pntrString *pntrSpace(long n);
+
+/*! Allocate and return an "empty" string n "characters" long
+   initialized to nmbrStrings instead of vStrings */
+temp_pntrString *pntrNSpace(long n);
+
+/*! Allocate and return an "empty" string n "characters" long
+   initialized to pntrStrings instead of vStrings */
+temp_pntrString *pntrPSpace(long n);
+
+/*!
+ * \fn long pntrLen(const pntrString *s)
+ * \brief Determine the length of a pntrString held in a \ref pgBlock "block"
+ * dedicated to it.
+ *
+ * returns the number of **reserved** pointers in the array pointed to by \p s,
+ * derived solely from administrative data in the surrounding \ref pgBlock.
+ * Thus, the value is valid, even if data has not yet been transferred to the
+ * reserved space, and the terminal NULL is not safely recognized. The returned
+ * value excludes the space set aside for a terminal NULL.
+ *
+ * \attention This is not the capacity of the array.
+ * \param[in] s points to a element 0 of a \ref pntrString  embedded in a block
+ * \return the number of pointers currently in use in the array pointed to by \p s.
+ * \pre the array pointed to by s is the sole user of a \ref pgBlock "block".
+ */
+long pntrLen(const pntrString *s);
+
+/*!
+ * \fn long pntrAllocLen(const pntrString *s)
+ * \brief Determine the capacity of a pntrString embedded in a dedicated block
+ *
+ * returns the capacity of pointers in the array pointed to by \p s,
+ * derived from administrative data in the surrounding block.  The result
+ * excludes the terminal element reserved for a null pointer.
+ *
+ * \param[in] s points to element 0 of a \ref pntrString embedded in a block
+ * \return the maximal number of pointers that can be used in the array pointed
+ * to by \p s.
+ * \pre the array pointed to by s is the sole user of a \ref pgBlock "block".
+ */
+long pntrAllocLen(const pntrString *s);
+void pntrZapLen(pntrString *s, long length);
+
+/*! Search for string2 in string 1 starting at start_position */
+long pntrInstr(long start, const pntrString *sin, const pntrString *s);
+
+/*! Search for string2 in string 1 in reverse starting at start_position
+    (Reverse pntrInstr) */
+long pntrRevInstr(long start_position, const pntrString *string1,
+    const pntrString *string2);
+
+/*! 1 if strings are equal, 0 otherwise */
+flag pntrEq(const pntrString *sout, const pntrString *sin);
+
+/*!
+ * \fn temp_pntrString *pntrAddElement(const pntrString *g)
+ * Add a single null string element to a pntrString - faster than pntrCat
+ *
+ * \param[in] g points to the first element of a NULL terminated array in a
+ *   \ref pgBlock.  It is assumed it is an array of pointer to \ref vstring.
+ * \return a copy of \p g, the terminal NULL replaced with a \ref vstring ""
+ *   followed by NULL.
+ * \attention   
+ *   the pointers in \p g are copied to the result.  If some of them
+ *   reference allocated memory, check for possible double free, for example.
+ * \pre
+ *   Intended to be used with arrays of \ref vstring * only.
+ * \post 
+ *   the elements of \p g are duplicated.
+ */
+temp_pntrString *pntrAddElement(const pntrString *g);
+
+/*! Add a single null pntrString element to a pntrString - faster than pntrCat */
+temp_pntrString *pntrAddGElement(const pntrString *g);
+
+/* Utility functions */
+
+/*! 0/1 knapsack algorithm */
+long knapsack01(long items, long *size, long *worth, long maxSize,
+       char *itemIncluded /* output: 1 = item included, 0 = not included */);
+
+/*! 2D matrix allocation */
+long **alloc2DMatrix(size_t xsize, size_t ysize);
+/*! 2D matrix deallocation */
+void free2DMatrix(long **matrix, size_t xsize /*, size_t ysize*/);
+
+/*! Returns the amount of indentation of a statement label.  Used to
+   determine how much to indent a saved proof. */
+long getSourceIndentation(long statemNum);
+
+/*! Returns any comment (description) that occurs just before a statement */
+vstring getDescription(long statemNum);
+
+/*! Returns the label section of a statement with all comments except the
+   last removed. */
+vstring getDescriptionAndLabel(long statemNum);
+
+/*! Returns 1 if comment has an "is discouraged" markup tag */
+flag getMarkupFlag(long statemNum, char mode);
+
+/*! Extract any contributors and dates from statement description.
+   If missing, the corresponding return string is blank.
+   See GC_RESET etc. modes above.  Caller must deallocate returned
+   string. */
+vstring getContrib(long stmtNum, char mode);
+
+
+/*! Extract up to 2 dates after a statement's proof.  If no date is present,
+   date1 will be blank.  If no 2nd date is present, date2 will be blank. */
+void getProofDate(long stmtNum, vstring *date1, vstring *date2);
+
+/*! Get date, month, year fields from a dd-mmm-yyyy date string,
+   where dd may be 1 or 2 digits, mmm is 1st 3 letters of month,
+   and yyyy is 2 or 4 digits.  A 1 is returned if an error was detected. */
+flag parseDate(vstring dateStr, long *dd, long *mmm, long *yyyy);
+
+/*! Build date from numeric fields.  mmm should be a number from 1 to 12. */
+void buildDate(long dd, long mmm, long yyyy, vstring *dateStr);
+
+/*! Compare two dates in the form dd-mmm-yyyy.  -1 = date1 < date2,
+   0 = date1 = date2,  1 = date1 > date2.  There is no error checking. */
+flag compareDates(vstring date1, vstring date2);
+
+extern vstring g_qsortKey;
+/*!
+ * \brief Comparison function for qsort
+ * \note Used by qsortStringCmp; pointer only, do not deallocate
+ */
+int qsortStringCmp(const void *p1, const void *p2);
+
+/*! Call on exit to free memory */
+void freeData(void);
+
+#endif /* METAMATH_MMDATA_H_ */
diff --git a/mmhlpa.c b/src/mmhlpa.c
similarity index 94%
rename from mmhlpa.c
rename to src/mmhlpa.c
index 71b745d..14189c0 100644
--- a/mmhlpa.c
+++ b/src/mmhlpa.c
@@ -1,1178 +1,1146 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* Part 1 of help file for Metamath */
-/* The content here was split into help0() and help1() because the original
-   help() overflowed the lcc compiler (at least before version 3.8; not
-   tested with 3.8 and above). */
-/* To add a new help entry, you must add the command syntax to mmcmdl.c
-   as well as adding it here. */
-
-#include <string.h>
-#include <stdio.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mmcmds.h"
-#include "mmhlpa.h"
-
-/* help0 is mostly for TOOLS help */
-void help0(vstring helpCmd)
-{
-
-/* 5-Sep-2012 nm */
-vstring saveHelpCmd = "";
-/* help0() may be called with a temporarily allocated argument (left(),
-   cat(), etc.), and the let()s in the eventual print2() calls will
-   deallocate and possibly corrupt helpCmd.  So, we grab a non-temporarily
-   allocated copy here.  (And after this let(), helpCmd will become invalid
-   for the same reason.)  */
-let(&saveHelpCmd, helpCmd);
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP");
-H("This utility assists with some common file manipulations.");
-H("Most commands will perform an identical operation on each line of a file.");
-H("Use HELP ? to see list of help topics.");
-H("Note:  When an output file is created, any previous version is renamed,");
-H(
- "with ~1 appended, and any ~1 renamed to ~2, etc. (up to ~9, which is lost).");
-H("Note:  All string-matching command arguments are case-sensitive.");
-H("");
-H("Line-by-line editing commands:");
-H("  ADD - Add a specified string to each line in a file");
-H("  CLEAN - Trim spaces and tabs on each line in a file; convert characters");
-H("  DELETE - Delete a section of each line in a file");
-H("  INSERT - Insert a string at a specified column in each line of a file");
-H("  SUBSTITUTE - Make a simple substitution on each line of the file");
-H("  TAG - Like ADD, but restricted to a range of lines");
-/*H("  LSUBSTITUTE - Substitute according to a match-and-substitute list");*/
-H("  SWAP - Swap the two halves of each line in a file");
-H("Other file processing commands:");
-H("  BREAK - Break up (parse) a file into a list of tokens (one per line)");
-H("  BUILD - Build a file with multiple tokens per line from a list");
-H("  COUNT - Count the occurrences in a file of a specified string");
-/*H("  FORMAT - Produce a formatted list of tokens for documentation");*/
-H("  NUMBER - Create a list of numbers");
-H("  PARALLEL - Put two files in parallel");
-H("  REVERSE - Reverse the order of the lines in a file");
-H("  RIGHT - Right-justify lines in a file (useful before sorting numbers)");
-H("  SORT - Sort the lines in a file with key starting at specified string");
-H("  MATCH - Extract lines containing (or not) a specified string");
-/*H("  LEXTRACT - Extract lines containing (or not) strings from a list");*/
-H("  UNDUPLICATE - Eliminate duplicate occurrences of lines in a file");
-H("  DUPLICATE - Extract first occurrence of any line occurring more than");
-H("      once in a file, discarding lines occurring exactly once");
-H("  UNIQUE - Extract lines occurring exactly once in a file");
-H(
-"  (UNDUPLICATE, DUPLICATE, and UNIQUE also sort the lines as a side effect.)");
-H("  UPDATE (deprecated) - Update a C program for revision control");
-H("  TYPE (10 lines) - Display 10 lines of a file; similar to Unix \"head\"");
-/*H("  COPY, RENAME - Similar to Unix cat, mv but with backups created");*/
-H(
-"  COPY - Similar to Unix \"cat\" but safe (same input & output name allowed)");
-H("  SUBMIT - Run a script containing Tools commands.");
-H("");
-/* 3-Jun-2016 nm Reorganize a little */
-H("Command syntax ([] means optional):");
-H("  From TOOLS prompt:  TOOLS> <command> [<arg1> <arg2>...]");
-/*
-if (listMode) {
-H("  From VMS shell:  $ DO TOOLS [<command>] [<arg1> <arg2>...]");
-H("  From Unix/DOS shell:  tools [<command>] [<arg1> <arg2>...]");
-}
-*/
-H("You need to type only as many characters of the command as are needed to");
-H("uniquely specify it.  Any arguments will answer questions automatically");
-H("until the argument list is exhausted; the remaining questions will be");
-H("prompted.  An argument may be optionally enclosed in quotes.  Use \"\" for");
-H("default or null argument.");
-H("");
-H("Notes:");
-H("(1) The commands are not case sensitive.  File names and match strings");
-H("are case sensitive.");
-/*
-H("(2) Output files are created only after a command finishes running.");
-H("Therefore it is usually safe to hit ^C before a command is completed.");
-H("(3) The file \"zztools.tmp\", which is always created, can be used as a");
-H("command file to re-run the command sequence with the SUBMIT command.");
-*/
-H("(2) Previous versions of output files (except under VMS) are renamed with");
-H("~1 (most recent), ~2,...,~9 (oldest) appended to file name.  You may want");
-H("to purge them periodically.");
-H("(3) The command B(EEP) will make the terminal beep.  It can be useful to");
-H("type it ahead to let you know when the current command is finished.");
-/*
-H("(6) It is suggested you use a \".tmp\" file extension for intermediate");
-H("results to eliminate directory clutter.");
-*/
-H("");
-/*
-H("Please see NDM if you have any suggestions for this program.");
-*/
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP ADD");
-H("This command adds a character string prefix and/or suffix to each");
-H("line in a file.");
-H("Syntax:  ADD <iofile> <begstr> <endstr>");
-
-/* 2-Jul-2011 nm Added TAG command */
-g_printHelp = !strcmp(saveHelpCmd, "HELP TAG");
-H("TAG is the same as ADD but has 4 additional arguments that let you");
-H("specify a range of lines.  Syntax:");
-H("  TAG <iofile> <begstr> <endstr> <startmatch> <s#> <endmatch> <e#>");
-H("where");
-H("  <iofile> = input/output file");
-H("  <begstr> = string to add to beginning of each line");
-H("  <endstr> = string to add to end of each line");
-H("  <startmatch> = a string to match; if empty, match any line");
-H("  <s#> = the 1st, 2nd, etc. occurrence of <startmatch> to start the range");
-H("  <endmatch> = a string to match; if empty, match any line");
-H("  <e#> = the 1st, 2nd, etc. occurrence of <endmatch> from the");
-H("      start of range line (inclusive) after which to end the range");
-H("Example:  To add \"!\" to the end of lines 51 through 60 inclusive:");
-H("  TAG \"a.txt\" \"\" \"!\" \"\" 51 \"\" 10");
-H("Example:  To add \"@@@\" to the beginning of each line in theorem");
-H("\"abc\" through the end of its proof:");
-H("  TAG \"set.mm\" \"@@@\" \"\" \"abc $p\" 1 \"$.\" 1");
-H("so that later, SUBSTITUTE can be used to affect only those lines.  You");
-H("can remove the \"@@@\" tags with SUBSTITUTE when done.");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP DELETE");
-H("This command deletes the part of a line between (and including) the first");
-H("occurrence of <startstr> and the first occurrence of <endstr> (when both");
-H("exist) for all lines in a file.  If either string doesn't exist in a line,");
-H("the line will be unchanged.  If <startstr> is blank (''), the deletion");
-H("will start from the beginning of the line.  If <endstr> is blank, the");
-H("deletion will end at the end of the line.");
-H("Syntax:  DELETE <iofile> <startstr> <endstr>");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP CLEAN");
-H("This command processes spaces and tabs in each line of a file");
-H("according to the following subcommands:");
-H("  D - Delete all spaces and tabs");
-H("  B - Delete spaces and tabs at the beginning of each line");
-H("  E - Delete spaces and tabs at the end of each line");
-H("  R - Reduce multiple spaces and tabs to one space");
-H("  Q - Do not alter characters in quotes (ignored by T and U)");
-H("  T - (Tab) Convert spaces to equivalent tabs");
-H("  U - (Untab) Convert tabs to equivalent spaces");
-H("Some other subcommands are also available:");
-H("  P - Trim parity (8th) bit from each character");
-H("  G - Discard garbage characters CR,FF,ESC,BS");
-H("  C - Convert to upper case");
-H("  L - Convert to lower case");
-H("  V - Convert VT220 screen print frame graphics to -,|,+ characters");
-H("Subcommands may be joined with commas (but no spaces), e.g., \"B,E,R,Q\"");
-H("Syntax:  CLEAN <iofile> <subcmd,subcmd,...>");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SUBSTITUTE")
-    || !strcmp(helpCmd, "HELP S");
-H("This command performs a simple string substitution in each line of a file.");
-H("If the string to be replaced is \"\\n\", then every other line will");
-H("be joined to the one below it.  If the replacement string is \"\\n\", then");
-H("each line will be split into two if there is a match.");
-H("The <matchstr> specifies a string that must also exist on a line");
-H("before the substitution takes place; null means match any line.");
-H("The <occurrence> is an integer (1 = first occurrence on each line, etc.)");
-H("or A for all occurrences on each line.");
-H("Syntax:  SUBSTITUTE <iofile> <oldstr> <newstr> <occurrence> <matchstr>");
-H("Note: The SUBSTITUTE command may be abbreviated by S.");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SWAP");
-H("This command swaps the parts of each line before and after a");
-H("specified string.");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP INSERT");
-H("This command inserts a string at a specified column in each line");
-H("in a file.  It is intended to aid further processing of column-");
-H("sensitive files.  Note: the index of the first column is 1, not 0.  If a");
-H("line is shorter than <column>, then it is padded with spaces so that");
-H("<string> is still added at <column>.");
-H("Syntax:  INSERT <iofile> <string> <column>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP BREAK");
-H("This command breaks up a file into tokens, one per line, breaking at");
-H("whitespace and any special characters you specify as delimiters.");
-/* 3-Jul-2020 nm Added: */
-H("Use an explicit (quoted) space as <specchars> to avoid the default");
-H("special characters and break only on whitespace.");
-H("Syntax:  BREAK <iofile> <specchars>");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP BUILD");
-H("This command combines a list of tokens into multiple tokens per line,");
-H("as many as will fit per line, separating them with spaces.");
-H("Syntax:  BUILD <iofile>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP MATCH");
-H("This command extracts from a file those lines containing (Y) or not");
-H("containing (N) a specified string.");
-H("Syntax:  MATCH <iofile> <matchstr> <Y/N>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SORT");
-H("This command sorts a file, comparing lines starting at a key string.");
-H("If the key string is blank, the line is compared starting at column 1.");
-H("If a line doesn't contain the key, it is compared starting at column 1.");
-H("Syntax:  SORT <iofile> <key>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP UNDUPLICATE");
-H("This command sorts a file then removes any duplicate lines from the output.");
-H("Syntax:  UNDUPLICATE <iofile>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP DUPLICATE");
-H("This command finds all duplicate lines in a file and places them, in");
-H("sorted order, into the output file.");
-H("Syntax:  DUPLICATE <iofile>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP UNIQUE");
-H("This command finds all unique lines in a file and places them, in");
-H("sorted order, into the output file.");
-H("Syntax:  UNIQUE <iofile>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP REVERSE");
-H("This command reverses the order of the lines in a file.");
-H("Syntax:  REVERSE <iofile>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP RIGHT");
-H("This command right-justifies the lines in a file by putting spaces in");
-H("front of them so that they end in the same column as the longest line");
-H("in the file.");
-H("Syntax:  RIGHT <iofile>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP PARALLEL");
-H("This command puts two files side-by-side.");
-H("The two files should have the same number of lines; if not, a warning is");
-H("issued and the longer file paralleled with empty strings at the end.");
-H("Syntax:  PARALLEL <inpfile1> <inpfile2> <outfile> <btwnstr>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP NUMBER");
-H("This command creates a list of numbers.  Hint:  Use the RIGHT command to");
-H("right-justify the list after creating it.");
-H("Syntax:  NUMBER <outfile> <first> <last> <incr>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP COUNT");
-H("This command counts the occurrences of a string in a file and displays");
-H("some other statistics about the file.  The sum of the lines is obtained");
-H("by extracting digits and is only valid if the file consists of genuine");
-H("numbers.");
-H("Syntax:  COUNT <inpfile> <string>");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP TYPE") || !strcmp(helpCmd, "HELP T");
-H("This command displays (i.e. types out) the first n lines of a file on the");
-H("terminal screen.  If n is not specified, it will default to 10.  If n is");
-H("the string \"ALL\", then the whole file will be typed.");
-H("Syntax:  TYPE <inpfile> <n>");
-H("Note: The TYPE command may be abbreviated by T.");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP COPY") || !strcmp(helpCmd, "HELP C");
-H("This command copies (concatenates) all input files in a comma-separated");
-H("list (no blanks allowed) to an output file.  The output file may have");
-H("the same name as an input file.  Any previous version of the output");
-H("file is renamed with a ~1 extension.");
-H("Example: \"COPY 1.tmp,1.tmp,2.tmp 1.tmp\" followed by \"UNIQUE 1.tmp\"");
-H("will result in 1.tmp containing those lines of 2.tmp that didn't");
-H("previously exist in 1.tmp.");
-H("Syntax:  COPY <inpfile,inpfile,...> <outfile>");
-H("Note: The COPY command may be abbreviated by C.");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP UPDATE");
-H("This command tags edits made to a program source.  The idea is to keep");
-H("all past history of a file in the file itself, in the form of comments.");
-H("UPDATE was written for a proprietary language that allowed nested C-style");
-H("comments, and it may not be generally useful without some modification.");
-H("Essentially a (Unix) diff-like algorithm looks for changes between an");
-H("original and a revised file and puts the original lines into the revised");
-H("file in the form of comments.  Currently it is not well documented and it");
-H("may be easiest just to type UPDATE <return> and answer the questions.");
-H("Try it on an original and edited version of a test file to see if you");
-H("find it useful.");
-H("Syntax:  UPDATE <originfile> <editedinfile> <editedoutfile> <tag> <match>");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP CLI");
-H("Each command line is an English-like word followed by arguments separated");
-H("by spaces, as in SUBMIT abc.cmd.  Commands are not case sensitive, and");
-H("only as many letters are needed as are necessary to eliminate ambiguity;");
-H("for example, \"a\" would work for the command ADD.  Command arguments");
-H("which are file names and match strings are case-sensitive (although file");
-H("names may not be on some operating systems).");
-H("");
-H("A command line is entered typing it in then pressing the <return> key.");
-H("");
-H("To find out what commands are available, type ? at the \"TOOLS>\" prompt.");
-H("");
-H("To find out the choices at any point in a command, press <return> and you");
-H("will be prompted for them.  The default choice (the one selected if you");
-H("just press <return>) is shown in brackets (<>).");
-H("");
-H("You may also type ? in place of a command word to tell");
-H("you what the choices are.  The ? method won't work, though, if a");
-H("non-keyword argument such as a file name is expected at that point,");
-H("because the CLI will think the ? is the argument.");
-H("");
-H("Some commands have one or more optional qualifiers which modify the");
-H("behavior of the command.  Qualifiers are indicated by a slash (/), such as");
-H("in ABC xyz / IJK.  Spaces are optional around the /.  If you need");
-H("to use a slash in a command argument, as in a Unix file name, put single");
-H("or double quotes around the command argument.");
-H("");
-H("If the response to a command is more than a screenful, you will be");
-H("prompted to \"<return> to continue, Q to quit, or S to scroll to end\".");
-H("Q will complete the command internally but suppress further output until");
-H("the next \"TOOLS>\" prompt.  S will suppress further pausing until the next");
-H("\"TOOLS>\" prompt.");
-H("");
-H("A command line enclosed in quotes is executed by your operating system.");
-H("See HELP SYSTEM.");
-H("");
-H("Some other commands you may want to review with HELP are:");
-H("    SUBMIT");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SUBMIT");
-H("Syntax:  SUBMIT <filename> [/ SILENT]");
-H("");
-H("This command causes further command lines to be taken from the specified");
-H("file.  Note that any line beginning with an exclamation point (!) is");
-H("treated as a comment (i.e. ignored).  Also note that the scrolling");
-H("of the screen output is continuous.");
-H("");
-H("Optional qualifier:");
-H("    / SILENT - This qualifier suppresses the screen output of the SUBMIT");
-H("        command.");
-H("");
-H("SUBMIT can be called recursively, i.e., SUBMIT commands are allowed");
-H("inside of a command file.");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SYSTEM");
-H("A line enclosed in single or double quotes will be executed by your");
-H("computer's operating system, if it has such a feature.  For example, on a");
-H("Unix system,");
-H("    Tools> 'ls | more'");
-H("will list disk directory contents.  Note that this feature will not work");
-H("on the pre-OSX Macintosh, which does not have a command line interface.");
-H("");
-H("For your convenience, the trailing quote is optional, for example:");
-H("    Tools> 'ls | more");
-H("");
-
-let(&saveHelpCmd, ""); /* Deallocate memory */
-
-return;
-} /* help0 */
-
-
-/* Note: help1 should contain Metamath help */
-void help1(vstring helpCmd)
-{
-
-/* 5-Sep-2012 nm */
-vstring saveHelpCmd = "";
-/* help1() may be called with a temporarily allocated argument (left(),
-   cat(), etc.), and the let()s in the eventual print2() calls will
-   deallocate and possibly corrupt helpCmd.  So, we grab a non-temporarily
-   allocated copy here.  (And after this let(), helpCmd will become invalid
-   for the same reason.)  */
-let(&saveHelpCmd, helpCmd);
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP CLI");
-H("The Metamath program was first developed on a VAX/VMS system, and some");
-H("aspects of its command line behavior reflect this heritage.  Hopefully");
-H(
-"you will find it reasonably user-friendly once you get used to it.");
-H("");
-H("Each command line is a sequence of English-like words separated by");
-H("spaces, as in SHOW SETTINGS.  Command words are not case sensitive, and");
-H("only as many letters are needed as are necessary to eliminate ambiguity;");
-H("for example, \"sh se\" would work for the command SHOW SETTINGS.  In some");
-H("cases arguments such as file names, statement labels, or symbol names are");
-H("required; these are case-sensitive (although file names may not be on");
-H("some operating systems).");
-H("");
-H("A command line is entered by typing it in then pressing the <return> key.");
-H("");
-H("To find out what commands are available, type ? at the MM> prompt,");
-H("followed by <return>. (This is actually just a trick to force an error");
-H("message, since ? is not a legal command.)");
-H("");
-H("To find out the choices for the next argument for a command, press");
-H("<return> and you will be prompted for it.  The default choice (the one");
-H("selected if you just press <return>) is shown in brackets (<>).");
-H("");
-H("You may also type ? in place of a command word to force Metamath to tell");
-H("you what the choices are.  The ? method won't work, though, if a");
-H("non-keyword argument such as a file name is expected at that point,");
-H("because the CLI will think the ? is the argument.");
-H("");
-H("Some commands have one or more optional qualifiers that modify the");
-H("behavior of the command.  Qualifiers are indicated by a slash (/), such as");
-H("in READ set.mm / VERIFY.  Spaces are optional around / and =.  If you need");
-H("to use / or = in a command argument, as in a Unix file name, put single");
-H("or double quotes around the command argument.  See the last section of");
-H("HELP LET for more information on special characters in arguments.");
-H("");
-H("The OPEN LOG command will save everything you see on the screen, and is");
-H("useful to help you recover should something go wrong in a proof, or if");
-H("you want to document a bug.");
-H("");
-H("If the response to a command is more than a screenful, you will be");
-H("prompted to '<return> to continue, Q to quit, or S to scroll to end'.");
-H("Q will complete the command internally but suppress further output until");
-H("the next \"MM>\" prompt.  S will suppress further pausing until the next");
-H("\"MM>\" prompt.  After the first screen, you can also choose B to go back");
-H("a screenful.  Note that B may also be entered at the \"MM>\" prompt");
-H("immediately after a command to scroll back through the output of that");
-H("command.  Scrolling can be disabled with SET SCROLL CONTINUOUS.");
-H("");
-H("**Warning**  Pressing CTRL-C will abort the Metamath program");
-H("unconditionally.  This means any unsaved work will be lost.");
-H("");
-H("A command line enclosed in quotes is executed by your operating system.");
-H("See HELP SYSTEM.");
-H("");
-H("Some additional CLI-related features are explained by:");
-H("");
-H("    HELP SET ECHO");
-H("    HELP SET SCROLL");
-H("    HELP SET WIDTH");  /* 18-Nov-05 nm Was SCREEN_WIDTH */
-H("    HELP SET HEIGHT"); /* 18-Nov-05 nm New */
-H("    HELP SUBMIT");
-H("    HELP UNDO (or REDO) - in Proof Assistant only");  /* 21-Oct-2016 */
-H("");
-
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP LANGUAGE");
-H("The language is best learned by reading the book and studying a few proofs");
-H("with the Metamath program.  This is a brief summary for reference.");
-H("");
-H("The database contains a series of tokens separated by whitespace (spaces,");
-H("tabs, returns).  A token is a keyword, a <label>, or a <symbol>.");
-H("");
-H("The pure language keywords are:  $c $v $a $p $e $f $d ${ $} $. and $=");
-H("The auxiliary keywords are:  $( $) $[ and $]");
-H("This is the complete set of language keywords.");
-H("");
-H("<symbol>s and <label>s are user-defined.  <symbol>s may contain any");
-H("printable characters other than $ , and <label>s may contain alphanumeric");
-H("characters, periods, dashes and underscores.");
-H("");
-H("Scoping statements:");
-H("");
-H("  ${ - Start of scope.");
-H("       Syntax:  \"${\"");
-H("");
-H("  $} - End of scope:  all $v, $e, $f, and $d statements in the current");
-H("         scope become inactive.");
-H("       Syntax:  \"$}\"");
-H("");
-H("  Note that $a and $p statements remain active forever.  Note that $c's");
-H("  may be used only in the outermost scope, so they are always active.");
-H("  The outermost scope is not bracketed by ${ ... $} .  The scope of a $v,");
-H("  $e, $f, or $d statement starts where the statement occurs and ends with");
-H("  the $} that matches the previous ${.  The scope of a $c, $a, or $p");
-H("  statement starts where the statement occurs and ends at the end of the");
-H("  database.");
-H("");
-H("Declarations:");
-H("");
-H("  $c - Constant declaration.  The <symbol>s become active constants.");
-H("       Syntax:  \"$c <symbol> ... <symbol> $.\"");
-H("");
-H("  $v - Variable declaration.  The <symbols>s become active variables.");
-H("       Syntax:  \"$v <symbol> ... <symbol> $.\"");
-H("");
-H("Hypotheses:");
-H("");
-H("  $f - Variable-type (or \"floating\") hypothesis (meaning it is");
-H("         \"required\" by a $p or $a statement in its scope only if its");
-H("         variable occurs in the $p or $a statement or in the essential");
-H("         hypotheses of the $p or $a statement).  Every $d, $e, $p, and $a");
-H("         statement variable must have an earlier active $f statement to");
-H("         specify the variable type.  Non-required i.e. \"optional\" $f");
-H("         statements may be referenced inside a proof when dummy variables");
-H("         are needed by the proof.");
-H("       Syntax:  \"<label> $f <constant> <variable> $.\" where both symbols");
-H("         are active");
-H("");
-H("  $e - Logical (or \"essential\") hypothesis (meaning it is always");
-H("         required by a $p or $a statement in its scope)");
-H("       Syntax:  \"<label> $e <symbol> ... <symbol> $.\"  where the first");
-H("         (and possibly only) <symbol> is a constant");
-H("");
-H("Assertions:");
-H("");
-H("  $a - Axiomatic assertion (starting assertion; used for axioms,");
-H("         definitions, and language syntax specification)");
-H("       Syntax:  \"<label> $a <symbol> ... <symbol> $.\"  where the first");
-H("         (and possibly only) <symbol> is a constant");
-H("");
-H("  $p - Provable assertion (derived assertion; used for deductions and");
-H("         theorems; must follow from previous statements as demonstrated by");
-H("         its proof)");
-H("       Syntax:");
-H("         \"<label> $p <symbol> ... <symbol> $= <label> ... <label> $.\"");
-H("         where the first (and possibly only) <symbol> is a constant.");
-H("         \"$= <label> ... <label> $.\" is the proof; see the book for more");
-H("         information.  Proofs may be compressed for storage efficiency.  A");
-H("         compressed proof is a series of labels in parentheses followed by");
-H("         a string of capital letters; see book for compression algorithm.");
-H("         SAVE PROOF <label> / NORMAL will convert a compressed proof to");
-H("         its uncompressed form.");
-H("");
-H("  A substitution is the replacement of a variable with a <symbol> string");
-H("  throughout an assertion and its required hypotheses.  The required");
-H("  hypotheses are shown as the \"mandatory hypotheses\" listed by");
-H("  SHOW STATEMENT <label> / FULL.");
-H("");
-H("  In a proof, the label of a hypothesis ($e or $f) pushes the stack, and");
-H("  the label of an assertion ($a or $p) pops from the stack a number of");
-H("  entries equal to the number of the assertion's required hypotheses and");
-H("  replaces the stack's top.  Whenever an assertion is specified, a unique");
-H("  set of substitutions must exist that makes the assertion's hypotheses");
-H("  match the top entries of the stack.");
-H("");
-H("  To see a readable proof format, type SHOW PROOF <label>, where <label>");
-H("  is the label of a $p statement.  To see how substitutions are made in a");
-H("  proof step, type SHOW PROOF <label> / DETAILED_STEP <n>, where <n> is");
-H("  the step number from the SHOW PROOF <label> listing.");
-H("");
-H("Disjoint variable restriction:");
-H("");
-H("  The substitution of symbol strings into variables may be subject to a");
-H("  $d restriction:");
-H("");
-H("  $d - Disjoint variable restriction (meaning substitutions may not");
-H("         have variables in common)");
-H("       Syntax:  \"$d <symbol> ... <symbol> $.\" where <symbol> is active");
-H("         and previously declared with $v, and all <symbol>s are distinct");
-H("");
-H("Auxiliary keywords:");
-H("");
-H("  $(   Begin comment");
-H("  $)   End comment");
-H("       Markup in comments:");
-H("         ` <symbol> ` - use graphical <symbol> in LaTeX/HTML output;");
-H("             `` means literal `; several <symbol>s may occur inside");
-H("             ` ... ` separated by whitespace");
-H("         ~ <label> - use typewriter font (hyperlink) in LaTeX (HTML) output;");
-H("             if <label> begins with \"http://\", it is assumed to be");
-H("             a URL (which is used as-is, except a \"~\" in the URL should");
-H("             be specified as \"~~\") rather than a statement label (which");
-H("             will have \".html\" appended for the hyperlink); only $a and $p");
-H("             statement labels may be used, since $e, $f pages don't exist");
-H("         [<author>] - link to bibliography; see HELP HTML and HELP WRITE");
-H("             BIBLIOGRAPHY");
-H("         $t - flags comment as containing LaTeX and/or HTML typesetting");
-H("             definitions; see HELP LATEX or HELP HTML");
-H("         _ - Italicize text from <space>_<non-space> to");
-H("             <non-space>_<space>; normal punctuation (e.g. trailing");
-H("             comma) is ignored when determining <space>");
-H("         _ - <non-space>_<non-space-string> will make <non-space-string>");
-H("             a subscript");
-H("         <HTML> - A comment containing \"<HTML>\" (case-sensitive) is");
-H("             bypassed by the algorithm of SHOW PROOF ... / REWRAP.  Also,");
-H("             \"<\" is not converted to \"&lt;\" by the algorithm.  The");
-H("             \"<HTML>\" is discarded in the generated web page.  Any");
-H("             \"</HTML>\" (deprecated) is discarded and ignored.  Note that");
-H("             the entire comment (not just sections delineated by");
-H("             \"<HTML>...</HTML>\") is treated as HTML code if any");
-H("             \"<HTML>\" is present anywhere in the comment.");
-H("             See also HELP WRITE SOURCE for more information.");
-H("         (Contributed by <author>, <date>.)");
-H("         (Revised by <author>, <date>.)");
-H("         (Proof shortened by <author>, <date>.)");
-H("             The above dates are checked by VERIFY MARKUP.");
-H("         (New usage is discouraged.)");
-H("         (Proof modification is discouraged.)");
-H("             See HELP SHOW DISCOURAGED and HELP SET DISCOURAGEMENT.");
-H("       Note:  Comments may not be nested.");
-H("");
-H("  $[ <file-name> $] - place contents of file <file-name> here; a second,");
-H("       recursive, or self reference to a file is ignored");
-H("");
-
-
-/* 10-Dec-2018 nm */
-g_printHelp = !strcmp(saveHelpCmd, "HELP MARKUP");
-H("(See HELP VERIFY MARKUP for the markup language used in database");
-H("comments.)");
-H("");
-H("Syntax:  MARKUP <inpfile> <outfile> [/ HTML] [/ ALT_HTML] [/ SYMBOLS]");
-H("    [/ LABELS] [/ NUMBER_AFTER_LABEL] [/ BIBLIOGRAPHY] [/ UNDERSCORES]");
-H("    [/ CSS]");
-H("This command will read an arbitrary <inpfile>, normally an HTML file");
-H("with markup, treating it as if it were a giant comment in a database file");
-H("and translating any markup into HTML.  The translated result is written to");
-H("<outfile>.  Note that the file names may be enclosed in single or double");
-H("quotes; this is required if a file name contains slashes, as might be the");
-H("case with Unix file path names.");
-H("");
-H("This command requires that a database source file (such as set.mm) be");
-H("read.  See HELP READ. The math symbols and other information are taken");
-H("from that database.  The use of VERIFY MARKUP * is recommended to help");
-H("ensure the database has no errors in its symbol definitions.");
-H("");
-H("Qualifiers:");
-H("    / HTML (/ ALT_HTML) - use the symbols defined by the htmldef");
-H("        (althtmldef) statements in the $t comment in the .mm database.");
-H("        Usually these are GIF or Unicode math symbols respectively.");
-H("        Exactly one of / HTML and / ALT_HTML must always be specified.");
-H("    / SYMBOLS - process symbols inside backquotes.");
-H("    / LABELS - process labels preceded by tilde.");
-H("    / NUMBER_AFTER_LABEL - add colored statement number after each label.");
-H("    / BIB_REFS - process bibliographic references in square brackets.");
-H("        The file specified by htmlbibliography in the $t comment in the");
-H("        .mm database is checked to be sure the references exist.");
-H("    / UNDERSCORES - process underscores to produce italic text or");
-H("        subscripts.");
-H("    / CSS - add CSS before \"</HEAD>\" in the input file.  The CSS is");
-H("        specified by htmlcss in the $t comment in .mm database.  If");
-H("        \"</HEAD>\" is not present, or the CSS is already present (with");
-H("        an exact match), nothing will be done.");
-H("");
-H("Note:  The existence of GIF files for symbols isn't checked.  Use VERIFY");
-H("MARKUP for that.  However, validity of bibliographical references is");
-H("checked since VERIFY MARKUP can't do that.  If the required file for");
-H("/ BIB_REFS (such as mmset.html) isn't present, a warning will be");
-H("displayed.  To avoid literal \"`\", \"~\", and \"[\" from being");
-H("interpreted by / SYMBOLS, / LABELS, and / BIB_REFS respectively, escape");
-H("them with \"``\", \"~~\", and \"[[\" in the input file.  Literal \"`\"");
-H("must always be escaped even if / SYMBOLS is omitted, because the");
-H("algorithm will still use \"`...`\" to avoid interpreting special");
-H("characters in math symbols.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP EXPLORE");
-H("When you first enter Metamath, you will first want to READ in a Metamath");
-H("source file.  The source file provided for set theory is called set.mm;");
-H("to read it type");
-H("    READ set.mm");
-H("");
-H("You may want to look over the contents of the source file with a text");
-H("editor, to get an idea of what's in it, before starting to use Metamath.");
-H("");
-H("The following commands will help you study the source file statements");
-H("and their proofs.  Use the HELP for the individual commands to get");
-H("more information about them.");
-H("    SEARCH <label-match> \"<symbol-match>\" - Displays statements whose");
-H("        labels match <label-match> and that contain <symbol-match>.");
-H("    SEARCH <label-match> \"<search-string>\" / COMMENTS - Shows statements");
-H("        whose preceding comment contains <search-string>");
-H("    SHOW LABELS <label-match> - Lists all labels matching <label-match>,");
-H("        with * and ? wildcards:  for example \"abc?def*\" will match all");
-H("        labels beginning with \"abc\" followed by any single character");
-H("        followed by \"def\".");
-H("    SHOW STATEMENT <label> / COMMENT - Shows the comment, contents, and");
-H("        logical hypotheses associated with a statement.");
-H("    SHOW PROOF <label> - Shows the proof of a $p statement in various");
-H("        formats, depending on what qualifiers you select.  One of the");
-H("        qualifiers, / TEX, lets you create LaTeX source for the proof.");
-H("        The / DETAILED_STEP qualifier is useful while you're learning how");
-H("        Metamath unifies the sources and targets of a step.  The");
-H("        / STATEMENT_SUMMARY qualifier gives you a quick summary of all");
-H("        the statements referenced in the proof.");
-H("    SHOW TRACE_BACK <label> - Traces a proof back to axioms, depending");
-H("        on various qualifiers you select.");
-H("    SHOW USAGE <label> - Shows what later proofs make use of this");
-H("        statement.");
-H("");
-
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP HTML");
-H("(Note: See HELP WRITE SOURCE for the \"<HTML>\" tag in comments.)");
-H("To create an HTML output file for a $a or $p statement, use");
-H("    SHOW STATEMENT <label> / HTML");
-H("The created web page will include a Description taken from the comment");
-H("that immediately precedes the $a or $p statement.  A warning will be");
-H("issued if this comment is not present.  Optional markup in the comment");
-H("will be processed according to the markup syntax described under HELP");
-H("LANGUAGE, in the \"Inside of comments\" section.  Warnings will be");
-H("issued for any errors in the markup.  Note that all other comments in");
-H("the database are ignored, including comments preceding $e statements.");
-H("");
-H("When <label> has wildcard (* and ?) characters, all statements with");
-H("matching labels will have HTML files produced for them.  Also, when");
-H("<label> starts with a * wildcard character, three additional files,");
-H("mmdefinitions.html, mmtheoremsall.html, and mmascii.html will be");
-H("produced.  Thus:");
-H("    SHOW STATEMENT * / HTML");
-H("will output the complete HTML proof database in the current directory,");
-H("one file per $a and $p statement, along with mmdefinitions.html,");
-H("mmtheoremsall.html, and mmascii.html.  The statement:");
-H("    SHOW STATEMENT *! / HTML");
-H("will produce only mmdefinitions.html, mmmmtheoremsall.html, and");
-H("mmascii.html, but no other HTML files (because no labels can match \"*!\"");
-H("since \"!\" is illegal in a statement label).  The statement:");
-H("    SHOW STATEMENT ?* / HTML");
-H("will output the complete HTML proof database but will not produce");
-H("mmdefinitions.html, etc.  Note added 30-Jan-06:  The mmtheoremsall.html");
-H("file produced by this command is deprecated and is replaced by the output");
-H("of WRITE THEOREM_LIST.");
-H("");
-H("The HTML definitions for the symbols and and other features are");
-H("specified by statements in a special typesetting comment in the input");
-H("database file.  The typesetting comment is identified by the token \"$t\"");
-H("in the comment, and the typesetting statements run until the next \"$)\":");
-H("   ...  $( ...  $t ................................ $) ...");
-H("                   <-- HTML definitions go here -->");
-H("See the set.mm database file for an extensive example of a $t comment");
-H("illustrating all features described below.  In the HTML definition");
-H("section, C-style comments /* ... */ are recognized.  The main HTML");
-H("specification statements are:");
-H("    htmldef \"<mathtoken>\" as \"<HTML code for mathtoken symbol>\" ;");
-H("                    ...");
-H("    htmldef \"<mathtoken>\" as \"<HTML code for mathtoken symbol>\" ;");
-H("    htmltitle \"<HTML code for title>\" ;");
-H("    htmlhome \"<HTML code for home link>\" ;");
-H("    htmlvarcolor \"<HTML code for variable color list>\" ;");
-H("    htmlbibliography \"<HTML file>\" ;");
-H("        (This <HTML file> is assumed to have a <A NAME=...> tag for each");
-H("        bibiographic reference in the database comments.  For example");
-H("        if \"[Monk]\" occurs in a comment, then \"<A NAME='Monk'>\" must");
-H("        be present in the <HTML file>; if not, a warning message is");
-H("        given.)");
-H("Single or double quotes surround the field strings, and fields too long");
-H("for a line may be broken up into multiple quoted strings connected with");
-H("(whitespace-surrounded) \"+\" signs (no quotes around them).  Inside");
-H("quoted strings, the opposite kind of quote may appear.  If both kinds of");
-H("quotes are needed, use separate quoted strings connected by \"+\".");
-H("Note that the \"$)\" character sequence will flag the end of the");
-H("typesetting Metamath comment even if embedded in quotes (which are not");
-H("meaningful for the Metamath language parser), so such a sequence must be");
-H("broken with \"+\".");
-H("");
-H("The typesetting Metamath comment may also contain LaTeX definitions");
-H("(with \"latexdef\" statements) that are ignored for HTML output.");
-H("");
-H("Several other qualifiers exist.  The command");
-H("    SHOW STATEMENT <label> / ALT_HTML");
-H("does the same as SHOW STATEMENT <label> / HTML, except that the HTML code");
-H("for the symbols is taken from \"althtmldef\" statements instead of");
-H("\"htmldef\" statements in the $(...$t...$) comment.  This is useful when");
-H("an alternate representation of symbols is desired, for example one that");
-H("uses Unicode entities instead of GIF images.  Associated with althtmldef");
-H("are the statements");
-H("    htmldir \"<directory for GIF HTML version>\" ;");
-H("    althtmldir \"<directory for Unicode HTML version>\" ;");
-H("that produce links to the alternate version.");
-H("");
-H("The command");
-H("    SHOW STATEMENT * / BRIEF_HTML");
-H("invokes a special mode that just produces definition and theorem lists");
-H("accompanied by their symbol strings, in a format suitable for copying and");
-H("pasting into another web page.");
-H("");
-H("Finally, the command");
-H("    SHOW STATEMENT * / BRIEF_ALT_HTML");
-H("does the same as SHOW STATEMENT * / BRIEF_HTML for the alternate HTML");
-H("symbol representation.");
-H("");
-H("When two different types of pages need to be produced from a single");
-H("database, such as the Hilbert Space Explorer that extends the Metamath");
-H("Proof Explorer, \"extended\" variables may be declared in the $t comment:");
-H("    exthtmltitle \"<HTML code for title>\" ;");
-H("    exthtmlhome \"<HTML code for home link>\" ;");
-H("    exthtmlbibliography \"<HTML file>\" ;");
-H("When these are declared, you also must declare");
-H("    exthtmllabel \"<label>\" ;");
-H("When the output statement is the one declared with \"exthtmllabel\" or");
-H("a later one, the HTML code assigned to \"exthtmltitle\" and");
-H("\"exthtmlhome\" is used instead of that assigned to \"htmltitle\" and");
-H("\"htmlhome\" respectively.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP LATEX");
-H("See HELP TEX.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP TEX");
-H("Metamath will create a \"turn-key\" LaTeX source file which can be");
-H("immediately compiled and printed using a TeX program.  The TeX program");
-H("must have the following minimum requirements:  the LaTeX style option and");
-H("the AMS font set, available from the American Mathematical Society.");
-H("");
-H("To write out a statement and its proof, use a command sequence similar");
-H("to the following example:");
-H("    (Enter Metamath)");
-H("    READ set.mm");
-H("    OPEN TEX example.tex");
-H("    SHOW STATEMENT uneq2 / TEX");
-H("    SHOW PROOF uneq2 / LEMMON / RENUMBER / TEX");
-H("    CLOSE TEX");
-H("");
-H("The LaTeX symbol definitions should be included in a special comment");
-H("containing a $t token.  See the set.mm file for an example.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP BEEP") || !strcmp(helpCmd, "HELP B");
-H("Syntax:  BEEP");
-H("");
-H("This command will produce a beep.  By typing it ahead after a long-");
-H("running command has started, it will alert you that the command is");
-H("finished. B is an abbreviation for BEEP.");
-H("");
-H("Note: If B is typed at the MM> prompt immediately after the end of a");
-H("multiple-page display paged with \"Press <return> for more...\" prompts,");
-H("then the B will back up to the previous page rather than perform the BEEP");
-H("command.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP QUIT");
-H("Syntax:  QUIT [/ FORCE]");
-H("");
-H("This command is a synonym for EXIT.  See HELP EXIT.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP EXIT");
-H("Syntax:  EXIT [/ FORCE]");
-H("");
-H("This command exits from Metamath.  If there have been changes to the");
-H("database with the SAVE PROOF or SAVE NEW_PROOF commands, you will be given");
-H("an opportunity to WRITE SOURCE to permanently save the changes.");
-H("");
-H("(In Proof Assistant mode) The EXIT command will return to the MM> prompt.");
-H("If there were changes to the proof, you will be given an opportunity to");
-H("SAVE NEW_PROOF.  In the Proof Assistant, _EXIT_PA is a synonym for EXIT");
-H("that gives an error message outside of the Proof Assistant.  This can be");
-H("useful to prevent scripts from exiting Metamath due to an error entering");
-H("the Proof Assistant.");
-H("");
-H("The QUIT command is a synonym for EXIT.");
-H("");
-H("Optional qualifier:");
-H("    / FORCE - Do not prompt if changes were not saved.  This qualifier is");
-H("        useful in SUBMIT command files (scripts) to ensure predictable");
-H("        behavior.");
-H("");
-H("**Warning**  Pressing CTRL-C will abort the Metamath program");
-H("unconditionally.  This means any unsaved work will be lost.");
-H("");
-
-
-/* Added 3-Jun-2018 nm */
-g_printHelp = !strcmp(saveHelpCmd, "HELP _EXIT_PA");
-H("Syntax:  _EXIT_PA [/ FORCE]");
-H("");
-H("This command is a synonym for EXIT inside the Proof Assistant but will");
-H("generate an error message (and otherwise have no effect) elsewhere.  It");
-H("can help prevent accidentally exiting Metamath when a script fails to");
-H("enter the Proof Assistant (PROVE command).  See HELP EXIT.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP READ");
-H("Syntax:  READ <file> [/ VERIFY]");
-H("");
-H("This command will read in a Metamath language source file and any included");
-H("files.  Normally it will be the first thing you do when entering Metamath.");
-H("Statement syntax is checked, but proof syntax is not checked.");
-H("Note that the file name may be enclosed in single or double quotes;");
-H("this is useful if the file name contains slashes, as might be the case");
-H("under Unix.");
-H("");
-H("If you are getting an \"?Expected VERIFY or NOVERIFY\" error when trying");
-H("to read a Unix file name with slashes, you probably haven't quoted it.");
-H("");
-H("You need nested quotes when a Unix file name with slashes is a Metamath");
-H("invocation argument.  See HELP INVOKE for examples.");
-H("");
-H("If you are prompted for the file name (by pressing <return> after READ)");
-H("you should _not_ put quotes around it, even if it is a Unix file name.");
-H("with slashes.");
-H("");
-H("Optional qualifier:");
-H("    / VERIFY - Verify all proofs as the database is read in.  This");
-H("        qualifier will slow down reading in the file.");
-H("");
-H("See also HELP ERASE.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP ERASE");
-H("Syntax:  ERASE");
-H("");
-H("This command will delete the database if one was READ in.  It does not");
-H("affect parameters listed in SHOW SETTINGS that are unrelated to the");
-H("database.  The user will be prompted for confirmation if the database was");
-H("changed but not saved with WRITE SOURCE.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP OPEN LOG");
-H("Syntax:  OPEN LOG <file>");
-H("");
-H("This command will open a log file that will store everything you see on");
-H("the screen.  It is useful to help recovery from a mistake in a long Proof");
-H("Assistant session, or to document bugs.");
-H("");
-H("The screen output of operating system commands (HELP SYSTEM) is not");
-H("logged.");
-H("");
-H("The log file can be closed with CLOSE LOG.  It will automatically be");
-H("closed upon exiting Metamath.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP CLOSE LOG");
-H("Syntax:  CLOSE LOG");
-H("");
-H("The CLOSE LOG command closes a log file if one is open.  See also OPEN");
-H("LOG.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP OPEN TEX");
-H("Syntax:  OPEN TEX <file> [/ NO_HEADER] [/ OLD_TEX]");
-H("");
-H("This command opens a file for writing LaTeX source and writes a LaTeX");
-H("header to the file.  LaTeX source can be written with the SHOW PROOF,");
-H("SHOW NEW_PROOF, and SHOW STATEMENT commands using the / TEX qualifier.");
-H("The mapping to LaTeX symbols is defined in a special comment containing");
-H("a $t token.  See the set.mm database file for an example.");
-H("");
-H("To format and print the LaTeX source, you will need the TeX program,");
-H("which is standard in most Linux, Unix, and MacOSX installations and");
-H("available for Windows.");
-H("");
-H("Optional qualifiers:");
-H("    / NO_HEADER - This qualifier prevents a standard LaTeX header and");
-H("        trailer from being included with the output LaTeX code.");
-H("    / OLD_TEX - This qualifier produces a header with macro definitions");
-H("        for use with / OLD_TEX qualifiers of SHOW STATEMENT and SHOW");
-H("        PROOF.  It is obsolete and will be removed eventually.");
-H("");
-H("See also CLOSE TEX.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP CLOSE TEX");
-H("Syntax:  CLOSE TEX");
-H("");
-H("This command writes a trailer to any LaTeX file that was opened with OPEN");
-H("TEX (unless / NO_HEADER was used with OPEN) and closes the LaTeX file.");
-H("");
-H("See also OPEN TEX.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP TOOLS");
-H("Syntax:  TOOLS");
-H("");
-H("This command invokes a utility to manipulate ASCII text files.  Type TOOLS");
-H("to enter this utility, which has its own HELP commands.  Once you are");
-H("inside, EXIT will return to Metamath.");
-H("");
-
-/* 3-May-2017 nm - removed CLEAN qualifier */
-g_printHelp = !strcmp(saveHelpCmd, "HELP WRITE SOURCE");
-H("Syntax:  WRITE SOURCE <filename> [/ FORMAT] [/ REWRAP] [/ SPLIT]");
-H("           [/ KEEP_INCLUDES] [/ NO_VERSIONING]");
-H("");
-H("This command will write the contents of a Metamath source (previously");
-H("read with READ) into a file");
-H("(or multiple files if / SPLIT is specified).");
-H("");
-H("Optional qualifiers:");
-H("    / FORMAT - Reformats statements and comments according to the");
-H("        convention used in the set.mm database.  Proofs are not");
-H("        reformatted; use SAVE PROOF * / COMPRESSED to do that.");
-H("        Incidentally, SAVE PROOF honors the SET WIDTH parameter");
-H("        currently in effect.");
-H("    / REWRAP - Same as / FORMAT but more aggressive.  It unwraps the");
-H("        lines in the comment before each $a and $p statement, then it");
-H("        rewraps the line.  You should compare the output to the original");
-H("        to make sure that the desired effect results; if not, go back to");
-H("        the original source.  The wrapped line length honors the");
-H("        SET WIDTH parameter currently in effect.  Note 1: The only lines");
-H("        that are rewrapped are those in comments immediately preceding a");
-H("        $a or $p statement.  In particular, formulas (such as the");
-H("        argument of a $p statement) are not rewrapped.  Note 2: A comment");
-H("        containing the string \"<HTML>\" is not rewrapped (see also");
-H("        HELP LANGUAGE and");
-H("   https://github.com/metamath/set.mm/pull/1695#issuecomment-652129129 .)");
-H("    / SPLIT - Files included in the source with $[ <inclfile> $] will be");
-H("        written out separately instead of included in a single output");
-H("        file.  The name of each separately written included file will be");
-H("        <inclfile> argument of its inclusion command.  See the");
-H("        21-Dec-2017 (file inclusion) entry in");
-H("        http://us.metamath.org/mpeuni/mmnotes.txt for further details");
-H("    / KEEP_INCLUDES - If a source file has includes but is written as a");
-H("        single file by omitting / SPLIT, by default the included files will");
-H("        be deleted (actually just renamed with a ~1 suffix unless");
-H("        / NO_VERSIONING is specified) to prevent the possibly confusing");
-H("        source duplication in both the output file and the included file.");
-H("        The / KEEP_INCLUDES qualifier will prevent this deletion.");
-H("    / NO_VERSIONING - Backup files suffixed with ~1 are not created.");
-/* 4-Sep-2020 nm Added EXTRACT */
-H("    / EXTRACT <label-match> - Write to the output file only those");
-H("        statements needed to support and prove the statements matching");
-H("        <label-match>.  See HELP SEARCH for the format of <label-match>.");
-H("        A single output file is created.  Note that all includes");
-H("        \"$[...$]\", all commented includes \"$( Begin $[...\" etc.,");
-H("        and all \"$j\" comments will be discarded.  / EXTRACT and / SPLIT");
-H("        may not be used together.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP WRITE THEOREM_LIST");
-H("Syntax:  WRITE THEOREM_LIST [/ THEOREMS_PER_PAGE <number>] [/ SHOW_LEMMAS]");
-H("               [/ HTML] [/ALT_HTML] [/ NO_VERSIONING]");
-H("");
-H("Optional qualifiers:");
-H("    / THEOREMS_PER_PAGE <number> - specifies the number of theorems to");
-H("        write per output file");
-H("    / SHOW_LEMMAS - show the math content of lemmas (by default, the math");
-H("        content of theorems whose comment begins \"Lemma for\" is");
-H("        suppressed to reduce the web page file size).");
-H("    / HTML (/ ALT_HTML) - use the symbols defined by the htmldef");
-H("        (althtmldef) statements in the $t comment in the .mm database.");
-H("        Usually these are GIF or Unicode math symbols respectively.");
-H("    / NO_VERSIONING - Backup files suffixed with ~1 are not created.");
-H("");
-H("This command writes a list of the $a and $p statements in the database");
-H("into web page files called \"mmtheorems.html\", \"mmtheorems1.html\",");
-H("\"mmtheorems2.html\", etc.  If / THEOREMS_PER_PAGE is omitted, the number");
-H("of theorems (and other statements) per page defaults to 100.");
-H("[Warning:  A value other than 100 for THEOREMS_PER_PAGE will cause the");
-H("list to become out of sync with the \"Related theorems\" links on the");
-H("web pages for individual theorems.  This may be corrected in a future");
-H("version.]");
-H("");
-H("If neither / HTML nor / ALT_HTML is specified, the output will default to");
-H("GIF format unless ALT_HTML was previously set as shown in SHOW SETTINGS.");
-H("");
-H("The first output file, \"mmtheorems.html\", includes a Table of Contents.");
-H("An entry is triggered in the database by \"$(\" immediately followed by a");
-H("new line starting with \"####\" (the marker for a major part header),");
-H("\"#*#*\" (for a section header), \"=-=-\" (for a subsection header), or");
-H("\"-.-.\" (for a subsubsection header).  The line following the marker line");
-H("will be used for the table of contents entry, after trimming spaces.  The");
-H("next line should be another (closing) marker line.  Any text after that");
-H("but before the closing \"$)\", such as an extended description of the");
-H("section, will be included on the mmtheoremsNNN.html page.  In between two");
-H("successive statements that generate web pages (i.e. $a and $p statements),");
-H("only the last of each header type (part, section, subsection,");
-H("subsubsection) will be used, and any smaller header type before a larger");
-H("header type (e.g. a subsection header before a section header) will be");
-H("ignored.  See the set.mm database file for examples.");
-H("");
-H("[Warning: For the above matching, white space is NOT ignored.  There");
-H("should be 0 or 1 spaces between \"$(\" and the end of the line.  This may");
-H("be allowed in a future version.]");
-H("");
-H("Note:  To create the files mmdefinitions.html and mmascii.html, use");
-H("SHOW STATEMENT *! / HTML.  See HELP HTML.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP BIBLIOGRAPHY");
-H("See HELP WRITE BIBLIOGRAPHY.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP WRITE BIBLIOGRAPHY");
-H("Syntax:  WRITE BIBLIOGRAPHY <filename>");
-H("");
-H("This command reads an HTML bibliographic cross-reference file, normally");
-H("called mmbiblio.html, and updates it per the bibliographic links in");
-H("the database comments.  The file is updated between the HTML comment");
-H("lines \"<!-- #START# -->\" and \"<!-- #END# -->\".  Any previous content");
-H("between these two lines is discarded.  The original input file is renamed");
-H("<filename>~1");
-H("");
-H("A name in square brackets in a statement's description (the comment");
-H("before a $a or $p statement) indicates a bibliographic reference.  The");
-H("full reference must be of the form");
-H("");
-H("    <keyword> <identifier> <noise word(s)> [<author>] p. <nnn>");
-H("");
-H("There should be no comma between \"[<author>]\" and \"p.\".  Whitespace,");
-H("comma, period, or semicolon should follow <nnn>.  Example:");
-H("");
-H("    Theorem 3.1 of [Monk] p. 22,");
-H("");
-H("The <keyword>, which is not case-sensitive, must be one of the following:");
-H("");
-H("    Axiom, Chapter, Claim, Compare, Conclusion, Condition, Conjecture,");
-H("    Corollary, Definition, Equation, Example, Exercise, Fact, Figure,");
-H("    Introduction, Item, Lemma, Lemmas, Line, Lines, Notation, Note,");
-H("    Observation, Paragraph, Part, Postulate, Problem, Proof, Property,");
-H("    Proposition, Remark, Result, Rule, Scheme, Scolia, Scolion, Section,");
-H("    Statement, Subsection, Table, Theorem");
-H("");
-H("The <identifier> is optional, as in for example \"Remark in [Monk] p. 22\".");
-H("");
-H("The <noise word(s)> are zero or more from the list:  from, in, of, on.");
-H("These are ignored when generating the bibliographic cross-reference.");
-H("");
-H("The <author> must be present in the file identified with the");
-H("htmlbibliography assignment (e.g. mmset.html) in the database $t comment,");
-H("in the form <A NAME=\"<author>\"></A> e.g. <A NAME=\"Monk\"></A>.");
-H("");
-H("The <nnn> may be any alphanumeric string such as an integer or Roman");
-H("numeral.");
-H("");
-H("The <keyword> and <noise word(s)> lists are hard-coded into the program.");
-H("Contact Norman Megill if you need to add to these lists.");
-H("");
-H("Additional notes:  1. The bibliographic reference in square brackets may");
-H("not contain whitespace.  If it does, the bracketed text will be treated");
-H("like normal text and not assumed to be a bibliographic reference.");
-H("2. A double opening bracket \"[[\" escapes the bracket and treats the");
-H("bracketed text as normal text, and a single bracket is rendered on the");
-H("web page output.  The closing bracket need not be escaped, and \"]]\"");
-H("will cause a double bracket to be rendered on the web page.");
-H("");
-H("See also");
-H("https://github.com/metamath/set.mm/pull/1761#issuecomment-672433658");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP WRITE RECENT_ADDITIONS");
-H("Syntax:  WRITE RECENT_ADDITIONS <filename>");
-H("");
-H("Optional qualifier:");
-H("    / LIMIT <number> - specifies the number of most recent theorems to");
-H("        write to the output file");
-H("    / HTML (/ ALT_HTML) - use GIF (Unicode) math symbols.");
-H("");
-H("This command reads an HTML Recent Additions file, normally called");
-H("\"mmrecent.html\", and updates it with the descriptions of the most recently");
-H("added $a and $p statements to the database.  If / LIMIT is omitted, the");
-H("number of theorems written defaults to 100.  The file is updated between the");
-H("HTML comment lines \"<!-- #START# -->\" and \"<!-- #END# -->\".  The");
-H("original input file is renamed to \"<filename>~1\"");
-H("");
-H("The date used for comparison is the most recent \"(Contributed by...)\",");
-H("\"(Revised by...)\", and \"(Proof shortened by...)\" date in the comment");
-H("immediately preceding the statement.");
-H("");
-H("If neither / HTML nor / ALT_HTML is specified, the output will default to");
-H("GIF format unless ALT_HTML was previously set as shown in SHOW SETTINGS.");
-H("");
-
-
-let(&saveHelpCmd, ""); /* Deallocate memory */
-return;
-
-} /* help1 */
+/*****************************************************************************/
+/*        Copyright (C) 2021  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* Part 1 of help file for Metamath */
+/* The content here was split into help0() and help1() because the original
+   help() overflowed the lcc compiler (at least before version 3.8; not
+   tested with 3.8 and above). */
+/* To add a new help entry, you must add the command syntax to mmcmdl.c
+   as well as adding it here. */
+
+#include <string.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mmcmds.h"
+#include "mmhlpa.h"
+
+void help0(vstring helpCmd) {
+
+vstring_def(saveHelpCmd);
+/* help0() may be called with a temporarily allocated argument (left(),
+   cat(), etc.), and the let()s in the eventual print2() calls will
+   deallocate and possibly corrupt helpCmd.  So, we grab a non-temporarily
+   allocated copy here.  (And after this let(), helpCmd will become invalid
+   for the same reason.)  */
+let(&saveHelpCmd, helpCmd);
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP");
+H("This utility assists with some common file manipulations.");
+H("Most commands will perform an identical operation on each line of a file.");
+H("Use HELP ? to see list of help topics.");
+H("Note:  When an output file is created, any previous version is renamed,");
+H(
+ "with ~1 appended, and any ~1 renamed to ~2, etc. (up to ~9, which is lost).");
+H("Note:  All string-matching command arguments are case-sensitive.");
+H("");
+H("Line-by-line editing commands:");
+H("  ADD - Add a specified string to each line in a file");
+H("  CLEAN - Trim spaces and tabs on each line in a file; convert characters");
+H("  DELETE - Delete a section of each line in a file");
+H("  INSERT - Insert a string at a specified column in each line of a file");
+H("  SUBSTITUTE - Make a simple substitution on each line of the file");
+H("  TAG - Like ADD, but restricted to a range of lines");
+H("  SWAP - Swap the two halves of each line in a file");
+H("Other file processing commands:");
+H("  BREAK - Break up (parse) a file into a list of tokens (one per line)");
+H("  BUILD - Build a file with multiple tokens per line from a list");
+H("  COUNT - Count the occurrences in a file of a specified string");
+H("  NUMBER - Create a list of numbers");
+H("  PARALLEL - Put two files in parallel");
+H("  REVERSE - Reverse the order of the lines in a file");
+H("  RIGHT - Right-justify lines in a file (useful before sorting numbers)");
+H("  SORT - Sort the lines in a file with key starting at specified string");
+H("  MATCH - Extract lines containing (or not) a specified string");
+H("  UNDUPLICATE - Eliminate duplicate occurrences of lines in a file");
+H("  DUPLICATE - Extract first occurrence of any line occurring more than");
+H("      once in a file, discarding lines occurring exactly once");
+H("  UNIQUE - Extract lines occurring exactly once in a file");
+H(
+"  (UNDUPLICATE, DUPLICATE, and UNIQUE also sort the lines as a side effect.)");
+H("  UPDATE (deprecated) - Update a C program for revision control");
+H("  TYPE (10 lines) - Display 10 lines of a file; similar to Unix \"head\"");
+H(
+"  COPY - Similar to Unix \"cat\" but safe (same input & output name allowed)");
+H("  SUBMIT - Run a script containing Tools commands.");
+H("");
+H("Command syntax ([] means optional):");
+H("  From TOOLS prompt:  TOOLS> <command> [<arg1> <arg2>...]");
+H("You need to type only as many characters of the command as are needed to");
+H("uniquely specify it.  Any arguments will answer questions automatically");
+H("until the argument list is exhausted; the remaining questions will be");
+H("prompted.  An argument may be optionally enclosed in quotes.  Use \"\" for");
+H("default or null argument.");
+H("");
+H("Notes:");
+H("(1) The commands are not case sensitive.  File names and match strings");
+H("are case sensitive.");
+H("(2) Previous versions of output files (except under VMS) are renamed with");
+H("~1 (most recent), ~2,...,~9 (oldest) appended to file name.  You may want");
+H("to purge them periodically.");
+H("(3) The command B(EEP) will make the terminal beep.  It can be useful to");
+H("type it ahead to let you know when the current command is finished.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP ADD");
+H("This command adds a character string prefix and/or suffix to each");
+H("line in a file.  To add only a prefix, set <endstr> to the empty string,");
+H("and set <begstr> to the empty string to add only a suffix.");
+H("Syntax:  ADD <iofile> <begstr> <endstr>");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP TAG");
+H("TAG is the same as ADD but has 4 additional arguments that let you");
+H("specify a range of lines.  Syntax:");
+H("  TAG <iofile> <begstr> <endstr> <startmatch> <s#> <endmatch> <e#>");
+H("where");
+H("  <iofile> = input/output file");
+H("  <begstr> = string to add to beginning of each line");
+H("  <endstr> = string to add to end of each line");
+H("  <startmatch> = a string to match; if empty, match any line");
+H("  <s#> = the 1st, 2nd, etc. occurrence of <startmatch> to start the range");
+H("  <endmatch> = a string to match; if empty, match any line");
+H("  <e#> = the 1st, 2nd, etc. occurrence of <endmatch> from the");
+H("      start of range line (inclusive) after which to end the range");
+H("Example:  To add \"!\" to the end of lines 51 through 60 inclusive:");
+H("  TAG \"a.txt\" \"\" \"!\" \"\" 51 \"\" 10");
+H("Example:  To add \"@@@\" to the beginning of each line in theorem");
+H("\"abc\" through the end of its proof:");
+H("  TAG \"set.mm\" \"@@@\" \"\" \"abc $p\" 1 \"$.\" 1");
+H("so that later, SUBSTITUTE can be used to affect only those lines.  You");
+H("can remove the \"@@@\" tags with SUBSTITUTE when done.");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP DELETE");
+H("This command deletes the part of a line between (and including) the first");
+H("occurrence of <startstr> and the first occurrence of <endstr> (when both");
+H("exist) for all lines in a file.  If either string doesn't exist in a line,");
+H("the line will be unchanged.  If <startstr> is blank (''), the deletion");
+H("will start from the beginning of the line.  If <endstr> is blank, the");
+H("deletion will end at the end of the line.");
+H("Syntax:  DELETE <iofile> <startstr> <endstr>");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP CLEAN");
+H("This command processes spaces and tabs in each line of a file");
+H("according to the following subcommands:");
+H("  D - Delete all spaces and tabs");
+H("  B - Delete spaces and tabs at the beginning of each line");
+H("  E - Delete spaces and tabs at the end of each line");
+H("  R - Reduce multiple spaces and tabs to one space");
+H("  Q - Do not alter characters in quotes (ignored by T and U)");
+H("  T - (Tab) Convert spaces to equivalent tabs");
+H("  U - (Untab) Convert tabs to equivalent spaces");
+H("Some other subcommands are also available:");
+H("  P - Clear parity (8th) bit from each character");
+H("  G - Discard garbage characters CR,FF,ESC,BS");
+H("  C - Convert to upper case");
+H("  L - Convert to lower case");
+H("  V - Convert VT220 screen print frame graphics to -,|,+ characters");
+H("Subcommands may be joined with commas (but no spaces), e.g., \"B,E,R,Q\"");
+H("Syntax:  CLEAN <iofile> <subcmd,subcmd,...>");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SUBSTITUTE")
+    || !strcmp(helpCmd, "HELP S");
+H("This command performs a simple string substitution in each line of a file.");
+H("If the string to be replaced is \"\\n\", then every other line will");
+H("be joined to the one below it.  If the replacement string is \"\\n\", then");
+H("each line will be split into two if there is a match.");
+H("The <matchstr> specifies a string that must also exist on a line");
+H("before the substitution takes place; null means match any line.");
+H("The <occurrence> is an integer (1 = first occurrence on each line, etc.)");
+H("or A for all occurrences on each line.");
+H("Syntax:  SUBSTITUTE <iofile> <oldstr> <newstr> <occurrence> <matchstr>");
+H("Note: The SUBSTITUTE command may be abbreviated by S.");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SWAP");
+H("This command swaps the parts of each line before and after a");
+H("specified string <middle>.");
+H("Syntax:  SWAP <iofile> <middle>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP INSERT");
+H("This command inserts a string at a specified column in each line");
+H("in a file.  It is intended to aid further processing of column-");
+H("sensitive files.  Note: the index of the first column is 1, not 0.  If a");
+H("line is shorter than <column>, then it is padded with spaces so that");
+H("<string> is still added at <column>.");
+H("Syntax:  INSERT <iofile> <string> <column>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP BREAK");
+H("This command breaks up a file into tokens, one per line, breaking at");
+H("whitespace and any special characters you specify as delimiters.");
+H("Use an explicit (quoted) space as <specchars> to avoid the default");
+H("special characters and break only on whitespace.");
+H("Syntax:  BREAK <iofile> <specchars>");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP BUILD");
+H("This command combines a list of tokens into multiple tokens per line,");
+H("as many as will fit per line, separating them with spaces.");
+H("Syntax:  BUILD <iofile>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP MATCH");
+H("This command extracts from a file those lines containing (Y) or not");
+H("containing (N) a specified string.");
+H("Syntax:  MATCH <iofile> <matchstr> <Y/N>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SORT");
+H("This command sorts a file, comparing lines starting at a key string.");
+H("If the key string is blank, the line is compared starting at column 1.");
+H("If a line doesn't contain the key, it is compared starting at column 1.");
+H("Syntax:  SORT <iofile> <key>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP UNDUPLICATE");
+H("This command sorts a file then removes any duplicate lines from the output.");
+H("Syntax:  UNDUPLICATE <iofile>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP DUPLICATE");
+H("This command finds all duplicate lines in a file and places them, in");
+H("sorted order, into the output file.");
+H("Syntax:  DUPLICATE <iofile>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP UNIQUE");
+H("This command finds all unique lines in a file and places them, in");
+H("sorted order, into the output file.");
+H("Syntax:  UNIQUE <iofile>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP REVERSE");
+H("This command reverses the order of the lines in a file.");
+H("Syntax:  REVERSE <iofile>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP RIGHT");
+H("This command right-justifies the lines in a file by putting spaces in");
+H("front of them so that they end in the same column as the longest line");
+H("in the file.");
+H("Syntax:  RIGHT <iofile>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP PARALLEL");
+H("This command puts two files side-by-side.");
+H("The two files should have the same number of lines; if not, a warning is");
+H("issued and the longer file paralleled with empty strings at the end.");
+H("Syntax:  PARALLEL <inpfile1> <inpfile2> <outfile> <btwnstr>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP NUMBER");
+H("This command creates a list of numbers.  Hint:  Use the RIGHT command to");
+H("right-justify the list after creating it.");
+H("Syntax:  NUMBER <outfile> <first> <last> <incr>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP COUNT");
+H("This command counts the occurrences of a string in a file and displays");
+H("some other statistics about the file.  The sum of the lines is obtained");
+H("by extracting digits and is only valid if the file consists of genuine");
+H("numbers.");
+H("Syntax:  COUNT <inpfile> <string>");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP TYPE") || !strcmp(helpCmd, "HELP T");
+H("This command displays (i.e. types out) the first n lines of a file on the");
+H("terminal screen.  If n is not specified, it will default to 10.  If n is");
+H("the string \"ALL\", then the whole file will be typed.");
+H("Syntax:  TYPE <inpfile> <n>");
+H("Note: The TYPE command may be abbreviated by T.");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP COPY") || !strcmp(helpCmd, "HELP C");
+H("This command copies (concatenates) all input files in a comma-separated");
+H("list (no blanks allowed) to an output file.  The output file may have");
+H("the same name as an input file.  Any previous version of the output");
+H("file is renamed with a ~1 extension.");
+H("Example: \"COPY 1.tmp,1.tmp,2.tmp 1.tmp\" followed by \"UNIQUE 1.tmp\"");
+H("will result in 1.tmp containing those lines of 2.tmp that didn't");
+H("previously exist in 1.tmp.");
+H("Syntax:  COPY <inpfile,inpfile,...> <outfile>");
+H("Note: The COPY command may be abbreviated by C.");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP UPDATE");
+H("This command tags edits made to a program source.  The idea is to keep");
+H("all past history of a file in the file itself, in the form of comments.");
+H("UPDATE was written for a proprietary language that allowed nested C-style");
+H("comments, and it may not be generally useful without some modification.");
+H("Essentially a (Unix) diff-like algorithm looks for changes between an");
+H("original and a revised file and puts the original lines into the revised");
+H("file in the form of comments.  Currently it is not well documented and it");
+H("may be easiest just to type UPDATE <return> and answer the questions.");
+H("Try it on an original and edited version of a test file to see if you");
+H("find it useful.");
+H("Syntax:  UPDATE <originfile> <editedinfile> <editedoutfile> <tag> <match>");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP CLI");
+H("Each command line is an English-like word followed by arguments separated");
+H("by spaces, as in SUBMIT abc.cmd.  Commands are not case sensitive, and");
+H("only as many letters are needed as are necessary to eliminate ambiguity;");
+H("for example, \"a\" would work for the command ADD.  Command arguments");
+H("which are file names and match strings are case-sensitive (although file");
+H("names may not be on some operating systems).");
+H("");
+H("A command line is entered typing it in then pressing the <return> key.");
+H("");
+H("To find out what commands are available, type ? at the \"TOOLS>\" prompt.");
+H("");
+H("To find out the choices at any point in a command, press <return> and you");
+H("will be prompted for them.  The default choice (the one selected if you");
+H("just press <return>) is shown in brackets (<>).");
+H("");
+H("You may also type ? in place of a command word to tell");
+H("you what the choices are.  The ? method won't work, though, if a");
+H("non-keyword argument such as a file name is expected at that point,");
+H("because the CLI will think the ? is the argument.");
+H("");
+H("Some commands have one or more optional qualifiers which modify the");
+H("behavior of the command.  Qualifiers are indicated by a slash (/), such as");
+H("in ABC xyz / IJK.  Spaces are optional around the /.  If you need");
+H("to use a slash in a command argument, as in a Unix file name, put single");
+H("or double quotes around the command argument.");
+H("");
+H("If the response to a command is more than a screenful, you will be");
+H("prompted to \"<return> to continue, Q to quit, or S to scroll to end\".");
+H("Q will complete the command internally but suppress further output until");
+H("the next \"TOOLS>\" prompt.  S will suppress further pausing until the next");
+H("\"TOOLS>\" prompt.");
+H("");
+H("A command line enclosed in quotes is executed by your operating system.");
+H("See HELP SYSTEM.");
+H("");
+H("Some other commands you may want to review with HELP are:");
+H("    SUBMIT");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SUBMIT");
+H("Syntax:  SUBMIT <filename> [/ SILENT]");
+H("");
+H("This command causes further command lines to be taken from the specified");
+H("file.  Note that any line beginning with an exclamation point (!) is");
+H("treated as a comment (i.e. ignored).  Also note that the scrolling");
+H("of the screen output is continuous.");
+H("");
+H("Optional qualifier:");
+H("    / SILENT - This qualifier suppresses the screen output of the SUBMIT");
+H("        command.");
+H("");
+H("SUBMIT can be called recursively, i.e., SUBMIT commands are allowed");
+H("inside of a command file.");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SYSTEM");
+H("A line enclosed in single or double quotes will be executed by your");
+H("computer's operating system, if it has such a feature.  For example, on a");
+H("Unix system,");
+H("    Tools> 'ls | more'");
+H("will list disk directory contents.  Note that this feature will not work");
+H("on the pre-OSX Macintosh, which does not have a command line interface.");
+H("");
+H("For your convenience, the trailing quote is optional, for example:");
+H("    Tools> 'ls | more");
+H("");
+
+free_vstring(saveHelpCmd); /* Deallocate memory */
+
+return;
+} /* help0 */
+
+
+void help1(vstring helpCmd) {
+
+vstring_def(saveHelpCmd);
+/* help1() may be called with a temporarily allocated argument (left(),
+   cat(), etc.), and the let()s in the eventual print2() calls will
+   deallocate and possibly corrupt helpCmd.  So, we grab a non-temporarily
+   allocated copy here.  (And after this let(), helpCmd will become invalid
+   for the same reason.)  */
+let(&saveHelpCmd, helpCmd);
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP CLI");
+H("The Metamath program was first developed on a VAX/VMS system, and some");
+H("aspects of its command line behavior reflect this heritage.  Hopefully");
+H(
+"you will find it reasonably user-friendly once you get used to it.");
+H("");
+H("Each command line is a sequence of English-like words separated by");
+H("spaces, as in SHOW SETTINGS.  Command words are not case sensitive, and");
+H("only as many letters are needed as are necessary to eliminate ambiguity;");
+H("for example, \"sh se\" would work for the command SHOW SETTINGS.  In some");
+H("cases arguments such as file names, statement labels, or symbol names are");
+H("required; these are case-sensitive (although file names may not be on");
+H("some operating systems).");
+H("");
+H("A command line is entered by typing it in then pressing the <return> key.");
+H("");
+H("To find out what commands are available, type ? at the MM> prompt,");
+H("followed by <return>. (This is actually just a trick to force an error");
+H("message, since ? is not a legal command.)");
+H("");
+H("To find out the choices for the next argument for a command, press");
+H("<return> and you will be prompted for it.  The default choice (the one");
+H("selected if you just press <return>) is shown in brackets (<>).");
+H("");
+H("You may also type ? in place of a command word to force Metamath to tell");
+H("you what the choices are.  The ? method won't work, though, if a");
+H("non-keyword argument such as a file name is expected at that point,");
+H("because the CLI will think the ? is the argument.");
+H("");
+H("Some commands have one or more optional qualifiers that modify the");
+H("behavior of the command.  Qualifiers are indicated by a slash (/), such as");
+H("in READ set.mm / VERIFY.  Spaces are optional around / and =.  If you need");
+H("to use / or = in a command argument, as in a Unix file name, put single");
+H("or double quotes around the command argument.  See the last section of");
+H("HELP LET for more information on special characters in arguments.");
+H("");
+H("The OPEN LOG command will save everything you see on the screen, and is");
+H("useful to help you recover should something go wrong in a proof, or if");
+H("you want to document a bug.");
+H("");
+H("If the response to a command is more than a screenful, you will be");
+H("prompted to '<return> to continue, Q to quit, or S to scroll to end'.");
+H("Q will complete the command internally but suppress further output until");
+H("the next \"MM>\" prompt.  S will suppress further pausing until the next");
+H("\"MM>\" prompt.  After the first screen, you can also choose B to go back");
+H("a screenful.  Note that B may also be entered at the \"MM>\" prompt");
+H("immediately after a command to scroll back through the output of that");
+H("command.  Scrolling can be disabled with SET SCROLL CONTINUOUS.");
+H("");
+H("**Warning**  Pressing CTRL-C will abort the Metamath program");
+H("unconditionally.  This means any unsaved work will be lost.");
+H("");
+H("A command line enclosed in quotes is executed by your operating system.");
+H("See HELP SYSTEM.");
+H("");
+H("Some additional CLI-related features are explained by:");
+H("");
+H("    HELP SET ECHO");
+H("    HELP SET SCROLL");
+H("    HELP SET WIDTH");
+H("    HELP SET HEIGHT");
+H("    HELP SUBMIT");
+H("    HELP UNDO (or REDO) - in Proof Assistant only");
+H("");
+
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP LANGUAGE");
+H("The language is best learned by reading the book and studying a few proofs");
+H("with the Metamath program.  This is a brief summary for reference.");
+H("");
+H("The database contains a series of tokens separated by whitespace (spaces,");
+H("tabs, returns).  A token is a keyword, a <label>, or a <symbol>.");
+H("");
+H("The pure language keywords are:  $c $v $a $p $e $f $d ${ $} $. and $=");
+H("The auxiliary keywords are:  $( $) $[ and $]");
+H("This is the complete set of language keywords.");
+H("");
+H("<symbol>s and <label>s are user-defined.  <symbol>s may contain any");
+H("printable characters other than $ , and <label>s may contain alphanumeric");
+H("characters, periods, dashes and underscores.");
+H("");
+H("Scoping statements:");
+H("");
+H("  ${ - Start of scope.");
+H("       Syntax:  \"${\"");
+H("");
+H("  $} - End of scope:  all $v, $e, $f, and $d statements in the current");
+H("         scope become inactive.");
+H("       Syntax:  \"$}\"");
+H("");
+H("  Note that $a and $p statements remain active forever.  Note that $c's");
+H("  may be used only in the outermost scope, so they are always active.");
+H("  The outermost scope is not bracketed by ${ ... $} .  The scope of a $v,");
+H("  $e, $f, or $d statement starts where the statement occurs and ends with");
+H("  the $} that matches the previous ${.  The scope of a $c, $a, or $p");
+H("  statement starts where the statement occurs and ends at the end of the");
+H("  database.");
+H("");
+H("Declarations:");
+H("");
+H("  $c - Constant declaration.  The <symbol>s become active constants.");
+H("       Syntax:  \"$c <symbol> ... <symbol> $.\"");
+H("");
+H("  $v - Variable declaration.  The <symbols>s become active variables.");
+H("       Syntax:  \"$v <symbol> ... <symbol> $.\"");
+H("");
+H("Hypotheses:");
+H("");
+H("  $f - Variable-type (or \"floating\") hypothesis (meaning it is");
+H("         \"required\" by a $p or $a statement in its scope only if its");
+H("         variable occurs in the $p or $a statement or in the essential");
+H("         hypotheses of the $p or $a statement).  Every $d, $e, $p, and $a");
+H("         statement variable must have an earlier active $f statement to");
+H("         specify the variable type.  Non-required i.e. \"optional\" $f");
+H("         statements may be referenced inside a proof when dummy variables");
+H("         are needed by the proof.");
+H("       Syntax:  \"<label> $f <constant> <variable> $.\" where both symbols");
+H("         are active");
+H("");
+H("  $e - Logical (or \"essential\") hypothesis (meaning it is always");
+H("         required by a $p or $a statement in its scope)");
+H("       Syntax:  \"<label> $e <symbol> ... <symbol> $.\"  where the first");
+H("         (and possibly only) <symbol> is a constant");
+H("");
+H("Assertions:");
+H("");
+H("  $a - Axiomatic assertion (starting assertion; used for axioms,");
+H("         definitions, and language syntax specification)");
+H("       Syntax:  \"<label> $a <symbol> ... <symbol> $.\"  where the first");
+H("         (and possibly only) <symbol> is a constant");
+H("");
+H("  $p - Provable assertion (derived assertion; used for deductions and");
+H("         theorems; must follow from previous statements as demonstrated by");
+H("         its proof)");
+H("       Syntax:");
+H("         \"<label> $p <symbol> ... <symbol> $= <label> ... <label> $.\"");
+H("         where the first (and possibly only) <symbol> is a constant.");
+H("         \"$= <label> ... <label> $.\" is the proof; see the book for more");
+H("         information.  Proofs may be compressed for storage efficiency.  A");
+H("         compressed proof is a series of labels in parentheses followed by");
+H("         a string of capital letters; see book for compression algorithm.");
+H("         SAVE PROOF <label> / NORMAL will convert a compressed proof to");
+H("         its uncompressed form.");
+H("");
+H("  A substitution is the replacement of a variable with a <symbol> string");
+H("  throughout an assertion and its required hypotheses.  The required");
+H("  hypotheses are shown as the \"mandatory hypotheses\" listed by");
+H("  SHOW STATEMENT <label> / FULL.");
+H("");
+H("  In a proof, the label of a hypothesis ($e or $f) pushes the stack, and");
+H("  the label of an assertion ($a or $p) pops from the stack a number of");
+H("  entries equal to the number of the assertion's required hypotheses and");
+H("  replaces the stack's top.  Whenever an assertion is specified, a unique");
+H("  set of substitutions must exist that makes the assertion's hypotheses");
+H("  match the top entries of the stack.");
+H("");
+H("  To see a readable proof format, type SHOW PROOF <label>, where <label>");
+H("  is the label of a $p statement.  To see how substitutions are made in a");
+H("  proof step, type SHOW PROOF <label> / DETAILED_STEP <n>, where <n> is");
+H("  the step number from the SHOW PROOF <label> listing.");
+H("");
+H("Disjoint variable restriction:");
+H("");
+H("  The substitution of symbol strings into variables may be subject to a");
+H("  $d restriction:");
+H("");
+H("  $d - Disjoint variable restriction (meaning substitutions may not");
+H("         have variables in common)");
+H("       Syntax:  \"$d <symbol> ... <symbol> $.\" where <symbol> is active");
+H("         and previously declared with $v, and all <symbol>s are distinct");
+H("");
+H("Auxiliary keywords:");
+H("");
+H("  $(   Begin comment");
+H("  $)   End comment");
+H("       Markup in comments:");
+H("         ` <symbol> ` - use graphical <symbol> in LaTeX/HTML output;");
+H("             `` means literal `; several <symbol>s may occur inside");
+H("             ` ... ` separated by whitespace");
+H("         ~ <label> - use typewriter font (hyperlink) in LaTeX (HTML) output;");
+H("             if <label> begins with \"http://\", it is assumed to be");
+H("             a URL (which is used as-is, except a \"~\" in the URL should");
+H("             be specified as \"~~\") rather than a statement label (which");
+H("             will have \".html\" appended for the hyperlink); only $a and $p");
+H("             statement labels may be used, since $e, $f pages don't exist");
+H("         [<author>] - link to bibliography; see HELP HTML and HELP WRITE");
+H("             BIBLIOGRAPHY");
+H("         $t - flags comment as containing LaTeX and/or HTML typesetting");
+H("             definitions; see HELP LATEX or HELP HTML");
+H("         _ - Italicize text from <space>_<non-space> to");
+H("             <non-space>_<space>; normal punctuation (e.g. trailing");
+H("             comma) is ignored when determining <space>");
+H("         _ - <non-space>_<non-space-string> will make <non-space-string>");
+H("             a subscript");
+H("         <HTML> - A comment containing \"<HTML>\" (case-sensitive) is");
+H("             bypassed by the algorithm of SHOW PROOF ... / REWRAP.  Also,");
+H("             \"<\" is not converted to \"&lt;\" by the algorithm.  The");
+H("             \"<HTML>\" is discarded in the generated web page.  Any");
+H("             \"</HTML>\" (deprecated) is discarded and ignored.  Note that");
+H("             the entire comment (not just sections delineated by");
+H("             \"<HTML>...</HTML>\") is treated as HTML code if any");
+H("             \"<HTML>\" is present anywhere in the comment.");
+H("             See also HELP WRITE SOURCE for more information.");
+H("         (Contributed by <author>, <date>.)");
+H("         (Revised by <author>, <date>.)");
+H("         (Proof shortened by <author>, <date>.)");
+H("             The above dates are checked by VERIFY MARKUP.");
+H("         (New usage is discouraged.)");
+H("         (Proof modification is discouraged.)");
+H("             See HELP SHOW DISCOURAGED and HELP SET DISCOURAGEMENT.");
+H("       Note:  Comments may not be nested.");
+H("");
+H("  $[ <file-name> $] - place contents of file <file-name> here; a second,");
+H("       recursive, or self reference to a file is ignored");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP MARKUP");
+H("(See HELP VERIFY MARKUP for the markup language used in database");
+H("comments.)");
+H("");
+H("Syntax:  MARKUP <inpfile> <outfile> [/ HTML] [/ ALT_HTML] [/ SYMBOLS]");
+H("    [/ LABELS] [/ NUMBER_AFTER_LABEL] [/ BIBLIOGRAPHY] [/ UNDERSCORES]");
+H("    [/ CSS]");
+H("");
+H("Note:  In most cases, use / ALT_HTML / SYMBOLS / LABELS / CSS.");
+H("");
+H("This command will read an arbitrary <inpfile>, normally an HTML file");
+H("with markup, treating it as if it were a giant comment in a database file");
+H("and translating any markup into HTML.  The translated result is written to");
+H("<outfile>.  Note that the file names may be enclosed in single or double");
+H("quotes; this is required if a file name contains slashes, as might be the");
+H("case with Unix file path names.");
+H("");
+H("This command requires that a database source file (such as set.mm) be");
+H("read.  See HELP READ. The math symbols and other information are taken");
+H("from that database.  The use of VERIFY MARKUP * is recommended to help");
+H("ensure the database has no errors in its symbol definitions.");
+H("");
+H("Qualifiers:");
+H("    / HTML (/ ALT_HTML) - use the symbols defined by the htmldef");
+H("        (althtmldef) statements in the $t comment in the .mm database.");
+H("        Usually these are GIF or Unicode math symbols respectively.");
+H("        Exactly one of / HTML and / ALT_HTML must always be specified.");
+H("    / SYMBOLS - process symbols inside backquotes.");
+H("    / LABELS - process labels preceded by tilde.");
+H("    / NUMBER_AFTER_LABEL - add colored statement number after each label.");
+H("    / BIB_REFS - process bibliographic references in square brackets.");
+H("        The file specified by htmlbibliography in the $t comment in the");
+H("        .mm database is checked to be sure the references exist.");
+H("    / UNDERSCORES - process underscores to produce italic text or");
+H("        subscripts.");
+H("    / CSS - add CSS before \"</HEAD>\" in the input file.  The CSS is");
+H("        specified by htmlcss in the $t comment in .mm database.  If");
+H("        \"</HEAD>\" is not present, or the CSS is already present (with");
+H("        an exact match), nothing will be done.");
+H("");
+H("Note:  The existence of GIF files for symbols isn't checked.  Use VERIFY");
+H("MARKUP for that.  However, validity of bibliographical references is");
+H("checked since VERIFY MARKUP can't do that.  If the required file for");
+H("/ BIB_REFS (such as mmset.html) isn't present, a warning will be");
+H("displayed.  To avoid literal \"`\", \"~\", and \"[\" from being");
+H("interpreted by / SYMBOLS, / LABELS, and / BIB_REFS respectively, escape");
+H("them with \"``\", \"~~\", and \"[[\" in the input file.  Literal \"`\"");
+H("must always be escaped even if / SYMBOLS is omitted, because the");
+H("algorithm will still use \"`...`\" to avoid interpreting special");
+H("characters in math symbols.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP EXPLORE");
+H("When you first enter Metamath, you will first want to READ in a Metamath");
+H("source file.  The source file provided for set theory is called set.mm;");
+H("to read it type");
+H("    READ set.mm");
+H("");
+H("You may want to look over the contents of the source file with a text");
+H("editor, to get an idea of what's in it, before starting to use Metamath.");
+H("");
+H("The following commands will help you study the source file statements");
+H("and their proofs.  Use the HELP for the individual commands to get");
+H("more information about them.");
+H("    SEARCH <label-match> \"<symbol-match>\" - Displays statements whose");
+H("        labels match <label-match> and that contain <symbol-match>.");
+H("    SEARCH <label-match> \"<search-string>\" / COMMENTS - Shows statements");
+H("        whose preceding comment contains <search-string>");
+H("    SHOW LABELS <label-match> - Lists all labels matching <label-match>,");
+H("        with * and ? wildcards:  for example \"abc?def*\" will match all");
+H("        labels beginning with \"abc\" followed by any single character");
+H("        followed by \"def\".");
+H("    SHOW STATEMENT <label> / COMMENT - Shows the comment, contents, and");
+H("        logical hypotheses associated with a statement.");
+H("    SHOW PROOF <label> - Shows the proof of a $p statement in various");
+H("        formats, depending on what qualifiers you select.  One of the");
+H("        qualifiers, / TEX, lets you create LaTeX source for the proof.");
+H("        The / DETAILED_STEP qualifier is useful while you're learning how");
+H("        Metamath unifies the sources and targets of a step.  The");
+H("        / STATEMENT_SUMMARY qualifier gives you a quick summary of all");
+H("        the statements referenced in the proof.");
+H("    SHOW TRACE_BACK <label> - Traces a proof back to axioms, depending");
+H("        on various qualifiers you select.");
+H("    SHOW USAGE <label> - Shows what later proofs make use of this");
+H("        statement.");
+H("");
+
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP HTML");
+H("(Note: See HELP WRITE SOURCE for the \"<HTML>\" tag in comments.)");
+H("To create an HTML output file for a $a or $p statement, use");
+H("    SHOW STATEMENT <label> / HTML");
+H("The created web page will include a Description taken from the comment");
+H("that immediately precedes the $a or $p statement.  A warning will be");
+H("issued if this comment is not present.  Optional markup in the comment");
+H("will be processed according to the markup syntax described under HELP");
+H("LANGUAGE, in the \"Inside of comments\" section.  Warnings will be");
+H("issued for any errors in the markup.  Note that all other comments in");
+H("the database are ignored, including comments preceding $e statements.");
+H("");
+H("When <label> has wildcard (* and ?) characters, all statements with");
+H("matching labels will have HTML files produced for them.  Also, when");
+H("<label> starts with a * wildcard character, three additional files,");
+H("mmdefinitions.html, mmtheoremsall.html, and mmascii.html will be");
+H("produced.  Thus:");
+H("    SHOW STATEMENT * / HTML");
+H("will output the complete HTML proof database in the current directory,");
+H("one file per $a and $p statement, along with mmdefinitions.html,");
+H("mmtheoremsall.html, and mmascii.html.  The statement:");
+H("    SHOW STATEMENT *! / HTML");
+H("will produce only mmdefinitions.html, mmtheoremsall.html, and");
+H("mmascii.html, but no other HTML files (because no labels can match \"*!\"");
+H("since \"!\" is illegal in a statement label).  The statement:");
+H("    SHOW STATEMENT ?* / HTML");
+H("will output the complete HTML proof database but will not produce");
+H("mmdefinitions.html, etc.  Note added 30-Jan-06:  The mmtheoremsall.html");
+H("file produced by this command is deprecated and is replaced by the output");
+H("of WRITE THEOREM_LIST.");
+H("");
+H("The HTML definitions for the symbols and and other features are");
+H("specified by statements in a special typesetting comment in the input");
+H("database file.  The typesetting comment is identified by the token \"$t\"");
+H("in the comment, and the typesetting statements run until the next \"$)\":");
+H("   ...  $( ...  $t ................................ $) ...");
+H("                   <-- HTML definitions go here -->");
+H("See the set.mm database file for an extensive example of a $t comment");
+H("illustrating all features described below.  In the HTML definition");
+H("section, C-style comments /* ... */ are recognized.  The main HTML");
+H("specification statements are:");
+H("    htmldef \"<mathtoken>\" as \"<HTML code for mathtoken symbol>\" ;");
+H("                    ...");
+H("    htmldef \"<mathtoken>\" as \"<HTML code for mathtoken symbol>\" ;");
+H("    htmltitle \"<HTML code for title>\" ;");
+H("    htmlhome \"<HTML code for home link>\" ;");
+H("    htmlvarcolor \"<HTML code for variable color list>\" ;");
+H("    htmlbibliography \"<HTML file>\" ;");
+H("        (This <HTML file> is assumed to have a <A NAME=...> tag for each");
+H("        bibliographic reference in the database comments.  For example");
+H("        if \"[Monk]\" occurs in a comment, then \"<A NAME='Monk'>\" must");
+H("        be present in the <HTML file>; if not, a warning message is");
+H("        given.)");
+H("Single or double quotes surround the field strings, and fields too long");
+H("for a line may be broken up into multiple quoted strings connected with");
+H("(whitespace-surrounded) \"+\" signs (no quotes around them).  Inside");
+H("quoted strings, the opposite kind of quote may appear.  If both kinds of");
+H("quotes are needed, use separate quoted strings connected by \"+\".");
+H("Note that the \"$)\" character sequence will flag the end of the");
+H("typesetting Metamath comment even if embedded in quotes (which are not");
+H("meaningful for the Metamath language parser), so such a sequence must be");
+H("broken with \"+\".");
+H("");
+H("The typesetting Metamath comment may also contain LaTeX definitions");
+H("(with \"latexdef\" statements) that are ignored for HTML output.");
+H("");
+H("Several other qualifiers exist.  The command");
+H("    SHOW STATEMENT <label> / ALT_HTML");
+H("does the same as SHOW STATEMENT <label> / HTML, except that the HTML code");
+H("for the symbols is taken from \"althtmldef\" statements instead of");
+H("\"htmldef\" statements in the $(...$t...$) comment.  This is useful when");
+H("an alternate representation of symbols is desired, for example one that");
+H("uses Unicode entities instead of GIF images.  Associated with althtmldef");
+H("are the statements");
+H("    htmldir \"<directory for GIF HTML version>\" ;");
+H("    althtmldir \"<directory for Unicode HTML version>\" ;");
+H("that produce links to the alternate version.");
+H("");
+H("The command");
+H("    SHOW STATEMENT * / BRIEF_HTML");
+H("invokes a special mode that just produces definition and theorem lists");
+H("accompanied by their symbol strings, in a format suitable for copying and");
+H("pasting into another web page.");
+H("");
+H("Finally, the command");
+H("    SHOW STATEMENT * / BRIEF_ALT_HTML");
+H("does the same as SHOW STATEMENT * / BRIEF_HTML for the alternate HTML");
+H("symbol representation.");
+H("");
+H("When two different types of pages need to be produced from a single");
+H("database, such as the Hilbert Space Explorer that extends the Metamath");
+H("Proof Explorer, \"extended\" variables may be declared in the $t comment:");
+H("    exthtmltitle \"<HTML code for title>\" ;");
+H("    exthtmlhome \"<HTML code for home link>\" ;");
+H("    exthtmlbibliography \"<HTML file>\" ;");
+H("When these are declared, you also must declare");
+H("    exthtmllabel \"<label>\" ;");
+H("When the output statement is the one declared with \"exthtmllabel\" or");
+H("a later one, the HTML code assigned to \"exthtmltitle\" and");
+H("\"exthtmlhome\" is used instead of that assigned to \"htmltitle\" and");
+H("\"htmlhome\" respectively.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP LATEX");
+H("See HELP TEX.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP TEX");
+H("Metamath will create a \"turn-key\" LaTeX source file which can be");
+H("immediately compiled and printed using a TeX program.  The TeX program");
+H("must have the following minimum requirements:  the LaTeX style option and");
+H("the AMS font set, available from the American Mathematical Society.");
+H("");
+H("To write out a statement and its proof, use a command sequence similar");
+H("to the following example:");
+H("    (Enter Metamath)");
+H("    READ set.mm");
+H("    OPEN TEX example.tex");
+H("    SHOW STATEMENT uneq2 / TEX");
+H("    SHOW PROOF uneq2 / LEMMON / RENUMBER / TEX");
+H("    CLOSE TEX");
+H("");
+H("The LaTeX symbol definitions should be included in a special comment");
+H("containing a $t token.  See the set.mm file for an example.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP BEEP") || !strcmp(helpCmd, "HELP B");
+H("Syntax:  BEEP");
+H("");
+H("This command will produce a beep.  By typing it ahead after a long-");
+H("running command has started, it will alert you that the command is");
+H("finished. B is an abbreviation for BEEP.");
+H("");
+H("Note: If B is typed at the MM> prompt immediately after the end of a");
+H("multiple-page display paged with \"Press <return> for more...\" prompts,");
+H("then the B will back up to the previous page rather than perform the BEEP");
+H("command.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP QUIT");
+H("Syntax:  QUIT [/ FORCE]");
+H("");
+H("This command is a synonym for EXIT.  See HELP EXIT.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP EXIT");
+H("Syntax:  EXIT [/ FORCE]");
+H("");
+H("This command exits from Metamath.  If there have been changes to the");
+H("database with the SAVE PROOF or SAVE NEW_PROOF commands, you will be given");
+H("an opportunity to WRITE SOURCE to permanently save the changes.");
+H("");
+H("(In Proof Assistant mode) The EXIT command will return to the MM> prompt.");
+H("If there were changes to the proof, you will be given an opportunity to");
+H("SAVE NEW_PROOF.  In the Proof Assistant, _EXIT_PA is a synonym for EXIT");
+H("that gives an error message outside of the Proof Assistant.  This can be");
+H("useful to prevent scripts from exiting Metamath due to an error entering");
+H("the Proof Assistant.");
+H("");
+H("The QUIT command is a synonym for EXIT.");
+H("");
+H("Optional qualifier:");
+H("    / FORCE - Do not prompt if changes were not saved.  This qualifier is");
+H("        useful in SUBMIT command files (scripts) to ensure predictable");
+H("        behavior.");
+H("");
+H("**Warning**  Pressing CTRL-C will abort the Metamath program");
+H("unconditionally.  This means any unsaved work will be lost.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP _EXIT_PA");
+H("Syntax:  _EXIT_PA [/ FORCE]");
+H("");
+H("This command is a synonym for EXIT inside the Proof Assistant but will");
+H("generate an error message (and otherwise have no effect) elsewhere.  It");
+H("can help prevent accidentally exiting Metamath when a script fails to");
+H("enter the Proof Assistant (PROVE command).  See HELP EXIT.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP READ");
+H("Syntax:  READ <file> [/ VERIFY]");
+H("");
+H("This command will read in a Metamath language source file and any included");
+H("files.  Normally it will be the first thing you do when entering Metamath.");
+H("Statement syntax is checked, but proof syntax is not checked.");
+H("Note that the file name may be enclosed in single or double quotes;");
+H("this is useful if the file name contains slashes, as might be the case");
+H("under Unix.");
+H("");
+H("If you are getting an \"?Expected VERIFY or NOVERIFY\" error when trying");
+H("to read a Unix file name with slashes, you probably haven't quoted it.");
+H("");
+H("You need nested quotes when a Unix file name with slashes is a Metamath");
+H("invocation argument.  See HELP INVOKE for examples.");
+H("");
+H("If you are prompted for the file name (by pressing <return> after READ)");
+H("you should _not_ put quotes around it, even if it is a Unix file name.");
+H("with slashes.");
+H("");
+H("Optional qualifier:");
+H("    / VERIFY - Verify all proofs as the database is read in.  This");
+H("        qualifier will slow down reading in the file.");
+H("");
+H("See also HELP ERASE.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP ERASE");
+H("Syntax:  ERASE");
+H("");
+H("This command will delete the database if one was READ in.  It does not");
+H("affect parameters listed in SHOW SETTINGS that are unrelated to the");
+H("database.  The user will be prompted for confirmation if the database was");
+H("changed but not saved with WRITE SOURCE.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP OPEN LOG");
+H("Syntax:  OPEN LOG <file>");
+H("");
+H("This command will open a log file that will store everything you see on");
+H("the screen.  It is useful to help recovery from a mistake in a long Proof");
+H("Assistant session, or to document bugs.");
+H("");
+H("The screen output of operating system commands (HELP SYSTEM) is not");
+H("logged.");
+H("");
+H("The log file can be closed with CLOSE LOG.  It will automatically be");
+H("closed upon exiting Metamath.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP CLOSE LOG");
+H("Syntax:  CLOSE LOG");
+H("");
+H("The CLOSE LOG command closes a log file if one is open.  See also OPEN");
+H("LOG.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP OPEN TEX");
+H("Syntax:  OPEN TEX <file> [/ NO_HEADER] [/ OLD_TEX]");
+H("");
+H("This command opens a file for writing LaTeX source and writes a LaTeX");
+H("header to the file.  LaTeX source can be written with the SHOW PROOF,");
+H("SHOW NEW_PROOF, and SHOW STATEMENT commands using the / TEX qualifier.");
+H("The mapping to LaTeX symbols is defined in a special comment containing");
+H("a $t token.  See the set.mm database file for an example.");
+H("");
+H("To format and print the LaTeX source, you will need the TeX program,");
+H("which is standard in most Linux, Unix, and MacOSX installations and");
+H("available for Windows.");
+H("");
+H("Optional qualifiers:");
+H("    / NO_HEADER - This qualifier prevents a standard LaTeX header and");
+H("        trailer from being included with the output LaTeX code.");
+H("    / OLD_TEX - This qualifier produces a header with macro definitions");
+H("        for use with / OLD_TEX qualifiers of SHOW STATEMENT and SHOW");
+H("        PROOF.  It is obsolete and will be removed eventually.");
+H("");
+H("See also CLOSE TEX.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP CLOSE TEX");
+H("Syntax:  CLOSE TEX");
+H("");
+H("This command writes a trailer to any LaTeX file that was opened with OPEN");
+H("TEX (unless / NO_HEADER was used with OPEN) and closes the LaTeX file.");
+H("");
+H("See also OPEN TEX.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP TOOLS");
+H("Syntax:  TOOLS");
+H("");
+H("This command invokes a utility to manipulate ASCII text files.  Type TOOLS");
+H("to enter this utility, which has its own HELP commands.  Once you are");
+H("inside, EXIT will return to Metamath.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP WRITE SOURCE");
+H("Syntax:  WRITE SOURCE <filename> [/ FORMAT] [/ REWRAP] [/ SPLIT]");
+H("           [/ KEEP_INCLUDES] [/ NO_VERSIONING]");
+H("");
+H("This command will write the contents of a Metamath source (previously");
+H("read with READ) into a file");
+H("(or multiple files if / SPLIT is specified).");
+H("");
+H("Optional qualifiers:");
+H("    / FORMAT - Reformats statements and comments according to the");
+H("        convention used in the set.mm database.  Proofs are not");
+H("        reformatted; use SAVE PROOF * / COMPRESSED to do that.");
+H("        Incidentally, SAVE PROOF honors the SET WIDTH parameter");
+H("        currently in effect.");
+H("    / REWRAP - Same as / FORMAT but more aggressive.  It unwraps the");
+H("        lines in the comment before each $a and $p statement, then it");
+H("        rewraps the line.  You should compare the output to the original");
+H("        to make sure that the desired effect results; if not, go back to");
+H("        the original source.  The wrapped line length honors the");
+H("        SET WIDTH parameter currently in effect.  Note 1: The only lines");
+H("        that are rewrapped are those in comments immediately preceding a");
+H("        $a or $p statement.  In particular, formulas (such as the");
+H("        argument of a $p statement) are not rewrapped.  Note 2: A comment");
+H("        containing the string \"<HTML>\" is not rewrapped (see also");
+H("        HELP LANGUAGE and");
+H("   https://github.com/metamath/set.mm/pull/1695#issuecomment-652129129 .)");
+H("    / SPLIT - Files included in the source with $[ <inclfile> $] will be");
+H("        written out separately instead of included in a single output");
+H("        file.  The name of each separately written included file will be");
+H("        <inclfile> argument of its inclusion command.  See the");
+H("        21-Dec-2017 (file inclusion) entry in");
+H("        http://us.metamath.org/mpeuni/mmnotes.txt for further details");
+H("    / KEEP_INCLUDES - If a source file has includes but is written as a");
+H("        single file by omitting / SPLIT, by default the included files will");
+H("        be deleted (actually just renamed with a ~1 suffix unless");
+H("        / NO_VERSIONING is specified) to prevent the possibly confusing");
+H("        source duplication in both the output file and the included file.");
+H("        The / KEEP_INCLUDES qualifier will prevent this deletion.");
+H("    / NO_VERSIONING - Backup files suffixed with ~1 are not created.");
+H("    / EXTRACT <label-match> - Write to the output file only those");
+H("        statements needed to support and prove the statements matching");
+H("        <label-match>.  See HELP SEARCH for the format of <label-match>.");
+H("        A single output file is created.  Note that all includes");
+H("        \"$[...$]\", all commented includes \"$( Begin $[...\" etc.,");
+H("        and all \"$j\" comments will be discarded.  / EXTRACT and / SPLIT");
+H("        may not be used together.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP WRITE THEOREM_LIST");
+H("Syntax:  WRITE THEOREM_LIST [/ THEOREMS_PER_PAGE <number>] [/ SHOW_LEMMAS]");
+H("               [/ HTML] [/ALT_HTML] [/ NO_VERSIONING]");
+H("");
+H("Optional qualifiers:");
+H("    / THEOREMS_PER_PAGE <number> - specifies the number of theorems to");
+H("        write per output file");
+H("    / SHOW_LEMMAS - show the math content of lemmas (by default, the math");
+H("        content of theorems whose comment begins \"Lemma for\" is");
+H("        suppressed to reduce the web page file size).");
+H("    / HTML (/ ALT_HTML) - use the symbols defined by the htmldef");
+H("        (althtmldef) statements in the $t comment in the .mm database.");
+H("        Usually these are GIF or Unicode math symbols respectively.");
+H("    / NO_VERSIONING - Backup files suffixed with ~1 are not created.");
+H("");
+H("This command writes a list of the $a and $p statements in the database");
+H("into web page files called \"mmtheorems.html\", \"mmtheorems1.html\",");
+H("\"mmtheorems2.html\", etc.  If / THEOREMS_PER_PAGE is omitted, the number");
+H("of theorems (and other statements) per page defaults to 100.");
+H("[Warning:  A value other than 100 for THEOREMS_PER_PAGE will cause the");
+H("list to become out of sync with the \"Related theorems\" links on the");
+H("web pages for individual theorems.  This may be corrected in a future");
+H("version.]");
+H("");
+H("If neither / HTML nor / ALT_HTML is specified, the output will default to");
+H("GIF format unless ALT_HTML was previously set as shown in SHOW SETTINGS.");
+H("");
+H("The first output file, \"mmtheorems.html\", includes a Table of Contents.");
+H("An entry is triggered in the database by \"$(\" immediately followed by a");
+H("new line starting with \"####\" (the marker for a major part header),");
+H("\"#*#*\" (for a section header), \"=-=-\" (for a subsection header), or");
+H("\"-.-.\" (for a subsubsection header).  The line following the marker line");
+H("will be used for the table of contents entry, after trimming spaces.  The");
+H("next line should be another (closing) marker line.  Any text after that");
+H("but before the closing \"$)\", such as an extended description of the");
+H("section, will be included on the mmtheoremsNNN.html page.  In between two");
+H("successive statements that generate web pages (i.e. $a and $p statements),");
+H("only the last of each header type (part, section, subsection,");
+H("subsubsection) will be used, and any smaller header type before a larger");
+H("header type (e.g. a subsection header before a section header) will be");
+H("ignored.  See the set.mm database file for examples.");
+H("");
+H("[Warning: For the above matching, white space is NOT ignored.  There");
+H("should be 0 or 1 spaces between \"$(\" and the end of the line.  This may");
+H("be allowed in a future version.]");
+H("");
+H("Note:  To create the files mmdefinitions.html and mmascii.html, use");
+H("SHOW STATEMENT *! / HTML.  See HELP HTML.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP BIBLIOGRAPHY");
+H("See HELP WRITE BIBLIOGRAPHY.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP WRITE BIBLIOGRAPHY");
+H("Syntax:  WRITE BIBLIOGRAPHY <filename>");
+H("");
+H("This command reads an HTML bibliographic cross-reference file, normally");
+H("called mmbiblio.html, and updates it per the bibliographic links in");
+H("the database comments.  The file is updated between the HTML comment");
+H("lines \"<!-- #START# -->\" and \"<!-- #END# -->\".  Any previous content");
+H("between these two lines is discarded.  The original input file is renamed");
+H("<filename>~1");
+H("");
+H("A name in square brackets in a statement's description (the comment");
+H("before a $a or $p statement) indicates a bibliographic reference.  The");
+H("full reference must be of the form");
+H("");
+H("    <keyword> <identifier> <noise word(s)> [<author>] p. <nnn>");
+H("");
+H("There should be no comma between \"[<author>]\" and \"p.\".  Whitespace,");
+H("comma, period, or semicolon should follow <nnn>.  Example:");
+H("");
+H("    Theorem 3.1 of [Monk] p. 22,");
+H("");
+H("The <keyword>, which is not case-sensitive, must be one of the following:");
+H("");
+H("    Axiom, Chapter, Claim, Compare, Conclusion, Condition, Conjecture,");
+H("    Corollary, Definition, Equation, Example, Exercise, Fact, Figure,");
+H("    Introduction, Item, Lemma, Lemmas, Line, Lines, Notation, Note,");
+H("    Observation, Paragraph, Part, Postulate, Problem, Proof, Property,");
+H("    Proposition, Remark, Result, Rule, Scheme, Scolia, Scolion, Section,");
+H("    Statement, Subsection, Table, Theorem");
+H("");
+H("The <identifier> is optional, as in for example \"Remark in [Monk] p. 22\".");
+H("");
+H("The <noise word(s)> are zero or more from the list:  from, in, of, on.");
+H("These are ignored when generating the bibliographic cross-reference.");
+H("");
+H("The <author> must be present in the file identified with the");
+H("htmlbibliography assignment (e.g. mmset.html) in the database $t comment,");
+H("in the form <A NAME=\"<author>\"></A> e.g. <A NAME=\"Monk\"></A>.");
+H("");
+H("The <nnn> may be any alphanumeric string such as an integer or Roman");
+H("numeral.");
+H("");
+H("The <keyword> and <noise word(s)> lists are hard-coded into the program.");
+H("Contact Norman Megill if you need to add to these lists.");
+H("");
+H("Additional notes:  1. The bibliographic reference in square brackets may");
+H("not contain whitespace.  If it does, the bracketed text will be treated");
+H("like normal text and not assumed to be a bibliographic reference.");
+H("2. A double opening bracket \"[[\" escapes the bracket and treats the");
+H("bracketed text as normal text, and a single bracket is rendered on the");
+H("web page output.  The closing bracket need not be escaped, and \"]]\"");
+H("will cause a double bracket to be rendered on the web page.");
+H("");
+H("See also");
+H("https://github.com/metamath/set.mm/pull/1761#issuecomment-672433658");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP WRITE RECENT_ADDITIONS");
+H("Syntax:  WRITE RECENT_ADDITIONS <filename>");
+H("");
+H("Optional qualifier:");
+H("    / LIMIT <number> - specifies the number of most recent theorems to");
+H("        write to the output file");
+H("    / HTML (/ ALT_HTML) - use GIF (Unicode) math symbols.");
+H("");
+H("This command reads an HTML Recent Additions file, normally called");
+H("\"mmrecent.html\", and updates it with the descriptions of the most recently");
+H("added $a and $p statements to the database.  If / LIMIT is omitted, the");
+H("number of theorems written defaults to 100.  The file is updated between the");
+H("HTML comment lines \"<!-- #START# -->\" and \"<!-- #END# -->\".  The");
+H("original input file is renamed to \"<filename>~1\"");
+H("");
+H("The date used for comparison is the most recent \"(Contributed by...)\",");
+H("\"(Revised by...)\", and \"(Proof shortened by...)\" date in the comment");
+H("immediately preceding the statement.");
+H("");
+H("If neither / HTML nor / ALT_HTML is specified, the output will default to");
+H("GIF format unless ALT_HTML was previously set as shown in SHOW SETTINGS.");
+H("");
+
+
+free_vstring(saveHelpCmd); /* Deallocate memory */
+return;
+
+} /* help1 */
diff --git a/mmhlpa.h b/src/mmhlpa.h
similarity index 85%
rename from mmhlpa.h
rename to src/mmhlpa.h
index 24fa3b4..85d6a7c 100644
--- a/mmhlpa.h
+++ b/src/mmhlpa.h
@@ -1,15 +1,19 @@
-/*****************************************************************************/
-/*        Copyright (C) 2005  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMHLPA_H_
-#define METAMATH_MMHLPA_H_
-
-#include "mmvstr.h"
-
-void help0(vstring helpCmd);
-void help1(vstring helpCmd);
-
-#endif /* METAMATH_MMHLPA_H_ */
+/*****************************************************************************/
+/*        Copyright (C) 2005  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMHLPA_H_
+#define METAMATH_MMHLPA_H_
+
+/*! \file */
+
+#include "mmvstr.h"
+
+/*! help0 is mostly for TOOLS help */
+void help0(vstring helpCmd);
+/*! \note help1 should contain Metamath help */
+void help1(vstring helpCmd);
+
+#endif /* METAMATH_MMHLPA_H_ */
diff --git a/mmhlpb.c b/src/mmhlpb.c
similarity index 92%
rename from mmhlpb.c
rename to src/mmhlpb.c
index 40bfdb8..9e07dd1 100644
--- a/mmhlpb.c
+++ b/src/mmhlpb.c
@@ -1,1479 +1,1403 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* Part 2 of help file for Metamath */
-
-/* To add a new help entry, you must add the command syntax to mmcmdl.c
-   as well as adding it here. */
-
-#include <string.h>
-#include <stdio.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mmcmds.h"
-#include "mmhlpb.h"
-
-void help2(vstring helpCmd)
-{
-
-/* 5-Sep-2012 nm */
-vstring saveHelpCmd = "";
-/* help2() may be called with a temporarily allocated argument (left(),
-   cat(), etc.), and the let()s in the eventual print2() calls will
-   deallocate and possibly corrupt helpCmd.  So, we grab a non-temporarily
-   allocated copy here.  (And after this let(), helpCmd will become invalid
-   for the same reason.)  */
-let(&saveHelpCmd, helpCmd);
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP");
-H("Welcome to Metamath.  Here are some general guidelines.");
-H("");
-H("To make the most effective use of Metamath, you should become familiar");
-H("with the Metamath book.  In particular, you will need to learn");
-H("the syntax of the Metamath language.");
-H("");
-H("For help using the command line, type HELP CLI.");
-H("For help invoking Metamath, type HELP INVOKE.");
-H("For a summary of the Metamath language, type HELP LANGUAGE.");
-H("For a summary of comment markup, type HELP VERIFY MARKUP.");
-H("For help getting started, type HELP DEMO.");
-H("For help exploring the data base, type HELP EXPLORE.");
-H("For help creating a LaTeX file, type HELP TEX.");
-H("For help creating Web pages, type HELP HTML.");
-H("For help proving new theorems, type HELP PROOF_ASSISTANT.");
-H("For a list of help topics, type HELP ? (to force an error message).");
-H("For current program settings, type SHOW SETTINGS.");
-H("For a simple but general-purpose ASCII file manipulator, type TOOLS.");
-H("To exit Metamath, type EXIT (or its synonym QUIT).");
-H("");
-H(cat("If you need technical support, contact Norman Megill at nm",
-    "@", "alum.mit.edu.", NULL));
-H("Copyright (C) 2020 Norman Megill  License terms:  GPL 2.0 or later");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP COMMENTS");
-H("Comment markup is described near the end of HELP LANGUAGE.  See also");
-H("HELP HTML for the $t comment and HTML definitions.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP INVOKE");
-H("To invoke Metamath from a Unix/Linux/MacOSX prompt, assuming that the");
-H("Metamath program is in the current directory, type");
-H("");
-H("  bash$ ./metamath");
-H("");
-H("To invoke Metamath from a Windows DOS or Command Prompt, assuming that");
-H("the Metamath program is in the current directory (or in a directory");
-H("included in the Path system environment variable), type");
-H("");
-H("  C:\\metamath>metamath");
-H("");
-H("To use command-line arguments at invocation, the command-line arguments");
-H("should be a list of Metamath commands, surrounded by quotes if they");
-H("contain spaces.  In Windows DOS, the surrounding quotes must be double");
-H("(not single) quotes.  For example, to read the database set.mm, verify");
-H("all proofs, and exit the program, type (under Unix)");
-H("");
-H("  bash$ ./metamath 'read set.mm' 'verify proof *' exit");
-H("");
-H("Note that in Unix, any directory path with /'s must be surrounded by");
-H("quotes so Metamath will not interpret the / as a command qualifier.  So");
-H("if set.mm is in the /tmp directory, use for the above example");
-H("");
-H("  bash$ ./metamath 'read \"/tmp/set.mm\"' 'verify proof *' exit");
-H("");
-H("For convenience, if the command-line has one argument and no spaces in");
-H("the argument, the command is implicitly assumed to be READ.  In this one");
-H("special case, /'s are not interpreted as command qualifiers, so you don't");
-H("need quotes around a Unix file name.  Thus");
-H("");
-H("  bash$ ./metamath /tmp/set.mm");
-H("");
-H("and");
-H("");
-H("  bash$ ./metamath \"read '/tmp/set.mm'\"");
-H("");
-H("are equivalent.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW MEMORY");
-H("Syntax:  SHOW MEMORY");
-H("");
-H("This command shows the available memory left.  It is not meaningful");
-H("on modern machines with virtual memory.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW SETTINGS");
-H("Syntax:  SHOW SETTINGS");
-H("");
-H("This command shows the state of various parameters.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW ELAPSED_TIME");
-H("Syntax:  SHOW ELAPSED_TIME");
-H("");
-H("This command shows the time elapsed in the session and from any");
-H("previous use of SHOW ELAPSED_TIME.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW LABELS");
-H("Syntax:  SHOW LABELS <label-match> [/ ALL] [/ LINEAR]");
-H("");
-H("This command shows the labels of $a and $p statements that match");
-H("<label-match>.  <label-match> may contain * and ? wildcard characters;");
-H("see HELP SEARCH for wildcard matching rules.");
-H("");
-H("Optional qualifier:");
-H("    / ALL - Include matches for $e and $f statement labels.");
-H("    / LINEAR - Display only one label per line.  This can be useful for");
-H("        building scripts in conjunction with the TOOLS utility.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW DISCOURAGED");
-H("Syntax:  SHOW DISCOURAGED");
-H("");
-H("This command shows the usage and proof statistics for statements with");
-H("\"(Proof modification is discouraged.)\" and \"(New usage is");
-H("discouraged.)\" markup tags in their description comments.  The output");
-H("is intended to be used by scripts that compare a modified .mm file");
-H("to a previous version.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW SOURCE");
-H("Syntax:  SHOW SOURCE <label>");
-H("");
-H("This command shows the ASCII source code associated with a statement.");
-H("Normally you should use SHOW STATEMENT for a more meaningful display,");
-H("but SHOW SOURCE can be used to see statements with multiple comments");
-H("and to see the exact content of the Metamath database.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW STATEMENT");
-/* 14-Sep-2010 nm Added OLD_TEX */
-H(
-"Syntax:  SHOW STATEMENT <label-match> [/ COMMENT] [/ FULL] [/ TEX]");
-H("             [/ OLD_TEX] [/ HTML] [/ ALT_HTML] [/ BRIEF_HTML]");
-H("             [/ BRIEF_ALT_HTML] [/ NO_VERSIONING] [/ MNEMONICS]");
-H("");
-H("This command provides information about a statement.  Only statements");
-H("that have labels ($f, $e, $a, and $p) may be specified. <label-match>");
-H("may contain * and ? wildcard characters; see HELP SEARCH for wildcard");
-H("matching rules.");
-H("");
-H("In Proof Assistant mode (MM-PA prompt), the symbol \"=\" is a synomym");
-H("for the label of the statement being proved.  Thus SHOW STATEMENT = will");
-H("display the statement being proved.");
-H("");
-H("By default, only the statement and its $e hypotheses are shown, and if");
-H("the label has wildcards, only $a and $p statements are shown.");
-H("");
-H("Optional qualifiers (only one qualifier at a time is allowed):");
-H("    / COMMENT - This qualifier includes the comment that immediately");
-H("        precedes the statement.");
-H("    / FULL - Show complete information about each statement, and show all");
-H("        statements matching <label> (including $e and $f statements).");
-H("    / TEX - This qualifier will write the statement information to the");
-H("        LaTeX file previously opened with OPEN TEX.");
-/* 14-Sep-2010 nm Added OLD_TEX */
-H("    / OLD_TEX - Same as / TEX, except that LaTeX macros are used to fit");
-H("        equations into line.  This mode is obsolete and will be");
-H("        removed eventually.");
-H("    / HTML - This qualifier invokes a special mode of SHOW STATEMENT which");
-H("        creates a Web page for the statement.  It may not be used with");
-H("        any other qualifier.  See HELP HTML for more information.");
-H("    / ALT_HTML, / BRIEF_HTML, / BRIEF_ALT_HTML - See HELP HTML for more");
-H("        information on these.");
-H("    / NO_VERSIONING - When used with / HTML or the 3 HTML qualifiers");
-H("        above, a backup file suffixed with ~1 is not created (i.e. the");
-H("        previous version is overwritten).");
-H("    / TIME - When used with / HTML or the 3 HTML qualifiers, prints");
-H("        the run time used by each statement.");
-/* 12-May-2009 nm  Added MNEMONICS */
-H("    / MNEMONICS - Produces the output file mnemosyne.txt for use with");
-H("        Mnemosyne http://www.mnemosyne-proj.org/principles.php.  Should");
-H("        not be used with any other qualifier.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW PROOF");
-H("Syntax:  SHOW PROOF <label-match> [<qualifiers (see below)>]");
-H("");
-H("This command displays the proof of the specified $p statement various");
-H("formats.  The <label-match> may contain \"*\" (0-or-more-character match) and");
-H("\"?\" (single-character match) wildcard characters to match multiple");
-H("statements.  See HELP SEARCH for other label matching conventions.");
-H("Without any qualifiers, only the logical steps will be shown (i.e.");
-H("syntax construction steps will be omitted), in indented format.");
-H("");
-H("Note that a compressed proof will have references to repeated parts of");
-H("the proof with \"local labels\" prefixed with \"@\".  To show all steps");
-H("in the original RPN proof, you must SAVE PROOF <label-match> / NORMAL before");
-H("using SHOW PROOF.");
-H("");
-H("Most of the time, you will use");
-H("    SHOW PROOF <label-match>");
-H("to see just the proof steps corresponding to logical deduction.");
-H("");
-H("Optional qualifiers:");
-H("    / ESSENTIAL - the proof tree is trimmed of all $f hypotheses before");
-H("        being displayed.  (This is the default, and it is redundant to");
-H("        specify it.)");
-H("    / ALL - the proof tree is not trimmed of all $f hypotheses before");
-H("        being displayed.  / ESSENTIAL and / ALL are mutually exclusive.");
-H("    / FROM_STEP <step> - the display starts at the specified step.  If");
-H("        this qualifier is omitted, the display starts at the first step.");
-H("    / TO_STEP <step> - the display ends at the specified step.  If this");
-H("        qualifier is omitted, the display ends at the last step.");
-H("    / DEPTH <number> - Only steps at less than the specified proof");
-H("        tree depth are displayed.  Useful for obtaining an overview of");
-H("        the proof.");
-H("    / REVERSE - the steps are displayed in reverse order.");
-H("    / RENUMBER - when used with / ESSENTIAL, the steps are renumbered");
-H("        to correspond only to the essential steps.");
-H("    / TEX - the proof is converted to LaTeX and stored in the file opened");
-H("        with OPEN TEX.  Tip:  SET WIDTH 120 (or so) to to fit equations");
-H("        to LaTeX line.  Then use SHOW PROOF / TEX / LEMMON / RENUMBER.");
-H("    / OLD_TEX - same as TEX but uses macros to fit line.  Obsolete and");
-H("        will be removed eventually.");
-H("    / LEMMON - The proof is displayed in a non-indented format known");
-H("        as Lemmon style, with explicit previous step number references.");
-H("        If this qualifier is omitted, steps are indented in a tree format.");
-H("    / START_COLUMN <number> - Overrides the default column at which");
-H("        the formula display starts in a Lemmon style display.  Affects");
-H("        only displays using the / LEMMON qualifier.");
-H("    / NO_REPEATED_STEPS - When a proof step is identical to an earlier");
-H("        step, it will not be repeated.  Instead, a reference to it will be");
-H("        changed to a reference to the earlier step.  In particular,");
-H("        SHOW PROOF <label-match> / LEMMON / RENUMBER / NO_REPEATED_STEPS");
-H("        will have the same proof step numbering as the web page proof");
-H("        generated by SHOW STATEMENT  <label-match> / HTML, rather than");
-H("        the proof step numbering of the indented format");
-H("        SHOW PROOF <label-match> / RENUMBER.  This qualifier affects only");
-H("        displays also using the / LEMMON qualifier.");
-H("    / STATEMENT_SUMMARY - Summarizes all statements (like a brief SHOW");
-H("        STATEMENT) used by the proof.  May not be used with any other");
-H("        qualifier except / ESSENTIAL.");
-H("    / SIZE - Shows size of the proof in the source.  The size depends on");
-H("        how it was last SAVEd (compressed or normal).");
-H("    / DETAILED_STEP <step> - Shows the details of what is happening at");
-H("        a specific proof step.  May not be used with any other qualifier.");
-H("    / NORMAL, / COMPRESSED, / EXPLICIT, / PACKED, / FAST,");
-H("        / OLD_COMPRESSION - These qualifiers are the same as for");
-H("        SAVE PROOF except that the proof is displayed on the screen in");
-H("        a format suitable for manual inclusion in a source file.  See");
-H("        HELP SAVE PROOF.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP MIDI");
-H("Syntax:  MIDI <label> [/ PARAMETER \"<parameter string>\"]");
-H("");
-H("This will create a MIDI sound file for the proof of <label>, where <label>");
-H("is one of the $p statement labels shown with SHOW LABELS.  The <label> may");
-H("contain \"*\" (0-or-more-character match) and \"?\" (single-character");
-H("match) wildcard characters to match multiple statements.  For each matched");
-H("label, a file will be created called <label>.txt which is a MIDI source");
-H("file that can be converted to a MIDI binary file with the \"t2mf\" utility");
-H("that can be obtained at:");
-H("   http://www.hitsquad.com/smm/programs/mf2t/download.shtml");
-H("Note: the MS-DOS version t2mf.exe only handles old-style 8.3 file names,");
-H("so files such as pm2.11.txt are rejected and must be renamed to");
-H("e.g. pm2_11.txt.");
-H("");
-H("The parameters are:");
-H("");
-H("  f = make the tempo fast (default is slow).");
-H("  m = make the tempo medium (default is slow).");
-H("      Both \"f\" and \"m\" should not be specified simultaneously.");
-H("  s = syncopate the melody by silencing repeated notes, using");
-H("      a method selected by whether the \"h\" parameter below is also");
-H("      present (default is no syncopation).");
-H("  h = allow syncopation to hesitate i.e. all notes in a");
-H("      sequence of repeated notes are silenced except the first (default");
-H("      is no hesitation, which means that every other note in a repeated");
-H("      sequence is silenced - this makes it sound slightly more rhythmic).");
-H("      The \"h\" parameter is meaningful only if the \"s\" parameter above");
-H("      is also present.");
-H("  w = use only the white keys on the piano keyboard (default");
-H("      is potentially to use all keys).");
-H("  b = use only the black keys on the piano keyboard (default");
-H("      is all keys).  Both \"w\" and \"b\" should not be specified");
-H("      simultaneously.");
-H("  i = use an increment of one keyboard note per proof");
-H("      indentation level.  The default is to use an automatic increment of");
-H("      up to four notes per level based on the dynamic range of the whole");
-H("      song.");
-H("");
-H("Quotes around the parameter string are optional if it has no spaces.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW NEW_PROOF");
-H("Syntax:  SHOW NEW_PROOF [<qualifiers (see below)]");
-H("");
-H("This command (available only in Proof Assistant mode) displays the proof");
-H("in progress.  It is identical to the SHOW PROOF command, except that the");
-H("statement is not specified (since it is the statement being proved) and");
-H("following qualifiers are not available:");
-H("    / STATEMENT_SUMMARY");
-H("    / DETAILED_STEP");
-H("    / FAST");
-H("");
-H("Also, the following additional qualifiers are available:");
-H("    / UNKNOWN - Shows only steps that have no statement assigned.");
-H("    / NOT_UNIFIED - Shows only steps that have not been unified.");
-H("");
-H("Note that / ALL, / DEPTH, / UNKNOWN, and / NOT_UNIFIED may");
-H("be used in any combination; each of them effectively filters out (or");
-H("\"unfilters\" in the case of / ALL) additional steps from the proof");
-H("display.");
-H("");
-H("See also:  SHOW PROOF");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW USAGE");
-H("Syntax:  SHOW USAGE <label-match> [/ RECURSIVE]");
-H("");
-H("This command lists the statements whose proofs make direct reference to");
-H("the statement(s) specified by <label-match>.  <label-match> may contain *");
-H("and ? wildcard characters; see HELP SEARCH for wildcard matching rules.");
-H("");
-H("Optional qualifiers:");
-H("    / RECURSIVE - Also include include statements whose proof ultimately");
-H("        depend on the statement specified.");
-H("    / ALL - Include $e and $f statements.  Without / ALL, $e and $f");
-H("        statements are excluded when <label-match> contains wildcard");
-H("        characters.");
-H("");
-
-let(&saveHelpCmd, ""); /* Deallocate memory */
-
-return;
-} /* help2 */
-
-
-/* 18-Jul-2015 nm Split up help2 into help2 and help3 so lcc
-   optimizer wouldn't overflow */
-
-
-void help3(vstring helpCmd)
-{
-
-/* 5-Sep-2012 nm */
-vstring saveHelpCmd = "";
-/* help3() may be called with a temporarily allocated argument (left(),
-   cat(), etc.), and the let()s in the eventual print2() calls will
-   deallocate and possibly corrupt helpCmd.  So, we grab a non-temporarily
-   allocated copy here.  (And after this let(), helpCmd will become invalid
-   for the same reason.)  */
-let(&saveHelpCmd, helpCmd);
-
-
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW TRACE_BACK");
-H("Syntax:  SHOW TRACE_BACK <label-match> [/ ESSENTIAL] [/ AXIOMS] [/ TREE]");
-H("             [/ DEPTH <number>] [/ COUNT_STEPS] [/MATCH <label-match>]");
-H("             [/TO <label-match>]");
-H("");
-H("This command lists all statements that the proof of the $p statement(s)");
-H("specified by <label-match> depends on.  <label-match> may contain *");
-H("and ? wildcard characters; see HELP SEARCH for wildcard matching rules.");
-H("");
-H("Optional qualifiers:");
-H("    / ESSENTIAL - Restrict the trace-back to $e hypotheses of proof");
-H("        trees.");
-H("    / AXIOMS - List only the axioms that the proof ultimately depends on.");
-H("    / TREE - Display the trace-back in an indented tree format.");
-H("    / DEPTH - Restrict the / TREE traceback to the specified indentation");
-H("        depth.");
-H("    / COUNT_STEPS - Counts the number of steps the proof would have if");
-H("        fully expanded back to axioms.  If / ESSENTIAL is specified,");
-H("        expansions of floating hypotheses are not counted.  The steps are");
-H("        counted based on how the proof is stored (compressed or normal).");
-H("    / MATCH <label-match> - include only statements matching <label-match>");
-H("        in the output display.  Undisplayed statements are still used to");
-H("        compute the list.  For example, / AXIOMS / MATCH ax-* will show");
-H("        set.mm axioms but not definitions.");
-H("    / TO <label-match> - include only statements  that depend on the");
-H("        <label-match> statement(s).  For example,");
-H("        SHOW TRACE_BACK ac6s / TO ax-reg will list all statements");
-H("        requiring ax-reg that ac6s depends on.  In case there are");
-H("        multiple paths from ac6s back to ax-reg, all statements involved");
-H("        in all paths are listed.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SEARCH");
-H("Syntax:  SEARCH <label-match> \"<symbol-match>\" [/ ALL] [/ COMMENTS]");
-H("             [/ JOIN]");
-H("");
-H("This command searches all $a and $p statements matching <label-match>");
-H("for occurrences of <symbol-match>.");
-H("");
-H("A * in <label-match> matches any zero or more characters in a label.");
-H("");
-H("A ? in <label-match> matches any single character.  For example,");
-H("SEARCH p?4* \"ph <-> ph\" will check statements whose labels have p and 4");
-H("in their first and third character positions, and display them when their");
-H("math strings contain \"ph <-> ph\".");
-H("");
-H("A ~ in <label-match> divides the match into two labels, <from>~<to>, and");
-H("matches statements located in the source file range between <from> and");
-H("<to> inclusive; <from> and <to> may not have * or ? wildcards; if <from>");
-H("(or <to>) is empty, the first (or last) statement will be assumed.");
-H("");
-H("If <label-match> is % (percent sign), it will match all statements with");
-H("proofs that were changed in the current session; cannot be used with");
-H("wildcards.");
-H("");
-H("If <label-match> is = (equals sign), it will match the statement being");
-H("proved or last proved in the Proof Assistant (MM-PA); note that");
-H("\"PROVE =\" is a quick way to return to the previous MM-PA session.");
-H("");
-H("If <label-match> is #nnn e.g. #1234, it will match the numeric statement");
-H("number nnn; digits cannot contain wildcards.");
-H("");
-H("If <label-match> is @nnn e.g. @1234, it will match the web page statement");
-H("number (small colored number) nnn; digits cannot contain wildcards.");
-H("");
-H("Multiple <label-match> forms may be joined with commas e.g.");
-H("SEARCH ab*,cd* ... will match all labels starting with ab or cd.");
-H("");
-H("");
-H("A $* in <symbol-match> matches any sequence of zero or more tokens");
-H("in the statement's math string.");
-H("");
-H("A $? in <symbol-match> matches zero or one character in a math token.");
-H("The quotes surrounding <symbol-match> may be single or double quotes.");
-H("For example,");
-H("SEARCH * 'E. $? A. $? $?$? -> $* E.' using the set.mm database will find");
-H("\"E. x A. y ph -> A. y E. x ph\".  As this example shows, $? is");
-H("particularly useful when you don't know what variable names were used in");
-H("a theorem of interest.");
-H("");
-H("");
-H("Note 1. The first and last characters of <label-match>, if they are not");
-H("wildcards (nor part of ~, =, %, #, or @ forms), will be matched against");
-H("the first and last characters of the label.  In contrast, <symbol-match>");
-H("is a substring match, i.e. it has implicit $* wildcards before and after");
-H("it.");
-H("");
-H("Note 2. An \"unofficial\" <symbol-match> feature is that $ and ? can be");
-H("used instead of $* and $? for brevity provided that no math token");
-H("contains a ? character.");
-H("");
-H("Optional qualifiers:");
-H("    / ALL - Also search $e and $f statements.");
-H("    / COMMENTS - Search the comment that immediately precedes each");
-H("        label-matched statement for <symbol-match>, instead of searching");
-H("        the statement's math string.  In this mode, <symbol-match> is an");
-H("        arbitrary, non-case-sensitive character string.  Wildcards in");
-H("        <symbol-match> are not implemented in this mode.");
-H("    / JOIN - In the case of a $a or $p statement, prepend its $e");
-H("        hypotheses for searching.  / JOIN has no effect in / COMMENTS");
-H("        mode.");
-H("");
-H("See the last section of HELP LET for how to handle quotes and special");
-H("characters.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET ECHO");
-H("Syntax:  SET ECHO ON or SET ECHO OFF");
-H("");
-H("The SET ECHO ON command will cause command lines to be echoed with any");
-H("abbreviations expanded.  While learning the Metamath commands, this");
-H("feature will show you the exact command that your abbreviated input");
-H("corresponds to.  This is also useful to assist creating robust command");
-H("files (see HELP SUBMIT) from your log file (see HELP OPEN LOG).  To make");
-H("it easier to extract these lines, \"!\" (which you will discard) is");
-H("prepended to each echoed command line.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET SCROLL");
-H("Syntax:  SET SCROLL PROMPTED or SET SCROLL CONTINUOUS");
-H("");
-H("The Metamath command line interface starts off in the PROMPTED mode,");
-H("which means that you will prompted to continue or quit after each");
-H("screenful of a long listing.  In CONTINUOUS mode, long listings will be");
-H("scrolled without pausing.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET WIDTH"); /* 18-Nov-05 nm Revised */
-H("Syntax:  SET WIDTH <number>");
-H("");
-H("Metamath assumes the width of your screen is 79 characters.  If your");
-H("screen is wider or narrower, this command lets you to change the screen");
-H("width.  A larger width is advantageous for logging proofs to an output");
-H("file to be printed on a wide printer.  A smaller width may be necessary");
-H("on some terminals; in this case, the wrapping of the information");
-H("messages may sometimes seem somewhat unnatural, however.  In LaTeX, there");
-H("is normally a maximum of 61 characters per line with typewriter font.");
-H("");
-H("Note:  The default width is 79 because Windows Command Prompt issues a");
-H("spurious blank line after an 80-character line (try it!).");
-H("");
-H("Note:  This command was SET SCREEN_WIDTH prior to Version 0.07.9.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET HEIGHT"); /* 18-Nov-05 nm New */
-H("Syntax:  SET HEIGHT <number>");
-H("");
-H("Metamath assumes your screen height is 24 lines of characters.  If your");
-H("screen is taller or shorter, this command lets you to change the number");
-H("of lines at which the display pauses and prompts you to continue.");
-H("");
-
-/* 10-Jul-2015 nm */
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET DISCOURAGEMENT");
-H("Syntax:  SET DISCOURAGEMENT OFF or SET DISCOURAGEMENT ON");
-H("");
-H("By default this is set to ON, which means that statements whose");
-H("description comments have the markup tags \"(New usage is discouraged.)\"");
-H("or \"(Proof modification is discouraged.)\" will be blocked from usage");
-H("or proof modification.  When this setting is OFF, those actions are no");
-H("longer blocked.  This setting is intended only for the convenience of");
-H("advanced users who are intimately familiar with the database, for use");
-H("when maintaining \"discouraged\" statements.  SHOW SETTINGS will show you");
-H("the current value.");
-H("");
-
-/* 14-May-2017 nm */
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET CONTRIBUTOR");
-H("Syntax:  SET CONTRIBUTOR <name>");
-H("");
-H("Specify the contributor name for new \"(Contributed by...\" comment");
-H("markup added by SAVE PROOF or SAVE NEW_PROOF.  Use quotes (' or \")");
-H("around <name> if it contains spaces.  The current contributor is");
-H("displayed by SHOW SETTINGS.");
-H("");
-
-/* 31-Dec-2017 nm */
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET ROOT_DIRECTORY");
-H("Syntax:  SET ROOT_DIRECTORY <directory path>");
-H("");
-H("Specify the directory path (relative to the working directory i.e. the");
-H("directory from which the Metamath program was launched) which will be");
-H("prepended to READ, WRITE SOURCE, and files included with $[...$].");
-H("Enclose <directory path> in single or double quotes if the path contains");
-H("\"/\".  A trailing \"/\" will be added automatically if missing.  The");
-H("current directory path is displayed by SHOW SETTINGS.");
-H("");
-H("Use a quoted space (' ' or \" \") for <directory path> if you want to");
-H("reset it to be the working directory.");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET UNIFICATION_TIMEOUT");
-H("Syntax:  SET UNIFICATION_TIMEOUT <number>");
-H("");
-H("(This command affects the Proof Assistant only.)");
-H("");
-H("Sometimes the Proof Assistant will inform you that a unification timeout");
-H("occurred.  This may happen when you try to UNIFY formulas with many");
-H("unknown variables, since the time to compute unifications may grow");
-H("exponentially with the number of variables.  If you want Metamath to try");
-H("harder (and you're willing to wait longer) you may increase this");
-H("parameter.  SHOW SETTINGS will show you the current value.");
-H("");
-H("Often, a better solution to resolve a unification timeout is to manually");
-H("assign some or all of the unknowns (see HELP LET) then try to unify");
-H("again.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET EMPTY_SUBSTITUTION");
-H("Syntax:  SET EMPTY_SUBSTITUTION ON or SET EMPTY_SUBSTITUTION OFF");
-H("");
-H("(This command affects the Proof Assistant only.  It may be issued");
-H("outside of the Proof Assistant.)");
-H("");
-H("The Metamath language allows variables to be substituted with empty");
-H("symbol sequences.  However, in most formal systems this will never happen");
-H("in a valid proof.  Allowing for this possibility increases the likelihood");
-H("of ambiguous unifications during proof creation.  The default is that");
-H("empty substitutions are not allowed; for formal systems requiring them,");
-H("you must SET EMPTY_SUBSTITUTION ON.  Note that empty substitutions are");
-H("always permissible in proof verification (VERIFY PROOF...) outside the");
-H("Proof Assistant.  (See the MIU system in the Metamath book for an example");
-H("of a system needing empty substitutions; another example would be a");
-H("system that implements a Deduction Rule and in which deductions from");
-H("empty assumption lists would be permissible.)");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET SEARCH_LIMIT");
-H("Syntax:  SET SEARCH_LIMIT <number>");
-H("");
-H("(This command affects the Proof Assistant only.)");
-H("");
-H("This command sets a parameter that determines when the IMPROVE command");
-H("in Proof Assistant mode gives up.  If you want IMPROVE to search harder,");
-H("you may increase it.  The SHOW SETTINGS command tells you its current");
-H("value.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET JEREMY_HENTY_FILTER");
-H("Syntax:  SET JEREMY_HENTY_FILTER ON or SET JEREMY_HENTY_FILTER OFF");
-H("");
-H("(This command affects the Proof Assistant only.)");
-H("");
-H("The \"Henty filter\" is an ingenious algorithm suggested by Jeremy Henty");
-H("that reduces the number of ambiguous unifications by eliminating");
-H("\"equivalent\" ones in a sense defined by Henty.  Normally this filter");
-H("is ON, and the only reason to turn it off would be for debugging.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP VERIFY PROOF");
-H("Syntax:  VERIFY PROOF <label-match> [/ SYNTAX_ONLY]");
-H("");
-H("This command verifies the proofs of the specified statements.");
-H("<label-match> may contain * and ? wildcard characters to verify more than");
-H("one proof; for example \"abc?def*\" will match all labels beginning with");
-H("\"abc\" followed by any single character followed by \"def\".");
-H("VERIFY PROOF * will verify all proofs in the database.");
-H("See HELP SEARCH for complete wildcard matching rules.");
-H("");
-H("Optional qualifier:");
-H("    / SYNTAX_ONLY - This qualifier will perform a check of syntax and RPN");
-H("        stack violations only.  It will not verify that the proof is");
-H("        correct.");
-H("");
-H("Note: READ, followed by VERIFY PROOF *, will ensure the database is free");
-H("from errors in Metamath language but will not check the markup language");
-H("in comments.  See HELP VERIFY MARKUP.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP VERIFY MARKUP");
-H("Syntax:  VERIFY MARKUP <label-match> [/ DATE_SKIP] [/ TOP_DATE_SKIP]");
-H("            [/ FILE_SKIP] [/ UNDERSCORE_SKIP] [/ MATHBOX_SKIP] [/VERBOSE]");
-H("");
-H("This command checks comment markup and other informal conventions we have");
-H("adopted.  It error-checks the latexdef, htmldef, and althtmldef statements");
-H("in the $t statement of a Metamath source file.  It error-checks any `...`,");
-H("~ <label>, and [bibref] markups in statement descriptions.  It checks that");
-H("$p and $a statements have the same content when their labels start with");
-H("\"ax\" and \"ax-\" respectively but are otherwise identical, for example");
-H("ax4 and ax-4.  It verifies the date consistency of \"(Contributed by...)\",");
-H("\"(Revised by...)\", and \"(Proof shortened by...)\" tags in the comment");
-H("above each $a and $p statement.  See HELP SEARCH for <label-match> rules.");
-H("");
-H("Optional qualifiers:");
-H("    / DATE_SKIP - This qualifier will skip date consistency checking,");
-H("        which is usually not required for databases other than set.mm");
-H("    / TOP_DATE_SKIP - This qualifier will check date consistency except");
-H("        that the version date at the top of the database file will not");
-H("        be checked.  Only one of / DATE_SKIP and / TOP_DATE_SKIP may be");
-H("        specified.");
-H("    / FILE_SKIP - This qualifier will skip checks that require");
-H("        external files to be present, such as checking GIF existence and");
-H("        bibliographic links to mmset.html or equivalent.  It is useful");
-H("        for doing a quick check from a directory without these files");
-       /* 25-Jun-2020 nm Added UNDERSCORE_SKIP */
-H("    / UNDERSCORE_SKIP - This qualifier will skip warnings for labels");
-H("        containing underscore (\"_\") characters.  Although they are");
-H("        legal per the Metamath spec, they may cause ambiguities with");
-H("        certain translators (such as to MM0) that convert \"-\" to \"_\".");
-H("        bibliographic links to mmset.html or equivalent.  It is useful");
-H("        for doing a quick check from a directory without these files");
-       /* 17-Jul-2020 nm Added MATHBOX_SKIP */
-H("    / MATHBOX_SKIP - This qualifier will skip checking for mathbox");
-H("        independence i.e. that no mathbox proof references a statement");
-H("        in another (earlier) mathbox.");
-H("    / VERBOSE - Provides more information.  Currently it provides a list");
-H("        of axXXX vs. ax-XXX matches.");
-H("");
-H("See also HELP LANGUAGE, HELP HTML, HELP WRITE THEOREM_LIST, and");
-H("HELP SET DISCOURAGEMENT for more details on the markup syntax.");
-H("See the 11-May-2016 (\"is discouraged\"), 14-May-2017 (date format), and");
-H("21-Dec-2017 (file inclusion) entries in");
-H("http://us.metamath.org/mpeuni/mmnotes.txt for further details on several");
-H("kinds of markup.  See HELP WRITE THEOREM_LIST for format of section headers.");
-H("");
-H("For help with modularization tags such as \"$( Begin $[ set-header.mm $] $)\",");
-H("see the 21-Dec-2017 entry in http://us.metamath.org/mpeuni/mmnotes.txt .");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SUBMIT");
-H("Syntax:  SUBMIT <filename> [/ SILENT]");
-H("");
-H("This command causes further command lines to be taken from the specified");
-H("command file.  Note that any line beginning with an exclamation point (!)");
-H("is treated as a comment (i.e. ignored).  Also note that the scrolling");
-H("of the screen output is continuous, so you may want to open a log file");
-H("(see HELP OPEN LOG) to record the results that fly by on the screen.");
-H("After the lines in the command file are exhausted, Metamath returns to");
-H("its normal user interface mode.");
-H("");
-H("SUBMIT commands can occur inside of a SUBMIT command file, up to 10 levels");
-H("deep (determined by MAX_COMMAND_FILE_NESTING in mminou.h.");
-H("");
-H("Optional qualifier:");
-H("    / SILENT - This qualifier suppresses the screen output of the SUBMIT");
-H("        command.  The output will still be recorded in any log file that");
-H("        has been opened with OPEN LOG (or is opened inside the command");
-H("        file itself).  The screen output of any operating system commands");
-H("        inside the command file (see HELP SYSTEM) is not suppressed.");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SYSTEM");
-H("A line enclosed in single or double quotes will be executed by your");
-H("computer's operating system, if it has such a feature.  For example, on a");
-H("GNU/Linux system,");
-H("    MM> 'ls | less -EX'");
-H("will list disk directory contents.  Note that this feature will not work");
-H("on the pre-OSX Macintosh, which does not have a command line interface.");
-H("");
-H("For your convenience, the trailing quote is optional, for example:");
-H("    MM> 'ls | less -EX");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP MM-PA");
-H("See HELP PROOF_ASSISTANT");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP MORE");
-H("Syntax:  MORE <filename>");
-H("");
-H("This command will type (i.e. display) the contents of an ASCII file on");
-H("your screen.  (This command is provided for convenience but is not very");
-H("powerful.  See HELP SYSTEM to invoke your operating system's command to");
-H("do this, such as \"less -EX\" in Linux.)");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP FILE SEARCH");
-H("Syntax:  FILE SEARCH <filename> \"<search string>\" [/ FROM_LINE");
-H("             <number>] [/ TO_LINE <number>]");
-H("");
-H("This command will search an ASCII file for the specified string in");
-H("quotes, within an optional range of line numbers, and display the result");
-H("on your screen.  The search is case-insensitive.  (This command is");
-H("deprecated.  See HELP SYSTEM to invoke your operating system's");
-H("equivalent command, such as \"grep\" in Linux.)");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP PROVE");
-H("Syntax:  PROVE <label> [/ OVERRIDE]");
-H("");
-H("This command will enter the Proof Assistant, which will allow you to");
-H("create or edit the proof of the specified statement.");
-H("");
-H("Optional qualifier:");
-H("    / OVERRIDE - By default, PROVE will refuse to enter the Proof");
-H("        Assistant if \"(Proof modification is discouraged.)\" is present");
-H("        in the statement's description comment.  This qualifier will");
-H("        allow the Proof Assistant to be entered.");
-H("");
-H("See also:  HELP PROOF_ASSISTANT and HELP EXIT");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP PROOF_ASSISTANT");
-H("Before using the Proof Assistant, you must add a $p to your source file");
-H("(using a text editor) containing the statement you want to prove.  Its");
-H("proof should consist of a single ?, meaning \"unknown step.\"  Example:");
-H("    eqid $p x = x $= ? $.");
-H("");
-H("To enter the Proof Assistant, type PROVE <label>, e.g. PROVE eqid.");
-H("Metamath will respond with the MM-PA> prompt.");
-H("");
-H("Proofs are created working backwards from the statement being proved,");
-H("primarily using a series of ASSIGN commands.  A proof is complete when");
-H("all steps are assigned to statements and all steps are unified and");
-H("completely known.  During the creation of a proof, Metamath will allow");
-H("only operations that are legal based on what is known up to that point.");
-H("For example, it will not allow an ASSIGN of a statement that cannot be");
-H("unified with the unknown proof step being assigned.");
-H("");
-H("IMPORTANT:  You should figure out your first few proofs completely and");
-H("write them down by hand, before using the Proof Assistant.  Otherwise you");
-H("will become extremely frustrated.  The Proof Assistant is NOT a tool to");
-H("help you discover proofs.  It is just a tool to help you add them to the");
-H("database.  For a tutorial read Section 2.4 of the Metamath book.  To");
-H("practice using the Proof Assistant, you may want to PROVE an existing");
-H("theorem, then delete all steps with DELETE ALL, then re-create it with");
-H("the Proof Assistant while looking at its proof display (before deletion).");
-H("");
-H("The commands available to help you create a proof are the following.");
-H("See the help for the individual commands for more detail.");
-H("    SHOW NEW_PROOF [/ ALL,...] - Displays the proof in progress.");
-H("        You will use this command a lot; see HELP SHOW NEW_PROOF to");
-H("        become familiar with its qualifiers.  The qualifiers / UNKNOWN");
-H("        and / NOT_UNIFIED are useful for seeing the work remaining to be");
-H("        done.  The combination / ALL / UNKNOWN is useful identifying");
-H("        dummy variables that must be assigned, or attempts to use illegal");
-H("        syntax, when IMPROVE ALL is unable to complete the syntax");
-H("        constructions.  Unknown variables are shown as $1, $2,...");
-H("    ASSIGN <step> <label> - Assigns an unknown step with the statement");
-H("        specified by <label>.  This will normally be your most frequently");
-H("        used command for creating proofs.  The usual proof entry process");
-H("        consists of successively ASSIGNing labels to unknown steps shown");
-H("        by SHOW NEW_PROOF / UNKNOWN.");
-H("    LET VARIABLE <variable> = \"<symbol sequence>\" - Forces a symbol");
-H("        sequence to replace an unknown variable in a proof.  It is useful");
-H("        for helping difficult unifications, and is necessary when you");
-H("        have dummy variables that must be specified.");
-H("    LET STEP <step> = \"<symbol sequence>\" - Forces a symbol sequence");
-H("        to replace the contents of a proof step, provided it can be");
-H("        unified with the existing step contents.  (Rarely useful.)");
-H("    UNIFY STEP <step> (or UNIFY ALL) - Unifies the source and target of");
-H("        a step.  If you specify a specific step, you will be prompted");
-H("        to select among the unifications that are possible.  If you");
-H("        specify ALL, only those steps with unique unifications will be");
-H("        unified.  UNIFY ALL / INTERACTIVE goes through all non-unified");
-H("        steps.");
-H("    INITIALIZE <step> (or ALL) - De-unifies the target and source of");
-H("        a step (or all steps), as well as the hypotheses of the source,");
-H("        and makes all variables in the source unknown.  Useful after");
-H("        an ASSIGN or LET mistake resulted in incorrect unifications.");
-H("    DELETE <step> (or ALL or FLOATING_HYPOTHESES) - Deletes the specified");
-H("        step(s).  DELETE FLOATING_HYPOTHESES then INITIALIZE ALL then");
-H("        UNIFY ALL / INTERACTIVE is useful for recovering from mistakes");
-H("        where incorrect unifications assigned wrong math symbol strings to");
-H("        variables.");
-H("    IMPROVE <step> (or ALL) - Automatically creates a proof for steps");
-H("        (with no unknown variables) whose proof requires no statements");
-H("        with $e hypotheses.  Useful for filling in proofs of $f");
-H("        hypotheses.  The / DEPTH qualifier will also try statements");
-H("        whose $e hypotheses contain no new variables.  WARNING: Save your");
-H("        work (SAVE NEW_PROOF, WRITE SOURCE) before using / DEPTH = 2 or");
-H("        greater, since the search time grows exponentially and may never");
-H("        terminate in a reasonable time, and you cannot interrupt the");
-H("        search.  I have rarely found / DEPTH = 3 or greater to be useful.");
-H("    SAVE NEW_PROOF - Saves the proof in progress internally in the");
-H("        database buffer.  To save it permanently, use WRITE SOURCE after");
-H("        SAVE NEW_PROOF.  To revert to the last SAVE NEW_PROOF,");
-H("        EXIT / FORCE from the Proof Assistant then re-enter the Proof");
-H("        Assistant.");
-H("    SHOW NEW_PROOF / COMPRESSED - Displays the proof in progress on the");
-H("        screen in a format that can be copied and pasted into the");
-H("        database source, as an alternative to a SAVE NEW_PROOF.");
-H("    MATCH STEP <step> (or MATCH ALL) - Shows what statements are");
-H("        possibilities for the ASSIGN statement. (This command is not very");
-H("        useful in its present form and hopefully will be improved");
-H("        eventually.  In the meantime, use the SEARCH statement for");
-H("        candidates matching specific math token combinations.)");
-H("    MINIMIZE_WITH - After a proof is complete, this command will attempt");
-H("        to match other database theorems to the proof to see if the proof");
-H("        size can be reduced as a result.");
-H("    UNDO - Undo the effect of a proof-changing command (all but the SHOW");
-H("        and SAVE commands above).");
-H("    REDO - Reverse the previous UNDO.");
-H("");
-H("The following commands set parameters that may be relevant to your proof:");
-H("    SET UNIFICATION_TIMEOUT");
-H("    SET SEARCH_LIMIT");
-H("    SET EMPTY_SUBSTITUTION - note that default is OFF (contrary to book)");
-H("");
-H("Type EXIT to exit the MM-PA> prompt and get back to the MM> prompt.");
-H("Another EXIT will then get you out of Metamath.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP UNDO");
-H("Syntax:  UNDO");
-H("");
-H("This command, available in the Proof Assistant only, allows any command");
-H("(such as ASSIGN, DELETE, IMPROVE) that affects the proof to be reversed.");
-H("See also HELP REDO and HELP SET UNDO.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP REDO");
-H("Syntax:  REDO");
-H("");
-H("This command, available in the Proof Assistant only, reverses the");
-H("effect of the last UNDO command.  Note that REDO can be issued only");
-H("if no proof-changing commands (such as ASSIGN, DELETE, IMPROVE)");
-H("were issued after the last UNDO.  A sequence of REDOs will reverse as");
-H("many UNDOs as were issued since the last proof-changing command.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SET UNDO"); /* 1-Nov-2013 nm */
-H("Syntax:  SET UNDO <number>");
-H("");
-H("(This command affects the Proof Assistant only.)");
-H("");
-H("This command changes the maximum number of UNDOs.  The current maximum");
-H("can be seen with SHOW SETTINGS.  Making it larger uses more memory,");
-H("especially for large proofs.  See also HELP UNDO.");
-H("");
-H("If this command is issued while inside of the Proof Assistant, the");
-H("UNDO stack is reset (i.e. previous possible UNDOs will be lost).");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP ASSIGN");
-H("Syntax:  ASSIGN <step> <label> [/ NO_UNIFY] [/ OVERRIDE]");
-H("         ASSIGN FIRST <label> [/ NO_UNIFY] [/ OVERRIDE]");  /* 11-Dec-05 nm */
-H("         ASSIGN LAST <label> [/ NO_UNIFY] [/ OVERRIDE]");
-H("");
-H("This command, available in the Proof Assistant only, assigns an unknown");
-H("step (one with ? in the SHOW NEW_PROOF listing) with the statement");
-H("specified by <label>.  The assignment will not be allowed if the");
-H("statement cannot be unified with the step.");
-H("");
-H("If <step> starts with \"-\", the -<step>th from last unknown step,");
-H("as shown by SHOW NEW_PROOF / UNKNOWN, will be used.  ASSIGN -0 will");
-H("assign the last unknown step, ASSIGN -1 <label> will assign the");
-H("penultimate unknown step, etc.  If <step> starts with \"+\", the <step>th");
-H("from the first unknown step will be used.  Otherwise, when the step is");
-H("a positive integer (with no \"+\" sign), ASSIGN assumes it is the actual");
-H("step number shown by SHOW NEW_PROOF / UNKNOWN.");
-H("");
-H("ASSIGN FIRST and ASSIGN LAST mean ASSIGN +0 and ASSIGN -0 respectively,");
-H("in other words the first and last steps shown by SHOW NEW_PROOF / UNKNOWN.");
-H("");
-H("Optional qualifiers:");
-H("    / NO_UNIFY - do not prompt user to select a unification if there is");
-H("        more than one possibility.  This is useful for noninteractive");
-H("        command files.  Later, the user can UNIFY ALL / INTERACTIVE.");
-H("        (The assignment will still be automatically unified if there is");
-H("        only one possibility.)");
-H("    / OVERRIDE - By default, ASSIGN will refuse to assign a statement");
-H("        if \"(New usage is discouraged.)\" is present in the statement's");
-H("        description comment.  This qualifier will allow the assignment.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP REPLACE");
-H("Syntax:  REPLACE <step> <label> [/ OVERRIDE]");
-H("Syntax:  REPLACE FIRST <label> [/ OVERRIDE]");
-H("Syntax:  REPLACE LAST <label> [/ OVERRIDE]");
-H("");
-H("This command, available in the Proof Assistant only, replaces the");
-H("current subproof ending at <step> with a new complete subproof (if one");
-H("can be found) ending with statement <label>.  The replacement will be");
-H("done only if complete subproofs can be found that match all of the");
-H("hypotheses of  <label>.  REPLACE is equivalent to IMPROVE with the");
-H("qualifiers / 3 / DEPTH 1 / SUBPROOFS (see HELP IMPROVE), except that");
-H("it considers only statement <label> rather than scanning all preceding");
-H("statements in the database, and it does somewhat more aggressive");
-H("guessing of assignments to work ($nn) variables.");
-H("");
-H("REPLACE will also assign a complete subproof to a currently unknown");
-H("(unassigned) step if a complete subproof can be found.  In many cases,");
-H("REPLACE provides an alternative to ASSIGN (with the same command syntax)");
-H("that will fill in more missing steps when it is successful.  It is often");
-H("useful to try REPLACE first and, if not successful, revert to ASSIGN.");
-H("Note that REPLACE may take a long time to run compared to ASSIGN.");
-H("");
-H("Currently, REPLACE does not allow a $e or $f statement for <label>.  Use");
-H("ASSIGN instead.  (These may be allowed in a future version.)");
-H("");
-H("Occasionally, REPLACE may be too aggressive in guessing assignments to");
-H("work ($nn) variables, and a message with recovery instructions is");
-H("provided when this could be the case.  Recovery can also be attempted with");
-H("DELETE FLOATING_HYPOTHESES then INITIALIZE ALL then");
-H("UNIFY ALL / INTERACTIVE; this will usually work and will salvage the");
-H("subproof found by REPLACE.  (The too-aggressive guessing behavior may be");
-H("improved in a future version.)");
-H("");
-H("If <step> starts with \"-\", the -<step>th from last unknown step,");
-H("as shown by SHOW NEW_PROOF / UNKNOWN, will be used.  REPLACE -0 will");
-H("assign the last unknown step, REPLACE -1 <label> will assign the");
-H("penultimate unknown step, etc.  If <step> starts with \"+\", the <step>th");
-H("from the first unknown step will be used.  Otherwise, when the step is");
-H("a positive integer (with no \"+\" sign), REPLACE assumes it is the actual");
-H("step number shown by SHOW NEW_PROOF (and can be used whether the step is");
-H("known or not).");
-H("");
-H("REPLACE FIRST and REPLACE LAST mean REPLACE +0 and REPLACE -0");
-H("respectively, in other words the first and last steps shown by");
-H("SHOW NEW_PROOF / UNKNOWN.");
-H("");
-H("Optional qualifier:");
-H("    / OVERRIDE - By default, REPLACE will refuse to assign a statement");
-H("        if \"(New usage is discouraged.)\" is present in the statement's");
-H("        description comment.  This qualifier will allow the assignment.");
-H("");
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP MATCH");
-H("Syntax:  MATCH STEP <step> [/ MAX_ESSENTIAL_HYP <number>]");
-H("    or:  MATCH ALL [/ ESSENTIAL_ONLY] [/ MAX_ESSENTIAL_HYP <number>]");
-H("");
-H("This command, available in the Proof Assistant only, shows what");
-H("statements can be unified with the specified step(s).");
-H("");
-H("Optional qualifiers:");
-H("    / MAX_ESSENTIAL_HYP <number> - filters out of the list any statements");
-H("        with more than the specified number of $e hypotheses");
-H("    / ESSENTIAL_ONLY - in the MATCH ALL statement, only the steps that");
-H("        would be listed in SHOW NEW_PROOF display are matched.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP LET");
-H("Syntax:  LET VARIABLE <variable> = \"<symbol sequence>\"");
-H("         LET STEP <step> = \"<symbol sequence>\"");
-H("         LET STEP FIRST = \"<symbol sequence>\"");
-H("         LET STEP LAST = \"<symbol sequence>\"");
-H("");
-H("These commands, available in the Proof Assistant only, assign an unknown");
-H("variable or step with a specific symbol sequence.  They are useful in the");
-H("middle of creating a proof, when you know what should be in the proof");
-H("step but the unification algorithm doesn't yet have enough information");
-H("to completely specify the unknown variables.  An \"unknown\" or \"work\"");
-H("variable is one which has the form $nn in the proof display, such as $1,");
-H("$2, etc.  The <symbol sequence> may contain other unknown variables if");
-H("desired.");
-H("Examples:");
-H("    LET VARIABLE $32 = \" A = B \"");
-H("    LET VARIABLE $32 = \" A = $35 \"");
-H("    LET STEP 10 = \" |- x = x \"");
-H("    LET STEP -2 = \" |- ( $7 <-> ph ) \"");
-H("    LET STEP +2 = \" |- ( $7 <-> ph ) \"");
-H("");
-H("Any symbol sequence will be accepted for the LET VARIABLE command.  In");
-H("LET STEP, only symbol sequences that can be unified with the step are");
-H("accepted.  LET STEP assignments are prefixed with \"=(User)\" in most");
-H("SHOW NEW_PROOF displays.");
-H("");
-H("If <step> starts with \"-\", the -<step>th from last unknown step,");
-H("as shown by SHOW NEW_PROOF / UNKNOWN, will be used.  LET STEP -0 will");
-H("assign the last unknown step, LET STEP -1 <label> will assign the");
-H("penultimate unknown step, etc.  If <step> starts with \"+\", the <step>th");
-H("from the first unknown step will be used.  LET STEP FIRST and LET STEP");
-H("LAST means LET STEP +0 and LET STEP -0 respectively.  Otherwise, when");
-H("the step is a positive integer (with no \"+\" sign), LET STEP may be");
-H("used to assign known as well as unknown steps.");
-H("");
-H("Note that SAVE PROOF does not save any LET VARIABLE or LET STEP");
-H("assignents.  However, IMPROVE ALL prior to SAVE PROOF will usually");
-H("preserve the information for steps with no unknown variables.");
-H("");
-H("Quotes and special characters in command-line arguments");
-H("-------------------------------------------------------");
-H("");
-H("You can use single quotes to surround the math symbol string argument of");
-H("a LET or SEARCH command when the argument contains a double quote.");
-H("For example,");
-H("  MM-PA> LET VARIABLE $2 = '( F \" A )'");
-H("");
-H("The trailing quote that would be the last character on the line");
-H("may be omitted for convenience, to save typing:");
-H("  MM-PA> LET VARIABLE $2 = '( F \" A )");
-H("");
-H("If a math symbol string has both single and double quotes, you must");
-H("use the prompted completion feature by pressing RETURN where the symbol");
-H("string would go.  Quotes aren't needed (and must not be used) around");
-H("the answer to a prompted completion question.");
-H("  MM-PA> LET VARIABLE $2 =");
-H("  With what math symbol string? ( `' F \" A )");
-H("");
-H("Quotes are optional around any math symbol string containing a single");
-H("symbol and and not containing \"=\", \"/\", a single quote, or a double");
-H("quote:");
-H("  MM-PA> LET VARIABLE $17 = ph");
-H("");
-H("Unquoted \"=\" and \"/\" are special in a command line in that they are");
-H("implicitly surrounded by white space:");
-H("  MM-PA> LET VARIABLE $17=ph");
-H("  MM-PA> SHOW NEW_PROOF/UNKNOWN");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP UNIFY");
-H("HELP UNIFY");
-H("Syntax:  UNIFY STEP <step>");
-H("         UNIFY ALL [/ INTERACTIVE]");
-H("");
-H("These commands, available in the Proof Assistant only, unify the source");
-H("and target of the specified step(s). If you specify a specific step, you");
-H("will be prompted to select among the unifications that are possible.  If");
-H("you specify ALL, only those steps with unique unifications will be");
-H("unified.");
-H("");
-H("Optional qualifier for UNIFY ALL:");
-H("    / INTERACTIVE - You will be prompted to select among the unifications");
-H("        that are possible for any steps that do not have unique");
-H("        unifications.");
-H("");
-H("See also SET UNIFICATION_TIMEOUT.  The default is 100000, but increasing");
-H("it to 1000000 can help difficult cases.  The LET VARIABLE command to");
-H("manually assign unknown variables also helps difficult cases.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP INITIALIZE");
-H("Syntax:  INITIALIZE ALL");
-H("         INITIALIZE USER");
-H("         INITIALIZE STEP <step>");
-H("");
-H("These commands, available in the Proof Assistant only, \"de-unify\" the");
-H("target and source of a step (or all steps), as well as the hypotheses of");
-H("the source, and make all variables in the source and the source's");
-H("hypotheses unknown.  This command is useful to help recover when an");
-H("ASSIGN mistake resulted in incorrect unifications.  After you DELETE the");
-H("incorrect ASSIGN, use INITIALIZE ALL then UNIFY ALL / INTERACTIVE to");
-H("recover the state before the mistake.");
-H("");
-H("INITIALIZE ALL will void all LET VARIABLE assignments as well as de-unify");
-H("all targets and sources.  INITIALIZE USER will delete all LET STEP");
-H("assignments but will not de-unify.  INITIALIZE STEP will do all of these");
-H("actions for the specified step.");
-H("");
-H("After de-unification and variable deassignment, proof steps that are");
-H("completely known will be automatically re-unified.  If you want to");
-H("de-unify these, use DELETE FLOATING_HYPOTHESES then INITIALIZE ALL.");
-H("");
-H("See also:  UNIFY and DELETE");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP DELETE");
-H("Syntax:  DELETE STEP <step>");
-H("         DELETE ALL");
-H("         DELETE FLOATING_HYPOTHESES");
-H("");
-H("These commands are available in the Proof Assistant only. The DELETE STEP");
-H("command deletes the proof tree section that branches off of the specified");
-H("step and makes the step become unknown.  DELETE ALL is equivalent to");
-H("DELETE STEP <step> where <step> is the last step in the proof (i.e. the");
-H("beginning of the proof tree).");
-H("");
-H("DELETE FLOATING_HYPOTHESES will delete all sections of the proof that");
-H("branch off of $f statements.  It is sometimes useful to do this before");
-H("an INITIALIZE command to recover from an error.  Note that once a proof");
-H("step with a $f hypothesis as the target is completely known, the IMPROVE");
-H("command can usually fill in the proof for that step.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP IMPROVE");
-H("Syntax:  IMPROVE <step> [/ DEPTH <number>] [/ NO_DISTINCT] [/ 2] [/ 3]");
-H("                       [/ SUBPROOFS] [/ INCLUDE_MATHBOXES] [/ OVERRIDE]");
-                              /* 26-Aug-2006 nm */ /* 4-Sep-2012 */
-H("         IMPROVE FIRST [/ DEPTH <number>] [/ NO_DISTINCT] [/ 2] [/ 3]");
-H("                       [/ SUBPROOFS] [/ INCLUDE_MATHBOXES] [/ OVERRIDE]");
-H("         IMPROVE LAST [/ DEPTH <number>] [/ NO_DISTINCT] [/ 2] [/ 3]");
-H("                       [/ SUBPROOFS] [/ INCLUDE_MATHBOXES] [/ OVERRIDE]");
-H("         IMPROVE ALL [/ DEPTH <number>] [/ NO_DISTINCT] [/ 2] [/ 3]");
-H("                       [/ SUBPROOFS] [/ INCLUDE_MATHBOXES] [/ OVERRIDE]");
-H("");
-H("This command, available in the Proof Assistant only, tries to");
-H("find proofs automatically for unknown steps whose symbol sequences are");
-H("completely known.  They are primarily useful for filling in proofs of $f");
-H("hypotheses.  By default, the search will be restricted to matching");
-H("statements having no $e hypotheses.");
-H("");
-H("If <step> starts with \"-\", the -<step>th from last unknown step,");
-H("as shown by SHOW NEW_PROOF / UNKNOWN, will be used.  IMPROVE -0 will try");
-H("to prove the last unknown step, IMPROVE -1 will try to prove the");
-H("penultimate unknown step, etc.  If <step> starts with \"+\", the <step>th");
-H("from the first unknown step will be used.  Otherwise, when <step> is");
-H("a positive integer (with no \"+\" sign), IMPROVE assumes it is the actual");
-H("step number shown by SHOW NEW_PROOF (and can be used whether the step is");
-H("known or not).");
-H("");
-H("IMPROVE FIRST and IMPROVE LAST mean IMPROVE +0 and IMPROVE -0");
-H("respectively, in other words the first and last steps shown by");
-H("SHOW NEW_PROOF / UNKNOWN.");
-H("");
-H("IMPROVE ALL scans all unknown steps.  If / SUBPROOFS is specified,");
-H("it also scans all steps with incomplete subproofs.");
-H("");
-H("Sometimes IMPROVE will find proofs for additional unknown steps when");
-H("it is run a second time.  This can happen when an unknown step is");
-H("identical to another step whose proof became completed by the first");
-H("IMPROVE run.  (This second pass is not done automatically because it");
-H("could double the IMPROVE runtime, usually with no benefit.)");
-
-H("");
-H("Optional qualifiers:");
-H("    / DEPTH <number> - This qualifier will cause the search to include");
-H("        statements with $e hypotheses (but no new variables in their $e");
-H("        hypotheses - these are called \"cut-free\" statements), provided");
-H("        that the backtracking has not exceeded the specified depth.");
-H("        **WARNING**:  Try DEPTH 1, then 2, then 3, etc. in sequence");
-H("        because of possible exponential blowups.  Save your work before");
-H("        trying DEPTH greater than 1!");
-H("    / NO_DISTINCT - Skip trial statements that have $d requirements.");
-H("        This qualifier will prevent assignments that might violate $d");
-H("        requirements, but it also could miss possible legal assignments.");
-H("    / 1 - Use the traditional search algorithm used in earlier versions");
-H("        of this program.  It is the default.  It tries to match cut-free");
-H("        statements only (those not having variables in their hypotheses");
-H("        that are not in the conclusion).  It is the fastest method when");
-H("        it can find a proof.");
-H("    / 2 - Try to match statements with cuts.  It also tries to match");
-H("        steps containing working ($nn) variables when they don't share");
-H("        working variables with the rest of the proof.  It runs slower");
-H("        than / 1.");
-H("    / 3 - Attempt to find (cut-free) proofs of $e hypotheses that result");
-H("        from a trial match, unlike / 2, which only attempts (cut-free)");
-H("        proofs of $f hypotheses.  It runs much slower than / 1, and you");
-H("        may prefer to use it with specific steps.  For example, if");
-H("        step 456 is unknown, you may want to use IMPROVE 456 / 3 rather");
-H("        than IMPROVE ALL / 3.  Note that / 3 respects the / DEPTH");
-H("        qualifier, although at the expense of additional run time.");
-H("    / SUBPROOFS - Look at each subproof that isn't completely known, and");
-H("        try to see if it can be proved independently.  This qualifier is");
-H("        meaningful only for IMPROVE ALL / 2 or IMPROVE ALL / 3.  It may");
-H("        take a very long time to run, especially with / 3.");
-/* 5-Aug-2020 nm - Added INCLUDE_MATHBOXES */
-H("    / INCLUDE_MATHBOXES - By default, MINIMIZE_WITH skips statements");
-H("        beyond the one with label \"mathbox\" and not in the mathbox of");
-H("        the PROVE argument.  This qualifier allows them to be included.");
-/* 3-May-2016 nm - Added OVERRIDE */
-H("    / OVERRIDE - By default, IMPROVE skips statements that have");
-H("        \"(New usage is discouraged.)\" in their description comment.");
-H("        This qualifier tries to use them anyway.");
-H("");
-H("Note that / 2 includes the search of / 1, and / 3 includes / 2.");
-H("Specifying / 1 / 2 / 3 has the same effect as specifying just / 3, so");
-H("there is no need to specify more than one.  Finally, since / 1 is the");
-H("default, you never need to use it; it is included for completeness (or");
-H("in case the default is changed in the future).");
-H("");
-/*   This seems obsolete now with today's computers.  4-Sep-2012 nm
-H("Note:  If memory is limited, IMPROVE ALL on a large proof may overflow");
-H("memory.  If you use SET UNIFICATION_TIMEOUT 1 before IMPROVE ALL,");
-H("there will usually be sufficient improvement to later easily recover and");
-H("completely IMPROVE the proof on a larger computer.  Warning:  Once");
-H("memory has overflowed, there is no recovery.  If in doubt, save the");
-H("intermediate proof (SAVE NEW_PROOF then WRITE SOURCE) before IMPROVE ALL.");
-H("");
-*/
-H("See also:  HELP SET SEARCH_LIMIT");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP MINIMIZE_WITH");
-/*
-H("Syntax:  MINIMIZE_WITH <label-match> [/ BRIEF] [/ ALLOW_GROWTH]");
-H("             [/ NO_DISTINCT] [/ EXCEPT <label-match>]");
-H("              [/ FORBID <label-match>] [/ REVERSE] [/ INCLUDE_MATHBOXES]");
-*/
-/*
-H("Syntax:  MINIMIZE_WITH <label-match> [/ VERBOSE] [/ ALLOW_GROWTH]");
-H("              [/ EXCEPT <label-match>] [/ FORBID <label-match>]");
-H("              [/ INCLUDE_MATHBOXES] [/ NO_NEW_AXIOMS_FROM <label-match>]");
-H("              [/ OVERRIDE] [/ TIME]");
-*/
-H("Syntax:  MINIMIZE_WITH <label-match> [/ VERBOSE] [/ MAY_GROW]");
-H("              [/ EXCEPT <label-match>] [/ INCLUDE_MATHBOXES]");
-H("              [/ ALLOW_NEW_AXIOMS <label-match>]");
-H("              [/ NO_NEW_AXIOMS_FROM <label-match>] [/ FORBID <label-match>]");
-H("              [/ OVERRIDE] [/ TIME]");
-H("");
-H("This command, available in the Proof Assistant only, checks whether");
-H("the proof can be shortened by using earlier $p or $a statements matching");
-H("<label-match>, and if so, shortens the proof.  <label-match> is a list of");
-H("comma-separated labels which can contain wildcards (* and ?) to test more");
-H("than one statement, but each statement is tested independently from the");
-H("others.  Note:  In the informational output, if the size is given in");
-H("bytes, it refers to the compressed proof size, otherwise it refers to the");
-H("number of steps in the uncompressed proof.");
-H("");
-H("For ordinary use with set.mm, we recommend running it as follows:");
-H("   MINIMIZE_WITH * / ALLOW_NEW_AXIOMS * / NO_NEW_AXIOMS_FROM ax-*");
-H("For some additional information on the qualifiers see");
-H("    https://groups.google.com/d/msg/metamath/f-L91-1jI24/3KJnGa8qCgAJ");
-/*
-H("  Warning:  MINIMIZE_WITH does not check for $d violations, so");
-H("SAVE PROOF then VERIFY PROOF should be run afterwards to check for them");
-H("if you don't use / NO_DISTINCT.");
-*/
-H("");
-H("Optional qualifiers:");
-/* 4-Feb-2013 nm - Added VERBOSE */
-H("    / VERBOSE - Shows additional information such as uncompressed proof");
-H("        lengths and reverted shortening attempts");
-/* 25-Jun-2014 nm Removed BRIEF, NO_DISTINCT, REVERSE */
-/*
-H("    / BRIEF - The labels of statements that were tested but didn't reduce");
-H("        the proof length will not be listed, for brevity.  (This qualifier");
-H("        is the default and is never needed, but is retained for backwards");
-H("        compatibility with older program versions.)");
-*/
-H("    / MAY_GROW - If a substitution is possible, it will be made even");
-H("        if the proof length increases.  This is useful if we are just");
-H("        updating the proof with a newer version of an obsolete theorem.");
-H("        (Note: this qualifier used to be named / ALLOW_GROWTH).");
-/*
-H("    / NO_DISTINCT - Skip the trial statement if it has a $d requirement.");
-H("        This qualifier is useful when <label-match> has wildcards, to");
-H("        prevent illegal shortenings that would violate $d requirements.");
-*/
-/* 7-Jan-06 nm - Added EXCEPT */
-H("    / EXCEPT <label-match> - Skip trial statements matching <label-match>,");
-H("        which may contain * and ? wildcard characters; see HELP SEARCH");
-H("        for wildcard matching rules.  Note:  Multiple EXCEPT qualifiers");
-H("        are not allowed; use wildcards instead.");
-/* 28-Jun-2011 nm - Added INCLUDE_MATHBOXES */
-/* 14-Aug-2012 nm - Added note about current mathbox */
-H("    / INCLUDE_MATHBOXES - By default, MINIMIZE_WITH skips statements");
-H("        beyond the one with label \"mathbox\" and not in the mathbox of");
-H("        the PROVE argument.  This qualifier allows them to be included.");
-/* 28-Jun-2011 nm - Added INCLUDE_MATHBOXES */
-/* 14-Aug-2012 nm - Added note about current mathbox */
-H("    / ALLOW_NEW_AXIOMS <label-match> - By default, MINIMIZE_WITH skips");
-H("        statements that depend on $a statements not already used by the");
-H("        proof.  This qualifier allows new $a consequences to be used.");
-H("        To better fine-tune which axioms are used, you may use / FORBID");
-H("        and / NO_NEW_AXIOMS, which take priority over / ALLOW_NEW_AXOMS.");
-H("        Example:  / ALLOW_NEW_AXIOMS df-* will allow new definitions to be");
-H("        used. / ALLOW_NEW_AXIOMS * / NO_NEW_AXIOMS_FROM ax-ac*,ax-reg");
-H("        will allow any new axioms except those matching ax-ac*,ax-reg.");
-/* 22-Nov-2014 nm - Added NO_NEW_AXIOMS_FROM */
-H("    / NO_NEW_AXIOMS_FROM <label-match> - skip any trial statement whose");
-H("        proof depends on a $a statement matching <label-match> but that");
-H("        isn't used by the current proof.  This makes it easier to avoid");
-H("        say ax-ac if the current proof doesn't already use ax-ac, but it");
-H("        permits ax-ac otherwise.  Example:");
-H("        / ALLOW_NEW_AXIOMS * / NO_NEW_AXIOMS_FROM ax-ac*,ax-reg");
-H("        will allow any new axioms except those matching ax-ac*,ax-reg.");
-H("        Notes:  1. In this example, if ax-reg is already used by the proof,");
-H("        statements depending on ax-reg WILL be tried. 2. The use of");
-H("        / NO_NEW_AXIOMS_FROM without / ALLOW_NEW_AXIOMS has no effect.");
-/* 20-May-2013 nm - Added FORBID */
-H("    / FORBID <label-match> - Skip any trial");
-H("        statement whose backtrack (from SHOW TRACE_BACK) contains any");
-H("        statement matching <label-match>.  This is useful for avoiding");
-H("        the use of undesired axioms when reducing proof length.  For");
-H("        example, MINIMIZE_WITH ... / FORBID ax-ac,ax-inf* will not shorten");
-H("        the proof with any statement that depends on ax-ac, ax-inf, or");
-H("        ax-inf2 (in the set.mm as of this writing).  Notes: 1. / FORBID");
-H("        can be less useful than / NO_NEW_AXIOMS_FROM because it will");
-H("        also suppress trying statements that depend on <label-list> axioms");
-H("        already used by the proof.  / FORBID may become deprecated.  2. The");
-H("        use of / FORBID without / ALLOW_NEW_AXIOMS has no effect.");
-/* 10-Nov-2011 nm - Added REVERSE */
-/*
-H("    / REVERSE - Reverse the order of statement scanning.  By default,");
-H("        statements are scanned from last to first, since empirically this");
-H("        usually leads to better results.  With this qualifier they are");
-H("        scanned from first to last.  You may wish to try both ways");
-H("        (from the same starting proof) and choose the shorter result.");
-*/
-/* 3-May-2016 nm - Added OVERRIDE */
-H("    / OVERRIDE - By default, MINIMIZE_WITH skips statements that have");
-H("        \"(New usage is discouraged.)\" in their description comment.");
-H("        With this qualifier it will try to use them anyway.");
-H("    / TIME - prints out the run time used by the MINIMIZE_WITH run.");
-H("");
-
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP EXPAND");
-H("Syntax:  EXPAND <label-match>");
-H("");
-H("This command, available in the Proof Assistant only, replaces any");
-H("references to <label-match> in the proof with the proof of <label-match>.");
-H("Before and after sizes are printed for the resulting compressed proof (in");
-H("bytes of source text).  Any dummy variables in the proof of <label-match>");
-H("are replaced with unknown steps and must be assigned by hand, and any $d");
-H("statements needed for them must be added by hand.");
-H("");
-H("Except for early theorems close to the axioms, it is best to use specific");
-H("labels rather than EXPAND * because the proof size grows exponentially as");
-H("each layer is eliminated.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SAVE PROOF");
-H("Syntax:  SAVE PROOF <label-match> [/ <qualifier>] [/ <qualfier>]...");
-H("");
-H("The SAVE PROOF command will reformat a proof in one of two formats and");
-H("replace the existing proof in the database buffer.  It is useful for");
-H("converting between proof formats.  Note that a proof will not be");
-H("permanently saved until a WRITE SOURCE command is issued.  Multiple");
-H("proofs can be saved by using comma-separated list of labels with optional");
-H("wildcards (* and ?) in <label-match>.");
-H("");
-H("Optional qualifiers:");
-H("    / NORMAL - The proof is saved in the basic format (i.e., as a sequence");
-H("        of labels, which is the defined format of the basic Metamath");
-H("        language).  This is the default format which is used if a");
-H("        qualifier is omitted.");
-H("    / EXPLICIT - The proof is saved with hypothesis assignments shown");
-H("        explicitly, allowing hypotheses to be reordered without disturbing");
-H("        proofs.");
-H("    / PACKED - The proof is saved in an efficient packed format.  It may");
-H("        be used together with / NORMAL or / EXPLICIT.");
-H("    / COMPRESSED - The proof is saved in the compressed format which");
-H("        reduces storage requirements in a source file.");
-H("    / FAST - The proof is merely reformatted and not recompressed.");
-H("        May be used (only) with / COMPRESSED and / PACKED to speed up");
-H("        conversion between formats.");
-H("    / OLD_COMPRESSION - When used with / COMPRESSED, specifies an older,");
-H("        slightly less space-efficient algorithm.  (Specifically, it does");
-H("        not try to rearrange labels to fit evenly on a line.)");
-H("    / TIME - prints out the run time used for each proof.");
-H("");
-H("Important note:  The / PACKED and / EXPLICIT qualifiers save the proof");
-H("in formats that are _not_ part of the Metamath standard and that probably");
-H("will not be recognized by other Metamath proof verifiers.  They are");
-H("primarily intended to assist database maintenance.  For example,");
-H("    SAVE PROOF * / EXPLICIT / PACKED / FAST");
-H("will allow the order of $e and $f hypotheses to be changed without");
-H("affecting any proofs.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP SAVE NEW_PROOF");
-H("Syntax:  SAVE NEW_PROOF <label-match> [/ <qualifier>] [/ <qualfier>]...");
-H("");
-H("The SAVE NEW_PROOF command is available in the Proof Assistant only. It");
-H("saves the proof in progress in the database buffer.  SAVE NEW_PROOF may be");
-H("used to save a completed proof, or it may be used to save a proof in");
-H("progress in order to work on it later.  If an incomplete proof is saved,");
-H("any user assignments with LET STEP or LET VARIABLE will be lost, as will");
-H("any ambiguous unifications that were resolved manually. To help make");
-H("recovery easier, it is advisable to IMPROVE ALL before SAVE NEW_PROOF so");
-H("that the incomplete proof will have as much information as possible.");
-H("");
-H("Note that the proof will not be permanently saved until a WRITE SOURCE");
-H("command is issued.");
-H("");
-H("Optional qualifiers:");
-H("    / NORMAL, / COMPRESSED, / EXPLICIT, / PACKED, / OLD_COMPRESSION -");
-H("        These qualifiers are the same as for SAVE PROOF.  See");
-H("        HELP SAVE PROOF.");
-H("    / OVERRIDE - By default, SAVE NEW_PROOF will refuse to overwrite");
-H("        the proof if \"(Proof modification is discouraged.)\" is present");
-H("        in the statement's description comment.  This qualifier will");
-H("        allow the proof to be saved.");
-H("");
-H("Note that if no qualifier is specified, / NORMAL is assumed.");
-H("");
-
-
-g_printHelp = !strcmp(saveHelpCmd, "HELP DEMO");
-H("For a quick demo that enables you to see Metamath do something, type");
-H("the following:");
-H("    READ set.mm");
-H("    SHOW STATEMENT id1 /COMMENT");
-H("    SHOW PROOF id1 /RENUMBER /LEMMON");
-H("will show you a proof of \"P implies P\" directly from the axioms");
-H("of propositional calculus.");
-H("    SEARCH * \"distributive law\" /COMMENTS");
-H("will show all the distributive laws in the database.");
-H("    SEARCH * \"C_ $* u.\"");
-H("will show all statements with subset then union in them.");
-H("");
-
-if (strcmp(helpCmd, saveHelpCmd)) bug(1401); /* helpCmd got corrupted */
-let(&saveHelpCmd, ""); /* Deallocate memory */
-
-}
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* Part 2 of help file for Metamath */
+
+/* To add a new help entry, you must add the command syntax to mmcmdl.c
+   as well as adding it here. */
+
+#include <string.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mmcmds.h"
+#include "mmhlpb.h"
+
+void help2(vstring helpCmd)
+{
+
+vstring_def(saveHelpCmd);
+/* help2() may be called with a temporarily allocated argument (left(),
+   cat(), etc.), and the let()s in the eventual print2() calls will
+   deallocate and possibly corrupt helpCmd.  So, we grab a non-temporarily
+   allocated copy here.  (And after this let(), helpCmd will become invalid
+   for the same reason.)  */
+let(&saveHelpCmd, helpCmd);
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP");
+H("Welcome to Metamath.  Here are some general guidelines.");
+H("");
+H("To make the most effective use of Metamath, you should become familiar");
+H("with the Metamath book.  In particular, you will need to learn");
+H("the syntax of the Metamath language.");
+H("");
+H("For help using the command line, type HELP CLI.");
+H("For help invoking Metamath, type HELP INVOKE.");
+H("For a summary of the Metamath language, type HELP LANGUAGE.");
+H("For a summary of comment markup, type HELP VERIFY MARKUP.");
+H("For help getting started, type HELP DEMO.");
+H("For help exploring the data base, type HELP EXPLORE.");
+H("For help creating a LaTeX file, type HELP TEX.");
+H("For help creating Web pages, type HELP HTML.");
+H("For help proving new theorems, type HELP PROOF_ASSISTANT.");
+H("For a list of help topics, type HELP ? (to force an error message).");
+H("For current program settings, type SHOW SETTINGS.");
+H("For a simple but general-purpose ASCII file manipulator, type TOOLS.");
+H("To exit Metamath, type EXIT (or its synonym QUIT).");
+H("");
+H("Copyright (C) 2020 Norman Megill  License terms:  GPL 2.0 or later");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP COMMENTS");
+H("Comment markup is described near the end of HELP LANGUAGE.  See also");
+H("HELP HTML for the $t comment and HTML definitions.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP INVOKE");
+H("To invoke Metamath from a Unix/Linux/MacOSX prompt, assuming that the");
+H("Metamath program is in the current directory, type");
+H("");
+H("  bash$ ./metamath");
+H("");
+H("To invoke Metamath from a Windows DOS or Command Prompt, assuming that");
+H("the Metamath program is in the current directory (or in a directory");
+H("included in the Path system environment variable), type");
+H("");
+H("  C:\\metamath>metamath");
+H("");
+H("To use command-line arguments at invocation, the command-line arguments");
+H("should be a list of Metamath commands, surrounded by quotes if they");
+H("contain spaces.  In Windows DOS, the surrounding quotes must be double");
+H("(not single) quotes.  For example, to read the database set.mm, verify");
+H("all proofs, and exit the program, type (under Unix)");
+H("");
+H("  bash$ ./metamath 'read set.mm' 'verify proof *' exit");
+H("");
+H("Note that in Unix, any directory path with /'s must be surrounded by");
+H("quotes so Metamath will not interpret the / as a command qualifier.  So");
+H("if set.mm is in the /tmp directory, use for the above example");
+H("");
+H("  bash$ ./metamath 'read \"/tmp/set.mm\"' 'verify proof *' exit");
+H("");
+H("For convenience, if the command-line has one argument and no spaces in");
+H("the argument, the command is implicitly assumed to be READ.  In this one");
+H("special case, /'s are not interpreted as command qualifiers, so you don't");
+H("need quotes around a Unix file name.  Thus");
+H("");
+H("  bash$ ./metamath /tmp/set.mm");
+H("");
+H("and");
+H("");
+H("  bash$ ./metamath \"read '/tmp/set.mm'\"");
+H("");
+H("are equivalent.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW MEMORY");
+H("Syntax:  SHOW MEMORY");
+H("");
+H("This command shows the available memory left.  It is not meaningful");
+H("on modern machines with virtual memory.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW SETTINGS");
+H("Syntax:  SHOW SETTINGS");
+H("");
+H("This command shows the state of various parameters.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW ELAPSED_TIME");
+H("Syntax:  SHOW ELAPSED_TIME");
+H("");
+H("This command shows the time elapsed in the session and from any");
+H("previous use of SHOW ELAPSED_TIME.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW LABELS");
+H("Syntax:  SHOW LABELS <label-match> [/ ALL] [/ LINEAR]");
+H("");
+H("This command shows the labels of $a and $p statements that match");
+H("<label-match>.  <label-match> may contain * and ? wildcard characters;");
+H("see HELP SEARCH for wildcard matching rules.");
+H("");
+H("Optional qualifier:");
+H("    / ALL - Include matches for $e and $f statement labels.");
+H("    / LINEAR - Display only one label per line.  This can be useful for");
+H("        building scripts in conjunction with the TOOLS utility.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW DISCOURAGED");
+H("Syntax:  SHOW DISCOURAGED");
+H("");
+H("This command shows the usage and proof statistics for statements with");
+H("\"(Proof modification is discouraged.)\" and \"(New usage is");
+H("discouraged.)\" markup tags in their description comments.  The output");
+H("is intended to be used by scripts that compare a modified .mm file");
+H("to a previous version.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW SOURCE");
+H("Syntax:  SHOW SOURCE <label>");
+H("");
+H("This command shows the ASCII source code associated with a statement.");
+H("Normally you should use SHOW STATEMENT for a more meaningful display,");
+H("but SHOW SOURCE can be used to see statements with multiple comments");
+H("and to see the exact content of the Metamath database.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW STATEMENT");
+H("Syntax:  SHOW STATEMENT <label-match> [/ COMMENT] [/ FULL] [/ TEX]");
+H("             [/ OLD_TEX] [/ HTML] [/ ALT_HTML] [/ BRIEF_HTML]");
+H("             [/ BRIEF_ALT_HTML] [/ NO_VERSIONING] [/ MNEMONICS]");
+H("");
+H("This command provides information about a statement.  Only statements");
+H("that have labels ($f, $e, $a, and $p) may be specified. <label-match>");
+H("may contain * and ? wildcard characters; see HELP SEARCH for wildcard");
+H("matching rules.");
+H("");
+H("In Proof Assistant mode (MM-PA prompt), the symbol \"=\" is a synonym");
+H("for the label of the statement being proved.  Thus SHOW STATEMENT = will");
+H("display the statement being proved.");
+H("");
+H("By default, only the statement and its $e hypotheses are shown, and if");
+H("the label has wildcards, only $a and $p statements are shown.");
+H("");
+H("Optional qualifiers (only one qualifier at a time is allowed):");
+H("    / COMMENT - This qualifier includes the comment that immediately");
+H("        precedes the statement.");
+H("    / FULL - Show complete information about each statement, and show all");
+H("        statements matching <label> (including $e and $f statements).");
+H("    / TEX - This qualifier will write the statement information to the");
+H("        LaTeX file previously opened with OPEN TEX.");
+H("    / OLD_TEX - Same as / TEX, except that LaTeX macros are used to fit");
+H("        equations into line.  This mode is obsolete and will be");
+H("        removed eventually.");
+H("    / HTML - This qualifier invokes a special mode of SHOW STATEMENT which");
+H("        creates a Web page for the statement.  It may not be used with");
+H("        any other qualifier.  See HELP HTML for more information.");
+H("    / ALT_HTML, / BRIEF_HTML, / BRIEF_ALT_HTML - See HELP HTML for more");
+H("        information on these.");
+H("    / NO_VERSIONING - When used with / HTML or the 3 HTML qualifiers");
+H("        above, a backup file suffixed with ~1 is not created (i.e. the");
+H("        previous version is overwritten).");
+H("    / TIME - When used with / HTML or the 3 HTML qualifiers, prints");
+H("        the run time used by each statement.");
+H("    / MNEMONICS - Produces the output file mnemosyne.txt for use with");
+H("        Mnemosyne http://www.mnemosyne-proj.org/principles.php.  Should");
+H("        not be used with any other qualifier.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW PROOF");
+H("Syntax:  SHOW PROOF <label-match> [<qualifiers (see below)>]");
+H("");
+H("This command displays the proof of the specified $p statement various");
+H("formats.  The <label-match> may contain \"*\" (0-or-more-character match) and");
+H("\"?\" (single-character match) wildcard characters to match multiple");
+H("statements.  See HELP SEARCH for other label matching conventions.");
+H("Without any qualifiers, only the logical steps will be shown (i.e.");
+H("syntax construction steps will be omitted), in indented format.");
+H("");
+H("Note that a compressed proof will have references to repeated parts of");
+H("the proof with \"local labels\" prefixed with \"@\".  To show all steps");
+H("in the original RPN proof, you must SAVE PROOF <label-match> / NORMAL before");
+H("using SHOW PROOF.");
+H("");
+H("Most of the time, you will use");
+H("    SHOW PROOF <label-match>");
+H("to see just the proof steps corresponding to logical deduction.");
+H("");
+H("Optional qualifiers:");
+H("    / ESSENTIAL - the proof tree is trimmed of all $f hypotheses before");
+H("        being displayed.  (This is the default, and it is redundant to");
+H("        specify it.)");
+H("    / ALL - the proof tree is not trimmed of all $f hypotheses before");
+H("        being displayed.  / ESSENTIAL and / ALL are mutually exclusive.");
+H("    / FROM_STEP <step> - the display starts at the specified step.  If");
+H("        this qualifier is omitted, the display starts at the first step.");
+H("    / TO_STEP <step> - the display ends at the specified step.  If this");
+H("        qualifier is omitted, the display ends at the last step.");
+H("    / DEPTH <number> - Only steps at less than the specified proof");
+H("        tree depth are displayed.  Useful for obtaining an overview of");
+H("        the proof.");
+H("    / REVERSE - the steps are displayed in reverse order.");
+H("    / RENUMBER - when used with / ESSENTIAL, the steps are renumbered");
+H("        to correspond only to the essential steps.");
+H("    / TEX - the proof is converted to LaTeX and stored in the file opened");
+H("        with OPEN TEX.  Tip:  SET WIDTH 120 (or so) to to fit equations");
+H("        to LaTeX line.  Then use SHOW PROOF / TEX / LEMMON / RENUMBER.");
+H("    / OLD_TEX - same as TEX but uses macros to fit line.  Obsolete and");
+H("        will be removed eventually.");
+H("    / LEMMON - The proof is displayed in a non-indented format known");
+H("        as Lemmon style, with explicit previous step number references.");
+H("        If this qualifier is omitted, steps are indented in a tree format.");
+H("    / START_COLUMN <number> - Overrides the default column at which");
+H("        the formula display starts in a Lemmon style display.  Affects");
+H("        only displays using the / LEMMON qualifier.");
+H("    / NO_REPEATED_STEPS - When a proof step is identical to an earlier");
+H("        step, it will not be repeated.  Instead, a reference to it will be");
+H("        changed to a reference to the earlier step.  In particular,");
+H("        SHOW PROOF <label-match> / LEMMON / RENUMBER / NO_REPEATED_STEPS");
+H("        will have the same proof step numbering as the web page proof");
+H("        generated by SHOW STATEMENT  <label-match> / HTML, rather than");
+H("        the proof step numbering of the indented format");
+H("        SHOW PROOF <label-match> / RENUMBER.  This qualifier affects only");
+H("        displays also using the / LEMMON qualifier.");
+H("    / STATEMENT_SUMMARY - Summarizes all statements (like a brief SHOW");
+H("        STATEMENT) used by the proof.  May not be used with any other");
+H("        qualifier except / ESSENTIAL.");
+H("    / SIZE - Shows size of the proof in the source.  The size depends on");
+H("        how it was last SAVEd (compressed or normal).");
+H("    / DETAILED_STEP <step> - Shows the details of what is happening at");
+H("        a specific proof step.  May not be used with any other qualifier.");
+H("    / NORMAL, / COMPRESSED, / EXPLICIT, / PACKED, / FAST,");
+H("        / OLD_COMPRESSION - These qualifiers are the same as for");
+H("        SAVE PROOF except that the proof is displayed on the screen in");
+H("        a format suitable for manual inclusion in a source file.  See");
+H("        HELP SAVE PROOF.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP MIDI");
+H("Syntax:  MIDI <label> [/ PARAMETER \"<parameter string>\"]");
+H("");
+H("This will create a MIDI sound file for the proof of <label>, where <label>");
+H("is one of the $p statement labels shown with SHOW LABELS.  The <label> may");
+H("contain \"*\" (0-or-more-character match) and \"?\" (single-character");
+H("match) wildcard characters to match multiple statements.  For each matched");
+H("label, a file will be created called <label>.txt which is a MIDI source");
+H("file that can be converted to a MIDI binary file with the \"t2mf\" utility");
+H("that can be obtained at:");
+H("   http://www.hitsquad.com/smm/programs/mf2t/download.shtml");
+H("Note: the MS-DOS version t2mf.exe only handles old-style 8.3 file names,");
+H("so files such as pm2.11.txt are rejected and must be renamed to");
+H("e.g. pm2_11.txt.");
+H("");
+H("The parameters are:");
+H("");
+H("  f = make the tempo fast (default is slow).");
+H("  m = make the tempo medium (default is slow).");
+H("      Both \"f\" and \"m\" should not be specified simultaneously.");
+H("  s = syncopate the melody by silencing repeated notes, using");
+H("      a method selected by whether the \"h\" parameter below is also");
+H("      present (default is no syncopation).");
+H("  h = allow syncopation to hesitate i.e. all notes in a");
+H("      sequence of repeated notes are silenced except the first (default");
+H("      is no hesitation, which means that every other note in a repeated");
+H("      sequence is silenced - this makes it sound slightly more rhythmic).");
+H("      The \"h\" parameter is meaningful only if the \"s\" parameter above");
+H("      is also present.");
+H("  w = use only the white keys on the piano keyboard (default");
+H("      is potentially to use all keys).");
+H("  b = use only the black keys on the piano keyboard (default");
+H("      is all keys).  Both \"w\" and \"b\" should not be specified");
+H("      simultaneously.");
+H("  i = use an increment of one keyboard note per proof");
+H("      indentation level.  The default is to use an automatic increment of");
+H("      up to four notes per level based on the dynamic range of the whole");
+H("      song.");
+H("");
+H("Quotes around the parameter string are optional if it has no spaces.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW NEW_PROOF");
+H("Syntax:  SHOW NEW_PROOF [<qualifiers (see below)]");
+H("");
+H("This command (available only in Proof Assistant mode) displays the proof");
+H("in progress.  It is identical to the SHOW PROOF command, except that the");
+H("statement is not specified (since it is the statement being proved) and");
+H("following qualifiers are not available:");
+H("    / STATEMENT_SUMMARY");
+H("    / DETAILED_STEP");
+H("    / FAST");
+H("");
+H("Also, the following additional qualifiers are available:");
+H("    / UNKNOWN - Shows only steps that have no statement assigned.");
+H("    / NOT_UNIFIED - Shows only steps that have not been unified.");
+H("");
+H("Note that / ALL, / DEPTH, / UNKNOWN, and / NOT_UNIFIED may");
+H("be used in any combination; each of them effectively filters out (or");
+H("\"unfilters\" in the case of / ALL) additional steps from the proof");
+H("display.");
+H("");
+H("See also:  SHOW PROOF");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW USAGE");
+H("Syntax:  SHOW USAGE <label-match> [/ RECURSIVE]");
+H("");
+H("This command lists the statements whose proofs make direct reference to");
+H("the statement(s) specified by <label-match>.  <label-match> may contain *");
+H("and ? wildcard characters; see HELP SEARCH for wildcard matching rules.");
+H("");
+H("Optional qualifiers:");
+H("    / RECURSIVE - Also include include statements whose proof ultimately");
+H("        depend on the statement specified.");
+H("    / ALL - Include $e and $f statements.  Without / ALL, $e and $f");
+H("        statements are excluded when <label-match> contains wildcard");
+H("        characters.");
+H("");
+
+free_vstring(saveHelpCmd); /* Deallocate memory */
+
+return;
+} /* help2 */
+
+
+/* Split up help2 into help2 and help3 so lcc optimizer wouldn't overflow */
+void help3(vstring helpCmd) {
+
+vstring_def(saveHelpCmd);
+/* help3() may be called with a temporarily allocated argument (left(),
+   cat(), etc.), and the let()s in the eventual print2() calls will
+   deallocate and possibly corrupt helpCmd.  So, we grab a non-temporarily
+   allocated copy here.  (And after this let(), helpCmd will become invalid
+   for the same reason.)  */
+let(&saveHelpCmd, helpCmd);
+
+
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SHOW TRACE_BACK");
+H("Syntax:  SHOW TRACE_BACK <label-match> [/ ESSENTIAL] [/ AXIOMS] [/ TREE]");
+H("             [/ DEPTH <number>] [/ COUNT_STEPS] [/MATCH <label-match>]");
+H("             [/TO <label-match>]");
+H("");
+H("This command lists all statements that the proof of the $p statement(s)");
+H("specified by <label-match> depends on.  <label-match> may contain *");
+H("and ? wildcard characters; see HELP SEARCH for wildcard matching rules.");
+H("");
+H("Optional qualifiers:");
+H("    / ESSENTIAL - Restrict the trace-back to $e hypotheses of proof");
+H("        trees.");
+H("    / AXIOMS - List only the axioms that the proof ultimately depends on.");
+H("    / TREE - Display the trace-back in an indented tree format.");
+H("    / DEPTH - Restrict the / TREE traceback to the specified indentation");
+H("        depth.");
+H("    / COUNT_STEPS - Counts the number of steps the proof would have if");
+H("        fully expanded back to axioms.  If / ESSENTIAL is specified,");
+H("        expansions of floating hypotheses are not counted.  The steps are");
+H("        counted based on how the proof is stored (compressed or normal).");
+H("    / MATCH <label-match> - include only statements matching <label-match>");
+H("        in the output display.  Undisplayed statements are still used to");
+H("        compute the list.  For example, / AXIOMS / MATCH ax-* will show");
+H("        set.mm axioms but not definitions.");
+H("    / TO <label-match> - include only statements  that depend on the");
+H("        <label-match> statement(s).  For example,");
+H("        SHOW TRACE_BACK ac6s / TO ax-reg will list all statements");
+H("        requiring ax-reg that ac6s depends on.  In case there are");
+H("        multiple paths from ac6s back to ax-reg, all statements involved");
+H("        in all paths are listed.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SEARCH");
+H("Syntax:  SEARCH <label-match> \"<symbol-match>\" [/ ALL] [/ COMMENTS]");
+H("             [/ JOIN]");
+H("");
+H("This command searches all $a and $p statements matching <label-match>");
+H("for occurrences of <symbol-match>.");
+H("");
+H("A * in <label-match> matches any zero or more characters in a label.");
+H("");
+H("A ? in <label-match> matches any single character.  For example,");
+H("SEARCH p?4* \"ph <-> ph\" will check statements whose labels have p and 4");
+H("in their first and third character positions, and display them when their");
+H("math strings contain \"ph <-> ph\".");
+H("");
+H("A ~ in <label-match> divides the match into two labels, <from>~<to>, and");
+H("matches statements located in the source file range between <from> and");
+H("<to> inclusive; <from> and <to> may not have * or ? wildcards; if <from>");
+H("(or <to>) is empty, the first (or last) statement will be assumed.");
+H("");
+H("If <label-match> is % (percent sign), it will match all statements with");
+H("proofs that were changed in the current session; cannot be used with");
+H("wildcards.");
+H("");
+H("If <label-match> is = (equals sign), it will match the statement being");
+H("proved or last proved in the Proof Assistant (MM-PA); note that");
+H("\"PROVE =\" is a quick way to return to the previous MM-PA session.");
+H("");
+H("If <label-match> is #nnn e.g. #1234, it will match the numeric statement");
+H("number nnn; digits cannot contain wildcards.");
+H("");
+H("If <label-match> is @nnn e.g. @1234, it will match the web page statement");
+H("number (small colored number) nnn; digits cannot contain wildcards.");
+H("");
+H("Multiple <label-match> forms may be joined with commas e.g.");
+H("SEARCH ab*,cd* ... will match all labels starting with ab or cd.");
+H("");
+H("");
+H("A $* in <symbol-match> matches any sequence of zero or more tokens");
+H("in the statement's math string.");
+H("");
+H("A $? in <symbol-match> matches zero or one character in a math token.");
+H("The quotes surrounding <symbol-match> may be single or double quotes.");
+H("For example,");
+H("SEARCH * 'E. $? A. $? $?$? -> $* E.' using the set.mm database will find");
+H("\"E. x A. y ph -> A. y E. x ph\".  As this example shows, $? is");
+H("particularly useful when you don't know what variable names were used in");
+H("a theorem of interest.");
+H("");
+H("");
+H("Note 1. The first and last characters of <label-match>, if they are not");
+H("wildcards (nor part of ~, =, %, #, or @ forms), will be matched against");
+H("the first and last characters of the label.  In contrast, <symbol-match>");
+H("is a substring match, i.e. it has implicit $* wildcards before and after");
+H("it.");
+H("");
+H("Note 2. An \"unofficial\" <symbol-match> feature is that $ and ? can be");
+H("used instead of $* and $? for brevity provided that no math token");
+H("contains a ? character.");
+H("");
+H("Optional qualifiers:");
+H("    / ALL - Also search $e and $f statements.");
+H("    / COMMENTS - Search the comment that immediately precedes each");
+H("        label-matched statement for <symbol-match>, instead of searching");
+H("        the statement's math string.  In this mode, <symbol-match> is an");
+H("        arbitrary, non-case-sensitive character string.  Wildcards in");
+H("        <symbol-match> are not implemented in this mode.");
+H("    / JOIN - In the case of a $a or $p statement, prepend its $e");
+H("        hypotheses for searching.  / JOIN has no effect in / COMMENTS");
+H("        mode.");
+H("");
+H("See the last section of HELP LET for how to handle quotes and special");
+H("characters.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET ECHO");
+H("Syntax:  SET ECHO ON or SET ECHO OFF");
+H("");
+H("The SET ECHO ON command will cause command lines to be echoed with any");
+H("abbreviations expanded.  While learning the Metamath commands, this");
+H("feature will show you the exact command that your abbreviated input");
+H("corresponds to.  This is also useful to assist creating robust command");
+H("files (see HELP SUBMIT) from your log file (see HELP OPEN LOG).  To make");
+H("it easier to extract these lines, \"!\" (which you will discard) is");
+H("prepended to each echoed command line.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET SCROLL");
+H("Syntax:  SET SCROLL PROMPTED or SET SCROLL CONTINUOUS");
+H("");
+H("The Metamath command line interface starts off in the PROMPTED mode,");
+H("which means that you will prompted to continue or quit after each");
+H("screenful of a long listing.  In CONTINUOUS mode, long listings will be");
+H("scrolled without pausing.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET WIDTH");
+H("Syntax:  SET WIDTH <number>");
+H("");
+H("Metamath assumes the width of your screen is 79 characters.  If your");
+H("screen is wider or narrower, this command lets you to change the screen");
+H("width.  A larger width is advantageous for logging proofs to an output");
+H("file to be printed on a wide printer.  A smaller width may be necessary");
+H("on some terminals; in this case, the wrapping of the information");
+H("messages may sometimes seem somewhat unnatural, however.  In LaTeX, there");
+H("is normally a maximum of 61 characters per line with typewriter font.");
+H("");
+H("Note:  The default width is 79 because Windows Command Prompt issues a");
+H("spurious blank line after an 80-character line (try it!).");
+H("");
+H("Note:  This command was SET SCREEN_WIDTH prior to Version 0.07.9.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET HEIGHT");
+H("Syntax:  SET HEIGHT <number>");
+H("");
+H("Metamath assumes your screen height is 24 lines of characters.  If your");
+H("screen is taller or shorter, this command lets you to change the number");
+H("of lines at which the display pauses and prompts you to continue.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET DISCOURAGEMENT");
+H("Syntax:  SET DISCOURAGEMENT OFF or SET DISCOURAGEMENT ON");
+H("");
+H("By default this is set to ON, which means that statements whose");
+H("description comments have the markup tags \"(New usage is discouraged.)\"");
+H("or \"(Proof modification is discouraged.)\" will be blocked from usage");
+H("or proof modification.  When this setting is OFF, those actions are no");
+H("longer blocked.  This setting is intended only for the convenience of");
+H("advanced users who are intimately familiar with the database, for use");
+H("when maintaining \"discouraged\" statements.  SHOW SETTINGS will show you");
+H("the current value.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET CONTRIBUTOR");
+H("Syntax:  SET CONTRIBUTOR <name>");
+H("");
+H("Specify the contributor name for new \"(Contributed by...\" comment");
+H("markup added by SAVE PROOF or SAVE NEW_PROOF.  Use quotes (' or \")");
+H("around <name> if it contains spaces.  The current contributor is");
+H("displayed by SHOW SETTINGS.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET ROOT_DIRECTORY");
+H("Syntax:  SET ROOT_DIRECTORY <directory path>");
+H("");
+H("Specify the directory path (relative to the working directory i.e. the");
+H("directory from which the Metamath program was launched) which will be");
+H("prepended to READ, WRITE SOURCE, and files included with $[...$].");
+H("Enclose <directory path> in single or double quotes if the path contains");
+H("\"/\".  A trailing \"/\" will be added automatically if missing.  The");
+H("current directory path is displayed by SHOW SETTINGS.");
+H("");
+H("Use a quoted space (' ' or \" \") for <directory path> if you want to");
+H("reset it to be the working directory.");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET UNIFICATION_TIMEOUT");
+H("Syntax:  SET UNIFICATION_TIMEOUT <number>");
+H("");
+H("(This command affects the Proof Assistant only.)");
+H("");
+H("Sometimes the Proof Assistant will inform you that a unification timeout");
+H("occurred.  This may happen when you try to UNIFY formulas with many");
+H("unknown variables, since the time to compute unifications may grow");
+H("exponentially with the number of variables.  If you want Metamath to try");
+H("harder (and you're willing to wait longer) you may increase this");
+H("parameter.  SHOW SETTINGS will show you the current value.");
+H("");
+H("Often, a better solution to resolve a unification timeout is to manually");
+H("assign some or all of the unknowns (see HELP LET) then try to unify");
+H("again.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET EMPTY_SUBSTITUTION");
+H("Syntax:  SET EMPTY_SUBSTITUTION ON or SET EMPTY_SUBSTITUTION OFF");
+H("");
+H("(This command affects the Proof Assistant only.  It may be issued");
+H("outside of the Proof Assistant.)");
+H("");
+H("The Metamath language allows variables to be substituted with empty");
+H("symbol sequences.  However, in most formal systems this will never happen");
+H("in a valid proof.  Allowing for this possibility increases the likelihood");
+H("of ambiguous unifications during proof creation.  The default is that");
+H("empty substitutions are not allowed; for formal systems requiring them,");
+H("you must SET EMPTY_SUBSTITUTION ON.  Note that empty substitutions are");
+H("always permissible in proof verification (VERIFY PROOF...) outside the");
+H("Proof Assistant.  (See the MIU system in the Metamath book for an example");
+H("of a system needing empty substitutions; another example would be a");
+H("system that implements a Deduction Rule and in which deductions from");
+H("empty assumption lists would be permissible.)");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET SEARCH_LIMIT");
+H("Syntax:  SET SEARCH_LIMIT <number>");
+H("");
+H("(This command affects the Proof Assistant only.)");
+H("");
+H("This command sets a parameter that determines when the IMPROVE command");
+H("in Proof Assistant mode gives up.  If you want IMPROVE to search harder,");
+H("you may increase it.  The SHOW SETTINGS command tells you its current");
+H("value.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET JEREMY_HENTY_FILTER");
+H("Syntax:  SET JEREMY_HENTY_FILTER ON or SET JEREMY_HENTY_FILTER OFF");
+H("");
+H("(This command affects the Proof Assistant only.)");
+H("");
+H("The \"Henty filter\" is an ingenious algorithm suggested by Jeremy Henty");
+H("that reduces the number of ambiguous unifications by eliminating");
+H("\"equivalent\" ones in a sense defined by Henty.  Normally this filter");
+H("is ON, and the only reason to turn it off would be for debugging.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP VERIFY PROOF");
+H("Syntax:  VERIFY PROOF <label-match> [/ SYNTAX_ONLY]");
+H("");
+H("This command verifies the proofs of the specified statements.");
+H("<label-match> may contain * and ? wildcard characters to verify more than");
+H("one proof; for example \"abc?def*\" will match all labels beginning with");
+H("\"abc\" followed by any single character followed by \"def\".");
+H("VERIFY PROOF * will verify all proofs in the database.");
+H("See HELP SEARCH for complete wildcard matching rules.");
+H("");
+H("Optional qualifier:");
+H("    / SYNTAX_ONLY - This qualifier will perform a check of syntax and RPN");
+H("        stack violations only.  It will not verify that the proof is");
+H("        correct.");
+H("");
+H("Note: READ, followed by VERIFY PROOF *, will ensure the database is free");
+H("from errors in Metamath language but will not check the markup language");
+H("in comments.  See HELP VERIFY MARKUP.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP VERIFY MARKUP");
+H("Syntax:  VERIFY MARKUP <label-match> [/ DATE_SKIP] [/ TOP_DATE_CHECK]");
+H("            [/ FILE_CHECK] [/ UNDERSCORE_SKIP] [/ MATHBOX_SKIP] [/VERBOSE]");
+H("");
+H("This command checks comment markup and other informal conventions we have");
+H("adopted.  It error-checks the latexdef, htmldef, and althtmldef statements");
+H("in the $t statement of a Metamath source file.  It error-checks any `...`,");
+H("~ <label>, and [bibref] markups in statement descriptions.  It checks that");
+H("$p and $a statements have the same content when their labels start with");
+H("\"ax\" and \"ax-\" respectively but are otherwise identical, for example");
+H("ax4 and ax-4.  It verifies the date consistency of \"(Contributed by...)\",");
+H("\"(Revised by...)\", and \"(Proof shortened by...)\" tags in the comment");
+H("above each $a and $p statement.  See HELP SEARCH for <label-match> rules.");
+H("");
+H("Optional qualifiers:");
+H("    / DATE_SKIP - This qualifier will skip date consistency checking,");
+H("        which is usually not required for databases other than set.mm");
+H("    / TOP_DATE_CHECK - This qualifier will check date consistency of");
+H("        the version date at the top of the database file.");
+H("    / TOP_DATE_SKIP - This is an obsolete option, enabled by default.");
+H("    / FILE_CHECK - This qualifier will include checks that require");
+H("        external files to be present, such as checking GIF existence and");
+H("        bibliographic links to mmset.html or equivalent.");
+H("    / FILE_SKIP - This is an obsolete option, enabled by default.");
+H("    / UNDERSCORE_SKIP - This qualifier will skip warnings for labels");
+H("        containing underscore (\"_\") characters.  Although they are");
+H("        legal per the Metamath spec, they may cause ambiguities with");
+H("        certain translators (such as to MM0) that convert \"-\" to \"_\".");
+H("        bibliographic links to mmset.html or equivalent.  It is useful");
+H("        for doing a quick check from a directory without these files");
+H("    / MATHBOX_SKIP - This qualifier will skip checking for mathbox");
+H("        independence i.e. that no mathbox proof references a statement");
+H("        in another (earlier) mathbox.");
+H("    / VERBOSE - Provides more information.  Currently it provides a list");
+H("        of axXXX vs. ax-XXX matches.");
+H("");
+H("See also HELP LANGUAGE, HELP HTML, HELP WRITE THEOREM_LIST, and");
+H("HELP SET DISCOURAGEMENT for more details on the markup syntax.");
+H("See the 11-May-2016 (\"is discouraged\"), 14-May-2017 (date format), and");
+H("21-Dec-2017 (file inclusion) entries in");
+H("http://us.metamath.org/mpeuni/mmnotes.txt for further details on several");
+H("kinds of markup.  See HELP WRITE THEOREM_LIST for format of section headers.");
+H("");
+H("For help with modularization tags such as \"$( Begin $[ set-header.mm $] $)\",");
+H("see the 21-Dec-2017 entry in http://us.metamath.org/mpeuni/mmnotes.txt .");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SUBMIT");
+H("Syntax:  SUBMIT <filename> [/ SILENT]");
+H("");
+H("This command causes further command lines to be taken from the specified");
+H("command file.  Note that any line beginning with an exclamation point (!)");
+H("is treated as a comment (i.e. ignored).  Also note that the scrolling");
+H("of the screen output is continuous, so you may want to open a log file");
+H("(see HELP OPEN LOG) to record the results that fly by on the screen.");
+H("After the lines in the command file are exhausted, Metamath returns to");
+H("its normal user interface mode.");
+H("");
+H("SUBMIT commands can occur inside of a SUBMIT command file, up to 10 levels");
+H("deep (determined by MAX_COMMAND_FILE_NESTING in mminou.h.");
+H("");
+H("Optional qualifier:");
+H("    / SILENT - This qualifier suppresses the screen output of the SUBMIT");
+H("        command.  The output will still be recorded in any log file that");
+H("        has been opened with OPEN LOG (or is opened inside the command");
+H("        file itself).  The screen output of any operating system commands");
+H("        inside the command file (see HELP SYSTEM) is not suppressed.");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SYSTEM");
+H("A line enclosed in single or double quotes will be executed by your");
+H("computer's operating system, if it has such a feature.  For example, on a");
+H("GNU/Linux system,");
+H("    MM> 'ls | less -EX'");
+H("will list disk directory contents.  Note that this feature will not work");
+H("on the pre-OSX Macintosh, which does not have a command line interface.");
+H("");
+H("For your convenience, the trailing quote is optional, for example:");
+H("    MM> 'ls | less -EX");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP MM-PA");
+H("See HELP PROOF_ASSISTANT");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP MORE");
+H("Syntax:  MORE <filename>");
+H("");
+H("This command will type (i.e. display) the contents of an ASCII file on");
+H("your screen.  (This command is provided for convenience but is not very");
+H("powerful.  See HELP SYSTEM to invoke your operating system's command to");
+H("do this, such as \"less -EX\" in Linux.)");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP FILE SEARCH");
+H("Syntax:  FILE SEARCH <filename> \"<search string>\" [/ FROM_LINE");
+H("             <number>] [/ TO_LINE <number>]");
+H("");
+H("This command will search an ASCII file for the specified string in");
+H("quotes, within an optional range of line numbers, and display the result");
+H("on your screen.  The search is case-insensitive.  (This command is");
+H("deprecated.  See HELP SYSTEM to invoke your operating system's");
+H("equivalent command, such as \"grep\" in Linux.)");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP PROVE");
+H("Syntax:  PROVE <label> [/ OVERRIDE]");
+H("");
+H("This command will enter the Proof Assistant, which will allow you to");
+H("create or edit the proof of the specified statement.");
+H("");
+H("Optional qualifier:");
+H("    / OVERRIDE - By default, PROVE will refuse to enter the Proof");
+H("        Assistant if \"(Proof modification is discouraged.)\" is present");
+H("        in the statement's description comment.  This qualifier will");
+H("        allow the Proof Assistant to be entered.");
+H("");
+H("See also:  HELP PROOF_ASSISTANT and HELP EXIT");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP PROOF_ASSISTANT");
+H("Before using the Proof Assistant, you must add a $p to your source file");
+H("(using a text editor) containing the statement you want to prove.  Its");
+H("proof should consist of a single ?, meaning \"unknown step.\"  Example:");
+H("    eqid $p x = x $= ? $.");
+H("");
+H("To enter the Proof Assistant, type PROVE <label>, e.g. PROVE eqid.");
+H("Metamath will respond with the MM-PA> prompt.");
+H("");
+H("Proofs are created working backwards from the statement being proved,");
+H("primarily using a series of ASSIGN commands.  A proof is complete when");
+H("all steps are assigned to statements and all steps are unified and");
+H("completely known.  During the creation of a proof, Metamath will allow");
+H("only operations that are legal based on what is known up to that point.");
+H("For example, it will not allow an ASSIGN of a statement that cannot be");
+H("unified with the unknown proof step being assigned.");
+H("");
+H("IMPORTANT:  You should figure out your first few proofs completely and");
+H("write them down by hand, before using the Proof Assistant.  Otherwise you");
+H("will become extremely frustrated.  The Proof Assistant is NOT a tool to");
+H("help you discover proofs.  It is just a tool to help you add them to the");
+H("database.  For a tutorial read Section 2.4 of the Metamath book.  To");
+H("practice using the Proof Assistant, you may want to PROVE an existing");
+H("theorem, then delete all steps with DELETE ALL, then re-create it with");
+H("the Proof Assistant while looking at its proof display (before deletion).");
+H("");
+H("The commands available to help you create a proof are the following.");
+H("See the help for the individual commands for more detail.");
+H("    SHOW NEW_PROOF [/ ALL,...] - Displays the proof in progress.");
+H("        You will use this command a lot; see HELP SHOW NEW_PROOF to");
+H("        become familiar with its qualifiers.  The qualifiers / UNKNOWN");
+H("        and / NOT_UNIFIED are useful for seeing the work remaining to be");
+H("        done.  The combination / ALL / UNKNOWN is useful identifying");
+H("        dummy variables that must be assigned, or attempts to use illegal");
+H("        syntax, when IMPROVE ALL is unable to complete the syntax");
+H("        constructions.  Unknown variables are shown as $1, $2,...");
+H("    ASSIGN <step> <label> - Assigns an unknown step with the statement");
+H("        specified by <label>.  This will normally be your most frequently");
+H("        used command for creating proofs.  The usual proof entry process");
+H("        consists of successively ASSIGNing labels to unknown steps shown");
+H("        by SHOW NEW_PROOF / UNKNOWN.");
+H("    LET VARIABLE <variable> = \"<symbol sequence>\" - Forces a symbol");
+H("        sequence to replace an unknown variable in a proof.  It is useful");
+H("        for helping difficult unifications, and is necessary when you");
+H("        have dummy variables that must be specified.");
+H("    LET STEP <step> = \"<symbol sequence>\" - Forces a symbol sequence");
+H("        to replace the contents of a proof step, provided it can be");
+H("        unified with the existing step contents.  (Rarely useful.)");
+H("    UNIFY STEP <step> (or UNIFY ALL) - Unifies the source and target of");
+H("        a step.  If you specify a specific step, you will be prompted");
+H("        to select among the unifications that are possible.  If you");
+H("        specify ALL, only those steps with unique unifications will be");
+H("        unified.  UNIFY ALL / INTERACTIVE goes through all non-unified");
+H("        steps.");
+H("    INITIALIZE <step> (or ALL) - De-unifies the target and source of");
+H("        a step (or all steps), as well as the hypotheses of the source,");
+H("        and makes all variables in the source unknown.  Useful after");
+H("        an ASSIGN or LET mistake resulted in incorrect unifications.");
+H("    DELETE <step> (or ALL or FLOATING_HYPOTHESES) - Deletes the specified");
+H("        step(s).  DELETE FLOATING_HYPOTHESES then INITIALIZE ALL then");
+H("        UNIFY ALL / INTERACTIVE is useful for recovering from mistakes");
+H("        where incorrect unifications assigned wrong math symbol strings to");
+H("        variables.");
+H("    IMPROVE <step> (or ALL) - Automatically creates a proof for steps");
+H("        (with no unknown variables) whose proof requires no statements");
+H("        with $e hypotheses.  Useful for filling in proofs of $f");
+H("        hypotheses.  The / DEPTH qualifier will also try statements");
+H("        whose $e hypotheses contain no new variables.  WARNING: Save your");
+H("        work (SAVE NEW_PROOF, WRITE SOURCE) before using / DEPTH = 2 or");
+H("        greater, since the search time grows exponentially and may never");
+H("        terminate in a reasonable time, and you cannot interrupt the");
+H("        search.  I have rarely found / DEPTH = 3 or greater to be useful.");
+H("    SAVE NEW_PROOF - Saves the proof in progress internally in the");
+H("        database buffer.  To save it permanently, use WRITE SOURCE after");
+H("        SAVE NEW_PROOF.  To revert to the last SAVE NEW_PROOF,");
+H("        EXIT / FORCE from the Proof Assistant then re-enter the Proof");
+H("        Assistant.");
+H("    SHOW NEW_PROOF / COMPRESSED - Displays the proof in progress on the");
+H("        screen in a format that can be copied and pasted into the");
+H("        database source, as an alternative to a SAVE NEW_PROOF.");
+H("    MATCH STEP <step> (or MATCH ALL) - Shows what statements are");
+H("        possibilities for the ASSIGN statement. (This command is not very");
+H("        useful in its present form and hopefully will be improved");
+H("        eventually.  In the meantime, use the SEARCH statement for");
+H("        candidates matching specific math token combinations.)");
+H("    MINIMIZE_WITH - After a proof is complete, this command will attempt");
+H("        to match other database theorems to the proof to see if the proof");
+H("        size can be reduced as a result.");
+H("    UNDO - Undo the effect of a proof-changing command (all but the SHOW");
+H("        and SAVE commands above).");
+H("    REDO - Reverse the previous UNDO.");
+H("");
+H("The following commands set parameters that may be relevant to your proof:");
+H("    SET UNIFICATION_TIMEOUT");
+H("    SET SEARCH_LIMIT");
+H("    SET EMPTY_SUBSTITUTION - note that default is OFF (contrary to book)");
+H("");
+H("Type EXIT to exit the MM-PA> prompt and get back to the MM> prompt.");
+H("Another EXIT will then get you out of Metamath.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP UNDO");
+H("Syntax:  UNDO");
+H("");
+H("This command, available in the Proof Assistant only, allows any command");
+H("(such as ASSIGN, DELETE, IMPROVE) that affects the proof to be reversed.");
+H("See also HELP REDO and HELP SET UNDO.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP REDO");
+H("Syntax:  REDO");
+H("");
+H("This command, available in the Proof Assistant only, reverses the");
+H("effect of the last UNDO command.  Note that REDO can be issued only");
+H("if no proof-changing commands (such as ASSIGN, DELETE, IMPROVE)");
+H("were issued after the last UNDO.  A sequence of REDOs will reverse as");
+H("many UNDOs as were issued since the last proof-changing command.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SET UNDO");
+H("Syntax:  SET UNDO <number>");
+H("");
+H("(This command affects the Proof Assistant only.)");
+H("");
+H("This command changes the maximum number of UNDOs.  The current maximum");
+H("can be seen with SHOW SETTINGS.  Making it larger uses more memory,");
+H("especially for large proofs.  See also HELP UNDO.");
+H("");
+H("If this command is issued while inside of the Proof Assistant, the");
+H("UNDO stack is reset (i.e. previous possible UNDOs will be lost).");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP ASSIGN");
+H("Syntax:  ASSIGN <step> <label> [/ NO_UNIFY] [/ OVERRIDE]");
+H("         ASSIGN FIRST <label> [/ NO_UNIFY] [/ OVERRIDE]");
+H("         ASSIGN LAST <label> [/ NO_UNIFY] [/ OVERRIDE]");
+H("");
+H("This command, available in the Proof Assistant only, assigns an unknown");
+H("step (one with ? in the SHOW NEW_PROOF listing) with the statement");
+H("specified by <label>.  The assignment will not be allowed if the");
+H("statement cannot be unified with the step.");
+H("");
+H("If <step> starts with \"-\", the -<step>th from last unknown step,");
+H("as shown by SHOW NEW_PROOF / UNKNOWN, will be used.  ASSIGN -0 will");
+H("assign the last unknown step, ASSIGN -1 <label> will assign the");
+H("penultimate unknown step, etc.  If <step> starts with \"+\", the <step>th");
+H("from the first unknown step will be used.  Otherwise, when the step is");
+H("a positive integer (with no \"+\" sign), ASSIGN assumes it is the actual");
+H("step number shown by SHOW NEW_PROOF / UNKNOWN.");
+H("");
+H("ASSIGN FIRST and ASSIGN LAST mean ASSIGN +0 and ASSIGN -0 respectively,");
+H("in other words the first and last steps shown by SHOW NEW_PROOF / UNKNOWN.");
+H("");
+H("Optional qualifiers:");
+H("    / NO_UNIFY - do not prompt user to select a unification if there is");
+H("        more than one possibility.  This is useful for noninteractive");
+H("        command files.  Later, the user can UNIFY ALL / INTERACTIVE.");
+H("        (The assignment will still be automatically unified if there is");
+H("        only one possibility.)");
+H("    / OVERRIDE - By default, ASSIGN will refuse to assign a statement");
+H("        if \"(New usage is discouraged.)\" is present in the statement's");
+H("        description comment.  This qualifier will allow the assignment.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP REPLACE");
+H("Syntax:  REPLACE <step> <label> [/ OVERRIDE]");
+H("Syntax:  REPLACE FIRST <label> [/ OVERRIDE]");
+H("Syntax:  REPLACE LAST <label> [/ OVERRIDE]");
+H("");
+H("This command, available in the Proof Assistant only, replaces the");
+H("current subproof ending at <step> with a new complete subproof (if one");
+H("can be found) ending with statement <label>.  The replacement will be");
+H("done only if complete subproofs can be found that match all of the");
+H("hypotheses of  <label>.  REPLACE is equivalent to IMPROVE with the");
+H("qualifiers / 3 / DEPTH 1 / SUBPROOFS (see HELP IMPROVE), except that");
+H("it considers only statement <label> rather than scanning all preceding");
+H("statements in the database, and it does somewhat more aggressive");
+H("guessing of assignments to work ($nn) variables.");
+H("");
+H("REPLACE will also assign a complete subproof to a currently unknown");
+H("(unassigned) step if a complete subproof can be found.  In many cases,");
+H("REPLACE provides an alternative to ASSIGN (with the same command syntax)");
+H("that will fill in more missing steps when it is successful.  It is often");
+H("useful to try REPLACE first and, if not successful, revert to ASSIGN.");
+H("Note that REPLACE may take a long time to run compared to ASSIGN.");
+H("");
+H("Currently, REPLACE does not allow a $e or $f statement for <label>.  Use");
+H("ASSIGN instead.  (These may be allowed in a future version.)");
+H("");
+H("Occasionally, REPLACE may be too aggressive in guessing assignments to");
+H("work ($nn) variables, and a message with recovery instructions is");
+H("provided when this could be the case.  Recovery can also be attempted with");
+H("DELETE FLOATING_HYPOTHESES then INITIALIZE ALL then");
+H("UNIFY ALL / INTERACTIVE; this will usually work and will salvage the");
+H("subproof found by REPLACE.  (The too-aggressive guessing behavior may be");
+H("improved in a future version.)");
+H("");
+H("If <step> starts with \"-\", the -<step>th from last unknown step,");
+H("as shown by SHOW NEW_PROOF / UNKNOWN, will be used.  REPLACE -0 will");
+H("assign the last unknown step, REPLACE -1 <label> will assign the");
+H("penultimate unknown step, etc.  If <step> starts with \"+\", the <step>th");
+H("from the first unknown step will be used.  Otherwise, when the step is");
+H("a positive integer (with no \"+\" sign), REPLACE assumes it is the actual");
+H("step number shown by SHOW NEW_PROOF (and can be used whether the step is");
+H("known or not).");
+H("");
+H("REPLACE FIRST and REPLACE LAST mean REPLACE +0 and REPLACE -0");
+H("respectively, in other words the first and last steps shown by");
+H("SHOW NEW_PROOF / UNKNOWN.");
+H("");
+H("Optional qualifier:");
+H("    / OVERRIDE - By default, REPLACE will refuse to assign a statement");
+H("        if \"(New usage is discouraged.)\" is present in the statement's");
+H("        description comment.  This qualifier will allow the assignment.");
+H("");
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP MATCH");
+H("Syntax:  MATCH STEP <step> [/ MAX_ESSENTIAL_HYP <number>]");
+H("    or:  MATCH ALL [/ ESSENTIAL_ONLY] [/ MAX_ESSENTIAL_HYP <number>]");
+H("");
+H("This command, available in the Proof Assistant only, shows what");
+H("statements can be unified with the specified step(s).");
+H("");
+H("Optional qualifiers:");
+H("    / MAX_ESSENTIAL_HYP <number> - filters out of the list any statements");
+H("        with more than the specified number of $e hypotheses");
+H("    / ESSENTIAL_ONLY - in the MATCH ALL statement, only the steps that");
+H("        would be listed in SHOW NEW_PROOF display are matched.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP LET");
+H("Syntax:  LET VARIABLE <variable> = \"<symbol sequence>\"");
+H("         LET STEP <step> = \"<symbol sequence>\"");
+H("         LET STEP FIRST = \"<symbol sequence>\"");
+H("         LET STEP LAST = \"<symbol sequence>\"");
+H("");
+H("These commands, available in the Proof Assistant only, assign an unknown");
+H("variable or step with a specific symbol sequence.  They are useful in the");
+H("middle of creating a proof, when you know what should be in the proof");
+H("step but the unification algorithm doesn't yet have enough information");
+H("to completely specify the unknown variables.  An \"unknown\" or \"work\"");
+H("variable is one which has the form $nn in the proof display, such as $1,");
+H("$2, etc.  The <symbol sequence> may contain other unknown variables if");
+H("desired.");
+H("Examples:");
+H("    LET VARIABLE $32 = \" A = B \"");
+H("    LET VARIABLE $32 = \" A = $35 \"");
+H("    LET STEP 10 = \" |- x = x \"");
+H("    LET STEP -2 = \" |- ( $7 <-> ph ) \"");
+H("    LET STEP +2 = \" |- ( $7 <-> ph ) \"");
+H("");
+H("Any symbol sequence will be accepted for the LET VARIABLE command.  In");
+H("LET STEP, only symbol sequences that can be unified with the step are");
+H("accepted.  LET STEP assignments are prefixed with \"=(User)\" in most");
+H("SHOW NEW_PROOF displays.");
+H("");
+H("If <step> starts with \"-\", the -<step>th from last unknown step,");
+H("as shown by SHOW NEW_PROOF / UNKNOWN, will be used.  LET STEP -0 will");
+H("assign the last unknown step, LET STEP -1 <label> will assign the");
+H("penultimate unknown step, etc.  If <step> starts with \"+\", the <step>th");
+H("from the first unknown step will be used.  LET STEP FIRST and LET STEP");
+H("LAST means LET STEP +0 and LET STEP -0 respectively.  Otherwise, when");
+H("the step is a positive integer (with no \"+\" sign), LET STEP may be");
+H("used to assign known as well as unknown steps.");
+H("");
+H("Note that SAVE PROOF does not save any LET VARIABLE or LET STEP");
+H("assignments.  However, IMPROVE ALL prior to SAVE PROOF will usually");
+H("preserve the information for steps with no unknown variables.");
+H("");
+H("Quotes and special characters in command-line arguments");
+H("-------------------------------------------------------");
+H("");
+H("You can use single quotes to surround the math symbol string argument of");
+H("a LET or SEARCH command when the argument contains a double quote.");
+H("For example,");
+H("  MM-PA> LET VARIABLE $2 = '( F \" A )'");
+H("");
+H("The trailing quote that would be the last character on the line");
+H("may be omitted for convenience, to save typing:");
+H("  MM-PA> LET VARIABLE $2 = '( F \" A )");
+H("");
+H("If a math symbol string has both single and double quotes, you must");
+H("use the prompted completion feature by pressing RETURN where the symbol");
+H("string would go.  Quotes aren't needed (and must not be used) around");
+H("the answer to a prompted completion question.");
+H("  MM-PA> LET VARIABLE $2 =");
+H("  With what math symbol string? ( `' F \" A )");
+H("");
+H("Quotes are optional around any math symbol string containing a single");
+H("symbol and and not containing \"=\", \"/\", a single quote, or a double");
+H("quote:");
+H("  MM-PA> LET VARIABLE $17 = ph");
+H("");
+H("Unquoted \"=\" and \"/\" are special in a command line in that they are");
+H("implicitly surrounded by white space:");
+H("  MM-PA> LET VARIABLE $17=ph");
+H("  MM-PA> SHOW NEW_PROOF/UNKNOWN");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP UNIFY");
+H("HELP UNIFY");
+H("Syntax:  UNIFY STEP <step>");
+H("         UNIFY ALL [/ INTERACTIVE]");
+H("");
+H("These commands, available in the Proof Assistant only, unify the source");
+H("and target of the specified step(s). If you specify a specific step, you");
+H("will be prompted to select among the unifications that are possible.  If");
+H("you specify ALL, only those steps with unique unifications will be");
+H("unified.");
+H("");
+H("Optional qualifier for UNIFY ALL:");
+H("    / INTERACTIVE - You will be prompted to select among the unifications");
+H("        that are possible for any steps that do not have unique");
+H("        unifications.");
+H("");
+H("See also SET UNIFICATION_TIMEOUT.  The default is 100000, but increasing");
+H("it to 1000000 can help difficult cases.  The LET VARIABLE command to");
+H("manually assign unknown variables also helps difficult cases.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP INITIALIZE");
+H("Syntax:  INITIALIZE ALL");
+H("         INITIALIZE USER");
+H("         INITIALIZE STEP <step>");
+H("");
+H("These commands, available in the Proof Assistant only, \"de-unify\" the");
+H("target and source of a step (or all steps), as well as the hypotheses of");
+H("the source, and make all variables in the source and the source's");
+H("hypotheses unknown.  This command is useful to help recover when an");
+H("ASSIGN mistake resulted in incorrect unifications.  After you DELETE the");
+H("incorrect ASSIGN, use INITIALIZE ALL then UNIFY ALL / INTERACTIVE to");
+H("recover the state before the mistake.");
+H("");
+H("INITIALIZE ALL will void all LET VARIABLE assignments as well as de-unify");
+H("all targets and sources.  INITIALIZE USER will delete all LET STEP");
+H("assignments but will not de-unify.  INITIALIZE STEP will do all of these");
+H("actions for the specified step.");
+H("");
+H("After de-unification and variable deassignment, proof steps that are");
+H("completely known will be automatically re-unified.  If you want to");
+H("de-unify these, use DELETE FLOATING_HYPOTHESES then INITIALIZE ALL.");
+H("");
+H("See also:  UNIFY and DELETE");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP DELETE");
+H("Syntax:  DELETE STEP <step>");
+H("         DELETE ALL");
+H("         DELETE FLOATING_HYPOTHESES");
+H("");
+H("These commands are available in the Proof Assistant only. The DELETE STEP");
+H("command deletes the proof tree section that branches off of the specified");
+H("step and makes the step become unknown.  DELETE ALL is equivalent to");
+H("DELETE STEP <step> where <step> is the last step in the proof (i.e. the");
+H("beginning of the proof tree).");
+H("");
+H("DELETE FLOATING_HYPOTHESES will delete all sections of the proof that");
+H("branch off of $f statements.  It is sometimes useful to do this before");
+H("an INITIALIZE command to recover from an error.  Note that once a proof");
+H("step with a $f hypothesis as the target is completely known, the IMPROVE");
+H("command can usually fill in the proof for that step.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP IMPROVE");
+H("Syntax:  IMPROVE <step> [/ DEPTH <number>] [/ NO_DISTINCT] [/ 2] [/ 3]");
+H("                       [/ SUBPROOFS] [/ INCLUDE_MATHBOXES] [/ OVERRIDE]");
+H("         IMPROVE FIRST [/ DEPTH <number>] [/ NO_DISTINCT] [/ 2] [/ 3]");
+H("                       [/ SUBPROOFS] [/ INCLUDE_MATHBOXES] [/ OVERRIDE]");
+H("         IMPROVE LAST [/ DEPTH <number>] [/ NO_DISTINCT] [/ 2] [/ 3]");
+H("                       [/ SUBPROOFS] [/ INCLUDE_MATHBOXES] [/ OVERRIDE]");
+H("         IMPROVE ALL [/ DEPTH <number>] [/ NO_DISTINCT] [/ 2] [/ 3]");
+H("                       [/ SUBPROOFS] [/ INCLUDE_MATHBOXES] [/ OVERRIDE]");
+H("");
+H("This command, available in the Proof Assistant only, tries to");
+H("find proofs automatically for unknown steps whose symbol sequences are");
+H("completely known.  They are primarily useful for filling in proofs of $f");
+H("hypotheses.  By default, the search will be restricted to matching");
+H("statements having no $e hypotheses.");
+H("");
+H("If <step> starts with \"-\", the -<step>th from last unknown step,");
+H("as shown by SHOW NEW_PROOF / UNKNOWN, will be used.  IMPROVE -0 will try");
+H("to prove the last unknown step, IMPROVE -1 will try to prove the");
+H("penultimate unknown step, etc.  If <step> starts with \"+\", the <step>th");
+H("from the first unknown step will be used.  Otherwise, when <step> is");
+H("a positive integer (with no \"+\" sign), IMPROVE assumes it is the actual");
+H("step number shown by SHOW NEW_PROOF (and can be used whether the step is");
+H("known or not).");
+H("");
+H("IMPROVE FIRST and IMPROVE LAST mean IMPROVE +0 and IMPROVE -0");
+H("respectively, in other words the first and last steps shown by");
+H("SHOW NEW_PROOF / UNKNOWN.");
+H("");
+H("IMPROVE ALL scans all unknown steps.  If / SUBPROOFS is specified,");
+H("it also scans all steps with incomplete subproofs.");
+H("");
+H("Sometimes IMPROVE will find proofs for additional unknown steps when");
+H("it is run a second time.  This can happen when an unknown step is");
+H("identical to another step whose proof became completed by the first");
+H("IMPROVE run.  (This second pass is not done automatically because it");
+H("could double the IMPROVE runtime, usually with no benefit.)");
+
+H("");
+H("Optional qualifiers:");
+H("    / DEPTH <number> - This qualifier will cause the search to include");
+H("        statements with $e hypotheses (but no new variables in their $e");
+H("        hypotheses - these are called \"cut-free\" statements), provided");
+H("        that the backtracking has not exceeded the specified depth.");
+H("        **WARNING**:  Try DEPTH 1, then 2, then 3, etc. in sequence");
+H("        because of possible exponential blowups.  Save your work before");
+H("        trying DEPTH greater than 1!");
+H("    / NO_DISTINCT - Skip trial statements that have $d requirements.");
+H("        This qualifier will prevent assignments that might violate $d");
+H("        requirements, but it also could miss possible legal assignments.");
+H("    / 1 - Use the traditional search algorithm used in earlier versions");
+H("        of this program.  It is the default.  It tries to match cut-free");
+H("        statements only (those not having variables in their hypotheses");
+H("        that are not in the conclusion).  It is the fastest method when");
+H("        it can find a proof.");
+H("    / 2 - Try to match statements with cuts.  It also tries to match");
+H("        steps containing working ($nn) variables when they don't share");
+H("        working variables with the rest of the proof.  It runs slower");
+H("        than / 1.");
+H("    / 3 - Attempt to find (cut-free) proofs of $e hypotheses that result");
+H("        from a trial match, unlike / 2, which only attempts (cut-free)");
+H("        proofs of $f hypotheses.  It runs much slower than / 1, and you");
+H("        may prefer to use it with specific steps.  For example, if");
+H("        step 456 is unknown, you may want to use IMPROVE 456 / 3 rather");
+H("        than IMPROVE ALL / 3.  Note that / 3 respects the / DEPTH");
+H("        qualifier, although at the expense of additional run time.");
+H("    / SUBPROOFS - Look at each subproof that isn't completely known, and");
+H("        try to see if it can be proved independently.  This qualifier is");
+H("        meaningful only for IMPROVE ALL / 2 or IMPROVE ALL / 3.  It may");
+H("        take a very long time to run, especially with / 3.");
+H("    / INCLUDE_MATHBOXES - By default, MINIMIZE_WITH skips statements");
+H("        beyond the one with label \"mathbox\" and not in the mathbox of");
+H("        the PROVE argument.  This qualifier allows them to be included.");
+H("    / OVERRIDE - By default, IMPROVE skips statements that have");
+H("        \"(New usage is discouraged.)\" in their description comment.");
+H("        This qualifier tries to use them anyway.");
+H("");
+H("Note that / 2 includes the search of / 1, and / 3 includes / 2.");
+H("Specifying / 1 / 2 / 3 has the same effect as specifying just / 3, so");
+H("there is no need to specify more than one.  Finally, since / 1 is the");
+H("default, you never need to use it; it is included for completeness (or");
+H("in case the default is changed in the future).");
+H("");
+H("See also:  HELP SET SEARCH_LIMIT");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP MINIMIZE_WITH");
+H("Syntax:  MINIMIZE_WITH <label-match> [/ VERBOSE] [/ MAY_GROW]");
+H("              [/ EXCEPT <label-match>] [/ INCLUDE_MATHBOXES]");
+H("              [/ ALLOW_NEW_AXIOMS <label-match>]");
+H("              [/ NO_NEW_AXIOMS_FROM <label-match>] [/ FORBID <label-match>]");
+H("              [/ OVERRIDE] [/ TIME]");
+H("");
+H("This command, available in the Proof Assistant only, checks whether");
+H("the proof can be shortened by using earlier $p or $a statements matching");
+H("<label-match>, and if so, shortens the proof.  <label-match> is a list of");
+H("comma-separated labels which can contain wildcards (* and ?) to test more");
+H("than one statement, but each statement is tested independently from the");
+H("others.  Note:  In the informational output, if the size is given in");
+H("bytes, it refers to the compressed proof size, otherwise it refers to the");
+H("number of steps in the uncompressed proof.");
+H("");
+H("For ordinary use with set.mm, we recommend running it as follows:");
+H("   MINIMIZE_WITH * / ALLOW_NEW_AXIOMS * / NO_NEW_AXIOMS_FROM ax-*");
+H("For some additional information on the qualifiers see");
+H("    https://groups.google.com/d/msg/metamath/f-L91-1jI24/3KJnGa8qCgAJ");
+H("");
+H("Optional qualifiers:");
+H("    / VERBOSE - Shows additional information such as uncompressed proof");
+H("        lengths and reverted shortening attempts");
+H("    / MAY_GROW - If a substitution is possible, it will be made even");
+H("        if the proof length increases.  This is useful if we are just");
+H("        updating the proof with a newer version of an obsolete theorem.");
+H("        (Note: this qualifier used to be named / ALLOW_GROWTH).");
+H("    / EXCEPT <label-match> - Skip trial statements matching <label-match>,");
+H("        which may contain * and ? wildcard characters; see HELP SEARCH");
+H("        for wildcard matching rules.  Note:  Multiple EXCEPT qualifiers");
+H("        are not allowed; use wildcards instead.");
+H("    / INCLUDE_MATHBOXES - By default, MINIMIZE_WITH skips statements");
+H("        beyond the one with label \"mathbox\" and not in the mathbox of");
+H("        the PROVE argument.  This qualifier allows them to be included.");
+H("    / ALLOW_NEW_AXIOMS <label-match> - By default, MINIMIZE_WITH skips");
+H("        statements that depend on $a statements not already used by the");
+H("        proof.  This qualifier allows new $a consequences to be used.");
+H("        To better fine-tune which axioms are used, you may use / FORBID");
+H("        and / NO_NEW_AXIOMS, which take priority over / ALLOW_NEW_AXIOMS.");
+H("        Example:  / ALLOW_NEW_AXIOMS df-* will allow new definitions to be");
+H("        used. / ALLOW_NEW_AXIOMS * / NO_NEW_AXIOMS_FROM ax-ac*,ax-reg");
+H("        will allow any new axioms except those matching ax-ac*,ax-reg.");
+H("    / NO_NEW_AXIOMS_FROM <label-match> - skip any trial statement whose");
+H("        proof depends on a $a statement matching <label-match> but that");
+H("        isn't used by the current proof.  This makes it easier to avoid");
+H("        say ax-ac if the current proof doesn't already use ax-ac, but it");
+H("        permits ax-ac otherwise.  Example:");
+H("        / ALLOW_NEW_AXIOMS * / NO_NEW_AXIOMS_FROM ax-ac*,ax-reg");
+H("        will allow any new axioms except those matching ax-ac*,ax-reg.");
+H("        Notes:  1. In this example, if ax-reg is already used by the proof,");
+H("        statements depending on ax-reg WILL be tried. 2. The use of");
+H("        / NO_NEW_AXIOMS_FROM without / ALLOW_NEW_AXIOMS has no effect.");
+H("    / FORBID <label-match> - Skip any trial");
+H("        statement whose backtrack (from SHOW TRACE_BACK) contains any");
+H("        statement matching <label-match>.  This is useful for avoiding");
+H("        the use of undesired axioms when reducing proof length.  For");
+H("        example, MINIMIZE_WITH ... / FORBID ax-ac,ax-inf* will not shorten");
+H("        the proof with any statement that depends on ax-ac, ax-inf, or");
+H("        ax-inf2 (in the set.mm as of this writing).  Notes: 1. / FORBID");
+H("        can be less useful than / NO_NEW_AXIOMS_FROM because it will");
+H("        also suppress trying statements that depend on <label-list> axioms");
+H("        already used by the proof.  / FORBID may become deprecated.  2. The");
+H("        use of / FORBID without / ALLOW_NEW_AXIOMS has no effect.");
+H("    / OVERRIDE - By default, MINIMIZE_WITH skips statements that have");
+H("        \"(New usage is discouraged.)\" in their description comment.");
+H("        With this qualifier it will try to use them anyway.");
+H("    / TIME - prints out the run time used by the MINIMIZE_WITH run.");
+H("");
+
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP EXPAND");
+H("Syntax:  EXPAND <label-match>");
+H("");
+H("This command, available in the Proof Assistant only, replaces any");
+H("references to <label-match> in the proof with the proof of <label-match>.");
+H("Before and after sizes are printed for the resulting compressed proof (in");
+H("bytes of source text).  Any dummy variables in the proof of <label-match>");
+H("are replaced with unknown steps and must be assigned by hand, and any $d");
+H("statements needed for them must be added by hand.");
+H("");
+H("Except for early theorems close to the axioms, it is best to use specific");
+H("labels rather than EXPAND * because the proof size grows exponentially as");
+H("each layer is eliminated.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SAVE PROOF");
+H("Syntax:  SAVE PROOF <label-match> [/ <qualifier>] [/ <qualifier>]...");
+H("");
+H("The SAVE PROOF command will reformat a proof in one of two formats and");
+H("replace the existing proof in the database buffer.  It is useful for");
+H("converting between proof formats.  Note that a proof will not be");
+H("permanently saved until a WRITE SOURCE command is issued.  Multiple");
+H("proofs can be saved by using comma-separated list of labels with optional");
+H("wildcards (* and ?) in <label-match>.");
+H("");
+H("Optional qualifiers:");
+H("    / NORMAL - The proof is saved in the basic format (i.e., as a sequence");
+H("        of labels, which is the defined format of the basic Metamath");
+H("        language).  This is the default format which is used if a");
+H("        qualifier is omitted.");
+H("    / EXPLICIT - The proof is saved with hypothesis assignments shown");
+H("        explicitly, allowing hypotheses to be reordered without disturbing");
+H("        proofs.");
+H("    / PACKED - The proof is saved in an efficient packed format.  It may");
+H("        be used together with / NORMAL or / EXPLICIT.");
+H("    / COMPRESSED - The proof is saved in the compressed format which");
+H("        reduces storage requirements in a source file.");
+H("    / FAST - The proof is merely reformatted and not recompressed.");
+H("        May be used (only) with / COMPRESSED and / PACKED to speed up");
+H("        conversion between formats.");
+H("    / OLD_COMPRESSION - When used with / COMPRESSED, specifies an older,");
+H("        slightly less space-efficient algorithm.  (Specifically, it does");
+H("        not try to rearrange labels to fit evenly on a line.)");
+H("    / TIME - prints out the run time used for each proof.");
+H("");
+H("Important note:  The / PACKED and / EXPLICIT qualifiers save the proof");
+H("in formats that are _not_ part of the Metamath standard and that probably");
+H("will not be recognized by other Metamath proof verifiers.  They are");
+H("primarily intended to assist database maintenance.  For example,");
+H("    SAVE PROOF * / EXPLICIT / PACKED / FAST");
+H("will allow the order of $e and $f hypotheses to be changed without");
+H("affecting any proofs.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP SAVE NEW_PROOF");
+H("Syntax:  SAVE NEW_PROOF <label-match> [/ <qualifier>] [/ <qualifier>]...");
+H("");
+H("The SAVE NEW_PROOF command is available in the Proof Assistant only. It");
+H("saves the proof in progress in the database buffer.  SAVE NEW_PROOF may be");
+H("used to save a completed proof, or it may be used to save a proof in");
+H("progress in order to work on it later.  If an incomplete proof is saved,");
+H("any user assignments with LET STEP or LET VARIABLE will be lost, as will");
+H("any ambiguous unifications that were resolved manually. To help make");
+H("recovery easier, it is advisable to IMPROVE ALL before SAVE NEW_PROOF so");
+H("that the incomplete proof will have as much information as possible.");
+H("");
+H("Note that the proof will not be permanently saved until a WRITE SOURCE");
+H("command is issued.");
+H("");
+H("Optional qualifiers:");
+H("    / NORMAL, / COMPRESSED, / EXPLICIT, / PACKED, / OLD_COMPRESSION -");
+H("        These qualifiers are the same as for SAVE PROOF.  See");
+H("        HELP SAVE PROOF.");
+H("    / OVERRIDE - By default, SAVE NEW_PROOF will refuse to overwrite");
+H("        the proof if \"(Proof modification is discouraged.)\" is present");
+H("        in the statement's description comment.  This qualifier will");
+H("        allow the proof to be saved.");
+H("");
+H("Note that if no qualifier is specified, / NORMAL is assumed.");
+H("");
+
+
+g_printHelp = !strcmp(saveHelpCmd, "HELP DEMO");
+H("For a quick demo that enables you to see Metamath do something, type");
+H("the following:");
+H("    READ set.mm");
+H("    SHOW STATEMENT id1 /COMMENT");
+H("    SHOW PROOF id1 /RENUMBER /LEMMON");
+H("will show you a proof of \"P implies P\" directly from the axioms");
+H("of propositional calculus.");
+H("    SEARCH * \"distributive law\" /COMMENTS");
+H("will show all the distributive laws in the database.");
+H("    SEARCH * \"C_ $* u.\"");
+H("will show all statements with subset then union in them.");
+H("");
+
+if (strcmp(helpCmd, saveHelpCmd)) bug(1401); /* helpCmd got corrupted */
+free_vstring(saveHelpCmd); /* Deallocate memory */
+
+}
diff --git a/mmhlpb.h b/src/mmhlpb.h
similarity index 97%
rename from mmhlpb.h
rename to src/mmhlpb.h
index cc22ca9..c826640 100644
--- a/mmhlpb.h
+++ b/src/mmhlpb.h
@@ -1,15 +1,17 @@
-/*****************************************************************************/
-/*        Copyright (C) 2015  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMHLPB_H_
-#define METAMATH_MMHLPB_H_
-
-#include "mmvstr.h"
-
-void help2(vstring helpCmd);
-void help3(vstring helpCmd);
-
-#endif /* METAMATH_MMHLPB_H_ */
+/*****************************************************************************/
+/*        Copyright (C) 2015  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMHLPB_H_
+#define METAMATH_MMHLPB_H_
+
+/*! \file */
+
+#include "mmvstr.h"
+
+void help2(vstring helpCmd);
+void help3(vstring helpCmd);
+
+#endif /* METAMATH_MMHLPB_H_ */
diff --git a/mminou.c b/src/mminou.c
similarity index 71%
rename from mminou.c
rename to src/mminou.c
index 7c6f0a0..4b5146f 100644
--- a/mminou.c
+++ b/src/mminou.c
@@ -1,1582 +1,1524 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <setjmp.h>
-#include <time.h>  /* 16-Aug-2016 nm For ELAPSED_TIME */
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mminou.h"
-#include "mmcmdl.h" /* 9/3/99 - for g_commandPrompt global */
-
-#ifdef __WATCOMC__
-  /* Bugs in WATCOMC:
-     1. #include <conio.h> has compile errors
-     2. Two consecutive calls to vprintf after va_start causes register dump
-            or corrupts 2nd arg.
-  */
-/* From <conio.h>: */
-#ifndef _CONIO_H_INCLUDED
-extern int cprintf(const char *f__mt,...);
-#define _CONIO_H_INCLUDED
-#endif
-#endif  /* End #ifdef __WATCOMC__ */
-
-#ifdef THINK_C
-#include <console.h>
-#endif
-
-#define QUOTED_SPACE 3 /* ASCII 3 that temporarily zaps a space */
-
-
-int g_errorCount = 0;
-
-/* Global variables used by print2() */
-flag g_logFileOpenFlag = 0;
-FILE *g_logFilePtr;
-FILE *g_listFile_fp = NULL;
-/* Global variables used by print2() */
-flag g_outputToString = 0;
-vstring g_printString = "";
-/* Global variables used by cmdInput() */
-long g_commandFileNestingLevel = 0;
-FILE *g_commandFilePtr[MAX_COMMAND_FILE_NESTING + 1];
-vstring g_commandFileName[MAX_COMMAND_FILE_NESTING + 1];
-flag g_commandFileSilent[MAX_COMMAND_FILE_NESTING + 1];
-flag g_commandFileSilentFlag = 0;
-                                   /* 23-Oct-2006 nm For SUBMIT ... /SILENT */
-
-FILE /* *inputDef_fp,*/ *g_input_fp /*,*g_output_fp*/; /* File pointers */
-                             /* 31-Dec-2017 nm g_output_fp deleted */
-                             /* 15-Aug-2020 nm inputDef_fp,fn deleted */
-vstring /* inputDef_fn="",*/ g_input_fn="", g_output_fn="";        /* File names */
-
-long g_screenWidth = MAX_LEN; /* Width default = 79 */
-/* g_screenHeight is one less than the physical screen to account for the
-   prompt line after pausing. */
-long g_screenHeight = SCREEN_HEIGHT; /* Default = 23 */ /* 18-Nov-05 nm */
-int printedLines = 0; /* Lines printed since last user input (mod scrn hght) */
-flag g_scrollMode = 1; /* Flag for continuous (0) or prompted (1) scroll */
-flag g_quitPrint = 0; /* Flag that user quit the output */
-flag localScrollMode = 1; /* 0 = Scroll continuously only till next prompt */
-
-/* Buffer for B (back) command at end-of-page prompt - for future use */
-pntrString *backBuffer = NULL_PNTRSTRING;
-long backBufferPos = 0;
-flag backFromCmdInput = 0; /* User typed "B" at main prompt */
-
-/* Special:  if global flag g_outputToString = 1, then the output is not
-             printed but is added to global string g_printString */
-/* Returns 0 if user typed "q" during scroll prompt; this lets a procedure
-   interrupt it's output for speedup (rest of output will be suppressed anyway
-   until next command line prompt) */
-flag print2(char* fmt,...)
-{
-  /* This performs the same operations as printf, except that if a log file is
-    open, the characters will also be printed to the log file. */
-  /* Also, scrolling is paused at each page if in scroll-prompted mode. */
-  va_list ap;
-  char c;
-  long nlpos, lineLen, charsPrinted;
-#ifdef THINK_C
-  int ii, jj;
-#endif
-  long i;
-
-  /* char printBuffer[PRINTBUFFERSIZE]; */ /* 19-Jun-2020 nm Deleted */
-  /* 19-Jun-2020 nm */
-  char *printBuffer; /* Allocated dynamically */
-  /* gcc (Debian 4.9.2-10+deb8u2) 4.9.2 gives error for ssize_t if -c99
-     is specified; gcc (GCC) 7.3.0 doesn't complain if -c99 is specified */
-  /* See https://sourceforge.net/p/predef/wiki/Compilers/ for __LCC__ */
-#ifdef __LCC__   /* 19-Jun-2020 nm ssize_t not defined for lcc compiler */
-  long bufsiz;
-#else
-  ssize_t bufsiz; /* ssize_t (signed size_t) can represent the number -1 for
-                     error checking */
-#endif
-
-
-  if (backBufferPos == 0) {
-    /* Initialize backBuffer - 1st time in program */
-    /* Warning:  Don't call bug(), because it calls print2. */
-    if (pntrLen(backBuffer)) {
-      printf("*** BUG #1501\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-    }
-    backBufferPos = 1;
-    pntrLet(&backBuffer, pntrAddElement(backBuffer));
-    /* Note: pntrAddElement() initializes the added element to the
-       empty string, so we don't need a separate initialization. */
-    /* backBuffer[backBufferPos - 1] = ""; */  /* already done */
-  }
-
-  if ((!g_quitPrint && g_commandFileNestingLevel == 0 && (g_scrollMode == 1
-           && localScrollMode == 1)
-      /* 18-Nov-05 nm - now a variable settable with SET HEIGHT */
-      && printedLines >= /*SCREEN_HEIGHT*/ g_screenHeight && !g_outputToString)
-      || backFromCmdInput) {
-    /* It requires a scrolling prompt */
-    while(1) {
-      if (backFromCmdInput && backBufferPos == pntrLen(backBuffer))
-        break; /* Exhausted buffer */
-      if (backBufferPos < 1 || backBufferPos > pntrLen(backBuffer)) {
-        /* Warning:  Don't call bug(), because it calls print2. */
-        printf("*** BUG #1502 %ld\n", backBufferPos);
-#if __STDC__
-        fflush(stdout);
-#endif
-      }
-      if (backBufferPos == 1) {
-        printf(
-"Press <return> for more, Q <return> to quit, S <return> to scroll to end... "
-          );
-#if __STDC__
-        fflush(stdout);
-#endif
-      } else {
-        printf(
-"Press <return> for more, Q <ret> quit, S <ret> scroll, B <ret> back up... "
-         );
-#if __STDC__
-        fflush(stdout);
-#endif
-      }
-      c = (char)(getchar());
-      if (c == '\n') {
-        if (backBufferPos == pntrLen(backBuffer)) {
-          /* Normal output */
-          break;
-        } else {
-          /* Get output from buffer */
-          backBufferPos++;
-          printf("%s", (vstring)(backBuffer[backBufferPos - 1]));
-#if __STDC__
-          fflush(stdout);
-#endif
-          continue;
-        }
-      }
-      if (getchar() == '\n') {
-        if (c == 'q' || c == 'Q') {
-          if (!backFromCmdInput)
-            g_quitPrint = 1;
-          break;
-        }
-        if (c == 's' || c == 'S') {
-
-          if (backBufferPos < pntrLen(backBuffer)) {
-            /* Print rest of buffer to screen */
-            /*
-            for (backBufferPos = backBufferPos + 1; backBufferPos <=
-                pntrLen(backBuffer); backBufferPos++) {
-            */
-            /* 11-Sep-04 Don't use global var as loop var */
-            while (backBufferPos + 1 <= pntrLen(backBuffer)) {
-              backBufferPos++;
-              printf("%s", (vstring)(backBuffer[backBufferPos - 1]));
-#if __STDC__
-              fflush(stdout);
-#endif
-            }
-          }
-          if (!backFromCmdInput)
-            localScrollMode = 0; /* Continuous scroll */
-          break;
-
-        }
-        if (backBufferPos > 1) {
-          if (c == 'b' || c == 'B') {
-            backBufferPos--;
-            printf("%s", (vstring)(backBuffer[backBufferPos - 1]));
-#if __STDC__
-            fflush(stdout);
-#endif
-            continue;
-          }
-        }
-
-        printf("%c", 7); /* Bell */
-#if __STDC__
-        fflush(stdout);
-#endif
-        continue;
-      }
-      while (c != '\n') c = (char)(getchar());
-    } /* While 1 */
-    if (backFromCmdInput)
-      goto PRINT2_RETURN;
-    printedLines = 0; /* Reset the number of lines printed on the screen */
-    if (!g_quitPrint) {
-      backBufferPos++;
-      pntrLet(&backBuffer, pntrAddElement(backBuffer));
-      /* Note: pntrAddElement() initializes the added element to the
-         empty string, so we don't need a separate initialization. */
-      /* backBuffer[backBufferPos - 1] = ""; */  /* already done */
-    }
-  }
-
-
-
-  if (g_quitPrint && !g_outputToString) {
-    goto PRINT2_RETURN;    /* User typed 'q'
-      above or earlier; 8/27/99: don't return if we're outputting to
-      a string since we want to complete the writing to the string. */
-  }
-
-
-  /* 19-Jun-2020 nm Allow unlimited output size */
-  va_start(ap, fmt);
-  bufsiz = vsnprintf(NULL, 0, fmt, ap); /* Get the buffer size we need */
-  va_end(ap);
-  /* Warning: some older complilers, including lcc-win32 version 3.8 (2004),
-     return -1 instead of the buffer size */
-  if (bufsiz == -1) bug(1527);
-  printBuffer = malloc((size_t)bufsiz + 1);
-
-  /* 19-Jun-2020 nm Let each vs[n]printf have its own va_start...va_end
-     in an attempt to fix crash with some compilers (e.g. gcc 4.9.2) */
-  va_start(ap, fmt);
-  charsPrinted = vsprintf(printBuffer, fmt, ap); /* Put formatted string into
-      buffer */
-  va_end(ap);
-  if (charsPrinted != bufsiz) {
-    /* Give some info with printf in case print2 crashes during bug() call */
-    printf("For bug #1528: charsPrinted = %ld != bufsiz = %ld\n", charsPrinted,
-        (long)bufsiz);
-    bug(1528);
-  }
-
-  /* 19-Jun-2020 nm We are now dynamically allocating printBuffer, so
-     there is no longer a possibility of overflow. */
-  /********** 19-Jun-2020 nm Deleted ***********
-  /@ Normally, long proofs are broken up into 80-or-less char lines
-     by this point (via printLongLine) so this should never be a problem
-     for them.  But in principle a very long line argument to print2
-     could be a problem, although currently it should never occur
-     (except maybe in long lines in tools? - if so, switch to printLongLine
-     there to fix the bug). @/
-  /@ Warning:  Don't call bug(), because it calls print2. @/
-  if (charsPrinted >= PRINTBUFFERSIZE
-      /@ || charsPrinted < 0 @/
-      /@ There is a bug on the Sun with gcc version 2.7.2.2 where
-         vsprintf returns approx. -268437768, so ignore the bug @/
-      ) {
-    printf("@@@ BUG 1503\n");
-    printf("?PRINTBUFFERSIZE %ld <= charsPrinted %ld in mminou.c\n",
-        (long)PRINTBUFFERSIZE, charsPrinted);
-    printf("?Memory may now be corrupted.\n");
-    printf("?Save your work, exit, and verify output files.\n");
-    printf("?You should recompile with increased PRINTBUFFERSIZE.\n");
-#if __STDC__
-    fflush(stdout);
-#endif
-  }
-  ********** 19-Jun-2020 nm End of deletion ***********/
-
-  nlpos = instr(1, printBuffer, "\n");
-  lineLen = (long)strlen(printBuffer);
-
-  /* 10/14/02 Change any ASCII 3's back to spaces, where they were set in
-     printLongLine to handle the broken quote problem */
-  for (i = 0; i < lineLen; i++) {
-    if (printBuffer[i] == QUOTED_SPACE) printBuffer[i] = ' ';
-  }
-
-  if ((lineLen > g_screenWidth + 1) /* && (g_screenWidth != MAX_LEN) */
-         && !g_outputToString  /* for HTML 7/3/98 */ ) {
-    /* Force wrapping of lines that are too long by recursively calling
-       print2() via printLongLine().  Note:  "+ 1" above accounts for \n. */
-    /* Note that breakMatch is "" so it may break in middle of a word */
-    if (!nlpos) {
-      /* No end of line */
-      printLongLine(left(printBuffer, lineLen), "", "");
-    } else {
-      printLongLine(left(printBuffer, lineLen - 1), "", "");
-    }
-    goto PRINT2_RETURN;
-  }
-
-  if (!g_outputToString && !g_commandFileSilentFlag) {
-           /* 22-Oct-2006 nm Added g_commandFileSilentFlag for SUBMIT /SILENT,
-              here and elsewhere in mminou.c */
-    if (nlpos == 0) { /* Partial line (usu. status bar) - print immediately */
-
-#ifdef __WATCOMC__
-      cprintf("%s", printBuffer); /* Immediate console I/O (printf buffers it)*/
-#else
-      printf("%s", printBuffer);
-#endif
-
-#ifdef THINK_C
-      /* Force a console flush to see it (otherwise only CR flushes it) */
-      cgetxy(&ii, &jj, stdout);
-#endif
-
-#if __STDC__
-      /*
-      The following change to mminou.c was necessary on my Unix (Linux)
-      system to get the `verify proof *' progress bar to display
-      progressively (rather than all at once). I've conditionalized it on
-      __STDC__, since it should be harmless on any ANSI C system.
-        -- Stephen McCamant  smccam @ uclink4.berkeley.edu 12/9/00
-      */
-      fflush(stdout);
-#endif
-
-    } else {
-      printf("%s", printBuffer); /* Normal line */
-#if __STDC__
-      fflush(stdout);
-#endif
-      printedLines++;
-      if (!(g_scrollMode == 1 && localScrollMode == 1)) {
-        /* Even in non-scroll (continuous output) mode, still put paged-mode
-           lines into backBuffer in case user types a "B" command later,
-           so user can page back from end. */
-        /* 18-Nov-05 nm - now a variable settable with SET HEIGHT */
-        if (printedLines > /*SCREEN_HEIGHT*/ g_screenHeight) {
-          printedLines = 1;
-          backBufferPos++;
-          pntrLet(&backBuffer, pntrAddElement(backBuffer));
-          /* Note: pntrAddElement() initializes the added element to the
-             empty string, so we don't need a separate initialization. */
-          /* backBuffer[backBufferPos - 1] = ""; */  /* already done */
-        }
-      }
-    }
-    /* Add line to backBuffer string array */
-    /* Warning:  Don't call bug(), because it calls print2. */
-    if (backBufferPos < 1) {
-      printf("*** PROGRAM BUG #1504\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-    }
-    let((vstring *)(&(backBuffer[backBufferPos - 1])), cat(
-        (vstring)(backBuffer[backBufferPos - 1]), printBuffer, NULL));
-  } /* End if !g_outputToString */
-
-  if (g_logFileOpenFlag && !g_outputToString /* && !g_commandFileSilentFlag */) {
-    fprintf(g_logFilePtr, "%s", printBuffer);  /* Print to log file */
-#if __STDC__
-    /* 10-Oct-2016 nm */
-    fflush(g_logFilePtr);
-#endif
-  }
-
-  if (g_listMode && g_listFile_fp != NULL && !g_outputToString) {
-    /* Put line in list.tmp as comment */
-    fprintf(g_listFile_fp, "! %s", printBuffer);  /* Print to list command file */
-  }
-
-  if (g_outputToString) {
-    let(&g_printString, cat(g_printString, printBuffer, NULL));
-  }
-
-  /* Check for lines too long */
-  if (lineLen > g_screenWidth + 1) { /* The +1 ignores \n */
-    /* Warning:  Don't call bug(), because it calls print2. */
-    /* If this bug occurs, the calling function should be fixed. */
-    printf("*** PROGRAM BUG #1505 (not serious, but please report it)\n");
-    printf("Line exceeds screen width; caller should use printLongLine.\n");
-    printf("%ld %s\n", lineLen, printBuffer);
-    /*printf(NULL);*/  /* Force crash on VAXC to see where it came from */
-#if __STDC__
-    fflush(stdout);
-#endif
-  }
-  /* \n not allowed in middle of line */
-  /* If this bug occurs, it means print2() is being called with \n in the
-     middle of the line and should be fixed in the caller.  printLongLine()
-     may be used if this is necessary. */
-  /* Warning:  Don't call bug(), because it calls print2. */
-  if (nlpos != 0 && nlpos != lineLen) {
-    printf("*** PROGRAM BUG #1506\n");
-#if __STDC__
-    fflush(stdout);
-#endif
-  }
-
-  free(printBuffer); /* 19-Jun-2020 nm */
-
- PRINT2_RETURN:
-  return (!g_quitPrint);
-}
-
-
-/* printLongLine automatically puts a newline \n in the output line. */
-/* startNextLine is the string to place before continuation lines. */
-/* breakMatch is a list of characters at which the line can be broken. */
-/* Special:  startNextLine starts with "~" means add tilde after broken line */
-/* Special:  breakMatch begins with "&" means compressed proof */
-/* Special:  breakMatch empty means break line anywhere */
-/* Special:  breakMatch begins with octal 1 means nested tree display (right
-             justify continuation lines); 1 is changed to space for
-             break matching */
-/* Special:  if breakMatch is \, then put % at end of previous line for LaTeX*/
-/* Special:  if breakMatch is " (quote), treat as if space but don't break
-             quotes, and also let lines grow long - use this call for all HTML
-             code */ /* Added 10/14/02 */
-void printLongLine(vstring line, vstring startNextLine, vstring breakMatch)
-{
-  vstring longLine = "";
-  vstring multiLine = "";
-  vstring prefix = "";
-  vstring startNextLine1 = "";
-  vstring breakMatch1 = "";
-  long i, j, p;
-  long startNextLineLen;
-  flag firstLine;
-  flag tildeFlag = 0;
-  flag treeIndentationFlag = 0;
-
-  /* 10/14/02 added for HTML handling */
-  /* 26-Jun-2014 nm No longer needed? */
-  /* flag g_htmlFlag = 0; */
-               /* 1 means printLongLine was called with "\"" as
-                        breakMatch argument (for HTML code) */
-  flag quoteMode = 0; /* 1 means inside quote */
-  /*char quoteChar = '"';*/ /* Current quote character */
-  /*long quoteStartPos = 0;*/ /* Start of quote */
-  long saveScreenWidth; /* To let g_screenWidth grow temporarily */
-
-  long saveTempAllocStack;
-
-  /* Blank line (the rest of algorithm would ignore it; output and return) */
-  if (!line[0]) {
-    /* 10/14/02 Do a dummy let() so caller can always depend on printLongLine
-       to empty the tempalloc string stack (for the rest of this code, the
-       first let() will do this) */
-    let(&longLine, "");
-    print2("\n");
-    return;
-  }
-
-  /* Change the stack allocation start to prevent arguments from being
-     deallocated.  We need to do this because more than one argument
-     may be passed in with cat(), chr(), etc. and let() can only grab
-     one, destroying the others  */
-  saveTempAllocStack = g_startTempAllocStack;
-  g_startTempAllocStack = g_tempAllocStackTop; /* For let() stack cleanup */
-  /* Added 10/14/02 */
-  /* Grab the input arguments */
-  let(&multiLine, line);
-  let(&startNextLine1, startNextLine);
-  let(&breakMatch1, breakMatch);
-  /* Now relax - back to normal; we can let temporary allocation stack die. */
-  g_startTempAllocStack = saveTempAllocStack;
-
-  /* 10/14/02 */
-  /* We must copy input argument breakMatch to a variable string because we
-     will be zapping one of its characters, and ordinarily breakMatch is
-     passed in as a constant string.  However, this is now done with argument
-     grabbing above so we're OK. */
-
-  /* Flag to right justify continuation lines */
-  if (breakMatch1[0] == 1) {
-    treeIndentationFlag = 1;
-    breakMatch1[0] = ' '; /* Change to a space (the real break character) */
-  }
-
-  /* HTML mode */   /* Added 10/14/02 */
-  /* The HTML mode is intended not to break inside quoted HTML tag
-     strings.  All HTML output should be called with this mode.
-     Since we don't parse HTML this method is not perfect.  Only double
-     quotes are inspected, so all HTML strings with spaces must be
-     surrounded by double quotes.  If text quotes surround a tag
-     with a quoted string, this code will not work and must be
-     enhanced. */
-  /* Whenever we are inside of a quote, we change a space to ASCII 3 to
-     prevent matching it.  The reverse is done in the print2() function,
-     where all ASCII 3's are converted back to space. */
-  /* Note added 10/20/02: tidy.exe breaks HREF quotes with new line.
-     Check HTML spec - do we really need this code? */
-  j = (long)strlen(multiLine);
-  /* Do a bug check to make sure no real ASCII 3's are ever printed */
-  for (i = 0; i < j; i++) {
-    if (multiLine[i] == QUOTED_SPACE) bug(1514); /* Should never be the case */
-  }
-  if (breakMatch1[0] == '\"') {
-    /* g_htmlFlag = 1; */ /* 26-Jun-2014 nm No longer needed? */
-    breakMatch1[0] = ' '; /* Change to a space (the real break character) */
-    /* Scan string for quoted strings */
-    quoteMode = 0;
-
-    /* 19-Nov-2007 nm  New algorithm:  don't put line breaks in anything inside
-       _double_ quotes that follows an = sign, such as TITLE="abc def".  Ignore
-       _single_ quotes (which could be apostrophes).  The HTML output code
-       must be (and so far is) written to conform to this. */
-    i = 0;
-    while (multiLine[i]) {
-      if (multiLine[i] == '"' && i > 0) {
-        if (!quoteMode && multiLine[i - 1] == '=')
-          quoteMode = 1;
-        else
-          quoteMode = 0;
-      }
-      if (multiLine[i] == ' ' && quoteMode)
-        multiLine[i] = QUOTED_SPACE;
-      i++;
-    }
-
-/* 19-Nov-2007 nm  Bypass old complex code that doesn't work right */
-/**************** THE CODE BELOW IS OBSOLETE AND WILL BE DELETED. ********/
-#ifdef OBSOLETE_QUOTE_HANDLING_CODE
-    for (i = 0; i < j; i++) {
-
-      /* 10/24/02 - This code has problems with SHOW STATEMENT/ALT_HTML
-         It is bypassed completely now. */
-      if (i == i) break;
-
-      /* Special case: ignore ALT='"' in set.mm */
-      if (multiLine[i] == '"' && i >= 5
-          && !strncmp("ALT='\"'\"", multiLine + i - 5, 7))
-        continue;
-      if (!quoteMode) {
-        /* If we wanted to handle double and single nested quotes: */
-        /* if (multiLine[i] == '\'' || multiLine[i] == '"') { */
-        /* But since single quotes are ambiguous with apostrophes, we will
-           demand that only double quotes be used around HTML tag strings
-           that have spaces such as <FONT FACE="Arial Narrow">. */
-        if (multiLine[i] == '"') {
-          quoteMode = 1;
-          quoteStartPos = i;
-          quoteChar = multiLine[i];
-        }
-      } else {
-        if (multiLine[i] == quoteChar) {
-          quoteMode = 0;
-        }
-      }
-      if (quoteMode == 1 && multiLine[i] == ' ') {
-        /* Zap the space with ASCII 3 */
-        multiLine[i] = QUOTED_SPACE;
-      }
-    }
-    /* If we ended in quoteMode, it wasn't a real quote.  Revert the
-       space zapping. */
-    if (quoteMode == 1) {
-      for (i = quoteStartPos; i < j; i++) {
-        if (multiLine[i] == QUOTED_SPACE) multiLine[i] = ' ';
-      }
-    }
-    /* As a special case for more safety, we'll zap the ubiquitous
-       "Arial Narrow" used for the little pink numbers. */
-    i = 0;
-    while (1) {
-      i = instr(i + 1, multiLine, "Arial Narrow");
-      if (i) multiLine[i + 4] = QUOTED_SPACE;
-      else break;
-    }
-#endif
-/**************** END OF OBSOLETE SECTION ********/
-
-  }
-
-
-  /* The tilde is a special flag for printLongLine to print a
-     tilde before the carriage return in a split line, not after */
-  if (startNextLine1[0] == '~') {
-    tildeFlag = 1;
-    let(&startNextLine1, " ");
-  }
-
-
-  while (multiLine[0]) { /* While there are multi caller-inserted newlines */
-
-    /* Process caller-inserted newlines */
-    p = instr(1, multiLine, "\n");
-    if (p) {
-      /* Get the next caller's line */
-      let(&longLine, left(multiLine, p - 1));
-      /* Postpone the remaining lines to multiLine for next time around */
-      /* /@ 12-Jun-2011 nm Put continuation line start (normally spaces) at
-         the beginning of explicit new line, removing spaces that may
-         already be there - for use after blank line in outputStatement()
-         in mmpars.c @/
-      let(&multiLine, cat(startNextLine1,
-          edit(right(multiLine, p + 1), 8 /@ Discard leading spaces @/),
-          NULL)); */
-     /* The above is bad, because it doesn't allow flexible user indentation */
-      /* OLD */ let(&multiLine, right(multiLine, p + 1));
-    } else {
-      let(&longLine, multiLine);
-      let(&multiLine, "");
-    }
-
-    saveScreenWidth = g_screenWidth;
-   HTML_RESTART:
-    /* Now we will break up one caller's line */
-    firstLine = 1;
-
-    startNextLineLen = (long)strlen(startNextLine1);
-    /* Prevent infinite loop if next line prefix is longer than screen */
-    if (startNextLineLen > g_screenWidth - 4) {
-      startNextLineLen = g_screenWidth - 4;
-      let(&startNextLine1, left(startNextLine1, g_screenWidth - 4));
-    }
-    while ((signed)(strlen(longLine)) + (1 - firstLine) * startNextLineLen >
-        g_screenWidth - (long)tildeFlag - (long)(breakMatch1[0] == '\\')) {
-      p = g_screenWidth - (long)tildeFlag - (long)(breakMatch1[0] == '\\') + 1;
-      if (!firstLine) p = p - startNextLineLen;
-
-      if (p < 4) bug(1524);  /* This may cause out-of-string ref below */
-      /* Assume compressed proof if 1st char of breakMatch1 is "&" */
-      if (breakMatch1[0] == '&'
-          && ((!instr(p, left(longLine, (long)strlen(longLine) - 3), " ")
-              && longLine[p - 3] != ' ') /* Don't split trailing "$." */
-            /* 2-Jan-2014 nm Added the condition below: */
-            || longLine[p - 4] == ')')) /* Label sect ends in col 77 */ {
-        /* We're in the compressed proof section; break line anywhere */
-        p = p + 0;  /* Don't change position */
-        /* 27-Dec-2013 nm */
-        /* In the case where the last space occurs at column 79 i.e.
-           g_screenWidth, break the line at column 78.  This can happen
-           when compressed proof ends at column 78, followed by space
-           and "$."  It prevents an extraneous trailing space on the line. */
-        if (longLine[p - 2] == ' ') p--; /* 27-Dec-2013 */
-      } else {
-        if (!breakMatch1[0]) {
-          p = p + 0; /* Break line anywhere; don't change position */
-        } else {
-          if (breakMatch1[0] == '&') {
-            /* Compressed proof */
-            /* 27-Dec-2013 nm - no longer add the trailing space */
-            /* p = p - 1; */ /* We will add a trailing space to line for easier
-                          label searches by the user during editing */
-          }
-          if (p <= 0) bug(1518);
-          /*while (!instr(1, breakMatch1, mid(longLine,p,1)) && p > 0) {*/
-          /* Speedup */
-          /* while (strchr(breakMatch1, longLine[p - 1]) == NULL) { */
-          /* 24-Feb-2010 nm For LaTeX, match space, not backslash */
-          /* (Todo:  is backslash match mode really needed?) */
-          while (strchr(breakMatch1[0] != '\\' ? breakMatch1 : " ",
-              longLine[p - 1]) == NULL) {
-            p--;
-            if (!p) break;
-          }
-          /* if (p <= 0 && g_htmlFlag) { */
-          /* 25-Jun-2014 nm We will now not break any line at non-space,
-             since it causes more problems that it solves e.g. with
-             WRITE SOURCE.../REWRAP with long URLs */
-          if (p <= 0) {
-            /* The line couldn't be broken.  Since it's an HTML line, we
-               can increase g_screenWidth until it will fit. */
-            g_screenWidth++;
-            /******* for debugging screen width change
-            if (g_outputToString){
-              g_outputToString = 0;
-              print2("debug: g_screenWidth = %ld\n", g_screenWidth);
-              g_outputToString = 1;
-            }
-            ********* end debug */
-            /* If this bug happens, we'll have to increase PRINTBUFFERSIZE
-               or change the HTML code being printed. */
-            /* 19-Jun-2020 nm We no longer care about buffer size since
-               printBuffer is dynamically allocated in print2() */
-            /*if (g_screenWidth >= PRINTBUFFERSIZE - 1) bug(1517);*/
-            goto HTML_RESTART; /* Ugly but another while loop nesting would
-                                  be even more confusing */
-          }
-
-          if (breakMatch1[0] == '&') {
-            /* Compressed proof */
-            /* 27-Dec-2013 nm - no longer add the trailing space */
-            /* p = p + 1; */ /* We will add a trailing space to line for easier
-                          label searches by the user during editing */
-          }
-        } /* end if (!breakMatch1[0]) else */
-      } /* end if (breakMatch1[0] == '&' &&... else */
-
-      if (p <= 0) {
-        /* Break character not found; give up at
-           g_screenWidth - (long)tildeFlag  - (long)(breakMatch1[0] == '\\')+ 1 */
-        p = g_screenWidth - (long)tildeFlag  - (long)(breakMatch1[0] == '\\')+ 1;
-        if (!firstLine) p = p - startNextLineLen;
-        if (p <= 0) p = 1; /* If startNextLine too long */
-      }
-      if (!p) bug(1515); /* p should never be 0 by this point */
-      /* If we broke at a non-space 1st char, line length won't get reduced */
-      /* Hopefully this will never happen with the breakMatch's we use,
-         otherwise the code will require a rework. */
-      if (p == 1 && longLine[0] != ' ') bug(1516);
-      if (firstLine) {
-        firstLine = 0;
-        let(&prefix, "");
-      } else {
-        let(&prefix, startNextLine1);
-        if (treeIndentationFlag) {
-          if (startNextLineLen + p - 1 < g_screenWidth) {
-            /* Right justify output for continuation lines */
-            let(&prefix, cat(prefix, space(g_screenWidth - startNextLineLen
-                - p + 1), NULL));
-          }
-        }
-      }
-      if (!tildeFlag) {
-        /* 7-Sep-2010 nm - Don't do this anymore with new (24-Feb-2010) LaTeX
-           output, since it isn't needed, and worse, it causes words in the
-           description to be joined together without space.  (It might be better
-           to analyze if breakMatch1[0] == '\\' is needed at all.) */
-        /*** start of 7-Sep-2010 commented out code
-        if (breakMatch1[0] == '\\') {
-          /@ Add LaTeX comment char to ignore carriage return @/
-          print2("%s\n",cat(prefix, left(longLine,p - 1), "%", NULL));
-        } else {
-        *** end of 7-Sep-2010 commented out code */
-          print2("%s\n",cat(prefix, left(longLine,p - 1), NULL));
-        /*** start of 7-Sep-2010 commented out code
-        }
-        *** end of 7-Sep-2010 commented out code */
-      } else {
-        print2("%s\n",cat(prefix, left(longLine,p - 1), "~", NULL));
-      }
-      if (longLine[p - 1] == ' ' &&
-          breakMatch1[0] /* But not "break anywhere" line */) {
-        /* Remove leading space for neatness */
-        if (longLine[p] == ' ') {
-          /* There could be 2 spaces at the end of a sentence. */
-          let(&longLine, right(longLine, p + 2));
-        } else {
-          let(&longLine, right(longLine,p + 1));
-        }
-      } else {
-        let(&longLine, right(longLine,p));
-      }
-    } /* end while longLine too long */
-    if (!firstLine) {
-      if (treeIndentationFlag) {
-        /* Right justify output for continuation lines */
-        print2("%s\n",cat(startNextLine1, space(g_screenWidth
-            - startNextLineLen - (long)(strlen(longLine))), longLine, NULL));
-      } else {
-        print2("%s\n",cat(startNextLine1, longLine, NULL));
-      }
-    } else {
-      print2("%s\n",longLine);
-    }
-    g_screenWidth = saveScreenWidth; /* Restore to normal */
-
-  } /* end while multiLine != "" */
-
-  let(&multiLine, ""); /* Deallocate */
-  let(&longLine, ""); /* Deallocate */
-  let(&prefix, ""); /* Deallocate */
-  let(&startNextLine1, ""); /* Deallocate */
-  let(&breakMatch1, ""); /* Deallocate */
-
-  return;
-} /* printLongLine */
-
-
-vstring cmdInput(FILE *stream, vstring ask)
-{
-  /* This function prints a prompt (if 'ask' is not NULL) and gets a line from
-    the input stream.  NULL is returned when end-of-file is encountered.
-    New memory is allocated each time linput is called.  This space must
-    be freed by the caller. */
-  vstring g = ""; /* Always init vstrings to "" for let(&...) to work */
-  long i;
-#define CMD_BUFFER_SIZE 2000
-
-  while (1) { /* For "B" backup loop */
-    if (ask != NULL && !g_commandFileSilentFlag) {
-      printf("%s",ask);
-#if __STDC__
-      fflush(stdout);
-#endif
-    }
-    let(&g, space(CMD_BUFFER_SIZE)); /* Allocate CMD_BUFFER_SIZE+1 bytes */
-    if (g[CMD_BUFFER_SIZE]) bug(1520); /* Bug in let() (improbable) */
-    g[CMD_BUFFER_SIZE - 1] = 0; /* For overflow detection */
-    if (!fgets(g, CMD_BUFFER_SIZE, stream)) {
-      /* End of file */
-      let(&g, ""); /* Deallocate memory */
-      return NULL;
-    }
-    if (g[CMD_BUFFER_SIZE - 1]) {
-      /* Detect input overflow */
-      /* Warning:  Don't call bug() - it calls print2 which may call this. */
-      printf("***BUG #1508\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-    }
-    i = (long)strlen(g);
-/*E*/db = db - (CMD_BUFFER_SIZE - i); /* Adjust string usage to detect leaks */
-    /* Detect operating system bug of inputting no characters */
-    if (!i) {
-      printf("***BUG #1507\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-
-    /* 12-Oct-2006 Fix bug that occurs when the last line in the file has
-       no new-line (reported by Marnix Klooster) */
-    } else {
-      if (g[i - 1] != '\n') {
-        /* Warning:  Don't call bug() - it calls print2 which may call this. */
-        if (!feof(stream)) {
-          printf("***BUG #1525\n");
-#if __STDC__
-          fflush(stdout);
-#endif
-        }
-        /* Add a new-line so processing below will behave correctly. */
-        let(&g, cat(g, chr('\n'), NULL));
-/*E*/db = db + (CMD_BUFFER_SIZE - i); /* Cancel extra piece of string */
-        i++;
-      }
-    /* 12-Oct-2006 End of new code */
-
-    }
-
-    if (g[1]) {
-      i--;
-      if (g[i] != '\n') {
-        printf("***BUG #1519\n");
-#if __STDC__
-        fflush(stdout);
-#endif
-      }
-      g[i]=0;   /* Eliminate new-line character by zapping it */
-/*E*/db = db - 1;
-    } else {
-      if (g[0] != '\n') {
-        printf("***BUG #1521\n");
-#if __STDC__
-        fflush(stdout);
-#endif
-      }
-      /* Eliminate new-line by deallocating vstring space (if we just zap
-         character [0], let() will later think g is an empty string constant
-         and will never deallocate g) */
-      let(&g, "");
-    }
-
-    /* If user typed "B" (for back), go back to the back buffer to
-       let the user scroll through it */
-    if ((!strcmp(g, "B") || !strcmp(g, "b")) /* User typed "B" */
-        && pntrLen(backBuffer) > 1   /* The back-buffer still exists and
-                                         there was a previous page */
-        && g_commandFileNestingLevel == 0
-        && (g_scrollMode == 1 && localScrollMode == 1)
-        && !g_outputToString) {
-      /* Set variables so only backup buffer will be looked at in print2() */
-      backBufferPos = pntrLen(backBuffer) - 1;
-      printf("%s", (vstring)(backBuffer[backBufferPos - 1]));
-#if __STDC__
-      fflush(stdout);
-#endif
-      backFromCmdInput = 1; /* Flag for print2() */
-      print2(""); /* Only the backup buffer will be looked at */
-      backFromCmdInput = 0;
-    } else {
-      /* If the command line is empty (at main prompt), let user still
-         type "B" for convenience in case too many
-         returns where hit while scrolling */
-      if (g_commandFileNestingLevel > 0) break;
-                            /* 23-Aug-04 We're taking from a SUBMIT
-                              file so break out of loop that looks for "B" */
-      if (ask == NULL) {
-        printf("***BUG #1523\n"); /* 23-Aug-04 In non-SUBMIT
-          mode 'ask' won't be NULL, so flag non-fatal bug here just in case */
-#if __STDC__
-        fflush(stdout);
-#endif
-      }
-      if (g[0]) break; /* 23-Aug-04 Command line not empty so break out of loop
-                          that looks for "B" */
-      if (ask != NULL &&
-          /* User entered empty command line but not at a prompt */
-          /* g_commandPrompt is assigned in metamath.c and declared in
-             mmcmdl.h */
-          strcmp(ask, g_commandPrompt)) {
-        break; /* Break out of loop that looks for "B" */
-      }
-    }
-  } /* while 1 */
-
-  return g;
-} /* cmdInput */
-
-vstring cmdInput1(vstring ask)
-{
-  /* This function gets a line from either the terminal or the command file
-    stream depending on g_commandFileNestingLevel > 0.  It calls cmdInput(). */
-  /* Warning: the calling program must deallocate the returned string. */
-  vstring commandLn = "";
-  vstring ask1 = "";
-  long p, i;
-
-  let(&ask1, ask); /* In case ask is temporarily allocated (i.e in case it
-                      will become deallocated at next let() */
-  /* Look for lines too long */
-  while ((signed)(strlen(ask1)) > g_screenWidth) {
-    p = g_screenWidth - 1;
-    while (ask1[p] != ' ' && p > 0) p--;
-    if (!p) p = g_screenWidth - 1;
-    print2("%s\n", left(ask1, p));
-    let(&ask1, right(ask1, p + 1));
-  }
-  /* Allow 10 characters for answer */
-  if ((signed)(strlen(ask1)) > g_screenWidth - 10) {
-    p = g_screenWidth - 11;
-    while (ask1[p] != ' ' && p > 0) p--;
-    if (p) {  /* (Give up if no spaces) */
-      print2("%s\n", left(ask1, p));
-      let(&ask1, right(ask1, p + 1));
-    }
-  }
-
-  printedLines = 0; /* Reset number of lines printed since last user input */
-  g_quitPrint = 0; /* Reset quit print flag */
-  localScrollMode = 1; /* Reset to prompted scroll */
-
-  while (1) {
-    if (g_commandFileNestingLevel == 0) {
-      commandLn = cmdInput(stdin, ask1);
-      if (!commandLn) {
-        commandLn = ""; /* Init vstring (was NULL) */
-        /* 21-Feb-2010 nm Allow ^D to exit */
-        /* 21-Feb-2010 Removed line: */
-        /* let(&commandLn, "^Z"); */
-        /* 21-Feb-2010 Added lines: */
-        if (strcmp(left(ask1, 2), "Do")) {
-          /* ^Z or ^D found at MM>, MM-PA>, or TOOLS> prompt */
-          let(&commandLn, "EXIT");
-        } else {
-          /* Detected the question "Do you want to EXIT anyway (Y, N) <N>?" */
-          /* Force exit with Y, to prevent infinite loop */
-          let(&commandLn, "Y");
-        }
-        printf("%s\n", commandLn); /* Let user see what's happening */
-        /* 21-Feb-2010 end of change */
-      }
-      if (g_logFileOpenFlag) fprintf(g_logFilePtr, "%s%s\n", ask1, commandLn);
-
-      /* Clear backBuffer from previous scroll session */
-      for (i = 0; i < pntrLen(backBuffer); i++) {
-        let((vstring *)(&(backBuffer[i])), "");
-      }
-      backBufferPos = 1;
-      pntrLet(&backBuffer, NULL_PNTRSTRING);
-      pntrLet(&backBuffer, pntrAddElement(backBuffer));
-      /* Note: pntrAddElement() initializes the added element to the
-         empty string, so we don't need a separate initialization. */
-      /* backBuffer[backBufferPos - 1] = ""; */  /* already done */
-
-      /* Add user's typing to the backup buffer for display on 1st screen */
-      let((vstring *)(&(backBuffer[backBufferPos - 1])), cat(
-          (vstring)(backBuffer[backBufferPos - 1]), ask1,
-          commandLn, "\n", NULL));
-
-      if (g_listMode && g_listFile_fp != NULL) {
-        /* Put line in list.tmp as comment */
-        fprintf(g_listFile_fp, "! %s\n", commandLn);
-      }
-
-    } else { /* Get line from SUBMIT file */
-      commandLn = cmdInput(g_commandFilePtr[g_commandFileNestingLevel], NULL);
-      if (!commandLn) { /* EOF found */
-        fclose(g_commandFilePtr[g_commandFileNestingLevel]);
-        print2("%s[End of command file \"%s\".]\n", ask1,
-            g_commandFileName[g_commandFileNestingLevel]);
-        let(&(g_commandFileName[g_commandFileNestingLevel]), "");
-                                                        /* Deallocate string */
-        g_commandFileNestingLevel--;
-        commandLn = "";
-        if (g_commandFileNestingLevel == 0) {
-          g_commandFileSilentFlag = 0; /* 23-Oct-2006 nm Added SUBMIT / SILENT */
-        } else {
-          g_commandFileSilentFlag = g_commandFileSilent[g_commandFileNestingLevel];
-               /* Revert to previous nesting level's silent flag */
-        }
-        break; /*continue;*/
-      }
-
-      /* 22-Jan-2018 nm */
-      /* Tolerate CRs in SUBMIT files (e.g. created on Windows and
-         run on Linux) */
-      let(&commandLn, edit(commandLn, 8192/* remove CR */));
-
-      print2("%s%s\n", ask1, commandLn);
-    }
-    break;
-  }
-
-  let(&ask1, ""); /* 10/20/02 Deallocate */
-  return commandLn;
-} /* cmdInput1 */
-
-
-void errorMessage(vstring line, long lineNum, long column, long tokenLength,
-  vstring error, vstring fileName, long statementNum, flag severity)
-{
-  /* Note:  "line" may be terminated with \n.  "error" and "fileName"
-     should NOT be terminated with \n.  This is done for the convenience
-     of the calling functions. */
-  vstring errorPointer = "";
-  vstring tmpStr = "";
-  vstring prntStr = "";
-  vstring line1 = "";
-  int j;
-  /*flag saveOutputToString;*/ /* 22-May-2016 nm */ /* 9-Jun-2016 reverted */
-
-  /* 22-May-2016 nm */
-  /* Prevent putting error message in g_printString */
-  /* 9-Jun-2016 nm Revert this change, because 'minimize_with' makes
-     use of the string to hold the DV violation error message.
-     We can reinstate this fix when 'minimize_with' is improved to
-     call a DV-checking function directly. */
-  /*
-  saveOutputToString = g_outputToString;
-  g_outputToString = 0;
-  */
-
-  /* Make sure vstring argument doesn't get deallocated with another let */
-/*??? USE SAVETEMPALLOC*/
-  let(&tmpStr,error); /* error will get deallocated here */
-  error = "";
-  let(&error,tmpStr); /* permanently allocate error */
-
-  /* Add a newline to line1 if there is none */
-  if (line) {
-    if (line[strlen(line) - 1] != '\n') {
-      let(&line1, line);
-    } else {
-      bug(1509);
-    }
-  } else {
-    line1 = NULL;
-  }
-
-  if (fileName) {
-    /* Put a blank line between error msgs if we are parsing a file */
-    print2("\n");
-  }
-
-  switch (severity) {
-    case (char)notice_:
-      let(&prntStr, "?Notice"); break;
-    case (char)warning_:
-      let(&prntStr, "?Warning"); break;
-    case (char)error_:
-      let(&prntStr, "?Error"); break;
-    case (char)fatal_:
-      let(&prntStr, "?Fatal error"); break;
-  }
-  if (lineNum) {
-    let(&prntStr, cat(prntStr, " on line ", str((double)lineNum), NULL));
-    if (fileName) {
-      let(&prntStr, cat(prntStr, " of file \"", fileName, "\"", NULL));
-    }
-  } else {
-    if (fileName) {
-      let(&prntStr, cat(prntStr, " in file \"", fileName, "\"", NULL));
-    }
-  }
-  if (statementNum) {
-    let(&prntStr, cat(prntStr, " at statement ", str((double)statementNum), NULL));
-    if (g_Statement[statementNum].labelName[0]) {
-      let(&prntStr, cat(prntStr, ", label \"",
-          g_Statement[statementNum].labelName, "\"", NULL));
-    }
-    let(&prntStr, cat(prntStr, ", type \"$", chr(g_Statement[statementNum].type),
-        "\"", NULL));
-  }
-  printLongLine(cat(prntStr, ":", NULL), "", " ");
-  if (line1) printLongLine(line1, "", "");
-  if (line1 && column && tokenLength) {
-    let(&errorPointer,"");
-    for (j=0; j<column-1; j++) {
-      /* Make sure that tabs on the line with the error are accounted for so
-         that the error pointer lines up correctly */
-      if (line1[j] == '\t') let (&errorPointer,cat(errorPointer,"\t",NULL));
-      else let(&errorPointer,cat(errorPointer," ",NULL));
-    }
-    for (j=0; j<tokenLength; j++)
-      let(&errorPointer,cat(errorPointer,"^",NULL));
-    printLongLine(errorPointer, "", "");
-  }
-  printLongLine(error,""," ");
-  if (severity == 2) g_errorCount++;
-
-  /* ???Should there be a limit? */
-  /* if (g_errorCount > 1000) {
-    print2("\n"); print2("?Too many errors - aborting Metamath.\n");
-    exit(0);
-  } */
-
-  /* 22-May-2016 nm */
-  /* Restore output to g_printString if it was enabled before */
-  /* 9-Jun-2016 nm Reverted */
-  /*
-  g_outputToString = saveOutputToString;
-  */
-
-  if (severity == 3) {
-    print2("Aborting Metamath.\n");
-    exit(0);
-  }
-  let(&errorPointer,"");
-  let(&tmpStr,"");
-  let(&prntStr,"");
-  let(&error,"");
-  if (line1) let(&line1,"");
-} /* errorMessage() */
-
-
-
-
-/* Opens files with error message; opens output files with
-   backup of previous version.   Mode must be "r" or "w" or "d" (delete). */
-/* 31-Dec-2017 nm Added "safe" delete */
-/* 31-Dec-2017 nm Added noVersioningFlag = don't create ~1 backup */
-/* 10-Aug-2019 nm Fixed problem w/ delete + noVersioningFlag */
-FILE *fSafeOpen(vstring fileName, vstring mode, flag noVersioningFlag)
-{
-  FILE *fp;
-  vstring prefix = "";
-  vstring postfix = "";
-  vstring bakName = "";
-  vstring newBakName = "";
-  long v;
-  long lastVersion; /* Last version before gap */ /* nm 29-Apr-2007 */
-
-  if (!strcmp(mode, "r")) {
-    fp = fopen(fileName, "r");
-    if (!fp) {
-      print2("?Sorry, couldn't open the file \"%s\".\n", fileName);
-    }
-    return (fp);
-  }
-
-  if (!strcmp(mode, "w")
-      || !strcmp(mode, "d")) {    /* 31-Dec-2017 */
-    if (noVersioningFlag) goto skip_backup; /* 10-Aug-2019 nm */
-    /* See if the file already exists. */
-    fp = fopen(fileName, "r");
-
-    if (fp) {
-      fclose(fp);
-
-#define VERSIONS 9
-      /* The file exists.  Rename it. */
-
-#if defined __WATCOMC__ /* MSDOS */
-      /* Make sure file name before extension is 8 chars or less */
-      i = instr(1, fileName, ".");
-      if (i) {
-        let(&prefix, left(fileName, i - 1));
-        let(&postfix, right(fileName, i));
-      } else {
-        let(&prefix, fileName);
-        let(&postfix, "");
-      }
-      let(&prefix, cat(left(prefix, 5), "~", NULL));
-      let(&postfix, cat("~", postfix, NULL));
-      if (0) goto skip_backup; /* Prevent compiler warning */
-
-#elif defined __GNUC__ /* Assume unix */
-      let(&prefix, cat(fileName, "~", NULL));
-      let(&postfix, "");
-
-#elif defined THINK_C /* Assume Macintosh */
-      let(&prefix, cat(fileName, "~", NULL));
-      let(&postfix, "");
-
-#elif defined VAXC /* Assume VMS */
-      /* For debugging on VMS: */
-      /* let(&prefix, cat(fileName, "-", NULL));
-         let(&postfix, "-"); */
-      /* Normal: */
-      goto skip_backup;
-
-#else /* Unknown; assume unix standard */
-      /*if (1) goto skip_backup;*/  /* [if no backup desired] */
-      let(&prefix, cat(fileName, "~", NULL));
-      let(&postfix, "");
-
-#endif
-
-
-      /* See if the lowest version already exists. */
-      let(&bakName, cat(prefix, str(1), postfix, NULL));
-      fp = fopen(bakName, "r");
-      if (fp) {
-        fclose(fp);
-        /* The lowest version already exists; rename all to higher versions. */
-
-        /* Find last version before gap, if any */ /* 29-Apr-2007 nm */
-        lastVersion = 0;
-        for (v = 1; v <= VERSIONS; v++) {
-          let(&bakName, cat(prefix, str((double)v), postfix, NULL));
-          fp = fopen(bakName, "r");
-          if (!fp) break; /* Version gap found; skip rest of scan */
-          fclose(fp);
-          lastVersion = v;
-        }
-
-        /* If there are no gaps before version VERSIONS, delete it. */
-        if (lastVersion == VERSIONS) {  /* 29-Apr-2007 nm */
-          let(&bakName, cat(prefix, str((double)VERSIONS), postfix, NULL));
-          fp = fopen(bakName, "r");
-          if (fp) {
-            fclose(fp);
-            remove(bakName);
-          }
-          lastVersion--;  /* 29-Apr-2007 nm */
-        }
-
-        for (v = lastVersion; v >= 1; v--) {  /* 29-Apr-2007 nm */
-          let(&bakName, cat(prefix, str((double)v), postfix, NULL));
-          fp = fopen(bakName, "r");
-          if (!fp) continue;
-          fclose(fp);
-          let(&newBakName, cat(prefix, str((double)v + 1), postfix, NULL));
-          rename(bakName/*old*/, newBakName/*new*/);
-        }
-
-      }
-      let(&bakName, cat(prefix, str(1), postfix, NULL));
-      rename(fileName, bakName);
-
-      /***
-      printLongLine(cat("The file \"", fileName,
-          "\" already exists.  The old file is being renamed to \"",
-          bakName, "\".", NULL), "  ", " ");
-      ***/
-    } /* End if file already exists */
-   skip_backup:
-
-    if (!strcmp(mode, "w")) {
-      fp = fopen(fileName, "w");
-      if (!fp) {
-        print2("?Sorry, couldn't open the file \"%s\".\n", fileName);
-      }
-    } else {
-      if (strcmp(mode, "d")) {
-        bug(1526);
-      }
-      /* 31-Dec-2017 nm */
-      /* For "safe" delete, the file was renamed to ~1, so there is nothing
-         to do. */
-      fp = NULL;
-      /* 10-Aug-2019 nm */
-      /* For non-safe (noVersioning) delete, we actually delete */
-      if (noVersioningFlag) {
-        if(remove(fileName) != 0) {
-          print2("?Sorry, couldn't delete the file \"%s\".\n", fileName);
-        }
-      }
-    }
-
-    /* Deallocate local strings */
-    let(&prefix, "");
-    let(&postfix, "");
-    let(&bakName, "");
-    let(&newBakName, "");
-
-    return (fp);
-  } /* End if mode = "w" or "d" */
-
-  bug(1510); /* Illegal mode */
-  return(NULL);
-
-} /* fSafeOpen() */
-
-
-/* Renames a file with backup of previous version.  If non-zero
-   is returned, there was an error. */
-int fSafeRename(vstring oldFileName, vstring newFileName)
-{
-  int error = 0;
-  int i;
-  FILE *fp;
-  /* Open the new file to force renaming of existing ones */
-  fp = fSafeOpen(newFileName, "w", 0/*noVersioningFlag*/);
-  if (!fp) error = -1;
-  /* Delete the file just created */
-  if (!error) {
-    error = fclose(fp);
-    if (error) print2("?Empty \"%s\" couldn't be closed.\n", newFileName);
-  }
-  if (!error) {
-    error = remove(newFileName);
-    /* On Windows 95, the first attempt may not succeed. */
-    if (error) {
-      for (i = 2; i < 1000; i++) {
-        error = remove(newFileName);
-        if (!error) break;
-      }
-      if (!error)
-        print2("OS WARNING: File delete succeeded only after %i attempts.", i);
-    }
-
-    if (error) print2("?Empty \"%s\" couldn't be deleted.\n", newFileName);
-  }
-  /* Rename the old one to it */
-  if (!error) {
-    error = rename(oldFileName, newFileName);
-    if (error) print2("?Rename of \"%s\" to \"%s\" failed.\n", oldFileName,
-        newFileName);
-  }
-  if (error) {
-    print2("?Sorry, couldn't rename the file \"%s\" to \"%s\".\n", oldFileName,
-        newFileName);
-    print2("\"%s\" may be empty; try recovering from \"%s\".\n", newFileName,
-        oldFileName);
-  }
-  return error;
-} /* fSafeRename */
-
-
-/* Finds the name of the first file of the form filePrefix +
-   nnn + ".tmp" that does not exist.  THE CALLER MUST DEALLOCATE
-   THE RETURNED STRING [i.e. assign function return directly
-   to a local empty vstring with = and not with let(), e.g.
-        let(&str1, "");
-        str1 = fTmpName("zz~list");  ]
-   The file whose name is the returned string is not left open;
-   the caller must separately open the file. */
-vstring fGetTmpName(vstring filePrefix)
-{
-  FILE *fp;
-  vstring fname = "";
-  static long counter = 0;
-  while (1) {
-    counter++;
-    let(&fname, cat(filePrefix, str((double)counter), ".tmp", NULL));
-    fp = fopen(fname, "r");
-    if (!fp) break;
-
-    /* Fix resource leak reported by David Binderman 10-Oct-2016 */
-    fclose(fp);  /* 10-Oct-2016 nm */
-
-    if (counter > 1000) {
-      print2("?Warning: too many %snnn.tmp files - will reuse %s\n",
-          filePrefix, fname);
-      break;
-    }
-  }
-  return fname; /* Caller must deallocate! */
-} /* fGetTmpName() */
-
-
-/* Added 10/10/02 */
-/* This function returns a character string containing the entire contents of
-   an ASCII file, or Unicode file with only ASCII characters.   On some
-   systems it is faster than reading the file line by line.  The caller
-   must deallocate the returned string.  If a NULL is returned, the file
-   could not be opened or had a non-ASCII Unicode character or some other
-   problem.   If verbose is 0, error and warning messages are suppressed. */
-/* 31-Dec-2017 nm Added charCount return arg */
-vstring readFileToString(vstring fileName, char verbose, long *charCount) {
-  FILE *inputFp;
-  long fileBufSize;
-  /*long charCount;*/ /* 31-Dec-2017 nm */
-  char *fileBuf;
-  long i, j;
-
-  /* Find out the upper limit of the number of characters in the file. */
-  /* Do this by opening the file in binary and seeking to the end. */
-  inputFp = fopen(fileName, "rb");
-  if (!inputFp) {
-    if (verbose) print2("?Sorry, couldn't open the file \"%s\".\n", fileName);
-    return (NULL);
-  }
-#ifndef SEEK_END
-/* An older GCC compiler didn't have this ANSI standard constant defined. */
-#define SEEK_END 2
-#endif
-  if (fseek(inputFp, 0, SEEK_END)) { /* fseek returns non-zero on error */
-    /*bug(1511);*/
-    /* 8-Dec-2019 nm Changed bug to error message, which occurs if input "file"
-       is a piped stream */
-    if (verbose) print2(
-        "?Sorry, \"%s\" doesn't seem to be a regular file.\n",
-        fileName);
-    return (NULL);
-  }
-  fileBufSize = ftell(inputFp);
-
-  /* Close and reopen the input file in text mode */
-  /* Text mode is needed for VAX, DOS, etc. with non-Unix end-of-lines */
-  fclose(inputFp);
-  inputFp = fopen(fileName, "r");
-  if (!inputFp) bug(1512);
-
-  /* Allocate space for the entire input file */
-  fileBufSize = fileBufSize + 10;
-            /* Add a factor for unknown text formats (just a guess) */
-  fileBuf = malloc((size_t)fileBufSize);
-  if (!fileBuf) {
-    if (verbose) print2(
-        "?Sorry, there was not enough memory to read the file \"%s\".\n",
-        fileName);
-    fclose(inputFp);
-    return (NULL);
-  }
-
-  /* Put the entire input file into the buffer as a giant character string */
-  (* charCount) = (long)fread(fileBuf, sizeof(char), (size_t)fileBufSize - 2,
-      inputFp);
-  if (!feof(inputFp)) {
-    print2("Note:  This bug will occur if there is a disk file read error.\n");
-    /* If this bug occurs (due to obscure future format such as compressed
-       text files) we'll have to add a realloc statement. */
-    bug(1513);
-  }
-  fclose(inputFp);
-
-  fileBuf[(*charCount)] = 0;
-
-  /* See if it's Unicode */
-  /* This only handles the case where all chars are in the ASCII subset */
-  if ((*charCount) > 1) {
-    if (fileBuf[0] == '\377' && fileBuf[1] == '\376') {
-      /* Yes, so strip out null high-order bytes */
-      if (2 * ((*charCount) / 2) != (*charCount)) {
-        if (verbose) print2(
-"?Sorry, there are an odd number of characters (%ld) %s \"%s\".\n",
-            (*charCount), "in Unicode file", fileName);
-        free(fileBuf);
-        return (NULL);
-      }
-      i = 0; /* ASCII character position */
-      j = 2; /* Unicode character position */
-      while (j < (*charCount)) {
-        if (fileBuf[j + 1] != 0) {
-          if (verbose) print2(
-              "?Sorry, the Unicode file \"%s\" %s %ld at byte %ld.\n",
-              fileName, "has a non-ASCII \ncharacter code",
-              (long)(fileBuf[j]) + ((long)(fileBuf[j + 1]) * 256), j);
-          free(fileBuf);
-          return (NULL);
-        }
-        if (fileBuf[j] == 0) {
-          if (verbose) print2(
-              "?Sorry, the Unicode file \"%s\" %s at byte %ld.\n",
-              fileName, "has a null character", j);
-          free(fileBuf);
-          return (NULL);
-        }
-        fileBuf[i] = fileBuf[j];
-        /* Suppress any carriage-returns */
-        if (fileBuf[i] == '\r') {
-          i--;
-        }
-        i++;
-        j = j + 2;
-      }
-      fileBuf[i] = 0; /* ASCII string terminator */
-      (*charCount) = i;
-    }
-  }
-
-  /* Make sure the file has no carriage-returns */
-  if (strchr(fileBuf, '\r') != NULL) {
-    if (verbose) print2(
-       "?Warning: the file \"%s\" has carriage-returns.  Cleaning them up...\n",
-        fileName);
-    /* Clean up the file, e.g. DOS or Mac file on Unix */
-    i = 0;
-    j = 0;
-    while (j <= (*charCount)) {
-      if (fileBuf[j] == '\r') {
-        if (fileBuf[j + 1] == '\n') {
-          /* DOS file - skip '\r' */
-          j++;
-        } else {
-          /* Mac file - change '\r' to '\n' */
-          fileBuf[j] = '\n';
-        }
-      }
-      fileBuf[i] = fileBuf[j];
-      i++;
-      j++;
-    }
-    (*charCount) = i - 1; /* nm 6-Feb-04 */
-  }
-
-  /* Make sure the last line is not a partial line */
-  if (fileBuf[(*charCount) - 1] != '\n') {
-    if (verbose) print2(
-        "?Warning: the last line in file \"%s\" is incomplete.\n",
-        fileName);
-    /* Add the end-of-line */
-    fileBuf[(*charCount)] = '\n';
-    (*charCount)++;
-    fileBuf[(*charCount)] = 0;
-  }
-
-  if (fileBuf[(*charCount)] != 0) {  /* nm 6-Feb-04 */
-    bug(1522); /* Keeping track of charCount went wrong somewhere */
-  }
-
-  /* Make sure there aren't null characters */
-  i = (long)strlen(fileBuf);
-  if ((*charCount) != i) {
-    if (verbose) {
-      print2(
-          "?Warning: the file \"%s\" is not an ASCII file.\n",
-          fileName);
-      print2(
-          "Its size is %ld characters with null at character %ld.\n",
-          (*charCount), strlen(fileBuf));
-    }
-  }
-/*E*/db = db + i;  /* For memory usage tracking (ignore stuff after null) */
-
-  /******* For debugging
-  print2("In binary mode the file has %ld bytes.\n", fileBufSize - 10);
-  print2("In text mode the file has %ld bytes.\n", (*charCount));
-  *******/
-
-  return ((char *)fileBuf);
-} /* readFileToString */
-
-
-/* 16-Aug-2016 nm */
-/* Returns total elapsed time in seconds since starting session (for the
-   lcc compiler) or the CPU time used (for the gcc compiler).  The
-   argument is assigned the time since the last call to this function. */
-double getRunTime(double *timeSinceLastCall) {
-#ifdef CLOCKS_PER_SEC
-  static clock_t timePrevious = 0;
-  clock_t timeNow;
-  timeNow = clock();
-  *timeSinceLastCall = (double)((1.0 * (double)(timeNow - timePrevious))
-          /CLOCKS_PER_SEC);
-  timePrevious = timeNow;
-  return (double)((1.0 * (double)timeNow)/CLOCKS_PER_SEC);
-  /* Example of printing double format: */
-  /* print2("Total elapsed time = %4.2f s\n", t) */
-#else
-  print2("The clock() function is not implemented on this computer.\n");
-  *timeSinceLastCall = 0;
-  return 0;
-#endif
-}
-
-
-/* 4-May-2017 Ari Ferrera */
-void freeInOu() {
-  long i, j;
-  j = pntrLen(backBuffer);
-  for (i = 0; i < j; i++) let((vstring *)(&backBuffer[i]), "");
-  pntrLet(&backBuffer, NULL_PNTRSTRING);
-}
+/*****************************************************************************/
+/*        Copyright (C) 2021  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/*!
+ * \file
+ * Implementation of basic input and output.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mminou.h"
+#include "mmcmdl.h" /* for g_commandPrompt global */
+
+#ifdef __WATCOMC__
+  /* Bugs in WATCOMC:
+     1. #include <conio.h> has compile errors
+     2. Two consecutive calls to vprintf after va_start causes register dump
+            or corrupts 2nd arg.
+  */
+/* From <conio.h>: */
+#ifndef _CONIO_H_INCLUDED
+extern int cprintf(const char *f__mt,...);
+#define _CONIO_H_INCLUDED
+#endif
+#endif  /* End #ifdef __WATCOMC__ */
+
+/*!
+ * \def QUOTED_SPACE
+ * The general line wrapping algorithm looks out for spaces as break positions.
+ * To prevent a quote delimited by __"__ be broken down, spaces are temporarily
+ * replaced with 0x03 (ETX, end of transmission), hopefully never used in
+ * text in this application.
+ */
+#define QUOTED_SPACE 3 /* ASCII 3 that temporarily zaps a space */
+
+
+int g_errorCount = 0;
+
+/* Global variables used by print2() */
+flag g_logFileOpenFlag = 0;
+FILE *g_logFilePtr;
+FILE *g_listFile_fp = NULL;
+/* Global variables used by print2() */
+flag g_outputToString = 0;
+vstring_def(g_printString);
+/* Global variables used by cmdInput() */
+long g_commandFileNestingLevel = 0;
+FILE *g_commandFilePtr[MAX_COMMAND_FILE_NESTING + 1];
+vstring g_commandFileName[MAX_COMMAND_FILE_NESTING + 1];
+
+/*!
+ * \var flag g_commandFileSilent[]
+ * a 1 for a particular \ref g_commandFileNestingLevel suppresses output for
+ * that submit nesting level.  The value for the interactive level
+ * (\ref g_commandFileNestingLevel == 0) is ignored.
+ */
+flag g_commandFileSilent[MAX_COMMAND_FILE_NESTING + 1];
+flag g_commandFileSilentFlag = 0; /* For SUBMIT ... /SILENT */
+
+FILE *g_input_fp; /* File pointers */
+vstring_def(g_input_fn);
+vstring_def(g_output_fn);        /* File names */
+
+long g_screenWidth = MAX_LEN; /* Width default = 79 */
+/* g_screenHeight is one less than the physical screen to account for the
+   prompt line after pausing. */
+long g_screenHeight = SCREEN_HEIGHT; /* Default = 23 */
+
+/*!
+ * \var int printedLines
+ * Lines printed since last user input (mod screen height).  This value is used
+ * to determine when a page of output is completed, and the user needs to be
+ * prompted for continuing output.  It counts the number of LF characters,
+ * which may differ from the lines actually used because some hard line breaks
+ * are enforced by overly long lines.
+ */
+int printedLines = 0;
+flag g_scrollMode = 1; /* Flag for continuous (0) or prompted (1) scroll */
+flag g_quitPrint = 0; /* Flag that user quit the output */
+
+/*!
+ * \var flag localScrollMode
+ *
+ * value 0: temporarily disables page-wise prompted scroll (see
+ * \ref g_scrollMode) until enabled (value 1) again.  Normal user input by
+ * \ref cmdInput1 allows scrolling during output (value 1).  A value of 0
+ * indicates the user has issued command _s_ or _S_ (see \ref pgBackBuffer)
+ * to print all pending output without interruption.  In this case the value 0
+ * is assigned temporarily to skip prompts.
+ */
+flag localScrollMode = 1;
+
+/*!
+ * \page pgBackBuffer History of Pages of Output
+ *
+ * Lengthy text can be displayed in a page-wise manner,  if requested.  In such
+ * a case text output is broken down into pieces small enough to fit into the
+ * screen rectangle of a virtual text device called a __page__ here.  The
+ * dimensions of this rectangle or page are given in \ref g_screenWidth and
+ * \ref g_screenHeight. An extra line outside of the page is reserved for a
+ * prompt and the echo of the user response.
+ *
+ * Regular output (not prompts or error messages) is added line by line both
+ * to the screen and the latest entry of a stack of strings \ref backBuffer,
+ * the.__current page__.  The number of lines pushed to it is kept in
+ * \ref printedLines.  The moment this value indicates a full page, a new empty
+ * page is pushed on the stack, and output is suspended to let the user read
+ * displayed contents in rest.  A user prompt asks for resuming pending output,
+ * alternatively s/he may step backwards and forward through the sequence of
+ * saved pages for redisplay.  The variable \ref backBufferPos tracks which
+ * page the user requested last (or points to the currently built-up page).
+ *
+ * On initialization memory is allocated for the \ref backBuffer, and an empty
+ * string (empty page) is pushed onto the stack as a guard.  This is a simple
+ * technical means to formally carry out a back step, even when you have just
+ * recalled the very first saved page.  The display of this empty guard page
+ * has no other effect than repeating the prompt.  The \ref backBufferPos is
+ * never updated to point (value 0) to this guard page.
+ *
+ * During a replay of the history normal user input is intercepted and
+ * interpreted as scroll commands.  These commands are at most a single
+ * character, followed by a LF.  A _b_ or _B_ backs up one step further in
+ * history,  an empty line means one step forward, _s_ or _S_ scrolls to the
+ * end, showing all pages in between and all pending output at one swoop,
+ * and _q_ or _Q_ skips all pending output and gets you directly back to normal
+ * input.  You cannot back up to the guard page (although it is shown on input
+ * B when at the very first saved page).  Moving a step forward when at the
+ * very last history page resumes pending normal output.  Unrecognized input is
+ * simply ignored.  The loop controlling these movements is found in
+ * \ref print2, but \ref cmdInput can trigger it as well.
+ */
+
+/*!
+ * \var pntrString* backBuffer
+ * Buffer for B (back) command at end-of-page prompt.  Although formally a
+ * \ref pntrString is using void*, this buffer contains pointer to \ref vstring
+ * only.  Its element at index 0 is fixed to an empty string (page), a guard
+ * representing contents not available.
+ *
+ * Some longer text (like help texts for example) provide a page wise display
+ * with a scroll option, so the user can move freely back and forth in the
+ * text.  This is the storage of already displayed text kept for possible
+ * redisplay (see \ref pgBackBuffer).
+ *
+ * The element last displayed, or currently built up, is denoted by
+ * \ref backBufferPos.
+ *
+ * The buffer is allocated and initialized to not-empty by \ref print2.
+ */
+pntrString_def(backBuffer);
+
+/*!
+ * \var backBufferPos
+ *
+ * Number of entries in the \ref backBuffer that are available for repeatedly
+ * scrolling back.  Initialized to 0.
+ *
+ * \invariant The value 0 indicates an unitialized and empty  \ref backBuffer.
+ */
+long backBufferPos = 0;
+
+/*!
+ * \var flag backFromCmdInput
+ * \brief user entered a B (scroll back command) when a command was expected.
+ * This \ref flag is set only by \ref cmdInput that handles some of the user's
+ * scroll input commands, in particular the B command for moving backwards
+ * in page-wise display.  All further scroll commands are interpreted by
+ * \ref print2, to which this flag is directed.  It signals the \ref backBuffer
+ * is being scrolled.
+ */
+flag backFromCmdInput = 0;
+
+/* Special:  if global flag g_outputToString = 1, then the output is not
+             printed but is added to global string g_printString */
+/* Returns 0 if user typed "q" during scroll prompt; this lets a procedure
+   interrupt it's output for speedup (rest of output will be suppressed anyway
+   until next command line prompt) */
+flag print2(const char* fmt, ...) {
+  /* This performs the same operations as printf, except that if a log file is
+    open, the characters will also be printed to the log file. */
+  /* Also, scrolling is paused at each page if in scroll-prompted mode. */
+  va_list ap;
+  char c;
+  long nlpos, lineLen, charsPrinted;
+  long i;
+
+  char *printBuffer; /* Allocated dynamically */
+
+/* step (1) initialize backBuffer  */
+
+  if (backBufferPos == 0) {
+    /* Initialize backBuffer - 1st time in program */
+    /* Warning:  Don't call bug(), because it calls print2. */
+    if (pntrLen(backBuffer)) {
+      printf("*** BUG #1501\n");
+#if __STDC__
+      fflush(stdout);
+#endif
+    }
+    backBufferPos = 1;
+    pntrLet(&backBuffer, pntrAddElement(backBuffer));
+    /* Note: pntrAddElement() initializes the added element to the
+       empty string, so we don't need a separate initialization. */
+    /* backBuffer[backBufferPos - 1] = ""; */  /* already done */
+  }
+
+  if ((!g_quitPrint && g_commandFileNestingLevel == 0 && (g_scrollMode == 1
+           && localScrollMode == 1)
+      && printedLines >= g_screenHeight && !g_outputToString)
+      || backFromCmdInput) {
+    /* It requires a scrolling prompt */
+
+/* step (2) perform scrolling */
+
+    while(1) {
+      if (backFromCmdInput && backBufferPos == pntrLen(backBuffer))
+        break; /* Exhausted buffer */
+      if (backBufferPos < 1 || backBufferPos > pntrLen(backBuffer)) {
+        /* Warning:  Don't call bug(), because it calls print2. */
+        printf("*** BUG #1502 %ld\n", backBufferPos);
+#if __STDC__
+        fflush(stdout);
+#endif
+      }
+      if (backBufferPos == 1) {
+        printf(
+"Press <return> for more, Q <return> to quit, S <return> to scroll to end... "
+          );
+#if __STDC__
+        fflush(stdout);
+#endif
+      } else {
+        printf(
+"Press <return> for more, Q <ret> quit, S <ret> scroll, B <ret> back up... "
+         );
+#if __STDC__
+        fflush(stdout);
+#endif
+      }
+      c = (char)(getchar());
+      if (c == '\n') {
+        if (backBufferPos == pntrLen(backBuffer)) {
+          /* Normal output */
+          break;
+        } else {
+          /* Get output from buffer */
+          backBufferPos++;
+          printf("%s", (vstring)(backBuffer[backBufferPos - 1]));
+#if __STDC__
+          fflush(stdout);
+#endif
+          continue;
+        }
+      }
+      if (getchar() == '\n') {
+        if (c == 'q' || c == 'Q') {
+          if (!backFromCmdInput)
+            g_quitPrint = 1;
+          break;
+        }
+        if (c == 's' || c == 'S') {
+
+          if (backBufferPos < pntrLen(backBuffer)) {
+            /* Print rest of buffer to screen */
+            while (backBufferPos + 1 <= pntrLen(backBuffer)) {
+              backBufferPos++;
+              printf("%s", (vstring)(backBuffer[backBufferPos - 1]));
+#if __STDC__
+              fflush(stdout);
+#endif
+            }
+          }
+          if (!backFromCmdInput)
+            localScrollMode = 0; /* Continuous scroll */
+          break;
+
+        }
+        if (backBufferPos > 1) {
+          if (c == 'b' || c == 'B') {
+            backBufferPos--;
+            printf("%s", (vstring)(backBuffer[backBufferPos - 1]));
+#if __STDC__
+            fflush(stdout);
+#endif
+            continue;
+          }
+        }
+
+        printf("%c", 7); /* Bell */
+#if __STDC__
+        fflush(stdout);
+#endif
+        continue;
+      }
+      while (c != '\n') c = (char)(getchar());
+    } /* While 1 */
+
+    if (backFromCmdInput)
+      goto PRINT2_RETURN;
+
+/* step (3) allocate a new page of output */
+
+    printedLines = 0; /* Reset the number of lines printed on the screen */
+    if (!g_quitPrint) {
+      backBufferPos++;
+      pntrLet(&backBuffer, pntrAddElement(backBuffer));
+      /* Note: pntrAddElement() initializes the added element to the
+         empty string, so we don't need a separate initialization. */
+      /* backBuffer[backBufferPos - 1] = ""; */  /* already done */
+    }
+  }
+
+
+
+  /* User typed 'q' above or earlier; don't return if we're outputting to
+    a string since we want to complete the writing to the string. */
+  if (g_quitPrint && !g_outputToString) goto PRINT2_RETURN;
+
+
+/* step (4) evaluate the output text */
+
+  /* Allow unlimited output size */
+  va_start(ap, fmt);
+  int bufsiz = vsnprintf(NULL, 0, fmt, ap); /* Get the buffer size we need */
+  va_end(ap);
+  /* Warning: some older compilers, including lcc-win32 version 3.8 (2004),
+     return -1 instead of the buffer size */
+  if (bufsiz == -1) bug(1527);
+  printBuffer = malloc((size_t)bufsiz + 1);
+
+  /* Let each vs[n]printf have its own va_start...va_end
+     in an attempt to fix crash with some compilers (e.g. gcc 4.9.2) */
+  va_start(ap, fmt);
+  charsPrinted = vsprintf(printBuffer, fmt, ap); /* Put formatted string into
+      buffer */
+  va_end(ap);
+  if (charsPrinted != bufsiz) {
+    /* Give some info with printf in case print2 crashes during bug() call */
+    printf("For bug #1528: charsPrinted = %ld != bufsiz = %ld\n", charsPrinted,
+        (long)bufsiz);
+    bug(1528);
+  }
+
+  nlpos = instr(1, printBuffer, "\n");
+  lineLen = (long)strlen(printBuffer);
+
+/* step (5) revert QUOTED_SPACE to space  */
+
+  /* Change any ASCII 3's back to spaces, where they were set in
+     printLongLine to handle the broken quote problem */
+  for (i = 0; i < lineLen; i++) {
+    if (printBuffer[i] == QUOTED_SPACE) printBuffer[i] = ' ';
+  }
+
+  if ((lineLen > g_screenWidth + 1)
+         && !g_outputToString  /* for HTML */ ) {
+
+/* step (6) line wrapping */
+
+    /* Force wrapping of lines that are too long by recursively calling
+       print2() via printLongLine().  Note:  "+ 1" above accounts for \n. */
+    /* Note that breakMatch is "" so it may break in middle of a word */
+    if (!nlpos) {
+      /* No end of line */
+      printLongLine(left(printBuffer, lineLen), "", "");
+    } else {
+      printLongLine(left(printBuffer, lineLen - 1), "", "");
+    }
+    goto PRINT2_RETURN;
+  }
+
+  if (!g_outputToString && !g_commandFileSilentFlag) {
+    if (nlpos == 0) { /* Partial line (usu. status bar) - print immediately */
+
+/* step (7) print to screen, part 1 */
+
+#ifdef __WATCOMC__
+      cprintf("%s", printBuffer); /* Immediate console I/O (printf buffers it)*/
+#else
+      printf("%s", printBuffer);
+#endif
+
+#if __STDC__
+      /*
+      The following change to mminou.c was necessary on my Unix (Linux)
+      system to get the `verify proof *' progress bar to display
+      progressively (rather than all at once). I've conditioned it on
+      __STDC__, since it should be harmless on any ANSI C system.
+        -- Stephen McCamant  smccam @ uclink4.berkeley.edu 12-Sep-00
+      */
+      fflush(stdout);
+#endif
+
+    } else {
+
+/* step (7) print to screen, part 2 */
+
+      printf("%s", printBuffer); /* Normal line */
+#if __STDC__
+      fflush(stdout);
+#endif
+      printedLines++;
+      if (!(g_scrollMode == 1 && localScrollMode == 1)) {
+
+/* step (8) address overflowed page */
+
+        /* Even in non-scroll (continuous output) mode, still put paged-mode
+           lines into backBuffer in case user types a "B" command later,
+           so user can page back from end. */
+        if (printedLines > g_screenHeight) {
+          printedLines = 1;
+          backBufferPos++;
+          pntrLet(&backBuffer, pntrAddElement(backBuffer));
+          /* Note: pntrAddElement() initializes the added element to the
+             empty string, so we don't need a separate initialization. */
+          /* backBuffer[backBufferPos - 1] = ""; */  /* already done */
+        }
+      }
+    }
+    /* Add line to backBuffer string array */
+    /* Warning:  Don't call bug(), because it calls print2. */
+    if (backBufferPos < 1) {
+      printf("*** PROGRAM BUG #1504\n");
+#if __STDC__
+      fflush(stdout);
+#endif
+    }
+
+/* step (9) copy output to backBuffer */
+
+    let((vstring *)(&(backBuffer[backBufferPos - 1])), cat(
+        (vstring)(backBuffer[backBufferPos - 1]), printBuffer, NULL));
+  } /* End if !g_outputToString */
+
+  if (g_logFileOpenFlag && !g_outputToString) {
+
+/* step (10) log output to file  */
+
+    fprintf(g_logFilePtr, "%s", printBuffer);  /* Print to log file */
+#if __STDC__
+    fflush(g_logFilePtr);
+#endif
+  }
+
+  if (g_listMode && g_listFile_fp != NULL && !g_outputToString) {
+    /* Put line in list.tmp as comment */
+    fprintf(g_listFile_fp, "! %s", printBuffer);  /* Print to list command file */
+  }
+
+  if (g_outputToString) {
+
+/* step (11) redirect output to a string */
+
+    let(&g_printString, cat(g_printString, printBuffer, NULL));
+  }
+
+  /* Check for lines too long */
+  if (lineLen > g_screenWidth + 1) { /* The +1 ignores \n */
+    /* Warning:  Don't call bug(), because it calls print2. */
+    /* If this bug occurs, the calling function should be fixed. */
+    printf("*** PROGRAM BUG #1505 (not serious, but please report it)\n");
+    printf("Line exceeds screen width; caller should use printLongLine.\n");
+    printf("%ld %s\n", lineLen, printBuffer);
+    /*printf(NULL);*/  /* Force crash on VAXC to see where it came from */
+#if __STDC__
+    fflush(stdout);
+#endif
+  }
+  /* \n not allowed in middle of line */
+  /* If this bug occurs, it means print2() is being called with \n in the
+     middle of the line and should be fixed in the caller.  printLongLine()
+     may be used if this is necessary. */
+  /* Warning:  Don't call bug(), because it calls print2. */
+  if (nlpos != 0 && nlpos != lineLen) {
+    printf("*** PROGRAM BUG #1506\n");
+#if __STDC__
+    fflush(stdout);
+#endif
+  }
+
+  free(printBuffer);
+
+ PRINT2_RETURN:
+  return !g_quitPrint;
+}
+
+
+/* printLongLine automatically puts a newline \n in the output line. */
+/* startNextLine is the string to place before continuation lines. */
+/* breakMatch is a list of characters at which the line can be broken. */
+/* Special:  startNextLine starts with "~" means add tilde after broken line */
+/* Special:  breakMatch begins with "&" means compressed proof */
+/* Special:  breakMatch empty means break line anywhere */
+/* Special:  breakMatch begins with octal 1 means nested tree display (right
+             justify continuation lines); 1 is changed to space for
+             break matching */
+/* Special:  if breakMatch is \, then put % at end of previous line for LaTeX*/
+/* Special:  if breakMatch is " (quote), treat as if space but don't break
+             quotes, and also let lines grow long - use this call for all HTML
+             code */
+void printLongLine(const char *line, const char *startNextLine, const char *breakMatch) {
+  vstring_def(longLine);
+  vstring_def(multiLine);
+  vstring_def(prefix);
+  vstring_def(startNextLine1);
+  vstring_def(breakMatch1);
+  long i, j, p;
+  long startNextLineLen;
+  flag firstLine;
+  flag tildeFlag = 0;
+  flag treeIndentationFlag = 0;
+  flag quoteMode = 0; /* 1 means inside quote */
+  long saveScreenWidth; /* To let g_screenWidth grow temporarily */
+
+  long saveTempAllocStack;
+
+  /* Blank line (the rest of algorithm would ignore it; output and return) */
+  if (!line[0]) {
+    /* Do a dummy let() so caller can always depend on printLongLine
+       to empty the tempalloc string stack (for the rest of this code, the
+       first let() will do this) */
+    free_vstring(longLine);
+    print2("\n");
+    return;
+  }
+
+  /* Change the stack allocation start to prevent arguments from being
+     deallocated.  We need to do this because more than one argument
+     may be passed in with cat(), chr(), etc. and let() can only grab
+     one, destroying the others  */
+  saveTempAllocStack = g_startTempAllocStack;
+  g_startTempAllocStack = g_tempAllocStackTop; /* For let() stack cleanup */
+
+  /* Grab the input arguments */
+  let(&multiLine, line);
+  let(&startNextLine1, startNextLine);
+  let(&breakMatch1, breakMatch);
+  /* Now relax - back to normal; we can let temporary allocation stack die. */
+  g_startTempAllocStack = saveTempAllocStack;
+
+  /* We must copy input argument breakMatch to a variable string because we
+     will be zapping one of its characters, and ordinarily breakMatch is
+     passed in as a constant string.  However, this is now done with argument
+     grabbing above so we're OK. */
+
+  /* Flag to right justify continuation lines */
+  if (breakMatch1[0] == 1) {
+    treeIndentationFlag = 1;
+    breakMatch1[0] = ' '; /* Change to a space (the real break character) */
+  }
+
+  /* HTML mode */
+  /* The HTML mode is intended not to break inside quoted HTML tag
+     strings.  All HTML output should be called with this mode.
+     Since we don't parse HTML this method is not perfect.  Only double
+     quotes are inspected, so all HTML strings with spaces must be
+     surrounded by double quotes.  If text quotes surround a tag
+     with a quoted string, this code will not work and must be
+     enhanced. */
+  /* Whenever we are inside of a quote, we change a space to ASCII 3 to
+     prevent matching it.  The reverse is done in the print2() function,
+     where all ASCII 3's are converted back to space. */
+  /* Note added 20-Oct-02: tidy.exe breaks HREF quotes with new line.
+     Check HTML spec - do we really need this code? */
+  j = (long)strlen(multiLine);
+  /* Do a bug check to make sure no real ASCII 3's are ever printed */
+  for (i = 0; i < j; i++) {
+    if (multiLine[i] == QUOTED_SPACE) bug(1514); /* Should never be the case */
+  }
+  if (breakMatch1[0] == '\"') {
+    breakMatch1[0] = ' '; /* Change to a space (the real break character) */
+    /* Scan string for quoted strings */
+    quoteMode = 0;
+
+    /* Don't put line breaks in anything inside
+       _double_ quotes that follows an = sign, such as TITLE="abc def".  Ignore
+       _single_ quotes (which could be apostrophes).  The HTML output code
+       must be (and so far is) written to conform to this. */
+    i = 0;
+    while (multiLine[i]) {
+      if (multiLine[i] == '"' && i > 0) {
+        if (!quoteMode && multiLine[i - 1] == '=')
+          quoteMode = 1;
+        else
+          quoteMode = 0;
+      }
+      if (multiLine[i] == ' ' && quoteMode)
+        multiLine[i] = QUOTED_SPACE;
+      i++;
+    }
+  } /* if (breakMatch1[0] == '\"') */
+
+
+  /* The tilde is a special flag for printLongLine to print a
+     tilde before the carriage return in a split line, not after */
+  if (startNextLine1[0] == '~') {
+    tildeFlag = 1;
+    let(&startNextLine1, " ");
+  }
+
+
+  while (multiLine[0]) { /* While there are multiple caller-inserted newlines */
+
+    /* Process caller-inserted newlines */
+    p = instr(1, multiLine, "\n");
+    if (p) {
+      /* Get the next caller's line */
+      let(&longLine, left(multiLine, p - 1));
+      let(&multiLine, right(multiLine, p + 1));
+    } else {
+      let(&longLine, multiLine);
+      free_vstring(multiLine);
+    }
+
+    saveScreenWidth = g_screenWidth;
+   HTML_RESTART:
+    /* Now we will break up one line from the caller i.e. longLine;
+       multiLine has any remaining lines to be processed in next pass */
+    firstLine = 1;
+
+    startNextLineLen = (long)strlen(startNextLine1);
+    /* Prevent infinite loop if next line prefix is longer than screen */
+    if (startNextLineLen > g_screenWidth - 4) {
+      startNextLineLen = g_screenWidth - 4;
+      let(&startNextLine1, left(startNextLine1, g_screenWidth - 4));
+    }
+
+    /* If startNextLine starts with "~" means add tilde
+       after broken line (used for command input comment continuation); if
+       breakMatch is "\\" i.e. single \, then put % at end of previous line
+       for LaTeX */
+    /* Otherwise, if first line:  use length of longLine;
+       if not first line:  use length of longLine - startNextLineLen */
+    while ((signed)(strlen(longLine)) + (1 - firstLine) * startNextLineLen >
+        g_screenWidth - (long)tildeFlag - (long)(breakMatch1[0] == '\\')) {
+      /* Get screen width + 1 (default is 79 + 1)*/
+      p = g_screenWidth - (long)tildeFlag - (long)(breakMatch1[0] == '\\') + 1;
+      if (!firstLine) p = p - startNextLineLen;
+
+      if (p < 4) bug(1524);  /* This may cause out-of-string ref below */
+      /* Assume compressed proof if 1st char of breakMatch1 is "&" */
+      if (breakMatch1[0] == '&'
+          && ((!instr(p, left(longLine, (long)strlen(longLine) - 3), " ")
+              && longLine[p - 3] != ' ') /* Don't split trailing "$." */
+            || longLine[p - 4] == ')')) /* Label sect ends in col 77 */ {
+        /* We're in the compressed proof section; break line anywhere */
+        p = p + 0;  /* Don't change position */
+        /* In the case where the last space occurs at column 79 i.e.
+           g_screenWidth, break the line at column 78.  This can happen
+           when compressed proof ends at column 78, followed by space
+           and "$."  It prevents an extraneous trailing space on the line. */
+        if (longLine[p - 2] == ' ') p--;
+      } else {
+        if (!breakMatch1[0]) {
+          p = p + 0; /* Break line anywhere; don't change position */
+        } else {
+          if (p <= 0) bug(1518);
+          /* For LaTeX, match space, not backslash */
+          /* (Todo:  is backslash match mode really needed?) */
+          while (strchr(breakMatch1[0] != '\\' ? breakMatch1 : " ",
+              longLine[p - 1]) == NULL) {
+            p--;
+            if (!p) break;
+          }
+          /* We will now not break any line at non-space,
+             since it causes more problems that it solves e.g. with
+             WRITE SOURCE.../REWRAP with long URLs */
+          if (p <= 0) {
+            /* The line couldn't be broken.  Since it's an HTML line, we
+               can increase g_screenWidth until it will fit. */
+            g_screenWidth++;
+            /* If this bug happens, we'll have to increase PRINTBUFFERSIZE
+               or change the HTML code being printed. */
+            goto HTML_RESTART; /* Ugly but another while loop nesting would
+                                  be even more confusing */
+          }
+        } /* end if (!breakMatch1[0]) else */
+      } /* end if (breakMatch1[0] == '&' &&... else */
+
+      if (p <= 0) {
+        /* Break character not found; give up at
+           g_screenWidth - (long)tildeFlag  - (long)(breakMatch1[0] == '\\')+ 1 */
+        p = g_screenWidth - (long)tildeFlag  - (long)(breakMatch1[0] == '\\')+ 1;
+        if (!firstLine) p = p - startNextLineLen;
+        if (p <= 0) p = 1; /* If startNextLine too long */
+      }
+      if (!p) bug(1515); /* p should never be 0 by this point */
+      /* If we broke at a non-space 1st char, line length won't get reduced */
+      /* Hopefully this will never happen with the breakMatch's we use,
+         otherwise the code will require a rework. */
+      if (p == 1 && longLine[0] != ' ') bug(1516);
+      if (firstLine) {
+        firstLine = 0;
+        free_vstring(prefix);
+      } else {
+        let(&prefix, startNextLine1);
+        if (treeIndentationFlag) {
+          if (startNextLineLen + p - 1 < g_screenWidth) {
+            /* Right justify output for continuation lines */
+            let(&prefix, cat(prefix, space(g_screenWidth - startNextLineLen
+                - p + 1), NULL));
+          }
+        }
+      }
+      if (!tildeFlag) {
+        print2("%s\n",cat(prefix, left(longLine,p - 1), NULL));
+      } else {
+        print2("%s\n",cat(prefix, left(longLine,p - 1), "~", NULL));
+      }
+      if (longLine[p - 1] == ' ' &&
+          breakMatch1[0] /* But not "break anywhere" line */) {
+        /* (Note:  search for "p--" ~100 lines above for the place
+           where the backward search for space happens.) */
+        /* Remove leading space for neatness */
+        if (longLine[p] == ' ') {
+          /* There could be 2 spaces at the end of a sentence. */
+          let(&longLine, right(longLine, p + 2));
+        } else {
+          let(&longLine, right(longLine,p + 1));
+        }
+      } else {
+        let(&longLine, right(longLine,p));
+      }
+    } /* end while longLine too long */
+    if (!firstLine) {
+      if (treeIndentationFlag) {
+        /* Right justify output for continuation lines */
+        print2("%s\n",cat(startNextLine1, space(g_screenWidth
+            - startNextLineLen - (long)(strlen(longLine))), longLine, NULL));
+      } else {
+        print2("%s\n",cat(startNextLine1, longLine, NULL));
+      }
+    } else {
+      print2("%s\n",longLine);
+    }
+    g_screenWidth = saveScreenWidth; /* Restore to normal */
+
+  } /* end while multiLine != "" */
+
+  free_vstring(multiLine); /* Deallocate */
+  free_vstring(longLine); /* Deallocate */
+  free_vstring(prefix); /* Deallocate */
+  free_vstring(startNextLine1); /* Deallocate */
+  free_vstring(breakMatch1); /* Deallocate */
+
+  return;
+} /* printLongLine */
+
+/*!
+ * \def CMD_BUFFER_SIZE
+ * Number of bytes allocated for prompted text, including the terminating NUL,
+ * but excluding the return key stroke the user finishes her/his input with.
+ */
+#define CMD_BUFFER_SIZE 2000
+
+vstring cmdInput(FILE *stream, const char *ask) {
+  /* This function prints a prompt (if 'ask' is not NULL) and gets a line from
+    the input stream.  NULL is returned when end-of-file is encountered.
+    New memory is allocated each time linput is called.  This space must
+    be freed by the caller. */
+  vstring_def(g); /* Always init vstrings to "" for let(&...) to work */
+  long i;
+
+  while (1) { /* For "B" backup loop */
+    if (ask != NULL && !g_commandFileSilentFlag) {
+      printf("%s", ask);
+#if __STDC__
+      fflush(stdout);
+#endif
+    }
+    let(&g, space(CMD_BUFFER_SIZE)); /* Allocate CMD_BUFFER_SIZE+1 bytes */
+    if (g[CMD_BUFFER_SIZE]) bug(1520); /* Bug in let() (improbable) */
+    g[CMD_BUFFER_SIZE - 1] = 0; /* For overflow detection */
+    if (!fgets(g, CMD_BUFFER_SIZE, stream)) {
+      /* End of file */
+      free_vstring(g); /* Deallocate memory */
+      return NULL;
+    }
+    if (g[CMD_BUFFER_SIZE - 1]) {
+      /* Detect input overflow */
+      /* Warning:  Don't call bug() - it calls print2 which may call this. */
+      printf("***BUG #1508\n");
+#if __STDC__
+      fflush(stdout);
+#endif
+    }
+    i = (long)strlen(g);
+/*E*/db = db - (CMD_BUFFER_SIZE - i); /* Adjust string usage to detect leaks */
+    /* Detect operating system bug of inputting no characters */
+    if (!i) {
+      printf("***BUG #1507\n");
+#if __STDC__
+      fflush(stdout);
+#endif
+
+    } else {
+      /* The last line in the file has no new-line */
+      if (g[i - 1] != '\n') {
+        /* Warning:  Don't call bug() - it calls print2 which may call this. */
+        if (!feof(stream)) {
+          printf("***BUG #1525\n");
+#if __STDC__
+          fflush(stdout);
+#endif
+        }
+        /* Add a new-line so processing below will behave correctly. */
+        let(&g, cat(g, chr('\n'), NULL));
+/*E*/db = db + (CMD_BUFFER_SIZE - i); /* Cancel extra piece of string */
+        i++;
+      }
+    }
+
+    if (g[1]) {
+      i--;
+      if (g[i] != '\n') {
+        printf("***BUG #1519\n");
+#if __STDC__
+        fflush(stdout);
+#endif
+      }
+      g[i]=0;   /* Eliminate new-line character by zapping it */
+/*E*/db = db - 1;
+    } else {
+      if (g[0] != '\n') {
+        printf("***BUG #1521\n");
+#if __STDC__
+        fflush(stdout);
+#endif
+      }
+      /* Eliminate new-line by deallocating vstring space (if we just zap
+         character [0], let() will later think g is an empty string constant
+         and will never deallocate g) */
+      free_vstring(g);
+    }
+
+    /* If user typed "B" (for back), go back to the back buffer to
+       let the user scroll through it */
+    if ((!strcmp(g, "B") || !strcmp(g, "b")) /* User typed "B" */
+        && pntrLen(backBuffer) > 1   /* The back-buffer still exists and
+                                         there was a previous page */
+        && g_commandFileNestingLevel == 0
+        && (g_scrollMode == 1 && localScrollMode == 1)
+        && !g_outputToString) {
+      /* Set variables so only backup buffer will be looked at in print2() */
+      backBufferPos = pntrLen(backBuffer) - 1;
+      printf("%s", (vstring)(backBuffer[backBufferPos - 1]));
+#if __STDC__
+      fflush(stdout);
+#endif
+      backFromCmdInput = 1; /* Flag for print2() */
+      print2(""); /* Only the backup buffer will be looked at */
+      backFromCmdInput = 0;
+    } else {
+      /* If the command line is empty (at main prompt), let user still
+         type "B" for convenience in case too many
+         returns where hit while scrolling */
+      if (g_commandFileNestingLevel > 0) break;
+                            /* We're taking from a SUBMIT
+                              file so break out of loop that looks for "B" */
+      if (ask == NULL) {
+        printf("***BUG #1523\n"); /* In non-SUBMIT
+          mode 'ask' won't be NULL, so flag non-fatal bug here just in case */
+#if __STDC__
+        fflush(stdout);
+#endif
+      }
+      if (g[0]) break; /* Command line not empty so break out of loop
+                          that looks for "B" */
+      if (ask != NULL &&
+          /* User entered empty command line but not at a prompt */
+          /* g_commandPrompt is assigned in metamath.c and declared in
+             mmcmdl.h */
+          strcmp(ask, g_commandPrompt)) {
+        break; /* Break out of loop that looks for "B" */
+      }
+    }
+  } /* while 1 */
+
+  return g;
+} /* cmdInput */
+
+vstring cmdInput1(const char *ask) {
+  /* This function gets a line from either the terminal or the command file
+    stream depending on g_commandFileNestingLevel > 0.  It calls cmdInput(). */
+  /* Warning: the calling program must deallocate the returned string. */
+  vstring_def(commandLn);
+  vstring_def(ask1);
+  long p, i;
+
+  let(&ask1, ask); /* In case ask is temporarily allocated (i.e in case it
+                      will become deallocated at next let() */
+  /* Look for lines too long */
+  while ((signed)(strlen(ask1)) > g_screenWidth) {
+    p = g_screenWidth - 1;
+    while (ask1[p] != ' ' && p > 0) p--;
+    if (!p) p = g_screenWidth - 1;
+    print2("%s\n", left(ask1, p));
+    let(&ask1, right(ask1, p + 1));
+  }
+  /* Allow 10 characters for answer */
+  if ((signed)(strlen(ask1)) > g_screenWidth - 10) {
+    p = g_screenWidth - 11;
+    while (ask1[p] != ' ' && p > 0) p--;
+    if (p) {  /* (Give up if no spaces) */
+      print2("%s\n", left(ask1, p));
+      let(&ask1, right(ask1, p + 1));
+    }
+  }
+
+  printedLines = 0; /* Reset number of lines printed since last user input */
+  g_quitPrint = 0; /* Reset quit print flag */
+  localScrollMode = 1; /* Reset to prompted scroll */
+
+  while (1) {
+    if (g_commandFileNestingLevel == 0) {
+      commandLn = cmdInput(stdin, ask1);
+      if (!commandLn) {
+        commandLn = ""; /* Init vstring (was NULL) */
+        /* Allow ^D to exit */
+        if (strcmp(left(ask1, 2), "Do")) {
+          /* ^Z or ^D found at MM>, MM-PA>, or TOOLS> prompt */
+          let(&commandLn, "EXIT");
+        } else {
+          /* Detected the question "Do you want to EXIT anyway (Y, N) <N>?" */
+          /* Force exit with Y, to prevent infinite loop */
+          let(&commandLn, "Y");
+        }
+        printf("%s\n", commandLn); /* Let user see what's happening */
+      }
+      if (g_logFileOpenFlag) fprintf(g_logFilePtr, "%s%s\n", ask1, commandLn);
+
+      /* Clear backBuffer from previous scroll session */
+      for (i = 0; i < pntrLen(backBuffer); i++) {
+        free_vstring(*(vstring *)(&backBuffer[i]));
+      }
+      backBufferPos = 1;
+      free_pntrString(backBuffer);
+      pntrLet(&backBuffer, pntrAddElement(backBuffer));
+      /* Note: pntrAddElement() initializes the added element to the
+         empty string, so we don't need a separate initialization. */
+      /* backBuffer[backBufferPos - 1] = ""; */  /* already done */
+
+      /* Add user's typing to the backup buffer for display on 1st screen */
+      let((vstring *)(&(backBuffer[backBufferPos - 1])), cat(
+          (vstring)(backBuffer[backBufferPos - 1]), ask1,
+          commandLn, "\n", NULL));
+
+      if (g_listMode && g_listFile_fp != NULL) {
+        /* Put line in list.tmp as comment */
+        fprintf(g_listFile_fp, "! %s\n", commandLn);
+      }
+
+    } else { /* Get line from SUBMIT file */
+      commandLn = cmdInput(g_commandFilePtr[g_commandFileNestingLevel], NULL);
+      if (!commandLn) { /* EOF found */
+        fclose(g_commandFilePtr[g_commandFileNestingLevel]);
+        print2("%s[End of command file \"%s\".]\n", ask1,
+            g_commandFileName[g_commandFileNestingLevel]);
+        free_vstring(g_commandFileName[g_commandFileNestingLevel]);
+                                                        /* Deallocate string */
+        g_commandFileNestingLevel--;
+        commandLn = "";
+        if (g_commandFileNestingLevel == 0) {
+          g_commandFileSilentFlag = 0;
+        } else {
+          g_commandFileSilentFlag = g_commandFileSilent[g_commandFileNestingLevel];
+               /* Revert to previous nesting level's silent flag */
+        }
+        break; /*continue;*/
+      }
+
+      /* Tolerate CRs in SUBMIT files (e.g. created on Windows and
+         run on Linux) */
+      let(&commandLn, edit(commandLn, 8192/* remove CR */));
+
+      print2("%s%s\n", ask1, commandLn);
+    }
+    break;
+  }
+
+  free_vstring(ask1);
+  return commandLn;
+} /* cmdInput1 */
+
+
+void errorMessage(vstring line, long lineNum, long column, long tokenLength,
+  vstring error, vstring fileName, long statementNum, flag severity)
+{
+  /* Note:  "line" may be terminated with \n.  "error" and "fileName"
+     should NOT be terminated with \n.  This is done for the convenience
+     of the calling functions. */
+  vstring_def(errorPointer);
+  vstring_def(tmpStr);
+  vstring_def(prntStr);
+  vstring_def(line1);
+  int j;
+
+  /* Prevent putting error message in g_printString */
+  /* 9-Jun-2016 nm Revert this change, because 'minimize_with' makes
+     use of the string to hold the DV violation error message.
+     We can reinstate this fix when 'minimize_with' is improved to
+     call a DV-checking function directly. */
+  /*
+  saveOutputToString = g_outputToString;
+  g_outputToString = 0;
+  */
+
+  /* Make sure vstring argument doesn't get deallocated with another let */
+/*??? USE SAVETEMPALLOC*/
+  let(&tmpStr, error); /* error will get deallocated here */
+  error = "";
+  let(&error, tmpStr); /* permanently allocate error */
+
+  /* Add a newline to line1 if there is none */
+  if (line) {
+    if (line[strlen(line) - 1] != '\n') {
+      let(&line1, line);
+    } else {
+      bug(1509);
+    }
+  } else {
+    line1 = NULL;
+  }
+
+  if (fileName) {
+    /* Put a blank line between error msgs if we are parsing a file */
+    print2("\n");
+  }
+
+  switch (severity) {
+    case (char)notice_:
+      let(&prntStr, "?Notice"); break;
+    case (char)warning_:
+      let(&prntStr, "?Warning"); break;
+    case (char)error_:
+      let(&prntStr, "?Error"); break;
+    case (char)fatal_:
+      let(&prntStr, "?Fatal error"); break;
+  }
+  if (lineNum) {
+    let(&prntStr, cat(prntStr, " on line ", str((double)lineNum), NULL));
+    if (fileName) {
+      let(&prntStr, cat(prntStr, " of file \"", fileName, "\"", NULL));
+    }
+  } else {
+    if (fileName) {
+      let(&prntStr, cat(prntStr, " in file \"", fileName, "\"", NULL));
+    }
+  }
+  if (statementNum) {
+    let(&prntStr, cat(prntStr, " at statement ", str((double)statementNum), NULL));
+    if (g_Statement[statementNum].labelName[0]) {
+      let(&prntStr, cat(prntStr, ", label \"",
+          g_Statement[statementNum].labelName, "\"", NULL));
+    }
+    let(&prntStr, cat(prntStr, ", type \"$", chr(g_Statement[statementNum].type),
+        "\"", NULL));
+  }
+  printLongLine(cat(prntStr, ":", NULL), "", " ");
+  if (line1) printLongLine(line1, "", "");
+  if (line1 && column && tokenLength) {
+    free_vstring(errorPointer);
+    for (j=0; j<column-1; j++) {
+      /* Make sure that tabs on the line with the error are accounted for so
+         that the error pointer lines up correctly */
+      if (line1[j] == '\t') let (&errorPointer,cat(errorPointer,"\t",NULL));
+      else let(&errorPointer,cat(errorPointer," ",NULL));
+    }
+    for (j=0; j<tokenLength; j++)
+      let(&errorPointer,cat(errorPointer,"^",NULL));
+    printLongLine(errorPointer, "", "");
+  }
+  printLongLine(error,""," ");
+  if (severity == 2) g_errorCount++;
+
+  /* ???Should there be a limit? */
+  /* if (g_errorCount > 1000) {
+    print2("\n"); print2("?Too many errors - aborting Metamath.\n");
+    exit(0);
+  } */
+
+  /* Restore output to g_printString if it was enabled before */
+  /* 9-Jun-2016 nm Reverted */
+  /*
+  g_outputToString = saveOutputToString;
+  */
+
+  if (severity == 3) {
+    print2("Aborting Metamath.\n");
+    exit(0);
+  }
+  free_vstring(errorPointer);
+  free_vstring(tmpStr);
+  free_vstring(prntStr);
+  free_vstring(error);
+  if (line1) free_vstring(line1);
+} /* errorMessage() */
+
+
+
+
+/* Opens files with error message; opens output files with
+   backup of previous version.   Mode must be "r" or "w" or "d" (delete). */
+FILE *fSafeOpen(const char *fileName, const char *mode, flag noVersioningFlag) {
+  FILE *fp;
+  vstring_def(prefix);
+  vstring_def(postfix);
+  vstring_def(bakName);
+  vstring_def(newBakName);
+  long v;
+  long lastVersion; /* Last version before gap */
+
+  if (!strcmp(mode, "r")) {
+    fp = fopen(fileName, "r");
+    if (!fp) {
+      print2("?Sorry, couldn't open the file \"%s\".\n", fileName);
+    }
+    return fp;
+  }
+
+  if (!strcmp(mode, "w") || !strcmp(mode, "d")) {
+    if (noVersioningFlag) goto skip_backup;
+    /* See if the file already exists. */
+    fp = fopen(fileName, "r");
+
+    if (fp) {
+      fclose(fp);
+
+#define VERSIONS 9
+      /* The file exists.  Rename it. */
+
+#if defined __WATCOMC__ /* MSDOS */
+      /* Make sure file name before extension is 8 chars or less */
+      i = instr(1, fileName, ".");
+      if (i) {
+        let(&prefix, left(fileName, i - 1));
+        let(&postfix, right(fileName, i));
+      } else {
+        let(&prefix, fileName);
+        free_vstring(postfix);
+      }
+      let(&prefix, cat(left(prefix, 5), "~", NULL));
+      let(&postfix, cat("~", postfix, NULL));
+      if (0) goto skip_backup; /* Prevent compiler warning */
+
+#elif defined __GNUC__ /* Assume unix */
+      let(&prefix, cat(fileName, "~", NULL));
+      free_vstring(postfix);
+
+#elif defined VAXC /* Assume VMS */
+      /* For debugging on VMS: */
+      /* let(&prefix, cat(fileName, "-", NULL));
+         let(&postfix, "-"); */
+      /* Normal: */
+      goto skip_backup;
+
+#else /* Unknown; assume unix standard */
+      /*if (1) goto skip_backup;*/  /* [if no backup desired] */
+      let(&prefix, cat(fileName, "~", NULL));
+      free_vstring(postfix);
+
+#endif
+
+
+      /* See if the lowest version already exists. */
+      let(&bakName, cat(prefix, str(1), postfix, NULL));
+      fp = fopen(bakName, "r");
+      if (fp) {
+        fclose(fp);
+        /* The lowest version already exists; rename all to higher versions. */
+
+        /* Find last version before gap, if any */
+        lastVersion = 0;
+        for (v = 1; v <= VERSIONS; v++) {
+          let(&bakName, cat(prefix, str((double)v), postfix, NULL));
+          fp = fopen(bakName, "r");
+          if (!fp) break; /* Version gap found; skip rest of scan */
+          fclose(fp);
+          lastVersion = v;
+        }
+
+        /* If there are no gaps before version VERSIONS, delete it. */
+        if (lastVersion == VERSIONS) {
+          let(&bakName, cat(prefix, str((double)VERSIONS), postfix, NULL));
+          fp = fopen(bakName, "r");
+          if (fp) {
+            fclose(fp);
+            remove(bakName);
+          }
+          lastVersion--;
+        }
+
+        for (v = lastVersion; v >= 1; v--) {
+          let(&bakName, cat(prefix, str((double)v), postfix, NULL));
+          fp = fopen(bakName, "r");
+          if (!fp) continue;
+          fclose(fp);
+          let(&newBakName, cat(prefix, str((double)v + 1), postfix, NULL));
+          rename(bakName/*old*/, newBakName/*new*/);
+        }
+
+      }
+      let(&bakName, cat(prefix, str(1), postfix, NULL));
+      rename(fileName, bakName);
+
+      /***
+      printLongLine(cat("The file \"", fileName,
+          "\" already exists.  The old file is being renamed to \"",
+          bakName, "\".", NULL), "  ", " ");
+      ***/
+    } /* End if file already exists */
+   skip_backup:
+
+    if (!strcmp(mode, "w")) {
+      fp = fopen(fileName, "w");
+      if (!fp) {
+        print2("?Sorry, couldn't open the file \"%s\".\n", fileName);
+      }
+    } else {
+      if (strcmp(mode, "d")) {
+        bug(1526);
+      }
+      /* For "safe" delete, the file was renamed to ~1, so there is nothing
+         to do. */
+      fp = NULL;
+      /* For non-safe (noVersioning) delete, we actually delete */
+      if (noVersioningFlag) {
+        if(remove(fileName) != 0) {
+          print2("?Sorry, couldn't delete the file \"%s\".\n", fileName);
+        }
+      }
+    }
+
+    /* Deallocate local strings */
+    free_vstring(prefix);
+    free_vstring(postfix);
+    free_vstring(bakName);
+    free_vstring(newBakName);
+
+    return fp;
+  } /* End if mode = "w" or "d" */
+
+  bug(1510); /* Illegal mode */
+  return(NULL);
+
+} /* fSafeOpen() */
+
+
+/* Renames a file with backup of previous version.  If non-zero
+   is returned, there was an error. */
+int fSafeRename(const char *oldFileName, const char *newFileName) {
+  int error = 0;
+  int i;
+  FILE *fp;
+  /* Open the new file to force renaming of existing ones */
+  fp = fSafeOpen(newFileName, "w", 0/*noVersioningFlag*/);
+  if (!fp) error = -1;
+  /* Delete the file just created */
+  if (!error) {
+    error = fclose(fp);
+    if (error) print2("?Empty \"%s\" couldn't be closed.\n", newFileName);
+  }
+  if (!error) {
+    error = remove(newFileName);
+    /* On Windows 95, the first attempt may not succeed. */
+    if (error) {
+      for (i = 2; i < 1000; i++) {
+        error = remove(newFileName);
+        if (!error) break;
+      }
+      if (!error)
+        print2("OS WARNING: File delete succeeded only after %i attempts.", i);
+    }
+
+    if (error) print2("?Empty \"%s\" couldn't be deleted.\n", newFileName);
+  }
+  /* Rename the old one to it */
+  if (!error) {
+    error = rename(oldFileName, newFileName);
+    if (error) print2("?Rename of \"%s\" to \"%s\" failed.\n", oldFileName,
+        newFileName);
+  }
+  if (error) {
+    print2("?Sorry, couldn't rename the file \"%s\" to \"%s\".\n", oldFileName,
+        newFileName);
+    print2("\"%s\" may be empty; try recovering from \"%s\".\n", newFileName,
+        oldFileName);
+  }
+  return error;
+} /* fSafeRename */
+
+
+/* Finds the name of the first file of the form filePrefix +
+   nnn + ".tmp" that does not exist.  THE CALLER MUST DEALLOCATE
+   THE RETURNED STRING [i.e. assign function return directly
+   to a local empty vstring with = and not with let(), e.g.
+        free_vstring(str1);
+        str1 = fTmpName("zz~list");  ]
+   The file whose name is the returned string is not left open;
+   the caller must separately open the file. */
+vstring fGetTmpName(const char *filePrefix) {
+  FILE *fp;
+  vstring_def(fname);
+  static long counter = 0;
+  while (1) {
+    counter++;
+    let(&fname, cat(filePrefix, str((double)counter), ".tmp", NULL));
+    fp = fopen(fname, "r");
+    if (!fp) break;
+    fclose(fp);
+
+    if (counter > 1000) {
+      print2("?Warning: too many %snnn.tmp files - will reuse %s\n",
+          filePrefix, fname);
+      break;
+    }
+  }
+  return fname; /* Caller must deallocate! */
+} /* fGetTmpName() */
+
+
+/* This function returns a character string containing the entire contents of
+   an ASCII file, or Unicode file with only ASCII characters.   On some
+   systems it is faster than reading the file line by line.  The caller
+   must deallocate the returned string.  If a NULL is returned, the file
+   could not be opened or had a non-ASCII Unicode character or some other
+   problem.   If verbose is 0, error and warning messages are suppressed. */
+vstring readFileToString(const char *fileName, char verbose, long *charCount) {
+  FILE *inputFp;
+  long fileBufSize;
+  char *fileBuf;
+  long i, j;
+
+  /* Find out the upper limit of the number of characters in the file. */
+  /* Do this by opening the file in binary and seeking to the end. */
+  inputFp = fopen(fileName, "rb");
+  if (!inputFp) {
+    if (verbose) print2("?Sorry, couldn't open the file \"%s\".\n", fileName);
+    return NULL;
+  }
+#ifndef SEEK_END
+/* An older GCC compiler didn't have this ANSI standard constant defined. */
+#define SEEK_END 2
+#endif
+  if (fseek(inputFp, 0, SEEK_END)) { /* fseek returns non-zero on error */
+    /* Error message which occurs if input "file" is a piped stream */
+    if (verbose) print2(
+        "?Sorry, \"%s\" doesn't seem to be a regular file.\n",
+        fileName);
+    return NULL;
+  }
+  fileBufSize = ftell(inputFp);
+
+  /* Close and reopen the input file in text mode */
+  /* Text mode is needed for VAX, DOS, etc. with non-Unix end-of-lines */
+  fclose(inputFp);
+  inputFp = fopen(fileName, "r");
+  if (!inputFp) bug(1512);
+
+  /* Allocate space for the entire input file */
+  fileBufSize = fileBufSize + 10;
+            /* Add a factor for unknown text formats (just a guess) */
+  fileBuf = malloc((size_t)fileBufSize);
+  if (!fileBuf) {
+    if (verbose) print2(
+        "?Sorry, there was not enough memory to read the file \"%s\".\n",
+        fileName);
+    fclose(inputFp);
+    return NULL;
+  }
+
+  /* Put the entire input file into the buffer as a giant character string */
+  (* charCount) = (long)fread(fileBuf, sizeof(char), (size_t)fileBufSize - 2,
+      inputFp);
+  if (!feof(inputFp)) {
+    print2("Note:  This bug will occur if there is a disk file read error.\n");
+    /* If this bug occurs (due to obscure future format such as compressed
+       text files) we'll have to add a realloc statement. */
+    bug(1513);
+  }
+  fclose(inputFp);
+
+  fileBuf[(*charCount)] = 0;
+
+  /* See if it's Unicode */
+  /* This only handles the case where all chars are in the ASCII subset */
+  if ((*charCount) > 1) {
+    if (fileBuf[0] == '\377' && fileBuf[1] == '\376') {
+      /* Yes, so strip out null high-order bytes */
+      if (2 * ((*charCount) / 2) != (*charCount)) {
+        if (verbose) print2(
+"?Sorry, there are an odd number of characters (%ld) %s \"%s\".\n",
+            (*charCount), "in Unicode file", fileName);
+        free(fileBuf);
+        return NULL;
+      }
+      i = 0; /* ASCII character position */
+      j = 2; /* Unicode character position */
+      while (j < (*charCount)) {
+        if (fileBuf[j + 1] != 0) {
+          if (verbose) print2(
+              "?Sorry, the Unicode file \"%s\" %s %ld at byte %ld.\n",
+              fileName, "has a non-ASCII \ncharacter code",
+              (long)(fileBuf[j]) + ((long)(fileBuf[j + 1]) * 256), j);
+          free(fileBuf);
+          return NULL;
+        }
+        if (fileBuf[j] == 0) {
+          if (verbose) print2(
+              "?Sorry, the Unicode file \"%s\" %s at byte %ld.\n",
+              fileName, "has a null character", j);
+          free(fileBuf);
+          return NULL;
+        }
+        fileBuf[i] = fileBuf[j];
+        /* Suppress any carriage-returns */
+        if (fileBuf[i] == '\r') {
+          i--;
+        }
+        i++;
+        j = j + 2;
+      }
+      fileBuf[i] = 0; /* ASCII string terminator */
+      (*charCount) = i;
+    }
+  }
+
+  /* Make sure the file has no carriage-returns */
+  if (strchr(fileBuf, '\r') != NULL) {
+    if (verbose) print2(
+       "?Warning: the file \"%s\" has carriage-returns.  Cleaning them up...\n",
+        fileName);
+    /* Clean up the file, e.g. DOS or Mac file on Unix */
+    i = 0;
+    j = 0;
+    while (j <= (*charCount)) {
+      if (fileBuf[j] == '\r') {
+        if (fileBuf[j + 1] == '\n') {
+          /* DOS file - skip '\r' */
+          j++;
+        } else {
+          /* Mac file - change '\r' to '\n' */
+          fileBuf[j] = '\n';
+        }
+      }
+      fileBuf[i] = fileBuf[j];
+      i++;
+      j++;
+    }
+    (*charCount) = i - 1;
+  }
+
+  /* Make sure the last line is not a partial line */
+  if (fileBuf[(*charCount) - 1] != '\n') {
+    if (verbose) print2(
+        "?Warning: the last line in file \"%s\" is incomplete.\n",
+        fileName);
+    /* Add the end-of-line */
+    fileBuf[(*charCount)] = '\n';
+    (*charCount)++;
+    fileBuf[(*charCount)] = 0;
+  }
+
+  if (fileBuf[(*charCount)] != 0) {
+    bug(1522); /* Keeping track of charCount went wrong somewhere */
+  }
+
+  /* Make sure there aren't null characters */
+  i = (long)strlen(fileBuf);
+  if ((*charCount) != i) {
+    if (verbose) {
+      print2(
+          "?Warning: the file \"%s\" is not an ASCII file.\n",
+          fileName);
+      print2(
+          "Its size is %ld characters with null at character %ld.\n",
+          (*charCount), strlen(fileBuf));
+    }
+  }
+/*E*/db = db + i;  /* For memory usage tracking (ignore stuff after null) */
+
+  /******* For debugging
+  print2("In binary mode the file has %ld bytes.\n", fileBufSize - 10);
+  print2("In text mode the file has %ld bytes.\n", (*charCount));
+  *******/
+
+  return (char *)fileBuf;
+} /* readFileToString */
+
+
+/* Returns total elapsed time in seconds since starting session (for the
+   lcc compiler) or the CPU time used (for the gcc compiler).  The
+   argument is assigned the time since the last call to this function. */
+double getRunTime(double *timeSinceLastCall) {
+#ifdef CLOCKS_PER_SEC
+  static clock_t timePrevious = 0;
+  clock_t timeNow;
+  timeNow = clock();
+  *timeSinceLastCall = (double)((1.0 * (double)(timeNow - timePrevious))
+          /CLOCKS_PER_SEC);
+  timePrevious = timeNow;
+  return (double)((1.0 * (double)timeNow)/CLOCKS_PER_SEC);
+  /* Example of printing double format: */
+  /* print2("Total elapsed time = %4.2f s\n", t) */
+#else
+  print2("The clock() function is not implemented on this computer.\n");
+  *timeSinceLastCall = 0;
+  return 0;
+#endif
+}
+
+
+void freeInOu() {
+  long i, j;
+  j = pntrLen(backBuffer);
+  for (i = 0; i < j; i++) free_vstring(*(vstring *)(&backBuffer[i]));
+  free_pntrString(backBuffer);
+}
diff --git a/src/mminou.h b/src/mminou.h
new file mode 100644
index 0000000..d6a5610
--- /dev/null
+++ b/src/mminou.h
@@ -0,0 +1,671 @@
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMINOU_H_
+#define METAMATH_MMINOU_H_
+
+/*!
+ * \file mminou.h
+ * \brief Basic input and output interface.
+ */
+
+#include <stdio.h>
+
+#include "mmvstr.h"
+#include "mmdata.h"
+
+extern int g_errorCount;     /*!< Total error count */
+
+/* Global variables used by print2() */
+
+/*!
+ * \var flag g_logFileOpenFlag
+ * If set to 1, logging of input is enabled.  Initially set to 0.
+ */
+extern flag g_logFileOpenFlag;
+
+/*!
+ * \var FILE *g_logFilePtr
+ * The OPEN LOG command opens a log file.  Its file descriptor is stored here,
+ * and not removed, when the log file is closed.  You should access this
+ * descriptor only when \ref g_logFileOpenFlag is 1.
+ */
+extern FILE *g_logFilePtr;
+
+extern FILE *g_listFile_fp;
+
+/*!
+ * \var g_outputToString
+ *
+ * Global variable redirecting the output of the function print2 from the
+ * console ( = 0) to a string ( = 1).  Initialized to 0 on program start.
+ */
+extern flag g_outputToString;
+
+/*!
+ * \var vstring g_printString
+ * If output is redirected to a string by \ref g_outputToString, this variable
+ * receives the contents.
+ */
+extern vstring g_printString;
+
+
+/* Global variables used by cmdInput() */
+
+/*!
+ * \def MAX_COMMAND_FILE_NESTING
+ * limits number of nested SUBMIT calls.  A SUBMIT redirects the input to a
+ * file, which in turn may temporarily redirect input to another file, and so
+ * on.
+ */
+#define MAX_COMMAND_FILE_NESTING 10
+
+/*!
+ * \var long g_commandFileNestingLevel
+ * current level of nested SUBMIT commands.  0 is top level and refers to stdin
+ * (usually the user controlled command line).  Any invocation of SUBMIT
+ * increases this value by 1.  A return from a SUBMIT decrases it by 1 again.
+ * Limited by \ref MAX_COMMAND_FILE_NESTING.
+ */
+extern long g_commandFileNestingLevel;
+
+/*!
+ * \var FILE *g_commandFilePtr[MAX_COMMAND_FILE_NESTING + 1]
+ * file descriptors pointing to files invoked by SUBMIT commands.  The 0-th
+ * element is fixed to stdin, and neither used nor assigned to.  Other
+ * elements, up to the current value of \ref g_commandFileNestingLevel, are
+ * valid, and point to opened files.
+ */
+extern FILE *g_commandFilePtr[MAX_COMMAND_FILE_NESTING + 1];
+
+/*!
+ * \var vstring g_commandFileName[]
+ * list of command file names in nested SUBMIT commands.  This name need not be
+ * fully qualified (with all directories down from the root directory).  The
+ * first element is reserved for stdin and never set.
+ */
+extern vstring g_commandFileName[MAX_COMMAND_FILE_NESTING + 1];
+
+extern flag g_commandFileSilent[MAX_COMMAND_FILE_NESTING + 1];
+
+/*!
+ * \var g_commandFileSilentFlag
+ * If set to 1, suppresses prompts on input.  Activated through
+ * SUBMIT ... /SILENT commands.  Initialized to 0 on program start.
+ */
+extern flag g_commandFileSilentFlag; /* For SUBMIT ... /SILENT */
+
+
+extern FILE *g_input_fp;  /*!< File pointers */
+extern vstring g_input_fn, g_output_fn;  /*!< File names */
+
+/*!
+ * \fn flag print2(const char* fmt,...)
+ * \brief formatted output of a single line with optional page-wise display.
+ *
+ * It is important to understand that the submitted parameters result in a
+ * NUL-terminated string that contains at most one LF character, only allowed
+ * in final position.  A long line exceeding \ref g_screenWidth is broken down
+ * into multiple lines using a built-in line wrap algorithm.  But this must
+ * never be preempted by preparing parameters accordingly.
+ *
+ * The presence of the LF character in a line shorter than \ref g_screenWidth
+ * controls whether the current screen line is closed after print, or output in
+ * a later call to print2 is padded right.  Output of more characters than this
+ * limit always closes the screen line.
+ *
+ * Although the output of a single line is the main goal of this function, it
+ * does a lot on the side, each effect individually enabled or disabled by
+ * various global variables that are honored, sometimes even updated.  We skim
+ * through these effects here in short:
+ *
+ * - (a) __Data embedding__.  The \p fmt parameter can contain simple text,
+ *   that is displayed as is.  Or embedded placeholders are replaced with data
+ *   pointed to by the following parameters.  If necessary, data are converted
+ *   to strings before insertion.
+ * - (b) Supporting __page-wise display__.  Output on the virtual text display
+ *   is stopped after showing \ref g_screenHeight lines, called a __page__
+ *   here.  The user can read the text at rest, and then request resuming
+ *   output with a short command.  In fact, this feature is enhanced by
+ *   maintaining a \ref pgBackBuffer, allowing the user to recall previously
+ *   shown pages, and navigate them freely forward and backwards.  This feature
+ *   temporarily grabs user input and interprets short commands as navigating
+ *   instructions.  For more see \ref pgBackBuffer.
+ * - (c) __Line wrapping__.  When the line to display exceeds the limits in
+ *   \ref g_screenWidth, line breaking is applied.  The wrapping is actually
+ *   done in \ref printLongLine which in turn uses separate print2 calls to
+ *   output each individual line.  This requires a minimum synchronization of
+ *   both the outer and inner calls, so that relevant state is carried back to
+ *   the outer, temporarily suspended call.
+ * - (d) __Redirection__.  Instead of displaying output on the virtual text
+ *   display, it may be stored in a NUL-terminated string variable.  Or even
+ *   completely suppressed, for example when executing a SUBMIT.
+ * - (e) __Logging__.  Output may be logged to a file for examination.
+ *
+ * These effects need not be carried out in this order, some may even be
+ * omitted.  We show the order of steps and their respective conditions here.
+ *
+ * 1. The \ref backBuffer is almost private to this function, so its
+ *     initialization is done here, right at the beginning.  The
+ *     \ref backBufferPos is always at least 1, so a value of 0 indicates an
+ *     outstanding \ref backBuffer memory allocation, and an empty string is
+ *     pushed as a first (guard) page onto it (see \ref pgBackBuffer).
+ *
+ *     \ref g_pntrTempAllocStackTop may be reset to
+ *     \ref g_pntrStartTempAllocStack.
+ *
+ * 2. If the current page is full and further output would overflow it, as
+ *     indicated by \ref printedLines, output may be suspended as described in
+ *     (b) and the user is prompted for scrolling actions.\n
+ *     This step is unconditionally executed when \ref backFromCmdInput = 1
+ *     (\ref cmdInput explicitly requested it).  The values in
+ *     \ref g_quitPrint and \ref localScrollMode are retained then, regardless
+ *     of user input.
+ *
+ *     Other conditions prevent this step:  Output is discarded on user request
+ *     (\ref g_quitPrint = 1), a SUBMIT call is running
+ *     (\ref g_commandFileNestingLevel > 0), scrolling is generally
+ *     (\ref g_scrollMode = 0) or temporarily (\ref localScrollMode = 0)
+ *     disabled, output is redirected (\ref g_outputToString = 1).
+ *
+ *     This step can set \ref g_quitPrint to 1 (if \ref backFromCmdInput = 0).
+ *     It can modify \ref backBufferPos (by command _q_ or _Q_) and set
+ *     \ref localScrollMode = 0 (by command _s_ or _S_, and
+ *     \ref backFromCmdInput = 0).
+ *
+ * 3. If pending output would overflow the screen, \ref backBuffer is extended
+ *     by a new page to receive pending output.
+ *
+ *     Several conditions can prevent this step:  Step (2) was not executed,
+ *     output is discarded on user request (\ref g_quitPrint = 1),
+ *     \ref backFromCmdInput = 1 (\ref cmdInput needs the scrolling loop
+ *     only).
+ *
+ *     Sets \ref printedLines to 0, indicating that nothing has been added to
+ *     the new page yet. Increments \ref backBufferPos so all pending output is
+ *     copied to the new page.  \ref g_pntrTempAllocStackTop may be reset to
+ *     \ref g_pntrStartTempAllocStack.
+ *
+ * 4. The function parameters are used to create a new line.  Placeholders in
+ *     \p fmt (see its description for details) are replaced with their
+ *     respective data values, converted to text  All data is first stored in
+ *     an internal buffer.
+ *
+ *     Some contexts prevent this step: Output (if to screen) is discarded on
+ *     user request (\ref g_quitPrint = 1 and \ref g_outputToString = 0),
+ *     \ref backFromCmdInput = 1 (\ref cmdInput uses only the scrolling
+ *     features)
+ *
+ * 5. Revert any \ref QUOTED_SPACE back to space characters in the string
+ *     produced in step (4) (\ref printLongLine might have introduced these
+ *     characters to prevent line breaks at certain space characters).  For
+ *     this to work correctly, \ref QUOTED_SPACE must not be part of the
+ *     regular output. (Doing so will trigger a bug check in
+ *     \ref printLongLine .)
+ *
+ *     Some contexts prevent this step: Output (if to screen) is discarded on
+ *     user request (\ref g_quitPrint = 1 and \ref g_outputToString = 0),
+ *     \ref backFromCmdInput = 1 (\ref cmdInput uses only the scrolling
+ *     features)
+ *
+ * 6. Perform line wrapping.  The wrapping is actually done in
+ *     \ref printLongLine, which in turn calls this function to handle each
+ *     broken down line separately.  The output generated in step (4) is copied
+ *     onto \ref tempAllocStack, omitting a trailing LF.
+ *
+ *     Some contexts prevent this step: The output from step (4) (excluding a
+ *     trailing LF) does not exceed \ref g_screenWidth, output is discarded on
+ *     user request (\ref g_quitPrint = 1), output is redirected to a string
+ *     (\ref g_outputToString = 1), \ref backFromCmdInput = 1
+ *     (\ref cmdInput uses only the scrolling features)
+ *
+ *      __todo__ clarify variants/invariants
+ *
+ * 7. Print the prepared output onto the screen.
+ *
+ *     Some contexts prevent this step: Step (6) (line wrapping) was executed,
+ *     output is discarded on user request (\ref g_quitPrint = 1)
+ *     output is redirected to a string (\ref g_outputToString = 1), a SUBMIT
+ *     is silently executing (\ref g_commandFileSilentFlag = 1),
+ *     \ref backFromCmdInput = 1 (\ref cmdInput uses only the scrolling
+ *     features)
+ *
+ *     \ref printedLines is increased if the prepared output terminates with
+ *     LF.
+ *
+ * 8. Add a new history page in \ref backBuffer for the output generated in
+ *     step (4) if the current page has overflowed.  Do this even when scrolling
+ *     is disabled.
+ *
+ *     Some contexts prevent this step: Step (7) was not executed,
+ *     \ref printedLines <= \ref g_screenHeight allows another line of output,
+ *     the output printed in step (7) has no trailing LF and is appended to the
+ *     last line of output, a SUBMIT is silently executing
+ *     (\ref g_commandFileSilentFlag = 1), scrolling is enabled
+ *     (\ref g_scrollMode = 1, step (c) handles this case).
+ *
+ *     \ref printedLines is set to 1.  \ref backBufferPos is increased.
+ *     \ref g_pntrTempAllocStackTop may be reset to
+ *     \ref g_pntrStartTempAllocStack.
+ *
+ * 9. Add the output to the \ref backBuffer.
+ *
+ *     Some contexts prevent this step: Step (7) was not executed.
+ *
+ *     \ref g_tempAllocStackPos is reset to \ref g_startTempAllocStack.
+ *
+ * 10. Log the output to a file given by \ref g_logFilePtr.
+ *
+ *     Some contexts prevent this step: Step (4) was not executed, a log file
+ *     is not opened (\ref g_logFileOpenFlag = 0), output is redirected to a string
+ *     (\ref g_outputToString = 1), \ref backFromCmdInput = 1 (\ref cmdInput
+ *     uses only the scrolling features)
+ *
+ *     \ref g_tempAllocStackPos is reset to \ref g_startTempAllocStack.
+ *
+ * 11. Copy the prepared output in step (4) to \ref g_printString.
+ *
+ *     Some contexts prevent this step: Step (4) was not executed, output is
+ *     not redirected to a string (\ref g_outputToString = 0),
+ *     \ref backFromCmdInput = 1 (\ref cmdInput uses only the scrolling
+ *     features)
+ * .
+ *
+ * \param[in] fmt (not null) NUL-terminated text to display with embedded
+ *   placeholders for insertion of data (which are converted into text if
+ *   necessary) pointed to by the following parameters.  The format string uses
+ *   the same syntax as 
+ *   <a href="https://en.cppreference.com/w/c/io/fprintf">[printf]</a>.  A LF
+ *   character must only be in final position, and ETX (0x03) is not allowed.
+ *   This parameter is ignored when \ref backFromCmdInput is 1.
+ * \param[in] "..." The data these (possibly empty sequence of) pointers refer
+ *   to are converted to string and then inserted at the respective placeholder
+ *   position in \p fmt.  They should strictly match in number, type and order
+ *   the placeholders.  The characters LF and ETX (0x03) must not be produced,
+ *   with the exemption that an LF is possible if it appers at the end of the
+ *   expanded text (see (a)).
+ *   These parameters are ignored when \ref backFromCmdInput is 1.
+ * \return \ref g_quitPrint 0: user has quit the printout.
+ * \pre
+ *   - \ref printedLines if indicating a full page of output was reached,
+ *     activates __step 2__ if not inhibited by other variables.
+ *   - \ref g_screenHeight number of lines to display (a page of output) to a
+ *     user without need of  __step 2__.
+ *   - \ref g_screenWidth if the expanded text exceeds this width, line
+ *     breaking may be required.  Other settings can still prevent this;
+ *   - \ref g_quitPrint value 1:  Do not enter __step 2__ and suppress output
+ *     to the (virtual) text display;
+ *   - \ref backFromCmdInput value 1: assume the last entry in \ref backBuffer
+ *     was just printed, \ref backBufferPos points to the entry before the
+ *     last.  All steps but __1__ and __2__ are disabled, and __step 2__ is
+ *     enforced, regardless of other settings.  No output apart from replaying
+ *     saved pages in the \ref backBuffer is generated.
+ *     This flag is set by \ref cmdInput only;
+ *   - \ref g_commandFileSilentFlag value 1 suppresses output on the screen;
+ *   - \ref g_commandFileNestingLevel a value > 0 indicates a SUBMIT call is
+ *     executing, where __step 2__ is disabled, unless
+ *     \ref backFromCmdInput is 1;
+ *   - \ref g_scrollMode value 0 disables __step 2__, unless
+ *     \ref backFromCmdInput is 1;
+ *   - \ref localScrollMode value 0 disables __step 2__, unless
+ *     \ref backFromCmdInput is 1;
+ *   - \ref g_outputToString value 1 output is redirected and __step 2__ is
+ *     disabled, unless \ref backFromCmdInput is 1.
+ * \post
+ *   - \ref g_quitPrint is set to 1, if the user entered _q_ or _Q_ in
+ *      __step 2__, and \ref backFromCmdInput is 0.
+ *   - \ref backBuffer is allocated and not empty (at least filled with an
+ *     empty string)
+ *   - \ref backBufferPos > 0, updated
+ *   - \ref localScrollMode = 0 if the user entered _s_ or _S_ in __step 2__.
+ *   - \ref g_printString receives the output if \ref g_outputToString = 1.
+ *   - \ref printedLines updated
+ *   - \ref g_pntrTempAllocStackTop may be reset to
+ *     \ref g_pntrStartTempAllocStack.
+ *     \ref g_tempAllocStackPos may be reset to \ref g_startTempAllocStack.
+ * \bug It is possible to produce lines exceeding \ref g_screenWidth by
+ *   concatenating substrings smaller than this value, but having no LF at the
+ *   end.
+ * \warning never call print2 with string longer than PRINTBUFFERSIZE - 1
+ */
+flag print2(const char* fmt,...);
+
+/*!
+ * \var long g_screenHeight
+ * Number of lines the (virtual) text display can display to the user at the
+ * same time, apart from an extra line set aside for a prompt and echo of user
+ * input.  The command SET HEIGHT changes this value.
+ *
+ * This value equals the number of lines in a page of output.  After a page
+ * output is interrupted to let the user read the contents and request
+ * resuming.
+ */
+extern long g_screenHeight;
+
+/*!
+ * \var long g_screenWidth
+ * \brief Width of screen
+ *
+ * The minimum width of the (virtual) text display, measured in fixed width
+ * characters, minus 1.  The command SET WIDTH changes this value.
+ */
+extern long g_screenWidth;
+
+/*!
+ * \def MAX_LEN
+ * \brief Default width of screen
+ * The default setting of \ref g_screenWidth on program start.
+ *
+ * Number of characters that can always be displayed in a single line.  This
+ * notion is reminiscent of CRT tubes with a fixed width character set.
+ * Graphical Displays on a notebook for example can display much more, but on
+ * some mobile devices this may be reduced to 30-40 characters.
+ */
+#define MAX_LEN 79
+
+/*!
+ * \def SCREEN_HEIGHT
+ * \brief Default height of screen
+ * The default setting of \ref g_screenHeight on program start.
+ *
+ * This notion is reminiscent of CRT tubes with a fixed width character set.
+ * Graphical Displays on a notebook for example can display much more.
+ */
+#define SCREEN_HEIGHT 23
+
+/*!
+ * \var flag g_scrollMode
+ * \brief controls whether output stops after a full page is printed.
+ *
+ * A value of 1 indicates the user wants prompted page wise output.
+ * The command SET SCROLL controls this value.  If followed by CONTINUOUS, this
+ * flag is reset to 0.
+ */
+extern flag g_scrollMode;
+
+/*!
+ * \var flag g_quitPrint
+ * \brief Flag that user typed 'q' to last scrolling prompt
+ * The value 1 indicates the user requested immediately discarding pending
+ * output, and return back to normal command input.
+ *
+ * This flag is set in \ref print2 when the user inputs q or Q.
+ */
+extern flag g_quitPrint;
+
+/*!
+ * \fn void printLongLine(const char *line, const char *startNextLine, const char *breakMatch)
+ * \brief perform line wrapping and print
+ * apply a line wrapping algorithm to fit a text into the screen rectangle
+ * defined by \ref g_screenWidth and \ref g_screenHeight.
+ * Submit each individual broken down line to \ref print2 for output.  All
+ * flags and data controlling \ref print2 are in effect.
+ *
+ * printLongLine automatically pads a LF character to the right of \p line, if
+ * necessary, and honors interspersed LF characters.  Even though, each
+ * individual line might need further breakdown.  The maximal line length is
+ * not limited by the general \p g_screenWidth + 1 only, but may be further
+ * reduced by leading or trailing prefix or suffix text.
+ *
+ * The following prefixes or suffixes are possible, and are applied to lines
+ * too wide for the virtual text display:
+ * - (a) reserve the last column for a trailing ~ (0x7E) that appears at the
+ *        end of each line needing a break down, and is left blank on the last
+ *        one.  Each follow-up line is indented by a space (SP, 0x20).
+ * - (b) a trailing % (0x25) is padded to the right at each line that needs to
+ *        be continued on following lines.
+ *
+ * Methods of breaking a long line not containing a LF character:
+ *
+ * 1. __Break at any character__.  The \p line is broken into equally sized
+ *    pieces (\ref g_screenWidth + 1, reduced by \p startNextLine), except for
+ *    the first and last line.  The first one is not reduced by
+ *    \p startNextLine, and may receive more characters than the following
+ *    ones, the last simply takes the rest.  This method is not UTF-8 safe.
+ *
+ * \param[in] line (not null) NUL-terminated text to apply line wrapping to.
+ *    If the text contains LF characters, line breaks are enforced at these
+ *    positions.
+ * \param[in] startNextLine (not null) NUL-terminated string to place before
+ *   continuation lines.  If this prefix leaves not at least 4 characters for
+ *   regular output on a screen line (\ref g_screenWidth), it is chopped
+ *   accordingly.
+ *
+ *   The following characters in first position trigger a special mode:
+ *     ~ (0x7E) all broken down lines end on a ~ character.  The rest of this
+ *       parameter is ignored, a single space will be used as a prefix.
+ * \param[in] breakMatch (not null) NUL-terminated list of characters at which
+ *   the line can be broken.  If empty, a break is possible anywhere
+ *   (method 1.).
+ *
+ *   The following characters in first position allow line breaks at spaces (SP), but
+ *   trigger in addition special modes:
+ *     SOH (0x01) nested tree display (right justify continuation lines);
+ *     " (0x22) a string in quotes (") immediately following a = is NOT broken
+ *        at space characters, even if this produces a line longer than
+ *        \ref g_screenWidth.  You cannot escape a quote character within such
+ *        a quote.  For use in HTML code.
+ *     \ (0x5C) pad each line that is continued with a % character  to the
+ *       right (LaTeX support).
+ * \post
+ *   \ref tempAllocStack is cleared down to \ref g_startTempAllocStack.
+ * \warning not UTF-8 safe.
+ */
+void printLongLine(const char *line, const char *startNextLine, const char *breakMatch);
+
+/*!
+ * \brief requests a line of text from the \p stream.
+ *
+ * If not suppressed, displays a prompt text on the screen.  Then reads a
+ * line from the \p stream.  Some lines are interpreted as described further
+ * below, in which case the prompt is reprinted and the next line is read.
+ * Returns the first not interpreted line as a \ref vstring.
+ *
+ * A line in the \p stream is terminated by a LF character (0x0D) character
+ * alone.  It is read, but removed from the result.  The maximum line length
+ * without the LF is \ref CMD_BUFFER_SIZE - 1.  Reaching EOF (end of file,
+ * CTRL-D) is equivalent to reading LF, if at least 1 byte was read before.
+ * Note that the NUL character can be part of the line.  Reading a NUL is not
+ * sufficiently supported in the current implementation and may or may not
+ * cause an error message or even undefined behavior.
+ *
+ * Reading from an empty \p stream (or one that is at EOF position, on the
+ * console CTRL-D) returns NULL, not the empty string, and is formally
+ * signalled as an error.  Overflowing the buffer is also an error.  No
+ * truncated value is returned.
+ *
+ * This routine interprets some input without returning it to the caller under
+ * following two conditions:
+ *
+ * 1. If scrolling is enabled, the input is interpreted.  A line consisting of
+ * a single character b or B indicates the user wants to scroll back through
+ * saved pages of output.  This is handled within this routine, as often as
+ * requested and possible.
+ *
+ * 2. The user hits ENTER only while prompted in top level context.  The empty
+ * line is not returned.
+ *
+ * No timeout is applied while waiting for user input from the console.
+ *
+ * A bug message need not result in an execution stop.  It is not directed to
+ * the metamath bug function to avoid stacking up calls (bug calling cmdInput
+ * again for scrolling etc.).
+ *
+ * \param[in] stream (not null) source to read the line from.  _stdin_ is
+ *   common for user input from the console.
+ * \param[in] ask prompt text displayed on the screen before \p stream is
+ *   read.  This prompt is suppressed by either a NULL value, or setting
+ *   \ref g_commandFileSilentFlag to 1.  This prompt must be not NULL (empty is
+ *   fine!) outside of a SUBMIT call, where user is expected to enter input.
+ *   \n
+ *   It may be compared to \ref g_commandPrompt.  If both match, it is inferred
+ *   the user is in top level command mode, where empty input is not returned
+ *   to the caller.
+ * \return a \ref vstring containing the first read and not interpreted line.
+ *   NULL indicates an error condition.  The result needs to be deallocated by
+ *   the caller, if not empty or NULL.
+ * \pre
+ *   The following variables are honored during execution and should be properly
+ *   set:
+ *   - \ref g_commandFileSilentFlag value 1 suppresses all prompts, not only
+ *     those used for scrolling through long text.  It does not suppress error
+ *     messages;
+ *   - \ref g_commandFileNestingLevel a value > 0 indicates a SUBMIT call is
+ *     executing, a read line is returned as is.  0 is seen as an interactive
+ *     mode, where read lines can be interpreted;
+ *   - \ref g_outputToString value 1 output is redirected and scrolling is
+ *     disabled
+ *   - \ref backBuffer may contain text to display on scroll back operations;
+ *   - \ref g_scrollMode value 1 enables scrolling back through text held in
+ *     \ref backBuffer;
+ *   - \ref localScrollMode a value of 0 temporarily disables scrolling,
+ *     overriding the setting in \ref g_scrollMode;
+ *   - \ref g_commandPrompt if this string matches ask, top level user input is
+ *     assumed, where empty lines are discarded.
+ * \post
+ *   \ref db is updated.
+ * \warning the calling program must deallocate the returned string (if not
+ *   null or empty).  Note that the result can be NULL.  This is outside of the
+ *   usual behavior of a \ref vstring type.
+ * \warning the returned string need not be valid ASCII or UTF-8.
+ * \bug If a character read from \p stream is NUL, this may sometimes cause a
+ *   print of an error message, but execution continues and in the wake may
+ *   cause all kind of undefined behavior, like memory accesses beyond
+ *   allocated buffers.
+ */
+vstring cmdInput(FILE *stream, const char *ask);
+
+/*!
+ * \brief print prompt (or explanatory) text and then read a line.
+ *
+ * After a prompt text is printed, gets a line from either stdin or the
+ * command file stream in \ref g_commandFilePtr, depending on the value of
+ * \ref g_commandFileNestingLevel.  If this value is 0, interactive input via
+ * stdin is assumed, else non interpreted lines are read from a file in submit
+ * mode.  The line returned to the caller is more or less what \ref cmdInput()
+ * yields, but some fine tuning is applied.
+ *
+ * \par Displaying the prompt text
+ *
+ * This function is prepared to display a longer text, before issuing a final
+ * prompt line (unlike \ref cmdInput).  The text shown to the user is usually
+ * wrapped around preferably at spaces to fit into a display of width
+ * \ref g_screenWidth.  If possible, wrapping shortens the last line such that
+ * space for 10 characters is left to the right for user input.
+ *
+ * \par Interactive Mode
+ *
+ * 1. A long prompt text may be interrupted for convenient page wise display.
+ * The user's scroll commands are interpreted internally and not seen by the
+ * caller.  If a line is either discarded or interpreted, the user is prompted
+ * again. The full prompt text is never repeated, only its last line after
+ * wrapping was applied.
+ *
+ * 2. Empty lines are discarded, and a reprompt is triggered.
+ *
+ * 3. A NULL resulting from an error (buffer overflow) or a premature EOF
+ * (CTRL_D from keyboard) from \ref cmdInput is either returned as "EXIT".  Or
+ * if the last line of the prompt starts with "Do", then it is assumed to
+ * expand to "Do you want to EXIT anyway (Y, N)?" and a "Y" is returned. In any
+ * case, the returned string is printed before it may finally trigger an
+ * immediate stop on the caller's side.
+ *
+ * 4. If logging is enabled, prompt and returned input is logged.
+ *
+ * \par Submit Mode
+ *
+ * 1. a non-interpreted line is read from the appropriate entry in
+ * \ref g_commandFilePtr by \ref cmdInput.
+ *
+ * 2. If NULL is returned, reaching EOF is assumed, the file is closed, its
+ * name in \ref g_commandFileName deallocated and the previous
+ * \ref g_commandFileNestingLevel is activated.  In this particular case the
+ * read line is the empty string.  A message indicating the end of the command
+ * file is printed.  The \ref g_commandFileSilentFlag controlling console
+ * output is copied from the appropriate entry of \ref g_commandFileSilent,
+ * unless the interactive mode is reached; here output is never suppressed
+ * (value 0).
+ *
+ * 3. remove all CR (0x0D) characters, not only those in combination with LF.
+ *
+ * 4. prompt and command is printed, if not suppressed, then the read line is
+ * returned.
+ *
+ * \return first not interpreted line as \ref vstring, or "EXIT" on error.
+ * \pre
+ *   The following variables are honored during execution and should be properly
+ *   set:
+ *   - \ref g_quitPrint a 1 suppresses wrapping and user controlled paging of
+ *     the prompt text.
+ *   - \ref g_commandFileSilentFlag value 1 suppresses output and prompts, but
+ *     not all error messages;
+ *   - \ref g_commandFileNestingLevel a value > 0 indicates a SUBMIT call is
+ *     executing, where a read line is returned as is. 0 is seen as interactive
+ *     mode, where read lines can be interpreted;
+ *   - \ref g_outputToString value 1 renders scrolling as pointless and
+ *     disables it;
+ *   - \ref backBuffer may contain text to display on scroll back operations;
+ *   - \ref g_scrollMode value 1 enables scrolling back through text held in
+ *     \ref backBuffer;
+ *   - \ref localScrollMode a value of 0 temporarily disables scrolling,
+ *     overriding the setting in \ref g_scrollMode;
+ *   - \ref g_commandPrompt if this string matches ask, top level user input is
+ *     assumed, and an empty line is usually discarded;
+ *   - \ref g_logFileOpenFlag if set to 1, a not interpreted returned line is
+ *     logged before it is passed on to the caller.
+ * \post
+ *   - \ref localScrollMode is set to 1
+ *   - \ref printedLines is reset to 0
+ *   - \ref g_quitPrint is reset to 0
+ *   - interactive mode: \ref tempAllocStack frees top elements down to
+ *     \ref g_startTempAllocStack.
+ *   - interactive mode: \ref pntrTempAllocStack frees top elements down to
+ *     \ref g_pntrStartTempAllocStack.
+ *   - interactive mode: The \ref backBuffer is cleared, then filled with
+ *     prompt (last line only) and input of the user.
+ *   - submit mode: In case of EOF the previous \ref g_commandFileNestingLevel
+ *     is activated, necessary cleanups performed, and
+ *     the \ref g_commandFileSilentFlag is updated appropriately.
+ * \warning the calling program must deallocate the returned string.
+ */
+vstring cmdInput1(const char *ask);
+
+flag cmdInputIsY(const char *ask);
+
+enum severity {notice_,warning_,error_,fatal_};
+void errorMessage(vstring line, long lineNum, long column, long tokenLength,
+  vstring error, vstring fileName, long statementNum, flag warnFlag);
+
+/*! Opens files with error message; opens output files with
+   backup of previous version.   Mode must be "r" or "w". */
+FILE *fSafeOpen(const char *fileName, const char *mode, flag noVersioningFlag);
+
+/*! Renames a file with backup of previous version.  If non-zero
+   is returned, there was an error. */
+int fSafeRename(const char *oldFileName, const char *newFileName);
+
+/*! Finds the name of the first file of the form filePrefix +
+   nnn + ".tmp" that does not exist.  THE CALLER MUST DEALLOCATE
+   THE RETURNED STRING. */
+vstring fGetTmpName(const char *filePrefix);
+
+/*! This function returns a character string containing the entire contents of
+   an ASCII file, or Unicode file with only ASCII characters.   On some
+   systems it is faster than reading the file line by line.  THE CALLER
+   MUST DEALLOCATE THE RETURNED STRING.  If a NULL is returned, the file
+   could not be opened or had a non-ASCII Unicode character or some other
+   problem.   If verbose is 0, error and warning messages are suppressed. */
+vstring readFileToString(const char *fileName, char verbose, long *charCount);
+
+/*! Returns total elapsed time in seconds since starting session (for the
+   lcc compiler) or the CPU time used (for the gcc compiler).  The
+   argument is assigned the time since the last call to this function. */
+double getRunTime(double *timeSinceLastCall);
+
+/*! Call before exiting to free memory allocated by this module */
+void freeInOu(void);
+
+#endif /* METAMATH_MMINOU_H_*/
diff --git a/mmpars.c b/src/mmpars.c
similarity index 83%
rename from mmpars.c
rename to src/mmpars.c
index 37f4a2c..beb8087 100644
--- a/mmpars.c
+++ b/src/mmpars.c
@@ -1,6665 +1,6135 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <setjmp.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mminou.h"
-#include "mmpars.h"
-/* #include "mmcmds.h" */  /* For getContribs() if used */
-#include "mmpfas.h" /* Needed for g_pipDummyVars, subproofLen() */
-#include "mmunif.h" /* Needed for g_minSubstLen */
-#include "mmcmdl.h" /* Needed for g_rootDirectory */
-
-long potentialStatements; /* Potential statements in source file (upper
-                                 limit) for memory allocation purposes */
-flag illegalLabelChar[256]; /* Illegal characters for labels -- initialized
-                               by parseLabels() */
-/* (Next 2 are global in mmpars.c only) */
-long *g_labelKeyBase; /* Start of assertion ($a, $p) labels */
-long g_numLabelKeys; /* Number of assertion labels */
-
-long *g_allLabelKeyBase; /* Start of all labels */
-long g_numAllLabelKeys; /* Number of all labels */
-
-
-/* Working structure for parsing proofs */
-/* This structure should be deallocated by the ERASE command. */
-long g_wrkProofMaxSize = 0; /* Maximum size so far - it may grow */
-long wrkMathPoolMaxSize = 0; /* Max mathStringPool size so far - it may grow */
-struct wrkProof_struct g_WrkProof;
-
-
-
-/* This function returns a pointer to a buffer containing the contents of an
-   input file and its 'include' calls.  'Size' returns the buffer's size.
-   Partial parsing is done; when 'include' statements are found, this function
-   is called recursively.
-   The file g_input_fn is assumed to be opened when this is called. */
-char *readRawSource(/*vstring inputFn,*/ /* 2-Feb-2018 nm Unused argument */
-         vstring fileBuf, /* added 31-Dec-2017 */
-         /*long bufOffsetSoFar,*/  /* deleted 31-Dec-2017 nm */
-         long *size /* 31-Dec-2017 Now an input argument */)
-{
-  long charCount = 0;
-  /*char *fileBuf;*/ /* 31-Dec-2017 */
-  char *fbPtr;
-  /*long lineNum;*/ /* 2-Feb-2018 Now computed in rawSourceError() */
-  flag insideComment;
-  /* flag insideLineComment; */ /* obsolete */
-  char mode;
-  char tmpch;
-
-  /* 31-Dec-2017 nm */
-  charCount = *size;
-
-  /* Look for $[ and $] 'include' statement start and end */
-  /* 31-Dec-2017 nm - these won't happen since they're now expanded earlier */
-  /* But it can't hurt, since the code is already written.
-     TODO - clean it up for speedup? */
-  fbPtr = fileBuf;
-  /*lineNum = 1;*/
-  mode = 0; /* 0 = outside of 'include', 1 = inside of 'include' */
-  insideComment = 0; /* 1 = inside $( $) comment */
-  /* insideLineComment = 0; */ /* 1 = inside $! comment */
-  while (1) {
-    /* Find a keyword or newline */
-    /* fbPtr = strpbrk(fbPtr, "\n$"); */ /* Takes 10 msec on VAX 4000/60 ! */
-    tmpch = fbPtr[0];
-    if (!tmpch) { /* End of file */
-      if (insideComment) {
-        rawSourceError(fileBuf, fbPtr - 1, 2, /*lineNum - 1, inputFn,*/
-         "The last comment in the file is incomplete.  \"$)\" was expected.");
-      } else {
-        if (mode != 0) {
-          rawSourceError(fileBuf, fbPtr - 1, 2, /*lineNum - 1, inputFn,*/
-   "The last include statement in the file is incomplete.  \"$]\" was expected."
-           );
-        }
-      }
-      break;
-    }
-    if (tmpch != '$') {
-      if (tmpch == '\n') {
-        /* insideLineComment = 0; */ /* obsolete */
-        /*lineNum++;*/
-      } else {
-        /* if (!insideComment && !insideLineComment) { */
-          if (!isgraph((unsigned char)tmpch) && !isspace((unsigned char)tmpch)) {
-            /* 19-Oct-2010 nm Used to bypass "lineNum++" below, which messed up
-               line numbering. */
-            rawSourceError(fileBuf, fbPtr, 1, /*lineNum, inputFn,*/
-                cat("Illegal character (ASCII code ",
-                str((double)((unsigned char)tmpch)),
-                " decimal).",NULL));
-        /* } */
-        }
-      }
-      fbPtr++;
-      continue;
-    }
-
-
-    /* 11-Sep-2009 nm Detect missing whitespace around keywords (per current
-       Metamath language spec) */
-    if (fbPtr > fileBuf) {  /* If this '$' is not the 1st file character */
-      if (isgraph((unsigned char)(fbPtr[-1]))) {
-        /* The character before the '$' is not white space */
-        if (!insideComment || fbPtr[1] == ')') {
-          /* Inside comments, we only care about the "$)" keyword */
-          rawSourceError(fileBuf, fbPtr, 2, /*lineNum, inputFn,*/
-              "A keyword must be preceded by white space.");
-        }
-      }
-    }
-    fbPtr++; /* (This line was already here before 11-Sep-2009 mod) */
-    if (fbPtr[0]) {  /* If the character after '$' is not end of file (which
-                        would be an error anyway, but detected elsewhere) */
-      if (isgraph((unsigned char)(fbPtr[1]))) {
-        /* The character after the character after the '$' is not white
-           space (nor end of file) */
-        if (!insideComment || fbPtr[0] == ')') {
-          /* Inside comments, we only care about the "$)" keyword */
-          rawSourceError(fileBuf, fbPtr + 1, 1, /*lineNum, inputFn,*/
-              "A keyword must be followed by white space.");
-          }
-      }
-    }
-    /* End of 11-Sep-2009 mod */
-
-    switch (fbPtr[0]) {
-      case '(': /* Start of comment */
-        if (insideComment) {
-          rawSourceError(fileBuf, fbPtr - 1, 2, /*lineNum, inputFn,*/
-          "Nested comments are not allowed.");
-        }
-        insideComment = 1;
-        continue;
-      case ')': /* End of comment */
-        if (!insideComment) {
-          rawSourceError(fileBuf, fbPtr - 1, 2, /*lineNum, inputFn,*/
-              "A comment terminator was found outside of a comment.");
-        }
-        insideComment = 0;
-        continue;
-      /* Comment to end-of-line */  /* obsolete */
-      /*
-      case '!':
-        if (!insideComment) {
-          insideLineComment = 1;
-          fbPtr++;
-          continue;
-        }
-      */
-    }
-    if (insideComment) continue;
-    switch (fbPtr[0]) {
-      case '[':
-        if (mode != 0) {
-          rawSourceError(fileBuf, fbPtr - 1, 2, /*lineNum, inputFn,*/
-              "Nested include statements are not allowed.");
-        } else {
-          /* 31-Dec-2017 nm */
-          /* $[ ... $] should have been processed by readSourceAndIncludes() */
-          rawSourceError(fileBuf, fbPtr - 1, 2, /*lineNum, inputFn,*/
-              "\"$[\" is unterminated or has ill-formed \"$]\".");
-
-        }
-        continue;
-      case ']':
-        if (mode == 0) {
-          rawSourceError(fileBuf, fbPtr - 1, 2, /*lineNum, inputFn,*/
-              "A \"$[\" is required before \"$]\".");
-          continue;
-        }
-
-        /* 31-Dec-2017 nm */
-        /* Because $[ $] are already expanded here, this should never happen */
-        bug(1759);
-
-        continue;
-
-      case '{':
-      case '}':
-      case '.':
-        potentialStatements++; /* Number of potential statements for malloc */
-        break;
-    } /* End switch fbPtr[0] */
-  } /* End while */
-
-  if (fbPtr != fileBuf + charCount) {
-    /* To help debugging: */
-    printf("fbPtr=%ld fileBuf=%ld charCount=%ld diff=%ld\n",
-        (long)fbPtr, (long)fileBuf, charCount, fbPtr - fileBuf - charCount);
-    bug(1704);
-  }
-
-  /* 31-Dec-2017 nm */
-  print2("%ld bytes were read into the source buffer.\n", charCount);
-
-  if (*size != charCount) bug(1761);
-  return (fileBuf);
-
-} /* readRawSource */
-
-/* This function initializes the g_Statement[] structure array and assigns
-   sections of the raw input text.  statements is updated.
-   g_sourcePtr is assumed to point to the raw input buffer.
-   g_sourceLen is assumed to be length of the raw input buffer. */
-void parseKeywords(void)
-{
-  long i, j, k;
-  char *fbPtr;
-  flag insideComment;
-  char mode, type;
-  char *startSection;
-  char tmpch;
-  long dollarPCount = 0; /* For statistics only */
-  long dollarACount = 0; /* For statistics only */
-
-  /*** 9-Jan-2018 nm Deleted
-  /@ Variables needed for line number and file name @/
-  long inclCallNum;
-  char *nextInclPtr;
-  long lineNum;
-  vstring fileName;
-  ***/
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  type = 0;
-
-  /* Determine the upper limit of the number of new statements added for
-     allocation purposes (computed in readRawInput) */
-  potentialStatements = potentialStatements + 3; /* To be cautious */
-/*E*/if(db5)print2("There are up to %ld potential statements.\n",
-/*E*/   potentialStatements);
-
-  /* Reallocate the statement array for all potential statements */
-  g_Statement = realloc(g_Statement, (size_t)potentialStatements
-      * sizeof(struct statement_struct));
-  if (!g_Statement) outOfMemory("#4 (statement)");
-
-  /* Initialize the statement array */
-  i = 0;
-  g_Statement[i].lineNum = 0;   /* assigned by assignStmtFileAndLineNum() */
-  g_Statement[i].fileName = ""; /* assigned by assignStmtFileAndLineNum() */
-  g_Statement[i].labelName = "";
-  g_Statement[i].uniqueLabel = 0;
-  g_Statement[i].type = illegal_;
-  g_Statement[i].scope = 0;
-  g_Statement[i].beginScopeStatementNum = 0;
-  g_Statement[i].endScopeStatementNum = 0; /* 24-Aug-2020 nm */
-  g_Statement[i].labelSectionPtr = "";
-  g_Statement[i].labelSectionLen = 0; /* 3-May-2017 nm */
-  g_Statement[i].labelSectionChanged = 0;
-  g_Statement[i].statementPtr = ""; /* input to assignStmtFileAndLineNum() */
-  g_Statement[i].mathSectionPtr = "";
-  g_Statement[i].mathSectionLen = 0;
-  g_Statement[i].mathSectionChanged = 0; /* 3-May-2017 nm */
-  g_Statement[i].proofSectionPtr = "";
-  g_Statement[i].proofSectionLen = 0;
-  g_Statement[i].proofSectionChanged = 0; /* 3-May-2017 nm */
-  g_Statement[i].mathString = NULL_NMBRSTRING;
-  g_Statement[i].mathStringLen = 0;
-  g_Statement[i].proofString = NULL_NMBRSTRING;
-  g_Statement[i].reqHypList = NULL_NMBRSTRING;
-  g_Statement[i].optHypList = NULL_NMBRSTRING;
-  g_Statement[i].numReqHyp = 0;
-  g_Statement[i].reqVarList = NULL_NMBRSTRING;
-  g_Statement[i].optVarList = NULL_NMBRSTRING;
-  g_Statement[i].reqDisjVarsA = NULL_NMBRSTRING;
-  g_Statement[i].reqDisjVarsB = NULL_NMBRSTRING;
-  g_Statement[i].reqDisjVarsStmt = NULL_NMBRSTRING;
-  g_Statement[i].optDisjVarsA = NULL_NMBRSTRING;
-  g_Statement[i].optDisjVarsB = NULL_NMBRSTRING;
-  g_Statement[i].optDisjVarsStmt = NULL_NMBRSTRING;
-  g_Statement[i].pinkNumber = 0;
-  g_Statement[i].headerStartStmt = 0; /* 18-Dec-2016 nm */
-  for (i = 1; i < potentialStatements; i++) {
-    g_Statement[i] = g_Statement[0];
-  }
-
-  /* 10-Dec-2018 nm */
-  /* In case there is no relevant statement (originally added for MARKUP
-     command) */
-  g_Statement[0].labelName = "(N/A)";
-
-/*E*/if(db5)print2("Finished initializing statement array.\n");
-
-  /* Fill in the statement array with raw source text */
-  fbPtr = g_sourcePtr;
-  mode = 0; /* 0 = label section, 1 = math section, 2 = proof section */
-  insideComment = 0; /* 1 = inside comment */
-  startSection = fbPtr;
-
-  while (1) {
-    /* Find a keyword or newline */
-    /* fbPtr = strpbrk(fbPtr, "\n$"); */ /* Takes 10 msec on VAX 4000/60 ! */
-    tmpch = fbPtr[0];
-    if (!tmpch) { /* End of file */
-      if (mode != 0) {
-        sourceError(fbPtr - 1, 2, g_statements,
-            "Expected \"$.\" here (last line of file).");
-        if (g_statements) { /* Adjustment for error messages */
-          startSection = g_Statement[g_statements].labelSectionPtr;
-          g_statements--;
-        }
-      }
-      break;
-    }
-
-    if (tmpch != '$') {
-      /* 9-Jan-2018 nm Deleted - now computed by assignStmtFileAndLineNum() */
-      /* if (tmpch == '\n') lineNum++; */
-      fbPtr++;
-      continue;
-    }
-    fbPtr++;
-    switch (fbPtr[0]) {
-      case '$': /* "$$" means literal "$" */
-        fbPtr++;
-        continue;
-      case '(': /* Start of comment */
-        /* if (insideComment) { */
-          /* "Nested comments are not allowed." - detected by readRawSource */
-        /* } */
-        insideComment = 1;
-        continue;
-      case ')': /* End of comment */
-        /* if (!insideComment) { */
-          /* "Comment terminator found outside of comment."- detected by
-             readRawSource */
-        /* } */
-        insideComment = 0;
-        continue;
-      case '!': /* Comment to end-of-line */
-        if (insideComment) continue; /* Not really a comment */
-        /* 8/23/99 - Made this syntax obsolete.  It was really obsolete
-           before but tolerating it created problems with LaTeX output. */
-        /******* Commented out these lines to force an error upon "$!"
-        fbPtr = strchr(fbPtr, (int)'\n');
-        if (!fbPtr) bug(1705);
-        lineNum++;
-        fbPtr++;
-        continue;
-        *******/
-    }
-    if (insideComment) continue;
-    switch (fbPtr[0]) {
-      case 'c':  type = c_; break;
-      case 'v':  type = v_; break;
-      case 'e':  type = e_; break;
-      case 'f':  type = f_; break;
-      case 'd':  type = d_; break;
-      case 'a':  type = a_; dollarACount++; break;
-      case 'p':  type = p_; dollarPCount++; break;
-      case '{':  type = lb_; break;
-      case '}':  type = rb_; break;
-    }
-    switch (fbPtr[0]) {
-      case 'c':
-      case 'v':
-      case 'e':
-      case 'f':
-      case 'd':
-      case 'a':
-      case 'p':
-      case '{':
-      case '}':
-        if (mode != 0) {
-          if (mode == 2 || type != p_) {
-            sourceError(fbPtr - 1, 2, g_statements,
-                "Expected \"$.\" here.");
-          } else {
-            sourceError(fbPtr - 1, 2, g_statements,
-                "Expected \"$=\" here.");
-          }
-          continue;
-        }
-        /* Initialize a new statement */
-        g_statements++;
-        /*** 9-Jan-2018 nm Now assigned by assignStmtFileAndLineNum() as needed
-        g_Statement[g_statements].lineNum = lineNum;
-        g_Statement[g_statements].fileName = fileName;
-        ****/
-        g_Statement[g_statements].type = type;
-        g_Statement[g_statements].labelSectionPtr = startSection;
-        g_Statement[g_statements].labelSectionLen = fbPtr - startSection - 1;
-        /* The character after label section is used by
-           assignStmtFileAndLineNum() to determine the "line number" for the
-           statement as a whole */
-        g_Statement[g_statements].statementPtr = startSection
-            + g_Statement[g_statements].labelSectionLen; /* 9-Jan-2018 nm */
-        startSection = fbPtr + 1;
-        if (type != lb_ && type != rb_) mode = 1;
-        continue;
-      default:
-        if (mode == 0) {
-          sourceError(fbPtr - 1, 2, g_statements, cat(
-              "Expected \"$c\", \"$v\", \"$e\", \"$f\", \"$d\",",
-              " \"$a\", \"$p\", \"${\", or \"$}\" here.",NULL));
-          continue;
-        }
-        if (mode == 1) {
-          if (type == p_ && fbPtr[0] != '=') {
-            sourceError(fbPtr - 1, 2, g_statements,
-                "Expected \"$=\" here.");
-            if (fbPtr[0] == '.') {
-              mode = 2; /* If $. switch mode to help reduce error msgs */
-            }
-          }
-          if (type != p_ && fbPtr[0] != '.') {
-            sourceError(fbPtr - 1, 2, g_statements,
-                "Expected \"$.\" here.");
-            continue;
-          }
-          /* Add math section to statement */
-          g_Statement[g_statements].mathSectionPtr = startSection;
-          g_Statement[g_statements].mathSectionLen = fbPtr - startSection - 1;
-          startSection = fbPtr + 1;
-          if (type == p_ && mode != 2 /* !error msg case */) {
-            mode = 2; /* Switch mode to proof section */
-          } else {
-            mode = 0;
-          }
-          continue;
-        } /* End if mode == 1 */
-        if (mode == 2) {
-          if (fbPtr[0] != '.') {
-            sourceError(fbPtr - 1, 2, g_statements,
-                "Expected \"$.\" here.");
-            continue;
-          }
-          /* Add proof section to statement */
-          g_Statement[g_statements].proofSectionPtr = startSection;
-          g_Statement[g_statements].proofSectionLen = fbPtr - startSection - 1;
-          startSection = fbPtr + 1;
-          mode = 0;
-          continue;
-        } /* End if mode == 2 */
-    } /* End switch fbPtr[0] */
-  } /* End while */
-
-  if (fbPtr != g_sourcePtr + g_sourceLen) bug(1706);
-
-  print2("The source has %ld statements; %ld are $a and %ld are $p.\n",
-       g_statements, dollarACount, dollarPCount);
-
-  /* Put chars after the last $. into the label section of a dummy statement */
-  /* g_Statement[g_statements + 1].lineNum = lineNum - 1; */
-  /* 10/14/02 Changed this to lineNum so mmwtex $t error msgs will be correct */
-  /* Here, lineNum will be one plus the number of lines in the file */
-  /*** 9-Jan-2018 nm Now assigned by assignStmtFileAndLineNum() as needed
-  g_Statement[g_statements].lineNum = lineNum;
-  g_Statement[g_statements].fileName = fileName;
-  ****/
-  g_Statement[g_statements + 1].type = illegal_;
-  g_Statement[g_statements + 1].labelSectionPtr = startSection;
-  g_Statement[g_statements + 1].labelSectionLen = fbPtr - startSection;
-  /* Point to last character of file in case we ever need lineNum/fileName */
-  g_Statement[g_statements + 1].statementPtr = fbPtr - 1; /* 9-Jan-2018 */
-
-  /* 10/25/02 Initialize the pink number to print after the statement labels
-   in HTML output. */
-  /* The pink number only counts $a and $p statements, unlike the statement
-     number which also counts $f, $e, $c, $v, ${, $} */
-  j = 0;
-  k = 0; /* 18-Dec-2016 nm */
-  for (i = 1; i <= g_statements; i++) {
-    if (g_Statement[i].type == a_ || g_Statement[i].type == p_) {
-
-      /* 18-Dec-2016 nm */
-      /* Use the statement _after_ the previous $a or $p; that is the start
-         of the "header area" for use by getSectionHeadings() in mmwtex.c.
-         headerStartStmt will be equal to the current statement if the
-         previous statement is also a $a or $p) */
-      g_Statement[i].headerStartStmt = k + 1;
-      k = i;
-
-      j++;
-      g_Statement[i].pinkNumber = j;
-    }
-  }
-  /* 10-Jan-04  Also, put the largest pink number in the last statement, no
-     matter what it kind it is, so we can look up the largest number in
-     pinkHTML() in mmwtex.c */
-  g_Statement[g_statements].pinkNumber = j;
-
-
-/*E*/if(db5){for (i=1; i<=g_statements; i++){
-/*E*/  if (i == 5) { print2("(etc.)\n");} else { if (i<5) {
-/*E*/  assignStmtFileAndLineNum(i);
-/*E*/  print2("Statement %ld: line %ld file %s.\n",i,g_Statement[i].lineNum,
-/*E*/      g_Statement[i].fileName);
-/*E*/}}}}
-
-}
-
-/* This function parses the label sections of the g_Statement[] structure array.
-   g_sourcePtr is assumed to point to the beginning of the raw input buffer.
-   g_sourceLen is assumed to be length of the raw input buffer. */
-void parseLabels(void)
-{
-  long i, j, k;
-  char *fbPtr;
-  char type;
-  long stmt;
-  flag dupFlag;
-
-  /* Define the legal label characters */
-  for (i = 0; i < 256; i++) {
-    illegalLabelChar[i] = !isalnum(i);
-  }
-  illegalLabelChar['-'] = 0;
-  illegalLabelChar['_'] = 0;
-  illegalLabelChar['.'] = 0;
-
-
-  /* Scan all statements and extract their labels */
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    type = g_Statement[stmt].type;
-    fbPtr = g_Statement[stmt].labelSectionPtr;
-    fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-    j = tokenLen(fbPtr);
-    if (j) {
-      for (k = 0; k < j; k++) {
-        if (illegalLabelChar[(unsigned char)fbPtr[k]]) {
-          sourceError(fbPtr + k, 1, stmt,
-        "Only letters, digits, \"_\", \"-\", and \".\" are allowed in labels.");
-          break;
-        }
-      }
-      switch (type) {
-        case d_:
-        case rb_:
-        case lb_:
-        case v_:
-        case c_:
-          sourceError(fbPtr, j, stmt,
-                "A label isn't allowed for this statement type.");
-      }
-      g_Statement[stmt].labelName = malloc((size_t)j + 1);
-      if (!g_Statement[stmt].labelName) outOfMemory("#5 (label)");
-      g_Statement[stmt].labelName[j] = 0;
-      memcpy(g_Statement[stmt].labelName, fbPtr, (size_t)j);
-      fbPtr = fbPtr + j;
-      fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-      j = tokenLen(fbPtr);
-      if (j) {
-        sourceError(fbPtr, j, stmt,
-            "A statement may have only one label.");
-      }
-    } else {
-      switch (type) {
-        case e_:
-        case f_:
-        case a_:
-        case p_:
-          sourceError(fbPtr, 2, stmt,
-                "A label is required for this statement type.");
-      }
-    }
-  } /* Next stmt */
-
-  /* Make sure there is no token after the last statement */
-  fbPtr = g_Statement[g_statements + 1].labelSectionPtr; /* Last (dummy) statement*/
-  i = whiteSpaceLen(fbPtr);
-  j = tokenLen(fbPtr + i);
-  if (j) {
-    sourceError(fbPtr + i, j, 0,
-        "There should be no tokens after the last statement.");
-  }
-
-  /* Sort the labels for later lookup */
-  g_labelKey = malloc(((size_t)g_statements + 1) * sizeof(long));
-  if (!g_labelKey) outOfMemory("#6 (g_labelKey)");
-  for (i = 1; i <= g_statements; i++) {
-    g_labelKey[i] = i;
-  }
-  g_labelKeyBase = &g_labelKey[1];
-  g_numLabelKeys = g_statements;
-  qsort(g_labelKeyBase, (size_t)g_numLabelKeys, sizeof(long), labelSortCmp);
-
-  /* Skip null labels. */
-  for (i = 1; i <= g_statements; i++) {
-    if (g_Statement[g_labelKey[i]].labelName[0]) break;
-  }
-  g_labelKeyBase = &g_labelKey[i];
-  g_numLabelKeys = g_statements - i + 1;
-/*E*/if(db5)print2("There are %ld non-empty labels.\n", g_numLabelKeys);
-/*E*/if(db5){print2("The first (up to 5) sorted labels are:\n");
-/*E*/  for (i=0; i<5; i++) {
-/*E*/    if (i >= g_numLabelKeys) break;
-/*E*/    print2("%s ",g_Statement[g_labelKeyBase[i]].labelName);
-/*E*/  } print2("\n");}
-
-
-
-  /* Copy the keys for all possible labels for lookup by the
-     squishProof command when local labels are generated in packed proofs. */
-  g_allLabelKeyBase = malloc((size_t)g_numLabelKeys * sizeof(long));
-  if (!g_allLabelKeyBase) outOfMemory("#60 (g_allLabelKeyBase)");
-  memcpy(g_allLabelKeyBase, g_labelKeyBase, (size_t)g_numLabelKeys * sizeof(long));
-  g_numAllLabelKeys = g_numLabelKeys;
-
-  /* Now back to the regular label stuff. */
-  /* Check for duplicate labels */
-  /* (This will go away if local labels on hypotheses are allowed.) */
-  /* 17-Sep-2005 nm - This code was reinstated to conform to strict spec.
-     The old check for duplicate active labels (see other comment for this
-     date below) was removed since it becomes redundant . */
-  dupFlag = 0;
-  for (i = 0; i < g_numLabelKeys; i++) {
-    if (dupFlag) {
-      /* This "if" condition causes only the 2nd in a pair of duplicate labels
-         to have an error message. */
-      dupFlag = 0;
-      if (!strcmp(g_Statement[g_labelKeyBase[i]].labelName,
-          g_Statement[g_labelKeyBase[i - 1]].labelName)) dupFlag = 1;
-    }
-    if (i < g_numLabelKeys - 1) {
-      if (!strcmp(g_Statement[g_labelKeyBase[i]].labelName,
-          g_Statement[g_labelKeyBase[i + 1]].labelName)) dupFlag = 1;
-    }
-    if (dupFlag) {
-      fbPtr = g_Statement[g_labelKeyBase[i]].labelSectionPtr;
-      k = whiteSpaceLen(fbPtr);
-      j = tokenLen(fbPtr + k);
-      sourceError(fbPtr + k, j, g_labelKeyBase[i],
-         "This label is declared more than once.  All labels must be unique.");
-    }
-  }
-
-}
-
-/* This functions retrieves all possible math symbols from $c and $v
-   statements. */
-void parseMathDecl(void)
-{
-  long potentialSymbols;
-  long stmt;
-  char *fbPtr;
-  long i, j, k;
-  char *tmpPtr;
-  nmbrString *nmbrTmpPtr;
-  long oldG_mathTokens;
-  void *voidPtr; /* bsearch returned value */  /* 4-Jun-06 nm */
-
-  /* Find the upper limit of the number of symbols declared for
-     pre-allocation:  at most, the number of symbols is half the number of
-     characters, since $c and $v statements require white space. */
-  potentialSymbols = 0;
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    switch (g_Statement[stmt].type) {
-      case c_:
-      case v_:
-        potentialSymbols = potentialSymbols + g_Statement[stmt].mathSectionLen;
-    }
-  }
-  potentialSymbols = (potentialSymbols / 2) + 2;
-/*E*/if(db5)print2("%ld potential symbols were computed.\n",potentialSymbols);
-  g_MathToken = realloc(g_MathToken, (size_t)potentialSymbols *
-      sizeof(struct mathToken_struct));
-  if (!g_MathToken) outOfMemory("#7 (g_MathToken)");
-
-  /* Scan $c and $v statements to accumulate all possible math symbols */
-  g_mathTokens = 0;
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-    switch (g_Statement[stmt].type) {
-      case c_:
-      case v_:
-        oldG_mathTokens = g_mathTokens;
-        fbPtr = g_Statement[stmt].mathSectionPtr;
-        while (1) {
-          i = whiteSpaceLen(fbPtr);
-          j = tokenLen(fbPtr + i);
-          if (!j) break;
-          tmpPtr = malloc((size_t)j + 1); /* Math symbol name */
-          if (!tmpPtr) outOfMemory("#8 (symbol name)");
-          tmpPtr[j] = 0; /* End of string */
-          memcpy(tmpPtr, fbPtr + i, (size_t)j);
-          fbPtr = fbPtr + i + j;
-          /* Create a new math symbol */
-          g_MathToken[g_mathTokens].tokenName = tmpPtr;
-          g_MathToken[g_mathTokens].length = j;
-          if (g_Statement[stmt].type == c_) {
-            g_MathToken[g_mathTokens].tokenType = (char)con_;
-          } else {
-            g_MathToken[g_mathTokens].tokenType = (char)var_;
-          }
-          g_MathToken[g_mathTokens].active = 0;
-          g_MathToken[g_mathTokens].scope = 0; /* Unknown for now */
-          g_MathToken[g_mathTokens].tmp = 0; /* Not used for now */
-          g_MathToken[g_mathTokens].statement = stmt;
-          g_MathToken[g_mathTokens].endStatement = g_statements; /* Unknown for now */
-                /* (Assign to 'g_statements' in case it's active until the end) */
-          g_mathTokens++;
-
-        }
-
-        /* Create the symbol list for this statement */
-        j = g_mathTokens - oldG_mathTokens; /* Number of tokens in this statement */
-        nmbrTmpPtr = poolFixedMalloc((j + 1) * (long)(sizeof(nmbrString)));
-        /* if (!nmbrTmpPtr) outOfMemory("#9 (symbol table)"); */ /*??? Not nec. with poolMalloc */
-        nmbrTmpPtr[j] = -1;
-        for (i = 0; i < j; i++) {
-          nmbrTmpPtr[i] = oldG_mathTokens + i;
-        }
-        g_Statement[stmt].mathString = nmbrTmpPtr;
-        g_Statement[stmt].mathStringLen = j;
-        if (!j) {
-          sourceError(fbPtr, 2, stmt,
-           "At least one math symbol should be declared.");
-        }
-    } /* end switch (g_Statement[stmt].type) */
-  } /* next stmt */
-
-/*E*/if(db5)print2("%ld math symbols were declared.\n",g_mathTokens);
-  /* Reallocate from potential to actual to reduce memory space */
-  /* Add 100 to allow for initial Proof Assistant use, and up to 100
-     errors in undeclared token references */
-  g_MAX_MATHTOKENS = g_mathTokens + 100;
-  g_MathToken = realloc(g_MathToken, (size_t)g_MAX_MATHTOKENS *
-      sizeof(struct mathToken_struct));
-  if (!g_MathToken) outOfMemory("#10 (g_MathToken)");
-
-  /* Create a special "$|$" boundary token to separate real and dummy ones */
-  g_MathToken[g_mathTokens].tokenName = "";
-  let(&g_MathToken[g_mathTokens].tokenName, "$|$");
-  g_MathToken[g_mathTokens].length = 2; /* Never used */
-  g_MathToken[g_mathTokens].tokenType = (char)con_;
-  g_MathToken[g_mathTokens].active = 0; /* Never used */
-  g_MathToken[g_mathTokens].scope = 0; /* Never used */
-  g_MathToken[g_mathTokens].tmp = 0; /* Never used */
-  g_MathToken[g_mathTokens].statement = 0; /* Never used */
-  g_MathToken[g_mathTokens].endStatement = g_statements; /* Never used */
-
-
-  /* Sort the math symbols for later lookup */
-  g_mathKey = malloc((size_t)g_mathTokens * sizeof(long));
-  if (!g_mathKey) outOfMemory("#11 (g_mathKey)");
-  for (i = 0; i < g_mathTokens; i++) {
-    g_mathKey[i] = i;
-  }
-  qsort(g_mathKey, (size_t)g_mathTokens, sizeof(long), mathSortCmp);
-/*E*/if(db5){print2("The first (up to 5) sorted math tokens are:\n");
-/*E*/  for (i=0; i<5; i++) {
-/*E*/    if (i >= g_mathTokens) break;
-/*E*/    print2("%s ",g_MathToken[g_mathKey[i]].tokenName);
-/*E*/  } print2("\n");}
-
-
-  /* 4-Jun-06 nm Check for labels with the same name as math tokens */
-  /* (This section implements the Metamath spec change proposed by O'Cat that
-     lets labels and math tokens occupy the same namespace and thus forbids
-     them from having common names.) */
-  /* For maximum speed, we scan M math tokens and look each up in the list
-     of L labels.  The we have M * log L comparisons, which is optimal when
-     (as in most cases) M << L. */
-  for (i = 0; i < g_mathTokens; i++) {
-    /* See if the math token is in the list of labels */
-    voidPtr = (void *)bsearch(g_MathToken[i].tokenName, g_labelKeyBase,
-        (size_t)g_numLabelKeys, sizeof(long), labelSrchCmp);
-    if (voidPtr) { /* A label matching the token was found */
-      stmt = (*(long *)voidPtr); /* Statement number */
-      fbPtr = g_Statement[stmt].labelSectionPtr;
-      k = whiteSpaceLen(fbPtr);
-      j = tokenLen(fbPtr + k);
-      /* Note that the line and file are only assigned when requested,
-         for speedup. */
-      assignStmtFileAndLineNum(stmt); /* 9-Jan-2018 nm */
-      assignStmtFileAndLineNum(g_MathToken[i].statement); /* 10-Dec-2019 nm */
-      sourceError(fbPtr + k, j, stmt, cat(
-         "This label has the same name as the math token declared on line ",
-         str((double)(g_Statement[g_MathToken[i].statement].lineNum)),
-         " of file \"",
-         g_Statement[g_MathToken[i].statement].fileName,
-         "\".", NULL));
-    }
-  }
-  /* End of 4-Jun-06 */
-
-
-}
-
-
-/* This functions parses statement contents, except for proofs */
-void parseStatements(void)
-{
-  long stmt;
-  char type;
-  long i, j, k, m, n, p;
-  char *fbPtr;
-  long mathStringLen;
-  long tokenNum;
-  long lowerKey, upperKey;
-  long symbolLen, origSymbolLen, mathSectionLen, g_mathKeyNum;
-  void *g_mathKeyPtr; /* bsearch returned value */
-  int maxScope;
-  long reqHyps, optHyps, reqVars, optVars;
-  flag reqFlag;
-  int undeclErrorCount = 0;
-  vstring tmpStr = "";
-
-  nmbrString *nmbrTmpPtr;
-
-  long *mathTokenSameAs; /* Flag that symbol is unique (for speed up) */
-  long *reverseMathKey; /* Map from g_mathTokens to g_mathKey */
-
-  long *labelTokenSameAs; /* Flag that label is unique (for speed up) */
-  long *reverseLabelKey; /* Map from statement # to label key */
-  flag *labelActiveFlag; /* Flag that label is active */
-
-  struct activeConstStack_struct {
-    long tokenNum;
-    int scope;
-  };
-  struct activeConstStack_struct *activeConstStack; /* Stack of active consts */
-  long activeConstStackPtr = 0;
-
-  struct activeVarStack_struct {
-    long tokenNum;
-    int scope;
-    char tmpFlag; /* Used by hypothesis variable scan; must be 0 otherwise */
-  };
-  struct activeVarStack_struct *activeVarStack; /* Stack of active variables */
-  nmbrString *wrkVarPtr1;
-  nmbrString *wrkVarPtr2;
-  long activeVarStackPtr = 0;
-
-  struct activeEHypStack_struct { /* Stack of $e hypotheses */
-    long statemNum;
-    nmbrString *varList; /* List of variables in the hypothesis */
-    int scope;
-  };
-  struct activeEHypStack_struct *activeEHypStack;
-  long activeEHypStackPtr = 0;
-  struct activeFHypStack_struct { /* Stack of $f hypotheses */
-    long statemNum;
-    nmbrString *varList; /* List of variables in the hypothesis */
-    int scope;
-  };
-  struct activeFHypStack_struct *activeFHypStack;
-  long activeFHypStackPtr = 0;
-  nmbrString *wrkHypPtr1;
-  nmbrString *wrkHypPtr2;
-  nmbrString *wrkHypPtr3;
-  long activeHypStackSize = 30; /* Starting value; could be as large as
-                                   g_statements. */
-
-
-  struct activeDisjHypStack_struct { /* Stack of disjoint variables in $d's */
-    long tokenNumA; /* First variable in disjoint pair */
-    long tokenNumB; /* Second variable in disjoint pair */
-    long statemNum; /* Statement it occurred in */
-    int scope;
-  };
-  struct activeDisjHypStack_struct *activeDisjHypStack;
-  nmbrString *wrkDisjHPtr1A;
-  nmbrString *wrkDisjHPtr1B;
-  nmbrString *wrkDisjHPtr1Stmt;
-  nmbrString *wrkDisjHPtr2A;
-  nmbrString *wrkDisjHPtr2B;
-  nmbrString *wrkDisjHPtr2Stmt;
-  long activeDisjHypStackPtr = 0;
-  long activeDisjHypStackSize = 30; /* Starting value; could be as large as
-                                        about g_mathTokens^2/2 */
-
-  /* Temporary working space */
-  long wrkLen;
-  nmbrString *wrkNmbrPtr;
-  char *wrkStrPtr;
-
-  long maxSymbolLen; /* Longest math symbol (for speedup) */
-  flag *symbolLenExists; /* A symbol with this length exists (for speedup) */
-
-  long beginScopeStmtNum = 0;
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  mathStringLen = 0;
-  tokenNum = 0;
-
-  /* Initialize flags for g_mathKey array that identify math symbols as
-     unique (when 0) or, if not unique, the flag is a number identifying a group
-     of identical names */
-  mathTokenSameAs = malloc((size_t)g_mathTokens * sizeof(long));
-  if (!mathTokenSameAs) outOfMemory("#12 (mathTokenSameAs)");
-  reverseMathKey = malloc((size_t)g_mathTokens * sizeof(long));
-  if (!reverseMathKey) outOfMemory("#13 (reverseMathKey)");
-  for (i = 0; i < g_mathTokens; i++) {
-    mathTokenSameAs[i] = 0; /* 0 means unique */
-    reverseMathKey[g_mathKey[i]] = i; /* Initialize reverse map to g_mathKey */
-  }
-  for (i = 1; i < g_mathTokens; i++) {
-    if (!strcmp(g_MathToken[g_mathKey[i]].tokenName,
-        g_MathToken[g_mathKey[i - 1]].tokenName)) {
-      if (!mathTokenSameAs[i - 1]) mathTokenSameAs[i - 1] = i;
-      mathTokenSameAs[i] = mathTokenSameAs[i - 1];
-    }
-  }
-
-  /* Initialize flags for g_labelKey array that identify labels as
-     unique (when 0) or, if not unique, the flag is a number identifying a group
-     of identical names */
-  labelTokenSameAs = malloc(((size_t)g_statements + 1) * sizeof(long));
-  if (!labelTokenSameAs) outOfMemory("#112 (labelTokenSameAs)");
-  reverseLabelKey = malloc(((size_t)g_statements + 1) * sizeof(long));
-  if (!reverseLabelKey) outOfMemory("#113 (reverseLabelKey)");
-  labelActiveFlag = malloc(((size_t)g_statements + 1) * sizeof(flag));
-  if (!labelActiveFlag) outOfMemory("#114 (labelActiveFlag)");
-  for (i = 1; i <= g_statements; i++) {
-    labelTokenSameAs[i] = 0; /* Initialize:  0 = unique */
-    reverseLabelKey[g_labelKey[i]] = i; /* Initialize reverse map to g_labelKey */
-    labelActiveFlag[i] = 0; /* Initialize */
-  }
-  for (i = 2; i <= g_statements; i++) {
-    if (!strcmp(g_Statement[g_labelKey[i]].labelName,
-        g_Statement[g_labelKey[i - 1]].labelName)) {
-      if (!labelTokenSameAs[i - 1]) labelTokenSameAs[i - 1] = i;
-      labelTokenSameAs[i] = labelTokenSameAs[i - 1];
-    }
-  }
-
-  /* Initialize variable and hypothesis stacks */
-
-  /* Allocate g_MAX_MATHTOKENS and not just g_mathTokens of them so that
-     they can accomodate any extra non-declared tokens (which get
-     declared as part of error handling, where the g_MAX_MATHTOKENS
-     limit is checked) */
-  activeConstStack = malloc((size_t)g_MAX_MATHTOKENS
-      * sizeof(struct activeConstStack_struct));
-  activeVarStack = malloc((size_t)g_MAX_MATHTOKENS
-      * sizeof(struct activeVarStack_struct));
-  wrkVarPtr1 = malloc((size_t)g_MAX_MATHTOKENS * sizeof(nmbrString));
-  wrkVarPtr2 = malloc((size_t)g_MAX_MATHTOKENS * sizeof(nmbrString));
-  if (!activeConstStack || !activeVarStack || !wrkVarPtr1 || !wrkVarPtr2)
-      outOfMemory("#14 (activeVarStack)");
-
-  activeEHypStack = malloc((size_t)activeHypStackSize
-      * sizeof(struct activeEHypStack_struct));
-  activeFHypStack = malloc((size_t)activeHypStackSize
-      * sizeof(struct activeFHypStack_struct));
-  wrkHypPtr1 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
-  wrkHypPtr2 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
-  wrkHypPtr3 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
-  if (!activeEHypStack || !activeFHypStack || !wrkHypPtr1 || !wrkHypPtr2 ||
-      !wrkHypPtr3)
-      outOfMemory("#15 (activeHypStack)");
-
-  activeDisjHypStack = malloc((size_t)activeDisjHypStackSize *
-      sizeof(struct activeDisjHypStack_struct));
-  wrkDisjHPtr1A = malloc((size_t)activeDisjHypStackSize * sizeof(nmbrString));
-  wrkDisjHPtr1B = malloc((size_t)activeDisjHypStackSize * sizeof(nmbrString));
-  wrkDisjHPtr1Stmt = malloc((size_t)activeDisjHypStackSize
-      * sizeof(nmbrString));
-  wrkDisjHPtr2A = malloc((size_t)activeDisjHypStackSize * sizeof(nmbrString));
-  wrkDisjHPtr2B = malloc((size_t)activeDisjHypStackSize * sizeof(nmbrString));
-  wrkDisjHPtr2Stmt = malloc((size_t)activeDisjHypStackSize
-      * sizeof(nmbrString));
-  if (!activeDisjHypStack
-      || !wrkDisjHPtr1A || !wrkDisjHPtr1B || !wrkDisjHPtr1Stmt
-      || !wrkDisjHPtr2A || !wrkDisjHPtr2B || !wrkDisjHPtr2Stmt)
-      outOfMemory("#27 (activeDisjHypStack)");
-
-  /* Initialize temporary working space for parsing tokens */
-  wrkLen = 1;
-  wrkNmbrPtr = malloc((size_t)wrkLen * sizeof(nmbrString));
-  if (!wrkNmbrPtr) outOfMemory("#22 (wrkNmbrPtr)");
-  wrkStrPtr = malloc((size_t)wrkLen + 1);
-  if (!wrkStrPtr) outOfMemory("#23 (wrkStrPtr)");
-
-  /* Find declared math symbol lengths (used to speed up parsing) */
-  maxSymbolLen = 0;
-  for (i = 0; i < g_mathTokens; i++) {
-    if (g_MathToken[i].length > maxSymbolLen) {
-      maxSymbolLen = g_MathToken[i].length;
-    }
-  }
-  symbolLenExists = malloc(((size_t)maxSymbolLen + 1) * sizeof(flag));
-  if (!symbolLenExists) outOfMemory("#25 (symbolLenExists)");
-  for (i = 0; i <= maxSymbolLen; i++) {
-    symbolLenExists[i] = 0;
-  }
-  for (i = 0; i < g_mathTokens; i++) {
-    symbolLenExists[g_MathToken[i].length] = 1;
-  }
-
-
-  g_currentScope = 0;
-  beginScopeStmtNum = 0;
-
-  /* Scan all statements.  Fill in statement structure and look for errors. */
-  for (stmt = 1; stmt <= g_statements; stmt++) {
-
-#ifdef VAXC
-    /* This line fixes an obscure bug with the VAXC compiler.  If it is taken
-       out, the variable 'stmt' does not get referenced properly when used as
-       an array index.  May be due to some boundary condition in optimization?
-       The assembly code is significantly different with this statement
-       removed. */
-    stmt = stmt;  /* Work around VAXC bug */
-#endif
-
-    g_Statement[stmt].beginScopeStatementNum = beginScopeStmtNum;
-    /* endScopeStatementNum is always 0 except in ${ statements */
-    g_Statement[stmt].endScopeStatementNum = 0; /* 24-Aug-2020 nm */
-    g_Statement[stmt].scope = g_currentScope;
-    type = g_Statement[stmt].type;
-    /******* Determine scope, stack active variables, process math strings ****/
-
-    switch (type) {
-      case lb_:
-        g_currentScope++;
-        if (g_currentScope > 32000) outOfMemory("#16 (more than 32000 \"${\"s)");
-            /* Not really an out-of-memory situation, but use the error msg. */
-        /* Note that g_Statement[stmt].beginScopeStatementNum for this ${
-           points to the previous ${ (or 0 if in outermost scope) */
-        beginScopeStmtNum = stmt; /* 24-Aug-2020 nm */
-        /* Note that g_Statement[stmt].endScopeStatementNum for this ${
-           will be assigned in the rb_ case below. */
-        break;
-      case rb_:
-        /* Remove all variables and hypotheses in current scope from stack */
-
-        while (activeConstStackPtr) {
-          if (activeConstStack[activeConstStackPtr - 1].scope < g_currentScope)
-              break;
-          activeConstStackPtr--;
-          g_MathToken[activeConstStack[activeConstStackPtr].tokenNum].active = 0;
-          g_MathToken[activeConstStack[activeConstStackPtr].tokenNum
-              ].endStatement = stmt;
-        }
-
-        while (activeVarStackPtr) {
-          if (activeVarStack[activeVarStackPtr - 1].scope < g_currentScope) break;
-          activeVarStackPtr--;
-          g_MathToken[activeVarStack[activeVarStackPtr].tokenNum].active = 0;
-          g_MathToken[activeVarStack[activeVarStackPtr].tokenNum].endStatement
-              = stmt;
-        }
-
-        while (activeEHypStackPtr) {
-          if (activeEHypStack[activeEHypStackPtr - 1].scope < g_currentScope)
-              break;
-          activeEHypStackPtr--;
-          labelActiveFlag[activeEHypStack[activeEHypStackPtr].statemNum] = 0;
-                                                   /* Make the label inactive */
-          free(activeEHypStack[activeEHypStackPtr].varList);
-        }
-        while (activeFHypStackPtr) {
-          if (activeFHypStack[activeFHypStackPtr - 1].scope < g_currentScope)
-              break;
-          activeFHypStackPtr--;
-          labelActiveFlag[activeFHypStack[activeFHypStackPtr].statemNum] = 0;
-                                                   /* Make the label inactive */
-          free(activeFHypStack[activeFHypStackPtr].varList);
-        }
-        while (activeDisjHypStackPtr) {
-          if (activeDisjHypStack[activeDisjHypStackPtr - 1].scope
-              < g_currentScope) break;
-          activeDisjHypStackPtr--;
-        }
-        g_currentScope--;
-        if (g_currentScope < 0) {
-          sourceError(g_Statement[stmt].labelSectionPtr +
-              g_Statement[stmt].labelSectionLen, 2, stmt,
-              "Too many \"$}\"s at this point.");
-        }
-
-        /* 24-Aug-2020 nm */
-        if (beginScopeStmtNum > 0) { /* We're not in outermost scope
-                  (precaution if there were too many $}'s) */
-          if (g_Statement[beginScopeStmtNum].type != lb_) bug(1773);
-          /* Populate the previous ${ with a pointer to this $} */
-          g_Statement[beginScopeStmtNum].endScopeStatementNum = stmt;
-          /* Update beginScopeStmtNum with start of outer scope */
-          beginScopeStmtNum
-              = g_Statement[beginScopeStmtNum].beginScopeStatementNum;
-        }
-
-        break;
-      case c_:
-      case v_:
-        /* Scan all symbols declared (they have already been parsed) and
-           flag them as active, add to stack, and check for errors */
-
-        /* 3-Jun-2018 nm Added this check back in (see 1-May-2018 Google
-           Group post) */
-        if (type == c_) {
-          if (g_currentScope > 0) {
-            sourceError(g_Statement[stmt].labelSectionPtr +
-                g_Statement[stmt].labelSectionLen, 2, stmt,
-        "A \"$c\" constant declaration may occur in the outermost scope only.");
-          }
-        }
-
-
-        i = 0; /* Symbol position in mathString */
-        nmbrTmpPtr = g_Statement[stmt].mathString;
-        while (1) {
-          tokenNum = nmbrTmpPtr[i];
-          if (tokenNum == -1) break; /* Done scanning symbols in $v or $c */
-          if (mathTokenSameAs[reverseMathKey[tokenNum]]) {
-            /* The variable name is not unique.  Find out if there's a
-               conflict with the others. */
-            lowerKey = reverseMathKey[tokenNum];
-            upperKey = lowerKey;
-            j = mathTokenSameAs[lowerKey];
-            while (lowerKey) {
-              if (j != mathTokenSameAs[lowerKey - 1]) break;
-              lowerKey--;
-            }
-            while (upperKey < g_mathTokens - 1) {
-              if (j != mathTokenSameAs[upperKey + 1]) break;
-              upperKey++;
-            }
-            for (j = lowerKey; j <= upperKey; j++) {
-              if (g_MathToken[g_mathKey[j]].active) {
-                /* 18-Jun-2011 nm Detect conflicting active vars declared
-                   in multiple scopes */
-                if (g_MathToken[g_mathKey[j]].scope <= g_currentScope) {
-                /* if (g_MathToken[g_mathKey[j]].scope == g_currentScope) { bad */
-                  mathTokenError(i, nmbrTmpPtr, stmt,
-                      "This symbol has already been declared in this scope.");
-                }
-              }
-            }
-
-
-            /************** Start of 9-Dec-2010 ****************/
-            /* 9-Dec-2010 nm Make sure that no constant has the same name
-               as a variable or vice-versa */
-            k = 0; /* Flag for $c */
-            m = 0; /* Flag for $v */
-            for (j = lowerKey; j <= upperKey; j++) {
-              if (g_MathToken[g_mathKey[j]].tokenType == (char)con_) k = 1;
-              if (g_MathToken[g_mathKey[j]].tokenType == (char)var_) m = 1;
-            }
-            if ((k == 1 && g_MathToken[tokenNum].tokenType == (char)var_) ||
-                (m == 1 && g_MathToken[tokenNum].tokenType == (char)con_)) {
-               mathTokenError(i, nmbrTmpPtr, stmt,
-                   "A symbol may not be both a constant and a variable.");
-            }
-            /************** End of 9-Dec-2010 ****************/
-
-          }
-
-          /* Flag the token as active */
-          g_MathToken[tokenNum].active = 1;
-          g_MathToken[tokenNum].scope = g_currentScope;
-
-          if (type == v_) {
-
-            /* Identify this stack position in the g_MathToken array, for use
-               by the hypothesis variable scan below */
-            g_MathToken[tokenNum].tmp = activeVarStackPtr;
-
-            /* Add the symbol to the stack */
-            activeVarStack[activeVarStackPtr].tokenNum = tokenNum;
-            activeVarStack[activeVarStackPtr].scope = g_currentScope;
-            activeVarStack[activeVarStackPtr].tmpFlag = 0;
-            activeVarStackPtr++;
-          } else {
-
-            /* Add the symbol to the stack */
-            activeConstStack[activeConstStackPtr].tokenNum = tokenNum;
-            activeConstStack[activeConstStackPtr].scope = g_currentScope;
-            activeConstStackPtr++;
-
-          }
-
-          i++;
-        }
-        break;
-      case d_:
-      case f_:
-      case e_:
-      case a_:
-      case p_:
-        /* Make sure we have enough working space */
-        mathSectionLen = g_Statement[stmt].mathSectionLen;
-        if (wrkLen < mathSectionLen) {
-          free(wrkNmbrPtr);
-          free(wrkStrPtr);
-          wrkLen = mathSectionLen + 100;
-          wrkNmbrPtr = malloc((size_t)wrkLen * sizeof(nmbrString));
-          if (!wrkNmbrPtr) outOfMemory("#20 (wrkNmbrPtr)");
-          wrkStrPtr = malloc((size_t)wrkLen + 1);
-          if (!wrkStrPtr) outOfMemory("#21 (wrkStrPtr)");
-        }
-
-        /* Scan the math section for tokens */
-        mathStringLen = 0;
-        fbPtr = g_Statement[stmt].mathSectionPtr;
-        while (1) {
-          fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-          origSymbolLen = tokenLen(fbPtr);
-          if (!origSymbolLen) break; /* Done scanning source line */
-
-          /* Scan for largest matching token from the left */
-         nextAdjToken:
-          /* maxSymbolLen is the longest declared symbol */
-          /* 18-Sep-2013 Disable unused old code
-          if (origSymbolLen > maxSymbolLen) {
-            symbolLen = maxSymbolLen;
-          } else {
-            symbolLen = origSymbolLen;
-          }
-          */
-
-          /* New code: don't allow missing white space */
-          symbolLen = origSymbolLen;
-
-          memcpy(wrkStrPtr, fbPtr, (size_t)symbolLen);
-
-          /* Old code: tolerate unambiguous missing white space
-          for (; symbolLen > 0; symbolLen--) {
-          */
-          /* New code: don't allow missing white space */
-          /* ???Speed-up is possible by rewriting this now unnec. code */
-          for (; symbolLen > 0; symbolLen = 0) {
-
-            /* symbolLenExists means a symbol of this length was declared */
-            if (!symbolLenExists[symbolLen]) continue;
-            wrkStrPtr[symbolLen] = 0; /* Define end of trial token to look up */
-            g_mathKeyPtr = (void *)bsearch(wrkStrPtr, g_mathKey, (size_t)g_mathTokens,
-                sizeof(long), mathSrchCmp);
-            if (!g_mathKeyPtr) continue; /* Trial token was not declared */
-            g_mathKeyNum = (long *)g_mathKeyPtr - g_mathKey; /* Pointer arithmetic! */
-            if (mathTokenSameAs[g_mathKeyNum]) { /* Multiply-declared symbol */
-              lowerKey = g_mathKeyNum;
-              upperKey = lowerKey;
-              j = mathTokenSameAs[lowerKey];
-              while (lowerKey) {
-                if (j != mathTokenSameAs[lowerKey - 1]) break;
-                lowerKey--;
-              }
-              while (upperKey < g_mathTokens - 1) {
-                if (j != mathTokenSameAs[upperKey + 1]) break;
-                upperKey++;
-              }
-              /* Find the active symbol with the most recent declaration */
-              /* (Note:  Here, 'active' means it's on the stack, not the
-                 official def.) */
-              maxScope = -1;
-              for (i = lowerKey; i <= upperKey; i++) {
-                j = g_mathKey[i];
-                if (g_MathToken[j].active) {
-                  if (g_MathToken[j].scope > maxScope) {
-                    tokenNum = j;
-                    maxScope = g_MathToken[j].scope;
-                    if (maxScope == g_currentScope) break; /* Speedup */
-                  }
-                }
-              }
-              if (maxScope == -1) {
-                tokenNum = g_mathKey[g_mathKeyNum]; /* Pick an arbitrary one */
-                sourceError(fbPtr, symbolLen, stmt,
-       "This math symbol is not active (i.e. was not declared in this scope).");
-                /*??? (This is done in 3 places. Make it a fn call & clean up?*/
-                /* Prevent stray pointers later */
-                g_MathToken[tokenNum].tmp = 0; /* Loc in active variable stack */
-                if (!activeVarStackPtr) { /* Make a ficticious entry */
-                  activeVarStack[activeVarStackPtr].tokenNum = tokenNum;
-                  activeVarStack[activeVarStackPtr].scope = g_currentScope;
-                  activeVarStack[activeVarStackPtr].tmpFlag = 0;
-                  activeVarStackPtr++;
-                }
-              }
-            } else { /* The symbol was declared only once. */
-              tokenNum = *((long *)g_mathKeyPtr);
-                  /* Same as: tokenNum = g_mathKey[g_mathKeyNum]; but faster */
-              if (!g_MathToken[tokenNum].active) {
-                sourceError(fbPtr, symbolLen, stmt,
-       "This math symbol is not active (i.e. was not declared in this scope).");
-                /* Prevent stray pointers later */
-                g_MathToken[tokenNum].tmp = 0; /* Loc in active variable stack */
-                if (!activeVarStackPtr) { /* Make a ficticious entry */
-                  activeVarStack[activeVarStackPtr].tokenNum = tokenNum;
-                  activeVarStack[activeVarStackPtr].scope = g_currentScope;
-                  activeVarStack[activeVarStackPtr].tmpFlag = 0;
-                  activeVarStackPtr++;
-                }
-              }
-            } /* End if multiply-defined symbol */
-            break; /* The symbol was found, so we are done */
-          } /* Next symbolLen */
-          if (symbolLen == 0) { /* Symbol was not found */
-            symbolLen = tokenLen(fbPtr);
-            sourceError(fbPtr, symbolLen, stmt,
-      "This math symbol was not declared (with a \"$c\" or \"$v\" statement).");
-            /* Call the symbol a dummy token of type variable so that spurious
-               errors (constants in $d's) won't be flagged also.  Prevent
-               stray pointer to active variable stack. */
-            undeclErrorCount++;
-            tokenNum = g_mathTokens + undeclErrorCount;
-            if (tokenNum >= g_MAX_MATHTOKENS) {
-              /* 21-Aug-04 nm */
-              /* There are current 100 places for bad tokens */
-              print2(
-"?Error: The temporary space for holding bad tokens has run out, because\n");
-              print2(
-"there are too many errors.  Therefore we will force an \"out of memory\"\n");
-              print2("program abort:\n");
-              outOfMemory("#33 (too many errors)");
-            }
-            g_MathToken[tokenNum].tokenName = "";
-            let(&g_MathToken[tokenNum].tokenName, left(fbPtr,symbolLen));
-            g_MathToken[tokenNum].length = symbolLen;
-            g_MathToken[tokenNum].tokenType = (char)var_;
-            /* Prevent stray pointers later */
-            g_MathToken[tokenNum].tmp = 0; /* Location in active variable stack */
-            if (!activeVarStackPtr) { /* Make a ficticious entry */
-              activeVarStack[activeVarStackPtr].tokenNum = tokenNum;
-              activeVarStack[activeVarStackPtr].scope = g_currentScope;
-              activeVarStack[activeVarStackPtr].tmpFlag = 0;
-              activeVarStackPtr++;
-            }
-          }
-
-          if (type == d_) {
-            if (g_MathToken[tokenNum].tokenType == (char)con_) {
-              sourceError(fbPtr, symbolLen, stmt,
-                  "Constant symbols are not allowed in a \"$d\" statement.");
-            }
-          } else {
-            if (mathStringLen == 0) {
-              if (g_MathToken[tokenNum].tokenType != (char)con_) {
-                sourceError(fbPtr, symbolLen, stmt, cat(
-                    "The first symbol must be a constant in a \"$",
-                    chr(type), "\" statement.", NULL));
-              }
-            } else {
-              if (type == f_) {
-                if (mathStringLen == 1) {
-                  if (g_MathToken[tokenNum].tokenType == (char)con_) {
-                    sourceError(fbPtr, symbolLen, stmt,
-                "The second symbol must be a variable in a \"$f\" statement.");
-                  }
-                } else {
-                  if (mathStringLen == 2) {
-                    sourceError(fbPtr, symbolLen, stmt,
-               "There cannot be more than two symbols in a \"$f\" statement.");
-                  }
-                }
-              }
-            }
-          }
-
-          /* Add symbol to mathString */
-          wrkNmbrPtr[mathStringLen] = tokenNum;
-          mathStringLen++;
-          fbPtr = fbPtr + symbolLen; /* Move on to next symbol */
-
-          if (symbolLen < origSymbolLen) {
-            /* This symbol is not separated from next by white space */
-            /* Speed-up: don't call tokenLen again; just jump past it */
-            origSymbolLen = origSymbolLen - symbolLen;
-            goto nextAdjToken; /* (Instead of continue) */
-          }
-        } /* End while */
-
-        if (type == d_) {
-          if (mathStringLen < 2) {
-            sourceError(fbPtr, 2, stmt,
-                "A \"$d\" statement requires at least two variable symbols.");
-          }
-        } else {
-          if (!mathStringLen) {
-            sourceError(fbPtr, 2, stmt,
-                "This statement type requires at least one math symbol.");
-          } else {
-            if (type == f_ && mathStringLen < 2) {
-              sourceError(fbPtr, 2, stmt,
-                  "A \"$f\" statement requires two math symbols.");
-            }
-          }
-        }
-
-
-        /* Assign mathString to statement array */
-        nmbrTmpPtr = poolFixedMalloc(
-            (mathStringLen + 1) * (long)(sizeof(nmbrString)));
-        /*if (!nmbrTmpPtr) outOfMemory("#24 (mathString)");*/ /*???Not nec. w/ poolMalloc */
-        for (i = 0; i < mathStringLen; i++) {
-          nmbrTmpPtr[i] = wrkNmbrPtr[i];
-        }
-        nmbrTmpPtr[mathStringLen] = -1;
-        g_Statement[stmt].mathString = nmbrTmpPtr;
-        g_Statement[stmt].mathStringLen = mathStringLen;
-/*E*/if(db5){if(stmt<5)print2("Statement %ld mathString: %s.\n",stmt,
-/*E*/  nmbrCvtMToVString(nmbrTmpPtr)); if(stmt==5)print2("(etc.)\n");}
-
-        break;  /* Switch case break */
-      default:
-        bug(1707);
-
-    } /* End switch */
-
-    /****** Process hypothesis and variable stacks *******/
-    /* (The switch section above does not depend on what is done in this
-       section, although this section assumes the above section has been done.
-       Variables valid only in this pass of the above section are so
-       indicated.) */
-
-    switch (type) {
-      case f_:
-      case e_:
-      case a_:
-      case p_:
-        /* These types have labels.  Make the label active, and make sure that
-           there is no other identical label that is also active. */
-        /* (If the label name is unique, we don't have to worry about this.) */
-        /* 17-Sep-05 nm - This check is no longer needed since all labels
-           must now be unique according to strict spec (see the other comment
-           for this date above).  So the code below was commented out. */
-        /*
-        if (labelTokenSameAs[reverseLabelKey[stmt]]) {
-          /@ The label is not unique.  Find out if there's a
-             conflict with the others. @/
-          lowerKey = reverseLabelKey[stmt];
-          upperKey = lowerKey;
-          j = labelTokenSameAs[lowerKey];
-          while (lowerKey > 1) {
-            if (j != labelTokenSameAs[lowerKey - 1]) break;
-            lowerKey--;
-          }
-          while (upperKey < g_statements) {
-            if (j != labelTokenSameAs[upperKey + 1]) break;
-            upperKey++;
-          }
-          for (j = lowerKey; j <= upperKey; j++) {
-            if (labelActiveFlag[g_labelKey[j]]) {
-              fbPtr = g_Statement[stmt].labelSectionPtr;
-              fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-              sourceError(fbPtr, tokenLen(fbPtr), g_labelKey[j], cat(
-                  "This label name is currently active (i.e. in use).",
-                  "  It became active at statement ",
-                  str(g_labelKey[j]),
-                  ", line ", str(g_Statement[g_labelKey[j]].lineNum),
-                  ", file \"", g_Statement[g_labelKey[j]].fileName,
-                  "\".  Use another name for this label.", NULL));
-              break;
-            }
-          }
-        }
-        */
-
-        /* Flag the label as active */
-        labelActiveFlag[stmt] = 1;
-
-    } /* End switch */
-
-
-    switch (type) {
-      case d_:
-
-        nmbrTmpPtr = g_Statement[stmt].mathString;
-        /* Stack all possible pairs of disjoint variables */
-        for (i = 0; i < mathStringLen - 1; i++) { /* mathStringLen is from the
-             above switch section; it is valid only in this pass of the above
-             section. */
-          p = nmbrTmpPtr[i];
-          for (j = i + 1; j < mathStringLen; j++) {
-            n = nmbrTmpPtr[j];
-            /* Get the disjoint variable pair m and n, sorted by tokenNum */
-            if (p < n) {
-              m = p;
-            } else {
-              if (p > n) {
-                /* Swap them */
-                m = n;
-                n = p;
-              } else {
-                mathTokenError(j, nmbrTmpPtr, stmt,
-                    "All variables in a \"$d\" statement must be unique.");
-                break;
-              }
-            }
-            /* See if this pair of disjoint variables is already on the stack;
-               if so, don't add it again */
-            for (k = 0; k < activeDisjHypStackPtr; k++) {
-              if (m == activeDisjHypStack[k].tokenNumA)
-                if (n == activeDisjHypStack[k].tokenNumB)
-                  break; /* It matches */
-            }
-            if (k == activeDisjHypStackPtr) {
-              /* It wasn't already on the stack, so add it. */
-              /* Increase stack size if necessary */
-              if (activeDisjHypStackPtr >= activeDisjHypStackSize) {
-                free(wrkDisjHPtr1A);
-                free(wrkDisjHPtr1B);
-                free(wrkDisjHPtr1Stmt);
-                free(wrkDisjHPtr2A);
-                free(wrkDisjHPtr2B);
-                free(wrkDisjHPtr2Stmt);
-                activeDisjHypStackSize = activeDisjHypStackSize + 100;
-                activeDisjHypStack = realloc(activeDisjHypStack,
-                    (size_t)activeDisjHypStackSize
-                    * sizeof(struct activeDisjHypStack_struct));
-                wrkDisjHPtr1A = malloc((size_t)activeDisjHypStackSize
-                    * sizeof(nmbrString));
-                wrkDisjHPtr1B = malloc((size_t)activeDisjHypStackSize
-                    * sizeof(nmbrString));
-                wrkDisjHPtr1Stmt = malloc((size_t)activeDisjHypStackSize
-                    * sizeof(nmbrString));
-                wrkDisjHPtr2A = malloc((size_t)activeDisjHypStackSize
-                    * sizeof(nmbrString));
-                wrkDisjHPtr2B = malloc((size_t)activeDisjHypStackSize
-                    * sizeof(nmbrString));
-                wrkDisjHPtr2Stmt = malloc((size_t)activeDisjHypStackSize
-                    * sizeof(nmbrString));
-                if (!activeDisjHypStack
-                    || !wrkDisjHPtr1A || !wrkDisjHPtr1B || !wrkDisjHPtr1Stmt
-                    || !wrkDisjHPtr2A || !wrkDisjHPtr2B || !wrkDisjHPtr2Stmt)
-                    outOfMemory("#28 (activeDisjHypStack)");
-              }
-              activeDisjHypStack[activeDisjHypStackPtr].tokenNumA = m;
-              activeDisjHypStack[activeDisjHypStackPtr].tokenNumB = n;
-              activeDisjHypStack[activeDisjHypStackPtr].scope = g_currentScope;
-              activeDisjHypStack[activeDisjHypStackPtr].statemNum = stmt;
-
-              activeDisjHypStackPtr++;
-            }
-
-          } /* Next j */
-        } /* Next i */
-
-        break; /* Switch case break */
-
-      case f_:
-      case e_:
-
-        /* Increase stack size if necessary */
-        /* For convenience, we will keep the size greater than the sum of
-           active $e and $f hypotheses, as this is the size needed for the
-           wrkHypPtr's, even though it wastes (temporary) memory for the
-           activeE and activeF structure arrays. */
-        if (activeEHypStackPtr + activeFHypStackPtr >= activeHypStackSize) {
-          free(wrkHypPtr1);
-          free(wrkHypPtr2);
-          free(wrkHypPtr3);
-          activeHypStackSize = activeHypStackSize + 100;
-          activeEHypStack = realloc(activeEHypStack, (size_t)activeHypStackSize
-              * sizeof(struct activeEHypStack_struct));
-          activeFHypStack = realloc(activeFHypStack, (size_t)activeHypStackSize
-              * sizeof(struct activeFHypStack_struct));
-          wrkHypPtr1 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
-          wrkHypPtr2 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
-          wrkHypPtr3 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
-          if (!activeEHypStack || !activeFHypStack || !wrkHypPtr1 ||
-              !wrkHypPtr2 || !wrkHypPtr3) outOfMemory("#32 (activeHypStack)");
-        }
-
-        /* Add the hypothesis to the stack */
-        if (type == e_) {
-          activeEHypStack[activeEHypStackPtr].statemNum = stmt;
-          activeEHypStack[activeEHypStackPtr].scope = g_currentScope;
-        } else {
-          activeFHypStack[activeFHypStackPtr].statemNum = stmt;
-          activeFHypStack[activeFHypStackPtr].scope = g_currentScope;
-        }
-
-        /* Create the list of variables used by this hypothesis */
-        reqVars = 0;
-        j = 0;
-        nmbrTmpPtr = g_Statement[stmt].mathString;
-        k = nmbrTmpPtr[j]; /* Math symbol number */
-        while (k != -1) {
-          if (g_MathToken[k].tokenType == (char)var_) {
-            if (!activeVarStack[g_MathToken[k].tmp].tmpFlag) {
-              /* Variable has not been already added to list */
-              wrkVarPtr1[reqVars] = k;
-              reqVars++;
-              activeVarStack[g_MathToken[k].tmp].tmpFlag = 1;
-            }
-          }
-          j++;
-          k = nmbrTmpPtr[j];
-        }
-        nmbrTmpPtr = malloc(((size_t)reqVars + 1) * sizeof(nmbrString));
-        if (!nmbrTmpPtr) outOfMemory("#32 (hypothesis variables)");
-        memcpy(nmbrTmpPtr, wrkVarPtr1, (size_t)reqVars * sizeof(nmbrString));
-        nmbrTmpPtr[reqVars] = -1;
-        /* Clear the variable flags for future re-use */
-        for (i = 0; i < reqVars; i++) {
-          activeVarStack[g_MathToken[nmbrTmpPtr[i]].tmp].tmpFlag = 0;
-        }
-
-        if (type == e_) {
-          activeEHypStack[activeEHypStackPtr].varList = nmbrTmpPtr;
-          activeEHypStackPtr++;
-        } else {
-          /* Taken care of earlier.
-          if (nmbrTmpPtr[0] == -1) {
-            sourceError(g_Statement[stmt].mathSectionPtr +
-                g_Statement[stmt].mathSectionLen, 2, stmt,
-                "A \"$f\" statement requires at least one variable.");
-          }
-          */
-          activeFHypStack[activeFHypStackPtr].varList = nmbrTmpPtr;
-          activeFHypStackPtr++;
-        }
-
-        break;  /* Switch case break */
-
-      case a_:
-      case p_:
-
-        /* Scan this statement for required variables */
-        reqVars = 0;
-        j = 0;
-        nmbrTmpPtr = g_Statement[stmt].mathString;
-        k = nmbrTmpPtr[j]; /* Math symbol number */
-        while (k != -1) {
-          if (g_MathToken[k].tokenType == (char)var_) {
-            if (!activeVarStack[g_MathToken[k].tmp].tmpFlag) {
-              /* Variable has not been already added to list */
-              wrkVarPtr1[reqVars] = k;
-              reqVars++;
-              activeVarStack[g_MathToken[k].tmp].tmpFlag = 2;
-                       /* 2 means it's an original variable in the assertion */
-                       /* (For error-checking) */
-            }
-          }
-          j++;
-          k = nmbrTmpPtr[j];
-        }
-
-        /* Scan $e stack for required variables and required hypotheses */
-        for (i = 0; i < activeEHypStackPtr; i++) {
-
-          /* Add $e hypotheses to required list */
-          wrkHypPtr1[i] = activeEHypStack[i].statemNum;
-
-          /* Add the $e's variables to required variable list */
-          nmbrTmpPtr = activeEHypStack[i].varList;
-          j = 0; /* Location in variable list */
-          k = nmbrTmpPtr[j]; /* Symbol number of variable */
-          while (k != -1) {
-            if (!activeVarStack[g_MathToken[k].tmp].tmpFlag) {
-              /* Variable has not been already added to list */
-              wrkVarPtr1[reqVars] = k;
-              reqVars++;
-            }
-            activeVarStack[g_MathToken[k].tmp].tmpFlag = 1;
-                            /* Could have been 0 or 2; 1 = in some hypothesis */
-            j++;
-            k = nmbrTmpPtr[j];
-          }
-        }
-
-        reqHyps = activeEHypStackPtr; /* The number of required hyp's so far */
-
-        /* We have finished determining required variables, so allocate the
-           permanent list for the statement array */
-        nmbrTmpPtr = poolFixedMalloc((reqVars + 1)
-            * (long)(sizeof(nmbrString)));
-        /* if (!nmbrTmpPtr) outOfMemory("#30 (reqVars)"); */
-                                                    /* Not nec. w/ poolMalloc */
-        memcpy(nmbrTmpPtr, wrkVarPtr1, (size_t)reqVars * sizeof(nmbrString));
-        nmbrTmpPtr[reqVars] = -1;
-        g_Statement[stmt].reqVarList = nmbrTmpPtr;
-
-        /* Scan the list of $f hypotheses to find those that are required */
-        optHyps = 0;
-        for (i = 0; i < activeFHypStackPtr; i++) {
-          nmbrTmpPtr = activeFHypStack[i].varList; /* Variable list */
-          tokenNum = nmbrTmpPtr[0];
-          if (tokenNum == -1) {
-            /* Default if no variables (an error in current version): */
-            /* Add it to list of required hypotheses */
-            wrkHypPtr1[reqHyps] = activeFHypStack[i].statemNum;
-            reqHyps++;
-            continue;
-          } else {
-            reqFlag = activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag;
-          }
-          if (reqFlag) {
-            /* Add it to list of required hypotheses */
-            wrkHypPtr1[reqHyps] = activeFHypStack[i].statemNum;
-            reqHyps++;
-            reqFlag = 1;
-            activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag = 1;
-                                /* Could have been 2; 1 = in some hypothesis */
-          } else {
-            /* Add it to list of optional hypotheses */
-            wrkHypPtr2[optHyps] = activeFHypStack[i].statemNum;
-            optHyps++;
-          }
-
-          /* Scan the other variables in the $f hyp to check for conflicts. */
-          j = 1;
-          tokenNum = nmbrTmpPtr[1];
-          while (tokenNum != -1) {
-            if (activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag == 2) {
-              activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag = 1;
-                                /* 2 = in $p; 1 = in some hypothesis */
-            }
-            if (reqFlag != activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag) {
-              k = activeFHypStack[i].statemNum;
-              m = nmbrElementIn(1, g_Statement[k].mathString, tokenNum);
-              n = nmbrTmpPtr[0];
-              if (reqFlag) {
-                mathTokenError(m - 1, g_Statement[k].mathString, k,
-                    cat("This variable does not occur in statement ",
-                    str((double)stmt)," (label \"",g_Statement[stmt].labelName,
-                    "\") or statement ", str((double)stmt),
-                    "'s \"$e\" hypotheses, whereas variable \"",
-                    g_MathToken[n].tokenName,
-                   "\" DOES occur.  A \"$f\" hypothesis may not contain such a",
-                    " mixture of variables.",NULL));
-              } else {
-                mathTokenError(m - 1, g_Statement[k].mathString, k,
-                    cat("This variable occurs in statement ",
-                    str((double)stmt)," (label \"",g_Statement[stmt].labelName,
-                    "\") or statement ", str((double)stmt),
-                    "'s \"$e\" hypotheses, whereas variable \"",
-                    g_MathToken[n].tokenName,
-               "\" does NOT occur.  A \"$f\" hypothesis may not contain such a",
-                    " mixture of variables.",NULL));
-              }
-              break;
-            } /* End if */
-            j++;
-            tokenNum = nmbrTmpPtr[j];
-          } /* End while */
-
-        } /* Next i */
-
-
-        /* Error check:  make sure that all variables in the original statement
-           appeared in some hypothesis */
-        j = 0;
-        nmbrTmpPtr = g_Statement[stmt].mathString;
-        k = nmbrTmpPtr[j]; /* Math symbol number */
-        while (k != -1) {
-          if (g_MathToken[k].tokenType == (char)var_) {
-            if (activeVarStack[g_MathToken[k].tmp].tmpFlag == 2) {
-              /* The variable did not appear in any hypothesis */
-              mathTokenError(j, g_Statement[stmt].mathString, stmt,
-                    cat("This variable does not occur in any active ",
-                    "\"$e\" or \"$f\" hypothesis.  All variables in \"$a\" and",
-                    " \"$p\" statements must appear in at least one such",
-                    " hypothesis.",NULL));
-              activeVarStack[g_MathToken[k].tmp].tmpFlag = 1; /* One msg per var*/
-            }
-          }
-          j++;
-          k = nmbrTmpPtr[j];
-        }
-
-
-        /* We have finished determining required $e & $f hyps, so allocate the
-           permanent list for the statement array */
-        /* First, sort the required hypotheses by statement number order
-           into wrkHypPtr3 */
-        i = 0; /* Start of $e's in wrkHypPtr1 */
-        j = activeEHypStackPtr; /* Start of $f's in wrkHypPtr1 */
-        for (k = 0; k < reqHyps; k++) {
-          if (i >= activeEHypStackPtr) {
-            wrkHypPtr3[k] = wrkHypPtr1[j];
-            j++;
-            continue;
-          }
-          if (j >= reqHyps) {
-            wrkHypPtr3[k] = wrkHypPtr1[i];
-            i++;
-            continue;
-          }
-          if (wrkHypPtr1[i] > wrkHypPtr1[j]) {
-            wrkHypPtr3[k] = wrkHypPtr1[j];
-            j++;
-          } else {
-            wrkHypPtr3[k] = wrkHypPtr1[i];
-            i++;
-          }
-        }
-
-        /* Now do the allocation */
-        nmbrTmpPtr = poolFixedMalloc((reqHyps + 1)
-            * (long)(sizeof(nmbrString)));
-        /* if (!nmbrTmpPtr) outOfMemory("#33 (reqHyps)"); */
-                                       /* Not nec. w/ poolMalloc */
-        memcpy(nmbrTmpPtr, wrkHypPtr3, (size_t)reqHyps * sizeof(nmbrString));
-        nmbrTmpPtr[reqHyps] = -1;
-        g_Statement[stmt].reqHypList = nmbrTmpPtr;
-        g_Statement[stmt].numReqHyp = reqHyps;
-
-        /* We have finished determining optional $f hyps, so allocate the
-           permanent list for the statement array */
-        if (type == p_) { /* Optional ones are not used by $a statements */
-          nmbrTmpPtr = poolFixedMalloc((optHyps + 1)
-              * (long)(sizeof(nmbrString)));
-          /* if (!nmbrTmpPtr) outOfMemory("#34 (optHyps)"); */ /* Not nec. w/ poolMalloc */
-          memcpy(nmbrTmpPtr, wrkHypPtr2, (size_t)optHyps * sizeof(nmbrString));
-          nmbrTmpPtr[optHyps] = -1;
-          g_Statement[stmt].optHypList = nmbrTmpPtr;
-        }
-
-
-        /* Scan the list of disjoint variable ($d) hypotheses to find those
-           that are required */
-        optHyps = 0;
-        reqHyps = 0;
-        for (i = 0; i < activeDisjHypStackPtr; i++) {
-          m = activeDisjHypStack[i].tokenNumA; /* First var in disjoint pair */
-          n = activeDisjHypStack[i].tokenNumB; /* 2nd var in disjoint pair */
-          if (activeVarStack[g_MathToken[m].tmp].tmpFlag &&
-              activeVarStack[g_MathToken[n].tmp].tmpFlag) {
-            /* Both variables in the disjoint pair are required, so put the
-               disjoint hypothesis in the required list. */
-            wrkDisjHPtr1A[reqHyps] = m;
-            wrkDisjHPtr1B[reqHyps] = n;
-            wrkDisjHPtr1Stmt[reqHyps] =
-                activeDisjHypStack[i].statemNum;
-            reqHyps++;
-          } else {
-            /* At least one variable is not required, so the disjoint hypothesis\
-               is not required. */
-            wrkDisjHPtr2A[optHyps] = m;
-            wrkDisjHPtr2B[optHyps] = n;
-            wrkDisjHPtr2Stmt[optHyps] =
-                activeDisjHypStack[i].statemNum;
-            optHyps++;
-          }
-        }
-
-        /* We have finished determining required $d hyps, so allocate the
-           permanent list for the statement array */
-
-        nmbrTmpPtr = poolFixedMalloc((reqHyps + 1)
-            * (long)(sizeof(nmbrString)));
-        /* if (!nmbrTmpPtr) outOfMemory("#40 (reqDisjHyps)"); */ /* Not nec. w/ poolMalloc */
-        memcpy(nmbrTmpPtr, wrkDisjHPtr1A, (size_t)reqHyps
-            * sizeof(nmbrString));
-        nmbrTmpPtr[reqHyps] = -1;
-        g_Statement[stmt].reqDisjVarsA = nmbrTmpPtr;
-
-        nmbrTmpPtr = poolFixedMalloc((reqHyps + 1)
-            * (long)(sizeof(nmbrString)));
-        /* if (!nmbrTmpPtr) outOfMemory("#41 (reqDisjHyps)"); */ /* Not nec. w/ poolMalloc */
-        memcpy(nmbrTmpPtr, wrkDisjHPtr1B, (size_t)reqHyps
-            * sizeof(nmbrString));
-        nmbrTmpPtr[reqHyps] = -1;
-        g_Statement[stmt].reqDisjVarsB = nmbrTmpPtr;
-
-        nmbrTmpPtr = poolFixedMalloc((reqHyps + 1)
-            * (long)(sizeof(nmbrString)));
-        /* if (!nmbrTmpPtr) outOfMemory("#42 (reqDisjHyps)"); */ /* Not nec. w/ poolMalloc */
-        memcpy(nmbrTmpPtr, wrkDisjHPtr1Stmt, (size_t)reqHyps
-            * sizeof(nmbrString));
-        nmbrTmpPtr[reqHyps] = -1;
-        g_Statement[stmt].reqDisjVarsStmt = nmbrTmpPtr;
-
-        /* We have finished determining optional $d hyps, so allocate the
-           permanent list for the statement array */
-
-        if (type == p_) { /* Optional ones are not used by $a statements */
-
-          nmbrTmpPtr = poolFixedMalloc((optHyps + 1)
-              * (long)(sizeof(nmbrString)));
-          /* if (!nmbrTmpPtr) outOfMemory("#43 (optDisjHyps)"); */ /* Not nec. w/ poolMalloc */
-          memcpy(nmbrTmpPtr, wrkDisjHPtr2A, (size_t)optHyps
-              * sizeof(nmbrString));
-          nmbrTmpPtr[optHyps] = -1;
-          g_Statement[stmt].optDisjVarsA = nmbrTmpPtr;
-
-          nmbrTmpPtr = poolFixedMalloc((optHyps + 1)
-              * (long)(sizeof(nmbrString)));
-          /* if (!nmbrTmpPtr) outOfMemory("#44 (optDisjHyps)"); */ /* Not nec. w/ poolMalloc */
-          memcpy(nmbrTmpPtr, wrkDisjHPtr2B, (size_t)optHyps
-              * sizeof(nmbrString));
-          nmbrTmpPtr[optHyps] = -1;
-          g_Statement[stmt].optDisjVarsB = nmbrTmpPtr;
-
-          nmbrTmpPtr = poolFixedMalloc((optHyps + 1)
-              * (long)(sizeof(nmbrString)));
-          /* if (!nmbrTmpPtr) outOfMemory("#45 (optDisjHyps)"); */ /* Not nec. w/ poolMalloc */
-          memcpy(nmbrTmpPtr, wrkDisjHPtr2Stmt, (size_t)optHyps
-              * sizeof(nmbrString));
-          nmbrTmpPtr[optHyps] = -1;
-          g_Statement[stmt].optDisjVarsStmt = nmbrTmpPtr;
-
-        }
-
-
-        /* Create list of optional variables (i.e. active but not required) */
-        optVars = 0;
-        for (i = 0; i < activeVarStackPtr; i++) {
-          if (activeVarStack[i].tmpFlag) {
-            activeVarStack[i].tmpFlag = 0; /* Clear it for future use */
-          } else {
-            wrkVarPtr2[optVars] = activeVarStack[i].tokenNum;
-            optVars++;
-          }
-        }
-        /* We have finished determining optional variables, so allocate the
-           permanent list for the statement array */
-        if (type == p_) { /* Optional ones are not used by $a statements */
-          nmbrTmpPtr = poolFixedMalloc((optVars + 1)
-              * (long)(sizeof(nmbrString)));
-          /* if (!nmbrTmpPtr) outOfMemory("#31 (optVars)"); */ /* Not nec. w/ poolMalloc */
-          memcpy(nmbrTmpPtr, wrkVarPtr2, (size_t)optVars * sizeof(nmbrString));
-          nmbrTmpPtr[optVars] = -1;
-          g_Statement[stmt].optVarList = nmbrTmpPtr;
-        }
-
-        if (optVars + reqVars != activeVarStackPtr) bug(1708);
-
-
-        break;  /* Switch case break */
-    }
-
-    /************** Start of 27-Sep-2010 ****************/
-    /* 27-Sep-2010 nm If a $a statement consists of a single constant,
-       e.g. "$a wff $.", it means an empty expression (wff) is allowed.
-       Before the user had to allow this manually with
-       SET EMPTY_SUBSTITUTION ON; now it is done automatically. */
-    type = g_Statement[stmt].type;
-    if (type == a_) {
-      if (g_minSubstLen) {
-        if (g_Statement[stmt].mathStringLen == 1) {
-          g_minSubstLen = 0;
-          printLongLine(cat("SET EMPTY_SUBSTITUTION was",
-             " turned ON (allowed) for this database.", NULL),
-             "    ", " ");
-          /* More detailed but more distracting message:
-          printLongLine(cat("Statement \"", g_Statement[stmt].labelName,
-             "\"  line ", str(g_Statement[stmt].lineNum),
-             " allows empty expressions, so SET EMPTY_SUBSTITUTION was",
-             " turned ON (allowed) for this database.", NULL),
-             "    ", " ");
-          */
-        }
-      }
-    }
-    /************** End of 27-Sep-2010 ****************/
-
-    /************** Start of 25-Sep-2010 ****************/
-    /* 25-Sep-2010 nm Ensure the current Metamath spec is met:  "There may
-       not be be two active $f statements containing the same variable.  Each
-       variable in a $e, $a, or $p statement must exist in an active $f
-       statement."  (Metamath book, p. 94) */
-    /* This section of code is stand-alone and may be removed without side
-       effects (other than less stringent error checking). */
-    /* ??? To do (maybe):  This might be better placed in-line with the scan
-       above, for faster speed and to get the pointer to the token for the
-       error message, but it would require a careful code analysis above. */
-    if (type == a_ || type == p_) {
-      /* Scan each hypothesis (and the statement itself in last pass) */
-      reqHyps = nmbrLen(g_Statement[stmt].reqHypList);
-      for (i = 0; i <= reqHyps; i++) {
-        if (i < reqHyps) {
-          m = (g_Statement[stmt].reqHypList)[i];
-        } else {
-          m = stmt;
-        }
-        if (g_Statement[m].type != f_) { /* Check $e,$a,$p */
-          /* This block implements: "Each variable in a $e, $a, or $p
-             statement must exist in an active $f statement" (Metamath
-             book p. 94). */
-          nmbrTmpPtr = g_Statement[m].mathString;
-          /* Scan all the vars in the $e (i<reqHyps) or $a/$p (i=reqHyps) */
-          for (j = 0; j < g_Statement[m].mathStringLen; j++) {
-            tokenNum = nmbrTmpPtr[j];
-            if (g_MathToken[tokenNum].tokenType == (char)con_) continue;
-                                            /* Ignore constants */
-            p = 0;  /* Initialize flag that we found a $f with the variable */
-            /* Scan all the mandatory $f's before this $e,$a,$p */
-            for (k = 0; k < i; k++) {
-              n = (g_Statement[stmt].reqHypList)[k];
-              if (g_Statement[n].type != f_) continue; /* Only check $f */
-              if (g_Statement[n].mathStringLen != 2) continue; /* This was
-                  already verified earlier; but if there was an error, don't
-                  cause memory violation by going out of bounds */
-              if ((g_Statement[n].mathString)[1] == tokenNum) {
-                p = 1;  /* Set flag that we found a $f with the variable */
-                break;
-              }
-            } /* next k ($f hyp scan) */
-            if (!p) {
-              sourceError(g_Statement[m].mathSectionPtr/*fbPtr*/,
-                  0/*tokenLen*/,
-                  m/*stmt*/, cat(
-                  "The variable \"", g_MathToken[tokenNum].tokenName,
-                  "\" does not appear in an active \"$f\" statement.", NULL));
-            }
-          } /* next j (variable scan) */
-        } else { /* g_Statement[m].type == f_ */
-          /* This block implements: "There may not be be two active $f
-             statements containing the same variable" (Metamath book p. 94). */
-          /* Check for duplicate vars in active $f's */
-          if (g_Statement[m].mathStringLen != 2) continue;  /* This was
-                  already verified earlier; but if there was an error, don't
-                  cause memory violation by going out of bounds */
-          tokenNum = (g_Statement[m].mathString)[1];
-          /* Scan all the mandatory $f's before this $f */
-          for (k = 0; k < i; k++) {
-            n = (g_Statement[stmt].reqHypList)[k];
-            if (g_Statement[n].type != f_) continue; /* Only check $f */
-            if (g_Statement[n].mathStringLen != 2) continue;  /* This was
-                  already verified earlier; but if there was an error, don't
-                  cause memory violation by going out of bounds */
-            if ((g_Statement[n].mathString)[1] == tokenNum) {
-              /* We found 2 $f's with the same variable */
-              assignStmtFileAndLineNum(n); /* 9-Jan-2018 nm */
-              sourceError(g_Statement[m].mathSectionPtr/*fbPtr*/,
-                  0/*tokenLen*/,
-                  m/*stmt*/, cat(
-                  "The variable \"", g_MathToken[tokenNum].tokenName,
-                "\" already appears in the earlier active \"$f\" statement \"",
-                  g_Statement[n].labelName, "\" on line ",
-                  str((double)(g_Statement[n].lineNum)),
-                  " in file \"",           /* 9-Jan-2018 nm */
-                  g_Statement[n].fileName,   /* 9-Jan-2018 nm */
-                  "\".", NULL));
-              break; /* Optional: suppresses add'l error msgs for this stmt */
-            }
-          } /* next k ($f hyp scan) */
-        } /* if not $f else is $f */
-      } /* next i ($e hyp scan of this statement, or its $a/$p) */
-    } /* if stmt is $a or $p */
-    /************** End of 25-Sep-2010 ****************/
-
-  } /* Next stmt */
-
-  if (g_currentScope > 0) {
-    if (g_currentScope == 1) {
-      let(&tmpStr,"A \"$}\" is");
-    } else {
-      let(&tmpStr,cat(str((double)g_currentScope)," \"$}\"s are",NULL));
-    }
-    sourceError(g_Statement[g_statements].labelSectionPtr +
-        g_Statement[g_statements].labelSectionLen, 2, 0,
-        cat(tmpStr," missing at the end of the file.",NULL));
-  }
-
-
-  /* Filter out all hypothesis labels from the label key array.  We do not
-     need them anymore, since they are stored locally in each statement
-     structure.  Removing them will speed up lookups during proofs, and
-     will prevent a lookup from finding an inactive hypothesis label (thus
-     forcing an error message). */
-  j = 0;
-/*E*/if(db5)print2("Number of label keys before filter: %ld",g_numLabelKeys);
-  for (i = 0; i < g_numLabelKeys; i++) {
-    type = g_Statement[g_labelKeyBase[i]].type;
-    if (type == e_ || type == f_) {
-      j++;
-    } else {
-      g_labelKeyBase[i - j] = g_labelKeyBase[i];
-    }
-  }
-  g_numLabelKeys = g_numLabelKeys - j;
-/*E*/if(db5)print2(".  After: %ld\n",g_numLabelKeys);
-
-
-  /* Deallocate temporary space */
-  free(mathTokenSameAs);
-  free(reverseMathKey);
-  free(labelTokenSameAs);
-  free(reverseLabelKey);
-  free(labelActiveFlag);
-  free(activeConstStack);
-  free(activeVarStack);
-  free(wrkVarPtr1);
-  free(wrkVarPtr2);
-  for (i = 0; i < activeEHypStackPtr; i++) {
-    free(activeEHypStack[i].varList);
-  }
-  free(activeEHypStack);
-  for (i = 0; i < activeFHypStackPtr; i++) {
-    free(activeFHypStack[i].varList);
-  }
-  free(activeFHypStack);
-  free(wrkHypPtr1);
-  free(wrkHypPtr2);
-  free(wrkHypPtr3);  /* 28-Aug-2013 am - added missing free */
-  free(activeDisjHypStack);
-  free(wrkDisjHPtr1A);
-  free(wrkDisjHPtr1B);
-  free(wrkDisjHPtr1Stmt);
-  free(wrkDisjHPtr2A);
-  free(wrkDisjHPtr2B);
-  free(wrkDisjHPtr2Stmt);
-  free(wrkNmbrPtr);
-  free(wrkStrPtr);
-  free(symbolLenExists);
-  let(&tmpStr, "");
-
-
-
-}
-
-
-/* Parse proof of one statement in source file.  Uses g_WrkProof structure. */
-/* Returns 0 if OK; returns 1 if proof is incomplete (is empty or has '?'
-   tokens);  returns 2 if error found; returns 3 if severe error found
-   (e.g. RPN stack violation); returns 4 if not a $p statement */
-char parseProof(long statemNum)
-{
-
-  long i, j, k, m, tok, step;
-  char *fbPtr;
-  long tokLength;
-  long numReqHyp;
-  long numOptHyp;
-  long numActiveHyp;
-  char zapSave;
-  flag labelFlag;
-  char returnFlag = 0;
-  nmbrString *nmbrTmpPtr;
-  void *voidPtr; /* bsearch returned value */
-  vstring tmpStrPtr;
-
-  /* 25-Jan-2016 nm */
-  flag explicitTargets = 0; /* Proof is of form <target>=<source> */
-  /* Source file pointers and token sizes for targets in a /EXPLICIT proof */
-  pntrString *targetPntr = NULL_PNTRSTRING; /* Pointers to target tokens */
-  nmbrString *targetNmbr = NULL_NMBRSTRING; /* Size of target tokens */
-  /* Variables for rearranging /EXPLICIT proof */
-  nmbrString *wrkProofString = NULL_NMBRSTRING; /* Holds g_WrkProof.proofString */
-  long hypStepNum, hypSubProofLen, conclSubProofLen;
-  long matchingHyp;
-  nmbrString *oldStepNums = NULL_NMBRSTRING; /* Just numbers 0 to numSteps-1 */
-  pntrString *reqHypSubProof = NULL_PNTRSTRING; /* Subproofs of hypotheses */
-  pntrString *reqHypOldStepNums = NULL_PNTRSTRING; /* Local label flag for
-                                                     subproofs of hypotheses */
-  nmbrString *rearrangedSubProofs = NULL_NMBRSTRING;
-  nmbrString *rearrangedOldStepNums = NULL_NMBRSTRING;
-  flag subProofMoved; /* Flag to restart scan after moving subproof */
-                 /* 10-Mar-2016 nm */
-
-  if (g_Statement[statemNum].type != p_) {
-    bug(1723); /* 13-Oct-05 nm - should never get here */
-    g_WrkProof.errorSeverity = 4;
-    return (4); /* Do nothing if not $p */
-  }
-  fbPtr = g_Statement[statemNum].proofSectionPtr; /* Start of proof section */
-  if (fbPtr[0] == 0) { /* The proof was never assigned (could be a $p statement
-                          with no $=; this would have been detected earlier) */
-    g_WrkProof.errorSeverity = 4;
-    return (4); /* Pretend it's an empty proof */
-  }
-  fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-  if (fbPtr[0] == '(') { /* "(" is flag for compressed proof */
-    g_WrkProof.errorSeverity = parseCompressedProof(statemNum);
-    return (g_WrkProof.errorSeverity);
-  }
-
-  /* Make sure we have enough working space to hold the proof */
-  /* The worst case is less than the number of chars in the source,
-     plus the number of active hypotheses */
-
-  numOptHyp = nmbrLen(g_Statement[statemNum].optHypList);
-  if (g_Statement[statemNum].proofSectionLen + g_Statement[statemNum].numReqHyp
-      + numOptHyp > g_wrkProofMaxSize) {
-    if (g_wrkProofMaxSize) { /* Not the first allocation */
-      free(g_WrkProof.tokenSrcPtrNmbr);
-      free(g_WrkProof.tokenSrcPtrPntr);
-      free(g_WrkProof.stepSrcPtrNmbr);
-      free(g_WrkProof.stepSrcPtrPntr);
-      free(g_WrkProof.localLabelFlag);
-      free(g_WrkProof.hypAndLocLabel);
-      free(g_WrkProof.localLabelPool);
-      poolFree(g_WrkProof.proofString);
-      free(g_WrkProof.mathStringPtrs);
-      free(g_WrkProof.RPNStack);
-      free(g_WrkProof.compressedPfLabelMap);
-    }
-    g_wrkProofMaxSize = g_Statement[statemNum].proofSectionLen
-        + g_Statement[statemNum].numReqHyp + numOptHyp
-        + 2; /* 2 is minimum for 1-step proof; the other terms could
-                all be 0 */
-    g_WrkProof.tokenSrcPtrNmbr = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(nmbrString));
-    g_WrkProof.tokenSrcPtrPntr = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(pntrString));
-    g_WrkProof.stepSrcPtrNmbr = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(nmbrString));
-    g_WrkProof.stepSrcPtrPntr = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(pntrString));
-    g_WrkProof.localLabelFlag = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(flag));
-    g_WrkProof.hypAndLocLabel =
-        malloc((size_t)g_wrkProofMaxSize * sizeof(struct sortHypAndLoc));
-    g_WrkProof.localLabelPool = malloc((size_t)g_wrkProofMaxSize);
-    g_WrkProof.proofString =
-        poolFixedMalloc(g_wrkProofMaxSize * (long)(sizeof(nmbrString)));
-         /* Use poolFixedMalloc instead of poolMalloc so that it won't get
-            trimmed by memUsedPoolPurge. */
-    g_WrkProof.mathStringPtrs =
-        malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
-    g_WrkProof.RPNStack = malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
-    g_WrkProof.compressedPfLabelMap =
-         malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
-    if (!g_WrkProof.tokenSrcPtrNmbr ||
-        !g_WrkProof.tokenSrcPtrPntr ||
-        !g_WrkProof.stepSrcPtrNmbr ||
-        !g_WrkProof.stepSrcPtrPntr ||
-        !g_WrkProof.localLabelFlag ||
-        !g_WrkProof.hypAndLocLabel ||
-        !g_WrkProof.localLabelPool ||
-        /* !g_WrkProof.proofString || */ /* Redundant because of poolMalloc */
-        !g_WrkProof.mathStringPtrs ||
-        !g_WrkProof.RPNStack
-        ) outOfMemory("#99 (g_WrkProof)");
-  }
-
-  /* Initialization for this proof */
-  g_WrkProof.errorCount = 0; /* Used as threshold for how many error msgs/proof */
-  g_WrkProof.numSteps = 0;
-  g_WrkProof.numTokens = 0;
-  g_WrkProof.numHypAndLoc = 0;
-  g_WrkProof.numLocalLabels = 0;
-  g_WrkProof.RPNStackPtr = 0;
-  g_WrkProof.localLabelPoolPtr = g_WrkProof.localLabelPool;
-
-  /* fbPtr points to the first token now. */
-
-  /* First break up proof section of source into tokens */
-  while (1) {
-    tokLength = proofTokenLen(fbPtr);
-    if (!tokLength) break;
-    g_WrkProof.tokenSrcPtrPntr[g_WrkProof.numTokens] = fbPtr;
-    g_WrkProof.tokenSrcPtrNmbr[g_WrkProof.numTokens] = tokLength;
-    g_WrkProof.numTokens++;
-    fbPtr = fbPtr + tokLength;
-    fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-  }
-
-  /* If there are no tokens, the proof is unknown; make the token a '?' */
-  /* (g_WrkProof.tokenSrcPtrPntr won't point to the source, but this is OK since
-     there will never be an error message for it.) */
-  if (!g_WrkProof.numTokens) {
-
-    /* For now, this is an error. */
-    if (!g_WrkProof.errorCount) {
-      sourceError(fbPtr, 2, statemNum,
-          "The proof is empty.  If you don't know the proof, make it a \"?\".");
-    }
-    g_WrkProof.errorCount++;
-    if (returnFlag < 1) returnFlag = 1;
-
-    /* Allow empty proofs anyway */
-    g_WrkProof.numTokens = 1;
-    g_WrkProof.tokenSrcPtrPntr[0] = "?";
-    g_WrkProof.tokenSrcPtrNmbr[0] = 1; /* Length */
-  }
-
-  /* Copy active (opt + req) hypotheses to hypAndLocLabel look-up table */
-  nmbrTmpPtr = g_Statement[statemNum].optHypList;
-  /* Transfer optional hypotheses */
-  while (1) {
-    i = nmbrTmpPtr[g_WrkProof.numHypAndLoc];
-    if (i == -1) break;
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum = i;
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName =
-        g_Statement[i].labelName;
-    g_WrkProof.numHypAndLoc++;
-  }
-  /* Transfer required hypotheses */
-  j = g_Statement[statemNum].numReqHyp;
-  nmbrTmpPtr = g_Statement[statemNum].reqHypList;
-  for (i = 0; i < j; i++) {
-    k = nmbrTmpPtr[i];
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum = k;
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName =
-        g_Statement[k].labelName;
-    g_WrkProof.numHypAndLoc++;
-  }
-
-  /* Sort the hypotheses by label name for lookup */
-  numActiveHyp = g_WrkProof.numHypAndLoc; /* Save for bsearch later */
-  qsort(g_WrkProof.hypAndLocLabel, (size_t)(g_WrkProof.numHypAndLoc),
-      sizeof(struct sortHypAndLoc), hypAndLocSortCmp);
-
-
-  /* Scan the parsed tokens for local label assignments */
-  fbPtr = g_WrkProof.tokenSrcPtrPntr[0];
-  if (fbPtr[0] == ':') {
-    if (!g_WrkProof.errorCount) {
-      sourceError(fbPtr, 1, statemNum,
-          "The colon at proof step 1 must be preceded by a local label.");
-    }
-    if (returnFlag < 2) returnFlag = 2;
-    g_WrkProof.tokenSrcPtrPntr[0] = "?";
-    g_WrkProof.tokenSrcPtrNmbr[0] = 1; /* Length */
-    g_WrkProof.errorCount++;
-  }
-  fbPtr = g_WrkProof.tokenSrcPtrPntr[g_WrkProof.numTokens - 1];
-  if (fbPtr[0] == ':') {
-    if (!g_WrkProof.errorCount) {
-      sourceError(fbPtr, 1, statemNum,
-          "The colon in the last proof step must be followed by a label.");
-    }
-    if (returnFlag < 2) returnFlag = 2;
-    g_WrkProof.errorCount++;
-    g_WrkProof.numTokens--;
-  }
-  labelFlag = 0;
-  for (tok = 0; tok < g_WrkProof.numTokens; tok++) {
-    fbPtr = g_WrkProof.tokenSrcPtrPntr[tok];
-
-    /* 25-Jan-2016 nm */
-    /* If next token is = then this token is a target for /EXPLICIT format,
-       so don't increment the proof step number */
-    if (tok < g_WrkProof.numTokens - 2) {
-      if (((char *)((g_WrkProof.tokenSrcPtrPntr)[tok + 1]))[0] == '=') {
-        explicitTargets = 1; /* Flag that proof has explicit targets */
-        continue;
-      }
-    }
-    if (fbPtr[0] == '=') continue; /* Skip the = token */
-
-    /* Save pointer to source file vs. step for error messages */
-    g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] =
-        g_WrkProof.tokenSrcPtrNmbr[tok]; /* Token length */
-    g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = fbPtr; /* Token ptr */
-
-    /* Save fact that this step has a local label declaration */
-    g_WrkProof.localLabelFlag[g_WrkProof.numSteps] = labelFlag;
-    labelFlag = 0;
-
-    g_WrkProof.numSteps++;
-    if (fbPtr[0] != ':') continue;
-
-    /* Colon found -- previous token is a label */
-    labelFlag = 1;
-
-    g_WrkProof.numSteps = g_WrkProof.numSteps - 2;
-    fbPtr = g_WrkProof.tokenSrcPtrPntr[tok - 1]; /* The local label */
-    tokLength = g_WrkProof.tokenSrcPtrNmbr[tok - 1]; /* Its length */
-
-    /* Check for illegal characters */
-    for (j = 0; j < tokLength; j++) {
-      if (illegalLabelChar[(unsigned char)fbPtr[j]]) {
-        if (!g_WrkProof.errorCount) {
-          sourceError(fbPtr + j, 1, statemNum,cat(
-              "The local label at proof step ",
-              str((double)(g_WrkProof.numSteps + 1)),
-              " is incorrect.  Only letters,",
-              " digits, \"_\", \"-\", and \".\" are allowed in local labels.",
-              NULL));
-        }
-        if (returnFlag < 2) returnFlag = 2;
-        g_WrkProof.errorCount++;
-      }
-    }
-
-    /* Add the label to the local label pool and hypAndLocLabel table */
-    memcpy(g_WrkProof.localLabelPoolPtr, fbPtr, (size_t)tokLength);
-    g_WrkProof.localLabelPoolPtr[tokLength] = 0; /* String terminator */
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum =
-       -g_WrkProof.numSteps - 1000; /* offset of -1000 is flag for local label*/
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName
-        = g_WrkProof.localLabelPoolPtr;
-
-    /* Make sure local label is different from all earlier $a and $p labels */
-    voidPtr = (void *)bsearch(g_WrkProof.localLabelPoolPtr, g_labelKeyBase,
-        (size_t)g_numLabelKeys, sizeof(long), labelSrchCmp);
-    if (voidPtr) { /* It was found */
-      j = *(long *)voidPtr; /* Statement number */
-      if (j <= statemNum) {
-        if (!g_WrkProof.errorCount) {
-          assignStmtFileAndLineNum(j); /* 9-Jan-2018 nm */
-          sourceError(fbPtr, tokLength, statemNum,cat(
-              "The local label at proof step ",
-              str((double)(g_WrkProof.numSteps + 1)),
-              " is the same as the label of statement ",
-              str((double)j),
-              " at line ",
-              str((double)(g_Statement[j].lineNum)),
-              " in file \"",
-              g_Statement[j].fileName,
-              "\".  Local labels must be different from active statement labels.",
-              NULL));
-        }
-        g_WrkProof.errorCount++;
-        if (returnFlag < 2) returnFlag = 2;
-      }
-    }
-
-    /* Make sure local label is different from all active $e and $f labels */
-    voidPtr = (void *)bsearch(g_WrkProof.localLabelPoolPtr,
-        g_WrkProof.hypAndLocLabel,
-        (size_t)numActiveHyp, sizeof(struct sortHypAndLoc), hypAndLocSrchCmp);
-    if (voidPtr) { /* It was found */
-      j = ( (struct sortHypAndLoc *)voidPtr)->labelTokenNum; /* Statement number */
-      if (!g_WrkProof.errorCount) {
-        assignStmtFileAndLineNum(j); /* 9-Jan-2018 nm */
-        sourceError(fbPtr, tokLength, statemNum,cat(
-            "The local label at proof step ",
-            str((double)(g_WrkProof.numSteps + 1)),
-            " is the same as the label of statement ",
-            str((double)j),
-            " at line ",
-            str((double)(g_Statement[j].lineNum)),
-            " in file \"",
-            g_Statement[j].fileName,
-            "\".  Local labels must be different from active statement labels.",
-            NULL));
-      }
-      g_WrkProof.errorCount++;
-      if (returnFlag < 2) returnFlag = 2;
-      g_WrkProof.numHypAndLoc--; /* Ignore the label */
-    }
-
-    g_WrkProof.numHypAndLoc++;
-    g_WrkProof.localLabelPoolPtr = &g_WrkProof.localLabelPoolPtr[tokLength + 1];
-
-  } /* Next i */
-
-  /* 25-Jan-2016 nm */
-  /* Collect all target labels in /EXPLICIT format */
-  /* I decided not to make targetPntr, targetNmbr part of the g_WrkProof
-     structure since other proof formats don't assign it, so we can't
-     reference it reliably outside of this function.  And it would waste
-     some memory if we don't use /EXPLICIT, which is intended primarily
-     for database maintenance. */
-  if (explicitTargets == 1) {
-    pntrLet(&targetPntr, pntrSpace(g_WrkProof.numSteps));
-    nmbrLet(&targetNmbr, nmbrSpace(g_WrkProof.numSteps));
-    step = 0;
-    for (tok = 0; tok < g_WrkProof.numTokens - 2; tok++) {
-      /* If next token is = then this token is a target for /EXPLICIT format,
-         so don't increment the proof step number */
-      if (((char *)((g_WrkProof.tokenSrcPtrPntr)[tok + 1]))[0] == '=') {
-        fbPtr = g_WrkProof.tokenSrcPtrPntr[tok];
-        if (step >= g_WrkProof.numSteps) {
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, g_WrkProof.tokenSrcPtrNmbr[tok], statemNum, cat(
-                "There are more target labels than proof steps.", NULL));
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-          break;
-        }
-        targetPntr[step] = fbPtr;
-        targetNmbr[step] = g_WrkProof.tokenSrcPtrNmbr[tok];
-        if (g_WrkProof.tokenSrcPtrPntr[tok + 2]
-            != g_WrkProof.stepSrcPtrPntr[step]) {
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, g_WrkProof.tokenSrcPtrNmbr[tok], statemNum, cat(
-                "The target label for step ", str((double)step + 1),
-                " is not assigned to that step.  ",
-                "(Check for missing or extra \"=\".)", NULL));
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-        }
-        step++;
-      }
-    } /* next tok */
-    if (step != g_WrkProof.numSteps) {
-      if (!g_WrkProof.errorCount) {
-        sourceError(
-            (char *)((g_WrkProof.tokenSrcPtrPntr)[g_WrkProof.numTokens - 1]),
-            g_WrkProof.tokenSrcPtrNmbr[g_WrkProof.numTokens - 1],
-            statemNum, cat(
-                "There are ", str((double)(g_WrkProof.numSteps)), " proof steps but only ",
-                str((double)step), " target labels.", NULL));
-      }
-      g_WrkProof.errorCount++;
-      if (returnFlag < 2) returnFlag = 2;
-    }
-  } /* if explicitTargets == 1 */
-
-  if (g_WrkProof.numHypAndLoc > numActiveHyp) { /* There were local labels */
-
-    /* Sort the local labels into the hypAndLocLabel look-up table */
-    qsort(g_WrkProof.hypAndLocLabel, (size_t)(g_WrkProof.numHypAndLoc),
-        sizeof(struct sortHypAndLoc), hypAndLocSortCmp);
-
-    /* Check for duplicate local labels */
-    for (i = 1; i < g_WrkProof.numHypAndLoc; i++) {
-      if (!strcmp(g_WrkProof.hypAndLocLabel[i - 1].labelName,
-          g_WrkProof.hypAndLocLabel[i].labelName)) { /* Duplicate label */
-        /* Get the step numbers */
-        j = -(g_WrkProof.hypAndLocLabel[i - 1].labelTokenNum + 1000);
-        k = -(g_WrkProof.hypAndLocLabel[i].labelTokenNum + 1000);
-        if (j > k) {
-          m = j;
-          j = k; /* Smaller step number */
-          k = m; /* Larger step number */
-        }
-        /* Find the token - back up a step then move forward to loc label */
-        fbPtr = g_WrkProof.stepSrcPtrPntr[k - 1]; /* Previous step */
-        fbPtr = fbPtr + g_WrkProof.stepSrcPtrNmbr[k - 1];
-        fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-        if (!g_WrkProof.errorCount) {
-          sourceError(fbPtr,
-              proofTokenLen(fbPtr), statemNum,
-              cat("The local label at proof step ", str((double)k + 1),
-              " is the same as the one declared at step ",
-              str((double)j + 1), ".", NULL));
-        }
-        g_WrkProof.errorCount++;
-        if (returnFlag < 2) returnFlag = 2;
-      } /* End if duplicate label */
-    } /* Next i */
-
-  } /* End if there are local labels */
-
-  /* Build the proof string and check the RPN stack */
-  g_WrkProof.proofString[g_WrkProof.numSteps] = -1; /* End of proof */
-  nmbrZapLen(g_WrkProof.proofString, g_WrkProof.numSteps);
-     /* Zap mem pool actual length (because nmbrLen will be used later on this)*/
-
-  /* 25-Jan-2016 nm */
-  if (explicitTargets == 1) {
-    /* List of original step numbers to keep track of local label movement */
-    nmbrLet(&oldStepNums, nmbrSpace(g_WrkProof.numSteps));
-    for (i = 0; i < g_WrkProof.numSteps; i++) {
-      oldStepNums[i] = i;
-    }
-  }
-
-  for (step = 0; step < g_WrkProof.numSteps; step++) {
-    tokLength = g_WrkProof.stepSrcPtrNmbr[step];
-    fbPtr = g_WrkProof.stepSrcPtrPntr[step];
-
-    /* Handle unknown proof steps */
-    if (fbPtr[0] == '?') {
-      if (returnFlag < 1) returnFlag = 1;
-                                      /* Flag that proof is partially unknown */
-      g_WrkProof.proofString[step] = -(long)'?';
-      /* Treat "?" like a hypothesis - push stack and continue */
-      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-      g_WrkProof.RPNStackPtr++;
-      continue;
-    }
-
-    /* Temporarily zap the token's end with a null for string comparisons */
-    zapSave = fbPtr[tokLength];
-    fbPtr[tokLength] = 0; /* Zap source */
-
-    /* See if the proof token is a hypothesis or local label ref. */
-    voidPtr = (void *)bsearch(fbPtr, g_WrkProof.hypAndLocLabel,
-        (size_t)(g_WrkProof.numHypAndLoc), sizeof(struct sortHypAndLoc),
-        hypAndLocSrchCmp);
-    if (voidPtr) {
-      fbPtr[tokLength] = zapSave; /* Unzap source */
-      j = ((struct sortHypAndLoc *)voidPtr)->labelTokenNum; /* Label lookup number */
-      g_WrkProof.proofString[step] = j; /* Proof string */
-
-      /* Push the stack */
-      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-      g_WrkProof.RPNStackPtr++;
-
-      if (j < 0) { /* It's a local label reference */
-        i = -1000 - j; /* Step number referenced */
-        if (i < 0) bug(1734);
-
-        /* Make sure we don't reference a later step */
-        if (i > step) {
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, tokLength, statemNum,cat("Proof step ",
-                str((double)step + 1),
-                " references a local label before it is declared.",
-                NULL));
-          }
-          g_WrkProof.proofString[step] = -(long)'?';
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-        }
-
-        if (g_WrkProof.localLabelFlag[step]) {
-          if (!g_WrkProof.errorCount) {
-            /* Chained labels not allowed because it complicates the language
-               but doesn't buy anything */
-            sourceError(fbPtr, tokLength, statemNum, cat(
-                "The local label reference at proof step ",
-                str((double)step + 1),
-                " declares a local label.  Only \"$a\" and \"$p\" statement",
-                " labels may have local label declarations.",NULL));
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-        }
-      } else { /* It's a hypothesis reference */
-        if (g_WrkProof.localLabelFlag[step]) {
-          /* Not allowed because it complicates the language but doesn't
-             buy anything; would make $e to $f assignments harder to detect */
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, tokLength, statemNum, cat(
-                "The hypothesis reference at proof step ",
-                str((double)step + 1),
-                " declares a local label.  Only \"$a\" and \"$p\" statement",
-                " labels may have local label declarations.",NULL));
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-        }
-        if (j <= 0) bug(1709);
-      }
-      continue;
-    } /* End if local label or hypothesis */
-
-    /* See if token is an assertion label */
-    voidPtr = (void *)bsearch(fbPtr, g_labelKeyBase, (size_t)g_numLabelKeys,
-        sizeof(long), labelSrchCmp);
-    fbPtr[tokLength] = zapSave; /* Unzap source */
-    if (!voidPtr) {
-      if (!g_WrkProof.errorCount) {
-        sourceError(fbPtr, tokLength, statemNum, cat(
-            "The token at proof step ",
-            str((double)step + 1),
-            " is not an active statement label or a local label.",NULL));
-      }
-      g_WrkProof.errorCount++;
-      g_WrkProof.proofString[step] = -(long)'?';
-      /* Push the stack */
-      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-      g_WrkProof.RPNStackPtr++;
-      if (returnFlag < 2) returnFlag = 2;
-      continue;
-    }
-
-    /* It's an assertion ($a or $p) */
-    j = *(long *)voidPtr; /* Statement number */
-    if (g_Statement[j].type != a_ && g_Statement[j].type != p_) bug(1710);
-    g_WrkProof.proofString[step] = j; /* Assign $a/$p label to proof string */
-
-    if (j >= statemNum) { /* Error */
-      if (!g_WrkProof.errorCount) {
-        if (j == statemNum) {
-          sourceError(fbPtr, tokLength, statemNum, cat(
-              "The label at proof step ",
-              str((double)step + 1),
-              " is the label of this statement.  A statement may not be used to",
-              " prove itself.",NULL));
-        } else {
-          assignStmtFileAndLineNum(j); /* 9-Jan-2018 nm */
-          sourceError(fbPtr, tokLength, statemNum, cat(
-              "The label \"", g_Statement[j].labelName, "\" at proof step ",
-              str((double)step + 1),
-              " is the label of a future statement (at line ",
-              str((double)(g_Statement[j].lineNum)),
-              " in file ",g_Statement[j].fileName,
-      ").  Only local labels or previous, active statements may be referenced.",
-              NULL));
-        }
-      }
-      g_WrkProof.errorCount++;
-      if (returnFlag < 2) returnFlag = 2;
-    }
-
-    /* It's a valid assertion, so pop the stack */
-    numReqHyp = g_Statement[j].numReqHyp;
-
-    /* Error check for exhausted stack */
-    if (g_WrkProof.RPNStackPtr < numReqHyp) { /* Stack exhausted -- error */
-      if (!g_WrkProof.errorCount) {
-        tmpStrPtr = shortDumpRPNStack();
-        if (strcmp(left(tmpStrPtr,18),"RPN stack is empty")){
-          i = instr(1,tmpStrPtr,"contains ");
-          let(&tmpStrPtr,cat(left(tmpStrPtr,i + 7)," only",
-            right(tmpStrPtr,i + 8),
-            NULL));
-        }
-        if (numReqHyp == 1) {
-          let(&tmpStrPtr,cat("a hypothesis but the ",tmpStrPtr,NULL));
-        } else {
-          let(&tmpStrPtr,cat(str((double)numReqHyp)," hypotheses but the ",tmpStrPtr,
-              NULL));
-        }
-        sourceError(fbPtr, tokLength, statemNum, cat(
-            "At proof step ",
-            str((double)step + 1),", statement \"",
-            g_Statement[j].labelName,"\" requires ",
-            tmpStrPtr,".",NULL));
-        let(&tmpStrPtr, "");
-      }
-      /* Treat it like an unknown step so stack won't get exhausted */
-      g_WrkProof.errorCount++;
-      g_WrkProof.proofString[step] = -(long)'?';
-      /* Push the stack */
-      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-      g_WrkProof.RPNStackPtr++;
-      if (returnFlag < 3) returnFlag = 3;
-      continue;
-    } /* End if stack exhausted */
-
-    /**** Start of 25-Jan-2016 nm ***/
-    /* For proofs saved with /EXPLICIT, the user may have changed the order
-       of hypotheses.  First, get the subproofs for the hypotheses.  Then
-       reassemble them in the right order. */
-    if (explicitTargets == 1) {
-      nmbrLet(&wrkProofString, g_WrkProof.proofString);
-            /* nmbrString to rearrange proof then when done reassign to
-               g_WrkProof.proofString structure component */
-      nmbrTmpPtr = g_Statement[j].reqHypList;
-      numReqHyp = g_Statement[j].numReqHyp;
-      conclSubProofLen = subproofLen(wrkProofString, step);
-      pntrLet(&reqHypSubProof, pntrNSpace(numReqHyp));
-                                         /* Initialize to NULL_NMBRSTRINGs */
-      pntrLet(&reqHypOldStepNums, pntrNSpace(numReqHyp));
-                                         /* Initialize to NULL_NMBRSTRINGs */
-      k = 0; /* Total hypothesis subproof lengths for error checking */
-      for (i = 0; i < numReqHyp; i++) {
-        m = g_WrkProof.RPNStackPtr - numReqHyp + i; /* Stack position of hyp */
-        hypStepNum = g_WrkProof.RPNStack[m]; /* Step number of hypothesis i */
-        hypSubProofLen = subproofLen(wrkProofString, hypStepNum);
-        k += hypSubProofLen;
-        nmbrLet((nmbrString **)(&(reqHypSubProof[i])),
-            /* For nmbrSeg, 1 = first step */
-            nmbrSeg(wrkProofString,
-                hypStepNum - hypSubProofLen + 2, hypStepNum + 1));
-        nmbrLet((nmbrString **)(&(reqHypOldStepNums[i])),
-            /* For nmbrSeg, 1 = first step */
-            nmbrSeg(oldStepNums,
-                hypStepNum - hypSubProofLen + 2, hypStepNum + 1));
-      } /* Next i */
-      if (k != conclSubProofLen - 1 /* && returnFlag < 2 */) {
-                        /* Uncomment above if bad proof triggers this bug */
-        bug(1731);
-      }
-      nmbrLet(&rearrangedSubProofs, NULL_NMBRSTRING);
-      matchingHyp = -1; /* In case there are no hypotheses */
-      for (i = 0; i < numReqHyp; i++) {
-        matchingHyp = -1;
-        for (k = 0; k < numReqHyp; k++) {
-          m = g_WrkProof.RPNStackPtr - numReqHyp + k; /* Stack position of hyp */
-          hypStepNum = g_WrkProof.RPNStack[m]; /* Step number of hypothesis k */
-
-
-          /* Temporarily zap the token's end with a null for string comparisons */
-          fbPtr = targetPntr[hypStepNum];
-          zapSave = fbPtr[targetNmbr[hypStepNum]];
-          fbPtr[targetNmbr[hypStepNum]] = 0; /* Zap source */
-          /* See if hypothesis i matches the target label k i.e. hypStepNum */
-          if (!strcmp(g_Statement[nmbrTmpPtr[i]].labelName, fbPtr)) {
-            matchingHyp = k;
-          }
-          fbPtr[targetNmbr[hypStepNum]] = zapSave; /* Unzap source */
-          if (matchingHyp != -1) break;
-        } /* next k (0 to numReqHyp-1) */
-        if (matchingHyp == -1) {
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, 1/*token length*/, statemNum, cat(
-                "The target labels for the hypotheses for step ", str((double)step + 1),
-                " do not match hypothesis \"",
-                g_Statement[nmbrTmpPtr[i]].labelName,
-                "\" of the assertion \"",
-                g_Statement[j].labelName,
-                "\" in step ",  str((double)step + 1), ".",
-                NULL));
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-          break; /* Give up; don't try to rearrange hypotheses */
-        }
-        /* Accumulate the subproof for hypothesis i */
-        nmbrLet(&rearrangedSubProofs, nmbrCat(rearrangedSubProofs,
-            reqHypSubProof[matchingHyp], NULL));
-        nmbrLet(&rearrangedOldStepNums, nmbrCat(rearrangedOldStepNums,
-            reqHypOldStepNums[matchingHyp], NULL));
-      } /* next i (0 to numReqHyp-1) */
-
-      if (matchingHyp != -1) { /* All hypotheses found */
-        if (nmbrLen(rearrangedSubProofs) != conclSubProofLen - 1
-             /* && returnFlag < 2 */) {
-                          /* Uncomment above if bad proof triggers this bug */
-          bug(1732);
-        }
-        nmbrLet(&(wrkProofString), nmbrCat(
-            nmbrLeft(wrkProofString, step - conclSubProofLen + 1),
-            rearrangedSubProofs,
-            nmbrRight(wrkProofString, step + 1), NULL));
-        nmbrLet(&oldStepNums, nmbrCat(
-            nmbrLeft(oldStepNums, step - conclSubProofLen + 1),
-            rearrangedOldStepNums,
-            nmbrRight(oldStepNums, step + 1), NULL));
-      }
-
-      /* Reassign g_WrkProof.proofString from rearranged wrkProofString */
-      for (i = 0; i < step; i++) {
-        /* Nothing from step to end has been changed, so stop at step */
-        (g_WrkProof.proofString)[i] = wrkProofString[i];
-      }
-      if ((g_WrkProof.proofString)[step] != wrkProofString[step]) bug(1735);
-
-      /* Deallocate */
-      for (i = 0; i < numReqHyp; i++) {
-        nmbrLet((nmbrString **)(&(reqHypSubProof[i])), NULL_NMBRSTRING);
-        nmbrLet((nmbrString **)(&(reqHypOldStepNums[i])), NULL_NMBRSTRING);
-      }
-      pntrLet(&reqHypSubProof, NULL_PNTRSTRING);
-      pntrLet(&reqHypOldStepNums, NULL_PNTRSTRING);
-      nmbrLet(&rearrangedSubProofs, NULL_NMBRSTRING);
-      nmbrLet(&rearrangedOldStepNums, NULL_NMBRSTRING);
-      nmbrLet(&wrkProofString, NULL_NMBRSTRING);
-    } /* if explicitTargets */
-    /**** End of 25-Jan-2016 ***/
-
-    numReqHyp = g_Statement[j].numReqHyp;
-
-    /* Error check for $e <- $f assignments (illegal) */
-    /* 18-Jun-2020 nm: Although it would be unusual to to this,
-       it is not illegal per the spec.  See
-       https://groups.google.com/d/msg/metamath/Cx_d84uorf8/0FrNYTM9BAAJ */
-    /**** Deleted 18-Jun-2020 nm ****
-    nmbrTmpPtr = g_Statement[j].reqHypList;
-    for (i = 0; i < numReqHyp; i++) {
-
-      /@ 25-Jan-2016 nm @/
-      /@ Skip this check if /EXPLICIT since hyps may be in random order @/
-      if (explicitTargets == 1) break;
-
-      if (g_Statement[nmbrTmpPtr[i]].type == e_) {
-        m = g_WrkProof.RPNStackPtr - numReqHyp + i;
-        k = g_WrkProof.proofString[g_WrkProof.RPNStack[m]];
-        if (k > 0) {
-          if (g_Statement[k].type == f_) {
-            if (!g_WrkProof.errorCount) {
-              sourceError(fbPtr, tokLength, statemNum, cat(
-                  "Statement \"",g_Statement[j].labelName,"\" (proof step ",
-                  str((double)step + 1),
-                  ") has its \"$e\" hypothesis \"",
-                  g_Statement[nmbrTmpPtr[i]].labelName,
-                  "\" assigned the \"$f\" hypothesis \"",
-                  g_Statement[k].labelName,
-                  "\" at step ", str((double)(g_WrkProof.RPNStack[m] + 1)),
-                  ".  The assignment of \"$e\" with \"$f\" is not allowed.",
-                  NULL));
-            }
-            g_WrkProof.errorCount++;
-            if (returnFlag < 2) returnFlag = 2;
-          }
-        }
-      }
-    }
-    **** End of 18-Jun-2020 deletion ****/
-
-    /* Pop the stack */
-    g_WrkProof.RPNStackPtr = g_WrkProof.RPNStackPtr - numReqHyp;
-    g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-    g_WrkProof.RPNStackPtr++;
-
-  } /* Next step */
-
-  /* The stack should have one entry */
-  if (g_WrkProof.RPNStackPtr != 1) {
-    tmpStrPtr = shortDumpRPNStack();
-    fbPtr = g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps - 1];
-    if (!g_WrkProof.errorCount) {
-      sourceError(fbPtr, proofTokenLen(fbPtr), statemNum, cat("After proof step ",
-          str((double)(g_WrkProof.numSteps))," (the last step), the ",
-          tmpStrPtr,".  It should contain exactly one entry.",NULL));
-    }
-    g_WrkProof.errorCount++;
-    if (returnFlag < 3) returnFlag = 3;
-  }
-
-  /**** Start of 25-Jan-2016 nm ***/
-  if (explicitTargets) {
-    /* Correct the local label refs in the rearranged proof */
-    for (step = 0; step < g_WrkProof.numSteps; step++) {
-      /* This is slow lookup with n^2 behavior, but should be ok since
-         /EXPLICIT isn't used that often */
-      k = (g_WrkProof.proofString)[step]; /* Will be <= -1000 if local label */
-      if (k <= -1000) {
-        k = -1000 - k; /* Restore step number */
-        if (k < 0 || k >= g_WrkProof.numSteps) bug(1733);
-        /* Find the original step */
-        if (oldStepNums[k] == k) {
-            /* Wasn't changed, so skip renumbering for speedup */
-          continue;
-        }
-        i = 0; /* For bug check */
-        /* Find the original step number and change it to the new one */
-        for (m = 0; m < g_WrkProof.numSteps; m++) {
-          if (oldStepNums[m] == k) {
-            (g_WrkProof.proofString)[step] = -1000 - m;
-            i = 1; /* Found */
-            break;
-          }
-        }
-        if (i == 0) bug(1740);
-      }
-    } /* next step */
-
-    /************** Start of section deleted 10-Mar-2016 nm
-    /@ Check if any local labels point to future steps: if so, we should
-       expand the proof so it will verify (since many functions require
-       that a local label be declared before it is used) @/
-    for (step = 0; step < g_WrkProof.numSteps; step++) {
-      k = (g_WrkProof.proofString)[step]; /@ Will be <= -1000 if local label @/
-      if (k <= -1000) { /@ References local label i.e. subproof @/
-        k = -1000 - k; /@ Restore step number subproof ends at @/
-        if (k > step) { /@ Refers to label declared after this step @/
-          /@ Expand the proof @/
-          nmbrLet(&wrkProofString, nmbrUnsquishProof(g_WrkProof.proofString));
-          /@ Recompress the proof @/
-          nmbrLet(&wrkProofString, nmbrSquishProof(wrkProofString));
-          /@ The number of steps shouldn't have changed @/
-          /@ (If this bug is valid behavior, it means we may have to
-             reallocate (grow) the wrkProof structure, which might be
-             unpleasant at this point.) @/
-          if (nmbrLen(wrkProofString) != g_WrkProof.numSteps) {
-            bug(1736);
-          }
-          /@ Reassign g_WrkProof.proofString from new wrkProofString @/
-          for (i = 0; i < g_WrkProof.numSteps; i++) {
-            (g_WrkProof.proofString)[i] = wrkProofString[i];
-          }
-          break;
-        } /@ if k>step @/
-      } /@ if k<= -1000 @/
-    } /@ next step @/
-    ************************* end of 10-Mar-2016 deletion */
-
-
-    /* 10-Mar-2016 nm (Replace above deleted secton) */
-    /* Check if any local labels point to future steps: if so, we should
-       moved the subproof they point to down (since many functions require
-       that a local label be declared before it is used) */
-    do {
-      subProofMoved = 0;  /* Flag to rescan after moving a subproof */
-      /* TODO: restart loop after step just finished for speedup?
-         (maybe not worth it).
-         We could just change subProofMoved to restartStep (long) and use
-         restartStep > 0 as the flag, since we would restart at the
-         last step processed plus 1. */
-      for (step = 0; step < g_WrkProof.numSteps; step++) {
-        k = (g_WrkProof.proofString)[step]; /* Will be <= -1000 if local label */
-        if (k <= -1000) { /* References local label i.e. subproof */
-          k = -1000 - k; /* Restore step number subproof ends at */
-          if (k > step) { /* Refers to label declared after this step */
-            m = subproofLen(g_WrkProof.proofString, k);
-            /*m = nmbrGetSubProofLen(g_WrkProof.proofString, k);*/
-
-            /* At this point:
-                   step = the step referencing a future subproof
-                   k = end of future subproof
-                   m = length of future subproof */
-
-            /* TODO - make this a direct assignment for speedup?
-               (with most $f's reversed, this has about 13K hits during
-               'verify proof *' - maybe not enough to justify rewriting this
-               to make direct assignment instead of nmbrLet().) */
-            /* Replace the step with the subproof it references */
-            /* Note that nmbrXxx() positions start at 1, not 0; add 1 to step */
-            nmbrLet(&wrkProofString, nmbrCat(
-                /* Proof before future label ref: */
-                nmbrLeft(g_WrkProof.proofString, step),
-                /* The future subproof moved to the current step: */
-                nmbrMid(g_WrkProof.proofString, k - m + 2, m),
-                /* The steps between this step and the future subproof: */
-                nmbrSeg(g_WrkProof.proofString, step + 2, k - m + 1),
-                /* The future subproof replaced with the current step (a local
-                   label to be renumbered below): */
-                nmbrMid(g_WrkProof.proofString, step + 1, 1),
-                /* The rest of the steps to the end of proof: */
-                nmbrRight(g_WrkProof.proofString, k + 2),
-                NULL));
-            if (nmbrLen(wrkProofString) != g_WrkProof.numSteps) {
-              bug(1736); /* Make sure proof length didn't change */
-            }
-            if (wrkProofString[k] != (g_WrkProof.proofString)[step]) {
-              bug(1737); /* Make sure future subproof is now the future local
-                            label (to be renumbered below) */
-            }
-
-            /* We now have this wrkProofString[...] content:
-                [0]...[step-1]                   same as original proof
-                [step+1]...[step+m-1]            moved subproof
-                [step+m]...[k-1]                 shifted orig proof
-                [k]...[k]                        subproof replaced by loc label
-                [k+1]...[g_WrkProof.numSteps-1]    same as orig proof */
-
-            /* Correct all local labels */
-            for (i = 0; i < g_WrkProof.numSteps; i++) {
-              j = (wrkProofString)[i]; /* Will be <= -1000 if local label */
-              if (j > -1000) continue; /* Not loc label ref */
-              j = -1000 - j; /* Restore step number subproof ends at */
-              /* Note: the conditions before the "&&" below are redundant
-                 but provide better sanity checking */
-              if (j >= 0 && j < step) { /* Before moved subproof */
-                /*j = j;*/ /* Same as orig proof */
-                /* 24-Mar-2016 workaround to clang complaint about j = j */
-                j = j + 0; /* Same as orig proof */
-              } else if (j == step) { /* The original local label ref */
-                bug(1738); /* A local label shouldn't ref a local label */
-              } else if (j > step && j <= k - m) {
-                                   /* Steps shifted up by subproof insertion */
-                j = j + m - 1; /* Offset by size of subproof moved down -1 */
-              } else if (j > k - m && j <= k) {
-                                   /* Reference to inside the moved subproof */
-                j = j + step + m - 1 - k;  /* Shift down */
-              } else if (j > k && j <= g_WrkProof.numSteps - 1) {
-                                              /* Ref to after moved subproof */
-                /*j = j;*/ /* Same as orig proof */
-                /* 24-Mar-2016 workaround to clang complaint about j = j */
-                j = j + 0; /* Same as orig proof */
-              } else {
-                bug(1739);  /* Cases not exhausted or j is out of range */
-              }
-              (wrkProofString)[i] = -j - 1000; /* Update new proof */
-            } /* next i */
-
-            /* Transfer proof back to original */
-            for (i = 0; i < g_WrkProof.numSteps; i++) {
-              (g_WrkProof.proofString)[i] = wrkProofString[i];
-            }
-
-            /* Set flag that a subproof was moved and restart the scan */
-            subProofMoved = 1;
-            /* Reassign g_WrkProof.proofString from new wrkProofString */
-            for (i = 0; i < g_WrkProof.numSteps; i++) {
-              (g_WrkProof.proofString)[i] = wrkProofString[i];
-            }
-            break; /* Break out of the 'for (step...' loop */
-
-          } /* if k>step */
-        } /* if k<= -1000 */
-      } /* next step */
-    } while (subProofMoved);
-
-    /* Deallocate */
-    pntrLet(&targetPntr, NULL_PNTRSTRING);
-    nmbrLet(&targetNmbr, NULL_NMBRSTRING);
-    nmbrLet(&oldStepNums, NULL_NMBRSTRING);
-    nmbrLet(&wrkProofString, NULL_NMBRSTRING);
-  } /* if (explicitTargets) */
-  /**** End of 25-Jan-2016 ***/
-
-  g_WrkProof.errorSeverity = returnFlag;
-  return (returnFlag);
-
-} /* parseProof() */
-
-
-
-/* Parse proof in compressed format */
-/* Parse proof of one statement in source file.  Uses wrkProof structure. */
-/* Returns 0 if OK; returns 1 if proof is incomplete (is empty or has '?'
-   tokens);  returns 2 if error found; returns 3 if severe error found
-   (e.g. RPN stack violation); returns 4 if not a $p statement */
-char parseCompressedProof(long statemNum)
-{
-
-  long i, j, k, step, stmt;
-  /* long m; */ /* 18-Jun-2020 nm No longer needed */
-  char *fbPtr;
-  char *fbStartProof;
-  char *labelStart;
-  long tokLength;
-  long numReqHyp;
-  long numOptHyp;
-  char zapSave;
-  flag breakFlag;
-  char returnFlag = 0;
-  nmbrString *nmbrTmpPtr;
-  void *voidPtr; /* bsearch returned value */
-  vstring tmpStrPtr;
-  flag hypLocUnkFlag;  /* Hypothesis, local label ref, or unknown step */
-  long labelMapIndex;
-
-  static unsigned char chrWeight[256]; /* Proof label character weights */
-  static unsigned char chrType[256]; /* Proof character types */
-  static flag chrTablesInited = 0;
-  static char *digits = "0123456789";
-  static char *letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-  static char labelChar = ':';
-  static long lettersLen;
-  static long digitsLen;
-
-  /* 15-Oct-05 nm - Used to detect old buggy compression */
-  long bggyProofLen;
-  char bggyZapSave;
-  flag bggyAlgo;
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  labelStart = "";
-
-  /* Do one-time initialization */
-  if (!chrTablesInited) {
-    chrTablesInited = 1;
-
-    /* Compression standard with all cap letters */
-    /* (For 500-700 step proofs, we only lose about 18% of file size --
-        but the compressed proof is more pleasing to the eye) */
-    letters = "ABCDEFGHIJKLMNOPQRST"; /* LSB is base 20 */
-    digits = "UVWXY"; /* MSB's are base 5 */
-    labelChar = 'Z'; /* Was colon */
-
-    lettersLen = (long)strlen(letters);
-    digitsLen = (long)strlen(digits);
-
-    /* Initialize compressed proof label character weights */
-    /* Initialize compressed proof character types */
-    for (i = 0; i < 256; i++) {
-      chrWeight[i] = 0;
-      chrType[i] = 6; /* Illegal */
-    }
-    j = lettersLen;
-    for (i = 0; i < j; i++) {
-      chrWeight[(long)(letters[i])] = (unsigned char)i;
-      chrType[(long)(letters[i])] = 0; /* Letter */
-    }
-    j = digitsLen;
-    for (i = 0; i < j; i++) {
-      chrWeight[(long)(digits[i])] = (unsigned char)i;
-      chrType[(long)(digits[i])] = 1; /* Digit */
-    }
-    for (i = 0; i < 256; i++) {
-      if (isspace(i)) chrType[i] = 3; /* White space */
-    } /* Next i */
-    chrType[(long)(labelChar)] = 2; /* Colon */
-    chrType['$'] = 4; /* Dollar */
-    chrType['?'] = 5; /* Question mark */
-  }
-
-
-  if (g_Statement[statemNum].type != p_) {
-    bug(1724); /* 13-Oct-05 nm - should never get here */
-    return (4); /* Do nothing if not $p */
-  }
-  fbPtr = g_Statement[statemNum].proofSectionPtr; /* Start of proof section */
-  if (fbPtr[0] == 0) { /* The proof was never assigned (could be a $p statement
-                          with no $=; this would have been detected earlier) */
-    bug(1711);
-  }
-  fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-  if (fbPtr[0] != '(') { /* ( is flag for start of compressed proof */
-    bug(1712);
-  }
-
-  /* Make sure we have enough working space to hold the proof */
-  /* The worst case is less than the number of chars in the source,
-     plus the number of active hypotheses */
-
-  numOptHyp = nmbrLen(g_Statement[statemNum].optHypList);
-  if (g_Statement[statemNum].proofSectionLen + g_Statement[statemNum].numReqHyp
-      + numOptHyp > g_wrkProofMaxSize) {
-    if (g_wrkProofMaxSize) { /* Not the first allocation */
-      free(g_WrkProof.tokenSrcPtrNmbr);
-      free(g_WrkProof.tokenSrcPtrPntr);
-      free(g_WrkProof.stepSrcPtrNmbr);
-      free(g_WrkProof.stepSrcPtrPntr);
-      free(g_WrkProof.localLabelFlag);
-      free(g_WrkProof.hypAndLocLabel);
-      free(g_WrkProof.localLabelPool);
-      poolFree(g_WrkProof.proofString);
-      free(g_WrkProof.mathStringPtrs);
-      free(g_WrkProof.RPNStack);
-      free(g_WrkProof.compressedPfLabelMap);
-    }
-    g_wrkProofMaxSize = g_Statement[statemNum].proofSectionLen
-        + g_Statement[statemNum].numReqHyp + numOptHyp;
-    g_WrkProof.tokenSrcPtrNmbr = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(nmbrString));
-    g_WrkProof.tokenSrcPtrPntr = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(pntrString));
-    g_WrkProof.stepSrcPtrNmbr = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(nmbrString));
-    g_WrkProof.stepSrcPtrPntr = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(pntrString));
-    g_WrkProof.localLabelFlag = malloc((size_t)g_wrkProofMaxSize
-        * sizeof(flag));
-    g_WrkProof.hypAndLocLabel =
-        malloc((size_t)g_wrkProofMaxSize * sizeof(struct sortHypAndLoc));
-    g_WrkProof.localLabelPool = malloc((size_t)g_wrkProofMaxSize);
-    g_WrkProof.proofString =
-        poolFixedMalloc(g_wrkProofMaxSize * (long)(sizeof(nmbrString)));
-         /* Use poolFixedMalloc instead of poolMalloc so that it won't get
-            trimmed by memUsedPoolPurge. */
-    g_WrkProof.mathStringPtrs =
-        malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
-    g_WrkProof.RPNStack = malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
-    g_WrkProof.compressedPfLabelMap =
-         malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
-    if (!g_WrkProof.tokenSrcPtrNmbr ||
-        !g_WrkProof.tokenSrcPtrPntr ||
-        !g_WrkProof.stepSrcPtrNmbr ||
-        !g_WrkProof.stepSrcPtrPntr ||
-        !g_WrkProof.localLabelFlag ||
-        !g_WrkProof.hypAndLocLabel ||
-        !g_WrkProof.localLabelPool ||
-        /* !g_WrkProof.proofString || */ /* Redundant because of poolMalloc */
-        !g_WrkProof.mathStringPtrs ||
-        !g_WrkProof.RPNStack
-        ) outOfMemory("#99 (g_WrkProof)");
-  }
-
-  /* Initialization for this proof */
-  g_WrkProof.errorCount = 0; /* Used as threshold for how many error msgs/proof */
-  g_WrkProof.numSteps = 0;
-  g_WrkProof.numTokens = 0;
-  g_WrkProof.numHypAndLoc = 0;
-  g_WrkProof.numLocalLabels = 0;
-  g_WrkProof.RPNStackPtr = 0;
-  g_WrkProof.localLabelPoolPtr = g_WrkProof.localLabelPool;
-
-  fbPtr++;
-  /* fbPtr points to the first token now. */
-
-
-  /****** This part of the code is heavily borrowed from the regular
-   ****** proof parsing, with local label and RPN handling removed,
-   ****** in order to easily parse the label section. */
-
-  /* First break up the label section of proof into tokens */
-  while (1) {
-    fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-    tokLength = proofTokenLen(fbPtr);
-    if (!tokLength) {
-      if (!g_WrkProof.errorCount) {
-        sourceError(fbPtr, 2, statemNum,
-            "A \")\" which ends the label list is not present.");
-      }
-      g_WrkProof.errorCount++;
-      if (returnFlag < 3) returnFlag = 3;
-      break;
-    }
-    if (fbPtr[0] == ')') {  /* End of label list */
-      fbPtr++;
-      break;
-    }
-    g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = fbPtr;
-    g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] = tokLength;
-    g_WrkProof.numSteps++;
-    fbPtr = fbPtr + tokLength;
-  }
-
-  fbStartProof = fbPtr; /* Save pointer to start of compressed proof */
-
-  /* Copy active (opt + req) hypotheses to hypAndLocLabel look-up table */
-  nmbrTmpPtr = g_Statement[statemNum].optHypList;
-  /* Transfer optional hypotheses */
-  while (1) {
-    i = nmbrTmpPtr[g_WrkProof.numHypAndLoc];
-    if (i == -1) break;
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum = i;
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName =
-        g_Statement[i].labelName;
-    g_WrkProof.numHypAndLoc++;
-  }
-  /* Transfer required hypotheses */
-  j = g_Statement[statemNum].numReqHyp;
-  nmbrTmpPtr = g_Statement[statemNum].reqHypList;
-  for (i = 0; i < j; i++) {
-    k = nmbrTmpPtr[i];
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum = -1000 - k;
-       /* Required hypothesis labels are not allowed; the -1000 - k is a
-          flag that tells that they are required for error detection */
-    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName =
-        g_Statement[k].labelName;
-    g_WrkProof.numHypAndLoc++;
-  }
-
-  /* Sort the hypotheses by label name for lookup */
-  qsort(g_WrkProof.hypAndLocLabel, (size_t)(g_WrkProof.numHypAndLoc),
-      sizeof(struct sortHypAndLoc), hypAndLocSortCmp);
-
-  /* Build the proof string (actually just a list of labels) */
-  g_WrkProof.proofString[g_WrkProof.numSteps] = -1; /* End of proof */
-  nmbrZapLen(g_WrkProof.proofString, g_WrkProof.numSteps);
-     /* Zap mem pool actual length (because nmbrLen will be used later on this)*/
-
-  /* Scan proof string with the label list (not really proof steps; we're just
-     using the structure for convenience) */
-  for (step = 0; step < g_WrkProof.numSteps; step++) {
-    tokLength = g_WrkProof.stepSrcPtrNmbr[step];
-    fbPtr = g_WrkProof.stepSrcPtrPntr[step];
-
-    /* Temporarily zap the token's end with a null for string comparisons */
-    zapSave = fbPtr[tokLength];
-    fbPtr[tokLength] = 0; /* Zap source */
-
-    /* See if the proof token is a hypothesis */
-    voidPtr = (void *)bsearch(fbPtr, g_WrkProof.hypAndLocLabel,
-        (size_t)(g_WrkProof.numHypAndLoc), sizeof(struct sortHypAndLoc),
-        hypAndLocSrchCmp);
-    if (voidPtr) {
-      /* It's a hypothesis reference */
-      fbPtr[tokLength] = zapSave; /* Unzap source */
-      j = ((struct sortHypAndLoc *)voidPtr)->labelTokenNum;
-                                                       /* Label lookup number */
-
-      /* Make sure it's not a required hypothesis, which is implicitly
-         declared */
-      if (j < 0) { /* Minus is used as flag for required hypothesis */
-        j = -1000 - j; /* Restore it to prevent side effects of the error */
-        if (!g_WrkProof.errorCount) {
-          sourceError(fbPtr, tokLength, statemNum,
-              "Required hypotheses may not be explicitly declared.");
-        }
-        g_WrkProof.errorCount++;
-        /* if (returnFlag < 2) returnFlag = 2; */
-        /* 19-Aug-2006 nm Tolerate this error so we can continue to work
-           on proof in Proof Assistant */
-        if (returnFlag < 1) returnFlag = 1;
-      }
-
-      g_WrkProof.proofString[step] = j; /* Proof string */
-      if (j <= 0) bug(1713);
-      continue;
-    } /* End if hypothesis */
-
-    /* See if token is an assertion label */
-    voidPtr = (void *)bsearch(fbPtr, g_labelKeyBase, (size_t)g_numLabelKeys,
-        sizeof(long), labelSrchCmp);
-    fbPtr[tokLength] = zapSave; /* Unzap source */
-    if (!voidPtr) {
-      if (!g_WrkProof.errorCount) {
-        sourceError(fbPtr, tokLength, statemNum,
-         "This token is not the label of an assertion or optional hypothesis.");
-      }
-      g_WrkProof.errorCount++;
-      g_WrkProof.proofString[step] = -(long)'?';
-      if (returnFlag < 2) returnFlag = 2;
-      continue;
-    }
-
-    /* It's an assertion ($a or $p) */
-    j = *(long *)voidPtr; /* Statement number */
-    if (g_Statement[j].type != a_ && g_Statement[j].type != p_) bug(1714);
-    g_WrkProof.proofString[step] = j; /* Proof string */
-
-    if (j >= statemNum) { /* Error */
-      if (!g_WrkProof.errorCount) {
-        if (j == statemNum) {
-          sourceError(fbPtr, tokLength, statemNum, cat(
-              "The label at proof step ",
-              str((double)step + 1),
-             " is the label of this statement.  A statement may not be used to",
-              " prove itself.",NULL));
-        } else {
-          assignStmtFileAndLineNum(j); /* 9-Jan-2018 nm */
-          sourceError(fbPtr, tokLength, statemNum, cat(
-              "The label \"", g_Statement[j].labelName, "\" at proof step ",
-              str((double)step + 1),
-              " is the label of a future statement (at line ",
-              str((double)(g_Statement[j].lineNum)),
-              " in file ",g_Statement[j].fileName,
-              ").  Only previous statements may be referenced.",
-              NULL));
-        }
-      }
-      g_WrkProof.errorCount++;
-      if (returnFlag < 2) returnFlag = 2;
-    }
-
-  } /* Next step */
-
-  /******* Create the starting label map (local labels will be
-           added as they are found) *****/
-  g_WrkProof.compressedPfNumLabels = g_Statement[statemNum].numReqHyp;
-  nmbrTmpPtr = g_Statement[statemNum].reqHypList;
-  for (i = 0; i < g_WrkProof.compressedPfNumLabels; i++) {
-    g_WrkProof.compressedPfLabelMap[i] = nmbrTmpPtr[i];
-  }
-  for (i = 0; i < g_WrkProof.numSteps; i++) {
-    g_WrkProof.compressedPfLabelMap[i + g_WrkProof.compressedPfNumLabels] =
-        g_WrkProof.proofString[i];
-  }
-  g_WrkProof.compressedPfNumLabels = g_WrkProof.compressedPfNumLabels +
-      g_WrkProof.numSteps;
-
-  /* Re-initialization for the actual proof */
-  g_WrkProof.numSteps = 0;
-  g_WrkProof.RPNStackPtr = 0;
-
-  /******* Parse the compressed part of the proof *****/
-
-  /* 15-Oct-05 nm - Check to see if the old buggy compression is used.  If so,
-     warn the user to reformat, and switch to the buggy algorithm so that
-     parsing can proceed. */
-  bggyProofLen = g_Statement[statemNum].proofSectionLen -
-             (fbPtr - g_Statement[statemNum].proofSectionPtr);
-  /* Zap a zero at the end of the proof so we can use C string operations */
-  bggyZapSave = fbPtr[bggyProofLen];
-  fbPtr[bggyProofLen] = 0;
-  /* If the proof has "UVA" but doesn't have "UUA", it means the buggy
-     algorithm was used. */
-  bggyAlgo = 0;
-  if (strstr(fbPtr, "UV") != NULL) {
-    if (strstr(fbPtr, "UU") == NULL) {
-      bggyAlgo = 1;
-      print2("?Warning: the proof of \"%s\" uses obsolete compression.\n",
-          g_Statement[statemNum].labelName);
-      print2(" Please SAVE PROOF * / COMPRESSED to reformat your proofs.\n");
-    }
-  }
-  fbPtr[bggyProofLen] = bggyZapSave;
-
-  /* (Build the proof string and check the RPN stack) */
-  fbPtr = fbStartProof;
-  breakFlag = 0;
-  labelMapIndex = 0;
-  while (1) {
-    switch (chrType[(long)(fbPtr[0])]) {
-      case 0: /* "Letter" (i.e. A...T) */
-        if (!labelMapIndex) labelStart = fbPtr; /* Save for error msg */
-
-        /* Save pointer to source file vs. step for error messages */
-        tokLength = fbPtr - labelStart + 1; /* Token length */
-        g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] = tokLength;
-        g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = labelStart; /* Token ptr */
-
-        /* 15-Oct-05 nm - Obsolete (skips from YT to UVA, missing UUA) */
-        /* (actually, this part is coincidentally the same:)
-        labelMapIndex = labelMapIndex * lettersLen +
-            chrWeight[(long)(fbPtr[0])];
-        */
-        /* 15-Oct-05 nm - Corrected algorithm provided by Marnix Klooster. */
-        /* Decoding can be done as follows:
-             * n := 0
-             * for each character c:
-                * if c in ['U'..'Y']: n := n * 5 + (c - 'U' + 1)
-                * if c in ['A'..'T']: n := n * 20 + (c - 'A' + 1) */
-        labelMapIndex = labelMapIndex * lettersLen +
-            chrWeight[(long)(fbPtr[0])];
-        if (labelMapIndex >= g_WrkProof.compressedPfNumLabels) {
-          if (!g_WrkProof.errorCount) {
-            sourceError(labelStart, tokLength, statemNum, cat(
-     "This compressed label reference is outside the range of the label list.",
-                "  The compressed label value is ", str((double)labelMapIndex),
-                " but the largest label defined is ",
-                str((double)(g_WrkProof.compressedPfNumLabels - 1)), ".", NULL));
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-          labelMapIndex = 0; /* Make it something legal to avoid side effects */
-        }
-
-        stmt = g_WrkProof.compressedPfLabelMap[labelMapIndex];
-        g_WrkProof.proofString[g_WrkProof.numSteps] = stmt;
-
-        /* Update stack */
-        hypLocUnkFlag = 0;
-        if (stmt < 0) { /* Local label or '?' */
-          hypLocUnkFlag = 1;
-        } else {
-          if (g_Statement[stmt].type != (char)a_ &&
-              g_Statement[stmt].type != (char)p_) hypLocUnkFlag = 1;
-                                                               /* Hypothesis */
-        }
-        if (hypLocUnkFlag) { /* Hypothesis, local label ref, or unknown step */
-          g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
-          g_WrkProof.RPNStackPtr++;
-        } else { /* An assertion */
-
-          /* It's a valid assertion, so pop the stack */
-          numReqHyp = g_Statement[stmt].numReqHyp;
-
-          /* Error check for exhausted stack */
-          if (g_WrkProof.RPNStackPtr < numReqHyp) { /* Stack exhausted -- error */
-            if (!g_WrkProof.errorCount) {
-              tmpStrPtr = shortDumpRPNStack();
-              if (strcmp(left(tmpStrPtr,18),"RPN stack is empty")){
-                i = instr(1,tmpStrPtr,"contains ");
-                let(&tmpStrPtr,cat(left(tmpStrPtr,i + 7)," only",
-                  right(tmpStrPtr,i + 8),
-                  NULL));
-              }
-              if (numReqHyp == 1) {
-                let(&tmpStrPtr,cat("a hypothesis but the ",tmpStrPtr,NULL));
-              } else {
-                let(&tmpStrPtr,cat(str((double)numReqHyp)," hypotheses but the ",tmpStrPtr,
-                    NULL));
-              }
-              sourceError(fbPtr, tokLength, statemNum, cat(
-                  "At proof step ",
-                  str((double)(g_WrkProof.numSteps + 1)),", statement \"",
-                  g_Statement[stmt].labelName,"\" requires ",
-                  tmpStrPtr,".",NULL));
-              let(&tmpStrPtr, "");
-            }
-            /* Treat it like an unknown step so stack won't get exhausted */
-            g_WrkProof.errorCount++;
-            g_WrkProof.proofString[g_WrkProof.numSteps] = -(long)'?';
-            /* Push the stack */
-            g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
-            g_WrkProof.RPNStackPtr++;
-            if (returnFlag < 3) returnFlag = 3;
-            continue;
-          } /* End if stack exhausted */
-
-          numReqHyp = g_Statement[stmt].numReqHyp;
-          /* Error check for $e <- $f assignments (illegal) */
-          /* 18-Jun-2020 nm: Although it would be unusual to to this,
-             it is not illegal per the spec.  See
-             https://groups.google.com/d/msg/metamath/Cx_d84uorf8/0FrNYTM9BAAJ */
-          /**** Deleted 18-Jun-2020 nm ****
-          nmbrTmpPtr = g_Statement[stmt].reqHypList;
-          for (i = 0; i < numReqHyp; i++) {
-            if (g_Statement[nmbrTmpPtr[i]].type == e_) {
-              m = g_WrkProof.RPNStackPtr - numReqHyp + i;
-              k = g_WrkProof.proofString[g_WrkProof.RPNStack[m]];
-              if (k > 0) {
-                if (g_Statement[k].type == f_) {
-                  if (!g_WrkProof.errorCount) {
-                    sourceError(fbPtr, tokLength, statemNum, cat(
-                        "Statement \"", g_Statement[stmt].labelName,
-                        "\" (proof step ",
-                        str((double)(g_WrkProof.numSteps + 1)),
-                        ") has its \"$e\" hypothesis \"",
-                        g_Statement[nmbrTmpPtr[i]].labelName,
-                        "\" assigned the \"$f\" hypothesis \"",
-                        g_Statement[k].labelName,
-                        "\" at step ", str((double)(g_WrkProof.RPNStack[m] + 1)),
-                      ".  The assignment of \"$e\" with \"$f\" is not allowed.",
-                        NULL));
-                  }
-                  g_WrkProof.errorCount++;
-                  if (returnFlag < 2) returnFlag = 2;
-                }
-              }
-            }
-          }
-          **** End of 18-Jun-2020 deletion ****/
-
-          /* Pop the stack */
-          g_WrkProof.RPNStackPtr = g_WrkProof.RPNStackPtr - numReqHyp;
-          g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
-          g_WrkProof.RPNStackPtr++;
-
-        }
-
-        g_WrkProof.numSteps++;
-        labelMapIndex = 0; /* Reset it for next label */
-        break;
-
-      case 1: /* "Digit" (i.e. U...Y) */
-        /* 15-Oct-05 nm - Obsolete (skips from YT to UVA, missing UUA) */
-        /*
-        if (!labelMapIndex) {
-          / * First digit; mod digitsLen+1 * /
-          labelMapIndex = chrWeight[(long)(fbPtr[0])] + 1;
-          labelStart = fbPtr; / * Save label start for error msg * /
-        } else {
-          labelMapIndex = labelMapIndex * digitsLen +
-              chrWeight[(long)(fbPtr[0])];
-        }
-        */
-        /* 15-Oct-05 nm - Corrected algorithm provided by Marnix Klooster. */
-        /* Decoding can be done as follows:
-             * n := 0
-             * for each character c:
-                * if c in ['U'..'Y']: n := n * 5 + (c - 'U' + 1)
-                * if c in ['A'..'T']: n := n * 20 + (c - 'A' + 1) */
-        if (!labelMapIndex) {
-          labelMapIndex = chrWeight[(long)(fbPtr[0])] + 1;
-          labelStart = fbPtr; /* Save label start for error msg */
-        } else {
-          labelMapIndex = labelMapIndex * digitsLen +
-              chrWeight[(long)(fbPtr[0])] + 1;
-          if (bggyAlgo) labelMapIndex--; /* Adjust for buggy algorithm */
-        }
-        break;
-
-      case 2: /* "Colon" (i.e. Z) */
-        if (labelMapIndex) { /* In the middle of some digits */
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, 1, statemNum,
-             "A compressed label character was expected here.");
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-          labelMapIndex = 0;
-        }
-
-        /* Put local label in label map */
-        g_WrkProof.compressedPfLabelMap[g_WrkProof.compressedPfNumLabels] =
-          -1000 - (g_WrkProof.numSteps - 1);
-        g_WrkProof.compressedPfNumLabels++;
-
-        hypLocUnkFlag = 0;
-
-        /* 21-Mar-06 nm Fix bug reported by o'cat */
-        if (g_WrkProof.numSteps == 0) {
-          /* This will happen if labelChar (Z) is in 1st char pos of
-             compressed proof */
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, 1, statemNum, cat(
-              "A local label character must occur after a proof step.",NULL));
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-          /* We want to break here to prevent out of bound g_WrkProof.proofString
-             index below. */
-          break;
-        }
-
-        stmt = g_WrkProof.proofString[g_WrkProof.numSteps - 1];
-        if (stmt < 0) { /* Local label or '?' */
-          hypLocUnkFlag = 1;
-        } else {
-          if (g_Statement[stmt].type != (char)a_ &&
-              g_Statement[stmt].type != (char)p_) hypLocUnkFlag = 1;
-                                                                /* Hypothesis */
-        }
-        if (hypLocUnkFlag) { /* Hypothesis, local label ref, or unknown step */
-          /* If local label references a hypothesis or other local label,
-             it is not allowed because it complicates the language but doesn't
-             buy anything; would make $e to $f assignments harder to detect */
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, 1, statemNum, cat(
-                "The hypothesis or local label reference at proof step ",
-                str((double)(g_WrkProof.numSteps)),
-                " declares a local label.  Only \"$a\" and \"$p\" statement",
-                " labels may have local label declarations.",NULL));
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-        }
-
-        break;
-
-      case 3: /* White space */
-        break;
-
-      case 4: /* Dollar */
-        /* See if we're at the end of the statement */
-        if (fbPtr[1] == '.') {
-          breakFlag = 1;
-          break;
-        }
-        /* Otherwise, it must be a comment */
-        if (fbPtr[1] != '(' && fbPtr[1] != '!') {
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr + 1, 1, statemNum,
-             "Expected \".\", \"(\", or \"!\" here.");
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-        } else {
-          fbPtr = fbPtr + whiteSpaceLen(fbPtr) - 1; /* -1 because
-                  fbPtr will get incremented at end of loop */
-        }
-        break;
-
-      case 5: /* Question mark */
-        if (labelMapIndex) { /* In the middle of some digits */
-          if (!g_WrkProof.errorCount) {
-            sourceError(fbPtr, 1, statemNum,
-             "A compressed label character was expected here.");
-          }
-          g_WrkProof.errorCount++;
-          if (returnFlag < 2) returnFlag = 2;
-          labelMapIndex = 0;
-        }
-
-        /* Save pointer to source file vs. step for error messages */
-        g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] = 1;
-        g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = fbPtr; /* Token ptr */
-
-        g_WrkProof.proofString[g_WrkProof.numSteps] = -(long)'?';
-        /* returnFlag = 1 means that proof has unknown steps */
-        /* 6-Oct-05 nm Ensure that a proof with unknown steps doesn't
-           reset the severe error flag if returnFlag > 1 */
-        /* returnFlag = 1; */ /*bad - resets severe error flag*/
-        if (returnFlag < 1) returnFlag = 1; /* 6-Oct-05 */
-
-        /* Update stack */
-        g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
-        g_WrkProof.RPNStackPtr++;
-
-        g_WrkProof.numSteps++;
-        break;
-
-      case 6: /* Illegal */
-        if (!g_WrkProof.errorCount) {
-          sourceError(fbPtr, 1, statemNum,
-           "This character is not legal in a compressed proof.");
-        }
-        g_WrkProof.errorCount++;
-        if (returnFlag < 2) returnFlag = 2;
-        break;
-      default:
-        bug(1715);
-        break;
-    } /* End switch chrType[fbPtr[0]] */
-
-    if (breakFlag) break;
-    fbPtr++;
-
-  } /* End while (1) */
-
-  if (labelMapIndex) { /* In the middle of some digits */
-    if (!g_WrkProof.errorCount) {
-      sourceError(fbPtr, 1, statemNum,
-       "A compressed label character was expected here.");
-    }
-    g_WrkProof.errorCount++;
-    if (returnFlag < 2) returnFlag = 2;
-    /* labelMapIndex = 0; */ /* 18-Sep-2013 never used */
-  }
-
-  /* If proof is empty, make it have one unknown step */
-  if (g_WrkProof.numSteps == 0) {
-
-    /* For now, this is an error. */
-    if (!g_WrkProof.errorCount) {
-      sourceError(fbPtr, 2, statemNum,
-          "The proof is empty.  If you don't know the proof, make it a \"?\".");
-    }
-    g_WrkProof.errorCount++;
-
-    /* Save pointer to source file vs. step for error messages */
-    g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] = 1;
-    g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = fbPtr; /* Token ptr */
-
-    g_WrkProof.proofString[g_WrkProof.numSteps] = -(long)'?';
-    /* 21-Mar-06 nm Deleted 2 lines below; added 3rd - there could be a
-       previous error; see 21-Mar-06 entry above */
-    /* if (returnFlag > 0) bug(1722); */ /* 13-Oct-05 nm */
-    /* returnFlag = 1; */ /* Flag that proof has unknown steps */
-    if (returnFlag < 1) returnFlag = 1; /* Flag that proof has unknown steps */
-
-    /* Update stack */
-    g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
-    g_WrkProof.RPNStackPtr++;
-
-    g_WrkProof.numSteps++;
-    /* 13-Oct-05 nm The line below is redundant */
-    /*if (returnFlag < 1) returnFlag = 1;*/ /* Flag for proof with unknown steps */
-  }
-
-  g_WrkProof.proofString[g_WrkProof.numSteps] = -1; /* End of proof */
-  nmbrZapLen(g_WrkProof.proofString, g_WrkProof.numSteps);
-    /* Zap mem pool actual length (because nmbrLen will be used later on this)*/
-
-  /* The stack should have one entry */
-  if (g_WrkProof.RPNStackPtr != 1) {
-    tmpStrPtr = shortDumpRPNStack();
-    fbPtr = g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps - 1];
-    if (!g_WrkProof.errorCount) {
-      sourceError(fbPtr, proofTokenLen(fbPtr), statemNum,
-          cat("After proof step ",
-          str((double)(g_WrkProof.numSteps))," (the last step), the ",
-          tmpStrPtr,".  It should contain exactly one entry.",NULL));
-    }
-    g_WrkProof.errorCount++;
-   if (returnFlag < 3) returnFlag = 3;
-  }
-
-  g_WrkProof.errorSeverity = returnFlag;
-  return (returnFlag);
-
-} /* parseCompressedProof */
-
-
-/* 11-Sep-2016 nm */
-/* The caller must deallocate the returned nmbrString! */
-/* This function just gets the proof so the caller doesn't have to worry
-   about cleaning up the g_WrkProof structure.  The returned proof is normal
-   or compressed depending on the .mm source; called nmbrUnsquishProof() to
-   make sure it is uncompressed if required. */
-/* If there is a severe error in the proof, a 1-step proof with "?" will
-   be returned. */
-/* If printFlag = 1, then error messages are printed, otherwise they aren't.
-   This is only partially implemented; some errors may still result in a
-   printout.  TODO: pass printFlag to parseProof(), verifyProof() */
-/* TODO: use this function to simplify some code that calls parseProof
-   directly. */
-nmbrString *getProof(long statemNum, flag printFlag) {
-  nmbrString *proof = NULL_NMBRSTRING;
-  parseProof(statemNum);
-  /* We do not need verifyProof() since we don't care about the math
-     strings for the proof steps in this function. */
-  /* verifyProof(statemNum); */ /* Necessary to set RPN stack ptrs
-                             before calling cleanWrkProof() */
-  if (g_WrkProof.errorSeverity > 1) {
-    if (printFlag) print2(
-         "The starting proof has a severe error.  It will not be used.\n");
-    nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
-  } else {
-    nmbrLet(&proof, g_WrkProof.proofString);
-  }
-  /* Note: the g_WrkProof structure is never deallocated but grows to
-     accomodate the largest proof found so far.  The ERASE command will
-     deallocate it, though.   cleanWrkProof() just deallocates math strings
-     assigned by verifyProof() that aren't needed by this function. */
-  /* cleanWrkProof(); */ /* Deallocate verifyProof() storage */
-  return proof;
-} /* getProof */
-
-
-
-/* 2-Feb-2018 nm - lineNum and fileName are now computed by
-   getFileAndLineNum() */
-void rawSourceError(char *startFile, char *ptr, long tokLen,
-    /*long lineNum, vstring fileName,*/ vstring errMsg)
-{
-  char *startLine;
-  char *endLine;
-  vstring errLine = "";
-  vstring errorMsg = "";
-
-  /* 2-Feb-2018 nm */
-  vstring fileName = "";
-  long lineNum;
-
-  let(&errorMsg, errMsg); /* Prevent deallocation of errMsg */
-
-  /* 2-Feb-2018 nm */
-  fileName = getFileAndLineNum(startFile/*=g_sourcePtr*/, ptr, &lineNum);
-
-  /* Get the line with the error on it */
-  startLine = ptr;
-  while (startLine[0] != '\n' && startLine > startFile) {
-    startLine--;
-  }
-  if (startLine[0] == '\n'
-      && startLine != ptr) /* 8/20/04 nm In case of 0-length line */
-    startLine++; /* Go to 1st char on line */
-  endLine = ptr;
-  while (endLine[0] != '\n' && endLine[0] != 0) {
-    endLine++;
-  }
-  endLine--;
-  let(&errLine, space(endLine - startLine + 1));
-  if (endLine - startLine + 1 < 0) bug(1721);
-  memcpy(errLine, startLine, (size_t)(endLine - startLine) + 1);
-  errorMessage(errLine, lineNum, ptr - startLine + 1, tokLen, errorMsg,
-      fileName, 0, (char)error_);
-  print2("\n");
-  let(&errLine, "");
-  let(&errorMsg, "");
-  let(&fileName, ""); /* 2-Feb-2018 nm */
-} /* rawSourceError */
-
-/* The global g_sourcePtr is assumed to point to the start of the raw input
-     buffer.
-   The global g_sourceLen is assumed to be length of the raw input buffer.
-   The global g_IncludeCall array is referenced. */
-void sourceError(char *ptr, long tokLen, long stmtNum, vstring errMsg)
-{
-  char *startLine;
-  char *endLine;
-  vstring errLine = "";
-  long lineNum;
-  vstring fileName = "";
-  vstring errorMsg = "";
-
-  /* 3-May-2017 nm */
-  /* Used for the case where a source file section has been modified */
-  char *locSourcePtr;
-  /*long locSourceLen;*/
-
-  /* 3-May-2017 nm */
-  /* Initialize local pointers to raw input source */
-  locSourcePtr = g_sourcePtr;
-  /*locSourceLen = g_sourceLen;*/
-
-  let(&errorMsg, errMsg); /* errMsg may become deallocated if this function is
-                         called with a string function argument (cat, etc.) */
-
-  if (!stmtNum) {
-    lineNum = 0;
-    goto SKIP_LINE_NUM; /* This isn't a source file parse */
-  }
-  if (ptr < g_sourcePtr || ptr > g_sourcePtr + g_sourceLen) {
-    /* The pointer is outside the raw input buffer, so it must be a
-       SAVEd proof or other overwritten section, so there is no line number. */
-    /* 3-May-2017 nm */
-    /* Reassign the beginning and end of the source pointer to the
-       changed section */
-    if (g_Statement[stmtNum].labelSectionChanged == 1
-         && ptr >= g_Statement[stmtNum].labelSectionPtr
-         && ptr <= g_Statement[stmtNum].labelSectionPtr
-             + g_Statement[stmtNum].labelSectionLen) {
-      locSourcePtr = g_Statement[stmtNum].labelSectionPtr;
-      /*locSourceLen = g_Statement[stmtNum].labelSectionLen;*/
-    } else if (g_Statement[stmtNum].mathSectionChanged == 1
-         && ptr >= g_Statement[stmtNum].mathSectionPtr
-         && ptr <= g_Statement[stmtNum].mathSectionPtr
-             + g_Statement[stmtNum].mathSectionLen) {
-      locSourcePtr = g_Statement[stmtNum].mathSectionPtr;
-      /*locSourceLen = g_Statement[stmtNum].mathSectionLen;*/
-    } else if (g_Statement[stmtNum].proofSectionChanged == 1
-         && ptr >= g_Statement[stmtNum].proofSectionPtr
-         && ptr <= g_Statement[stmtNum].proofSectionPtr
-             + g_Statement[stmtNum].proofSectionLen) {
-      locSourcePtr = g_Statement[stmtNum].proofSectionPtr;
-      /*locSourceLen = g_Statement[stmtNum].proofSectionLen;*/
-    } else {
-      /* ptr points to neither the original source nor a modified section */
-      bug(1741);
-    }
-
-    lineNum = 0;
-    goto SKIP_LINE_NUM;
-  }
-
-  /* 9-Jan-2018 nm */
-  /*let(&fileName, "");*/ /* No need - already assigned to empty string */
-  fileName = getFileAndLineNum(locSourcePtr/*=g_sourcePtr here*/, ptr, &lineNum);
-
- SKIP_LINE_NUM:
-  /* Get the line with the error on it */
-  if (lineNum != 0 && ptr > locSourcePtr) {
-    startLine = ptr - 1; /* Allows pointer to point to \n. */
-  } else {
-    /* Special case:  Error message starts at beginning of file or
-       the beginning of a changed section. */
-    /* Or, it's a non-source statement; must not point to \n. */
-    startLine = ptr;
-  }
-
-
-  /**** 3-May-2017 nm Deleted
-  /@ Scan back to beginning of line with error @/
-  while (startLine[0] != '\n' && (!lineNum || startLine > locSourcePtr)
-      /@ ASCII 1 flags start of SAVEd proof @/
-      && (lineNum || startLine[0] != 1)
-      /@ lineNum is 0 (e.g. no stmt); stop scan at beg. of file
-         or beginning of a changed section @/
-      && startLine != locSourcePtr) {
-  ***/
-
-  /* 3-May-2017 nm */
-  /* Scan back to beginning of line with error */
-  while (startLine[0] != '\n' && startLine > locSourcePtr) {
-
-    startLine--;
-  }
-  /* if (startLine[0] == '\n' || startLine[0] == 1) startLine++; */
-  /* 3-May-2017 nm */
-  if (startLine[0] == '\n') startLine++;
-
-  /* Scan forward to end of line with error */
-  endLine = ptr;
-  while (endLine[0] != '\n' && endLine[0] != 0) {
-    endLine++;
-  }
-  endLine--;
-
-  /* Save line with error (with no newline on it) */
-  let(&errLine, space(endLine - startLine + 1));
-  memcpy(errLine, startLine, (size_t)(endLine - startLine) + 1);
-
-  if (!lineNum) {
-    /* Not a source file parse */
-    errorMessage(errLine, lineNum, ptr - startLine + 1, tokLen, errorMsg,
-        NULL, stmtNum, (char)error_);
-  } else {
-    errorMessage(errLine, lineNum,
-        ptr - startLine + 1, tokLen, /* column */
-        errorMsg,
-        /*g_IncludeCall[i].source_fn,*/
-        fileName,
-        stmtNum,
-        (char)error_ /* severity */);
-  }
-  let(&errLine, "");
-  let(&errorMsg, "");
-  let(&fileName, "");
-} /* sourceError */
-
-
-void mathTokenError(long tokenNum /* 0 is 1st one */,
-    nmbrString *tokenList, long stmtNum, vstring errMsg)
-{
-  long i;
-  char *fbPtr;
-  fbPtr = g_Statement[stmtNum].mathSectionPtr;
-  fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-  /* Scan past the tokens before the desired one. */
-  /* We use the parsed token length rather than tokenLen() to
-     account for adjacent tokens with no white space. */
-  for (i = 0; i < tokenNum; i++) {
-    fbPtr = fbPtr + g_MathToken[tokenList[i]].length;
-    fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-  }
-  sourceError(fbPtr, g_MathToken[tokenList[tokenNum]].length,
-      stmtNum, errMsg);
-} /* mathTokenError */
-
-vstring shortDumpRPNStack(void) {
-  /* The caller must deallocate the returned string. */
-  vstring tmpStr = "";
-  vstring tmpStr2 = "";
-  long i, k, m;
-
-  for (i = 0; i < g_WrkProof.RPNStackPtr; i++) {
-     k = g_WrkProof.RPNStack[i]; /* Step */
-     let(&tmpStr,space(g_WrkProof.stepSrcPtrNmbr[k]));
-     memcpy(tmpStr,g_WrkProof.stepSrcPtrPntr[k],
-         (size_t)(g_WrkProof.stepSrcPtrNmbr[k])); /* Label at step */
-     let(&tmpStr2,cat(
-         tmpStr2,", ","\"",tmpStr,"\" (step ", str((double)k + 1),")",NULL));
-  }
-  let(&tmpStr2,right(tmpStr2,3));
-  if (g_WrkProof.RPNStackPtr == 2) {
-    m = instr(1, tmpStr2, ",");
-    let(&tmpStr2,cat(left(tmpStr2,m - 1)," and ",
-        right(tmpStr2,m + 1),NULL));
-  }
-  if (g_WrkProof.RPNStackPtr > 2) {
-    for (m = (long)strlen(tmpStr2); m > 0; m--) { /* Find last comma */
-      if (tmpStr2[m - 1] == ',') break;
-    }
-    let(&tmpStr2,cat(left(tmpStr2,m - 1),", and ",
-        right(tmpStr2,m + 1),NULL));
-  }
-  if (g_WrkProof.RPNStackPtr == 1) {
-    let(&tmpStr2,cat("one entry, ",tmpStr2,NULL));
-  } else {
-    let(&tmpStr2,cat(str((double)(g_WrkProof.RPNStackPtr))," entries: ",tmpStr2,NULL));
-  }
-  let(&tmpStr2,cat("RPN stack contains ",tmpStr2,NULL));
-  if (g_WrkProof.RPNStackPtr == 0) let(&tmpStr2,"RPN stack is empty");
-  let(&tmpStr, "");
-  return(tmpStr2);
-} /* shortDumpRPNStack */
-
-/* 10/10/02 */
-/* ???Todo:  use this elsewhere in mmpars.c to modularize this lookup */
-/* Lookup $a or $p label and return statement number.
-   Return -1 if not found. */
-long lookupLabel(vstring label)
-{
-  void *voidPtr; /* bsearch returned value */
-  long statemNum;
-  /* Find the statement number */
-  voidPtr = (void *)bsearch(label, g_labelKeyBase, (size_t)g_numLabelKeys,
-      sizeof(long), labelSrchCmp);
-  if (!voidPtr) {
-    return (-1);
-  }
-  statemNum = (*(long *)voidPtr); /* Statement number */
-  if (g_Statement[statemNum].type != a_ && g_Statement[statemNum].type != p_)
-      bug(1718);
-  return (statemNum);
-} /* lookupLabel */
-
-
-/* Label comparison for qsort */
-int labelSortCmp(const void *key1, const void *key2)
-{
-  /* Returns -1 if key1 < key2, 0 if equal, 1 if key1 > key2 */
-  return (strcmp(g_Statement[ *((long *)key1) ].labelName,
-      g_Statement[ *((long *)key2) ].labelName));
-} /* labelSortCmp */
-
-
-/* Label comparison for bsearch */
-int labelSrchCmp(const void *key, const void *data)
-{
-  /* Returns -1 if key < data, 0 if equal, 1 if key > data */
-  return (strcmp(key,
-      g_Statement[ *((long *)data) ].labelName));
-} /* labelSrchCmp */
-
-
-/* Math symbol comparison for qsort */
-int mathSortCmp(const void *key1, const void *key2)
-{
-  /* Returns -1 if key1 < key2, 0 if equal, 1 if key1 > key2 */
-  return (strcmp(g_MathToken[ *((long *)key1) ].tokenName,
-      g_MathToken[ *((long *)key2) ].tokenName));
-}
-
-
-/* Math symbol comparison for bsearch */
-/* Here, key is pointer to a character string. */
-int mathSrchCmp(const void *key, const void *data)
-{
-  /* Returns -1 if key < data, 0 if equal, 1 if key > data */
-  return (strcmp(key, g_MathToken[ *((long *)data) ].tokenName));
-}
-
-
-/* Hypotheses and local label comparison for qsort */
-int hypAndLocSortCmp(const void *key1, const void *key2)
-{
-  /* Returns -1 if key1 < key2, 0 if equal, 1 if key1 > key2 */
-  return (strcmp( ((struct sortHypAndLoc *)key1)->labelName,
-      ((struct sortHypAndLoc *)key2)->labelName));
-}
-
-
-/* Hypotheses and local label comparison for bsearch */
-/* Here, key is pointer to a character string. */
-int hypAndLocSrchCmp(const void *key, const void *data)
-{
-  /* Returns -1 if key < data, 0 if equal, 1 if key > data */
-  return (strcmp(key, ((struct sortHypAndLoc *)data)->labelName));
-}
-
-
-/* This function returns the length of the white space starting at ptr.
-   Comments are considered white space.  ptr should point to the first character
-   of the white space.  If ptr does not point to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned. */
-long whiteSpaceLen(char *ptr)
-{
-  long i = 0;
-  char tmpchr;
-  char *ptr1;
-  while (1) {
-    tmpchr = ptr[i];
-    if (!tmpchr) return (i); /* End of string */
-    if (tmpchr == '$') {
-      if (ptr[i + 1] == '(') {
-        while (1) {
-          /*ptr1 = strchr(ptr + i + 2, '$'); */
-          /* in-line code for speed */
-          /* (for the lcc-win32 compiler, this speeds it up from 94 sec
-              for set.mm read to 4 sec) */
-          for (ptr1 = ptr + i + 2; ptr1[0] != '$'; ptr1++) {
-            if (ptr1[0] == 0) {
-              if ('$' != 0)
-                ptr1 = NULL;
-              break;
-            }
-          }
-          /* end in-line strchr code */
-          if (!ptr1) {
-            return(i + (long)strlen(&ptr[i])); /* Unterminated comment - goto EOF */
-          }
-          if (ptr1[1] == ')') break;
-          i = ptr1 - ptr;
-        }
-        i = ptr1 - ptr + 2;
-        continue;
-      } else {
-        if (ptr[i + 1] == '!') {
-          ptr1 = strchr(ptr + i + 2, '\n');
-          if (!ptr1) bug(1716);
-          i = ptr1 - ptr + 1;
-          continue;
-        }
-        return(i);
-      }
-    } /* if (tmpchr == '$') */
-    if (isgraph((unsigned char)tmpchr)) return (i);
-    i++;
-  }
-  return(0); /* Dummy return - never happens */
-} /* whiteSpaceLen */
-
-
-/* 31-Dec-2017 nm For .mm file splitting */
-/* This function is like whiteSpaceLen() except that comments are NOT
-   considered white space.  ptr should point to the first character
-   of the white space.  If ptr does not point to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned. */
-long rawWhiteSpaceLen(char *ptr)
-{
-  long i = 0;
-  char tmpchr;
-  while (1) {
-    tmpchr = ptr[i];
-    if (!tmpchr) return (i); /* End of string */
-    if (isgraph((unsigned char)tmpchr)) return (i);
-    i++;
-  }
-  return(0); /* Dummy return - never happens */
-} /* rawWhiteSpaceLen */
-
-
-/* This function returns the length of the token (non-white-space) starting at
-   ptr.  Comments are considered white space.  ptr should point to the first
-   character of the token.  If ptr points to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned.  If ptr
-   points to a keyword, 0 is returned.  A keyword ends a token. */
-/* Tokens may be of the form "$nn"; this is tolerated (used in parsing
-   user math strings in parseMathTokens()).  An (illegal) token of this form
-   in the source will be detected earlier, so this won't cause
-   syntax violations to slip by in the source. */
-long tokenLen(char *ptr)
-{
-  long i = 0;
-  char tmpchr;
-  while (1) {
-    tmpchr = ptr[i];
-    if (tmpchr == '$') {
-      if (ptr[i + 1] == '$') { /* '$$' character */
-        i = i + 2;
-        continue;
-      } else {
-        /* Tolerate digit after "$" */
-        if (ptr[i + 1] >= '0' && ptr[i + 1] <= '9') {
-          i = i + 2;
-          continue;
-        } else {
-          return(i); /* Keyword or comment */
-        }
-      }
-    }
-    if (!isgraph((unsigned char)tmpchr)) return (i); /* White space or null */
-    i++;
-  }
-  return(0); /* Dummy return (never happens) */
-} /* tokenLen */
-
-
-/* This function returns the length of the token (non-white-space) starting at
-   ptr.  Comments are considered white space.  ptr should point to the first
-   character of the token.  If ptr points to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned. */
-/* Unlike tokenLen(), keywords are not treated as special.  In particular:
-   if ptr points to a keyword, 0 is NOT returned (instead, 2 is returned),
-   and a keyword does NOT end a token (which is a relic of days before
-   whitespace surrounding a token was part of the spec, but still serves
-   a useful purpose in token() for friendlier error detection). */
-long rawTokenLen(char *ptr)
-{
-  long i = 0;
-  char tmpchr;
-  while (1) {
-    tmpchr = ptr[i];
-    if (!isgraph((unsigned char)tmpchr)) return (i); /* White space or null */
-    i++;
-  }
-  return(0); /* Dummy return (never happens) */
-} /* rawTokenLen */
-
-
-/* This function returns the length of the proof token starting at
-   ptr.  Comments are considered white space.  ptr should point to the first
-   character of the token.  If ptr points to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned.  If ptr
-   points to a keyword, 0 is returned.  A keyword ends a token.
-   ":" and "?" and "(" and ")" and "=" (25-Jan-2016) are considered tokens. */
-long proofTokenLen(char *ptr)
-{
-  long i = 0;
-  char tmpchr;
-  if (ptr[0] == ':') return (1); /* The token is a colon */
-  if (ptr[0] == '?') return (1); /* The token is a "?" */
-  if (ptr[0] == '(') return (1); /* The token is a "(" (compressed proof) */
-  if (ptr[0] == ')') return (1); /* The token is a ")" (compressed proof) */
-  /* 25-Jan-2016 nm */
-  if (ptr[0] == '=') return (1); /* The token is a "=" (/EXPLICIT proof) */
-  while (1) {
-    tmpchr = ptr[i];
-    if (tmpchr == '$') {
-      if (ptr[i + 1] == '$') { /* '$$' character */
-        i = i + 2;
-        continue;
-      } else {
-        return(i); /* Keyword or comment */
-      }
-    }
-    if (!isgraph((unsigned char)tmpchr)) return (i); /* White space or null */
-    if (tmpchr == ':') return(i); /* Colon ends a token */
-    if (tmpchr == '?') return(i); /* "?" ends a token */
-    if (tmpchr == '(') return(i); /* "(" ends a token */
-    if (tmpchr == ')') return(i); /* ")" ends a token */
-    if (tmpchr == '=') return(i); /* "=" ends a token */ /* 25-Jan-2016 nm */
-    i++;
-  }
-  return(0); /* Dummy return - never happens */
-}
-
-
-/* 9-Jan-2018 nm */
-/* Counts the number of \n between start for length chars.
-   If length = -1, then use end-of-string 0 to stop.
-   If length >= 0, then scan at most length chars, but stop
-       if end-of-string 0 is found. */
-long countLines(vstring start, long length) {
-  long lines, i;
-  lines = 0;
-  if (length == -1) {
-    i = 0;
-    while (1) {
-      if (start[i] == '\n') lines++;
-      if (start[i] == 0) break;
-      i++;
-    }
-  } else {
-    for (i = 0; i < length; i++) {
-      if (start[i] == '\n') lines++;
-      if (start[i] == 0) break;
-    }
-  }
-  return lines;
-} /* countLines */
-
-
-/* Return (for output) the complete contents of a statement, including all
-   white space and comments, from first token through all white space and
-   comments after last token. */
-/* This allows us to modify the input file with Metamath. */
-/* Note: the text near end of file is obtained from g_Statement[g_statements
-   + 1] */
-/* ???This does not yet implement restoration of the various input files;
-      all included files are merged into one. */
-/* Caller must deallocated returned string. */
-/* reformatFlag= 0: WRITE SOURCE, 1: WRITE SOURCE / REFORMAT,
-   2: WRITE SOURCE / WRAP */
-/* Note that the labelSection, mathSection, and proofSection do not
-   contain keywords ($a, $p,...; $=; $.).  The keywords are added
-   by this function when the statement is written. */
-vstring outputStatement(long stmt, /*flag cleanFlag, 3-May-2017 removed */
-    flag reformatFlag)
-{
-  vstring labelSection = "";
-  vstring mathSection = "";
-  vstring proofSection = "";
-  vstring labelSectionSave = "";
-  vstring mathSectionSave = "";
-  vstring proofSectionSave = "";
-  vstring output = "";
-  /* flag newProofFlag; */ /* deleted 3-May-2017 nm */
-  /* For reformatting: */
-  long pos;
-  long indent;
-  static long dollarDpos = 0;
-  static char previousType = illegal_;  /* '?' in mmdata.h */
-  long commentStart;
-  long commentEnd;
-  vstring comment = "";
-  vstring str1 = "";
-  long length;
-  flag nowrapHtml;
-
-  /* For getContribs in-line error insertion to assist massive corrections
-  long i;
-  vstring ca = "", cd = "", ra = "", rd = "", sa = "", sd = "", md = "";
-  long saveWidth;
-  */
-
-
-  let(&labelSection, space(g_Statement[stmt].labelSectionLen));
-  memcpy(labelSection, g_Statement[stmt].labelSectionPtr,
-      (size_t)(g_Statement[stmt].labelSectionLen));
-
-  if (stmt == g_statements + 1) return labelSection; /* Special case - EOF */
-
-  /******* 3-May-2017 nm  "/CLEAN" is no longer supported
-  /@ 1-Jul-2011 nm @/
-  if (cleanFlag) {
-    /@ cleanFlag = 1: User is removing theorems with $( [?] $) dates @/
-    /@ Most of the WRITE SOURCE / CLEAN processing is done in the
-       writeSource() that calls this.  Here, we just remove any
-       $( [?} $) date comment missed by that algorithm. @/
-    if (labelSection[0] == '\n') { /@ True unless user edited source @/
-      pos = instr(1, labelSection, "$( [?] $)");
-      if (pos != 0) {
-        pos = instr(pos + 9, labelSection, "\n");
-        if (pos != 0) {
-          /@ Use pos instead of pos + 1 so that we include the \n @/
-          let(&labelSection, right(labelSection, pos));
-        }
-      }
-    }
-  }
-  ************/
-
-  let(&mathSection, space(g_Statement[stmt].mathSectionLen));
-  memcpy(mathSection, g_Statement[stmt].mathSectionPtr,
-      (size_t)(g_Statement[stmt].mathSectionLen));
-
-  let(&proofSection, space(g_Statement[stmt].proofSectionLen));
-  memcpy(proofSection, g_Statement[stmt].proofSectionPtr,
-      (size_t)(g_Statement[stmt].proofSectionLen));
-
-  /* 31-Dec-2017 nm */
-  let(&labelSectionSave, labelSection);
-  let(&mathSectionSave, mathSection);
-  let(&proofSectionSave, proofSection);
-
-
-  /* 12-Jun-2011 nm Added this section to reformat statements to match the
-     current set.mm convention */
-  if (reformatFlag > 0) {  /* 1 = WRITE SOURCE / FORMAT or 2 = / REWRAP */
-    /* Put standard indentation before the ${, etc. */
-#define INDENT_FIRST 2
-#define INDENT_INCR 2
-    indent = INDENT_FIRST + (INDENT_INCR * g_Statement[stmt].scope);
-    /* g_Statement[stmt].scope is at start of stmt not end; adjust $} */
-    if (g_Statement[stmt].type == rb_) indent = indent - INDENT_INCR;
-
-    /* 9-Jul-2011 nm Added */
-    /* Untab the label section */
-    if (strchr(labelSection, '\t') != NULL) { /* Only if has tab (speedup) */
-      let(&labelSection, edit(labelSection, 2048/*untab*/));
-    }
-    /* Untab the math section */
-    if (strchr(mathSection, '\t') != NULL) { /* Only if has tab (speedup) */
-      let(&mathSection, edit(mathSection, 2048/*untab*/));
-    }
-
-    /* Reformat the label section */
-
-    /* Remove spaces a end of line */
-    /* This is a pretty inefficient loop, but hopefully lots of spaces
-       at end of lines is a rare occurrence */
-    while (1) {
-      pos = instr(1, labelSection, " \n");
-      if (pos == 0) break;
-      let(&labelSection, cat(left(labelSection, pos - 1),
-          right(labelSection, pos + 1), NULL));
-    }
-
-    /* Don't allow more than 2 consecutive blank lines */
-    while (1) {
-      pos = instr(1, labelSection, "\n\n\n\n");
-      if (pos == 0) break;
-      let(&labelSection, cat(left(labelSection, pos - 1),
-          right(labelSection, pos + 1), NULL));
-    }
-
-    switch (g_Statement[stmt].type) {
-      case lb_: /* ${ */
-      case rb_: /* $} */
-      case v_: /* $v */
-      case c_: /* $c */
-      case d_: /* $d */
-        /* Get the last newline */
-        pos = rinstr(labelSection, "\n");
-        /* If there is none, insert it (unless first line in file) */
-        if (pos == 0 && stmt > 1) {
-          let(&labelSection, cat(edit(labelSection, 128 /* trailing spaces */),
-              "\n", NULL));
-          pos = (long)strlen(labelSection) + 1;
-        }
-        /* Put a blank line between $} and ${ if there is none */
-        if (stmt > 1) {
-          if (pos == 1 && g_Statement[stmt].type == lb_
-              && g_Statement[stmt - 1].type == rb_) {
-            let(&labelSection, "\n\n");
-            pos = 2;
-          }
-        }
-        let(&labelSection, cat(left(labelSection, pos),
-            space(indent), NULL));
-        if (g_Statement[stmt].type == d_) {
-          let(&mathSection, edit(mathSection,
-              4 /* discard LF */ + 16 /* reduce spaces */));
-          if (previousType == d_) {
-            /* See if the $d can be added to the current line */
-            if (dollarDpos + 2 + (signed)(strlen(mathSection)) + 4
-                <= g_screenWidth) {
-              let(&labelSection, "  ");  /* 2 spaces between $d's */
-              dollarDpos = dollarDpos + 2 + (long)strlen(mathSection) + 4;
-            } else {
-              /* Add 4 = '$d' length + '$.' length */
-              dollarDpos = indent + (long)strlen(mathSection) + 4;
-            }
-          } else {
-            dollarDpos = indent + (long)strlen(mathSection) + 4;
-          }
-        }
-        break;
-      case a_:    /* $a */
-      case p_:    /* $p */
-        /* Get last $( */
-        commentStart = rinstr(labelSection,  "$(");
-        /* Get last $) */
-        commentEnd = rinstr(labelSection, "$)") + 1;
-        if (commentEnd < commentStart) {
-          print2("?Make sure syntax passes before running / REWRAP.\n");
-          bug(1725);
-        }
-        if (commentStart != 0) {
-          let(&comment, seg(labelSection, commentStart, commentEnd));
-        } else {
-          /* If there is no comment before $a or $p, add dummy comment */
-          let(&comment, "$( PLEASE PUT DESCRIPTION HERE. $)");
-        }
-
-        /* 4-Nov-2015 The section below is for a one-time attribution in
-           definitions and should be commented out for normal use. */
-        /******* TODO: DELETE THIS SOMEDAY *********/
-        /*********
-        long j;
-        if (g_Statement[stmt].type == a_
-             && instr(1, comment, "(Contributed") == 0
-             && (!strcmp(left(g_Statement[stmt].labelName, 3), "df-")
-               || !strcmp(left(g_Statement[stmt].labelName, 3), "ax-"))) {
-          let(&str1, "");
-          str1 = traceUsage(stmt, 0, 0);
-          for (i = 1; i <= g_statements; i++) {
-            if (str1[i] == 'Y') break;
-          }
-          if (i >= g_statements) {
-             let(&ca, "??");
-             let(&cd, cat("??", "-???", "-????", NULL));
-          } else {
-
-            /@ 3-May-2017 nm (not tested because code is commented out) @/
-            let(&ca, "");
-            ca = getContrib(i, CONTRIBUTOR);
-            let(&cd, "");
-            cd = getContrib(i, CONTRIB_DATE);
-            let (&rd, "");
-            rd = getContrib(i, REVISE_DATE);
-
-            /@@@@@@@@@ deleted 3-May-2017
-            getContrib(i, &ca, &cd, &ra, &rd, &sa, &sd, &md, 0);
-            @@@@@@/
-
-            if (cd[0] == 0) {
-              let(&ca, "??");
-              getProofDate(i, &cd, &rd);
-              if (rd[0]) let(&cd, rd);
-              if (cd[0] == 0) {
-                let(&cd, cat("??", "-???", "-????", NULL));
-              }
-            }
-          }
-          let(&comment, cat(left(comment, (long)strlen(comment) - 2),
-              " (Contributed by ", ca, ", ", cd, ".) $)", NULL));
-          let(&ca, "");
-          let(&cd, "");
-          let(&ra, "");
-          let(&rd, "");
-          let(&sa, "");
-          let(&sd, "");
-          let(&str1, "");
-        }
-
-        if (g_Statement[stmt].type == p_
-           && instr(1, comment, "(Contributed") == 0) {
-          getProofDate(stmt, &cd, &rd);
-          if (rd[0]) let(&cd, rd);
-          if (cd[0] == 0) {
-            let(&cd, cat("??", "-???", "-????", NULL));
-          }
-
-          i = instr(1, comment, "(Revised") - 1;
-          if (i <= 0) i = (long)strlen(comment);
-          j = instr(1, comment, "(Proof shorten") - 1;
-          if (j <= 0) j = (long)strlen(comment);
-
-          if (j < i) i = j;
-          if ((long)strlen(comment) - 2 < i) i = (long)strlen(comment) - 2;
-
-          let(&ca, "??");
-          let(&comment, cat(left(comment, i - 1),
-              " (Contributed by ", ca, ", ", cd, ".) ", right(comment, i),
-              NULL));
-          let(&ca, "");
-          let(&cd, "");
-          let(&ra, "");
-          let(&rd, "");
-          let(&sa, "");
-          let(&sd, "");
-          let(&str1, "");
-        }
-        ************/
-
-        let(&labelSection, left(labelSection, commentStart - 1));
-        /* Get the last newline before the comment */
-        pos = rinstr(labelSection, "\n");
-
-        /* 9-Jul-2011 nm Added */
-        /* If previous statement was $e, take out any blank line */
-        if (previousType == e_ && pos == 2 && labelSection[0] == '\n') {
-          let(&labelSection, right(labelSection, 2));
-          pos = 1;
-        }
-
-        /* If there is no '\n', insert it (unless first line in file) */
-        if (pos == 0 && stmt > 1) {
-          let(&labelSection, cat(edit(labelSection, 128 /* trailing spaces */),
-              "\n", NULL));
-          pos = (long)strlen(labelSection) + 1;
-        }
-
-        /* 30-Jun-2020 nm */
-        /* If comment has "<HTML>", don't reformat. */
-        if (instr(1, comment, "<HTML>") != 0) {
-          nowrapHtml = 1;
-        } else {
-          nowrapHtml = 0;
-        }
-
-        /* 30-Jun-2020 nm Added nowrapHtml condition */
-        if (nowrapHtml == 0) {
-
-          /* 30-Jun-2020 nm */
-          /* This strips off leading spaces before $( (start of comment).  Don't
-             do it for <HTML>, since spacing before $( is manual. */
-          let(&labelSection, left(labelSection, pos));
-
-          if (reformatFlag == 2) {
-            /* If / REWRAP was specified, unwrap and rewrap the line */
-            let(&str1, "");
-            str1 = rewrapComment(comment);
-            let(&comment, str1);
-          }
-
-          /* Make sure that start of new lines inside the comment have no
-             trailing space (because printLongLine doesn't do this after
-             explict break) */
-          pos = 0;
-          while (1) {
-            pos = instr(pos + 1, comment, "\n");
-            if (pos == 0) break; /* Beyond last line in comment */
-            /* Remove leading spaces from comment line */
-            length = 0;
-            while (1) {
-              if (comment[pos + length] != ' ') break;
-              length++;
-            }
-            /* Add back indent+3 spaces to beginning of line in comment */
-            let(&comment, cat(left(comment, pos),
-                (comment[pos + length] != '\n')
-                    ? space(indent + 3)
-                    : "",  /* Don't add indentation if line is blank */
-                right(comment, pos + length + 1), NULL));
-          }
-
-          /* Reformat the comment to wrap if necessary */
-          if (g_outputToString == 1) bug(1726);
-          g_outputToString = 1;
-          let(&g_printString, "");
-          /* 7-Nov-2015 nm For getContribs in-line error insertion to assist
-             massive corrections; maybe delete someday */
-          /***********
-          saveWidth = g_screenWidth;
-          g_screenWidth = 9999;
-          /@i=getContrib(stmt, &ca, &cd, &ra, &rd, &sa, &sd, &md, 1);@/
-          let(&ca, "");
-          /@ 3-May-2017 nm @/
-          ca = getContrib(stmt, ERROR_CHECK);
-          g_screenWidth = saveWidth;
-          ************/
-          printLongLine(cat(space(indent), comment, NULL),
-              space(indent + 3), " ");
-          let(&comment, g_printString);
-          let(&g_printString, "");
-          g_outputToString = 0;
-#define ASCII_4 4
-          /* Restore ASCII_4 characters put in by rewrapComment() to space */
-          length = (long)strlen(comment);
-          for (pos = 2; pos < length - 2; pos++) {
-             /* For debugging: */
-             /* if (comment[pos] == ASCII_4) comment[pos] = '#'; */
-             if (comment[pos] == ASCII_4) comment[pos] = ' ';
-          }
-        } /* if nowrapHtml == 0 */ else {
-          /* 30-Jun-2020 nm */
-          /* If there was an <HTML> tag, we don't modify the comment. */
-          /* However, we need "\n" after "$)" (end of comment), corresponding to
-             the one that was put there by printLongLine() in the normal
-             non-<HTML> case above */
-          let(&comment, cat(comment, "\n", NULL));
-        }
-
-        /* Remove any trailing spaces */
-        pos = 2;
-        while(1) {
-          pos = instr(pos + 1, comment, " \n");
-          if (!pos) break;
-          let(&comment, cat(left(comment, pos - 1), right(comment, pos + 1),
-              NULL));
-          pos = pos - 2;
-        }
-
-        /* Complete the label section */
-        let(&labelSection, cat(labelSection, comment,
-            space(indent), g_Statement[stmt].labelName, " ", NULL));
-        break;
-      case e_:    /* $e */
-      case f_:    /* $f */
-        pos = rinstr(labelSection, g_Statement[stmt].labelName);
-        let(&labelSection, left(labelSection, pos - 1));
-        pos = rinstr(labelSection, "\n");
-        /* If there is none, insert it (unless first line in file) */
-        if (pos == 0 && stmt > 1) {
-          let(&labelSection, cat(edit(labelSection, 128 /* trailing spaces */),
-              "\n", NULL));
-          pos = (long)strlen(labelSection) + 1;
-        }
-        let(&labelSection, left(labelSection, pos));
-        /* If previous statement is $d or $e and there is no comment after it,
-           discard entire rest of label to get rid of redundant blank lines */
-        if (stmt > 1) {
-          if ((g_Statement[stmt - 1].type == d_
-                || g_Statement[stmt - 1].type == e_)
-              && instr(1, labelSection, "$(") == 0) {
-            let(&labelSection, "\n");
-          }
-        }
-        /* Complete the label section */
-        let(&labelSection, cat(labelSection,
-            space(indent), g_Statement[stmt].labelName, " ", NULL));
-        break;
-      default: bug(1727);
-    } /* switch (g_Statement[stmt].type) */
-
-    /* Reformat the math section */
-    switch (g_Statement[stmt].type) {
-      case lb_: /* ${ */
-      case rb_: /* $} */
-      case v_: /* $v */
-      case c_: /* $c */
-      case d_: /* $d */
-      case a_: /* $a */
-      case p_: /* $p */
-      case e_: /* $e */
-      case f_: /* $f */
-        /* Remove blank lines */
-        while (1) {
-          pos = instr(1, mathSection, "\n\n");
-          if (pos == 0) break;
-          let(&mathSection, cat(left(mathSection, pos),
-              right(mathSection, pos + 2), NULL));
-        }
-
-        /* 6-Mar-2016 nm Turn off wrapping of math section.  It should be
-           done manually for best readability. */
-        /*
-        /@ Remove leading and trailing space and trailing new lines @/
-        while(1) {
-          let(&mathSection, edit(mathSection,
-              8 /@ leading sp @/ + 128 /@ trailing sp @/));
-          if (mathSection[strlen(mathSection) - 1] != '\n') break;
-          let(&mathSection, left(mathSection, (long)strlen(mathSection) - 1));
-        }
-        let(&mathSection, cat(" ", mathSection, " ", NULL));
-                   /@ Restore standard leading/trailing space stripped above @/
-        */
-
-        /* Reduce multiple in-line spaces to single space */
-        pos = 0;
-        while(1) {
-          pos = instr(pos + 1, mathSection, "  ");
-          if (pos == 0) break;
-          if (pos > 1) {
-            if (mathSection[pos - 2] != '\n' && mathSection[pos - 2] != ' ') {
-              /* It's not at the start of a line, so reduce it */
-              let(&mathSection, cat(left(mathSection, pos),
-                  right(mathSection, pos + 2), NULL));
-              pos--;
-            }
-          }
-        }
-
-        /* 6-Mar-2016 nm Turn off wrapping of math section.  It should be
-           done manually for best readability. */
-        /*
-        /@ Wrap long lines @/
-        length = indent + 2 /@ Prefix length - add 2 for keyword ${, etc. @/
-            /@ Add 1 for space after label, if $e, $f, $a, $p @/
-            + (((g_Statement[stmt].labelName)[0]) ?
-                ((long)strlen(g_Statement[stmt].labelName) + 1) : 0);
-        if (g_outputToString == 1) bug(1728);
-        g_outputToString = 1;
-        let(&g_printString, "");
-        printLongLine(cat(space(length), mathSection, "$.", NULL),
-            space(indent + 4), " ");
-        g_outputToString = 0;
-        let(&mathSection, left(g_printString, (long)strlen(g_printString) - 3));
-            /@ Trim off "$." plus "\n" @/
-        let(&mathSection, right(mathSection, length + 1));
-        let(&g_printString, "");
-        */
-
-        break;
-      default: bug(1729);
-    }
-
-    /* Set previous state for next statement */
-    if (g_Statement[stmt].type == d_) {
-      /* dollarDpos is computed in the processing above */
-    } else {
-      dollarDpos = 0; /* Reset it */
-    }
-    previousType = g_Statement[stmt].type;
-    /* let(&comment, ""); */  /* Deallocate string memory */ /* (done below) */
-  } /* if reformatFlag */
-
-  let(&output, labelSection);
-
-  /* Add statement keyword */
-  let(&output, cat(output, "$", chr(g_Statement[stmt].type), NULL));
-
-  /* Add math section and proof */
-  if (g_Statement[stmt].mathSectionLen != 0) {
-    let(&output, cat(output, mathSection, NULL));
-    /* newProofFlag = 0; */  /* deleted 3-May-2017 nm */
-
-    if (g_Statement[stmt].type == (char)p_) {
-      let(&output, cat(output, "$=", proofSection, NULL));
-
-      /******** deleted 3-May-2017 nm
-      if (g_Statement[stmt].proofSectionPtr[-1] == 1) {
-        /@ ASCII 1 is flag that line is not from original source file @/
-        newProofFlag = 1; /@ Proof is result of SAVE (NEW_)PROOF command @/
-      }
-    }
-    /@ If it's not a source file line, the proof text should supply the
-       statement terminator, so that additional text may be added after
-       the terminator if desired.  (I.e., date in SAVE NEW_PROOF command) @/
-    if (!newProofFlag) let(&output, cat(output, "$.", NULL));
-    ********/
-
-    /* 3-May-2017 nm */
-    }
-    let(&output, cat(output, "$.", NULL));
-
-  }
-
-  /* Added 10/24/03 */
-  /* Make sure the line has no carriage-returns */
-  if (strchr(output, '\r') != NULL) {
-
-    /* 31-Dec-2017 nm */
-    /* We are now using readFileToString, so this should never happen. */
-    bug(1758);
-
-    /* This may happen with Cygwin's gcc, where DOS CR-LF becomes CR-CR-LF
-       in the output file */
-    /* Someday we should investigate the use of readFileToString() in
-       mminou.c for the main set.mm READ function, to solve this cleanly. */
-    let(&output, edit(output, 8192)); /* Discard CR's */
-  }
-
-  /* 31-Dec-2017 nm */
-  /* This function is no longer used to supply the output, but just to
-     do any reformatting/wrapping.  Now writeSourceToBuffer() builds the
-     output source.  So instead, we update the g_Statement[] content with
-     any changes, which are read by writeSourceToBuffer() and also saved
-     in the g_Statement[] array for any future write source.  Eventually
-     we should replace WRITE SOURCE.../REWRAP with a REWRAP(?) command. */
-  if (strcmp(labelSection, labelSectionSave)) {
-    g_Statement[stmt].labelSectionLen = (long)strlen(labelSection);
-    if (g_Statement[stmt].labelSectionChanged == 1) {
-      let(&(g_Statement[stmt].labelSectionPtr), labelSection);
-    } else {
-      /* This is the first time we've updated the label section */
-      g_Statement[stmt].labelSectionChanged = 1;
-      g_Statement[stmt].labelSectionPtr = labelSection;
-      labelSection = ""; /* so that labelSectionPtr won't be deallocated */
-    }
-  }
-  if (strcmp(mathSection, mathSectionSave)) {
-    g_Statement[stmt].mathSectionLen = (long)strlen(mathSection);
-    if (g_Statement[stmt].mathSectionChanged == 1) {
-      let(&(g_Statement[stmt].mathSectionPtr), mathSection);
-    } else {
-      /* This is the first time we've updated the math section */
-      g_Statement[stmt].mathSectionChanged = 1;
-      g_Statement[stmt].mathSectionPtr = mathSection;
-      mathSection = ""; /* so that mathSectionPtr won't be deallocated */
-    }
-  }
-  /* (I don't see anywhere that proofSection will change.  So make
-     it a bug check to force us to look into it.) */
-  if (strcmp(proofSection, proofSectionSave)) {
-    bug(1757); /* This may not be a bug */
-    g_Statement[stmt].proofSectionLen = (long)strlen(proofSection);
-    if (g_Statement[stmt].proofSectionChanged == 1) {
-      let(&(g_Statement[stmt].proofSectionPtr), proofSection);
-    } else {
-      /* This is the first time we've updated the proof section */
-      g_Statement[stmt].proofSectionChanged = 1;
-      g_Statement[stmt].proofSectionPtr = proofSection;
-      proofSection = ""; /* so that proofSectionPtr won't be deallocated */
-    }
-  }
-
-  let(&labelSection, "");
-  let(&mathSection, "");
-  let(&proofSection, "");
-  let(&labelSectionSave, "");
-  let(&mathSectionSave, "");
-  let(&proofSectionSave, "");
-  let(&comment, "");
-  let(&str1, "");
-  return output; /* The calling routine must deallocate this vstring */
-} /* outputStatement */
-
-/* 12-Jun-2011 nm */
-/* Unwrap the lines in a comment then re-wrap them according to set.mm
-   conventions.  This may be overly aggressive, and user should do a
-   diff to see if result is as desired.  Called by WRITE SOURCE / REWRAP.
-   Caller must deallocate returned vstring. */
-vstring rewrapComment(vstring comment1)
-{
-  /* Punctuation from mmwtex.c */
-#define OPENING_PUNCTUATION "(['\""
-/* #define CLOSING_PUNCTUATION ".,;)?!:]'\"_-" */
-#define CLOSING_PUNCTUATION ".,;)?!:]'\""
-#define SENTENCE_END_PUNCTUATION ")'\""
-  vstring comment = "";
-  vstring commentTemplate = ""; /* Non-breaking space template */
-  long length, pos, i, j;
-  vstring ch; /* Pointer only; do not allocate */
-  flag mathmode = 0;
-
-  let(&comment, comment1); /* Grab arg so it can be reassigned */
-
-  /* Ignore pre-formatted comments */
-  /* if (instr(1, comment, "<PRE>") != 0) return comment; */
-  /* 30-Jun-2020 nm This is now done in the calling program */
-  /*
-  if (instr(1, comment, "<HTML>") != 0) {
-    return comment;  /@ 26-Dec-2011 nm @/
-  }
-  */
-
-  /* Make sure back quotes are surrounded by space */
-  pos = 2;
-  mathmode = 0;
-  while (1) {
-    pos = instr(pos + 1, comment, "`");
-    if (pos == 0) break;
-    mathmode = (flag)(1 - mathmode);
-    if (comment[pos - 2] == '`' || comment[pos] == '`') continue;
-            /* See if previous or next char is "`"; ignore "``" escape */
-    if (comment[pos] != ' ' && comment[pos] != '\n') {
-      /* Currently, mmwtex.c doesn't correctly handle broken subscript (_)
-         or broken hyphen (-) in the CLOSING_PUNCTUATION, so allow these two as
-         exceptions until that is fixed.   E.g. ` a ` _2 doesn't yield
-         HTML subscript; instead we need ` a `_2. */
-      if (mathmode == 1 || (comment[pos] != '_' && comment[pos] != '-')) {
-        /* Add a space after back quote if none */
-        let(&comment, cat(left(comment, pos), " ",
-            right(comment, pos + 1), NULL));
-      }
-    }
-    if (comment[pos - 2] != ' ') {
-      /* Add a space before back quote if none */
-      let(&comment, cat(left(comment, pos - 1), " ",
-          right(comment, pos), NULL));
-      pos++; /* Go past the "`" */
-    }
-  }
-
-  /* Make sure "~" for labels are surrounded by space */
-  if (instr(2, comment, "`") == 0) {  /* For now, just process comments
-         not containing math symbols.  More complicated code is needed
-         to ignore ~ in math symbols; maybe add it someday. */
-    pos = 2;
-    while (1) {
-      pos = instr(pos + 1, comment, "~");
-      if (pos == 0) break;
-      if (comment[pos - 2] == '~' || comment[pos] == '~') continue;
-              /* See if previous or next char is "~"; ignore "~~" escape */
-      if (comment[pos] != ' ') {
-        /* Add a space after tilde if none */
-        let(&comment, cat(left(comment, pos), " ",
-            right(comment, pos + 1), NULL));
-      }
-      if (comment[pos - 2] != ' ') {
-        /* Add a space before tilde if none */
-        let(&comment, cat(left(comment, pos - 1), " ",
-            right(comment, pos), NULL));
-        pos++; /* Go past the "~" */
-      }
-    }
-  }
-
-  /* Change all newlines to space unless double newline */
-  /* Note:  it is assumed that blank lines have no spaces
-     for this to work; the user must ensure that. */
-  length = (long)strlen(comment);
-  for (pos = 2; pos < length - 2; pos++) {
-    if (comment[pos] == '\n' && comment[pos - 1] != '\n'
-        && comment[pos + 1] != '\n')
-      comment[pos] = ' ';
-  }
-  let(&comment, edit(comment, 16 /* reduce spaces */));
-
-  /* Remove spaces and blank lines at end of comment */
-  while (1) {
-    length = (long)strlen(comment);
-    if (comment[length - 3] != ' ') bug(1730);
-            /* Should have been syntax err (no space before "$)") */
-    if (comment[length - 4] != ' ' && comment[length - 4] != '\n') break;
-    let(&comment, cat(left(comment, length - 4),
-        right(comment, length - 2), NULL));
-  }
-
-  /* Put period at end of comment ending with lowercase letter */
-  /* Note:  This will not detect a '~ label' at end of comment.
-     A diff by the user is needed to verify it doesn't happen.
-     (We could enhace the code here to do that if it becomes a problem.) */
-  length = (long)strlen(comment);
-  if (islower((unsigned char)(comment[length - 4]))) {
-    let(&comment, cat(left(comment, length - 3), ". $)", NULL));
-  }
-
-  /* Change to ASCII 4 those spaces where the line shouldn't be
-     broken */
-  mathmode = 0;
-  for (pos = 3; pos < length - 2; pos++) {
-    if (comment[pos] == '`') { /* Start or end of math string */
-/*
-      if (mathmode == 0) {
-        if (comment[pos - 1] == ' '
-            && strchr(OPENING_PUNCTUATION, comment[pos - 2]) != NULL)
-          /@ Keep opening punctuation on same line @/
-          comment[pos - 1] = ASCII_4;
-      } else {
-        if (comment[pos + 1] == ' '
-            && strchr(CLOSING_PUNCTUATION, comment[pos + 2]) != NULL)
-          /@ Keep closing punctuation on same line @/
-          comment[pos + 1] = ASCII_4;
-      }
-*/
-      mathmode = (char)(1 - mathmode);
-    }
-    if ( mathmode == 1 && comment[pos] == ' ')
-      /* We assign comment[] rather than commentTemplate to avoid confusion of
-         math with punctuation.  Also, commentTemplate would be misaligned
-         anyway due to adding of spaces below. */
-      comment[pos] = ASCII_4;
-  }
-
-  /* 3-May-2016 nm */
-  /* Look for proof discouraged or usage discouraged markup and change their
-     spaces to ASCII 4 to prevent line breaks in the middle */
-  if (g_proofDiscouragedMarkup[0] == 0) {
-    /* getMarkupFlags() in mmdata.c has never been called, so initialize the
-       markup strings to their defaults */
-    let(&g_proofDiscouragedMarkup, PROOF_DISCOURAGED_MARKUP);
-    let(&g_usageDiscouragedMarkup, USAGE_DISCOURAGED_MARKUP);
-  }
-  pos = instr(1, comment, g_proofDiscouragedMarkup);
-  if (pos != 0) {
-    i = (long)strlen(g_proofDiscouragedMarkup);
-    for (j = pos; j < pos + i - 1; j++) { /* Check 2nd thru penultimate char */
-      if (comment[j] == ' ') {
-        comment[j] = ASCII_4;
-      }
-    }
-  }
-  pos = instr(1, comment, g_usageDiscouragedMarkup);
-  if (pos != 0) {
-    i = (long)strlen(g_usageDiscouragedMarkup);
-    for (j = pos; j < pos + i - 1; j++) { /* Check 2nd thru penultimate char */
-      if (comment[j] == ' ') {
-        comment[j] = ASCII_4;
-      }
-    }
-  }
-
-
-  /* Put two spaces after end of sentence and colon */
-  ch = ""; /* Prevent compiler warning */
-  for (i = 0; i < 4; i++) {
-    switch (i) {
-      case 0: ch = "."; break;
-      case 1: ch = "?"; break;
-      case 2: ch = "!"; break;
-      case 3: ch = ":";
-    }
-    pos = 2;
-    while (1) {
-      pos = instr(pos + 1, comment, ch);
-      if (pos == 0) break;
-      if (ch[0] == '.' && comment[pos - 2] >= 'A' && comment[pos - 2] <= 'Z')
-        continue;  /* Ignore initials of names */
-      if (strchr(SENTENCE_END_PUNCTUATION, comment[pos]) != NULL)
-        pos++;
-      if (comment[pos] != ' ') continue;
-      if ((comment[pos + 1] >= 'A' && comment[pos + 1] <= 'Z')
-          || strchr(OPENING_PUNCTUATION, comment[pos + 1]) != NULL) {
-        comment[pos] = ASCII_4; /* Prevent break so next line won't have
-                                   leading space; instead, break at 2nd space */
-        let(&comment, cat(left(comment, pos + 1), " ",
-            right(comment, pos + 2), NULL));
-      }
-    } /* end while */
-  } /* next i */
-
-  length = (long)strlen(comment);
-  let(&commentTemplate, space(length));
-  for (pos = 3; pos < length - 2; pos++) {
-    if (comment[pos] == ' ') {
-      if (comment[pos - 1] == '~' && comment[pos - 2] != '~') {
-        /* Don't break "~ <label>" */
-        commentTemplate[pos] = ASCII_4;
-      } else if ((comment[pos - 2] == ' '
-            || strchr(OPENING_PUNCTUATION, comment[pos - 2]) != NULL)
-          && strchr(OPENING_PUNCTUATION, comment[pos - 1]) != NULL) {
-        /* Don't break space after opening punctuation */
-        commentTemplate[pos] = ASCII_4;
-      } else if ((comment[pos + 2] == ' '
-            || comment[pos + 2] == '\n' /* Period etc. before line break */
-            || comment[pos + 2] == ASCII_4 /* 2-space sentence break
-                              done above where 1st space is ASCII_4 */
-            || strchr(CLOSING_PUNCTUATION, comment[pos + 2]) != NULL)
-          && strchr(CLOSING_PUNCTUATION, comment[pos + 1]) != NULL) {
-        /* Don't break space before closing punctuation */
-        commentTemplate[pos] = ASCII_4;
-/*
-      } else if (comment[pos + 2] == ' '
-          && strchr(CLOSING_PUNCTUATION, comment[pos + 1]) != NULL) {
-        /@ Don't break space after "~ <label>" if followed by punctuation @/
-        commentTemplate[pos] = ASCII_4;
-      } else if (comment[pos - 2] == ' '
-          && strchr(OPENING_PUNCTUATION, comment[pos - 1]) != NULL) {
-        /@ Don't break space before "~ <label>" if preceded by punctuation @/
-        commentTemplate[pos] = ASCII_4;
-      } else if (comment[pos + 1] == '`'
-          && strchr(OPENING_PUNCTUATION, comment[pos - 1]) != NULL) {
-        /@ Don't break space between punctuation and math start '`' @/
-        commentTemplate[pos] = ASCII_4;
-      } else if (comment[pos - 1] == '`'
-          && strchr(CLOSING_PUNCTUATION, comment[pos + 1]) != NULL) {
-        /@ Don't break space between punctuation and math end '`' @/
-        commentTemplate[pos] = ASCII_4;
-*/
-      } else if (comment[pos - 3] == ' ' && comment[pos - 2] == 'p'
-          && comment[pos - 1] == '.') {
-        /* Don't break " p. nnn" */
-        commentTemplate[pos] = ASCII_4;
-      }
-    }
-  }
-  commentTemplate[length - 3] = ASCII_4; /* Last space in comment */
-
-  for (pos = 3; pos < length - 2; pos++) {
-    /* Transfer the non-breaking spaces from the template to the comment */
-    if (commentTemplate[pos] == ASCII_4) comment[pos] = ASCII_4;
-  }
-
-  let(&commentTemplate, "");
-
-  return(comment);
-} /* rewrapComment */
-
-/* This is a general-purpose function to parse a math token string,
-   typically input by the user at the keyboard.  The statemNum is
-   needed to provide a context to determine which symbols are active.
-   Lack of whitespace is tolerated according to standard rules.
-   g_mathTokens must be set to the proper value. */
-/* The code in this section is complex because it uses the fast parsing
-   method borrowed from parseStatements().  On the other hand, it must set
-   up some initial tables by scanning the entire g_MathToken array; this may
-   slow it down in some applications. */
-/* Warning:  g_mathTokens must be the correct value (some procedures might
-   artificially adjust g_mathTokens to add dummy tokens [schemes] to the
-   g_MathToken array) */
-/* The user text may include existing or new dummy variables of the
-   form "?nnn". */
-nmbrString *parseMathTokens(vstring userText, long statemNum)
-{
-  long i, j;
-  char *fbPtr;
-  long mathStringLen;
-  long tokenNum;
-  long lowerKey, upperKey;
-  long symbolLen, origSymbolLen, g_mathKeyNum;
-  void *g_mathKeyPtr; /* bsearch returned value */
-  int maxScope;
-  flag errorFlag = 0; /* Prevents bad token from being added to output */
-  int errCount = 0; /* Cumulative error count */
-  vstring tmpStr = "";
-  vstring nlUserText = "";
-
-
-  long *mathTokenSameAs; /* Flag that symbol is unique (for speed up) */
-  long *reverseMathKey; /* Map from g_mathTokens to g_mathKey */
-
-
-  /* Temporary working space */
-  long wrkLen;
-  nmbrString *wrkNmbrPtr;
-  char *wrkStrPtr;
-
-  /* The answer */
-  nmbrString *mathString = NULL_NMBRSTRING;
-
-  long maxSymbolLen; /* Longest math symbol (for speedup) */
-  flag *symbolLenExists; /* A symbol with this length exists (for speedup) */
-
-  long nmbrSaveTempAllocStack; /* For nmbrLet() stack cleanup */
-  long saveTempAllocStack; /* For let() stack cleanup */
-  nmbrSaveTempAllocStack = g_nmbrStartTempAllocStack;
-  g_nmbrStartTempAllocStack = g_nmbrTempAllocStackTop;
-  saveTempAllocStack = g_startTempAllocStack;
-  g_startTempAllocStack = g_tempAllocStackTop;
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  tokenNum = 0;
-
-  /* Add a newline before user text for sourceError() */
-  let(&nlUserText, cat("\n", userText, NULL));
-
-  /* Make sure that g_mathTokens has been initialized */
-  if (!g_mathTokens) bug(1717);
-
-  /* Set the 'active' flag based on statemNum; here 'active' just means it
-     would be in the stack during normal parsing, not the Metamath manual
-     definition. */
-  for (i = 0; i < g_mathTokens; i++) {
-    if (g_MathToken[i].statement <= statemNum && g_MathToken[i].endStatement >=
-        statemNum) {
-      g_MathToken[i].active = 1;
-    } else {
-      g_MathToken[i].active = 0;
-    }
-  }
-
-  /* Initialize flags for g_mathKey array that identify math symbols as
-     unique (when 0) or, if not unique, the flag is a number identifying a group
-     of identical names */
-  mathTokenSameAs = malloc((size_t)g_mathTokens * sizeof(long));
-  if (!mathTokenSameAs) outOfMemory("#12 (mathTokenSameAs)");
-  reverseMathKey = malloc((size_t)g_mathTokens * sizeof(long));
-  if (!reverseMathKey) outOfMemory("#13 (reverseMathKey)");
-  for (i = 0; i < g_mathTokens; i++) {
-    mathTokenSameAs[i] = 0; /* 0 means unique */
-    reverseMathKey[g_mathKey[i]] = i; /* Initialize reverse map to g_mathKey */
-  }
-  for (i = 1; i < g_mathTokens; i++) {
-    if (!strcmp(g_MathToken[g_mathKey[i]].tokenName,
-        g_MathToken[g_mathKey[i - 1]].tokenName)) {
-      if (!mathTokenSameAs[i - 1]) mathTokenSameAs[i - 1] = i;
-      mathTokenSameAs[i] = mathTokenSameAs[i - 1];
-    }
-  }
-
-  /* Initialize temporary working space for parsing tokens */
-  /* Assume the worst case of one token per userText character */
-  wrkLen = (long)strlen(userText);
-  wrkNmbrPtr = malloc((size_t)wrkLen * sizeof(nmbrString));
-  if (!wrkNmbrPtr) outOfMemory("#22 (wrkNmbrPtr)");
-  wrkStrPtr = malloc((size_t)wrkLen + 1);
-  if (!wrkStrPtr) outOfMemory("#23 (wrkStrPtr)");
-
-  /* Find declared math symbol lengths (used to speed up parsing) */
-  maxSymbolLen = 0;
-  for (i = 0; i < g_mathTokens; i++) {
-    if (g_MathToken[i].length > maxSymbolLen) {
-      maxSymbolLen = g_MathToken[i].length;
-    }
-  }
-  symbolLenExists = malloc(((size_t)maxSymbolLen + 1) * sizeof(flag));
-  if (!symbolLenExists) outOfMemory("#25 (symbolLenExists)");
-  for (i = 0; i <= maxSymbolLen; i++) {
-    symbolLenExists[i] = 0;
-  }
-  for (i = 0; i < g_mathTokens; i++) {
-    symbolLenExists[g_MathToken[i].length] = 1;
-  }
-
-
-  g_currentScope = g_Statement[statemNum].scope; /* Scope of the ref. statement */
-
-
-  /* The code below is indented because it was borrowed from parseStatements().
-     We will leave the indentation intact for easier future comparison
-     with that code. */
-
-        /* Scan the math section for tokens */
-        mathStringLen = 0;
-        fbPtr = nlUserText;
-        while (1) {
-          fbPtr = fbPtr + whiteSpaceLen(fbPtr);
-          origSymbolLen = tokenLen(fbPtr);
-          if (!origSymbolLen) break; /* Done scanning source line */
-
-          /* Scan for largest matching token from the left */
-         nextAdjToken:
-          /* maxSymbolLen is the longest declared symbol */
-          if (origSymbolLen > maxSymbolLen) {
-            symbolLen = maxSymbolLen;
-          } else {
-            symbolLen = origSymbolLen;
-          }
-          memcpy(wrkStrPtr, fbPtr, (size_t)symbolLen);
-          for (; symbolLen > 0; symbolLen--) {
-            /* symbolLenExists means a symbol of this length was declared */
-            if (!symbolLenExists[symbolLen]) continue;
-            wrkStrPtr[symbolLen] = 0; /* Define end of trial token to look up */
-            g_mathKeyPtr = (void *)bsearch(wrkStrPtr, g_mathKey,
-                (size_t)g_mathTokens, sizeof(long), mathSrchCmp);
-            if (!g_mathKeyPtr) continue; /* Trial token was not declared */
-            g_mathKeyNum = (long *)g_mathKeyPtr - g_mathKey; /* Pointer arithmetic! */
-            if (mathTokenSameAs[g_mathKeyNum]) { /* Multiply-declared symbol */
-              lowerKey = g_mathKeyNum;
-              upperKey = lowerKey;
-              j = mathTokenSameAs[lowerKey];
-              while (lowerKey) {
-                if (j != mathTokenSameAs[lowerKey - 1]) break;
-                lowerKey--;
-              }
-              while (upperKey < g_mathTokens - 1) {
-                if (j != mathTokenSameAs[upperKey + 1]) break;
-                upperKey++;
-              }
-              /* Find the active symbol with the most recent declaration */
-              /* (Note:  Here, 'active' means it's on the stack, not the
-                 official def.) */
-              maxScope = -1;
-              for (i = lowerKey; i <= upperKey; i++) {
-                j = g_mathKey[i];
-                if (g_MathToken[j].active) {
-                  if (g_MathToken[j].scope > maxScope) {
-                    tokenNum = j;
-                    maxScope = g_MathToken[j].scope;
-                    if (maxScope == g_currentScope) break; /* Speedup */
-                  }
-                }
-              }
-              if (maxScope == -1) {
-                tokenNum = g_mathKey[g_mathKeyNum]; /* Pick an arbitrary one */
-                errCount++;
-                if (errCount <= 1) { /* Print 1st error only */
-                  sourceError(fbPtr, symbolLen, /*stmt*/ 0,
-       "This math symbol is not active (i.e. was not declared in this scope).");
-                }
-                errorFlag = 1;
-              }
-            } else { /* The symbol was declared only once. */
-              tokenNum = *((long *)g_mathKeyPtr);
-                  /* Same as: tokenNum = g_mathKey[g_mathKeyNum]; but faster */
-              if (!g_MathToken[tokenNum].active) {
-                errCount++;
-                if (errCount <= 1) { /* Print 1st error only */
-                  sourceError(fbPtr, symbolLen, /*stmt*/ 0,
-       "This math symbol is not active (i.e. was not declared in this scope).");
-                }
-                errorFlag = 1;
-              }
-            } /* End if multiply-defined symbol */
-            break; /* The symbol was found, so we are done */
-          } /* Next symbolLen */
-
-          if (symbolLen == 0) { /* Symbol was not found */
-            /* See if the symbol is a dummy variable name */
-            if (fbPtr[0] == '$') {
-              symbolLen = tokenLen(fbPtr);
-              for (i = 1; i < symbolLen; i++) {
-                if (fbPtr[i] < '0' || fbPtr[i] > '9') break;
-              }
-              symbolLen = i;
-              if (symbolLen == 1) {
-                symbolLen = 0; /* No # after '$' -- error */
-              } else {
-                memcpy(wrkStrPtr, fbPtr + 1, (size_t)i - 1);
-                wrkStrPtr[i - 1] = 0; /* End of string */
-                tokenNum = (long)(val(wrkStrPtr)) + g_mathTokens;
-                /* See if dummy var has been declared; if not, declare it */
-                if (tokenNum > g_pipDummyVars + g_mathTokens) {
-                  declareDummyVars(tokenNum - g_pipDummyVars - g_mathTokens);
-                }
-              }
-            } /* End if fbPtr == '$' */
-         } /* End if symbolLen == 0 */
-
-
-          if (symbolLen == 0) { /* Symbol was not found */
-            symbolLen = tokenLen(fbPtr);
-            errCount++;
-            if (errCount <= 1) { /* Print 1st error only */
-              sourceError(fbPtr, symbolLen, /*stmt*/ 0,
-      "This math symbol was not declared (with a \"$c\" or \"$v\" statement).");
-            }
-            errorFlag = 1;
-          }
-
-          /* Add symbol to mathString */
-          if (!errorFlag) {
-            wrkNmbrPtr[mathStringLen] = tokenNum;
-            mathStringLen++;
-          } else {
-            errorFlag = 0;
-          }
-          fbPtr = fbPtr + symbolLen; /* Move on to next symbol */
-
-          if (symbolLen < origSymbolLen) {
-            /* This symbol is not separated from next by white space */
-            /* Speed-up: don't call tokenLen again; just jump past it */
-            origSymbolLen = origSymbolLen - symbolLen;
-            goto nextAdjToken; /* (Instead of continue) */
-          }
-        } /* End while */
-
-
-        /* Assign mathString */
-        nmbrLet(&mathString, nmbrSpace(mathStringLen));
-        for (i = 0; i < mathStringLen; i++) {
-          mathString[i] = wrkNmbrPtr[i];
-        }
-
-  /* End of unconventionally indented section borrowed from parseStatements() */
-
-  g_startTempAllocStack = saveTempAllocStack;
-  g_nmbrStartTempAllocStack = nmbrSaveTempAllocStack;
-  if (mathStringLen) nmbrMakeTempAlloc(mathString); /* Flag for dealloc*/
-
-  /* Deallocate temporary space */
-  free(mathTokenSameAs);
-  free(reverseMathKey);
-  free(wrkNmbrPtr);
-  free(wrkStrPtr);
-  free(symbolLenExists);
-  let(&tmpStr, "");
-  let(&nlUserText, "");
-
-  return (mathString);
-
-} /* parseMathTokens */
-
-
-/* 31-Dec-2017 nm For .mm file splitting */
-/* Get the next real $[...$] or virtual $( Begin $[... inclusion */
-/* This uses the convention of mmvstr.c where beginning of a string
-   is position 1.  However, startOffset = 0 means no offset i.e.
-   start at fileBuf. */
-void getNextInclusion(char *fileBuf, long startOffset, /* inputs */
-    /* outputs: */
-    long *cmdPos1, long *cmdPos2,
-    long *endPos1, long *endPos2,
-    char *cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
-                      'I' = "[$...",
-                      'S' = "$( Skip [$...",
-                      'E' = Start missing matched End
-                      'N' = no include found; includeFn = "" */
-    vstring *fileName /* name of included file */
-    )
-{
-
-/*
-cmdType = 'B' or 'E':
-      ....... $( Begin $[ prop.mm $] $)   ......   $( End $[ prop.mm $] $) ...
-       ^      ^           ^^^^^^^       ^          ^                      ^
-  startOffset cmdPos1    fileName  cmdPos2     endPos1              endPos2
-                                             (=0 if no End)   (=0 if no End)
-
-   Note: in the special case of Begin, cmdPos2 points _after_ the whitespace
-   after "$( Begin $[ prop.mm $] $)" i.e. the whitespace is considered part of
-   the Begin command.  The is needed because prop.mm content doesn't
-   necessarily start with whitespace.  prop.mm does, however, end with
-   whitespace (\n) as enforced by readFileToString().
-
-cmdType = 'I':
-      ............... $[ prop.mm $]  ..............
-       ^              ^  ^^^^^^^   ^
-  startOffset   cmdPos1 fileName  cmdPos2     endPos1=0  endPos2=0
-
-cmdType = 'S':
-      ....... $( Skip $[ prop.mm $] $)   ......
-       ^      ^          ^^^^^^^      ^
-  startOffset cmdPos1    fileName  cmdPos2     endPos1=0  endPos2=0
-*/
-  char *fbPtr;
-  char *tmpPtr;
-  flag lookForEndMode = 0; /* 1 if inside of $( Begin, End, Skip... */
-  long i, j;
-
-  fbPtr = fileBuf + startOffset;
-
-  while (1) {
-    fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr); /* Count $( as a token */
-    j = rawTokenLen(fbPtr); /* Treat $(, $[ as tokens */
-    if (j == 0) {
-      *cmdType = 'N'; /* No include found */
-      break;  /* End of file */
-    }
-    if (fbPtr[0] != '$') {
-      fbPtr = fbPtr + j;
-      continue;
-    }
-
-    /* Process normal include $[ $] */
-    if (fbPtr[1] == '[') {
-      if (lookForEndMode == 0) {
-        /* If lookForEndMode is 1, ignore everything until we find a matching
-           "$( End $[..." */
-        *cmdPos1 = fbPtr - fileBuf + 1; /* 1 = beginning of file */
-        fbPtr = fbPtr + j;
-        fbPtr = fbPtr + whiteSpaceLen(fbPtr); /* Comments = whitespace here */
-        j = rawTokenLen(fbPtr); /* Should be file name */
-        /* Note that mid, seg, left, right do not waste time computing
-           the length of the input string fbPtr */
-        let(&(*fileName), left(fbPtr, j));
-        fbPtr = fbPtr + j;
-        fbPtr = fbPtr + whiteSpaceLen(fbPtr); /* Comments = whitespace here */
-        j = rawTokenLen(fbPtr);
-        if (j == 2/*speedup*/ && !strncmp("$]", fbPtr, (size_t)j)) {
-          *cmdPos2 = fbPtr - fileBuf + j + 1;
-          *endPos1 = 0;
-          *endPos2 = 0;
-          *cmdType = 'I';
-          return;
-        }
-        /* TODO - more precise error message */
-        print2("?Missing \"$]\" after \"$[ %s\"\n", *fileName);
-        fbPtr = fbPtr + j;
-        continue; /* Not a completed include */
-      } /* if (lookForEndMode == 0) */
-      fbPtr = fbPtr + j;
-      continue; /* Either not a legal include - error detected later,
-             or we're in lookForEndMode */
-    /* Process markup-type include inside comment */
-    } else if (fbPtr[1] == '(') {
-      /* Process comment starting at "$(" */
-      if (lookForEndMode == 0) {
-        *cmdPos1 = fbPtr - fileBuf + 1;
-      } else {
-        *endPos1 = fbPtr - fileBuf + 1;
-      }
-      fbPtr = fbPtr + j;
-      fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr); /* comment != whitespace */
-      j = rawTokenLen(fbPtr);
-      *cmdType = '?';
-      if (j == 5/*speedup*/ && !strncmp("Begin", fbPtr, (size_t)j)) {
-        /* If lookForEndMode is 1, we're looking for End matching earlier Begin */
-        if (lookForEndMode == 0) {
-          *cmdType = 'B';
-        }
-      } else if (j == 4/*speedup*/ && !strncmp("Skip", fbPtr, (size_t)j)) {
-        /* If lookForEndMode is 1, we're looking for End matching earlier Begin */
-        if (lookForEndMode == 0) {
-          *cmdType = 'S';
-        }
-      } else if (j == 3/*speedup*/ && !strncmp("End", fbPtr, (size_t)j)) {
-        /* If lookForEndMode is 0, there was no matching Begin */
-        if (lookForEndMode == 1) {
-          *cmdType = 'E';
-        }
-      }
-      if (*cmdType == '?') { /* The comment doesn't qualify as $[ $] markup */
-        /* Find end of comment and continue */
-        goto GET_PASSED_END_OF_COMMENT;
-      } else {
-        /* It's Begin or Skip or End */
-        fbPtr = fbPtr + j;
-        fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr);
-        j = rawTokenLen(fbPtr);
-        if (j != 2 || strncmp("$[", fbPtr, (size_t)j)) {
-          /* Find end of comment and continue */
-          goto GET_PASSED_END_OF_COMMENT;
-        }
-        fbPtr = fbPtr + j;
-        fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr);  /* comment != whitespace */
-        j = rawTokenLen(fbPtr);
-        /* Note that mid, seg, left, right do not waste time computing
-           the length of the input string fbPtr */
-        if (lookForEndMode == 0) {
-          /* It's Begin or Skip */
-          let(&(*fileName), left(fbPtr, j));
-        } else {
-          /* It's an End command */
-          if (strncmp(*fileName, fbPtr, (size_t)j)) {
-            /* But the file name didn't match, so it's not a matching End */
-            /* Find end of comment and continue */
-            goto GET_PASSED_END_OF_COMMENT;
-          }
-        }
-        fbPtr = fbPtr + j;
-        fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr);  /* comment != whitespace */
-        j = rawTokenLen(fbPtr);
-        if (j != 2 || strncmp("$]", fbPtr, (size_t)j)) {
-          /* The token after the file name isn't "$]" */
-          /* Find end of comment and continue */
-          goto GET_PASSED_END_OF_COMMENT;
-        }
-        fbPtr = fbPtr + j;
-        fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr);  /* comment != whitespace */
-        j = rawTokenLen(fbPtr);
-        if (j != 2 || strncmp("$)", fbPtr, (size_t)j)) {
-          /* The token after the "$]" isn't "$)" */
-          /* Find end of comment and continue */
-          goto GET_PASSED_END_OF_COMMENT;
-        }
-        /* We are now at the end of "$( Begin/Skip/End $[ file $] $)" */
-        fbPtr = fbPtr + j;
-        if (lookForEndMode == 0) {
-          *cmdPos2 = fbPtr - fileBuf + 1
-            + ((*cmdType == 'B') ? 1 : 0);/*after whitespace for 'B' (see above)*/
-          if (*cmdType == 'S') {  /* Skip command; we're done */
-            *endPos1 = 0;
-            *endPos2 = 0;
-            return;
-          }
-          if (*cmdType != 'B') bug(1742);
-          lookForEndMode = 1;
-        } else { /* We're at an End */
-          if (*cmdType != 'E') bug(1743);
-          /*lookForEndMode = 0;*/ /* Not needed since we will return soon */
-          *cmdType = 'B'; /* Restore it to B for Begin/End pair */
-          *endPos2 = fbPtr - fileBuf + 1;
-          return;
-        }
-        continue; /* We're past Begin; start search for End */
-      } /* Begin, End, or Skip */
-    } else if (i != i + 1) { /* Suppress "unreachable code" warning for bug trap below */
-      /* It's '$' not followed by '[' or '('; j is token length */
-      fbPtr = fbPtr + j;
-      continue;
-    }
-    bug(1746); /* Should never get here */
-   GET_PASSED_END_OF_COMMENT:
-    /* Note that fbPtr should be at beginning of last token found, which
-       may be "$)" (in which case i will be 1 from the instr) */
-
-    /* Don't use instr because it computes string length each call */
-    /*i = instr(1, fbPtr, "$)");*/ /* Normally this will be fast because we only
-        have to find the end of the comment that we're in */
-    /* Emulater the instr() */
-    tmpPtr = fbPtr;
-    i = 0;
-    while (1) {
-
-      /* strchr is incredibly slow under lcc - why?
-         Is it computing strlen internally maybe? */
-      /*
-      tmpPtr = strchr(tmpPtr, '$');
-      if (tmpPtr == NULL) {
-        i = 0;
-        break;
-      }
-      if (tmpPtr[1] == ')') {
-        i = tmpPtr - fbPtr + 1;
-        break;
-      }
-      tmpPtr++;
-      */
-
-      /* Emulate strchr */
-      while (tmpPtr[0] != '$') {
-        if (tmpPtr[0] == 0) break;
-        tmpPtr++;
-      }
-      if (tmpPtr[0] == 0) {
-        i = 0;
-        break;
-      }
-      if (tmpPtr[1] == ')') {
-        i = tmpPtr - fbPtr + 1;
-        break;
-      }
-      tmpPtr++;
-
-    } /* while (1) */
-
-    if (i == 0) {
-      /* TODO: better error msg */
-      printf("?End of comment not found\n");
-      i = (long)strlen(fileBuf); /* Slow, but this is a rare error */
-      fbPtr = fileBuf + i; /* Points to null at end of fileBuf */
-    } else {
-      fbPtr = fbPtr + i + 2 - 1; /* Skip the "$)" - skip 2 characters, then
-           back up 1 since the instr result starts at 1 */
-    }
-    /* continue; */  /* Not necessary since we're at end of loop */
-  } /* while (1) */
-  if (j != 0) bug(1744); /* Should be at end of file */
-  if (lookForEndMode == 1) {
-    /* We didn't find an End */
-    *cmdType = 'E';
-    *endPos1 = 0; *endPos2 = 0;
-  } else {
-    *cmdType = 'N'; /* no include was found */
-    *cmdPos1 = 0; *cmdPos2 = 0; *endPos1 = 0; *endPos2 = 0;
-    let(&(*fileName), "");
-  }
-  return;
-
-} /* getNextInclusion */
-
-
-
-/* This function transfers the content of the g_Statement[] array
-   to a linear buffer in preparation for creating the output file.
-   Any changes such as modified proofs will be updated in the buffer. */
-/* The caller is responsible for deallocating the returned string. */
-vstring writeSourceToBuffer(void)
-{
-  long stmt, size;
-  vstring buf = "";
-  char *ptr;
-
-  /* Compute the size of the buffer */
-  /* Note that g_Statement[g_statements + 1] is a dummy statement
-     containing the text after the last statement in its
-     labelSection */
-  size = 0;
-  for (stmt = 1; stmt <= g_statements + 1; stmt++) {
-    /* Add the sizes of the sections.  When sections don't exist
-       (like a proof for a $a statement), the section length is 0. */
-    size += g_Statement[stmt].labelSectionLen
-        + g_Statement[stmt].mathSectionLen
-        + g_Statement[stmt].proofSectionLen;
-    /* Add in the 2-char length of keywords, which aren't stored in
-       the statement sections */
-    switch (g_Statement[stmt].type) {
-      case lb_: /* ${ */
-      case rb_: /* $} */
-        size += 2;
-        break;
-      case v_: /* $v */
-      case c_: /* $c */
-      case d_: /* $d */
-      case e_: /* $e */
-      case f_: /* $f */
-      case a_: /* $a */
-        size += 4;
-        break;
-      case p_: /* $p */
-        size += 6;
-        break;
-      case illegal_: /* dummy */
-        if (stmt != g_statements + 1) bug(1747);
-        /* The labelLen is text after last statement */
-        size += 0; /* There are no keywords in g_statements + 1 */
-        break;
-      default: bug(1748);
-    } /* switch (g_Statement[stmt].type) */
-  } /* next stmt */
-
-  /* Create the output buffer */
-  /* We could have created it with let(&buf, space(size)), but malloc should
-     be slightly faster since we don't have to initialize each entry */
-  buf = malloc((size_t)(size + 1) * sizeof(char));
-
-  ptr = buf; /* Pointer to keep track of buf location */
-  /* Transfer the g_Statement[] array to buf */
-  for (stmt = 1; stmt <= g_statements + 1; stmt++) {
-    /* Always transfer the label section (text before $ keyword) */
-    memcpy(ptr/*dest*/, g_Statement[stmt].labelSectionPtr/*source*/,
-        (size_t)(g_Statement[stmt].labelSectionLen)/*size*/);
-    ptr += g_Statement[stmt].labelSectionLen;
-    switch (g_Statement[stmt].type) {
-      case illegal_:
-        if (stmt != g_statements + 1) bug(1749);
-        break;
-      case lb_: /* ${ */
-      case rb_: /* $} */
-        ptr[0] = '$';
-        ptr[1] = g_Statement[stmt].type;
-        ptr += 2;
-        break;
-      case v_: /* $v */
-      case c_: /* $c */
-      case d_: /* $d */
-      case e_: /* $e */
-      case f_: /* $f */
-      case a_: /* $a */
-        ptr[0] = '$';
-        ptr[1] = g_Statement[stmt].type;
-        ptr += 2;
-        memcpy(ptr/*dest*/, g_Statement[stmt].mathSectionPtr/*source*/,
-            (size_t)(g_Statement[stmt].mathSectionLen)/*size*/);
-        ptr += g_Statement[stmt].mathSectionLen;
-        ptr[0] = '$';
-        ptr[1] = '.';
-        ptr += 2;
-        break;
-      case p_: /* $p */
-        ptr[0] = '$';
-        ptr[1] = g_Statement[stmt].type;
-        ptr += 2;
-        memcpy(ptr/*dest*/, g_Statement[stmt].mathSectionPtr/*source*/,
-            (size_t)(g_Statement[stmt].mathSectionLen)/*size*/);
-        ptr += g_Statement[stmt].mathSectionLen;
-        ptr[0] = '$';
-        ptr[1] = '=';
-        ptr += 2;
-        memcpy(ptr/*dest*/, g_Statement[stmt].proofSectionPtr/*source*/,
-            (size_t)(g_Statement[stmt].proofSectionLen)/*size*/);
-        ptr += g_Statement[stmt].proofSectionLen;
-        ptr[0] = '$';
-        ptr[1] = '.';
-        ptr += 2;
-        break;
-      default: bug(1750);
-    } /* switch (g_Statement[stmt].type) */
-  } /* next stmt */
-  if (ptr - buf != size) bug(1751);
-  buf[size] = 0; /* End of string marker */
-  return buf;
-} /* writeSourceToBuffer */
-
-
-/* 31-Dec-2017 nm */
-/* This function creates split files containing $[ $] inclusions, from
-   a nonsplit source with $( Begin $[... etc. inclusions */
-/* This function calls itself recursively, and after the recursive call
-   the fileBuf (=includeBuf) argument is deallocated. */
-/* For the top level call, fileName MUST NOT HAVE A DIRECTORY PATH */
-/* Note that fileBuf must be deallocated by initial caller.  This will let the
-   caller decide whether to say re-use fileBuf to create an unsplit version
-   of the .mm file in case the split version generation encounters an error. */
-/*                                     TODO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
-/*flag(TODO)*/ void writeSplitSource(vstring *fileBuf, vstring fileName,
-    flag noVersioningFlag, flag noDeleteFlag) {
-  FILE *fp;
-  vstring tmpStr1 = "";
-  vstring tmpFileName = "";
-  vstring includeBuf = "";
-  vstring includeFn = "";
-  vstring fileNameWithPath = "";
-  long size;
-  flag writeFlag;
-  long startOffset;
-  long cmdPos1;
-  long cmdPos2;
-  long endPos1;
-  long endPos2;
-  char cmdType;
-  startOffset = 0;
-  let(&fileNameWithPath, cat(g_rootDirectory, fileName, NULL));
-  while (1) {
-    getNextInclusion(*fileBuf, startOffset, /* inputs */
-        /* outputs: */
-        &cmdPos1, &cmdPos2,
-        &endPos1, &endPos2,
-        &cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
-                     'I' = "[$...",
-                     'S' = "$( Skip [$...",
-                     'E' = Start missing matched End
-                     'N' = no include found; includeFn = "" */
-        &includeFn /* name of included file */ );
-    if (cmdType == 'N') {
-      writeFlag = 0;
-      /* There are no more includes to expand, so write out the file */
-      if (!strcmp(fileName, g_output_fn)) {
-        /* We're writing the top-level file - always create new version */
-        writeFlag = 1;
-      } else {
-        /* We're writing an included file */
-        /* See if the file already exists */
-        let(&tmpStr1, "");
-        tmpStr1 = readFileToString(fileNameWithPath, 0/*quiet*/, &size);
-        if (tmpStr1 == NULL) {
-          tmpStr1 = ""; /* Prevent seg fault */
-          /* If it doesn't exist, see if the ~1 version exists */
-          let(&tmpFileName, cat(fileNameWithPath, "~1", NULL));
-          tmpStr1 = readFileToString(tmpFileName, 0/*quiet*/, &size);
-          if (tmpStr1 == NULL) {
-            tmpStr1 = ""; /* Prevent seg fault */
-            /* Create and write the file */
-            writeFlag = 1;
-          } else {
-            /* See if the ~1 backup file content changed */
-            if (strcmp(tmpStr1, *fileBuf)) {
-              /* Content is different; write the file */
-              writeFlag = 1;
-            } else {
-              /* Just rename the ~1 version to the main version */
-              print2("Recovering \"%s\" from \"%s\"...\n",
-                  fileNameWithPath, tmpFileName);
-              rename(tmpFileName/*old*/, fileNameWithPath/*new*/);
-            }
-          } /* if (tmpStr1 == NULL) */
-        } else { /* tmpStr1 != NULL */
-          /* The include file already exists; see if the content changed */
-          if (strcmp(tmpStr1, *fileBuf)) {
-            /* Content is different; write the file */
-            writeFlag = 1;
-          } else {
-            /* Just rename the ~1 version to the main version */
-            print2("Content of \"%s\" did not change.\n",
-                fileNameWithPath);
-            rename(tmpFileName/*old*/, fileNameWithPath/*new*/);
-          }
-        }
-      }
-      if (writeFlag == 1) {
-        fp = fSafeOpen(fileNameWithPath, "w", 0/*noVersioningFlag*/);
-        if (fp == NULL) {
-          /* TODO: better error msg?  Abort and don't split? */
-          print2("?Error: couldn't create the file \"%s\"\n", fileNameWithPath);
-          print2("  Make sure any directories needed have been created.\n");
-          print2("  Try WRITE SOURCE without / SPLIT to recover your work.\n");
-          break;
-        } else {
-          print2("Writing \"%s\"...\n", fileNameWithPath);
-          fprintf(fp, "%s", *fileBuf);
-          fclose(fp);
-          break;
-        }
-      } /* if (writeFlag == 1 ) */
-      break;
-    } else if (cmdType == 'S') {
-      /* Change "Skip" to a real inclusion */
-      let(&tmpStr1, cat("$[ ", includeFn, " $]", NULL));
-      startOffset = cmdPos1 - 1 + (long)strlen(tmpStr1);
-      let(&(*fileBuf), cat(left(*fileBuf, cmdPos1 - 1), tmpStr1,
-          right(*fileBuf, cmdPos2), NULL));
-      continue;
-    } else if (cmdType == 'B') {
-      /* Extract included file content and call this recursively to process */
-      let(&tmpStr1, cat("$[ ", includeFn, " $]", NULL));
-      startOffset = cmdPos1 - 1 + (long)strlen(tmpStr1);
-      /* We start _after_ the whitespace after cmdPos2 because it wasn't
-         in the original included file but was put there by us in
-         readSourceAndIncludes() */
-      let(&includeBuf, seg(*fileBuf, cmdPos2, endPos1 - 1));
-      let(&(*fileBuf), cat(left(*fileBuf, cmdPos1 - 1), tmpStr1,
-          right(*fileBuf, endPos2), NULL));
-      /* TODO: intercept error from deeper calls? */
-      writeSplitSource(&includeBuf, includeFn, noVersioningFlag, noDeleteFlag);
-      continue;
-    } else if (cmdType == 'I') {
-      bug(1752); /* Any real $[ $] should have been converted to commment */
-      /* However in theory, user could have faked an assignable description
-         if modifiable comments are added in the future...*/
-      startOffset = cmdPos2 - 1;
-      continue;
-    } else if (cmdType == 'E') {
-      /* TODO What error message should go here? */
-      print2("?Unterminated \"$( Begin $[...\" inclusion markup in \"%s\".",
-          fileNameWithPath);
-      startOffset = cmdPos2 - 1;
-      continue;
-    } else {
-      /* Should never happen */
-      bug(1753);
-    }
-  } /* while (1) */
-  /* Deallocate memory */
-  /*let(&(*fileBuf), "");*/ /* Let caller decide whether to do this */
-  let(&tmpStr1, "");
-  let(&tmpFileName, "");
-  let(&includeFn, "");
-  let(&includeBuf, "");
-  let(&fileNameWithPath, "");
-} /* writeSplitSource */
-
-
-/* 31-Dec-2017 nm */
-/* When "write source" does not have the "/split" qualifier, by default
-   (i.e. without "/no_delete") the included modules are "deleted" (renamed
-   to ~1) since their content will be in the main output file. */
-/*flag(TODO)*/ void deleteSplits(vstring *fileBuf, flag noVersioningFlag) {
-  FILE *fp;
-  vstring includeFn = "";
-  vstring fileNameWithPath = "";
-  long startOffset;
-  long cmdPos1;
-  long cmdPos2;
-  long endPos1;
-  long endPos2;
-  char cmdType;
-  startOffset = 0;
-  while (1) {
-    /* We scan the source for all "$( Begin $[ file $] $)...$( End $[ file $]"
-       and when found, we "delete" file. */
-    getNextInclusion(*fileBuf, startOffset, /* inputs */
-        /* outputs: */
-        &cmdPos1, &cmdPos2,
-        &endPos1, &endPos2,
-        &cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
-                     'I' = "[$...",
-                     'S' = "$( Skip [$...",
-                     'E' = Start missing matched End
-                     'N' = no include found; includeFn = "" */
-        &includeFn /* name of included file */ );
-    /* We only care about the 'B' command */
-    if (cmdType == 'B') {
-      let(&fileNameWithPath, cat(g_rootDirectory, includeFn, NULL));
-      /* See if the included file exists */
-      fp = fopen(fileNameWithPath, "r");
-      if (fp != NULL) {
-        fclose(fp);
-        if (noVersioningFlag == 1) {
-          print2("Deleting \"%s\"...\n", fileNameWithPath);
-        } else {
-          print2("Renaming \"%s\" to \"%s~1\"...\n", fileNameWithPath,
-              fileNameWithPath);
-        }
-        fp = fSafeOpen(fileNameWithPath, "d", noVersioningFlag);
-      }
-      /* Adjust offset and continue */
-      /* We don't go the the normal end of the 'B' because it may
-         have other 'B's inside.  Instead, just skip past the Begin. */
-      startOffset = cmdPos2 - 1; /* don't use endPos2 */
-    } else if (cmdType == 'N') {
-      /* We're done */
-      break;
-    } else if (cmdType == 'S') {
-      /* Adjust offset and continue */
-      startOffset = cmdPos2 - 1;
-    } else if (cmdType == 'E') {
-      /* There's a problem, but ignore - should have been reported earlier */
-      /* Adjust offset and continue */
-      startOffset = cmdPos2 - 1;
-    } else if (cmdType == 'I') {
-      bug(1755); /* Should never happen */
-    } else {
-      bug(1756);
-    }
-    continue;
-  } /* while (1) */
-  /* Deallocate memory */
-  /*let(&(*fileBuf), "");*/ /* Let caller decide whether to do this */
-  let(&includeFn, "");
-  let(&fileNameWithPath, "");
-  return;
-} /* deleteSplits */
-
-
-/* 9-Jan-2018 nm */
-/* Get file name and line number given a pointer into the read buffer */
-/* The user must deallocate the returned string (file name) */
-/* The globals g_IncludeCall structure and g_includeCalls are used */
-vstring getFileAndLineNum(vstring buffPtr/*start of read buffer*/,
-    vstring currentPtr/*place at which to get file name and line no*/,
-    long *lineNum/*return argument*/) {
-  long i, smallestOffset, smallestNdx;
-  vstring fileName = "";
-
-  /* Make sure it's not outside the read buffer */
-  if (currentPtr < buffPtr
-      || currentPtr >= buffPtr + g_IncludeCall[1].current_offset) {
-    bug(1769);
-  }
-
-  /* Find the g_IncludeCall that is closest to currentPtr but does not
-     exceed it */
-  smallestOffset = currentPtr - buffPtr; /* Start with g_IncludeCall[0] */
-  if (smallestOffset < 0) bug(1767);
-  smallestNdx = 0; /* Point to g_IncludeCall[0] */
-  for (i = 0; i <= g_includeCalls; i++) {
-    if (g_IncludeCall[i].current_offset <= currentPtr - buffPtr) {
-      if ((currentPtr - buffPtr) - g_IncludeCall[i].current_offset
-          <= smallestOffset) {
-        smallestOffset = (currentPtr - buffPtr) - g_IncludeCall[i].current_offset;
-        smallestNdx = i;
-      }
-    }
-  }
-  if (smallestOffset < 0) bug(1768);
-  *lineNum = g_IncludeCall[smallestNdx].current_line
-      + countLines(buffPtr + g_IncludeCall[smallestNdx].current_offset,
-          smallestOffset);
-  /* Assign to new string to prevent original from being deallocated */
-  let(&fileName, g_IncludeCall[smallestNdx].source_fn);
-/*D*//*printf("smallestNdx=%ld smallestOffset=%ld i[].co=%ld\n",smallestNdx,smallestOffset,g_IncludeCall[smallestNdx].current_offset);*/
-  return fileName;
-} /* getFileAndLineNo */
-
-
-/* 9-Jan-2018 nm */
-/* g_Statement[stmtNum].fileName and .lineNum are initialized to "" and 0.
-   To save CPU time, they aren't normally assigned until needed, but once
-   assigned they can be reused without looking them up again.  This function
-   will assign them if they haven't been assigned yet.  It just returns if
-   they have been assigned. */
-/* The globals g_Statement[] and g_sourcePtr are used */
-void assignStmtFileAndLineNum(long stmtNum) {
-  if (g_Statement[stmtNum].lineNum > 0) return; /* Already assigned */
-  if (g_Statement[stmtNum].lineNum < 0) bug(1766);
-  if (g_Statement[stmtNum].fileName[0] != 0) bug(1770); /* Should be empty string */
-  /* We can make a direct string assignment here since previous value was "" */
-  g_Statement[stmtNum].fileName = getFileAndLineNum(g_sourcePtr,
-      g_Statement[stmtNum].statementPtr, &(g_Statement[stmtNum].lineNum));
-  return;
-} /* assignStmtFileAndLineNum */
-
-
-
-/* This function returns a pointer to a buffer containing the contents of an
-   input file and its 'include' calls.  'Size' returns the buffer's size.  */
-/* TODO - ability to flag error to skip raw source function */
-/* Recursive function that processes a found include */
-/* If NULL is returned, it means a serious error occured (like missing file)
-   and reading should be aborted. */
-/* Globals used:  g_IncludeCall[], g_includeCalls */
-char *readInclude(vstring fileBuf, long fileBufOffset,
-    /*vstring parentFileName,*/ vstring sourceFileName,
-    long *size, long parentLineNum, flag *errorFlag)
-{
-  long i;
-  /*vstring fileBuf = "";*/
-  long inclSize;
-  /* flag insideLineComment; */ /* obsolete */
-  vstring newFileBuf = "";
-  vstring inclPrefix = "";
-  vstring tmpSource = "";
-  vstring inclSource = "";
-  vstring oldSource = "";
-  vstring inclSuffix = "";
-
-  long startOffset;
-  long cmdPos1;
-  long cmdPos2;
-  long endPos1;
-  long endPos2;
-  char cmdType;
-  long oldInclSize = 0; /* Init to avoid compiler warning */
-  long newInclSize = 0; /* Init to avoid compiler warning */
-  long befInclLineNum;
-  long aftInclLineNum;
-  /*long contLineNum;*/
-  vstring includeFn = "";
-  vstring fullInputFn = "";
-  vstring fullIncludeFn = "";
-  long alreadyInclBy;
-  long saveInclCalls;
-
-  let(&newFileBuf, fileBuf);  /* TODO - can this be avoided for speedup? */
-  /* Look for and process includes */
-  startOffset = 0;
-
-  while (1) {
-    getNextInclusion(newFileBuf, startOffset, /* inputs */
-        /* outputs: */
-        &cmdPos1, &cmdPos2,
-        &endPos1, &endPos2,
-        &cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
-                     'I' = "[$...",
-                     'S' = "$( Skip [$...",
-           TODO: add error code for missing $]?
-                     'E' = Begin missing matched End
-                     'N' = no include found; includeFn = "" */
-        &includeFn /* name of included file */ );
-    /*
-    cmdType = 'B' or 'E':
-          ....... $( Begin $[ prop.mm $] $)   ......   $( End $[ prop.mm $] $) ...
-           ^      ^           ^^^^^^^       ^          ^                      ^
-      startOffset cmdPos1    fileName  cmdPos2     endPos1              endPos2
-                                                 (=0 if no End)   (=0 if no End)
-
-       Note: in the special case of Begin, cmdPos2 points _after_ the whitespace
-       after "$( Begin $[ prop.mm $] $)" i.e. the whitespace is considered part of
-       the Begin command.  The is needed because prop.mm content doesn't
-       necessarily start with whitespace.  prop.mm does, however, end with
-       whitespace (\n) as enforced by readFileToString().
-
-    cmdType = 'I':
-          ............... $[ prop.mm $]  ..............
-           ^              ^  ^^^^^^^   ^
-      startOffset   cmdPos1 fileName  cmdPos2     endPos1=0  endPos2=0
-
-    cmdType = 'S':
-          ....... $( Skip $[ prop.mm $] $)   ......
-           ^      ^          ^^^^^^^      ^
-      startOffset cmdPos1    fileName  cmdPos2     endPos1=0  endPos2=0
-    */
-
-    if (cmdType == 'N') break; /* No (more) includes */
-    if (cmdType == 'E') {
-      /* TODO: Better error msg here or in getNextInclude */
-      print2("?Error: \"$( Begin $[...\" without matching \"$( End $[...\"\n");
-      startOffset = cmdPos2; /* Get passed the bad "$( Begin $[..." */
-      *errorFlag = 1;
-      continue;
-    }
-
-    /* Count lines between start of last source continuation and end of
-       the inclusion, before the newFileBuf is modified */
-
-    if (g_IncludeCall[g_includeCalls].pushOrPop != 1) bug(1764);
-    /*
-    contLineNum = g_IncludeCall[g_includeCalls].current_line
-      + countLines(newFileBuf, ((cmdType == 'B') ? endPos2 : cmdPos2)
-          - g_IncludeCall[g_includeCalls].current_offset);\
-    */
-
-
-    /* If we're here, cmdType is 'B', 'I', or 'S' */
-
-    /* Create 2 new includeCall entries before recursive call, so that
-       alreadyInclBy will scan entries in proper order (e.g. if this
-       include calls itself at a deeper level - weird but not illegal - the
-       deeper one should be Skip'd per the Metamath spec). */
-    /* This entry is identified by pushOrPop = 0 */
-    g_includeCalls++;
-    /* We will use two more entries here (include call and return), and
-       in parseKeywords() a dummy additional top entry is assumed to exist.
-       Thus the comparison must be to 3 less than g_MAX_INCLUDECALLS. */
-    if (g_includeCalls >= g_MAX_INCLUDECALLS - 3) {
-      g_MAX_INCLUDECALLS = g_MAX_INCLUDECALLS + 20;
-/*E*/if(db5)print2("'Include' call table was increased to %ld entries.\n",
-/*E*/    g_MAX_INCLUDECALLS);
-      g_IncludeCall = realloc(g_IncludeCall, (size_t)g_MAX_INCLUDECALLS *
-          sizeof(struct includeCall_struct));
-      if (g_IncludeCall == NULL) outOfMemory("#2 (g_IncludeCall)");
-    }
-    g_IncludeCall[g_includeCalls].pushOrPop = 0;
-
-    /* This entry is identified by pushOrPop = 1 */
-    g_includeCalls++;
-    g_IncludeCall[g_includeCalls].pushOrPop = 1;
-    /* Save the value before recursive calls will increment the global
-       g_includeCalls */
-    saveInclCalls = g_includeCalls;
-
-    g_IncludeCall[saveInclCalls - 1].included_fn = "";  /* Initialize string */
-    let(&(g_IncludeCall[saveInclCalls - 1].included_fn), includeFn); /* Name of the
-       file in the inclusion statement e.g. "$( Begin $[ included_fn..." */
-    g_IncludeCall[saveInclCalls].included_fn = "";
-    let(&g_IncludeCall[saveInclCalls].included_fn,
-        sourceFileName); /* Continuation of parent file after this include */
-
-
-    /* See if includeFn file has already been included */
-    alreadyInclBy = -1;
-    for (i = 0; i <= saveInclCalls - 2; i++) {
-      if (g_IncludeCall[i].pushOrPop == 0
-         && !strcmp(g_IncludeCall[i].included_fn, includeFn)) {
-        /*
-        print2("%s",cat(
-            "(File \"",
-            g_IncludeCall[g_includeCalls].source_fn,
-            "\", referenced at line ",
-            str((double)(g_IncludeCall[g_includeCalls].calledBy_line)),
-            " in \"",
-            g_IncludeCall[g_includeCalls].calledBy_fn,
-            "\", has already been included.)\n",NULL));
-        */
-        alreadyInclBy = i;
-        break;
-      }
-    }
-    if (alreadyInclBy == -1) {
-      /* This is the first time the included file has been included */
-      switch (cmdType) {
-        case 'B':
-          let(&inclPrefix, seg(newFileBuf, cmdPos1, cmdPos2 - 1)); /* Keep trailing
-              \n (or other whitespace) as part of prefix for the special
-              case of Begin - cmdPos2 points to char after \n */
-          let(&inclSuffix, seg(newFileBuf, endPos1, endPos2 - 1));
-          let(&tmpSource, seg(newFileBuf, cmdPos2, endPos1 - 1));  /* Save the
-              included source */
-          inclSize = endPos1 - cmdPos2; /* Actual included source size */
-
-          /* Get the parent line number up to the inclusion */
-          befInclLineNum = parentLineNum + countLines(
-              newFileBuf + startOffset + 1,
-              cmdPos2 - 1 - startOffset);
-          g_IncludeCall[saveInclCalls - 1].current_line = befInclLineNum - 1;
-          aftInclLineNum = befInclLineNum + countLines(newFileBuf
-              + cmdPos2/*start at (cmdPos2+1)th character*/,
-              endPos2 - cmdPos2 - 1) + 1;
-          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum - 1;
-          parentLineNum = aftInclLineNum;
-
-          /* Call recursively to expand any includes in the included source */
-          /* Use parentLineNum since the inclusion source is in the parent file */
-          let(&inclSource, "");
-          inclSource = readInclude(tmpSource,
-              fileBufOffset + cmdPos1 - 1 + (long)strlen(inclPrefix), /*new offset*/
-              /*includeFn,*/ sourceFileName,
-              &inclSize /*input/output*/, befInclLineNum, &(*errorFlag));
-
-          oldInclSize = endPos2 - cmdPos1; /* Includes old prefix and suffix */
-          /*newInclSize = oldInclSize;*/ /* Includes new prefix and suffix */
-          newInclSize = (long)strlen(inclPrefix) + inclSize +
-                (long)strlen(inclSuffix); /* Includes new prefix and suffix */
-          /* It is already a Begin comment, so leave it alone */
-          /* *size = *size; */
-          /* Adjust starting position for next inclusion search */
-          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is 0-based but
-              cmdPos2 is 1-based */
-          break;
-        case 'I':
-          /* Read the included file */
-          let(&fullIncludeFn, cat(g_rootDirectory, includeFn, NULL));
-          let(&tmpSource, "");
-          tmpSource = readFileToString(fullIncludeFn, 0/*verbose*/, &inclSize);
-          if (tmpSource == NULL) {
-            /* TODO: print better error msg?*/
-            print2(
-                /* 23-Jan-2018 nm */
-                "?Error: file \"%s%s\" (included in \"%s\") was not found\n",
-                fullIncludeFn, g_rootDirectory, sourceFileName);
-            tmpSource = "";
-            inclSize = 0;
-            *errorFlag = 1;
-          } else {
-            print2("Reading included file \"%s\"... %ld bytes\n",
-                fullIncludeFn, inclSize);
-          }
-
-          /* Change inclusion command to Begin...End comment */
-          let(&inclPrefix, cat("$( Begin $[ ", includeFn, " $] $)\n", NULL));
-               /* Note that trailing whitespace is part of the prefix in
-                  the special case of Begin, because the included file might
-                  not start with whitespace.  However, the included file
-                  will always end with whitespace i.e. \n as enforced by
-                  readFileToString(). */
-          let(&inclSuffix, cat("$( End $[ ", includeFn, " $] $)", NULL));
-
-          /* Get the parent line number up to the inclusion */
-          /* TODO: compute aftInclLineNum directly and eliminate befInclLineNum */
-          befInclLineNum = parentLineNum + countLines(
-              newFileBuf + startOffset + 1,
-              cmdPos1 - 1 - startOffset);
-          g_IncludeCall[saveInclCalls - 1].current_line = 0;
-          aftInclLineNum = befInclLineNum + countLines(newFileBuf
-              + cmdPos1/*start at (cmdPos1+1)th character*/,
-              cmdPos2 - cmdPos1 - 1);
-          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum;
-          parentLineNum = aftInclLineNum;
-
-          /* Call recursively to expand includes in the included source */
-          /* Start at line 1 since source is in external file */
-          let(&inclSource, "");
-          inclSource = readInclude(tmpSource,
-              fileBufOffset + cmdPos1 - 1 + (long)strlen(inclPrefix), /*new offset*/
-              /*includeFn,*/ includeFn,
-              &inclSize /*input/output*/, 1/*parentLineNum*/, &(*errorFlag));
-
-          oldInclSize = cmdPos2 - cmdPos1; /* Includes old prefix and suffix */
-          /* "$( Begin $[...$] $)" must have a whitespace
-             after it; we use a newline.  readFileToString will ensure a
-             newline at its end.  We don't add whitespace after
-             "$( End $[...$] $)" but reuse the whitespace after the original
-             "$[...$]". */
-          let(&newFileBuf, cat(left(newFileBuf, cmdPos1 - 1),
-              inclPrefix, inclSource, inclSuffix,
-              right(newFileBuf, cmdPos2), NULL));
-          *size = *size - (cmdPos2 - cmdPos1) + (long)strlen(inclPrefix)
-              + inclSize + (long)strlen(inclSuffix);
-          newInclSize = (long)strlen(inclPrefix) + inclSize +
-                (long)strlen(inclSuffix); /* Includes new prefix and suffix */
-          /* Adjust starting position for next inclusion search (which will
-             be at the start of the included file continuing into the remaining
-             parent file) */
-          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
-              0-based but cmdPos2 is 1-based */
-              /*
-              + inclSize  /@ Use instead of strlen for speed @/
-              + (long)strlen(inclSuffix) */
-              ;  /* -1 since startOffset is 0-based but cmdPos1 is 1-based */
-          /* TODO: update line numbers for error msgs */
-          break;
-        case 'S':
-          /* Read the included file */
-          let(&fullIncludeFn, cat(g_rootDirectory, includeFn, NULL));
-          let(&tmpSource, "");
-          tmpSource = readFileToString(fullIncludeFn, 1/*verbose*/, &inclSize);
-          if (tmpSource == NULL) {
-            /* TODO: print better error msg */
-            print2(
-                /* 23-Jan-2018 nm */
-                "?Error: file \"%s%s\" (included in \"%s\") was not found\n",
-                fullIncludeFn, g_rootDirectory, sourceFileName);
-            *errorFlag = 1;
-            tmpSource = ""; /* Prevent seg fault */
-            inclSize = 0;
-          }
-
-          /* Change Skip comment to Begin...End comment */
-          let(&inclPrefix, cat("$( Begin $[ ", includeFn, " $] $)\n", NULL));
-          let(&inclSuffix, cat("$( End $[ ", includeFn, " $] $)", NULL));
-
-          /* Get the parent line number up to the inclusion */
-          befInclLineNum = parentLineNum + countLines(
-              newFileBuf + startOffset + 1,
-          /* TODO: compute aftInclLineNum directly and eliminate befInclLineNum */
-              cmdPos1 - 1 - startOffset);
-          g_IncludeCall[saveInclCalls - 1].current_line = 0;
-          aftInclLineNum = befInclLineNum + countLines(newFileBuf
-              + cmdPos1/*start at (cmdPos1+1)th character*/,
-              cmdPos2 - cmdPos1 - 1);
-          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum;
-          parentLineNum = aftInclLineNum;
-
-          /* Call recursively to expand includes in the included source */
-          /* Start at line 1 since source is in external file */
-          let(&inclSource, "");
-          inclSource = readInclude(tmpSource,
-              fileBufOffset + cmdPos1 - 1 + (long)strlen(inclPrefix), /*new offset*/
-              /*includeFn,*/ includeFn,
-              &inclSize /*input/output*/, 1/*parentLineNum*/, &(*errorFlag));
-
-          oldInclSize = cmdPos2 - cmdPos1; /* Includes old prefix and suffix */
-          let(&newFileBuf, cat(left(newFileBuf, cmdPos1 - 1),
-              inclPrefix, inclSource, inclSuffix,
-              right(newFileBuf, cmdPos2), NULL));
-          newInclSize = (long)strlen(inclPrefix) + inclSize +
-                (long)strlen(inclSuffix); /* Includes new prefix and suffix */
-          *size = *size - (cmdPos2 - cmdPos1) + (long)strlen(inclPrefix)
-              + inclSize + (long)strlen(inclSuffix);
-          /* Adjust starting position for next inclusion search */
-          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
-              0-based but cmdPos2 is 1-based */
-          /* TODO: update line numbers for error msgs */
-          break;
-        default:
-          bug(1745);
-      } /* end switch (cmdType) */
-    } else {
-      /* This file has already been included.  Change Begin and $[ $] to
-         Skip.  alreadyInclBy is the index of the previous g_IncludeCall that
-         included it. */
-      if (!(alreadyInclBy > 0)) bug(1765);
-      switch (cmdType) {
-        case 'B':
-          /* Save the included source temporarily */
-          let(&inclSource,
-              seg(newFileBuf, cmdPos2, endPos1 - 1));
-          /* Make sure it's content matches */
-          let(&oldSource, "");
-          oldSource = g_IncludeCall[  /* Connect to source for brevity */
-                  alreadyInclBy
-                  ].current_includeSource;
-          if (strcmp(inclSource, oldSource)) {
-            /* TODO - print better error msg */
-            print2(
-        "?Warning: \"$( Begin $[...\" source, with %ld characters, mismatches\n",
-                (long)strlen(inclSource));
-            print2(
-                "  earlier inclusion, with %ld characters.\n",
-                (long)strlen(oldSource));
-          }
-          oldSource = ""; /* Disconnect from source */
-          /* We need to delete it from the source and change to Skip */
-          let(&inclPrefix, cat("$( Skip $[ ", includeFn, " $] $)", NULL));
-          let(&inclSuffix, "");
-
-          /* Get the parent line number up to the inclusion */
-          befInclLineNum = parentLineNum + countLines(
-              newFileBuf + startOffset + 1,
-              cmdPos2 - 1 - startOffset);
-          g_IncludeCall[saveInclCalls - 1].current_line = befInclLineNum;
-          aftInclLineNum = befInclLineNum + countLines(newFileBuf
-              + cmdPos2/*start at (cmdPos2+1)th character*/,
-              endPos2 - cmdPos2 /*- 1*/);
-          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum;
-          parentLineNum = aftInclLineNum;
-
-          let(&inclSource, ""); /* Final source to be stored - none */
-          inclSize = 0; /* Size of just the included source */
-          oldInclSize = endPos2 - cmdPos1; /* Includes old prefix and suffix */
-          let(&newFileBuf, cat(left(newFileBuf, cmdPos1 - 1),
-              inclPrefix,
-              right(newFileBuf, endPos2), NULL));
-          newInclSize = (long)strlen(inclPrefix); /* Includes new prefix and suffix */
-          *size = *size - (endPos2 - cmdPos1) + newInclSize;
-          /* Adjust starting position for next inclusion search (which may
-             occur inside the source we just included, so don't skip passed
-             that source) */
-          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
-              0-based but cmdPos2 is 1-based */
-          break;
-        case 'I':
-          /* Change inclusion command to Skip comment */
-          let(&inclPrefix, cat("$( Skip $[ ", includeFn, " $] $)", NULL));
-          let(&inclSuffix, "");
-
-          /* Get the parent line number up to the inclusion */
-          befInclLineNum = parentLineNum + countLines(
-              newFileBuf + startOffset + 1,
-              cmdPos1 - 1 - startOffset);
-          g_IncludeCall[saveInclCalls - 1].current_line = befInclLineNum;
-          aftInclLineNum = befInclLineNum + countLines(newFileBuf
-              + cmdPos1/*start at (cmdPos1+1)th character*/,
-              cmdPos2 - cmdPos1 /*- 1*/);
-          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum;
-          parentLineNum = aftInclLineNum;
-
-          let(&inclSource, ""); /* Final source to be stored - none */
-          inclSize = 0; /* Size of just the included source */
-          oldInclSize = cmdPos2 - cmdPos1; /* Includes old prefix and suffix */
-          let(&newFileBuf, cat(left(newFileBuf, cmdPos1 - 1),
-              inclPrefix,
-              right(newFileBuf, cmdPos2), NULL));
-          newInclSize = (long)strlen(inclPrefix); /* Includes new prefix and suffix */
-          *size = *size - (cmdPos2 - cmdPos1) + (long)strlen(inclPrefix);
-          /* Adjust starting position for next inclusion search */
-          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
-              0-based but cmdPos2 is 1-based */
-          break;
-        case 'S':
-          /* It is already Skipped, so leave it alone */
-          /* *size = *size; */
-          /* Adjust starting position for next inclusion search */
-          let(&inclPrefix, seg(newFileBuf, cmdPos1, cmdPos2 - 1));
-          let(&inclSuffix, "");
-
-          /* Get the parent line number up to the inclusion */
-          befInclLineNum = parentLineNum + countLines(
-              newFileBuf + startOffset + 1,
-              cmdPos1 - 1 - startOffset);
-          g_IncludeCall[saveInclCalls - 1].current_line = befInclLineNum;
-          aftInclLineNum = befInclLineNum + countLines(newFileBuf
-              + cmdPos1/*start at (cmdPos1+1)th character*/,
-              cmdPos2 - cmdPos1 /*- 1*/);
-          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum - 1;
-          parentLineNum = aftInclLineNum;
-
-          let(&inclSource, ""); /* Final source to be stored - none */
-          inclSize = 0; /* Size of just the included source */
-          oldInclSize = cmdPos2 - cmdPos1; /* Includes old prefix and suffix */
-          newInclSize = oldInclSize; /* Includes new prefix and suffix */
-          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
-              0-based but cmdPos2 is 1-based */
-          if (startOffset != cmdPos2 - 1) bug(1772);
-          break;
-        default:
-          bug(1745);
-      } /* end switch(cmdType) */
-    } /* if alreadyInclBy == -1 */
-
-    /* Assign structure with information for subsequent passes and
-       (later) error messages */
-    g_IncludeCall[saveInclCalls - 1].source_fn = "";  /* Name of the file where the
-       inclusion source is located (= parent file for $( Begin $[... etc.) */
-    g_IncludeCall[saveInclCalls - 1].current_offset = fileBufOffset + cmdPos1 - 1
-        + (long)strlen(inclPrefix) - 1; /* This is the starting character position
-            of the included file w.r.t entire source buffer */
-    if (alreadyInclBy >= 0 || cmdType == 'B') {
-      /* No external file was included, so let the includeFn be the
-         same as the source */
-      let(&g_IncludeCall[saveInclCalls - 1].source_fn, sourceFileName);
-    } else {
-      /* It hasn't been included yet, and cmdType is 'I' or 'S', meaning
-         that we bring in an external file */
-      let(&g_IncludeCall[saveInclCalls - 1].source_fn, includeFn); /* $[ $], Begin,
-          or Skip file name for this inclusion */
-      /*
-      g_IncludeCall[saveInclCalls - 1].current_line = 0; */ /* The line number
-          of the start of the included file (=1) */
-    }
-    g_IncludeCall[saveInclCalls - 1].current_includeSource = inclSource; /* let() not
-        needed because we're just assigning a new name to inclSource */
-    inclSource = ""; /* Detach from
-        g_IncludeCall[saveInclCalls - 1].current_includeSource for later reuse */
-    g_IncludeCall[saveInclCalls - 1].current_includeSource = "";
-    g_IncludeCall[saveInclCalls - 1].current_includeLength = inclSize; /* Length of the file
-        to be included (0 if the file was previously included) */
-
-
-    /* Initialize a new include call for the continuation of the parent. */
-    /* This entry is identified by pushOrPop = 1 */
-    g_IncludeCall[saveInclCalls].source_fn = "";  /* Name of the file to be
-        included */
-    let(&g_IncludeCall[saveInclCalls].source_fn,
-        sourceFileName); /* Source file containing
-         this inclusion */
-    g_IncludeCall[saveInclCalls].current_offset = fileBufOffset + cmdPos1
-        + newInclSize - 1;
-        /* This is the position of the continuation of the parent */
-    g_IncludeCall[saveInclCalls].current_includeSource = ""; /* (Currently) assigned
-        only if we may need it for a later Begin comparison */
-    g_IncludeCall[saveInclCalls].current_includeLength = 0; /* Length of the file
-        to be included (0 if the file was previously included) */
-
-  } /* while (1) */
-
-  /* Deallocate strings */
-  let(&inclSource, "");
-  let(&tmpSource, "");
-  let(&oldSource, "");
-  let(&inclPrefix, "");
-  let(&inclSuffix, "");
-  let(&includeFn, "");
-  let(&fullInputFn, "");
-  let(&fullIncludeFn, "");
-
-  return newFileBuf;
-} /* readInclude */
-
-
-/* This function returns a pointer to a buffer containing the contents of an
-   input file and its 'include' calls.  'Size' returns the buffer's size.  */
-/* TODO - ability to flag error to skip raw source function */
-/* If NULL is returned, it means a serious error occured (like missing file)
-   and reading should be aborted. */
-char *readSourceAndIncludes(vstring inputFn /*input*/, long *size /*output*/)
-{
-  long i;
-/*D*//*long j;*/
-/*D*//*vstring s=""; */
-  vstring fileBuf = "";
-  vstring newFileBuf = "";
-
-  vstring fullInputFn = "";
-  flag errorFlag = 0;
-
-  /* Read starting file */
-  let(&fullInputFn, cat(g_rootDirectory, inputFn, NULL));
-  fileBuf = readFileToString(fullInputFn, 1/*verbose*/, &(*size));
-  if (fileBuf == NULL) {
-    print2(
-        "?Error: file \"%s\" was not found\n", fullInputFn);
-    fileBuf = "";
-    *size = 0;
-    errorFlag = 1;
-    /* goto RETURN_POINT; */ /* Don't go now so that g_IncludeCall[]
-         strings will be initialized.  If error, then blank fileBuf will
-         cause main while loop to break immediately after first
-         getNextInclusion() call. */
-  }
-  print2("Reading source file \"%s\"... %ld bytes\n", fullInputFn, *size);
-
-  /* Create a ficticious initial include for the main file (at least 2
-     g_IncludeCall structure array entries have been already been allocated
-     in initBigArrays() in mmdata.c) */
-  g_includeCalls = 0;
-  g_IncludeCall[g_includeCalls].pushOrPop = 0; /* 0 means start of included file,
-       1 means continuation of including file */
-  g_IncludeCall[g_includeCalls].source_fn = "";
-  let(&g_IncludeCall[g_includeCalls].source_fn, inputFn); /* $[ $], Begin,
-      of Skip file name for this inclusion */
-  g_IncludeCall[g_includeCalls].included_fn = "";
-  let(&g_IncludeCall[g_includeCalls].included_fn, inputFn); /* $[ $], Begin,
-      of Skip file name for this inclusion */
-  g_IncludeCall[g_includeCalls].current_offset = 0;  /* This is the starting
-      character position of the included file w.r.t entire source buffer */
-  g_IncludeCall[g_includeCalls].current_line = 1; /* The line number
-      of the start of the included file (=1) or the continuation line of
-      the parent file */
-  g_IncludeCall[g_includeCalls].current_includeSource = ""; /* (Currently) assigned
-      only if we may need it for a later Begin comparison */
-  g_IncludeCall[g_includeCalls].current_includeLength = *size; /* Length of the file
-      to be included (0 if the file was previously included) */
-
-  /* Create a ficticious entry for the "continuation" after the
-     main file, to make error message line searching easier */
-  g_includeCalls++;
-  g_IncludeCall[g_includeCalls].pushOrPop = 1; /* 0 means start of included file,
-       1 means continuation of including file */
-  g_IncludeCall[g_includeCalls].source_fn = "";
-  /*let(&g_IncludeCall[g_includeCalls].source_fn, inputFn);*/ /* Leave empty;
-        there is no "continuation" file for the main file, so no need to assign
-        (it won't be used) */
-  g_IncludeCall[g_includeCalls].included_fn = "";
-  /*let(&g_IncludeCall[g_includeCalls].included_fn, inputFn);*/ /* $[ $], Begin,
-      of Skip file name for this inclusion */
-  g_IncludeCall[g_includeCalls].current_line = -1; /* Ideally this should be
-      countLines(fileBuf), but since it's never used we don't bother to
-      call countLines(fileBuf) to save CPU time */
-  g_IncludeCall[g_includeCalls].current_includeSource = ""; /* (Currently) assigned
-      only if we may need it for a later Begin comparison */
-  g_IncludeCall[g_includeCalls].current_includeLength = 0; /* The "continuation"
-      of the main file is ficticious, so just set it to 0 length */
-
-  /* Recursively expand the source of an included file */
-  newFileBuf = "";
-  newFileBuf = readInclude(fileBuf, 0, /*inputFn,*/ inputFn, &(*size),
-      1/*parentLineNum*/, &errorFlag);
-  g_IncludeCall[1].current_offset = *size;  /* This is the starting
-      character position of the included file w.r.t entire source buffer.
-      Here, it points to the nonexistent character just beyond end of main file
-      (after all includes are expanded).
-      Note that readInclude() may change g_includeCalls, so use 1 explicitly. */
-  let(&fileBuf, ""); /* Deallocate */
-/*D*//*printf("*size=%ld\n",*size);                                       */
-/*D*//*for(i=0;i<*size;i++){                                              */
-/*D*//*let(&s,"");                                                        */
-/*D*//*s=getFileAndLineNum(newFileBuf,newFileBuf+i,&j);                   */
-/*D*//*printf("i=%ld ln=%ld fn=%s ch=%c\n",i,j,s,(newFileBuf+i)[0]);  }   */
-  if (errorFlag == 1) {
-    /* The read should be aborted by the caller. */
-    /* Deallocate the strings in the g_IncludeCall[] structure. */
-    for (i = 0; i <= g_includeCalls; i++) {
-      let(&g_IncludeCall[i].source_fn, "");
-      let(&g_IncludeCall[i].included_fn, "");
-      let(&g_IncludeCall[i].current_includeSource, "");
-      g_includeCalls = -1; /* For the eraseSource() function in mmcmds.c */
-    }
-    return NULL;
-  } else {
-/*D*//*for (i=0; i<=g_includeCalls;i++)                                       */
-/*D*//*  printf("i=%ld p=%ld f=%s,%s l=%ld o=%ld s=%ld\n",                  */
-/*D*//*i,(long)g_IncludeCall[i].pushOrPop,g_IncludeCall[i].source_fn,           */
-/*D*//*g_IncludeCall[i].included_fn,g_IncludeCall[i].current_line,              */
-/*D*//*g_IncludeCall[i].current_offset,g_IncludeCall[i].current_includeLength); */
-    return newFileBuf;
-  }
-
-} /* readSourceAndIncludes */
-
+/*****************************************************************************/
+/*        Copyright (C) 2021  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mminou.h"
+#include "mmpars.h"
+/* #include "mmcmds.h" */  /* For getContribs() if used */
+#include "mmpfas.h" /* Needed for g_pipDummyVars, subproofLen() */
+#include "mmunif.h" /* Needed for g_minSubstLen */
+#include "mmcmdl.h" /* Needed for g_rootDirectory */
+
+long potentialStatements; /* Potential statements in source file (upper
+                                 limit) for memory allocation purposes */
+flag illegalLabelChar[256]; /* Illegal characters for labels -- initialized
+                               by parseLabels() */
+/* (Next 2 are global in mmpars.c only) */
+long *g_labelKeyBase; /* Start of assertion ($a, $p) labels */
+long g_numLabelKeys; /* Number of assertion labels */
+
+long *g_allLabelKeyBase; /* Start of all labels */
+long g_numAllLabelKeys; /* Number of all labels */
+
+
+/* Working structure for parsing proofs */
+/* This structure should be deallocated by the ERASE command. */
+long g_wrkProofMaxSize = 0; /* Maximum size so far - it may grow */
+long wrkMathPoolMaxSize = 0; /* Max mathStringPool size so far - it may grow */
+struct wrkProof_struct g_WrkProof;
+
+
+
+/* This function returns a pointer to a buffer containing the contents of an
+   input file and its 'include' calls.  'Size' returns the buffer's size.
+   Partial parsing is done; when 'include' statements are found, this function
+   is called recursively.
+   The file g_input_fn is assumed to be opened when this is called. */
+char *readRawSource(vstring fileBuf, long *size) {
+  long charCount = 0;
+  char *fbPtr;
+  flag insideComment;
+  char mode;
+  char tmpch;
+
+  charCount = *size;
+
+  /* Look for $[ and $] 'include' statement start and end */
+  /* These won't happen since they're now expanded earlier */
+  /* But it can't hurt, since the code is already written.
+     TODO - clean it up for speedup? */
+  fbPtr = fileBuf;
+  mode = 0; /* 0 = outside of 'include', 1 = inside of 'include' */
+  insideComment = 0; /* 1 = inside $( $) comment */
+  while (1) {
+    /* Find a keyword or newline */
+    tmpch = fbPtr[0];
+    if (!tmpch) { /* End of file */
+      if (insideComment) {
+        rawSourceError(fileBuf, fbPtr - 1, 2,
+         "The last comment in the file is incomplete.  \"$)\" was expected.");
+      } else {
+        if (mode != 0) {
+          rawSourceError(fileBuf, fbPtr - 1, 2,
+   "The last include statement in the file is incomplete.  \"$]\" was expected."
+           );
+        }
+      }
+      break;
+    }
+    if (tmpch != '$') {
+      if (!isgraph((unsigned char)tmpch) && !isspace((unsigned char)tmpch)) {
+        rawSourceError(fileBuf, fbPtr, 1,
+            cat("Illegal character (ASCII code ",
+            str((double)((unsigned char)tmpch)),
+            " decimal).",NULL));
+      }
+      fbPtr++;
+      continue;
+    }
+
+
+    /* Detect missing whitespace around keywords (per current
+       Metamath language spec) */
+    if (fbPtr > fileBuf) {  /* If this '$' is not the 1st file character */
+      if (isgraph((unsigned char)(fbPtr[-1]))) {
+        /* The character before the '$' is not white space */
+        if (!insideComment || fbPtr[1] == ')') {
+          /* Inside comments, we only care about the "$)" keyword */
+          rawSourceError(fileBuf, fbPtr, 2,
+              "A keyword must be preceded by white space.");
+        }
+      }
+    }
+    fbPtr++;
+    if (fbPtr[0]) {  /* If the character after '$' is not end of file (which
+                        would be an error anyway, but detected elsewhere) */
+      if (isgraph((unsigned char)(fbPtr[1]))) {
+        /* The character after the character after the '$' is not white
+           space (nor end of file) */
+        if (!insideComment || fbPtr[0] == ')') {
+          /* Inside comments, we only care about the "$)" keyword */
+          rawSourceError(fileBuf, fbPtr + 1, 1,
+              "A keyword must be followed by white space.");
+          }
+      }
+    }
+
+    switch (fbPtr[0]) {
+      case '(': /* Start of comment */
+        if (insideComment) {
+          rawSourceError(fileBuf, fbPtr - 1, 2,
+          "Nested comments are not allowed.");
+        }
+        insideComment = 1;
+        continue;
+      case ')': /* End of comment */
+        if (!insideComment) {
+          rawSourceError(fileBuf, fbPtr - 1, 2,
+              "A comment terminator was found outside of a comment.");
+        }
+        insideComment = 0;
+        continue;
+    }
+    if (insideComment) continue;
+    switch (fbPtr[0]) {
+      case '[':
+        if (mode != 0) {
+          rawSourceError(fileBuf, fbPtr - 1, 2,
+              "Nested include statements are not allowed.");
+        } else {
+          /* $[ ... $] should have been processed by readSourceAndIncludes() */
+          rawSourceError(fileBuf, fbPtr - 1, 2,
+              "\"$[\" is unterminated or has ill-formed \"$]\".");
+
+        }
+        continue;
+      case ']':
+        if (mode == 0) {
+          rawSourceError(fileBuf, fbPtr - 1, 2,
+              "A \"$[\" is required before \"$]\".");
+          continue;
+        }
+
+        /* Because $[ $] are already expanded here, this should never happen */
+        bug(1759);
+
+        continue;
+
+      case '{':
+      case '}':
+      case '.':
+        potentialStatements++; /* Number of potential statements for malloc */
+        break;
+    } /* End switch fbPtr[0] */
+  } /* End while */
+
+  if (fbPtr != fileBuf + charCount) {
+    /* To help debugging: */
+    printf("fbPtr=%ld fileBuf=%ld charCount=%ld diff=%ld\n",
+        (long)fbPtr, (long)fileBuf, charCount, fbPtr - fileBuf - charCount);
+    bug(1704);
+  }
+
+  print2("%ld bytes were read into the source buffer.\n", charCount);
+
+  if (*size != charCount) bug(1761);
+  return (fileBuf);
+
+} /* readRawSource */
+
+/* This function initializes the g_Statement[] structure array and assigns
+   sections of the raw input text.  statements is updated.
+   g_sourcePtr is assumed to point to the raw input buffer.
+   g_sourceLen is assumed to be length of the raw input buffer. */
+void parseKeywords(void)
+{
+  long i, j, k;
+  char *fbPtr;
+  flag insideComment;
+  char mode, type;
+  char *startSection;
+  char tmpch;
+  long dollarPCount = 0; /* For statistics only */
+  long dollarACount = 0; /* For statistics only */
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  type = 0;
+
+  /* Determine the upper limit of the number of new statements added for
+     allocation purposes (computed in readRawInput) */
+  potentialStatements = potentialStatements + 3; /* To be cautious */
+/*E*/if(db5)print2("There are up to %ld potential statements.\n",
+/*E*/   potentialStatements);
+
+  /* Reallocate the statement array for all potential statements */
+  g_Statement = realloc(g_Statement, (size_t)potentialStatements
+      * sizeof(struct statement_struct));
+  if (!g_Statement) outOfMemory("#4 (statement)");
+
+  /* Initialize the statement array */
+  i = 0;
+  g_Statement[i].lineNum = 0;   /* assigned by assignStmtFileAndLineNum() */
+  g_Statement[i].fileName = ""; /* assigned by assignStmtFileAndLineNum() */
+  g_Statement[i].labelName = "";
+  g_Statement[i].uniqueLabel = 0;
+  g_Statement[i].type = illegal_;
+  g_Statement[i].scope = 0;
+  g_Statement[i].beginScopeStatementNum = 0;
+  g_Statement[i].endScopeStatementNum = 0;
+  g_Statement[i].labelSectionPtr = "";
+  g_Statement[i].labelSectionLen = 0;
+  g_Statement[i].labelSectionChanged = 0;
+  g_Statement[i].statementPtr = ""; /* input to assignStmtFileAndLineNum() */
+  g_Statement[i].mathSectionPtr = "";
+  g_Statement[i].mathSectionLen = 0;
+  g_Statement[i].mathSectionChanged = 0;
+  g_Statement[i].proofSectionPtr = "";
+  g_Statement[i].proofSectionLen = 0;
+  g_Statement[i].proofSectionChanged = 0;
+  g_Statement[i].mathString = NULL_NMBRSTRING;
+  g_Statement[i].mathStringLen = 0;
+  g_Statement[i].proofString = NULL_NMBRSTRING;
+  g_Statement[i].reqHypList = NULL_NMBRSTRING;
+  g_Statement[i].optHypList = NULL_NMBRSTRING;
+  g_Statement[i].numReqHyp = 0;
+  g_Statement[i].reqVarList = NULL_NMBRSTRING;
+  g_Statement[i].optVarList = NULL_NMBRSTRING;
+  g_Statement[i].reqDisjVarsA = NULL_NMBRSTRING;
+  g_Statement[i].reqDisjVarsB = NULL_NMBRSTRING;
+  g_Statement[i].reqDisjVarsStmt = NULL_NMBRSTRING;
+  g_Statement[i].optDisjVarsA = NULL_NMBRSTRING;
+  g_Statement[i].optDisjVarsB = NULL_NMBRSTRING;
+  g_Statement[i].optDisjVarsStmt = NULL_NMBRSTRING;
+  g_Statement[i].pinkNumber = 0;
+  g_Statement[i].headerStartStmt = 0;
+  for (i = 1; i < potentialStatements; i++) {
+    g_Statement[i] = g_Statement[0];
+  }
+
+  /* In case there is no relevant statement (originally added for MARKUP
+     command) */
+  g_Statement[0].labelName = "(N/A)";
+
+/*E*/if(db5)print2("Finished initializing statement array.\n");
+
+  /* Fill in the statement array with raw source text */
+  fbPtr = g_sourcePtr;
+  mode = 0; /* 0 = label section, 1 = math section, 2 = proof section */
+  insideComment = 0; /* 1 = inside comment */
+  startSection = fbPtr;
+
+  while (1) {
+    /* Find a keyword or newline */
+    tmpch = fbPtr[0];
+    if (!tmpch) { /* End of file */
+      if (mode != 0) {
+        sourceError(fbPtr - 1, 2, g_statements,
+            "Expected \"$.\" here (last line of file).");
+        if (g_statements) { /* Adjustment for error messages */
+          startSection = g_Statement[g_statements].labelSectionPtr;
+          g_statements--;
+        }
+      }
+      break;
+    }
+
+    if (tmpch != '$') {
+      fbPtr++;
+      continue;
+    }
+    fbPtr++;
+    switch (fbPtr[0]) {
+      case '$': /* "$$" means literal "$" */
+        fbPtr++;
+        continue;
+      case '(': /* Start of comment */
+        insideComment = 1;
+        continue;
+      case ')': /* End of comment */
+        insideComment = 0;
+        continue;
+    }
+    if (insideComment) continue;
+    switch (fbPtr[0]) {
+      case 'c':  type = c_; break;
+      case 'v':  type = v_; break;
+      case 'e':  type = e_; break;
+      case 'f':  type = f_; break;
+      case 'd':  type = d_; break;
+      case 'a':  type = a_; dollarACount++; break;
+      case 'p':  type = p_; dollarPCount++; break;
+      case '{':  type = lb_; break;
+      case '}':  type = rb_; break;
+    }
+    switch (fbPtr[0]) {
+      case 'c':
+      case 'v':
+      case 'e':
+      case 'f':
+      case 'd':
+      case 'a':
+      case 'p':
+      case '{':
+      case '}':
+        if (mode != 0) {
+          if (mode == 2 || type != p_) {
+            sourceError(fbPtr - 1, 2, g_statements,
+                "Expected \"$.\" here.");
+          } else {
+            sourceError(fbPtr - 1, 2, g_statements,
+                "Expected \"$=\" here.");
+          }
+          continue;
+        }
+        /* Initialize a new statement */
+        g_statements++;
+        g_Statement[g_statements].type = type;
+        g_Statement[g_statements].labelSectionPtr = startSection;
+        g_Statement[g_statements].labelSectionLen = fbPtr - startSection - 1;
+        /* The character after label section is used by
+           assignStmtFileAndLineNum() to determine the "line number" for the
+           statement as a whole */
+        g_Statement[g_statements].statementPtr = startSection
+            + g_Statement[g_statements].labelSectionLen;
+        startSection = fbPtr + 1;
+        if (type != lb_ && type != rb_) mode = 1;
+        continue;
+      default:
+        if (mode == 0) {
+          sourceError(fbPtr - 1, 2, g_statements, cat(
+              "Expected \"$c\", \"$v\", \"$e\", \"$f\", \"$d\",",
+              " \"$a\", \"$p\", \"${\", or \"$}\" here.",NULL));
+          continue;
+        }
+        if (mode == 1) {
+          if (type == p_ && fbPtr[0] != '=') {
+            sourceError(fbPtr - 1, 2, g_statements,
+                "Expected \"$=\" here.");
+            if (fbPtr[0] == '.') {
+              mode = 2; /* If $. switch mode to help reduce error msgs */
+            }
+          }
+          if (type != p_ && fbPtr[0] != '.') {
+            sourceError(fbPtr - 1, 2, g_statements,
+                "Expected \"$.\" here.");
+            continue;
+          }
+          /* Add math section to statement */
+          g_Statement[g_statements].mathSectionPtr = startSection;
+          g_Statement[g_statements].mathSectionLen = fbPtr - startSection - 1;
+          startSection = fbPtr + 1;
+          if (type == p_ && mode != 2 /* !error msg case */) {
+            mode = 2; /* Switch mode to proof section */
+          } else {
+            mode = 0;
+          }
+          continue;
+        } /* End if mode == 1 */
+        if (mode == 2) {
+          if (fbPtr[0] != '.') {
+            sourceError(fbPtr - 1, 2, g_statements,
+                "Expected \"$.\" here.");
+            continue;
+          }
+          /* Add proof section to statement */
+          g_Statement[g_statements].proofSectionPtr = startSection;
+          g_Statement[g_statements].proofSectionLen = fbPtr - startSection - 1;
+          startSection = fbPtr + 1;
+          mode = 0;
+          continue;
+        } /* End if mode == 2 */
+    } /* End switch fbPtr[0] */
+  } /* End while */
+
+  if (fbPtr != g_sourcePtr + g_sourceLen) bug(1706);
+
+  print2("The source has %ld statements; %ld are $a and %ld are $p.\n",
+       g_statements, dollarACount, dollarPCount);
+
+  /* Put chars after the last $. into the label section of a dummy statement */
+  g_Statement[g_statements + 1].type = illegal_;
+  g_Statement[g_statements + 1].labelSectionPtr = startSection;
+  g_Statement[g_statements + 1].labelSectionLen = fbPtr - startSection;
+  /* Point to last character of file in case we ever need lineNum/fileName */
+  g_Statement[g_statements + 1].statementPtr = fbPtr - 1;
+
+  /* Initialize the pink number to print after the statement labels
+   in HTML output. */
+  /* The pink number only counts $a and $p statements, unlike the statement
+     number which also counts $f, $e, $c, $v, ${, $} */
+  j = 0;
+  k = 0;
+  for (i = 1; i <= g_statements; i++) {
+    if (g_Statement[i].type == a_ || g_Statement[i].type == p_) {
+
+      /* Use the statement _after_ the previous $a or $p; that is the start
+         of the "header area" for use by getSectionHeadings() in mmwtex.c.
+         headerStartStmt will be equal to the current statement if the
+         previous statement is also a $a or $p) */
+      g_Statement[i].headerStartStmt = k + 1;
+      k = i;
+
+      j++;
+      g_Statement[i].pinkNumber = j;
+    }
+  }
+  /* Also, put the largest pink number in the last statement, no
+     matter what it kind it is, so we can look up the largest number in
+     pinkHTML() in mmwtex.c */
+  g_Statement[g_statements].pinkNumber = j;
+
+
+/*E*/if(db5){for (i=1; i<=g_statements; i++){
+/*E*/  if (i == 5) { print2("(etc.)\n");} else { if (i<5) {
+/*E*/  assignStmtFileAndLineNum(i);
+/*E*/  print2("Statement %ld: line %ld file %s.\n",i,g_Statement[i].lineNum,
+/*E*/      g_Statement[i].fileName);
+/*E*/}}}}
+
+}
+
+/* This function parses the label sections of the g_Statement[] structure array.
+   g_sourcePtr is assumed to point to the beginning of the raw input buffer.
+   g_sourceLen is assumed to be length of the raw input buffer. */
+void parseLabels(void) {
+  long i, j, k;
+  char *fbPtr;
+  char type;
+  long stmt;
+  flag dupFlag;
+
+  /* Define the legal label characters */
+  for (i = 0; i < 256; i++) {
+    illegalLabelChar[i] = !isalnum(i);
+  }
+  illegalLabelChar['-'] = 0;
+  illegalLabelChar['_'] = 0;
+  illegalLabelChar['.'] = 0;
+
+
+  /* Scan all statements and extract their labels */
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+    type = g_Statement[stmt].type;
+    fbPtr = g_Statement[stmt].labelSectionPtr;
+    fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+    j = tokenLen(fbPtr);
+    if (j) {
+      for (k = 0; k < j; k++) {
+        if (illegalLabelChar[(unsigned char)fbPtr[k]]) {
+          sourceError(fbPtr + k, 1, stmt,
+        "Only letters, digits, \"_\", \"-\", and \".\" are allowed in labels.");
+          break;
+        }
+      }
+      switch (type) {
+        case d_:
+        case rb_:
+        case lb_:
+        case v_:
+        case c_:
+          sourceError(fbPtr, j, stmt,
+                "A label isn't allowed for this statement type.");
+      }
+      g_Statement[stmt].labelName = malloc((size_t)j + 1);
+      if (!g_Statement[stmt].labelName) outOfMemory("#5 (label)");
+      g_Statement[stmt].labelName[j] = 0;
+      memcpy(g_Statement[stmt].labelName, fbPtr, (size_t)j);
+      fbPtr = fbPtr + j;
+      fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+      j = tokenLen(fbPtr);
+      if (j) {
+        sourceError(fbPtr, j, stmt,
+            "A statement may have only one label.");
+      }
+    } else {
+      switch (type) {
+        case e_:
+        case f_:
+        case a_:
+        case p_:
+          sourceError(fbPtr, 2, stmt,
+                "A label is required for this statement type.");
+      }
+    }
+  } /* Next stmt */
+
+  /* Make sure there is no token after the last statement */
+  fbPtr = g_Statement[g_statements + 1].labelSectionPtr; /* Last (dummy) statement*/
+  i = whiteSpaceLen(fbPtr);
+  j = tokenLen(fbPtr + i);
+  if (j) {
+    sourceError(fbPtr + i, j, 0,
+        "There should be no tokens after the last statement.");
+  }
+
+  /* Sort the labels for later lookup */
+  g_labelKey = malloc(((size_t)g_statements + 1) * sizeof(long));
+  if (!g_labelKey) outOfMemory("#6 (g_labelKey)");
+  for (i = 1; i <= g_statements; i++) {
+    g_labelKey[i] = i;
+  }
+  g_labelKeyBase = &g_labelKey[1];
+  g_numLabelKeys = g_statements;
+  qsort(g_labelKeyBase, (size_t)g_numLabelKeys, sizeof(long), labelSortCmp);
+
+  /* Skip null labels. */
+  for (i = 1; i <= g_statements; i++) {
+    if (g_Statement[g_labelKey[i]].labelName[0]) break;
+  }
+  g_labelKeyBase = &g_labelKey[i];
+  g_numLabelKeys = g_statements - i + 1;
+/*E*/if(db5)print2("There are %ld non-empty labels.\n", g_numLabelKeys);
+/*E*/if(db5){print2("The first (up to 5) sorted labels are:\n");
+/*E*/  for (i=0; i<5; i++) {
+/*E*/    if (i >= g_numLabelKeys) break;
+/*E*/    print2("%s ",g_Statement[g_labelKeyBase[i]].labelName);
+/*E*/  } print2("\n");}
+
+
+
+  /* Copy the keys for all possible labels for lookup by the
+     squishProof command when local labels are generated in packed proofs. */
+  g_allLabelKeyBase = malloc((size_t)g_numLabelKeys * sizeof(long));
+  if (!g_allLabelKeyBase) outOfMemory("#60 (g_allLabelKeyBase)");
+  memcpy(g_allLabelKeyBase, g_labelKeyBase, (size_t)g_numLabelKeys * sizeof(long));
+  g_numAllLabelKeys = g_numLabelKeys;
+
+  /* Now back to the regular label stuff. */
+  /* Check for duplicate labels */
+  /* (This will go away if local labels on hypotheses are allowed.) */
+  dupFlag = 0;
+  for (i = 0; i < g_numLabelKeys; i++) {
+    if (dupFlag) {
+      /* This "if" condition causes only the 2nd in a pair of duplicate labels
+         to have an error message. */
+      dupFlag = 0;
+      if (!strcmp(g_Statement[g_labelKeyBase[i]].labelName,
+          g_Statement[g_labelKeyBase[i - 1]].labelName)) dupFlag = 1;
+    }
+    if (i < g_numLabelKeys - 1) {
+      if (!strcmp(g_Statement[g_labelKeyBase[i]].labelName,
+          g_Statement[g_labelKeyBase[i + 1]].labelName)) dupFlag = 1;
+    }
+    if (dupFlag) {
+      fbPtr = g_Statement[g_labelKeyBase[i]].labelSectionPtr;
+      k = whiteSpaceLen(fbPtr);
+      j = tokenLen(fbPtr + k);
+      sourceError(fbPtr + k, j, g_labelKeyBase[i],
+         "This label is declared more than once.  All labels must be unique.");
+    }
+  }
+
+}
+
+/* This functions retrieves all possible math symbols from $c and $v
+   statements. */
+void parseMathDecl(void) {
+  long potentialSymbols;
+  long stmt;
+  char *fbPtr;
+  long i, j, k;
+  char *tmpPtr;
+  nmbrString *nmbrTmpPtr;
+  long oldG_mathTokens;
+  void *voidPtr; /* bsearch returned value */
+
+  /* Find the upper limit of the number of symbols declared for
+     pre-allocation:  at most, the number of symbols is half the number of
+     characters, since $c and $v statements require white space. */
+  potentialSymbols = 0;
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+    switch (g_Statement[stmt].type) {
+      case c_:
+      case v_:
+        potentialSymbols = potentialSymbols + g_Statement[stmt].mathSectionLen;
+    }
+  }
+  potentialSymbols = (potentialSymbols / 2) + 2;
+/*E*/if(db5)print2("%ld potential symbols were computed.\n",potentialSymbols);
+  g_MathToken = realloc(g_MathToken, (size_t)potentialSymbols *
+      sizeof(struct mathToken_struct));
+  if (!g_MathToken) outOfMemory("#7 (g_MathToken)");
+
+  /* Scan $c and $v statements to accumulate all possible math symbols */
+  g_mathTokens = 0;
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+    switch (g_Statement[stmt].type) {
+      case c_:
+      case v_:
+        oldG_mathTokens = g_mathTokens;
+        fbPtr = g_Statement[stmt].mathSectionPtr;
+        while (1) {
+          i = whiteSpaceLen(fbPtr);
+          j = tokenLen(fbPtr + i);
+          if (!j) break;
+          tmpPtr = malloc((size_t)j + 1); /* Math symbol name */
+          if (!tmpPtr) outOfMemory("#8 (symbol name)");
+          tmpPtr[j] = 0; /* End of string */
+          memcpy(tmpPtr, fbPtr + i, (size_t)j);
+          fbPtr = fbPtr + i + j;
+          /* Create a new math symbol */
+          g_MathToken[g_mathTokens].tokenName = tmpPtr;
+          g_MathToken[g_mathTokens].length = j;
+          if (g_Statement[stmt].type == c_) {
+            g_MathToken[g_mathTokens].tokenType = (char)con_;
+          } else {
+            g_MathToken[g_mathTokens].tokenType = (char)var_;
+          }
+          g_MathToken[g_mathTokens].active = 0;
+          g_MathToken[g_mathTokens].scope = 0; /* Unknown for now */
+          g_MathToken[g_mathTokens].tmp = 0; /* Not used for now */
+          g_MathToken[g_mathTokens].statement = stmt;
+          g_MathToken[g_mathTokens].endStatement = g_statements; /* Unknown for now */
+                /* (Assign to 'g_statements' in case it's active until the end) */
+          g_mathTokens++;
+
+        }
+
+        /* Create the symbol list for this statement */
+        j = g_mathTokens - oldG_mathTokens; /* Number of tokens in this statement */
+        nmbrTmpPtr = poolFixedMalloc((j + 1) * (long)(sizeof(nmbrString)));
+        nmbrTmpPtr[j] = -1;
+        for (i = 0; i < j; i++) {
+          nmbrTmpPtr[i] = oldG_mathTokens + i;
+        }
+        g_Statement[stmt].mathString = nmbrTmpPtr;
+        g_Statement[stmt].mathStringLen = j;
+        if (!j) {
+          sourceError(fbPtr, 2, stmt,
+           "At least one math symbol should be declared.");
+        }
+    } /* end switch (g_Statement[stmt].type) */
+  } /* next stmt */
+
+/*E*/if(db5)print2("%ld math symbols were declared.\n",g_mathTokens);
+  /* Reallocate from potential to actual to reduce memory space */
+  /* Add 100 to allow for initial Proof Assistant use, and up to 100
+     errors in undeclared token references */
+  g_MAX_MATHTOKENS = g_mathTokens + 100;
+  g_MathToken = realloc(g_MathToken, (size_t)g_MAX_MATHTOKENS *
+      sizeof(struct mathToken_struct));
+  if (!g_MathToken) outOfMemory("#10 (g_MathToken)");
+
+  /* Create a special "$|$" boundary token to separate real and dummy ones */
+  g_MathToken[g_mathTokens].tokenName = "";
+  let(&g_MathToken[g_mathTokens].tokenName, "$|$");
+  g_MathToken[g_mathTokens].length = 2; /* Never used */
+  g_MathToken[g_mathTokens].tokenType = (char)con_;
+  g_MathToken[g_mathTokens].active = 0; /* Never used */
+  g_MathToken[g_mathTokens].scope = 0; /* Never used */
+  g_MathToken[g_mathTokens].tmp = 0; /* Never used */
+  g_MathToken[g_mathTokens].statement = 0; /* Never used */
+  g_MathToken[g_mathTokens].endStatement = g_statements; /* Never used */
+
+
+  /* Sort the math symbols for later lookup */
+  g_mathKey = malloc((size_t)g_mathTokens * sizeof(long));
+  if (!g_mathKey) outOfMemory("#11 (g_mathKey)");
+  for (i = 0; i < g_mathTokens; i++) {
+    g_mathKey[i] = i;
+  }
+  qsort(g_mathKey, (size_t)g_mathTokens, sizeof(long), mathSortCmp);
+/*E*/if(db5){print2("The first (up to 5) sorted math tokens are:\n");
+/*E*/  for (i=0; i<5; i++) {
+/*E*/    if (i >= g_mathTokens) break;
+/*E*/    print2("%s ",g_MathToken[g_mathKey[i]].tokenName);
+/*E*/  } print2("\n");}
+
+
+  /* Check for labels with the same name as math tokens */
+  /* (This section implements the Metamath spec change proposed by O'Cat that
+     lets labels and math tokens occupy the same namespace and thus forbids
+     them from having common names.) */
+  /* For maximum speed, we scan M math tokens and look each up in the list
+     of L labels.  The we have M * log L comparisons, which is optimal when
+     (as in most cases) M << L. */
+  for (i = 0; i < g_mathTokens; i++) {
+    /* See if the math token is in the list of labels */
+    voidPtr = (void *)bsearch(g_MathToken[i].tokenName, g_labelKeyBase,
+        (size_t)g_numLabelKeys, sizeof(long), labelSrchCmp);
+    if (voidPtr) { /* A label matching the token was found */
+      stmt = (*(long *)voidPtr); /* Statement number */
+      fbPtr = g_Statement[stmt].labelSectionPtr;
+      k = whiteSpaceLen(fbPtr);
+      j = tokenLen(fbPtr + k);
+      /* Note that the line and file are only assigned when requested,
+         for speedup. */
+      assignStmtFileAndLineNum(stmt);
+      assignStmtFileAndLineNum(g_MathToken[i].statement);
+      sourceError(fbPtr + k, j, stmt, cat(
+         "This label has the same name as the math token declared on line ",
+         str((double)(g_Statement[g_MathToken[i].statement].lineNum)),
+         " of file \"",
+         g_Statement[g_MathToken[i].statement].fileName,
+         "\".", NULL));
+    }
+  }
+}
+
+
+/* This functions parses statement contents, except for proofs */
+void parseStatements(void) {
+  long stmt;
+  char type;
+  long i, j, k, m, n, p;
+  char *fbPtr;
+  long mathStringLen;
+  long tokenNum;
+  long lowerKey, upperKey;
+  long symbolLen, origSymbolLen, mathSectionLen, g_mathKeyNum;
+  void *g_mathKeyPtr; /* bsearch returned value */
+  int maxScope;
+  long reqHyps, optHyps, reqVars, optVars;
+  flag reqFlag;
+  int undeclErrorCount = 0;
+  vstring_def(tmpStr);
+
+  nmbrString *nmbrTmpPtr;
+
+  long *mathTokenSameAs; /* Flag that symbol is unique (for speed up) */
+  long *reverseMathKey; /* Map from g_mathTokens to g_mathKey */
+
+  long *labelTokenSameAs; /* Flag that label is unique (for speed up) */
+  long *reverseLabelKey; /* Map from statement # to label key */
+  flag *labelActiveFlag; /* Flag that label is active */
+
+  struct activeConstStack_struct {
+    long tokenNum;
+    int scope;
+  };
+  struct activeConstStack_struct *activeConstStack; /* Stack of active consts */
+  long activeConstStackPtr = 0;
+
+  struct activeVarStack_struct {
+    long tokenNum;
+    int scope;
+    char tmpFlag; /* Used by hypothesis variable scan; must be 0 otherwise */
+  };
+  struct activeVarStack_struct *activeVarStack; /* Stack of active variables */
+  nmbrString *wrkVarPtr1;
+  nmbrString *wrkVarPtr2;
+  long activeVarStackPtr = 0;
+
+  struct activeEHypStack_struct { /* Stack of $e hypotheses */
+    long statemNum;
+    nmbrString *varList; /* List of variables in the hypothesis */
+    int scope;
+  };
+  struct activeEHypStack_struct *activeEHypStack;
+  long activeEHypStackPtr = 0;
+  struct activeFHypStack_struct { /* Stack of $f hypotheses */
+    long statemNum;
+    nmbrString *varList; /* List of variables in the hypothesis */
+    int scope;
+  };
+  struct activeFHypStack_struct *activeFHypStack;
+  long activeFHypStackPtr = 0;
+  nmbrString *wrkHypPtr1;
+  nmbrString *wrkHypPtr2;
+  nmbrString *wrkHypPtr3;
+  long activeHypStackSize = 30; /* Starting value; could be as large as
+                                   g_statements. */
+
+
+  struct activeDisjHypStack_struct { /* Stack of disjoint variables in $d's */
+    long tokenNumA; /* First variable in disjoint pair */
+    long tokenNumB; /* Second variable in disjoint pair */
+    long statemNum; /* Statement it occurred in */
+    int scope;
+  };
+  struct activeDisjHypStack_struct *activeDisjHypStack;
+  nmbrString *wrkDisjHPtr1A;
+  nmbrString *wrkDisjHPtr1B;
+  nmbrString *wrkDisjHPtr1Stmt;
+  nmbrString *wrkDisjHPtr2A;
+  nmbrString *wrkDisjHPtr2B;
+  nmbrString *wrkDisjHPtr2Stmt;
+  long activeDisjHypStackPtr = 0;
+  long activeDisjHypStackSize = 30; /* Starting value; could be as large as
+                                        about g_mathTokens^2/2 */
+
+  /* Temporary working space */
+  long wrkLen;
+  nmbrString *wrkNmbrPtr;
+  char *wrkStrPtr;
+
+  long maxSymbolLen; /* Longest math symbol (for speedup) */
+  flag *symbolLenExists; /* A symbol with this length exists (for speedup) */
+
+  long beginScopeStmtNum = 0;
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  mathStringLen = 0;
+  tokenNum = 0;
+
+  /* Initialize flags for g_mathKey array that identify math symbols as
+     unique (when 0) or, if not unique, the flag is a number identifying a group
+     of identical names */
+  mathTokenSameAs = malloc((size_t)g_mathTokens * sizeof(long));
+  if (!mathTokenSameAs) outOfMemory("#12 (mathTokenSameAs)");
+  reverseMathKey = malloc((size_t)g_mathTokens * sizeof(long));
+  if (!reverseMathKey) outOfMemory("#13 (reverseMathKey)");
+  for (i = 0; i < g_mathTokens; i++) {
+    mathTokenSameAs[i] = 0; /* 0 means unique */
+    reverseMathKey[g_mathKey[i]] = i; /* Initialize reverse map to g_mathKey */
+  }
+  for (i = 1; i < g_mathTokens; i++) {
+    if (!strcmp(g_MathToken[g_mathKey[i]].tokenName,
+        g_MathToken[g_mathKey[i - 1]].tokenName)) {
+      if (!mathTokenSameAs[i - 1]) mathTokenSameAs[i - 1] = i;
+      mathTokenSameAs[i] = mathTokenSameAs[i - 1];
+    }
+  }
+
+  /* Initialize flags for g_labelKey array that identify labels as
+     unique (when 0) or, if not unique, the flag is a number identifying a group
+     of identical names */
+  labelTokenSameAs = malloc(((size_t)g_statements + 1) * sizeof(long));
+  if (!labelTokenSameAs) outOfMemory("#112 (labelTokenSameAs)");
+  reverseLabelKey = malloc(((size_t)g_statements + 1) * sizeof(long));
+  if (!reverseLabelKey) outOfMemory("#113 (reverseLabelKey)");
+  labelActiveFlag = malloc(((size_t)g_statements + 1) * sizeof(flag));
+  if (!labelActiveFlag) outOfMemory("#114 (labelActiveFlag)");
+  for (i = 1; i <= g_statements; i++) {
+    labelTokenSameAs[i] = 0; /* Initialize:  0 = unique */
+    reverseLabelKey[g_labelKey[i]] = i; /* Initialize reverse map to g_labelKey */
+    labelActiveFlag[i] = 0; /* Initialize */
+  }
+  for (i = 2; i <= g_statements; i++) {
+    if (!strcmp(g_Statement[g_labelKey[i]].labelName,
+        g_Statement[g_labelKey[i - 1]].labelName)) {
+      if (!labelTokenSameAs[i - 1]) labelTokenSameAs[i - 1] = i;
+      labelTokenSameAs[i] = labelTokenSameAs[i - 1];
+    }
+  }
+
+  /* Initialize variable and hypothesis stacks */
+
+  /* Allocate g_MAX_MATHTOKENS and not just g_mathTokens of them so that
+     they can accommodate any extra non-declared tokens (which get
+     declared as part of error handling, where the g_MAX_MATHTOKENS
+     limit is checked) */
+  activeConstStack = malloc((size_t)g_MAX_MATHTOKENS
+      * sizeof(struct activeConstStack_struct));
+  activeVarStack = malloc((size_t)g_MAX_MATHTOKENS
+      * sizeof(struct activeVarStack_struct));
+  wrkVarPtr1 = malloc((size_t)g_MAX_MATHTOKENS * sizeof(nmbrString));
+  wrkVarPtr2 = malloc((size_t)g_MAX_MATHTOKENS * sizeof(nmbrString));
+  if (!activeConstStack || !activeVarStack || !wrkVarPtr1 || !wrkVarPtr2)
+      outOfMemory("#14 (activeVarStack)");
+
+  activeEHypStack = malloc((size_t)activeHypStackSize
+      * sizeof(struct activeEHypStack_struct));
+  activeFHypStack = malloc((size_t)activeHypStackSize
+      * sizeof(struct activeFHypStack_struct));
+  wrkHypPtr1 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
+  wrkHypPtr2 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
+  wrkHypPtr3 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
+  if (!activeEHypStack || !activeFHypStack || !wrkHypPtr1 || !wrkHypPtr2 ||
+      !wrkHypPtr3)
+      outOfMemory("#15 (activeHypStack)");
+
+  activeDisjHypStack = malloc((size_t)activeDisjHypStackSize *
+      sizeof(struct activeDisjHypStack_struct));
+  wrkDisjHPtr1A = malloc((size_t)activeDisjHypStackSize * sizeof(nmbrString));
+  wrkDisjHPtr1B = malloc((size_t)activeDisjHypStackSize * sizeof(nmbrString));
+  wrkDisjHPtr1Stmt = malloc((size_t)activeDisjHypStackSize
+      * sizeof(nmbrString));
+  wrkDisjHPtr2A = malloc((size_t)activeDisjHypStackSize * sizeof(nmbrString));
+  wrkDisjHPtr2B = malloc((size_t)activeDisjHypStackSize * sizeof(nmbrString));
+  wrkDisjHPtr2Stmt = malloc((size_t)activeDisjHypStackSize
+      * sizeof(nmbrString));
+  if (!activeDisjHypStack
+      || !wrkDisjHPtr1A || !wrkDisjHPtr1B || !wrkDisjHPtr1Stmt
+      || !wrkDisjHPtr2A || !wrkDisjHPtr2B || !wrkDisjHPtr2Stmt)
+      outOfMemory("#27 (activeDisjHypStack)");
+
+  /* Initialize temporary working space for parsing tokens */
+  wrkLen = 1;
+  wrkNmbrPtr = malloc((size_t)wrkLen * sizeof(nmbrString));
+  if (!wrkNmbrPtr) outOfMemory("#22 (wrkNmbrPtr)");
+  wrkStrPtr = malloc((size_t)wrkLen + 1);
+  if (!wrkStrPtr) outOfMemory("#23 (wrkStrPtr)");
+
+  /* Find declared math symbol lengths (used to speed up parsing) */
+  maxSymbolLen = 0;
+  for (i = 0; i < g_mathTokens; i++) {
+    if (g_MathToken[i].length > maxSymbolLen) {
+      maxSymbolLen = g_MathToken[i].length;
+    }
+  }
+  symbolLenExists = malloc(((size_t)maxSymbolLen + 1) * sizeof(flag));
+  if (!symbolLenExists) outOfMemory("#25 (symbolLenExists)");
+  for (i = 0; i <= maxSymbolLen; i++) {
+    symbolLenExists[i] = 0;
+  }
+  for (i = 0; i < g_mathTokens; i++) {
+    symbolLenExists[g_MathToken[i].length] = 1;
+  }
+
+
+  g_currentScope = 0;
+  beginScopeStmtNum = 0;
+
+  /* Scan all statements.  Fill in statement structure and look for errors. */
+  for (stmt = 1; stmt <= g_statements; stmt++) {
+
+#ifdef VAXC
+    /* This line fixes an obscure bug with the VAXC compiler.  If it is taken
+       out, the variable 'stmt' does not get referenced properly when used as
+       an array index.  May be due to some boundary condition in optimization?
+       The assembly code is significantly different with this statement
+       removed. */
+    stmt = stmt;  /* Work around VAXC bug */
+#endif
+
+    g_Statement[stmt].beginScopeStatementNum = beginScopeStmtNum;
+    /* endScopeStatementNum is always 0 except in ${ statements */
+    g_Statement[stmt].endScopeStatementNum = 0;
+    g_Statement[stmt].scope = g_currentScope;
+    type = g_Statement[stmt].type;
+    /******* Determine scope, stack active variables, process math strings ****/
+
+    switch (type) {
+      case lb_:
+        g_currentScope++;
+        if (g_currentScope > 32000) outOfMemory("#16 (more than 32000 \"${\"s)");
+            /* Not really an out-of-memory situation, but use the error msg. */
+        /* Note that g_Statement[stmt].beginScopeStatementNum for this ${
+           points to the previous ${ (or 0 if in outermost scope) */
+        beginScopeStmtNum = stmt;
+        /* Note that g_Statement[stmt].endScopeStatementNum for this ${
+           will be assigned in the rb_ case below. */
+        break;
+      case rb_:
+        /* Remove all variables and hypotheses in current scope from stack */
+
+        while (activeConstStackPtr) {
+          if (activeConstStack[activeConstStackPtr - 1].scope < g_currentScope)
+              break;
+          activeConstStackPtr--;
+          g_MathToken[activeConstStack[activeConstStackPtr].tokenNum].active = 0;
+          g_MathToken[activeConstStack[activeConstStackPtr].tokenNum
+              ].endStatement = stmt;
+        }
+
+        while (activeVarStackPtr) {
+          if (activeVarStack[activeVarStackPtr - 1].scope < g_currentScope) break;
+          activeVarStackPtr--;
+          g_MathToken[activeVarStack[activeVarStackPtr].tokenNum].active = 0;
+          g_MathToken[activeVarStack[activeVarStackPtr].tokenNum].endStatement
+              = stmt;
+        }
+
+        while (activeEHypStackPtr) {
+          if (activeEHypStack[activeEHypStackPtr - 1].scope < g_currentScope)
+              break;
+          activeEHypStackPtr--;
+          labelActiveFlag[activeEHypStack[activeEHypStackPtr].statemNum] = 0;
+                                                   /* Make the label inactive */
+          free(activeEHypStack[activeEHypStackPtr].varList);
+        }
+        while (activeFHypStackPtr) {
+          if (activeFHypStack[activeFHypStackPtr - 1].scope < g_currentScope)
+              break;
+          activeFHypStackPtr--;
+          labelActiveFlag[activeFHypStack[activeFHypStackPtr].statemNum] = 0;
+                                                   /* Make the label inactive */
+          free(activeFHypStack[activeFHypStackPtr].varList);
+        }
+        while (activeDisjHypStackPtr) {
+          if (activeDisjHypStack[activeDisjHypStackPtr - 1].scope
+              < g_currentScope) break;
+          activeDisjHypStackPtr--;
+        }
+        g_currentScope--;
+        if (g_currentScope < 0) {
+          sourceError(g_Statement[stmt].labelSectionPtr +
+              g_Statement[stmt].labelSectionLen, 2, stmt,
+              "Too many \"$}\"s at this point.");
+        }
+
+        if (beginScopeStmtNum > 0) { /* We're not in outermost scope
+                  (precaution if there were too many $}'s) */
+          if (g_Statement[beginScopeStmtNum].type != lb_) bug(1773);
+          /* Populate the previous ${ with a pointer to this $} */
+          g_Statement[beginScopeStmtNum].endScopeStatementNum = stmt;
+          /* Update beginScopeStmtNum with start of outer scope */
+          beginScopeStmtNum
+              = g_Statement[beginScopeStmtNum].beginScopeStatementNum;
+        }
+
+        break;
+      case c_:
+      case v_:
+        /* Scan all symbols declared (they have already been parsed) and
+           flag them as active, add to stack, and check for errors */
+        if (type == c_) {
+          if (g_currentScope > 0) {
+            sourceError(g_Statement[stmt].labelSectionPtr +
+                g_Statement[stmt].labelSectionLen, 2, stmt,
+        "A \"$c\" constant declaration may occur in the outermost scope only.");
+          }
+        }
+
+
+        i = 0; /* Symbol position in mathString */
+        nmbrTmpPtr = g_Statement[stmt].mathString;
+        while (1) {
+          tokenNum = nmbrTmpPtr[i];
+          if (tokenNum == -1) break; /* Done scanning symbols in $v or $c */
+          if (mathTokenSameAs[reverseMathKey[tokenNum]]) {
+            /* The variable name is not unique.  Find out if there's a
+               conflict with the others. */
+            lowerKey = reverseMathKey[tokenNum];
+            upperKey = lowerKey;
+            j = mathTokenSameAs[lowerKey];
+            while (lowerKey) {
+              if (j != mathTokenSameAs[lowerKey - 1]) break;
+              lowerKey--;
+            }
+            while (upperKey < g_mathTokens - 1) {
+              if (j != mathTokenSameAs[upperKey + 1]) break;
+              upperKey++;
+            }
+            for (j = lowerKey; j <= upperKey; j++) {
+              if (g_MathToken[g_mathKey[j]].active) {
+                /* Detect conflicting active vars declared
+                   in multiple scopes */
+                if (g_MathToken[g_mathKey[j]].scope <= g_currentScope) {
+                  mathTokenError(i, nmbrTmpPtr, stmt,
+                      "This symbol has already been declared in this scope.");
+                }
+              }
+            }
+
+
+            /* Make sure that no constant has the same name
+               as a variable or vice-versa */
+            k = 0; /* Flag for $c */
+            m = 0; /* Flag for $v */
+            for (j = lowerKey; j <= upperKey; j++) {
+              if (g_MathToken[g_mathKey[j]].tokenType == (char)con_) k = 1;
+              if (g_MathToken[g_mathKey[j]].tokenType == (char)var_) m = 1;
+            }
+            if ((k == 1 && g_MathToken[tokenNum].tokenType == (char)var_) ||
+                (m == 1 && g_MathToken[tokenNum].tokenType == (char)con_)) {
+               mathTokenError(i, nmbrTmpPtr, stmt,
+                   "A symbol may not be both a constant and a variable.");
+            }
+          }
+
+          /* Flag the token as active */
+          g_MathToken[tokenNum].active = 1;
+          g_MathToken[tokenNum].scope = g_currentScope;
+
+          if (type == v_) {
+
+            /* Identify this stack position in the g_MathToken array, for use
+               by the hypothesis variable scan below */
+            g_MathToken[tokenNum].tmp = activeVarStackPtr;
+
+            /* Add the symbol to the stack */
+            activeVarStack[activeVarStackPtr].tokenNum = tokenNum;
+            activeVarStack[activeVarStackPtr].scope = g_currentScope;
+            activeVarStack[activeVarStackPtr].tmpFlag = 0;
+            activeVarStackPtr++;
+          } else {
+
+            /* Add the symbol to the stack */
+            activeConstStack[activeConstStackPtr].tokenNum = tokenNum;
+            activeConstStack[activeConstStackPtr].scope = g_currentScope;
+            activeConstStackPtr++;
+          }
+
+          i++;
+        }
+        break;
+      case d_:
+      case f_:
+      case e_:
+      case a_:
+      case p_:
+        /* Make sure we have enough working space */
+        mathSectionLen = g_Statement[stmt].mathSectionLen;
+        if (wrkLen < mathSectionLen) {
+          free(wrkNmbrPtr);
+          free(wrkStrPtr);
+          wrkLen = mathSectionLen + 100;
+          wrkNmbrPtr = malloc((size_t)wrkLen * sizeof(nmbrString));
+          if (!wrkNmbrPtr) outOfMemory("#20 (wrkNmbrPtr)");
+          wrkStrPtr = malloc((size_t)wrkLen + 1);
+          if (!wrkStrPtr) outOfMemory("#21 (wrkStrPtr)");
+        }
+
+        /* Scan the math section for tokens */
+        mathStringLen = 0;
+        fbPtr = g_Statement[stmt].mathSectionPtr;
+        while (1) {
+          fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+          origSymbolLen = tokenLen(fbPtr);
+          if (!origSymbolLen) break; /* Done scanning source line */
+
+          /* Scan for largest matching token from the left */
+         nextAdjToken:
+          /* don't allow missing white space */
+          symbolLen = origSymbolLen;
+
+          memcpy(wrkStrPtr, fbPtr, (size_t)symbolLen);
+
+          /* ???Speed-up is possible by rewriting this now unnecessary code */
+          for (; symbolLen > 0; symbolLen = 0) {
+
+            /* symbolLenExists means a symbol of this length was declared */
+            if (!symbolLenExists[symbolLen]) continue;
+            wrkStrPtr[symbolLen] = 0; /* Define end of trial token to look up */
+            g_mathKeyPtr = (void *)bsearch(wrkStrPtr, g_mathKey, (size_t)g_mathTokens,
+                sizeof(long), mathSrchCmp);
+            if (!g_mathKeyPtr) continue; /* Trial token was not declared */
+            g_mathKeyNum = (long *)g_mathKeyPtr - g_mathKey; /* Pointer arithmetic! */
+            if (mathTokenSameAs[g_mathKeyNum]) { /* Multiply-declared symbol */
+              lowerKey = g_mathKeyNum;
+              upperKey = lowerKey;
+              j = mathTokenSameAs[lowerKey];
+              while (lowerKey) {
+                if (j != mathTokenSameAs[lowerKey - 1]) break;
+                lowerKey--;
+              }
+              while (upperKey < g_mathTokens - 1) {
+                if (j != mathTokenSameAs[upperKey + 1]) break;
+                upperKey++;
+              }
+              /* Find the active symbol with the most recent declaration */
+              /* (Note:  Here, 'active' means it's on the stack, not the
+                 official def.) */
+              maxScope = -1;
+              for (i = lowerKey; i <= upperKey; i++) {
+                j = g_mathKey[i];
+                if (g_MathToken[j].active) {
+                  if (g_MathToken[j].scope > maxScope) {
+                    tokenNum = j;
+                    maxScope = g_MathToken[j].scope;
+                    if (maxScope == g_currentScope) break; /* Speedup */
+                  }
+                }
+              }
+              if (maxScope == -1) {
+                tokenNum = g_mathKey[g_mathKeyNum]; /* Pick an arbitrary one */
+                sourceError(fbPtr, symbolLen, stmt,
+       "This math symbol is not active (i.e. was not declared in this scope).");
+                /*??? (This is done in 3 places. Make it a fn call & clean up?*/
+                /* Prevent stray pointers later */
+                g_MathToken[tokenNum].tmp = 0; /* Loc in active variable stack */
+                if (!activeVarStackPtr) { /* Make a fictitious entry */
+                  activeVarStack[activeVarStackPtr].tokenNum = tokenNum;
+                  activeVarStack[activeVarStackPtr].scope = g_currentScope;
+                  activeVarStack[activeVarStackPtr].tmpFlag = 0;
+                  activeVarStackPtr++;
+                }
+              }
+            } else { /* The symbol was declared only once. */
+              tokenNum = *((long *)g_mathKeyPtr);
+                  /* Same as: tokenNum = g_mathKey[g_mathKeyNum]; but faster */
+              if (!g_MathToken[tokenNum].active) {
+                sourceError(fbPtr, symbolLen, stmt,
+       "This math symbol is not active (i.e. was not declared in this scope).");
+                /* Prevent stray pointers later */
+                g_MathToken[tokenNum].tmp = 0; /* Loc in active variable stack */
+                if (!activeVarStackPtr) { /* Make a fictitious entry */
+                  activeVarStack[activeVarStackPtr].tokenNum = tokenNum;
+                  activeVarStack[activeVarStackPtr].scope = g_currentScope;
+                  activeVarStack[activeVarStackPtr].tmpFlag = 0;
+                  activeVarStackPtr++;
+                }
+              }
+            } /* End if multiply-defined symbol */
+            break; /* The symbol was found, so we are done */
+          } /* Next symbolLen */
+          if (symbolLen == 0) { /* Symbol was not found */
+            symbolLen = tokenLen(fbPtr);
+            sourceError(fbPtr, symbolLen, stmt,
+      "This math symbol was not declared (with a \"$c\" or \"$v\" statement).");
+            /* Call the symbol a dummy token of type variable so that spurious
+               errors (constants in $d's) won't be flagged also.  Prevent
+               stray pointer to active variable stack. */
+            undeclErrorCount++;
+            tokenNum = g_mathTokens + undeclErrorCount;
+            if (tokenNum >= g_MAX_MATHTOKENS) {
+              /* There are current 100 places for bad tokens */
+              print2(
+"?Error: The temporary space for holding bad tokens has run out, because\n");
+              print2(
+"there are too many errors.  Therefore we will force an \"out of memory\"\n");
+              print2("program abort:\n");
+              outOfMemory("#33 (too many errors)");
+            }
+            g_MathToken[tokenNum].tokenName = "";
+            let(&g_MathToken[tokenNum].tokenName, left(fbPtr,symbolLen));
+            g_MathToken[tokenNum].length = symbolLen;
+            g_MathToken[tokenNum].tokenType = (char)var_;
+            /* Prevent stray pointers later */
+            g_MathToken[tokenNum].tmp = 0; /* Location in active variable stack */
+            if (!activeVarStackPtr) { /* Make a fictitious entry */
+              activeVarStack[activeVarStackPtr].tokenNum = tokenNum;
+              activeVarStack[activeVarStackPtr].scope = g_currentScope;
+              activeVarStack[activeVarStackPtr].tmpFlag = 0;
+              activeVarStackPtr++;
+            }
+          }
+
+          if (type == d_) {
+            if (g_MathToken[tokenNum].tokenType == (char)con_) {
+              sourceError(fbPtr, symbolLen, stmt,
+                  "Constant symbols are not allowed in a \"$d\" statement.");
+            }
+          } else {
+            if (mathStringLen == 0) {
+              if (g_MathToken[tokenNum].tokenType != (char)con_) {
+                sourceError(fbPtr, symbolLen, stmt, cat(
+                    "The first symbol must be a constant in a \"$",
+                    chr(type), "\" statement.", NULL));
+              }
+            } else {
+              if (type == f_) {
+                if (mathStringLen == 1) {
+                  if (g_MathToken[tokenNum].tokenType == (char)con_) {
+                    sourceError(fbPtr, symbolLen, stmt,
+                "The second symbol must be a variable in a \"$f\" statement.");
+                  }
+                } else {
+                  if (mathStringLen == 2) {
+                    sourceError(fbPtr, symbolLen, stmt,
+               "There cannot be more than two symbols in a \"$f\" statement.");
+                  }
+                }
+              }
+            }
+          }
+
+          /* Add symbol to mathString */
+          wrkNmbrPtr[mathStringLen] = tokenNum;
+          mathStringLen++;
+          fbPtr = fbPtr + symbolLen; /* Move on to next symbol */
+
+          if (symbolLen < origSymbolLen) {
+            /* This symbol is not separated from next by white space */
+            /* Speed-up: don't call tokenLen again; just jump past it */
+            origSymbolLen = origSymbolLen - symbolLen;
+            goto nextAdjToken; /* (Instead of continue) */
+          }
+        } /* End while */
+
+        if (type == d_) {
+          if (mathStringLen < 2) {
+            sourceError(fbPtr, 2, stmt,
+                "A \"$d\" statement requires at least two variable symbols.");
+          }
+        } else {
+          if (!mathStringLen) {
+            sourceError(fbPtr, 2, stmt,
+                "This statement type requires at least one math symbol.");
+          } else {
+            if (type == f_ && mathStringLen < 2) {
+              sourceError(fbPtr, 2, stmt,
+                  "A \"$f\" statement requires two math symbols.");
+            }
+          }
+        }
+
+
+        /* Assign mathString to statement array */
+        nmbrTmpPtr = poolFixedMalloc(
+            (mathStringLen + 1) * (long)(sizeof(nmbrString)));
+        for (i = 0; i < mathStringLen; i++) {
+          nmbrTmpPtr[i] = wrkNmbrPtr[i];
+        }
+        nmbrTmpPtr[mathStringLen] = -1;
+        g_Statement[stmt].mathString = nmbrTmpPtr;
+        g_Statement[stmt].mathStringLen = mathStringLen;
+/*E*/if(db5){if(stmt<5)print2("Statement %ld mathString: %s.\n",stmt,
+/*E*/  nmbrCvtMToVString(nmbrTmpPtr)); if(stmt==5)print2("(etc.)\n");}
+
+        break;  /* Switch case break */
+      default:
+        bug(1707);
+
+    } /* End switch */
+
+    /****** Process hypothesis and variable stacks *******/
+    /* (The switch section above does not depend on what is done in this
+       section, although this section assumes the above section has been done.
+       Variables valid only in this pass of the above section are so
+       indicated.) */
+
+    switch (type) {
+      case f_:
+      case e_:
+      case a_:
+      case p_:
+        /* Flag the label as active */
+        labelActiveFlag[stmt] = 1;
+
+    } /* End switch */
+
+
+    switch (type) {
+      case d_:
+
+        nmbrTmpPtr = g_Statement[stmt].mathString;
+        /* Stack all possible pairs of disjoint variables */
+        for (i = 0; i < mathStringLen - 1; i++) { /* mathStringLen is from the
+             above switch section; it is valid only in this pass of the above
+             section. */
+          p = nmbrTmpPtr[i];
+          for (j = i + 1; j < mathStringLen; j++) {
+            n = nmbrTmpPtr[j];
+            /* Get the disjoint variable pair m and n, sorted by tokenNum */
+            if (p < n) {
+              m = p;
+            } else {
+              if (p > n) {
+                /* Swap them */
+                m = n;
+                n = p;
+              } else {
+                mathTokenError(j, nmbrTmpPtr, stmt,
+                    "All variables in a \"$d\" statement must be unique.");
+                break;
+              }
+            }
+            /* See if this pair of disjoint variables is already on the stack;
+               if so, don't add it again */
+            for (k = 0; k < activeDisjHypStackPtr; k++) {
+              if (m == activeDisjHypStack[k].tokenNumA)
+                if (n == activeDisjHypStack[k].tokenNumB)
+                  break; /* It matches */
+            }
+            if (k == activeDisjHypStackPtr) {
+              /* It wasn't already on the stack, so add it. */
+              /* Increase stack size if necessary */
+              if (activeDisjHypStackPtr >= activeDisjHypStackSize) {
+                free(wrkDisjHPtr1A);
+                free(wrkDisjHPtr1B);
+                free(wrkDisjHPtr1Stmt);
+                free(wrkDisjHPtr2A);
+                free(wrkDisjHPtr2B);
+                free(wrkDisjHPtr2Stmt);
+                activeDisjHypStackSize = activeDisjHypStackSize + 100;
+                activeDisjHypStack = realloc(activeDisjHypStack,
+                    (size_t)activeDisjHypStackSize
+                    * sizeof(struct activeDisjHypStack_struct));
+                wrkDisjHPtr1A = malloc((size_t)activeDisjHypStackSize
+                    * sizeof(nmbrString));
+                wrkDisjHPtr1B = malloc((size_t)activeDisjHypStackSize
+                    * sizeof(nmbrString));
+                wrkDisjHPtr1Stmt = malloc((size_t)activeDisjHypStackSize
+                    * sizeof(nmbrString));
+                wrkDisjHPtr2A = malloc((size_t)activeDisjHypStackSize
+                    * sizeof(nmbrString));
+                wrkDisjHPtr2B = malloc((size_t)activeDisjHypStackSize
+                    * sizeof(nmbrString));
+                wrkDisjHPtr2Stmt = malloc((size_t)activeDisjHypStackSize
+                    * sizeof(nmbrString));
+                if (!activeDisjHypStack
+                    || !wrkDisjHPtr1A || !wrkDisjHPtr1B || !wrkDisjHPtr1Stmt
+                    || !wrkDisjHPtr2A || !wrkDisjHPtr2B || !wrkDisjHPtr2Stmt)
+                    outOfMemory("#28 (activeDisjHypStack)");
+              }
+              activeDisjHypStack[activeDisjHypStackPtr].tokenNumA = m;
+              activeDisjHypStack[activeDisjHypStackPtr].tokenNumB = n;
+              activeDisjHypStack[activeDisjHypStackPtr].scope = g_currentScope;
+              activeDisjHypStack[activeDisjHypStackPtr].statemNum = stmt;
+
+              activeDisjHypStackPtr++;
+            }
+
+          } /* Next j */
+        } /* Next i */
+
+        break; /* Switch case break */
+
+      case f_:
+      case e_:
+
+        /* Increase stack size if necessary */
+        /* For convenience, we will keep the size greater than the sum of
+           active $e and $f hypotheses, as this is the size needed for the
+           wrkHypPtr's, even though it wastes (temporary) memory for the
+           activeE and activeF structure arrays. */
+        if (activeEHypStackPtr + activeFHypStackPtr >= activeHypStackSize) {
+          free(wrkHypPtr1);
+          free(wrkHypPtr2);
+          free(wrkHypPtr3);
+          activeHypStackSize = activeHypStackSize + 100;
+          activeEHypStack = realloc(activeEHypStack, (size_t)activeHypStackSize
+              * sizeof(struct activeEHypStack_struct));
+          activeFHypStack = realloc(activeFHypStack, (size_t)activeHypStackSize
+              * sizeof(struct activeFHypStack_struct));
+          wrkHypPtr1 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
+          wrkHypPtr2 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
+          wrkHypPtr3 = malloc((size_t)activeHypStackSize * sizeof(nmbrString));
+          if (!activeEHypStack || !activeFHypStack || !wrkHypPtr1 ||
+              !wrkHypPtr2 || !wrkHypPtr3) outOfMemory("#32 (activeHypStack)");
+        }
+
+        /* Add the hypothesis to the stack */
+        if (type == e_) {
+          activeEHypStack[activeEHypStackPtr].statemNum = stmt;
+          activeEHypStack[activeEHypStackPtr].scope = g_currentScope;
+        } else {
+          activeFHypStack[activeFHypStackPtr].statemNum = stmt;
+          activeFHypStack[activeFHypStackPtr].scope = g_currentScope;
+        }
+
+        /* Create the list of variables used by this hypothesis */
+        reqVars = 0;
+        j = 0;
+        nmbrTmpPtr = g_Statement[stmt].mathString;
+        k = nmbrTmpPtr[j]; /* Math symbol number */
+        while (k != -1) {
+          if (g_MathToken[k].tokenType == (char)var_) {
+            if (!activeVarStack[g_MathToken[k].tmp].tmpFlag) {
+              /* Variable has not been already added to list */
+              wrkVarPtr1[reqVars] = k;
+              reqVars++;
+              activeVarStack[g_MathToken[k].tmp].tmpFlag = 1;
+            }
+          }
+          j++;
+          k = nmbrTmpPtr[j];
+        }
+        nmbrTmpPtr = malloc(((size_t)reqVars + 1) * sizeof(nmbrString));
+        if (!nmbrTmpPtr) outOfMemory("#32 (hypothesis variables)");
+        memcpy(nmbrTmpPtr, wrkVarPtr1, (size_t)reqVars * sizeof(nmbrString));
+        nmbrTmpPtr[reqVars] = -1;
+        /* Clear the variable flags for future re-use */
+        for (i = 0; i < reqVars; i++) {
+          activeVarStack[g_MathToken[nmbrTmpPtr[i]].tmp].tmpFlag = 0;
+        }
+
+        if (type == e_) {
+          activeEHypStack[activeEHypStackPtr].varList = nmbrTmpPtr;
+          activeEHypStackPtr++;
+        } else {
+          activeFHypStack[activeFHypStackPtr].varList = nmbrTmpPtr;
+          activeFHypStackPtr++;
+        }
+
+        break;  /* Switch case break */
+
+      case a_:
+      case p_:
+
+        /* Scan this statement for required variables */
+        reqVars = 0;
+        j = 0;
+        nmbrTmpPtr = g_Statement[stmt].mathString;
+        k = nmbrTmpPtr[j]; /* Math symbol number */
+        while (k != -1) {
+          if (g_MathToken[k].tokenType == (char)var_) {
+            if (!activeVarStack[g_MathToken[k].tmp].tmpFlag) {
+              /* Variable has not been already added to list */
+              wrkVarPtr1[reqVars] = k;
+              reqVars++;
+              activeVarStack[g_MathToken[k].tmp].tmpFlag = 2;
+                       /* 2 means it's an original variable in the assertion */
+                       /* (For error-checking) */
+            }
+          }
+          j++;
+          k = nmbrTmpPtr[j];
+        }
+
+        /* Scan $e stack for required variables and required hypotheses */
+        for (i = 0; i < activeEHypStackPtr; i++) {
+
+          /* Add $e hypotheses to required list */
+          wrkHypPtr1[i] = activeEHypStack[i].statemNum;
+
+          /* Add the $e's variables to required variable list */
+          nmbrTmpPtr = activeEHypStack[i].varList;
+          j = 0; /* Location in variable list */
+          k = nmbrTmpPtr[j]; /* Symbol number of variable */
+          while (k != -1) {
+            if (!activeVarStack[g_MathToken[k].tmp].tmpFlag) {
+              /* Variable has not been already added to list */
+              wrkVarPtr1[reqVars] = k;
+              reqVars++;
+            }
+            activeVarStack[g_MathToken[k].tmp].tmpFlag = 1;
+                            /* Could have been 0 or 2; 1 = in some hypothesis */
+            j++;
+            k = nmbrTmpPtr[j];
+          }
+        }
+
+        reqHyps = activeEHypStackPtr; /* The number of required hyp's so far */
+
+        /* We have finished determining required variables, so allocate the
+           permanent list for the statement array */
+        nmbrTmpPtr = poolFixedMalloc((reqVars + 1)
+            * (long)(sizeof(nmbrString)));
+        /* if (!nmbrTmpPtr) outOfMemory("#30 (reqVars)"); */
+                                              /* Not necessary w/ poolMalloc */
+        memcpy(nmbrTmpPtr, wrkVarPtr1, (size_t)reqVars * sizeof(nmbrString));
+        nmbrTmpPtr[reqVars] = -1;
+        g_Statement[stmt].reqVarList = nmbrTmpPtr;
+
+        /* Scan the list of $f hypotheses to find those that are required */
+        optHyps = 0;
+        for (i = 0; i < activeFHypStackPtr; i++) {
+          nmbrTmpPtr = activeFHypStack[i].varList; /* Variable list */
+          tokenNum = nmbrTmpPtr[0];
+          if (tokenNum == -1) {
+            /* Default if no variables (an error in current version): */
+            /* Add it to list of required hypotheses */
+            wrkHypPtr1[reqHyps] = activeFHypStack[i].statemNum;
+            reqHyps++;
+            continue;
+          } else {
+            reqFlag = activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag;
+          }
+          if (reqFlag) {
+            /* Add it to list of required hypotheses */
+            wrkHypPtr1[reqHyps] = activeFHypStack[i].statemNum;
+            reqHyps++;
+            reqFlag = 1;
+            activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag = 1;
+                                /* Could have been 2; 1 = in some hypothesis */
+          } else {
+            /* Add it to list of optional hypotheses */
+            wrkHypPtr2[optHyps] = activeFHypStack[i].statemNum;
+            optHyps++;
+          }
+
+          /* Scan the other variables in the $f hyp to check for conflicts. */
+          j = 1;
+          tokenNum = nmbrTmpPtr[1];
+          while (tokenNum != -1) {
+            if (activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag == 2) {
+              activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag = 1;
+                                /* 2 = in $p; 1 = in some hypothesis */
+            }
+            if (reqFlag != activeVarStack[g_MathToken[tokenNum].tmp].tmpFlag) {
+              k = activeFHypStack[i].statemNum;
+              m = nmbrElementIn(1, g_Statement[k].mathString, tokenNum);
+              n = nmbrTmpPtr[0];
+              if (reqFlag) {
+                mathTokenError(m - 1, g_Statement[k].mathString, k,
+                    cat("This variable does not occur in statement ",
+                    str((double)stmt)," (label \"",g_Statement[stmt].labelName,
+                    "\") or statement ", str((double)stmt),
+                    "'s \"$e\" hypotheses, whereas variable \"",
+                    g_MathToken[n].tokenName,
+                   "\" DOES occur.  A \"$f\" hypothesis may not contain such a",
+                    " mixture of variables.",NULL));
+              } else {
+                mathTokenError(m - 1, g_Statement[k].mathString, k,
+                    cat("This variable occurs in statement ",
+                    str((double)stmt)," (label \"",g_Statement[stmt].labelName,
+                    "\") or statement ", str((double)stmt),
+                    "'s \"$e\" hypotheses, whereas variable \"",
+                    g_MathToken[n].tokenName,
+               "\" does NOT occur.  A \"$f\" hypothesis may not contain such a",
+                    " mixture of variables.",NULL));
+              }
+              break;
+            } /* End if */
+            j++;
+            tokenNum = nmbrTmpPtr[j];
+          } /* End while */
+
+        } /* Next i */
+
+
+        /* Error check:  make sure that all variables in the original statement
+           appeared in some hypothesis */
+        j = 0;
+        nmbrTmpPtr = g_Statement[stmt].mathString;
+        k = nmbrTmpPtr[j]; /* Math symbol number */
+        while (k != -1) {
+          if (g_MathToken[k].tokenType == (char)var_) {
+            if (activeVarStack[g_MathToken[k].tmp].tmpFlag == 2) {
+              /* The variable did not appear in any hypothesis */
+              mathTokenError(j, g_Statement[stmt].mathString, stmt,
+                    cat("This variable does not occur in any active ",
+                    "\"$e\" or \"$f\" hypothesis.  All variables in \"$a\" and",
+                    " \"$p\" statements must appear in at least one such",
+                    " hypothesis.",NULL));
+              activeVarStack[g_MathToken[k].tmp].tmpFlag = 1; /* One msg per var*/
+            }
+          }
+          j++;
+          k = nmbrTmpPtr[j];
+        }
+
+
+        /* We have finished determining required $e & $f hyps, so allocate the
+           permanent list for the statement array */
+        /* First, sort the required hypotheses by statement number order
+           into wrkHypPtr3 */
+        i = 0; /* Start of $e's in wrkHypPtr1 */
+        j = activeEHypStackPtr; /* Start of $f's in wrkHypPtr1 */
+        for (k = 0; k < reqHyps; k++) {
+          if (i >= activeEHypStackPtr) {
+            wrkHypPtr3[k] = wrkHypPtr1[j];
+            j++;
+            continue;
+          }
+          if (j >= reqHyps) {
+            wrkHypPtr3[k] = wrkHypPtr1[i];
+            i++;
+            continue;
+          }
+          if (wrkHypPtr1[i] > wrkHypPtr1[j]) {
+            wrkHypPtr3[k] = wrkHypPtr1[j];
+            j++;
+          } else {
+            wrkHypPtr3[k] = wrkHypPtr1[i];
+            i++;
+          }
+        }
+
+        /* Now do the allocation */
+        nmbrTmpPtr = poolFixedMalloc((reqHyps + 1)
+            * (long)(sizeof(nmbrString)));
+        /* if (!nmbrTmpPtr) outOfMemory("#33 (reqHyps)"); */
+                                       /* Not necessary w/ poolMalloc */
+        memcpy(nmbrTmpPtr, wrkHypPtr3, (size_t)reqHyps * sizeof(nmbrString));
+        nmbrTmpPtr[reqHyps] = -1;
+        g_Statement[stmt].reqHypList = nmbrTmpPtr;
+        g_Statement[stmt].numReqHyp = reqHyps;
+
+        /* We have finished determining optional $f hyps, so allocate the
+           permanent list for the statement array */
+        if (type == p_) { /* Optional ones are not used by $a statements */
+          nmbrTmpPtr = poolFixedMalloc((optHyps + 1)
+              * (long)(sizeof(nmbrString)));
+          memcpy(nmbrTmpPtr, wrkHypPtr2, (size_t)optHyps * sizeof(nmbrString));
+          nmbrTmpPtr[optHyps] = -1;
+          g_Statement[stmt].optHypList = nmbrTmpPtr;
+        }
+
+
+        /* Scan the list of disjoint variable ($d) hypotheses to find those
+           that are required */
+        optHyps = 0;
+        reqHyps = 0;
+        for (i = 0; i < activeDisjHypStackPtr; i++) {
+          m = activeDisjHypStack[i].tokenNumA; /* First var in disjoint pair */
+          n = activeDisjHypStack[i].tokenNumB; /* 2nd var in disjoint pair */
+          if (activeVarStack[g_MathToken[m].tmp].tmpFlag &&
+              activeVarStack[g_MathToken[n].tmp].tmpFlag) {
+            /* Both variables in the disjoint pair are required, so put the
+               disjoint hypothesis in the required list. */
+            wrkDisjHPtr1A[reqHyps] = m;
+            wrkDisjHPtr1B[reqHyps] = n;
+            wrkDisjHPtr1Stmt[reqHyps] =
+                activeDisjHypStack[i].statemNum;
+            reqHyps++;
+          } else {
+            /* At least one variable is not required, so the disjoint hypothesis\
+               is not required. */
+            wrkDisjHPtr2A[optHyps] = m;
+            wrkDisjHPtr2B[optHyps] = n;
+            wrkDisjHPtr2Stmt[optHyps] =
+                activeDisjHypStack[i].statemNum;
+            optHyps++;
+          }
+        }
+
+        /* We have finished determining required $d hyps, so allocate the
+           permanent list for the statement array */
+
+        nmbrTmpPtr = poolFixedMalloc((reqHyps + 1)
+            * (long)(sizeof(nmbrString)));
+        memcpy(nmbrTmpPtr, wrkDisjHPtr1A, (size_t)reqHyps
+            * sizeof(nmbrString));
+        nmbrTmpPtr[reqHyps] = -1;
+        g_Statement[stmt].reqDisjVarsA = nmbrTmpPtr;
+
+        nmbrTmpPtr = poolFixedMalloc((reqHyps + 1)
+            * (long)(sizeof(nmbrString)));
+        memcpy(nmbrTmpPtr, wrkDisjHPtr1B, (size_t)reqHyps
+            * sizeof(nmbrString));
+        nmbrTmpPtr[reqHyps] = -1;
+        g_Statement[stmt].reqDisjVarsB = nmbrTmpPtr;
+
+        nmbrTmpPtr = poolFixedMalloc((reqHyps + 1)
+            * (long)(sizeof(nmbrString)));
+        memcpy(nmbrTmpPtr, wrkDisjHPtr1Stmt, (size_t)reqHyps
+            * sizeof(nmbrString));
+        nmbrTmpPtr[reqHyps] = -1;
+        g_Statement[stmt].reqDisjVarsStmt = nmbrTmpPtr;
+
+        /* We have finished determining optional $d hyps, so allocate the
+           permanent list for the statement array */
+
+        if (type == p_) { /* Optional ones are not used by $a statements */
+
+          nmbrTmpPtr = poolFixedMalloc((optHyps + 1)
+              * (long)(sizeof(nmbrString)));
+          memcpy(nmbrTmpPtr, wrkDisjHPtr2A, (size_t)optHyps
+              * sizeof(nmbrString));
+          nmbrTmpPtr[optHyps] = -1;
+          g_Statement[stmt].optDisjVarsA = nmbrTmpPtr;
+
+          nmbrTmpPtr = poolFixedMalloc((optHyps + 1)
+              * (long)(sizeof(nmbrString)));
+          memcpy(nmbrTmpPtr, wrkDisjHPtr2B, (size_t)optHyps
+              * sizeof(nmbrString));
+          nmbrTmpPtr[optHyps] = -1;
+          g_Statement[stmt].optDisjVarsB = nmbrTmpPtr;
+
+          nmbrTmpPtr = poolFixedMalloc((optHyps + 1)
+              * (long)(sizeof(nmbrString)));
+          memcpy(nmbrTmpPtr, wrkDisjHPtr2Stmt, (size_t)optHyps
+              * sizeof(nmbrString));
+          nmbrTmpPtr[optHyps] = -1;
+          g_Statement[stmt].optDisjVarsStmt = nmbrTmpPtr;
+
+        }
+
+
+        /* Create list of optional variables (i.e. active but not required) */
+        optVars = 0;
+        for (i = 0; i < activeVarStackPtr; i++) {
+          if (activeVarStack[i].tmpFlag) {
+            activeVarStack[i].tmpFlag = 0; /* Clear it for future use */
+          } else {
+            wrkVarPtr2[optVars] = activeVarStack[i].tokenNum;
+            optVars++;
+          }
+        }
+        /* We have finished determining optional variables, so allocate the
+           permanent list for the statement array */
+        if (type == p_) { /* Optional ones are not used by $a statements */
+          nmbrTmpPtr = poolFixedMalloc((optVars + 1)
+              * (long)(sizeof(nmbrString)));
+          memcpy(nmbrTmpPtr, wrkVarPtr2, (size_t)optVars * sizeof(nmbrString));
+          nmbrTmpPtr[optVars] = -1;
+          g_Statement[stmt].optVarList = nmbrTmpPtr;
+        }
+
+        if (optVars + reqVars != activeVarStackPtr) bug(1708);
+
+
+        break;  /* Switch case break */
+    }
+
+    /* If a $a statement consists of a single constant,
+       e.g. "$a wff $.", it means an empty expression (wff) is allowed.
+       Before the user had to allow this manually with
+       SET EMPTY_SUBSTITUTION ON; now it is done automatically. */
+    type = g_Statement[stmt].type;
+    if (type == a_) {
+      if (g_minSubstLen) {
+        if (g_Statement[stmt].mathStringLen == 1) {
+          g_minSubstLen = 0;
+          printLongLine(cat("SET EMPTY_SUBSTITUTION was",
+             " turned ON (allowed) for this database.", NULL),
+             "    ", " ");
+        }
+      }
+    }
+
+    /* Ensure the current Metamath spec is met:  "There may
+       not be be two active $f statements containing the same variable.  Each
+       variable in a $e, $a, or $p statement must exist in an active $f
+       statement."  (Metamath book, p. 94) */
+    /* This section of code is stand-alone and may be removed without side
+       effects (other than less stringent error checking). */
+    /* ??? To do (maybe):  This might be better placed in-line with the scan
+       above, for faster speed and to get the pointer to the token for the
+       error message, but it would require a careful code analysis above. */
+    if (type == a_ || type == p_) {
+      /* Scan each hypothesis (and the statement itself in last pass) */
+      reqHyps = nmbrLen(g_Statement[stmt].reqHypList);
+      for (i = 0; i <= reqHyps; i++) {
+        if (i < reqHyps) {
+          m = (g_Statement[stmt].reqHypList)[i];
+        } else {
+          m = stmt;
+        }
+        if (g_Statement[m].type != f_) { /* Check $e,$a,$p */
+          /* This block implements: "Each variable in a $e, $a, or $p
+             statement must exist in an active $f statement" (Metamath
+             book p. 94). */
+          nmbrTmpPtr = g_Statement[m].mathString;
+          /* Scan all the vars in the $e (i<reqHyps) or $a/$p (i=reqHyps) */
+          for (j = 0; j < g_Statement[m].mathStringLen; j++) {
+            tokenNum = nmbrTmpPtr[j];
+            if (g_MathToken[tokenNum].tokenType == (char)con_) continue;
+                                            /* Ignore constants */
+            p = 0;  /* Initialize flag that we found a $f with the variable */
+            /* Scan all the mandatory $f's before this $e,$a,$p */
+            for (k = 0; k < i; k++) {
+              n = (g_Statement[stmt].reqHypList)[k];
+              if (g_Statement[n].type != f_) continue; /* Only check $f */
+              if (g_Statement[n].mathStringLen != 2) continue; /* This was
+                  already verified earlier; but if there was an error, don't
+                  cause memory violation by going out of bounds */
+              if ((g_Statement[n].mathString)[1] == tokenNum) {
+                p = 1;  /* Set flag that we found a $f with the variable */
+                break;
+              }
+            } /* next k ($f hyp scan) */
+            if (!p) {
+              sourceError(g_Statement[m].mathSectionPtr/*fbPtr*/,
+                  0/*tokenLen*/,
+                  m/*stmt*/, cat(
+                  "The variable \"", g_MathToken[tokenNum].tokenName,
+                  "\" does not appear in an active \"$f\" statement.", NULL));
+            }
+          } /* next j (variable scan) */
+        } else { /* g_Statement[m].type == f_ */
+          /* This block implements: "There may not be be two active $f
+             statements containing the same variable" (Metamath book p. 94). */
+          /* Check for duplicate vars in active $f's */
+          if (g_Statement[m].mathStringLen != 2) continue;  /* This was
+                  already verified earlier; but if there was an error, don't
+                  cause memory violation by going out of bounds */
+          tokenNum = (g_Statement[m].mathString)[1];
+          /* Scan all the mandatory $f's before this $f */
+          for (k = 0; k < i; k++) {
+            n = (g_Statement[stmt].reqHypList)[k];
+            if (g_Statement[n].type != f_) continue; /* Only check $f */
+            if (g_Statement[n].mathStringLen != 2) continue;  /* This was
+                  already verified earlier; but if there was an error, don't
+                  cause memory violation by going out of bounds */
+            if ((g_Statement[n].mathString)[1] == tokenNum) {
+              /* We found 2 $f's with the same variable */
+              assignStmtFileAndLineNum(n);
+              sourceError(g_Statement[m].mathSectionPtr/*fbPtr*/,
+                  0/*tokenLen*/,
+                  m/*stmt*/, cat(
+                  "The variable \"", g_MathToken[tokenNum].tokenName,
+                "\" already appears in the earlier active \"$f\" statement \"",
+                  g_Statement[n].labelName, "\" on line ",
+                  str((double)(g_Statement[n].lineNum)),
+                  " in file \"",
+                  g_Statement[n].fileName,
+                  "\".", NULL));
+              break; /* Optional: suppresses add'l error msgs for this stmt */
+            }
+          } /* next k ($f hyp scan) */
+        } /* if not $f else is $f */
+      } /* next i ($e hyp scan of this statement, or its $a/$p) */
+    } /* if stmt is $a or $p */
+
+  } /* Next stmt */
+
+  if (g_currentScope > 0) {
+    if (g_currentScope == 1) {
+      let(&tmpStr,"A \"$}\" is");
+    } else {
+      let(&tmpStr,cat(str((double)g_currentScope)," \"$}\"s are",NULL));
+    }
+    sourceError(g_Statement[g_statements].labelSectionPtr +
+        g_Statement[g_statements].labelSectionLen, 2, 0,
+        cat(tmpStr," missing at the end of the file.",NULL));
+  }
+
+
+  /* Filter out all hypothesis labels from the label key array.  We do not
+     need them anymore, since they are stored locally in each statement
+     structure.  Removing them will speed up lookups during proofs, and
+     will prevent a lookup from finding an inactive hypothesis label (thus
+     forcing an error message). */
+  j = 0;
+/*E*/if(db5)print2("Number of label keys before filter: %ld",g_numLabelKeys);
+  for (i = 0; i < g_numLabelKeys; i++) {
+    type = g_Statement[g_labelKeyBase[i]].type;
+    if (type == e_ || type == f_) {
+      j++;
+    } else {
+      g_labelKeyBase[i - j] = g_labelKeyBase[i];
+    }
+  }
+  g_numLabelKeys = g_numLabelKeys - j;
+/*E*/if(db5)print2(".  After: %ld\n",g_numLabelKeys);
+
+
+  /* Deallocate temporary space */
+  free(mathTokenSameAs);
+  free(reverseMathKey);
+  free(labelTokenSameAs);
+  free(reverseLabelKey);
+  free(labelActiveFlag);
+  free(activeConstStack);
+  free(activeVarStack);
+  free(wrkVarPtr1);
+  free(wrkVarPtr2);
+  for (i = 0; i < activeEHypStackPtr; i++) {
+    free(activeEHypStack[i].varList);
+  }
+  free(activeEHypStack);
+  for (i = 0; i < activeFHypStackPtr; i++) {
+    free(activeFHypStack[i].varList);
+  }
+  free(activeFHypStack);
+  free(wrkHypPtr1);
+  free(wrkHypPtr2);
+  free(wrkHypPtr3);
+  free(activeDisjHypStack);
+  free(wrkDisjHPtr1A);
+  free(wrkDisjHPtr1B);
+  free(wrkDisjHPtr1Stmt);
+  free(wrkDisjHPtr2A);
+  free(wrkDisjHPtr2B);
+  free(wrkDisjHPtr2Stmt);
+  free(wrkNmbrPtr);
+  free(wrkStrPtr);
+  free(symbolLenExists);
+  free_vstring(tmpStr);
+}
+
+
+/* Parse proof of one statement in source file.  Uses g_WrkProof structure. */
+/* Returns 0 if OK; returns 1 if proof is incomplete (is empty or has '?'
+   tokens);  returns 2 if error found; returns 3 if severe error found
+   (e.g. RPN stack violation); returns 4 if not a $p statement */
+char parseProof(long statemNum)
+{
+
+  long i, j, k, m, tok, step;
+  char *fbPtr;
+  long tokLength;
+  long numReqHyp;
+  long numOptHyp;
+  long numActiveHyp;
+  char zapSave;
+  flag labelFlag;
+  char returnFlag = 0;
+  nmbrString *nmbrTmpPtr;
+  void *voidPtr; /* bsearch returned value */
+  vstring tmpStrPtr;
+
+  flag explicitTargets = 0; /* Proof is of form <target>=<source> */
+  /* Source file pointers and token sizes for targets in a /EXPLICIT proof */
+  pntrString_def(targetPntr); /* Pointers to target tokens */
+  nmbrString_def(targetNmbr); /* Size of target tokens */
+  /* Variables for rearranging /EXPLICIT proof */
+  nmbrString_def(wrkProofString); /* Holds g_WrkProof.proofString */
+  long hypStepNum, hypSubProofLen, conclSubProofLen;
+  long matchingHyp;
+  nmbrString_def(oldStepNums); /* Just numbers 0 to numSteps-1 */
+  pntrString_def(reqHypSubProof); /* Subproofs of hypotheses */
+  pntrString_def(reqHypOldStepNums); /* Local label flag for
+                                                     subproofs of hypotheses */
+  nmbrString_def(rearrangedSubProofs);
+  nmbrString_def(rearrangedOldStepNums);
+  flag subProofMoved; /* Flag to restart scan after moving subproof */
+
+  if (g_Statement[statemNum].type != p_) {
+    bug(1723); /* should never get here */
+    g_WrkProof.errorSeverity = 4;
+    return 4; /* Do nothing if not $p */
+  }
+  fbPtr = g_Statement[statemNum].proofSectionPtr; /* Start of proof section */
+  if (fbPtr[0] == 0) { /* The proof was never assigned (could be a $p statement
+                          with no $=; this would have been detected earlier) */
+    g_WrkProof.errorSeverity = 4;
+    return 4; /* Pretend it's an empty proof */
+  }
+  fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+  if (fbPtr[0] == '(') { /* "(" is flag for compressed proof */
+    g_WrkProof.errorSeverity = parseCompressedProof(statemNum);
+    return (g_WrkProof.errorSeverity);
+  }
+
+  /* Make sure we have enough working space to hold the proof */
+  /* The worst case is less than the number of chars in the source,
+     plus the number of active hypotheses */
+
+  numOptHyp = nmbrLen(g_Statement[statemNum].optHypList);
+  if (g_Statement[statemNum].proofSectionLen + g_Statement[statemNum].numReqHyp
+      + numOptHyp > g_wrkProofMaxSize) {
+    if (g_wrkProofMaxSize) { /* Not the first allocation */
+      free(g_WrkProof.tokenSrcPtrNmbr);
+      free(g_WrkProof.tokenSrcPtrPntr);
+      free(g_WrkProof.stepSrcPtrNmbr);
+      free(g_WrkProof.stepSrcPtrPntr);
+      free(g_WrkProof.localLabelFlag);
+      free(g_WrkProof.hypAndLocLabel);
+      free(g_WrkProof.localLabelPool);
+      poolFree(g_WrkProof.proofString);
+      free(g_WrkProof.mathStringPtrs);
+      free(g_WrkProof.RPNStack);
+      free(g_WrkProof.compressedPfLabelMap);
+    }
+    g_wrkProofMaxSize = g_Statement[statemNum].proofSectionLen
+        + g_Statement[statemNum].numReqHyp + numOptHyp
+        + 2; /* 2 is minimum for 1-step proof; the other terms could
+                all be 0 */
+    g_WrkProof.tokenSrcPtrNmbr = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(nmbrString));
+    g_WrkProof.tokenSrcPtrPntr = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(pntrString));
+    g_WrkProof.stepSrcPtrNmbr = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(nmbrString));
+    g_WrkProof.stepSrcPtrPntr = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(pntrString));
+    g_WrkProof.localLabelFlag = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(flag));
+    g_WrkProof.hypAndLocLabel =
+        malloc((size_t)g_wrkProofMaxSize * sizeof(struct sortHypAndLoc));
+    g_WrkProof.localLabelPool = malloc((size_t)g_wrkProofMaxSize);
+    g_WrkProof.proofString =
+        poolFixedMalloc(g_wrkProofMaxSize * (long)(sizeof(nmbrString)));
+         /* Use poolFixedMalloc instead of poolMalloc so that it won't get
+            trimmed by memUsedPoolPurge. */
+    g_WrkProof.mathStringPtrs =
+        malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
+    g_WrkProof.RPNStack = malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
+    g_WrkProof.compressedPfLabelMap =
+         malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
+    if (!g_WrkProof.tokenSrcPtrNmbr ||
+        !g_WrkProof.tokenSrcPtrPntr ||
+        !g_WrkProof.stepSrcPtrNmbr ||
+        !g_WrkProof.stepSrcPtrPntr ||
+        !g_WrkProof.localLabelFlag ||
+        !g_WrkProof.hypAndLocLabel ||
+        !g_WrkProof.localLabelPool ||
+        !g_WrkProof.mathStringPtrs ||
+        !g_WrkProof.RPNStack
+        ) outOfMemory("#99 (g_WrkProof)");
+  }
+
+  /* Initialization for this proof */
+  g_WrkProof.errorCount = 0; /* Used as threshold for how many error msgs/proof */
+  g_WrkProof.numSteps = 0;
+  g_WrkProof.numTokens = 0;
+  g_WrkProof.numHypAndLoc = 0;
+  g_WrkProof.numLocalLabels = 0;
+  g_WrkProof.RPNStackPtr = 0;
+  g_WrkProof.localLabelPoolPtr = g_WrkProof.localLabelPool;
+
+  /* fbPtr points to the first token now. */
+
+  /* First break up proof section of source into tokens */
+  while (1) {
+    tokLength = proofTokenLen(fbPtr);
+    if (!tokLength) break;
+    g_WrkProof.tokenSrcPtrPntr[g_WrkProof.numTokens] = fbPtr;
+    g_WrkProof.tokenSrcPtrNmbr[g_WrkProof.numTokens] = tokLength;
+    g_WrkProof.numTokens++;
+    fbPtr = fbPtr + tokLength;
+    fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+  }
+
+  /* If there are no tokens, the proof is unknown; make the token a '?' */
+  /* (g_WrkProof.tokenSrcPtrPntr won't point to the source, but this is OK since
+     there will never be an error message for it.) */
+  if (!g_WrkProof.numTokens) {
+
+    /* For now, this is an error. */
+    if (!g_WrkProof.errorCount) {
+      sourceError(fbPtr, 2, statemNum,
+          "The proof is empty.  If you don't know the proof, make it a \"?\".");
+    }
+    g_WrkProof.errorCount++;
+    if (returnFlag < 1) returnFlag = 1;
+
+    /* Allow empty proofs anyway */
+    g_WrkProof.numTokens = 1;
+    g_WrkProof.tokenSrcPtrPntr[0] = "?";
+    g_WrkProof.tokenSrcPtrNmbr[0] = 1; /* Length */
+  }
+
+  /* Copy active (opt + req) hypotheses to hypAndLocLabel look-up table */
+  nmbrTmpPtr = g_Statement[statemNum].optHypList;
+  /* Transfer optional hypotheses */
+  while (1) {
+    i = nmbrTmpPtr[g_WrkProof.numHypAndLoc];
+    if (i == -1) break;
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum = i;
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName =
+        g_Statement[i].labelName;
+    g_WrkProof.numHypAndLoc++;
+  }
+  /* Transfer required hypotheses */
+  j = g_Statement[statemNum].numReqHyp;
+  nmbrTmpPtr = g_Statement[statemNum].reqHypList;
+  for (i = 0; i < j; i++) {
+    k = nmbrTmpPtr[i];
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum = k;
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName =
+        g_Statement[k].labelName;
+    g_WrkProof.numHypAndLoc++;
+  }
+
+  /* Sort the hypotheses by label name for lookup */
+  numActiveHyp = g_WrkProof.numHypAndLoc; /* Save for bsearch later */
+  qsort(g_WrkProof.hypAndLocLabel, (size_t)(g_WrkProof.numHypAndLoc),
+      sizeof(struct sortHypAndLoc), hypAndLocSortCmp);
+
+
+  /* Scan the parsed tokens for local label assignments */
+  fbPtr = g_WrkProof.tokenSrcPtrPntr[0];
+  if (fbPtr[0] == ':') {
+    if (!g_WrkProof.errorCount) {
+      sourceError(fbPtr, 1, statemNum,
+          "The colon at proof step 1 must be preceded by a local label.");
+    }
+    if (returnFlag < 2) returnFlag = 2;
+    g_WrkProof.tokenSrcPtrPntr[0] = "?";
+    g_WrkProof.tokenSrcPtrNmbr[0] = 1; /* Length */
+    g_WrkProof.errorCount++;
+  }
+  fbPtr = g_WrkProof.tokenSrcPtrPntr[g_WrkProof.numTokens - 1];
+  if (fbPtr[0] == ':') {
+    if (!g_WrkProof.errorCount) {
+      sourceError(fbPtr, 1, statemNum,
+          "The colon in the last proof step must be followed by a label.");
+    }
+    if (returnFlag < 2) returnFlag = 2;
+    g_WrkProof.errorCount++;
+    g_WrkProof.numTokens--;
+  }
+  labelFlag = 0;
+  for (tok = 0; tok < g_WrkProof.numTokens; tok++) {
+    fbPtr = g_WrkProof.tokenSrcPtrPntr[tok];
+
+    /* If next token is = then this token is a target for /EXPLICIT format,
+       so don't increment the proof step number */
+    if (tok < g_WrkProof.numTokens - 2) {
+      if (((char *)((g_WrkProof.tokenSrcPtrPntr)[tok + 1]))[0] == '=') {
+        explicitTargets = 1; /* Flag that proof has explicit targets */
+        continue;
+      }
+    }
+    if (fbPtr[0] == '=') continue; /* Skip the = token */
+
+    /* Save pointer to source file vs. step for error messages */
+    g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] =
+        g_WrkProof.tokenSrcPtrNmbr[tok]; /* Token length */
+    g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = fbPtr; /* Token ptr */
+
+    /* Save fact that this step has a local label declaration */
+    g_WrkProof.localLabelFlag[g_WrkProof.numSteps] = labelFlag;
+    labelFlag = 0;
+
+    g_WrkProof.numSteps++;
+    if (fbPtr[0] != ':') continue;
+
+    /* Colon found -- previous token is a label */
+    labelFlag = 1;
+
+    g_WrkProof.numSteps = g_WrkProof.numSteps - 2;
+    fbPtr = g_WrkProof.tokenSrcPtrPntr[tok - 1]; /* The local label */
+    tokLength = g_WrkProof.tokenSrcPtrNmbr[tok - 1]; /* Its length */
+
+    /* Check for illegal characters */
+    for (j = 0; j < tokLength; j++) {
+      if (illegalLabelChar[(unsigned char)fbPtr[j]]) {
+        if (!g_WrkProof.errorCount) {
+          sourceError(fbPtr + j, 1, statemNum,cat(
+              "The local label at proof step ",
+              str((double)(g_WrkProof.numSteps + 1)),
+              " is incorrect.  Only letters,",
+              " digits, \"_\", \"-\", and \".\" are allowed in local labels.",
+              NULL));
+        }
+        if (returnFlag < 2) returnFlag = 2;
+        g_WrkProof.errorCount++;
+      }
+    }
+
+    /* Add the label to the local label pool and hypAndLocLabel table */
+    memcpy(g_WrkProof.localLabelPoolPtr, fbPtr, (size_t)tokLength);
+    g_WrkProof.localLabelPoolPtr[tokLength] = 0; /* String terminator */
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum =
+       -g_WrkProof.numSteps - 1000; /* offset of -1000 is flag for local label*/
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName
+        = g_WrkProof.localLabelPoolPtr;
+
+    /* Make sure local label is different from all earlier $a and $p labels */
+    voidPtr = (void *)bsearch(g_WrkProof.localLabelPoolPtr, g_labelKeyBase,
+        (size_t)g_numLabelKeys, sizeof(long), labelSrchCmp);
+    if (voidPtr) { /* It was found */
+      j = *(long *)voidPtr; /* Statement number */
+      if (j <= statemNum) {
+        if (!g_WrkProof.errorCount) {
+          assignStmtFileAndLineNum(j);
+          sourceError(fbPtr, tokLength, statemNum,cat(
+              "The local label at proof step ",
+              str((double)(g_WrkProof.numSteps + 1)),
+              " is the same as the label of statement ",
+              str((double)j),
+              " at line ",
+              str((double)(g_Statement[j].lineNum)),
+              " in file \"",
+              g_Statement[j].fileName,
+              "\".  Local labels must be different from active statement labels.",
+              NULL));
+        }
+        g_WrkProof.errorCount++;
+        if (returnFlag < 2) returnFlag = 2;
+      }
+    }
+
+    /* Make sure local label is different from all active $e and $f labels */
+    voidPtr = (void *)bsearch(g_WrkProof.localLabelPoolPtr,
+        g_WrkProof.hypAndLocLabel,
+        (size_t)numActiveHyp, sizeof(struct sortHypAndLoc), hypAndLocSrchCmp);
+    if (voidPtr) { /* It was found */
+      j = ( (struct sortHypAndLoc *)voidPtr)->labelTokenNum; /* Statement number */
+      if (!g_WrkProof.errorCount) {
+        assignStmtFileAndLineNum(j);
+        sourceError(fbPtr, tokLength, statemNum,cat(
+            "The local label at proof step ",
+            str((double)(g_WrkProof.numSteps + 1)),
+            " is the same as the label of statement ",
+            str((double)j),
+            " at line ",
+            str((double)(g_Statement[j].lineNum)),
+            " in file \"",
+            g_Statement[j].fileName,
+            "\".  Local labels must be different from active statement labels.",
+            NULL));
+      }
+      g_WrkProof.errorCount++;
+      if (returnFlag < 2) returnFlag = 2;
+      g_WrkProof.numHypAndLoc--; /* Ignore the label */
+    }
+
+    g_WrkProof.numHypAndLoc++;
+    g_WrkProof.localLabelPoolPtr = &g_WrkProof.localLabelPoolPtr[tokLength + 1];
+
+  } /* Next i */
+
+  /* Collect all target labels in /EXPLICIT format */
+  /* I decided not to make targetPntr, targetNmbr part of the g_WrkProof
+     structure since other proof formats don't assign it, so we can't
+     reference it reliably outside of this function.  And it would waste
+     some memory if we don't use /EXPLICIT, which is intended primarily
+     for database maintenance. */
+  if (explicitTargets == 1) {
+    pntrLet(&targetPntr, pntrSpace(g_WrkProof.numSteps));
+    nmbrLet(&targetNmbr, nmbrSpace(g_WrkProof.numSteps));
+    step = 0;
+    for (tok = 0; tok < g_WrkProof.numTokens - 2; tok++) {
+      /* If next token is = then this token is a target for /EXPLICIT format,
+         so don't increment the proof step number */
+      if (((char *)((g_WrkProof.tokenSrcPtrPntr)[tok + 1]))[0] == '=') {
+        fbPtr = g_WrkProof.tokenSrcPtrPntr[tok];
+        if (step >= g_WrkProof.numSteps) {
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, g_WrkProof.tokenSrcPtrNmbr[tok], statemNum, cat(
+                "There are more target labels than proof steps.", NULL));
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+          break;
+        }
+        targetPntr[step] = fbPtr;
+        targetNmbr[step] = g_WrkProof.tokenSrcPtrNmbr[tok];
+        if (g_WrkProof.tokenSrcPtrPntr[tok + 2]
+            != g_WrkProof.stepSrcPtrPntr[step]) {
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, g_WrkProof.tokenSrcPtrNmbr[tok], statemNum, cat(
+                "The target label for step ", str((double)step + 1),
+                " is not assigned to that step.  ",
+                "(Check for missing or extra \"=\".)", NULL));
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+        }
+        step++;
+      }
+    } /* next tok */
+    if (step != g_WrkProof.numSteps) {
+      if (!g_WrkProof.errorCount) {
+        sourceError(
+            (char *)((g_WrkProof.tokenSrcPtrPntr)[g_WrkProof.numTokens - 1]),
+            g_WrkProof.tokenSrcPtrNmbr[g_WrkProof.numTokens - 1],
+            statemNum, cat(
+                "There are ", str((double)(g_WrkProof.numSteps)), " proof steps but only ",
+                str((double)step), " target labels.", NULL));
+      }
+      g_WrkProof.errorCount++;
+      if (returnFlag < 2) returnFlag = 2;
+    }
+  } /* if explicitTargets == 1 */
+
+  if (g_WrkProof.numHypAndLoc > numActiveHyp) { /* There were local labels */
+
+    /* Sort the local labels into the hypAndLocLabel look-up table */
+    qsort(g_WrkProof.hypAndLocLabel, (size_t)(g_WrkProof.numHypAndLoc),
+        sizeof(struct sortHypAndLoc), hypAndLocSortCmp);
+
+    /* Check for duplicate local labels */
+    for (i = 1; i < g_WrkProof.numHypAndLoc; i++) {
+      if (!strcmp(g_WrkProof.hypAndLocLabel[i - 1].labelName,
+          g_WrkProof.hypAndLocLabel[i].labelName)) { /* Duplicate label */
+        /* Get the step numbers */
+        j = -(g_WrkProof.hypAndLocLabel[i - 1].labelTokenNum + 1000);
+        k = -(g_WrkProof.hypAndLocLabel[i].labelTokenNum + 1000);
+        if (j > k) {
+          m = j;
+          j = k; /* Smaller step number */
+          k = m; /* Larger step number */
+        }
+        /* Find the token - back up a step then move forward to loc label */
+        fbPtr = g_WrkProof.stepSrcPtrPntr[k - 1]; /* Previous step */
+        fbPtr = fbPtr + g_WrkProof.stepSrcPtrNmbr[k - 1];
+        fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+        if (!g_WrkProof.errorCount) {
+          sourceError(fbPtr,
+              proofTokenLen(fbPtr), statemNum,
+              cat("The local label at proof step ", str((double)k + 1),
+              " is the same as the one declared at step ",
+              str((double)j + 1), ".", NULL));
+        }
+        g_WrkProof.errorCount++;
+        if (returnFlag < 2) returnFlag = 2;
+      } /* End if duplicate label */
+    } /* Next i */
+
+  } /* End if there are local labels */
+
+  /* Build the proof string and check the RPN stack */
+  g_WrkProof.proofString[g_WrkProof.numSteps] = -1; /* End of proof */
+  nmbrZapLen(g_WrkProof.proofString, g_WrkProof.numSteps);
+     /* Zap mem pool actual length (because nmbrLen will be used later on this)*/
+
+  if (explicitTargets == 1) {
+    /* List of original step numbers to keep track of local label movement */
+    nmbrLet(&oldStepNums, nmbrSpace(g_WrkProof.numSteps));
+    for (i = 0; i < g_WrkProof.numSteps; i++) {
+      oldStepNums[i] = i;
+    }
+  }
+
+  for (step = 0; step < g_WrkProof.numSteps; step++) {
+    tokLength = g_WrkProof.stepSrcPtrNmbr[step];
+    fbPtr = g_WrkProof.stepSrcPtrPntr[step];
+
+    /* Handle unknown proof steps */
+    if (fbPtr[0] == '?') {
+      if (returnFlag < 1) returnFlag = 1;
+                                      /* Flag that proof is partially unknown */
+      g_WrkProof.proofString[step] = -(long)'?';
+      /* Treat "?" like a hypothesis - push stack and continue */
+      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+      g_WrkProof.RPNStackPtr++;
+      continue;
+    }
+
+    /* Temporarily zap the token's end with a null for string comparisons */
+    zapSave = fbPtr[tokLength];
+    fbPtr[tokLength] = 0; /* Zap source */
+
+    /* See if the proof token is a hypothesis or local label ref. */
+    voidPtr = (void *)bsearch(fbPtr, g_WrkProof.hypAndLocLabel,
+        (size_t)(g_WrkProof.numHypAndLoc), sizeof(struct sortHypAndLoc),
+        hypAndLocSrchCmp);
+    if (voidPtr) {
+      fbPtr[tokLength] = zapSave; /* Unzap source */
+      j = ((struct sortHypAndLoc *)voidPtr)->labelTokenNum; /* Label lookup number */
+      g_WrkProof.proofString[step] = j; /* Proof string */
+
+      /* Push the stack */
+      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+      g_WrkProof.RPNStackPtr++;
+
+      if (j < 0) { /* It's a local label reference */
+        i = -1000 - j; /* Step number referenced */
+        if (i < 0) bug(1734);
+
+        /* Make sure we don't reference a later step */
+        if (i > step) {
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, tokLength, statemNum,cat("Proof step ",
+                str((double)step + 1),
+                " references a local label before it is declared.",
+                NULL));
+          }
+          g_WrkProof.proofString[step] = -(long)'?';
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+        }
+
+        if (g_WrkProof.localLabelFlag[step]) {
+          if (!g_WrkProof.errorCount) {
+            /* Chained labels not allowed because it complicates the language
+               but doesn't buy anything */
+            sourceError(fbPtr, tokLength, statemNum, cat(
+                "The local label reference at proof step ",
+                str((double)step + 1),
+                " declares a local label.  Only \"$a\" and \"$p\" statement",
+                " labels may have local label declarations.",NULL));
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+        }
+      } else { /* It's a hypothesis reference */
+        if (g_WrkProof.localLabelFlag[step]) {
+          /* Not allowed because it complicates the language but doesn't
+             buy anything; would make $e to $f assignments harder to detect */
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, tokLength, statemNum, cat(
+                "The hypothesis reference at proof step ",
+                str((double)step + 1),
+                " declares a local label.  Only \"$a\" and \"$p\" statement",
+                " labels may have local label declarations.",NULL));
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+        }
+        if (j <= 0) bug(1709);
+      }
+      continue;
+    } /* End if local label or hypothesis */
+
+    /* See if token is an assertion label */
+    voidPtr = (void *)bsearch(fbPtr, g_labelKeyBase, (size_t)g_numLabelKeys,
+        sizeof(long), labelSrchCmp);
+    fbPtr[tokLength] = zapSave; /* Unzap source */
+    if (!voidPtr) {
+      if (!g_WrkProof.errorCount) {
+        sourceError(fbPtr, tokLength, statemNum, cat(
+            "The token at proof step ",
+            str((double)step + 1),
+            " is not an active statement label or a local label.",NULL));
+      }
+      g_WrkProof.errorCount++;
+      g_WrkProof.proofString[step] = -(long)'?';
+      /* Push the stack */
+      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+      g_WrkProof.RPNStackPtr++;
+      if (returnFlag < 2) returnFlag = 2;
+      continue;
+    }
+
+    /* It's an assertion ($a or $p) */
+    j = *(long *)voidPtr; /* Statement number */
+    if (g_Statement[j].type != a_ && g_Statement[j].type != p_) bug(1710);
+    g_WrkProof.proofString[step] = j; /* Assign $a/$p label to proof string */
+
+    if (j >= statemNum) { /* Error */
+      if (!g_WrkProof.errorCount) {
+        if (j == statemNum) {
+          sourceError(fbPtr, tokLength, statemNum, cat(
+              "The label at proof step ",
+              str((double)step + 1),
+              " is the label of this statement.  A statement may not be used to",
+              " prove itself.",NULL));
+        } else {
+          assignStmtFileAndLineNum(j);
+          sourceError(fbPtr, tokLength, statemNum, cat(
+              "The label \"", g_Statement[j].labelName, "\" at proof step ",
+              str((double)step + 1),
+              " is the label of a future statement (at line ",
+              str((double)(g_Statement[j].lineNum)),
+              " in file ",g_Statement[j].fileName,
+      ").  Only local labels or previous, active statements may be referenced.",
+              NULL));
+        }
+      }
+      g_WrkProof.errorCount++;
+      if (returnFlag < 2) returnFlag = 2;
+    }
+
+    /* It's a valid assertion, so pop the stack */
+    numReqHyp = g_Statement[j].numReqHyp;
+
+    /* Error check for exhausted stack */
+    if (g_WrkProof.RPNStackPtr < numReqHyp) { /* Stack exhausted -- error */
+      if (!g_WrkProof.errorCount) {
+        tmpStrPtr = shortDumpRPNStack();
+        if (strcmp(left(tmpStrPtr,18),"RPN stack is empty")){
+          i = instr(1,tmpStrPtr,"contains ");
+          let(&tmpStrPtr,cat(left(tmpStrPtr,i + 7)," only",
+            right(tmpStrPtr,i + 8),
+            NULL));
+        }
+        if (numReqHyp == 1) {
+          let(&tmpStrPtr,cat("a hypothesis but the ",tmpStrPtr,NULL));
+        } else {
+          let(&tmpStrPtr,cat(str((double)numReqHyp)," hypotheses but the ",tmpStrPtr,
+              NULL));
+        }
+        sourceError(fbPtr, tokLength, statemNum, cat(
+            "At proof step ",
+            str((double)step + 1),", statement \"",
+            g_Statement[j].labelName,"\" requires ",
+            tmpStrPtr,".",NULL));
+        free_vstring(tmpStrPtr);
+      }
+      /* Treat it like an unknown step so stack won't get exhausted */
+      g_WrkProof.errorCount++;
+      g_WrkProof.proofString[step] = -(long)'?';
+      /* Push the stack */
+      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+      g_WrkProof.RPNStackPtr++;
+      if (returnFlag < 3) returnFlag = 3;
+      continue;
+    } /* End if stack exhausted */
+
+    /* For proofs saved with /EXPLICIT, the user may have changed the order
+       of hypotheses.  First, get the subproofs for the hypotheses.  Then
+       reassemble them in the right order. */
+    if (explicitTargets == 1) {
+      nmbrLet(&wrkProofString, g_WrkProof.proofString);
+            /* nmbrString to rearrange proof then when done reassign to
+               g_WrkProof.proofString structure component */
+      nmbrTmpPtr = g_Statement[j].reqHypList;
+      numReqHyp = g_Statement[j].numReqHyp;
+      conclSubProofLen = subproofLen(wrkProofString, step);
+      pntrLet(&reqHypSubProof, pntrNSpace(numReqHyp));
+                                         /* Initialize to NULL_NMBRSTRINGs */
+      pntrLet(&reqHypOldStepNums, pntrNSpace(numReqHyp));
+                                         /* Initialize to NULL_NMBRSTRINGs */
+      k = 0; /* Total hypothesis subproof lengths for error checking */
+      for (i = 0; i < numReqHyp; i++) {
+        m = g_WrkProof.RPNStackPtr - numReqHyp + i; /* Stack position of hyp */
+        hypStepNum = g_WrkProof.RPNStack[m]; /* Step number of hypothesis i */
+        hypSubProofLen = subproofLen(wrkProofString, hypStepNum);
+        k += hypSubProofLen;
+        nmbrLet((nmbrString **)(&(reqHypSubProof[i])),
+            /* For nmbrSeg, 1 = first step */
+            nmbrSeg(wrkProofString,
+                hypStepNum - hypSubProofLen + 2, hypStepNum + 1));
+        nmbrLet((nmbrString **)(&(reqHypOldStepNums[i])),
+            /* For nmbrSeg, 1 = first step */
+            nmbrSeg(oldStepNums,
+                hypStepNum - hypSubProofLen + 2, hypStepNum + 1));
+      } /* Next i */
+      if (k != conclSubProofLen - 1 /* && returnFlag < 2 */) {
+                        /* Uncomment above if bad proof triggers this bug */
+        bug(1731);
+      }
+      free_nmbrString(rearrangedSubProofs);
+      matchingHyp = -1; /* In case there are no hypotheses */
+      for (i = 0; i < numReqHyp; i++) {
+        matchingHyp = -1;
+        for (k = 0; k < numReqHyp; k++) {
+          m = g_WrkProof.RPNStackPtr - numReqHyp + k; /* Stack position of hyp */
+          hypStepNum = g_WrkProof.RPNStack[m]; /* Step number of hypothesis k */
+
+
+          /* Temporarily zap the token's end with a null for string comparisons */
+          fbPtr = targetPntr[hypStepNum];
+          zapSave = fbPtr[targetNmbr[hypStepNum]];
+          fbPtr[targetNmbr[hypStepNum]] = 0; /* Zap source */
+          /* See if hypothesis i matches the target label k i.e. hypStepNum */
+          if (!strcmp(g_Statement[nmbrTmpPtr[i]].labelName, fbPtr)) {
+            matchingHyp = k;
+          }
+          fbPtr[targetNmbr[hypStepNum]] = zapSave; /* Unzap source */
+          if (matchingHyp != -1) break;
+        } /* next k (0 to numReqHyp-1) */
+        if (matchingHyp == -1) {
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, 1/*token length*/, statemNum, cat(
+                "The target labels for the hypotheses for step ", str((double)step + 1),
+                " do not match hypothesis \"",
+                g_Statement[nmbrTmpPtr[i]].labelName,
+                "\" of the assertion \"",
+                g_Statement[j].labelName,
+                "\" in step ",  str((double)step + 1), ".",
+                NULL));
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+          break; /* Give up; don't try to rearrange hypotheses */
+        }
+        /* Accumulate the subproof for hypothesis i */
+        nmbrLet(&rearrangedSubProofs, nmbrCat(rearrangedSubProofs,
+            reqHypSubProof[matchingHyp], NULL));
+        nmbrLet(&rearrangedOldStepNums, nmbrCat(rearrangedOldStepNums,
+            reqHypOldStepNums[matchingHyp], NULL));
+      } /* next i (0 to numReqHyp-1) */
+
+      if (matchingHyp != -1) { /* All hypotheses found */
+        if (nmbrLen(rearrangedSubProofs) != conclSubProofLen - 1
+             /* && returnFlag < 2 */) {
+                          /* Uncomment above if bad proof triggers this bug */
+          bug(1732);
+        }
+        nmbrLet(&(wrkProofString), nmbrCat(
+            nmbrLeft(wrkProofString, step - conclSubProofLen + 1),
+            rearrangedSubProofs,
+            nmbrRight(wrkProofString, step + 1), NULL));
+        nmbrLet(&oldStepNums, nmbrCat(
+            nmbrLeft(oldStepNums, step - conclSubProofLen + 1),
+            rearrangedOldStepNums,
+            nmbrRight(oldStepNums, step + 1), NULL));
+      }
+
+      /* Reassign g_WrkProof.proofString from rearranged wrkProofString */
+      for (i = 0; i < step; i++) {
+        /* Nothing from step to end has been changed, so stop at step */
+        (g_WrkProof.proofString)[i] = wrkProofString[i];
+      }
+      if ((g_WrkProof.proofString)[step] != wrkProofString[step]) bug(1735);
+
+      /* Deallocate */
+      for (i = 0; i < numReqHyp; i++) {
+        free_nmbrString(*(nmbrString **)(&(reqHypSubProof[i])));
+        free_nmbrString(*(nmbrString **)(&(reqHypOldStepNums[i])));
+      }
+      free_pntrString(reqHypSubProof);
+      free_pntrString(reqHypOldStepNums);
+      free_nmbrString(rearrangedSubProofs);
+      free_nmbrString(rearrangedOldStepNums);
+      free_nmbrString(wrkProofString);
+    } /* if explicitTargets */
+
+    numReqHyp = g_Statement[j].numReqHyp;
+
+    /* Pop the stack */
+    g_WrkProof.RPNStackPtr = g_WrkProof.RPNStackPtr - numReqHyp;
+    g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+    g_WrkProof.RPNStackPtr++;
+
+  } /* Next step */
+
+  /* The stack should have one entry */
+  if (g_WrkProof.RPNStackPtr != 1) {
+    tmpStrPtr = shortDumpRPNStack();
+    fbPtr = g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps - 1];
+    if (!g_WrkProof.errorCount) {
+      sourceError(fbPtr, proofTokenLen(fbPtr), statemNum, cat("After proof step ",
+          str((double)(g_WrkProof.numSteps))," (the last step), the ",
+          tmpStrPtr,".  It should contain exactly one entry.",NULL));
+    }
+    g_WrkProof.errorCount++;
+    if (returnFlag < 3) returnFlag = 3;
+  }
+
+  if (explicitTargets) {
+    /* Correct the local label refs in the rearranged proof */
+    for (step = 0; step < g_WrkProof.numSteps; step++) {
+      /* This is slow lookup with n^2 behavior, but should be ok since
+         /EXPLICIT isn't used that often */
+      k = (g_WrkProof.proofString)[step]; /* Will be <= -1000 if local label */
+      if (k <= -1000) {
+        k = -1000 - k; /* Restore step number */
+        if (k < 0 || k >= g_WrkProof.numSteps) bug(1733);
+        /* Find the original step */
+        if (oldStepNums[k] == k) {
+            /* Wasn't changed, so skip renumbering for speedup */
+          continue;
+        }
+        i = 0; /* For bug check */
+        /* Find the original step number and change it to the new one */
+        for (m = 0; m < g_WrkProof.numSteps; m++) {
+          if (oldStepNums[m] == k) {
+            (g_WrkProof.proofString)[step] = -1000 - m;
+            i = 1; /* Found */
+            break;
+          }
+        }
+        if (i == 0) bug(1740);
+      }
+    } /* next step */
+
+    /* Check if any local labels point to future steps: if so, we should
+       moved the subproof they point to down (since many functions require
+       that a local label be declared before it is used) */
+    do {
+      subProofMoved = 0;  /* Flag to rescan after moving a subproof */
+      /* TODO: restart loop after step just finished for speedup?
+         (maybe not worth it).
+         We could just change subProofMoved to restartStep (long) and use
+         restartStep > 0 as the flag, since we would restart at the
+         last step processed plus 1. */
+      for (step = 0; step < g_WrkProof.numSteps; step++) {
+        k = (g_WrkProof.proofString)[step]; /* Will be <= -1000 if local label */
+        if (k <= -1000) { /* References local label i.e. subproof */
+          k = -1000 - k; /* Restore step number subproof ends at */
+          if (k > step) { /* Refers to label declared after this step */
+            m = subproofLen(g_WrkProof.proofString, k);
+            /*m = nmbrGetSubProofLen(g_WrkProof.proofString, k);*/
+
+            /* At this point:
+                   step = the step referencing a future subproof
+                   k = end of future subproof
+                   m = length of future subproof */
+
+            /* TODO - make this a direct assignment for speedup?
+               (with most $f's reversed, this has about 13K hits during
+               'verify proof *' - maybe not enough to justify rewriting this
+               to make direct assignment instead of nmbrLet().) */
+            /* Replace the step with the subproof it references */
+            /* Note that nmbrXxx() positions start at 1, not 0; add 1 to step */
+            nmbrLet(&wrkProofString, nmbrCat(
+                /* Proof before future label ref: */
+                nmbrLeft(g_WrkProof.proofString, step),
+                /* The future subproof moved to the current step: */
+                nmbrMid(g_WrkProof.proofString, k - m + 2, m),
+                /* The steps between this step and the future subproof: */
+                nmbrSeg(g_WrkProof.proofString, step + 2, k - m + 1),
+                /* The future subproof replaced with the current step (a local
+                   label to be renumbered below): */
+                nmbrMid(g_WrkProof.proofString, step + 1, 1),
+                /* The rest of the steps to the end of proof: */
+                nmbrRight(g_WrkProof.proofString, k + 2),
+                NULL));
+            if (nmbrLen(wrkProofString) != g_WrkProof.numSteps) {
+              bug(1736); /* Make sure proof length didn't change */
+            }
+            if (wrkProofString[k] != (g_WrkProof.proofString)[step]) {
+              bug(1737); /* Make sure future subproof is now the future local
+                            label (to be renumbered below) */
+            }
+
+            /* We now have this wrkProofString[...] content:
+                [0]...[step-1]                   same as original proof
+                [step+1]...[step+m-1]            moved subproof
+                [step+m]...[k-1]                 shifted orig proof
+                [k]...[k]                        subproof replaced by loc label
+                [k+1]...[g_WrkProof.numSteps-1]    same as orig proof */
+
+            /* Correct all local labels */
+            for (i = 0; i < g_WrkProof.numSteps; i++) {
+              j = (wrkProofString)[i]; /* Will be <= -1000 if local label */
+              if (j > -1000) continue; /* Not loc label ref */
+              j = -1000 - j; /* Restore step number subproof ends at */
+              /* Note: the conditions before the "&&" below are redundant
+                 but provide better sanity checking */
+              if (j >= 0 && j < step) { /* Before moved subproof */
+                /*j = j;*/ /* Same as orig proof */
+              } else if (j == step) { /* The original local label ref */
+                bug(1738); /* A local label shouldn't ref a local label */
+              } else if (j > step && j <= k - m) {
+                                   /* Steps shifted up by subproof insertion */
+                j = j + m - 1; /* Offset by size of subproof moved down -1 */
+              } else if (j > k - m && j <= k) {
+                                   /* Reference to inside the moved subproof */
+                j = j + step + m - 1 - k;  /* Shift down */
+              } else if (j > k && j <= g_WrkProof.numSteps - 1) {
+                                              /* Ref to after moved subproof */
+                /*j = j;*/ /* Same as orig proof */
+              } else {
+                bug(1739);  /* Cases not exhausted or j is out of range */
+              }
+              (wrkProofString)[i] = -j - 1000; /* Update new proof */
+            } /* next i */
+
+            /* Transfer proof back to original */
+            for (i = 0; i < g_WrkProof.numSteps; i++) {
+              (g_WrkProof.proofString)[i] = wrkProofString[i];
+            }
+
+            /* Set flag that a subproof was moved and restart the scan */
+            subProofMoved = 1;
+            /* Reassign g_WrkProof.proofString from new wrkProofString */
+            for (i = 0; i < g_WrkProof.numSteps; i++) {
+              (g_WrkProof.proofString)[i] = wrkProofString[i];
+            }
+            break; /* Break out of the 'for (step...' loop */
+
+          } /* if k>step */
+        } /* if k<= -1000 */
+      } /* next step */
+    } while (subProofMoved);
+
+    /* Deallocate */
+    free_pntrString(targetPntr);
+    free_nmbrString(targetNmbr);
+    free_nmbrString(oldStepNums);
+    free_nmbrString(wrkProofString);
+  } /* if (explicitTargets) */
+
+  g_WrkProof.errorSeverity = returnFlag;
+  return (returnFlag);
+
+} /* parseProof() */
+
+
+
+/* Parse proof in compressed format */
+/* Parse proof of one statement in source file.  Uses wrkProof structure. */
+/* Returns 0 if OK; returns 1 if proof is incomplete (is empty or has '?'
+   tokens);  returns 2 if error found; returns 3 if severe error found
+   (e.g. RPN stack violation); returns 4 if not a $p statement */
+char parseCompressedProof(long statemNum)
+{
+
+  long i, j, k, step, stmt;
+  char *fbPtr;
+  char *fbStartProof;
+  char *labelStart;
+  long tokLength;
+  long numReqHyp;
+  long numOptHyp;
+  char zapSave;
+  flag breakFlag;
+  char returnFlag = 0;
+  nmbrString *nmbrTmpPtr;
+  void *voidPtr; /* bsearch returned value */
+  vstring tmpStrPtr;
+  flag hypLocUnkFlag;  /* Hypothesis, local label ref, or unknown step */
+  long labelMapIndex;
+
+  static unsigned char chrWeight[256]; /* Proof label character weights */
+  static unsigned char chrType[256]; /* Proof character types */
+  static flag chrTablesInited = 0;
+  static char *digits = "0123456789";
+  static char *letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  static char labelChar = ':';
+  static long lettersLen;
+  static long digitsLen;
+
+  /* Used to detect old buggy compression */
+  long bggyProofLen;
+  char bggyZapSave;
+  flag bggyAlgo;
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  labelStart = "";
+
+  /* Do one-time initialization */
+  if (!chrTablesInited) {
+    chrTablesInited = 1;
+
+    /* Compression standard with all cap letters */
+    /* (For 500-700 step proofs, we only lose about 18% of file size --
+        but the compressed proof is more pleasing to the eye) */
+    letters = "ABCDEFGHIJKLMNOPQRST"; /* LSB is base 20 */
+    digits = "UVWXY"; /* MSB's are base 5 */
+    labelChar = 'Z'; /* Was colon */
+
+    lettersLen = (long)strlen(letters);
+    digitsLen = (long)strlen(digits);
+
+    /* Initialize compressed proof label character weights */
+    /* Initialize compressed proof character types */
+    for (i = 0; i < 256; i++) {
+      chrWeight[i] = 0;
+      chrType[i] = 6; /* Illegal */
+    }
+    j = lettersLen;
+    for (i = 0; i < j; i++) {
+      chrWeight[(long)(letters[i])] = (unsigned char)i;
+      chrType[(long)(letters[i])] = 0; /* Letter */
+    }
+    j = digitsLen;
+    for (i = 0; i < j; i++) {
+      chrWeight[(long)(digits[i])] = (unsigned char)i;
+      chrType[(long)(digits[i])] = 1; /* Digit */
+    }
+    for (i = 0; i < 256; i++) {
+      if (isspace(i)) chrType[i] = 3; /* White space */
+    } /* Next i */
+    chrType[(long)(labelChar)] = 2; /* Colon */
+    chrType['$'] = 4; /* Dollar */
+    chrType['?'] = 5; /* Question mark */
+  }
+
+
+  if (g_Statement[statemNum].type != p_) {
+    bug(1724); /* should never get here */
+    return 4; /* Do nothing if not $p */
+  }
+  fbPtr = g_Statement[statemNum].proofSectionPtr; /* Start of proof section */
+  if (fbPtr[0] == 0) { /* The proof was never assigned (could be a $p statement
+                          with no $=; this would have been detected earlier) */
+    bug(1711);
+  }
+  fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+  if (fbPtr[0] != '(') { /* ( is flag for start of compressed proof */
+    bug(1712);
+  }
+
+  /* Make sure we have enough working space to hold the proof */
+  /* The worst case is less than the number of chars in the source,
+     plus the number of active hypotheses */
+
+  numOptHyp = nmbrLen(g_Statement[statemNum].optHypList);
+  if (g_Statement[statemNum].proofSectionLen + g_Statement[statemNum].numReqHyp
+      + numOptHyp > g_wrkProofMaxSize) {
+    if (g_wrkProofMaxSize) { /* Not the first allocation */
+      free(g_WrkProof.tokenSrcPtrNmbr);
+      free(g_WrkProof.tokenSrcPtrPntr);
+      free(g_WrkProof.stepSrcPtrNmbr);
+      free(g_WrkProof.stepSrcPtrPntr);
+      free(g_WrkProof.localLabelFlag);
+      free(g_WrkProof.hypAndLocLabel);
+      free(g_WrkProof.localLabelPool);
+      poolFree(g_WrkProof.proofString);
+      free(g_WrkProof.mathStringPtrs);
+      free(g_WrkProof.RPNStack);
+      free(g_WrkProof.compressedPfLabelMap);
+    }
+    g_wrkProofMaxSize = g_Statement[statemNum].proofSectionLen
+        + g_Statement[statemNum].numReqHyp + numOptHyp;
+    g_WrkProof.tokenSrcPtrNmbr = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(nmbrString));
+    g_WrkProof.tokenSrcPtrPntr = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(pntrString));
+    g_WrkProof.stepSrcPtrNmbr = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(nmbrString));
+    g_WrkProof.stepSrcPtrPntr = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(pntrString));
+    g_WrkProof.localLabelFlag = malloc((size_t)g_wrkProofMaxSize
+        * sizeof(flag));
+    g_WrkProof.hypAndLocLabel =
+        malloc((size_t)g_wrkProofMaxSize * sizeof(struct sortHypAndLoc));
+    g_WrkProof.localLabelPool = malloc((size_t)g_wrkProofMaxSize);
+    g_WrkProof.proofString =
+        poolFixedMalloc(g_wrkProofMaxSize * (long)(sizeof(nmbrString)));
+         /* Use poolFixedMalloc instead of poolMalloc so that it won't get
+            trimmed by memUsedPoolPurge. */
+    g_WrkProof.mathStringPtrs =
+        malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
+    g_WrkProof.RPNStack = malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
+    g_WrkProof.compressedPfLabelMap =
+         malloc((size_t)g_wrkProofMaxSize * sizeof(nmbrString));
+    if (!g_WrkProof.tokenSrcPtrNmbr ||
+        !g_WrkProof.tokenSrcPtrPntr ||
+        !g_WrkProof.stepSrcPtrNmbr ||
+        !g_WrkProof.stepSrcPtrPntr ||
+        !g_WrkProof.localLabelFlag ||
+        !g_WrkProof.hypAndLocLabel ||
+        !g_WrkProof.localLabelPool ||
+        !g_WrkProof.mathStringPtrs ||
+        !g_WrkProof.RPNStack
+        ) outOfMemory("#99 (g_WrkProof)");
+  }
+
+  /* Initialization for this proof */
+  g_WrkProof.errorCount = 0; /* Used as threshold for how many error msgs/proof */
+  g_WrkProof.numSteps = 0;
+  g_WrkProof.numTokens = 0;
+  g_WrkProof.numHypAndLoc = 0;
+  g_WrkProof.numLocalLabels = 0;
+  g_WrkProof.RPNStackPtr = 0;
+  g_WrkProof.localLabelPoolPtr = g_WrkProof.localLabelPool;
+
+  fbPtr++;
+  /* fbPtr points to the first token now. */
+
+
+  /****** This part of the code is heavily borrowed from the regular
+   ****** proof parsing, with local label and RPN handling removed,
+   ****** in order to easily parse the label section. */
+
+  /* First break up the label section of proof into tokens */
+  while (1) {
+    fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+    tokLength = proofTokenLen(fbPtr);
+    if (!tokLength) {
+      if (!g_WrkProof.errorCount) {
+        sourceError(fbPtr, 2, statemNum,
+            "A \")\" which ends the label list is not present.");
+      }
+      g_WrkProof.errorCount++;
+      if (returnFlag < 3) returnFlag = 3;
+      break;
+    }
+    if (fbPtr[0] == ')') {  /* End of label list */
+      fbPtr++;
+      break;
+    }
+    g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = fbPtr;
+    g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] = tokLength;
+    g_WrkProof.numSteps++;
+    fbPtr = fbPtr + tokLength;
+  }
+
+  fbStartProof = fbPtr; /* Save pointer to start of compressed proof */
+
+  /* Copy active (opt + req) hypotheses to hypAndLocLabel look-up table */
+  nmbrTmpPtr = g_Statement[statemNum].optHypList;
+  /* Transfer optional hypotheses */
+  while (1) {
+    i = nmbrTmpPtr[g_WrkProof.numHypAndLoc];
+    if (i == -1) break;
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum = i;
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName =
+        g_Statement[i].labelName;
+    g_WrkProof.numHypAndLoc++;
+  }
+  /* Transfer required hypotheses */
+  j = g_Statement[statemNum].numReqHyp;
+  nmbrTmpPtr = g_Statement[statemNum].reqHypList;
+  for (i = 0; i < j; i++) {
+    k = nmbrTmpPtr[i];
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelTokenNum = -1000 - k;
+       /* Required hypothesis labels are not allowed; the -1000 - k is a
+          flag that tells that they are required for error detection */
+    g_WrkProof.hypAndLocLabel[g_WrkProof.numHypAndLoc].labelName =
+        g_Statement[k].labelName;
+    g_WrkProof.numHypAndLoc++;
+  }
+
+  /* Sort the hypotheses by label name for lookup */
+  qsort(g_WrkProof.hypAndLocLabel, (size_t)(g_WrkProof.numHypAndLoc),
+      sizeof(struct sortHypAndLoc), hypAndLocSortCmp);
+
+  /* Build the proof string (actually just a list of labels) */
+  g_WrkProof.proofString[g_WrkProof.numSteps] = -1; /* End of proof */
+  nmbrZapLen(g_WrkProof.proofString, g_WrkProof.numSteps);
+     /* Zap mem pool actual length (because nmbrLen will be used later on this)*/
+
+  /* Scan proof string with the label list (not really proof steps; we're just
+     using the structure for convenience) */
+  for (step = 0; step < g_WrkProof.numSteps; step++) {
+    tokLength = g_WrkProof.stepSrcPtrNmbr[step];
+    fbPtr = g_WrkProof.stepSrcPtrPntr[step];
+
+    /* Temporarily zap the token's end with a null for string comparisons */
+    zapSave = fbPtr[tokLength];
+    fbPtr[tokLength] = 0; /* Zap source */
+
+    /* See if the proof token is a hypothesis */
+    voidPtr = (void *)bsearch(fbPtr, g_WrkProof.hypAndLocLabel,
+        (size_t)(g_WrkProof.numHypAndLoc), sizeof(struct sortHypAndLoc),
+        hypAndLocSrchCmp);
+    if (voidPtr) {
+      /* It's a hypothesis reference */
+      fbPtr[tokLength] = zapSave; /* Unzap source */
+      j = ((struct sortHypAndLoc *)voidPtr)->labelTokenNum;
+                                                       /* Label lookup number */
+
+      /* Make sure it's not a required hypothesis, which is implicitly
+         declared */
+      if (j < 0) { /* Minus is used as flag for required hypothesis */
+        j = -1000 - j; /* Restore it to prevent side effects of the error */
+        if (!g_WrkProof.errorCount) {
+          sourceError(fbPtr, tokLength, statemNum,
+              "Required hypotheses may not be explicitly declared.");
+        }
+        g_WrkProof.errorCount++;
+        /* Tolerate this error so we can continue to work
+           on proof in Proof Assistant */
+        if (returnFlag < 1) returnFlag = 1;
+      }
+
+      g_WrkProof.proofString[step] = j; /* Proof string */
+      if (j <= 0) bug(1713);
+      continue;
+    } /* End if hypothesis */
+
+    /* See if token is an assertion label */
+    voidPtr = (void *)bsearch(fbPtr, g_labelKeyBase, (size_t)g_numLabelKeys,
+        sizeof(long), labelSrchCmp);
+    fbPtr[tokLength] = zapSave; /* Unzap source */
+    if (!voidPtr) {
+      if (!g_WrkProof.errorCount) {
+        sourceError(fbPtr, tokLength, statemNum,
+         "This token is not the label of an assertion or optional hypothesis.");
+      }
+      g_WrkProof.errorCount++;
+      g_WrkProof.proofString[step] = -(long)'?';
+      if (returnFlag < 2) returnFlag = 2;
+      continue;
+    }
+
+    /* It's an assertion ($a or $p) */
+    j = *(long *)voidPtr; /* Statement number */
+    if (g_Statement[j].type != a_ && g_Statement[j].type != p_) bug(1714);
+    g_WrkProof.proofString[step] = j; /* Proof string */
+
+    if (j >= statemNum) { /* Error */
+      if (!g_WrkProof.errorCount) {
+        if (j == statemNum) {
+          sourceError(fbPtr, tokLength, statemNum, cat(
+              "The label at proof step ",
+              str((double)step + 1),
+             " is the label of this statement.  A statement may not be used to",
+              " prove itself.",NULL));
+        } else {
+          assignStmtFileAndLineNum(j);
+          sourceError(fbPtr, tokLength, statemNum, cat(
+              "The label \"", g_Statement[j].labelName, "\" at proof step ",
+              str((double)step + 1),
+              " is the label of a future statement (at line ",
+              str((double)(g_Statement[j].lineNum)),
+              " in file ",g_Statement[j].fileName,
+              ").  Only previous statements may be referenced.",
+              NULL));
+        }
+      }
+      g_WrkProof.errorCount++;
+      if (returnFlag < 2) returnFlag = 2;
+    }
+
+  } /* Next step */
+
+  /******* Create the starting label map (local labels will be
+           added as they are found) *****/
+  g_WrkProof.compressedPfNumLabels = g_Statement[statemNum].numReqHyp;
+  nmbrTmpPtr = g_Statement[statemNum].reqHypList;
+  for (i = 0; i < g_WrkProof.compressedPfNumLabels; i++) {
+    g_WrkProof.compressedPfLabelMap[i] = nmbrTmpPtr[i];
+  }
+  for (i = 0; i < g_WrkProof.numSteps; i++) {
+    g_WrkProof.compressedPfLabelMap[i + g_WrkProof.compressedPfNumLabels] =
+        g_WrkProof.proofString[i];
+  }
+  g_WrkProof.compressedPfNumLabels = g_WrkProof.compressedPfNumLabels +
+      g_WrkProof.numSteps;
+
+  /* Re-initialization for the actual proof */
+  g_WrkProof.numSteps = 0;
+  g_WrkProof.RPNStackPtr = 0;
+
+  /******* Parse the compressed part of the proof *****/
+
+  /* Check to see if the old buggy compression is used.  If so,
+     warn the user to reformat, and switch to the buggy algorithm so that
+     parsing can proceed. */
+  bggyProofLen = g_Statement[statemNum].proofSectionLen -
+             (fbPtr - g_Statement[statemNum].proofSectionPtr);
+  /* Zap a zero at the end of the proof so we can use C string operations */
+  bggyZapSave = fbPtr[bggyProofLen];
+  fbPtr[bggyProofLen] = 0;
+  /* If the proof has "UVA" but doesn't have "UUA", it means the buggy
+     algorithm was used. */
+  bggyAlgo = 0;
+  if (strstr(fbPtr, "UV") != NULL) {
+    if (strstr(fbPtr, "UU") == NULL) {
+      bggyAlgo = 1;
+      print2("?Warning: the proof of \"%s\" uses obsolete compression.\n",
+          g_Statement[statemNum].labelName);
+      print2(" Please SAVE PROOF * / COMPRESSED to reformat your proofs.\n");
+    }
+  }
+  fbPtr[bggyProofLen] = bggyZapSave;
+
+  /* (Build the proof string and check the RPN stack) */
+  fbPtr = fbStartProof;
+  breakFlag = 0;
+  labelMapIndex = 0;
+  while (1) {
+    switch (chrType[(long)(fbPtr[0])]) {
+      case 0: /* "Letter" (i.e. A...T) */
+        if (!labelMapIndex) labelStart = fbPtr; /* Save for error msg */
+
+        /* Save pointer to source file vs. step for error messages */
+        tokLength = fbPtr - labelStart + 1; /* Token length */
+        g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] = tokLength;
+        g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = labelStart; /* Token ptr */
+
+        /* Corrected algorithm provided by Marnix Klooster. */
+        /* Decoding can be done as follows:
+             * n := 0
+             * for each character c:
+                * if c in ['U'..'Y']: n := n * 5 + (c - 'U' + 1)
+                * if c in ['A'..'T']: n := n * 20 + (c - 'A' + 1) */
+        labelMapIndex = labelMapIndex * lettersLen +
+            chrWeight[(long)(fbPtr[0])];
+        if (labelMapIndex >= g_WrkProof.compressedPfNumLabels) {
+          if (!g_WrkProof.errorCount) {
+            sourceError(labelStart, tokLength, statemNum, cat(
+     "This compressed label reference is outside the range of the label list.",
+                "  The compressed label value is ", str((double)labelMapIndex),
+                " but the largest label defined is ",
+                str((double)(g_WrkProof.compressedPfNumLabels - 1)), ".", NULL));
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+          labelMapIndex = 0; /* Make it something legal to avoid side effects */
+        }
+
+        stmt = g_WrkProof.compressedPfLabelMap[labelMapIndex];
+        g_WrkProof.proofString[g_WrkProof.numSteps] = stmt;
+
+        /* Update stack */
+        hypLocUnkFlag = 0;
+        if (stmt < 0) { /* Local label or '?' */
+          hypLocUnkFlag = 1;
+        } else {
+          if (g_Statement[stmt].type != (char)a_ &&
+              g_Statement[stmt].type != (char)p_) hypLocUnkFlag = 1;
+                                                               /* Hypothesis */
+        }
+        if (hypLocUnkFlag) { /* Hypothesis, local label ref, or unknown step */
+          g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
+          g_WrkProof.RPNStackPtr++;
+        } else { /* An assertion */
+
+          /* It's a valid assertion, so pop the stack */
+          numReqHyp = g_Statement[stmt].numReqHyp;
+
+          /* Error check for exhausted stack */
+          if (g_WrkProof.RPNStackPtr < numReqHyp) { /* Stack exhausted -- error */
+            if (!g_WrkProof.errorCount) {
+              tmpStrPtr = shortDumpRPNStack();
+              if (strcmp(left(tmpStrPtr,18),"RPN stack is empty")) {
+                i = instr(1,tmpStrPtr,"contains ");
+                let(&tmpStrPtr,cat(left(tmpStrPtr,i + 7)," only",
+                  right(tmpStrPtr,i + 8),
+                  NULL));
+              }
+              if (numReqHyp == 1) {
+                let(&tmpStrPtr,cat("a hypothesis but the ",tmpStrPtr,NULL));
+              } else {
+                let(&tmpStrPtr,cat(str((double)numReqHyp)," hypotheses but the ",tmpStrPtr,
+                    NULL));
+              }
+              sourceError(fbPtr, tokLength, statemNum, cat(
+                  "At proof step ",
+                  str((double)(g_WrkProof.numSteps + 1)),", statement \"",
+                  g_Statement[stmt].labelName,"\" requires ",
+                  tmpStrPtr,".",NULL));
+              free_vstring(tmpStrPtr);
+            }
+            /* Treat it like an unknown step so stack won't get exhausted */
+            g_WrkProof.errorCount++;
+            g_WrkProof.proofString[g_WrkProof.numSteps] = -(long)'?';
+            /* Push the stack */
+            g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
+            g_WrkProof.RPNStackPtr++;
+            if (returnFlag < 3) returnFlag = 3;
+            continue;
+          } /* End if stack exhausted */
+
+          numReqHyp = g_Statement[stmt].numReqHyp;
+
+          /* Pop the stack */
+          g_WrkProof.RPNStackPtr = g_WrkProof.RPNStackPtr - numReqHyp;
+          g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
+          g_WrkProof.RPNStackPtr++;
+
+        }
+
+        g_WrkProof.numSteps++;
+        labelMapIndex = 0; /* Reset it for next label */
+        break;
+
+      case 1: /* "Digit" (i.e. U...Y) */
+        if (!labelMapIndex) {
+          labelMapIndex = chrWeight[(long)(fbPtr[0])] + 1;
+          labelStart = fbPtr; /* Save label start for error msg */
+        } else {
+          labelMapIndex = labelMapIndex * digitsLen +
+              chrWeight[(long)(fbPtr[0])] + 1;
+          if (bggyAlgo) labelMapIndex--; /* Adjust for buggy algorithm */
+        }
+        break;
+
+      case 2: /* "Colon" (i.e. Z) */
+        if (labelMapIndex) { /* In the middle of some digits */
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, 1, statemNum,
+             "A compressed label character was expected here.");
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+          labelMapIndex = 0;
+        }
+
+        /* Put local label in label map */
+        g_WrkProof.compressedPfLabelMap[g_WrkProof.compressedPfNumLabels] =
+          -1000 - (g_WrkProof.numSteps - 1);
+        g_WrkProof.compressedPfNumLabels++;
+
+        hypLocUnkFlag = 0;
+
+        if (g_WrkProof.numSteps == 0) {
+          /* This will happen if labelChar (Z) is in 1st char pos of
+             compressed proof */
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, 1, statemNum, cat(
+              "A local label character must occur after a proof step.",NULL));
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+          /* We want to break here to prevent out of bound g_WrkProof.proofString
+             index below. */
+          break;
+        }
+
+        stmt = g_WrkProof.proofString[g_WrkProof.numSteps - 1];
+        if (stmt < 0) { /* Local label or '?' */
+          hypLocUnkFlag = 1;
+        } else {
+          if (g_Statement[stmt].type != (char)a_ &&
+              g_Statement[stmt].type != (char)p_) hypLocUnkFlag = 1;
+                                                                /* Hypothesis */
+        }
+        if (hypLocUnkFlag) { /* Hypothesis, local label ref, or unknown step */
+          /* If local label references a hypothesis or other local label,
+             it is not allowed because it complicates the language but doesn't
+             buy anything; would make $e to $f assignments harder to detect */
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, 1, statemNum, cat(
+                "The hypothesis or local label reference at proof step ",
+                str((double)(g_WrkProof.numSteps)),
+                " declares a local label.  Only \"$a\" and \"$p\" statement",
+                " labels may have local label declarations.",NULL));
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+        }
+
+        break;
+
+      case 3: /* White space */
+        break;
+
+      case 4: /* Dollar */
+        /* See if we're at the end of the statement */
+        if (fbPtr[1] == '.') {
+          breakFlag = 1;
+          break;
+        }
+        /* Otherwise, it must be a comment */
+        if (fbPtr[1] != '(' && fbPtr[1] != '!') {
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr + 1, 1, statemNum,
+             "Expected \".\", \"(\", or \"!\" here.");
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+        } else {
+          fbPtr = fbPtr + whiteSpaceLen(fbPtr) - 1; /* -1 because
+                  fbPtr will get incremented at end of loop */
+        }
+        break;
+
+      case 5: /* Question mark */
+        if (labelMapIndex) { /* In the middle of some digits */
+          if (!g_WrkProof.errorCount) {
+            sourceError(fbPtr, 1, statemNum,
+             "A compressed label character was expected here.");
+          }
+          g_WrkProof.errorCount++;
+          if (returnFlag < 2) returnFlag = 2;
+          labelMapIndex = 0;
+        }
+
+        /* Save pointer to source file vs. step for error messages */
+        g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] = 1;
+        g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = fbPtr; /* Token ptr */
+
+        g_WrkProof.proofString[g_WrkProof.numSteps] = -(long)'?';
+        /* returnFlag = 1 means that proof has unknown steps */
+        /* Ensure that a proof with unknown steps doesn't
+           reset the severe error flag if returnFlag > 1 */
+        if (returnFlag < 1) returnFlag = 1;
+
+        /* Update stack */
+        g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
+        g_WrkProof.RPNStackPtr++;
+
+        g_WrkProof.numSteps++;
+        break;
+
+      case 6: /* Illegal */
+        if (!g_WrkProof.errorCount) {
+          sourceError(fbPtr, 1, statemNum,
+           "This character is not legal in a compressed proof.");
+        }
+        g_WrkProof.errorCount++;
+        if (returnFlag < 2) returnFlag = 2;
+        break;
+      default:
+        bug(1715);
+        break;
+    } /* End switch chrType[fbPtr[0]] */
+
+    if (breakFlag) break;
+    fbPtr++;
+
+  } /* End while (1) */
+
+  if (labelMapIndex) { /* In the middle of some digits */
+    if (!g_WrkProof.errorCount) {
+      sourceError(fbPtr, 1, statemNum,
+       "A compressed label character was expected here.");
+    }
+    g_WrkProof.errorCount++;
+    if (returnFlag < 2) returnFlag = 2;
+  }
+
+  /* If proof is empty, make it have one unknown step */
+  if (g_WrkProof.numSteps == 0) {
+
+    /* For now, this is an error. */
+    if (!g_WrkProof.errorCount) {
+      sourceError(fbPtr, 2, statemNum,
+          "The proof is empty.  If you don't know the proof, make it a \"?\".");
+    }
+    g_WrkProof.errorCount++;
+
+    /* Save pointer to source file vs. step for error messages */
+    g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps] = 1;
+    g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps] = fbPtr; /* Token ptr */
+
+    g_WrkProof.proofString[g_WrkProof.numSteps] = -(long)'?';
+    if (returnFlag < 1) returnFlag = 1; /* Flag that proof has unknown steps */
+
+    /* Update stack */
+    g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = g_WrkProof.numSteps;
+    g_WrkProof.RPNStackPtr++;
+
+    g_WrkProof.numSteps++;
+  }
+
+  g_WrkProof.proofString[g_WrkProof.numSteps] = -1; /* End of proof */
+  nmbrZapLen(g_WrkProof.proofString, g_WrkProof.numSteps);
+    /* Zap mem pool actual length (because nmbrLen will be used later on this)*/
+
+  /* The stack should have one entry */
+  if (g_WrkProof.RPNStackPtr != 1) {
+    tmpStrPtr = shortDumpRPNStack();
+    fbPtr = g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps - 1];
+    if (!g_WrkProof.errorCount) {
+      sourceError(fbPtr, proofTokenLen(fbPtr), statemNum,
+          cat("After proof step ",
+          str((double)(g_WrkProof.numSteps))," (the last step), the ",
+          tmpStrPtr,".  It should contain exactly one entry.",NULL));
+    }
+    g_WrkProof.errorCount++;
+   if (returnFlag < 3) returnFlag = 3;
+  }
+
+  g_WrkProof.errorSeverity = returnFlag;
+  return (returnFlag);
+
+} /* parseCompressedProof */
+
+
+/* The caller must deallocate the returned nmbrString! */
+/* This function just gets the proof so the caller doesn't have to worry
+   about cleaning up the g_WrkProof structure.  The returned proof is normal
+   or compressed depending on the .mm source; called nmbrUnsquishProof() to
+   make sure it is uncompressed if required. */
+/* If there is a severe error in the proof, a 1-step proof with "?" will
+   be returned. */
+/* If printFlag = 1, then error messages are printed, otherwise they aren't.
+   This is only partially implemented; some errors may still result in a
+   printout.  TODO: pass printFlag to parseProof(), verifyProof() */
+/* TODO: use this function to simplify some code that calls parseProof
+   directly. */
+nmbrString *getProof(long statemNum, flag printFlag) {
+  nmbrString_def(proof);
+  parseProof(statemNum);
+  /* We do not need verifyProof() since we don't care about the math
+     strings for the proof steps in this function. */
+  /* verifyProof(statemNum); */ /* Necessary to set RPN stack ptrs
+                             before calling cleanWrkProof() */
+  if (g_WrkProof.errorSeverity > 1) {
+    if (printFlag) print2(
+         "The starting proof has a severe error.  It will not be used.\n");
+    nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING, -(long)'?'));
+  } else {
+    nmbrLet(&proof, g_WrkProof.proofString);
+  }
+  /* Note: the g_WrkProof structure is never deallocated but grows to
+     accommodate the largest proof found so far.  The ERASE command will
+     deallocate it, though.   cleanWrkProof() just deallocates math strings
+     assigned by verifyProof() that aren't needed by this function. */
+  /* cleanWrkProof(); */ /* Deallocate verifyProof() storage */
+  return proof;
+} /* getProof */
+
+
+
+void rawSourceError(char *startFile, char *ptr, long tokLen, vstring errMsg) {
+  char *startLine;
+  char *endLine;
+  vstring_def(errLine);
+  vstring_def(errorMsg);
+  vstring_def(fileName);
+  long lineNum;
+
+  let(&errorMsg, errMsg); /* Prevent deallocation of errMsg */
+
+  fileName = getFileAndLineNum(startFile/*=g_sourcePtr*/, ptr, &lineNum);
+
+  /* Get the line with the error on it */
+  startLine = ptr;
+  while (startLine[0] != '\n' && startLine > startFile) {
+    startLine--;
+  }
+  if (startLine[0] == '\n' && startLine != ptr /* In case of 0-length line */)
+    startLine++; /* Go to 1st char on line */
+  endLine = ptr;
+  while (endLine[0] != '\n' && endLine[0] != 0) {
+    endLine++;
+  }
+  endLine--;
+  let(&errLine, space(endLine - startLine + 1));
+  if (endLine - startLine + 1 < 0) bug(1721);
+  memcpy(errLine, startLine, (size_t)(endLine - startLine) + 1);
+  errorMessage(errLine, lineNum, ptr - startLine + 1, tokLen, errorMsg,
+      fileName, 0, (char)error_);
+  print2("\n");
+  free_vstring(errLine);
+  free_vstring(errorMsg);
+  free_vstring(fileName);
+} /* rawSourceError */
+
+/* The global g_sourcePtr is assumed to point to the start of the raw input
+     buffer.
+   The global g_sourceLen is assumed to be length of the raw input buffer.
+   The global g_IncludeCall array is referenced. */
+void sourceError(char *ptr, long tokLen, long stmtNum, vstring errMsg)
+{
+  char *startLine;
+  char *endLine;
+  vstring_def(errLine);
+  long lineNum;
+  vstring_def(fileName);
+  vstring_def(errorMsg);
+
+  /* Used for the case where a source file section has been modified */
+  char *locSourcePtr;
+
+  /* Initialize local pointers to raw input source */
+  locSourcePtr = g_sourcePtr;
+
+  let(&errorMsg, errMsg); /* errMsg may become deallocated if this function is
+                         called with a string function argument (cat, etc.) */
+
+  if (!stmtNum) {
+    lineNum = 0;
+    goto SKIP_LINE_NUM; /* This isn't a source file parse */
+  }
+  if (ptr < g_sourcePtr || ptr > g_sourcePtr + g_sourceLen) {
+    /* The pointer is outside the raw input buffer, so it must be a
+       SAVEd proof or other overwritten section, so there is no line number. */
+    /* Reassign the beginning and end of the source pointer to the
+       changed section */
+    if (g_Statement[stmtNum].labelSectionChanged == 1
+         && ptr >= g_Statement[stmtNum].labelSectionPtr
+         && ptr <= g_Statement[stmtNum].labelSectionPtr
+             + g_Statement[stmtNum].labelSectionLen) {
+      locSourcePtr = g_Statement[stmtNum].labelSectionPtr;
+    } else if (g_Statement[stmtNum].mathSectionChanged == 1
+         && ptr >= g_Statement[stmtNum].mathSectionPtr
+         && ptr <= g_Statement[stmtNum].mathSectionPtr
+             + g_Statement[stmtNum].mathSectionLen) {
+      locSourcePtr = g_Statement[stmtNum].mathSectionPtr;
+    } else if (g_Statement[stmtNum].proofSectionChanged == 1
+         && ptr >= g_Statement[stmtNum].proofSectionPtr
+         && ptr <= g_Statement[stmtNum].proofSectionPtr
+             + g_Statement[stmtNum].proofSectionLen) {
+      locSourcePtr = g_Statement[stmtNum].proofSectionPtr;
+    } else {
+      /* ptr points to neither the original source nor a modified section */
+      bug(1741);
+    }
+
+    lineNum = 0;
+    goto SKIP_LINE_NUM;
+  }
+
+  /*free_vstring(fileName);*/ /* No need - already assigned to empty string */
+  fileName = getFileAndLineNum(locSourcePtr/*=g_sourcePtr here*/, ptr, &lineNum);
+
+ SKIP_LINE_NUM:
+  /* Get the line with the error on it */
+  if (lineNum != 0 && ptr > locSourcePtr) {
+    startLine = ptr - 1; /* Allows pointer to point to \n. */
+  } else {
+    /* Special case:  Error message starts at beginning of file or
+       the beginning of a changed section. */
+    /* Or, it's a non-source statement; must not point to \n. */
+    startLine = ptr;
+  }
+
+  /* Scan back to beginning of line with error */
+  while (startLine[0] != '\n' && startLine > locSourcePtr) startLine--;
+  if (startLine[0] == '\n') startLine++;
+
+  /* Scan forward to end of line with error */
+  endLine = ptr;
+  while (endLine[0] != '\n' && endLine[0] != 0) {
+    endLine++;
+  }
+  endLine--;
+
+  /* Save line with error (with no newline on it) */
+  let(&errLine, space(endLine - startLine + 1));
+  memcpy(errLine, startLine, (size_t)(endLine - startLine) + 1);
+
+  if (!lineNum) {
+    /* Not a source file parse */
+    errorMessage(errLine, lineNum, ptr - startLine + 1, tokLen, errorMsg,
+        NULL, stmtNum, (char)error_);
+  } else {
+    errorMessage(errLine, lineNum,
+        ptr - startLine + 1, tokLen, /* column */
+        errorMsg,
+        fileName,
+        stmtNum,
+        (char)error_ /* severity */);
+  }
+  free_vstring(errLine);
+  free_vstring(errorMsg);
+  free_vstring(fileName);
+} /* sourceError */
+
+
+void mathTokenError(long tokenNum /* 0 is 1st one */,
+    nmbrString *tokenList, long stmtNum, vstring errMsg)
+{
+  long i;
+  char *fbPtr;
+  fbPtr = g_Statement[stmtNum].mathSectionPtr;
+  fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+  /* Scan past the tokens before the desired one. */
+  /* We use the parsed token length rather than tokenLen() to
+     account for adjacent tokens with no white space. */
+  for (i = 0; i < tokenNum; i++) {
+    fbPtr = fbPtr + g_MathToken[tokenList[i]].length;
+    fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+  }
+  sourceError(fbPtr, g_MathToken[tokenList[tokenNum]].length,
+      stmtNum, errMsg);
+} /* mathTokenError */
+
+vstring shortDumpRPNStack(void) {
+  /* The caller must deallocate the returned string. */
+  vstring_def(tmpStr);
+  vstring_def(tmpStr2);
+  long i, k, m;
+
+  for (i = 0; i < g_WrkProof.RPNStackPtr; i++) {
+     k = g_WrkProof.RPNStack[i]; /* Step */
+     let(&tmpStr,space(g_WrkProof.stepSrcPtrNmbr[k]));
+     memcpy(tmpStr,g_WrkProof.stepSrcPtrPntr[k],
+         (size_t)(g_WrkProof.stepSrcPtrNmbr[k])); /* Label at step */
+     let(&tmpStr2,cat(
+         tmpStr2,", ","\"",tmpStr,"\" (step ", str((double)k + 1),")",NULL));
+  }
+  let(&tmpStr2,right(tmpStr2,3));
+  if (g_WrkProof.RPNStackPtr == 2) {
+    m = instr(1, tmpStr2, ",");
+    let(&tmpStr2,cat(left(tmpStr2,m - 1)," and ",
+        right(tmpStr2,m + 1),NULL));
+  }
+  if (g_WrkProof.RPNStackPtr > 2) {
+    for (m = (long)strlen(tmpStr2); m > 0; m--) { /* Find last comma */
+      if (tmpStr2[m - 1] == ',') break;
+    }
+    let(&tmpStr2,cat(left(tmpStr2,m - 1),", and ",
+        right(tmpStr2,m + 1),NULL));
+  }
+  if (g_WrkProof.RPNStackPtr == 1) {
+    let(&tmpStr2,cat("one entry, ",tmpStr2,NULL));
+  } else {
+    let(&tmpStr2,cat(str((double)(g_WrkProof.RPNStackPtr))," entries: ",tmpStr2,NULL));
+  }
+  let(&tmpStr2,cat("RPN stack contains ",tmpStr2,NULL));
+  if (g_WrkProof.RPNStackPtr == 0) let(&tmpStr2,"RPN stack is empty");
+  free_vstring(tmpStr);
+  return tmpStr2;
+} /* shortDumpRPNStack */
+
+/* ???Todo:  use this elsewhere in mmpars.c to modularize this lookup */
+/* Lookup $a or $p label and return statement number.
+   Return -1 if not found. */
+long lookupLabel(const char *label)
+{
+  void *voidPtr; /* bsearch returned value */
+  long statemNum;
+  /* Find the statement number */
+  voidPtr = (void *)bsearch(label, g_labelKeyBase, (size_t)g_numLabelKeys,
+      sizeof(long), labelSrchCmp);
+  if (!voidPtr) {
+    return -1;
+  }
+  statemNum = (*(long *)voidPtr); /* Statement number */
+  if (g_Statement[statemNum].type != a_ && g_Statement[statemNum].type != p_)
+    bug(1718);
+  return statemNum;
+} /* lookupLabel */
+
+
+/* Label comparison for qsort */
+int labelSortCmp(const void *key1, const void *key2) {
+  /* Returns -1 if key1 < key2, 0 if equal, 1 if key1 > key2 */
+  return strcmp(g_Statement[ *((long *)key1) ].labelName,
+      g_Statement[ *((long *)key2) ].labelName);
+} /* labelSortCmp */
+
+
+/* Label comparison for bsearch */
+int labelSrchCmp(const void *key, const void *data) {
+  /* Returns -1 if key < data, 0 if equal, 1 if key > data */
+  return strcmp(key, g_Statement[ *((long *)data) ].labelName);
+} /* labelSrchCmp */
+
+
+/* Math symbol comparison for qsort */
+int mathSortCmp(const void *key1, const void *key2) {
+  /* Returns -1 if key1 < key2, 0 if equal, 1 if key1 > key2 */
+  return strcmp(g_MathToken[ *((long *)key1) ].tokenName,
+      g_MathToken[ *((long *)key2) ].tokenName);
+}
+
+
+/* Math symbol comparison for bsearch */
+/* Here, key is pointer to a character string. */
+int mathSrchCmp(const void *key, const void *data) {
+  /* Returns -1 if key < data, 0 if equal, 1 if key > data */
+  return strcmp(key, g_MathToken[ *((long *)data) ].tokenName);
+}
+
+
+/* Hypotheses and local label comparison for qsort */
+int hypAndLocSortCmp(const void *key1, const void *key2) {
+  /* Returns -1 if key1 < key2, 0 if equal, 1 if key1 > key2 */
+  return strcmp(
+    ((struct sortHypAndLoc *)key1)->labelName,
+    ((struct sortHypAndLoc *)key2)->labelName);
+}
+
+
+/* Hypotheses and local label comparison for bsearch */
+/* Here, key is pointer to a character string. */
+int hypAndLocSrchCmp(const void *key, const void *data) {
+  /* Returns -1 if key < data, 0 if equal, 1 if key > data */
+  return strcmp(key, ((struct sortHypAndLoc *)data)->labelName);
+}
+
+
+/* This function returns the length of the white space starting at ptr.
+   Comments are considered white space.  ptr should point to the first character
+   of the white space.  If ptr does not point to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned. */
+long whiteSpaceLen(char *ptr) {
+  long i = 0;
+  char tmpchr;
+  char *ptr1;
+  while (1) {
+    tmpchr = ptr[i];
+    if (!tmpchr) return (i); /* End of string */
+    if (tmpchr == '$') {
+      if (ptr[i + 1] == '(') {
+        while (1) {
+          /*ptr1 = strchr(ptr + i + 2, '$'); */
+          /* in-line code for speed */
+          /* (for the lcc-win32 compiler, this speeds it up from 94 sec
+              for set.mm read to 4 sec) */
+          for (ptr1 = ptr + i + 2; ptr1[0] != '$'; ptr1++) {
+            if (ptr1[0] == 0) {
+              if ('$' != 0)
+                ptr1 = NULL;
+              break;
+            }
+          }
+          /* end in-line strchr code */
+          if (!ptr1) {
+            return i + (long)strlen(&ptr[i]); /* Unterminated comment - goto EOF */
+          }
+          if (ptr1[1] == ')') break;
+          i = ptr1 - ptr;
+        }
+        i = ptr1 - ptr + 2;
+        continue;
+      } else {
+        if (ptr[i + 1] == '!') {
+          ptr1 = strchr(ptr + i + 2, '\n');
+          if (!ptr1) bug(1716);
+          i = ptr1 - ptr + 1;
+          continue;
+        }
+        return i;
+      }
+    } /* if (tmpchr == '$') */
+    if (isgraph((unsigned char)tmpchr)) return i;
+    i++;
+  }
+  return 0; /* Dummy return - never happens */
+} /* whiteSpaceLen */
+
+
+/* For .mm file splitting */
+/* This function is like whiteSpaceLen() except that comments are NOT
+   considered white space.  ptr should point to the first character
+   of the white space.  If ptr does not point to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned. */
+long rawWhiteSpaceLen(char *ptr) {
+  long i = 0;
+  char tmpchr;
+  while (1) {
+    tmpchr = ptr[i];
+    if (!tmpchr) return (i); /* End of string */
+    if (isgraph((unsigned char)tmpchr)) return i;
+    i++;
+  }
+  return 0; /* Dummy return - never happens */
+} /* rawWhiteSpaceLen */
+
+
+/* This function returns the length of the token (non-white-space) starting at
+   ptr.  Comments are considered white space.  ptr should point to the first
+   character of the token.  If ptr points to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned.  If ptr
+   points to a keyword, 0 is returned.  A keyword ends a token. */
+/* Tokens may be of the form "$nn"; this is tolerated (used in parsing
+   user math strings in parseMathTokens()).  An (illegal) token of this form
+   in the source will be detected earlier, so this won't cause
+   syntax violations to slip by in the source. */
+long tokenLen(char *ptr)
+{
+  long i = 0;
+  char tmpchr;
+  while (1) {
+    tmpchr = ptr[i];
+    if (tmpchr == '$') {
+      if (ptr[i + 1] == '$') { /* '$$' character */
+        i = i + 2;
+        continue;
+      } else {
+        /* Tolerate digit after "$" */
+        if (ptr[i + 1] >= '0' && ptr[i + 1] <= '9') {
+          i = i + 2;
+          continue;
+        } else {
+          return i; /* Keyword or comment */
+        }
+      }
+    }
+    if (!isgraph((unsigned char)tmpchr)) return i; /* White space or null */
+    i++;
+  }
+  return 0; /* Dummy return (never happens) */
+} /* tokenLen */
+
+
+/* This function returns the length of the token (non-white-space) starting at
+   ptr.  Comments are considered white space.  ptr should point to the first
+   character of the token.  If ptr points to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned. */
+/* Unlike tokenLen(), keywords are not treated as special.  In particular:
+   if ptr points to a keyword, 0 is NOT returned (instead, 2 is returned),
+   and a keyword does NOT end a token (which is a relic of days before
+   whitespace surrounding a token was part of the spec, but still serves
+   a useful purpose in token() for friendlier error detection). */
+long rawTokenLen(char *ptr)
+{
+  long i = 0;
+  char tmpchr;
+  while (1) {
+    tmpchr = ptr[i];
+    if (!isgraph((unsigned char)tmpchr)) return i; /* White space or null */
+    i++;
+  }
+  return 0; /* Dummy return (never happens) */
+} /* rawTokenLen */
+
+
+/* This function returns the length of the proof token starting at
+   ptr.  Comments are considered white space.  ptr should point to the first
+   character of the token.  If ptr points to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned.  If ptr
+   points to a keyword, 0 is returned.  A keyword ends a token.
+   ":" and "?" and "(" and ")" and "=" are considered tokens. */
+long proofTokenLen(char *ptr)
+{
+  long i = 0;
+  char tmpchr;
+  if (ptr[0] == ':') return 1; /* The token is a colon */
+  if (ptr[0] == '?') return 1; /* The token is a "?" */
+  if (ptr[0] == '(') return 1; /* The token is a "(" (compressed proof) */
+  if (ptr[0] == ')') return 1; /* The token is a ")" (compressed proof) */
+  if (ptr[0] == '=') return 1; /* The token is a "=" (/EXPLICIT proof) */
+  while (1) {
+    tmpchr = ptr[i];
+    if (tmpchr == '$') {
+      if (ptr[i + 1] == '$') { /* '$$' character */
+        i = i + 2;
+        continue;
+      } else {
+        return i; /* Keyword or comment */
+      }
+    }
+    if (!isgraph((unsigned char)tmpchr)) return i; /* White space or null */
+    if (tmpchr == ':') return i; /* Colon ends a token */
+    if (tmpchr == '?') return i; /* "?" ends a token */
+    if (tmpchr == '(') return i; /* "(" ends a token */
+    if (tmpchr == ')') return i; /* ")" ends a token */
+    if (tmpchr == '=') return i; /* "=" ends a token */
+    i++;
+  }
+  return 0; /* Dummy return - never happens */
+}
+
+
+/* Counts the number of \n between start for length chars.
+   If length = -1, then use end-of-string 0 to stop.
+   If length >= 0, then scan at most length chars, but stop
+       if end-of-string 0 is found. */
+long countLines(const char *start, long length) {
+  long lines, i;
+  lines = 0;
+  if (length == -1) {
+    i = 0;
+    while (1) {
+      if (start[i] == '\n') lines++;
+      if (start[i] == 0) break;
+      i++;
+    }
+  } else {
+    for (i = 0; i < length; i++) {
+      if (start[i] == '\n') lines++;
+      if (start[i] == 0) break;
+    }
+  }
+  return lines;
+} /* countLines */
+
+
+/* Return (for output) the complete contents of a statement, including all
+   white space and comments, from first token through all white space and
+   comments after last token. */
+/* This allows us to modify the input file with Metamath. */
+/* Note: the text near end of file is obtained from g_Statement[g_statements
+   + 1] */
+/* ???This does not yet implement restoration of the various input files;
+      all included files are merged into one. */
+/* Caller must deallocated returned string. */
+/* reformatFlag= 0: WRITE SOURCE, 1: WRITE SOURCE / FORMAT,
+   2: WRITE SOURCE / REWRAP */
+/* Note that the labelSection, mathSection, and proofSection do not
+   contain keywords ($a, $p,...; $=; $.).  The keywords are added
+   by this function when the statement is written. */
+/* This must be called in sequence for all statements,
+   since the previous statement is needed to populate dollarDpos
+   and previousType */
+vstring outputStatement(long stmt, flag reformatFlag) {
+  vstring_def(labelSection);
+  vstring_def(mathSection);
+  vstring_def(proofSection);
+  vstring_def(labelSectionSave);
+  vstring_def(mathSectionSave);
+  vstring_def(proofSectionSave);
+  vstring_def(output);
+  /* For reformatting: */
+  long slen; /* To save local string length */
+  long pos;
+  long indent;
+  static long dollarDpos = 0;
+  static char previousType = illegal_;  /* '?' in mmdata.h */
+  long commentStart;
+  long commentEnd;
+  vstring_def(comment);
+  vstring_def(str1);
+  long length;
+  flag nowrapHtml;
+
+  /* Re-initialize static variables for a second 'write source' */
+  if (stmt == 1) {
+    previousType = illegal_;  /* '?' in mmdata.h */
+    dollarDpos = 0;
+  }
+
+  let(&labelSection, space(g_Statement[stmt].labelSectionLen));
+  memcpy(labelSection, g_Statement[stmt].labelSectionPtr,
+      (size_t)(g_Statement[stmt].labelSectionLen));
+
+  if (stmt == g_statements + 1) return labelSection; /* Special case - EOF */
+
+  let(&mathSection, space(g_Statement[stmt].mathSectionLen));
+  memcpy(mathSection, g_Statement[stmt].mathSectionPtr,
+      (size_t)(g_Statement[stmt].mathSectionLen));
+
+  let(&proofSection, space(g_Statement[stmt].proofSectionLen));
+  memcpy(proofSection, g_Statement[stmt].proofSectionPtr,
+      (size_t)(g_Statement[stmt].proofSectionLen));
+
+  let(&labelSectionSave, labelSection);
+  let(&mathSectionSave, mathSection);
+  let(&proofSectionSave, proofSection);
+
+
+  /* Reformat statements to match the current set.mm convention */
+  if (reformatFlag > 0) {  /* 1 = WRITE SOURCE / FORMAT or 2 = / REWRAP */
+    /* Put standard indentation before the ${, etc. */
+#define INDENT_FIRST 2
+#define INDENT_INCR 2
+    indent = INDENT_FIRST + (INDENT_INCR * g_Statement[stmt].scope);
+    /* g_Statement[stmt].scope is at start of stmt not end; adjust $} */
+    if (g_Statement[stmt].type == rb_) indent = indent - INDENT_INCR;
+
+    /* Untab the label section */
+    if (strchr(labelSection, '\t') != NULL) { /* Only if has tab (speedup) */
+      let(&labelSection, edit(labelSection, 2048/*untab*/));
+    }
+    /* Untab the math section */
+    if (strchr(mathSection, '\t') != NULL) { /* Only if has tab (speedup) */
+      let(&mathSection, edit(mathSection, 2048/*untab*/));
+    }
+
+    /* Reformat the label section */
+
+    /* Remove spaces a end of line */
+    /* This is a pretty inefficient loop, but hopefully lots of spaces
+       at end of lines is a rare occurrence */
+    while (1) {
+      pos = instr(1, labelSection, " \n");
+      if (pos == 0) break;
+      let(&labelSection, cat(left(labelSection, pos - 1),
+          right(labelSection, pos + 1), NULL));
+    }
+
+    /* Don't allow more than 2 consecutive blank lines */
+    while (1) {
+      /* Match to 3 or more blank lines */
+      pos = instr(1, labelSection, "\n\n\n\n");
+      /* If it matches, remove one of the \n and restart loop */
+      if (pos == 0) break;
+      let(&labelSection, cat(left(labelSection, pos - 1),
+          right(labelSection, pos + 1), NULL));
+    }
+
+    switch (g_Statement[stmt].type) {
+      case lb_: /* ${ */
+      case rb_: /* $} */
+      case v_: /* $v */
+      case c_: /* $c */
+      case d_: /* $d */
+        /* These 5 cases are for keywords that don't have labels, so that
+           labelSection is simply the text before the keyword. */
+
+        /* Strip any trailing spaces */
+        let(&labelSection, edit(labelSection, 128 /*trailing spaces*/));
+        slen = (long)strlen(labelSection); /* Save to avoid recomputing */
+        /* See if last character is \n; if not, add it */
+        /* (If there's no text - just spaces - they've been stripped, and
+            leave labelSection as an empty string.) */
+        /* We use slen - 1 because native C strings start at index 0. */
+        if (slen != 0 && labelSection[slen - 1] != '\n') {
+          let(&labelSection, cat(labelSection, "\n", NULL));
+          slen++;
+        }
+        /* Put a blank line between $} and ${ if there is none. */
+        if (g_Statement[stmt].type == lb_
+            && previousType == rb_) {
+          if (slen == 0) {
+            /* There's no text (comment) between $} and ${, so make it
+               a blank line */
+            let(&labelSection, "\n\n");
+            slen = 2;
+          } else {
+            /* There's text between $} and ${ */
+            if (instr(1, labelSection, "\n\n") == 0) {
+              /* If there's no blank line, add one (note that code above
+                 ensures non-empty labelSection will end with \n, so
+                 add just 1 more) */
+              let(&labelSection, cat(labelSection, "\n", NULL));
+              slen++;
+            }
+          } /* if slen == 0 else */
+        } /* if $}...${ */
+        if (slen == 0) {
+          /* If the statement continues on this line, put 2 spaces before it */
+          let(&labelSection, cat(labelSection, "  ", NULL));
+          slen = 2;
+        } else {
+          /* Add indentation to end of labelSection i.e. before the keyword */
+          /* If there was text (comment) before the keyword on the same line,
+             it now has a \n after it, thus the indentation of the keyword will
+             be consistent. */
+          let(&labelSection, cat(labelSection, space(indent), NULL));
+          slen = slen + indent;
+        }
+        if (g_Statement[stmt].type == d_/*$d*/) {
+          /* Try to put as many $d's on one line as will fit.
+             First we remove redundant spaces in mathSection. */
+          /* Don't discard \n so that user can
+            insert \n to break a huge $d with say >40 variables,
+            which itself can exceed line length. */
+          let(&mathSection, edit(mathSection,
+              /*4*/ /* discard \n */ + 16 /* reduce spaces */));
+          if (strlen(edit(labelSection, 4/*discard \n*/ + 2/*discard spaces*/))
+              == 0) /* This and previous $d are separated by spaces
+                       and newlines only */
+              {
+            if (previousType == d_) { /* The previous statement was a $d */
+              /* See if the $d will fit on the current line */
+              if (dollarDpos + 2 + (signed)(strlen(mathSection)) + 4
+                  <= g_screenWidth) {
+                let(&labelSection, "  ");  /* 2 spaces between $d's */
+                dollarDpos = dollarDpos + 2 + (long)strlen(mathSection) + 4;
+              } else {
+                /* The $d assembly overflowed; start on new line */
+                /* Add 4 = '$d' length + '$.' length */
+                dollarDpos = indent + (long)strlen(mathSection) + 4;
+                /* Start new line */
+                let(&labelSection, cat("\n", space(indent), NULL));
+              }
+            } else { /* previousType != $d */
+              /* If the previous statement type (keyword) was not $d,
+                 we want to start the assembly of $d statements here. */
+              dollarDpos = indent + (long)strlen(mathSection) + 4;
+            } /* if previousType == $d else */
+          } else {
+            /* There is some text (comment) between this $d and previous,
+               so we restart assembling $d groups on this line */
+            dollarDpos = indent + (long)strlen(mathSection) + 4;
+          } /* if labelSection = spaces and newlines only else */
+        } /* if g_Statement[stmt].type == d_ */
+
+        break; /* End of ${, $}, $v, $c, $d cases */
+      case a_:    /* $a */
+      case p_:    /* $p */
+        /* Get last $( */
+        commentStart = rinstr(labelSection,  "$(");
+        /* Get last $) */
+        commentEnd = rinstr(labelSection, "$)") + 1;
+        if (commentEnd < commentStart) {
+          print2("?Make sure syntax passes before running / REWRAP.\n");
+          print2("(Forcing a bug check since output may be corrupted.)\n");
+          bug(1725);
+        }
+        if (commentStart != 0) {
+          let(&comment, seg(labelSection, commentStart, commentEnd));
+        } else {
+          /* If there is no comment before $a or $p, add dummy comment */
+          let(&comment, "$( PLEASE PUT DESCRIPTION HERE. $)");
+        }
+
+        let(&labelSection, left(labelSection, commentStart - 1));
+        /* Get the last newline before the comment */
+        pos = rinstr(labelSection, "\n");
+
+        /* If previous statement was $e, take out any blank line */
+        if (previousType == e_ && pos == 2 && labelSection[0] == '\n') {
+          let(&labelSection, right(labelSection, 2));
+          pos = 1;
+        }
+
+        /* If there is no '\n', insert it (unless first line in file) */
+        if (pos == 0 && stmt > 1) {
+          let(&labelSection, cat(edit(labelSection, 128 /* trailing spaces */),
+              "\n", NULL));
+          pos = (long)strlen(labelSection) + 1;
+        }
+
+        /* If comment has "<HTML>", don't reformat. */
+        if (instr(1, comment, "<HTML>") != 0) {
+          nowrapHtml = 1;
+        } else {
+          nowrapHtml = 0;
+        }
+
+        if (nowrapHtml == 0) {
+          /* This strips off leading spaces before $( (start of comment).  Don't
+             do it for <HTML>, since spacing before $( is manual. */
+          let(&labelSection, left(labelSection, pos));
+
+          if (reformatFlag == 2) {
+            /* If / REWRAP was specified, unwrap and rewrap the line */
+            free_vstring(str1);
+            str1 = rewrapComment(comment);
+            let(&comment, str1);
+          }
+
+          /* Make sure that start of new lines inside the comment have no
+             trailing space (because printLongLine doesn't do this after
+             explicit break) */
+          pos = 0;
+          while (1) {
+            pos = instr(pos + 1, comment, "\n");
+            if (pos == 0) break; /* Beyond last line in comment */
+            /* Remove leading spaces from comment line */
+            length = 0;
+            while (1) {
+              if (comment[pos + length] != ' ') break;
+              length++;
+            }
+            /* Add back indent+3 spaces to beginning of line in comment */
+            let(&comment, cat(left(comment, pos),
+                (comment[pos + length] != '\n')
+                    ? space(indent + 3)
+                    : "",  /* Don't add indentation if line is blank */
+                right(comment, pos + length + 1), NULL));
+          }
+
+          /* Reformat the comment to wrap if necessary */
+          if (g_outputToString == 1) bug(1726);
+          g_outputToString = 1;
+          free_vstring(g_printString);
+
+          printLongLine(cat(space(indent), comment, NULL),
+              space(indent + 3), " ");
+          let(&comment, g_printString);
+          free_vstring(g_printString);
+          g_outputToString = 0;
+#define ASCII_4 4
+          /* Restore ASCII_4 characters put in by rewrapComment() to space */
+          length = (long)strlen(comment);
+          for (pos = 2; pos < length - 2; pos++) {
+             /* For debugging: */
+             /* if (comment[pos] == ASCII_4) comment[pos] = '#'; */
+             if (comment[pos] == ASCII_4) comment[pos] = ' ';
+          }
+        } /* if nowrapHtml == 0 */ else {
+          /* If there was an <HTML> tag, we don't modify the comment. */
+          /* However, we need "\n" after "$)" (end of comment), corresponding to
+             the one that was put there by printLongLine() in the normal
+             non-<HTML> case above */
+          let(&comment, cat(comment, "\n", NULL));
+        }
+
+        /* Remove any trailing spaces */
+        pos = 2;
+        while(1) {
+          pos = instr(pos + 1, comment, " \n");
+          if (!pos) break;
+          let(&comment, cat(left(comment, pos - 1), right(comment, pos + 1),
+              NULL));
+          pos = pos - 2;
+        }
+
+        /* Complete the label section */
+        let(&labelSection, cat(labelSection, comment,
+            space(indent), g_Statement[stmt].labelName, " ", NULL));
+        break; /* End of $a, $p cases */
+      case e_:    /* $e */
+      case f_:    /* $f */
+        pos = rinstr(labelSection, g_Statement[stmt].labelName);
+        let(&labelSection, left(labelSection, pos - 1));
+        pos = rinstr(labelSection, "\n");
+        /* If there is none, insert it (unless first line in file) */
+        if (pos == 0 && stmt > 1) {
+          let(&labelSection, cat(edit(labelSection, 128 /* trailing spaces */),
+              "\n", NULL));
+          pos = (long)strlen(labelSection) + 1;
+        }
+        let(&labelSection, left(labelSection, pos));
+        /* If previous statement is $d or $e and there is no comment after it,
+           discard entire rest of label to get rid of redundant blank lines */
+        if ((previousType == d_ || previousType == e_)
+            && instr(1, labelSection, "$(") == 0) {
+          let(&labelSection, "\n");
+        }
+        /* Complete the label section */
+        let(&labelSection, cat(labelSection,
+            space(indent), g_Statement[stmt].labelName, " ", NULL));
+        break; /* End of $e, $f cases */
+      default: bug(1727);
+    } /* switch (g_Statement[stmt].type) */
+
+    /* Reformat the math section */
+    switch (g_Statement[stmt].type) {
+      case lb_: /* ${ */
+      case rb_: /* $} */
+      case v_: /* $v */
+      case c_: /* $c */
+      case d_: /* $d */
+      case a_: /* $a */
+      case p_: /* $p */
+      case e_: /* $e */
+      case f_: /* $f */
+        /* Remove blank lines */
+        while (1) {
+          pos = instr(1, mathSection, "\n\n");
+          if (pos == 0) break;
+          let(&mathSection, cat(left(mathSection, pos),
+              right(mathSection, pos + 2), NULL));
+        }
+
+        /* Reduce multiple in-line spaces to single space */
+        pos = 0;
+        while (1) {
+          pos = instr(pos + 1, mathSection, "  ");
+          if (pos == 0) break;
+          if (pos > 1) {
+            if (mathSection[pos - 2] != '\n' && mathSection[pos - 2] != ' ') {
+              /* It's not at the start of a line, so reduce it */
+              let(&mathSection, cat(left(mathSection, pos),
+                  right(mathSection, pos + 2), NULL));
+              pos--;
+            }
+          }
+        }
+
+        break;
+      default: bug(1729);
+    }
+
+    /* Set previous state for next statement */
+    if (g_Statement[stmt].type == d_) {
+      /* dollarDpos is computed in the processing above */
+    } else {
+      dollarDpos = 0; /* Reset it */
+    }
+    previousType = g_Statement[stmt].type;
+  } /* if reformatFlag */
+
+  let(&output, labelSection);
+
+  /* Add statement keyword */
+  let(&output, cat(output, "$", chr(g_Statement[stmt].type), NULL));
+
+  /* Add math section and proof */
+  if (g_Statement[stmt].mathSectionLen != 0) {
+    let(&output, cat(output, mathSection, NULL));
+
+    if (g_Statement[stmt].type == (char)p_) {
+      let(&output, cat(output, "$=", proofSection, NULL));
+    }
+    let(&output, cat(output, "$.", NULL));
+
+  }
+
+  /* Make sure the line has no carriage-returns */
+  if (strchr(output, '\r') != NULL) {
+
+    /* We are now using readFileToString, so this should never happen. */
+    bug(1758);
+
+    /* This may happen with Cygwin's gcc, where DOS CR-LF becomes CR-CR-LF
+       in the output file */
+    /* Someday we should investigate the use of readFileToString() in
+       mminou.c for the main set.mm READ function, to solve this cleanly. */
+    let(&output, edit(output, 8192)); /* Discard CR's */
+  }
+
+  /* This function is no longer used to supply the output, but just to
+     do any reformatting/wrapping.  Now writeSourceToBuffer() builds the
+     output source.  So instead, we update the g_Statement[] content with
+     any changes, which are read by writeSourceToBuffer() and also saved
+     in the g_Statement[] array for any future write source.  Eventually
+     we should replace WRITE SOURCE.../REWRAP with a REWRAP(?) command. */
+  if (strcmp(labelSection, labelSectionSave)) {
+    g_Statement[stmt].labelSectionLen = (long)strlen(labelSection);
+    if (g_Statement[stmt].labelSectionChanged == 1) {
+      let(&(g_Statement[stmt].labelSectionPtr), labelSection);
+    } else {
+      /* This is the first time we've updated the label section */
+      g_Statement[stmt].labelSectionChanged = 1;
+      g_Statement[stmt].labelSectionPtr = labelSection;
+      labelSection = ""; /* so that labelSectionPtr won't be deallocated */
+    }
+  }
+  if (strcmp(mathSection, mathSectionSave)) {
+    g_Statement[stmt].mathSectionLen = (long)strlen(mathSection);
+    if (g_Statement[stmt].mathSectionChanged == 1) {
+      let(&(g_Statement[stmt].mathSectionPtr), mathSection);
+    } else {
+      /* This is the first time we've updated the math section */
+      g_Statement[stmt].mathSectionChanged = 1;
+      g_Statement[stmt].mathSectionPtr = mathSection;
+      mathSection = ""; /* so that mathSectionPtr won't be deallocated */
+    }
+  }
+  /* (I don't see anywhere that proofSection will change.  So make
+     it a bug check to force us to look into it.) */
+  if (strcmp(proofSection, proofSectionSave)) {
+    bug(1757); /* This may not be a bug */
+    g_Statement[stmt].proofSectionLen = (long)strlen(proofSection);
+    if (g_Statement[stmt].proofSectionChanged == 1) {
+      let(&(g_Statement[stmt].proofSectionPtr), proofSection);
+    } else {
+      /* This is the first time we've updated the proof section */
+      g_Statement[stmt].proofSectionChanged = 1;
+      g_Statement[stmt].proofSectionPtr = proofSection;
+      proofSection = ""; /* so that proofSectionPtr won't be deallocated */
+    }
+  }
+
+  free_vstring(labelSection);
+  free_vstring(mathSection);
+  free_vstring(proofSection);
+  free_vstring(labelSectionSave);
+  free_vstring(mathSectionSave);
+  free_vstring(proofSectionSave);
+  free_vstring(comment);
+  free_vstring(str1);
+  return output; /* The calling routine must deallocate this vstring */
+} /* outputStatement */
+
+/* Unwrap the lines in a comment then re-wrap them according to set.mm
+   conventions.  This may be overly aggressive, and user should do a
+   diff to see if result is as desired.  Called by WRITE SOURCE / REWRAP.
+   Caller must deallocate returned vstring. */
+vstring rewrapComment(const char *comment1) {
+  /* Punctuation from mmwtex.c */
+#define OPENING_PUNCTUATION "(['\""
+/* #define CLOSING_PUNCTUATION ".,;)?!:]'\"_-" */
+#define CLOSING_PUNCTUATION ".,;)?!:]'\""
+#define SENTENCE_END_PUNCTUATION ")'\""
+  vstring_def(comment);
+  vstring_def(commentTemplate); /* Non-breaking space template */
+  long length, pos, i, j;
+  vstring ch; /* Pointer only; do not allocate */
+  flag mathmode = 0;
+
+  let(&comment, comment1); /* Grab arg so it can be reassigned */
+
+  /* Make sure back quotes are surrounded by space */
+  pos = 2;
+  mathmode = 0;
+  while (1) {
+    pos = instr(pos + 1, comment, "`");
+    if (pos == 0) break;
+    mathmode = (flag)(1 - mathmode);
+    if (comment[pos - 2] == '`' || comment[pos] == '`') continue;
+            /* See if previous or next char is "`"; ignore "``" escape */
+    if (comment[pos] != ' ' && comment[pos] != '\n') {
+      /* Currently, mmwtex.c doesn't correctly handle broken subscript (_)
+         or broken hyphen (-) in the CLOSING_PUNCTUATION, so allow these two as
+         exceptions until that is fixed.   E.g. ` a ` _2 doesn't yield
+         HTML subscript; instead we need ` a `_2. */
+      if (mathmode == 1 || (comment[pos] != '_' && comment[pos] != '-')) {
+        /* Add a space after back quote if none */
+        let(&comment, cat(left(comment, pos), " ",
+            right(comment, pos + 1), NULL));
+      }
+    }
+    if (comment[pos - 2] != ' ') {
+      /* Add a space before back quote if none */
+      let(&comment, cat(left(comment, pos - 1), " ",
+          right(comment, pos), NULL));
+      pos++; /* Go past the "`" */
+    }
+  }
+
+  /* Make sure "~" for labels are surrounded by space */
+  if (instr(2, comment, "`") == 0) {  /* For now, just process comments
+         not containing math symbols.  More complicated code is needed
+         to ignore ~ in math symbols; maybe add it someday. */
+    pos = 2;
+    while (1) {
+      pos = instr(pos + 1, comment, "~");
+      if (pos == 0) break;
+      if (comment[pos - 2] == '~' || comment[pos] == '~') continue;
+              /* See if previous or next char is "~"; ignore "~~" escape */
+      if (comment[pos] != ' ') {
+        /* Add a space after tilde if none */
+        let(&comment, cat(left(comment, pos), " ",
+            right(comment, pos + 1), NULL));
+      }
+      if (comment[pos - 2] != ' ') {
+        /* Add a space before tilde if none */
+        let(&comment, cat(left(comment, pos - 1), " ",
+            right(comment, pos), NULL));
+        pos++; /* Go past the "~" */
+      }
+    }
+  }
+
+  /* Change all newlines to space unless double newline */
+  /* Note:  it is assumed that blank lines have no spaces
+     for this to work; the user must ensure that. */
+  length = (long)strlen(comment);
+  for (pos = 2; pos < length - 2; pos++) {
+    if (comment[pos] == '\n' && comment[pos - 1] != '\n'
+        && comment[pos + 1] != '\n')
+      comment[pos] = ' ';
+  }
+  let(&comment, edit(comment, 16 /* reduce spaces */));
+
+  /* Remove spaces and blank lines at end of comment */
+  while (1) {
+    length = (long)strlen(comment);
+    if (comment[length - 3] != ' ') bug(1730);
+            /* Should have been syntax err (no space before "$)") */
+    if (comment[length - 4] != ' ' && comment[length - 4] != '\n') break;
+    let(&comment, cat(left(comment, length - 4),
+        right(comment, length - 2), NULL));
+  }
+
+  /* Put period at end of comment ending with lowercase letter */
+  /* Note:  This will not detect a '~ label' at end of comment.
+     However, user should have ended it with a period, and if not the
+     label + period is unlikely to be valid and thus will
+     usually be detected by 'verify markup'.
+     (We could enhance the code here to do that if it becomes a problem.) */
+  length = (long)strlen(comment);
+  if (islower((unsigned char)(comment[length - 4]))) {
+    let(&comment, cat(left(comment, length - 3), ". $)", NULL));
+  }
+
+  /* Change to ASCII 4 those spaces where the line shouldn't be
+     broken */
+  mathmode = 0;
+  for (pos = 3; pos < length - 2; pos++) {
+    if (comment[pos] == '`') { /* Start or end of math string */
+      mathmode = (char)(1 - mathmode);
+    }
+    if ( mathmode == 1 && comment[pos] == ' ')
+      /* We assign comment[] rather than commentTemplate to avoid confusion of
+         math with punctuation.  Also, commentTemplate would be misaligned
+         anyway due to adding of spaces below. */
+      comment[pos] = ASCII_4;
+  }
+
+  /* Look for proof discouraged or usage discouraged markup and change their
+     spaces to ASCII 4 to prevent line breaks in the middle */
+  if (g_proofDiscouragedMarkup[0] == 0) {
+    /* getMarkupFlags() in mmdata.c has never been called, so initialize the
+       markup strings to their defaults */
+    let(&g_proofDiscouragedMarkup, PROOF_DISCOURAGED_MARKUP);
+    let(&g_usageDiscouragedMarkup, USAGE_DISCOURAGED_MARKUP);
+  }
+  pos = instr(1, comment, g_proofDiscouragedMarkup);
+  if (pos != 0) {
+    i = (long)strlen(g_proofDiscouragedMarkup);
+    for (j = pos; j < pos + i - 1; j++) { /* Check 2nd thru penultimate char */
+      if (comment[j] == ' ') {
+        comment[j] = ASCII_4;
+      }
+    }
+  }
+  pos = instr(1, comment, g_usageDiscouragedMarkup);
+  if (pos != 0) {
+    i = (long)strlen(g_usageDiscouragedMarkup);
+    for (j = pos; j < pos + i - 1; j++) { /* Check 2nd thru penultimate char */
+      if (comment[j] == ' ') {
+        comment[j] = ASCII_4;
+      }
+    }
+  }
+
+
+  /* Put two spaces after end of sentence and colon */
+  ch = ""; /* Prevent compiler warning */
+  for (i = 0; i < 4; i++) {
+    switch (i) {
+      case 0: ch = "."; break;
+      case 1: ch = "?"; break;
+      case 2: ch = "!"; break;
+      case 3: ch = ":";
+    }
+    pos = 2;
+    while (1) {
+      pos = instr(pos + 1, comment, ch);
+      if (pos == 0) break;
+      if (ch[0] == '.' && comment[pos - 2] >= 'A' && comment[pos - 2] <= 'Z')
+        continue;  /* Ignore initials of names */
+      if (strchr(SENTENCE_END_PUNCTUATION, comment[pos]) != NULL)
+        pos++;
+      if (comment[pos] != ' ') continue;
+      if ((comment[pos + 1] >= 'A' && comment[pos + 1] <= 'Z')
+          || strchr(OPENING_PUNCTUATION, comment[pos + 1]) != NULL) {
+        /* A change of space to ASCII_4 is not needed, and in fact
+           prevents end of sentence e.g. "." from ever appearing at column 79,
+           triggering an earlier break that makes line unnecessarily short.
+           There is no problem with next line having leading space:
+           it is removed in mminou.c (search
+           for "Remove leading space for neatness" there).  (Note that we use
+           ASCII_4 to prevent bad line breaks, then later change them to
+           spaces.) */
+        /* comment[pos] = ASCII_4; */
+
+        /* Add a second space after end of sentence, which is recommended for
+           monospaced (typewriter) fonts to more easily see sentence
+           separation. */
+        let(&comment, cat(left(comment, pos + 1), " ",
+            right(comment, pos + 2), NULL));
+      }
+    } /* end while */
+  } /* next i */
+
+  length = (long)strlen(comment);
+  let(&commentTemplate, space(length));
+  for (pos = 3; pos < length - 2; pos++) {
+    if (comment[pos] == ' ') {
+      if (comment[pos - 1] == '~' && comment[pos - 2] != '~') {
+        /* Don't break "~ <label>" */
+        commentTemplate[pos] = ASCII_4;
+      } else if ((comment[pos - 2] == ' '
+            || strchr(OPENING_PUNCTUATION, comment[pos - 2]) != NULL)
+          && strchr(OPENING_PUNCTUATION, comment[pos - 1]) != NULL) {
+        /* Don't break space after opening punctuation */
+        commentTemplate[pos] = ASCII_4;
+      } else if ((comment[pos + 2] == ' '
+            || comment[pos + 2] == '\n' /* Period etc. before line break */
+            || comment[pos + 2] == ASCII_4 /* 2-space sentence break
+                              done above where 1st space is ASCII_4 */
+            || strchr(CLOSING_PUNCTUATION, comment[pos + 2]) != NULL)
+          && strchr(CLOSING_PUNCTUATION, comment[pos + 1]) != NULL) {
+        /* Don't break space before closing punctuation */
+        commentTemplate[pos] = ASCII_4;
+/*
+      } else if (comment[pos + 2] == ' '
+          && strchr(CLOSING_PUNCTUATION, comment[pos + 1]) != NULL) {
+        /@ Don't break space after "~ <label>" if followed by punctuation @/
+        commentTemplate[pos] = ASCII_4;
+      } else if (comment[pos - 2] == ' '
+          && strchr(OPENING_PUNCTUATION, comment[pos - 1]) != NULL) {
+        /@ Don't break space before "~ <label>" if preceded by punctuation @/
+        commentTemplate[pos] = ASCII_4;
+      } else if (comment[pos + 1] == '`'
+          && strchr(OPENING_PUNCTUATION, comment[pos - 1]) != NULL) {
+        /@ Don't break space between punctuation and math start '`' @/
+        commentTemplate[pos] = ASCII_4;
+      } else if (comment[pos - 1] == '`'
+          && strchr(CLOSING_PUNCTUATION, comment[pos + 1]) != NULL) {
+        /@ Don't break space between punctuation and math end '`' @/
+        commentTemplate[pos] = ASCII_4;
+*/
+      } else if (comment[pos - 3] == ' ' && comment[pos - 2] == 'p'
+          && comment[pos - 1] == '.') {
+        /* Don't break " p. nnn" */
+        commentTemplate[pos] = ASCII_4;
+      }
+    }
+  }
+  commentTemplate[length - 3] = ASCII_4; /* Last space in comment */
+
+  for (pos = 3; pos < length - 2; pos++) {
+    /* Transfer the non-breaking spaces from the template to the comment */
+    if (commentTemplate[pos] == ASCII_4) comment[pos] = ASCII_4;
+  }
+
+  free_vstring(commentTemplate);
+  return comment;
+} /* rewrapComment */
+
+/* This is a general-purpose function to parse a math token string,
+   typically input by the user at the keyboard.  The statemNum is
+   needed to provide a context to determine which symbols are active.
+   Lack of whitespace is tolerated according to standard rules.
+   g_mathTokens must be set to the proper value. */
+/* The code in this section is complex because it uses the fast parsing
+   method borrowed from parseStatements().  On the other hand, it must set
+   up some initial tables by scanning the entire g_MathToken array; this may
+   slow it down in some applications. */
+/* Warning:  g_mathTokens must be the correct value (some procedures might
+   artificially adjust g_mathTokens to add dummy tokens [schemes] to the
+   g_MathToken array) */
+/* The user text may include existing or new dummy variables of the
+   form "?nnn". */
+nmbrString *parseMathTokens(vstring userText, long statemNum)
+{
+  long i, j;
+  char *fbPtr;
+  long mathStringLen;
+  long tokenNum;
+  long lowerKey, upperKey;
+  long symbolLen, origSymbolLen, g_mathKeyNum;
+  void *g_mathKeyPtr; /* bsearch returned value */
+  int maxScope;
+  flag errorFlag = 0; /* Prevents bad token from being added to output */
+  int errCount = 0; /* Cumulative error count */
+  vstring_def(tmpStr);
+  vstring_def(nlUserText);
+
+
+  long *mathTokenSameAs; /* Flag that symbol is unique (for speed up) */
+  long *reverseMathKey; /* Map from g_mathTokens to g_mathKey */
+
+
+  /* Temporary working space */
+  long wrkLen;
+  nmbrString *wrkNmbrPtr;
+  char *wrkStrPtr;
+
+  /* The answer */
+  nmbrString_def(mathString);
+
+  long maxSymbolLen; /* Longest math symbol (for speedup) */
+  flag *symbolLenExists; /* A symbol with this length exists (for speedup) */
+
+  long nmbrSaveTempAllocStack; /* For nmbrLet() stack cleanup */
+  long saveTempAllocStack; /* For let() stack cleanup */
+  nmbrSaveTempAllocStack = g_nmbrStartTempAllocStack;
+  g_nmbrStartTempAllocStack = g_nmbrTempAllocStackTop;
+  saveTempAllocStack = g_startTempAllocStack;
+  g_startTempAllocStack = g_tempAllocStackTop;
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  tokenNum = 0;
+
+  /* Add a newline before user text for sourceError() */
+  let(&nlUserText, cat("\n", userText, NULL));
+
+  /* Make sure that g_mathTokens has been initialized */
+  if (!g_mathTokens) bug(1717);
+
+  /* Set the 'active' flag based on statemNum; here 'active' just means it
+     would be in the stack during normal parsing, not the Metamath manual
+     definition. */
+  for (i = 0; i < g_mathTokens; i++) {
+    if (g_MathToken[i].statement <= statemNum && g_MathToken[i].endStatement >=
+        statemNum) {
+      g_MathToken[i].active = 1;
+    } else {
+      g_MathToken[i].active = 0;
+    }
+  }
+
+  /* Initialize flags for g_mathKey array that identify math symbols as
+     unique (when 0) or, if not unique, the flag is a number identifying a group
+     of identical names */
+  mathTokenSameAs = malloc((size_t)g_mathTokens * sizeof(long));
+  if (!mathTokenSameAs) outOfMemory("#12 (mathTokenSameAs)");
+  reverseMathKey = malloc((size_t)g_mathTokens * sizeof(long));
+  if (!reverseMathKey) outOfMemory("#13 (reverseMathKey)");
+  for (i = 0; i < g_mathTokens; i++) {
+    mathTokenSameAs[i] = 0; /* 0 means unique */
+    reverseMathKey[g_mathKey[i]] = i; /* Initialize reverse map to g_mathKey */
+  }
+  for (i = 1; i < g_mathTokens; i++) {
+    if (!strcmp(g_MathToken[g_mathKey[i]].tokenName,
+        g_MathToken[g_mathKey[i - 1]].tokenName)) {
+      if (!mathTokenSameAs[i - 1]) mathTokenSameAs[i - 1] = i;
+      mathTokenSameAs[i] = mathTokenSameAs[i - 1];
+    }
+  }
+
+  /* Initialize temporary working space for parsing tokens */
+  /* Assume the worst case of one token per userText character */
+  wrkLen = (long)strlen(userText);
+  wrkNmbrPtr = malloc((size_t)wrkLen * sizeof(nmbrString));
+  if (!wrkNmbrPtr) outOfMemory("#22 (wrkNmbrPtr)");
+  wrkStrPtr = malloc((size_t)wrkLen + 1);
+  if (!wrkStrPtr) outOfMemory("#23 (wrkStrPtr)");
+
+  /* Find declared math symbol lengths (used to speed up parsing) */
+  maxSymbolLen = 0;
+  for (i = 0; i < g_mathTokens; i++) {
+    if (g_MathToken[i].length > maxSymbolLen) {
+      maxSymbolLen = g_MathToken[i].length;
+    }
+  }
+  symbolLenExists = malloc(((size_t)maxSymbolLen + 1) * sizeof(flag));
+  if (!symbolLenExists) outOfMemory("#25 (symbolLenExists)");
+  for (i = 0; i <= maxSymbolLen; i++) {
+    symbolLenExists[i] = 0;
+  }
+  for (i = 0; i < g_mathTokens; i++) {
+    symbolLenExists[g_MathToken[i].length] = 1;
+  }
+
+
+  g_currentScope = g_Statement[statemNum].scope; /* Scope of the ref. statement */
+
+
+  /* The code below is indented because it was borrowed from parseStatements().
+     We will leave the indentation intact for easier future comparison
+     with that code. */
+
+        /* Scan the math section for tokens */
+        mathStringLen = 0;
+        fbPtr = nlUserText;
+        while (1) {
+          fbPtr = fbPtr + whiteSpaceLen(fbPtr);
+          origSymbolLen = tokenLen(fbPtr);
+          if (!origSymbolLen) break; /* Done scanning source line */
+
+          /* Scan for largest matching token from the left */
+         nextAdjToken:
+          /* maxSymbolLen is the longest declared symbol */
+          if (origSymbolLen > maxSymbolLen) {
+            symbolLen = maxSymbolLen;
+          } else {
+            symbolLen = origSymbolLen;
+          }
+          memcpy(wrkStrPtr, fbPtr, (size_t)symbolLen);
+          for (; symbolLen > 0; symbolLen--) {
+            /* symbolLenExists means a symbol of this length was declared */
+            if (!symbolLenExists[symbolLen]) continue;
+            wrkStrPtr[symbolLen] = 0; /* Define end of trial token to look up */
+            g_mathKeyPtr = (void *)bsearch(wrkStrPtr, g_mathKey,
+                (size_t)g_mathTokens, sizeof(long), mathSrchCmp);
+            if (!g_mathKeyPtr) continue; /* Trial token was not declared */
+            g_mathKeyNum = (long *)g_mathKeyPtr - g_mathKey; /* Pointer arithmetic! */
+            if (mathTokenSameAs[g_mathKeyNum]) { /* Multiply-declared symbol */
+              lowerKey = g_mathKeyNum;
+              upperKey = lowerKey;
+              j = mathTokenSameAs[lowerKey];
+              while (lowerKey) {
+                if (j != mathTokenSameAs[lowerKey - 1]) break;
+                lowerKey--;
+              }
+              while (upperKey < g_mathTokens - 1) {
+                if (j != mathTokenSameAs[upperKey + 1]) break;
+                upperKey++;
+              }
+              /* Find the active symbol with the most recent declaration */
+              /* (Note:  Here, 'active' means it's on the stack, not the
+                 official def.) */
+              maxScope = -1;
+              for (i = lowerKey; i <= upperKey; i++) {
+                j = g_mathKey[i];
+                if (g_MathToken[j].active) {
+                  if (g_MathToken[j].scope > maxScope) {
+                    tokenNum = j;
+                    maxScope = g_MathToken[j].scope;
+                    if (maxScope == g_currentScope) break; /* Speedup */
+                  }
+                }
+              }
+              if (maxScope == -1) {
+                tokenNum = g_mathKey[g_mathKeyNum]; /* Pick an arbitrary one */
+                errCount++;
+                if (errCount <= 1) { /* Print 1st error only */
+                  sourceError(fbPtr, symbolLen, /*stmt*/ 0,
+       "This math symbol is not active (i.e. was not declared in this scope).");
+                }
+                errorFlag = 1;
+              }
+            } else { /* The symbol was declared only once. */
+              tokenNum = *((long *)g_mathKeyPtr);
+                  /* Same as: tokenNum = g_mathKey[g_mathKeyNum]; but faster */
+              if (!g_MathToken[tokenNum].active) {
+                errCount++;
+                if (errCount <= 1) { /* Print 1st error only */
+                  sourceError(fbPtr, symbolLen, /*stmt*/ 0,
+       "This math symbol is not active (i.e. was not declared in this scope).");
+                }
+                errorFlag = 1;
+              }
+            } /* End if multiply-defined symbol */
+            break; /* The symbol was found, so we are done */
+          } /* Next symbolLen */
+
+          if (symbolLen == 0) { /* Symbol was not found */
+            /* See if the symbol is a dummy variable name */
+            if (fbPtr[0] == '$') {
+              symbolLen = tokenLen(fbPtr);
+              for (i = 1; i < symbolLen; i++) {
+                if (fbPtr[i] < '0' || fbPtr[i] > '9') break;
+              }
+              symbolLen = i;
+              if (symbolLen == 1) {
+                symbolLen = 0; /* No # after '$' -- error */
+              } else {
+                memcpy(wrkStrPtr, fbPtr + 1, (size_t)i - 1);
+                wrkStrPtr[i - 1] = 0; /* End of string */
+                tokenNum = (long)(val(wrkStrPtr)) + g_mathTokens;
+                /* See if dummy var has been declared; if not, declare it */
+                if (tokenNum > g_pipDummyVars + g_mathTokens) {
+                  declareDummyVars(tokenNum - g_pipDummyVars - g_mathTokens);
+                }
+              }
+            } /* End if fbPtr == '$' */
+         } /* End if symbolLen == 0 */
+
+
+          if (symbolLen == 0) { /* Symbol was not found */
+            symbolLen = tokenLen(fbPtr);
+            errCount++;
+            if (errCount <= 1) { /* Print 1st error only */
+              sourceError(fbPtr, symbolLen, /*stmt*/ 0,
+      "This math symbol was not declared (with a \"$c\" or \"$v\" statement).");
+            }
+            errorFlag = 1;
+          }
+
+          /* Add symbol to mathString */
+          if (!errorFlag) {
+            wrkNmbrPtr[mathStringLen] = tokenNum;
+            mathStringLen++;
+          } else {
+            errorFlag = 0;
+          }
+          fbPtr = fbPtr + symbolLen; /* Move on to next symbol */
+
+          if (symbolLen < origSymbolLen) {
+            /* This symbol is not separated from next by white space */
+            /* Speed-up: don't call tokenLen again; just jump past it */
+            origSymbolLen = origSymbolLen - symbolLen;
+            goto nextAdjToken; /* (Instead of continue) */
+          }
+        } /* End while */
+
+
+        /* Assign mathString */
+        nmbrLet(&mathString, nmbrSpace(mathStringLen));
+        for (i = 0; i < mathStringLen; i++) {
+          mathString[i] = wrkNmbrPtr[i];
+        }
+
+  /* End of unconventionally indented section borrowed from parseStatements() */
+
+  g_startTempAllocStack = saveTempAllocStack;
+  g_nmbrStartTempAllocStack = nmbrSaveTempAllocStack;
+
+  /* Deallocate temporary space */
+  free(mathTokenSameAs);
+  free(reverseMathKey);
+  free(wrkNmbrPtr);
+  free(wrkStrPtr);
+  free(symbolLenExists);
+  free_vstring(tmpStr);
+  free_vstring(nlUserText);
+
+  return nmbrMakeTempAlloc(mathString); /* Flag for dealloc */
+} /* parseMathTokens */
+
+
+/* For .mm file splitting */
+/* Get the next real $[...$] or virtual $( Begin $[... inclusion */
+/* This uses the convention of mmvstr.c where beginning of a string
+   is position 1.  However, startOffset = 0 means no offset i.e.
+   start at fileBuf. */
+void getNextInclusion(char *fileBuf, long startOffset, /* inputs */
+    /* outputs: */
+    long *cmdPos1, long *cmdPos2,
+    long *endPos1, long *endPos2,
+    char *cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
+                      'I' = "[$...",
+                      'S' = "$( Skip [$...",
+                      'E' = Start missing matched End
+                      'N' = no include found; includeFn = "" */
+    vstring *fileName /* name of included file */
+    )
+{
+
+/*
+cmdType = 'B' or 'E':
+      ....... $( Begin $[ prop.mm $] $)   ......   $( End $[ prop.mm $] $) ...
+       ^      ^           ^^^^^^^       ^          ^                      ^
+  startOffset cmdPos1    fileName  cmdPos2     endPos1              endPos2
+                                             (=0 if no End)   (=0 if no End)
+
+   Note: in the special case of Begin, cmdPos2 points _after_ the whitespace
+   after "$( Begin $[ prop.mm $] $)" i.e. the whitespace is considered part of
+   the Begin command.  The is needed because prop.mm content doesn't
+   necessarily start with whitespace.  prop.mm does, however, end with
+   whitespace (\n) as enforced by readFileToString().
+
+cmdType = 'I':
+      ............... $[ prop.mm $]  ..............
+       ^              ^  ^^^^^^^   ^
+  startOffset   cmdPos1 fileName  cmdPos2     endPos1=0  endPos2=0
+
+cmdType = 'S':
+      ....... $( Skip $[ prop.mm $] $)   ......
+       ^      ^          ^^^^^^^      ^
+  startOffset cmdPos1    fileName  cmdPos2     endPos1=0  endPos2=0
+*/
+  char *fbPtr;
+  char *tmpPtr;
+  flag lookForEndMode = 0; /* 1 if inside of $( Begin, End, Skip... */
+  long i, j;
+
+  fbPtr = fileBuf + startOffset;
+
+  while (1) {
+    fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr); /* Count $( as a token */
+    j = rawTokenLen(fbPtr); /* Treat $(, $[ as tokens */
+    if (j == 0) {
+      *cmdType = 'N'; /* No include found */
+      break;  /* End of file */
+    }
+    if (fbPtr[0] != '$') {
+      fbPtr = fbPtr + j;
+      continue;
+    }
+
+    /* Process normal include $[ $] */
+    if (fbPtr[1] == '[') {
+      if (lookForEndMode == 0) {
+        /* If lookForEndMode is 1, ignore everything until we find a matching
+           "$( End $[..." */
+        *cmdPos1 = fbPtr - fileBuf + 1; /* 1 = beginning of file */
+        fbPtr = fbPtr + j;
+        fbPtr = fbPtr + whiteSpaceLen(fbPtr); /* Comments = whitespace here */
+        j = rawTokenLen(fbPtr); /* Should be file name */
+        /* Note that mid, seg, left, right do not waste time computing
+           the length of the input string fbPtr */
+        let(&(*fileName), left(fbPtr, j));
+        fbPtr = fbPtr + j;
+        fbPtr = fbPtr + whiteSpaceLen(fbPtr); /* Comments = whitespace here */
+        j = rawTokenLen(fbPtr);
+        if (j == 2/*speedup*/ && !strncmp("$]", fbPtr, (size_t)j)) {
+          *cmdPos2 = fbPtr - fileBuf + j + 1;
+          *endPos1 = 0;
+          *endPos2 = 0;
+          *cmdType = 'I';
+          return;
+        }
+        /* TODO - more precise error message */
+        print2("?Missing \"$]\" after \"$[ %s\"\n", *fileName);
+        fbPtr = fbPtr + j;
+        continue; /* Not a completed include */
+      } /* if (lookForEndMode == 0) */
+      fbPtr = fbPtr + j;
+      continue; /* Either not a legal include - error detected later,
+             or we're in lookForEndMode */
+    /* Process markup-type include inside comment */
+    } else if (fbPtr[1] == '(') {
+      /* Process comment starting at "$(" */
+      if (lookForEndMode == 0) {
+        *cmdPos1 = fbPtr - fileBuf + 1;
+      } else {
+        *endPos1 = fbPtr - fileBuf + 1;
+      }
+      fbPtr = fbPtr + j;
+      fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr); /* comment != whitespace */
+      j = rawTokenLen(fbPtr);
+      *cmdType = '?';
+      if (j == 5/*speedup*/ && !strncmp("Begin", fbPtr, (size_t)j)) {
+        /* If lookForEndMode is 1, we're looking for End matching earlier Begin */
+        if (lookForEndMode == 0) {
+          *cmdType = 'B';
+        }
+      } else if (j == 4/*speedup*/ && !strncmp("Skip", fbPtr, (size_t)j)) {
+        /* If lookForEndMode is 1, we're looking for End matching earlier Begin */
+        if (lookForEndMode == 0) {
+          *cmdType = 'S';
+        }
+      } else if (j == 3/*speedup*/ && !strncmp("End", fbPtr, (size_t)j)) {
+        /* If lookForEndMode is 0, there was no matching Begin */
+        if (lookForEndMode == 1) {
+          *cmdType = 'E';
+        }
+      }
+      if (*cmdType == '?') { /* The comment doesn't qualify as $[ $] markup */
+        /* Find end of comment and continue */
+        goto GET_PASSED_END_OF_COMMENT;
+      } else {
+        /* It's Begin or Skip or End */
+        fbPtr = fbPtr + j;
+        fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr);
+        j = rawTokenLen(fbPtr);
+        if (j != 2 || strncmp("$[", fbPtr, (size_t)j)) {
+          /* Find end of comment and continue */
+          goto GET_PASSED_END_OF_COMMENT;
+        }
+        fbPtr = fbPtr + j;
+        fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr);  /* comment != whitespace */
+        j = rawTokenLen(fbPtr);
+        /* Note that mid, seg, left, right do not waste time computing
+           the length of the input string fbPtr */
+        if (lookForEndMode == 0) {
+          /* It's Begin or Skip */
+          let(&(*fileName), left(fbPtr, j));
+        } else {
+          /* It's an End command */
+          if (strncmp(*fileName, fbPtr, (size_t)j)) {
+            /* But the file name didn't match, so it's not a matching End */
+            /* Find end of comment and continue */
+            goto GET_PASSED_END_OF_COMMENT;
+          }
+        }
+        fbPtr = fbPtr + j;
+        fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr);  /* comment != whitespace */
+        j = rawTokenLen(fbPtr);
+        if (j != 2 || strncmp("$]", fbPtr, (size_t)j)) {
+          /* The token after the file name isn't "$]" */
+          /* Find end of comment and continue */
+          goto GET_PASSED_END_OF_COMMENT;
+        }
+        fbPtr = fbPtr + j;
+        fbPtr = fbPtr + rawWhiteSpaceLen(fbPtr);  /* comment != whitespace */
+        j = rawTokenLen(fbPtr);
+        if (j != 2 || strncmp("$)", fbPtr, (size_t)j)) {
+          /* The token after the "$]" isn't "$)" */
+          /* Find end of comment and continue */
+          goto GET_PASSED_END_OF_COMMENT;
+        }
+        /* We are now at the end of "$( Begin/Skip/End $[ file $] $)" */
+        fbPtr = fbPtr + j;
+        if (lookForEndMode == 0) {
+          *cmdPos2 = fbPtr - fileBuf + 1
+            + ((*cmdType == 'B') ? 1 : 0);/*after whitespace for 'B' (see above)*/
+          if (*cmdType == 'S') {  /* Skip command; we're done */
+            *endPos1 = 0;
+            *endPos2 = 0;
+            return;
+          }
+          if (*cmdType != 'B') bug(1742);
+          lookForEndMode = 1;
+        } else { /* We're at an End */
+          if (*cmdType != 'E') bug(1743);
+          /*lookForEndMode = 0;*/ /* Not needed since we will return soon */
+          *cmdType = 'B'; /* Restore it to B for Begin/End pair */
+          *endPos2 = fbPtr - fileBuf + 1;
+          return;
+        }
+        continue; /* We're past Begin; start search for End */
+      } /* Begin, End, or Skip */
+    } else if (i != i + 1) { /* Suppress "unreachable code" warning for bug trap below */
+      /* It's '$' not followed by '[' or '('; j is token length */
+      fbPtr = fbPtr + j;
+      continue;
+    }
+    bug(1746); /* Should never get here */
+   GET_PASSED_END_OF_COMMENT:
+    /* Note that fbPtr should be at beginning of last token found, which
+       may be "$)" (in which case i will be 1 from the instr) */
+
+    /* Don't use instr because it computes string length each call */
+    /*i = instr(1, fbPtr, "$)");*/ /* Normally this will be fast because we only
+        have to find the end of the comment that we're in */
+    /* Emulate the instr() */
+    tmpPtr = fbPtr;
+    i = 0;
+    while (1) {
+
+      /* strchr is incredibly slow under lcc - why?
+         Is it computing strlen internally maybe? */
+      /*
+      tmpPtr = strchr(tmpPtr, '$');
+      if (tmpPtr == NULL) {
+        i = 0;
+        break;
+      }
+      if (tmpPtr[1] == ')') {
+        i = tmpPtr - fbPtr + 1;
+        break;
+      }
+      tmpPtr++;
+      */
+
+      /* Emulate strchr */
+      while (tmpPtr[0] != '$') {
+        if (tmpPtr[0] == 0) break;
+        tmpPtr++;
+      }
+      if (tmpPtr[0] == 0) {
+        i = 0;
+        break;
+      }
+      if (tmpPtr[1] == ')') {
+        i = tmpPtr - fbPtr + 1;
+        break;
+      }
+      tmpPtr++;
+
+    } /* while (1) */
+
+    if (i == 0) {
+      /* TODO: better error msg */
+      printf("?End of comment not found\n");
+      i = (long)strlen(fileBuf); /* Slow, but this is a rare error */
+      fbPtr = fileBuf + i; /* Points to null at end of fileBuf */
+    } else {
+      fbPtr = fbPtr + i + 2 - 1; /* Skip the "$)" - skip 2 characters, then
+           back up 1 since the instr result starts at 1 */
+    }
+    /* continue; */  /* Not necessary since we're at end of loop */
+  } /* while (1) */
+  if (j != 0) bug(1744); /* Should be at end of file */
+  if (lookForEndMode == 1) {
+    /* We didn't find an End */
+    *cmdType = 'E';
+    *endPos1 = 0; *endPos2 = 0;
+  } else {
+    *cmdType = 'N'; /* no include was found */
+    *cmdPos1 = 0; *cmdPos2 = 0; *endPos1 = 0; *endPos2 = 0;
+    free_vstring(*fileName);
+  }
+  return;
+
+} /* getNextInclusion */
+
+
+
+/* This function transfers the content of the g_Statement[] array
+   to a linear buffer in preparation for creating the output file.
+   Any changes such as modified proofs will be updated in the buffer. */
+/* The caller is responsible for deallocating the returned string. */
+vstring writeSourceToBuffer(void) {
+  long stmt, size;
+  vstring_def(buf);
+  char *ptr;
+
+  /* Compute the size of the buffer */
+  /* Note that g_Statement[g_statements + 1] is a dummy statement
+     containing the text after the last statement in its
+     labelSection */
+  size = 0;
+  for (stmt = 1; stmt <= g_statements + 1; stmt++) {
+    /* Add the sizes of the sections.  When sections don't exist
+       (like a proof for a $a statement), the section length is 0. */
+    size += g_Statement[stmt].labelSectionLen
+        + g_Statement[stmt].mathSectionLen
+        + g_Statement[stmt].proofSectionLen;
+    /* Add in the 2-char length of keywords, which aren't stored in
+       the statement sections */
+    switch (g_Statement[stmt].type) {
+      case lb_: /* ${ */
+      case rb_: /* $} */
+        size += 2;
+        break;
+      case v_: /* $v */
+      case c_: /* $c */
+      case d_: /* $d */
+      case e_: /* $e */
+      case f_: /* $f */
+      case a_: /* $a */
+        size += 4;
+        break;
+      case p_: /* $p */
+        size += 6;
+        break;
+      case illegal_: /* dummy */
+        if (stmt != g_statements + 1) bug(1747);
+        /* The labelLen is text after last statement */
+        size += 0; /* There are no keywords in g_statements + 1 */
+        break;
+      default: bug(1748);
+    } /* switch (g_Statement[stmt].type) */
+  } /* next stmt */
+
+  /* Create the output buffer */
+  /* We could have created it with let(&buf, space(size)), but malloc should
+     be slightly faster since we don't have to initialize each entry */
+  buf = malloc((size_t)(size + 1) * sizeof(char));
+
+  ptr = buf; /* Pointer to keep track of buf location */
+  /* Transfer the g_Statement[] array to buf */
+  for (stmt = 1; stmt <= g_statements + 1; stmt++) {
+    /* Always transfer the label section (text before $ keyword) */
+    memcpy(ptr/*dest*/, g_Statement[stmt].labelSectionPtr/*source*/,
+        (size_t)(g_Statement[stmt].labelSectionLen)/*size*/);
+    ptr += g_Statement[stmt].labelSectionLen;
+    switch (g_Statement[stmt].type) {
+      case illegal_:
+        if (stmt != g_statements + 1) bug(1749);
+        break;
+      case lb_: /* ${ */
+      case rb_: /* $} */
+        ptr[0] = '$';
+        ptr[1] = g_Statement[stmt].type;
+        ptr += 2;
+        break;
+      case v_: /* $v */
+      case c_: /* $c */
+      case d_: /* $d */
+      case e_: /* $e */
+      case f_: /* $f */
+      case a_: /* $a */
+        ptr[0] = '$';
+        ptr[1] = g_Statement[stmt].type;
+        ptr += 2;
+        memcpy(ptr/*dest*/, g_Statement[stmt].mathSectionPtr/*source*/,
+            (size_t)(g_Statement[stmt].mathSectionLen)/*size*/);
+        ptr += g_Statement[stmt].mathSectionLen;
+        ptr[0] = '$';
+        ptr[1] = '.';
+        ptr += 2;
+        break;
+      case p_: /* $p */
+        ptr[0] = '$';
+        ptr[1] = g_Statement[stmt].type;
+        ptr += 2;
+        memcpy(ptr/*dest*/, g_Statement[stmt].mathSectionPtr/*source*/,
+            (size_t)(g_Statement[stmt].mathSectionLen)/*size*/);
+        ptr += g_Statement[stmt].mathSectionLen;
+        ptr[0] = '$';
+        ptr[1] = '=';
+        ptr += 2;
+        memcpy(ptr/*dest*/, g_Statement[stmt].proofSectionPtr/*source*/,
+            (size_t)(g_Statement[stmt].proofSectionLen)/*size*/);
+        ptr += g_Statement[stmt].proofSectionLen;
+        ptr[0] = '$';
+        ptr[1] = '.';
+        ptr += 2;
+        break;
+      default: bug(1750);
+    } /* switch (g_Statement[stmt].type) */
+  } /* next stmt */
+  if (ptr - buf != size) bug(1751);
+  buf[size] = 0; /* End of string marker */
+  return buf;
+} /* writeSourceToBuffer */
+
+
+/* This function creates split files containing $[ $] inclusions, from
+   an unsplit source with $( Begin $[... etc. inclusions */
+/* This function calls itself recursively, and after the recursive call
+   the fileBuf (=includeBuf) argument is deallocated. */
+/* For the top level call, fileName MUST NOT HAVE A DIRECTORY PATH */
+/* Note that fileBuf must be deallocated by initial caller.  This will let the
+   caller decide whether to say re-use fileBuf to create an unsplit version
+   of the .mm file in case the split version generation encounters an error. */
+/*                                     TODO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
+/*flag(TODO)*/ void writeSplitSource(vstring *fileBuf, const char *fileName,
+    flag noVersioningFlag, flag noDeleteFlag) {
+  FILE *fp;
+  vstring_def(tmpStr1);
+  vstring_def(tmpFileName);
+  vstring_def(includeBuf);
+  vstring_def(includeFn);
+  vstring_def(fileNameWithPath);
+  long size;
+  flag writeFlag;
+  long startOffset;
+  long cmdPos1;
+  long cmdPos2;
+  long endPos1;
+  long endPos2;
+  char cmdType;
+  startOffset = 0;
+  let(&fileNameWithPath, cat(g_rootDirectory, fileName, NULL));
+  while (1) {
+    getNextInclusion(*fileBuf, startOffset, /* inputs */
+        /* outputs: */
+        &cmdPos1, &cmdPos2,
+        &endPos1, &endPos2,
+        &cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
+                     'I' = "[$...",
+                     'S' = "$( Skip [$...",
+                     'E' = Start missing matched End
+                     'N' = no include found; includeFn = "" */
+        &includeFn /* name of included file */ );
+    if (cmdType == 'N') {
+      writeFlag = 0;
+      /* There are no more includes to expand, so write out the file */
+      if (!strcmp(fileName, g_output_fn)) {
+        /* We're writing the top-level file - always create new version */
+        writeFlag = 1;
+      } else {
+        /* We're writing an included file */
+        /* See if the file already exists */
+        free_vstring(tmpStr1);
+        tmpStr1 = readFileToString(fileNameWithPath, 0/*quiet*/, &size);
+        if (tmpStr1 == NULL) {
+          tmpStr1 = ""; /* Prevent seg fault */
+          /* If it doesn't exist, see if the ~1 version exists */
+          let(&tmpFileName, cat(fileNameWithPath, "~1", NULL));
+          tmpStr1 = readFileToString(tmpFileName, 0/*quiet*/, &size);
+          if (tmpStr1 == NULL) {
+            tmpStr1 = ""; /* Prevent seg fault */
+            /* Create and write the file */
+            writeFlag = 1;
+          } else {
+            /* See if the ~1 backup file content changed */
+            if (strcmp(tmpStr1, *fileBuf)) {
+              /* Content is different; write the file */
+              writeFlag = 1;
+            } else {
+              /* Just rename the ~1 version to the main version */
+              print2("Recovering \"%s\" from \"%s\"...\n",
+                  fileNameWithPath, tmpFileName);
+              rename(tmpFileName/*old*/, fileNameWithPath/*new*/);
+            }
+          } /* if (tmpStr1 == NULL) */
+        } else { /* tmpStr1 != NULL */
+          /* The include file already exists; see if the content changed */
+          if (strcmp(tmpStr1, *fileBuf)) {
+            /* Content is different; write the file */
+            writeFlag = 1;
+          } else {
+            /* Just rename the ~1 version to the main version */
+            print2("Content of \"%s\" did not change.\n",
+                fileNameWithPath);
+            rename(tmpFileName/*old*/, fileNameWithPath/*new*/);
+          }
+        }
+      }
+      if (writeFlag == 1) {
+        fp = fSafeOpen(fileNameWithPath, "w", 0/*noVersioningFlag*/);
+        if (fp == NULL) {
+          /* TODO: better error msg?  Abort and don't split? */
+          print2("?Error: couldn't create the file \"%s\"\n", fileNameWithPath);
+          print2("  Make sure any directories needed have been created.\n");
+          print2("  Try WRITE SOURCE without / SPLIT to recover your work.\n");
+          break;
+        } else {
+          print2("Writing \"%s\"...\n", fileNameWithPath);
+          fprintf(fp, "%s", *fileBuf);
+          fclose(fp);
+          break;
+        }
+      } /* if (writeFlag == 1 ) */
+      break;
+    } else if (cmdType == 'S') {
+      /* Change "Skip" to a real inclusion */
+      let(&tmpStr1, cat("$[ ", includeFn, " $]", NULL));
+      startOffset = cmdPos1 - 1 + (long)strlen(tmpStr1);
+      let(&(*fileBuf), cat(left(*fileBuf, cmdPos1 - 1), tmpStr1,
+          right(*fileBuf, cmdPos2), NULL));
+      continue;
+    } else if (cmdType == 'B') {
+      /* Extract included file content and call this recursively to process */
+      let(&tmpStr1, cat("$[ ", includeFn, " $]", NULL));
+      startOffset = cmdPos1 - 1 + (long)strlen(tmpStr1);
+      /* We start _after_ the whitespace after cmdPos2 because it wasn't
+         in the original included file but was put there by us in
+         readSourceAndIncludes() */
+      let(&includeBuf, seg(*fileBuf, cmdPos2, endPos1 - 1));
+      let(&(*fileBuf), cat(left(*fileBuf, cmdPos1 - 1), tmpStr1,
+          right(*fileBuf, endPos2), NULL));
+      /* TODO: intercept error from deeper calls? */
+      writeSplitSource(&includeBuf, includeFn, noVersioningFlag, noDeleteFlag);
+      continue;
+    } else if (cmdType == 'I') {
+      bug(1752); /* Any real $[ $] should have been converted to comment */
+      /* However in theory, user could have faked an assignable description
+         if modifiable comments are added in the future...*/
+      startOffset = cmdPos2 - 1;
+      continue;
+    } else if (cmdType == 'E') {
+      /* TODO What error message should go here? */
+      print2("?Unterminated \"$( Begin $[...\" inclusion markup in \"%s\".",
+          fileNameWithPath);
+      startOffset = cmdPos2 - 1;
+      continue;
+    } else {
+      /* Should never happen */
+      bug(1753);
+    }
+  } /* while (1) */
+  /* Deallocate memory */
+  /* free_vstring(*fileBuf); */ /* Let caller decide whether to do this */
+  free_vstring(tmpStr1);
+  free_vstring(tmpFileName);
+  free_vstring(includeFn);
+  free_vstring(includeBuf);
+  free_vstring(fileNameWithPath);
+} /* writeSplitSource */
+
+
+/* When "write source" does not have the "/split" qualifier, by default
+   (i.e. without "/no_delete") the included modules are "deleted" (renamed
+   to ~1) since their content will be in the main output file. */
+/*flag(TODO)*/ void deleteSplits(vstring *fileBuf, flag noVersioningFlag) {
+  FILE *fp;
+  vstring_def(includeFn);
+  vstring_def(fileNameWithPath);
+  long startOffset;
+  long cmdPos1;
+  long cmdPos2;
+  long endPos1;
+  long endPos2;
+  char cmdType;
+  startOffset = 0;
+  while (1) {
+    /* We scan the source for all "$( Begin $[ file $] $)...$( End $[ file $]"
+       and when found, we "delete" file. */
+    getNextInclusion(*fileBuf, startOffset, /* inputs */
+        /* outputs: */
+        &cmdPos1, &cmdPos2,
+        &endPos1, &endPos2,
+        &cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
+                     'I' = "[$...",
+                     'S' = "$( Skip [$...",
+                     'E' = Start missing matched End
+                     'N' = no include found; includeFn = "" */
+        &includeFn /* name of included file */ );
+    /* We only care about the 'B' command */
+    if (cmdType == 'B') {
+      let(&fileNameWithPath, cat(g_rootDirectory, includeFn, NULL));
+      /* See if the included file exists */
+      fp = fopen(fileNameWithPath, "r");
+      if (fp != NULL) {
+        fclose(fp);
+        if (noVersioningFlag == 1) {
+          print2("Deleting \"%s\"...\n", fileNameWithPath);
+        } else {
+          print2("Renaming \"%s\" to \"%s~1\"...\n", fileNameWithPath,
+              fileNameWithPath);
+        }
+        fp = fSafeOpen(fileNameWithPath, "d", noVersioningFlag);
+      }
+      /* Adjust offset and continue */
+      /* We don't go the the normal end of the 'B' because it may
+         have other 'B's inside.  Instead, just skip past the Begin. */
+      startOffset = cmdPos2 - 1; /* don't use endPos2 */
+    } else if (cmdType == 'N') {
+      /* We're done */
+      break;
+    } else if (cmdType == 'S') {
+      /* Adjust offset and continue */
+      startOffset = cmdPos2 - 1;
+    } else if (cmdType == 'E') {
+      /* There's a problem, but ignore - should have been reported earlier */
+      /* Adjust offset and continue */
+      startOffset = cmdPos2 - 1;
+    } else if (cmdType == 'I') {
+      bug(1755); /* Should never happen */
+    } else {
+      bug(1756);
+    }
+    continue;
+  } /* while (1) */
+  /* Deallocate memory */
+  /* free_vstring(*fileBuf); */ /* Let caller decide whether to do this */
+  free_vstring(includeFn);
+  free_vstring(fileNameWithPath);
+  return;
+} /* deleteSplits */
+
+
+/* Get file name and line number given a pointer into the read buffer */
+/* The user must deallocate the returned string (file name) */
+/* The global g_IncludeCall structure and g_includeCalls are used */
+vstring getFileAndLineNum(const char *buffPtr /* start of read buffer */,
+    const char *currentPtr /* place at which to get file name and line no */,
+    long *lineNum /* return argument */) {
+  long i, smallestOffset, smallestNdx;
+  vstring_def(fileName);
+
+  /* Make sure it's not outside the read buffer */
+  if (currentPtr < buffPtr
+      || currentPtr >= buffPtr + g_IncludeCall[1].current_offset) {
+    bug(1769);
+  }
+
+  /* Find the g_IncludeCall that is closest to currentPtr but does not
+     exceed it */
+  smallestOffset = currentPtr - buffPtr; /* Start with g_IncludeCall[0] */
+  if (smallestOffset < 0) bug(1767);
+  smallestNdx = 0; /* Point to g_IncludeCall[0] */
+  for (i = 0; i <= g_includeCalls; i++) {
+    if (g_IncludeCall[i].current_offset <= currentPtr - buffPtr) {
+      if ((currentPtr - buffPtr) - g_IncludeCall[i].current_offset
+          <= smallestOffset) {
+        smallestOffset = (currentPtr - buffPtr) - g_IncludeCall[i].current_offset;
+        smallestNdx = i;
+      }
+    }
+  }
+  if (smallestOffset < 0) bug(1768);
+  *lineNum = g_IncludeCall[smallestNdx].current_line
+      + countLines(buffPtr + g_IncludeCall[smallestNdx].current_offset,
+          smallestOffset);
+  /* Assign to new string to prevent original from being deallocated */
+  let(&fileName, g_IncludeCall[smallestNdx].source_fn);
+/*D*//*printf("smallestNdx=%ld smallestOffset=%ld i[].co=%ld\n",smallestNdx,smallestOffset,g_IncludeCall[smallestNdx].current_offset);*/
+  return fileName;
+} /* getFileAndLineNo */
+
+
+/* g_Statement[stmtNum].fileName and .lineNum are initialized to "" and 0.
+   To save CPU time, they aren't normally assigned until needed, but once
+   assigned they can be reused without looking them up again.  This function
+   will assign them if they haven't been assigned yet.  It just returns if
+   they have been assigned. */
+/* The globals g_Statement[] and g_sourcePtr are used */
+void assignStmtFileAndLineNum(long stmtNum) {
+  if (g_Statement[stmtNum].lineNum > 0) return; /* Already assigned */
+  if (g_Statement[stmtNum].lineNum < 0) bug(1766);
+  if (g_Statement[stmtNum].fileName[0] != 0) bug(1770); /* Should be empty string */
+  /* We can make a direct string assignment here since previous value was "" */
+  g_Statement[stmtNum].fileName = getFileAndLineNum(g_sourcePtr,
+      g_Statement[stmtNum].statementPtr, &(g_Statement[stmtNum].lineNum));
+  return;
+} /* assignStmtFileAndLineNum */
+
+
+
+/* This function returns a pointer to a buffer containing the contents of an
+   input file and its 'include' calls.  'Size' returns the buffer's size.  */
+/* TODO - ability to flag error to skip raw source function */
+/* Recursive function that processes a found include */
+/* If NULL is returned, it means a serious error occurred (like missing file)
+   and reading should be aborted. */
+/* Globals used:  g_IncludeCall[], g_includeCalls */
+vstring readInclude(const char *fileBuf, long fileBufOffset,
+    const char *sourceFileName, long *size, long parentLineNum, flag *errorFlag)
+{
+  long i;
+  long inclSize;
+  vstring_def(newFileBuf);
+  vstring_def(inclPrefix);
+  vstring_def(tmpSource);
+  vstring_def(inclSource);
+  vstring_def(oldSource);
+  vstring_def(inclSuffix);
+
+  long startOffset;
+  long cmdPos1;
+  long cmdPos2;
+  long endPos1;
+  long endPos2;
+  char cmdType;
+  long oldInclSize = 0; /* Init to avoid compiler warning */
+  long newInclSize = 0; /* Init to avoid compiler warning */
+  long befInclLineNum;
+  long aftInclLineNum;
+  vstring_def(includeFn);
+  vstring_def(fullInputFn);
+  vstring_def(fullIncludeFn);
+  long alreadyInclBy;
+  long saveInclCalls;
+
+  let(&newFileBuf, fileBuf);  /* TODO - can this be avoided for speedup? */
+  /* Look for and process includes */
+  startOffset = 0;
+
+  while (1) {
+    getNextInclusion(newFileBuf, startOffset, /* inputs */
+        /* outputs: */
+        &cmdPos1, &cmdPos2,
+        &endPos1, &endPos2,
+        &cmdType, /* 'B' = "$( Begin [$..." through "$( End [$...",
+                     'I' = "[$...",
+                     'S' = "$( Skip [$...",
+           TODO: add error code for missing $]?
+                     'E' = Begin missing matched End
+                     'N' = no include found; includeFn = "" */
+        &includeFn /* name of included file */ );
+    /*
+    cmdType = 'B' or 'E':
+          ....... $( Begin $[ prop.mm $] $)   ......   $( End $[ prop.mm $] $) ...
+           ^      ^           ^^^^^^^       ^          ^                      ^
+      startOffset cmdPos1    fileName  cmdPos2     endPos1              endPos2
+                                                 (=0 if no End)   (=0 if no End)
+
+       Note: in the special case of Begin, cmdPos2 points _after_ the whitespace
+       after "$( Begin $[ prop.mm $] $)" i.e. the whitespace is considered part of
+       the Begin command.  The is needed because prop.mm content doesn't
+       necessarily start with whitespace.  prop.mm does, however, end with
+       whitespace (\n) as enforced by readFileToString().
+
+    cmdType = 'I':
+          ............... $[ prop.mm $]  ..............
+           ^              ^  ^^^^^^^   ^
+      startOffset   cmdPos1 fileName  cmdPos2     endPos1=0  endPos2=0
+
+    cmdType = 'S':
+          ....... $( Skip $[ prop.mm $] $)   ......
+           ^      ^          ^^^^^^^      ^
+      startOffset cmdPos1    fileName  cmdPos2     endPos1=0  endPos2=0
+    */
+
+    if (cmdType == 'N') break; /* No (more) includes */
+    if (cmdType == 'E') {
+      /* TODO: Better error msg here or in getNextInclude */
+      print2("?Error: \"$( Begin $[...\" without matching \"$( End $[...\"\n");
+      startOffset = cmdPos2; /* Get passed the bad "$( Begin $[..." */
+      *errorFlag = 1;
+      continue;
+    }
+
+    /* Count lines between start of last source continuation and end of
+       the inclusion, before the newFileBuf is modified */
+
+    if (g_IncludeCall[g_includeCalls].pushOrPop != 1) bug(1764);
+    /*
+    contLineNum = g_IncludeCall[g_includeCalls].current_line
+      + countLines(newFileBuf, ((cmdType == 'B') ? endPos2 : cmdPos2)
+          - g_IncludeCall[g_includeCalls].current_offset);\
+    */
+
+
+    /* If we're here, cmdType is 'B', 'I', or 'S' */
+
+    /* Create 2 new includeCall entries before recursive call, so that
+       alreadyInclBy will scan entries in proper order (e.g. if this
+       include calls itself at a deeper level - weird but not illegal - the
+       deeper one should be Skip'd per the Metamath spec). */
+    /* This entry is identified by pushOrPop = 0 */
+    g_includeCalls++;
+    /* We will use two more entries here (include call and return), and
+       in parseKeywords() a dummy additional top entry is assumed to exist.
+       Thus the comparison must be to 3 less than g_MAX_INCLUDECALLS. */
+    if (g_includeCalls >= g_MAX_INCLUDECALLS - 3) {
+      g_MAX_INCLUDECALLS = g_MAX_INCLUDECALLS + 20;
+/*E*/if(db5)print2("'Include' call table was increased to %ld entries.\n",
+/*E*/    g_MAX_INCLUDECALLS);
+      g_IncludeCall = realloc(g_IncludeCall, (size_t)g_MAX_INCLUDECALLS *
+          sizeof(struct includeCall_struct));
+      if (g_IncludeCall == NULL) outOfMemory("#2 (g_IncludeCall)");
+    }
+    g_IncludeCall[g_includeCalls].pushOrPop = 0;
+
+    /* This entry is identified by pushOrPop = 1 */
+    g_includeCalls++;
+    g_IncludeCall[g_includeCalls].pushOrPop = 1;
+    /* Save the value before recursive calls will increment the global
+       g_includeCalls */
+    saveInclCalls = g_includeCalls;
+
+    g_IncludeCall[saveInclCalls - 1].included_fn = "";  /* Initialize string */
+    let(&(g_IncludeCall[saveInclCalls - 1].included_fn), includeFn); /* Name of the
+       file in the inclusion statement e.g. "$( Begin $[ included_fn..." */
+    g_IncludeCall[saveInclCalls].included_fn = "";
+    let(&g_IncludeCall[saveInclCalls].included_fn,
+        sourceFileName); /* Continuation of parent file after this include */
+
+
+    /* See if includeFn file has already been included */
+    alreadyInclBy = -1;
+    for (i = 0; i <= saveInclCalls - 2; i++) {
+      if (g_IncludeCall[i].pushOrPop == 0
+         && !strcmp(g_IncludeCall[i].included_fn, includeFn)) {
+        /*
+        print2("%s",cat(
+            "(File \"",
+            g_IncludeCall[g_includeCalls].source_fn,
+            "\", referenced at line ",
+            str((double)(g_IncludeCall[g_includeCalls].calledBy_line)),
+            " in \"",
+            g_IncludeCall[g_includeCalls].calledBy_fn,
+            "\", has already been included.)\n",NULL));
+        */
+        alreadyInclBy = i;
+        break;
+      }
+    }
+    if (alreadyInclBy == -1) {
+      /* This is the first time the included file has been included */
+      switch (cmdType) {
+        case 'B':
+          let(&inclPrefix, seg(newFileBuf, cmdPos1, cmdPos2 - 1)); /* Keep trailing
+              \n (or other whitespace) as part of prefix for the special
+              case of Begin - cmdPos2 points to char after \n */
+          let(&inclSuffix, seg(newFileBuf, endPos1, endPos2 - 1));
+          let(&tmpSource, seg(newFileBuf, cmdPos2, endPos1 - 1));  /* Save the
+              included source */
+          inclSize = endPos1 - cmdPos2; /* Actual included source size */
+
+          /* Get the parent line number up to the inclusion */
+          befInclLineNum = parentLineNum + countLines(
+              newFileBuf + startOffset + 1,
+              cmdPos2 - 1 - startOffset);
+          g_IncludeCall[saveInclCalls - 1].current_line = befInclLineNum - 1;
+          aftInclLineNum = befInclLineNum + countLines(newFileBuf
+              + cmdPos2/*start at (cmdPos2+1)th character*/,
+              endPos2 - cmdPos2 - 1) + 1;
+          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum - 1;
+          parentLineNum = aftInclLineNum;
+
+          /* Call recursively to expand any includes in the included source */
+          /* Use parentLineNum since the inclusion source is in the parent file */
+          free_vstring(inclSource);
+          inclSource = readInclude(tmpSource,
+              fileBufOffset + cmdPos1 - 1 + (long)strlen(inclPrefix), /*new offset*/
+              /*includeFn,*/ sourceFileName,
+              &inclSize /*input/output*/, befInclLineNum, &(*errorFlag));
+
+          oldInclSize = endPos2 - cmdPos1; /* Includes old prefix and suffix */
+          /*newInclSize = oldInclSize;*/ /* Includes new prefix and suffix */
+          newInclSize = (long)strlen(inclPrefix) + inclSize +
+                (long)strlen(inclSuffix); /* Includes new prefix and suffix */
+          /* It is already a Begin comment, so leave it alone */
+          /* *size = *size; */
+          /* Adjust starting position for next inclusion search */
+          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is 0-based but
+              cmdPos2 is 1-based */
+          break;
+        case 'I':
+          /* Read the included file */
+          let(&fullIncludeFn, cat(g_rootDirectory, includeFn, NULL));
+          free_vstring(tmpSource);
+          tmpSource = readFileToString(fullIncludeFn, 0/*verbose*/, &inclSize);
+          if (tmpSource == NULL) {
+            /* TODO: print better error msg?*/
+            print2(
+                "?Error: file \"%s%s\" (included in \"%s\") was not found\n",
+                fullIncludeFn, g_rootDirectory, sourceFileName);
+            tmpSource = "";
+            inclSize = 0;
+            *errorFlag = 1;
+          } else {
+            print2("Reading included file \"%s\"... %ld bytes\n",
+                fullIncludeFn, inclSize);
+          }
+
+          /* Change inclusion command to Begin...End comment */
+          let(&inclPrefix, cat("$( Begin $[ ", includeFn, " $] $)\n", NULL));
+               /* Note that trailing whitespace is part of the prefix in
+                  the special case of Begin, because the included file might
+                  not start with whitespace.  However, the included file
+                  will always end with whitespace i.e. \n as enforced by
+                  readFileToString(). */
+          let(&inclSuffix, cat("$( End $[ ", includeFn, " $] $)", NULL));
+
+          /* Get the parent line number up to the inclusion */
+          /* TODO: compute aftInclLineNum directly and eliminate befInclLineNum */
+          befInclLineNum = parentLineNum + countLines(
+              newFileBuf + startOffset + 1,
+              cmdPos1 - 1 - startOffset);
+          g_IncludeCall[saveInclCalls - 1].current_line = 0;
+          aftInclLineNum = befInclLineNum + countLines(newFileBuf
+              + cmdPos1/*start at (cmdPos1+1)th character*/,
+              cmdPos2 - cmdPos1 - 1);
+          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum;
+          parentLineNum = aftInclLineNum;
+
+          /* Call recursively to expand includes in the included source */
+          /* Start at line 1 since source is in external file */
+          free_vstring(inclSource);
+          inclSource = readInclude(tmpSource,
+              fileBufOffset + cmdPos1 - 1 + (long)strlen(inclPrefix), /*new offset*/
+              /*includeFn,*/ includeFn,
+              &inclSize /*input/output*/, 1/*parentLineNum*/, &(*errorFlag));
+
+          oldInclSize = cmdPos2 - cmdPos1; /* Includes old prefix and suffix */
+          /* "$( Begin $[...$] $)" must have a whitespace
+             after it; we use a newline.  readFileToString will ensure a
+             newline at its end.  We don't add whitespace after
+             "$( End $[...$] $)" but reuse the whitespace after the original
+             "$[...$]". */
+          let(&newFileBuf, cat(left(newFileBuf, cmdPos1 - 1),
+              inclPrefix, inclSource, inclSuffix,
+              right(newFileBuf, cmdPos2), NULL));
+          *size = *size - (cmdPos2 - cmdPos1) + (long)strlen(inclPrefix)
+              + inclSize + (long)strlen(inclSuffix);
+          newInclSize = (long)strlen(inclPrefix) + inclSize +
+                (long)strlen(inclSuffix); /* Includes new prefix and suffix */
+          /* Adjust starting position for next inclusion search (which will
+             be at the start of the included file continuing into the remaining
+             parent file) */
+          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
+              0-based but cmdPos2 is 1-based */
+              /*
+              + inclSize  /@ Use instead of strlen for speed @/
+              + (long)strlen(inclSuffix) */
+              ;  /* -1 since startOffset is 0-based but cmdPos1 is 1-based */
+          /* TODO: update line numbers for error msgs */
+          break;
+        case 'S':
+          /* Read the included file */
+          let(&fullIncludeFn, cat(g_rootDirectory, includeFn, NULL));
+          free_vstring(tmpSource);
+          tmpSource = readFileToString(fullIncludeFn, 1/*verbose*/, &inclSize);
+          if (tmpSource == NULL) {
+            /* TODO: print better error msg */
+            print2(
+                "?Error: file \"%s%s\" (included in \"%s\") was not found\n",
+                fullIncludeFn, g_rootDirectory, sourceFileName);
+            *errorFlag = 1;
+            tmpSource = ""; /* Prevent seg fault */
+            inclSize = 0;
+          }
+
+          /* Change Skip comment to Begin...End comment */
+          let(&inclPrefix, cat("$( Begin $[ ", includeFn, " $] $)\n", NULL));
+          let(&inclSuffix, cat("$( End $[ ", includeFn, " $] $)", NULL));
+
+          /* Get the parent line number up to the inclusion */
+          befInclLineNum = parentLineNum + countLines(
+              newFileBuf + startOffset + 1,
+          /* TODO: compute aftInclLineNum directly and eliminate befInclLineNum */
+              cmdPos1 - 1 - startOffset);
+          g_IncludeCall[saveInclCalls - 1].current_line = 0;
+          aftInclLineNum = befInclLineNum + countLines(newFileBuf
+              + cmdPos1/*start at (cmdPos1+1)th character*/,
+              cmdPos2 - cmdPos1 - 1);
+          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum;
+          parentLineNum = aftInclLineNum;
+
+          /* Call recursively to expand includes in the included source */
+          /* Start at line 1 since source is in external file */
+          free_vstring(inclSource);
+          inclSource = readInclude(tmpSource,
+              fileBufOffset + cmdPos1 - 1 + (long)strlen(inclPrefix), /*new offset*/
+              /*includeFn,*/ includeFn,
+              &inclSize /*input/output*/, 1/*parentLineNum*/, &(*errorFlag));
+
+          oldInclSize = cmdPos2 - cmdPos1; /* Includes old prefix and suffix */
+          let(&newFileBuf, cat(left(newFileBuf, cmdPos1 - 1),
+              inclPrefix, inclSource, inclSuffix,
+              right(newFileBuf, cmdPos2), NULL));
+          newInclSize = (long)strlen(inclPrefix) + inclSize +
+                (long)strlen(inclSuffix); /* Includes new prefix and suffix */
+          *size = *size - (cmdPos2 - cmdPos1) + (long)strlen(inclPrefix)
+              + inclSize + (long)strlen(inclSuffix);
+          /* Adjust starting position for next inclusion search */
+          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
+              0-based but cmdPos2 is 1-based */
+          /* TODO: update line numbers for error msgs */
+          break;
+        default:
+          bug(1745);
+      } /* end switch (cmdType) */
+    } else {
+      /* This file has already been included.  Change Begin and $[ $] to
+         Skip.  alreadyInclBy is the index of the previous g_IncludeCall that
+         included it. */
+      if (!(alreadyInclBy > 0)) bug(1765);
+      switch (cmdType) {
+        case 'B':
+          /* Save the included source temporarily */
+          let(&inclSource,
+              seg(newFileBuf, cmdPos2, endPos1 - 1));
+          /* Make sure it's content matches */
+          free_vstring(oldSource);
+          oldSource = g_IncludeCall[  /* Connect to source for brevity */
+                  alreadyInclBy
+                  ].current_includeSource;
+          if (strcmp(inclSource, oldSource)) {
+            /* TODO - print better error msg */
+            print2(
+        "?Warning: \"$( Begin $[...\" source, with %ld characters, mismatches\n",
+                (long)strlen(inclSource));
+            print2(
+                "  earlier inclusion, with %ld characters.\n",
+                (long)strlen(oldSource));
+          }
+          oldSource = ""; /* Disconnect from source */
+          /* We need to delete it from the source and change to Skip */
+          let(&inclPrefix, cat("$( Skip $[ ", includeFn, " $] $)", NULL));
+          let(&inclSuffix, "");
+
+          /* Get the parent line number up to the inclusion */
+          befInclLineNum = parentLineNum + countLines(
+              newFileBuf + startOffset + 1,
+              cmdPos2 - 1 - startOffset);
+          g_IncludeCall[saveInclCalls - 1].current_line = befInclLineNum;
+          aftInclLineNum = befInclLineNum + countLines(newFileBuf
+              + cmdPos2/*start at (cmdPos2+1)th character*/,
+              endPos2 - cmdPos2 /*- 1*/);
+          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum;
+          parentLineNum = aftInclLineNum;
+
+          let(&inclSource, ""); /* Final source to be stored - none */
+          inclSize = 0; /* Size of just the included source */
+          oldInclSize = endPos2 - cmdPos1; /* Includes old prefix and suffix */
+          let(&newFileBuf, cat(left(newFileBuf, cmdPos1 - 1),
+              inclPrefix,
+              right(newFileBuf, endPos2), NULL));
+          newInclSize = (long)strlen(inclPrefix); /* Includes new prefix and suffix */
+          *size = *size - (endPos2 - cmdPos1) + newInclSize;
+          /* Adjust starting position for next inclusion search (which may
+             occur inside the source we just included, so don't skip passed
+             that source) */
+          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
+              0-based but cmdPos2 is 1-based */
+          break;
+        case 'I':
+          /* Change inclusion command to Skip comment */
+          let(&inclPrefix, cat("$( Skip $[ ", includeFn, " $] $)", NULL));
+          let(&inclSuffix, "");
+
+          /* Get the parent line number up to the inclusion */
+          befInclLineNum = parentLineNum + countLines(
+              newFileBuf + startOffset + 1,
+              cmdPos1 - 1 - startOffset);
+          g_IncludeCall[saveInclCalls - 1].current_line = befInclLineNum;
+          aftInclLineNum = befInclLineNum + countLines(newFileBuf
+              + cmdPos1/*start at (cmdPos1+1)th character*/,
+              cmdPos2 - cmdPos1 /*- 1*/);
+          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum;
+          parentLineNum = aftInclLineNum;
+
+          let(&inclSource, ""); /* Final source to be stored - none */
+          inclSize = 0; /* Size of just the included source */
+          oldInclSize = cmdPos2 - cmdPos1; /* Includes old prefix and suffix */
+          let(&newFileBuf, cat(left(newFileBuf, cmdPos1 - 1),
+              inclPrefix,
+              right(newFileBuf, cmdPos2), NULL));
+          newInclSize = (long)strlen(inclPrefix); /* Includes new prefix and suffix */
+          *size = *size - (cmdPos2 - cmdPos1) + (long)strlen(inclPrefix);
+          /* Adjust starting position for next inclusion search */
+          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
+              0-based but cmdPos2 is 1-based */
+          break;
+        case 'S':
+          /* It is already Skipped, so leave it alone */
+          /* *size = *size; */
+          /* Adjust starting position for next inclusion search */
+          let(&inclPrefix, seg(newFileBuf, cmdPos1, cmdPos2 - 1));
+          let(&inclSuffix, "");
+
+          /* Get the parent line number up to the inclusion */
+          befInclLineNum = parentLineNum + countLines(
+              newFileBuf + startOffset + 1,
+              cmdPos1 - 1 - startOffset);
+          g_IncludeCall[saveInclCalls - 1].current_line = befInclLineNum;
+          aftInclLineNum = befInclLineNum + countLines(newFileBuf
+              + cmdPos1/*start at (cmdPos1+1)th character*/,
+              cmdPos2 - cmdPos1 /*- 1*/);
+          g_IncludeCall[saveInclCalls].current_line = aftInclLineNum - 1;
+          parentLineNum = aftInclLineNum;
+
+          let(&inclSource, ""); /* Final source to be stored - none */
+          inclSize = 0; /* Size of just the included source */
+          oldInclSize = cmdPos2 - cmdPos1; /* Includes old prefix and suffix */
+          newInclSize = oldInclSize; /* Includes new prefix and suffix */
+          startOffset = cmdPos1 + newInclSize - 1; /* -1 since startOffset is
+              0-based but cmdPos2 is 1-based */
+          if (startOffset != cmdPos2 - 1) bug(1772);
+          break;
+        default:
+          bug(1745);
+      } /* end switch(cmdType) */
+    } /* if alreadyInclBy == -1 */
+
+    /* Assign structure with information for subsequent passes and
+       (later) error messages */
+    g_IncludeCall[saveInclCalls - 1].source_fn = "";  /* Name of the file where the
+       inclusion source is located (= parent file for $( Begin $[... etc.) */
+    g_IncludeCall[saveInclCalls - 1].current_offset = fileBufOffset + cmdPos1 - 1
+        + (long)strlen(inclPrefix) - 1; /* This is the starting character position
+            of the included file w.r.t entire source buffer */
+    if (alreadyInclBy >= 0 || cmdType == 'B') {
+      /* No external file was included, so let the includeFn be the
+         same as the source */
+      let(&g_IncludeCall[saveInclCalls - 1].source_fn, sourceFileName);
+    } else {
+      /* It hasn't been included yet, and cmdType is 'I' or 'S', meaning
+         that we bring in an external file */
+      let(&g_IncludeCall[saveInclCalls - 1].source_fn, includeFn); /* $[ $], Begin,
+          or Skip file name for this inclusion */
+      /*
+      g_IncludeCall[saveInclCalls - 1].current_line = 0; */ /* The line number
+          of the start of the included file (=1) */
+    }
+    g_IncludeCall[saveInclCalls - 1].current_includeSource = inclSource; /* let() not
+        needed because we're just assigning a new name to inclSource */
+    inclSource = ""; /* Detach from
+        g_IncludeCall[saveInclCalls - 1].current_includeSource for later reuse */
+    g_IncludeCall[saveInclCalls - 1].current_includeSource = "";
+    g_IncludeCall[saveInclCalls - 1].current_includeLength = inclSize; /* Length of the file
+        to be included (0 if the file was previously included) */
+
+
+    /* Initialize a new include call for the continuation of the parent. */
+    /* This entry is identified by pushOrPop = 1 */
+    g_IncludeCall[saveInclCalls].source_fn = "";  /* Name of the file to be
+        included */
+    let(&g_IncludeCall[saveInclCalls].source_fn,
+        sourceFileName); /* Source file containing
+         this inclusion */
+    g_IncludeCall[saveInclCalls].current_offset = fileBufOffset + cmdPos1
+        + newInclSize - 1;
+        /* This is the position of the continuation of the parent */
+    g_IncludeCall[saveInclCalls].current_includeSource = ""; /* (Currently) assigned
+        only if we may need it for a later Begin comparison */
+    g_IncludeCall[saveInclCalls].current_includeLength = 0; /* Length of the file
+        to be included (0 if the file was previously included) */
+
+  } /* while (1) */
+
+  /* Deallocate strings */
+  free_vstring(inclSource);
+  free_vstring(tmpSource);
+  free_vstring(oldSource);
+  free_vstring(inclPrefix);
+  free_vstring(inclSuffix);
+  free_vstring(includeFn);
+  free_vstring(fullInputFn);
+  free_vstring(fullIncludeFn);
+
+  return newFileBuf;
+} /* readInclude */
+
+
+/* This function returns a pointer to a buffer containing the contents of an
+   input file and its 'include' calls.  'Size' returns the buffer's size.  */
+/* TODO - ability to flag error to skip raw source function */
+/* If NULL is returned, it means a serious error occurred (like missing file)
+   and reading should be aborted. */
+vstring readSourceAndIncludes(const char *inputFn /*input*/, long *size /*output*/) {
+  long i;
+/*D*//* long j; */
+/*D*//* vstring_def(s); */
+  vstring_def(fileBuf);
+  vstring_def(newFileBuf);
+
+  vstring_def(fullInputFn);
+  flag errorFlag = 0;
+
+  /* Read starting file */
+  let(&fullInputFn, cat(g_rootDirectory, inputFn, NULL));
+  fileBuf = readFileToString(fullInputFn, 1/*verbose*/, &(*size));
+  if (fileBuf == NULL) {
+    print2("?Error: file \"%s\" was not found\n", fullInputFn);
+    fileBuf = "";
+    *size = 0;
+    errorFlag = 1;
+    /* goto RETURN_POINT; */ /* Don't go now so that g_IncludeCall[]
+         strings will be initialized.  If error, then blank fileBuf will
+         cause main while loop to break immediately after first
+         getNextInclusion() call. */
+  }
+  print2("Reading source file \"%s\"... %ld bytes\n", fullInputFn, *size);
+
+  /* Create a fictitious initial include for the main file (at least 2
+     g_IncludeCall structure array entries have been already been allocated
+     in initBigArrays() in mmdata.c) */
+  g_includeCalls = 0;
+  g_IncludeCall[g_includeCalls].pushOrPop = 0; /* 0 means start of included file,
+       1 means continuation of including file */
+  g_IncludeCall[g_includeCalls].source_fn = "";
+  let(&g_IncludeCall[g_includeCalls].source_fn, inputFn); /* $[ $], Begin,
+      of Skip file name for this inclusion */
+  g_IncludeCall[g_includeCalls].included_fn = "";
+  let(&g_IncludeCall[g_includeCalls].included_fn, inputFn); /* $[ $], Begin,
+      of Skip file name for this inclusion */
+  g_IncludeCall[g_includeCalls].current_offset = 0;  /* This is the starting
+      character position of the included file w.r.t entire source buffer */
+  g_IncludeCall[g_includeCalls].current_line = 1; /* The line number
+      of the start of the included file (=1) or the continuation line of
+      the parent file */
+  g_IncludeCall[g_includeCalls].current_includeSource = ""; /* (Currently) assigned
+      only if we may need it for a later Begin comparison */
+  g_IncludeCall[g_includeCalls].current_includeLength = *size; /* Length of the file
+      to be included (0 if the file was previously included) */
+
+  /* Create a fictitious entry for the "continuation" after the
+     main file, to make error message line searching easier */
+  g_includeCalls++;
+  g_IncludeCall[g_includeCalls].pushOrPop = 1; /* 0 means start of included file,
+       1 means continuation of including file */
+  g_IncludeCall[g_includeCalls].source_fn = "";
+  /*let(&g_IncludeCall[g_includeCalls].source_fn, inputFn);*/ /* Leave empty;
+        there is no "continuation" file for the main file, so no need to assign
+        (it won't be used) */
+  g_IncludeCall[g_includeCalls].included_fn = "";
+  /*let(&g_IncludeCall[g_includeCalls].included_fn, inputFn);*/ /* $[ $], Begin,
+      of Skip file name for this inclusion */
+  g_IncludeCall[g_includeCalls].current_line = -1; /* Ideally this should be
+      countLines(fileBuf), but since it's never used we don't bother to
+      call countLines(fileBuf) to save CPU time */
+  g_IncludeCall[g_includeCalls].current_includeSource = ""; /* (Currently) assigned
+      only if we may need it for a later Begin comparison */
+  g_IncludeCall[g_includeCalls].current_includeLength = 0; /* The "continuation"
+      of the main file is fictitious, so just set it to 0 length */
+
+  /* Recursively expand the source of an included file */
+  newFileBuf = "";
+  newFileBuf = readInclude(fileBuf, 0, /*inputFn,*/ inputFn, &(*size),
+      1/*parentLineNum*/, &errorFlag);
+  g_IncludeCall[1].current_offset = *size;  /* This is the starting
+      character position of the included file w.r.t entire source buffer.
+      Here, it points to the nonexistent character just beyond end of main file
+      (after all includes are expanded).
+      Note that readInclude() may change g_includeCalls, so use 1 explicitly. */
+  free_vstring(fileBuf); /* Deallocate */
+/*D*//*printf("*size=%ld\n",*size);                                       */
+/*D*//*for(i=0;i<*size;i++){                                              */
+/*D*//*  free_vstring(s);                                                     */
+/*D*//*s=getFileAndLineNum(newFileBuf,newFileBuf+i,&j);                   */
+/*D*//*printf("i=%ld ln=%ld fn=%s ch=%c\n",i,j,s,(newFileBuf+i)[0]);  }   */
+  if (errorFlag == 1) {
+    /* The read should be aborted by the caller. */
+    /* Deallocate the strings in the g_IncludeCall[] structure. */
+    for (i = 0; i <= g_includeCalls; i++) {
+      free_vstring(g_IncludeCall[i].source_fn);
+      free_vstring(g_IncludeCall[i].included_fn);
+      free_vstring(g_IncludeCall[i].current_includeSource);
+      g_includeCalls = -1; /* For the eraseSource() function in mmcmds.c */
+    }
+    return NULL;
+  } else {
+/*D*//*for (i=0; i<=g_includeCalls;i++)                                       */
+/*D*//*  printf("i=%ld p=%ld f=%s,%s l=%ld o=%ld s=%ld\n",                  */
+/*D*//*i,(long)g_IncludeCall[i].pushOrPop,g_IncludeCall[i].source_fn,           */
+/*D*//*g_IncludeCall[i].included_fn,g_IncludeCall[i].current_line,              */
+/*D*//*g_IncludeCall[i].current_offset,g_IncludeCall[i].current_includeLength); */
+    return newFileBuf;
+  }
+
+} /* readSourceAndIncludes */
+
diff --git a/src/mmpars.h b/src/mmpars.h
new file mode 100644
index 0000000..0d53953
--- /dev/null
+++ b/src/mmpars.h
@@ -0,0 +1,208 @@
+/*****************************************************************************/
+/*        Copyright (C) 2018  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMPARS_H_
+#define METAMATH_MMPARS_H_
+
+/*! \file */
+
+#include "mmvstr.h"
+#include "mmdata.h"
+
+char *readRawSource(vstring inputBuf, long *size);
+void parseKeywords(void);
+void parseLabels(void);
+void parseMathDecl(void);
+void parseStatements(void);
+char parseProof(long statemNum);
+char parseCompressedProof(long statemNum);
+nmbrString *getProof(long statemNum, flag printFlag);
+
+void rawSourceError(char *startFile, char *ptr, long tokenLen, vstring errMsg);
+void sourceError(char *ptr, long tokenLen, long stmtNum, vstring errMsg);
+void mathTokenError(long tokenNum /* 0 is 1st one */,
+    nmbrString *tokenList, long stmtNum, vstring errMsg);
+vstring shortDumpRPNStack(void);
+
+/*! Label comparison for qsort */
+int labelSortCmp(const void *key1, const void *key2);
+
+/*! Label comparison for bsearch */
+int labelSrchCmp(const void *key, const void *data);
+
+/*! Math token comparison for qsort */
+int mathSortCmp(const void *key1, const void *key2);
+
+/*! Math token label comparison for bsearch */
+int mathSrchCmp(const void *key, const void *data);
+
+/*! Hypothesis and local label comparison for qsort */
+int hypAndLocSortCmp(const void *key1, const void *key2);
+
+/*! Hypothesis and local label comparison for bsearch */
+int hypAndLocSrchCmp(const void *key, const void *data);
+
+/*! This function returns the length of the white space starting at ptr.
+   Comments are considered white space.  ptr should point to the first character
+   of the white space.  If ptr does not point to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned. */
+long whiteSpaceLen(char *ptr);
+
+/* For .mm file splitting */
+/*! Like whiteSpaceLen except comments are not whitespace */
+long rawWhiteSpaceLen(char *ptr);
+
+/*! This function returns the length of the token (non-white-space) starting at
+   ptr.  Comments are considered white space.  ptr should point to the first
+   character of the token.  If ptr points to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned.  If ptr
+   points to a keyword, 0 is returned.  A keyword ends a token. */
+long tokenLen(char *ptr);
+
+/*! Unlike tokenLen(), keywords are not treated as special.  In particular:
+   if ptr points to a keyword, 0 is NOT returned (instead, 2 is returned),
+   and a keyword does NOT end a token (which is a relic of days before
+   whitespace surrounding a token was part of the spec, but still serves
+   a useful purpose in token() for friendlier error detection). */
+long rawTokenLen(char *ptr);
+
+/*! This function returns the length of the proof token starting at
+   ptr.  Comments are considered white space.  ptr should point to the first
+   character of the token.  If ptr points to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned.  If ptr
+   points to a keyword, 0 is returned.  A keyword ends a token.
+   ":" is considered a token. */
+long proofTokenLen(char *ptr);
+
+/*! Counts the number of \n between start for length chars.
+   If length = -1, then use end-of-string 0 to stop.
+   If length >= 0, then scan at most length chars, but stop
+       if end-of-string 0 is found. */
+long countLines(const char *start, long length);
+
+/*! \brief Array with sort keys for all possible labels, including the ones for
+   hypotheses (which may not always be active)
+
+   This array is used to see if any label is used anywhere, and is used
+   to make sure there are no conflicts when local labels inside of compact
+   proofs are generated. */
+extern long *g_allLabelKeyBase;
+extern long g_numAllLabelKeys;
+
+
+extern long g_wrkProofMaxSize; /*!< Maximum size so far - it may grow */
+struct sortHypAndLoc {  /* Used for sorting hypAndLocLabel field */
+  long labelTokenNum;
+  void *labelName;
+};
+/*! \brief Working structure for parsing proofs
+  \note This structure should be deallocated by the ERASE command. */
+struct wrkProof_struct {
+  long numTokens; /*!< Number of tokens in proof */
+  long numSteps; /*!< Number of steps in proof */
+  long numLocalLabels; /*!< Number of local labels */
+  long numHypAndLoc; /*!< Number of active hypotheses and local labels */
+  char *localLabelPoolPtr; /*!< Next free location in local label pool */
+  long RPNStackPtr; /*!< Offset of end of RPNStack */
+  long errorCount; /*!< Errors in proof - used to suppress too many error msgs */
+  flag errorSeverity; /*!< 0 = OK, 1 = unknown step, 2 = error, 3 = severe error,
+                          4 = not a $p statement */
+
+  /* The following pointers will always be allocated with g_wrkProofMaxSize
+     entries.  If a function needs more than g_wrkProofMaxSize, it must
+     reallocate all of these and increase g_wrkProofMaxSize. */
+  nmbrString *tokenSrcPtrNmbr; /*!< Source parsed into tokens vs. token number
+                                    - token size */
+  pntrString *tokenSrcPtrPntr; /*!< Source parsed into tokens vs. token number
+                                    - token src ptrs */
+  nmbrString *stepSrcPtrNmbr; /*!< Pointer to label token in source file
+                                   vs. step number - label size */
+  pntrString *stepSrcPtrPntr; /*!< Pointer to label token in source file
+                                   vs. step number - label src ptrs */
+  flag *localLabelFlag; /*!< 1 means step has a local label declaration */
+  /*! Sorted ptrs to hyp and local label names + token# */
+  struct sortHypAndLoc *hypAndLocLabel;
+  char *localLabelPool; /*!< String pool to hold local labels */
+  nmbrString *proofString; /*!< The proof in RPN - statement # if > 0
+                             or -(step # + 1000) of local label decl if < -1 */
+  /*! Ptr to math string vs. each step (Allocated in verifyProof() as needed by nmbrLet()) */
+  pntrString *mathStringPtrs;
+  nmbrString *RPNStack; /*!< Stack for RPN parsing */
+
+  /* For compressed proof parsing */
+  nmbrString *compressedPfLabelMap; /*!< Map from compressed label to actual */
+  long compressedPfNumLabels; /*!< Number of compressed labels */
+};
+extern struct wrkProof_struct g_WrkProof;
+
+/*! Converts an ASCII string to a nmbrString of math symbols.  statemNum
+   provides the context for the parse (to get correct active symbols) */
+nmbrString *parseMathTokens(vstring userText, long statemNum);
+
+vstring outputStatement(long stmt, /*flag cleanFlag,*/ flag reformatFlag);
+/*! Caller must deallocate return string */
+vstring rewrapComment(const char *comment);
+
+/*! Lookup $a or $p label and return statement number.
+   Return -1 if not found. */
+long lookupLabel(const char *label);
+
+/* For file splitting */
+
+/*! Get the next real $[...$] or virtual $( Begin $[... inclusion */
+void getNextInclusion(char *fileBuf, long startOffset, /*!< inputs */
+    /*! outputs: */
+    long *cmdPos1, long *cmdPos2,
+    long *endPos1, long *endPos2,
+    char *cmdType, /*!< 'B' = "$( Begin [$..." through "$( End [$...",
+                        'I' = "[$...",
+                        'S' = "$( Skip [$...",
+                        'E' = Start missing matched End
+                        'N' = no include found */
+    vstring *fileName /*!< name of included file */
+    );
+
+/*! This function transfers the content of the statement[] array
+   to a linear buffer in preparation for creating the output file. */
+vstring writeSourceToBuffer(void);
+
+/*! This function creates split files containing $[ $] inclusions, from
+   an unsplit source with $( Begin $[... etc. inclusions
+  \note that *fileBuf is assigned to the empty string upon return, to
+   conserve memory */
+void writeSplitSource(vstring *fileBuf, const char *fileName,
+    flag noVersioningFlag, flag noDeleteFlag);
+
+/*! When "write source" does not have the "/split" qualifier, by default
+   (i.e. without "/no_delete") the included modules are "deleted" (renamed
+   to ~1) since their content will be in the main output file. */
+void deleteSplits(vstring *fileBuf, flag noVersioningFlag);
+
+/*! \brief Get file name and line number given a pointer into the read buffer
+ * \note The user must deallocate the returned string (file name)
+ * \note The globals includeCall structure and includeCalls are used
+ */
+vstring getFileAndLineNum(const char *buffPtr/*start of read buffer*/,
+    const char *currentPtr/*place at which to get file name and line no*/,
+    long *lineNum/*return argument*/);
+
+/*! statement[stmtNum].fileName and .lineNum are initialized to "" and 0.
+   To save CPU time, they aren't normally assigned until needed, but once
+   assigned they can be reused without looking them up again.  This function
+   will assign them if they haven't been assigned yet.  It just returns if
+   they have already been assigned.
+  \note The globals statement[] and sourcePtr are used */
+void assignStmtFileAndLineNum(long stmtNum);
+
+/*! Initial read of source file */
+vstring readSourceAndIncludes(const char *inputFn, long *size);
+
+/*! Recursively expand the source of an included file */
+vstring readInclude(const char *fileBuf, long fileBufOffset,
+    /*vstring inclFileName,*/ const char *sourceFileName,
+    long *size, long parentLineNum, flag *errorFlag);
+
+#endif /* METAMATH_MMPARS_H_ */
diff --git a/mmpfas.c b/src/mmpfas.c
similarity index 82%
rename from mmpfas.c
rename to src/mmpfas.c
index 676fd8f..0503300 100644
--- a/mmpfas.c
+++ b/src/mmpfas.c
@@ -1,3707 +1,3484 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* mmpfas.c - Proof assistant module */
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <setjmp.h>
-#include <time.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mminou.h"
-#include "mmpars.h"
-#include "mmunif.h"
-#include "mmpfas.h"
-/* For mathbox stuff: */ /* 5-Aug-2020 nm */
-#include "mmwtex.h"
-
-/* Allow user to define INLINE as "inline".  lcc doesn't support inline. */
-#ifndef INLINE
-#define INLINE
-#endif
-
-long g_proveStatement = 0; /* The statement to be proved - global */
-flag g_proofChangedFlag; /* Flag to push 'undo' stack - global */
-
-/* 4-Aug-2011 nm Changed from 25000 to 50000 */
-/* 11-Dec-2010 nm Changed from 10000 to 25000 to accomodate df-plig in set.mm
-   (which needs >= 23884 to generate with 'show statement / html'). */
-/* g_userMaxProveFloat can be overridden by user with SET SEARCH_LIMIT */
-long g_userMaxProveFloat = /*10000*/ 50000; /* Upper limit for proveFloating */
-
-long g_dummyVars = 0; /* Total number of dummy variables declared */
-long g_pipDummyVars = 0; /* Number of dummy variables used by proof in progress */
-
-/* Structure for holding a proof in progress. */
-/* This structure should be deallocated after use. */
-struct pip_struct g_ProofInProgress = {
-    NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
-
-/* Interactively select statement assignments that match */
-/* maxEssential is the maximum number of essential hypotheses that a
-   statement may have in order to be included in the matched list.
-   If -1, there is no limit. */
-void interactiveMatch(long step, long maxEssential)
-{
-  long matchCount = 0;
-  long timeoutCount = 0;
-  long essHypCount, hyp;
-  vstring matchFlags = "";
-  vstring timeoutFlags = "";
-  char unifFlag;
-  vstring tmpStr1 = "";
-  vstring tmpStr4 = "";
-  vstring tmpStr2 = "";
-  vstring tmpStr3 = "";
-  nmbrString *matchList = NULL_NMBRSTRING;
-  nmbrString *timeoutList = NULL_NMBRSTRING;
-  long stmt, matchListPos, timeoutListPos;
-
-  printLongLine(cat("Step ", str((double)step + 1), ":  ", nmbrCvtMToVString(
-      (g_ProofInProgress.target)[step]), NULL), "  ", " ");
-  if (nmbrLen((g_ProofInProgress.user)[step])) {
-    printLongLine(cat("Step ", str((double)step + 1), "(user):  ", nmbrCvtMToVString(
-        (g_ProofInProgress.user)[step]), NULL), "  ", " ");
-  }
-  /* Allocate a flag for each step to be tested */
-  /* 1 means no match, 2 means match */
-  let(&matchFlags, string(g_proveStatement, 1));
-  /* 1 means no timeout, 2 means timeout */
-  let(&timeoutFlags, string(g_proveStatement, 1));
-  for (stmt = 1; stmt < g_proveStatement; stmt++) {
-    if (g_Statement[stmt].type != (char)e_ &&
-        g_Statement[stmt].type != (char)f_ &&
-        g_Statement[stmt].type != (char)a_ &&
-        g_Statement[stmt].type != (char)p_) continue;
-
-    /* See if the maximum number of requested essential hypotheses is
-       exceeded */
-    if (maxEssential != -1) {
-      essHypCount = 0;
-      for (hyp = 0; hyp < g_Statement[stmt].numReqHyp; hyp++) {
-        if (g_Statement[g_Statement[stmt].reqHypList[hyp]].type == (char)e_) {
-          essHypCount++;
-          if (essHypCount > maxEssential) break;
-        }
-      }
-      if (essHypCount > maxEssential) continue;
-    }
-
-    unifFlag = checkStmtMatch(stmt, step);
-    if (unifFlag) {
-      if (unifFlag == 1) {
-        matchFlags[stmt] = 2;
-        matchCount++;
-      } else { /* unifFlag = 2 */
-        timeoutFlags[stmt] = 2;
-        timeoutCount++;
-      }
-    }
-  }
-
-  if (matchCount == 0 && timeoutCount == 0) {
-    print2("No statements match step %ld.  The proof has an error.\n",
-        (long)(step + 1));
-    let(&matchFlags, "");
-    let(&timeoutFlags, "");
-    return;
-  }
-
-#define MATCH_LIMIT 100
-  if (matchCount > MATCH_LIMIT) {
-    let(&tmpStr1, cat("There are ", str((double)matchCount), " matches for step ",
-      str((double)step + 1), ".  View them (Y, N) <N>? ", NULL));
-    tmpStr2 = cmdInput1(tmpStr1);
-    let(&tmpStr1, "");
-
-    if (tmpStr2[0] != 'Y' && tmpStr2[0] != 'y') {
-      let(&tmpStr2, "");
-      let(&matchFlags, "");
-      let(&timeoutFlags, "");
-      return;
-    }
-
-  }
-
-  nmbrLet(&matchList, nmbrSpace(matchCount));
-  matchListPos = 0;
-  for (stmt = 1; stmt < g_proveStatement; stmt++) {
-    if (matchFlags[stmt] == 2) {
-      matchList[matchListPos] = stmt;
-      matchListPos++;
-    }
-  }
-
-  nmbrLet(&timeoutList, nmbrSpace(timeoutCount));
-  timeoutListPos = 0;
-  for (stmt = 1; stmt < g_proveStatement; stmt++) {
-    if (timeoutFlags[stmt] == 2) {
-      timeoutList[timeoutListPos] = stmt;
-      timeoutListPos++;
-    }
-  }
-
-  let(&tmpStr1, nmbrCvtRToVString(matchList,
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum, used only if explicitTargets*/));
-  let(&tmpStr4, nmbrCvtRToVString(timeoutList,
-                /* 25-Jan-2016 nm */
-                0, /*explicitTargets*/
-                0 /*statemNum, used only if explicitTargets*/));
-
-  printLongLine(cat("Step ", str((double)step + 1), " matches statements:  ", tmpStr1,
-      NULL), "  ", " ");
-  if (timeoutCount) {
-    printLongLine(cat("In addition, there were unification timeouts with the",
-        " following steps, which may or may not match:  ", tmpStr4, NULL),
-        "  ", " ");
-  }
-
-  if (matchCount == 1 && timeoutCount == 0 && maxEssential == -1) {
-    /* Assign it automatically */
-    matchListPos = 0;
-    stmt = matchList[matchListPos];
-    print2("Step %ld was assigned statement %s since it is the only match.\n",
-        (long)(step + 1),
-        g_Statement[stmt].labelName);
-  } else {
-
-    while (1) {
-      let(&tmpStr3, cat("What statement to select for step ", str((double)step + 1),
-          " (<return> to bypass)? ", NULL));
-      tmpStr2 = cmdInput1(tmpStr3);
-      let(&tmpStr3, "");
-
-      if (tmpStr2[0] == 0) {
-        let(&tmpStr1, "");
-        let(&tmpStr4, "");
-        let(&tmpStr2, "");
-        let(&matchFlags, "");
-        let(&timeoutFlags, "");
-        nmbrLet(&matchList, NULL_NMBRSTRING);
-        nmbrLet(&timeoutList, NULL_NMBRSTRING);
-        return;
-      }
-      if (!instr(1, cat(" ", tmpStr1, " ", tmpStr4, " ", NULL),
-           cat(" ", tmpStr2, " ", NULL))) {
-        print2("\"%s\" is not one of the choices.  Try again.\n", tmpStr2);
-      } else {
-        break;
-      }
-    }
-
-    for (matchListPos = 0; matchListPos < matchCount; matchListPos++) {
-      if (!strcmp(tmpStr2, g_Statement[matchList[matchListPos]].labelName)) break;
-    }
-    if (matchListPos < matchCount) {
-      stmt = matchList[matchListPos];
-    } else {
-      for (timeoutListPos = 0; timeoutListPos < timeoutCount;
-          timeoutListPos++) {
-      if (!strcmp(tmpStr2, g_Statement[timeoutList[timeoutListPos]].labelName))
-          break;
-      } /* Next timeoutListPos */
-      if (timeoutListPos == timeoutCount) bug(1801);
-      stmt = timeoutList[timeoutListPos];
-    }
-    print2("Step %ld was assigned statement %s.\n",
-        (long)(step + 1),
-        g_Statement[stmt].labelName);
-
-  } /* End if matchCount == 1 */
-
-  /* Add to statement to the proof */
-  assignStatement(matchList[matchListPos], step);
-  g_proofChangedFlag = 1; /* Flag for 'undo' stack */
-
-  let(&tmpStr1, "");
-  let(&tmpStr4, "");
-  let(&tmpStr2, "");
-  let(&matchFlags, "");
-  let(&timeoutFlags, "");
-  nmbrLet(&matchList, NULL_NMBRSTRING);
-  nmbrLet(&timeoutList, NULL_NMBRSTRING);
-  return;
-
-} /* interactiveMatch */
-
-
-
-/* Assign a statement to an unknown proof step */
-void assignStatement(long statemNum, long step)
-{
-  long hyp;
-  nmbrString *hypList = NULL_NMBRSTRING;
-
-  if ((g_ProofInProgress.proof)[step] != -(long)'?') bug(1802);
-
-  /* Add the statement to the proof */
-  nmbrLet(&hypList, nmbrSpace(g_Statement[statemNum].numReqHyp + 1));
-  for (hyp = 0; hyp < g_Statement[statemNum].numReqHyp; hyp++) {
-    /* A hypothesis of the added statement */
-    hypList[hyp] = -(long)'?';
-  }
-  hypList[g_Statement[statemNum].numReqHyp] = statemNum; /* The added statement */
-  addSubProof(hypList, step);
-  initStep(step + g_Statement[statemNum].numReqHyp);
-  nmbrLet(&hypList, NULL_NMBRSTRING);
-  return;
-} /* assignStatement */
-
-
-
-/* Find proof of formula by using the replaceStatement() algorithm i.e.
-   see if any statement matches current step AND each of its hypotheses
-   matches a proof in progress hypothesis or some step already in the proof.
-   If a proof is found, it is returned, otherwise an empty (length 0) proof is
-   returned. */
-/* The caller must deallocate the returned nmbrString. */
-nmbrString *proveByReplacement(long prfStmt,
-    long prfStep, /* 0 means step 1 */
-    flag noDistinct, /* 1 means don't try statements with $d's */
-    flag dummyVarFlag, /* 0 means no dummy vars are in prfStmt */
-    flag searchMethod, /* 1 means to try proveFloating on $e's also */
-    long improveDepth, /* depth for proveFloating() */
-    /* 3-May-2016 nm */
-    flag overrideFlag, /* 1 means to override usage locks */
-    /* 5-Aug-2020 nm */
-    flag mathboxFlag
-    )
-{
-
-  long trialStmt;
-  nmbrString *prfMath;
-  nmbrString *trialPrf = NULL_NMBRSTRING;
-  long prfMbox; /* 5-Aug-2020 nm */
-
-  prfMath = (g_ProofInProgress.target)[prfStep];
-  prfMbox = getMathboxNum(prfStmt); /* 5-Aug-2020 nm */
-  for (trialStmt = 1; trialStmt < prfStmt; trialStmt++) {
-
-    if (quickMatchFilter(trialStmt, prfMath, dummyVarFlag) == 0) continue;
-
-    /* 3-May-2016 nm */
-    /* Skip statements with discouraged usage (the above skips non-$a,p) */
-    if (overrideFlag == 0 && getMarkupFlag(trialStmt, USAGE_DISCOURAGED)) {
-      continue;
-    }
-
-    /* 5-Aug-2020 nm */
-    /* Skip statements in other mathboxes unless /INCLUDE_MATHBOXES.  (We don't
-       care about the first mathbox since there are no others above it.) */
-    if (mathboxFlag == 0 && prfMbox >= 2) {
-      /* Note that g_mathboxStart[] starts a 0 */
-      if (trialStmt > g_mathboxStmt && trialStmt < g_mathboxStart[prfMbox - 1]) {
-        continue;
-      }
-    }
-
-    /* noDistinct is set by NO_DISTICT qualifier in IMPROVE */
-    if (noDistinct) {
-      /* Skip the statement if it has a $d requirement.  This option
-         prevents illegal proofs that would violate $d requirements
-         since the Proof Assistant does not check for $d violations. */
-      if (nmbrLen(g_Statement[trialStmt].reqDisjVarsA)) {
-        continue;
-      }
-    }
-
-    trialPrf = replaceStatement(trialStmt, prfStep,
-        prfStmt, 0,/*scan whole proof to maximize chance of a match*/
-        noDistinct,
-        searchMethod,
-        improveDepth,
-        overrideFlag,  /* 3-May-2016 nm */
-        mathboxFlag /* 1 means allow mathboxes */ /* 5-Aug-2020 nm */
-        );
-    if (nmbrLen(trialPrf) > 0) {
-      /* A proof for the step was found. */
-
-      /* 3-May-2016 nm */
-      /* Inform user that we're using a statement with discouraged usage */
-      if (overrideFlag == 1 && getMarkupFlag(trialStmt, USAGE_DISCOURAGED)) {
-        /* print2("\n"); */ /* Enable for more emphasis */
-        print2(
-          ">>> ?Warning: Overriding discouraged usage of statement \"%s\".\n",
-            g_Statement[trialStmt].labelName);
-        /* print2("\n"); */ /* Enable for more emphasis */
-      }
-
-      return trialPrf;
-    }
-    /* nmbrLet(&trialPrf, NULL_NMBRSTRING); */
-                       /* Don't need to do this because it is already null */
-  }
-  return trialPrf;  /* Proof not found - return empty proof */
-}
-
-
-nmbrString *replaceStatement(long replStatemNum, long prfStep,
-    long provStmtNum,
-    flag subProofFlag, /* If 1, then scan only subproof at prfStep to look for
-   matches, instead of whole proof, for faster speed (used by MINIMIZE_WITH) */
-    flag noDistinct, /* 1 means proveFloating won't try statements with $d's */
-    flag searchMethod, /* 1 means to try proveFloating on $e's also */
-    long improveDepth, /* Depth for proveFloating */
-    /* 3-May-2016 nm */
-    flag overrideFlag,  /* 1 means to override statement usage locks */
-    flag mathboxFlag /* 1 means allow mathboxes */ /* 5-Aug-2020 nm */
-    ) {
-  nmbrString *prfMath; /* Pointer only */
-  long reqHyps;
-  long hyp, sym, var, i, j, k, trialStep;
-  nmbrString *proof = NULL_NMBRSTRING;
-  nmbrString *scheme = NULL_NMBRSTRING;
-  pntrString *hypList = NULL_PNTRSTRING;
-  nmbrString *hypSortMap = NULL_NMBRSTRING; /* Order remapping for speedup */
-  pntrString *hypProofList = NULL_PNTRSTRING;
-  pntrString *stateVector = NULL_PNTRSTRING;
-  nmbrString *replStmtSchemePtr;
-  nmbrString *hypSchemePtr;
-  nmbrString *hypProofPtr;
-  nmbrString *makeSubstPtr;
-  pntrString *hypMakeSubstList = NULL_PNTRSTRING;
-  pntrString *hypStateVectorList = NULL_PNTRSTRING;
-  vstring hypReEntryFlagList = "";
-  nmbrString *hypStepList = NULL_NMBRSTRING;
-  flag reEntryFlag;
-  flag tmpFlag;
-  flag noHypMatch;
-  flag foundTrialStepMatch;
-  long replStmtSchemeLen, schemeVars, schReqHyps, hypLen, reqVars,
-      schEHyps, subPfLen;
-  long saveUnifTrialCount;
-  flag reenterFFlag;
-  flag dummyVarFlag; /* Flag that replacement hypothesis under consideration has
-                        dummy variables */
-  nmbrString *hypTestPtr; /* Points to what we are testing hyp. against */
-  flag hypOrSubproofFlag; /* 0 means testing against hyp., 1 against proof*/
-  vstring indepKnownSteps = ""; /* 'Y' = ok to try step; 'N' = not ok */
-                                                 /* 22-Aug-2012 nm */
-  long pfLen;          /* 22-Aug-2012 nm */
-  long scanLen;         /* 22-Aug-2012 nm */
-  long scanUpperBound;  /* 22-Aug-2012 nm */
-  long scanLowerBound;  /* 22-Aug-2012 nm */
-  vstring hasFloatingProof = "";  /* 'N' or 'Y' for $e hyps */
-                                                            /* 4-Sep-2012 nm */
-  vstring tryFloatingProofLater = "";  /* 'N' or 'Y' */     /* 4-Sep-2012 nm */
-  flag hasDummyVar;     /* 4-Sep-2012 nm */
-
-  /* 3-May-2016 nm */
-  /* If we are overriding discouraged usage, a warning has already been
-     printed.  If we are not, then we should never get here. */
-  if (overrideFlag == 0 && getMarkupFlag(replStatemNum, USAGE_DISCOURAGED)) {
-    bug(1868);
-  }
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  trialStep = 0;
-
-  prfMath = (g_ProofInProgress.target)[prfStep];
-  if (subProofFlag) {
-    /* Get length of the existing subproof at the replacement step.  The
-       existing subproof will be scanned to see if there is a match to
-       the $e hypotheses of the replacement statement.  */
-    subPfLen = subproofLen(g_ProofInProgress.proof, prfStep);
-    scanLen = subPfLen;
-    scanUpperBound = prfStep;
-    scanLowerBound = scanUpperBound - scanLen + 1;
-  } else {
-    /* Treat the whole proof as a "subproof" and get its length.  The whole
-       existing proof will be scanned to see if there is a match to
-       the $e hypotheses of the replacement statement.  */
-    pfLen = nmbrLen(g_ProofInProgress.proof);
-    /* scanLen = pfLen; */ /* 28-Sep-2013 never used */
-    scanUpperBound = pfLen - 1;  /* Last proof step (0=1st step, 1=2nd, etc. */
-    scanLowerBound = 0; /* scanUpperBound - scanLen + 1;  */
-  }
-  /* Note: the variables subPfLen, pfLen, and scanLen aren't
-     used again.  They could be eliminated above if we wanted. */
-
-  if (g_Statement[replStatemNum].type != (char)a_ &&
-      g_Statement[replStatemNum].type != (char)p_)
-    bug(1822); /* Not $a or $p */
-
-  schReqHyps = g_Statement[replStatemNum].numReqHyp;
-  reqVars = nmbrLen(g_Statement[replStatemNum].reqVarList);
-
-  /* hasFloatingProof is used only when searchMethod=1 */
-  let(&hasFloatingProof, string(schReqHyps, ' ')); /* 4-Sep-2012 nm Init */
-  let(&tryFloatingProofLater, string(schReqHyps, ' ')); /* 4-Sep-2012 nm Init */
-  replStmtSchemePtr = g_Statement[replStatemNum].mathString;
-  replStmtSchemeLen = nmbrLen(replStmtSchemePtr);
-
-  /* Change all variables in the statement to dummy vars for unification */
-  nmbrLet(&scheme, replStmtSchemePtr);
-  schemeVars = reqVars;
-  if (schemeVars + g_pipDummyVars/*global*/ > g_dummyVars/*global*/) {
-    /* Declare more dummy vars if necessary */
-    declareDummyVars(schemeVars + g_pipDummyVars - g_dummyVars);
-  }
-  for (var = 0; var < schemeVars; var++) {
-    /* Put dummy var mapping into g_MathToken[].tmp field */
-    g_MathToken[g_Statement[replStatemNum].reqVarList[var]].tmp
-        = g_mathTokens/*global*/ + 1 + g_pipDummyVars/*global*/ + var;
-  }
-  for (sym = 0; sym < replStmtSchemeLen; sym++) {
-    if (g_MathToken[replStmtSchemePtr[sym]].tokenType != (char)var_) continue;
-    /* Use dummy var mapping from g_MathToken[].tmp field */
-    scheme[sym] = g_MathToken[replStmtSchemePtr[sym]].tmp;
-  }
-
-  /* Change all variables in the statement's hyps to dummy vars for subst. */
-  pntrLet(&hypList, pntrNSpace(schReqHyps));
-  nmbrLet(&hypSortMap, nmbrSpace(schReqHyps));
-  pntrLet(&hypProofList, pntrNSpace(schReqHyps));
-
-  for (hyp = 0; hyp < schReqHyps; hyp++) {
-    hypSchemePtr = NULL_NMBRSTRING;
-    nmbrLet(&hypSchemePtr,
-      g_Statement[g_Statement[replStatemNum].reqHypList[hyp]].mathString);
-    hypLen = nmbrLen(hypSchemePtr);
-    for (sym = 0; sym < hypLen; sym++) {
-      if (g_MathToken[hypSchemePtr[sym]].tokenType
-          != (char)var_) continue;
-      /* Use dummy var mapping from g_MathToken[].tmp field */
-      hypSchemePtr[sym] = g_MathToken[hypSchemePtr[sym]].tmp;
-    }
-    hypList[hyp] = hypSchemePtr;
-    hypSortMap[hyp] = hyp;
-  }
-
-  /* Move all $e's to front of hypothesis list */
-  schEHyps = 0;
-  for (hyp = 0; hyp < schReqHyps; hyp++) {
-    if (g_Statement[g_Statement[replStatemNum].reqHypList[hypSortMap[hyp]]].type
-         == (char)e_) {
-      j = hypSortMap[hyp];
-      hypSortMap[hyp] = hypSortMap[schEHyps];
-      hypSortMap[schEHyps] = j;
-      schEHyps++;
-    }
-  }
-
-  /* 9/2/99 - Speedup - sort essential hyp's according to decreasing length to
-     maximize the chance of early rejection */
-  for (hyp = 0; hyp < schEHyps; hyp++) {
-    /* Bubble sort - but should be OK for typically small # of hyp's */
-    for (i = hyp + 1; i < schEHyps; i++) {
-      if (nmbrLen(hypList[hypSortMap[i]]) > nmbrLen(hypList[hypSortMap[hyp]])) {
-        j = hypSortMap[hyp];
-        hypSortMap[hyp] = hypSortMap[i];
-        hypSortMap[i] = j;
-      }
-    }
-  }
-
-  /* If we are just scanning the subproof, all subproof steps are independent,
-     so the getIndepKnownSteps scan would be redundant. */
-  if (!subProofFlag) {
-    /* 22-Aug-2012 nm - if subProofFlag is not set, we scan the whole proof,
-       not just the subproof starting at prfStep, to find additional possible
-       matches. */
-    /* Get a list of the possible steps to look at that are not dependent
-       on the prfStep.  A value of 'Y'  means we can try the step. */
-    let(&indepKnownSteps, "");
-    indepKnownSteps = getIndepKnownSteps(provStmtNum, prfStep);
-  }
-
-  /* Initialize state vector list for hypothesis unifications */
-  /* (We will really only use up to schEHyp entries, but allocate all
-     for possible future use) */
-  pntrLet(&hypStateVectorList, pntrPSpace(schReqHyps));
-  /* Initialize unification reentry flags for hypothesis unifications */
-  /* (1 means 0, and 2 means 1, because 0 means end-of-character-string.) */
-  /* (3 means previous proveFloating call found proof) */
-  let(&hypReEntryFlagList, string(schReqHyps, 1));
-  /* Initialize starting subproof step to scan for each hypothesis */
-  nmbrLet(&hypStepList, nmbrSpace(schReqHyps));
-  /* Initialize list of hypotheses after substitutions made */
-  pntrLet(&hypMakeSubstList, pntrNSpace(schReqHyps));
-
-
-  g_unifTrialCount = 1; /* Reset unification timeout */
-  reEntryFlag = 0; /* For unifyH() */
-
-  /* Number of required hypotheses of statement we're proving */
-  reqHyps = g_Statement[provStmtNum].numReqHyp;
-
-  while (1) { /* Try all possible unifications */
-    tmpFlag = unifyH(scheme, prfMath, &stateVector, reEntryFlag);
-    if (!tmpFlag) break; /* (Next) unification not possible */
-    if (tmpFlag == 2) {
-      print2(
-"Unification timed out.  Larger SET UNIFICATION_TIMEOUT may improve results.\n");
-      g_unifTrialCount = 1; /* Reset unification timeout */
-      break; /* Treat timeout as if unification not possible */
-    }
-
-    reEntryFlag = 1; /* For next unifyH() */
-
-    /* Make substitutions into each hypothesis, and try to prove that
-       hypothesis */
-    nmbrLet(&proof, NULL_NMBRSTRING);
-    noHypMatch = 0;
-    for (hyp = 0; hyp < schReqHyps; hyp++) {
-
-      /* Make substitutions from replacement statement's stateVector */
-      nmbrLet((nmbrString **)(&(hypMakeSubstList[hypSortMap[hyp]])),
-          NULL_NMBRSTRING); /* Deallocate previous pass if any */
-      hypMakeSubstList[hypSortMap[hyp]] =
-          makeSubstUnif(&dummyVarFlag, hypList[hypSortMap[hyp]],
-          stateVector);
-
-      /* Initially, a $e has no proveFloating proof */
-      hasFloatingProof[hyp] = 'N';  /* Init for this pass */ /* 4-Sep-2012 nm */
-      tryFloatingProofLater[hyp] = 'N'; /* Init for this pass */ /* 4-Sep-2012 nm */
-
-      /* Make substitutions from each earlier hypothesis unification */
-      for (i = 0; i < hyp; i++) {
-        /* Only do substitutions for $e's -- the $f's will have no
-           dummy vars., and they have no stateVector
-           since they were found with proveFloating below */
-        if (i >= schEHyps) break;
-
-        /* If it is an essential hypothesis with a proveFloating proof,
-           we don't want to make substitutions since it has no
-           stateVector.  (This is the only place we look
-           at hasFloatingProof.) */
-        if (hasFloatingProof[i] == 'Y') continue;  /* 4-Sep-2012 nm */
-
-        makeSubstPtr = makeSubstUnif(&dummyVarFlag,
-            hypMakeSubstList[hypSortMap[hyp]],
-            hypStateVectorList[hypSortMap[i]]);
-        nmbrLet((nmbrString **)(&(hypMakeSubstList[hypSortMap[hyp]])),
-            NULL_NMBRSTRING);
-        hypMakeSubstList[hypSortMap[hyp]] = makeSubstPtr;
-      }
-
-      if (hyp < schEHyps) {
-        /* It's a $e hypothesis */
-        if (g_Statement[g_Statement[replStatemNum].reqHypList[hypSortMap[hyp]]
-            ].type != (char)e_) bug (1823);
-      } else {
-        /* It's a $f hypothesis */
-        if (g_Statement[g_Statement[replStatemNum].reqHypList[hypSortMap[hyp]]
-             ].type != (char)f_) bug(1824);
-        /* At this point there should be no dummy variables in $f
-           hypotheses */
-        /* 22-Aug-2012 nm This can now occur with new algorithm.  For now,
-           let it continue; I'm not sure if there are adverse side effects. */
-        /*
-        if (dummyVarFlag) {
-          printSubst(stateVector);
-          bug(1825);
-        }
-        */
-      }
-
-
-      /* Scan all known steps of existing subproof to find a hypothesis
-         match */
-      foundTrialStepMatch = 0;
-      reenterFFlag = 0;
-      if (hypReEntryFlagList[hypSortMap[hyp]] == 2) {
-        /* Reentry flag is set; we're continuing with a previously unified
-           subproof step */
-        trialStep = hypStepList[hypSortMap[hyp]];
-
-        /* If we are re-entering the unification for a $f, it means we
-           backtracked from a later failure, and there won't be another
-           unification possible.  In this case we should bypass the
-           proveFloating call to force a further backtrack.  (Otherwise
-           we will have an infinite loop.)  Note that for $f's, all
-           variables will be known so there will only be one unification
-           anyway. */
-        if (hyp >= schEHyps
-            || hasFloatingProof[hyp] == 'Y' /* 5-Sep-2012 nm */
-            ) {
-          reenterFFlag = 1;
-        }
-      } else {
-        if (hypReEntryFlagList[hypSortMap[hyp]] == 1) {
-          /* Start at the beginning of the proof */
-          /* trialStep = prfStep - subPfLen + 1; */ /* obsolete */
-          trialStep = scanLowerBound;   /* 22-Aug-2012 nm */
-          /* Later enhancement:  start at required hypotheses */
-          /* (Here we use the trick of shifting down the starting
-              trialStep to below the real subproof start) */
-          trialStep = trialStep - reqHyps;
-        } else {
-          if (hypReEntryFlagList[hypSortMap[hyp]] == 3) {
-            /* This is the case where proveFloating previously found a
-               proof for the step, and we've backtracked.  In this case,
-               we want to backtrack further - no scan or proveFloating
-               call again. */
-            hypReEntryFlagList[hypSortMap[hyp]] = 1;
-            reenterFFlag = 1; /* Skip proveFloating call */
-            /*trialStep = prfStep;  old */ /* Skip loop */
-            trialStep = scanUpperBound; /* Skip loop */
-          } else {
-            bug(1826);
-          }
-        }
-      }
-
-      /* for (trialStep = trialStep; trialStep < prfStep; trialStep++) { old */
-      for (trialStep = trialStep + 0; trialStep < scanUpperBound;
-          trialStep++) {                             /* 22-Aug-2012 nm */
-        /* Note that step scanUpperBound is not scanned since that is
-           the statement we want to replace (subProofFlag = 1) or the
-           last statement of the proof (subProofFlag = 0), neither of
-           which would be useful for a replacement step subproof. */
-
-        /* if (trialStep < prfStep - subPfLen + 1) { */ /* obsolete */
-        if (trialStep < scanLowerBound) {  /* 22-Aug-2012 nm */
-          /* We're scanning required hypotheses */
-          hypOrSubproofFlag = 0;
-          /* Point to what we are testing hyp. against */
-          /* (Note offset to trialStep needed to compensate for trick) */
-          hypTestPtr =
-              g_Statement[g_Statement[provStmtNum].reqHypList[
-              /* trialStep - (prfStep - subPfLen + 1 - reqHyps)]].mathString;*/
-              trialStep - (scanLowerBound - reqHyps)]].mathString;
-                                                           /* 22-Aug-2012 nm */
-        } else {
-          /* We're scanning the subproof */
-          hypOrSubproofFlag = 1;
-          /* Point to what we are testing hyp. against */
-          hypTestPtr = (g_ProofInProgress.target)[trialStep];
-
-          /* Do not consider unknown subproof steps or those with
-             unknown variables */
-          /* Break flag */
-/***** obsolete  22-Aug-2012 nm - the new IMPROVE allows non-shared dummy
-       vars - but we'll keep the commented-out code for a while in case
-       it's needed for debugging
-          j = 0;
-          i = nmbrLen(hypTestPtr);
-          if (i == 0) bug(1824);
-          for (i = i - 1; i >= 0; i--) {
-            if (((nmbrString *)hypTestPtr)[i] >
-                g_mathTokens) {
-              j = 1;
-              break;
-            }
-          }
-          if (j) continue;
-*******/
-
-          /* Subproof step has dummy var.; don't use it */
-          if (!subProofFlag) {
-            if (indepKnownSteps[trialStep] != 'Y') {
-              if (indepKnownSteps[trialStep] != 'N') bug(1836);
-              continue; /* Don't use the step */
-            }
-          }
-        }
-
-        /* Speedup - skip if no dummy vars in hyp and statements not equal */
-        if (!dummyVarFlag) {
-          if (!nmbrEq(hypTestPtr, hypMakeSubstList[hypSortMap[hyp]])) {
-            continue;
-          }
-        }
-
-        /* Speedup - skip if 1st symbols are constants and don't match */
-        /* First symbol */
-        i = hypTestPtr[0];
-        j = ((nmbrString *)(hypMakeSubstList[hypSortMap[hyp]]))[0];
-        if (g_MathToken[i].tokenType == (char)con_) {
-          if (g_MathToken[j].tokenType == (char)con_) {
-            if (i != j) {
-              continue;
-            }
-          }
-        }
-
-        /* g_unifTrialCount = 1; */ /* ??Don't reset it here in order to
-           detect exponential blowup in hypotheses trials */
-        g_unifTrialCount = 1; /* Reset unification timeout counter */
-        if (hypReEntryFlagList[hypSortMap[hyp]] < 1
-            || hypReEntryFlagList[hypSortMap[hyp]] > 2)
-          bug(1851);
-        tmpFlag = unifyH(hypMakeSubstList[hypSortMap[hyp]],
-            hypTestPtr,
-            (pntrString **)(&(hypStateVectorList[hypSortMap[hyp]])),
-            /* (Remember: 1 = false, 2 = true in hypReEntryFlagList) */
-            hypReEntryFlagList[hypSortMap[hyp]] - 1);
-        if (!tmpFlag || tmpFlag == 2) {
-          /* (Next) unification not possible or timeout */
-          if (tmpFlag == 2) {
-            print2(
-"Unification timed out.  SET UNIFICATION_TIMEOUT larger for better results.\n");
-            g_unifTrialCount = 1; /* Reset unification timeout */
-            /* Deallocate unification state vector */
-            purgeStateVector(
-                (pntrString **)(&(hypStateVectorList[hypSortMap[hyp]])));
-          }
-
-          /* If this is a reenter, and there are no dummy vars in replacement
-             hypothesis, we have already backtracked from a unique exact
-             match that didn't work.  There is no point in continuing to
-             look for another exact match for this hypothesis, so we'll just
-             skip the rest of the subproof scan. */
-          /* (Note that we could in principle bypass the redundant unification
-             above since we know it will fail, but it will clear out our
-             stateVector for us.) */
-          if (!dummyVarFlag) {
-            if (hypReEntryFlagList[hypSortMap[hyp]] - 1 == 1) {
-              /* There are no dummy variables, so previous match
-                 was exact.  Force the trialStep loop to terminate as
-                 if nothing further was found.  (If we don't do this,
-                 there could be, say 50 more matches for "var x",
-                 so we might need a factor of 50 greater runtime for each
-                 replacement hypothesis having this situation.) */
-              /* trialStep = prfStep - 1;  old version */
-              trialStep = scanUpperBound - 1;
-                       /* Make this the last loop pass */  /* 22-Aug-2012 nm */
-            }
-          }
-
-          hypReEntryFlagList[hypSortMap[hyp]] = 1;
-          continue;
-        } else {
-
-          /* tmpFlag = 1:  a unification was found */
-          if (tmpFlag != 1) bug(1828);
-
-          /* (Speedup) */
-          /* If this subproof step has previously occurred in a hypothesis
-             or an earlier subproof step, don't consider it since that
-             would be redundant. */
-          if (hypReEntryFlagList[hypSortMap[hyp]] == 1
-              /* && 0 */  /* To skip this for testing */
-              ) {
-            j = 0; /* Break flag */
-            /*for (i = prfStep - subPfLen + 1 - reqHyps; i < trialStep; i++) {*/
-            for (i = scanLowerBound - reqHyps; i < trialStep; i++) {
-                                                            /* 22-Aug-2012 nm */
-              /* if (i < prfStep - subPfLen + 1) { */
-              if (i < scanLowerBound) { /* 22-Aug-2012 nm */
-                /* A required hypothesis */
-                if (nmbrEq(hypTestPtr,
-                    g_Statement[g_Statement[provStmtNum].reqHypList[
-                    /*i - (prfStep - subPfLen + 1 - reqHyps)]].mathString)) { */
-                    i - (scanLowerBound - reqHyps)]].mathString)) {
-                                                           /* 22-Aug-2012 nm */
-                  j = 1;
-                  break;
-                }
-              } else {
-                /* A subproof step */
-                if (nmbrEq(hypTestPtr,
-                    (g_ProofInProgress.target)[i])) {
-                  j = 1;
-                  break;
-                }
-              }
-            } /* next i */
-            if (j) {
-              /* This subproof step was already considered earlier, so
-                 we can skip considering it again. */
-              /* Deallocate unification state vector */
-              purgeStateVector(
-                  (pntrString **)(&(hypStateVectorList[hypSortMap[hyp]])));
-              continue;
-            }
-          } /* end if not reentry */
-          /* (End speedup) */
-
-
-          hypReEntryFlagList[hypSortMap[hyp]] = 2; /* For next unifyH() */
-          hypStepList[hypSortMap[hyp]] = trialStep;
-
-          if (!hypOrSubproofFlag) {
-            /* We're scanning required hypotheses */
-            nmbrLet((nmbrString **)(&hypProofList[hypSortMap[hyp]]),
-                nmbrAddElement(NULL_NMBRSTRING,
-                g_Statement[provStmtNum].reqHypList[
-                /* trialStep - (prfStep - subPfLen + 1 - reqHyps)])); */
-                trialStep - (scanLowerBound - reqHyps)])); /* 22-Aug-2012 nm */
-          } else {
-            /* We're scanning the subproof */
-            i = subproofLen(g_ProofInProgress.proof, trialStep);
-            nmbrLet((nmbrString **)(&hypProofList[hypSortMap[hyp]]),
-                nmbrSeg(g_ProofInProgress.proof, trialStep - i + 2,
-                trialStep + 1));
-          }
-
-          foundTrialStepMatch = 1;
-          break;
-        } /* end if (!tmpFlag || tmpFlag = 2) */
-      } /* next trialStep */
-
-      if (!foundTrialStepMatch) {
-        hasDummyVar = 0;
-        hypLen = nmbrLen(hypMakeSubstList[hypSortMap[hyp]]);
-        for (sym = 0; sym < hypLen; sym++) {
-          k = ((nmbrString *)(hypMakeSubstList[hypSortMap[hyp]]))[sym];
-          if (k > g_mathTokens/*global*/) {
-            hasDummyVar = 1;
-            break;
-          }
-        }
-        /* There was no (completely known) step in the (sub)proof that
-           matched the hypothesis.  If it's a $f hypothesis, we will try
-           to prove it by itself. */
-        /* (However, if this is 2nd pass of backtrack, i.e. reenterFFlag is
-           set, we already got an exact $f match earlier and don't need this
-           scan, and shouldn't do it to prevent inf. loop.) */
-        /* if (hyp >= schEHyps */   /* old */
-        if ((hyp >= schEHyps || searchMethod == 1) /* 4-Sep-2012 */
-             && !reenterFFlag
-             /*&& !hasDummyVar*/) { /* 25-Aug-2012 nm (Do we need this?) */
-          /* It's a $f hypothesis, or any hypothesis when searchMethod=1 */
-          if (hasDummyVar) {
-            /* If it's a $f and we have dummy vars, that is bad so we leave
-               foundTrialStepMatch = 0 to backtrack */
-            if (hyp < schEHyps) {
-              /* It's a $e with dummy vars, so we flag it to try later in
-                 case further matches get rid of the dummy vars */
-              tryFloatingProofLater[hyp] = 'Y';
-              /* Unify the hypothesis with itself to initialize the
-                 stateVector to allow further substitutions */
-              tmpFlag = unifyH(hypMakeSubstList[hypSortMap[hyp]],
-                  hypMakeSubstList[hypSortMap[hyp]],
-                  (pntrString **)(&(hypStateVectorList[hypSortMap[hyp]])),
-                  /* (Remember: 1 = false, 2 = true in hypReEntryFlagList) */
-                  hypReEntryFlagList[hypSortMap[hyp]] - 1);
-              if (tmpFlag != 1) bug (1849);  /* This should be a trivial
-                      unification, so it should never fail */
-              foundTrialStepMatch = 1; /* So we can continue */
-            }
-          } else {
-            saveUnifTrialCount = g_unifTrialCount; /* Save unification timeout */
-            hypProofPtr =
-                proveFloating(hypMakeSubstList[hypSortMap[hyp]],
-                    provStmtNum, improveDepth, prfStep, noDistinct,
-                    overrideFlag, mathboxFlag /* 5-Aug-2020 nm */
-                    );
-            g_unifTrialCount = saveUnifTrialCount; /* Restore unif. timeout */
-            if (nmbrLen(hypProofPtr)) { /* Proof was found */
-              nmbrLet((nmbrString **)(&hypProofList[hypSortMap[hyp]]),
-                  NULL_NMBRSTRING);
-              hypProofList[hypSortMap[hyp]] = hypProofPtr;
-              foundTrialStepMatch = 1;
-              hypReEntryFlagList[hypSortMap[hyp]] = 3;
-              /* Set flag so that we won't attempt subst. on $e w/ float prf */
-              hasFloatingProof[hyp] = 'Y'; /* 4-Sep-2012 */
-            }
-          }
-        } /* end if $f, or $e and searchMethod 1 */
-      }
-
-      if (hyp == schEHyps - 1 && foundTrialStepMatch) {
-        /* Scan all the postponed $e hypotheses in case they are known now */
-        for (i = 0; i < schEHyps; i++) {
-          if (tryFloatingProofLater[i] == 'Y') {
-
-            /* Incorporate substitutions of all later hypotheses
-               into this one (only earlier ones were done in main scan) */
-            for (j = i + 1; j < schEHyps; j++) {
-              if (hasFloatingProof[j] == 'Y') continue;
-              makeSubstPtr = makeSubstUnif(&dummyVarFlag,
-                  hypMakeSubstList[hypSortMap[i]],
-                  hypStateVectorList[hypSortMap[j]]);
-              nmbrLet((nmbrString **)(&(hypMakeSubstList[hypSortMap[i]])),
-                  NULL_NMBRSTRING);
-              hypMakeSubstList[hypSortMap[i]] = makeSubstPtr;
-            }
-
-            hasDummyVar = 0;
-            hypLen = nmbrLen(hypMakeSubstList[hypSortMap[i]]);
-            for (sym = 0; sym < hypLen; sym++) {
-              k = ((nmbrString *)(hypMakeSubstList[hypSortMap[i]]))[sym];
-              if (k > g_mathTokens/*global*/) {
-                hasDummyVar = 1;
-                break;
-              }
-            }
-            if (hasDummyVar) {
-              foundTrialStepMatch = 0; /* Force backtrack */
-              /* If we don't have a proof at this point, we didn't save
-                 enough info to backtrack easily, so we'll break out to
-                 the top-most next unification (if any). */
-              hyp = 0; /* Force breakout below */
-              break;
-            }
-            saveUnifTrialCount = g_unifTrialCount; /* Save unification timeout */
-            hypProofPtr =
-                proveFloating(hypMakeSubstList[hypSortMap[i]],
-                    provStmtNum, improveDepth, prfStep, noDistinct,
-                    overrideFlag,  /* 3-May-2016 nm */
-                    mathboxFlag /* 5-Aug-2020 nm */
-                    );
-            g_unifTrialCount = saveUnifTrialCount; /* Restore unif. timeout */
-            if (nmbrLen(hypProofPtr)) { /* Proof was found */
-              nmbrLet((nmbrString **)(&hypProofList[hypSortMap[i]]),
-                  NULL_NMBRSTRING);
-              hypProofList[hypSortMap[i]] = hypProofPtr;
-              foundTrialStepMatch = 1;
-              hypReEntryFlagList[hypSortMap[i]] = 3;
-              /* Set flag so that we won't attempt subst. on $e w/ float prf */
-              hasFloatingProof[i] = 'Y'; /* 4-Sep-2012 */
-            } else {   /* Proof not found */
-              foundTrialStepMatch = 0; /* Force backtrack */
-              /* If we don't have a proof at this point, we didn't save
-                 enough info to backtrack easily, so we'll break out to
-                 the top-most next unification (if any). */
-              hyp = 0; /* Force breakout below */
-              break;
-            } /* if (nmbrLen(hypProofPtr)) */
-          } /* if (tryFloatingProofLater[i] == 'Y') */
-        } /* for (i = 0; i < schEHyps; i++) */
-      } /* if (hyp == schEHyps - 1 && foundTrialStepMatch) */
-
-      if (!foundTrialStepMatch) {
-        /* We must backtrack */
-
-        /* 16-Sep-2012 nm fix infinite loop under weird conditions */
-        /* Backtrack through all of postponed hypotheses with
-           dummy variables whose proof wasn't found yet.  If we
-           don't do this, we could end up with an infinite loop since we
-           would just repeat the postponement and move forward again. */
-        for (i = hyp - 1; i >=0; i--) {
-          if (tryFloatingProofLater[i] == 'N') break;
-          if (tryFloatingProofLater[i] != 'Y') bug(1853);
-          hyp--;
-        }
-
-        if (hyp == 0) {
-          /* No more possibilities to try */
-          noHypMatch = 1;
-          break;
-        }
-        hyp = hyp - 2; /* Go back one interation (subtract 2 to offset
-                          end of loop increment) */
-      } /* if (!foundTrialStepMatch) */
-
-    } /* next hyp */
-
-    if (noHypMatch) {
-      /* Proof was not found for some hypothesis. */
-      continue; /* Get next unification */
-    } /* End if noHypMatch */
-
-    /* Proofs were found for all hypotheses */
-
-    /* Build the proof */
-    for (hyp = 0; hyp < schReqHyps; hyp++) {
-      if (nmbrLen(hypProofList[hyp]) == 0) bug(1852); /* Should have proof */
-      nmbrLet(&proof, nmbrCat(proof, hypProofList[hyp], NULL));
-    }
-    nmbrLet(&proof, nmbrAddElement(proof, replStatemNum));
-                                                     /* Complete the proof */
-
-    /* Deallocate hypothesis schemes and proofs */
-    /* 25-Jun-2014 This is now done after returnPoint (why was it incomplete?)
-    for (hyp = 0; hyp < schReqHyps; hyp++) {
-      nmbrLet((nmbrString **)(&hypList[hyp]), NULL_NMBRSTRING);
-      nmbrLet((nmbrString **)(&hypProofList[hyp]), NULL_NMBRSTRING);
-    }
-    */
-    goto returnPoint;
-
-  } /* End while (next unifyH() call for main replacement statement) */
-
-  nmbrLet(&proof, NULL_NMBRSTRING);  /* Proof not possible */
-
- returnPoint:
-
-  /* 25-Jun-2014 nm This was moved from before returnPoint to after
-     returnPoint.  This seems to solve a memory leak problem with
-     MINIMIZE_WITH.  Not clear why it wasn't discovered before; this
-     is very old from before Feb. 2010 at least */
-  /* Deallocate hypothesis schemes and proofs */
-  for (hyp = 0; hyp < schReqHyps; hyp++) {
-    nmbrLet((nmbrString **)(&(hypList[hyp])), NULL_NMBRSTRING);
-    nmbrLet((nmbrString **)(&(hypProofList[hyp])), NULL_NMBRSTRING);
-    nmbrLet((nmbrString **)(&(hypMakeSubstList[hyp])), NULL_NMBRSTRING);
-    purgeStateVector((pntrString **)(&(hypStateVectorList[hyp])));
-  }
-
-  /* Deallocate unification state vector */
-  purgeStateVector(&stateVector);
-
-  nmbrLet(&scheme, NULL_NMBRSTRING);
-  pntrLet(&hypList, NULL_PNTRSTRING);
-  nmbrLet(&hypSortMap, NULL_NMBRSTRING);
-  pntrLet(&hypProofList, NULL_PNTRSTRING);
-  pntrLet(&hypMakeSubstList, NULL_PNTRSTRING);
-  pntrLet(&hypStateVectorList, NULL_PNTRSTRING);
-  let(&hypReEntryFlagList, "");
-  nmbrLet(&hypStepList, NULL_NMBRSTRING);
-  let(&indepKnownSteps, "");
-  let(&hasFloatingProof, ""); /* 4-Sep-2012 */
-  let(&tryFloatingProofLater, ""); /* 4-Sep-2012 */
-
-
-/*E*/if(db8)print2("%s\n", cat("Returned: ",
-/*E*/   nmbrCvtRToVString(proof,
-/*E*/                /* 25-Jan-2016 nm */
-/*E*/                0, /*explicitTargets*/
-/*E*/                0 /*statemNum, used only if explicitTargets*/), NULL));
-  return (proof); /* Caller must deallocate */
-} /* replaceStatement */
-
-
-
-/* 22-Aug-2012 nm Added this function */
-/* This function identifies all steps in the proof in progress that (1) are
-   independent of step refStep, (2) have no dummy variables, (3) are
-   not $f's or $e's, and (4) have subproofs that are complete
-   (no unassigned steps).  A "Y" is returned for each such step,
-   and "N" is returned for all other steps.  The "Y" steps can be used
-   for scanning for useful subproofs outside of the subProof of refStep.
-   Note: The caller must deallocate the returned vstring. */
-vstring getIndepKnownSteps(long proofStmt, long refStep)
-{
-  long proofLen, prfStep, step2;
-  long wrkSubPfLen, mathLen;
-  nmbrString *proofStepContent;
-  vstring indepSteps = "";
-  vstring unkSubPrfSteps = ""; /* 'K' if subproof is known, 'U' if unknown */
-
-  /* g_ProofInProgress is global */
-  proofLen = nmbrLen(g_ProofInProgress.proof);
-  /* Preallocate the return argument */
-  let(&indepSteps, string(proofLen, 'N'));
-
-  /* Scan back from last step to get independent subproofs */
-  for (prfStep = proofLen - 2 /*next to last step*/; prfStep >= 0;
-      prfStep--) {
-    wrkSubPfLen = subproofLen(g_ProofInProgress.proof, prfStep);
-    if (prfStep >= refStep && prfStep - wrkSubPfLen + 1 <= refStep) {
-      /* The subproof includes the refStep; reject it */
-      continue;
-    }
-    /* Mark all steps in the subproof as independent */
-    for (step2 = prfStep - wrkSubPfLen + 1; step2 <= prfStep; step2++) {
-      if (indepSteps[step2] == 'Y') bug(1832); /* Should be 1st Y assignment */
-      indepSteps[step2] = 'Y';
-    }
-    /* Speedup: skip over independent subproof to reduce subproofLen() calls */
-    prfStep = prfStep - wrkSubPfLen + 1; /* Decrement loop counter */
-        /* (Note that a 1-step subproof won't modify loop counter) */
-  } /* next prfStep */
-
-  /* Scan all of the 'Y' steps and mark them as 'N' if $e, $f, or the
-     step has dummy variables */
-  for (prfStep = 0; prfStep < proofLen; prfStep++) {
-    if (indepSteps[prfStep] == 'N') continue;
-
-    /* Flag $e, $f as 'N' */
-    proofStmt = (g_ProofInProgress.proof)[prfStep];
-    if (proofStmt < 0) {
-      if (proofStmt == -(long)'?') {
-        /* indepSteps[prfStep] = 'N' */ /* We can still use its mathstring */
-      } else {
-        bug(1833); /* Packed ("squished") proof not handled (yet?) */
-      }
-    } else {
-      if (g_Statement[proofStmt].type == (char)e_
-          || g_Statement[proofStmt].type == (char)f_) {
-        /* $e or $f */
-        indepSteps[prfStep] = 'N';
-      } else if (g_Statement[proofStmt].type != (char)p_
-            && g_Statement[proofStmt].type != (char)a_) {
-        bug(1834);
-      }
-    }
-
-    if (indepSteps[prfStep] == 'N') continue;
-
-    /* Flag statements with dummy variables with 'N' */
-
-    /* Get the math tokens in the proof step */
-    proofStepContent = (g_ProofInProgress.target)[prfStep];
-
-    /* Do not consider unknown subproof steps or those with
-       unknown variables */
-    mathLen = nmbrLen(proofStepContent);
-    if (mathLen == 0) bug(1835); /* Shouldn't be empty */
-    for (mathLen = mathLen - 1; mathLen >= 0; mathLen--) {
-      if (((nmbrString *)proofStepContent)[mathLen] >
-          g_mathTokens/*global*/) {
-        /* The token is a dummy variable */
-        indepSteps[prfStep] = 'N';
-        break;
-      }
-    }
-  } /* next prfStep */
-
-  /* Identify subproofs that have unknown steps */
-  unkSubPrfSteps = getKnownSubProofs();
-  /* Propagate unknown subproofs to Y/N flags */
-  for (prfStep = 0; prfStep < proofLen; prfStep++) {
-    if (unkSubPrfSteps[prfStep] == 'U') indepSteps[prfStep] = 'N';
-  }
-
-  let(&unkSubPrfSteps, ""); /* Deallocate */
-
-  return indepSteps; /* Caller must deallocate */
-
-} /* getIndepKnownSteps */
-
-
-
-/* 22-Aug-2012 nm Added this function */
-/* This function classifies each proof step in g_ProofInProgress.proof
-   as known or unknown ('K' or 'U' in the returned string) depending
-   on whether the step has a completely known subproof.
-   Note: The caller must deallocate the returned vstring. */
-vstring getKnownSubProofs(void)
-{
-  long proofLen, hyp;
-  vstring unkSubPrfSteps = ""; /* 'K' if subproof is known, 'U' if unknown */
-  vstring unkSubPrfStack = ""; /* 'K' if subproof is known, 'U' if unknown */
-  long stackPtr, prfStep, stmt;
-
-  /* g_ProofInProgress is global */
-  proofLen = nmbrLen(g_ProofInProgress.proof);
-
-  /* Scan the proof and identify subproofs that have unknown steps */
-  let(&unkSubPrfSteps, space(proofLen));
-  let(&unkSubPrfStack, space(proofLen));
-  stackPtr = -1;
-  for (prfStep = 0; prfStep < proofLen; prfStep++) {
-    stmt = (g_ProofInProgress.proof)[prfStep];
-    if (stmt < 0) { /* Unknown step or local label */
-      if (stmt != -(long)'?') bug(1837); /* We don't handle compact proofs */
-      unkSubPrfSteps[prfStep] = 'U'; /* Subproof is unknown */
-      stackPtr++;
-      unkSubPrfStack[stackPtr] = 'U';
-      continue;
-    }
-    if (g_Statement[stmt].type == (char)e_ ||
-        g_Statement[stmt].type == (char)f_) { /* A hypothesis */
-      unkSubPrfSteps[prfStep] = 'K'; /* Subproof is known */
-      stackPtr++;
-      unkSubPrfStack[stackPtr] = 'K';
-      continue;
-    }
-    if (g_Statement[stmt].type != (char)a_ &&
-        g_Statement[stmt].type != (char)p_) bug(1838);
-    unkSubPrfSteps[prfStep] = 'K';  /* Start assuming subproof is known */
-    for (hyp = 1; hyp <= g_Statement[stmt].numReqHyp; hyp++) {
-      if (stackPtr < 0) bug(1839);
-      if (unkSubPrfStack[stackPtr] == 'U') {
-        /* If any hypothesis is unknown, the statement's subproof is unknown */
-        unkSubPrfSteps[prfStep] = 'U';
-      }
-      stackPtr--;
-    }
-    stackPtr++;
-    if (stackPtr < 0) bug(1840);
-    unkSubPrfStack[stackPtr] = unkSubPrfSteps[prfStep];
-  } /* next prfStep */
-  if (stackPtr != 0) bug(1841);
-  let(&unkSubPrfStack, ""); /* Deallocate */
-  return unkSubPrfSteps; /* Caller must deallocate */
-
-} /* getKnownSubProofs */
-
-
-
-/* Add a subproof in place of an unknown step to g_ProofInProgress.  The
-   .target, .source, and .user fields are initialized to empty (except
-   .target and .user of the deleted unknown step are retained). */
-void addSubProof(nmbrString *subProof, long step) {
-  long sbPfLen;
-
-  if ((g_ProofInProgress.proof)[step] != -(long)'?') bug(1803);
-                     /* Only unknown steps should be allowed at cmd interface */
-  sbPfLen = nmbrLen(subProof);
-  nmbrLet(&g_ProofInProgress.proof, nmbrCat(nmbrLeft(g_ProofInProgress.proof, step),
-      subProof, nmbrRight(g_ProofInProgress.proof, step + 2), NULL));
-  pntrLet(&g_ProofInProgress.target, pntrCat(pntrLeft(g_ProofInProgress.target,
-      step), pntrNSpace(sbPfLen - 1), pntrRight(g_ProofInProgress.target,
-      step + 1), NULL));
-  /* Deallocate .source in case not empty (if not, though, it's a bug) */
-  if (nmbrLen((g_ProofInProgress.source)[step])) bug(1804);
- /*nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])), NULL_NMBRSTRING);*/
-  pntrLet(&g_ProofInProgress.source, pntrCat(pntrLeft(g_ProofInProgress.source,
-      step), pntrNSpace(sbPfLen - 1), pntrRight(g_ProofInProgress.source,
-      step + 1), NULL));
-  pntrLet(&g_ProofInProgress.user, pntrCat(pntrLeft(g_ProofInProgress.user,
-      step), pntrNSpace(sbPfLen - 1), pntrRight(g_ProofInProgress.user,
-      step + 1), NULL));
-} /* addSubProof */
-
-/* 11-Sep-2016 nm */
-/* This function eliminates any occurrences of statement sourceStmtNum in the
-   targetProof by substituting it with the proof of sourceStmtNum.  The
-   unchanged targetProof is returned if there was an error. */
-/* Normally, targetProof is the global g_ProofInProgress.proof.  However,
-   we make it an argument in case in the future we'd like to do this
-   outside of the Proof Assistant. */
-/* The rawTargetProof may be uncompressed or compressed. */
-nmbrString *expandProof(
-    nmbrString *rawTargetProof, /* May be compressed or uncompressed */
-    long sourceStmtNum   /* The statement whose proof will be expanded */
-    /* , long targetStmtNum */) { /* The statement begin proved */
-  nmbrString *origTargetProof = NULL_NMBRSTRING;
-  nmbrString *targetProof = NULL_NMBRSTRING;
-  nmbrString *sourceProof = NULL_NMBRSTRING;
-  nmbrString *expandedTargetProof = NULL_NMBRSTRING;
-  pntrString *hypSubproofs = NULL_PNTRSTRING;
-  nmbrString *expandedSubproof = NULL_NMBRSTRING;
-  long targetStep, srcHyp, hypStep, totalSubpLen, subpLen, srcStep;
-  long sourcePLen, sourceHyps, targetPLen, targetSubpLen;
-  flag hasDummyVar = 0;
-  flag hasUnknownStep = 0;
-  char srcStepType;
-  long srcHypNum;
-  flag foundMatch;
-
-  sourceProof = getProof(sourceStmtNum, 0); /* Retrieve from source file */
-  nmbrLet(&sourceProof, nmbrUnsquishProof(sourceProof)); /* Uncompress */
-  /* (The following nmbrUnsquishProof() is unnecessary when called from
-     within the Proof Assistant.) */
-  nmbrLet(&origTargetProof, nmbrUnsquishProof(rawTargetProof)); /* Uncompress */
-  nmbrLet(&expandedTargetProof, origTargetProof);
-  sourcePLen = nmbrLen(sourceProof);
-  sourceHyps = nmbrLen(g_Statement[sourceStmtNum].reqHypList);
-  pntrLet(&hypSubproofs, pntrNSpace(sourceHyps)); /* pntrNSpace initializes
-        to null nmbrStrings */
-  if (g_Statement[sourceStmtNum].type != (char)p_) {
-    /* Caller should enforce $p statements only */
-    bug(1871);
-    nmbrLet(&expandedTargetProof, targetProof);
-    goto RETURN_POINT;
-  }
-
-  while (1) { /* Restart after every expansion (to handle nested
-                       references to sourceStmtNum correctly) */
-    nmbrLet(&targetProof, expandedTargetProof);
-    targetPLen = nmbrLen(targetProof);
-    foundMatch = 0;
-    for (targetStep = targetPLen - 1; targetStep >= 0; targetStep--) {
-      if (targetProof[targetStep] != sourceStmtNum) continue;
-      foundMatch = 1;
-      /* Found a use of the source statement in the proof */
-      targetSubpLen = subproofLen(targetProof, targetStep);
-      /* Collect the proofs of the hypotheses */
-      /*pntrLet(&hypSubproofs, pntrNSpace(sourceHyps));*/ /* done above */
-      hypStep = targetStep - 1;
-      totalSubpLen = 0;
-      for (srcHyp = sourceHyps - 1; srcHyp >= 0; srcHyp--) {
-        subpLen = subproofLen(targetProof, hypStep);
-                                      /* Find length of proof of hypothesis */
-        nmbrLet((nmbrString **)(&(hypSubproofs[srcHyp])),
-          nmbrMid(targetProof, (hypStep + 1) - (subpLen - 1), subpLen));
-                                    /* Note that nmbrStrings start at 1, not 0 */
-        hypStep = hypStep - subpLen;
-        totalSubpLen = totalSubpLen + subpLen;
-      }
-      if (totalSubpLen != targetSubpLen - 1) {
-        /* Independent calculation of source statement subproof failed.
-           Could be caused by corrupted proof also.  If this is confirmed,
-           change the bug() to an error message (or depend on getProof() error
-           messages) */
-        bug(1872);
-        nmbrLet(&expandedTargetProof, targetProof);
-        goto RETURN_POINT;
-      }
-
-      /* Build the expanded subproof */
-      nmbrLet(&expandedSubproof, NULL_NMBRSTRING);
-      /* Scan the proof of the statement to be expanded */
-      for (srcStep = 0; srcStep < sourcePLen; srcStep++) {
-        /* 14-Sep-2016 nm */
-        if (sourceProof[srcStep] < 0) {
-          if (sourceProof[srcStep] == -(long)'?') {
-            /* It's an unknown step in the source proof; make it an
-               unknown step in the target proof */
-            hasUnknownStep = 1;
-          } else {
-            /* It shouldn't be a compressed proof because we called
-               unSquishProof() above */
-            bug(1873);
-          }
-          /* Assign unknown to the target proof */
-          nmbrLet(&expandedSubproof, nmbrAddElement(expandedSubproof,
-              -(long)'?'));
-          continue;
-        }
-        srcStepType = g_Statement[sourceProof[srcStep]].type;
-        if (srcStepType == (char)e_ || srcStepType == (char)f_) {
-          srcHypNum = -1;  /* Means the step is not a (required) hypothesis */
-          for (srcHyp = 0; srcHyp < sourceHyps; srcHyp++) {
-            /* Find out if the proof step references a required hyp */
-            if ((g_Statement[sourceStmtNum].reqHypList)[srcHyp]
-                == sourceProof[srcStep]) {
-              srcHypNum = srcHyp;
-              break;
-            }
-          }
-          if (srcHypNum > -1) {
-            /* It's a required hypothesis */
-            nmbrLet(&expandedSubproof, nmbrCat(expandedSubproof,
-                hypSubproofs[srcHypNum], NULL));
-          } else if (srcStepType == (char)e_) {
-            /* A non-required hypothesis cannot be $e */
-            bug(1874);
-          } else if (srcStepType == (char)f_) {
-            /* It's an optional hypothesis (dummy variable), which we don't
-               know what it will be in final proof, so make it an unknown
-               step in final proof */
-            hasDummyVar = 1;
-            nmbrLet(&expandedSubproof, nmbrAddElement(expandedSubproof,
-                -(long)'?'));
-          }
-        } else if (srcStepType != (char)a_ && srcStepType != (char)p_) {
-          bug(1875);
-        } else {
-          /* It's a normal statement reference ($a, $p); use it as is */
-          /* (This adds normal ref steps one by one, each requiring a new
-             allocation in nmbrAddElement.  This should be OK if, as expected,
-             only relatively short proofs are expanded.  If it becomes a problem,
-             we can modify the code to do bigger chunks of $a, $p steps at a
-             time.) */
-          nmbrLet(&expandedSubproof, nmbrAddElement(expandedSubproof,
-              sourceProof[srcStep]));
-        } /* if srcStepType... *? */
-      } /* next srcStep */
-      /* Insert the expanded subproof into the final expanded proof */
-      nmbrLet(&expandedTargetProof, nmbrCat(
-          nmbrLeft(expandedTargetProof, (targetStep + 1) - targetSubpLen),
-          expandedSubproof,
-          nmbrRight(expandedTargetProof, (targetStep + 1) + 1), NULL));
-      break; /* Start over after processing an expansion */
-    } /* next targetStep */
-    if (!foundMatch) break;
-    /* A matching statement was expanded.  Start over so we can accurate
-       process nested references to sourceStmt */
-  } /* end while(1) */
-
-
- RETURN_POINT:
-  if (nmbrEq(origTargetProof, expandedTargetProof)) {
-    /*
-    print2("No expansion occurred.  The proof was not changed.\n");
-    */
-  } else {
-    /*
-    printLongLine(cat("All references to theorem \"",
-        g_Statement[sourceStmtNum].labelName,
-        "\" were expanded in the proof of \"",
-        g_Statement[targetStmtNum].labelName,
-        "\", which increased from ",
-        str((double)(nmbrLen(targetProof))), " to ",
-        str((double)(nmbrLen(expandedTargetProof))), " steps (uncompressed).",
-        NULL), " ", " ");
-    */
-    if (hasDummyVar == 1) {
-      printLongLine(cat(
-      "******* Note: The expansion of \"",
-      g_Statement[sourceStmtNum].labelName,
-      "\" has dummy variable(s) that need to be assigned.", NULL), " ", " ");
-    }
-    if (hasUnknownStep == 1) {
-      printLongLine(cat(
-      "******* Note: The expansion of \"",
-      g_Statement[sourceStmtNum].labelName,
-      "\" has unknown step(s) that need to be assigned.", NULL), " ", " ");
-    }
-  }
-  /* Deallocate memory */
-  nmbrLet(&sourceProof, NULL_NMBRSTRING);
-  nmbrLet(&origTargetProof, NULL_NMBRSTRING);
-  nmbrLet(&targetProof, NULL_NMBRSTRING);
-  nmbrLet(&expandedSubproof, NULL_NMBRSTRING);
-  /* Deallocate array entries */
-  for (srcHyp = 0; srcHyp < sourceHyps; srcHyp++) {
-    nmbrLet((nmbrString **)(&(hypSubproofs[srcHyp])), NULL_NMBRSTRING);
-  }
-  /* Deallocate array */
-  pntrLet(&hypSubproofs, NULL_PNTRSTRING);
-  return expandedTargetProof;
-} /* expandProof */
-
-
-/* Delete a subproof starting (in reverse from) step.  The step is replaced
-   with an unknown step, and its .target and .user fields are retained. */
-void deleteSubProof(long step) {
-  long sbPfLen, pos;
-
-  /* 22-Aug-2012 nm  We now allow this so we can REPLACE an unassigned
-     step.  User detection for DELETE of unassigned step is still done
-     separately in metamath.c. */
-  /*
-  if ((g_ProofInProgress.proof)[step] == -(long)'?') bug (1805);
-  */
-                       /* Unknown step should not be allowed at cmd interface */
-  if ((g_ProofInProgress.proof)[step] == -(long)'?') return;
-           /* 22-Aug-2012 nm Don't do anything if step is unassigned. */
-
-  sbPfLen = subproofLen(g_ProofInProgress.proof, step);
-  nmbrLet(&g_ProofInProgress.proof, nmbrCat(nmbrAddElement(
-      nmbrLeft(g_ProofInProgress.proof, step - sbPfLen + 1), -(long)'?'),
-      nmbrRight(g_ProofInProgress.proof, step + 2), NULL));
-  for (pos = step - sbPfLen + 1; pos <= step; pos++) {
-    if (pos < step) {
-      /* Deallocate .target and .user */
-      nmbrLet((nmbrString **)(&((g_ProofInProgress.target)[pos])), NULL_NMBRSTRING);
-      nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[pos])), NULL_NMBRSTRING);
-    }
-    /* Deallocate .source */
-    nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[pos])), NULL_NMBRSTRING);
-  }
-  pntrLet(&g_ProofInProgress.target, pntrCat(pntrLeft(g_ProofInProgress.target,
-      step - sbPfLen + 1), pntrRight(g_ProofInProgress.target,
-      step + 1), NULL));
-  pntrLet(&g_ProofInProgress.source, pntrCat(pntrLeft(g_ProofInProgress.source,
-      step - sbPfLen + 1), pntrRight(g_ProofInProgress.source,
-      step + 1), NULL));
-  pntrLet(&g_ProofInProgress.user, pntrCat(pntrLeft(g_ProofInProgress.user,
-      step - sbPfLen + 1), pntrRight(g_ProofInProgress.user,
-      step + 1), NULL));
-} /* deleteSubProof */
-
-
-/* Check to see if a statement will match the g_ProofInProgress.target (or .user)
-   of an unknown step.  Returns 1 if match, 0 if not, 2 if unification
-   timed out. */
-char checkStmtMatch(long statemNum, long step)
-{
-  char targetFlag;
-  char userFlag = 1; /* Default if no user field */
-  pntrString *stateVector = NULL_PNTRSTRING;
-  nmbrString *mString; /* Pointer only; not allocated */
-  nmbrString *scheme = NULL_NMBRSTRING;
-  long targetLen, mStringLen, reqVars, stsym, tasym, sym, var, hyp, numHyps;
-  flag breakFlag;
-  flag firstSymbsAreConstsFlag;
-
-  /* This is no longer a bug.  (Could be true for REPLACE command.)
-  if ((g_ProofInProgress.proof)[step] != -(long)'?') bug(1806);
-  */
-
-  targetLen = nmbrLen((g_ProofInProgress.target)[step]);
-  if (!targetLen) bug(1807);
-
-  /* If the statement is a hypothesis, just see if it unifies. */
-  if (g_Statement[statemNum].type == (char)e_ || g_Statement[statemNum].type ==
-      (char)f_) {
-
-    /* Make sure it's a hypothesis of the statement being proved */
-    breakFlag = 0;
-    numHyps = g_Statement[g_proveStatement].numReqHyp;
-    for (hyp = 0; hyp < numHyps; hyp++) {
-      if (g_Statement[g_proveStatement].reqHypList[hyp] == statemNum) {
-        breakFlag = 1;
-        break;
-      }
-    }
-    if (!breakFlag) { /* Not a required hypothesis; is it optional? */
-      numHyps = nmbrLen(g_Statement[g_proveStatement].optHypList);
-      for (hyp = 0; hyp < numHyps; hyp++) {
-        if (g_Statement[g_proveStatement].optHypList[hyp] == statemNum) {
-          breakFlag = 1;
-          break;
-        }
-      }
-      if (!breakFlag) { /* Not a hypothesis of statement being proved */
-        targetFlag = 0;
-        goto returnPoint;
-      }
-    }
-
-    g_unifTrialCount = 1; /* Reset unification timeout */
-    targetFlag = unifyH((g_ProofInProgress.target)[step],
-        g_Statement[statemNum].mathString, &stateVector, 0);
-   if (nmbrLen((g_ProofInProgress.user)[step])) {
-      g_unifTrialCount = 1; /* Reset unification timeout */
-      userFlag = unifyH((g_ProofInProgress.user)[step],
-        g_Statement[statemNum].mathString, &stateVector, 0);
-    }
-    goto returnPoint;
-  }
-
-  mString = g_Statement[statemNum].mathString;
-  mStringLen = g_Statement[statemNum].mathStringLen;
-
-  /* For speedup - 1st, 2nd, & last math symbols should match if constants */
-  /* (The speedup is only done for .target; the .user is assumed to be
-     infrequent.) */
-  /* First symbol */
-  firstSymbsAreConstsFlag = 0;
-  stsym = mString[0];
-  tasym = ((nmbrString *)((g_ProofInProgress.target)[step]))[0];
-  if (g_MathToken[stsym].tokenType == (char)con_) {
-    if (g_MathToken[tasym].tokenType == (char)con_) {
-      firstSymbsAreConstsFlag = 1; /* The first symbols are constants */
-      if (stsym != tasym) {
-        targetFlag = 0;
-        goto returnPoint;
-      }
-    }
-  }
-  /* Last symbol */
-  stsym = mString[mStringLen - 1];
-  tasym = ((nmbrString *)((g_ProofInProgress.target)[step]))[targetLen - 1];
-  if (stsym != tasym) {
-    if (g_MathToken[stsym].tokenType == (char)con_) {
-      if (g_MathToken[tasym].tokenType == (char)con_) {
-        targetFlag = 0;
-        goto returnPoint;
-      }
-    }
-  }
-  /* Second symbol */
-  if (targetLen > 1 && mStringLen > 1 && firstSymbsAreConstsFlag) {
-    stsym = mString[1];
-    tasym = ((nmbrString *)((g_ProofInProgress.target)[step]))[1];
-    if (stsym != tasym) {
-      if (g_MathToken[stsym].tokenType == (char)con_) {
-        if (g_MathToken[tasym].tokenType == (char)con_) {
-          targetFlag = 0;
-          goto returnPoint;
-        }
-      }
-    }
-  }
-
-  /* Change variables in statement to dummy variables for unification */
-  nmbrLet(&scheme, mString);
-  reqVars = nmbrLen(g_Statement[statemNum].reqVarList);
-  if (reqVars + g_pipDummyVars > g_dummyVars) {
-    /* Declare more dummy vars if necessary */
-    declareDummyVars(reqVars + g_pipDummyVars - g_dummyVars);
-  }
-  for (var = 0; var < reqVars; var++) {
-    /* Put dummy var mapping into g_MathToken[].tmp field */
-    g_MathToken[g_Statement[statemNum].reqVarList[var]].tmp = g_mathTokens + 1 +
-        g_pipDummyVars + var;
-  }
-  for (sym = 0; sym < mStringLen; sym++) {
-    if (g_MathToken[scheme[sym]].tokenType != (char)var_)
-        continue;
-    /* Use dummy var mapping from g_MathToken[].tmp field */
-    scheme[sym] = g_MathToken[scheme[sym]].tmp;
-  }
-
-  /* Now see if we can unify */
-  g_unifTrialCount = 1; /* Reset unification timeout */
-  targetFlag = unifyH((g_ProofInProgress.target)[step],
-      scheme, &stateVector, 0);
-  if (nmbrLen((g_ProofInProgress.user)[step])) {
-    g_unifTrialCount = 1; /* Reset unification timeout */
-    userFlag = unifyH((g_ProofInProgress.user)[step],
-      scheme, &stateVector, 0);
-  }
-
- returnPoint:
-  nmbrLet(&scheme, NULL_NMBRSTRING);
-  purgeStateVector(&stateVector);
-
-  if (!targetFlag || !userFlag) return (0);
-  if (targetFlag == 1 && userFlag == 1) return (1);
-  return (2);
-
-} /* checkStmtMatch */
-
-/* Check to see if a (user-specified) math string will match the
-   g_ProofInProgress.target (or .user) of an step.  Returns 1 if match, 0 if
-   not, 2 if unification timed out. */
-char checkMStringMatch(nmbrString *mString, long step)
-{
-  pntrString *stateVector = NULL_PNTRSTRING;
-  char targetFlag;
-  char sourceFlag = 1; /* Default if no .source */
-
-  g_unifTrialCount = 1; /* Reset unification timeout */
-  targetFlag = unifyH(mString, (g_ProofInProgress.target)[step],
-      &stateVector, 0);
-  if (nmbrLen((g_ProofInProgress.source)[step])) {
-    g_unifTrialCount = 1; /* Reset unification timeout */
-    sourceFlag = unifyH(mString, (g_ProofInProgress.source)[step],
-        &stateVector, 0);
-  }
-
-  purgeStateVector(&stateVector);
-
-  if (!targetFlag || !sourceFlag) return (0);
-  if (targetFlag == 1 && sourceFlag == 1) return (1);
-  return (2);
-
-} /* checkMStringMatch */
-
-
-/* Find proof of formula or simple theorem (no new vars in $e's) */
-/* maxEDepth is the maximum depth at which statements with $e hypotheses are
-   considered.  A value of 0 means none are considered. */
-/* The caller must deallocate the returned nmbrString. */
-nmbrString *proveFloating(nmbrString *mString, long statemNum, long maxEDepth,
-    long step, /* 0 means step 1; used for messages */
-    flag noDistinct, /* 1 means don't try statements with $d's  16-Aug-04 */
-    /* 3-May-2016 nm */
-    flag overrideFlag, /* 1 means to override usage locks, 2 means to
-              override silently (for web-page syntax breakdown in mmcmds.c) */
-    flag mathboxFlag /* 5-Aug-2020 nm */
-    )
-{
-
-  long reqHyps, optHyps;
-  long hyp, stmt, sym, var, i, j;
-  nmbrString *proof = NULL_NMBRSTRING;
-  nmbrString *scheme = NULL_NMBRSTRING;
-  pntrString *hypList = NULL_PNTRSTRING;
-  nmbrString *hypOrdMap = NULL_NMBRSTRING; /* Order remapping for speedup */
-  pntrString *hypProofList = NULL_PNTRSTRING;
-  pntrString *stateVector = NULL_PNTRSTRING;
-  /* long firstSymbol, secondSymbol, lastSymbol; */ /* 22-Aug-2012 nm Deleted */
-  nmbrString *stmtMathPtr;
-  nmbrString *hypSchemePtr;
-  nmbrString *hypProofPtr;
-  nmbrString *makeSubstPtr;
-  flag reEntryFlag;
-  flag tmpFlag;
-  flag breakFlag;
-  flag firstEHypFlag;
-  long schemeLen, /*mStringLen,*/ schemeVars, schReqHyps, hypLen, reqVars;
-  long saveUnifTrialCount;
-  static long depth = 0;
-  static long trials;
-  static flag maxDepthExceeded; /* 2-Oct-2015 nm */
-  long selfScanSteps;
-  long selfScanStep;
-  long prfMbox; /* 5-Aug-2020 nm */
-
-/*E*/  long unNum;
-/*E*/if (db8)print2("%s\n", cat(space(depth+2), "Entered: ",
-/*E*/   nmbrCvtMToVString(mString), NULL));
-
-  prfMbox = getMathboxNum(statemNum); /* 5-Aug-2020 nm */
-
-  if (depth == 0) {
-    trials = 0; /* Initialize trials */
-    maxDepthExceeded = 0; /* 2-Oct-2015 nm  Initialize */
-  } else {
-    trials++;
-  }
-  depth++; /* Update backtracking depth */
-  if (trials > g_userMaxProveFloat) {
-    nmbrLet(&proof, NULL_NMBRSTRING);
-    print2(
-"Exceeded trial limit at step %ld.  You may increase with SET SEARCH_LIMIT.\n",
-        (long)(step + 1));
-    goto returnPoint;
-  }
-
-  /* 2-Oct-2015 nm */
-  if (maxDepthExceeded) {
-    /* Pop out of the recursive calls to avoid an infinite loop */
-    nmbrLet(&proof, NULL_NMBRSTRING);
-    goto returnPoint;
-  }
-
-#define MAX_DEPTH 40  /* > this, infinite loop assumed */ /*???User setting?*/
-  if (depth > MAX_DEPTH) {
-    nmbrLet(&proof, NULL_NMBRSTRING);
-/*??? Document in Metamath manual. */
-    printLongLine(cat(
-       "?Warning: A possible infinite loop was found in $f hypothesis ",
-       "backtracking (i.e., depth > ", str((double)MAX_DEPTH),
-       ").  The last proof attempt was for math string \"",
-       nmbrCvtMToVString(mString),
-       "\".  Your axiom system may have an error ",
-       "or you may have to SET EMPTY_SUBSTITUTION ON.", NULL), " ", " ");
-    maxDepthExceeded = 1;  /* 2-Oct-2015 nm  Flag to exit recursion */
-    goto returnPoint;
-  }
-
-
-  /* First see if mString matches a required or optional hypothesis; if so,
-     we're done; the proof is just the hypothesis. */
-  reqHyps = g_Statement[statemNum].numReqHyp;
-  for (hyp = 0; hyp < reqHyps; hyp++) {
-    if (nmbrEq(mString,
-        g_Statement[g_Statement[statemNum].reqHypList[hyp]].mathString)) {
-      nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING,
-          g_Statement[statemNum].reqHypList[hyp]));
-      goto returnPoint;
-    }
-  }
-  optHyps = nmbrLen(g_Statement[statemNum].optHypList);
-  for (hyp = 0; hyp < optHyps; hyp++) {
-    if (nmbrEq(mString,
-        g_Statement[g_Statement[statemNum].optHypList[hyp]].mathString)) {
-      nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING,
-          g_Statement[statemNum].optHypList[hyp]));
-      goto returnPoint;
-    }
-  }
-
-  /* 7/31/99 - Scan all proved steps in the current proof to see if the
-     statement has already been proved in another subproof */
-  selfScanSteps = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
-  /* Note: proveFloating() can be called from typeStatement() (for HTML syntax
-     breakdown), and we don't want to do a self-scan that case.  If
-     g_ProofInProgress.proof has a non-zero length, it tells us that
-     we are in Proof Assistant mode.  If g_ProofInProgress.proof has zero
-     length, the loop below will be skipped, and we're still OK. */
-  /* We scan backwards for maximum speed since IMPROVE ALL processes steps
-     backwards, so we maximize the chance of a proved hit earlier on */
-  for (selfScanStep = selfScanSteps - 1; selfScanStep >= 0; selfScanStep--) {
-    if (nmbrEq(mString, (g_ProofInProgress.target)[selfScanStep])) {
-      /* The step matches.  Now see if the step was proved. */
-
-      /* Get the subproof at the step */
-      /* Note that for subproof length of 1, the 2nd argument of nmbrSeg
-         evaluates to selfScanStep + 1, so nmbrSeg will be length 1 */
-      nmbrLet(&proof, nmbrSeg(g_ProofInProgress.proof, selfScanStep -
-          subproofLen(g_ProofInProgress.proof, selfScanStep) + 2,
-          selfScanStep + 1));
-
-      /* Check to see that the subproof has no unknown steps. */
-      if (nmbrElementIn(1, proof, -(long)'?')) {
-        /* Clear out the trial proof */
-        nmbrLet(&proof, NULL_NMBRSTRING);
-        /* And give up this trial */
-        continue; /* next selfScanStep */
-      }
-      /* Otherwise, we've found our proof; use it and exit */
-      goto returnPoint;
-    } /* if (nmbrEq(mString, (g_ProofInProgress.target)[selfScanStep]) */
-  } /* Next selfScanStep */
-  /* 7/31/99 - End of scanning current proof */
-
-  /* Scan all statements up to the current statement to see if we can unify */
-
-  /* 22-Aug-2012 nm Now done with quickMatchFilter() *******
-  mStringLen = nmbrLen(mString);
-  /@ For speedup @/
-  firstSymbol = mString[0];
-  if (g_MathToken[firstSymbol].tokenType != (char)con_) firstSymbol = 0;
-  if (mStringLen > 1) {
-    secondSymbol = mString[1];
-    if (g_MathToken[secondSymbol].tokenType != (char)con_) secondSymbol = 0;
-    /@ If first symbol is a variable, second symbol shouldn't be tested. @/
-    if (!firstSymbol) secondSymbol = 0;
-  } else {
-    secondSymbol = 0;
-  }
-  lastSymbol = mString[mStringLen - 1];
-  if (g_MathToken[lastSymbol].tokenType != (char)con_) lastSymbol = 0;
-  **** */
-
-  /* for (stmt = 1; stmt < statemNum; stmt++) { */   /* old code */
-  /* 30-Nov-2013 nm Reversed the scan order so that w3a will match before
-     wa, helping to prevent an exponential blowup for definition syntax
-     breakdown.  If wa is first, then wa will incorrectly match a w3a
-     subexpression, with the incorrect trial only detected deeper down;
-     whereas w3a will rarely match a wa subexpression, so the trial match
-     will get rejected immediately. */
-  for (stmt = statemNum - 1; stmt >= 1; stmt--) {
-
-    /* 22-Aug-2012 nm Separated quick filter for reuse in other functions */
-    if (quickMatchFilter(stmt, mString, 0/*no dummy vars*/) == 0) continue;
-
-    /* 3-May-2016 nm */
-    if (!overrideFlag && getMarkupFlag(stmt, USAGE_DISCOURAGED)) {
-      /* Skip usage-discouraged statements */
-      continue;
-    }
-
-    /* 5-Aug-2020 nm */
-    /* Skip statements in other mathboxes unless /INCLUDE_MATHBOXES.  (We don't
-       care about the first mathbox since there are no others above it.) */
-    if (mathboxFlag == 0 && prfMbox >= 2) {
-      /* Note that g_mathboxStart[] starts a 0 */
-      if (stmt > g_mathboxStmt && stmt < g_mathboxStart[prfMbox - 1]) {
-        continue;
-      }
-    }
-
-    /* 22-Aug-2012 nm Now done with quickMatchFilter() ****
-    if (g_Statement[stmt].type != (char)a_ &&
-        g_Statement[stmt].type != (char)p_) continue; /@ Not $a or $p @/
-    **** */
-
-    /* 16-Aug-04 nm  noDistinct is set by NO_DISTICT qualifier in IMPROVE */
-    if (noDistinct) {
-      /* Skip the statement if it has a $d requirement.  This option
-         prevents illegal minimizations that would violate $d requirements
-         since the Proof Assistant does not check for $d violations. */
-      if (nmbrLen(g_Statement[stmt].reqDisjVarsA)) {
-        continue;
-      }
-    }
-
-    stmtMathPtr = g_Statement[stmt].mathString;
-
-    /* 22-Aug-2012 nm Now done with quickMatchFilter() ****
-    /@ Speedup:  if first or last tokens in instance and scheme are constants,
-       they must match @/
-    if (firstSymbol) { /@ First symbol in mString is a constant @/
-      if (firstSymbol != stmtMathPtr[0]) {
-        if (g_MathToken[stmtMathPtr[0]].tokenType == (char)con_) continue;
-      }
-    }
-    **** */
-
-    schemeLen = nmbrLen(stmtMathPtr);
-
-    /* 22-Aug-2012 nm Now done with quickMatchFilter() ****
-    /@ ...Continuation of speedup @/
-    if (secondSymbol) { /@ Second symbol in mString is a constant @/
-      if (schemeLen > 1) {
-        if (secondSymbol != stmtMathPtr[1]) {
-          /@ Second symbol should be tested only if 1st symbol is a constant @/
-          if (g_MathToken[stmtMathPtr[0]].tokenType == (char)con_) {
-            if (g_MathToken[stmtMathPtr[1]].tokenType == (char)con_)
-                continue;
-          }
-        }
-      }
-    }
-    if (lastSymbol) { /@ Last symbol in mString is a constant @/
-      if (lastSymbol != stmtMathPtr[schemeLen - 1]) {
-        if (g_MathToken[stmtMathPtr[schemeLen - 1]].tokenType ==
-           (char)con_) continue;
-      }
-    }
-    **** */
-
-    /* 22-Aug-2012 nm Now done with quickMatchFilter() ****
-    /@ Speedup:  make sure all constants in scheme are in instance (i.e.
-       mString) @/
-    /@ First, set g_MathToken[].tmp for all symbols in scheme @/
-    for (sym = 0; sym < schemeLen; sym++) {
-      g_MathToken[stmtMathPtr[sym]].tmp = 1;
-    }
-    /@ Next, clear g_MathToken[].tmp for all symbols in instance @/
-    for (sym = 0; sym < mStringLen; sym++) {
-      g_MathToken[mString[sym]].tmp = 0;
-    }
-    /@ Finally, check that they got cleared for all constants in scheme @/
-    breakFlag = 0;
-    for (sym = 0; sym < schemeLen; sym++) {
-      if (g_MathToken[stmtMathPtr[sym]].tokenType == (char)con_) {
-        if (g_MathToken[stmtMathPtr[sym]].tmp) {
-          breakFlag = 1;
-          break;
-        }
-      }
-    }
-    if (breakFlag) continue; /@ To next stmt @/
-    **** */
-
-
-    schReqHyps = g_Statement[stmt].numReqHyp;
-    reqVars = nmbrLen(g_Statement[stmt].reqVarList);
-
-    /* Skip any statements with $e hypotheses based on maxEDepth */
-    /* (This prevents exponential growth of backtracking) */
-    breakFlag = 0;
-    firstEHypFlag = 1;
-    for (hyp = 0; hyp < schReqHyps; hyp++) {
-      if (g_Statement[g_Statement[stmt].reqHypList[hyp]].type == (char)e_) {
-        /* (???Maybe, in the future, we'd want to do this only for depths >
-           a small nonzero amount -- specified by global variable) */
-        if (depth > maxEDepth) {
-          breakFlag = 1;
-          break;
-        } else {
-          /* We should also skip cases where a $e hypothesis has a variable
-             not in the assertion. */
-          if (firstEHypFlag) { /* This scan is needed only once */
-            /* First, set g_MathToken[].tmp for each required variable */
-            for (var = 0; var < reqVars; var++) {
-              g_MathToken[g_Statement[stmt].reqVarList[var]].tmp = 1;
-            }
-            /* Next, clear g_MathToken[].tmp for each symbol in scheme */
-            for (sym = 0; sym < schemeLen; sym++) {
-              g_MathToken[stmtMathPtr[sym]].tmp = 0;
-            }
-            /* If any were left over, a $e hyp. has a new variable. */
-            for (var = 0; var < reqVars; var++) {
-              if (g_MathToken[g_Statement[stmt].reqVarList[var]].tmp) {
-                breakFlag = 1;
-                break;
-              }
-            }
-            if (breakFlag) break;
-            firstEHypFlag = 0; /* Don't need to do this scan again for stmt. */
-          } /* End if firstHypFlag */
-        } /* End if depth > maxEDepth */
-      } /* End if $e */
-    } /* Next hyp */
-    if (breakFlag) continue; /* To next stmt */
-
-
-
-    /* Change all variables in the statement to dummy vars for unification */
-    nmbrLet(&scheme, stmtMathPtr);
-    schemeVars = reqVars; /* S.b. same after eliminated new $e vars above */
-    if (schemeVars + g_pipDummyVars > g_dummyVars) {
-      /* Declare more dummy vars if necessary */
-      declareDummyVars(schemeVars + g_pipDummyVars - g_dummyVars);
-    }
-    for (var = 0; var < schemeVars; var++) {
-      /* Put dummy var mapping into g_MathToken[].tmp field */
-      g_MathToken[g_Statement[stmt].reqVarList[var]].tmp = g_mathTokens + 1 +
-          g_pipDummyVars + var;
-    }
-    for (sym = 0; sym < schemeLen; sym++) {
-      if (g_MathToken[stmtMathPtr[sym]].tokenType != (char)var_) continue;
-      /* Use dummy var mapping from g_MathToken[].tmp field */
-      scheme[sym] = g_MathToken[stmtMathPtr[sym]].tmp;
-    }
-
-    /* Change all variables in the statement's hyps to dummy vars for subst. */
-    pntrLet(&hypList, pntrNSpace(schReqHyps));
-    nmbrLet(&hypOrdMap, nmbrSpace(schReqHyps));
-    pntrLet(&hypProofList, pntrNSpace(schReqHyps));
-    for (hyp = 0; hyp < schReqHyps; hyp++) {
-      hypSchemePtr = NULL_NMBRSTRING;
-      nmbrLet(&hypSchemePtr,
-        g_Statement[g_Statement[stmt].reqHypList[hyp]].mathString);
-      hypLen = nmbrLen(hypSchemePtr);
-      for (sym = 0; sym < hypLen; sym++) {
-        if (g_MathToken[hypSchemePtr[sym]].tokenType
-            != (char)var_) continue;
-        /* Use dummy var mapping from g_MathToken[].tmp field */
-        hypSchemePtr[sym] = g_MathToken[hypSchemePtr[sym]].tmp;
-      }
-      hypList[hyp] = hypSchemePtr;
-      hypOrdMap[hyp] = hyp;
-    }
-
-    g_unifTrialCount = 1; /* Reset unification timeout */
-    reEntryFlag = 0; /* For unifyH() */
-
-/*E*/unNum = 0;
-    while (1) { /* Try all possible unifications */
-      tmpFlag = unifyH(scheme, mString, &stateVector, reEntryFlag);
-      if (!tmpFlag) break; /* (Next) unification not possible */
-      if (tmpFlag == 2) {
-        print2(
-"Unification timed out.  SET UNIFICATION_TIMEOUT larger for better results.\n");
-        g_unifTrialCount = 1; /* Reset unification timeout */
-        break; /* Treat timeout as if unification not possible */
-      }
-
-/*E*/unNum++;
-/*E*/if (db8)print2("%s\n", cat(space(depth+2), "Testing unification ",
-/*E*/   str((double)unNum), " statement ", g_Statement[stmt].labelName,
-/*E*/   ": ", nmbrCvtMToVString(scheme), NULL));
-      reEntryFlag = 1; /* For next unifyH() */
-
-      /* Make substitutions into each hypothesis, and try to prove that
-         hypothesis */
-      nmbrLet(&proof, NULL_NMBRSTRING);
-      breakFlag = 0;
-      for (hyp = 0; hyp < schReqHyps; hyp++) {
-/*E*/if (db8)print2("%s\n", cat(space(depth+2), "Proving hyp. ",
-/*E*/   str((double)(hypOrdMap[hyp])), "(#", str((double)hyp), "):  ",
-/*E*/   nmbrCvtMToVString(hypList[hypOrdMap[hyp]]), NULL));
-        makeSubstPtr = makeSubstUnif(&tmpFlag, hypList[hypOrdMap[hyp]],
-            stateVector);
-        if (tmpFlag) bug(1808); /* No dummy vars. should result unless bad $a's*/
-                            /*??? Implement an error check for this in parser */
-
-        saveUnifTrialCount = g_unifTrialCount; /* Save unification timeout */
-        hypProofPtr = proveFloating(makeSubstPtr, statemNum, maxEDepth, step,
-            noDistinct,
-            overrideFlag, /* 3-May-2016 nm */
-            mathboxFlag /* 5-Aug-2020 nm */
-            );
-        g_unifTrialCount = saveUnifTrialCount; /* Restore unification timeout */
-
-        nmbrLet(&makeSubstPtr, NULL_NMBRSTRING); /* Deallocate */
-        if (!nmbrLen(hypProofPtr)) {
-          /* Not possible */
-          breakFlag = 1;
-          break;
-        }
-
-        /* Deallocate in case this is the 2nd or later pass of main
-           unification */
-        nmbrLet((nmbrString **)(&hypProofList[hypOrdMap[hyp]]),
-            NULL_NMBRSTRING);
-
-        hypProofList[hypOrdMap[hyp]] = hypProofPtr;
-      }
-      if (breakFlag) {
-       /* Proof is not possible for some hypothesis. */
-
-       /* 6-Feb-2007 Jason Orendorff - Patch to eliminate the duplicate
-          "Exceeded trial limit at step n" messages when the limit is
-          reached. */
-       /* Perhaps the search limit was reached. */
-       if (trials > g_userMaxProveFloat) {
-         /* Deallocate hypothesis schemes and proofs */
-         for (hyp = 0; hyp < schReqHyps; hyp++) {
-           nmbrLet((nmbrString **)(&hypList[hyp]), NULL_NMBRSTRING);
-           nmbrLet((nmbrString **)(&hypProofList[hyp]), NULL_NMBRSTRING);
-         }
-         /* The error message has already been printed. */
-         nmbrLet(&proof, NULL_NMBRSTRING);
-         goto returnPoint;
-       }
-       /* End of 6-Feb-2007 patch */
-
-       /* Speedup:  Move the hypothesis for which the proof was not found
-          to the beginning of the hypothesis list, so it will be tried
-          first next time. */
-       j = hypOrdMap[hyp];
-       for (i = hyp; i >= 1; i--) {
-         hypOrdMap[i] = hypOrdMap[i - 1];
-       }
-       hypOrdMap[0] = j;
-
-       continue; /* Not possible; get next unification */
-
-      } /* End if breakFlag */
-
-      /* Proofs were found for all hypotheses */
-
-      /* Build the proof */
-      for (hyp = 0; hyp < schReqHyps; hyp++) {
-        nmbrLet(&proof, nmbrCat(proof, hypProofList[hyp], NULL));
-      }
-
-      if (getMarkupFlag(stmt, USAGE_DISCOURAGED)) {
-        switch (overrideFlag) {
-          case 0: bug(1869); break; /* Should never get here if no override */
-          case 2: break; /* Accept overrided silently (in mmcmds.c syntax
-                            breakdown calls for $a web pages) */
-          case 1:  /* Normal override */
-            /* print2("\n"); */ /* Enable for more emphasis */
-            print2(
-          ">>> ?Warning: Overriding discouraged usage of statement \"%s\".\n",
-                g_Statement[stmt].labelName);
-            /* print2("\n"); */ /* Enable for more emphasis */
-            break;
-          default: bug(1870); /* Illegal value */
-        } /* end switch (overrideFlag) */
-      } /* end if (getMarkupFlag(stmt, USAGE_DISCOURAGED)) */
-
-      /* 8-Aug-2020 nm */
-      /* TODO: Put this in proveByReplacement? */
-      /* Notify mathbox user when other mathboxes are used */
-      if (mathboxFlag != 0) {  /* Skip unless /INCLUDE_MATHBOXES was specified */
-        /* See if it's in another mathbox; if so, let user know */
-        assignMathboxInfo();
-        if (stmt > g_mathboxStmt && g_proveStatement > g_mathboxStmt) {
-          if (stmt < g_mathboxStart[getMathboxNum(g_proveStatement) - 1]) {
-            printLongLine(cat("Used \"", g_Statement[stmt].labelName,
-                  "\" from the mathbox for ",
-                  g_mathboxUser[getMathboxNum(stmt) - 1], ".",
-                  NULL),
-                "  ", " ");
-          }
-        }
-      }
-
-      nmbrLet(&proof, nmbrAddElement(proof, stmt)); /* Complete the proof */
-
-      /* Deallocate hypothesis schemes and proofs */
-      for (hyp = 0; hyp < schReqHyps; hyp++) {
-        nmbrLet((nmbrString **)(&hypList[hyp]), NULL_NMBRSTRING);
-        nmbrLet((nmbrString **)(&hypProofList[hyp]), NULL_NMBRSTRING);
-      }
-      goto returnPoint;
-
-    } /* End while (next unifyH() call) */
-
-    /* Deallocate hypothesis schemes and proofs */
-    for (hyp = 0; hyp < schReqHyps; hyp++) {
-      nmbrLet((nmbrString **)(&hypList[hyp]), NULL_NMBRSTRING);
-      nmbrLet((nmbrString **)(&hypProofList[hyp]), NULL_NMBRSTRING);
-    }
-
-  } /* Next stmt */
-
-  nmbrLet(&proof, NULL_NMBRSTRING);  /* Proof not possible */
-
- returnPoint:
-  /* Deallocate unification state vector */
-  purgeStateVector(&stateVector);
-
-  nmbrLet(&scheme, NULL_NMBRSTRING);
-  pntrLet(&hypList, NULL_PNTRSTRING);
-  nmbrLet(&hypOrdMap, NULL_NMBRSTRING);
-  pntrLet(&hypProofList, NULL_PNTRSTRING);
-  depth--; /* Restore backtracking depth */
-/*E*/if(db8)print2("%s\n", cat(space(depth+2), "Returned: ",
-/*E*/   nmbrCvtRToVString(proof,
-/*E*/                /* 25-Jan-2016 nm */
-/*E*/                0, /*explicitTargets*/
-/*E*/                0 /*statemNum, used only if explicitTargets*/), NULL));
-/*E*/if(db8){if(!depth)print2("Trials: %ld\n", trials);}
-  return (proof); /* Caller must deallocate */
-} /* proveFloating */
-
-
-
-/* This function does quick check for some common conditions that prevent
-   a trial statement (scheme) from being unified with a given instance.
-   Return value 0 means it can't be unified, 1 means it might be unifiable. */
-INLINE flag quickMatchFilter(long trialStmt, nmbrString *mString,
-    long dummyVarFlag /* 0 if no dummy vars in mString */) {
-  /* 22-Aug-2012 nm This function used to be part of proveFloating().  It
-     was separated out for reuse in other places */
-  long sym;
-  long firstSymbol, secondSymbol, lastSymbol;
-  nmbrString *stmtMathPtr;
-  flag breakFlag;
-  long schemeLen, mStringLen;
-
-  if (g_Statement[trialStmt].type != (char)p_ &&
-      g_Statement[trialStmt].type != (char)a_) return 0; /* Not $a or $p */
-
-  /* This section is common to all trial statements and in principle
-     could be computed once for speedup (it used to be when this code
-     was in g_proveStatement() ), but it doesn't seem too compute-intensive. */
-  mStringLen = nmbrLen(mString);
-  firstSymbol = mString[0];
-  if (g_MathToken[firstSymbol].tokenType != (char)con_) firstSymbol = 0;
-  if (mStringLen > 1) {
-    secondSymbol = mString[1];
-    if (g_MathToken[secondSymbol].tokenType != (char)con_) secondSymbol = 0;
-    /* If first symbol is a variable, second symbol shouldn't be tested. */
-    if (!firstSymbol) secondSymbol = 0;
-  } else {
-    secondSymbol = 0;
-  }
-  lastSymbol = mString[mStringLen - 1];
-  if (g_MathToken[lastSymbol].tokenType != (char)con_) lastSymbol = 0;
-  /* (End of common section) */
-
-
-  stmtMathPtr = g_Statement[trialStmt].mathString;
-
-  /* Speedup:  if first or last tokens in instance and scheme are constants,
-     they must match */
-  if (firstSymbol) { /* First symbol in mString is a constant */
-    if (firstSymbol != stmtMathPtr[0]) {
-      if (g_MathToken[stmtMathPtr[0]].tokenType == (char)con_) return 0;
-    }
-  }
-
-  schemeLen = nmbrLen(stmtMathPtr);
-
-  /* ...Continuation of speedup */
-  if (secondSymbol) { /* Second symbol in mString is a constant */
-    if (schemeLen > 1) {
-      if (secondSymbol != stmtMathPtr[1]) {
-        /* Second symbol should be tested only if 1st symbol is a constant */
-        if (g_MathToken[stmtMathPtr[0]].tokenType == (char)con_) {
-          if (g_MathToken[stmtMathPtr[1]].tokenType == (char)con_)
-              return 0;
-        }
-      }
-    }
-  }
-  if (lastSymbol) { /* Last symbol in mString is a constant */
-    if (lastSymbol != stmtMathPtr[schemeLen - 1]) {
-      if (g_MathToken[stmtMathPtr[schemeLen - 1]].tokenType ==
-         (char)con_) return 0;
-    }
-  }
-
-  /* Speedup:  make sure all constants in scheme are in instance (i.e.
-     mString) */
-  /* First, set g_MathToken[].tmp for all symbols in scheme */
-  for (sym = 0; sym < schemeLen; sym++) {
-    g_MathToken[stmtMathPtr[sym]].tmp = 1;
-  }
-  /* Next, clear g_MathToken[].tmp for all symbols in instance */
-  for (sym = 0; sym < mStringLen; sym++) {
-    g_MathToken[mString[sym]].tmp = 0;
-  }
-  /* Finally, check that they got cleared for all constants in scheme */
-  /* Only do this when there are no dummy variables in mString; this
-     is the case when dummyVarFlag = 0 (we depend on caller to set this
-     correctly) */
-  if (dummyVarFlag == 0) {
-    breakFlag = 0;
-    for (sym = 0; sym < schemeLen; sym++) {
-      if (g_MathToken[stmtMathPtr[sym]].tokenType == (char)con_) {
-        if (g_MathToken[stmtMathPtr[sym]].tmp) {
-          breakFlag = 1;
-          break;
-        }
-      }
-    }
-    if (breakFlag) return 0; /* No match */
-  } /* if dummyVarFlag == 0 */
-
-  return 1;
-
-} /* quickMatchFilter */
-
-
-/* Shorten proof by using specified statement. */
-void minimizeProof(long repStatemNum, long prvStatemNum,
-    flag allowGrowthFlag)
-{
-  /* repStatemNum is the statement number we're trying to use
-     in the proof to shorten it */
-  /* prvStatemNum is the statement number we're proving */
-  /* allowGrowthFlag means to make the replacement when possible,
-     even if it doesn't shorten the proof length */
-
-  long plen, step, mlen, sym, sublen;
-  long startingPlen = 0; /* 25-Jun-2014 nm */
-  flag foundFlag, breakFlag;
-  nmbrString *mString; /* Pointer only; not allocated */
-  nmbrString *newSubProofPtr = NULL_NMBRSTRING; /* Pointer only; not allocated;
-                however initialize for nmbrLen function before it's assigned */
-  /* 25-Jun-2014 nm */
-  if (allowGrowthFlag) startingPlen = nmbrLen(g_ProofInProgress.proof);
-
-  while (1) {
-    plen = nmbrLen(g_ProofInProgress.proof);
-    foundFlag = 0;
-    for (step = plen - 1; step >= 0; step--) {
-      /* Reject step with dummy vars */
-      mString = (g_ProofInProgress.target)[step];
-      mlen = nmbrLen(mString);
-      breakFlag = 0;
-      for (sym = 0; sym < mlen; sym++) {
-        if (mString[sym] > g_mathTokens) {
-          /* It is a dummy var. (i.e. work variable $1, $2, etc.) */
-          breakFlag = 1;
-          break;
-        }
-      }
-      if (breakFlag) continue;  /* Step has dummy var.; don't try it */
-
-      /* Reject step not matching replacement step */
-      if (!checkStmtMatch(repStatemNum, step)) {
-        continue;
-      }
-
-      /* Try the replacement */
-      /* Don't replace a step with itself (will cause infinite loop in
-         ALLOW_GROWTH mode) */
-      if ((g_ProofInProgress.proof)[step] != repStatemNum
-          /* || 1 */  /* For special replacement with same label; also below */
-          /* 3-Feb-06 nm */
-          /* When not in ALLOW_GROWTH mode i.e. when an infinite loop can't
-             occur, we _do_ let a label be tested against itself so that e.g. a
-             do-nothing chain of bitr's/pm4.2's will be trimmed off with a
-             better bitr match. */
-          || !allowGrowthFlag) {
-        newSubProofPtr = replaceStatement(repStatemNum,
-            step,
-            prvStatemNum,
-            1,/*scan just subproof for speed*/
-            0,/*noDistinct=0 OK since searchMethod=0 will only
-               call proveFloating for $f's */
-            0,/*searchMethod=0: call proveFloating only for $f's*/
-            0,/*improveDepth=0 OK since we call proveFloating only for $f's*/
-            2,/*overrideFlag=2(silent) OK since MINIMIZE_WITH checked it*/
-            1/*mathboxFlag=1 since MINIMIZE_WITH has checked it before here*/
-            );
-      }
-      if (!nmbrLen(newSubProofPtr)) continue;
-                                           /* Replacement was not successful */
-
-      if (nmbrElementIn(1, newSubProofPtr, -(long)'?')) {
-        /* 8/28/99 Don't do a replacement if the replacement has unknown
-           steps - this causes assignKnownSteps to abort, and it's not
-           clear if we should do that anyway since it doesn't necessarily
-           minimize the proof */
-        nmbrLet(&newSubProofPtr, NULL_NMBRSTRING); /* Deallocate */
-        continue;
-      }
-
-      /* Get the subproof at step s */
-      sublen = subproofLen(g_ProofInProgress.proof, step);
-      if (sublen > nmbrLen(newSubProofPtr) || allowGrowthFlag) {
-        /* Success - proof length was reduced */
-        /* 7-Jun-2011 nm Delete the old subproof only if it is not an unknown
-           step (since if it is an unknown step, it is already deleted) */
-        if ((g_ProofInProgress.proof)[step] == -(long)'?') {
-          /* 7-Jun-2011 nm This can only occur in / ALLOW_GROWTH mode */
-          if (!allowGrowthFlag) bug(1831);
-        } else {
-          deleteSubProof(step);
-        }
-        addSubProof(newSubProofPtr, step - sublen + 1);
-        assignKnownSteps(step - sublen + 1, nmbrLen(newSubProofPtr));
-        foundFlag = 1;
-        nmbrLet(&newSubProofPtr, NULL_NMBRSTRING);
-        break;
-      }
-
-      nmbrLet(&newSubProofPtr, NULL_NMBRSTRING);
-    } /* next step */
-
-    if (!foundFlag) break; /* Done */
-    /* 25-Jun-2014 nm */
-#define MAX_GROWTH_FACTOR 2
-    if (allowGrowthFlag && plen > MAX_GROWTH_FACTOR * startingPlen) {
-      /* 25-Jun-2014 nm */
-      /* This will prevent an infinite loop in some cases with ALLOW_GROWTH,
-         for example 'MINIMIZE_WITH idi/ALLOW_GROWTH' in 'PROVE a1i' */
-      print2("Suppressed excessive ALLOW_GROWTH growth.\n");
-      break; /* Too much growth */
-    }
-    /* break; */  /* For special replacement with same label: always break
-                      to prevent inf loop */
-  } /* end while */
-
-} /* minimizeProof */
-
-
-
-
-/* Initialize g_ProofInProgress.source of the step, and .target of all
-   hypotheses, to schemes using new dummy variables. */
-void initStep(long step)
-{
-  long stmt, reqHyps, pos, hyp, sym, reqVars, var, mlen;
-  nmbrString *reqHypPos = NULL_NMBRSTRING;
-  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated */
-
-  stmt = (g_ProofInProgress.proof)[step];
-  if (stmt < 0) {
-    if (stmt == -(long)'?') {
-      /* Initialize unknown step source to nothing */
-      nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])),
-          NULL_NMBRSTRING);
-    } else {
-/*E*/print2("step %ld stmt %ld\n",step,stmt);
-      bug(1809); /* Packed ("squished") proof not handled (yet?) */
-    }
-    return;
-  }
-  if (g_Statement[stmt].type == (char)e_ || g_Statement[stmt].type == (char)f_) {
-    /* A hypothesis -- initialize to the actual statement */
-    nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])),
-        g_Statement[stmt].mathString);
-    return;
-  }
-
-  /* It must be an assertion ($a or $p) */
-
-  /* Assign the assertion to .source */
-  nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])),
-      g_Statement[stmt].mathString);
-
-  /* Find the position in proof of all required hyps, and
-     assign them */
-  reqHyps = g_Statement[stmt].numReqHyp;
-  nmbrLet(&reqHypPos, nmbrSpace(reqHyps)); /* Preallocate */
-  pos = step - 1; /* Step with last hyp */
-  for (hyp = reqHyps - 1; hyp >= 0; hyp--) {
-    reqHypPos[hyp] = pos;
-    nmbrLet((nmbrString **)(&((g_ProofInProgress.target)[pos])),
-        g_Statement[g_Statement[stmt].reqHypList[hyp]].mathString);
-                                           /* Assign the hypothesis to target */
-    if (hyp > 0) { /* Don't care about subproof length for 1st hyp */
-      pos = pos - subproofLen(g_ProofInProgress.proof, pos);
-                                             /* Get to step with previous hyp */
-    }
-  }
-
-  /* Change the variables in the assertion and hypotheses to dummy variables */
-  reqVars = nmbrLen(g_Statement[stmt].reqVarList);
-  if (g_pipDummyVars + reqVars > g_dummyVars) {
-    /* Declare more dummy vars if necessary */
-    declareDummyVars(g_pipDummyVars + reqVars - g_dummyVars);
-  }
-  for (var = 0; var < reqVars; var++) {
-    /* Put dummy var mapping into g_MathToken[].tmp field */
-    g_MathToken[g_Statement[stmt].reqVarList[var]].tmp = g_mathTokens + 1 +
-      g_pipDummyVars + var;
-  }
-  /* Change vars in assertion */
-  nmbrTmpPtr = (g_ProofInProgress.source)[step];
-  mlen = nmbrLen(nmbrTmpPtr);
-  for (sym = 0; sym < mlen; sym++) {
-    if (g_MathToken[nmbrTmpPtr[sym]].tokenType == (char)var_) {
-      /* Use dummy var mapping from g_MathToken[].tmp field */
-      nmbrTmpPtr[sym] = g_MathToken[nmbrTmpPtr[sym]].tmp;
-    }
-  }
-  /* Change vars in hypotheses */
-  for (hyp = 0; hyp < reqHyps; hyp++) {
-    nmbrTmpPtr = (g_ProofInProgress.target)[reqHypPos[hyp]];
-    mlen = nmbrLen(nmbrTmpPtr);
-    for (sym = 0; sym < mlen; sym++) {
-      if (g_MathToken[nmbrTmpPtr[sym]].tokenType == (char)var_) {
-        /* Use dummy var mapping from g_MathToken[].tmp field */
-        nmbrTmpPtr[sym] = g_MathToken[nmbrTmpPtr[sym]].tmp;
-      }
-    }
-  }
-
-  /* Update the number of dummy vars used so far */
-  g_pipDummyVars = g_pipDummyVars + reqVars;
-
-  nmbrLet(&reqHypPos, NULL_NMBRSTRING); /* Deallocate */
-
-  return;
-} /* initStep */
-
-
-
-
-/* Look for completely known subproofs in g_ProofInProgress.proof and
-   assign g_ProofInProgress.target and .source.  Calls assignKnownSteps(). */
-void assignKnownSubProofs(void)
-{
-  long plen, pos, subplen, q;
-  flag breakFlag;
-
-  plen = nmbrLen(g_ProofInProgress.proof);
-  /* Scan proof for known subproofs (backwards, to get biggest ones first) */
-  for (pos = plen - 1; pos >= 0; pos--) {
-    subplen = subproofLen(g_ProofInProgress.proof, pos); /* Find length of subpr*/
-    breakFlag = 0;
-    for (q = pos - subplen + 1; q <= pos; q++) {
-      if ((g_ProofInProgress.proof)[q] == -(long)'?') {
-        breakFlag = 1;
-        break;
-      }
-    }
-    if (breakFlag) continue; /* Skip subproof - it has an unknown step */
-
-    /* See if all steps in subproof are assigned and known; if so, don't assign
-       them again. */
-    /* (???Add this code if needed for speedup) */
-
-    /* Assign the steps of the known subproof to g_ProofInProgress.target */
-    assignKnownSteps(pos - subplen + 1, subplen);
-
-    /* Adjust pos for next pass through 'for' loop */
-    pos = pos - subplen + 1;
-
-  } /* Next pos */
-  return;
-} /* assignKnownSubProofs */
-
-
-/* This function assigns math strings to all steps (g_ProofInProgress.target and
-   .source fields) in a subproof with all known steps. */
-void assignKnownSteps(long startStep, long sbProofLen)
-{
-
-  long stackPtr, st;
-  nmbrString *stack = NULL_NMBRSTRING;
-  nmbrString *instance = NULL_NMBRSTRING;
-  nmbrString *scheme = NULL_NMBRSTRING;
-  nmbrString *assertion = NULL_NMBRSTRING;
-  long pos, stmt, reqHyps, instLen, instPos, schemeLen, schemePos, hypLen,
-      hypPos, hyp, reqVars, var, assLen, assPos;
-  flag tmpFlag;
-  pntrString *stateVector = NULL_PNTRSTRING;
-
-  nmbrLet(&stack, nmbrSpace(sbProofLen));
-  stackPtr = 0;
-  for (pos = startStep; pos < startStep + sbProofLen; pos++) {
-    stmt = (g_ProofInProgress.proof)[pos];
-
-    if (stmt <= 0) {
-      if (stmt != -(long)'?') bug(1810);
-                                     /* Packed proofs are not handled (yet?) */
-      if (stmt == -(long)'?') bug(1830);
-                                    /* Unknown proofs are not handled (yet?) */
-    }
-
-    if (g_Statement[stmt].type == (char)e_ || g_Statement[stmt].type == (char)f_){
-      /* It's a hypothesis or unknown step; assign step; push the stack */
-      nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[pos])),
-          g_Statement[stmt].mathString);
-      stack[stackPtr] = pos;
-      stackPtr++;
-    } else {
-      /* It's an assertion. */
-
-      /* Assemble the hypotheses for unification */
-      reqHyps = g_Statement[stmt].numReqHyp;
-
-      instLen = 1; /* First "$|$" separator token */
-      for (st = stackPtr - reqHyps; st < stackPtr; st++) {
-        if (st < 0) bug(1850); /* Proof sent in may be corrupted */
-        /* Add 1 for "$|$" separator token */
-        instLen = instLen + nmbrLen((g_ProofInProgress.source)[stack[st]]) + 1;
-      }
-      /* Preallocate instance */
-      nmbrLet(&instance, nmbrSpace(instLen));
-      /* Assign instance */
-      instance[0] = g_mathTokens; /* "$|$" separator */
-      instPos = 1;
-      for (st = stackPtr - reqHyps; st < stackPtr; st++) {
-        hypLen = nmbrLen((g_ProofInProgress.source)[stack[st]]);
-        for (hypPos = 0; hypPos < hypLen; hypPos++) {
-          instance[instPos] =
-              ((nmbrString *)((g_ProofInProgress.source)[stack[st]]))[hypPos];
-          instPos++;
-        }
-        instance[instPos] = g_mathTokens; /* "$|$" separator */
-        instPos++;
-      }
-      if (instLen != instPos) bug(1811); /* ???Delete after debugging */
-
-      schemeLen = 1; /* First "$|$" separator token */
-      for (hyp = 0; hyp < reqHyps; hyp++) {
-        /* Add 1 for "$|$" separator token */
-        schemeLen = schemeLen +
-            g_Statement[g_Statement[stmt].reqHypList[hyp]].mathStringLen + 1;
-      }
-      /* Preallocate scheme */
-      nmbrLet(&scheme, nmbrSpace(schemeLen));
-      /* Assign scheme */
-      scheme[0] = g_mathTokens; /* "$|$" separator */
-      schemePos = 1;
-      for (hyp = 0; hyp < reqHyps; hyp++) {
-        hypLen = g_Statement[g_Statement[stmt].reqHypList[hyp]].mathStringLen;
-        for (hypPos = 0; hypPos < hypLen; hypPos++) {
-          scheme[schemePos] =
-              g_Statement[g_Statement[stmt].reqHypList[hyp]].mathString[hypPos];
-          schemePos++;
-        }
-        scheme[schemePos] = g_mathTokens; /* "$|$" separator */
-        schemePos++;
-      }
-      if (schemeLen != schemePos) bug(1812); /* ???Delete after debugging */
-
-      /* Change variables in scheme to dummy variables for unification */
-      reqVars = nmbrLen(g_Statement[stmt].reqVarList);
-      if (reqVars + g_pipDummyVars > g_dummyVars) {
-        /* Declare more dummy vars if necessary */
-        declareDummyVars(reqVars + g_pipDummyVars - g_dummyVars);
-      }
-      for (var = 0; var < reqVars; var++) {
-        /* Put dummy var mapping into g_MathToken[].tmp field */
-        g_MathToken[g_Statement[stmt].reqVarList[var]].tmp = g_mathTokens + 1 +
-          g_pipDummyVars + var;
-      }
-      for (schemePos = 0; schemePos < schemeLen; schemePos++) {
-        if (g_MathToken[scheme[schemePos]].tokenType
-            != (char)var_) continue;
-        /* Use dummy var mapping from g_MathToken[].tmp field */
-        scheme[schemePos] = g_MathToken[scheme[schemePos]].tmp;
-      }
-
-      /* Change variables in assertion to dummy variables for substitition */
-      nmbrLet(&assertion, g_Statement[stmt].mathString);
-      assLen = nmbrLen(assertion);
-      for (assPos = 0; assPos < assLen; assPos++) {
-        if (g_MathToken[assertion[assPos]].tokenType
-            != (char)var_) continue;
-        /* Use dummy var mapping from g_MathToken[].tmp field */
-        assertion[assPos] = g_MathToken[assertion[assPos]].tmp;
-      }
-
-      /* Unify scheme and instance */
-      g_unifTrialCount = 0; /* Reset unification to no timeout */
-      tmpFlag = unifyH(scheme, instance, &stateVector, 0);
-      if (!tmpFlag) {
-        /*bug(1813);*/ /* Not poss. */
-        /* Actually this is possible if the starting proof had an error
-           in it.  Give the user some information then give up */
-        printLongLine(cat("?Error in step ", str((double)pos + 1),
-            ":  Could not simultaneously unify the hypotheses of \"",
-            g_Statement[stmt].labelName, "\":\n    ",
-            nmbrCvtMToVString(scheme),
-            "\nwith the following statement list:\n    ",
-            nmbrCvtMToVString(instance),
-            "\n(The $|$ tokens are internal statement separation markers)",
-            "\nZapping targets so we can proceed (but you should exit the ",
-            "Proof Assistant and fix this problem)",
-            "\n(This may take a while; please wait...)",
-            NULL), "", " ");
-        purgeStateVector(&stateVector);
-        goto returnPoint;
-      }
-      /* Substitute and assign assertion to proof in progress */
-      nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[pos])), NULL_NMBRSTRING);
-      (g_ProofInProgress.source)[pos] = makeSubstUnif(&tmpFlag, assertion,
-          stateVector);
-      if (tmpFlag) bug(1814); /* All vars s.b. assigned */
-
-      /* Verify unification is unique; also deallocates stateVector */
-      if (unifyH(scheme, instance, &stateVector, 1)) bug(1815); /* Not unique */
-
-      /* Adjust stack */
-      stackPtr = stackPtr - reqHyps;
-      stack[stackPtr] = pos;
-      stackPtr++;
-
-    } /* End if (not) $e, $f */
-  } /* Next pos */
-
-  if (stackPtr != 1) bug(1816); /* Make sure stack emptied */
-
- returnPoint:
-  /* Assign .target field for all but last step */
-  for (pos = startStep; pos < startStep + sbProofLen - 1; pos++) {
-    nmbrLet((nmbrString **)(&((g_ProofInProgress.target)[pos])),
-        (g_ProofInProgress.source)[pos]);
-  }
-
-  /* Deallocate (stateVector was deallocated by 2nd unif. call) */
-  nmbrLet(&stack, NULL_NMBRSTRING);
-  nmbrLet(&instance, NULL_NMBRSTRING);
-  nmbrLet(&scheme, NULL_NMBRSTRING);
-  nmbrLet(&assertion, NULL_NMBRSTRING);
-  return;
-} /* assignKnownSteps */
-
-
-/* Interactively unify a step.  Calls interactiveUnify(). */
-/* If two unifications must take place (.target,.user and .source,.user),
-   then the user must invoke this command twice, as only one will be
-   done at a time.  ???Note in manual. */
-/* If messageFlag is 1, a message will be issued if the
-   step is already unified.   If messageFlag is 0, show the step #
-   being unified.  If messageFlag is 2, don't print step #, and do nothing
-   if step is already unified. */
-void interactiveUnifyStep(long step, char messageFlag)
-{
-  pntrString *stateVector = NULL_PNTRSTRING;
-  char unifFlag;
-
-  /* Target should never be empty */
-  if (!nmbrLen((g_ProofInProgress.target)[step])) bug (1817);
-
-  /* First, see if .target and .user should be unified */
-  /* If not, then see if .source and .user should be unified */
-  /* If not, then see if .target and .source should be unified */
-  if (nmbrLen((g_ProofInProgress.user)[step])) {
-    if (!nmbrEq((g_ProofInProgress.target)[step], (g_ProofInProgress.user)[step])) {
-      if (messageFlag == 0) print2("Step %ld:\n", step + 1);
-      unifFlag = interactiveUnify((g_ProofInProgress.target)[step],
-        (g_ProofInProgress.user)[step], &stateVector);
-      goto subAndReturn;
-    }
-    if (nmbrLen((g_ProofInProgress.source)[step])) {
-      if (!nmbrEq((g_ProofInProgress.source)[step], (g_ProofInProgress.user)[step])) {
-        if (messageFlag == 0) print2("Step %ld:\n", step + 1);
-        unifFlag = interactiveUnify((g_ProofInProgress.source)[step],
-          (g_ProofInProgress.user)[step], &stateVector);
-        goto subAndReturn;
-      }
-    }
-  } else {
-    if (nmbrLen((g_ProofInProgress.source)[step])) {
-      if (!nmbrEq((g_ProofInProgress.target)[step], (g_ProofInProgress.source)[step])) {
-        if (messageFlag == 0) print2("Step %ld:\n", step + 1);
-        unifFlag = interactiveUnify((g_ProofInProgress.target)[step],
-          (g_ProofInProgress.source)[step], &stateVector);
-        goto subAndReturn;
-      }
-    }
-  }
-
-  /* The step must already be unified */
-  if (messageFlag == 1) {
-    print2("?Step %ld is already unified.\n", step + 1);
-  }
-  unifFlag = 0; /* To skip subst. below */
-
- subAndReturn:
-  /* If the unification was successful, make substitutions everywhere
-     before returning */
-  if (unifFlag == 1) {
-
-    g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
-
-    makeSubstAll(stateVector);
-
-  } /* End if unifFlag = 1 */
-
-  purgeStateVector(&stateVector);
-
-  return;
-
-} /* interactiveUnifyStep */
-
-
-/* Interactively select one of several possible unifications */
-/* Returns:  0 = no unification possible
-             1 = unification was selected; held in stateVector
-             2 = unification timed out
-             3 = no unification was selected */
-char interactiveUnify(nmbrString *schemeA, nmbrString *schemeB,
-    pntrString **stateVector)
-{
-
-  long var, i;
-  long unifCount, unifNum;
-  char unifFlag;
-  flag reEntryFlag;
-  nmbrString *stackUnkVar; /* Pointer only - not allocated */
-  nmbrString *unifiedScheme; /* Pointer only - not allocated */
-  nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
-  nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
-  long stackTop;
-  vstring tmpStr = "";
-  nmbrString *nmbrTmp = NULL_NMBRSTRING;
-  char returnValue;
-
-  /* 8/14/99 - present unifications in increasing order of the number
-     of symbols in the unified result.  It seems that usually the unification
-     with the fewest symbols in the correct one. */
-  nmbrString *unifWeight = NULL_NMBRSTRING; /* Symbol count in unification */
-  long unifTrialWeight;
-  long maxUnifWeight;
-  long minUnifWeight;
-  long unifTrials;
-  long thisUnifWeight;
-  long onesCount;
-  nmbrString *substResult = NULL_NMBRSTRING;
-  long unkCount;
-
-  if (nmbrEq(schemeA, schemeB)) bug(1818); /* No reason to call this */
-
-  /* Count the number of possible unifications */
-  g_unifTrialCount = 1; /* Reset unification timeout */
-  unifCount = 0;
-  reEntryFlag = 0;
-  minUnifWeight = -1;
-  maxUnifWeight = 0;
-  while (1) {
-    unifFlag = unifyH(schemeA, schemeB, &(*stateVector), reEntryFlag);
-    if (unifFlag == 2) {
-      printLongLine(
-          cat("Unify:  ", nmbrCvtMToVString(schemeA), NULL), "    ", " ");
-      printLongLine(
-          cat(" with:  ", nmbrCvtMToVString(schemeB), NULL), "    ", " ");
-      print2(
-"The unification timed out.  Increase timeout (SET UNIFICATION_TIMEOUT) or\n");
-      print2(
-"assign some variables (LET VARIABLE) or the step (LET STEP) manually.\n");
-      /*return (2);*/
-      returnValue = 2;
-      goto returnPoint;
-    }
-    if (!unifFlag) break;
-    reEntryFlag = 1;
-
-
-    /* 8/14/99 Compute heuristic "weight" of resulting unification */
-    /* The unification with the least "weight" is intended to be the
-       most likely correct choice.  The heuristic was based on
-       empirical observations of typical unification sets */
-
-    stackTop = ((nmbrString *)((*stateVector)[11]))[1];
-    /* stackUnkVar = (nmbrString *)((*stateVector)[1]); */ /* 18-Sep-2013 -
-                                           This assignement is never used */
-    stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
-    stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
-    unifiedScheme = (nmbrString *)((*stateVector)[8]);
-
-    /* 8/15/99 - Heuristic */
-    thisUnifWeight = stackTop * 2;
-    onesCount = 0;
-    unkCount = 0;
-    for (var = 0; var <= stackTop; var++) {
-      /* 8/15/99 - Heuristic */
-      thisUnifWeight = thisUnifWeight + stackUnkVarLen[var];
-      /* 8/15/99 - Heuristic - Subtract for subst. of length 1 */
-      if (stackUnkVarLen[var] == 1) onesCount++;
-
-      /* Count the number of unknown variables in substitution result */
-      nmbrLet(&substResult, nmbrMid(unifiedScheme, stackUnkVarStart[var] + 1,
-              stackUnkVarLen[var]));
-      for (i = 0; i < nmbrLen(substResult); i++) {
-        if (substResult[i] > g_mathTokens) unkCount++;
-      }
-
-    } /* Next var */
-    thisUnifWeight = thisUnifWeight - onesCount;
-    thisUnifWeight = thisUnifWeight + 7 * unkCount;
-
-
-    /* Get new min and max weight for interactive scan ordering */
-    if (thisUnifWeight > maxUnifWeight) maxUnifWeight = thisUnifWeight;
-    if (thisUnifWeight < minUnifWeight || minUnifWeight == -1)
-      minUnifWeight = thisUnifWeight;
-
-    nmbrLet(&unifWeight, nmbrAddElement(unifWeight, 0));
-
-    unifWeight[unifCount] = thisUnifWeight;
-    unifCount++;
-    if (nmbrLen(unifWeight) != unifCount) bug(1827);
-  } /* while (1) */
-
-  if (!unifCount) {
-    printf("The unification is not possible.  The proof has an error.\n");
-    /*return(0);*/ /* Not possible */
-    returnValue = 0;
-    goto returnPoint;
-  }
-  if (unifCount > 1) {
-    printLongLine(cat("There are ", str((double)unifCount),
-      " possible unifications.  Please select the correct one or QUIT if",
-      " you want to UNIFY later.", NULL),
-        "    ", " ");
-    printLongLine(cat("Unify:  ", nmbrCvtMToVString(schemeA), NULL),
-        "    ", " ");
-    printLongLine(cat(" with:  ", nmbrCvtMToVString(schemeB), NULL),
-        "    ", " ");
-  }
-
-  /* 8/14/99 Scan possible unifications in order of increasing unified scheme
-     size.  This is not an optimal way to do it since the unification must
-     be completely redone for each trial size, but since it is interactive
-     the speed should be tolerable.  A faster method would be to save
-     the unifications and present them for selection in sorted order, but
-     this would require more code. */
-  unifTrials = 0;
-  for (unifTrialWeight = minUnifWeight; unifTrialWeight <= maxUnifWeight;
-      unifTrialWeight++) {
-
-    if (!nmbrElementIn(1, unifWeight, unifTrialWeight)) continue;
-
-    g_unifTrialCount = 1; /* Reset unification timeout */
-    reEntryFlag = 0;
-
-    for (unifNum = 1; unifNum <= unifCount; unifNum++) {
-      unifFlag = unifyH(schemeA, schemeB, &(*stateVector), reEntryFlag);
-      if (unifFlag != 1) bug(1819);
-
-      reEntryFlag = 1;
-      if (unifWeight[unifNum - 1] != unifTrialWeight) continue;
-
-      if (unifCount == 1) {
-        print2("Step was successfully unified.\n");
-        /*return(1);*/ /* Always accept unique unification */
-        returnValue = 1;
-        goto returnPoint;
-      }
-
-      unifTrials++;
-      print2("Unification #%ld of %ld (weight = %ld):\n",
-          unifTrials, unifCount, unifTrialWeight);
-
-      stackTop = ((nmbrString *)((*stateVector)[11]))[1];
-      stackUnkVar = (nmbrString *)((*stateVector)[1]);
-      stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
-      stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
-      unifiedScheme = (nmbrString *)((*stateVector)[8]);
-      for (var = 0; var <= stackTop; var++) {
-        printLongLine(cat("  Replace \"",
-          g_MathToken[stackUnkVar[var]].tokenName,"\" with \"",
-            nmbrCvtMToVString(
-                nmbrMid(unifiedScheme,stackUnkVarStart[var] + 1,
-                stackUnkVarLen[var])), "\"", NULL),"    "," ");
-        /* Clear temporary string allocation during print */
-        let(&tmpStr,"");
-        nmbrLet(&nmbrTmp,NULL_NMBRSTRING);
-      } /* Next var */
-
-      while(1) {
-        tmpStr = cmdInput1("  Accept (A), reject (R), or quit (Q) <A>? ");
-        if (!tmpStr[0]) {
-          /* Default value - accept */
-          returnValue = 1;
-          goto returnPoint;
-        }
-        if (tmpStr[0] == 'R' || tmpStr[0] == 'r') {
-          if (!tmpStr[1]) {
-            let(&tmpStr, "");
-            break;
-          }
-        }
-        if (tmpStr[0] == 'Q' || tmpStr[0] == 'q') {
-          if (!tmpStr[1]) {
-            /*return (3);*/
-            returnValue = 3;
-            goto returnPoint;
-          }
-        }
-        if (tmpStr[0] == 'A' || tmpStr[0] == 'a') {
-          if (!tmpStr[1]) {
-            /*return (1);*/
-            returnValue = 1;
-            goto returnPoint;
-          }
-        }
-        let(&tmpStr, "");
-      }
-
-    } /* Next unifNum */
-
-  } /* Next unifTrialWeight */
-
-  /* (The user should reject everything to test for this bug) */
-  if (unifTrials != unifCount) bug(1829);
-
-  /*return (3);*/  /* No unification was selected */
-  returnValue = 3;
-  goto returnPoint;
-
- returnPoint:
-  let(&tmpStr, ""); /* Deallocate */
-  nmbrLet(&unifWeight, NULL_NMBRSTRING); /* Deallocate */
-  nmbrLet(&substResult, NULL_NMBRSTRING); /* Deallocate */
-  return returnValue;
-
-} /* interactiveUnify */
-
-
-
-/* Automatically unify steps that have unique unification */
-/* Prints "congratulation" if congrats = 1 */
-void autoUnify(flag congrats)
-{
-  long step, plen;
-  char unifFlag;
-  /* flag stepChanged; */
-  flag somethingChanged = 1;
-  int pass;
-  nmbrString *schemeAPtr; /* Pointer only; not allocated */
-  nmbrString *schemeBPtr; /* Pointer only; not allocated */
-  pntrString *stateVector = NULL_PNTRSTRING;
-  flag somethingNotUnified = 0;
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  schemeAPtr = NULL_NMBRSTRING;
-  schemeBPtr = NULL_NMBRSTRING;
-
-  plen = nmbrLen(g_ProofInProgress.proof);
-
-  while (somethingChanged) {
-    somethingChanged = 0;
-    for (step = 0; step < plen; step++) {
-      /* stepChanged = 0; */
-
-      for (pass = 0; pass < 3; pass++) {
-
-        switch (pass) {
-          case 0:
-            /* Check target vs. user */
-            schemeAPtr = (g_ProofInProgress.target)[step];
-            if (!nmbrLen(schemeAPtr)) print2(
- "?Bad unification selected:  A proof step should never be completely empty\n");
-      /*if (!nmbrLen(schemeAPtr)) bug (1820);*/ /* Target can never be empty */
-            schemeBPtr = (g_ProofInProgress.user)[step];
-            break;
-          case 1:
-            /* Check source vs. user */
-            schemeAPtr = (g_ProofInProgress.source)[step];
-            schemeBPtr = (g_ProofInProgress.user)[step];
-            break;
-          case 2:
-            /* Check target vs. source */
-            schemeAPtr = (g_ProofInProgress.target)[step];
-            schemeBPtr = (g_ProofInProgress.source)[step];
-            break;
-        }
-        if (nmbrLen(schemeAPtr) && nmbrLen(schemeBPtr)) {
-          if (!nmbrEq(schemeAPtr, schemeBPtr)) {
-            g_unifTrialCount = 1; /* Reset unification timeout */
-            unifFlag = uniqueUnif(schemeAPtr, schemeBPtr, &stateVector);
-            if (unifFlag != 1) somethingNotUnified = 1;
-            if (unifFlag == 2) {
-              print2("A unification timeout occurred at step %ld.\n", step + 1);
-            }
-            if (!unifFlag) {
-              print2(
-              "Step %ld cannot be unified.  THERE IS AN ERROR IN THE PROOF.\n",
-                  (long)(step + 1));
-              /* 27-Sep-2010 nm No longer needed since this is now automatic
-              print2(
-"If your axioms allow empty substitutions, see HELP SET EMPTY_SUBSTITUTION.\n"
-                 );
-              */
-              continue;
-            }
-            if (unifFlag == 1) {
-              /* Make substitutions to all steps */
-              makeSubstAll(stateVector);
-              somethingChanged = 1;
-              g_proofChangedFlag = 1; /* Flag for undo stack */
-              /* 8-Apr-05 nm This message can be annoying. */
-              /*
-              print2("Step %ld was successfully unified.\n", (long)(step + 1));
-              */
-            }
-          }
-        }
-      } /* Next pass */
-    } /* Next step */
-  } /* End while somethingChanged */
-
-  purgeStateVector(&stateVector);
-
-  /* Check to see if proof is complete */
-  if (congrats) {
-    if (!somethingNotUnified) {
-      if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
-        print2(
-  "CONGRATULATIONS!  The proof is complete.  Use SAVE NEW_PROOF to save it.\n");
-        print2(
-  "Note:  The Proof Assistant does not detect $d violations.  After saving\n");
-        print2(
-  "the proof, you should verify it with VERIFY PROOF.\n");
-      }
-    }
-  }
-
-  return;
-
-} /* autoUnify */
-
-
-/* Make stateVector substitutions in all steps.  The stateVector must
-   contain the result of a valid unification. */
-void makeSubstAll(pntrString *stateVector) {
-
-  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated */
-  long plen, step;
-  flag tmpFlag;
-
-  plen = nmbrLen(g_ProofInProgress.proof);
-  for (step = 0; step < plen; step++) {
-
-    nmbrTmpPtr = (g_ProofInProgress.target)[step];
-    (g_ProofInProgress.target)[step] = makeSubstUnif(&tmpFlag, nmbrTmpPtr,
-      stateVector);
-    nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);
-
-    nmbrTmpPtr = (g_ProofInProgress.source)[step];
-    if (nmbrLen(nmbrTmpPtr)) {
-      (g_ProofInProgress.source)[step] = makeSubstUnif(&tmpFlag, nmbrTmpPtr,
-        stateVector);
-      nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);
-    }
-
-    nmbrTmpPtr = (g_ProofInProgress.user)[step];
-    if (nmbrLen(nmbrTmpPtr)) {
-      (g_ProofInProgress.user)[step] = makeSubstUnif(&tmpFlag, nmbrTmpPtr,
-        stateVector); /* 16-Apr-06 nm Fixed bug here */
-      nmbrLet(&nmbrTmpPtr, NULL_NMBRSTRING);
-    }
-
-  } /* Next step */
-  return;
-} /* makeSubstAll */
-
-/* Replace a dummy variable with a user-specified math string */
-void replaceDummyVar(long dummyVar, nmbrString *mString)
-{
-  long numSubs = 0;
-  long numSteps = 0;
-  long plen, step, sym, slen;
-  flag stepChanged;
-  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated */
-
-  plen = nmbrLen(g_ProofInProgress.proof);
-  for (step = 0; step < plen; step++) {
-
-    stepChanged = 0;
-
-    nmbrTmpPtr = (g_ProofInProgress.target)[step];
-    slen = nmbrLen(nmbrTmpPtr);
-    for (sym = slen - 1; sym >= 0; sym--) {
-      if (nmbrTmpPtr[sym] == dummyVar + g_mathTokens) {
-        nmbrLet((nmbrString **)(&((g_ProofInProgress.target)[step])),
-            nmbrCat(nmbrLeft(nmbrTmpPtr, sym), mString,
-            nmbrRight(nmbrTmpPtr, sym + 2), NULL));
-        nmbrTmpPtr = (g_ProofInProgress.target)[step];
-        stepChanged = 1;
-        numSubs++;
-      }
-    } /* Next sym */
-
-    nmbrTmpPtr = (g_ProofInProgress.source)[step];
-    slen = nmbrLen(nmbrTmpPtr);
-    for (sym = slen - 1; sym >= 0; sym--) {
-      if (nmbrTmpPtr[sym] == dummyVar + g_mathTokens) {
-        nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])),
-            nmbrCat(nmbrLeft(nmbrTmpPtr, sym), mString,
-            nmbrRight(nmbrTmpPtr, sym + 2), NULL));
-        nmbrTmpPtr = (g_ProofInProgress.source)[step];
-        stepChanged = 1;
-        numSubs++;
-      }
-    } /* Next sym */
-
-    nmbrTmpPtr = (g_ProofInProgress.user)[step];
-    slen = nmbrLen(nmbrTmpPtr);
-    for (sym = slen - 1; sym >= 0; sym--) {
-      if (nmbrTmpPtr[sym] == dummyVar + g_mathTokens) {
-        nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[step])),
-            nmbrCat(nmbrLeft(nmbrTmpPtr, sym), mString,
-            nmbrRight(nmbrTmpPtr, sym + 2), NULL));
-        nmbrTmpPtr = (g_ProofInProgress.user)[step];
-        stepChanged = 1;
-        numSubs++;
-      }
-    } /* Next sym */
-
-    if (stepChanged) numSteps++;
-  } /* Next step */
-
-  if (numSubs) {
-    g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
-    print2("%ld substitutions were made in %ld steps.\n", numSubs, numSteps);
-  } else {
-    print2("?The dummy variable $%ld is nowhere in the proof.\n", dummyVar);
-  }
-
-  return;
-} /* replaceDummyVar */
-
-/* Get subproof length of a proof, starting at endStep and going backwards.
-   Note that the first step is 0, the second is 1, etc. */
-long subproofLen(nmbrString *proof, long endStep)
-{
-  long stmt, p, lvl;
-  lvl = 1;
-  p = endStep + 1;
-  while (lvl) {
-    p--;
-    lvl--;
-    if (p < 0) bug(1821);
-    stmt = proof[p];
-    if (stmt < 0) { /* Unknown step or local label */
-      continue;
-    }
-    if (g_Statement[stmt].type == (char)e_ ||
-        g_Statement[stmt].type == (char)f_) { /* A hypothesis */
-      continue;
-    }
-    lvl = lvl + g_Statement[stmt].numReqHyp;
-  }
-  return (endStep - p + 1);
-} /* subproofLen */
-
-
-/* 25-Aug-2012 nm Added this function */
-/* If testStep has no dummy variables, return 0;
-   if testStep has isolated dummy variables (that don't affect rest of
-   proof), return 1;
-   if testStep has dummy variables used elsewhere in proof, return 2 */
-char checkDummyVarIsolation(long testStep) /* 0=1st step, 1=2nd, etc. */
-{
-  long proofLen, hyp, parentStep, tokpos, token;
-  char dummyVarIndicator;
-  long prfStep, parentStmt;
-  nmbrString *dummyVarList = NULL_NMBRSTRING;
-  flag bugCheckFlag;
-  char hypType;
-
-  /* Get list of dummy variables */
-  for (tokpos = 0; tokpos < nmbrLen((g_ProofInProgress.target)[testStep]);
-      tokpos++) {
-    token = ((nmbrString *)((g_ProofInProgress.target)[testStep]))[tokpos];
-    if (token > g_mathTokens/*global*/) {
-      if (!nmbrElementIn(1, dummyVarList, token)) {
-        nmbrLet(&dummyVarList, nmbrAddElement(dummyVarList, token));
-      }
-    }
-  }
-  if (nmbrLen(dummyVarList) == 0) {
-    dummyVarIndicator = 0; /* No dummy variables */
-    goto RETURN_POINT;
-  }
-  /* g_ProofInProgress is global */
-  proofLen = nmbrLen(g_ProofInProgress.proof);
-  if (testStep == proofLen - 1) {
-    dummyVarIndicator = 1; /* Dummy variables don't affect rest of proof
-       (ignoring the subproof of testStep, which would be replaced by
-       a replaceStatement success later on) */
-    goto RETURN_POINT;
-  }
-
-  parentStep = getParentStep(testStep); /* testStep is a hyp of parent step */
-
-  /* Check if parent step has the dummy vars - if not, they will not
-     occur outside of the subproof of the parent step */
-  for (tokpos = 0; tokpos < nmbrLen((g_ProofInProgress.target)[parentStep]);
-      tokpos++) {
-    token = ((nmbrString *)((g_ProofInProgress.target)[parentStep]))[tokpos];
-    if (token > g_mathTokens/*global*/) {
-      if (nmbrElementIn(1, dummyVarList, token)) {
-        /* One of testStep's dummy vars occurs in the parent, so it
-           could be used elsewhere and is thus not "isolated" */
-        dummyVarIndicator = 2;
-        goto RETURN_POINT;
-      }
-    }
-  }
-  /* Check all hypotheses of parentStep other than testStep - if none have
-     testStep's dummy vars, then the dummy vars are "isolated" */
-  parentStmt = (g_ProofInProgress.proof)[parentStep];
-  if (parentStmt < 0) bug(1845);
-  if (g_Statement[parentStmt].type != (char)a_ &&
-      g_Statement[parentStmt].type != (char)p_) bug(1846);
-  bugCheckFlag = 0;
-  prfStep = parentStep - 1;
-  for (hyp = g_Statement[parentStmt].numReqHyp - 1; hyp >= 0; hyp--) {
-    if (hyp < g_Statement[parentStmt].numReqHyp - 1) { /* Skip computation at
-                                       first loop iteration */
-      /* Skip to proof step of previous hypothesis of parent step */
-      prfStep = prfStep - subproofLen(g_ProofInProgress.proof, prfStep);
-    }
-    if (prfStep == testStep) { /* Don't check the hypothesis of testStep */
-      bugCheckFlag = 1; /* Make sure we encountered it during scan */
-      continue;
-    }
-    hypType = g_Statement[g_Statement[parentStmt].reqHypList[hyp]].type;
-    if (hypType == (char)e_) {
-      /* Check whether (other) $e hyps of parent step have the dummy vars
-         of testStep */
-      for (tokpos = 0; tokpos < nmbrLen((g_ProofInProgress.target)[prfStep]);
-          tokpos++) {
-        token = ((nmbrString *)((g_ProofInProgress.target)[prfStep]))[tokpos];
-        if (token > g_mathTokens/*global*/) {
-          if (nmbrElementIn(1, dummyVarList, token)) {
-            /* One of testStep's dummy vars occurs in the parent, so it
-               could be used elsewhere and is thus not "isolated" */
-            dummyVarIndicator = 2;
-            goto RETURN_POINT;
-          }
-        }
-      } /* next tokpos */
-    } else if (hypType != (char)f_) {
-      bug(1848);
-    }
-  } /* next hyp */
-  if (bugCheckFlag == 0) bug(1847); /* Scan didn't encounter testStep */
-  /* If we passed the whole scan, the dummy vars are "isolated" */
-  dummyVarIndicator = 1;
-
- RETURN_POINT:
-  nmbrLet(&dummyVarList, NULL_NMBRSTRING); /* Deallocate */
-  return dummyVarIndicator;
-} /* checkDummyVarIsolation */
-
-
-/* 25-Aug-2012 nm Added this function */
-/* Given a starting step, find its parent (the step it is a hypothesis of) */
-/* If the starting step is the last proof step, just return it */
-long getParentStep(long startStep) /* 0=1st step, 1=2nd, etc. */
-{
-  long proofLen;
-  long stackPtr, prfStep, stmt;
-
-  /* g_ProofInProgress is global */
-  proofLen = nmbrLen(g_ProofInProgress.proof);
-
-  stackPtr = 0;
-  for (prfStep = startStep + 1; prfStep < proofLen; prfStep++) {
-    stmt = (g_ProofInProgress.proof)[prfStep];
-    if (stmt < 0) { /* Unknown step or local label */
-      if (stmt != -(long)'?') bug(1842); /* We don't handle compact proofs */
-      stackPtr++;
-    } else if (g_Statement[stmt].type == (char)e_ ||
-          g_Statement[stmt].type == (char)f_) { /* A hypothesis */
-      stackPtr++;
-    } else {
-      if (g_Statement[stmt].type != (char)a_ &&
-          g_Statement[stmt].type != (char)p_) bug(1843);
-      stackPtr = stackPtr - g_Statement[stmt].numReqHyp + 1;
-      if (stackPtr <= 0) return prfStep; /* This identifies the parent step */
-    }
-  } /* next prfStep */
-  if (startStep != proofLen - 1) bug(1844); /* Didn't find parent... */
-  return startStep; /* ...unless we started with the last proof step */
-} /* getParentStep */
-
-
-/* This function puts numNewVars dummy variables, named "$nnn", at the end
-   of the g_MathToken array and modifies the global variable g_dummyVars. */
-/* Note:  The g_MathToken array will grow forever as this gets called;
-   it is never purged, as this might worsen memory fragmentation. */
-/* ???Should we add a purge function? */
-void declareDummyVars(long numNewVars)
-{
-
-  long i;
-
-  long saveTempAllocStack;
-  saveTempAllocStack = g_startTempAllocStack;
-  g_startTempAllocStack = g_tempAllocStackTop; /* For let() stack cleanup */
-
-  for (i = 0; i < numNewVars; i++) {
-
-    g_dummyVars++;
-    /* First, check to see if we need to allocate more g_MathToken memory */
-    if (g_mathTokens + 1 + g_dummyVars >= g_MAX_MATHTOKENS) {
-      /* The +1 above accounts for the dummy "$|$" boundary token */
-      /* Reallocate */
-      /* Add 1000 so we won't have to do this very often */
-      g_MAX_MATHTOKENS = g_MAX_MATHTOKENS + 1000;
-      g_MathToken = realloc(g_MathToken, (size_t)g_MAX_MATHTOKENS *
-        sizeof(struct mathToken_struct));
-      if (!g_MathToken) outOfMemory("#10 (mathToken)");
-    }
-
-    g_MathToken[g_mathTokens + g_dummyVars].tokenName = "";
-                                  /* Initialize vstring before let() */
-    let(&g_MathToken[g_mathTokens + g_dummyVars].tokenName,
-        cat("$", str((double)g_dummyVars), NULL));
-    g_MathToken[g_mathTokens + g_dummyVars].length =
-        (long)strlen(g_MathToken[g_mathTokens + g_dummyVars].tokenName);
-    g_MathToken[g_mathTokens + g_dummyVars].scope = g_currentScope;
-    g_MathToken[g_mathTokens + g_dummyVars].active = 1;
-    g_MathToken[g_mathTokens + g_dummyVars].tokenType = (char)var_;
-    g_MathToken[g_mathTokens + g_dummyVars].tmp = 0;
-
-  }
-
-  g_startTempAllocStack = saveTempAllocStack;
-
-  return;
-
-} /* declareDummyVars */
-
-
-
-/* 20-May-2013 nm Added this function */
-/* Copy inProofStruct to outProofStruct.  A proof structure contains
-   the state of the proof in the Proof Assistant MM-PA.  The one
-   used by MM-PA is the global g_ProofInProgress.  This function lets
-   it be copied for temporary storage and retrieval. */
-void copyProofStruct(struct pip_struct *outProofStruct,
-    struct pip_struct inProofStruct)
-{
-  long proofLen, j;
-  /* First, make sure the output structure is empty to prevent memory
-     leaks. */
-  deallocProofStruct(&(*outProofStruct));
-
-  /* Get the proof length of the input structure */
-  proofLen = nmbrLen(inProofStruct.proof);
-  if (proofLen == 0) bug(1854); /* An empty proof should never occur
-    here; proof should have at least one step (possibly unknown) */
-  if (proofLen == 0) return;  /* The input proof is empty */
-  nmbrLet(&((*outProofStruct).proof), inProofStruct.proof);
-
-  /* Allocate pointers to empty nmbrStrings that will be assigned
-     the proof step contents */
-  pntrLet(&((*outProofStruct).target), pntrNSpace(proofLen));
-  pntrLet(&((*outProofStruct).source), pntrNSpace(proofLen));
-  pntrLet(&((*outProofStruct).user), pntrNSpace(proofLen));
-
-  if (proofLen != pntrLen(inProofStruct.target)) bug(1855);
-  if (proofLen != pntrLen(inProofStruct.source)) bug(1856);
-  if (proofLen != pntrLen(inProofStruct.user)) bug(1857);
-  /* Copy the individual proof step contents */
-  for (j = 0; j < proofLen; j++) {
-    nmbrLet((nmbrString **)(&(((*outProofStruct).target)[j])),
-        (inProofStruct.target)[j]);
-    nmbrLet((nmbrString **)(&(((*outProofStruct).source)[j])),
-        (inProofStruct.source)[j]);
-    nmbrLet((nmbrString **)(&(((*outProofStruct).user)[j])),
-        (inProofStruct.user)[j]);
-  }
-  return;
-} /* copyProofStruct */
-
-
-/* 11-Sep-2016 nm Added this function */
-/* Create an initial proof structure needed for the Proof Assistant, given
-   a starting proof.  Normally, proofStruct is the global g_ProofInProgress,
-   although we've made it an argument to help modularize the function.  There
-   are still globals such as g_pipDummyVars, updated by various functions. */
-void initProofStruct(struct pip_struct *proofStruct, nmbrString *proof,
-    long proveStmt)
-{
-  nmbrString *tmpProof = NULL_NMBRSTRING;
-  long plen, step;
-  /* Right now, only non-packed proofs are handled. */
-  nmbrLet(&tmpProof, nmbrUnsquishProof(proof));
-
-  /* Assign initial proof structure */
-  if (nmbrLen((*proofStruct).proof)) bug(1876); /* Should've been deall.*/
-  nmbrLet(&((*proofStruct).proof), tmpProof);
-  plen = nmbrLen((*proofStruct).proof);
-  pntrLet(&((*proofStruct).target), pntrNSpace(plen));
-  pntrLet(&((*proofStruct).source), pntrNSpace(plen));
-  pntrLet(&((*proofStruct).user), pntrNSpace(plen));
-  nmbrLet((nmbrString **)(&(((*proofStruct).target)[plen - 1])),
-      g_Statement[proveStmt].mathString);
-  g_pipDummyVars = 0; /* (Global) number of dummy (work) $nn variables, updated
-        by function calls below */
-  /* Assign known subproofs */
-  assignKnownSubProofs();
-  /* Initialize remaining steps */
-  for (step = 0; step < plen/*proof length*/; step++) {
-    if (!nmbrLen(((*proofStruct).source)[step])) {
-      initStep(step);
-    }
-  }
-  /* Unify whatever can be unified */
-  autoUnify(0); /* 0 means no "congrats" message */
-
-  /* Deallocate memory */
-  nmbrLet(&tmpProof, NULL_NMBRSTRING);
-  return;
-} /* initProofStruct */
-
-
-/* 20-May-2013 nm Added this function */
-/* Deallocate memory used by a proof structure and set it to the initial
-   state.  A proof structure contains the state of the proof in the Proof
-   Assistant MM-PA.  It is assumed that proofStruct was declared with:
-     struct pip_struct proofStruct = {
-       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
-   This function sets it back to that initial assignment. */
-void deallocProofStruct(struct pip_struct *proofStruct)
-{
-  long proofLen, j;
-  /* Deallocate proof structure */
-  proofLen = nmbrLen((*proofStruct).proof);
-  if (proofLen == 0) return;  /* Already deallocated */
-  nmbrLet(&((*proofStruct).proof), NULL_NMBRSTRING);
-  for (j = 0; j < proofLen; j++) {
-    nmbrLet((nmbrString **)(&(((*proofStruct).target)[j])), NULL_NMBRSTRING);
-    nmbrLet((nmbrString **)(&(((*proofStruct).source)[j])), NULL_NMBRSTRING);
-    nmbrLet((nmbrString **)(&(((*proofStruct).user)[j])), NULL_NMBRSTRING);
-  }
-  pntrLet(&((*proofStruct).target), NULL_PNTRSTRING);
-  pntrLet(&((*proofStruct).source), NULL_PNTRSTRING);
-  pntrLet(&((*proofStruct).user), NULL_PNTRSTRING);
-  return;
-} /* deallocProofStruct */
-
-
-/* 1-Nov-2013 nm Added this function */
-#define DEFAULT_UNDO_STACK_SIZE 20
-/* This function handles the UNDO/REDO commands.  It is called
-   with action PUS_INIT then with PUS_PUSH upon entering MM-PA.  It is
-   called with PUS_INIT upon exiting MM-PA.  It should be called with
-   PUS_PUSH after every command changing the proof.
-
-   PUS_UNDO and PUS_REDO are called by the UNDO and REDO CLI commands.
-
-   PUS_NEW_SIZE is called by the SET UNDO command to change the size
-   of the undo stack.  SET UNDO may be called outside or inside MM-PA;
-   in the latter case, the current UNDO stack is aborted (discarded).
-   If inside of MM-PA, PUS_PUSH must be called after PUS_NEW_SIZE.
-
-   PUS_GET_SIZE does not affect the stack; it returns the maximum UNDOs
-   PUS_GET_STATUS does not affect the stack; it returns 0 if it is
-     safe to exit MM-PA without saving (assuming there was no SAVE NEW_PROOF
-     while the UNDO stack was not empty).
-
-   Inputs:
-   proofStruct - must be current proof in progress (&g_ProofInProgress)
-       for PUS_PUSH, PUS_UNDO, and PUS_REDO actions; may be NULL for
-       other actions
-   action - the action the function should perform
-   info - description of command which will be reversed by UNDO; required
-       for all PUS_PUSH actions (except the first upon entering MM-PA that
-       loads the starting proof structure).  It is ignored for all other
-       actions and may be the empty string.
-   newSize - for PUS_NEW_SIZE, the new size (>= 0).
-
-   Return value = see PUS_GET_SIZE and PUS_GET_STATUS above
- */
-long processUndoStack(struct pip_struct *proofStruct,
-    char action,  /* PUS_INIT 1 Deallocates and initializes undo stack
-                     PUS_PUSH 2 Pushes the current proof state onto the stack
-                     PUS_UNDO 3 Restores the previous proof state
-                     PUS_REDO 4 Reverses PUS_UNDO
-                     PUS_NEW_SIZE 5 Changes size of stack
-                     PUS_GET_SIZE 6 Returns stack size
-                     PUS_GET_STATUS 7 Returns proof changed status */
-    vstring info, /* Info to print upon PUS_UNDO or PUS_REDO */
-    long newSize) /* New maximum number of UNDOs for PUS_NEW_SIZE */
-{
-
-  static struct pip_struct *proofStack = NULL;
-  static pntrString *infoStack = NULL_PNTRSTRING; /* UNDO/REDO command info */
-  static long stackSize = DEFAULT_UNDO_STACK_SIZE; /* Change w/ SET UNDO */
-  static long stackEnd = -1;
-  static long stackPtr = -1;
-  static flag firstTime = 1;
-  static flag stackOverflowed = 0; /* For user msg and prf chg determination */
-  static flag stackAborted = 0;  /* For proof changed determination */
-  long i;
-
-  if (stackPtr < -1 || stackPtr > stackEnd || stackPtr > stackSize - 1
-      || stackEnd < -1 || stackEnd > stackSize -1 ) {
-    bug(1858);
-  }
-
-  if (firstTime == 1) { /* First time ever called */
-    firstTime = 0;
-    proofStack = malloc((size_t)(stackSize) * sizeof(struct pip_struct));
-    if (!proofStack) bug(1859);
-    for (i = 0; i < stackSize; i++) { /* Set to empty proofs */
-      proofStack[i].proof = NULL_NMBRSTRING;
-      proofStack[i].target = NULL_PNTRSTRING;
-      proofStack[i].source = NULL_PNTRSTRING;
-      proofStack[i].user = NULL_PNTRSTRING;
-    }
-    pntrLet(&infoStack, pntrSpace(stackSize)); /* Set to empty vstrings */
-  }
-
-  if (!proofStack) bug(1860);
-
-  switch (action) {
-    case PUS_GET_SIZE:
-    case PUS_GET_STATUS:
-      break;  /* Do nothing; just return stack size */
-
-    case PUS_INIT:
-    case PUS_NEW_SIZE:
-      /* Deallocate old contents */
-      for (i = 0; i <= stackEnd; i++) {
-        deallocProofStruct(&(proofStack[i]));
-        let((vstring *)(&(infoStack[i])), "");
-      }
-
-      /* If UNDOs weren't exhausted and thus are abandoned due to size change,
-         this flag will prevent the program from falsely thinking the proof
-         hasn't changed */
-      if (action == PUS_NEW_SIZE) {
-        if (stackPtr > 0) {
-          print2("The previous UNDOs are no longer available.\n");
-          stackAborted = 1;
-        }
-        /* Since we're going to reset stackOverflowed, save its state
-           in stackAborted so we don't falsely exit MM-PA without saving */
-        if (stackOverflowed) stackAborted = 1;
-      }
-
-      stackEnd = -1; /* Nothing in UNDO stack now */
-      stackPtr = -1;
-      stackOverflowed = 0;
-
-      if (action == PUS_INIT) {
-        stackAborted = 0;
-        break;
-      }
-
-      /* Re-size the stack */
-      /* Free the old stack (pntrLet() below will free old infoStack) */
-      free(proofStack);
-      /* Reinitialize new stack */
-      stackSize = newSize + 1;
-      if (stackSize < 1) bug(1867);
-      proofStack = malloc((size_t)(stackSize) * sizeof(struct pip_struct));
-      if (!proofStack) bug(1861);
-      for (i = 0; i < stackSize; i++) { /* Set to empty proofs */
-        proofStack[i].proof = NULL_NMBRSTRING;
-        proofStack[i].target = NULL_PNTRSTRING;
-        proofStack[i].source = NULL_PNTRSTRING;
-        proofStack[i].user = NULL_PNTRSTRING;
-      }
-      pntrLet(&infoStack, pntrSpace(stackSize)); /* Set to empty vstrings */
-      break;
-
-    case PUS_PUSH:
-      /* Warning: PUS_PUSH must be called upon entering Proof Assistant to put
-         the original proof into stack locaton 0.  It also must be
-         called after PUS_NEW_SIZE if inside of MM-PA. */
-
-      /* Any new command after UNDO should erase the REDO part */
-      if (stackPtr < stackEnd) {
-        for (i = stackPtr + 1; i <= stackEnd; i++) {
-          deallocProofStruct(&(proofStack[i]));
-          let((vstring *)(&(infoStack[i])), "");
-        }
-        stackEnd = stackPtr;
-      }
-
-      /* If the stack is full, deallocate bottom of stack and move things
-         down to make room for new stack entry */
-      if (stackPtr == stackSize - 1) {
-        stackOverflowed = 1; /* To  modify user message if UNDO exhausted */
-        deallocProofStruct(&(proofStack[0])); /* Deallocate the bottom entry */
-        let((vstring *)(&(infoStack[0])), "");
-        for (i = 0; i < stackSize - 1; i++) {
-          /* Instead of
-               "copyProofStruct(&(proofStack[i]), proofStack[i + 1]);
-            (which involves de/reallocation), copy the pointers directly
-            for improved speed */
-          proofStack[i].proof = proofStack[i + 1].proof;
-          proofStack[i].target = proofStack[i + 1].target;
-          proofStack[i].source = proofStack[i + 1].source;
-          proofStack[i].user = proofStack[i + 1].user;
-          infoStack[i] = infoStack[i + 1];
-        }
-        /* Now initialize the top of the stack pointers (don't deallocate since
-           its old contents are pointed to by the next one down) */
-        proofStack[stackPtr].proof = NULL_NMBRSTRING;
-        proofStack[stackPtr].target = NULL_PNTRSTRING;
-        proofStack[stackPtr].source = NULL_PNTRSTRING;
-        proofStack[stackPtr].user = NULL_PNTRSTRING;
-        infoStack[stackPtr] = "";
-        stackPtr--;
-        stackEnd--;
-        if (stackPtr != stackSize - 2 || stackPtr != stackEnd) bug(1862);
-      }
-
-      /* Add the new command to the stack */
-      stackPtr++;
-      stackEnd++;
-      if (stackPtr != stackEnd) bug(1863);
-      copyProofStruct(&(proofStack[stackPtr]), *proofStruct);
-      let((vstring *)(&(infoStack[stackPtr])), info);
-      break;
-
-    case PUS_UNDO:
-      if (stackPtr < 0) bug(1864); /* A first PUSH wasn't called upon entry to
-                   Proof Assistant (MM-PA) */
-      if (stackPtr == 0) {
-        if (stackOverflowed == 0) {
-          print2("There is nothing to undo.\n");
-        } else {
-          printLongLine(cat("Exceeded maximum of ", str((double)stackSize - 1),
-              " UNDOs.  To increase the number, see HELP SET UNDO.",
-              NULL), "", " ");
-        }
-        break;
-      }
-
-      /* Print the Undid message for the most recent action */
-      printLongLine(cat("Undid:  ", infoStack[stackPtr],
-              NULL), "", " ");
-      stackPtr--;
-      /* Restore the version of the proof before that action */
-      copyProofStruct(&(*proofStruct), proofStack[stackPtr]);
-      break;
-
-    case PUS_REDO:
-      if (stackPtr == stackEnd) {
-        print2("There is nothing more to redo.\n");
-        break;
-      }
-
-      /* Move up stack pointer and return its entry. */
-      stackPtr++;
-      /* Restore the last undo and print the message for its action */
-      copyProofStruct(&(*proofStruct), proofStack[stackPtr]);
-      printLongLine(cat("Redid:  ", infoStack[stackPtr],
-              NULL), "", " ");
-      break;
-
-    default:
-      bug(1865);
-  } /* end switch(action) */
-
-  if (stackPtr < -1 || stackPtr > stackEnd || stackPtr > stackSize - 1
-      || stackEnd < -1 || stackEnd > stackSize -1 ) {
-    bug(1866);
-  }
-
-  if (action == PUS_GET_STATUS) {
-    /* Return the OR of all conditions which might indicate that the
-       proof has changed, so that it may not be safe to exit MM-PA without
-       a warning to save the proof */
-    return (stackOverflowed || stackAborted || stackPtr != 0);
-  } else {
-    return stackSize - 1;
-  }
-} /* processUndoStack */
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* mmpfas.c - Proof assistant module */
+
+#include <string.h>
+#include <stdlib.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mminou.h"
+#include "mmpars.h"
+#include "mmunif.h"
+#include "mmpfas.h"
+#include "mmwtex.h"
+
+/* Allow user to define INLINE as "inline".  lcc doesn't support inline. */
+#ifndef INLINE
+#define INLINE
+#endif
+
+long g_proveStatement = 0; /* The statement to be proved - global */
+flag g_proofChangedFlag; /* Flag to push 'undo' stack - global */
+
+/* 4-Aug-2011 nm Changed from 25000 to 50000 */
+/* 11-Dec-2010 nm Changed from 10000 to 25000 to accommodate df-plig in set.mm
+   (which needs >= 23884 to generate with 'show statement / html'). */
+/* g_userMaxProveFloat can be overridden by user with SET SEARCH_LIMIT */
+long g_userMaxProveFloat = 50000; /* Upper limit for proveFloating */
+
+long g_dummyVars = 0; /* Total number of dummy variables declared */
+long g_pipDummyVars = 0; /* Number of dummy variables used by proof in progress */
+
+/* Structure for holding a proof in progress. */
+/* This structure should be deallocated after use. */
+struct pip_struct g_ProofInProgress = {
+    NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
+
+/* Interactively select statement assignments that match */
+/* maxEssential is the maximum number of essential hypotheses that a
+   statement may have in order to be included in the matched list.
+   If -1, there is no limit. */
+void interactiveMatch(long step, long maxEssential)
+{
+  long matchCount = 0;
+  long timeoutCount = 0;
+  long essHypCount, hyp;
+  vstring_def(matchFlags);
+  vstring_def(timeoutFlags);
+  char unifFlag;
+  vstring_def(tmpStr1);
+  vstring_def(tmpStr4);
+  vstring_def(tmpStr2);
+  vstring_def(tmpStr3);
+  nmbrString_def(matchList);
+  nmbrString_def(timeoutList);
+  long stmt, matchListPos, timeoutListPos;
+
+  printLongLine(cat("Step ", str((double)step + 1), ":  ", nmbrCvtMToVString(
+      (g_ProofInProgress.target)[step]), NULL), "  ", " ");
+  if (nmbrLen((g_ProofInProgress.user)[step])) {
+    printLongLine(cat("Step ", str((double)step + 1), "(user):  ", nmbrCvtMToVString(
+        (g_ProofInProgress.user)[step]), NULL), "  ", " ");
+  }
+  /* Allocate a flag for each step to be tested */
+  /* 1 means no match, 2 means match */
+  let(&matchFlags, string(g_proveStatement, 1));
+  /* 1 means no timeout, 2 means timeout */
+  let(&timeoutFlags, string(g_proveStatement, 1));
+  for (stmt = 1; stmt < g_proveStatement; stmt++) {
+    if (g_Statement[stmt].type != (char)e_ &&
+        g_Statement[stmt].type != (char)f_ &&
+        g_Statement[stmt].type != (char)a_ &&
+        g_Statement[stmt].type != (char)p_) continue;
+
+    /* See if the maximum number of requested essential hypotheses is
+       exceeded */
+    if (maxEssential != -1) {
+      essHypCount = 0;
+      for (hyp = 0; hyp < g_Statement[stmt].numReqHyp; hyp++) {
+        if (g_Statement[g_Statement[stmt].reqHypList[hyp]].type == (char)e_) {
+          essHypCount++;
+          if (essHypCount > maxEssential) break;
+        }
+      }
+      if (essHypCount > maxEssential) continue;
+    }
+
+    unifFlag = checkStmtMatch(stmt, step);
+    if (unifFlag) {
+      if (unifFlag == 1) {
+        matchFlags[stmt] = 2;
+        matchCount++;
+      } else { /* unifFlag = 2 */
+        timeoutFlags[stmt] = 2;
+        timeoutCount++;
+      }
+    }
+  }
+
+  if (matchCount == 0 && timeoutCount == 0) {
+    print2("No statements match step %ld.  The proof has an error.\n",
+        (long)(step + 1));
+    free_vstring(matchFlags);
+    free_vstring(timeoutFlags);
+    return;
+  }
+
+#define MATCH_LIMIT 100
+  if (matchCount > MATCH_LIMIT) {
+    let(&tmpStr1, cat("There are ", str((double)matchCount), " matches for step ",
+      str((double)step + 1), ".  View them (Y, N) <N>? ", NULL));
+    tmpStr2 = cmdInput1(tmpStr1);
+    free_vstring(tmpStr1);
+
+    if (tmpStr2[0] != 'Y' && tmpStr2[0] != 'y') {
+      free_vstring(tmpStr2);
+      free_vstring(matchFlags);
+      free_vstring(timeoutFlags);
+      return;
+    }
+
+  }
+
+  nmbrLet(&matchList, nmbrSpace(matchCount));
+  matchListPos = 0;
+  for (stmt = 1; stmt < g_proveStatement; stmt++) {
+    if (matchFlags[stmt] == 2) {
+      matchList[matchListPos] = stmt;
+      matchListPos++;
+    }
+  }
+
+  nmbrLet(&timeoutList, nmbrSpace(timeoutCount));
+  timeoutListPos = 0;
+  for (stmt = 1; stmt < g_proveStatement; stmt++) {
+    if (timeoutFlags[stmt] == 2) {
+      timeoutList[timeoutListPos] = stmt;
+      timeoutListPos++;
+    }
+  }
+
+  let(&tmpStr1, nmbrCvtRToVString(matchList,
+                0, /*explicitTargets*/
+                0 /*statemNum, used only if explicitTargets*/));
+  let(&tmpStr4, nmbrCvtRToVString(timeoutList,
+                0, /*explicitTargets*/
+                0 /*statemNum, used only if explicitTargets*/));
+
+  printLongLine(cat("Step ", str((double)step + 1), " matches statements:  ", tmpStr1,
+      NULL), "  ", " ");
+  if (timeoutCount) {
+    printLongLine(cat("In addition, there were unification timeouts with the",
+        " following steps, which may or may not match:  ", tmpStr4, NULL),
+        "  ", " ");
+  }
+
+  if (matchCount == 1 && timeoutCount == 0 && maxEssential == -1) {
+    /* Assign it automatically */
+    matchListPos = 0;
+    stmt = matchList[matchListPos];
+    print2("Step %ld was assigned statement %s since it is the only match.\n",
+        (long)(step + 1),
+        g_Statement[stmt].labelName);
+  } else {
+
+    while (1) {
+      let(&tmpStr3, cat("What statement to select for step ", str((double)step + 1),
+          " (<return> to bypass)? ", NULL));
+      tmpStr2 = cmdInput1(tmpStr3);
+      free_vstring(tmpStr3);
+
+      if (tmpStr2[0] == 0) {
+        free_vstring(tmpStr1);
+        free_vstring(tmpStr4);
+        free_vstring(tmpStr2);
+        free_vstring(matchFlags);
+        free_vstring(timeoutFlags);
+        free_nmbrString(matchList);
+        free_nmbrString(timeoutList);
+        return;
+      }
+      if (!instr(1, cat(" ", tmpStr1, " ", tmpStr4, " ", NULL),
+           cat(" ", tmpStr2, " ", NULL))) {
+        print2("\"%s\" is not one of the choices.  Try again.\n", tmpStr2);
+      } else {
+        break;
+      }
+    }
+
+    for (matchListPos = 0; matchListPos < matchCount; matchListPos++) {
+      if (!strcmp(tmpStr2, g_Statement[matchList[matchListPos]].labelName)) break;
+    }
+    if (matchListPos < matchCount) {
+      stmt = matchList[matchListPos];
+    } else {
+      for (timeoutListPos = 0; timeoutListPos < timeoutCount;
+          timeoutListPos++) {
+      if (!strcmp(tmpStr2, g_Statement[timeoutList[timeoutListPos]].labelName))
+          break;
+      } /* Next timeoutListPos */
+      if (timeoutListPos == timeoutCount) bug(1801);
+      stmt = timeoutList[timeoutListPos];
+    }
+    print2("Step %ld was assigned statement %s.\n",
+        (long)(step + 1),
+        g_Statement[stmt].labelName);
+
+  } /* End if matchCount == 1 */
+
+  /* Add to statement to the proof */
+  assignStatement(matchList[matchListPos], step);
+  g_proofChangedFlag = 1; /* Flag for 'undo' stack */
+
+  free_vstring(tmpStr1);
+  free_vstring(tmpStr4);
+  free_vstring(tmpStr2);
+  free_vstring(matchFlags);
+  free_vstring(timeoutFlags);
+  free_nmbrString(matchList);
+  free_nmbrString(timeoutList);
+  return;
+
+} /* interactiveMatch */
+
+
+
+/* Assign a statement to an unknown proof step */
+void assignStatement(long statemNum, long step)
+{
+  long hyp;
+  nmbrString_def(hypList);
+
+  if ((g_ProofInProgress.proof)[step] != -(long)'?') bug(1802);
+
+  /* Add the statement to the proof */
+  nmbrLet(&hypList, nmbrSpace(g_Statement[statemNum].numReqHyp + 1));
+  for (hyp = 0; hyp < g_Statement[statemNum].numReqHyp; hyp++) {
+    /* A hypothesis of the added statement */
+    hypList[hyp] = -(long)'?';
+  }
+  hypList[g_Statement[statemNum].numReqHyp] = statemNum; /* The added statement */
+  addSubProof(hypList, step);
+  initStep(step + g_Statement[statemNum].numReqHyp);
+  free_nmbrString(hypList);
+  return;
+} /* assignStatement */
+
+
+
+/* Find proof of formula by using the replaceStatement() algorithm i.e.
+   see if any statement matches current step AND each of its hypotheses
+   matches a proof in progress hypothesis or some step already in the proof.
+   If a proof is found, it is returned, otherwise an empty (length 0) proof is
+   returned. */
+/* The caller must deallocate the returned nmbrString. */
+nmbrString *proveByReplacement(long prfStmt,
+    long prfStep, /* 0 means step 1 */
+    flag noDistinct, /* 1 means don't try statements with $d's */
+    flag dummyVarFlag, /* 0 means no dummy vars are in prfStmt */
+    flag searchMethod, /* 1 means to try proveFloating on $e's also */
+    long improveDepth, /* depth for proveFloating() */
+    flag overrideFlag, /* 1 means to override usage locks */
+    flag mathboxFlag
+    )
+{
+
+  long trialStmt;
+  nmbrString *prfMath;
+  nmbrString_def(trialPrf);
+  long prfMbox;
+
+  prfMath = (g_ProofInProgress.target)[prfStep];
+  prfMbox = getMathboxNum(prfStmt);
+  for (trialStmt = 1; trialStmt < prfStmt; trialStmt++) {
+
+    if (quickMatchFilter(trialStmt, prfMath, dummyVarFlag) == 0) continue;
+
+    /* Skip statements with discouraged usage (the above skips non-$a,p) */
+    if (overrideFlag == 0 && getMarkupFlag(trialStmt, USAGE_DISCOURAGED)) {
+      continue;
+    }
+
+    /* Skip statements in other mathboxes unless /INCLUDE_MATHBOXES.  (We don't
+       care about the first mathbox since there are no others above it.) */
+    if (mathboxFlag == 0 && prfMbox >= 2) {
+      /* Note that g_mathboxStart[] starts a 0 */
+      if (trialStmt > g_mathboxStmt && trialStmt < g_mathboxStart[prfMbox - 1]) {
+        continue;
+      }
+    }
+
+    /* noDistinct is set by NO_DISTINCT qualifier in IMPROVE */
+    if (noDistinct) {
+      /* Skip the statement if it has a $d requirement.  This option
+         prevents illegal proofs that would violate $d requirements
+         since the Proof Assistant does not check for $d violations. */
+      if (nmbrLen(g_Statement[trialStmt].reqDisjVarsA)) {
+        continue;
+      }
+    }
+
+    trialPrf = replaceStatement(trialStmt, prfStep,
+        prfStmt, 0,/*scan whole proof to maximize chance of a match*/
+        noDistinct,
+        searchMethod,
+        improveDepth,
+        overrideFlag,
+        mathboxFlag /* 1 means allow mathboxes */
+        );
+    if (nmbrLen(trialPrf) > 0) {
+      /* A proof for the step was found. */
+
+      /* Inform user that we're using a statement with discouraged usage */
+      if (overrideFlag == 1 && getMarkupFlag(trialStmt, USAGE_DISCOURAGED)) {
+        /* print2("\n"); */ /* Enable for more emphasis */
+        print2(
+          ">>> ?Warning: Overriding discouraged usage of statement \"%s\".\n",
+            g_Statement[trialStmt].labelName);
+        /* print2("\n"); */ /* Enable for more emphasis */
+      }
+
+      return trialPrf;
+    }
+    /* Don't need to do this because it is already null */
+    /* free_nmbrString(trialPrf); */
+  }
+  return trialPrf;  /* Proof not found - return empty proof */
+}
+
+
+nmbrString *replaceStatement(long replStatemNum, long prfStep,
+    long provStmtNum,
+    flag subProofFlag, /* If 1, then scan only subproof at prfStep to look for
+   matches, instead of whole proof, for faster speed (used by MINIMIZE_WITH) */
+    flag noDistinct, /* 1 means proveFloating won't try statements with $d's */
+    flag searchMethod, /* 1 means to try proveFloating on $e's also */
+    long improveDepth, /* Depth for proveFloating */
+    flag overrideFlag,  /* 1 means to override statement usage locks */
+    flag mathboxFlag /* 1 means allow mathboxes */
+    ) {
+  nmbrString *prfMath; /* Pointer only */
+  long reqHyps;
+  long hyp, sym, var, i, j, k, trialStep;
+  nmbrString_def(proof);
+  nmbrString_def(scheme);
+  pntrString_def(hypList);
+  nmbrString_def(hypSortMap); /* Order remapping for speedup */
+  pntrString_def(hypProofList);
+  pntrString_def(stateVector);
+  nmbrString *replStmtSchemePtr;
+  nmbrString *hypSchemePtr;
+  nmbrString *hypProofPtr;
+  nmbrString *makeSubstPtr;
+  pntrString_def(hypMakeSubstList);
+  pntrString_def(hypStateVectorList);
+  vstring_def(hypReEntryFlagList);
+  nmbrString_def(hypStepList);
+  flag reEntryFlag;
+  flag tmpFlag;
+  flag noHypMatch;
+  flag foundTrialStepMatch;
+  long replStmtSchemeLen, schemeVars, schReqHyps, hypLen, reqVars,
+      schEHyps, subPfLen;
+  long saveUnifTrialCount;
+  flag reenterFFlag;
+  flag dummyVarFlag; /* Flag that replacement hypothesis under consideration has
+                        dummy variables */
+  nmbrString *hypTestPtr; /* Points to what we are testing hyp. against */
+  flag hypOrSubproofFlag; /* 0 means testing against hyp., 1 against proof*/
+  vstring_def(indepKnownSteps); /* 'Y' = ok to try step; 'N' = not ok */
+  long pfLen;
+  long scanLen;
+  long scanUpperBound;
+  long scanLowerBound;
+  vstring_def(hasFloatingProof);  /* 'N' or 'Y' for $e hyps */
+  vstring_def(tryFloatingProofLater);  /* 'N' or 'Y' */
+  flag hasDummyVar;
+
+  /* If we are overriding discouraged usage, a warning has already been
+     printed.  If we are not, then we should never get here. */
+  if (overrideFlag == 0 && getMarkupFlag(replStatemNum, USAGE_DISCOURAGED)) {
+    bug(1868);
+  }
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  trialStep = 0;
+
+  prfMath = (g_ProofInProgress.target)[prfStep];
+  if (subProofFlag) {
+    /* Get length of the existing subproof at the replacement step.  The
+       existing subproof will be scanned to see if there is a match to
+       the $e hypotheses of the replacement statement.  */
+    subPfLen = subproofLen(g_ProofInProgress.proof, prfStep);
+    scanLen = subPfLen;
+    scanUpperBound = prfStep;
+    scanLowerBound = scanUpperBound - scanLen + 1;
+  } else {
+    /* Treat the whole proof as a "subproof" and get its length.  The whole
+       existing proof will be scanned to see if there is a match to
+       the $e hypotheses of the replacement statement.  */
+    pfLen = nmbrLen(g_ProofInProgress.proof);
+    scanUpperBound = pfLen - 1;  /* Last proof step (0=1st step, 1=2nd, etc. */
+    scanLowerBound = 0; /* scanUpperBound - scanLen + 1;  */
+  }
+  /* Note: the variables subPfLen, pfLen, and scanLen aren't
+     used again.  They could be eliminated above if we wanted. */
+
+  if (g_Statement[replStatemNum].type != (char)a_ &&
+      g_Statement[replStatemNum].type != (char)p_)
+    bug(1822); /* Not $a or $p */
+
+  schReqHyps = g_Statement[replStatemNum].numReqHyp;
+  reqVars = nmbrLen(g_Statement[replStatemNum].reqVarList);
+
+  /* hasFloatingProof is used only when searchMethod=1 */
+  let(&hasFloatingProof, string(schReqHyps, ' '));
+  let(&tryFloatingProofLater, string(schReqHyps, ' '));
+  replStmtSchemePtr = g_Statement[replStatemNum].mathString;
+  replStmtSchemeLen = nmbrLen(replStmtSchemePtr);
+
+  /* Change all variables in the statement to dummy vars for unification */
+  nmbrLet(&scheme, replStmtSchemePtr);
+  schemeVars = reqVars;
+  if (schemeVars + g_pipDummyVars/*global*/ > g_dummyVars/*global*/) {
+    /* Declare more dummy vars if necessary */
+    declareDummyVars(schemeVars + g_pipDummyVars - g_dummyVars);
+  }
+  for (var = 0; var < schemeVars; var++) {
+    /* Put dummy var mapping into g_MathToken[].tmp field */
+    g_MathToken[g_Statement[replStatemNum].reqVarList[var]].tmp
+        = g_mathTokens/*global*/ + 1 + g_pipDummyVars/*global*/ + var;
+  }
+  for (sym = 0; sym < replStmtSchemeLen; sym++) {
+    if (g_MathToken[replStmtSchemePtr[sym]].tokenType != (char)var_) continue;
+    /* Use dummy var mapping from g_MathToken[].tmp field */
+    scheme[sym] = g_MathToken[replStmtSchemePtr[sym]].tmp;
+  }
+
+  /* Change all variables in the statement's hyps to dummy vars for subst. */
+  pntrLet(&hypList, pntrNSpace(schReqHyps));
+  nmbrLet(&hypSortMap, nmbrSpace(schReqHyps));
+  pntrLet(&hypProofList, pntrNSpace(schReqHyps));
+
+  for (hyp = 0; hyp < schReqHyps; hyp++) {
+    hypSchemePtr = NULL_NMBRSTRING;
+    nmbrLet(&hypSchemePtr,
+      g_Statement[g_Statement[replStatemNum].reqHypList[hyp]].mathString);
+    hypLen = nmbrLen(hypSchemePtr);
+    for (sym = 0; sym < hypLen; sym++) {
+      if (g_MathToken[hypSchemePtr[sym]].tokenType
+          != (char)var_) continue;
+      /* Use dummy var mapping from g_MathToken[].tmp field */
+      hypSchemePtr[sym] = g_MathToken[hypSchemePtr[sym]].tmp;
+    }
+    hypList[hyp] = hypSchemePtr;
+    hypSortMap[hyp] = hyp;
+  }
+
+  /* Move all $e's to front of hypothesis list */
+  schEHyps = 0;
+  for (hyp = 0; hyp < schReqHyps; hyp++) {
+    if (g_Statement[g_Statement[replStatemNum].reqHypList[hypSortMap[hyp]]].type
+         == (char)e_) {
+      j = hypSortMap[hyp];
+      hypSortMap[hyp] = hypSortMap[schEHyps];
+      hypSortMap[schEHyps] = j;
+      schEHyps++;
+    }
+  }
+
+  /* Speedup - sort essential hyp's according to decreasing length to
+     maximize the chance of early rejection */
+  for (hyp = 0; hyp < schEHyps; hyp++) {
+    /* Bubble sort - but should be OK for typically small # of hyp's */
+    for (i = hyp + 1; i < schEHyps; i++) {
+      if (nmbrLen(hypList[hypSortMap[i]]) > nmbrLen(hypList[hypSortMap[hyp]])) {
+        j = hypSortMap[hyp];
+        hypSortMap[hyp] = hypSortMap[i];
+        hypSortMap[i] = j;
+      }
+    }
+  }
+
+  /* If we are just scanning the subproof, all subproof steps are independent,
+     so the getIndepKnownSteps scan would be redundant. */
+  if (!subProofFlag) {
+    /* If subProofFlag is not set, we scan the whole proof,
+       not just the subproof starting at prfStep, to find additional possible
+       matches. */
+    /* Get a list of the possible steps to look at that are not dependent
+       on the prfStep.  A value of 'Y'  means we can try the step. */
+    free_vstring(indepKnownSteps);
+    indepKnownSteps = getIndepKnownSteps(provStmtNum, prfStep);
+  }
+
+  /* Initialize state vector list for hypothesis unifications */
+  /* (We will really only use up to schEHyp entries, but allocate all
+     for possible future use) */
+  pntrLet(&hypStateVectorList, pntrPSpace(schReqHyps));
+  /* Initialize unification reentry flags for hypothesis unifications */
+  /* (1 means 0, and 2 means 1, because 0 means end-of-character-string.) */
+  /* (3 means previous proveFloating call found proof) */
+  let(&hypReEntryFlagList, string(schReqHyps, 1));
+  /* Initialize starting subproof step to scan for each hypothesis */
+  nmbrLet(&hypStepList, nmbrSpace(schReqHyps));
+  /* Initialize list of hypotheses after substitutions made */
+  pntrLet(&hypMakeSubstList, pntrNSpace(schReqHyps));
+
+
+  g_unifTrialCount = 1; /* Reset unification timeout */
+  reEntryFlag = 0; /* For unifyH() */
+
+  /* Number of required hypotheses of statement we're proving */
+  reqHyps = g_Statement[provStmtNum].numReqHyp;
+
+  while (1) { /* Try all possible unifications */
+    tmpFlag = unifyH(scheme, prfMath, &stateVector, reEntryFlag);
+    if (!tmpFlag) break; /* (Next) unification not possible */
+    if (tmpFlag == 2) {
+      print2("Unification timed out.  "
+        "Larger SET UNIFICATION_TIMEOUT may improve results.\n");
+      g_unifTrialCount = 1; /* Reset unification timeout */
+      break; /* Treat timeout as if unification not possible */
+    }
+
+    reEntryFlag = 1; /* For next unifyH() */
+
+    /* Make substitutions into each hypothesis, and try to prove that
+       hypothesis */
+    free_nmbrString(proof);
+    noHypMatch = 0;
+    for (hyp = 0; hyp < schReqHyps; hyp++) {
+
+      /* Make substitutions from replacement statement's stateVector */
+      nmbrLet((nmbrString **)(&(hypMakeSubstList[hypSortMap[hyp]])),
+          NULL_NMBRSTRING); /* Deallocate previous pass if any */
+      hypMakeSubstList[hypSortMap[hyp]] =
+          makeSubstUnif(&dummyVarFlag, hypList[hypSortMap[hyp]],
+          stateVector);
+
+      /* Initially, a $e has no proveFloating proof */
+      hasFloatingProof[hyp] = 'N';  /* Init for this pass */
+      tryFloatingProofLater[hyp] = 'N'; /* Init for this pass */
+
+      /* Make substitutions from each earlier hypothesis unification */
+      for (i = 0; i < hyp; i++) {
+        /* Only do substitutions for $e's -- the $f's will have no
+           dummy vars., and they have no stateVector
+           since they were found with proveFloating below */
+        if (i >= schEHyps) break;
+
+        /* If it is an essential hypothesis with a proveFloating proof,
+           we don't want to make substitutions since it has no
+           stateVector.  (This is the only place we look
+           at hasFloatingProof.) */
+        if (hasFloatingProof[i] == 'Y') continue;
+
+        makeSubstPtr = makeSubstUnif(&dummyVarFlag,
+            hypMakeSubstList[hypSortMap[hyp]],
+            hypStateVectorList[hypSortMap[i]]);
+        nmbrLet((nmbrString **)(&(hypMakeSubstList[hypSortMap[hyp]])),
+            NULL_NMBRSTRING);
+        hypMakeSubstList[hypSortMap[hyp]] = makeSubstPtr;
+      }
+
+      if (hyp < schEHyps) {
+        /* It's a $e hypothesis */
+        if (g_Statement[g_Statement[replStatemNum].reqHypList[hypSortMap[hyp]]
+            ].type != (char)e_) bug(1823);
+      } else {
+        /* It's a $f hypothesis */
+        if (g_Statement[g_Statement[replStatemNum].reqHypList[hypSortMap[hyp]]
+             ].type != (char)f_) bug(1824);
+      }
+
+
+      /* Scan all known steps of existing subproof to find a hypothesis
+         match */
+      foundTrialStepMatch = 0;
+      reenterFFlag = 0;
+      if (hypReEntryFlagList[hypSortMap[hyp]] == 2) {
+        /* Reentry flag is set; we're continuing with a previously unified
+           subproof step */
+        trialStep = hypStepList[hypSortMap[hyp]];
+
+        /* If we are re-entering the unification for a $f, it means we
+           backtracked from a later failure, and there won't be another
+           unification possible.  In this case we should bypass the
+           proveFloating call to force a further backtrack.  (Otherwise
+           we will have an infinite loop.)  Note that for $f's, all
+           variables will be known so there will only be one unification
+           anyway. */
+        if (hyp >= schEHyps || hasFloatingProof[hyp] == 'Y') {
+          reenterFFlag = 1;
+        }
+      } else {
+        if (hypReEntryFlagList[hypSortMap[hyp]] == 1) {
+          /* Start at the beginning of the proof */
+          /* trialStep = prfStep - subPfLen + 1; */ /* obsolete */
+          trialStep = scanLowerBound;
+          /* Later enhancement:  start at required hypotheses */
+          /* (Here we use the trick of shifting down the starting
+              trialStep to below the real subproof start) */
+          trialStep = trialStep - reqHyps;
+        } else {
+          if (hypReEntryFlagList[hypSortMap[hyp]] == 3) {
+            /* This is the case where proveFloating previously found a
+               proof for the step, and we've backtracked.  In this case,
+               we want to backtrack further - no scan or proveFloating
+               call again. */
+            hypReEntryFlagList[hypSortMap[hyp]] = 1;
+            reenterFFlag = 1; /* Skip proveFloating call */
+            trialStep = scanUpperBound; /* Skip loop */
+          } else {
+            bug(1826);
+          }
+        }
+      }
+
+      for (trialStep = trialStep + 0; trialStep < scanUpperBound;
+          trialStep++) {
+        /* Note that step scanUpperBound is not scanned since that is
+           the statement we want to replace (subProofFlag = 1) or the
+           last statement of the proof (subProofFlag = 0), neither of
+           which would be useful for a replacement step subproof. */
+
+        if (trialStep < scanLowerBound) {
+          /* We're scanning required hypotheses */
+          hypOrSubproofFlag = 0;
+          /* Point to what we are testing hyp. against */
+          /* (Note offset to trialStep needed to compensate for trick) */
+          hypTestPtr =
+              g_Statement[g_Statement[provStmtNum].reqHypList[
+              trialStep - (scanLowerBound - reqHyps)]].mathString;
+        } else {
+          /* We're scanning the subproof */
+          hypOrSubproofFlag = 1;
+          /* Point to what we are testing hyp. against */
+          hypTestPtr = (g_ProofInProgress.target)[trialStep];
+
+          /* Subproof step has dummy var.; don't use it */
+          if (!subProofFlag) {
+            if (indepKnownSteps[trialStep] != 'Y') {
+              if (indepKnownSteps[trialStep] != 'N') bug(1836);
+              continue; /* Don't use the step */
+            }
+          }
+        }
+
+        /* Speedup - skip if no dummy vars in hyp and statements not equal */
+        if (!dummyVarFlag) {
+          if (!nmbrEq(hypTestPtr, hypMakeSubstList[hypSortMap[hyp]])) {
+            continue;
+          }
+        }
+
+        /* Speedup - skip if 1st symbols are constants and don't match */
+        /* First symbol */
+        i = hypTestPtr[0];
+        j = ((nmbrString *)(hypMakeSubstList[hypSortMap[hyp]]))[0];
+        if (g_MathToken[i].tokenType == (char)con_) {
+          if (g_MathToken[j].tokenType == (char)con_) {
+            if (i != j) {
+              continue;
+            }
+          }
+        }
+
+        /* g_unifTrialCount = 1; */ /* ??Don't reset it here in order to
+           detect exponential blowup in hypotheses trials */
+        g_unifTrialCount = 1; /* Reset unification timeout counter */
+        if (hypReEntryFlagList[hypSortMap[hyp]] < 1
+            || hypReEntryFlagList[hypSortMap[hyp]] > 2)
+          bug(1851);
+        tmpFlag = unifyH(hypMakeSubstList[hypSortMap[hyp]],
+            hypTestPtr,
+            (pntrString **)(&(hypStateVectorList[hypSortMap[hyp]])),
+            /* (Remember: 1 = false, 2 = true in hypReEntryFlagList) */
+            hypReEntryFlagList[hypSortMap[hyp]] - 1);
+        if (!tmpFlag || tmpFlag == 2) {
+          /* (Next) unification not possible or timeout */
+          if (tmpFlag == 2) {
+            print2("Unification timed out.  SET UNIFICATION_TIMEOUT larger for better results.\n");
+            g_unifTrialCount = 1; /* Reset unification timeout */
+            /* Deallocate unification state vector */
+            purgeStateVector(
+                (pntrString **)(&(hypStateVectorList[hypSortMap[hyp]])));
+          }
+
+          /* If this is a reenter, and there are no dummy vars in replacement
+             hypothesis, we have already backtracked from a unique exact
+             match that didn't work.  There is no point in continuing to
+             look for another exact match for this hypothesis, so we'll just
+             skip the rest of the subproof scan. */
+          /* (Note that we could in principle bypass the redundant unification
+             above since we know it will fail, but it will clear out our
+             stateVector for us.) */
+          if (!dummyVarFlag) {
+            if (hypReEntryFlagList[hypSortMap[hyp]] - 1 == 1) {
+              /* There are no dummy variables, so previous match
+                 was exact.  Force the trialStep loop to terminate as
+                 if nothing further was found.  (If we don't do this,
+                 there could be, say 50 more matches for "var x",
+                 so we might need a factor of 50 greater runtime for each
+                 replacement hypothesis having this situation.) */
+              /* trialStep = prfStep - 1;  old version */
+              trialStep = scanUpperBound - 1;
+                       /* Make this the last loop pass */
+            }
+          }
+
+          hypReEntryFlagList[hypSortMap[hyp]] = 1;
+          continue;
+        } else {
+
+          /* tmpFlag = 1:  a unification was found */
+          if (tmpFlag != 1) bug(1828);
+
+          /* (Speedup) */
+          /* If this subproof step has previously occurred in a hypothesis
+             or an earlier subproof step, don't consider it since that
+             would be redundant. */
+          if (hypReEntryFlagList[hypSortMap[hyp]] == 1) {
+            j = 0; /* Break flag */
+            for (i = scanLowerBound - reqHyps; i < trialStep; i++) {
+              if (i < scanLowerBound) {
+                /* A required hypothesis */
+                if (nmbrEq(hypTestPtr,
+                    g_Statement[g_Statement[provStmtNum].reqHypList[
+                    i - (scanLowerBound - reqHyps)]].mathString)) {
+                  j = 1;
+                  break;
+                }
+              } else {
+                /* A subproof step */
+                if (nmbrEq(hypTestPtr,
+                    (g_ProofInProgress.target)[i])) {
+                  j = 1;
+                  break;
+                }
+              }
+            } /* next i */
+            if (j) {
+              /* This subproof step was already considered earlier, so
+                 we can skip considering it again. */
+              /* Deallocate unification state vector */
+              purgeStateVector((pntrString **)(&(hypStateVectorList[hypSortMap[hyp]])));
+              continue;
+            }
+          } /* end if not reentry */
+          /* (End speedup) */
+
+
+          hypReEntryFlagList[hypSortMap[hyp]] = 2; /* For next unifyH() */
+          hypStepList[hypSortMap[hyp]] = trialStep;
+
+          if (!hypOrSubproofFlag) {
+            /* We're scanning required hypotheses */
+            nmbrLet((nmbrString **)(&hypProofList[hypSortMap[hyp]]),
+                nmbrAddElement(NULL_NMBRSTRING,
+                g_Statement[provStmtNum].reqHypList[
+                trialStep - (scanLowerBound - reqHyps)]));
+          } else {
+            /* We're scanning the subproof */
+            i = subproofLen(g_ProofInProgress.proof, trialStep);
+            nmbrLet((nmbrString **)(&hypProofList[hypSortMap[hyp]]),
+                nmbrSeg(g_ProofInProgress.proof, trialStep - i + 2,
+                trialStep + 1));
+          }
+
+          foundTrialStepMatch = 1;
+          break;
+        } /* end if (!tmpFlag || tmpFlag = 2) */
+      } /* next trialStep */
+
+      if (!foundTrialStepMatch) {
+        hasDummyVar = 0;
+        hypLen = nmbrLen(hypMakeSubstList[hypSortMap[hyp]]);
+        for (sym = 0; sym < hypLen; sym++) {
+          k = ((nmbrString *)(hypMakeSubstList[hypSortMap[hyp]]))[sym];
+          if (k > g_mathTokens/*global*/) {
+            hasDummyVar = 1;
+            break;
+          }
+        }
+        /* There was no (completely known) step in the (sub)proof that
+           matched the hypothesis.  If it's a $f hypothesis, we will try
+           to prove it by itself. */
+        /* (However, if this is 2nd pass of backtrack, i.e. reenterFFlag is
+           set, we already got an exact $f match earlier and don't need this
+           scan, and shouldn't do it to prevent inf. loop.) */
+        if ((hyp >= schEHyps || searchMethod == 1) && !reenterFFlag) {
+          /* It's a $f hypothesis, or any hypothesis when searchMethod=1 */
+          if (hasDummyVar) {
+            /* If it's a $f and we have dummy vars, that is bad so we leave
+               foundTrialStepMatch = 0 to backtrack */
+            if (hyp < schEHyps) {
+              /* It's a $e with dummy vars, so we flag it to try later in
+                 case further matches get rid of the dummy vars */
+              tryFloatingProofLater[hyp] = 'Y';
+              /* Unify the hypothesis with itself to initialize the
+                 stateVector to allow further substitutions */
+              tmpFlag = unifyH(hypMakeSubstList[hypSortMap[hyp]],
+                  hypMakeSubstList[hypSortMap[hyp]],
+                  (pntrString **)(&(hypStateVectorList[hypSortMap[hyp]])),
+                  /* (Remember: 1 = false, 2 = true in hypReEntryFlagList) */
+                  hypReEntryFlagList[hypSortMap[hyp]] - 1);
+              if (tmpFlag != 1) bug (1849);  /* This should be a trivial
+                      unification, so it should never fail */
+              foundTrialStepMatch = 1; /* So we can continue */
+            }
+          } else {
+            saveUnifTrialCount = g_unifTrialCount; /* Save unification timeout */
+            hypProofPtr =
+                proveFloating(hypMakeSubstList[hypSortMap[hyp]],
+                    provStmtNum, improveDepth, prfStep, noDistinct,
+                    overrideFlag, mathboxFlag);
+            g_unifTrialCount = saveUnifTrialCount; /* Restore unif. timeout */
+            if (nmbrLen(hypProofPtr)) { /* Proof was found */
+              nmbrLet((nmbrString **)(&hypProofList[hypSortMap[hyp]]),
+                  NULL_NMBRSTRING);
+              hypProofList[hypSortMap[hyp]] = hypProofPtr;
+              foundTrialStepMatch = 1;
+              hypReEntryFlagList[hypSortMap[hyp]] = 3;
+              /* Set flag so that we won't attempt subst. on $e w/ float prf */
+              hasFloatingProof[hyp] = 'Y';
+            }
+          }
+        } /* end if $f, or $e and searchMethod 1 */
+      }
+
+      if (hyp == schEHyps - 1 && foundTrialStepMatch) {
+        /* Scan all the postponed $e hypotheses in case they are known now */
+        for (i = 0; i < schEHyps; i++) {
+          if (tryFloatingProofLater[i] == 'Y') {
+
+            /* Incorporate substitutions of all later hypotheses
+               into this one (only earlier ones were done in main scan) */
+            for (j = i + 1; j < schEHyps; j++) {
+              if (hasFloatingProof[j] == 'Y') continue;
+              makeSubstPtr = makeSubstUnif(&dummyVarFlag,
+                  hypMakeSubstList[hypSortMap[i]],
+                  hypStateVectorList[hypSortMap[j]]);
+              nmbrLet((nmbrString **)(&(hypMakeSubstList[hypSortMap[i]])),
+                  NULL_NMBRSTRING);
+              hypMakeSubstList[hypSortMap[i]] = makeSubstPtr;
+            }
+
+            hasDummyVar = 0;
+            hypLen = nmbrLen(hypMakeSubstList[hypSortMap[i]]);
+            for (sym = 0; sym < hypLen; sym++) {
+              k = ((nmbrString *)(hypMakeSubstList[hypSortMap[i]]))[sym];
+              if (k > g_mathTokens/*global*/) {
+                hasDummyVar = 1;
+                break;
+              }
+            }
+            if (hasDummyVar) {
+              foundTrialStepMatch = 0; /* Force backtrack */
+              /* If we don't have a proof at this point, we didn't save
+                 enough info to backtrack easily, so we'll break out to
+                 the top-most next unification (if any). */
+              hyp = 0; /* Force breakout below */
+              break;
+            }
+            saveUnifTrialCount = g_unifTrialCount; /* Save unification timeout */
+            hypProofPtr =
+                proveFloating(hypMakeSubstList[hypSortMap[i]],
+                    provStmtNum, improveDepth, prfStep, noDistinct,
+                    overrideFlag, mathboxFlag);
+            g_unifTrialCount = saveUnifTrialCount; /* Restore unif. timeout */
+            if (nmbrLen(hypProofPtr)) { /* Proof was found */
+              nmbrLet((nmbrString **)(&hypProofList[hypSortMap[i]]),
+                  NULL_NMBRSTRING);
+              hypProofList[hypSortMap[i]] = hypProofPtr;
+              foundTrialStepMatch = 1;
+              hypReEntryFlagList[hypSortMap[i]] = 3;
+              /* Set flag so that we won't attempt subst. on $e w/ float prf */
+              hasFloatingProof[i] = 'Y';
+            } else {   /* Proof not found */
+              foundTrialStepMatch = 0; /* Force backtrack */
+              /* If we don't have a proof at this point, we didn't save
+                 enough info to backtrack easily, so we'll break out to
+                 the top-most next unification (if any). */
+              hyp = 0; /* Force breakout below */
+              break;
+            } /* if (nmbrLen(hypProofPtr)) */
+          } /* if (tryFloatingProofLater[i] == 'Y') */
+        } /* for (i = 0; i < schEHyps; i++) */
+      } /* if (hyp == schEHyps - 1 && foundTrialStepMatch) */
+
+      if (!foundTrialStepMatch) {
+        /* We must backtrack */
+
+        /* Backtrack through all of postponed hypotheses with
+           dummy variables whose proof wasn't found yet.  If we
+           don't do this, we could end up with an infinite loop since we
+           would just repeat the postponement and move forward again. */
+        for (i = hyp - 1; i >=0; i--) {
+          if (tryFloatingProofLater[i] == 'N') break;
+          if (tryFloatingProofLater[i] != 'Y') bug(1853);
+          hyp--;
+        }
+
+        if (hyp == 0) {
+          /* No more possibilities to try */
+          noHypMatch = 1;
+          break;
+        }
+        hyp = hyp - 2; /* Go back one iteration (subtract 2 to offset
+                          end of loop increment) */
+      } /* if (!foundTrialStepMatch) */
+
+    } /* next hyp */
+
+    if (noHypMatch) {
+      /* Proof was not found for some hypothesis. */
+      continue; /* Get next unification */
+    } /* End if noHypMatch */
+
+    /* Proofs were found for all hypotheses */
+
+    /* Build the proof */
+    for (hyp = 0; hyp < schReqHyps; hyp++) {
+      if (nmbrLen(hypProofList[hyp]) == 0) bug(1852); /* Should have proof */
+      nmbrLet(&proof, nmbrCat(proof, hypProofList[hyp], NULL));
+    }
+    nmbrLet(&proof, nmbrAddElement(proof, replStatemNum));
+                                                     /* Complete the proof */
+
+    goto returnPoint;
+
+  } /* End while (next unifyH() call for main replacement statement) */
+
+  free_nmbrString(proof);  /* Proof not possible */
+
+ returnPoint:
+
+  /* Deallocate hypothesis schemes and proofs */
+  for (hyp = 0; hyp < schReqHyps; hyp++) {
+    free_nmbrString(*(nmbrString **)(&hypList[hyp]));
+    free_nmbrString(*(nmbrString **)(&hypProofList[hyp]));
+    free_nmbrString(*(nmbrString **)(&hypMakeSubstList[hyp]));
+    purgeStateVector((pntrString **)(&hypStateVectorList[hyp]));
+  }
+
+  /* Deallocate unification state vector */
+  purgeStateVector(&stateVector);
+
+  free_nmbrString(scheme);
+  free_pntrString(hypList);
+  free_nmbrString(hypSortMap);
+  free_pntrString(hypProofList);
+  free_pntrString(hypMakeSubstList);
+  free_pntrString(hypStateVectorList);
+  free_vstring(hypReEntryFlagList);
+  free_nmbrString(hypStepList);
+  free_vstring(indepKnownSteps);
+  free_vstring(hasFloatingProof);
+  free_vstring(tryFloatingProofLater);
+
+
+/*E*/if(db8)print2("%s\n", cat("Returned: ",
+/*E*/   nmbrCvtRToVString(proof,
+/*E*/                0, /*explicitTargets*/
+/*E*/                0 /*statemNum, used only if explicitTargets*/), NULL));
+  return proof; /* Caller must deallocate */
+} /* replaceStatement */
+
+
+
+/* This function identifies all steps in the proof in progress that (1) are
+   independent of step refStep, (2) have no dummy variables, (3) are
+   not $f's or $e's, and (4) have subproofs that are complete
+   (no unassigned steps).  A "Y" is returned for each such step,
+   and "N" is returned for all other steps.  The "Y" steps can be used
+   for scanning for useful subproofs outside of the subProof of refStep.
+   Note: The caller must deallocate the returned vstring. */
+vstring getIndepKnownSteps(long proofStmt, long refStep)
+{
+  long proofLen, prfStep, step2;
+  long wrkSubPfLen, mathLen;
+  nmbrString *proofStepContent;
+  vstring_def(indepSteps);
+  vstring_def(unkSubPrfSteps); /* 'K' if subproof is known, 'U' if unknown */
+
+  /* g_ProofInProgress is global */
+  proofLen = nmbrLen(g_ProofInProgress.proof);
+  /* Preallocate the return argument */
+  let(&indepSteps, string(proofLen, 'N'));
+
+  /* Scan back from last step to get independent subproofs */
+  for (prfStep = proofLen - 2 /*next to last step*/; prfStep >= 0;
+      prfStep--) {
+    wrkSubPfLen = subproofLen(g_ProofInProgress.proof, prfStep);
+    if (prfStep >= refStep && prfStep - wrkSubPfLen + 1 <= refStep) {
+      /* The subproof includes the refStep; reject it */
+      continue;
+    }
+    /* Mark all steps in the subproof as independent */
+    for (step2 = prfStep - wrkSubPfLen + 1; step2 <= prfStep; step2++) {
+      if (indepSteps[step2] == 'Y') bug(1832); /* Should be 1st Y assignment */
+      indepSteps[step2] = 'Y';
+    }
+    /* Speedup: skip over independent subproof to reduce subproofLen() calls */
+    prfStep = prfStep - wrkSubPfLen + 1; /* Decrement loop counter */
+        /* (Note that a 1-step subproof won't modify loop counter) */
+  } /* next prfStep */
+
+  /* Scan all of the 'Y' steps and mark them as 'N' if $e, $f, or the
+     step has dummy variables */
+  for (prfStep = 0; prfStep < proofLen; prfStep++) {
+    if (indepSteps[prfStep] == 'N') continue;
+
+    /* Flag $e, $f as 'N' */
+    proofStmt = (g_ProofInProgress.proof)[prfStep];
+    if (proofStmt < 0) {
+      if (proofStmt == -(long)'?') {
+        /* indepSteps[prfStep] = 'N' */ /* We can still use its math string */
+      } else {
+        bug(1833); /* Packed ("squished") proof not handled (yet?) */
+      }
+    } else {
+      if (g_Statement[proofStmt].type == (char)e_
+          || g_Statement[proofStmt].type == (char)f_) {
+        /* $e or $f */
+        indepSteps[prfStep] = 'N';
+      } else if (g_Statement[proofStmt].type != (char)p_
+            && g_Statement[proofStmt].type != (char)a_) {
+        bug(1834);
+      }
+    }
+
+    if (indepSteps[prfStep] == 'N') continue;
+
+    /* Flag statements with dummy variables with 'N' */
+
+    /* Get the math tokens in the proof step */
+    proofStepContent = (g_ProofInProgress.target)[prfStep];
+
+    /* Do not consider unknown subproof steps or those with
+       unknown variables */
+    mathLen = nmbrLen(proofStepContent);
+    if (mathLen == 0) bug(1835); /* Shouldn't be empty */
+    for (mathLen = mathLen - 1; mathLen >= 0; mathLen--) {
+      if (((nmbrString *)proofStepContent)[mathLen] >
+          g_mathTokens/*global*/) {
+        /* The token is a dummy variable */
+        indepSteps[prfStep] = 'N';
+        break;
+      }
+    }
+  } /* next prfStep */
+
+  /* Identify subproofs that have unknown steps */
+  unkSubPrfSteps = getKnownSubProofs();
+  /* Propagate unknown subproofs to Y/N flags */
+  for (prfStep = 0; prfStep < proofLen; prfStep++) {
+    if (unkSubPrfSteps[prfStep] == 'U') indepSteps[prfStep] = 'N';
+  }
+
+  free_vstring(unkSubPrfSteps); /* Deallocate */
+
+  return indepSteps; /* Caller must deallocate */
+
+} /* getIndepKnownSteps */
+
+
+
+/* This function classifies each proof step in g_ProofInProgress.proof
+   as known or unknown ('K' or 'U' in the returned string) depending
+   on whether the step has a completely known subproof.
+   Note: The caller must deallocate the returned vstring. */
+vstring getKnownSubProofs(void)
+{
+  long proofLen, hyp;
+  vstring_def(unkSubPrfSteps); /* 'K' if subproof is known, 'U' if unknown */
+  vstring_def(unkSubPrfStack); /* 'K' if subproof is known, 'U' if unknown */
+  long stackPtr, prfStep, stmt;
+
+  /* g_ProofInProgress is global */
+  proofLen = nmbrLen(g_ProofInProgress.proof);
+
+  /* Scan the proof and identify subproofs that have unknown steps */
+  let(&unkSubPrfSteps, space(proofLen));
+  let(&unkSubPrfStack, space(proofLen));
+  stackPtr = -1;
+  for (prfStep = 0; prfStep < proofLen; prfStep++) {
+    stmt = (g_ProofInProgress.proof)[prfStep];
+    if (stmt < 0) { /* Unknown step or local label */
+      if (stmt != -(long)'?') bug(1837); /* We don't handle compact proofs */
+      unkSubPrfSteps[prfStep] = 'U'; /* Subproof is unknown */
+      stackPtr++;
+      unkSubPrfStack[stackPtr] = 'U';
+      continue;
+    }
+    if (g_Statement[stmt].type == (char)e_ ||
+        g_Statement[stmt].type == (char)f_) { /* A hypothesis */
+      unkSubPrfSteps[prfStep] = 'K'; /* Subproof is known */
+      stackPtr++;
+      unkSubPrfStack[stackPtr] = 'K';
+      continue;
+    }
+    if (g_Statement[stmt].type != (char)a_ &&
+        g_Statement[stmt].type != (char)p_) bug(1838);
+    unkSubPrfSteps[prfStep] = 'K';  /* Start assuming subproof is known */
+    for (hyp = 1; hyp <= g_Statement[stmt].numReqHyp; hyp++) {
+      if (stackPtr < 0) bug(1839);
+      if (unkSubPrfStack[stackPtr] == 'U') {
+        /* If any hypothesis is unknown, the statement's subproof is unknown */
+        unkSubPrfSteps[prfStep] = 'U';
+      }
+      stackPtr--;
+    }
+    stackPtr++;
+    if (stackPtr < 0) bug(1840);
+    unkSubPrfStack[stackPtr] = unkSubPrfSteps[prfStep];
+  } /* next prfStep */
+  if (stackPtr != 0) bug(1841);
+  free_vstring(unkSubPrfStack); /* Deallocate */
+  return unkSubPrfSteps; /* Caller must deallocate */
+
+} /* getKnownSubProofs */
+
+
+
+/* Add a subproof in place of an unknown step to g_ProofInProgress.  The
+   .target, .source, and .user fields are initialized to empty (except
+   .target and .user of the deleted unknown step are retained). */
+void addSubProof(const nmbrString *subProof, long step) {
+  long sbPfLen;
+
+  if ((g_ProofInProgress.proof)[step] != -(long)'?') bug(1803);
+                     /* Only unknown steps should be allowed at cmd interface */
+  sbPfLen = nmbrLen(subProof);
+  nmbrLet(&g_ProofInProgress.proof, nmbrCat(nmbrLeft(g_ProofInProgress.proof, step),
+      subProof, nmbrRight(g_ProofInProgress.proof, step + 2), NULL));
+  pntrLet(&g_ProofInProgress.target, pntrCat(pntrLeft(g_ProofInProgress.target,
+      step), pntrNSpace(sbPfLen - 1), pntrRight(g_ProofInProgress.target,
+      step + 1), NULL));
+  /* Deallocate .source in case not empty (if not, though, it's a bug) */
+  if (nmbrLen((g_ProofInProgress.source)[step])) bug(1804);
+  /*free_nmbrString(*(nmbrString **)(&g_ProofInProgress.source[step]));*/
+  pntrLet(&g_ProofInProgress.source, pntrCat(pntrLeft(g_ProofInProgress.source,
+      step), pntrNSpace(sbPfLen - 1), pntrRight(g_ProofInProgress.source,
+      step + 1), NULL));
+  pntrLet(&g_ProofInProgress.user, pntrCat(pntrLeft(g_ProofInProgress.user,
+      step), pntrNSpace(sbPfLen - 1), pntrRight(g_ProofInProgress.user,
+      step + 1), NULL));
+} /* addSubProof */
+
+/* This function eliminates any occurrences of statement sourceStmtNum in the
+   targetProof by substituting it with the proof of sourceStmtNum.  The
+   unchanged targetProof is returned if there was an error. */
+/* Normally, targetProof is the global g_ProofInProgress.proof.  However,
+   we make it an argument in case in the future we'd like to do this
+   outside of the Proof Assistant. */
+/* The rawTargetProof may be uncompressed or compressed. */
+/* Note: The caller must deallocate the returned nmbrString. */
+nmbrString *expandProof(
+    const nmbrString *rawTargetProof, /* May be compressed or uncompressed */
+    long sourceStmtNum   /* The statement whose proof will be expanded */
+    /* , long targetStmtNum */) { /* The statement begin proved */
+  nmbrString_def(origTargetProof);
+  nmbrString_def(targetProof);
+  nmbrString_def(sourceProof);
+  nmbrString_def(expandedTargetProof);
+  pntrString_def(hypSubproofs);
+  nmbrString_def(expandedSubproof);
+  long targetStep, srcHyp, hypStep, totalSubpLen, subpLen, srcStep;
+  long sourcePLen, sourceHyps, targetPLen, targetSubpLen;
+  flag hasDummyVar = 0;
+  flag hasUnknownStep = 0;
+  char srcStepType;
+  long srcHypNum;
+  flag foundMatch;
+
+  sourceProof = getProof(sourceStmtNum, 0); /* Retrieve from source file */
+  nmbrLet(&sourceProof, nmbrUnsquishProof(sourceProof)); /* Uncompress */
+  /* (The following nmbrUnsquishProof() is unnecessary when called from
+     within the Proof Assistant.) */
+  nmbrLet(&origTargetProof, nmbrUnsquishProof(rawTargetProof)); /* Uncompress */
+  nmbrLet(&expandedTargetProof, origTargetProof);
+  sourcePLen = nmbrLen(sourceProof);
+  sourceHyps = nmbrLen(g_Statement[sourceStmtNum].reqHypList);
+  pntrLet(&hypSubproofs, pntrNSpace(sourceHyps)); /* pntrNSpace initializes
+        to null nmbrStrings */
+  if (g_Statement[sourceStmtNum].type != (char)p_) {
+    /* Caller should enforce $p statements only */
+    bug(1871);
+    nmbrLet(&expandedTargetProof, targetProof);
+    goto RETURN_POINT;
+  }
+
+  while (1) { /* Restart after every expansion (to handle nested
+                       references to sourceStmtNum correctly) */
+    nmbrLet(&targetProof, expandedTargetProof);
+    targetPLen = nmbrLen(targetProof);
+    foundMatch = 0;
+    for (targetStep = targetPLen - 1; targetStep >= 0; targetStep--) {
+      if (targetProof[targetStep] != sourceStmtNum) continue;
+      foundMatch = 1;
+      /* Found a use of the source statement in the proof */
+      targetSubpLen = subproofLen(targetProof, targetStep);
+      /* Collect the proofs of the hypotheses */
+      /*pntrLet(&hypSubproofs, pntrNSpace(sourceHyps));*/ /* done above */
+      hypStep = targetStep - 1;
+      totalSubpLen = 0;
+      for (srcHyp = sourceHyps - 1; srcHyp >= 0; srcHyp--) {
+        subpLen = subproofLen(targetProof, hypStep);
+                                      /* Find length of proof of hypothesis */
+        nmbrLet((nmbrString **)(&(hypSubproofs[srcHyp])),
+          nmbrMid(targetProof, (hypStep + 1) - (subpLen - 1), subpLen));
+                                    /* Note that nmbrStrings start at 1, not 0 */
+        hypStep = hypStep - subpLen;
+        totalSubpLen = totalSubpLen + subpLen;
+      }
+      if (totalSubpLen != targetSubpLen - 1) {
+        /* Independent calculation of source statement subproof failed.
+           Could be caused by corrupted proof also.  If this is confirmed,
+           change the bug() to an error message (or depend on getProof() error
+           messages) */
+        bug(1872);
+        nmbrLet(&expandedTargetProof, targetProof);
+        goto RETURN_POINT;
+      }
+
+      /* Build the expanded subproof */
+      free_nmbrString(expandedSubproof);
+      /* Scan the proof of the statement to be expanded */
+      for (srcStep = 0; srcStep < sourcePLen; srcStep++) {
+        if (sourceProof[srcStep] < 0) {
+          if (sourceProof[srcStep] == -(long)'?') {
+            /* It's an unknown step in the source proof; make it an
+               unknown step in the target proof */
+            hasUnknownStep = 1;
+          } else {
+            /* It shouldn't be a compressed proof because we called
+               unSquishProof() above */
+            bug(1873);
+          }
+          /* Assign unknown to the target proof */
+          nmbrLet(&expandedSubproof, nmbrAddElement(expandedSubproof,
+              -(long)'?'));
+          continue;
+        }
+        srcStepType = g_Statement[sourceProof[srcStep]].type;
+        if (srcStepType == (char)e_ || srcStepType == (char)f_) {
+          srcHypNum = -1;  /* Means the step is not a (required) hypothesis */
+          for (srcHyp = 0; srcHyp < sourceHyps; srcHyp++) {
+            /* Find out if the proof step references a required hyp */
+            if ((g_Statement[sourceStmtNum].reqHypList)[srcHyp]
+                == sourceProof[srcStep]) {
+              srcHypNum = srcHyp;
+              break;
+            }
+          }
+          if (srcHypNum > -1) {
+            /* It's a required hypothesis */
+            nmbrLet(&expandedSubproof, nmbrCat(expandedSubproof,
+                hypSubproofs[srcHypNum], NULL));
+          } else if (srcStepType == (char)e_) {
+            /* A non-required hypothesis cannot be $e */
+            bug(1874);
+          } else if (srcStepType == (char)f_) {
+            /* It's an optional hypothesis (dummy variable), which we don't
+               know what it will be in final proof, so make it an unknown
+               step in final proof */
+            hasDummyVar = 1;
+            nmbrLet(&expandedSubproof, nmbrAddElement(expandedSubproof,
+                -(long)'?'));
+          }
+        } else if (srcStepType != (char)a_ && srcStepType != (char)p_) {
+          bug(1875);
+        } else {
+          /* It's a normal statement reference ($a, $p); use it as is */
+          /* (This adds normal ref steps one by one, each requiring a new
+             allocation in nmbrAddElement.  This should be OK if, as expected,
+             only relatively short proofs are expanded.  If it becomes a problem,
+             we can modify the code to do bigger chunks of $a, $p steps at a
+             time.) */
+          nmbrLet(&expandedSubproof, nmbrAddElement(expandedSubproof,
+              sourceProof[srcStep]));
+        } /* if srcStepType... *? */
+      } /* next srcStep */
+      /* Insert the expanded subproof into the final expanded proof */
+      nmbrLet(&expandedTargetProof, nmbrCat(
+          nmbrLeft(expandedTargetProof, (targetStep + 1) - targetSubpLen),
+          expandedSubproof,
+          nmbrRight(expandedTargetProof, (targetStep + 1) + 1), NULL));
+      break; /* Start over after processing an expansion */
+    } /* next targetStep */
+    if (!foundMatch) break;
+    /* A matching statement was expanded.  Start over so we can accurate
+       process nested references to sourceStmt */
+  } /* end while(1) */
+
+
+ RETURN_POINT:
+  if (nmbrEq(origTargetProof, expandedTargetProof)) {
+    /*
+    print2("No expansion occurred.  The proof was not changed.\n");
+    */
+  } else {
+    /*
+    printLongLine(cat("All references to theorem \"",
+        g_Statement[sourceStmtNum].labelName,
+        "\" were expanded in the proof of \"",
+        g_Statement[targetStmtNum].labelName,
+        "\", which increased from ",
+        str((double)(nmbrLen(targetProof))), " to ",
+        str((double)(nmbrLen(expandedTargetProof))), " steps (uncompressed).",
+        NULL), " ", " ");
+    */
+    if (hasDummyVar == 1) {
+      printLongLine(cat(
+      "******* Note: The expansion of \"",
+      g_Statement[sourceStmtNum].labelName,
+      "\" has dummy variable(s) that need to be assigned.", NULL), " ", " ");
+    }
+    if (hasUnknownStep == 1) {
+      printLongLine(cat(
+      "******* Note: The expansion of \"",
+      g_Statement[sourceStmtNum].labelName,
+      "\" has unknown step(s) that need to be assigned.", NULL), " ", " ");
+    }
+  }
+  /* Deallocate memory */
+  free_nmbrString(sourceProof);
+  free_nmbrString(origTargetProof);
+  free_nmbrString(targetProof);
+  free_nmbrString(expandedSubproof);
+  /* Deallocate array entries */
+  for (srcHyp = 0; srcHyp < sourceHyps; srcHyp++) {
+    free_nmbrString(*(nmbrString **)(&hypSubproofs[srcHyp]));
+  }
+  /* Deallocate array */
+  free_pntrString(hypSubproofs);
+  return expandedTargetProof;
+} /* expandProof */
+
+
+/* Delete a subproof starting (in reverse from) step.  The step is replaced
+   with an unknown step, and its .target and .user fields are retained. */
+void deleteSubProof(long step) {
+  long sbPfLen, pos;
+
+  /* Unknown step should not be allowed at cmd interface */
+  /* Don't do anything if step is unassigned. */
+  if ((g_ProofInProgress.proof)[step] == -(long)'?') return;
+
+  sbPfLen = subproofLen(g_ProofInProgress.proof, step);
+  nmbrLet(&g_ProofInProgress.proof, nmbrCat(nmbrAddElement(
+      nmbrLeft(g_ProofInProgress.proof, step - sbPfLen + 1), -(long)'?'),
+      nmbrRight(g_ProofInProgress.proof, step + 2), NULL));
+  for (pos = step - sbPfLen + 1; pos <= step; pos++) {
+    if (pos < step) {
+      /* Deallocate .target and .user */
+      free_nmbrString(*(nmbrString **)(&g_ProofInProgress.target[pos]));
+      free_nmbrString(*(nmbrString **)(&g_ProofInProgress.user[pos]));
+    }
+    /* Deallocate .source */
+    free_nmbrString(*(nmbrString **)(&g_ProofInProgress.source[pos]));
+  }
+  pntrLet(&g_ProofInProgress.target, pntrCat(pntrLeft(g_ProofInProgress.target,
+      step - sbPfLen + 1), pntrRight(g_ProofInProgress.target,
+      step + 1), NULL));
+  pntrLet(&g_ProofInProgress.source, pntrCat(pntrLeft(g_ProofInProgress.source,
+      step - sbPfLen + 1), pntrRight(g_ProofInProgress.source,
+      step + 1), NULL));
+  pntrLet(&g_ProofInProgress.user, pntrCat(pntrLeft(g_ProofInProgress.user,
+      step - sbPfLen + 1), pntrRight(g_ProofInProgress.user,
+      step + 1), NULL));
+} /* deleteSubProof */
+
+
+/* Check to see if a statement will match the g_ProofInProgress.target (or .user)
+   of an unknown step.  Returns 1 if match, 0 if not, 2 if unification
+   timed out. */
+char checkStmtMatch(long statemNum, long step)
+{
+  char targetFlag;
+  char userFlag = 1; /* Default if no user field */
+  pntrString_def(stateVector);
+  nmbrString *mString; /* Pointer only; not allocated */
+  nmbrString_def(scheme);
+  long targetLen, mStringLen, reqVars, stsym, tasym, sym, var, hyp, numHyps;
+  flag breakFlag;
+  flag firstSymbsAreConstsFlag;
+
+  targetLen = nmbrLen((g_ProofInProgress.target)[step]);
+  if (!targetLen) bug(1807);
+
+  /* If the statement is a hypothesis, just see if it unifies. */
+  if (g_Statement[statemNum].type == (char)e_ || g_Statement[statemNum].type ==
+      (char)f_) {
+
+    /* Make sure it's a hypothesis of the statement being proved */
+    breakFlag = 0;
+    numHyps = g_Statement[g_proveStatement].numReqHyp;
+    for (hyp = 0; hyp < numHyps; hyp++) {
+      if (g_Statement[g_proveStatement].reqHypList[hyp] == statemNum) {
+        breakFlag = 1;
+        break;
+      }
+    }
+    if (!breakFlag) { /* Not a required hypothesis; is it optional? */
+      numHyps = nmbrLen(g_Statement[g_proveStatement].optHypList);
+      for (hyp = 0; hyp < numHyps; hyp++) {
+        if (g_Statement[g_proveStatement].optHypList[hyp] == statemNum) {
+          breakFlag = 1;
+          break;
+        }
+      }
+      if (!breakFlag) { /* Not a hypothesis of statement being proved */
+        targetFlag = 0;
+        goto returnPoint;
+      }
+    }
+
+    g_unifTrialCount = 1; /* Reset unification timeout */
+    targetFlag = unifyH((g_ProofInProgress.target)[step],
+        g_Statement[statemNum].mathString, &stateVector, 0);
+   if (nmbrLen((g_ProofInProgress.user)[step])) {
+      g_unifTrialCount = 1; /* Reset unification timeout */
+      userFlag = unifyH((g_ProofInProgress.user)[step],
+        g_Statement[statemNum].mathString, &stateVector, 0);
+    }
+    goto returnPoint;
+  }
+
+  mString = g_Statement[statemNum].mathString;
+  mStringLen = g_Statement[statemNum].mathStringLen;
+
+  /* For speedup - 1st, 2nd, & last math symbols should match if constants */
+  /* (The speedup is only done for .target; the .user is assumed to be
+     infrequent.) */
+  /* First symbol */
+  firstSymbsAreConstsFlag = 0;
+  stsym = mString[0];
+  tasym = ((nmbrString *)((g_ProofInProgress.target)[step]))[0];
+  if (g_MathToken[stsym].tokenType == (char)con_) {
+    if (g_MathToken[tasym].tokenType == (char)con_) {
+      firstSymbsAreConstsFlag = 1; /* The first symbols are constants */
+      if (stsym != tasym) {
+        targetFlag = 0;
+        goto returnPoint;
+      }
+    }
+  }
+  /* Last symbol */
+  stsym = mString[mStringLen - 1];
+  tasym = ((nmbrString *)((g_ProofInProgress.target)[step]))[targetLen - 1];
+  if (stsym != tasym) {
+    if (g_MathToken[stsym].tokenType == (char)con_) {
+      if (g_MathToken[tasym].tokenType == (char)con_) {
+        targetFlag = 0;
+        goto returnPoint;
+      }
+    }
+  }
+  /* Second symbol */
+  if (targetLen > 1 && mStringLen > 1 && firstSymbsAreConstsFlag) {
+    stsym = mString[1];
+    tasym = ((nmbrString *)((g_ProofInProgress.target)[step]))[1];
+    if (stsym != tasym) {
+      if (g_MathToken[stsym].tokenType == (char)con_) {
+        if (g_MathToken[tasym].tokenType == (char)con_) {
+          targetFlag = 0;
+          goto returnPoint;
+        }
+      }
+    }
+  }
+
+  /* Change variables in statement to dummy variables for unification */
+  nmbrLet(&scheme, mString);
+  reqVars = nmbrLen(g_Statement[statemNum].reqVarList);
+  if (reqVars + g_pipDummyVars > g_dummyVars) {
+    /* Declare more dummy vars if necessary */
+    declareDummyVars(reqVars + g_pipDummyVars - g_dummyVars);
+  }
+  for (var = 0; var < reqVars; var++) {
+    /* Put dummy var mapping into g_MathToken[].tmp field */
+    g_MathToken[g_Statement[statemNum].reqVarList[var]].tmp = g_mathTokens + 1 +
+        g_pipDummyVars + var;
+  }
+  for (sym = 0; sym < mStringLen; sym++) {
+    if (g_MathToken[scheme[sym]].tokenType != (char)var_)
+        continue;
+    /* Use dummy var mapping from g_MathToken[].tmp field */
+    scheme[sym] = g_MathToken[scheme[sym]].tmp;
+  }
+
+  /* Now see if we can unify */
+  g_unifTrialCount = 1; /* Reset unification timeout */
+  targetFlag = unifyH((g_ProofInProgress.target)[step],
+      scheme, &stateVector, 0);
+  if (nmbrLen((g_ProofInProgress.user)[step])) {
+    g_unifTrialCount = 1; /* Reset unification timeout */
+    userFlag = unifyH((g_ProofInProgress.user)[step],
+      scheme, &stateVector, 0);
+  }
+
+ returnPoint:
+  free_nmbrString(scheme);
+  purgeStateVector(&stateVector);
+
+  if (!targetFlag || !userFlag) return (0);
+  if (targetFlag == 1 && userFlag == 1) return (1);
+  return (2);
+
+} /* checkStmtMatch */
+
+/* Check to see if a (user-specified) math string will match the
+   g_ProofInProgress.target (or .user) of an step.  Returns 1 if match, 0 if
+   not, 2 if unification timed out. */
+char checkMStringMatch(const nmbrString *mString, long step) {
+  pntrString_def(stateVector);
+  char targetFlag;
+  char sourceFlag = 1; /* Default if no .source */
+
+  g_unifTrialCount = 1; /* Reset unification timeout */
+  targetFlag = unifyH(mString, (g_ProofInProgress.target)[step],
+      &stateVector, 0);
+  if (nmbrLen((g_ProofInProgress.source)[step])) {
+    g_unifTrialCount = 1; /* Reset unification timeout */
+    sourceFlag = unifyH(mString, (g_ProofInProgress.source)[step],
+        &stateVector, 0);
+  }
+
+  purgeStateVector(&stateVector);
+
+  if (!targetFlag || !sourceFlag) return (0);
+  if (targetFlag == 1 && sourceFlag == 1) return (1);
+  return (2);
+
+} /* checkMStringMatch */
+
+
+/* Find proof of formula or simple theorem (no new vars in $e's) */
+/* maxEDepth is the maximum depth at which statements with $e hypotheses are
+   considered.  A value of 0 means none are considered. */
+/* The caller must deallocate the returned nmbrString. */
+nmbrString *proveFloating(const nmbrString *mString, long statemNum, long maxEDepth,
+    long step, /* 0 means step 1; used for messages */
+    flag noDistinct, /* 1 means don't try statements with $d's */
+    flag overrideFlag, /* 1 means to override usage locks, 2 means to
+              override silently (for web-page syntax breakdown in mmcmds.c) */
+    flag mathboxFlag) {
+
+  long reqHyps, optHyps;
+  long hyp, stmt, sym, var, i, j;
+  nmbrString_def(proof);
+  nmbrString_def(scheme);
+  pntrString_def(hypList);
+  nmbrString_def(hypOrdMap); /* Order remapping for speedup */
+  pntrString_def(hypProofList);
+  pntrString_def(stateVector);
+  nmbrString *stmtMathPtr;
+  nmbrString *hypSchemePtr;
+  nmbrString *hypProofPtr;
+  nmbrString *makeSubstPtr;
+  flag reEntryFlag;
+  flag tmpFlag;
+  flag breakFlag;
+  flag firstEHypFlag;
+  long schemeLen, schemeVars, schReqHyps, hypLen, reqVars;
+  long saveUnifTrialCount;
+  static long depth = 0;
+  static long trials;
+  static flag maxDepthExceeded;
+  long selfScanSteps;
+  long selfScanStep;
+  long prfMbox;
+
+/*E*/  long unNum;
+/*E*/if (db8)print2("%s\n", cat(space(depth+2), "Entered: ",
+/*E*/   nmbrCvtMToVString(mString), NULL));
+
+  prfMbox = getMathboxNum(statemNum);
+
+  if (depth == 0) {
+    trials = 0; /* Initialize trials */
+    maxDepthExceeded = 0;
+  } else {
+    trials++;
+  }
+  depth++; /* Update backtracking depth */
+  if (trials > g_userMaxProveFloat) {
+    free_nmbrString(proof);
+    print2(
+"Exceeded trial limit at step %ld.  You may increase with SET SEARCH_LIMIT.\n",
+        (long)(step + 1));
+    goto returnPoint;
+  }
+
+  if (maxDepthExceeded) {
+    /* Pop out of the recursive calls to avoid an infinite loop */
+    free_nmbrString(proof);
+    goto returnPoint;
+  }
+
+#define MAX_DEPTH 40  /* > this, infinite loop assumed */ /*???User setting?*/
+  if (depth > MAX_DEPTH) {
+    free_nmbrString(proof);
+/*??? Document in Metamath manual. */
+    printLongLine(cat(
+       "?Warning: A possible infinite loop was found in $f hypothesis ",
+       "backtracking (i.e., depth > ", str((double)MAX_DEPTH),
+       ").  The last proof attempt was for math string \"",
+       nmbrCvtMToVString(mString),
+       "\".  Your axiom system may have an error ",
+       "or you may have to SET EMPTY_SUBSTITUTION ON.", NULL), " ", " ");
+    maxDepthExceeded = 1;  /* Flag to exit recursion */
+    goto returnPoint;
+  }
+
+
+  /* First see if mString matches a required or optional hypothesis; if so,
+     we're done; the proof is just the hypothesis. */
+  reqHyps = g_Statement[statemNum].numReqHyp;
+  for (hyp = 0; hyp < reqHyps; hyp++) {
+    if (nmbrEq(mString,
+        g_Statement[g_Statement[statemNum].reqHypList[hyp]].mathString)) {
+      nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING,
+          g_Statement[statemNum].reqHypList[hyp]));
+      goto returnPoint;
+    }
+  }
+  optHyps = nmbrLen(g_Statement[statemNum].optHypList);
+  for (hyp = 0; hyp < optHyps; hyp++) {
+    if (nmbrEq(mString,
+        g_Statement[g_Statement[statemNum].optHypList[hyp]].mathString)) {
+      nmbrLet(&proof, nmbrAddElement(NULL_NMBRSTRING,
+          g_Statement[statemNum].optHypList[hyp]));
+      goto returnPoint;
+    }
+  }
+
+  /* Scan all proved steps in the current proof to see if the
+     statement has already been proved in another subproof */
+  selfScanSteps = nmbrLen(g_ProofInProgress.proof); /* Original proof length */
+  /* Note: proveFloating() can be called from typeStatement() (for HTML syntax
+     breakdown), and we don't want to do a self-scan that case.  If
+     g_ProofInProgress.proof has a non-zero length, it tells us that
+     we are in Proof Assistant mode.  If g_ProofInProgress.proof has zero
+     length, the loop below will be skipped, and we're still OK. */
+  /* We scan backwards for maximum speed since IMPROVE ALL processes steps
+     backwards, so we maximize the chance of a proved hit earlier on */
+  for (selfScanStep = selfScanSteps - 1; selfScanStep >= 0; selfScanStep--) {
+    if (nmbrEq(mString, (g_ProofInProgress.target)[selfScanStep])) {
+      /* The step matches.  Now see if the step was proved. */
+
+      /* Get the subproof at the step */
+      /* Note that for subproof length of 1, the 2nd argument of nmbrSeg
+         evaluates to selfScanStep + 1, so nmbrSeg will be length 1 */
+      nmbrLet(&proof, nmbrSeg(g_ProofInProgress.proof, selfScanStep -
+          subproofLen(g_ProofInProgress.proof, selfScanStep) + 2,
+          selfScanStep + 1));
+
+      /* Check to see that the subproof has no unknown steps. */
+      if (nmbrElementIn(1, proof, -(long)'?')) {
+        /* Clear out the trial proof */
+        free_nmbrString(proof);
+        /* And give up this trial */
+        continue; /* next selfScanStep */
+      }
+      /* Otherwise, we've found our proof; use it and exit */
+      goto returnPoint;
+    } /* if (nmbrEq(mString, (g_ProofInProgress.target)[selfScanStep]) */
+  } /* Next selfScanStep */
+
+  /* Scan all statements up to the current statement to see if we can unify */
+
+  /* Reversed scan order so that w3a will match before
+     wa, helping to prevent an exponential blowup for definition syntax
+     breakdown.  If wa is first, then wa will incorrectly match a w3a
+     subexpression, with the incorrect trial only detected deeper down;
+     whereas w3a will rarely match a wa subexpression, so the trial match
+     will get rejected immediately. */
+  for (stmt = statemNum - 1; stmt >= 1; stmt--) {
+
+    /* Separated quick filter for reuse in other functions */
+    if (quickMatchFilter(stmt, mString, 0/*no dummy vars*/) == 0) continue;
+
+    if (!overrideFlag && getMarkupFlag(stmt, USAGE_DISCOURAGED)) {
+      /* Skip usage-discouraged statements */
+      continue;
+    }
+
+    /* Skip statements in other mathboxes unless /INCLUDE_MATHBOXES.  (We don't
+       care about the first mathbox since there are no others above it.) */
+    if (mathboxFlag == 0 && prfMbox >= 2) {
+      /* Note that g_mathboxStart[] starts a 0 */
+      if (stmt > g_mathboxStmt && stmt < g_mathboxStart[prfMbox - 1]) {
+        continue;
+      }
+    }
+
+    /* noDistinct is set by NO_DISTINCT qualifier in IMPROVE */
+    if (noDistinct) {
+      /* Skip the statement if it has a $d requirement.  This option
+         prevents illegal minimizations that would violate $d requirements
+         since the Proof Assistant does not check for $d violations. */
+      if (nmbrLen(g_Statement[stmt].reqDisjVarsA)) {
+        continue;
+      }
+    }
+
+    stmtMathPtr = g_Statement[stmt].mathString;
+    schemeLen = nmbrLen(stmtMathPtr);
+    schReqHyps = g_Statement[stmt].numReqHyp;
+    reqVars = nmbrLen(g_Statement[stmt].reqVarList);
+
+    /* Skip any statements with $e hypotheses based on maxEDepth */
+    /* (This prevents exponential growth of backtracking) */
+    breakFlag = 0;
+    firstEHypFlag = 1;
+    for (hyp = 0; hyp < schReqHyps; hyp++) {
+      if (g_Statement[g_Statement[stmt].reqHypList[hyp]].type == (char)e_) {
+        /* (???Maybe, in the future, we'd want to do this only for depths >
+           a small nonzero amount -- specified by global variable) */
+        if (depth > maxEDepth) {
+          breakFlag = 1;
+          break;
+        } else {
+          /* We should also skip cases where a $e hypothesis has a variable
+             not in the assertion. */
+          if (firstEHypFlag) { /* This scan is needed only once */
+            /* First, set g_MathToken[].tmp for each required variable */
+            for (var = 0; var < reqVars; var++) {
+              g_MathToken[g_Statement[stmt].reqVarList[var]].tmp = 1;
+            }
+            /* Next, clear g_MathToken[].tmp for each symbol in scheme */
+            for (sym = 0; sym < schemeLen; sym++) {
+              g_MathToken[stmtMathPtr[sym]].tmp = 0;
+            }
+            /* If any were left over, a $e hyp. has a new variable. */
+            for (var = 0; var < reqVars; var++) {
+              if (g_MathToken[g_Statement[stmt].reqVarList[var]].tmp) {
+                breakFlag = 1;
+                break;
+              }
+            }
+            if (breakFlag) break;
+            firstEHypFlag = 0; /* Don't need to do this scan again for stmt. */
+          } /* End if firstHypFlag */
+        } /* End if depth > maxEDepth */
+      } /* End if $e */
+    } /* Next hyp */
+    if (breakFlag) continue; /* To next stmt */
+
+
+
+    /* Change all variables in the statement to dummy vars for unification */
+    nmbrLet(&scheme, stmtMathPtr);
+    schemeVars = reqVars; /* S.b. same after eliminated new $e vars above */
+    if (schemeVars + g_pipDummyVars > g_dummyVars) {
+      /* Declare more dummy vars if necessary */
+      declareDummyVars(schemeVars + g_pipDummyVars - g_dummyVars);
+    }
+    for (var = 0; var < schemeVars; var++) {
+      /* Put dummy var mapping into g_MathToken[].tmp field */
+      g_MathToken[g_Statement[stmt].reqVarList[var]].tmp = g_mathTokens + 1 +
+          g_pipDummyVars + var;
+    }
+    for (sym = 0; sym < schemeLen; sym++) {
+      if (g_MathToken[stmtMathPtr[sym]].tokenType != (char)var_) continue;
+      /* Use dummy var mapping from g_MathToken[].tmp field */
+      scheme[sym] = g_MathToken[stmtMathPtr[sym]].tmp;
+    }
+
+    /* Change all variables in the statement's hyps to dummy vars for subst. */
+    pntrLet(&hypList, pntrNSpace(schReqHyps));
+    nmbrLet(&hypOrdMap, nmbrSpace(schReqHyps));
+    pntrLet(&hypProofList, pntrNSpace(schReqHyps));
+    for (hyp = 0; hyp < schReqHyps; hyp++) {
+      hypSchemePtr = NULL_NMBRSTRING;
+      nmbrLet(&hypSchemePtr,
+        g_Statement[g_Statement[stmt].reqHypList[hyp]].mathString);
+      hypLen = nmbrLen(hypSchemePtr);
+      for (sym = 0; sym < hypLen; sym++) {
+        if (g_MathToken[hypSchemePtr[sym]].tokenType
+            != (char)var_) continue;
+        /* Use dummy var mapping from g_MathToken[].tmp field */
+        hypSchemePtr[sym] = g_MathToken[hypSchemePtr[sym]].tmp;
+      }
+      hypList[hyp] = hypSchemePtr;
+      hypOrdMap[hyp] = hyp;
+    }
+
+    g_unifTrialCount = 1; /* Reset unification timeout */
+    reEntryFlag = 0; /* For unifyH() */
+
+/*E*/unNum = 0;
+    while (1) { /* Try all possible unifications */
+      tmpFlag = unifyH(scheme, mString, &stateVector, reEntryFlag);
+      if (!tmpFlag) break; /* (Next) unification not possible */
+      if (tmpFlag == 2) {
+        print2(
+"Unification timed out.  SET UNIFICATION_TIMEOUT larger for better results.\n");
+        g_unifTrialCount = 1; /* Reset unification timeout */
+        break; /* Treat timeout as if unification not possible */
+      }
+
+/*E*/unNum++;
+/*E*/if (db8)print2("%s\n", cat(space(depth+2), "Testing unification ",
+/*E*/   str((double)unNum), " statement ", g_Statement[stmt].labelName,
+/*E*/   ": ", nmbrCvtMToVString(scheme), NULL));
+      reEntryFlag = 1; /* For next unifyH() */
+
+      /* Make substitutions into each hypothesis, and try to prove that
+         hypothesis */
+      free_nmbrString(proof);
+      breakFlag = 0;
+      for (hyp = 0; hyp < schReqHyps; hyp++) {
+/*E*/if (db8)print2("%s\n", cat(space(depth+2), "Proving hyp. ",
+/*E*/   str((double)(hypOrdMap[hyp])), "(#", str((double)hyp), "):  ",
+/*E*/   nmbrCvtMToVString(hypList[hypOrdMap[hyp]]), NULL));
+        makeSubstPtr = makeSubstUnif(&tmpFlag, hypList[hypOrdMap[hyp]],
+            stateVector);
+        if (tmpFlag) bug(1808); /* No dummy vars. should result unless bad $a's*/
+                            /*??? Implement an error check for this in parser */
+
+        saveUnifTrialCount = g_unifTrialCount; /* Save unification timeout */
+        hypProofPtr = proveFloating(makeSubstPtr, statemNum, maxEDepth, step,
+            noDistinct, overrideFlag, mathboxFlag);
+        g_unifTrialCount = saveUnifTrialCount; /* Restore unification timeout */
+
+        free_nmbrString(makeSubstPtr); /* Deallocate */
+        if (!nmbrLen(hypProofPtr)) {
+          /* Not possible */
+          breakFlag = 1;
+          break;
+        }
+
+        /* Deallocate in case this is the 2nd or later pass of main
+           unification */
+        nmbrLet((nmbrString **)(&hypProofList[hypOrdMap[hyp]]),
+            NULL_NMBRSTRING);
+
+        hypProofList[hypOrdMap[hyp]] = hypProofPtr;
+      }
+      if (breakFlag) {
+       /* Proof is not possible for some hypothesis. */
+
+       /* Perhaps the search limit was reached. */
+       if (trials > g_userMaxProveFloat) {
+         /* Deallocate hypothesis schemes and proofs */
+         for (hyp = 0; hyp < schReqHyps; hyp++) {
+           free_nmbrString(*(nmbrString **)(&hypList[hyp]));
+           free_nmbrString(*(nmbrString **)(&hypProofList[hyp]));
+         }
+         /* The error message has already been printed. */
+         free_nmbrString(proof);
+         goto returnPoint;
+       }
+
+       /* Speedup:  Move the hypothesis for which the proof was not found
+          to the beginning of the hypothesis list, so it will be tried
+          first next time. */
+       j = hypOrdMap[hyp];
+       for (i = hyp; i >= 1; i--) {
+         hypOrdMap[i] = hypOrdMap[i - 1];
+       }
+       hypOrdMap[0] = j;
+
+       continue; /* Not possible; get next unification */
+
+      } /* End if breakFlag */
+
+      /* Proofs were found for all hypotheses */
+
+      /* Build the proof */
+      for (hyp = 0; hyp < schReqHyps; hyp++) {
+        nmbrLet(&proof, nmbrCat(proof, hypProofList[hyp], NULL));
+      }
+
+      if (getMarkupFlag(stmt, USAGE_DISCOURAGED)) {
+        switch (overrideFlag) {
+          case 0: bug(1869); break; /* Should never get here if no override */
+          case 2: break; /* Accept override silently (in mmcmds.c syntax
+                            breakdown calls for $a web pages) */
+          case 1:  /* Normal override */
+            /* print2("\n"); */ /* Enable for more emphasis */
+            print2(
+          ">>> ?Warning: Overriding discouraged usage of statement \"%s\".\n",
+                g_Statement[stmt].labelName);
+            /* print2("\n"); */ /* Enable for more emphasis */
+            break;
+          default: bug(1870); /* Illegal value */
+        } /* end switch (overrideFlag) */
+      } /* end if (getMarkupFlag(stmt, USAGE_DISCOURAGED)) */
+
+      /* TODO: Put this in proveByReplacement? */
+      /* Notify mathbox user when other mathboxes are used */
+      if (mathboxFlag != 0) {  /* Skip unless /INCLUDE_MATHBOXES was specified */
+        /* See if it's in another mathbox; if so, let user know */
+        assignMathboxInfo();
+        if (stmt > g_mathboxStmt && g_proveStatement > g_mathboxStmt) {
+          if (stmt < g_mathboxStart[getMathboxNum(g_proveStatement) - 1]) {
+            printLongLine(cat("Used \"", g_Statement[stmt].labelName,
+                  "\" from the mathbox for ",
+                  g_mathboxUser[getMathboxNum(stmt) - 1], ".",
+                  NULL),
+                "  ", " ");
+          }
+        }
+      }
+
+      nmbrLet(&proof, nmbrAddElement(proof, stmt)); /* Complete the proof */
+
+      /* Deallocate hypothesis schemes and proofs */
+      for (hyp = 0; hyp < schReqHyps; hyp++) {
+        free_nmbrString(*(nmbrString **)(&hypList[hyp]));
+        free_nmbrString(*(nmbrString **)(&hypProofList[hyp]));
+      }
+      goto returnPoint;
+
+    } /* End while (next unifyH() call) */
+
+    /* Deallocate hypothesis schemes and proofs */
+    for (hyp = 0; hyp < schReqHyps; hyp++) {
+      free_nmbrString(*(nmbrString **)(&hypList[hyp]));
+      free_nmbrString(*(nmbrString **)(&hypProofList[hyp]));
+    }
+
+  } /* Next stmt */
+
+  free_nmbrString(proof);  /* Proof not possible */
+
+ returnPoint:
+  /* Deallocate unification state vector */
+  purgeStateVector(&stateVector);
+
+  free_nmbrString(scheme);
+  free_pntrString(hypList);
+  free_nmbrString(hypOrdMap);
+  free_pntrString(hypProofList);
+  depth--; /* Restore backtracking depth */
+/*E*/if(db8)print2("%s\n", cat(space(depth+2), "Returned: ",
+/*E*/   nmbrCvtRToVString(proof,
+/*E*/                0, /*explicitTargets*/
+/*E*/                0 /*statemNum, used only if explicitTargets*/), NULL));
+/*E*/if(db8){if(!depth)print2("Trials: %ld\n", trials);}
+  return (proof); /* Caller must deallocate */
+} /* proveFloating */
+
+
+
+/* This function does quick check for some common conditions that prevent
+   a trial statement (scheme) from being unified with a given instance.
+   Return value 0 means it can't be unified, 1 means it might be unifiable. */
+INLINE flag quickMatchFilter(long trialStmt, const nmbrString *mString,
+    long dummyVarFlag /* 0 if no dummy vars in mString */) {
+  /* This function used to be part of proveFloating().
+     It was separated out for reuse in other places */
+  long sym;
+  long firstSymbol, secondSymbol, lastSymbol;
+  nmbrString *stmtMathPtr;
+  flag breakFlag;
+  long schemeLen, mStringLen;
+
+  if (g_Statement[trialStmt].type != (char)p_ &&
+      g_Statement[trialStmt].type != (char)a_) return 0; /* Not $a or $p */
+
+  /* This section is common to all trial statements and in principle
+     could be computed once for speedup (it used to be when this code
+     was in g_proveStatement() ), but it doesn't seem too compute-intensive. */
+  mStringLen = nmbrLen(mString);
+  firstSymbol = mString[0];
+  if (g_MathToken[firstSymbol].tokenType != (char)con_) firstSymbol = 0;
+  if (mStringLen > 1) {
+    secondSymbol = mString[1];
+    if (g_MathToken[secondSymbol].tokenType != (char)con_) secondSymbol = 0;
+    /* If first symbol is a variable, second symbol shouldn't be tested. */
+    if (!firstSymbol) secondSymbol = 0;
+  } else {
+    secondSymbol = 0;
+  }
+  lastSymbol = mString[mStringLen - 1];
+  if (g_MathToken[lastSymbol].tokenType != (char)con_) lastSymbol = 0;
+  /* (End of common section) */
+
+
+  stmtMathPtr = g_Statement[trialStmt].mathString;
+
+  /* Speedup:  if first or last tokens in instance and scheme are constants,
+     they must match */
+  if (firstSymbol) { /* First symbol in mString is a constant */
+    if (firstSymbol != stmtMathPtr[0]) {
+      if (g_MathToken[stmtMathPtr[0]].tokenType == (char)con_) return 0;
+    }
+  }
+
+  schemeLen = nmbrLen(stmtMathPtr);
+
+  /* ...Continuation of speedup */
+  if (secondSymbol) { /* Second symbol in mString is a constant */
+    if (schemeLen > 1) {
+      if (secondSymbol != stmtMathPtr[1]) {
+        /* Second symbol should be tested only if 1st symbol is a constant */
+        if (g_MathToken[stmtMathPtr[0]].tokenType == (char)con_) {
+          if (g_MathToken[stmtMathPtr[1]].tokenType == (char)con_)
+              return 0;
+        }
+      }
+    }
+  }
+  if (lastSymbol) { /* Last symbol in mString is a constant */
+    if (lastSymbol != stmtMathPtr[schemeLen - 1]) {
+      if (g_MathToken[stmtMathPtr[schemeLen - 1]].tokenType ==
+         (char)con_) return 0;
+    }
+  }
+
+  /* Speedup:  make sure all constants in scheme are in instance (i.e.
+     mString) */
+  /* First, set g_MathToken[].tmp for all symbols in scheme */
+  for (sym = 0; sym < schemeLen; sym++) {
+    g_MathToken[stmtMathPtr[sym]].tmp = 1;
+  }
+  /* Next, clear g_MathToken[].tmp for all symbols in instance */
+  for (sym = 0; sym < mStringLen; sym++) {
+    g_MathToken[mString[sym]].tmp = 0;
+  }
+  /* Finally, check that they got cleared for all constants in scheme */
+  /* Only do this when there are no dummy variables in mString; this
+     is the case when dummyVarFlag = 0 (we depend on caller to set this
+     correctly) */
+  if (dummyVarFlag == 0) {
+    breakFlag = 0;
+    for (sym = 0; sym < schemeLen; sym++) {
+      if (g_MathToken[stmtMathPtr[sym]].tokenType == (char)con_) {
+        if (g_MathToken[stmtMathPtr[sym]].tmp) {
+          breakFlag = 1;
+          break;
+        }
+      }
+    }
+    if (breakFlag) return 0; /* No match */
+  } /* if dummyVarFlag == 0 */
+
+  return 1;
+
+} /* quickMatchFilter */
+
+
+/* Shorten proof by using specified statement. */
+void minimizeProof(long repStatemNum, long prvStatemNum,
+    flag allowGrowthFlag)
+{
+  /* repStatemNum is the statement number we're trying to use
+     in the proof to shorten it */
+  /* prvStatemNum is the statement number we're proving */
+  /* allowGrowthFlag means to make the replacement when possible,
+     even if it doesn't shorten the proof length */
+
+  long plen, step, mlen, sym, sublen;
+  long startingPlen = 0;
+  flag foundFlag, breakFlag;
+  nmbrString *mString; /* Pointer only; not allocated */
+  nmbrString_def(newSubProofPtr); /* Pointer only; not allocated;
+                however initialize for nmbrLen function before it's assigned */
+  if (allowGrowthFlag) startingPlen = nmbrLen(g_ProofInProgress.proof);
+
+  while (1) {
+    plen = nmbrLen(g_ProofInProgress.proof);
+    foundFlag = 0;
+    for (step = plen - 1; step >= 0; step--) {
+      /* Reject step with dummy vars */
+      mString = (g_ProofInProgress.target)[step];
+      mlen = nmbrLen(mString);
+      breakFlag = 0;
+      for (sym = 0; sym < mlen; sym++) {
+        if (mString[sym] > g_mathTokens) {
+          /* It is a dummy var. (i.e. work variable $1, $2, etc.) */
+          breakFlag = 1;
+          break;
+        }
+      }
+      if (breakFlag) continue;  /* Step has dummy var.; don't try it */
+
+      /* Reject step not matching replacement step */
+      if (!checkStmtMatch(repStatemNum, step)) {
+        continue;
+      }
+
+      /* Try the replacement */
+      /* Don't replace a step with itself (will cause infinite loop in
+         ALLOW_GROWTH mode) */
+      if ((g_ProofInProgress.proof)[step] != repStatemNum
+          /* || 1 */  /* For special replacement with same label; also below */
+          /* When not in ALLOW_GROWTH mode i.e. when an infinite loop can't
+             occur, we _do_ let a label be tested against itself so that e.g. a
+             do-nothing chain of bitr's/pm4.2's will be trimmed off with a
+             better bitr match. */
+          || !allowGrowthFlag) {
+        newSubProofPtr = replaceStatement(repStatemNum,
+            step,
+            prvStatemNum,
+            1,/*scan just subproof for speed*/
+            0,/*noDistinct=0 OK since searchMethod=0 will only
+               call proveFloating for $f's */
+            0,/*searchMethod=0: call proveFloating only for $f's*/
+            0,/*improveDepth=0 OK since we call proveFloating only for $f's*/
+            2,/*overrideFlag=2(silent) OK since MINIMIZE_WITH checked it*/
+            1/*mathboxFlag=1 since MINIMIZE_WITH has checked it before here*/
+            );
+      }
+      if (!nmbrLen(newSubProofPtr)) continue;
+                                           /* Replacement was not successful */
+
+      if (nmbrElementIn(1, newSubProofPtr, -(long)'?')) {
+        /* Don't do a replacement if the replacement has unknown
+           steps - this causes assignKnownSteps to abort, and it's not
+           clear if we should do that anyway since it doesn't necessarily
+           minimize the proof */
+        free_nmbrString(newSubProofPtr); /* Deallocate */
+        continue;
+      }
+
+      /* Get the subproof at step s */
+      sublen = subproofLen(g_ProofInProgress.proof, step);
+      if (sublen > nmbrLen(newSubProofPtr) || allowGrowthFlag) {
+        /* Success - proof length was reduced */
+        /* Delete the old subproof only if it is not an unknown
+           step (since if it is an unknown step, it is already deleted) */
+        if ((g_ProofInProgress.proof)[step] == -(long)'?') {
+          /* This can only occur in / ALLOW_GROWTH mode */
+          if (!allowGrowthFlag) bug(1831);
+        } else {
+          deleteSubProof(step);
+        }
+        addSubProof(newSubProofPtr, step - sublen + 1);
+        assignKnownSteps(step - sublen + 1, nmbrLen(newSubProofPtr));
+        foundFlag = 1;
+        free_nmbrString(newSubProofPtr);
+        break;
+      }
+
+      free_nmbrString(newSubProofPtr);
+    } /* next step */
+
+    if (!foundFlag) break; /* Done */
+
+#define MAX_GROWTH_FACTOR 2
+    if (allowGrowthFlag && plen > MAX_GROWTH_FACTOR * startingPlen) {
+      /* This will prevent an infinite loop in some cases with ALLOW_GROWTH,
+         for example 'MINIMIZE_WITH idi/ALLOW_GROWTH' in 'PROVE a1i' */
+      print2("Suppressed excessive ALLOW_GROWTH growth.\n");
+      break; /* Too much growth */
+    }
+    /* break; */  /* For special replacement with same label: always break
+                      to prevent inf loop */
+  } /* end while */
+
+} /* minimizeProof */
+
+
+/* Initialize g_ProofInProgress.source of the step, and .target of all
+   hypotheses, to schemes using new dummy variables. */
+void initStep(long step)
+{
+  long stmt, reqHyps, pos, hyp, sym, reqVars, var, mlen;
+  nmbrString_def(reqHypPos);
+  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated */
+
+  stmt = (g_ProofInProgress.proof)[step];
+  if (stmt < 0) {
+    if (stmt == -(long)'?') {
+      /* Initialize unknown step source to nothing */
+      nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])),
+          NULL_NMBRSTRING);
+    } else {
+/*E*/print2("step %ld stmt %ld\n",step,stmt);
+      bug(1809); /* Packed ("squished") proof not handled (yet?) */
+    }
+    return;
+  }
+  if (g_Statement[stmt].type == (char)e_ || g_Statement[stmt].type == (char)f_) {
+    /* A hypothesis -- initialize to the actual statement */
+    nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])),
+        g_Statement[stmt].mathString);
+    return;
+  }
+
+  /* It must be an assertion ($a or $p) */
+
+  /* Assign the assertion to .source */
+  nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])),
+      g_Statement[stmt].mathString);
+
+  /* Find the position in proof of all required hyps, and
+     assign them */
+  reqHyps = g_Statement[stmt].numReqHyp;
+  nmbrLet(&reqHypPos, nmbrSpace(reqHyps)); /* Preallocate */
+  pos = step - 1; /* Step with last hyp */
+  for (hyp = reqHyps - 1; hyp >= 0; hyp--) {
+    reqHypPos[hyp] = pos;
+    nmbrLet((nmbrString **)(&((g_ProofInProgress.target)[pos])),
+        g_Statement[g_Statement[stmt].reqHypList[hyp]].mathString);
+                                           /* Assign the hypothesis to target */
+    if (hyp > 0) { /* Don't care about subproof length for 1st hyp */
+      pos = pos - subproofLen(g_ProofInProgress.proof, pos);
+                                             /* Get to step with previous hyp */
+    }
+  }
+
+  /* Change the variables in the assertion and hypotheses to dummy variables */
+  reqVars = nmbrLen(g_Statement[stmt].reqVarList);
+  if (g_pipDummyVars + reqVars > g_dummyVars) {
+    /* Declare more dummy vars if necessary */
+    declareDummyVars(g_pipDummyVars + reqVars - g_dummyVars);
+  }
+  for (var = 0; var < reqVars; var++) {
+    /* Put dummy var mapping into g_MathToken[].tmp field */
+    g_MathToken[g_Statement[stmt].reqVarList[var]].tmp = g_mathTokens + 1 +
+      g_pipDummyVars + var;
+  }
+  /* Change vars in assertion */
+  nmbrTmpPtr = (g_ProofInProgress.source)[step];
+  mlen = nmbrLen(nmbrTmpPtr);
+  for (sym = 0; sym < mlen; sym++) {
+    if (g_MathToken[nmbrTmpPtr[sym]].tokenType == (char)var_) {
+      /* Use dummy var mapping from g_MathToken[].tmp field */
+      nmbrTmpPtr[sym] = g_MathToken[nmbrTmpPtr[sym]].tmp;
+    }
+  }
+  /* Change vars in hypotheses */
+  for (hyp = 0; hyp < reqHyps; hyp++) {
+    nmbrTmpPtr = (g_ProofInProgress.target)[reqHypPos[hyp]];
+    mlen = nmbrLen(nmbrTmpPtr);
+    for (sym = 0; sym < mlen; sym++) {
+      if (g_MathToken[nmbrTmpPtr[sym]].tokenType == (char)var_) {
+        /* Use dummy var mapping from g_MathToken[].tmp field */
+        nmbrTmpPtr[sym] = g_MathToken[nmbrTmpPtr[sym]].tmp;
+      }
+    }
+  }
+
+  /* Update the number of dummy vars used so far */
+  g_pipDummyVars = g_pipDummyVars + reqVars;
+
+  free_nmbrString(reqHypPos); /* Deallocate */
+
+  return;
+} /* initStep */
+
+
+
+
+/* Look for completely known subproofs in g_ProofInProgress.proof and
+   assign g_ProofInProgress.target and .source.  Calls assignKnownSteps(). */
+void assignKnownSubProofs(void)
+{
+  long plen, pos, subplen, q;
+  flag breakFlag;
+
+  plen = nmbrLen(g_ProofInProgress.proof);
+  /* Scan proof for known subproofs (backwards, to get biggest ones first) */
+  for (pos = plen - 1; pos >= 0; pos--) {
+    subplen = subproofLen(g_ProofInProgress.proof, pos); /* Find length of subproof */
+    breakFlag = 0;
+    for (q = pos - subplen + 1; q <= pos; q++) {
+      if ((g_ProofInProgress.proof)[q] == -(long)'?') {
+        breakFlag = 1;
+        break;
+      }
+    }
+    if (breakFlag) continue; /* Skip subproof - it has an unknown step */
+
+    /* See if all steps in subproof are assigned and known; if so, don't assign
+       them again. */
+    /* (???Add this code if needed for speedup) */
+
+    /* Assign the steps of the known subproof to g_ProofInProgress.target */
+    assignKnownSteps(pos - subplen + 1, subplen);
+
+    /* Adjust pos for next pass through 'for' loop */
+    pos = pos - subplen + 1;
+
+  } /* Next pos */
+  return;
+} /* assignKnownSubProofs */
+
+
+/* This function assigns math strings to all steps (g_ProofInProgress.target and
+   .source fields) in a subproof with all known steps. */
+void assignKnownSteps(long startStep, long sbProofLen)
+{
+
+  long stackPtr, st;
+  nmbrString_def(stack);
+  nmbrString_def(instance);
+  nmbrString_def(scheme);
+  nmbrString_def(assertion);
+  long pos, stmt, reqHyps, instLen, instPos, schemeLen, schemePos, hypLen,
+      hypPos, hyp, reqVars, var, assLen, assPos;
+  flag tmpFlag;
+  pntrString_def(stateVector);
+
+  nmbrLet(&stack, nmbrSpace(sbProofLen));
+  stackPtr = 0;
+  for (pos = startStep; pos < startStep + sbProofLen; pos++) {
+    stmt = (g_ProofInProgress.proof)[pos];
+
+    if (stmt <= 0) {
+      if (stmt != -(long)'?') bug(1810);
+                                     /* Packed proofs are not handled (yet?) */
+      if (stmt == -(long)'?') bug(1830);
+                                    /* Unknown proofs are not handled (yet?) */
+    }
+
+    if (g_Statement[stmt].type == (char)e_ || g_Statement[stmt].type == (char)f_){
+      /* It's a hypothesis or unknown step; assign step; push the stack */
+      nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[pos])),
+          g_Statement[stmt].mathString);
+      stack[stackPtr] = pos;
+      stackPtr++;
+    } else {
+      /* It's an assertion. */
+
+      /* Assemble the hypotheses for unification */
+      reqHyps = g_Statement[stmt].numReqHyp;
+
+      instLen = 1; /* First "$|$" separator token */
+      for (st = stackPtr - reqHyps; st < stackPtr; st++) {
+        if (st < 0) bug(1850); /* Proof sent in may be corrupted */
+        /* Add 1 for "$|$" separator token */
+        instLen = instLen + nmbrLen((g_ProofInProgress.source)[stack[st]]) + 1;
+      }
+      /* Preallocate instance */
+      nmbrLet(&instance, nmbrSpace(instLen));
+      /* Assign instance */
+      instance[0] = g_mathTokens; /* "$|$" separator */
+      instPos = 1;
+      for (st = stackPtr - reqHyps; st < stackPtr; st++) {
+        hypLen = nmbrLen((g_ProofInProgress.source)[stack[st]]);
+        for (hypPos = 0; hypPos < hypLen; hypPos++) {
+          instance[instPos] =
+              ((nmbrString *)((g_ProofInProgress.source)[stack[st]]))[hypPos];
+          instPos++;
+        }
+        instance[instPos] = g_mathTokens; /* "$|$" separator */
+        instPos++;
+      }
+      if (instLen != instPos) bug(1811); /* ???Delete after debugging */
+
+      schemeLen = 1; /* First "$|$" separator token */
+      for (hyp = 0; hyp < reqHyps; hyp++) {
+        /* Add 1 for "$|$" separator token */
+        schemeLen = schemeLen +
+            g_Statement[g_Statement[stmt].reqHypList[hyp]].mathStringLen + 1;
+      }
+      /* Preallocate scheme */
+      nmbrLet(&scheme, nmbrSpace(schemeLen));
+      /* Assign scheme */
+      scheme[0] = g_mathTokens; /* "$|$" separator */
+      schemePos = 1;
+      for (hyp = 0; hyp < reqHyps; hyp++) {
+        hypLen = g_Statement[g_Statement[stmt].reqHypList[hyp]].mathStringLen;
+        for (hypPos = 0; hypPos < hypLen; hypPos++) {
+          scheme[schemePos] =
+              g_Statement[g_Statement[stmt].reqHypList[hyp]].mathString[hypPos];
+          schemePos++;
+        }
+        scheme[schemePos] = g_mathTokens; /* "$|$" separator */
+        schemePos++;
+      }
+      if (schemeLen != schemePos) bug(1812); /* ???Delete after debugging */
+
+      /* Change variables in scheme to dummy variables for unification */
+      reqVars = nmbrLen(g_Statement[stmt].reqVarList);
+      if (reqVars + g_pipDummyVars > g_dummyVars) {
+        /* Declare more dummy vars if necessary */
+        declareDummyVars(reqVars + g_pipDummyVars - g_dummyVars);
+      }
+      for (var = 0; var < reqVars; var++) {
+        /* Put dummy var mapping into g_MathToken[].tmp field */
+        g_MathToken[g_Statement[stmt].reqVarList[var]].tmp = g_mathTokens + 1 +
+          g_pipDummyVars + var;
+      }
+      for (schemePos = 0; schemePos < schemeLen; schemePos++) {
+        if (g_MathToken[scheme[schemePos]].tokenType
+            != (char)var_) continue;
+        /* Use dummy var mapping from g_MathToken[].tmp field */
+        scheme[schemePos] = g_MathToken[scheme[schemePos]].tmp;
+      }
+
+      /* Change variables in assertion to dummy variables for substitution */
+      nmbrLet(&assertion, g_Statement[stmt].mathString);
+      assLen = nmbrLen(assertion);
+      for (assPos = 0; assPos < assLen; assPos++) {
+        if (g_MathToken[assertion[assPos]].tokenType
+            != (char)var_) continue;
+        /* Use dummy var mapping from g_MathToken[].tmp field */
+        assertion[assPos] = g_MathToken[assertion[assPos]].tmp;
+      }
+
+      /* Unify scheme and instance */
+      g_unifTrialCount = 0; /* Reset unification to no timeout */
+      tmpFlag = unifyH(scheme, instance, &stateVector, 0);
+      if (!tmpFlag) {
+        /* This is possible if the starting proof had an error
+           in it.  Give the user some information then give up */
+        printLongLine(cat("?Error in step ", str((double)pos + 1),
+            ":  Could not simultaneously unify the hypotheses of \"",
+            g_Statement[stmt].labelName, "\":\n    ",
+            nmbrCvtMToVString(scheme),
+            "\nwith the following statement list:\n    ",
+            nmbrCvtMToVString(instance),
+            "\n(The $|$ tokens are internal statement separation markers)",
+            "\nZapping targets so we can proceed (but you should exit the ",
+            "Proof Assistant and fix this problem)",
+            "\n(This may take a while; please wait...)",
+            NULL), "", " ");
+        purgeStateVector(&stateVector);
+        goto returnPoint;
+      }
+      /* Substitute and assign assertion to proof in progress */
+      free_nmbrString(*(nmbrString **)(&g_ProofInProgress.source[pos]));
+      g_ProofInProgress.source[pos] = makeSubstUnif(&tmpFlag, assertion, stateVector);
+      if (tmpFlag) bug(1814); /* All vars s.b. assigned */
+
+      /* Verify unification is unique; also deallocates stateVector */
+      if (unifyH(scheme, instance, &stateVector, 1)) bug(1815); /* Not unique */
+
+      /* Adjust stack */
+      stackPtr = stackPtr - reqHyps;
+      stack[stackPtr] = pos;
+      stackPtr++;
+
+    } /* End if (not) $e, $f */
+  } /* Next pos */
+
+  if (stackPtr != 1) bug(1816); /* Make sure stack emptied */
+
+ returnPoint:
+  /* Assign .target field for all but last step */
+  for (pos = startStep; pos < startStep + sbProofLen - 1; pos++) {
+    nmbrLet((nmbrString **)(&((g_ProofInProgress.target)[pos])),
+        (g_ProofInProgress.source)[pos]);
+  }
+
+  /* Deallocate (stateVector was deallocated by 2nd unif. call) */
+  free_nmbrString(stack);
+  free_nmbrString(instance);
+  free_nmbrString(scheme);
+  free_nmbrString(assertion);
+  return;
+} /* assignKnownSteps */
+
+
+/* Interactively unify a step.  Calls interactiveUnify(). */
+/* If two unifications must take place (.target,.user and .source,.user),
+   then the user must invoke this command twice, as only one will be
+   done at a time.  ???Note in manual. */
+/* If messageFlag is 1, a message will be issued if the
+   step is already unified.   If messageFlag is 0, show the step #
+   being unified.  If messageFlag is 2, don't print step #, and do nothing
+   if step is already unified. */
+void interactiveUnifyStep(long step, char messageFlag)
+{
+  pntrString_def(stateVector);
+  char unifFlag;
+
+  /* Target should never be empty */
+  if (!nmbrLen((g_ProofInProgress.target)[step])) bug (1817);
+
+  /* First, see if .target and .user should be unified */
+  /* If not, then see if .source and .user should be unified */
+  /* If not, then see if .target and .source should be unified */
+  if (nmbrLen((g_ProofInProgress.user)[step])) {
+    if (!nmbrEq((g_ProofInProgress.target)[step], (g_ProofInProgress.user)[step])) {
+      if (messageFlag == 0) print2("Step %ld:\n", step + 1);
+      unifFlag = interactiveUnify((g_ProofInProgress.target)[step],
+        (g_ProofInProgress.user)[step], &stateVector);
+      goto subAndReturn;
+    }
+    if (nmbrLen((g_ProofInProgress.source)[step])) {
+      if (!nmbrEq((g_ProofInProgress.source)[step], (g_ProofInProgress.user)[step])) {
+        if (messageFlag == 0) print2("Step %ld:\n", step + 1);
+        unifFlag = interactiveUnify((g_ProofInProgress.source)[step],
+          (g_ProofInProgress.user)[step], &stateVector);
+        goto subAndReturn;
+      }
+    }
+  } else {
+    if (nmbrLen((g_ProofInProgress.source)[step])) {
+      if (!nmbrEq((g_ProofInProgress.target)[step], (g_ProofInProgress.source)[step])) {
+        if (messageFlag == 0) print2("Step %ld:\n", step + 1);
+        unifFlag = interactiveUnify((g_ProofInProgress.target)[step],
+          (g_ProofInProgress.source)[step], &stateVector);
+        goto subAndReturn;
+      }
+    }
+  }
+
+  /* The step must already be unified */
+  if (messageFlag == 1) {
+    print2("?Step %ld is already unified.\n", step + 1);
+  }
+  unifFlag = 0; /* To skip subst. below */
+
+ subAndReturn:
+  /* If the unification was successful, make substitutions everywhere
+     before returning */
+  if (unifFlag == 1) {
+
+    g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
+
+    makeSubstAll(stateVector);
+
+  } /* End if unifFlag = 1 */
+
+  purgeStateVector(&stateVector);
+
+  return;
+
+} /* interactiveUnifyStep */
+
+
+/* Interactively select one of several possible unifications */
+/* Returns:  0 = no unification possible
+             1 = unification was selected; held in stateVector
+             2 = unification timed out
+             3 = no unification was selected */
+char interactiveUnify(const nmbrString *schemeA, const nmbrString *schemeB,
+    pntrString **stateVector)
+{
+
+  long var, i;
+  long unifCount, unifNum;
+  char unifFlag;
+  flag reEntryFlag;
+  nmbrString *stackUnkVar; /* Pointer only - not allocated */
+  nmbrString *unifiedScheme; /* Pointer only - not allocated */
+  nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
+  nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
+  long stackTop;
+  vstring_def(tmpStr);
+  nmbrString_def(nmbrTmp);
+  char returnValue;
+
+  /* Present unifications in increasing order of the number
+     of symbols in the unified result.  It seems that usually the unification
+     with the fewest symbols in the correct one. */
+  nmbrString_def(unifWeight); /* Symbol count in unification */
+  long unifTrialWeight;
+  long maxUnifWeight;
+  long minUnifWeight;
+  long unifTrials;
+  long thisUnifWeight;
+  long onesCount;
+  nmbrString_def(substResult);
+  long unkCount;
+
+  if (nmbrEq(schemeA, schemeB)) bug(1818); /* No reason to call this */
+
+  /* Count the number of possible unifications */
+  g_unifTrialCount = 1; /* Reset unification timeout */
+  unifCount = 0;
+  reEntryFlag = 0;
+  minUnifWeight = -1;
+  maxUnifWeight = 0;
+  while (1) {
+    unifFlag = unifyH(schemeA, schemeB, &(*stateVector), reEntryFlag);
+    if (unifFlag == 2) {
+      printLongLine(
+          cat("Unify:  ", nmbrCvtMToVString(schemeA), NULL), "    ", " ");
+      printLongLine(
+          cat(" with:  ", nmbrCvtMToVString(schemeB), NULL), "    ", " ");
+      print2(
+"The unification timed out.  Increase timeout (SET UNIFICATION_TIMEOUT) or\n");
+      print2(
+"assign some variables (LET VARIABLE) or the step (LET STEP) manually.\n");
+      returnValue = 2;
+      goto returnPoint;
+    }
+    if (!unifFlag) break;
+    reEntryFlag = 1;
+
+
+    /* Compute heuristic "weight" of resulting unification */
+    /* The unification with the least "weight" is intended to be the
+       most likely correct choice.  The heuristic was based on
+       empirical observations of typical unification sets */
+
+    stackTop = ((nmbrString *)((*stateVector)[11]))[1];
+    stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
+    stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
+    unifiedScheme = (nmbrString *)((*stateVector)[8]);
+
+    /* Heuristic */
+    thisUnifWeight = stackTop * 2;
+    onesCount = 0;
+    unkCount = 0;
+    for (var = 0; var <= stackTop; var++) {
+      /* Heuristic */
+      thisUnifWeight = thisUnifWeight + stackUnkVarLen[var];
+      /* Heuristic - Subtract for subst. of length 1 */
+      if (stackUnkVarLen[var] == 1) onesCount++;
+
+      /* Count the number of unknown variables in substitution result */
+      nmbrLet(&substResult, nmbrMid(unifiedScheme, stackUnkVarStart[var] + 1,
+              stackUnkVarLen[var]));
+      for (i = 0; i < nmbrLen(substResult); i++) {
+        if (substResult[i] > g_mathTokens) unkCount++;
+      }
+
+    } /* Next var */
+    thisUnifWeight = thisUnifWeight - onesCount;
+    thisUnifWeight = thisUnifWeight + 7 * unkCount;
+
+
+    /* Get new min and max weight for interactive scan ordering */
+    if (thisUnifWeight > maxUnifWeight) maxUnifWeight = thisUnifWeight;
+    if (thisUnifWeight < minUnifWeight || minUnifWeight == -1)
+      minUnifWeight = thisUnifWeight;
+
+    nmbrLet(&unifWeight, nmbrAddElement(unifWeight, 0));
+
+    unifWeight[unifCount] = thisUnifWeight;
+    unifCount++;
+    if (nmbrLen(unifWeight) != unifCount) bug(1827);
+  } /* while (1) */
+
+  if (!unifCount) {
+    printf("The unification is not possible.  The proof has an error.\n");
+    returnValue = 0;
+    goto returnPoint;
+  }
+  if (unifCount > 1) {
+    printLongLine(cat("There are ", str((double)unifCount),
+      " possible unifications.  Please select the correct one or QUIT if",
+      " you want to UNIFY later.", NULL),
+        "    ", " ");
+    printLongLine(cat("Unify:  ", nmbrCvtMToVString(schemeA), NULL),
+        "    ", " ");
+    printLongLine(cat(" with:  ", nmbrCvtMToVString(schemeB), NULL),
+        "    ", " ");
+  }
+
+  /* Scan possible unifications in order of increasing unified scheme
+     size.  This is not an optimal way to do it since the unification must
+     be completely redone for each trial size, but since it is interactive
+     the speed should be tolerable.  A faster method would be to save
+     the unifications and present them for selection in sorted order, but
+     this would require more code. */
+  unifTrials = 0;
+  for (unifTrialWeight = minUnifWeight; unifTrialWeight <= maxUnifWeight;
+      unifTrialWeight++) {
+
+    if (!nmbrElementIn(1, unifWeight, unifTrialWeight)) continue;
+
+    g_unifTrialCount = 1; /* Reset unification timeout */
+    reEntryFlag = 0;
+
+    for (unifNum = 1; unifNum <= unifCount; unifNum++) {
+      unifFlag = unifyH(schemeA, schemeB, &(*stateVector), reEntryFlag);
+      if (unifFlag != 1) bug(1819);
+
+      reEntryFlag = 1;
+      if (unifWeight[unifNum - 1] != unifTrialWeight) continue;
+
+      if (unifCount == 1) {
+        print2("Step was successfully unified.\n");
+        returnValue = 1;
+        goto returnPoint;
+      }
+
+      unifTrials++;
+      print2("Unification #%ld of %ld (weight = %ld):\n",
+          unifTrials, unifCount, unifTrialWeight);
+
+      stackTop = ((nmbrString *)((*stateVector)[11]))[1];
+      stackUnkVar = (nmbrString *)((*stateVector)[1]);
+      stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
+      stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
+      unifiedScheme = (nmbrString *)((*stateVector)[8]);
+      for (var = 0; var <= stackTop; var++) {
+        printLongLine(cat("  Replace \"",
+          g_MathToken[stackUnkVar[var]].tokenName,"\" with \"",
+            nmbrCvtMToVString(
+                nmbrMid(unifiedScheme,stackUnkVarStart[var] + 1,
+                stackUnkVarLen[var])), "\"", NULL),"    "," ");
+        /* Clear temporary string allocation during print */
+        free_vstring(tmpStr);
+        free_nmbrString(nmbrTmp);
+      } /* Next var */
+
+      while(1) {
+        tmpStr = cmdInput1("  Accept (A), reject (R), or quit (Q) <A>? ");
+        if (!tmpStr[0]) {
+          /* Default value - accept */
+          returnValue = 1;
+          goto returnPoint;
+        }
+        if (tmpStr[0] == 'R' || tmpStr[0] == 'r') {
+          if (!tmpStr[1]) {
+            free_vstring(tmpStr);
+            break;
+          }
+        }
+        if (tmpStr[0] == 'Q' || tmpStr[0] == 'q') {
+          if (!tmpStr[1]) {
+            /*return (3);*/
+            returnValue = 3;
+            goto returnPoint;
+          }
+        }
+        if (tmpStr[0] == 'A' || tmpStr[0] == 'a') {
+          if (!tmpStr[1]) {
+            /*return (1);*/
+            returnValue = 1;
+            goto returnPoint;
+          }
+        }
+        free_vstring(tmpStr);
+      }
+
+    } /* Next unifNum */
+
+  } /* Next unifTrialWeight */
+
+  /* (The user should reject everything to test for this bug) */
+  if (unifTrials != unifCount) bug(1829);
+
+  /* No unification was selected */
+  returnValue = 3;
+  goto returnPoint;
+
+ returnPoint:
+  free_vstring(tmpStr); /* Deallocate */
+  free_nmbrString(unifWeight); /* Deallocate */
+  free_nmbrString(substResult); /* Deallocate */
+  return returnValue;
+
+} /* interactiveUnify */
+
+
+
+/* Automatically unify steps that have unique unification */
+/* Prints "congratulation" if congrats = 1 */
+void autoUnify(flag congrats)
+{
+  long step, plen;
+  char unifFlag;
+  flag somethingChanged = 1;
+  int pass;
+  nmbrString *schemeAPtr; /* Pointer only; not allocated */
+  nmbrString *schemeBPtr; /* Pointer only; not allocated */
+  pntrString_def(stateVector);
+  flag somethingNotUnified = 0;
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  schemeAPtr = NULL_NMBRSTRING;
+  schemeBPtr = NULL_NMBRSTRING;
+
+  plen = nmbrLen(g_ProofInProgress.proof);
+
+  while (somethingChanged) {
+    somethingChanged = 0;
+    for (step = 0; step < plen; step++) {
+      /* stepChanged = 0; */
+
+      for (pass = 0; pass < 3; pass++) {
+
+        switch (pass) {
+          case 0:
+            /* Check target vs. user */
+            schemeAPtr = (g_ProofInProgress.target)[step];
+            if (!nmbrLen(schemeAPtr))
+              print2("?Bad unification selected:  "
+                "A proof step should never be completely empty\n");
+            schemeBPtr = (g_ProofInProgress.user)[step];
+            break;
+          case 1:
+            /* Check source vs. user */
+            schemeAPtr = (g_ProofInProgress.source)[step];
+            schemeBPtr = (g_ProofInProgress.user)[step];
+            break;
+          case 2:
+            /* Check target vs. source */
+            schemeAPtr = (g_ProofInProgress.target)[step];
+            schemeBPtr = (g_ProofInProgress.source)[step];
+            break;
+        }
+        if (nmbrLen(schemeAPtr) && nmbrLen(schemeBPtr)) {
+          if (!nmbrEq(schemeAPtr, schemeBPtr)) {
+            g_unifTrialCount = 1; /* Reset unification timeout */
+            unifFlag = uniqueUnif(schemeAPtr, schemeBPtr, &stateVector);
+            if (unifFlag != 1) somethingNotUnified = 1;
+            if (unifFlag == 2) {
+              print2("A unification timeout occurred at step %ld.\n", step + 1);
+            }
+            if (!unifFlag) {
+              print2("Step %ld cannot be unified.  "
+                "THERE IS AN ERROR IN THE PROOF.\n", (long)(step + 1));
+              continue;
+            }
+            if (unifFlag == 1) {
+              /* Make substitutions to all steps */
+              makeSubstAll(stateVector);
+              somethingChanged = 1;
+              g_proofChangedFlag = 1; /* Flag for undo stack */
+              /* This message can be annoying. */
+              /*
+              print2("Step %ld was successfully unified.\n", (long)(step + 1));
+              */
+            }
+          }
+        }
+      } /* Next pass */
+    } /* Next step */
+  } /* End while somethingChanged */
+
+  purgeStateVector(&stateVector);
+
+  /* Check to see if proof is complete */
+  if (congrats) {
+    if (!somethingNotUnified) {
+      if (!nmbrElementIn(1, g_ProofInProgress.proof, -(long)'?')) {
+        print2(
+  "CONGRATULATIONS!  The proof is complete.  Use SAVE NEW_PROOF to save it.\n");
+        print2(
+  "Note:  The Proof Assistant does not detect $d violations.  After saving\n");
+        print2(
+  "the proof, you should verify it with VERIFY PROOF.\n");
+      }
+    }
+  }
+
+  return;
+
+} /* autoUnify */
+
+
+/* Make stateVector substitutions in all steps.  The stateVector must
+   contain the result of a valid unification. */
+void makeSubstAll(pntrString *stateVector) {
+
+  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated */
+  long plen, step;
+  flag tmpFlag;
+
+  plen = nmbrLen(g_ProofInProgress.proof);
+  for (step = 0; step < plen; step++) {
+
+    nmbrTmpPtr = (g_ProofInProgress.target)[step];
+    (g_ProofInProgress.target)[step] = makeSubstUnif(&tmpFlag, nmbrTmpPtr,
+      stateVector);
+    free_nmbrString(nmbrTmpPtr);
+
+    nmbrTmpPtr = (g_ProofInProgress.source)[step];
+    if (nmbrLen(nmbrTmpPtr)) {
+      (g_ProofInProgress.source)[step] = makeSubstUnif(&tmpFlag, nmbrTmpPtr,
+        stateVector);
+      free_nmbrString(nmbrTmpPtr);
+    }
+
+    nmbrTmpPtr = (g_ProofInProgress.user)[step];
+    if (nmbrLen(nmbrTmpPtr)) {
+      (g_ProofInProgress.user)[step] = makeSubstUnif(&tmpFlag, nmbrTmpPtr,
+        stateVector);
+      free_nmbrString(nmbrTmpPtr);
+    }
+
+  } /* Next step */
+  return;
+} /* makeSubstAll */
+
+/* Replace a dummy variable with a user-specified math string */
+void replaceDummyVar(long dummyVar, const nmbrString *mString)
+{
+  long numSubs = 0;
+  long numSteps = 0;
+  long plen, step, sym, slen;
+  flag stepChanged;
+  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated */
+
+  plen = nmbrLen(g_ProofInProgress.proof);
+  for (step = 0; step < plen; step++) {
+
+    stepChanged = 0;
+
+    nmbrTmpPtr = (g_ProofInProgress.target)[step];
+    slen = nmbrLen(nmbrTmpPtr);
+    for (sym = slen - 1; sym >= 0; sym--) {
+      if (nmbrTmpPtr[sym] == dummyVar + g_mathTokens) {
+        nmbrLet((nmbrString **)(&((g_ProofInProgress.target)[step])),
+            nmbrCat(nmbrLeft(nmbrTmpPtr, sym), mString,
+            nmbrRight(nmbrTmpPtr, sym + 2), NULL));
+        nmbrTmpPtr = (g_ProofInProgress.target)[step];
+        stepChanged = 1;
+        numSubs++;
+      }
+    } /* Next sym */
+
+    nmbrTmpPtr = (g_ProofInProgress.source)[step];
+    slen = nmbrLen(nmbrTmpPtr);
+    for (sym = slen - 1; sym >= 0; sym--) {
+      if (nmbrTmpPtr[sym] == dummyVar + g_mathTokens) {
+        nmbrLet((nmbrString **)(&((g_ProofInProgress.source)[step])),
+            nmbrCat(nmbrLeft(nmbrTmpPtr, sym), mString,
+            nmbrRight(nmbrTmpPtr, sym + 2), NULL));
+        nmbrTmpPtr = (g_ProofInProgress.source)[step];
+        stepChanged = 1;
+        numSubs++;
+      }
+    } /* Next sym */
+
+    nmbrTmpPtr = (g_ProofInProgress.user)[step];
+    slen = nmbrLen(nmbrTmpPtr);
+    for (sym = slen - 1; sym >= 0; sym--) {
+      if (nmbrTmpPtr[sym] == dummyVar + g_mathTokens) {
+        nmbrLet((nmbrString **)(&((g_ProofInProgress.user)[step])),
+            nmbrCat(nmbrLeft(nmbrTmpPtr, sym), mString,
+            nmbrRight(nmbrTmpPtr, sym + 2), NULL));
+        nmbrTmpPtr = (g_ProofInProgress.user)[step];
+        stepChanged = 1;
+        numSubs++;
+      }
+    } /* Next sym */
+
+    if (stepChanged) numSteps++;
+  } /* Next step */
+
+  if (numSubs) {
+    g_proofChangedFlag = 1; /* Flag to push 'undo' stack */
+    print2("%ld substitutions were made in %ld steps.\n", numSubs, numSteps);
+  } else {
+    print2("?The dummy variable $%ld is nowhere in the proof.\n", dummyVar);
+  }
+
+  return;
+} /* replaceDummyVar */
+
+/* Get subproof length of a proof, starting at endStep and going backwards.
+   Note that the first step is 0, the second is 1, etc. */
+long subproofLen(const nmbrString *proof, long endStep) {
+  long stmt, p, lvl;
+  lvl = 1;
+  p = endStep + 1;
+  while (lvl) {
+    p--;
+    lvl--;
+    if (p < 0) bug(1821);
+    stmt = proof[p];
+    if (stmt < 0) { /* Unknown step or local label */
+      continue;
+    }
+    if (g_Statement[stmt].type == (char)e_ ||
+        g_Statement[stmt].type == (char)f_) { /* A hypothesis */
+      continue;
+    }
+    lvl = lvl + g_Statement[stmt].numReqHyp;
+  }
+  return (endStep - p + 1);
+} /* subproofLen */
+
+
+/* If testStep has no dummy variables, return 0;
+   if testStep has isolated dummy variables (that don't affect rest of
+   proof), return 1;
+   if testStep has dummy variables used elsewhere in proof, return 2 */
+char checkDummyVarIsolation(long testStep) { /* 0=1st step, 1=2nd, etc. */
+  long proofLen, hyp, parentStep, tokpos, token;
+  char dummyVarIndicator;
+  long prfStep, parentStmt;
+  nmbrString_def(dummyVarList);
+  flag bugCheckFlag;
+  char hypType;
+
+  /* Get list of dummy variables */
+  for (tokpos = 0; tokpos < nmbrLen((g_ProofInProgress.target)[testStep]);
+      tokpos++) {
+    token = ((nmbrString *)((g_ProofInProgress.target)[testStep]))[tokpos];
+    if (token > g_mathTokens/*global*/) {
+      if (!nmbrElementIn(1, dummyVarList, token)) {
+        nmbrLet(&dummyVarList, nmbrAddElement(dummyVarList, token));
+      }
+    }
+  }
+  if (nmbrLen(dummyVarList) == 0) {
+    dummyVarIndicator = 0; /* No dummy variables */
+    goto RETURN_POINT;
+  }
+  /* g_ProofInProgress is global */
+  proofLen = nmbrLen(g_ProofInProgress.proof);
+  if (testStep == proofLen - 1) {
+    dummyVarIndicator = 1; /* Dummy variables don't affect rest of proof
+       (ignoring the subproof of testStep, which would be replaced by
+       a replaceStatement success later on) */
+    goto RETURN_POINT;
+  }
+
+  parentStep = getParentStep(testStep); /* testStep is a hyp of parent step */
+
+  /* Check if parent step has the dummy vars - if not, they will not
+     occur outside of the subproof of the parent step */
+  for (tokpos = 0; tokpos < nmbrLen((g_ProofInProgress.target)[parentStep]);
+      tokpos++) {
+    token = ((nmbrString *)((g_ProofInProgress.target)[parentStep]))[tokpos];
+    if (token > g_mathTokens/*global*/) {
+      if (nmbrElementIn(1, dummyVarList, token)) {
+        /* One of testStep's dummy vars occurs in the parent, so it
+           could be used elsewhere and is thus not "isolated" */
+        dummyVarIndicator = 2;
+        goto RETURN_POINT;
+      }
+    }
+  }
+  /* Check all hypotheses of parentStep other than testStep - if none have
+     testStep's dummy vars, then the dummy vars are "isolated" */
+  parentStmt = (g_ProofInProgress.proof)[parentStep];
+  if (parentStmt < 0) bug(1845);
+  if (g_Statement[parentStmt].type != (char)a_ &&
+      g_Statement[parentStmt].type != (char)p_) bug(1846);
+  bugCheckFlag = 0;
+  prfStep = parentStep - 1;
+  for (hyp = g_Statement[parentStmt].numReqHyp - 1; hyp >= 0; hyp--) {
+    if (hyp < g_Statement[parentStmt].numReqHyp - 1) { /* Skip computation at
+                                       first loop iteration */
+      /* Skip to proof step of previous hypothesis of parent step */
+      prfStep = prfStep - subproofLen(g_ProofInProgress.proof, prfStep);
+    }
+    if (prfStep == testStep) { /* Don't check the hypothesis of testStep */
+      bugCheckFlag = 1; /* Make sure we encountered it during scan */
+      continue;
+    }
+    hypType = g_Statement[g_Statement[parentStmt].reqHypList[hyp]].type;
+    if (hypType == (char)e_) {
+      /* Check whether (other) $e hyps of parent step have the dummy vars
+         of testStep */
+      for (tokpos = 0; tokpos < nmbrLen((g_ProofInProgress.target)[prfStep]);
+          tokpos++) {
+        token = ((nmbrString *)((g_ProofInProgress.target)[prfStep]))[tokpos];
+        if (token > g_mathTokens/*global*/) {
+          if (nmbrElementIn(1, dummyVarList, token)) {
+            /* One of testStep's dummy vars occurs in the parent, so it
+               could be used elsewhere and is thus not "isolated" */
+            dummyVarIndicator = 2;
+            goto RETURN_POINT;
+          }
+        }
+      } /* next tokpos */
+    } else if (hypType != (char)f_) {
+      bug(1848);
+    }
+  } /* next hyp */
+  if (bugCheckFlag == 0) bug(1847); /* Scan didn't encounter testStep */
+  /* If we passed the whole scan, the dummy vars are "isolated" */
+  dummyVarIndicator = 1;
+
+ RETURN_POINT:
+  free_nmbrString(dummyVarList); /* Deallocate */
+  return dummyVarIndicator;
+} /* checkDummyVarIsolation */
+
+
+/* Given a starting step, find its parent (the step it is a hypothesis of) */
+/* If the starting step is the last proof step, just return it */
+long getParentStep(long startStep) /* 0=1st step, 1=2nd, etc. */
+{
+  long proofLen;
+  long stackPtr, prfStep, stmt;
+
+  /* g_ProofInProgress is global */
+  proofLen = nmbrLen(g_ProofInProgress.proof);
+
+  stackPtr = 0;
+  for (prfStep = startStep + 1; prfStep < proofLen; prfStep++) {
+    stmt = (g_ProofInProgress.proof)[prfStep];
+    if (stmt < 0) { /* Unknown step or local label */
+      if (stmt != -(long)'?') bug(1842); /* We don't handle compact proofs */
+      stackPtr++;
+    } else if (g_Statement[stmt].type == (char)e_ ||
+          g_Statement[stmt].type == (char)f_) { /* A hypothesis */
+      stackPtr++;
+    } else {
+      if (g_Statement[stmt].type != (char)a_ &&
+          g_Statement[stmt].type != (char)p_) bug(1843);
+      stackPtr = stackPtr - g_Statement[stmt].numReqHyp + 1;
+      if (stackPtr <= 0) return prfStep; /* This identifies the parent step */
+    }
+  } /* next prfStep */
+  if (startStep != proofLen - 1) bug(1844); /* Didn't find parent... */
+  return startStep; /* ...unless we started with the last proof step */
+} /* getParentStep */
+
+
+/* This function puts numNewVars dummy variables, named "$nnn", at the end
+   of the g_MathToken array and modifies the global variable g_dummyVars. */
+/* Note:  The g_MathToken array will grow forever as this gets called;
+   it is never purged, as this might worsen memory fragmentation. */
+/* ???Should we add a purge function? */
+void declareDummyVars(long numNewVars)
+{
+
+  long i;
+
+  long saveTempAllocStack;
+  saveTempAllocStack = g_startTempAllocStack;
+  g_startTempAllocStack = g_tempAllocStackTop; /* For let() stack cleanup */
+
+  for (i = 0; i < numNewVars; i++) {
+
+    g_dummyVars++;
+    /* First, check to see if we need to allocate more g_MathToken memory */
+    if (g_mathTokens + 1 + g_dummyVars >= g_MAX_MATHTOKENS) {
+      /* The +1 above accounts for the dummy "$|$" boundary token */
+      /* Reallocate */
+      /* Add 1000 so we won't have to do this very often */
+      g_MAX_MATHTOKENS = g_MAX_MATHTOKENS + 1000;
+      g_MathToken = realloc(g_MathToken, (size_t)g_MAX_MATHTOKENS *
+        sizeof(struct mathToken_struct));
+      if (!g_MathToken) outOfMemory("#10 (mathToken)");
+    }
+
+    g_MathToken[g_mathTokens + g_dummyVars].tokenName = "";
+                                  /* Initialize vstring before let() */
+    let(&g_MathToken[g_mathTokens + g_dummyVars].tokenName,
+        cat("$", str((double)g_dummyVars), NULL));
+    g_MathToken[g_mathTokens + g_dummyVars].length =
+        (long)strlen(g_MathToken[g_mathTokens + g_dummyVars].tokenName);
+    g_MathToken[g_mathTokens + g_dummyVars].scope = g_currentScope;
+    g_MathToken[g_mathTokens + g_dummyVars].active = 1;
+    g_MathToken[g_mathTokens + g_dummyVars].tokenType = (char)var_;
+    g_MathToken[g_mathTokens + g_dummyVars].tmp = 0;
+
+  }
+
+  g_startTempAllocStack = saveTempAllocStack;
+
+  return;
+
+} /* declareDummyVars */
+
+
+
+/* Copy inProofStruct to outProofStruct.  A proof structure contains
+   the state of the proof in the Proof Assistant MM-PA.  The one
+   used by MM-PA is the global g_ProofInProgress.  This function lets
+   it be copied for temporary storage and retrieval. */
+void copyProofStruct(struct pip_struct *outProofStruct,
+    struct pip_struct inProofStruct)
+{
+  long proofLen, j;
+  /* First, make sure the output structure is empty to prevent memory
+     leaks. */
+  deallocProofStruct(outProofStruct);
+
+  /* Get the proof length of the input structure */
+  proofLen = nmbrLen(inProofStruct.proof);
+  if (proofLen == 0) bug(1854); /* An empty proof should never occur
+    here; proof should have at least one step (possibly unknown) */
+  if (proofLen == 0) return;  /* The input proof is empty */
+  nmbrLet(&((*outProofStruct).proof), inProofStruct.proof);
+
+  /* Allocate pointers to empty nmbrStrings that will be assigned
+     the proof step contents */
+  pntrLet(&((*outProofStruct).target), pntrNSpace(proofLen));
+  pntrLet(&((*outProofStruct).source), pntrNSpace(proofLen));
+  pntrLet(&((*outProofStruct).user), pntrNSpace(proofLen));
+
+  if (proofLen != pntrLen(inProofStruct.target)) bug(1855);
+  if (proofLen != pntrLen(inProofStruct.source)) bug(1856);
+  if (proofLen != pntrLen(inProofStruct.user)) bug(1857);
+  /* Copy the individual proof step contents */
+  for (j = 0; j < proofLen; j++) {
+    nmbrLet((nmbrString **)(&(((*outProofStruct).target)[j])),
+        (inProofStruct.target)[j]);
+    nmbrLet((nmbrString **)(&(((*outProofStruct).source)[j])),
+        (inProofStruct.source)[j]);
+    nmbrLet((nmbrString **)(&(((*outProofStruct).user)[j])),
+        (inProofStruct.user)[j]);
+  }
+  return;
+} /* copyProofStruct */
+
+
+/* Create an initial proof structure needed for the Proof Assistant, given
+   a starting proof.  Normally, proofStruct is the global g_ProofInProgress,
+   although we've made it an argument to help modularize the function.  There
+   are still globals such as g_pipDummyVars, updated by various functions. */
+void initProofStruct(struct pip_struct *proofStruct, const nmbrString *proof,
+    long proveStmt)
+{
+  nmbrString_def(tmpProof);
+  long plen, step;
+  /* Right now, only non-packed proofs are handled. */
+  nmbrLet(&tmpProof, nmbrUnsquishProof(proof));
+
+  /* Assign initial proof structure */
+  if (nmbrLen((*proofStruct).proof)) bug(1876); /* Should've been deallocated */
+  nmbrLet(&((*proofStruct).proof), tmpProof);
+  plen = nmbrLen((*proofStruct).proof);
+  pntrLet(&((*proofStruct).target), pntrNSpace(plen));
+  pntrLet(&((*proofStruct).source), pntrNSpace(plen));
+  pntrLet(&((*proofStruct).user), pntrNSpace(plen));
+  nmbrLet((nmbrString **)(&(((*proofStruct).target)[plen - 1])),
+      g_Statement[proveStmt].mathString);
+  g_pipDummyVars = 0; /* (Global) number of dummy (work) $nn variables, updated
+        by function calls below */
+  /* Assign known subproofs */
+  assignKnownSubProofs();
+  /* Initialize remaining steps */
+  for (step = 0; step < plen/*proof length*/; step++) {
+    if (!nmbrLen(((*proofStruct).source)[step])) {
+      initStep(step);
+    }
+  }
+  /* Unify whatever can be unified */
+  autoUnify(0); /* 0 means no "congrats" message */
+
+  /* Deallocate memory */
+  free_nmbrString(tmpProof);
+  return;
+} /* initProofStruct */
+
+
+/* Deallocate memory used by a proof structure and set it to the initial
+   state.  A proof structure contains the state of the proof in the Proof
+   Assistant MM-PA.  It is assumed that proofStruct was declared with:
+     struct pip_struct proofStruct = {
+       NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING, NULL_PNTRSTRING };
+   This function sets it back to that initial assignment. */
+void deallocProofStruct(struct pip_struct *proofStruct)
+{
+  long proofLen, j;
+  /* Deallocate proof structure */
+  proofLen = nmbrLen(proofStruct->proof);
+  if (proofLen == 0) return;  /* Already deallocated */
+  free_nmbrString(proofStruct->proof);
+  for (j = 0; j < proofLen; j++) {
+    free_nmbrString(*(nmbrString **)(&proofStruct->target[j]));
+    free_nmbrString(*(nmbrString **)(&proofStruct->source[j]));
+    free_nmbrString(*(nmbrString **)(&proofStruct->user[j]));
+  }
+  free_pntrString(proofStruct->target);
+  free_pntrString(proofStruct->source);
+  free_pntrString(proofStruct->user);
+  return;
+} /* deallocProofStruct */
+
+
+#define DEFAULT_UNDO_STACK_SIZE 20
+/* This function handles the UNDO/REDO commands.  It is called
+   with action PUS_INIT then with PUS_PUSH upon entering MM-PA.  It is
+   called with PUS_INIT upon exiting MM-PA.  It should be called with
+   PUS_PUSH after every command changing the proof.
+
+   PUS_UNDO and PUS_REDO are called by the UNDO and REDO CLI commands.
+
+   PUS_NEW_SIZE is called by the SET UNDO command to change the size
+   of the undo stack.  SET UNDO may be called outside or inside MM-PA;
+   in the latter case, the current UNDO stack is aborted (discarded).
+   If inside of MM-PA, PUS_PUSH must be called after PUS_NEW_SIZE.
+
+   PUS_GET_SIZE does not affect the stack; it returns the maximum UNDOs
+   PUS_GET_STATUS does not affect the stack; it returns 0 if it is
+     safe to exit MM-PA without saving (assuming there was no SAVE NEW_PROOF
+     while the UNDO stack was not empty).
+
+   Inputs:
+   proofStruct - must be current proof in progress (&g_ProofInProgress)
+       for PUS_PUSH, PUS_UNDO, and PUS_REDO actions; may be NULL for
+       other actions
+   action - the action the function should perform
+   info - description of command which will be reversed by UNDO; required
+       for all PUS_PUSH actions (except the first upon entering MM-PA that
+       loads the starting proof structure).  It is ignored for all other
+       actions and may be the empty string.
+   newSize - for PUS_NEW_SIZE, the new size (>= 0).
+
+   Return value = see PUS_GET_SIZE and PUS_GET_STATUS above
+ */
+long processUndoStack(struct pip_struct *proofStruct,
+    char action,  /* PUS_INIT 1 Deallocates and initializes undo stack
+                     PUS_PUSH 2 Pushes the current proof state onto the stack
+                     PUS_UNDO 3 Restores the previous proof state
+                     PUS_REDO 4 Reverses PUS_UNDO
+                     PUS_NEW_SIZE 5 Changes size of stack
+                     PUS_GET_SIZE 6 Returns stack size
+                     PUS_GET_STATUS 7 Returns proof changed status */
+    vstring info, /* Info to print upon PUS_UNDO or PUS_REDO */
+    long newSize) /* New maximum number of UNDOs for PUS_NEW_SIZE */
+{
+
+  static struct pip_struct *proofStack = NULL;
+  static pntrString_def(infoStack); /* UNDO/REDO command info */
+  static long stackSize = DEFAULT_UNDO_STACK_SIZE; /* Change w/ SET UNDO */
+  static long stackEnd = -1;
+  static long stackPtr = -1;
+  static flag firstTime = 1;
+  static flag stackOverflowed = 0; /* For user msg and prf chg determination */
+  static flag stackAborted = 0;  /* For proof changed determination */
+  long i;
+
+  if (stackPtr < -1 || stackPtr > stackEnd || stackPtr > stackSize - 1
+      || stackEnd < -1 || stackEnd > stackSize -1 ) {
+    bug(1858);
+  }
+
+  if (firstTime == 1) { /* First time ever called */
+    firstTime = 0;
+    proofStack = malloc((size_t)(stackSize) * sizeof(struct pip_struct));
+    if (!proofStack) bug(1859);
+    for (i = 0; i < stackSize; i++) { /* Set to empty proofs */
+      proofStack[i].proof = NULL_NMBRSTRING;
+      proofStack[i].target = NULL_PNTRSTRING;
+      proofStack[i].source = NULL_PNTRSTRING;
+      proofStack[i].user = NULL_PNTRSTRING;
+    }
+    pntrLet(&infoStack, pntrSpace(stackSize)); /* Set to empty vstrings */
+  }
+
+  if (!proofStack) bug(1860);
+
+  switch (action) {
+    case PUS_GET_SIZE:
+    case PUS_GET_STATUS:
+      break;  /* Do nothing; just return stack size */
+
+    case PUS_INIT:
+    case PUS_NEW_SIZE:
+      /* Deallocate old contents */
+      for (i = 0; i <= stackEnd; i++) {
+        deallocProofStruct(&proofStack[i]);
+        free_vstring(*(vstring *)(&infoStack[i]));
+      }
+
+      /* If UNDOs weren't exhausted and thus are abandoned due to size change,
+         this flag will prevent the program from falsely thinking the proof
+         hasn't changed */
+      if (action == PUS_NEW_SIZE) {
+        if (stackPtr > 0) {
+          print2("The previous UNDOs are no longer available.\n");
+          stackAborted = 1;
+        }
+        /* Since we're going to reset stackOverflowed, save its state
+           in stackAborted so we don't falsely exit MM-PA without saving */
+        if (stackOverflowed) stackAborted = 1;
+      }
+
+      stackEnd = -1; /* Nothing in UNDO stack now */
+      stackPtr = -1;
+      stackOverflowed = 0;
+
+      if (action == PUS_INIT) {
+        stackAborted = 0;
+        break;
+      }
+
+      /* Re-size the stack */
+      /* Free the old stack (pntrLet() below will free old infoStack) */
+      free(proofStack);
+      /* Reinitialize new stack */
+      stackSize = newSize + 1;
+      if (stackSize < 1) bug(1867);
+      proofStack = malloc((size_t)(stackSize) * sizeof(struct pip_struct));
+      if (!proofStack) bug(1861);
+      for (i = 0; i < stackSize; i++) { /* Set to empty proofs */
+        proofStack[i].proof = NULL_NMBRSTRING;
+        proofStack[i].target = NULL_PNTRSTRING;
+        proofStack[i].source = NULL_PNTRSTRING;
+        proofStack[i].user = NULL_PNTRSTRING;
+      }
+      pntrLet(&infoStack, pntrSpace(stackSize)); /* Set to empty vstrings */
+      break;
+
+    case PUS_PUSH:
+      /* Warning: PUS_PUSH must be called upon entering Proof Assistant to put
+         the original proof into stack location 0.  It also must be
+         called after PUS_NEW_SIZE if inside of MM-PA. */
+
+      /* Any new command after UNDO should erase the REDO part */
+      if (stackPtr < stackEnd) {
+        for (i = stackPtr + 1; i <= stackEnd; i++) {
+          deallocProofStruct(&(proofStack[i]));
+          free_vstring(*(vstring *)(&infoStack[i]));
+        }
+        stackEnd = stackPtr;
+      }
+
+      /* If the stack is full, deallocate bottom of stack and move things
+         down to make room for new stack entry */
+      if (stackPtr == stackSize - 1) {
+        stackOverflowed = 1; /* To  modify user message if UNDO exhausted */
+        deallocProofStruct(&(proofStack[0])); /* Deallocate the bottom entry */
+        free_vstring(*(vstring *)(&(infoStack[0])));
+        for (i = 0; i < stackSize - 1; i++) {
+          /* Instead of
+               "copyProofStruct(&(proofStack[i]), proofStack[i + 1]);
+            (which involves de/reallocation), copy the pointers directly
+            for improved speed */
+          proofStack[i].proof = proofStack[i + 1].proof;
+          proofStack[i].target = proofStack[i + 1].target;
+          proofStack[i].source = proofStack[i + 1].source;
+          proofStack[i].user = proofStack[i + 1].user;
+          infoStack[i] = infoStack[i + 1];
+        }
+        /* Now initialize the top of the stack pointers (don't deallocate since
+           its old contents are pointed to by the next one down) */
+        proofStack[stackPtr].proof = NULL_NMBRSTRING;
+        proofStack[stackPtr].target = NULL_PNTRSTRING;
+        proofStack[stackPtr].source = NULL_PNTRSTRING;
+        proofStack[stackPtr].user = NULL_PNTRSTRING;
+        infoStack[stackPtr] = "";
+        stackPtr--;
+        stackEnd--;
+        if (stackPtr != stackSize - 2 || stackPtr != stackEnd) bug(1862);
+      }
+
+      /* Add the new command to the stack */
+      stackPtr++;
+      stackEnd++;
+      if (stackPtr != stackEnd) bug(1863);
+      copyProofStruct(&(proofStack[stackPtr]), *proofStruct);
+      let((vstring *)(&(infoStack[stackPtr])), info);
+      break;
+
+    case PUS_UNDO:
+      if (stackPtr < 0) bug(1864); /* A first PUSH wasn't called upon entry to
+                   Proof Assistant (MM-PA) */
+      if (stackPtr == 0) {
+        if (stackOverflowed == 0) {
+          print2("There is nothing to undo.\n");
+        } else {
+          printLongLine(cat("Exceeded maximum of ", str((double)stackSize - 1),
+              " UNDOs.  To increase the number, see HELP SET UNDO.",
+              NULL), "", " ");
+        }
+        break;
+      }
+
+      /* Print the Undid message for the most recent action */
+      printLongLine(cat("Undid:  ", infoStack[stackPtr],
+              NULL), "", " ");
+      stackPtr--;
+      /* Restore the version of the proof before that action */
+      copyProofStruct(&(*proofStruct), proofStack[stackPtr]);
+      break;
+
+    case PUS_REDO:
+      if (stackPtr == stackEnd) {
+        print2("There is nothing more to redo.\n");
+        break;
+      }
+
+      /* Move up stack pointer and return its entry. */
+      stackPtr++;
+      /* Restore the last undo and print the message for its action */
+      copyProofStruct(&(*proofStruct), proofStack[stackPtr]);
+      printLongLine(cat("Redid:  ", infoStack[stackPtr],
+              NULL), "", " ");
+      break;
+
+    default:
+      bug(1865);
+  } /* end switch(action) */
+
+  if (stackPtr < -1 || stackPtr > stackEnd || stackPtr > stackSize - 1
+      || stackEnd < -1 || stackEnd > stackSize -1 ) {
+    bug(1866);
+  }
+
+  if (action == PUS_GET_STATUS) {
+    /* Return the OR of all conditions which might indicate that the
+       proof has changed, so that it may not be safe to exit MM-PA without
+       a warning to save the proof */
+    return (stackOverflowed || stackAborted || stackPtr != 0);
+  } else {
+    return stackSize - 1;
+  }
+} /* processUndoStack */
diff --git a/src/mmpfas.h b/src/mmpfas.h
new file mode 100644
index 0000000..5977439
--- /dev/null
+++ b/src/mmpfas.h
@@ -0,0 +1,215 @@
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMPFAS_H_
+#define METAMATH_MMPFAS_H_
+
+/*! \file */
+
+#include "mmvstr.h"
+#include "mmdata.h"
+
+extern long g_proveStatement; /*!< The statement to be proved */
+extern flag g_proofChangedFlag; /*!< Flag to push 'undo' stack */
+
+extern long g_userMaxProveFloat; /*!< Upper limit for proveFloating */
+
+extern long g_dummyVars; /*!< The number of dummy variables currently declared */
+extern long g_pipDummyVars; /*!< Number of dummy vars used by proof in progress */
+
+/*!< Structure for holding a proof in progress.
+  \note This structure should be deallocated after use. */
+struct pip_struct {
+  nmbrString *proof; /*!< The proof itself */
+  pntrString *target; /*!< Left hand side of = in display */
+  pntrString *source; /*!< Right hand side of = in display */
+  pntrString *user; /*!< User-specified math string assignments to step */
+};
+extern struct pip_struct g_ProofInProgress;
+
+/*! Interactively select statement assignments that match
+  \param maxEssential the maximum number of essential hypotheses that a
+    statement may have in order to be included in the matched list. */
+void interactiveMatch(long step, long maxEssential);
+
+/*! Assign a statement to an unknown proof step */
+void assignStatement(long statemNum, long step);
+
+/*! Find proof of formula by using the replaceStatement() algorithm i.e.
+   see if any statement matches current step AND each of its hypotheses
+   matches a proof in progress hypothesis or some step already in the proof.
+   If a proof is found, it is returned, otherwise an empty (length 0) proof is
+   returned.
+  \note The caller must deallocate the returned nmbrString. */
+nmbrString *proveByReplacement(long prfStmt,
+    long prfStep, /*!< 0 means step 1 */
+    flag noDistinct, /*!< 1 means don't try statements with $d's */
+    flag dummyVarFlag, /*!< 0 means no dummy vars are in prfStmt */
+    flag searchMethod, /*!< 1 means to try proveFloating on $e's also */
+    long improveDepth,
+    flag overrideFlag, /*!< 1 means to override usage locks */
+    flag mathboxFlag /*!< 1 means allow mathboxes */
+    );
+
+nmbrString *replaceStatement(long replStatemNum,
+    long prfStep,
+    long provStmtNum,
+    flag subProofFlag, /*!< If 1, then scan only subproof at prfStep to look for
+   matches, instead of whole proof, for faster speed (used by MINIMIZE_WITH) */
+    flag noDistinct, /*!< 1 means don't try statements with $d's */
+    flag searchMethod, /*!< 1 means to try proveFloating on $e's also */
+    long improveDepth,
+    flag overrideFlag, /*!< 1 means to override usage locks */
+    flag mathboxFlag /*!< 1 means allow mathboxes */
+    );
+
+/*! This function identifies all steps in the proof in progress that (1) are
+   independent of step refStep, (2) have no dummy variables, (3) are
+   not $f's or $e's, and (4) have subproofs that are complete
+   (no unassigned steps).  A "Y" is returned for each such step,
+   and "N" is returned for all other steps.  The "Y" steps can be used
+   for scanning for useful subproofs outside of the subProof of refStep.
+   \note The caller must deallocate the returned vstring. */
+vstring getIndepKnownSteps(long proofStmt, long refStep);
+
+/*! This function classifies each proof step in g_ProofInProgress.proof
+   as known or unknown ('K' or 'U' in the returned string) depending
+   on whether the step has a completely known subproof.
+   \note The caller must deallocate the returned vstring. */
+vstring getKnownSubProofs(void);
+
+/*! Add a subproof in place of an unknown step to g_ProofInProgress.  The
+   .target, .source, and .user fields are initialized to empty (except
+   .target of the deleted unknown step is retained). */
+void addSubProof(const nmbrString *subProof, long step);
+
+/*! This function eliminates any occurrences of statement sourceStmtNum in the
+   targetProof by substituting it with the proof of sourceStmtNum.  An empty
+   nmbrString is returned if there was an error.
+
+   Normally, targetProof is the global g_ProofInProgress.proof.  However,
+   we make it an argument in case in the future we'd like to do this
+   outside of the proof assistant.
+  \note The caller must deallocate the returned nmbrString. */
+nmbrString *expandProof(const nmbrString *targetProof, long sourceStmtNum);
+
+/*! Delete a subproof starting (in reverse from) step.  The step is replaced
+   with an unknown step, and its .target field is retained. */
+void deleteSubProof(long step);
+
+/*! Check to see if a statement will match the g_ProofInProgress.target (or .user)
+   of an unknown step.  Returns 1 if match, 0 if not, 2 if unification
+   timed out. */
+char checkStmtMatch(long statemNum, long step);
+
+/*! Check to see if a (user-specified) math string will match the
+   g_ProofInProgress.target (or .user) of an step.  Returns 1 if match, 0 if
+   not, 2 if unification timed out. */
+char checkMStringMatch(const nmbrString *mString, long step);
+
+/*! Find proof of formula or simple theorem (no new vars in $e's)
+
+  \param maxEDepth the maximum depth at which statements with $e hypotheses are
+    considered.  A value of 0 means none are considered. */
+nmbrString *proveFloating(const nmbrString *mString, long statemNum, long maxEDepth,
+    long step, flag noDistinct,
+    flag overrideFlag, /*!< 0 means respect usage locks
+                         1 means to override usage locks
+                         2 means override silently */
+    flag mathboxFlag /*!< 1 means allow mathboxes */
+);
+
+/*! This function does quick check for some common conditions that prevent
+   a trial statement (scheme) from being unified with a given instance.
+   Return value 0 means it can't be unified, 1 means it might be unifiable. */
+char quickMatchFilter(long trialStmt, const nmbrString *mString,
+    long dummyVarFlag /*!< 0 if no dummy vars in mString */);
+
+/*! Shorten proof by using specified statement. */
+void minimizeProof(long repStatemNum, long prvStatemNum, flag allowGrowthFlag);
+
+/*! Initialize g_ProofInProgress.source of the step, and .target of all
+   hypotheses, to schemes using new dummy variables. */
+void initStep(long step);
+
+/*! Look for completely known subproofs in g_ProofInProgress.proof and
+   assign g_ProofInProgress.target and .source.  Calls assignKnownSteps(). */
+void assignKnownSubProofs(void);
+
+/*! This function assigns math strings to all steps (g_ProofInProgress.target and
+   .source fields) in a subproof with all known steps. */
+void assignKnownSteps(long startStep, long sbProofLen);
+
+/*! \brief Interactive unify a step.  Calls interactiveUnify().
+   If messageFlag is 1, a message will be issued if the
+   step is already unified.   If messageFlag is 0, show the step #
+   being unified.  If messageFlag is 2, don't print step #, and do nothing
+   if step is already unified. */
+void interactiveUnifyStep(long step, char messageFlag);
+
+/*! Interactively select one of several possible unifications
+  \returns * 0 = no unification possible
+           * 1 = unification was selected; held in stateVector
+           * 2 = unification timed out
+           * 3 = no unification was selected */
+char interactiveUnify(const nmbrString *schemeA, const nmbrString *schemeB,
+    pntrString **stateVector);
+
+/*! Automatically unify steps with unique unification */
+void autoUnify(flag congrats);
+
+/*! Make stateVector substitutions in all steps.  The stateVector must
+   contain the result of a valid unification. */
+void makeSubstAll(pntrString *stateVector);
+
+/*! Replace a dummy variable with a user-specified math string */
+void replaceDummyVar(long dummyVar, const nmbrString *mString);
+
+/*! Get subproof length of a proof, starting at endStep and going backwards */
+long subproofLen(const nmbrString *proof, long endStep);
+
+/*! If testStep has no dummy variables, return 0;
+   if testStep has isolated dummy variables (that don't affect rest of
+   proof), return 1;
+   if testStep has dummy variables used elsewhere in proof, return 2 */
+char checkDummyVarIsolation(long testStep /*!< 0=1st step, 1=2nd, etc. */);
+
+/*! Given a starting step, find its parent (the step it is a hypothesis of)
+
+  If the starting step is the last proof step, just return it */
+long getParentStep(long startStep /*!< 0=1st step, 1=2nd, etc. */);
+
+/*! Adds a dummy variable to end of mathToken array
+  \note it now grows forever, but purging it might worsen fragmentation */
+void declareDummyVars(long numNewVars);
+
+/*! Copies the Proof Assistant proof state */
+void copyProofStruct(struct pip_struct *outProofStruct,
+    struct pip_struct inProofStruct);
+
+/*! Initializes the Proof Assistant proof state */
+void initProofStruct(struct pip_struct *proofStruct, const nmbrString *proof,
+    long proveStatement);
+
+/*! Clears the Proof Assistant proof state */
+void deallocProofStruct(struct pip_struct *proofStruct);
+
+/* Actions for processUndoStack() */
+#define PUS_INIT 1
+#define PUS_PUSH 2
+#define PUS_UNDO 3
+#define PUS_REDO 4
+#define PUS_NEW_SIZE 5
+#define PUS_GET_SIZE 6
+#define PUS_GET_STATUS 7
+
+/*! Handle the PUSH, UNDO, and REDO commands */
+long processUndoStack(struct pip_struct *proofStruct,
+    char action,
+    vstring info, /*!< Info to print upon UNDO or REDO */
+    long newStackSize /*!< Used only by NEW_SIZE */);
+
+#endif /* METAMATH_MMPFAS_H_ */
diff --git a/mmunif.c b/src/mmunif.c
similarity index 90%
rename from mmunif.c
rename to src/mmunif.c
index 9364f4d..0f5ab59 100644
--- a/mmunif.c
+++ b/src/mmunif.c
@@ -1,1734 +1,1709 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* mmunif.c - Unifications for proof assistant (note: unifications for normal
-   proof verification is done in mmveri.c) */
-
-/*
-This module deals with an object called the stateVector, which is a pntrString
-of 16 pointers (called entries 0 through 15 below) to either other pntrStrings
-or to nmbrStrings.  In the description, data in stateVector may be referred to
-by the local C variable the data is typically assigned to, such as
-"unkVarsLen".  The word "variable" in the context of scheme content refers
-to temporary (or "work" or "dummy") variables $1, $2, etc.  The entries are not
-organized in logical order for historical reasons, e.g. entry 11 logically
-comes first.
-
-Entry 11 is a nmbrString of length 4 holding individual parameters.
-
-11[0] is the total number of variables ($1, $2, etc.) in schemeA and schemeB,
-i.e. the two schemes being unified.
-
-  unkVarsLen = ((nmbrString *)((*stateVector)[11]))[0];
-
-11[1] or stackTop is the number of variables (minus 1) that will require
-substitutions in order to perform the unification.  Warning:  stackTop may be
--1, which could be confused with "end of nmbrString" by some nmbrString
-functions.
-
-  stackTop = ((nmbrString *)((*stateVector)[11]))[1];
-
-11[2] is the number of variables in schemeA, used by oneDirUnif() (only).
-
-  schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
-
-11[3] is the number of entries in the "Henty filter", used by unifyH() (only).
-
-  g_hentyFilterSize = ((nmbrString *)((*stateVector)[11]))[3];
-
-Entry 8 is the result of unifying schemeA and schemeB, which are the two
-schemes being unified.
-
-  unifiedScheme = (nmbrString *)((*stateVector)[8]);
-
-Entries 0 through 3 each have length unkVarsLen.  Entry 1 is a list of token
-numbers for the temporary variables substituted in the unification.
-
-Entry 0 has all variables ($1, $2, etc.) in schemeA and schemeB.
-
-  unkVars = (nmbrString *)((*stateVector)[0]);
-
-In entries 1 through 3, only variables 0 through stackTop (inclusive) have
-meaning.  These entries, along with unifiedScheme, determine what variables
-were substituted and there substitutions.
-
-Entry 1 is the list of variables that were substituted.
-Entry 2 is the location of the substitution in unifiedScheme, for each variable
-in entry 1.
-Entry 3 is the length of the substitution for each variable in entry 1.
-
-  stackUnkVar = (nmbrString *)((*stateVector)[1]);
-  stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
-  stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
-
-Entries 4 thru 7 each point to unkVarsLen nmbrString's.  These entries save the
-data needed to resume unification at any point.  Entries 4 and 5 are
-nmbrString's of length unkVarsLen.  Entries 6 and 7 will have variable length.
-Only the first stackTop+1 nmbrString's have meaning.  Note that stackTop may be
--1.
-
-  stackSaveUnkVarStart = (pntrString *)((*stateVector)[4]);
-  stackSaveUnkVarLen = (pntrString *)((*stateVector)[5]);
-  stackSaveSchemeA = (pntrString *)((*stateVector)[6]);
-  stackSaveSchemeB = (pntrString *)((*stateVector)[7]);
-
-Entries 9 and 10 save the contents of 2 and 3 in oneDirUnif (only)
-
-  nmbrLet((nmbrString **)(&(*stateVector)[9]),
-      (nmbrString *)((*stateVector)[2]));
-  nmbrLet((nmbrString **)(&(*stateVector)[10]),
-      (nmbrString *)((*stateVector)[3]));
-
-Entries 12 through 15 hold the "Henty filter", i.e. a list of all "normalized"
-unifications so far.  Used by unifyH() (only).  Each entry 12 through 15 is a
-list of pointers of length g_hentyFilterSize, each pointing to g_hentyFilterSize
-nmbrString's.  The Henty filter eliminates redundant equivalent unifications.
-
-Entry 12[i] is a list of variables substituted by the normalized unification.
-Entry 13[i] is the start of each substitution in hentySubstList.
-Entry 14[i] is the length of each substitution in hentySubstList.
-Entry 15[i] is the unified scheme that resulted from the particular unification.
-Note:  i = 0 through g_hentyFilterSize-1 below.
-
-  hentyVars = (nmbrString *)(((pntrString *)((*stateVector)[12]))[i]);
-  hentyVarStart = (nmbrString *)(((pntrString *)((*stateVector)[13]))[i]);
-  hentyVarLen = (nmbrString *)(((pntrString *)((*stateVector)[14]))[i]);
-  hentySubstList = (nmbrString *)(((pntrString *)((*stateVector)[15]))[i]);
-
-*/
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <setjmp.h>
-#include <time.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mminou.h"
-#include "mmpars.h"
-#include "mmunif.h"
-#include "mmpfas.h" /* 8/28/99 For proveStatement global variable */
-
-/*long g_minSubstLen = 0;*/ /* User-settable value - 0 or 1 */
-long g_minSubstLen = 1; /* It was decided to disallow empty subst. by default
-                         since most formal systems don't need it */
-long g_userMaxUnifTrials = 100000; /* Initial value */
-           /* User-defined upper limit (# backtracks) for unification trials */
-           /* 1-Jun-04 nm Changed g_userMaxUnifTrials from 1000 to 100000, which
-              is not a problem with today's faster computers.  This results in
-              fewer annoying "Unification timed out" messages, but the drawback
-              is that (rarely) there may be hundreds of unification
-              choices for the user (which the user can quit from though). */
-long g_unifTrialCount = 0;
-                    /* 0 means don't time out; 1 means start counting trials */
-long g_unifTimeouts = 0; /* Number of timeouts so far for this command */
-flag g_hentyFilter = 1; /* Default to ON (turn OFF for debugging). */
-flag g_bracketMatchInit = 0; /* Global so eraseSource() (mmcmds.c) can clr it */
-
-/* Additional local prototypes */
-void hentyNormalize(nmbrString **hentyVars, nmbrString **hentyVarStart,
-    nmbrString **hentyVarLen, nmbrString **hentySubstList,
-    pntrString **stateVector);
-flag hentyMatch(nmbrString *hentyVars, nmbrString *hentyVarStart,
-    /*nmbrString *hentyVarLen,*/ nmbrString *hentySubstList,
-    pntrString **stateVector);
-void hentyAdd(nmbrString *hentyVars, nmbrString *hentyVarStart,
-    nmbrString *hentyVarLen, nmbrString *hentySubstList,
-    pntrString **stateVector);
-
-
-/* For heuristics */
-int maxNestingLevel = -1;
-int nestingLevel = 0;
-
-/* 8/29/99 For improving rejection of impossible substitutions */
-/* 1-Oct-2017 nm Made g_firstConst global so eraseSource() can clear it */
-/* 2-Oct-2017 nm Made them all global so valgrind won't complain */
-nmbrString *g_firstConst = NULL_NMBRSTRING;
-nmbrString *g_lastConst = NULL_NMBRSTRING;
-nmbrString *g_oneConst = NULL_NMBRSTRING;
-
-
-
-/* Typical call:
-     nmbrStringXxx = makeSubstUnif(&newVarFlag,trialScheme,
-         stateVector);
-     Call this after calling unify().
-     trialScheme should have the same unknown variable names as were in
-         the schemes given to unify().
-     nmbrStringXxx will have these unknown variables substituted with
-         the result of the unification.
-     newVarFlag is 1 if there are new $nn variables in nmbrStringXxx.
-   The caller must deallocate the returned nmbrString.
-*/
-nmbrString *makeSubstUnif(flag *newVarFlag,
-    nmbrString *trialScheme, pntrString *stateVector)
-{
-  long p,q,i,j,k,m,tokenNum;
-  long schemeLen;
-  nmbrString *result = NULL_NMBRSTRING;
-  nmbrString *stackUnkVar = NULL_NMBRSTRING;
-  nmbrString *unifiedScheme; /* Pointer only - not allocated */
-  nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
-  nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
-  long stackTop;
-/*E*/long d;
-/*E*/vstring tmpStr = "";
-/*E*/let(&tmpStr,tmpStr);
-
-  stackTop = ((nmbrString *)(stateVector[11]))[1];
-  nmbrLet(&stackUnkVar,nmbrLeft((nmbrString *)(stateVector[1]), stackTop + 1));
-  stackUnkVarStart = (nmbrString *)(stateVector[2]); /* stackUnkVarStart */
-  stackUnkVarLen = (nmbrString *)(stateVector[3]); /* stackUnkVarLen */
-  unifiedScheme = (nmbrString *)(stateVector[8]);
-
-/*E*/if(db7)print2("Entered makeSubstUnif.\n");
-/*E*/if(db7)printLongLine(cat("unifiedScheme is ",
-/*E*/    nmbrCvtMToVString(unifiedScheme), NULL), "", " ");
-/*E*/if(db7)printLongLine(cat("trialScheme is ",
-/*E*/    nmbrCvtMToVString(trialScheme), NULL), "", " ");
-/*E*/if(db7)print2("stackTop is %ld.\n",stackTop);
-/*E*/for (d = 0; d <= stackTop; d++) {
-/*E*/  if(db7)print2("Unknown var %ld is %s.\n",d,
-/*E*/      g_MathToken[stackUnkVar[d]].tokenName);
-/*E*/  if(db7)print2("  Its start is %ld; its length is %ld.\n",
-/*E*/      stackUnkVarStart[d],stackUnkVarLen[d]);
-/*E*/}
-  schemeLen = nmbrLen(trialScheme);
-  /* Make the substitutions into trialScheme. */
-  /* First, calculate the length of the final result */
-  q = 0;
-  *newVarFlag = 0; /* Flag that there are new variables in the output string */
-/*E*/if(db7)print2("schemeLen is %ld.\n",schemeLen);
-  for (p = 0; p < schemeLen; p++) {
-/*E*/if(db7)print2("p is %ld.\n",p);
-    tokenNum = trialScheme[p];
-/*E*/if(db7)print2("token is %s, tokenType is %ld\n",g_MathToken[tokenNum].tokenName,
-/*E*/  (long)g_MathToken[tokenNum].tokenType);
-    if (g_MathToken[tokenNum].tokenType == (char)con_) {
-      q++;
-    } else {
-      if (tokenNum > g_mathTokens) {
-        /* It's a candidate for substitution */
-        m = nmbrElementIn(1,stackUnkVar,tokenNum);
-/*E*/if(db7)print2("token is %s, m is %ld\n",g_MathToken[tokenNum].tokenName,m);
-        if (m) {
-          /* It will be substituted */
-          q = q + stackUnkVarLen[m - 1];
-          /* Flag the token position */
-          g_MathToken[tokenNum].tmp = m - 1;
-        } else {
-          /* It will not be substituted */
-          *newVarFlag = 1; /* The result will contain an "unknown" variable */
-          q++;
-          /* Flag the token position */
-          g_MathToken[tokenNum].tmp = -1;
-        }
-      } else {
-        /* It's not an "unknown" variable, so it won't be substituted */
-        q++;
-      }
-    }
-  }
-  /* Allocate space for the final result */
-  nmbrLet(&result, nmbrSpace(q));
-  /* Assign the final result */
-  q = 0;
-  for (p = 0; p < schemeLen; p++) {
-    tokenNum = trialScheme[p];
-    if (g_MathToken[tokenNum].tokenType == (char)con_) {
-      result[q] = tokenNum;
-      q++;
-    } else {
-      if (tokenNum > g_mathTokens) {
-        /* It's a candidate for substitution */
-        k = g_MathToken[tokenNum].tmp; /* Position in stackUnkVar */
-        if (k != -1) {
-          /* It will be substituted */
-          m = stackUnkVarStart[k]; /* Start of substitution */
-          j = stackUnkVarLen[k]; /* Length of substitition */
-          for (i = 0; i < j; i++) {
-            result[q + i] = unifiedScheme[m + i];
-          }
-          q = q + j;
-        } else {
-          /* It will not be substituted */
-          result[q] = tokenNum;
-          q++;
-        }
-      } else {
-        /* It's not an "unknown" variable, so it won't be substituted */
-        result[q] = tokenNum;
-        q++;
-      }
-    } /* end "if a constant" */
-  }
-/*E*/if(db7)print2("after newVarFlag %d\n",(int)*newVarFlag);
-/*E*/if(db7)print2("final len is %ld\n",q);
-/*E*/if(db7)printLongLine(cat("result ",nmbrCvtMToVString(result),NULL),""," ");
-  nmbrLet(&stackUnkVar, NULL_NMBRSTRING); /* Deallocate */
-  return (result);
-} /* makeSubstUnif */
-
-
-
-
-
-char unify(
-    nmbrString *schemeA,
-    nmbrString *schemeB,
-    /* nmbrString **unifiedScheme, */ /* stateVector[8] holds this */
-    pntrString **stateVector,
-    long reEntryFlag)
-{
-
-
-/* This function unifies two math token strings, schemeA and
-   schemeB.  The result is contained in unifiedScheme.
-   0 is returned if no assignment is possible, 1 if an assignment was
-   found, and 2 if the unification timed out.
-   If reEntryFlag is 1, the next possible set of assignments, if any,
-   is returned.  (*stateVector) contains the state of the previous
-   call.  It is the caller's responsibility to deallocate the
-   contents of (*stateVector) when done, UNLESS a 0 is returned.
-   The caller must assign (*stateVector) to a legal pntrString
-   (e.g. NULL_PNTRSTRING) before calling.
-
-   All variables with a tokenNum > g_mathTokens are assumed
-   to be "unknown" variables that can be assigned; all other
-   variables are treated like constants in the unification
-   algorithm.
-
-   The "unknown" variable assignments are contained in (*stateVector)
-   (which is a complex structure, described above).  Some "unknown"
-   variables may have no assignment, in which case they will
-   remain "unknown", and others may have assignments which include
-   "unknown" variables.  The (*stateVector) entries 9 and 10 are used
-   by oneDirUnif() only.
-*/
-
-  long stackTop;
-  nmbrString *unkVars; /* List of all unknown vars */
-  long unkVarsLen;
-  long schemeAUnkVarsLen;
-  nmbrString *stackUnkVar; /* Location of stacked var in unkVars */
-  nmbrString *stackUnkVarStart; /* Start of stacked var in unifiedScheme*/
-  nmbrString *stackUnkVarLen; /* Length of stacked var assignment */
-  pntrString *stackSaveUnkVarStart; /* stackUnkVarStart at the time a
-                                           variable was first stacked */
-  pntrString *stackSaveUnkVarLen; /* stackUnkVarLen at the time a variable
-                                           was first stacked */
-  pntrString *stackSaveSchemeA; /* Pointer to saved schemeA at the time
-                                         the variable was first stacked */
-  pntrString *stackSaveSchemeB; /* Pointer to saved schemeB at the time
-                                         the variable was first stacked */
-  nmbrString *unifiedScheme; /* Final result */
-  long p; /* Current position in schemeA or schemeB */
-  long substToken; /* Token from schemeA or schemeB that will be substituted */
-  nmbrString *substitution = NULL_NMBRSTRING;
-                                       /* String to be subst. for substToken */
-  nmbrString *nmbrTmpPtr; /* temp pointer only */
-  pntrString *pntrTmpPtr; /* temp pointer only */
-  nmbrString *schA = NULL_NMBRSTRING; /* schemeA with dummy token at end */
-  nmbrString *schB = NULL_NMBRSTRING; /* schemeB with dummy token at end */
-  long i,j,k,m, pairingMismatches;
-  flag breakFlag;
-  flag schemeAFlag;
-  flag timeoutAbortFlag = 0;
-  vstring mToken; /* Pointer only; not allocated */
-
-  /* 8/28/99 For detection of simple impossible unifications */
-  flag impossible;
-  long stmt;
-
-  /* 26-Sep-2010 nm For bracket matching heuristic for set.mm */
-  static char bracketMatchOn; /* 26-Sep-2010 nm Default is 'on' */
-  /* static char g_bracketMatchInit = 0; */ /* Global so ERASE can init it */
-  long bracketScanStart, bracketScanStop; /* For one-time $a scan */
-  flag bracketMismatchFound;
-
-
-/*E*/long d;
-/*E*/vstring tmpStr = "";
-/*E*/let(&tmpStr,tmpStr);
-/*E*/if(db5)print2("Entering unify() with reEntryFlag = %ld.\n",
-/*E*/  (long)reEntryFlag);
-/*E*/if(db5)printLongLine(cat("schemeA is ",
-/*E*/    nmbrCvtMToVString(schemeA),".",NULL),"    ","  ");
-/*E*/if(db5)printLongLine(cat("schemeB is ",
-/*E*/    nmbrCvtMToVString(schemeB),".",NULL),"    ","  ");
-
-  /* Initialization to avoid compiler warning (should not be theoretically
-     necessary) */
-  p = 0;
-  bracketMismatchFound = 0;
-
-  /* Fast early exit -- first or last constants of schemes don't match */
-  if (g_MathToken[schemeA[0]].tokenType == (char)con_) {
-    if (g_MathToken[schemeB[0]].tokenType == (char)con_) {
-      if (schemeA[0] != schemeB[0]) {
-        return (0);
-      }
-    }
-  }
-  /* (j and k are used below also) */
-  j = nmbrLen(schemeA);
-  k = nmbrLen(schemeB);
-  if (!j || !k) bug(1901);
-  if (g_MathToken[schemeA[j-1]].tokenType == (char)con_) {
-    if (g_MathToken[schemeB[k-1]].tokenType == (char)con_) {
-      if (schemeA[j-1] != schemeB[k-1]) {
-        return (0);
-      }
-    }
-  }
-  /* Add dummy token to end of schemeA and schemeB */
-  /* Use one beyond the last mathTokenArray entry for this */
-  nmbrLet(&schA, nmbrAddElement(schemeA, g_mathTokens));
-  nmbrLet(&schB, nmbrAddElement(schemeB, g_mathTokens));
-
-  /* 8/29/99 Initialize the usage of constants as the first, last,
-     only constant in a $a statement - for rejecting some simple impossible
-     substitutions - Speed-up: this is now done once and never deallocated*/
-  /* 1-Oct-2017 nm g_firstConst is now cleared in eraseSource.c() (mmcmds.c)
-     to trigger this initialization after "erase" */
-  if (!nmbrLen(g_firstConst)) {
-    /* nmbrSpace() sets all entries to 0, not 32 (ASCII space) */
-    nmbrLet(&g_firstConst, nmbrSpace(g_mathTokens));
-    nmbrLet(&g_lastConst, nmbrSpace(g_mathTokens));
-    nmbrLet(&g_oneConst, nmbrSpace(g_mathTokens));
-    /*for (stmt = 1; stmt < proveStatement; stmt++) {*/
-    /* Do it for all statements since we do it once permanently now */
-    for (stmt = 1; stmt <= g_statements; stmt++) {
-      if (g_Statement[stmt].type != (char)a_)
-        continue; /* Not $a */
-      if (g_Statement[stmt].mathStringLen < 2) continue;
-      /* Look at first symbol after variable type symbol */
-      if (g_MathToken[(g_Statement[stmt].mathString)[1]].tokenType == (char)con_) {
-        g_firstConst[(g_Statement[stmt].mathString)[1]] = 1; /* Set flag */
-        if (g_Statement[stmt].mathStringLen == 2) {
-          g_oneConst[(g_Statement[stmt].mathString)[1]] = 1; /* Set flag */
-        }
-      }
-      /* Look at last symbol */
-      if (g_MathToken[(g_Statement[stmt].mathString)[
-          g_Statement[stmt].mathStringLen - 1]].tokenType == (char)con_) {
-        g_lastConst[(g_Statement[stmt].mathString)[
-          g_Statement[stmt].mathStringLen - 1]] = 1; /* Set flag for const */
-      }
-    } /* Next stmt */
-  }
-
-
-  if (!reEntryFlag) {
-    /* First time called */
-    p = 0;
-
-    /* Collect the list of "unknown" variables */
-    /* (Pre-allocate max. length) */
-    /* (Note j and k assignment above) */
-    unkVars = NULL_NMBRSTRING;
-    nmbrLet(&unkVars, nmbrSpace(j + k));
-    unkVarsLen = 0;
-    for (i = 0; i < j; i++) {
-      if (schemeA[i] > g_mathTokens) {
-        /* It's an "unknown" variable */
-        breakFlag = 0;
-        for (m = 0; m < unkVarsLen; m++) {
-          if (unkVars[m] == schemeA[i]) {
-            /* It's already been added to the list */
-            breakFlag = 1;
-          }
-        }
-        if (!breakFlag) {
-          /* Add the new "unknown" var */
-          unkVars[unkVarsLen++] = schemeA[i];
-        }
-      }
-    }
-    /* Save the length of the list of unknown variables in schemeA */
-    schemeAUnkVarsLen = unkVarsLen;
-    for (i = 0; i < k; i++) {
-      if (schemeB[i] > g_mathTokens) {
-        /* It's an "unknown" variable */
-        breakFlag = 0;
-        for (m = 0; m < unkVarsLen; m++) {
-          if (unkVars[m] == schemeB[i]) {
-            /* It's already been added to the list */
-            breakFlag = 1;
-          }
-        }
-        if (!breakFlag) {
-          /* Add the new "unknown" var */
-          unkVars[unkVarsLen++] = schemeB[i];
-        }
-      }
-    }
-
-    /* Deallocate old (*stateVector) assignments */
-    if (pntrLen(*stateVector)) {
-    /*if (((nmbrString *)((*stateVector)[11]))[0] != -1) { */ /*???Change to nmbrLen?*/
-      /* If (*stateVector) not an empty nmbrString */
-      for (i = 4; i <= 7; i++) {
-        pntrTmpPtr = (pntrString *)((*stateVector)[i]);
-        for (j = 0; j < ((nmbrString *)((*stateVector)[11]))[0]; j++) {
-          nmbrLet((nmbrString **)(&pntrTmpPtr[j]),
-              NULL_NMBRSTRING);
-        }
-        pntrLet((pntrString **)(&(*stateVector)[i]),
-            NULL_PNTRSTRING);
-      }
-      for (i = 0; i <= 3; i++) {
-        nmbrLet((nmbrString **)(&(*stateVector)[i]),
-            NULL_NMBRSTRING);
-      }
-      for (i = 8; i <= 10; i++) {
-        nmbrLet((nmbrString **)(&(*stateVector)[i]),
-            NULL_NMBRSTRING);
-      }
-      k = pntrLen((pntrString *)((*stateVector)[12]));
-      for (i = 12; i < 16; i++) {
-        pntrTmpPtr = (pntrString *)((*stateVector)[i]);
-        for (j = 0; j < k; j++) {
-          nmbrLet((nmbrString **)(&pntrTmpPtr[j]),
-              NULL_NMBRSTRING);
-        }
-        pntrLet((pntrString **)(&(*stateVector)[i]),
-            NULL_PNTRSTRING);
-      }
-      /* Leave [11] pre-allocated to length 4 */
-    } else {
-      /* It was never allocated before -- do it now */
-      /* Allocate stateVector - it will be assigned upon exiting */
-      pntrLet(&(*stateVector), pntrPSpace(16));
-      nmbrLet((nmbrString **)(&(*stateVector)[11]), nmbrSpace(4));
-    }
-
-    /* Pre-allocate the (*stateVector) structure */
-    stackTop = -1;
-    stackUnkVar = NULL_NMBRSTRING;
-    stackUnkVarStart = NULL_NMBRSTRING;
-    stackUnkVarLen = NULL_NMBRSTRING;
-    stackSaveUnkVarStart = NULL_PNTRSTRING;
-    stackSaveUnkVarLen = NULL_PNTRSTRING;
-    stackSaveSchemeA = NULL_PNTRSTRING;
-    stackSaveSchemeB = NULL_PNTRSTRING;
-    unifiedScheme = NULL_NMBRSTRING;
-    nmbrLet(&stackUnkVar, nmbrSpace(unkVarsLen));
-    nmbrLet(&stackUnkVarStart, stackUnkVar);
-    nmbrLet(&stackUnkVarLen, stackUnkVar);
-
-    /* These next 4 hold pointers to nmbrStrings */
-    pntrLet(&stackSaveUnkVarStart, pntrNSpace(unkVarsLen));
-    pntrLet(&stackSaveUnkVarLen, stackSaveUnkVarStart);
-    pntrLet(&stackSaveSchemeA, stackSaveUnkVarStart);
-    pntrLet(&stackSaveSchemeB, stackSaveUnkVarStart);
-    for (i = 0; i < unkVarsLen; i++) {
-      /* Preallocate the stack space for these */
-      nmbrLet((nmbrString **)(&stackSaveUnkVarStart[i]),
-          stackUnkVar);
-      nmbrLet((nmbrString **)(&stackSaveUnkVarLen[i]),
-          stackUnkVar);
-    }
-
-    /* Set a flag that the "unknown" variables are not on the stack yet */
-    /* (Otherwise this will be the position on the stack) */
-    for (i = 0; i < unkVarsLen; i++) {
-      g_MathToken[unkVars[i]].tmp = -1;
-    }
-
-  } else { /* reEntryFlag != 0 */
-
-    /* We are re-entering to get the next possible assignment. */
-
-    /* Restore the (*stateVector) variables */
-    unkVarsLen = ((nmbrString *)((*stateVector)[11]))[0];
-    unkVars = (nmbrString *)((*stateVector)[0]);
-    stackTop = ((nmbrString *)((*stateVector)[11]))[1];
-    stackUnkVar = (nmbrString *)((*stateVector)[1]);
-    stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
-    stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
-    stackSaveUnkVarStart = (pntrString *)((*stateVector)[4]);
-    stackSaveUnkVarLen = (pntrString *)((*stateVector)[5]);
-    stackSaveSchemeA = (pntrString *)((*stateVector)[6]);
-    stackSaveSchemeB = (pntrString *)((*stateVector)[7]);
-    unifiedScheme = (nmbrString *)((*stateVector)[8]);
-    schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
-                                                      /* Used by oneDirUnif() */
-
-    /* Set the location of the "unknown" variables on the stack */
-    /* (This may have been corrupted outside this function) */
-    for (i = 0; i < unkVarsLen; i++) {
-      g_MathToken[unkVars[i]].tmp = -1; /* Not on the stack */
-    }
-    for (i = 0; i <= stackTop; i++) {
-      g_MathToken[stackUnkVar[i]].tmp = i;
-    }
-
-    /* Force a backtrack to the next assignment */
-    goto backtrack;
-   reEntry1: /* goto backtrack will come back here if reEntryFlag is set */
-    reEntryFlag = 0;
-
-
-  }
-
-  /* Perform the unification */
-
- scan:
-/*E*/if(db6)print2("Entered scan: p=%ld\n",p);
-/*E*/if(db6)print2("Enter scn sbA %s\n",nmbrCvtMToVString(schA));
-/*E*/if(db6)print2("Enter scn sbB %s\n",nmbrCvtMToVString(schB));
-/*E*/if(db6)let(&tmpStr,tmpStr);
-  while (schA[p] == schB[p] &&
-      schA[p + 1] != -1) {
-    p++;
-  }
-/*E*/if(db6)print2("First mismatch: p=%ld\n",p);
-
-  if (schA[p] == g_mathTokens
-      || schB[p] == g_mathTokens) {
-    /* One of the strings is at the end. */
-    if (schA[p] != schB[p]) {
-      /* But one is longer than the other. */
-      if (schA[p] <= g_mathTokens &&
-          schB[p] <= g_mathTokens) {
-        /* Neither token is an unknown variable.  (Otherwise we might be able
-           to assign the unknown variable to a null string, thus making
-           the schemes match, so we shouldn't backtrack.) */
-/*E*/if(db6)print2("Backtracked because end-of-string\n");
-        goto backtrack;
-      }
-    } else {
-      if (schA[p + 1] == -1) {
-        /* End of schA; a successful unification occurred */
-        goto done;
-      }
-      /* Otherwise, we are in the middle of several schemes being unified
-         simultaneously, so just continue. */
-      /* (g_mathTokens should be used by the caller to separate
-         schemes that are joined together for simultaneous unification) */
-    }
-  }
-
-  /* This test, combined with switchover to schemeB in backtrack,
-     prevents variable lockup, for example where
-     schemeA = ?1 B C, schemeB = ?2 A B C.  Without this test, schemeB
-     becomes ?1 A B C, and then it can never match schemeA.  This should be
-     checked out further:  what about more than 2 variables?  This kind of
-     "variable lockup" may be a more serious problem. */
-  /* A test case:
-   schemeA is $$ |- ( ( ?463 -> -. -. ph ) -> ( -. ph -> ( ph -> -. -. ph ) ) ).
-   schemeB is $$ |- ( ?464 -> ( ?465 -> ?464 ) ).
-  */
-  if (schB[p] > g_mathTokens && schA[p] > g_mathTokens) {
-    /* Both scheme A and scheme B have variables in the match position.
-       Which one to use? */
-    /* If neither A nor B is on the stack, use A.   Backtrack will put B
-       on the stack when A's possibilities are exhausted. */
-    /* If A is on the stack, use A. */
-    /* If B is on the stack, use B. */
-    /* If A and B are on the stack, bug. */
-    /* In other words:  if B is not on the stack, use A. */
-    if (g_MathToken[schB[p]].tmp == -1) {
-      /* B is not on the stack */
-      goto schAUnk;
-    } else {
-      if (g_MathToken[schA[p]].tmp != -1) bug(1902); /* Both are on the stack */
-      goto schBUnk;
-    }
-  }
-
- schBUnk:
-  if (schB[p] > g_mathTokens) {
-/*E*/if(db6)print2("schB has unknown variable\n");
-    /* An "unknown" variable is in scheme B */
-    schemeAFlag = 0;
-    substToken = schB[p];
-
-    if (g_MathToken[substToken].tmp == -1) {
-      /* The "unknown" variable is not on the stack; add it */
-      stackTop++;
-      stackUnkVar[stackTop] = substToken;
-      g_MathToken[substToken].tmp = stackTop;
-      stackUnkVarStart[stackTop] = p;
-      /* Start with a variable length of 0 or 1 */
-      stackUnkVarLen[stackTop] = g_minSubstLen;
-      /* Save the rest of the current state for backtracking */
-      nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarStart[stackTop]);
-      for (i = 0; i <= stackTop; i++) {
-        nmbrTmpPtr[i] = stackUnkVarStart[i];
-      }
-      nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
-      for (i = 0; i <= stackTop; i++) {
-        nmbrTmpPtr[i] = stackUnkVarLen[i];
-      }
-      nmbrLet((nmbrString **)(&stackSaveSchemeA[stackTop]),
-          schA);
-      nmbrLet((nmbrString **)(&stackSaveSchemeB[stackTop]),
-          schB);
-    }
-
-    if (substToken != stackUnkVar[stackTop]) {
-      print2("PROGRAM BUG #1903\n");
-      print2("substToken is %s\n", g_MathToken[substToken].tokenName);
-      print2("stackTop %ld\n", stackTop);
-      print2("p %ld stackUnkVar[stackTop] %s\n", p,
-        g_MathToken[stackUnkVar[stackTop]].tokenName);
-      print2("schA %s\nschB %s\n", nmbrCvtMToVString(schA),
-        nmbrCvtMToVString(schB));
-      bug(1903);
-    }
-    nmbrLet(&substitution, nmbrMid(schA, p + 1,
-        stackUnkVarLen[stackTop]));
-    goto substitute;
-  }
-
- schAUnk:
-  if (schA[p] > g_mathTokens) {
-/*E*/if(db6)print2("schA has unknown variable\n");
-    /* An "unknown" variable is in scheme A */
-    schemeAFlag = 1;
-    substToken = schA[p];
-    if (g_MathToken[substToken].tmp == -1) {
-      /* The "unknown" variable is not on the stack; add it */
-      stackTop++;
-      stackUnkVar[stackTop] = substToken;
-      g_MathToken[substToken].tmp = stackTop;
-      stackUnkVarStart[stackTop] = p;
-      /* Start with a variable length of 0 or 1 */
-      stackUnkVarLen[stackTop] = g_minSubstLen;
-      /* Save the rest of the current state for backtracking */
-      nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarStart[stackTop]);
-      for (i = 0; i <= stackTop; i++) {
-        nmbrTmpPtr[i] = stackUnkVarStart[i];
-      }
-      nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
-      for (i = 0; i <= stackTop; i++) {
-        nmbrTmpPtr[i] = stackUnkVarLen[i];
-      }
-      nmbrLet((nmbrString **)(&stackSaveSchemeA[stackTop]),
-          schA);
-      nmbrLet((nmbrString **)(&stackSaveSchemeB[stackTop]),
-          schB);
-    }
-
-    if (substToken != stackUnkVar[stackTop]) {
-/*E*/print2("PROGRAM BUG #1904\n");
-/*E*/print2("\nsubstToken is %s\n",g_MathToken[substToken].tokenName);
-/*E*/print2("stack top %ld\n",stackTop);
-/*E*/print2("p %ld stUnV[stakTop] %s\n",p,
-/*E*/g_MathToken[stackUnkVar[stackTop]].tokenName);
-/*E*/print2("schA %s\nschB %s\n",nmbrCvtMToVString(schA),nmbrCvtMToVString(schB));
-      bug(1904);
-    }
-    nmbrLet(&substitution, nmbrMid(schB, p + 1,
-        stackUnkVarLen[stackTop]));
-    goto substitute;
-  }
-
-  /* Neither scheme has an unknown variable; unification with current assignment
-     failed, so backtrack */
-/*E*/if(db6)print2("Neither scheme has unknown variable\n");
-  goto backtrack;
-
-
- substitute:
-/*E*/if(db6)print2("Entering substitute...\n");
-/*E*/for (d = 0; d <= stackTop; d++) {
-/*E*/  if(db6)print2("Unknown var %ld is %s.\n",d,
-/*E*/      g_MathToken[stackUnkVar[d]].tokenName);
-/*E*/  if(db6)print2("  Its start is %ld; its length is %ld.\n",
-/*E*/      stackUnkVarStart[d],stackUnkVarLen[d]);
-/*E*/}
-  /* Subst. all occurrences of substToken with substitution in schA and schB */
-  /*???We could speed things up by making substitutions before the pointer to
-    ???to the unifiedScheme only, and keeping track of 3 pointers (A, B, &
-    ???unified schemes); unifiedScheme would hold only stuff before pointer */
-
-  /* First, we must make sure that the substToken doesn't occur in the
-     substutition. */
-  if (nmbrElementIn(1, substitution, substToken)) {
-/*E*/if(db6)print2("Substituted token occurs in substitution string\n");
-    goto backtrack;
-  }
-
-  /* Next, we must make sure that the end of string doesn't occur in the
-     substutition. */
-  /* (This takes care of the case where the unknown variable aligns with
-     end of string character; in this case, only a null substitution is
-     permissible.  If the substitution length is 1 or greater, this "if"
-     statement will detect it.) */
-  if (substitution[0] == g_mathTokens) {
-/*E*/if(db6)print2("End of string token occurs in substitution string\n");
-    /* We must pop the stack here rather than in backtrack, because we
-       are already one token beyond the end of a scheme, and backtrack
-       would therefore test one token beyond that, missing the fact that
-       the substitution has overflowed beyond the end of a scheme. */
-    /* Set the flag that it's not on the stack and pop stack */
-    g_MathToken[stackUnkVar[stackTop]].tmp = -1;
-    stackTop--;
-    goto backtrack;
-  }
-
-  /************* Start of 26-Sep-2010 *************/
-  /* Bracket matching is customized to set.mm to result in fewer ambiguous
-     unifications. */
-  /* 26-Sep-2010 nm Automatically disable bracket matching if any $a has
-     unmatched brackets */
-  /* The static variable g_bracketMatchInit tells us to check all $a's
-     if it is 0; if 1, skip the $a checking.  Make sure that the RESET
-     command sets g_bracketMatchInit=0. */
-  /* ???  To do:  put individual bracket type checks into a loop or
-     function call for code efficiency (but don't slow down program); maybe
-     read the bracket types to check from a list; maybe refine so that only
-     the mismatched bracket types found in the $a scan are skipped, but
-     matched one are not */
-  for (i = g_bracketMatchInit; i <= 1; i++) {
-    /* This loop has 2 passes (0 and 1) if g_bracketMatchInit=0 to set
-       bracketMatchOn = 0 or 1, and 1 pass otherwise */
-    bracketMismatchFound = 0;  /* Don't move down; needed for break below */
-    if (g_bracketMatchInit == 0) {  /* Initialization pass */
-      if (i != 0) bug(1908);
-      /* Scan all ($a) statements */
-      bracketScanStart = 1;
-      bracketScanStop = g_statements;
-    } else {    /* Normal pass */
-      if (i != 1) bug(1909);
-      if (!bracketMatchOn) break; /* Skip the whole bracket check because a
-          mismatched bracket was found in some $a in the initialization pass */
-      /* Set dummy parameters to force a single loop pass */
-      bracketScanStart = 0;
-      bracketScanStop = 0;
-    }
-    for (m = bracketScanStart; m <= bracketScanStop; m++) {
-      if (g_bracketMatchInit == 0) {  /* Initialization pass */
-        if (g_Statement[m].type != a_) continue;
-        nmbrTmpPtr = g_Statement[m].mathString;
-      } else {  /* Normal pass */
-        nmbrTmpPtr = substitution;
-      }
-      j = nmbrLen(nmbrTmpPtr);
-
-      /* Make sure left and right parentheses match */
-      pairingMismatches = 0; /* Counter of parens: + for "(" and - for ")" */
-      for (k = 0; k < j; k++) {
-        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
-        if (mToken[0] == '(' && mToken[1] == 0 ) {
-          pairingMismatches++;
-        } else if (mToken[0] == ')' && mToken[1] == 0 ) {
-          pairingMismatches--;
-          if (pairingMismatches < 0) break; /* Detect wrong order */
-        }
-      } /* Next k */
-      if (pairingMismatches != 0) {
-        bracketMismatchFound = 1;
-        break;
-      }
-
-      /* Make sure left and right braces match */
-      pairingMismatches = 0; /* Counter of braces: + for "{" and - for "}" */
-      for (k = 0; k < j; k++) {
-        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
-        if (mToken[0] == '{' && mToken[1] == 0 ) pairingMismatches++;
-        else
-          if (mToken[0] == '}' && mToken[1] == 0 ) {
-            pairingMismatches--;
-            if (pairingMismatches < 0) break; /* Detect wrong order */
-          }
-      } /* Next k */
-      if (pairingMismatches != 0) {
-        bracketMismatchFound = 1;
-        break;
-      }
-
-      /* Make sure left and right brackets match */  /* Added 12-Nov-05 nm */
-      pairingMismatches = 0; /* Counter of brackets: + for "[" and - for "]" */
-      for (k = 0; k < j; k++) {
-        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
-        if (mToken[0] == '[' && mToken[1] == 0 )
-          pairingMismatches++;
-        else
-          if (mToken[0] == ']' && mToken[1] == 0 ) {
-            pairingMismatches--;
-            if (pairingMismatches < 0) break; /* Detect wrong order */
-          }
-      } /* Next k */
-      if (pairingMismatches != 0) {
-        bracketMismatchFound = 1;
-        break;
-      }
-
-      /* Make sure left and right triangle brackets match */
-      pairingMismatches = 0; /* Counter of brackets: + for "<.", - for ">." */
-      for (k = 0; k < j; k++) {
-        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
-        if (mToken[1] == 0) continue;
-        if (mToken[0] == '<' && mToken[1] == '.' && mToken[2] == 0 )
-            pairingMismatches++;
-        else
-          if (mToken[0] == '>' && mToken[1] == '.' && mToken[2] == 0 ) {
-            pairingMismatches--;
-            if (pairingMismatches < 0) break; /* Detect wrong order */
-          }
-      } /* Next k */
-      if (pairingMismatches != 0) {
-        bracketMismatchFound = 1;
-        break;
-      }
-
-      /* Make sure underlined brackets match */  /* Added 12-Nov-05 nm */
-      pairingMismatches = 0; /* Counter of brackets: + for "[_", - for "]_" */
-      for (k = 0; k < j; k++) {
-        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
-        if (mToken[1] == 0) continue;
-        if (mToken[0] == '[' && mToken[1] == '_' && mToken[2] == 0 )
-            pairingMismatches++;
-        else
-          if (mToken[0] == ']' && mToken[1] == '_' && mToken[2] == 0 ) {
-            pairingMismatches--;
-            if (pairingMismatches < 0) break; /* Detect wrong order */
-          }
-      } /* Next k */
-      if (pairingMismatches != 0) {
-        bracketMismatchFound = 1;
-        break;
-      }
-    } /* next m */
-
-    if (g_bracketMatchInit == 0) {   /* Initialization pass */
-      /* We've finished the one-time $a scan.  Set flags accordingly. */
-      if (bracketMismatchFound) { /* Some $a has a bracket mismatch */
-        if (m < 1 || m > g_statements) bug(1910);
-        printLongLine(cat("The bracket matching unification heuristic was",
-           " turned off for this database because of a bracket mismatch in",
-           " statement \"",
-           /* (m should be accurate due to break above) */
-           g_Statement[m].labelName,
-           "\".", NULL),
-           "    ", " ");
-        /*
-        printLongLine(cat("The bracket matching unification heuristic was",
-           " turned off for this database.", NULL),
-           "    ", " ");
-        */
-        bracketMatchOn = 0; /* Turn off static flag for this database */
-      } else {    /* Normal pass */
-        bracketMatchOn = 1; /* Turn it on */
-      }
-/*E*/if(db6)print2("bracketMatchOn = %ld\n", (long)bracketMatchOn);
-      g_bracketMatchInit = 1; /* We're done with the one-time $a scan */
-    }
-  } /* next i */
-
-  if (bracketMismatchFound) goto backtrack;
-  /************* End of 26-Sep-2010 *************/
-
-  j = nmbrLen(substitution);
-
-  /* 8/29/99 - Quick scan to reject some impossible unifications: If the
-     first symbol in a substitution is a constant, it must match
-     the 2nd constant of some earlier $a statement (e.g. "<" matches
-     "class <", "Ord (/)" matches "class Ord A").  Same applies to
-     last symbol. */
-  /* 10/12/02 - This prefilter is too aggressive when empty substitutions
-     are allowed.  Therefore added "g_minSubstLen > 0" to fix miu.mm theorem1
-     Proof Assistant failure reported by Josh Purinton. */
-  if (j/*subst len*/ > 0 && g_minSubstLen > 0) {
-    impossible = 0;
-    if (g_MathToken[substitution[0]].tokenType == (char)con_) {
-      if (!g_firstConst[substitution[0]]
-         || (j == 1 && !g_oneConst[substitution[0]])) {
-        impossible = 1;
-      }
-    }
-    if (g_MathToken[substitution[j - 1]].tokenType == (char)con_) {
-      if (!g_lastConst[substitution[j - 1]]) {
-        impossible = 1;
-      }
-    }
-    if (impossible) {
-/*E*/if(db6)print2("Impossible subst: %s\n", nmbrCvtMToVString(substitution));
-      goto backtrack;
-    }
-  }
-
-  /* Now perform the substitutions */
-/*E*/if(db6)print2("Substitution is '%s'\n",nmbrCvtMToVString(substitution));
-  k = 1;
-  while (1) {
-    /* Perform the substitutions into scheme A */
-    k = nmbrElementIn(k, schA, substToken);
-    if (!k) break;
-
-    if (schemeAFlag) {
-      /* The token to be substituted was in scheme A */
-      /* Adjust position and earlier var. starts and lengths */
-      if (k - 1 <= p) {
-        if (k <= p) {
-          /* Adjust assignments in stack */
-          for (i = 0; i <= stackTop; i++) {
-            if (k - 1 < stackUnkVarStart[i]) {
-              stackUnkVarStart[i] = stackUnkVarStart[i] + j-1;
-            } else {
-              if (k <= stackUnkVarStart[i] +
-                  stackUnkVarLen[i]) {
-                stackUnkVarLen[i] = stackUnkVarLen[i] + j - 1;
-              }
-            }
-          }
-        }
-        p = p + j - 1; /* Adjust scan position */
-/*E*/if(db6)print2("Scheme A adjusted p=%ld\n",p);
-      }
-    }
-
-    nmbrLet(&schA, nmbrCat(
-        nmbrLeft(schA, k - 1), substitution, nmbrRight(schA, k + 1), NULL));
-    k = k + j - 1;
-  }
-  k = 1;
-  while (1) {
-    /* Perform the substitutions into scheme B */
-    k = nmbrElementIn(k, schB, substToken);
-    if (!k) break;
-
-    if (!schemeAFlag) {
-      /* The token to be substituted was in scheme B */
-      /* Adjust scan position and earlier var. starts and lengths */
-      if (k - 1 <= p) {
-        if (k <= p) {
-          /* Adjust assignments in stack */
-          for (i = 0; i <= stackTop; i++) {
-            if (k - 1 < stackUnkVarStart[i]) {
-              stackUnkVarStart[i] = stackUnkVarStart[i] + j-1;
-            } else {
-              if (k <= stackUnkVarStart[i] +
-                  stackUnkVarLen[i]) {
-                stackUnkVarLen[i] = stackUnkVarLen[i] + j - 1;
-              }
-            }
-          }
-        }
-        p = p + j - 1; /* Adjust scan position */
-      }
-/*E*/if(db6)print2("Scheme B adjusted p=%ld\n",p);
-    }
-
-    nmbrLet(&schB, nmbrCat(
-        nmbrLeft(schB, k - 1), substitution, nmbrRight(schB, k + 1), NULL));
-    k = k + j - 1;
-  }
-  p++;
-/*E*/if(db6)print2("Scheme A or B final p=%ld\n",p);
-/*E*/if(db6)print2("after sub sbA %s\n",nmbrCvtMToVString(schA));
-/*E*/if(db6)print2("after sub sbB %s\n",nmbrCvtMToVString(schB));
-/*E*/for (d = 0; d <= stackTop; d++) {
-/*E*/  if(db6)print2("Unknown var %ld is %s.\n",d,
-/*E*/      g_MathToken[stackUnkVar[d]].tokenName);
-/*E*/  if(db6)print2("  Its start is %ld; its length is %ld.\n",
-/*E*/      stackUnkVarStart[d],stackUnkVarLen[d]);
-/*E*/}
-  goto scan;
-
- backtrack:
-/*E*/if(db6)print2("Entered backtrack with p=%ld stackTop=%ld\n",p,stackTop);
-  if (stackTop < 0) {
-    goto abort;
-  }
-  if (g_unifTrialCount > 0) { /* Flag that timeout is active */
-    g_unifTrialCount++;
-    if (g_unifTrialCount > g_userMaxUnifTrials) {
-      g_unifTimeouts++; /* Update number of timeouts found */
-/*E*/if(db5)print2("Aborted due to timeout: %ld > %ld\n",
-/*E*/    g_unifTrialCount, g_userMaxUnifTrials);
-      timeoutAbortFlag = 1;
-      goto abort;
-    }
-  }
-  /* Add 1 to stackTop variable length */
-  nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
-  nmbrTmpPtr[stackTop]++;
-  /* Restore the state from the stack top */
-  nmbrLet(&stackUnkVarStart, (nmbrString *)(stackSaveUnkVarStart[stackTop]));
-  nmbrLet(&schA, (nmbrString *)(stackSaveSchemeA[stackTop]));
-  nmbrLet(&schB, (nmbrString *)(stackSaveSchemeB[stackTop]));
-  /* Restore the scan position */
-  p = stackUnkVarStart[stackTop];
- switchVarToB:
-  /* Restore the state from the stack top */
-  nmbrLet(&stackUnkVarLen, (nmbrString *)(stackSaveUnkVarLen[stackTop]));
-
-  /* If the variable overflows the end of the scheme its assigned to,
-     pop the stack */
-/*E*/if(db6)print2("Backtracked to token %s.\n",
-/*E*/  g_MathToken[stackUnkVar[stackTop]].tokenName);
-  if (stackUnkVar[stackTop] == schA[p]) {
-    /* It was in scheme A; see if it overflows scheme B */
-    if (schB[p - 1 + stackUnkVarLen[stackTop]]
-        == g_mathTokens) {
-/*E*/if(db6)print2("It was in scheme A; overflowed scheme B: p=%ld, len=%ld.\n",
-/*E*/  p,stackUnkVarLen[stackTop]);
-      /* Set the flag that it's not on the stack */
-      g_MathToken[stackUnkVar[stackTop]].tmp = -1;
-
-      /* See if the token in scheme B at this position is also a variable */
-      /* If so, switch the stack top variable to the one in scheme B and
-         restart the scan on its length */
-      if (schB[p] > g_mathTokens) {
-/*E*/if(db6)print2("Switched var-var match to scheme B token %s\n",
-/*E*/     g_MathToken[stackUnkVar[stackTop]].tokenName);
-        /* The scheme B variable will not be on the stack. */
-        if (g_MathToken[schB[p]].tmp != -1) bug(1905);
-        /* Make the token in scheme B become the variable at the stack top */
-        stackUnkVar[stackTop] = schB[p];
-        g_MathToken[schB[p]].tmp = stackTop;
-        /* Start with a variable length of 0 or 1 */
-        stackUnkVarLen[stackTop] = g_minSubstLen;
-        /* Initialize stackTop variable length */
-        nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
-        nmbrTmpPtr[stackTop] = g_minSubstLen;
-        /* Restart the backtrack with double variable switched to scheme B */
-        goto switchVarToB;
-      }
-
-      /* Pop stack */
-      stackTop--;
-      goto backtrack;
-    }
-  } else {
-    /* It was in scheme B; see if it overflows scheme A */
-    if (schA[p - 1 + stackUnkVarLen[stackTop]]
-        == g_mathTokens) {
-/*E*/if(db6)print2("It was in scheme B; overflowed scheme A: p=%ld, len=%ld.\n",
-/*E*/  p,stackUnkVarLen[stackTop]);
-      /* Set the flag that it's not on the stack and pop stack */
-      g_MathToken[stackUnkVar[stackTop]].tmp = -1;
-      stackTop--;
-      goto backtrack;
-    }
-  }
-/*E*/if(db6)print2("Exited backtrack with p=%ld stackTop=%ld\n",p,stackTop);
-  if (reEntryFlag) goto reEntry1;
-  goto scan; /* Continue the scan */
-
- done:
-
-  /* Assign the final result */
-  nmbrLet(&unifiedScheme, nmbrLeft(schA, nmbrLen(schA) - 1));
-/*E*/if(db5)print2("Backtrack count was %ld\n",g_unifTrialCount);
-/*E*/if(db5)printLongLine(cat("Unified scheme is ",
-/*E*/    nmbrCvtMToVString(unifiedScheme),".",NULL),"    ","  ");
-  /* Assign the 12 components of (*stateVector).  Some of the components hold
-     pointers to pointer strings. */
-  /* 0 holds the unkVars array and length */
-  ((nmbrString *)((*stateVector)[11]))[0] = unkVarsLen;
-  (*stateVector)[0] = unkVars;
-  /* 1 holds the stack top and the "unknown" vars on the stack */
-  ((nmbrString *)((*stateVector)[11]))[1] = stackTop;
-  (*stateVector)[1] = stackUnkVar;
-  (*stateVector)[2] = stackUnkVarStart;
-  (*stateVector)[3] = stackUnkVarLen;
-  (*stateVector)[4] = stackSaveUnkVarStart;
-  (*stateVector)[5] = stackSaveUnkVarLen;
-  (*stateVector)[6] = stackSaveSchemeA;
-  (*stateVector)[7] = stackSaveSchemeB;
-  /* Save the result */
-  (*stateVector)[8] = unifiedScheme;
-  /* Components 9 and 10 save the previous assignment.
-     This is handled by oneDirUnif() if oneDirUnif() is called. */
-  ((nmbrString *)((*stateVector)[11]))[2] = schemeAUnkVarsLen; /* Used by oneDirUnif() */
-
-/*E*/if(db5)printSubst(*stateVector);
-  /* Deallocate nmbrStrings */
-  nmbrLet(&schA, NULL_NMBRSTRING);
-  nmbrLet(&schB, NULL_NMBRSTRING);
-  nmbrLet(&substitution, NULL_NMBRSTRING);
-  return (1);
-
- abort:
-/*E*/if(db5)print2("Backtrack count was %ld\n",g_unifTrialCount);
-  /* Deallocate stateVector contents */
-  nmbrLet(&unkVars,NULL_NMBRSTRING);
-  nmbrLet(&stackUnkVar,NULL_NMBRSTRING);
-  nmbrLet(&stackUnkVarStart,NULL_NMBRSTRING);
-  nmbrLet(&stackUnkVarLen,NULL_NMBRSTRING);
-  for (i = 0; i < unkVarsLen; i++) {
-    /* Deallocate the stack space for these */
-    nmbrLet((nmbrString **)(&stackSaveUnkVarStart[i]),
-        NULL_NMBRSTRING);
-    nmbrLet((nmbrString **)(&stackSaveUnkVarLen[i]),
-        NULL_NMBRSTRING);
-    nmbrLet((nmbrString **)(&stackSaveSchemeA[i]),
-        NULL_NMBRSTRING);
-    nmbrLet((nmbrString **)(&stackSaveSchemeB[i]),
-        NULL_NMBRSTRING);
-  }
-  pntrLet(&stackSaveUnkVarStart,NULL_PNTRSTRING);
-  pntrLet(&stackSaveUnkVarLen,NULL_PNTRSTRING);
-  pntrLet(&stackSaveSchemeA,NULL_PNTRSTRING);
-  pntrLet(&stackSaveSchemeB,NULL_PNTRSTRING);
-  nmbrLet(&unifiedScheme,NULL_NMBRSTRING);
-  /* Deallocate entries used by oneDirUnif() */
-  nmbrLet((nmbrString **)(&(*stateVector)[9]),NULL_NMBRSTRING);
-  nmbrLet((nmbrString **)(&(*stateVector)[10]),NULL_NMBRSTRING);
-  /* Deallocate entries used by unifyH() */
-  k = pntrLen((pntrString *)((*stateVector)[12]));
-  for (i = 12; i < 16; i++) {
-    pntrTmpPtr = (pntrString *)((*stateVector)[i]);
-    for (j = 0; j < k; j++) {
-      nmbrLet((nmbrString **)(&pntrTmpPtr[j]),
-          NULL_NMBRSTRING);
-    }
-    pntrLet((pntrString **)(&(*stateVector)[i]),
-        NULL_PNTRSTRING);
-  }
-  /* Deallocate the stateVector itself */
-  ((nmbrString *)((*stateVector)[11]))[1] = 0;
-      /* stackTop: Make sure it's not -1 before calling nmbrLet() */
-  nmbrLet((nmbrString **)(&(*stateVector)[11]),NULL_NMBRSTRING);
-  pntrLet(&(*stateVector),NULL_PNTRSTRING);
-
-  /* Deallocate nmbrStrings */
-  nmbrLet(&schA,NULL_NMBRSTRING);
-  nmbrLet(&schB,NULL_NMBRSTRING);
-  nmbrLet(&substitution,NULL_NMBRSTRING);
-
-  if (timeoutAbortFlag) {
-    return (2);
-  }
-  return (0);
-} /* unify */
-
-
-/* oneDirUnif() is like unify(), except that when reEntryFlag is 1,
-   a new unification is returned ONLY if the assignments to the
-   variables in schemeA have changed.  This is used to speed up the
-   program. */
-/*???This whole thing may be screwed up -- it seems to be based on
-   all unknown vars (including those without substitutions), not just
-   the ones on the stack. */
-flag oneDirUnif(
-    nmbrString *schemeA,
-    nmbrString *schemeB,
-    pntrString **stateVector,
-    long reEntryFlag)
-{
-long i;
-flag tmpFlag;
-long schemeAUnkVarsLen;
-nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
-nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
-nmbrString *oldStackUnkVarStart; /* Pointer only - not allocated */
-nmbrString *oldStackUnkVarLen; /* Pointer only - not allocated */
-
-  if (!reEntryFlag) {
-    tmpFlag = unify(schemeA, schemeB, stateVector, 0);
-    if (tmpFlag) {
-      /* Save the initial variable assignments */
-      nmbrLet((nmbrString **)(&(*stateVector)[9]),
-          (nmbrString *)((*stateVector)[2]));
-      nmbrLet((nmbrString **)(&(*stateVector)[10]),
-          (nmbrString *)((*stateVector)[3]));
-    }
-    return (tmpFlag);
-  } else {
-    while (1) {
-      tmpFlag = unify(schemeA, schemeB, stateVector, 1);
-      if (!tmpFlag) return (0);
-      /* Check to see if the variables in schemeA changed */
-      schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
-      stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
-      stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
-      oldStackUnkVarStart = (nmbrString *)((*stateVector)[9]);
-      oldStackUnkVarLen = (nmbrString *)((*stateVector)[10]);
-      for (i = 0; i < schemeAUnkVarsLen; i++) {
-        if (stackUnkVarStart[i] != oldStackUnkVarStart[i]) {
-          /* The assignment changed */
-          /* Save the new assignment */
-          nmbrLet(&oldStackUnkVarStart, stackUnkVarStart);
-          nmbrLet(&oldStackUnkVarLen, stackUnkVarLen);
-          return (1);
-        }
-        if (stackUnkVarLen[i] != oldStackUnkVarLen[i]) {
-          /* The assignment changed */
-          /* Save the new assignment */
-          nmbrLet(&oldStackUnkVarStart, stackUnkVarStart);
-          nmbrLet(&oldStackUnkVarLen, stackUnkVarLen);
-          return (1);
-        }
-      }
-    } /* End while (1) */
-  }
-  return(0); /* Dummy return value - never happens */
-} /* oneDirUnif */
-
-
-
-/* uniqueUnif() is like unify(), but there is no reEntryFlag, and 3 possible
-   values are returned:
-     0: no unification was possible.
-     1: exactly one unification was possible, and stateVector is valid.
-     2: unification timed out
-     3: more than one unification was possible */
-char uniqueUnif(
-    nmbrString *schemeA,
-    nmbrString *schemeB,
-    pntrString **stateVector)
-{
-  pntrString *saveStateVector = NULL_PNTRSTRING;
-  pntrString *pntrTmpPtr1; /* Pointer only; not allocated */
-  pntrString *pntrTmpPtr2; /* Pointer only; not allocated */
-  long i, j, k;
-  char tmpFlag;
-
-  tmpFlag = unifyH(schemeA, schemeB, stateVector, 0);
-  if (!tmpFlag) {
-    return (0); /* No unification possible */
-  }
-  if (tmpFlag == 2) {
-    return (2); /* Unification timed out */
-  }
-
-  /* Save the state vector */
-  pntrLet(&saveStateVector,*stateVector);
-  if (pntrLen(*stateVector) != 16) bug(1906);
-  for (i = 0; i < 4; i++) {
-    /* Force new space to be allocated for each pointer */
-    saveStateVector[i] = NULL_NMBRSTRING;
-    nmbrLet((nmbrString **)(&saveStateVector[i]),
-        (nmbrString *)((*stateVector)[i]));
-  }
-  for (i = 4; i <= 7; i++) {
-    /* Force new space to be allocated for each pointer */
-    saveStateVector[i] = NULL_PNTRSTRING;
-    pntrLet((pntrString **)(&saveStateVector[i]),
-        (pntrString *)((*stateVector)[i]));
-  }
-  for (i = 8; i < 12; i++) {
-    /* Force new space to be allocated for each pointer */
-    saveStateVector[i] = NULL_NMBRSTRING;
-    nmbrLet((nmbrString **)(&saveStateVector[i]),
-        (nmbrString *)((*stateVector)[i]));
-  }
-  for (i = 12; i < 16; i++) {
-    /* Force new space to be allocated for each pointer */
-    saveStateVector[i] = NULL_PNTRSTRING;
-    pntrLet((pntrString **)(&saveStateVector[i]),
-        (pntrString *)((*stateVector)[i]));
-  }
-  k = ((nmbrString *)((*stateVector)[11]))[0]; /* unkVarsLen */
-  for (i = 4; i <= 7; i++) {
-    pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
-    pntrTmpPtr2 = (pntrString *)((*stateVector)[i]);
-    for (j = 0; j < k; j++) {
-      /* Force new space to be allocated for each pointer */
-      pntrTmpPtr1[j] = NULL_NMBRSTRING;
-      nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
-          (nmbrString *)(pntrTmpPtr2[j]));
-    }
-  }
-  k = pntrLen((pntrString *)((*stateVector)[12]));
-  for (i = 12; i < 16; i++) {
-    pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
-    pntrTmpPtr2 = (pntrString *)((*stateVector)[i]);
-    for (j = 0; j < k; j++) {
-      /* Force new space to be allocated for each pointer */
-      pntrTmpPtr1[j] = NULL_NMBRSTRING;
-      nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
-          (nmbrString *)(pntrTmpPtr2[j]));
-    }
-  }
-
-  /* See if there is a second unification */
-  tmpFlag = unifyH(schemeA, schemeB, stateVector, 1);
-
-  if (!tmpFlag) {
-    /* There is no 2nd unification.  Unify cleared the original stateVector,
-       so return the saved version. */
-    *stateVector = saveStateVector;
-    return (1); /* Return flag that unification is unique. */
-  }
-
-  /* There are two or more unifications.  Deallocate the stateVector
-     we just saved before returning, since we will not use it. */
-  /* stackTop: Make sure it's not -1 before calling nmbrLet() */
-  ((nmbrString *)(saveStateVector[11]))[1] = 0;
-  for (i = 4; i <= 7; i++) {
-    pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
-    for (j = 0; j < ((nmbrString *)(saveStateVector[11]))[0]; j++) {
-                      /* ((nmbrString *)(saveStateVector[11]))[0] is unkVarsLen */
-      nmbrLet((nmbrString **)(&pntrTmpPtr1[j]), NULL_NMBRSTRING);
-    }
-  }
-  for (i = 0; i <= 3; i++) {
-    nmbrLet((nmbrString **)(&saveStateVector[i]),
-        NULL_NMBRSTRING);
-  }
-  for (i = 4; i <= 7; i++) {
-    pntrLet((pntrString **)(&saveStateVector[i]),
-        NULL_PNTRSTRING);
-  }
-  for (i = 8; i <= 11; i++) {
-    nmbrLet((nmbrString **)(&saveStateVector[i]),
-        NULL_NMBRSTRING);
-  }
-  k = pntrLen((pntrString *)(saveStateVector[12]));
-  for (i = 12; i < 16; i++) {
-    pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
-    for (j = 0; j < k; j++) {
-      nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
-          NULL_NMBRSTRING);
-    }
-    pntrLet((pntrString **)(&saveStateVector[i]),
-        NULL_PNTRSTRING);
-  }
-  pntrLet(&saveStateVector, NULL_PNTRSTRING);
-
-  if (tmpFlag == 2) {
-    return (2); /* Unification timed out */
-  }
-
-  return (3); /* Return flag that unification is not unique */
-
-} /* uniqueUnif */
-
-
-/* Deallocates the contents of a stateVector */
-/* Note:  If unifyH() returns 0, there were no more unifications and
-   the stateVector is left empty, so we don't have to call
-   purgeStateVector.  But no harm done if called anyway. */
-void purgeStateVector(pntrString **stateVector) {
-
-  long i, j, k;
-  pntrString *pntrTmpPtr1;
-
-  if (!pntrLen(*stateVector)) return; /* It's already been purged */
-
-  /* stackTop: Make sure it's not -1 before calling nmbrLet() */
-  ((nmbrString *)((*stateVector)[11]))[1] = 0;
-  for (i = 4; i <= 7; i++) {
-    pntrTmpPtr1 = (pntrString *)((*stateVector)[i]);
-    for (j = 0; j < ((nmbrString *)((*stateVector)[11]))[0]; j++) {
-                      /* ((nmbrString *)((*stateVector)[11]))[0] is unkVarsLen */
-      nmbrLet((nmbrString **)(&pntrTmpPtr1[j]), NULL_NMBRSTRING);
-    }
-  }
-  for (i = 0; i <= 3; i++) {
-    nmbrLet((nmbrString **)(&(*stateVector)[i]),
-        NULL_NMBRSTRING);
-  }
-  for (i = 4; i <= 7; i++) {
-    pntrLet((pntrString **)(&(*stateVector)[i]),
-        NULL_PNTRSTRING);
-  }
-  for (i = 8; i <= 11; i++) {
-    nmbrLet((nmbrString **)(&(*stateVector)[i]),
-        NULL_NMBRSTRING);
-  }
-  k = pntrLen((pntrString *)((*stateVector)[12]));
-  for (i = 12; i < 16; i++) {
-    pntrTmpPtr1 = (pntrString *)((*stateVector)[i]);
-    for (j = 0; j < k; j++) {
-      nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
-          NULL_NMBRSTRING);
-    }
-    pntrLet((pntrString **)(&(*stateVector)[i]),
-        NULL_PNTRSTRING);
-  }
-  pntrLet(&(*stateVector), NULL_PNTRSTRING);
-
-  return;
-
-} /* purgeStateVector */
-
-
-/* Prints the substitutions determined by unify for debugging purposes */
-void printSubst(pntrString *stateVector)
-{
-  long d;
-  nmbrString *stackUnkVar; /* Pointer only - not allocated */
-  nmbrString *unifiedScheme; /* Pointer only - not allocated */
-  nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
-  nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
-  long stackTop;
-  vstring tmpStr = "";
-  nmbrString *nmbrTmp = NULL_NMBRSTRING;
-
-  stackTop = ((nmbrString *)(stateVector[11]))[1];
-  stackUnkVar = (nmbrString *)(stateVector[1]);
-  stackUnkVarStart = (nmbrString *)(stateVector[2]);
-  stackUnkVarLen = (nmbrString *)(stateVector[3]);
-  unifiedScheme = (nmbrString *)(stateVector[8]);
-
-  for (d = 0; d <= stackTop; d++) {
-    printLongLine(cat(" Variable '",
-        g_MathToken[stackUnkVar[d]].tokenName,"' was replaced with '",
-        nmbrCvtMToVString(
-            nmbrMid(unifiedScheme,stackUnkVarStart[d] + 1,
-            stackUnkVarLen[d])),"'.",NULL),"    "," ");
-    /* Clear temporary string allocation */
-    let(&tmpStr,"");
-    nmbrLet(&nmbrTmp,NULL_NMBRSTRING);
-  }
-} /* printSubst */
-
-
-/* unifyH() is like unify(), except that when reEntryFlag is 1, a new
-   unification is returned ONLY if the normalized unification does not
-   previously exist in the "Henty filter" part of the  stateVector.  This
-   reduces ambiguous unifications.  The values returned are the same as those
-   returned by unify().  (The redundancy of equivalent unifications was
-   a deficiency pointed out by Jeremy Henty.) */
-char unifyH(
-    nmbrString *schemeA,
-    nmbrString *schemeB,
-    pntrString **stateVector,
-    long reEntryFlag)
-{
-  char tmpFlag;
-  nmbrString *hentyVars = NULL_NMBRSTRING;
-  nmbrString *hentyVarStart = NULL_NMBRSTRING;
-  nmbrString *hentyVarLen = NULL_NMBRSTRING;
-  nmbrString *hentySubstList = NULL_NMBRSTRING;
-
-  /* Bypass this filter if SET HENTY_FILTER OFF is selected. */
-  if (!g_hentyFilter) return unify(schemeA, schemeB, stateVector, reEntryFlag);
-
-  if (!reEntryFlag) {
-
-    tmpFlag = unify(schemeA, schemeB, stateVector, 0);
-    if (tmpFlag == 1) { /* Unification OK */
-
-      /* Get the normalized equivalent substitutions */
-      hentyNormalize(&hentyVars, &hentyVarStart, &hentyVarLen,
-          &hentySubstList, stateVector);
-
-      /* This is the first unification so add it to the filter then return 1 */
-      hentyAdd(hentyVars, hentyVarStart, hentyVarLen,
-          hentySubstList, stateVector);
-
-    }
-    return (tmpFlag);
-
-  } else {
-
-    while (1) {
-      tmpFlag = unify(schemeA, schemeB, stateVector, 1);
-      if (tmpFlag == 1) { /* 0 = not possible, 1 == OK, 2 = timed out */
-
-        /* Get the normalized equivalent substitution */
-        hentyNormalize(&hentyVars, &hentyVarStart, &hentyVarLen,
-            &hentySubstList, stateVector);
-
-        /* Scan the Henty filter to see if this substitution is in it */
-        if (!hentyMatch(hentyVars, hentyVarStart, /*hentyVarLen,*/
-            hentySubstList, stateVector)) {
-
-          /* If it's not in there, this is a new unification so add it
-             to the filter then return 1 */
-          hentyAdd(hentyVars, hentyVarStart, hentyVarLen,
-              hentySubstList, stateVector);
-          return (1);
-        }
-
-      } else {
-        /* No unification is possible, or it timed out */
-        break;
-      }
-
-      /* If we get here this unification is in the Henty filter, so bypass it
-         and get the next unification. */
-
-    } /* End while (1) */
-
-    /* Deallocate memory (when reEntryFlag is 1 and (not possible or timeout)).
-       (In the other cases, hentyVars and hentySubsts pointers are assigned
-       directly to stateVector so they should not be deallocated) */
-    nmbrLet(&hentyVars, NULL_NMBRSTRING);
-    nmbrLet(&hentyVarStart, NULL_NMBRSTRING);
-    nmbrLet(&hentyVarLen, NULL_NMBRSTRING);
-    nmbrLet(&hentySubstList, NULL_NMBRSTRING);
-    return (tmpFlag);
-
-  }
-} /* unifyH */
-
-/* Extract and normalize the unification substitutions. */
-void hentyNormalize(nmbrString **hentyVars, nmbrString **hentyVarStart,
-    nmbrString **hentyVarLen, nmbrString **hentySubstList,
-    pntrString **stateVector)
-{
-  long vars, var1, var2, schLen;
-  long n, el, rra, rrb, rrc, ir, i, j; /* Variables for heap sort */
-  long totalSubstLen, pos;
-  nmbrString *substList = NULL_NMBRSTRING;
-
-  /* Extract the substitutions. */
-  vars = ((nmbrString *)((*stateVector)[11]))[1] + 1; /* stackTop + 1 */
-  nmbrLet((nmbrString **)(&(*hentyVars)), nmbrLeft(
-      (nmbrString *)((*stateVector)[1]), vars)); /* stackUnkVar */
-  nmbrLet((nmbrString **)(&(*hentyVarStart)), nmbrLeft(
-      (nmbrString *)((*stateVector)[2]), vars)); /* stackUnkVarStart */
-  nmbrLet((nmbrString **)(&(*hentyVarLen)), nmbrLeft(
-      (nmbrString *)((*stateVector)[3]), vars)); /* stackUnkVarLen */
-  nmbrLet((nmbrString **)(&(*hentySubstList)),
-      (nmbrString *)((*stateVector)[8])); /* unifiedScheme */
-
-  /* First, if a variable is substituted with another variable,
-     reverse the substitution if the substituted variable has a larger
-     tokenNum. */
-  for (i = 0; i < vars; i++) {
-    if ((*hentyVarLen)[i] == 1) {
-      var2 = (*hentySubstList)[(*hentyVarStart)[i]];
-      if (var2 > g_mathTokens) {
-        /* It's a variable-for-variable substitution */
-        var1 = (*hentyVars)[i];
-        if (var1 > var2) {
-          /* Swap the variables */
-          (*hentyVars)[i] = var2;
-          schLen = nmbrLen(*hentySubstList);
-          for (j = 0; j < schLen; j++) {
-            if ((*hentySubstList)[(*hentyVarStart)[i]] == var2) {
-              (*hentySubstList)[(*hentyVarStart)[i]] = var1;
-            }
-          } /* Next j */
-        } /* End if (var1 > var2) */
-      } /* End if (var2 > g_mathTokens) */
-    } /* End if ((*hentyVarLen)[i] == 1) */
-  } /* Next i */
-
-  /* Next, sort the variables to be substituted in tokenNum order */
-  /* Heap sort from "Numerical Recipes" (Press et. al.) p. 231 */
-  /* Note:  The algorithm in the text has a bug; it does not work for n<2. */
-  n = vars;
-  if (n < 2) goto heapExit;
-  el = n / 2 + 1;
-  ir = n;
- label10:
-  if (el > 1) {
-    el = el - 1;
-    rra = (*hentyVars)[el - 1];
-    rrb = (*hentyVarStart)[el - 1];
-    rrc = (*hentyVarLen)[el - 1];
-  } else {
-    rra = (*hentyVars)[ir - 1];
-    rrb = (*hentyVarStart)[ir - 1];
-    rrc = (*hentyVarLen)[ir - 1];
-    (*hentyVars)[ir - 1] = (*hentyVars)[0];
-    (*hentyVarStart)[ir - 1] = (*hentyVarStart)[0];
-    (*hentyVarLen)[ir - 1] = (*hentyVarLen)[0];
-    ir = ir - 1;
-    if (ir == 1) {
-      (*hentyVars)[0] = rra;
-      (*hentyVarStart)[0] = rrb;
-      (*hentyVarLen)[0] = rrc;
-      goto heapExit;
-    }
-  }
-  i = el;
-  j = el + el;
- label20:
-  if (j <= ir) {
-    if (j < ir) {
-      if ((*hentyVars)[j - 1] < (*hentyVars)[j]) j = j + 1;
-    }
-    if (rra < (*hentyVars)[j - 1]) {
-      (*hentyVars)[i - 1] = (*hentyVars)[j - 1];
-      (*hentyVarStart)[i - 1] = (*hentyVarStart)[j - 1];
-      (*hentyVarLen)[i - 1] = (*hentyVarLen)[j - 1];
-      i = j;
-      j = j + j;
-    } else {
-      j = ir + 1;
-    }
-    goto label20;
-  }
-  (*hentyVars)[i - 1] = rra;
-  (*hentyVarStart)[i - 1] = rrb;
-  (*hentyVarLen)[i - 1] = rrc;
-  goto label10;
-
- heapExit:
-
-  /* Finally, reconstruct the list of substitutions in
-     variable tokenNum order */
-  totalSubstLen = 0;
-  for (i = 0; i < vars; i++) {
-    totalSubstLen = totalSubstLen + (*hentyVarLen)[i];
-  }
-  /* For speedup, preallocate total string needed for the substitution list */
-  nmbrLet(&substList, nmbrSpace(totalSubstLen));
-
-  pos = 0; /* Position in list of substitutions */
-  for (i = 0; i < vars; i++) {
-    for (j = 0; j < (*hentyVarLen)[i]; j++) {
-      substList[pos + j] = (*hentySubstList)[(*hentyVarStart)[i] + j];
-    }
-    (*hentyVarStart)[i] = pos; /* Never used, but assign in case it ever does */
-    pos = pos + (*hentyVarLen)[i];
-  }
-  if (pos != totalSubstLen) bug(1907);
-  nmbrLet((nmbrString **)(&(*hentySubstList)), substList);
-
-  /* Deallocate memory */
-  nmbrLet(&substList, NULL_NMBRSTRING);
-
-  return;
-
-} /* hentyNormalize */
-
-/* Check to see if an equivalent unification exists in the Henty filter */
-flag hentyMatch(nmbrString *hentyVars, nmbrString *hentyVarStart,
-    /*nmbrString *hentyVarLen,*/ nmbrString *hentySubstList,
-    pntrString **stateVector)
-{
-  long i, size;
-
-  size = pntrLen((pntrString *)((*stateVector)[12]));
-
-  for (i = 0; i < size; i++) {
-    if (nmbrEq(hentyVars,
-        (nmbrString *)(((pntrString *)((*stateVector)[12]))[i]))) {
-      if (nmbrEq(hentyVarStart,
-          (nmbrString *)(((pntrString *)((*stateVector)[13]))[i]))) {
-        /* (We don't need to look at [14] because it is determined by [13]) */
-        if (nmbrEq(hentySubstList,
-            (nmbrString *)(((pntrString *)((*stateVector)[15]))[i]))) {
-          return(1); /* A previous equivalent unification was found */
-        }
-      }
-    }
-  } /* Next i */
-
-  return (0); /* There was no previous equivalent unification */
-} /* hentyMatch */
-
-/* Add an entry to the Henty filter */
-void hentyAdd(nmbrString *hentyVars, nmbrString *hentyVarStart,
-    nmbrString *hentyVarLen, nmbrString *hentySubstList,
-    pntrString **stateVector)
-{
-  long size;
-  size = pntrLen((pntrString *)((*stateVector)[12]));
-
-  pntrLet((pntrString **)(&(*stateVector)[12]), pntrAddGElement(
-      (pntrString *)((*stateVector)[12])));
-  ((pntrString *)((*stateVector)[12]))[size] = hentyVars;
-  pntrLet((pntrString **)(&(*stateVector)[13]), pntrAddGElement(
-      (pntrString *)((*stateVector)[13])));
-  ((pntrString *)((*stateVector)[13]))[size] = hentyVarStart;
-  pntrLet((pntrString **)(&(*stateVector)[14]), pntrAddGElement(
-      (pntrString *)((*stateVector)[14])));
-  ((pntrString *)((*stateVector)[14]))[size] = hentyVarLen;
-  pntrLet((pntrString **)(&(*stateVector)[15]), pntrAddGElement(
-      (pntrString *)((*stateVector)[15])));
-  ((pntrString *)((*stateVector)[15]))[size] =
-      hentySubstList;
-} /* hentyAdd */
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* mmunif.c - Unifications for proof assistant (note: unifications for normal
+   proof verification is done in mmveri.c) */
+
+/*
+This module deals with an object called the stateVector, which is a pntrString
+of 16 pointers (called entries 0 through 15 below) to either other pntrStrings
+or to nmbrStrings.  In the description, data in stateVector may be referred to
+by the local C variable the data is typically assigned to, such as
+"unkVarsLen".  The word "variable" in the context of scheme content refers
+to temporary (or "work" or "dummy") variables $1, $2, etc.  The entries are not
+organized in logical order for historical reasons, e.g. entry 11 logically
+comes first.
+
+Entry 11 is a nmbrString of length 4 holding individual parameters.
+
+11[0] is the total number of variables ($1, $2, etc.) in schemeA and schemeB,
+i.e. the two schemes being unified.
+
+  unkVarsLen = ((nmbrString *)((*stateVector)[11]))[0];
+
+11[1] or stackTop is the number of variables (minus 1) that will require
+substitutions in order to perform the unification.  Warning:  stackTop may be
+-1, which could be confused with "end of nmbrString" by some nmbrString
+functions.
+
+  stackTop = ((nmbrString *)((*stateVector)[11]))[1];
+
+11[2] is the number of variables in schemeA, used by oneDirUnif() (only).
+
+  schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
+
+11[3] is the number of entries in the "Henty filter", used by unifyH() (only).
+
+  g_hentyFilterSize = ((nmbrString *)((*stateVector)[11]))[3];
+
+Entry 8 is the result of unifying schemeA and schemeB, which are the two
+schemes being unified.
+
+  unifiedScheme = (nmbrString *)((*stateVector)[8]);
+
+Entries 0 through 3 each have length unkVarsLen.  Entry 1 is a list of token
+numbers for the temporary variables substituted in the unification.
+
+Entry 0 has all variables ($1, $2, etc.) in schemeA and schemeB.
+
+  unkVars = (nmbrString *)((*stateVector)[0]);
+
+In entries 1 through 3, only variables 0 through stackTop (inclusive) have
+meaning.  These entries, along with unifiedScheme, determine what variables
+were substituted and there substitutions.
+
+Entry 1 is the list of variables that were substituted.
+Entry 2 is the location of the substitution in unifiedScheme, for each variable
+in entry 1.
+Entry 3 is the length of the substitution for each variable in entry 1.
+
+  stackUnkVar = (nmbrString *)((*stateVector)[1]);
+  stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
+  stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
+
+Entries 4 thru 7 each point to unkVarsLen nmbrString's.  These entries save the
+data needed to resume unification at any point.  Entries 4 and 5 are
+nmbrString's of length unkVarsLen.  Entries 6 and 7 will have variable length.
+Only the first stackTop+1 nmbrString's have meaning.  Note that stackTop may be
+-1.
+
+  stackSaveUnkVarStart = (pntrString *)((*stateVector)[4]);
+  stackSaveUnkVarLen = (pntrString *)((*stateVector)[5]);
+  stackSaveSchemeA = (pntrString *)((*stateVector)[6]);
+  stackSaveSchemeB = (pntrString *)((*stateVector)[7]);
+
+Entries 9 and 10 save the contents of 2 and 3 in oneDirUnif (only)
+
+  nmbrLet((nmbrString **)(&(*stateVector)[9]),
+      (nmbrString *)((*stateVector)[2]));
+  nmbrLet((nmbrString **)(&(*stateVector)[10]),
+      (nmbrString *)((*stateVector)[3]));
+
+Entries 12 through 15 hold the "Henty filter", i.e. a list of all "normalized"
+unifications so far.  Used by unifyH() (only).  Each entry 12 through 15 is a
+list of pointers of length g_hentyFilterSize, each pointing to g_hentyFilterSize
+nmbrString's.  The Henty filter eliminates redundant equivalent unifications.
+
+Entry 12[i] is a list of variables substituted by the normalized unification.
+Entry 13[i] is the start of each substitution in hentySubstList.
+Entry 14[i] is the length of each substitution in hentySubstList.
+Entry 15[i] is the unified scheme that resulted from the particular unification.
+Note:  i = 0 through g_hentyFilterSize-1 below.
+
+  hentyVars = (nmbrString *)(((pntrString *)((*stateVector)[12]))[i]);
+  hentyVarStart = (nmbrString *)(((pntrString *)((*stateVector)[13]))[i]);
+  hentyVarLen = (nmbrString *)(((pntrString *)((*stateVector)[14]))[i]);
+  hentySubstList = (nmbrString *)(((pntrString *)((*stateVector)[15]))[i]);
+
+*/
+
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mminou.h"
+#include "mmpars.h"
+#include "mmunif.h"
+#include "mmpfas.h" /* For proveStatement global variable */
+
+/*long g_minSubstLen = 0;*/ /* User-settable value - 0 or 1 */
+long g_minSubstLen = 1; /* It was decided to disallow empty subst. by default
+                         since most formal systems don't need it */
+long g_userMaxUnifTrials = 100000; /* Initial value */
+           /* User-defined upper limit (# backtracks) for unification trials */
+           /* 1-Jun-04 nm Changed g_userMaxUnifTrials from 1000 to 100000, which
+              is not a problem with today's faster computers.  This results in
+              fewer annoying "Unification timed out" messages, but the drawback
+              is that (rarely) there may be hundreds of unification
+              choices for the user (which the user can quit from though). */
+long g_unifTrialCount = 0;
+                    /* 0 means don't time out; 1 means start counting trials */
+long g_unifTimeouts = 0; /* Number of timeouts so far for this command */
+flag g_hentyFilter = 1; /* Default to ON (turn OFF for debugging). */
+flag g_bracketMatchInit = 0; /* Global so eraseSource() (mmcmds.c) can clear it */
+
+/* Additional local prototypes */
+void hentyNormalize(nmbrString **hentyVars, nmbrString **hentyVarStart,
+    nmbrString **hentyVarLen, nmbrString **hentySubstList,
+    pntrString **stateVector);
+flag hentyMatch(nmbrString *hentyVars, nmbrString *hentyVarStart,
+    /*nmbrString *hentyVarLen,*/ nmbrString *hentySubstList,
+    pntrString **stateVector);
+void hentyAdd(nmbrString *hentyVars, nmbrString *hentyVarStart,
+    nmbrString *hentyVarLen, nmbrString *hentySubstList,
+    pntrString **stateVector);
+
+
+/* For heuristics */
+int maxNestingLevel = -1;
+int nestingLevel = 0;
+
+/* For improving rejection of impossible substitutions */
+nmbrString_def(g_firstConst);
+nmbrString_def(g_lastConst);
+nmbrString_def(g_oneConst);
+
+
+
+/* Typical call:
+     nmbrStringXxx = makeSubstUnif(&newVarFlag,trialScheme,
+         stateVector);
+     Call this after calling unify().
+     trialScheme should have the same unknown variable names as were in
+         the schemes given to unify().
+     nmbrStringXxx will have these unknown variables substituted with
+         the result of the unification.
+     newVarFlag is 1 if there are new $nn variables in nmbrStringXxx.
+   The caller must deallocate the returned nmbrString.
+*/
+nmbrString *makeSubstUnif(flag *newVarFlag,
+    const nmbrString *trialScheme, pntrString *stateVector)
+{
+  long p,q,i,j,k,m,tokenNum;
+  long schemeLen;
+  nmbrString_def(result);
+  nmbrString_def(stackUnkVar);
+  nmbrString *unifiedScheme; /* Pointer only - not allocated */
+  nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
+  nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
+  long stackTop;
+/*E*/long d;
+/*E*/vstring_def(tmpStr);
+/*E*/let(&tmpStr,tmpStr);
+
+  stackTop = ((nmbrString *)(stateVector[11]))[1];
+  nmbrLet(&stackUnkVar,nmbrLeft((nmbrString *)(stateVector[1]), stackTop + 1));
+  stackUnkVarStart = (nmbrString *)(stateVector[2]); /* stackUnkVarStart */
+  stackUnkVarLen = (nmbrString *)(stateVector[3]); /* stackUnkVarLen */
+  unifiedScheme = (nmbrString *)(stateVector[8]);
+
+/*E*/if(db7)print2("Entered makeSubstUnif.\n");
+/*E*/if(db7)printLongLine(cat("unifiedScheme is ",
+/*E*/    nmbrCvtMToVString(unifiedScheme), NULL), "", " ");
+/*E*/if(db7)printLongLine(cat("trialScheme is ",
+/*E*/    nmbrCvtMToVString(trialScheme), NULL), "", " ");
+/*E*/if(db7)print2("stackTop is %ld.\n",stackTop);
+/*E*/for (d = 0; d <= stackTop; d++) {
+/*E*/  if(db7)print2("Unknown var %ld is %s.\n",d,
+/*E*/      g_MathToken[stackUnkVar[d]].tokenName);
+/*E*/  if(db7)print2("  Its start is %ld; its length is %ld.\n",
+/*E*/      stackUnkVarStart[d],stackUnkVarLen[d]);
+/*E*/}
+  schemeLen = nmbrLen(trialScheme);
+  /* Make the substitutions into trialScheme. */
+  /* First, calculate the length of the final result */
+  q = 0;
+  *newVarFlag = 0; /* Flag that there are new variables in the output string */
+/*E*/if(db7)print2("schemeLen is %ld.\n",schemeLen);
+  for (p = 0; p < schemeLen; p++) {
+/*E*/if(db7)print2("p is %ld.\n",p);
+    tokenNum = trialScheme[p];
+/*E*/if(db7)print2("token is %s, tokenType is %ld\n",g_MathToken[tokenNum].tokenName,
+/*E*/  (long)g_MathToken[tokenNum].tokenType);
+    if (g_MathToken[tokenNum].tokenType == (char)con_) {
+      q++;
+    } else {
+      if (tokenNum > g_mathTokens) {
+        /* It's a candidate for substitution */
+        m = nmbrElementIn(1,stackUnkVar,tokenNum);
+/*E*/if(db7)print2("token is %s, m is %ld\n",g_MathToken[tokenNum].tokenName,m);
+        if (m) {
+          /* It will be substituted */
+          q = q + stackUnkVarLen[m - 1];
+          /* Flag the token position */
+          g_MathToken[tokenNum].tmp = m - 1;
+        } else {
+          /* It will not be substituted */
+          *newVarFlag = 1; /* The result will contain an "unknown" variable */
+          q++;
+          /* Flag the token position */
+          g_MathToken[tokenNum].tmp = -1;
+        }
+      } else {
+        /* It's not an "unknown" variable, so it won't be substituted */
+        q++;
+      }
+    }
+  }
+  /* Allocate space for the final result */
+  nmbrLet(&result, nmbrSpace(q));
+  /* Assign the final result */
+  q = 0;
+  for (p = 0; p < schemeLen; p++) {
+    tokenNum = trialScheme[p];
+    if (g_MathToken[tokenNum].tokenType == (char)con_) {
+      result[q] = tokenNum;
+      q++;
+    } else {
+      if (tokenNum > g_mathTokens) {
+        /* It's a candidate for substitution */
+        k = g_MathToken[tokenNum].tmp; /* Position in stackUnkVar */
+        if (k != -1) {
+          /* It will be substituted */
+          m = stackUnkVarStart[k]; /* Start of substitution */
+          j = stackUnkVarLen[k]; /* Length of substitution */
+          for (i = 0; i < j; i++) {
+            result[q + i] = unifiedScheme[m + i];
+          }
+          q = q + j;
+        } else {
+          /* It will not be substituted */
+          result[q] = tokenNum;
+          q++;
+        }
+      } else {
+        /* It's not an "unknown" variable, so it won't be substituted */
+        result[q] = tokenNum;
+        q++;
+      }
+    } /* end "if a constant" */
+  }
+/*E*/if(db7)print2("after newVarFlag %d\n",(int)*newVarFlag);
+/*E*/if(db7)print2("final len is %ld\n",q);
+/*E*/if(db7)printLongLine(cat("result ",nmbrCvtMToVString(result),NULL),""," ");
+  free_nmbrString(stackUnkVar); /* Deallocate */
+  return (result);
+} /* makeSubstUnif */
+
+
+
+
+
+char unify(
+    const nmbrString *schemeA,
+    const nmbrString *schemeB,
+    /* nmbrString **unifiedScheme, */ /* stateVector[8] holds this */
+    pntrString **stateVector,
+    long reEntryFlag)
+{
+
+
+/* This function unifies two math token strings, schemeA and
+   schemeB.  The result is contained in unifiedScheme.
+   0 is returned if no assignment is possible, 1 if an assignment was
+   found, and 2 if the unification timed out.
+   If reEntryFlag is 1, the next possible set of assignments, if any,
+   is returned.  (*stateVector) contains the state of the previous
+   call.  It is the caller's responsibility to deallocate the
+   contents of (*stateVector) when done, UNLESS a 0 is returned.
+   The caller must assign (*stateVector) to a legal pntrString
+   (e.g. NULL_PNTRSTRING) before calling.
+
+   All variables with a tokenNum > g_mathTokens are assumed
+   to be "unknown" variables that can be assigned; all other
+   variables are treated like constants in the unification
+   algorithm.
+
+   The "unknown" variable assignments are contained in (*stateVector)
+   (which is a complex structure, described above).  Some "unknown"
+   variables may have no assignment, in which case they will
+   remain "unknown", and others may have assignments which include
+   "unknown" variables.  The (*stateVector) entries 9 and 10 are used
+   by oneDirUnif() only.
+*/
+
+  long stackTop;
+  nmbrString *unkVars; /* List of all unknown vars */
+  long unkVarsLen;
+  long schemeAUnkVarsLen;
+  nmbrString *stackUnkVar; /* Location of stacked var in unkVars */
+  nmbrString *stackUnkVarStart; /* Start of stacked var in unifiedScheme*/
+  nmbrString *stackUnkVarLen; /* Length of stacked var assignment */
+  pntrString *stackSaveUnkVarStart; /* stackUnkVarStart at the time a
+                                           variable was first stacked */
+  pntrString *stackSaveUnkVarLen; /* stackUnkVarLen at the time a variable
+                                           was first stacked */
+  pntrString *stackSaveSchemeA; /* Pointer to saved schemeA at the time
+                                         the variable was first stacked */
+  pntrString *stackSaveSchemeB; /* Pointer to saved schemeB at the time
+                                         the variable was first stacked */
+  nmbrString *unifiedScheme; /* Final result */
+  long p; /* Current position in schemeA or schemeB */
+  long substToken; /* Token from schemeA or schemeB that will be substituted */
+  nmbrString_def(substitution);
+                                       /* String to be subst. for substToken */
+  nmbrString *nmbrTmpPtr; /* temp pointer only */
+  pntrString *pntrTmpPtr; /* temp pointer only */
+  nmbrString_def(schA); /* schemeA with dummy token at end */
+  nmbrString_def(schB); /* schemeB with dummy token at end */
+  long i,j,k,m, pairingMismatches;
+  flag breakFlag;
+  flag schemeAFlag;
+  flag timeoutAbortFlag = 0;
+  vstring mToken; /* Pointer only; not allocated */
+
+  /* For detection of simple impossible unifications */
+  flag impossible;
+  long stmt;
+
+  /* For bracket matching heuristic for set.mm */
+  static char bracketMatchOn; /* Default is 'on' */
+  /* static char g_bracketMatchInit = 0; */ /* Global so ERASE can init it */
+  long bracketScanStart, bracketScanStop; /* For one-time $a scan */
+  flag bracketMismatchFound;
+
+
+/*E*/long d;
+/*E*/vstring_def(tmpStr);
+/*E*/let(&tmpStr,tmpStr);
+/*E*/if(db5)print2("Entering unify() with reEntryFlag = %ld.\n",
+/*E*/  (long)reEntryFlag);
+/*E*/if(db5)printLongLine(cat("schemeA is ",
+/*E*/    nmbrCvtMToVString(schemeA),".",NULL),"    ","  ");
+/*E*/if(db5)printLongLine(cat("schemeB is ",
+/*E*/    nmbrCvtMToVString(schemeB),".",NULL),"    ","  ");
+
+  /* Initialization to avoid compiler warning (should not be theoretically
+     necessary) */
+  p = 0;
+  bracketMismatchFound = 0;
+
+  /* Fast early exit -- first or last constants of schemes don't match */
+  if (g_MathToken[schemeA[0]].tokenType == (char)con_) {
+    if (g_MathToken[schemeB[0]].tokenType == (char)con_) {
+      if (schemeA[0] != schemeB[0]) {
+        return (0);
+      }
+    }
+  }
+  /* (j and k are used below also) */
+  j = nmbrLen(schemeA);
+  k = nmbrLen(schemeB);
+  if (!j || !k) bug(1901);
+  if (g_MathToken[schemeA[j-1]].tokenType == (char)con_) {
+    if (g_MathToken[schemeB[k-1]].tokenType == (char)con_) {
+      if (schemeA[j-1] != schemeB[k-1]) {
+        return (0);
+      }
+    }
+  }
+  /* Add dummy token to end of schemeA and schemeB */
+  /* Use one beyond the last mathTokenArray entry for this */
+  nmbrLet(&schA, nmbrAddElement(schemeA, g_mathTokens));
+  nmbrLet(&schB, nmbrAddElement(schemeB, g_mathTokens));
+
+  /* Initialize the usage of constants as the first, last,
+     only constant in a $a statement - for rejecting some simple impossible
+     substitutions - Speed-up: this is now done once and never deallocated */
+  /* g_firstConst is now cleared in eraseSource.c() (mmcmds.c)
+     to trigger this initialization after "erase" */
+  if (!nmbrLen(g_firstConst)) {
+    /* nmbrSpace() sets all entries to 0, not 32 (ASCII space) */
+    nmbrLet(&g_firstConst, nmbrSpace(g_mathTokens));
+    nmbrLet(&g_lastConst, nmbrSpace(g_mathTokens));
+    nmbrLet(&g_oneConst, nmbrSpace(g_mathTokens));
+    /*for (stmt = 1; stmt < proveStatement; stmt++) {*/
+    /* Do it for all statements since we do it once permanently now */
+    for (stmt = 1; stmt <= g_statements; stmt++) {
+      if (g_Statement[stmt].type != (char)a_)
+        continue; /* Not $a */
+      if (g_Statement[stmt].mathStringLen < 2) continue;
+      /* Look at first symbol after variable type symbol */
+      if (g_MathToken[(g_Statement[stmt].mathString)[1]].tokenType == (char)con_) {
+        g_firstConst[(g_Statement[stmt].mathString)[1]] = 1; /* Set flag */
+        if (g_Statement[stmt].mathStringLen == 2) {
+          g_oneConst[(g_Statement[stmt].mathString)[1]] = 1; /* Set flag */
+        }
+      }
+      /* Look at last symbol */
+      if (g_MathToken[(g_Statement[stmt].mathString)[
+          g_Statement[stmt].mathStringLen - 1]].tokenType == (char)con_) {
+        g_lastConst[(g_Statement[stmt].mathString)[
+          g_Statement[stmt].mathStringLen - 1]] = 1; /* Set flag for const */
+      }
+    } /* Next stmt */
+  }
+
+
+  if (!reEntryFlag) {
+    /* First time called */
+    p = 0;
+
+    /* Collect the list of "unknown" variables */
+    /* (Pre-allocate max. length) */
+    /* (Note j and k assignment above) */
+    unkVars = NULL_NMBRSTRING;
+    nmbrLet(&unkVars, nmbrSpace(j + k));
+    unkVarsLen = 0;
+    for (i = 0; i < j; i++) {
+      if (schemeA[i] > g_mathTokens) {
+        /* It's an "unknown" variable */
+        breakFlag = 0;
+        for (m = 0; m < unkVarsLen; m++) {
+          if (unkVars[m] == schemeA[i]) {
+            /* It's already been added to the list */
+            breakFlag = 1;
+          }
+        }
+        if (!breakFlag) {
+          /* Add the new "unknown" var */
+          unkVars[unkVarsLen++] = schemeA[i];
+        }
+      }
+    }
+    /* Save the length of the list of unknown variables in schemeA */
+    schemeAUnkVarsLen = unkVarsLen;
+    for (i = 0; i < k; i++) {
+      if (schemeB[i] > g_mathTokens) {
+        /* It's an "unknown" variable */
+        breakFlag = 0;
+        for (m = 0; m < unkVarsLen; m++) {
+          if (unkVars[m] == schemeB[i]) {
+            /* It's already been added to the list */
+            breakFlag = 1;
+          }
+        }
+        if (!breakFlag) {
+          /* Add the new "unknown" var */
+          unkVars[unkVarsLen++] = schemeB[i];
+        }
+      }
+    }
+
+    /* Deallocate old (*stateVector) assignments */
+    if (pntrLen(*stateVector)) {
+    /*if (((nmbrString *)((*stateVector)[11]))[0] != -1) { */ /*???Change to nmbrLen?*/
+      /* If (*stateVector) not an empty nmbrString */
+      for (i = 4; i <= 7; i++) {
+        pntrTmpPtr = (pntrString *)((*stateVector)[i]);
+        for (j = 0; j < ((nmbrString *)((*stateVector)[11]))[0]; j++) {
+          nmbrLet((nmbrString **)(&pntrTmpPtr[j]),
+              NULL_NMBRSTRING);
+        }
+        pntrLet((pntrString **)(&(*stateVector)[i]),
+            NULL_PNTRSTRING);
+      }
+      for (i = 0; i <= 3; i++) {
+        nmbrLet((nmbrString **)(&(*stateVector)[i]),
+            NULL_NMBRSTRING);
+      }
+      for (i = 8; i <= 10; i++) {
+        nmbrLet((nmbrString **)(&(*stateVector)[i]),
+            NULL_NMBRSTRING);
+      }
+      k = pntrLen((pntrString *)((*stateVector)[12]));
+      for (i = 12; i < 16; i++) {
+        pntrTmpPtr = (pntrString *)((*stateVector)[i]);
+        for (j = 0; j < k; j++) {
+          nmbrLet((nmbrString **)(&pntrTmpPtr[j]),
+              NULL_NMBRSTRING);
+        }
+        pntrLet((pntrString **)(&(*stateVector)[i]),
+            NULL_PNTRSTRING);
+      }
+      /* Leave [11] pre-allocated to length 4 */
+    } else {
+      /* It was never allocated before -- do it now */
+      /* Allocate stateVector - it will be assigned upon exiting */
+      pntrLet(&(*stateVector), pntrPSpace(16));
+      nmbrLet((nmbrString **)(&(*stateVector)[11]), nmbrSpace(4));
+    }
+
+    /* Pre-allocate the (*stateVector) structure */
+    stackTop = -1;
+    stackUnkVar = NULL_NMBRSTRING;
+    stackUnkVarStart = NULL_NMBRSTRING;
+    stackUnkVarLen = NULL_NMBRSTRING;
+    stackSaveUnkVarStart = NULL_PNTRSTRING;
+    stackSaveUnkVarLen = NULL_PNTRSTRING;
+    stackSaveSchemeA = NULL_PNTRSTRING;
+    stackSaveSchemeB = NULL_PNTRSTRING;
+    unifiedScheme = NULL_NMBRSTRING;
+    nmbrLet(&stackUnkVar, nmbrSpace(unkVarsLen));
+    nmbrLet(&stackUnkVarStart, stackUnkVar);
+    nmbrLet(&stackUnkVarLen, stackUnkVar);
+
+    /* These next 4 hold pointers to nmbrStrings */
+    pntrLet(&stackSaveUnkVarStart, pntrNSpace(unkVarsLen));
+    pntrLet(&stackSaveUnkVarLen, stackSaveUnkVarStart);
+    pntrLet(&stackSaveSchemeA, stackSaveUnkVarStart);
+    pntrLet(&stackSaveSchemeB, stackSaveUnkVarStart);
+    for (i = 0; i < unkVarsLen; i++) {
+      /* Preallocate the stack space for these */
+      nmbrLet((nmbrString **)(&stackSaveUnkVarStart[i]),
+          stackUnkVar);
+      nmbrLet((nmbrString **)(&stackSaveUnkVarLen[i]),
+          stackUnkVar);
+    }
+
+    /* Set a flag that the "unknown" variables are not on the stack yet */
+    /* (Otherwise this will be the position on the stack) */
+    for (i = 0; i < unkVarsLen; i++) {
+      g_MathToken[unkVars[i]].tmp = -1;
+    }
+
+  } else { /* reEntryFlag != 0 */
+
+    /* We are re-entering to get the next possible assignment. */
+
+    /* Restore the (*stateVector) variables */
+    unkVarsLen = ((nmbrString *)((*stateVector)[11]))[0];
+    unkVars = (nmbrString *)((*stateVector)[0]);
+    stackTop = ((nmbrString *)((*stateVector)[11]))[1];
+    stackUnkVar = (nmbrString *)((*stateVector)[1]);
+    stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
+    stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
+    stackSaveUnkVarStart = (pntrString *)((*stateVector)[4]);
+    stackSaveUnkVarLen = (pntrString *)((*stateVector)[5]);
+    stackSaveSchemeA = (pntrString *)((*stateVector)[6]);
+    stackSaveSchemeB = (pntrString *)((*stateVector)[7]);
+    unifiedScheme = (nmbrString *)((*stateVector)[8]);
+    schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
+                                                      /* Used by oneDirUnif() */
+
+    /* Set the location of the "unknown" variables on the stack */
+    /* (This may have been corrupted outside this function) */
+    for (i = 0; i < unkVarsLen; i++) {
+      g_MathToken[unkVars[i]].tmp = -1; /* Not on the stack */
+    }
+    for (i = 0; i <= stackTop; i++) {
+      g_MathToken[stackUnkVar[i]].tmp = i;
+    }
+
+    /* Force a backtrack to the next assignment */
+    goto backtrack;
+   reEntry1: /* goto backtrack will come back here if reEntryFlag is set */
+    reEntryFlag = 0;
+
+
+  }
+
+  /* Perform the unification */
+
+ scan:
+/*E*/if(db6)print2("Entered scan: p=%ld\n",p);
+/*E*/if(db6)print2("Enter scan sbA %s\n",nmbrCvtMToVString(schA));
+/*E*/if(db6)print2("Enter scan sbB %s\n",nmbrCvtMToVString(schB));
+/*E*/if(db6)let(&tmpStr,tmpStr);
+  while (schA[p] == schB[p] &&
+      schA[p + 1] != -1) {
+    p++;
+  }
+/*E*/if(db6)print2("First mismatch: p=%ld\n",p);
+
+  if (schA[p] == g_mathTokens
+      || schB[p] == g_mathTokens) {
+    /* One of the strings is at the end. */
+    if (schA[p] != schB[p]) {
+      /* But one is longer than the other. */
+      if (schA[p] <= g_mathTokens &&
+          schB[p] <= g_mathTokens) {
+        /* Neither token is an unknown variable.  (Otherwise we might be able
+           to assign the unknown variable to a null string, thus making
+           the schemes match, so we shouldn't backtrack.) */
+/*E*/if(db6)print2("Backtracked because end-of-string\n");
+        goto backtrack;
+      }
+    } else {
+      if (schA[p + 1] == -1) {
+        /* End of schA; a successful unification occurred */
+        goto done;
+      }
+      /* Otherwise, we are in the middle of several schemes being unified
+         simultaneously, so just continue. */
+      /* (g_mathTokens should be used by the caller to separate
+         schemes that are joined together for simultaneous unification) */
+    }
+  }
+
+  /* This test, combined with switchover to schemeB in backtrack,
+     prevents variable lockup, for example where
+     schemeA = ?1 B C, schemeB = ?2 A B C.  Without this test, schemeB
+     becomes ?1 A B C, and then it can never match schemeA.  This should be
+     checked out further:  what about more than 2 variables?  This kind of
+     "variable lockup" may be a more serious problem. */
+  /* A test case:
+   schemeA is $$ |- ( ( ?463 -> -. -. ph ) -> ( -. ph -> ( ph -> -. -. ph ) ) ).
+   schemeB is $$ |- ( ?464 -> ( ?465 -> ?464 ) ).
+  */
+  if (schB[p] > g_mathTokens && schA[p] > g_mathTokens) {
+    /* Both scheme A and scheme B have variables in the match position.
+       Which one to use? */
+    /* If neither A nor B is on the stack, use A.   Backtrack will put B
+       on the stack when A's possibilities are exhausted. */
+    /* If A is on the stack, use A. */
+    /* If B is on the stack, use B. */
+    /* If A and B are on the stack, bug. */
+    /* In other words:  if B is not on the stack, use A. */
+    if (g_MathToken[schB[p]].tmp == -1) {
+      /* B is not on the stack */
+      goto schAUnk;
+    } else {
+      if (g_MathToken[schA[p]].tmp != -1) bug(1902); /* Both are on the stack */
+      goto schBUnk;
+    }
+  }
+
+ schBUnk:
+  if (schB[p] > g_mathTokens) {
+/*E*/if(db6)print2("schB has unknown variable\n");
+    /* An "unknown" variable is in scheme B */
+    schemeAFlag = 0;
+    substToken = schB[p];
+
+    if (g_MathToken[substToken].tmp == -1) {
+      /* The "unknown" variable is not on the stack; add it */
+      stackTop++;
+      stackUnkVar[stackTop] = substToken;
+      g_MathToken[substToken].tmp = stackTop;
+      stackUnkVarStart[stackTop] = p;
+      /* Start with a variable length of 0 or 1 */
+      stackUnkVarLen[stackTop] = g_minSubstLen;
+      /* Save the rest of the current state for backtracking */
+      nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarStart[stackTop]);
+      for (i = 0; i <= stackTop; i++) {
+        nmbrTmpPtr[i] = stackUnkVarStart[i];
+      }
+      nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
+      for (i = 0; i <= stackTop; i++) {
+        nmbrTmpPtr[i] = stackUnkVarLen[i];
+      }
+      nmbrLet((nmbrString **)(&stackSaveSchemeA[stackTop]),
+          schA);
+      nmbrLet((nmbrString **)(&stackSaveSchemeB[stackTop]),
+          schB);
+    }
+
+    if (substToken != stackUnkVar[stackTop]) {
+      print2("PROGRAM BUG #1903\n");
+      print2("substToken is %s\n", g_MathToken[substToken].tokenName);
+      print2("stackTop %ld\n", stackTop);
+      print2("p %ld stackUnkVar[stackTop] %s\n", p,
+        g_MathToken[stackUnkVar[stackTop]].tokenName);
+      print2("schA %s\nschB %s\n", nmbrCvtMToVString(schA),
+        nmbrCvtMToVString(schB));
+      bug(1903);
+    }
+    nmbrLet(&substitution, nmbrMid(schA, p + 1,
+        stackUnkVarLen[stackTop]));
+    goto substitute;
+  }
+
+ schAUnk:
+  if (schA[p] > g_mathTokens) {
+/*E*/if(db6)print2("schA has unknown variable\n");
+    /* An "unknown" variable is in scheme A */
+    schemeAFlag = 1;
+    substToken = schA[p];
+    if (g_MathToken[substToken].tmp == -1) {
+      /* The "unknown" variable is not on the stack; add it */
+      stackTop++;
+      stackUnkVar[stackTop] = substToken;
+      g_MathToken[substToken].tmp = stackTop;
+      stackUnkVarStart[stackTop] = p;
+      /* Start with a variable length of 0 or 1 */
+      stackUnkVarLen[stackTop] = g_minSubstLen;
+      /* Save the rest of the current state for backtracking */
+      nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarStart[stackTop]);
+      for (i = 0; i <= stackTop; i++) {
+        nmbrTmpPtr[i] = stackUnkVarStart[i];
+      }
+      nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
+      for (i = 0; i <= stackTop; i++) {
+        nmbrTmpPtr[i] = stackUnkVarLen[i];
+      }
+      nmbrLet((nmbrString **)(&stackSaveSchemeA[stackTop]),
+          schA);
+      nmbrLet((nmbrString **)(&stackSaveSchemeB[stackTop]),
+          schB);
+    }
+
+    if (substToken != stackUnkVar[stackTop]) {
+/*E*/print2("PROGRAM BUG #1904\n");
+/*E*/print2("\nsubstToken is %s\n",g_MathToken[substToken].tokenName);
+/*E*/print2("stack top %ld\n",stackTop);
+/*E*/print2("p %ld stackUnkVar[stackTop] %s\n",p,
+/*E*/g_MathToken[stackUnkVar[stackTop]].tokenName);
+/*E*/print2("schA %s\nschB %s\n",nmbrCvtMToVString(schA),nmbrCvtMToVString(schB));
+      bug(1904);
+    }
+    nmbrLet(&substitution, nmbrMid(schB, p + 1,
+        stackUnkVarLen[stackTop]));
+    goto substitute;
+  }
+
+  /* Neither scheme has an unknown variable; unification with current assignment
+     failed, so backtrack */
+/*E*/if(db6)print2("Neither scheme has unknown variable\n");
+  goto backtrack;
+
+
+ substitute:
+/*E*/if(db6)print2("Entering substitute...\n");
+/*E*/for (d = 0; d <= stackTop; d++) {
+/*E*/  if(db6)print2("Unknown var %ld is %s.\n",d,
+/*E*/      g_MathToken[stackUnkVar[d]].tokenName);
+/*E*/  if(db6)print2("  Its start is %ld; its length is %ld.\n",
+/*E*/      stackUnkVarStart[d],stackUnkVarLen[d]);
+/*E*/}
+  /* Subst. all occurrences of substToken with substitution in schA and schB */
+  /*???We could speed things up by making substitutions before the pointer to
+    ???to the unifiedScheme only, and keeping track of 3 pointers (A, B, &
+    ???unified schemes); unifiedScheme would hold only stuff before pointer */
+
+  /* First, we must make sure that the substToken doesn't occur in the
+     substutition. */
+  if (nmbrElementIn(1, substitution, substToken)) {
+/*E*/if(db6)print2("Substituted token occurs in substitution string\n");
+    goto backtrack;
+  }
+
+  /* Next, we must make sure that the end of string doesn't occur in the
+     substitution. */
+  /* (This takes care of the case where the unknown variable aligns with
+     end of string character; in this case, only a null substitution is
+     permissible.  If the substitution length is 1 or greater, this "if"
+     statement will detect it.) */
+  if (substitution[0] == g_mathTokens) {
+/*E*/if(db6)print2("End of string token occurs in substitution string\n");
+    /* We must pop the stack here rather than in backtrack, because we
+       are already one token beyond the end of a scheme, and backtrack
+       would therefore test one token beyond that, missing the fact that
+       the substitution has overflowed beyond the end of a scheme. */
+    /* Set the flag that it's not on the stack and pop stack */
+    g_MathToken[stackUnkVar[stackTop]].tmp = -1;
+    stackTop--;
+    goto backtrack;
+  }
+
+  /* Bracket matching is customized to set.mm to result in fewer ambiguous
+     unifications. */
+  /* Automatically disable bracket matching if any $a has
+     unmatched brackets */
+  /* The static variable g_bracketMatchInit tells us to check all $a's
+     if it is 0; if 1, skip the $a checking.  Make sure that the RESET
+     command sets g_bracketMatchInit=0. */
+  /* ???  To do:  put individual bracket type checks into a loop or
+     function call for code efficiency (but don't slow down program); maybe
+     read the bracket types to check from a list; maybe refine so that only
+     the mismatched bracket types found in the $a scan are skipped, but
+     matched one are not */
+  for (i = g_bracketMatchInit; i <= 1; i++) {
+    /* This loop has 2 passes (0 and 1) if g_bracketMatchInit=0 to set
+       bracketMatchOn = 0 or 1, and 1 pass otherwise */
+    bracketMismatchFound = 0;  /* Don't move down; needed for break below */
+    if (g_bracketMatchInit == 0) {  /* Initialization pass */
+      if (i != 0) bug(1908);
+      /* Scan all ($a) statements */
+      bracketScanStart = 1;
+      bracketScanStop = g_statements;
+    } else {    /* Normal pass */
+      if (i != 1) bug(1909);
+      if (!bracketMatchOn) break; /* Skip the whole bracket check because a
+          mismatched bracket was found in some $a in the initialization pass */
+      /* Set dummy parameters to force a single loop pass */
+      bracketScanStart = 0;
+      bracketScanStop = 0;
+    }
+    for (m = bracketScanStart; m <= bracketScanStop; m++) {
+      if (g_bracketMatchInit == 0) {  /* Initialization pass */
+        if (g_Statement[m].type != a_) continue;
+        nmbrTmpPtr = g_Statement[m].mathString;
+      } else {  /* Normal pass */
+        nmbrTmpPtr = substitution;
+      }
+      j = nmbrLen(nmbrTmpPtr);
+
+      /* Make sure left and right parentheses match */
+      pairingMismatches = 0; /* Counter of parens: + for "(" and - for ")" */
+      for (k = 0; k < j; k++) {
+        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
+        if (mToken[0] == '(' && mToken[1] == 0 ) {
+          pairingMismatches++;
+        } else if (mToken[0] == ')' && mToken[1] == 0 ) {
+          pairingMismatches--;
+          if (pairingMismatches < 0) break; /* Detect wrong order */
+        }
+      } /* Next k */
+      if (pairingMismatches != 0) {
+        bracketMismatchFound = 1;
+        break;
+      }
+
+      /* Make sure left and right braces match */
+      pairingMismatches = 0; /* Counter of braces: + for "{" and - for "}" */
+      for (k = 0; k < j; k++) {
+        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
+        if (mToken[0] == '{' && mToken[1] == 0 ) pairingMismatches++;
+        else
+          if (mToken[0] == '}' && mToken[1] == 0 ) {
+            pairingMismatches--;
+            if (pairingMismatches < 0) break; /* Detect wrong order */
+          }
+      } /* Next k */
+      if (pairingMismatches != 0) {
+        bracketMismatchFound = 1;
+        break;
+      }
+
+      /* Make sure left and right brackets match */
+      pairingMismatches = 0; /* Counter of brackets: + for "[" and - for "]" */
+      for (k = 0; k < j; k++) {
+        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
+        if (mToken[0] == '[' && mToken[1] == 0 )
+          pairingMismatches++;
+        else
+          if (mToken[0] == ']' && mToken[1] == 0 ) {
+            pairingMismatches--;
+            if (pairingMismatches < 0) break; /* Detect wrong order */
+          }
+      } /* Next k */
+      if (pairingMismatches != 0) {
+        bracketMismatchFound = 1;
+        break;
+      }
+
+      /* Make sure left and right triangle brackets match */
+      pairingMismatches = 0; /* Counter of brackets: + for "<.", - for ">." */
+      for (k = 0; k < j; k++) {
+        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
+        if (mToken[1] == 0) continue;
+        if (mToken[0] == '<' && mToken[1] == '.' && mToken[2] == 0 )
+            pairingMismatches++;
+        else
+          if (mToken[0] == '>' && mToken[1] == '.' && mToken[2] == 0 ) {
+            pairingMismatches--;
+            if (pairingMismatches < 0) break; /* Detect wrong order */
+          }
+      } /* Next k */
+      if (pairingMismatches != 0) {
+        bracketMismatchFound = 1;
+        break;
+      }
+
+      /* Make sure underlined brackets match */
+      pairingMismatches = 0; /* Counter of brackets: + for "[_", - for "]_" */
+      for (k = 0; k < j; k++) {
+        mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
+        if (mToken[1] == 0) continue;
+        if (mToken[0] == '[' && mToken[1] == '_' && mToken[2] == 0 )
+            pairingMismatches++;
+        else
+          if (mToken[0] == ']' && mToken[1] == '_' && mToken[2] == 0 ) {
+            pairingMismatches--;
+            if (pairingMismatches < 0) break; /* Detect wrong order */
+          }
+      } /* Next k */
+      if (pairingMismatches != 0) {
+        bracketMismatchFound = 1;
+        break;
+      }
+    } /* next m */
+
+    if (g_bracketMatchInit == 0) {   /* Initialization pass */
+      /* We've finished the one-time $a scan.  Set flags accordingly. */
+      if (bracketMismatchFound) { /* Some $a has a bracket mismatch */
+        if (m < 1 || m > g_statements) bug(1910);
+        printLongLine(cat("The bracket matching unification heuristic was",
+           " turned off for this database because of a bracket mismatch in",
+           " statement \"",
+           /* (m should be accurate due to break above) */
+           g_Statement[m].labelName,
+           "\".", NULL),
+           "    ", " ");
+        /*
+        printLongLine(cat("The bracket matching unification heuristic was",
+           " turned off for this database.", NULL),
+           "    ", " ");
+        */
+        bracketMatchOn = 0; /* Turn off static flag for this database */
+      } else {    /* Normal pass */
+        bracketMatchOn = 1; /* Turn it on */
+      }
+/*E*/if(db6)print2("bracketMatchOn = %ld\n", (long)bracketMatchOn);
+      g_bracketMatchInit = 1; /* We're done with the one-time $a scan */
+    }
+  } /* next i */
+
+  if (bracketMismatchFound) goto backtrack;
+
+  j = nmbrLen(substitution);
+
+  /* Quick scan to reject some impossible unifications: If the
+     first symbol in a substitution is a constant, it must match
+     the 2nd constant of some earlier $a statement (e.g. "<" matches
+     "class <", "Ord (/)" matches "class Ord A").  Same applies to
+     last symbol. */
+  /* This prefilter is too aggressive when empty substitutions
+     are allowed.  Therefore added "g_minSubstLen > 0" to fix miu.mm theorem1
+     Proof Assistant failure reported by Josh Purinton. */
+  if (j/*subst len*/ > 0 && g_minSubstLen > 0) {
+    impossible = 0;
+    if (g_MathToken[substitution[0]].tokenType == (char)con_) {
+      if (!g_firstConst[substitution[0]]
+         || (j == 1 && !g_oneConst[substitution[0]])) {
+        impossible = 1;
+      }
+    }
+    if (g_MathToken[substitution[j - 1]].tokenType == (char)con_) {
+      if (!g_lastConst[substitution[j - 1]]) {
+        impossible = 1;
+      }
+    }
+    if (impossible) {
+/*E*/if(db6)print2("Impossible subst: %s\n", nmbrCvtMToVString(substitution));
+      goto backtrack;
+    }
+  }
+
+  /* Now perform the substitutions */
+/*E*/if(db6)print2("Substitution is '%s'\n",nmbrCvtMToVString(substitution));
+  k = 1;
+  while (1) {
+    /* Perform the substitutions into scheme A */
+    k = nmbrElementIn(k, schA, substToken);
+    if (!k) break;
+
+    if (schemeAFlag) {
+      /* The token to be substituted was in scheme A */
+      /* Adjust position and earlier var. starts and lengths */
+      if (k - 1 <= p) {
+        if (k <= p) {
+          /* Adjust assignments in stack */
+          for (i = 0; i <= stackTop; i++) {
+            if (k - 1 < stackUnkVarStart[i]) {
+              stackUnkVarStart[i] = stackUnkVarStart[i] + j-1;
+            } else {
+              if (k <= stackUnkVarStart[i] +
+                  stackUnkVarLen[i]) {
+                stackUnkVarLen[i] = stackUnkVarLen[i] + j - 1;
+              }
+            }
+          }
+        }
+        p = p + j - 1; /* Adjust scan position */
+/*E*/if(db6)print2("Scheme A adjusted p=%ld\n",p);
+      }
+    }
+
+    nmbrLet(&schA, nmbrCat(
+        nmbrLeft(schA, k - 1), substitution, nmbrRight(schA, k + 1), NULL));
+    k = k + j - 1;
+  }
+  k = 1;
+  while (1) {
+    /* Perform the substitutions into scheme B */
+    k = nmbrElementIn(k, schB, substToken);
+    if (!k) break;
+
+    if (!schemeAFlag) {
+      /* The token to be substituted was in scheme B */
+      /* Adjust scan position and earlier var. starts and lengths */
+      if (k - 1 <= p) {
+        if (k <= p) {
+          /* Adjust assignments in stack */
+          for (i = 0; i <= stackTop; i++) {
+            if (k - 1 < stackUnkVarStart[i]) {
+              stackUnkVarStart[i] = stackUnkVarStart[i] + j-1;
+            } else {
+              if (k <= stackUnkVarStart[i] +
+                  stackUnkVarLen[i]) {
+                stackUnkVarLen[i] = stackUnkVarLen[i] + j - 1;
+              }
+            }
+          }
+        }
+        p = p + j - 1; /* Adjust scan position */
+      }
+/*E*/if(db6)print2("Scheme B adjusted p=%ld\n",p);
+    }
+
+    nmbrLet(&schB, nmbrCat(
+        nmbrLeft(schB, k - 1), substitution, nmbrRight(schB, k + 1), NULL));
+    k = k + j - 1;
+  }
+  p++;
+/*E*/if(db6)print2("Scheme A or B final p=%ld\n",p);
+/*E*/if(db6)print2("after sub sbA %s\n",nmbrCvtMToVString(schA));
+/*E*/if(db6)print2("after sub sbB %s\n",nmbrCvtMToVString(schB));
+/*E*/for (d = 0; d <= stackTop; d++) {
+/*E*/  if(db6)print2("Unknown var %ld is %s.\n",d,
+/*E*/      g_MathToken[stackUnkVar[d]].tokenName);
+/*E*/  if(db6)print2("  Its start is %ld; its length is %ld.\n",
+/*E*/      stackUnkVarStart[d],stackUnkVarLen[d]);
+/*E*/}
+  goto scan;
+
+ backtrack:
+/*E*/if(db6)print2("Entered backtrack with p=%ld stackTop=%ld\n",p,stackTop);
+  if (stackTop < 0) {
+    goto abort;
+  }
+  if (g_unifTrialCount > 0) { /* Flag that timeout is active */
+    g_unifTrialCount++;
+    if (g_unifTrialCount > g_userMaxUnifTrials) {
+      g_unifTimeouts++; /* Update number of timeouts found */
+/*E*/if(db5)print2("Aborted due to timeout: %ld > %ld\n",
+/*E*/    g_unifTrialCount, g_userMaxUnifTrials);
+      timeoutAbortFlag = 1;
+      goto abort;
+    }
+  }
+  /* Add 1 to stackTop variable length */
+  nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
+  nmbrTmpPtr[stackTop]++;
+  /* Restore the state from the stack top */
+  nmbrLet(&stackUnkVarStart, (nmbrString *)(stackSaveUnkVarStart[stackTop]));
+  nmbrLet(&schA, (nmbrString *)(stackSaveSchemeA[stackTop]));
+  nmbrLet(&schB, (nmbrString *)(stackSaveSchemeB[stackTop]));
+  /* Restore the scan position */
+  p = stackUnkVarStart[stackTop];
+ switchVarToB:
+  /* Restore the state from the stack top */
+  nmbrLet(&stackUnkVarLen, (nmbrString *)(stackSaveUnkVarLen[stackTop]));
+
+  /* If the variable overflows the end of the scheme its assigned to,
+     pop the stack */
+/*E*/if(db6)print2("Backtracked to token %s.\n",
+/*E*/  g_MathToken[stackUnkVar[stackTop]].tokenName);
+  if (stackUnkVar[stackTop] == schA[p]) {
+    /* It was in scheme A; see if it overflows scheme B */
+    if (schB[p - 1 + stackUnkVarLen[stackTop]]
+        == g_mathTokens) {
+/*E*/if(db6)print2("It was in scheme A; overflowed scheme B: p=%ld, len=%ld.\n",
+/*E*/  p,stackUnkVarLen[stackTop]);
+      /* Set the flag that it's not on the stack */
+      g_MathToken[stackUnkVar[stackTop]].tmp = -1;
+
+      /* See if the token in scheme B at this position is also a variable */
+      /* If so, switch the stack top variable to the one in scheme B and
+         restart the scan on its length */
+      if (schB[p] > g_mathTokens) {
+/*E*/if(db6)print2("Switched var-var match to scheme B token %s\n",
+/*E*/     g_MathToken[stackUnkVar[stackTop]].tokenName);
+        /* The scheme B variable will not be on the stack. */
+        if (g_MathToken[schB[p]].tmp != -1) bug(1905);
+        /* Make the token in scheme B become the variable at the stack top */
+        stackUnkVar[stackTop] = schB[p];
+        g_MathToken[schB[p]].tmp = stackTop;
+        /* Start with a variable length of 0 or 1 */
+        stackUnkVarLen[stackTop] = g_minSubstLen;
+        /* Initialize stackTop variable length */
+        nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
+        nmbrTmpPtr[stackTop] = g_minSubstLen;
+        /* Restart the backtrack with double variable switched to scheme B */
+        goto switchVarToB;
+      }
+
+      /* Pop stack */
+      stackTop--;
+      goto backtrack;
+    }
+  } else {
+    /* It was in scheme B; see if it overflows scheme A */
+    if (schA[p - 1 + stackUnkVarLen[stackTop]]
+        == g_mathTokens) {
+/*E*/if(db6)print2("It was in scheme B; overflowed scheme A: p=%ld, len=%ld.\n",
+/*E*/  p,stackUnkVarLen[stackTop]);
+      /* Set the flag that it's not on the stack and pop stack */
+      g_MathToken[stackUnkVar[stackTop]].tmp = -1;
+      stackTop--;
+      goto backtrack;
+    }
+  }
+/*E*/if(db6)print2("Exited backtrack with p=%ld stackTop=%ld\n",p,stackTop);
+  if (reEntryFlag) goto reEntry1;
+  goto scan; /* Continue the scan */
+
+ done:
+
+  /* Assign the final result */
+  nmbrLet(&unifiedScheme, nmbrLeft(schA, nmbrLen(schA) - 1));
+/*E*/if(db5)print2("Backtrack count was %ld\n",g_unifTrialCount);
+/*E*/if(db5)printLongLine(cat("Unified scheme is ",
+/*E*/    nmbrCvtMToVString(unifiedScheme),".",NULL),"    ","  ");
+  /* Assign the 12 components of (*stateVector).  Some of the components hold
+     pointers to pointer strings. */
+  /* 0 holds the unkVars array and length */
+  ((nmbrString *)((*stateVector)[11]))[0] = unkVarsLen;
+  (*stateVector)[0] = unkVars;
+  /* 1 holds the stack top and the "unknown" vars on the stack */
+  ((nmbrString *)((*stateVector)[11]))[1] = stackTop;
+  (*stateVector)[1] = stackUnkVar;
+  (*stateVector)[2] = stackUnkVarStart;
+  (*stateVector)[3] = stackUnkVarLen;
+  (*stateVector)[4] = stackSaveUnkVarStart;
+  (*stateVector)[5] = stackSaveUnkVarLen;
+  (*stateVector)[6] = stackSaveSchemeA;
+  (*stateVector)[7] = stackSaveSchemeB;
+  /* Save the result */
+  (*stateVector)[8] = unifiedScheme;
+  /* Components 9 and 10 save the previous assignment.
+     This is handled by oneDirUnif() if oneDirUnif() is called. */
+  ((nmbrString *)((*stateVector)[11]))[2] = schemeAUnkVarsLen; /* Used by oneDirUnif() */
+
+/*E*/if(db5)printSubst(*stateVector);
+  /* Deallocate nmbrStrings */
+  free_nmbrString(schA);
+  free_nmbrString(schB);
+  free_nmbrString(substitution);
+  return (1);
+
+ abort:
+/*E*/if(db5)print2("Backtrack count was %ld\n",g_unifTrialCount);
+  /* Deallocate stateVector contents */
+  free_nmbrString(unkVars);
+  free_nmbrString(stackUnkVar);
+  free_nmbrString(stackUnkVarStart);
+  free_nmbrString(stackUnkVarLen);
+  for (i = 0; i < unkVarsLen; i++) {
+    /* Deallocate the stack space for these */
+    nmbrLet((nmbrString **)(&stackSaveUnkVarStart[i]),
+        NULL_NMBRSTRING);
+    nmbrLet((nmbrString **)(&stackSaveUnkVarLen[i]),
+        NULL_NMBRSTRING);
+    nmbrLet((nmbrString **)(&stackSaveSchemeA[i]),
+        NULL_NMBRSTRING);
+    nmbrLet((nmbrString **)(&stackSaveSchemeB[i]),
+        NULL_NMBRSTRING);
+  }
+  free_pntrString(stackSaveUnkVarStart);
+  free_pntrString(stackSaveUnkVarLen);
+  free_pntrString(stackSaveSchemeA);
+  free_pntrString(stackSaveSchemeB);
+  free_nmbrString(unifiedScheme);
+  /* Deallocate entries used by oneDirUnif() */
+  free_nmbrString(*(nmbrString **)(&(*stateVector)[9]));
+  free_nmbrString(*(nmbrString **)(&(*stateVector)[10]));
+  /* Deallocate entries used by unifyH() */
+  k = pntrLen((pntrString *)((*stateVector)[12]));
+  for (i = 12; i < 16; i++) {
+    pntrTmpPtr = (pntrString *)((*stateVector)[i]);
+    for (j = 0; j < k; j++) {
+      free_nmbrString(*(nmbrString **)(&pntrTmpPtr[j]));
+    }
+    free_pntrString(*(pntrString **)(&(*stateVector)[i]));
+  }
+  /* Deallocate the stateVector itself */
+  ((nmbrString *)((*stateVector)[11]))[1] = 0;
+      /* stackTop: Make sure it's not -1 before calling nmbrLet() */
+  free_nmbrString(*(nmbrString **)(&(*stateVector)[11]));
+  free_pntrString(*stateVector);
+
+  /* Deallocate nmbrStrings */
+  free_nmbrString(schA);
+  free_nmbrString(schB);
+  free_nmbrString(substitution);
+
+  if (timeoutAbortFlag) {
+    return (2);
+  }
+  return (0);
+} /* unify */
+
+
+/* oneDirUnif() is like unify(), except that when reEntryFlag is 1,
+   a new unification is returned ONLY if the assignments to the
+   variables in schemeA have changed.  This is used to speed up the
+   program. */
+/*???This whole thing may be screwed up -- it seems to be based on
+   all unknown vars (including those without substitutions), not just
+   the ones on the stack. */
+flag oneDirUnif(
+    const nmbrString *schemeA,
+    const nmbrString *schemeB,
+    pntrString **stateVector,
+    long reEntryFlag)
+{
+long i;
+flag tmpFlag;
+long schemeAUnkVarsLen;
+nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
+nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
+nmbrString *oldStackUnkVarStart; /* Pointer only - not allocated */
+nmbrString *oldStackUnkVarLen; /* Pointer only - not allocated */
+
+  if (!reEntryFlag) {
+    tmpFlag = unify(schemeA, schemeB, stateVector, 0);
+    if (tmpFlag) {
+      /* Save the initial variable assignments */
+      nmbrLet((nmbrString **)(&(*stateVector)[9]),
+          (nmbrString *)((*stateVector)[2]));
+      nmbrLet((nmbrString **)(&(*stateVector)[10]),
+          (nmbrString *)((*stateVector)[3]));
+    }
+    return (tmpFlag);
+  } else {
+    while (1) {
+      tmpFlag = unify(schemeA, schemeB, stateVector, 1);
+      if (!tmpFlag) return (0);
+      /* Check to see if the variables in schemeA changed */
+      schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
+      stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
+      stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
+      oldStackUnkVarStart = (nmbrString *)((*stateVector)[9]);
+      oldStackUnkVarLen = (nmbrString *)((*stateVector)[10]);
+      for (i = 0; i < schemeAUnkVarsLen; i++) {
+        if (stackUnkVarStart[i] != oldStackUnkVarStart[i]) {
+          /* The assignment changed */
+          /* Save the new assignment */
+          nmbrLet(&oldStackUnkVarStart, stackUnkVarStart);
+          nmbrLet(&oldStackUnkVarLen, stackUnkVarLen);
+          return (1);
+        }
+        if (stackUnkVarLen[i] != oldStackUnkVarLen[i]) {
+          /* The assignment changed */
+          /* Save the new assignment */
+          nmbrLet(&oldStackUnkVarStart, stackUnkVarStart);
+          nmbrLet(&oldStackUnkVarLen, stackUnkVarLen);
+          return (1);
+        }
+      }
+    } /* End while (1) */
+  }
+  return(0); /* Dummy return value - never happens */
+} /* oneDirUnif */
+
+
+
+/* uniqueUnif() is like unify(), but there is no reEntryFlag, and 3 possible
+   values are returned:
+     0: no unification was possible.
+     1: exactly one unification was possible, and stateVector is valid.
+     2: unification timed out
+     3: more than one unification was possible */
+char uniqueUnif(
+    const nmbrString *schemeA,
+    const nmbrString *schemeB,
+    pntrString **stateVector)
+{
+  pntrString_def(saveStateVector);
+  pntrString *pntrTmpPtr1; /* Pointer only; not allocated */
+  pntrString *pntrTmpPtr2; /* Pointer only; not allocated */
+  long i, j, k;
+  char tmpFlag;
+
+  tmpFlag = unifyH(schemeA, schemeB, stateVector, 0);
+  if (!tmpFlag) {
+    return (0); /* No unification possible */
+  }
+  if (tmpFlag == 2) {
+    return (2); /* Unification timed out */
+  }
+
+  /* Save the state vector */
+  pntrLet(&saveStateVector,*stateVector);
+  if (pntrLen(*stateVector) != 16) bug(1906);
+  for (i = 0; i < 4; i++) {
+    /* Force new space to be allocated for each pointer */
+    saveStateVector[i] = NULL_NMBRSTRING;
+    nmbrLet((nmbrString **)(&saveStateVector[i]),
+        (nmbrString *)((*stateVector)[i]));
+  }
+  for (i = 4; i <= 7; i++) {
+    /* Force new space to be allocated for each pointer */
+    saveStateVector[i] = NULL_PNTRSTRING;
+    pntrLet((pntrString **)(&saveStateVector[i]),
+        (pntrString *)((*stateVector)[i]));
+  }
+  for (i = 8; i < 12; i++) {
+    /* Force new space to be allocated for each pointer */
+    saveStateVector[i] = NULL_NMBRSTRING;
+    nmbrLet((nmbrString **)(&saveStateVector[i]),
+        (nmbrString *)((*stateVector)[i]));
+  }
+  for (i = 12; i < 16; i++) {
+    /* Force new space to be allocated for each pointer */
+    saveStateVector[i] = NULL_PNTRSTRING;
+    pntrLet((pntrString **)(&saveStateVector[i]),
+        (pntrString *)((*stateVector)[i]));
+  }
+  k = ((nmbrString *)((*stateVector)[11]))[0]; /* unkVarsLen */
+  for (i = 4; i <= 7; i++) {
+    pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
+    pntrTmpPtr2 = (pntrString *)((*stateVector)[i]);
+    for (j = 0; j < k; j++) {
+      /* Force new space to be allocated for each pointer */
+      pntrTmpPtr1[j] = NULL_NMBRSTRING;
+      nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
+          (nmbrString *)(pntrTmpPtr2[j]));
+    }
+  }
+  k = pntrLen((pntrString *)((*stateVector)[12]));
+  for (i = 12; i < 16; i++) {
+    pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
+    pntrTmpPtr2 = (pntrString *)((*stateVector)[i]);
+    for (j = 0; j < k; j++) {
+      /* Force new space to be allocated for each pointer */
+      pntrTmpPtr1[j] = NULL_NMBRSTRING;
+      nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
+          (nmbrString *)(pntrTmpPtr2[j]));
+    }
+  }
+
+  /* See if there is a second unification */
+  tmpFlag = unifyH(schemeA, schemeB, stateVector, 1);
+
+  if (!tmpFlag) {
+    /* There is no 2nd unification.  Unify cleared the original stateVector,
+       so return the saved version. */
+    *stateVector = saveStateVector;
+    return (1); /* Return flag that unification is unique. */
+  }
+
+  /* There are two or more unifications.  Deallocate the stateVector
+     we just saved before returning, since we will not use it. */
+  /* stackTop: Make sure it's not -1 before calling nmbrLet() */
+  ((nmbrString *)(saveStateVector[11]))[1] = 0;
+  for (i = 4; i <= 7; i++) {
+    pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
+    for (j = 0; j < ((nmbrString *)(saveStateVector[11]))[0]; j++) {
+                      /* ((nmbrString *)(saveStateVector[11]))[0] is unkVarsLen */
+      free_nmbrString(*(nmbrString **)(&pntrTmpPtr1[j]));
+    }
+  }
+  for (i = 0; i <= 3; i++) {
+    free_nmbrString(*(nmbrString **)(&saveStateVector[i]));
+  }
+  for (i = 4; i <= 7; i++) {
+    free_pntrString(*(pntrString **)(&saveStateVector[i]));
+  }
+  for (i = 8; i <= 11; i++) {
+    free_nmbrString(*(nmbrString **)(&saveStateVector[i]));
+  }
+  k = pntrLen((pntrString *)(saveStateVector[12]));
+  for (i = 12; i < 16; i++) {
+    pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
+    for (j = 0; j < k; j++) {
+      free_nmbrString(*(nmbrString **)(&pntrTmpPtr1[j]));
+    }
+    free_pntrString(*(pntrString **)(&saveStateVector[i]));
+  }
+  free_pntrString(saveStateVector);
+
+  if (tmpFlag == 2) {
+    return (2); /* Unification timed out */
+  }
+
+  return (3); /* Return flag that unification is not unique */
+
+} /* uniqueUnif */
+
+
+/* Deallocates the contents of a stateVector */
+/* Note:  If unifyH() returns 0, there were no more unifications and
+   the stateVector is left empty, so we don't have to call
+   purgeStateVector.  But no harm done if called anyway. */
+void purgeStateVector(pntrString **stateVector) {
+
+  long i, j, k;
+  pntrString *pntrTmpPtr1;
+
+  if (!pntrLen(*stateVector)) return; /* It's already been purged */
+
+  /* stackTop: Make sure it's not -1 before calling nmbrLet() */
+  ((nmbrString *)((*stateVector)[11]))[1] = 0;
+  for (i = 4; i <= 7; i++) {
+    pntrTmpPtr1 = (pntrString *)((*stateVector)[i]);
+    for (j = 0; j < ((nmbrString *)((*stateVector)[11]))[0]; j++) {
+                      /* ((nmbrString *)((*stateVector)[11]))[0] is unkVarsLen */
+      free_nmbrString(*(nmbrString **)(&pntrTmpPtr1[j]));
+    }
+  }
+  for (i = 0; i <= 3; i++) {
+    free_nmbrString(*(nmbrString **)(&(*stateVector)[i]));
+  }
+  for (i = 4; i <= 7; i++) {
+    free_pntrString(*(pntrString **)(&(*stateVector)[i]));
+  }
+  for (i = 8; i <= 11; i++) {
+    free_nmbrString(*(nmbrString **)(&(*stateVector)[i]));
+  }
+  k = pntrLen((pntrString *)((*stateVector)[12]));
+  for (i = 12; i < 16; i++) {
+    pntrTmpPtr1 = (pntrString *)((*stateVector)[i]);
+    for (j = 0; j < k; j++) {
+      free_nmbrString(*(nmbrString **)(&pntrTmpPtr1[j]));
+    }
+    free_pntrString(*(pntrString **)(&(*stateVector)[i]));
+  }
+  free_pntrString(*stateVector);
+
+  return;
+
+} /* purgeStateVector */
+
+
+/* Prints the substitutions determined by unify for debugging purposes */
+void printSubst(pntrString *stateVector) {
+  long d;
+  nmbrString *stackUnkVar; /* Pointer only - not allocated */
+  nmbrString *unifiedScheme; /* Pointer only - not allocated */
+  nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
+  nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
+  long stackTop;
+  vstring_def(tmpStr);
+  nmbrString_def(nmbrTmp);
+
+  stackTop = ((nmbrString *)(stateVector[11]))[1];
+  stackUnkVar = (nmbrString *)(stateVector[1]);
+  stackUnkVarStart = (nmbrString *)(stateVector[2]);
+  stackUnkVarLen = (nmbrString *)(stateVector[3]);
+  unifiedScheme = (nmbrString *)(stateVector[8]);
+
+  for (d = 0; d <= stackTop; d++) {
+    printLongLine(cat(" Variable '",
+        g_MathToken[stackUnkVar[d]].tokenName,"' was replaced with '",
+        nmbrCvtMToVString(
+            nmbrMid(unifiedScheme,stackUnkVarStart[d] + 1,
+            stackUnkVarLen[d])),"'.",NULL),"    "," ");
+    /* Clear temporary string allocation */
+    free_vstring(tmpStr);
+    free_nmbrString(nmbrTmp);
+  }
+} /* printSubst */
+
+
+/* unifyH() is like unify(), except that when reEntryFlag is 1, a new
+   unification is returned ONLY if the normalized unification does not
+   previously exist in the "Henty filter" part of the  stateVector.  This
+   reduces ambiguous unifications.  The values returned are the same as those
+   returned by unify().  (The redundancy of equivalent unifications was
+   a deficiency pointed out by Jeremy Henty.) */
+char unifyH(
+    const nmbrString *schemeA,
+    const nmbrString *schemeB,
+    pntrString **stateVector,
+    long reEntryFlag)
+{
+  char tmpFlag;
+  nmbrString_def(hentyVars);
+  nmbrString_def(hentyVarStart);
+  nmbrString_def(hentyVarLen);
+  nmbrString_def(hentySubstList);
+
+  /* Bypass this filter if SET HENTY_FILTER OFF is selected. */
+  if (!g_hentyFilter) return unify(schemeA, schemeB, stateVector, reEntryFlag);
+
+  if (!reEntryFlag) {
+
+    tmpFlag = unify(schemeA, schemeB, stateVector, 0);
+    if (tmpFlag == 1) { /* Unification OK */
+
+      /* Get the normalized equivalent substitutions */
+      hentyNormalize(&hentyVars, &hentyVarStart, &hentyVarLen,
+          &hentySubstList, stateVector);
+
+      /* This is the first unification so add it to the filter then return 1 */
+      hentyAdd(hentyVars, hentyVarStart, hentyVarLen,
+          hentySubstList, stateVector);
+
+    }
+    return (tmpFlag);
+
+  } else {
+
+    while (1) {
+      tmpFlag = unify(schemeA, schemeB, stateVector, 1);
+      if (tmpFlag == 1) { /* 0 = not possible, 1 == OK, 2 = timed out */
+
+        /* Get the normalized equivalent substitution */
+        hentyNormalize(&hentyVars, &hentyVarStart, &hentyVarLen,
+            &hentySubstList, stateVector);
+
+        /* Scan the Henty filter to see if this substitution is in it */
+        if (!hentyMatch(hentyVars, hentyVarStart, /*hentyVarLen,*/
+            hentySubstList, stateVector)) {
+
+          /* If it's not in there, this is a new unification so add it
+             to the filter then return 1 */
+          hentyAdd(hentyVars, hentyVarStart, hentyVarLen,
+              hentySubstList, stateVector);
+          return (1);
+        }
+
+      } else {
+        /* No unification is possible, or it timed out */
+        break;
+      }
+
+      /* If we get here this unification is in the Henty filter, so bypass it
+         and get the next unification. */
+
+    } /* End while (1) */
+
+    /* Deallocate memory (when reEntryFlag is 1 and (not possible or timeout)).
+       (In the other cases, hentyVars and hentySubsts pointers are assigned
+       directly to stateVector so they should not be deallocated) */
+    free_nmbrString(hentyVars);
+    free_nmbrString(hentyVarStart);
+    free_nmbrString(hentyVarLen);
+    free_nmbrString(hentySubstList);
+    return (tmpFlag);
+
+  }
+} /* unifyH */
+
+/* Extract and normalize the unification substitutions. */
+void hentyNormalize(nmbrString **hentyVars, nmbrString **hentyVarStart,
+    nmbrString **hentyVarLen, nmbrString **hentySubstList,
+    pntrString **stateVector)
+{
+  long vars, var1, var2, schLen;
+  long n, el, rra, rrb, rrc, ir, i, j; /* Variables for heap sort */
+  long totalSubstLen, pos;
+  nmbrString_def(substList);
+
+  /* Extract the substitutions. */
+  vars = ((nmbrString *)((*stateVector)[11]))[1] + 1; /* stackTop + 1 */
+  nmbrLet((nmbrString **)(&(*hentyVars)), nmbrLeft(
+      (nmbrString *)((*stateVector)[1]), vars)); /* stackUnkVar */
+  nmbrLet((nmbrString **)(&(*hentyVarStart)), nmbrLeft(
+      (nmbrString *)((*stateVector)[2]), vars)); /* stackUnkVarStart */
+  nmbrLet((nmbrString **)(&(*hentyVarLen)), nmbrLeft(
+      (nmbrString *)((*stateVector)[3]), vars)); /* stackUnkVarLen */
+  nmbrLet((nmbrString **)(&(*hentySubstList)),
+      (nmbrString *)((*stateVector)[8])); /* unifiedScheme */
+
+  /* First, if a variable is substituted with another variable,
+     reverse the substitution if the substituted variable has a larger
+     tokenNum. */
+  for (i = 0; i < vars; i++) {
+    if ((*hentyVarLen)[i] == 1) {
+      var2 = (*hentySubstList)[(*hentyVarStart)[i]];
+      if (var2 > g_mathTokens) {
+        /* It's a variable-for-variable substitution */
+        var1 = (*hentyVars)[i];
+        if (var1 > var2) {
+          /* Swap the variables */
+          (*hentyVars)[i] = var2;
+          schLen = nmbrLen(*hentySubstList);
+          for (j = 0; j < schLen; j++) {
+            if ((*hentySubstList)[(*hentyVarStart)[i]] == var2) {
+              (*hentySubstList)[(*hentyVarStart)[i]] = var1;
+            }
+          } /* Next j */
+        } /* End if (var1 > var2) */
+      } /* End if (var2 > g_mathTokens) */
+    } /* End if ((*hentyVarLen)[i] == 1) */
+  } /* Next i */
+
+  /* Next, sort the variables to be substituted in tokenNum order */
+  /* Heap sort from "Numerical Recipes" (Press et. al.) p. 231 */
+  /* Note:  The algorithm in the text has a bug; it does not work for n<2. */
+  n = vars;
+  if (n < 2) goto heapExit;
+  el = n / 2 + 1;
+  ir = n;
+ label10:
+  if (el > 1) {
+    el = el - 1;
+    rra = (*hentyVars)[el - 1];
+    rrb = (*hentyVarStart)[el - 1];
+    rrc = (*hentyVarLen)[el - 1];
+  } else {
+    rra = (*hentyVars)[ir - 1];
+    rrb = (*hentyVarStart)[ir - 1];
+    rrc = (*hentyVarLen)[ir - 1];
+    (*hentyVars)[ir - 1] = (*hentyVars)[0];
+    (*hentyVarStart)[ir - 1] = (*hentyVarStart)[0];
+    (*hentyVarLen)[ir - 1] = (*hentyVarLen)[0];
+    ir = ir - 1;
+    if (ir == 1) {
+      (*hentyVars)[0] = rra;
+      (*hentyVarStart)[0] = rrb;
+      (*hentyVarLen)[0] = rrc;
+      goto heapExit;
+    }
+  }
+  i = el;
+  j = el + el;
+ label20:
+  if (j <= ir) {
+    if (j < ir) {
+      if ((*hentyVars)[j - 1] < (*hentyVars)[j]) j = j + 1;
+    }
+    if (rra < (*hentyVars)[j - 1]) {
+      (*hentyVars)[i - 1] = (*hentyVars)[j - 1];
+      (*hentyVarStart)[i - 1] = (*hentyVarStart)[j - 1];
+      (*hentyVarLen)[i - 1] = (*hentyVarLen)[j - 1];
+      i = j;
+      j = j + j;
+    } else {
+      j = ir + 1;
+    }
+    goto label20;
+  }
+  (*hentyVars)[i - 1] = rra;
+  (*hentyVarStart)[i - 1] = rrb;
+  (*hentyVarLen)[i - 1] = rrc;
+  goto label10;
+
+ heapExit:
+
+  /* Finally, reconstruct the list of substitutions in
+     variable tokenNum order */
+  totalSubstLen = 0;
+  for (i = 0; i < vars; i++) {
+    totalSubstLen = totalSubstLen + (*hentyVarLen)[i];
+  }
+  /* For speedup, preallocate total string needed for the substitution list */
+  nmbrLet(&substList, nmbrSpace(totalSubstLen));
+
+  pos = 0; /* Position in list of substitutions */
+  for (i = 0; i < vars; i++) {
+    for (j = 0; j < (*hentyVarLen)[i]; j++) {
+      substList[pos + j] = (*hentySubstList)[(*hentyVarStart)[i] + j];
+    }
+    (*hentyVarStart)[i] = pos; /* Never used, but assign in case it ever does */
+    pos = pos + (*hentyVarLen)[i];
+  }
+  if (pos != totalSubstLen) bug(1907);
+  nmbrLet((nmbrString **)(&(*hentySubstList)), substList);
+
+  /* Deallocate memory */
+  free_nmbrString(substList);
+
+  return;
+
+} /* hentyNormalize */
+
+/* Check to see if an equivalent unification exists in the Henty filter */
+flag hentyMatch(nmbrString *hentyVars, nmbrString *hentyVarStart,
+    /*nmbrString *hentyVarLen,*/ nmbrString *hentySubstList,
+    pntrString **stateVector)
+{
+  long i, size;
+
+  size = pntrLen((pntrString *)((*stateVector)[12]));
+
+  for (i = 0; i < size; i++) {
+    if (nmbrEq(hentyVars,
+        (nmbrString *)(((pntrString *)((*stateVector)[12]))[i]))) {
+      if (nmbrEq(hentyVarStart,
+          (nmbrString *)(((pntrString *)((*stateVector)[13]))[i]))) {
+        /* (We don't need to look at [14] because it is determined by [13]) */
+        if (nmbrEq(hentySubstList,
+            (nmbrString *)(((pntrString *)((*stateVector)[15]))[i]))) {
+          return(1); /* A previous equivalent unification was found */
+        }
+      }
+    }
+  } /* Next i */
+
+  return (0); /* There was no previous equivalent unification */
+} /* hentyMatch */
+
+/* Add an entry to the Henty filter */
+void hentyAdd(nmbrString *hentyVars, nmbrString *hentyVarStart,
+    nmbrString *hentyVarLen, nmbrString *hentySubstList,
+    pntrString **stateVector)
+{
+  long size;
+  size = pntrLen((pntrString *)((*stateVector)[12]));
+
+  pntrLet((pntrString **)(&(*stateVector)[12]), pntrAddGElement(
+      (pntrString *)((*stateVector)[12])));
+  ((pntrString *)((*stateVector)[12]))[size] = hentyVars;
+  pntrLet((pntrString **)(&(*stateVector)[13]), pntrAddGElement(
+      (pntrString *)((*stateVector)[13])));
+  ((pntrString *)((*stateVector)[13]))[size] = hentyVarStart;
+  pntrLet((pntrString **)(&(*stateVector)[14]), pntrAddGElement(
+      (pntrString *)((*stateVector)[14])));
+  ((pntrString *)((*stateVector)[14]))[size] = hentyVarLen;
+  pntrLet((pntrString **)(&(*stateVector)[15]), pntrAddGElement(
+      (pntrString *)((*stateVector)[15])));
+  ((pntrString *)((*stateVector)[15]))[size] =
+      hentySubstList;
+} /* hentyAdd */
diff --git a/mmunif.h b/src/mmunif.h
similarity index 67%
rename from mmunif.h
rename to src/mmunif.h
index efd2e92..06faceb 100644
--- a/mmunif.h
+++ b/src/mmunif.h
@@ -1,104 +1,103 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMUNIF_H_
-#define METAMATH_MMUNIF_H_
-
-#include "mmdata.h"
-
-extern long g_minSubstLen; /* User-settable value - 0 or 1 */
-extern long g_userMaxUnifTrials;
-            /* User-defined upper limit (# backtracks) for unification trials */
-extern long g_unifTrialCount;
-                     /* 0 means don't time out; 1 means start counting trials */
-extern long g_unifTimeouts; /* Number of timeouts so far for this command */
-extern flag g_hentyFilter; /* Turns Henty filter on or off */
-
-/* 26-Sep-2010 nm */
-extern flag g_bracketMatchInit; /* So eraseSource() (mmcmds.c) can clr it */
-
-/* 1-Oct-2017 nm Made this global so eraseSource() (mmcmds.c) can clr it */
-extern nmbrString *g_firstConst;
-/* 2-Oct-2017 nm Made these global so eraseSource() (mmcmds.c) can clr them */
-extern nmbrString *g_lastConst;
-extern nmbrString *g_oneConst;
-
-
-nmbrString *makeSubstUnif(flag *newVarFlag,
-    nmbrString *trialScheme, pntrString *stateVector);
-
-
-char unify(
-    nmbrString *schemeA,
-    nmbrString *schemeB,
-    /* nmbrString **unifiedScheme, */ /* stateVector[8] holds this */
-    pntrString **stateVector,
-    long reEntryFlag);
-/* This function unifies two math token strings, schemeA and
-   schemeB.  The result is contained in unifiedScheme.
-   0 is returned if no assignment is possible.
-   If reEntryFlag is 1, the next possible set of assignments, if any,
-   is returned.  2 is returned if the unification times out.
-   (*stateVector) contains the state of the previous
-   call.  It is the caller's responsibility to deallocate the
-   contents of (*stateVector) when done, UNLESS a 0 is returned.
-   The caller must assign (*stateVector) to a legal pntrString
-   (e.g. NULL_PNTRSTRING) before calling.
-
-   All variables with a tokenNum > saveMathTokens are assumed
-   to be "unknown" variables that can be assigned; all other
-   variables are treated like constants in the unification
-   algorithm.
-
-   The "unknown" variable assignments are contained in (*stateVector)
-   (which is a complex structure, described below).  Some "unknown"
-   variables may have no assignment, in which case they will
-   remain "unknown", and others may have assignments which include
-   "unknown" variables.
-*/
-
-
-/* oneDirUnif() is like unify(), except that when reEntryFlag is 1,
-   a new unification is returned ONLY if the assignments to the
-   variables in schemeA have changed.  This is used to speed up the
-   program. */
-flag oneDirUnif(
-    nmbrString *schemeA,
-    nmbrString *schemeB,
-    pntrString **stateVector,
-    long reEntryFlag);
-
-
-/* uniqueUnif() is like unify(), but there is no reEntryFlag, and 3 possible
-   values are returned:
-     0: no unification was possible.
-     1: exactly one unification was possible, and stateVector is valid.
-     2: unification timed out.
-     3: more than one unification was possible. */
-char uniqueUnif(
-    nmbrString *schemeA,
-    nmbrString *schemeB,
-    pntrString **stateVector);
-
-/* unifyH() is like unify(), except that when reEntryFlag is 1,
-   a new unification is returned ONLY if the normalized unification
-   does not previously exist in the "Henty filter".  This reduces
-   ambiguous unifications.  The values returned are the same as
-   those returned by unify().  (The elimination of equivalent
-   unifications was suggested by Jeremy Henty.) */
-char unifyH(
-    nmbrString *schemeA,
-    nmbrString *schemeB,
-    pntrString **stateVector,
-    long reEntryFlag);
-
-/* Cleans out a stateVector if not empty */
-void purgeStateVector(pntrString **stateVector);
-
-/* Prints the substitutions determined by unify for debugging purposes */
-void printSubst(pntrString *stateVector);
-
-#endif /* METAMATH_MMUNIF_H_ */
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMUNIF_H_
+#define METAMATH_MMUNIF_H_
+
+/*! \file */
+
+#include "mmdata.h"
+
+extern long g_minSubstLen; /*!< User-settable value - 0 or 1 */
+extern long g_userMaxUnifTrials;
+            /*!< User-defined upper limit (# backtracks) for unification trials */
+extern long g_unifTrialCount;
+                     /*!< 0 means don't time out; 1 means start counting trials */
+extern long g_unifTimeouts; /*!< Number of timeouts so far for this command */
+extern flag g_hentyFilter; /*!< Turns Henty filter on or off */
+
+/* Global so eraseSource() (mmcmds.c) can clear them */
+extern flag g_bracketMatchInit;
+extern nmbrString *g_firstConst;
+extern nmbrString *g_lastConst;
+extern nmbrString *g_oneConst;
+
+
+nmbrString *makeSubstUnif(flag *newVarFlag,
+    const nmbrString *trialScheme, pntrString *stateVector);
+
+
+/*! This function unifies two math token strings, schemeA and
+   schemeB.  The result is contained in unifiedScheme.
+   0 is returned if no assignment is possible.
+   If reEntryFlag is 1, the next possible set of assignments, if any,
+   is returned.  2 is returned if the unification times out.
+   (*stateVector) contains the state of the previous
+   call.  It is the caller's responsibility to deallocate the
+   contents of (*stateVector) when done, UNLESS a 0 is returned.
+   The caller must assign (*stateVector) to a legal pntrString
+   (e.g. NULL_PNTRSTRING) before calling.
+
+   All variables with a tokenNum > saveMathTokens are assumed
+   to be "unknown" variables that can be assigned; all other
+   variables are treated like constants in the unification
+   algorithm.
+
+   The "unknown" variable assignments are contained in (*stateVector)
+   (which is a complex structure, described below).  Some "unknown"
+   variables may have no assignment, in which case they will
+   remain "unknown", and others may have assignments which include
+   "unknown" variables.
+*/
+char unify(
+    const nmbrString *schemeA,
+    const nmbrString *schemeB,
+    /* nmbrString **unifiedScheme, */ /* stateVector[8] holds this */
+    pntrString **stateVector,
+    long reEntryFlag);
+
+
+/*! oneDirUnif() is like unify(), except that when reEntryFlag is 1,
+   a new unification is returned ONLY if the assignments to the
+   variables in schemeA have changed.  This is used to speed up the
+   program. */
+flag oneDirUnif(
+    const nmbrString *schemeA,
+    const nmbrString *schemeB,
+    pntrString **stateVector,
+    long reEntryFlag);
+
+
+/*! uniqueUnif() is like unify(), but there is no reEntryFlag, and 3 possible
+   values are returned:
+     0: no unification was possible.
+     1: exactly one unification was possible, and stateVector is valid.
+     2: unification timed out.
+     3: more than one unification was possible. */
+char uniqueUnif(
+    const nmbrString *schemeA,
+    const nmbrString *schemeB,
+    pntrString **stateVector);
+
+/*! unifyH() is like unify(), except that when reEntryFlag is 1,
+   a new unification is returned ONLY if the normalized unification
+   does not previously exist in the "Henty filter".  This reduces
+   ambiguous unifications.  The values returned are the same as
+   those returned by unify().  (The elimination of equivalent
+   unifications was suggested by Jeremy Henty.) */
+char unifyH(
+    const nmbrString *schemeA,
+    const nmbrString *schemeB,
+    pntrString **stateVector,
+    long reEntryFlag);
+
+/*! Cleans out a stateVector if not empty */
+void purgeStateVector(pntrString **stateVector);
+
+/*! Prints the substitutions determined by unify for debugging purposes */
+void printSubst(pntrString *stateVector);
+
+#endif /* METAMATH_MMUNIF_H_ */
diff --git a/mmveri.c b/src/mmveri.c
similarity index 91%
rename from mmveri.c
rename to src/mmveri.c
index 57bf3c9..a2c8ec8 100644
--- a/mmveri.c
+++ b/src/mmveri.c
@@ -1,895 +1,888 @@
-/*****************************************************************************/
-/*        Copyright (C) 2017  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <setjmp.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mminou.h"
-#include "mmpars.h"
-#include "mmveri.h"
-
-/* Global structure used for getting information about a step */
-/* If getStep.stepNum is nonzero, we should get info about that step. */
-struct getStep_struct getStep = {0, 0, 0, 0, 0,
-    NULL_NMBRSTRING, NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_NMBRSTRING,
-    NULL_PNTRSTRING};
-
-/* Verify proof of one statement in source file.  Uses wrkProof structure.
-   Assumes that parseProof() has just been called for this statement. */
-/* Returns 0 if proof is OK; 1 if proof is incomplete (has '?' tokens);
-   returns 2 if error found; returns 3 if not severe error
-   found; returns 4 if not a $p statement */
-char verifyProof(long statemNum)
-{
-
-  long stmt; /* Contents of proof string location */
-  long i, j, step;
-  char type;
-  char *fbPtr;
-  long tokenLength;
-  long numReqHyp;
-  char returnFlag = 0;
-  nmbrString *nmbrTmpPtr;
-  nmbrString *nmbrHypPtr;
-  nmbrString *bigSubstSchemeHyp = NULL_NMBRSTRING;
-  nmbrString *bigSubstInstHyp = NULL_NMBRSTRING;
-  flag unkHypFlag;
-  nmbrString *nmbrTmp = NULL_NMBRSTRING; /* Used to force tmp stack dealloc */
-
-  if (g_Statement[statemNum].type != p_) return (4); /* Do nothing if not $p */
-
-  /* Initialize pointers to math strings in RPN stack and vs. statement. */
-  /* (Must be initialized, even if severe error, to prevent crashes later.) */
-  for (i = 0; i < g_WrkProof.numSteps; i++) {
-    g_WrkProof.mathStringPtrs[i] = NULL_NMBRSTRING;
-  }
-
-  if (g_WrkProof.errorSeverity > 2) return (g_WrkProof.errorSeverity);
-                                    /* Error too severe to check here */
-  g_WrkProof.RPNStackPtr = 0;
-
-  if (g_WrkProof.numSteps == 0) return (2);
-                        /* Empty proof caused by error found by in parseProof */
-
-  for (step = 0; step < g_WrkProof.numSteps; step++) {
-
-    stmt = g_WrkProof.proofString[step];
-
-    /* Handle unknown proof steps */
-    if (stmt == -(long)'?') {
-      if (returnFlag < 1) returnFlag = 1;
-                                      /* Flag that proof is partially unknown */
-      /* Treat "?" like a hypothesis - push stack and continue */
-      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-
-      g_WrkProof.RPNStackPtr++;
-      /* Leave the step's math string empty and continue */
-      continue;
-    }
-
-    /* See if the proof token is a local label ref. */
-    if (stmt < 0) {
-      /* It's a local label reference */
-      if (stmt > -1000) bug(2101);
-      i = -1000 - stmt; /* Get the step number it refers to */
-
-      /* Push the stack */
-      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-      g_WrkProof.RPNStackPtr++;
-
-      /* Assign a math string to the step (must not be deallocated by
-         cleanWrkProof()!) */
-      g_WrkProof.mathStringPtrs[step] =
-          g_WrkProof.mathStringPtrs[i];
-
-      continue;
-    }
-
-    type = g_Statement[stmt].type;
-
-    /* See if the proof token is a hypothesis */
-    if (type == e_ || type == f_) {
-      /* It's a hypothesis reference */
-
-      /* Push the stack */
-      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-      g_WrkProof.RPNStackPtr++;
-
-      /* Assign a math string to the step (must not be deallocated by
-         cleanWrkProof()!) */
-      g_WrkProof.mathStringPtrs[step] =
-          g_Statement[stmt].mathString;
-
-      continue;
-    }
-
-    /* The proof token must be an assertion */
-    if (type != a_ && type != p_) bug(2102);
-
-    /* It's an valid assertion. */
-    numReqHyp = g_Statement[stmt].numReqHyp;
-    nmbrHypPtr = g_Statement[stmt].reqHypList;
-
-    /* Assemble the hypotheses into two big math strings for unification */
-    /* Use a "dummy" token, the top of g_mathTokens array, to separate them. */
-    /* This is already done by the source parsing routines:
-    g_MathToken[g_mathTokens].tokenType = (char)con_;
-    g_MathToken[g_mathTokens].tokenName = "$|$"; */ /* Don't deallocate! */
-
-    nmbrLet(&bigSubstSchemeHyp, nmbrAddElement(NULL_NMBRSTRING, g_mathTokens));
-    nmbrLet(&bigSubstInstHyp, nmbrAddElement(NULL_NMBRSTRING, g_mathTokens));
-    unkHypFlag = 0; /* Flag that there are unknown hypotheses */
-    j = 0;
-    for (i = g_WrkProof.RPNStackPtr - numReqHyp; i < g_WrkProof.RPNStackPtr; i++) {
-      nmbrTmpPtr = g_WrkProof.mathStringPtrs[
-          g_WrkProof.RPNStack[i]];
-      if (nmbrTmpPtr[0] == -1) { /* If length is zero, hyp is unknown */
-        unkHypFlag = 1;
-        /* Assign scheme to empty nmbrString so it will always match instance */
-        nmbrLet(&bigSubstSchemeHyp,
-            nmbrCat(bigSubstSchemeHyp,
-            nmbrAddElement(nmbrTmpPtr, g_mathTokens), NULL));
-      } else {
-        nmbrLet(&bigSubstSchemeHyp,
-            nmbrCat(bigSubstSchemeHyp,
-            nmbrAddElement(g_Statement[nmbrHypPtr[j]].mathString,
-            g_mathTokens), NULL));
-      }
-      nmbrLet(&bigSubstInstHyp,
-          nmbrCat(bigSubstInstHyp,
-          nmbrAddElement(nmbrTmpPtr, g_mathTokens), NULL));
-      j++;
-
-      /* Get information about the step if requested */
-      if (getStep.stepNum) { /* If non-zero, step info is requested */
-        if (g_WrkProof.RPNStack[i] == getStep.stepNum - 1) {
-          /* Get parent of target if this is one of its hyp's */
-          getStep.targetParentStep = step + 1;
-          getStep.targetParentStmt = stmt;
-        }
-        if (step == getStep.stepNum - 1) {
-          /* Add to source hypothesis list */
-          nmbrLet(&getStep.sourceHyps, nmbrAddElement(getStep.sourceHyps,
-              g_WrkProof.RPNStack[i]));
-        }
-      } /* End of if (getStep.stepNum) */
-
-    }
-
-/*E*/if(db7)printLongLine(cat("step ", str((double)step+1), " sch ",
-/*E*/    nmbrCvtMToVString(bigSubstSchemeHyp), NULL), "", " ");
-/*E*/if(db7)printLongLine(cat("step ", str((double)step+1), " ins ",
-/*E*/    nmbrCvtMToVString(bigSubstInstHyp), NULL), "", " ");
-    /* Unify the hypotheses of the scheme with their instances and assign
-       the variables of the scheme.  If some of the hypotheses are unknown
-       (due to proof being debugged or previous error) we will try to unify
-       anyway; if the result is unique, we will use it. */
-    nmbrTmpPtr = assignVar(bigSubstSchemeHyp,
-        bigSubstInstHyp, stmt, statemNum, step, unkHypFlag);
-/*E*/if(db7)printLongLine(cat("step ", str((double)step+1), " res ",
-/*E*/    nmbrCvtMToVString(nmbrTmpPtr), NULL), "", " ");
-
-    /* Deallocate stack built up if there are many $d violations */
-    nmbrLet(&nmbrTmp, NULL_NMBRSTRING);
-
-    /* Assign the substituted assertion (must be deallocated by
-         cleanWrkProof()!) */
-    g_WrkProof.mathStringPtrs[step] = nmbrTmpPtr;
-    if (nmbrTmpPtr[0] == -1) {
-      if (!unkHypFlag) {
-        returnFlag = 2; /* An error occurred (assignVar printed it) */
-      }
-    }
-
-
-    /* Pop the stack */
-    g_WrkProof.RPNStackPtr = g_WrkProof.RPNStackPtr - numReqHyp;
-    g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
-    g_WrkProof.RPNStackPtr++;
-
-  } /* Next step */
-
-  /* If there was a stack error, the verifier should have never been
-     called. */
-  if (g_WrkProof.RPNStackPtr != 1) bug(2108);
-
-  /* See if the result matches the statement to be proved */
-  if (returnFlag == 0) {
-    if (!nmbrEq(g_Statement[statemNum].mathString,
-        g_WrkProof.mathStringPtrs[g_WrkProof.numSteps - 1])) {
-      if (!g_WrkProof.errorCount) {
-        fbPtr = g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps - 1];
-        tokenLength = g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps - 1];
-        /*??? Make sure suggested commands are correct. */
-        sourceError(fbPtr, tokenLength, statemNum, cat(
-            "The result of the proof (step ", str((double)(g_WrkProof.numSteps)),
-            ") does not match the statement being proved.  The result is \"",
-            nmbrCvtMToVString(
-            g_WrkProof.mathStringPtrs[g_WrkProof.numSteps - 1]),
-            "\" but the statement is \"",
-            nmbrCvtMToVString(g_Statement[statemNum].mathString),
-            "\".  Type \"SHOW PROOF ",g_Statement[statemNum].labelName,
-            "\" to see the proof attempt.",NULL));
-      }
-      g_WrkProof.errorCount++;
-    }
-  }
-
-  nmbrLet(&bigSubstSchemeHyp, NULL_NMBRSTRING);
-  nmbrLet(&bigSubstInstHyp, NULL_NMBRSTRING);
-
-  return (returnFlag);
-
-} /* verifyProof */
-
-
-
-
-/* assignVar() finds an assignment to substScheme variables that match
-   the assumptions specified in the reason string */
-nmbrString *assignVar(nmbrString *bigSubstSchemeAss,
-  nmbrString *bigSubstInstAss, long substScheme,
-  /* For error messages: */
-  long statementNum, long step, flag unkHypFlag)
-{
-  nmbrString *bigSubstSchemeVars = NULL_NMBRSTRING;
-  nmbrString *substSchemeFrstVarOcc = NULL_NMBRSTRING;
-  nmbrString *varAssLen = NULL_NMBRSTRING;
-  nmbrString *substInstFrstVarOcc = NULL_NMBRSTRING;
-  nmbrString *result = NULL_NMBRSTRING; /* value returned */
-  long bigSubstSchemeLen,bigSubstInstLen,bigSubstSchemeVarLen,substSchemeLen,
-      resultLen;
-  long i,v,v1,p,q,tokenNum;
-  flag breakFlag, contFlag;
-  vstring tmpStr = "";
-  vstring tmpStr2 = "";
-  flag ambiguityCheckFlag = 0;
-  nmbrString *saveResult = NULL_NMBRSTRING;
-
-  /* Variables for disjoint variable ($d) check */
-  nmbrString *nmbrTmpPtrAS;
-  nmbrString *nmbrTmpPtrBS;
-  nmbrString *nmbrTmpPtrAIR;
-  nmbrString *nmbrTmpPtrBIR;
-  nmbrString *nmbrTmpPtrAIO;
-  nmbrString *nmbrTmpPtrBIO;
-  long dLen, pos, substAPos, substALen, instAPos, substBPos, substBLen,
-      instBPos, a, b, aToken, bToken, aToken2, bToken2, dILenR, dILenO,
-      optStart, reqStart;
-  flag foundFlag;
-
-  /* Variables for getting step info */
-  long j,k,m;
-  long numReqHyp;
-  nmbrString *nmbrHypPtr;
-
-  nmbrString *nmbrTmp = NULL_NMBRSTRING; /* Used to force tmp stack dealloc */
-
-  long nmbrSaveTempAllocStack;
-
-  /* Initialization to avoid compiler warnings (should not be theoretically
-     necessary) */
-  dILenR = 0;
-  dILenO = 0;
-  optStart = 0;
-  reqStart = 0;
-  nmbrTmpPtrAIR = NULL_NMBRSTRING;
-  nmbrTmpPtrBIR = NULL_NMBRSTRING;
-  nmbrTmpPtrAIO = NULL_NMBRSTRING;
-  nmbrTmpPtrBIO = NULL_NMBRSTRING;
-
-  nmbrSaveTempAllocStack = g_nmbrStartTempAllocStack;
-  g_nmbrStartTempAllocStack = g_nmbrTempAllocStackTop; /* For nmbrLet() stack cleanup*/
-
-  bigSubstSchemeLen = nmbrLen(bigSubstSchemeAss);
-  bigSubstInstLen = nmbrLen(bigSubstInstAss);
-  nmbrLet(&bigSubstSchemeVars,nmbrExtractVars(bigSubstSchemeAss));
-  bigSubstSchemeVarLen = nmbrLen(bigSubstSchemeVars);
-
-  /* If there are no variables in the hypotheses, there won't be any in the
-     assertion (unless there was a previously detected error).  In this case,
-     the unification is just the assertion itself. */
-  if (!bigSubstSchemeVarLen) {
-    if (!nmbrLen(g_Statement[substScheme].reqVarList)) {
-      nmbrLet(&result,g_Statement[substScheme].mathString);
-    } else {
-      /* There must have been a previous error; let result remain empty */
-      if (!unkHypFlag) bug(2109);
-    }
-    goto returnPoint;
-  }
-
-  /* Allocate nmbrStrings used only to hold extra data for bigSubstSchemeAss;
-     don't use further nmbrString functions on them! */
-  /* substSchemeFrstVarOcc[] is the 1st occurrence of the variable
-         in bigSubstSchemeAss */
-  /* varAssLen[] is the length of the
-         assignment to the variable */
-  /* substInstFrstVarOcc[] is the 1st occurrence of the variable
-         in bigSubstInstAss */
-  nmbrLet(&substSchemeFrstVarOcc,nmbrSpace(bigSubstSchemeVarLen));
-  nmbrLet(&varAssLen,substSchemeFrstVarOcc);
-  nmbrLet(&substInstFrstVarOcc,substSchemeFrstVarOcc);
-
-  if (bigSubstSchemeVarLen != nmbrLen(g_Statement[substScheme].reqVarList)) {
-    if (unkHypFlag) {
-      /* If there are unknown hypotheses and all variables aren't present,
-         give up here */
-      goto returnPoint;
-    } else {
-      /* Actually, this could happen if there was a previous error,
-         which would have already been reported. */
-      if (!g_WrkProof.errorCount) bug(2103); /* There must have been an error */
-      goto returnPoint;
-    }
-  }
-
-  for (i = 0; i < bigSubstSchemeVarLen; i++) {
-    substSchemeFrstVarOcc[i] = -1; /* Initialize */
-    /* (varAssLen[], substInstFrstVarOcc[], are
-       all initialized to 0 by nmbrSpace().) */
-  }
-
-  /* Use the .tmp field of g_MathToken[]. to hold position of variable in
-     bigSubstSchemeVars for quicker lookup */
-  for (i = 0; i < bigSubstSchemeVarLen; i++) {
-    g_MathToken[bigSubstSchemeVars[i]].tmp = i;
-  }
-
-  /* Scan bigSubstSchemeAss to get substSchemeFrstVarOcc[] (1st var
-     occurrence) */
-  for (i = 0; i < bigSubstSchemeLen; i++) {
-    if (g_MathToken[bigSubstSchemeAss[i]].tokenType ==
-        (char)var_) {
-      if (substSchemeFrstVarOcc[g_MathToken[bigSubstSchemeAss[
-          i]].tmp] == -1) {
-        substSchemeFrstVarOcc[g_MathToken[bigSubstSchemeAss[
-            i]].tmp] = i;
-      }
-    }
-  }
-
-  /* Do the scan */
-  v = -1; /* Position in bigSubstSchemeVars */
-  p = 0; /* Position in bigSubstSchemeAss */
-  q = 0; /* Position in bigSubstInstAss */
- ambiguityCheck: /* Re-entry point to see if unification is unique */
-  while (p != bigSubstSchemeLen-1 || q != bigSubstInstLen-1) {
-/*E*/if(db7&&v>=0)printLongLine(cat("p ", str((double)p), " q ", str((double)q), " VAR ",str((double)v),
-/*E*/    " ASSIGNED ", nmbrCvtMToVString(
-/*E*/    nmbrMid(bigSubstInstAss,substInstFrstVarOcc[v]+1,
-/*E*/    varAssLen[v])), NULL), "", " ");
-/*E*/if(db7)nmbrLet(&bigSubstInstAss,bigSubstInstAss);
-/*E*/if(db7){print2("Enter scan: v=%ld,p=%ld,q=%ld\n",v,p,q); let(&tmpStr,"");}
-    tokenNum = bigSubstSchemeAss[p];
-    if (g_MathToken[tokenNum].tokenType == (char)con_) {
-      /* Constants must match in both substScheme and definiendum assumptions */
-      if (tokenNum == bigSubstInstAss[q]) {
-        p++;
-        q++;
-/*E*/if(db7)print2(" Exit, c ok: v=%ld,p=%ld,q=%ld\n",v,p,q);
-        continue;
-      } else {
-        /* Backtrack to last variable assigned and add 1 to its length */
-        breakFlag = 0;
-        contFlag = 1;
-        while (contFlag) {
-          if (v < 0) {
-            breakFlag = 1;
-            break; /* Error - possibilities exhausted */
-          }
-          varAssLen[v]++;
-          p = substSchemeFrstVarOcc[v] + 1;
-          q = substInstFrstVarOcc[v] + varAssLen[v];
-          contFlag = 0;
-          if (bigSubstInstAss[q-1] == g_mathTokens) {
-            /* It ran into the dummy token separating the assumptions.
-               A variable cannot be assigned this dummy token.  Therefore,
-               we must pop back a variable.  (This test speeds up
-               the program; theoretically, it is not needed.) */
-/*E*/if(db7){print2("GOT TO DUMMY TOKEN1\n");}
-            v--;
-            contFlag = 1;
-            continue;
-          }
-          if (q >= bigSubstInstLen) {
-            /* It overflowed the end of bigSubstInstAss; pop back a variable */
-            v--;
-            contFlag = 1;
-            bug(2104); /* Should be trapped above */
-          }
-        } /* end while */
-        if (breakFlag) {
-/*E*/if(db7)print2(" Exit, c bktrk bad: v=%ld,p=%ld,q=%ld\n",v,p,q);
-          break;
-        }
-/*E*/if(db7)print2(" Exit, c bktrk ok: v=%ld,p=%ld,q=%ld\n",v,p,q);
-      }
-    } else {
-      /* It's a variable.  If its the first occurrence, init length to 0 */
-      v1 = g_MathToken[tokenNum].tmp;
-      if (v1 > v) {
-        if (v1 != v + 1) bug(2105);
-        v = v1;
-        varAssLen[v] = 0; /* variable length */
-        substInstFrstVarOcc[v] = q;   /* variable start in bigSubstInstAss */
-        p++;
-/*E*/if(db7)print2(" Exit, v new: v=%ld,p=%ld,q=%ld\n",v,p,q);
-        continue;
-      } else { /* It's not the first occurrence; check that it matches */
-        breakFlag = 0;
-        for (i = 0; i < varAssLen[v1]; i++) {
-          if (q + i >= bigSubstInstLen) {
-            /* It overflowed the end of bigSubstInstAss */
-            breakFlag = 1;
-            break;
-          }
-          if (bigSubstInstAss[substInstFrstVarOcc[v1] + i] !=
-              bigSubstInstAss[q + i]) {
-            /* The variable assignment mismatched */
-            breakFlag = 1;
-            break;
-          }
-        }
-        if (breakFlag) {
-          /* Backtrack to last variable assigned and add 1 to its length */
-          breakFlag = 0;
-          contFlag = 1;
-          while (contFlag) {
-            if (v < 0) {
-              breakFlag = 1;
-              break; /* Error - possibilities exhausted */
-            }
-            varAssLen[v]++;
-            p = substSchemeFrstVarOcc[v] + 1;
-            q = substInstFrstVarOcc[v] + varAssLen[v];
-            contFlag = 0;
-            if (bigSubstInstAss[q-1] == g_mathTokens) {
-              /* It ran into the dummy token separating the assumptions.
-                 A variable cannot be assigned this dummy token.  Therefore,
-                 we must pop back a variable.  (This test speeds up
-                 the program; theoretically, it is not needed.) */
-/*E*/if(db7)print2("GOT TO DUMMY TOKEN\n");
-              v--;
-              contFlag = 1;
-              continue; /* 24-Sep-2010 nm Added missing trap to fix bug(2106) */
-            }
-            if (q >= bigSubstInstLen) {
-              /* It overflowed the end of bigSubstInstAss; pop back a variable */
-              v--;
-              contFlag = 1;
-              bug(2106); /* Should be trapped above */
-            }
-          }
-          if (breakFlag) {
-/*E*/if(db7){print2(" Exit, vold bck bad: v=%ld,p=%ld,q=%ld\n",v,p,q);}
-            break;
-          }
-/*E*/if(db7)print2(" Exit, vold bck ok: v=%ld,p=%ld,q=%ld\n",v,p,q);
-          continue;
-        } else {
-          p++;
-          q = q + varAssLen[v1];
-/*E*/if(db7)print2(" Exit, vold ok: v=%ld,p=%ld,q=%ld\n",v,p,q);
-          continue;
-        }
-      } /* end if first occurrence */
-    } /* end if constant */
-  } /* end while */
-
-/*E*/if(db7)printLongLine(cat("BIGVR ", nmbrCvtMToVString(bigSubstSchemeVars),
-/*E*/    NULL), "", " ");
-/*E*/if(db7)print2(
-/*E*/"p=%ld,bigSubstSchemeLen=%ld;q=%ld,bigSubstInstLen=%ld;v=%ld,bigSubstSchemeVarLen=%ld\n",
-/*E*/  p,bigSubstSchemeLen,q,bigSubstInstLen,v,bigSubstSchemeVarLen);
-  /* See if the assignment completed normally */
-  if (v == -1) {
-    if (ambiguityCheckFlag) {
-      /* This is what we wanted to see -- no further unification possible */
-      goto returnPoint;
-    }
-    if (!g_WrkProof.errorCount) {
-      let(&tmpStr, "");
-      j = g_Statement[substScheme].numReqHyp;
-      for (i = 0; i < j; i++) {
-        k = g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr - j + i]; /* Step */
-        let(&tmpStr2, nmbrCvtMToVString(g_WrkProof.mathStringPtrs[k]));
-        if (tmpStr2[0] == 0) let(&tmpStr2,
-            "? (Unknown step or previous error; unification ignored)");
-        let(&tmpStr, cat(tmpStr, "\n  Hypothesis ", str((double)i + 1), ":  ",
-            nmbrCvtMToVString(
-                g_Statement[g_Statement[substScheme].reqHypList[i]].mathString),
-            "\n  Step ", str((double)k + 1),
-            ":  ", tmpStr2, NULL));
-      } /* Next i */
-      /* tmpStr = shortDumpRPNStack(); */ /* Old version */
-      sourceError(g_WrkProof.stepSrcPtrPntr[step],
-          g_WrkProof.stepSrcPtrNmbr[step],
-          statementNum, cat(
-          "The hypotheses of statement \"", g_Statement[substScheme].labelName,
-          "\" at proof step ", str((double)step + 1),
-          " cannot be unified.", tmpStr, NULL));
-      /* sourceError(g_WrkProof.stepSrcPtrPntr[step],
-          g_WrkProof.stepSrcPtrNmbr[step],
-          statementNum, cat(
-          "The hypotheses of the statement at proof step ",
-          str(step + 1),
-          " cannot be unified.  The statement \"",
-          g_Statement[substScheme].labelName,
-          "\" requires ",
-          str(g_Statement[substScheme].numReqHyp),
-          " hypotheses.  The ",tmpStr,
-          ".  Type \"SHOW PROOF ",g_Statement[statementNum].labelName,
-          "\" to see the proof attempt.",NULL)); */ /* Old version */
-      let(&tmpStr, "");
-      let(&tmpStr2, "");
-    }
-    g_WrkProof.errorCount++;
-    goto returnPoint;
-  }
-  if (p != bigSubstSchemeLen - 1 || q != bigSubstInstLen - 1
-      || v != bigSubstSchemeVarLen - 1) bug(2107);
-
-  /* If a second unification was possible, save the first result for the
-     error message */
-  if (ambiguityCheckFlag) {
-    if (unkHypFlag) {
-      /* If a hypothesis was unknown, the fact that the unification is ambiguous
-         doesn't matter, so just return with an empty (unknown) answer. */
-      nmbrLet(&result,NULL_NMBRSTRING);
-      goto returnPoint;
-    }
-    nmbrLet(&saveResult, result);
-    nmbrLet(&result, NULL_NMBRSTRING);
-  }
-
-
-  /***** Get step information if requested *****/
-  if (!ambiguityCheckFlag) { /* This is the real (first) unification */
-    if (getStep.stepNum) {
-      /* See if this step is the requested step; if so get source substitutions */
-      if (step + 1 == getStep.stepNum) {
-        nmbrLet(&getStep.sourceSubstsNmbr, nmbrExtractVars(
-            g_Statement[substScheme].mathString));
-        k = nmbrLen(getStep.sourceSubstsNmbr);
-        pntrLet(&getStep.sourceSubstsPntr,
-            pntrNSpace(k));
-        for (m = 0; m < k; m++) {
-          pos = g_MathToken[getStep.sourceSubstsNmbr[m]].tmp; /* Subst pos */
-          nmbrLet((nmbrString **)(&getStep.sourceSubstsPntr[m]),
-              nmbrMid(bigSubstInstAss,
-              substInstFrstVarOcc[pos] + 1, /* Subst pos */
-              varAssLen[pos]) /* Subst length */ );
-        }
-      }
-      /* See if this step is a target hyp; if so get target substitutions */
-      j = 0;
-      numReqHyp = g_Statement[substScheme].numReqHyp;
-      nmbrHypPtr = g_Statement[substScheme].reqHypList;
-      for (i = g_WrkProof.RPNStackPtr - numReqHyp; i < g_WrkProof.RPNStackPtr; i++) {
-        if (g_WrkProof.RPNStack[i] == getStep.stepNum - 1) {
-          /* This is parent of target; get hyp's variable substitutions */
-          nmbrLet(&getStep.targetSubstsNmbr, nmbrExtractVars(
-              g_Statement[nmbrHypPtr[j]].mathString));
-          k = nmbrLen(getStep.targetSubstsNmbr);
-          pntrLet(&getStep.targetSubstsPntr, pntrNSpace(k));
-          for (m = 0; m < k; m++) {
-            pos = g_MathToken[getStep.targetSubstsNmbr[m]].tmp;
-                                                     /* Substitution position */
-            nmbrLet((nmbrString **)(&getStep.targetSubstsPntr[m]),
-                nmbrMid(bigSubstInstAss,
-                substInstFrstVarOcc[pos] + 1, /* Subst pos */
-                varAssLen[pos]) /* Subst length */ );
-          } /* Next m */
-        } /* End if (g_WrkProof.RPNStack[i] == getStep.stepNum - 1) */
-        j++;
-      } /* Next i */
-    } /* End if (getStep.stepNum) */
-  } /* End if (!ambiguityCheckFlag) */
-  /***** End of getting step information *****/
-
-
-  /***** Check for $d violations *****/
-  if (!ambiguityCheckFlag) { /* This is the real (first) unification */
-    nmbrTmpPtrAS = g_Statement[substScheme].reqDisjVarsA;
-    nmbrTmpPtrBS = g_Statement[substScheme].reqDisjVarsB;
-    dLen = nmbrLen(nmbrTmpPtrAS); /* Number of disjoint variable pairs */
-    if (dLen) { /* There is a disjoint variable requirement */
-      /* (Speedup) Save pointers and lengths for statement being proved */
-      nmbrTmpPtrAIR = g_Statement[statementNum].reqDisjVarsA;
-      nmbrTmpPtrBIR = g_Statement[statementNum].reqDisjVarsB;
-      dILenR = nmbrLen(nmbrTmpPtrAIR); /* Number of disj hypotheses */
-      nmbrTmpPtrAIO = g_Statement[statementNum].optDisjVarsA;
-      nmbrTmpPtrBIO = g_Statement[statementNum].optDisjVarsB;
-      dILenO = nmbrLen(nmbrTmpPtrAIO); /* Number of disj hypotheses */
-    }
-    for (pos = 0; pos < dLen; pos++) { /* Scan the disj var pairs */
-      substAPos = g_MathToken[nmbrTmpPtrAS[pos]].tmp;
-      substALen = varAssLen[substAPos];
-      instAPos = substInstFrstVarOcc[substAPos];
-      substBPos = g_MathToken[nmbrTmpPtrBS[pos]].tmp;
-      substBLen = varAssLen[substBPos];
-      instBPos = substInstFrstVarOcc[substBPos];
-      for (a = 0; a < substALen; a++) { /* Scan subst of 1st var in disj pair */
-        aToken = bigSubstInstAss[instAPos + a];
-        if (g_MathToken[aToken].tokenType == (char)con_) continue; /* Ignore */
-
-        /* Speed up:  find the 1st occurrence of aToken in the disjoint variable
-           list of the statement being proved. */
-        /* To bypass speedup, we would do this:
-              reqStart = 0;
-              optStart = 0; */
-        /* First, see if the variable is in the required list. */
-        foundFlag = 0;
-        for (i = 0; i < dILenR; i++) {
-          if (nmbrTmpPtrAIR[i] == aToken
-              || nmbrTmpPtrBIR[i] == aToken) {
-            foundFlag = 1;
-            reqStart = i;
-            break;
-          }
-        }
-        /* If not, see if it is in the optional list. */
-        if (!foundFlag) {
-          reqStart = dILenR; /* Force skipping required scan */
-          foundFlag = 0;
-          for (i = 0; i < dILenO; i++) {
-            if (nmbrTmpPtrAIO[i] == aToken
-                || nmbrTmpPtrBIO[i] == aToken) {
-              foundFlag = 1;
-              optStart = i;
-              break;
-            }
-          }
-          if (!foundFlag) optStart = dILenO; /* Force skipping optional scan */
-        } else {
-          optStart = 0;
-        } /* (End if (!foundFlag)) */
-        /* (End of speedup section) */
-
-        for (b = 0; b < substBLen; b++) { /* Scan subst of 2nd var in pair */
-          bToken = bigSubstInstAss[instBPos + b];
-          if (g_MathToken[bToken].tokenType == (char)con_) continue; /* Ignore */
-          if (aToken == bToken) {
-            if (!g_WrkProof.errorCount) { /* No previous errors in this proof */
-              sourceError(g_WrkProof.stepSrcPtrPntr[step], /* source ptr */
-                  g_WrkProof.stepSrcPtrNmbr[step], /* size of token */
-                  statementNum, cat(
-                  "There is a disjoint variable ($d) violation at proof step ",
-                  str((double)step + 1),".  Assertion \"",
-                  g_Statement[substScheme].labelName,
-                  "\" requires that variables \"",
-                  g_MathToken[nmbrTmpPtrAS[pos]].tokenName,
-                  "\" and \"",
-                  g_MathToken[nmbrTmpPtrBS[pos]].tokenName,
-                  "\" be disjoint.  But \"",
-                  g_MathToken[nmbrTmpPtrAS[pos]].tokenName,
-                  "\" was substituted with \"",
-                  nmbrCvtMToVString(nmbrMid(bigSubstInstAss,instAPos + 1,
-                      substALen)),
-                  "\" and \"",
-                  g_MathToken[nmbrTmpPtrBS[pos]].tokenName,
-                  "\" was substituted with \"",
-                  nmbrCvtMToVString(nmbrMid(bigSubstInstAss,instBPos + 1,
-                      substBLen)),
-                  "\".  These substitutions have variable \"",
-                  g_MathToken[aToken].tokenName,
-                  "\" in common.",
-                  NULL));
-              let(&tmpStr, ""); /* Force tmp string stack dealloc */
-              nmbrLet(&nmbrTmp,NULL_NMBRSTRING); /* Force tmp stack dealloc */
-            } /* (End if (!g_WrkProof.errorCount) ) */
-          } else { /* aToken != bToken */
-            /* The variables are different.  We're still not done though:  We
-               must make sure that the $d's of the statement being proved
-               guarantee that they will be disjoint. */
-            /*???Future:  use bsearch for speedup?  Must modify main READ
-               parsing to produce sorted disj var lists; this would slow down
-               the main READ. */
-            /* Make sure that the variables are in the right order for lookup.*/
-            if (aToken > bToken) {
-              aToken2 = bToken;
-              bToken2 = aToken;
-            } else {
-              aToken2 = aToken;
-              bToken2 = bToken;
-            }
-            /* Scan the required disjoint variable hypotheses to see if they're
-               in it. */
-            /* First, see if both variables are in the required list. */
-            foundFlag = 0;
-            for (i = reqStart; i < dILenR; i++) {
-              if (nmbrTmpPtrAIR[i] == aToken2) {
-                if (nmbrTmpPtrBIR[i] == bToken2) {
-                  foundFlag = 1;
-                  break;
-                }
-              }
-            }
-            /* If not, see if they are in the optional list. */
-            if (!foundFlag) {
-              foundFlag = 0;
-              for (i = optStart; i < dILenO; i++) {
-                if (nmbrTmpPtrAIO[i] == aToken2) {
-                  if (nmbrTmpPtrBIO[i] == bToken2) {
-                    foundFlag = 1;
-                    break;
-                  }
-                }
-              }
-            } /* (End if (!foundFlag)) */
-            /* If they were in neither place, we have a violation. */
-            if (!foundFlag) {
-              if (!g_WrkProof.errorCount) { /* No previous errors in this proof */
-                sourceError(g_WrkProof.stepSrcPtrPntr[step], /* source */
-                    g_WrkProof.stepSrcPtrNmbr[step], /* size of token */
-                    statementNum, cat(
-                   "There is a disjoint variable ($d) violation at proof step ",
-                    str((double)step + 1), ".  Assertion \"",
-                    g_Statement[substScheme].labelName,
-                    "\" requires that variables \"",
-                    g_MathToken[nmbrTmpPtrAS[pos]].tokenName,
-                    "\" and \"",
-                    g_MathToken[nmbrTmpPtrBS[pos]].tokenName,
-                    "\" be disjoint.  But \"",
-                    g_MathToken[nmbrTmpPtrAS[pos]].tokenName,
-                    "\" was substituted with \"",
-                    nmbrCvtMToVString(nmbrMid(bigSubstInstAss, instAPos + 1,
-                        substALen)),
-                    "\" and \"",
-                    g_MathToken[nmbrTmpPtrBS[pos]].tokenName,
-                    "\" was substituted with \"",
-                    nmbrCvtMToVString(nmbrMid(bigSubstInstAss, instBPos + 1,
-                        substBLen)),
-                    "\".", NULL));
-                /* Put missing $d requirement in new line so grep can find
-                   them easily in log file */
-                printLongLine(cat("Variables \"",
-                    /* 30-Apr-04 nm Put in alphabetic order for easier use if
-                       user sorts the list of errors */
-                    /* strcmp returns <0 if 1st<2nd */
-                    (strcmp(g_MathToken[aToken].tokenName,
-                        g_MathToken[bToken].tokenName) < 0)
-                      ? g_MathToken[aToken].tokenName
-                      : g_MathToken[bToken].tokenName,
-                    "\" and \"",
-                    (strcmp(g_MathToken[aToken].tokenName,
-                        g_MathToken[bToken].tokenName) < 0)
-                      ? g_MathToken[bToken].tokenName
-                      : g_MathToken[aToken].tokenName,
-                    "\" do not have a disjoint variable requirement in the ",
-                    "assertion being proved, \"",
-                    g_Statement[statementNum].labelName,
-                    "\".", NULL), "", " ");
-                let(&tmpStr, ""); /* Force tmp string stack dealloc */
-                nmbrLet(&nmbrTmp,NULL_NMBRSTRING); /* Force tmp stack dealloc */
-              } /* (End if (!g_WrkProof.errorCount) ) */
-            } /* (End if (!foundFlag)) */
-          } /* (End if (aToken == bToken)) */
-        } /* (Next b) */
-      } /* (Next a) */
-    } /* (Next pos) */
-  } /* (End if (!ambiguityCheck)) */
-  /***** (End of $d violation check) *****/
-
-  /* Assemble the final result */
-  substSchemeLen = nmbrLen(g_Statement[substScheme].mathString);
-  /* Calculate the length of the final result */
-  q = 0;
-  for (p = 0; p < substSchemeLen; p++) {
-    tokenNum = g_Statement[substScheme].mathString[p];
-    if (g_MathToken[tokenNum].tokenType == (char)con_) {
-      q++;
-    } else {
-      q = q + varAssLen[g_MathToken[tokenNum].tmp];
-    }
-  }
-  /* Allocate space for the final result */
-  resultLen = q;
-  nmbrLet(&result,nmbrSpace(resultLen));
-  /* Assign the final result */
-  q = 0;
-  for (p = 0; p < substSchemeLen; p++) {
-    tokenNum = g_Statement[substScheme].mathString[p];
-    if (g_MathToken[tokenNum].tokenType == (char)con_) {
-      result[q] = tokenNum;
-      q++;
-    } else {
-      for (i = 0; i < varAssLen[g_MathToken[tokenNum].tmp]; i++){
-        result[q + i] = bigSubstInstAss[i +
-            substInstFrstVarOcc[g_MathToken[tokenNum].tmp]];
-      }
-      q = q + i;
-    }
-  }
-/*E*/if(db7)printLongLine(cat("result ", nmbrCvtMToVString(result), NULL),""," ");
-
-  if (ambiguityCheckFlag) {
-    if (!g_WrkProof.errorCount) {
-      /*??? Make sure suggested commands are correct. */
-      sourceError(g_WrkProof.stepSrcPtrPntr[step],
-          g_WrkProof.stepSrcPtrNmbr[step],
-          statementNum, cat(
-          "The unification with the hypotheses of the statement at proof step ",
-          str((double)step + 1),
-          " is not unique.  Two possible results at this step are \"",
-          nmbrCvtMToVString(saveResult),
-          "\" and \"",nmbrCvtMToVString(result),
-          "\".  Type \"SHOW PROOF ",g_Statement[statementNum].labelName,
-          "\" to see the proof attempt.",NULL));
-    }
-    g_WrkProof.errorCount++;
-    goto returnPoint;
-  } else {
-
-    /* Prepare to see if the unification is unique */
-    while (1) {
-      v--;
-      if (v < 0) {
-        goto returnPoint; /* It's unique */
-      }
-      varAssLen[v]++;
-      p = substSchemeFrstVarOcc[v] + 1;
-      q = substInstFrstVarOcc[v] + varAssLen[v];
-      if (bigSubstInstAss[q - 1] != g_mathTokens) break;
-      if (q >= bigSubstInstLen) bug(2110);
-    }
-    ambiguityCheckFlag = 1;
-    goto ambiguityCheck;
-  }
-
-
- returnPoint:
-
-  /* Free up all allocated nmbrString space */
-  for (i = 0; i < bigSubstSchemeVarLen; i++) {
-    /* Make the data-holding structures legal nmbrStrings before nmbrLet() */
-    /*???Make more efficient by deallocating directly*/
-    substSchemeFrstVarOcc[i] = 0;
-    varAssLen[i] = 0;
-    substInstFrstVarOcc[i] = 0;
-  }
-  nmbrLet(&bigSubstSchemeVars,NULL_NMBRSTRING);
-  nmbrLet(&substSchemeFrstVarOcc,NULL_NMBRSTRING);
-  nmbrLet(&varAssLen,NULL_NMBRSTRING);
-  nmbrLet(&substInstFrstVarOcc,NULL_NMBRSTRING);
-  nmbrLet(&saveResult,NULL_NMBRSTRING);
-
-  g_nmbrStartTempAllocStack = nmbrSaveTempAllocStack;
-  return(result);
-
-}
-
-
-/* Deallocate the math symbol strings assigned in wrkProof structure during
-   proof verification.  This should be called after verifyProof() and after the
-   math symbol strings have been used for proof printouts, etc. */
-/* Note that this does NOT free the other allocations in g_WrkProof.  The
-   ERASE command will do this. */
-void cleanWrkProof(void) {
-
-  long step;
-  char type;
-
-  for (step = 0; step < g_WrkProof.numSteps; step++) {
-    if (g_WrkProof.proofString[step] > 0) {
-      type = g_Statement[g_WrkProof.proofString[step]].type;
-      if (type == a_ || type == p_) {
-        /* Allocation was only done if: (1) it's not a local label reference
-           and (2) it's not a hypothesis.  In this case, deallocate. */
-        nmbrLet((nmbrString **)(&g_WrkProof.mathStringPtrs[step]),
-            NULL_NMBRSTRING);
-      }
-    }
-  }
-
-}
+/*****************************************************************************/
+/*        Copyright (C) 2017  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#include <string.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mminou.h"
+#include "mmpars.h"
+#include "mmveri.h"
+
+/* Global structure used for getting information about a step */
+/* If getStep.stepNum is nonzero, we should get info about that step. */
+struct getStep_struct getStep = {0, 0, 0, 0, 0,
+    NULL_NMBRSTRING, NULL_NMBRSTRING, NULL_PNTRSTRING, NULL_NMBRSTRING,
+    NULL_PNTRSTRING};
+
+/* Verify proof of one statement in source file.  Uses wrkProof structure.
+   Assumes that parseProof() has just been called for this statement. */
+/* Returns 0 if proof is OK; 1 if proof is incomplete (has '?' tokens);
+   returns 2 if error found; returns 3 if not severe error
+   found; returns 4 if not a $p statement */
+char verifyProof(long statemNum)
+{
+
+  long stmt; /* Contents of proof string location */
+  long i, j, step;
+  char type;
+  char *fbPtr;
+  long tokenLength;
+  long numReqHyp;
+  char returnFlag = 0;
+  nmbrString *nmbrTmpPtr;
+  nmbrString *nmbrHypPtr;
+  nmbrString_def(bigSubstSchemeHyp);
+  nmbrString_def(bigSubstInstHyp);
+  flag unkHypFlag;
+  nmbrString_def(nmbrTmp); /* Used to force tmp stack dealloc */
+
+  if (g_Statement[statemNum].type != p_) return (4); /* Do nothing if not $p */
+
+  /* Initialize pointers to math strings in RPN stack and vs. statement. */
+  /* (Must be initialized, even if severe error, to prevent crashes later.) */
+  for (i = 0; i < g_WrkProof.numSteps; i++) {
+    g_WrkProof.mathStringPtrs[i] = NULL_NMBRSTRING;
+  }
+
+  if (g_WrkProof.errorSeverity > 2) return (g_WrkProof.errorSeverity);
+                                    /* Error too severe to check here */
+  g_WrkProof.RPNStackPtr = 0;
+
+  if (g_WrkProof.numSteps == 0) return (2);
+                        /* Empty proof caused by error found by in parseProof */
+
+  for (step = 0; step < g_WrkProof.numSteps; step++) {
+
+    stmt = g_WrkProof.proofString[step];
+
+    /* Handle unknown proof steps */
+    if (stmt == -(long)'?') {
+      if (returnFlag < 1) returnFlag = 1;
+                                      /* Flag that proof is partially unknown */
+      /* Treat "?" like a hypothesis - push stack and continue */
+      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+
+      g_WrkProof.RPNStackPtr++;
+      /* Leave the step's math string empty and continue */
+      continue;
+    }
+
+    /* See if the proof token is a local label ref. */
+    if (stmt < 0) {
+      /* It's a local label reference */
+      if (stmt > -1000) bug(2101);
+      i = -1000 - stmt; /* Get the step number it refers to */
+
+      /* Push the stack */
+      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+      g_WrkProof.RPNStackPtr++;
+
+      /* Assign a math string to the step (must not be deallocated by
+         cleanWrkProof()!) */
+      g_WrkProof.mathStringPtrs[step] =
+          g_WrkProof.mathStringPtrs[i];
+
+      continue;
+    }
+
+    type = g_Statement[stmt].type;
+
+    /* See if the proof token is a hypothesis */
+    if (type == e_ || type == f_) {
+      /* It's a hypothesis reference */
+
+      /* Push the stack */
+      g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+      g_WrkProof.RPNStackPtr++;
+
+      /* Assign a math string to the step (must not be deallocated by
+         cleanWrkProof()!) */
+      g_WrkProof.mathStringPtrs[step] =
+          g_Statement[stmt].mathString;
+
+      continue;
+    }
+
+    /* The proof token must be an assertion */
+    if (type != a_ && type != p_) bug(2102);
+
+    /* It's an valid assertion. */
+    numReqHyp = g_Statement[stmt].numReqHyp;
+    nmbrHypPtr = g_Statement[stmt].reqHypList;
+
+    /* Assemble the hypotheses into two big math strings for unification */
+    /* Use a "dummy" token, the top of g_mathTokens array, to separate them. */
+    /* This is already done by the source parsing routines:
+    g_MathToken[g_mathTokens].tokenType = (char)con_;
+    g_MathToken[g_mathTokens].tokenName = "$|$"; */ /* Don't deallocate! */
+
+    nmbrLet(&bigSubstSchemeHyp, nmbrAddElement(NULL_NMBRSTRING, g_mathTokens));
+    nmbrLet(&bigSubstInstHyp, nmbrAddElement(NULL_NMBRSTRING, g_mathTokens));
+    unkHypFlag = 0; /* Flag that there are unknown hypotheses */
+    j = 0;
+    for (i = g_WrkProof.RPNStackPtr - numReqHyp; i < g_WrkProof.RPNStackPtr; i++) {
+      nmbrTmpPtr = g_WrkProof.mathStringPtrs[
+          g_WrkProof.RPNStack[i]];
+      if (nmbrTmpPtr[0] == -1) { /* If length is zero, hyp is unknown */
+        unkHypFlag = 1;
+        /* Assign scheme to empty nmbrString so it will always match instance */
+        nmbrLet(&bigSubstSchemeHyp,
+            nmbrCat(bigSubstSchemeHyp,
+            nmbrAddElement(nmbrTmpPtr, g_mathTokens), NULL));
+      } else {
+        nmbrLet(&bigSubstSchemeHyp,
+            nmbrCat(bigSubstSchemeHyp,
+            nmbrAddElement(g_Statement[nmbrHypPtr[j]].mathString,
+            g_mathTokens), NULL));
+      }
+      nmbrLet(&bigSubstInstHyp,
+          nmbrCat(bigSubstInstHyp,
+          nmbrAddElement(nmbrTmpPtr, g_mathTokens), NULL));
+      j++;
+
+      /* Get information about the step if requested */
+      if (getStep.stepNum) { /* If non-zero, step info is requested */
+        if (g_WrkProof.RPNStack[i] == getStep.stepNum - 1) {
+          /* Get parent of target if this is one of its hyp's */
+          getStep.targetParentStep = step + 1;
+          getStep.targetParentStmt = stmt;
+        }
+        if (step == getStep.stepNum - 1) {
+          /* Add to source hypothesis list */
+          nmbrLet(&getStep.sourceHyps, nmbrAddElement(getStep.sourceHyps,
+              g_WrkProof.RPNStack[i]));
+        }
+      } /* End of if (getStep.stepNum) */
+
+    }
+
+/*E*/if(db7)printLongLine(cat("step ", str((double)step+1), " sch ",
+/*E*/    nmbrCvtMToVString(bigSubstSchemeHyp), NULL), "", " ");
+/*E*/if(db7)printLongLine(cat("step ", str((double)step+1), " ins ",
+/*E*/    nmbrCvtMToVString(bigSubstInstHyp), NULL), "", " ");
+    /* Unify the hypotheses of the scheme with their instances and assign
+       the variables of the scheme.  If some of the hypotheses are unknown
+       (due to proof being debugged or previous error) we will try to unify
+       anyway; if the result is unique, we will use it. */
+    nmbrTmpPtr = assignVar(bigSubstSchemeHyp,
+        bigSubstInstHyp, stmt, statemNum, step, unkHypFlag);
+/*E*/if(db7)printLongLine(cat("step ", str((double)step+1), " res ",
+/*E*/    nmbrCvtMToVString(nmbrTmpPtr), NULL), "", " ");
+
+    /* Deallocate stack built up if there are many $d violations */
+    free_nmbrString(nmbrTmp);
+
+    /* Assign the substituted assertion (must be deallocated by
+         cleanWrkProof()!) */
+    g_WrkProof.mathStringPtrs[step] = nmbrTmpPtr;
+    if (nmbrTmpPtr[0] == -1) {
+      if (!unkHypFlag) {
+        returnFlag = 2; /* An error occurred (assignVar printed it) */
+      }
+    }
+
+
+    /* Pop the stack */
+    g_WrkProof.RPNStackPtr = g_WrkProof.RPNStackPtr - numReqHyp;
+    g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr] = step;
+    g_WrkProof.RPNStackPtr++;
+
+  } /* Next step */
+
+  /* If there was a stack error, the verifier should have never been
+     called. */
+  if (g_WrkProof.RPNStackPtr != 1) bug(2108);
+
+  /* See if the result matches the statement to be proved */
+  if (returnFlag == 0) {
+    if (!nmbrEq(g_Statement[statemNum].mathString,
+        g_WrkProof.mathStringPtrs[g_WrkProof.numSteps - 1])) {
+      if (!g_WrkProof.errorCount) {
+        fbPtr = g_WrkProof.stepSrcPtrPntr[g_WrkProof.numSteps - 1];
+        tokenLength = g_WrkProof.stepSrcPtrNmbr[g_WrkProof.numSteps - 1];
+        /*??? Make sure suggested commands are correct. */
+        sourceError(fbPtr, tokenLength, statemNum, cat(
+            "The result of the proof (step ", str((double)(g_WrkProof.numSteps)),
+            ") does not match the statement being proved.  The result is \"",
+            nmbrCvtMToVString(
+            g_WrkProof.mathStringPtrs[g_WrkProof.numSteps - 1]),
+            "\" but the statement is \"",
+            nmbrCvtMToVString(g_Statement[statemNum].mathString),
+            "\".  Type \"SHOW PROOF ",g_Statement[statemNum].labelName,
+            "\" to see the proof attempt.",NULL));
+      }
+      g_WrkProof.errorCount++;
+    }
+  }
+
+  free_nmbrString(bigSubstSchemeHyp);
+  free_nmbrString(bigSubstInstHyp);
+
+  return (returnFlag);
+
+} /* verifyProof */
+
+
+
+
+/* assignVar() finds an assignment to substScheme variables that match
+   the assumptions specified in the reason string */
+nmbrString *assignVar(nmbrString *bigSubstSchemeAss,
+  nmbrString *bigSubstInstAss, long substScheme,
+  /* For error messages: */
+  long statementNum, long step, flag unkHypFlag)
+{
+  nmbrString_def(bigSubstSchemeVars);
+  nmbrString_def(substSchemeFrstVarOcc);
+  nmbrString_def(varAssLen);
+  nmbrString_def(substInstFrstVarOcc);
+  nmbrString_def(result); /* value returned */
+  long bigSubstSchemeLen,bigSubstInstLen,bigSubstSchemeVarLen,substSchemeLen,
+      resultLen;
+  long i,v,v1,p,q,tokenNum;
+  flag breakFlag, contFlag;
+  vstring_def(tmpStr);
+  vstring_def(tmpStr2);
+  flag ambiguityCheckFlag = 0;
+  nmbrString_def(saveResult);
+
+  /* Variables for disjoint variable ($d) check */
+  nmbrString *nmbrTmpPtrAS;
+  nmbrString *nmbrTmpPtrBS;
+  nmbrString *nmbrTmpPtrAIR;
+  nmbrString *nmbrTmpPtrBIR;
+  nmbrString *nmbrTmpPtrAIO;
+  nmbrString *nmbrTmpPtrBIO;
+  long dLen, pos, substAPos, substALen, instAPos, substBPos, substBLen,
+      instBPos, a, b, aToken, bToken, aToken2, bToken2, dILenR, dILenO,
+      optStart, reqStart;
+  flag foundFlag;
+
+  /* Variables for getting step info */
+  long j,k,m;
+  long numReqHyp;
+  nmbrString *nmbrHypPtr;
+
+  nmbrString_def(nmbrTmp); /* Used to force tmp stack dealloc */
+
+  long nmbrSaveTempAllocStack;
+
+  /* Initialization to avoid compiler warnings (should not be theoretically
+     necessary) */
+  dILenR = 0;
+  dILenO = 0;
+  optStart = 0;
+  reqStart = 0;
+  nmbrTmpPtrAIR = NULL_NMBRSTRING;
+  nmbrTmpPtrBIR = NULL_NMBRSTRING;
+  nmbrTmpPtrAIO = NULL_NMBRSTRING;
+  nmbrTmpPtrBIO = NULL_NMBRSTRING;
+
+  nmbrSaveTempAllocStack = g_nmbrStartTempAllocStack;
+  g_nmbrStartTempAllocStack = g_nmbrTempAllocStackTop; /* For nmbrLet() stack cleanup*/
+
+  bigSubstSchemeLen = nmbrLen(bigSubstSchemeAss);
+  bigSubstInstLen = nmbrLen(bigSubstInstAss);
+  nmbrLet(&bigSubstSchemeVars,nmbrExtractVars(bigSubstSchemeAss));
+  bigSubstSchemeVarLen = nmbrLen(bigSubstSchemeVars);
+
+  /* If there are no variables in the hypotheses, there won't be any in the
+     assertion (unless there was a previously detected error).  In this case,
+     the unification is just the assertion itself. */
+  if (!bigSubstSchemeVarLen) {
+    if (!nmbrLen(g_Statement[substScheme].reqVarList)) {
+      nmbrLet(&result,g_Statement[substScheme].mathString);
+    } else {
+      /* There must have been a previous error; let result remain empty */
+      if (!unkHypFlag) bug(2109);
+    }
+    goto returnPoint;
+  }
+
+  /* Allocate nmbrStrings used only to hold extra data for bigSubstSchemeAss;
+     don't use further nmbrString functions on them! */
+  /* substSchemeFrstVarOcc[] is the 1st occurrence of the variable
+         in bigSubstSchemeAss */
+  /* varAssLen[] is the length of the
+         assignment to the variable */
+  /* substInstFrstVarOcc[] is the 1st occurrence of the variable
+         in bigSubstInstAss */
+  nmbrLet(&substSchemeFrstVarOcc,nmbrSpace(bigSubstSchemeVarLen));
+  nmbrLet(&varAssLen,substSchemeFrstVarOcc);
+  nmbrLet(&substInstFrstVarOcc,substSchemeFrstVarOcc);
+
+  if (bigSubstSchemeVarLen != nmbrLen(g_Statement[substScheme].reqVarList)) {
+    if (unkHypFlag) {
+      /* If there are unknown hypotheses and all variables aren't present,
+         give up here */
+      goto returnPoint;
+    } else {
+      /* Actually, this could happen if there was a previous error,
+         which would have already been reported. */
+      if (!g_WrkProof.errorCount) bug(2103); /* There must have been an error */
+      goto returnPoint;
+    }
+  }
+
+  for (i = 0; i < bigSubstSchemeVarLen; i++) {
+    substSchemeFrstVarOcc[i] = -1; /* Initialize */
+    /* (varAssLen[], substInstFrstVarOcc[], are
+       all initialized to 0 by nmbrSpace().) */
+  }
+
+  /* Use the .tmp field of g_MathToken[]. to hold position of variable in
+     bigSubstSchemeVars for quicker lookup */
+  for (i = 0; i < bigSubstSchemeVarLen; i++) {
+    g_MathToken[bigSubstSchemeVars[i]].tmp = i;
+  }
+
+  /* Scan bigSubstSchemeAss to get substSchemeFrstVarOcc[] (1st var
+     occurrence) */
+  for (i = 0; i < bigSubstSchemeLen; i++) {
+    if (g_MathToken[bigSubstSchemeAss[i]].tokenType ==
+        (char)var_) {
+      if (substSchemeFrstVarOcc[g_MathToken[bigSubstSchemeAss[
+          i]].tmp] == -1) {
+        substSchemeFrstVarOcc[g_MathToken[bigSubstSchemeAss[
+            i]].tmp] = i;
+      }
+    }
+  }
+
+  /* Do the scan */
+  v = -1; /* Position in bigSubstSchemeVars */
+  p = 0; /* Position in bigSubstSchemeAss */
+  q = 0; /* Position in bigSubstInstAss */
+ ambiguityCheck: /* Re-entry point to see if unification is unique */
+  while (p != bigSubstSchemeLen-1 || q != bigSubstInstLen-1) {
+/*E*/if(db7&&v>=0)printLongLine(cat("p ", str((double)p), " q ", str((double)q), " VAR ",str((double)v),
+/*E*/    " ASSIGNED ", nmbrCvtMToVString(
+/*E*/    nmbrMid(bigSubstInstAss,substInstFrstVarOcc[v]+1,
+/*E*/    varAssLen[v])), NULL), "", " ");
+/*E*/if(db7)nmbrLet(&bigSubstInstAss,bigSubstInstAss);
+/*E*/if(db7){print2("Enter scan: v=%ld,p=%ld,q=%ld\n",v,p,q); free_vstring(tmpStr);}
+    tokenNum = bigSubstSchemeAss[p];
+    if (g_MathToken[tokenNum].tokenType == (char)con_) {
+      /* Constants must match in both substScheme and definiendum assumptions */
+      if (tokenNum == bigSubstInstAss[q]) {
+        p++;
+        q++;
+/*E*/if(db7)print2(" Exit, c ok: v=%ld,p=%ld,q=%ld\n",v,p,q);
+        continue;
+      } else {
+        /* Backtrack to last variable assigned and add 1 to its length */
+        breakFlag = 0;
+        contFlag = 1;
+        while (contFlag) {
+          if (v < 0) {
+            breakFlag = 1;
+            break; /* Error - possibilities exhausted */
+          }
+          varAssLen[v]++;
+          p = substSchemeFrstVarOcc[v] + 1;
+          q = substInstFrstVarOcc[v] + varAssLen[v];
+          contFlag = 0;
+          if (bigSubstInstAss[q-1] == g_mathTokens) {
+            /* It ran into the dummy token separating the assumptions.
+               A variable cannot be assigned this dummy token.  Therefore,
+               we must pop back a variable.  (This test speeds up
+               the program; theoretically, it is not needed.) */
+/*E*/if(db7){print2("GOT TO DUMMY TOKEN1\n");}
+            v--;
+            contFlag = 1;
+            continue;
+          }
+          if (q >= bigSubstInstLen) {
+            /* It overflowed the end of bigSubstInstAss; pop back a variable */
+            v--;
+            contFlag = 1;
+            bug(2104); /* Should be trapped above */
+          }
+        } /* end while */
+        if (breakFlag) {
+/*E*/if(db7)print2(" Exit, c backtrack bad: v=%ld,p=%ld,q=%ld\n",v,p,q);
+          break;
+        }
+/*E*/if(db7)print2(" Exit, c backtrack ok: v=%ld,p=%ld,q=%ld\n",v,p,q);
+      }
+    } else {
+      /* It's a variable.  If its the first occurrence, init length to 0 */
+      v1 = g_MathToken[tokenNum].tmp;
+      if (v1 > v) {
+        if (v1 != v + 1) bug(2105);
+        v = v1;
+        varAssLen[v] = 0; /* variable length */
+        substInstFrstVarOcc[v] = q;   /* variable start in bigSubstInstAss */
+        p++;
+/*E*/if(db7)print2(" Exit, v new: v=%ld,p=%ld,q=%ld\n",v,p,q);
+        continue;
+      } else { /* It's not the first occurrence; check that it matches */
+        breakFlag = 0;
+        for (i = 0; i < varAssLen[v1]; i++) {
+          if (q + i >= bigSubstInstLen) {
+            /* It overflowed the end of bigSubstInstAss */
+            breakFlag = 1;
+            break;
+          }
+          if (bigSubstInstAss[substInstFrstVarOcc[v1] + i] !=
+              bigSubstInstAss[q + i]) {
+            /* The variable assignment mismatched */
+            breakFlag = 1;
+            break;
+          }
+        }
+        if (breakFlag) {
+          /* Backtrack to last variable assigned and add 1 to its length */
+          breakFlag = 0;
+          contFlag = 1;
+          while (contFlag) {
+            if (v < 0) {
+              breakFlag = 1;
+              break; /* Error - possibilities exhausted */
+            }
+            varAssLen[v]++;
+            p = substSchemeFrstVarOcc[v] + 1;
+            q = substInstFrstVarOcc[v] + varAssLen[v];
+            contFlag = 0;
+            if (bigSubstInstAss[q-1] == g_mathTokens) {
+              /* It ran into the dummy token separating the assumptions.
+                 A variable cannot be assigned this dummy token.  Therefore,
+                 we must pop back a variable.  (This test speeds up
+                 the program; theoretically, it is not needed.) */
+/*E*/if(db7)print2("GOT TO DUMMY TOKEN\n");
+              v--;
+              contFlag = 1;
+              continue; /* Added missing trap to fix bug(2106) */
+            }
+            if (q >= bigSubstInstLen) {
+              /* It overflowed the end of bigSubstInstAss; pop back a variable */
+              v--;
+              contFlag = 1;
+              bug(2106); /* Should be trapped above */
+            }
+          }
+          if (breakFlag) {
+/*E*/if(db7){print2(" Exit, v_old back bad: v=%ld,p=%ld,q=%ld\n",v,p,q);}
+            break;
+          }
+/*E*/if(db7)print2(" Exit, v_old back ok: v=%ld,p=%ld,q=%ld\n",v,p,q);
+          continue;
+        } else {
+          p++;
+          q = q + varAssLen[v1];
+/*E*/if(db7)print2(" Exit, v_old ok: v=%ld,p=%ld,q=%ld\n",v,p,q);
+          continue;
+        }
+      } /* end if first occurrence */
+    } /* end if constant */
+  } /* end while */
+
+/*E*/if(db7)printLongLine(cat("BIGVR ", nmbrCvtMToVString(bigSubstSchemeVars),
+/*E*/    NULL), "", " ");
+/*E*/if(db7)print2(
+/*E*/"p=%ld,bigSubstSchemeLen=%ld;q=%ld,bigSubstInstLen=%ld;v=%ld,bigSubstSchemeVarLen=%ld\n",
+/*E*/  p,bigSubstSchemeLen,q,bigSubstInstLen,v,bigSubstSchemeVarLen);
+  /* See if the assignment completed normally */
+  if (v == -1) {
+    if (ambiguityCheckFlag) {
+      /* This is what we wanted to see -- no further unification possible */
+      goto returnPoint;
+    }
+    if (!g_WrkProof.errorCount) {
+      let(&tmpStr, "");
+      j = g_Statement[substScheme].numReqHyp;
+      for (i = 0; i < j; i++) {
+        k = g_WrkProof.RPNStack[g_WrkProof.RPNStackPtr - j + i]; /* Step */
+        let(&tmpStr2, nmbrCvtMToVString(g_WrkProof.mathStringPtrs[k]));
+        if (tmpStr2[0] == 0) let(&tmpStr2,
+            "? (Unknown step or previous error; unification ignored)");
+        let(&tmpStr, cat(tmpStr, "\n  Hypothesis ", str((double)i + 1), ":  ",
+            nmbrCvtMToVString(
+                g_Statement[g_Statement[substScheme].reqHypList[i]].mathString),
+            "\n  Step ", str((double)k + 1),
+            ":  ", tmpStr2, NULL));
+      } /* Next i */
+      /* tmpStr = shortDumpRPNStack(); */ /* Old version */
+      sourceError(g_WrkProof.stepSrcPtrPntr[step],
+          g_WrkProof.stepSrcPtrNmbr[step],
+          statementNum, cat(
+          "The hypotheses of statement \"", g_Statement[substScheme].labelName,
+          "\" at proof step ", str((double)step + 1),
+          " cannot be unified.", tmpStr, NULL));
+      /* sourceError(g_WrkProof.stepSrcPtrPntr[step],
+          g_WrkProof.stepSrcPtrNmbr[step],
+          statementNum, cat(
+          "The hypotheses of the statement at proof step ",
+          str(step + 1),
+          " cannot be unified.  The statement \"",
+          g_Statement[substScheme].labelName,
+          "\" requires ",
+          str(g_Statement[substScheme].numReqHyp),
+          " hypotheses.  The ",tmpStr,
+          ".  Type \"SHOW PROOF ",g_Statement[statementNum].labelName,
+          "\" to see the proof attempt.",NULL)); */ /* Old version */
+      free_vstring(tmpStr);
+      free_vstring(tmpStr2);
+    }
+    g_WrkProof.errorCount++;
+    goto returnPoint;
+  }
+  if (p != bigSubstSchemeLen - 1 || q != bigSubstInstLen - 1
+      || v != bigSubstSchemeVarLen - 1) bug(2107);
+
+  /* If a second unification was possible, save the first result for the
+     error message */
+  if (ambiguityCheckFlag) {
+    if (unkHypFlag) {
+      /* If a hypothesis was unknown, the fact that the unification is ambiguous
+         doesn't matter, so just return with an empty (unknown) answer. */
+      free_nmbrString(result);
+      goto returnPoint;
+    }
+    nmbrLet(&saveResult, result);
+    free_nmbrString(result);
+  }
+
+
+  /***** Get step information if requested *****/
+  if (!ambiguityCheckFlag) { /* This is the real (first) unification */
+    if (getStep.stepNum) {
+      /* See if this step is the requested step; if so get source substitutions */
+      if (step + 1 == getStep.stepNum) {
+        nmbrLet(&getStep.sourceSubstsNmbr, nmbrExtractVars(
+            g_Statement[substScheme].mathString));
+        k = nmbrLen(getStep.sourceSubstsNmbr);
+        pntrLet(&getStep.sourceSubstsPntr,
+            pntrNSpace(k));
+        for (m = 0; m < k; m++) {
+          pos = g_MathToken[getStep.sourceSubstsNmbr[m]].tmp; /* Subst pos */
+          nmbrLet((nmbrString **)(&getStep.sourceSubstsPntr[m]),
+              nmbrMid(bigSubstInstAss,
+              substInstFrstVarOcc[pos] + 1, /* Subst pos */
+              varAssLen[pos]) /* Subst length */ );
+        }
+      }
+      /* See if this step is a target hyp; if so get target substitutions */
+      j = 0;
+      numReqHyp = g_Statement[substScheme].numReqHyp;
+      nmbrHypPtr = g_Statement[substScheme].reqHypList;
+      for (i = g_WrkProof.RPNStackPtr - numReqHyp; i < g_WrkProof.RPNStackPtr; i++) {
+        if (g_WrkProof.RPNStack[i] == getStep.stepNum - 1) {
+          /* This is parent of target; get hyp's variable substitutions */
+          nmbrLet(&getStep.targetSubstsNmbr, nmbrExtractVars(
+              g_Statement[nmbrHypPtr[j]].mathString));
+          k = nmbrLen(getStep.targetSubstsNmbr);
+          pntrLet(&getStep.targetSubstsPntr, pntrNSpace(k));
+          for (m = 0; m < k; m++) {
+            pos = g_MathToken[getStep.targetSubstsNmbr[m]].tmp;
+                                                     /* Substitution position */
+            nmbrLet((nmbrString **)(&getStep.targetSubstsPntr[m]),
+                nmbrMid(bigSubstInstAss,
+                substInstFrstVarOcc[pos] + 1, /* Subst pos */
+                varAssLen[pos]) /* Subst length */ );
+          } /* Next m */
+        } /* End if (g_WrkProof.RPNStack[i] == getStep.stepNum - 1) */
+        j++;
+      } /* Next i */
+    } /* End if (getStep.stepNum) */
+  } /* End if (!ambiguityCheckFlag) */
+  /***** End of getting step information *****/
+
+
+  /***** Check for $d violations *****/
+  if (!ambiguityCheckFlag) { /* This is the real (first) unification */
+    nmbrTmpPtrAS = g_Statement[substScheme].reqDisjVarsA;
+    nmbrTmpPtrBS = g_Statement[substScheme].reqDisjVarsB;
+    dLen = nmbrLen(nmbrTmpPtrAS); /* Number of disjoint variable pairs */
+    if (dLen) { /* There is a disjoint variable requirement */
+      /* (Speedup) Save pointers and lengths for statement being proved */
+      nmbrTmpPtrAIR = g_Statement[statementNum].reqDisjVarsA;
+      nmbrTmpPtrBIR = g_Statement[statementNum].reqDisjVarsB;
+      dILenR = nmbrLen(nmbrTmpPtrAIR); /* Number of disj hypotheses */
+      nmbrTmpPtrAIO = g_Statement[statementNum].optDisjVarsA;
+      nmbrTmpPtrBIO = g_Statement[statementNum].optDisjVarsB;
+      dILenO = nmbrLen(nmbrTmpPtrAIO); /* Number of disj hypotheses */
+    }
+    for (pos = 0; pos < dLen; pos++) { /* Scan the disj var pairs */
+      substAPos = g_MathToken[nmbrTmpPtrAS[pos]].tmp;
+      substALen = varAssLen[substAPos];
+      instAPos = substInstFrstVarOcc[substAPos];
+      substBPos = g_MathToken[nmbrTmpPtrBS[pos]].tmp;
+      substBLen = varAssLen[substBPos];
+      instBPos = substInstFrstVarOcc[substBPos];
+      for (a = 0; a < substALen; a++) { /* Scan subst of 1st var in disj pair */
+        aToken = bigSubstInstAss[instAPos + a];
+        if (g_MathToken[aToken].tokenType == (char)con_) continue; /* Ignore */
+
+        /* Speed up:  find the 1st occurrence of aToken in the disjoint variable
+           list of the statement being proved. */
+        /* To bypass speedup, we would do this:
+              reqStart = 0;
+              optStart = 0; */
+        /* First, see if the variable is in the required list. */
+        foundFlag = 0;
+        for (i = 0; i < dILenR; i++) {
+          if (nmbrTmpPtrAIR[i] == aToken
+              || nmbrTmpPtrBIR[i] == aToken) {
+            foundFlag = 1;
+            reqStart = i;
+            break;
+          }
+        }
+        /* If not, see if it is in the optional list. */
+        if (!foundFlag) {
+          reqStart = dILenR; /* Force skipping required scan */
+          foundFlag = 0;
+          for (i = 0; i < dILenO; i++) {
+            if (nmbrTmpPtrAIO[i] == aToken
+                || nmbrTmpPtrBIO[i] == aToken) {
+              foundFlag = 1;
+              optStart = i;
+              break;
+            }
+          }
+          if (!foundFlag) optStart = dILenO; /* Force skipping optional scan */
+        } else {
+          optStart = 0;
+        } /* (End if (!foundFlag)) */
+        /* (End of speedup section) */
+
+        for (b = 0; b < substBLen; b++) { /* Scan subst of 2nd var in pair */
+          bToken = bigSubstInstAss[instBPos + b];
+          if (g_MathToken[bToken].tokenType == (char)con_) continue; /* Ignore */
+          if (aToken == bToken) {
+            if (!g_WrkProof.errorCount) { /* No previous errors in this proof */
+              sourceError(g_WrkProof.stepSrcPtrPntr[step], /* source ptr */
+                  g_WrkProof.stepSrcPtrNmbr[step], /* size of token */
+                  statementNum, cat(
+                  "There is a disjoint variable ($d) violation at proof step ",
+                  str((double)step + 1),".  Assertion \"",
+                  g_Statement[substScheme].labelName,
+                  "\" requires that variables \"",
+                  g_MathToken[nmbrTmpPtrAS[pos]].tokenName,
+                  "\" and \"",
+                  g_MathToken[nmbrTmpPtrBS[pos]].tokenName,
+                  "\" be disjoint.  But \"",
+                  g_MathToken[nmbrTmpPtrAS[pos]].tokenName,
+                  "\" was substituted with \"",
+                  nmbrCvtMToVString(nmbrMid(bigSubstInstAss,instAPos + 1,
+                      substALen)),
+                  "\" and \"",
+                  g_MathToken[nmbrTmpPtrBS[pos]].tokenName,
+                  "\" was substituted with \"",
+                  nmbrCvtMToVString(nmbrMid(bigSubstInstAss,instBPos + 1,
+                      substBLen)),
+                  "\".  These substitutions have variable \"",
+                  g_MathToken[aToken].tokenName,
+                  "\" in common.",
+                  NULL));
+              freeTempAlloc(); /* Force tmp string stack dealloc */
+              free_nmbrString(nmbrTmp); /* Force tmp stack dealloc */
+            } /* (End if (!g_WrkProof.errorCount) ) */
+          } else { /* aToken != bToken */
+            /* The variables are different.  We're still not done though:  We
+               must make sure that the $d's of the statement being proved
+               guarantee that they will be disjoint. */
+            /*???Future:  use bsearch for speedup?  Must modify main READ
+               parsing to produce sorted disj var lists; this would slow down
+               the main READ. */
+            /* Make sure that the variables are in the right order for lookup.*/
+            if (aToken > bToken) {
+              aToken2 = bToken;
+              bToken2 = aToken;
+            } else {
+              aToken2 = aToken;
+              bToken2 = bToken;
+            }
+            /* Scan the required disjoint variable hypotheses to see if they're
+               in it. */
+            /* First, see if both variables are in the required list. */
+            foundFlag = 0;
+            for (i = reqStart; i < dILenR; i++) {
+              if (nmbrTmpPtrAIR[i] == aToken2) {
+                if (nmbrTmpPtrBIR[i] == bToken2) {
+                  foundFlag = 1;
+                  break;
+                }
+              }
+            }
+            /* If not, see if they are in the optional list. */
+            if (!foundFlag) {
+              foundFlag = 0;
+              for (i = optStart; i < dILenO; i++) {
+                if (nmbrTmpPtrAIO[i] == aToken2) {
+                  if (nmbrTmpPtrBIO[i] == bToken2) {
+                    foundFlag = 1;
+                    break;
+                  }
+                }
+              }
+            } /* (End if (!foundFlag)) */
+            /* If they were in neither place, we have a violation. */
+            if (!foundFlag) {
+              if (!g_WrkProof.errorCount) { /* No previous errors in this proof */
+                sourceError(g_WrkProof.stepSrcPtrPntr[step], /* source */
+                    g_WrkProof.stepSrcPtrNmbr[step], /* size of token */
+                    statementNum, cat(
+                   "There is a disjoint variable ($d) violation at proof step ",
+                    str((double)step + 1), ".  Assertion \"",
+                    g_Statement[substScheme].labelName,
+                    "\" requires that variables \"",
+                    g_MathToken[nmbrTmpPtrAS[pos]].tokenName,
+                    "\" and \"",
+                    g_MathToken[nmbrTmpPtrBS[pos]].tokenName,
+                    "\" be disjoint.  But \"",
+                    g_MathToken[nmbrTmpPtrAS[pos]].tokenName,
+                    "\" was substituted with \"",
+                    nmbrCvtMToVString(nmbrMid(bigSubstInstAss, instAPos + 1,
+                        substALen)),
+                    "\" and \"",
+                    g_MathToken[nmbrTmpPtrBS[pos]].tokenName,
+                    "\" was substituted with \"",
+                    nmbrCvtMToVString(nmbrMid(bigSubstInstAss, instBPos + 1,
+                        substBLen)),
+                    "\".", NULL));
+                /* Put missing $d requirement in new line so grep can find
+                   them easily in log file */
+                printLongLine(cat("Variables \"",
+                    /* Put in alphabetic order for easier use if
+                       user sorts the list of errors */
+                    /* strcmp returns <0 if 1st<2nd */
+                    (strcmp(g_MathToken[aToken].tokenName,
+                        g_MathToken[bToken].tokenName) < 0)
+                      ? g_MathToken[aToken].tokenName
+                      : g_MathToken[bToken].tokenName,
+                    "\" and \"",
+                    (strcmp(g_MathToken[aToken].tokenName,
+                        g_MathToken[bToken].tokenName) < 0)
+                      ? g_MathToken[bToken].tokenName
+                      : g_MathToken[aToken].tokenName,
+                    "\" do not have a disjoint variable requirement in the ",
+                    "assertion being proved, \"",
+                    g_Statement[statementNum].labelName,
+                    "\".", NULL), "", " ");
+                freeTempAlloc(); /* Force tmp string stack dealloc */
+                free_nmbrString(nmbrTmp); /* Force tmp stack dealloc */
+              } /* (End if (!g_WrkProof.errorCount) ) */
+            } /* (End if (!foundFlag)) */
+          } /* (End if (aToken == bToken)) */
+        } /* (Next b) */
+      } /* (Next a) */
+    } /* (Next pos) */
+  } /* (End if (!ambiguityCheck)) */
+  /***** (End of $d violation check) *****/
+
+  /* Assemble the final result */
+  substSchemeLen = nmbrLen(g_Statement[substScheme].mathString);
+  /* Calculate the length of the final result */
+  q = 0;
+  for (p = 0; p < substSchemeLen; p++) {
+    tokenNum = g_Statement[substScheme].mathString[p];
+    if (g_MathToken[tokenNum].tokenType == (char)con_) {
+      q++;
+    } else {
+      q = q + varAssLen[g_MathToken[tokenNum].tmp];
+    }
+  }
+  /* Allocate space for the final result */
+  resultLen = q;
+  nmbrLet(&result,nmbrSpace(resultLen));
+  /* Assign the final result */
+  q = 0;
+  for (p = 0; p < substSchemeLen; p++) {
+    tokenNum = g_Statement[substScheme].mathString[p];
+    if (g_MathToken[tokenNum].tokenType == (char)con_) {
+      result[q] = tokenNum;
+      q++;
+    } else {
+      for (i = 0; i < varAssLen[g_MathToken[tokenNum].tmp]; i++){
+        result[q + i] = bigSubstInstAss[i +
+            substInstFrstVarOcc[g_MathToken[tokenNum].tmp]];
+      }
+      q = q + i;
+    }
+  }
+/*E*/if(db7)printLongLine(cat("result ", nmbrCvtMToVString(result), NULL),""," ");
+
+  if (ambiguityCheckFlag) {
+    if (!g_WrkProof.errorCount) {
+      /*??? Make sure suggested commands are correct. */
+      sourceError(g_WrkProof.stepSrcPtrPntr[step],
+          g_WrkProof.stepSrcPtrNmbr[step],
+          statementNum, cat(
+          "The unification with the hypotheses of the statement at proof step ",
+          str((double)step + 1),
+          " is not unique.  Two possible results at this step are \"",
+          nmbrCvtMToVString(saveResult),
+          "\" and \"",nmbrCvtMToVString(result),
+          "\".  Type \"SHOW PROOF ",g_Statement[statementNum].labelName,
+          "\" to see the proof attempt.",NULL));
+    }
+    g_WrkProof.errorCount++;
+    goto returnPoint;
+  } else {
+
+    /* Prepare to see if the unification is unique */
+    while (1) {
+      v--;
+      if (v < 0) {
+        goto returnPoint; /* It's unique */
+      }
+      varAssLen[v]++;
+      p = substSchemeFrstVarOcc[v] + 1;
+      q = substInstFrstVarOcc[v] + varAssLen[v];
+      if (bigSubstInstAss[q - 1] != g_mathTokens) break;
+      if (q >= bigSubstInstLen) bug(2110);
+    }
+    ambiguityCheckFlag = 1;
+    goto ambiguityCheck;
+  }
+
+
+ returnPoint:
+
+  /* Free up all allocated nmbrString space */
+  for (i = 0; i < bigSubstSchemeVarLen; i++) {
+    /* Make the data-holding structures legal nmbrStrings before nmbrLet() */
+    /*???Make more efficient by deallocating directly*/
+    substSchemeFrstVarOcc[i] = 0;
+    varAssLen[i] = 0;
+    substInstFrstVarOcc[i] = 0;
+  }
+  free_nmbrString(bigSubstSchemeVars);
+  free_nmbrString(substSchemeFrstVarOcc);
+  free_nmbrString(varAssLen);
+  free_nmbrString(substInstFrstVarOcc);
+  free_nmbrString(saveResult);
+
+  g_nmbrStartTempAllocStack = nmbrSaveTempAllocStack;
+  return result;
+
+}
+
+
+/* Deallocate the math symbol strings assigned in wrkProof structure during
+   proof verification.  This should be called after verifyProof() and after the
+   math symbol strings have been used for proof printouts, etc. */
+/* Note that this does NOT free the other allocations in g_WrkProof.  The
+   ERASE command will do this. */
+void cleanWrkProof(void) {
+
+  long step;
+  char type;
+
+  for (step = 0; step < g_WrkProof.numSteps; step++) {
+    if (g_WrkProof.proofString[step] > 0) {
+      type = g_Statement[g_WrkProof.proofString[step]].type;
+      if (type == a_ || type == p_) {
+        /* Allocation was only done if: (1) it's not a local label reference
+           and (2) it's not a hypothesis.  In this case, deallocate. */
+        free_nmbrString(*(nmbrString **)(&g_WrkProof.mathStringPtrs[step]));
+      }
+    }
+  }
+
+}
diff --git a/mmveri.h b/src/mmveri.h
similarity index 75%
rename from mmveri.h
rename to src/mmveri.h
index 23b4d53..7aebfc4 100644
--- a/mmveri.h
+++ b/src/mmveri.h
@@ -1,45 +1,48 @@
-/*****************************************************************************/
-/*        Copyright (C) 2005  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMVERI_H_
-#define METAMATH_MMVERI_H_
-
-#include "mmdata.h"
-
-char verifyProof(long statemNum);
-
-/* assignVar() finds an assignment to substScheme variables that match
-   the assumptions specified in the reason string */
-nmbrString *assignVar(nmbrString *bigSubstSchemeAss,
-  nmbrString *bigSubstInstAss, long substScheme,
-    /* For error messages: */
-  long statementNum, long step, flag unkHypFlag);
-
-/* Deallocate the math symbol strings assigned in g_WrkProof structure during
-   proof verification.  This should be called after verifyProof() and after the
-   math symbol strings have been used for proof printouts, etc. */
-/* Note that this does NOT free the other allocations in g_WrkProof.  The
-   ERASE command will do this. */
-void cleanWrkProof(void);
-
-/* Structure for getting info about a step for SHOW PROOF/STEP command */
-/* If getStep.stepNum is nonzero, we should get info about that step. */
-/* This structure should be deallocated after use. */
-struct getStep_struct {
-  long stepNum; /* Step # to get info about */
-  long sourceStmt; /* Right side of = in proof display */
-  long targetStmt; /* Left side of = in proof display */
-  long targetParentStep; /* Step # of target's parent */
-  long targetParentStmt; /* Statement # of target's parent */
-  nmbrString *sourceHyps; /* List of step #'s */
-  nmbrString *sourceSubstsNmbr; /* List of vars w/ ptr to subst math tokens */
-  pntrString *sourceSubstsPntr; /* List of vars w/ ptr to subst math tokens */
-  nmbrString *targetSubstsNmbr; /* List of vars w/ ptr to subst math tokens */
-  pntrString *targetSubstsPntr; /* List of vars w/ ptr to subst math tokens */
-};
-extern struct getStep_struct getStep;
-
-#endif /* METAMATH_MMVERI_H_ */
+/*****************************************************************************/
+/*        Copyright (C) 2005  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMVERI_H_
+#define METAMATH_MMVERI_H_
+
+/*! \file */
+
+#include "mmdata.h"
+
+char verifyProof(long statemNum);
+
+/*! assignVar() finds an assignment to substScheme variables that match
+   the assumptions specified in the reason string */
+nmbrString *assignVar(nmbrString *bigSubstSchemeAss,
+  nmbrString *bigSubstInstAss, long substScheme,
+    /* For error messages: */
+  long statementNum, long step, flag unkHypFlag);
+
+/*! Deallocate the math symbol strings assigned in g_WrkProof structure during
+   proof verification.  This should be called after verifyProof() and after the
+   math symbol strings have been used for proof printouts, etc.
+  \note this does NOT free the other allocations in g_WrkProof.  The
+   ERASE command will do this. */
+void cleanWrkProof(void);
+
+/*! \brief Structure for getting info about a step for SHOW PROOF/STEP command
+
+  If getStep.stepNum is nonzero, we should get info about that step.
+  \note This structure should be deallocated after use. */
+struct getStep_struct {
+  long stepNum; /* Step # to get info about */
+  long sourceStmt; /* Right side of = in proof display */
+  long targetStmt; /* Left side of = in proof display */
+  long targetParentStep; /* Step # of target's parent */
+  long targetParentStmt; /* Statement # of target's parent */
+  nmbrString *sourceHyps; /* List of step #'s */
+  nmbrString *sourceSubstsNmbr; /* List of vars w/ ptr to subst math tokens */
+  pntrString *sourceSubstsPntr; /* List of vars w/ ptr to subst math tokens */
+  nmbrString *targetSubstsNmbr; /* List of vars w/ ptr to subst math tokens */
+  pntrString *targetSubstsPntr; /* List of vars w/ ptr to subst math tokens */
+};
+extern struct getStep_struct getStep;
+
+#endif /* METAMATH_MMVERI_H_ */
diff --git a/mmvstr.c b/src/mmvstr.c
similarity index 76%
rename from mmvstr.c
rename to src/mmvstr.c
index 7d86f6b..a6c9f2b 100644
--- a/mmvstr.c
+++ b/src/mmvstr.c
@@ -1,997 +1,1018 @@
-/*****************************************************************************/
-/*        Copyright (C) 2019  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/*
-mmvstr.c - VMS-BASIC variable length string library routines header
-This is an emulation of the string functions available in VMS BASIC.
-*/
-
-/*** See the comments in mmvstr.h for an explanation of these functions ******/
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <time.h>
-#include <ctype.h>
-#include "mmvstr.h"
-/*E*/ /*Next line is need to declare "db" for debugging*/
-#include "mmdata.h"
-/* 1-Dec-05 nm
-   mmdata.h is also used to declare the bug() function that is called in
-   several places by mmvstr.c.  To make mmvstr.c and mmvstr.h completely
-   independent of the other programs, for use with another project, do the
-   following:
-     (1) Remove all lines beginning with the "/ *E* /" comment.
-     (2) Remove all calls to the bug() function (4 places).
-   To see an example of stand-alone usage of the mmvstr.c functions, see
-   the program lattice.c and several others included in
-     http://us.metamath.org/downloads/quantum-logic.tar.gz
-*/
-
-/*E*/long db1=0;
-#ifdef NDEBUG
-# define INCDB1(x)
-#else
-# define INCDB1(x) db1 += (x)
-#endif
-
-#define MAX_ALLOC_STACK 100
-long g_tempAllocStackTop = 0;      /* Top of stack for tempAlloc functon */
-long g_startTempAllocStack = 0;    /* Where to start freeing temporary allocation
-                                    when let() is called (normally 0, except in
-                                    special nested vstring functions) */
-void *tempAllocStack[MAX_ALLOC_STACK];
-
-static void freeTempAlloc(void)
-{
-  /* All memory previously allocated with tempAlloc is deallocated. */
-  /* EXCEPT:  When g_startTempAllocStack != 0, the freeing will start at
-     g_startTempAllocStack. */
-  long i;
-  for (i = g_startTempAllocStack; i < g_tempAllocStackTop; i++) {
-/*E*/INCDB1(-1 - (long)strlen(tempAllocStack[i]));
-/*E* /printf("%ld removing [%s]\n", db1, tempAllocStack[i]);*/
-    free(tempAllocStack[i]);
-  }
-  g_tempAllocStackTop = g_startTempAllocStack;
-} /* freeTempAlloc */
-
-
-static void pushTempAlloc(void *mem)
-{
-  if (g_tempAllocStackTop >= (MAX_ALLOC_STACK-1)) {
-    printf("*** FATAL ERROR ***  Temporary string stack overflow\n");
-#if __STDC__
-    fflush(stdout);
-#endif
-    bug(2201);
-  }
-  tempAllocStack[g_tempAllocStackTop++] = mem;
-} /* pushTempAlloc */
-
-
-static void* tempAlloc(long size)  /* String memory allocation/deallocation */
-{
-  void* memptr = malloc((size_t)size);
-  if (!memptr || size == 0) {
-    printf("*** FATAL ERROR ***  Temporary string allocation failed\n");
-#if __STDC__
-    fflush(stdout);
-#endif
-    bug(2202);
-  }
-  pushTempAlloc(memptr);
-/*E*/INCDB1(size);
-/*E* /printf("%ld adding\n",db1);*/
-  return memptr;
-} /* tempAlloc */
-
-
-/* Make string have temporary allocation to be released by next let() */
-/* Warning:  after makeTempAlloc() is called, the vstring may NOT be
-   assigned again with let() */
-void makeTempAlloc(vstring s)
-{
-  pushTempAlloc(s);
-/*E*/INCDB1((long)strlen(s) + 1);
-/*E*/db-=(long)strlen(s) + 1;
-/*E* /printf("%ld temping[%s]\n", db1, s);*/
-} /* makeTempAlloc */
-
-
-/* 8-Jul-2013 Wolf Lammen - rewritten to simplify it */
-void let(vstring *target, vstring source)        /* String assignment */
-/* This function must ALWAYS be called to make assignment to */
-/* a vstring in order for the memory cleanup routines, etc. */
-/* to work properly.  If a vstring has never been assigned before, */
-/* it is the user's responsibility to initialize it to "" (the */
-/* null string). */
-{
-
-  size_t sourceLength = strlen(source);  /* Save its length */
-  size_t targetLength = strlen(*target); /* Save its length */
-/*E*/if (targetLength) {
-/*E*/  db -= (long)targetLength+1;
-/*E*/  /* printf("%ld Deleting %s\n",db,*target); */
-/*E*/}
-/*E*/if (sourceLength) {
-/*E*/  db += (long)sourceLength+1;
-/*E*/  /* printf("%ld Adding %s\n",db,source); */
-/*E*/}
-  if (targetLength < sourceLength) { /* Old string has not enough room for new one */
-    /* Free old string space and allocate new space */
-    if (targetLength)
-      free(*target);  /* Free old space */
-    *target = malloc(sourceLength + 1); /* Allocate new space */
-    if (!*target) {
-      printf("*** FATAL ERROR ***  String memory couldn't be allocated\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-      bug(2204);
-    }
-  }
-  if (sourceLength) {
-    strcpy(*target, source);
-  } else {
-    /* Empty strings could still be temporaries, so always assign a constant */
-    if (targetLength) {
-      free(*target);
-    }
-    *target= "";
-  }
-
-  freeTempAlloc(); /* Free up temporary strings used in expression computation */
-
-} /* let */
-
-vstring cat(vstring string1,...)        /* String concatenation */
-#define MAX_CAT_ARGS 50
-{
-  va_list ap;   /* Declare list incrementer */
-  vstring arg[MAX_CAT_ARGS];    /* Array to store arguments */
-  size_t argPos[MAX_CAT_ARGS]; /* Array of argument positions in result */
-  vstring result;
-  int i;
-  int numArgs = 0;        /* Define "last argument" */
-
-  size_t pos = 0;
-  char* curArg = string1;
-
-  va_start(ap, string1); /* Begin the session */
-  do {
-        /* User-provided argument list must terminate with 0 */
-    if (numArgs >= MAX_CAT_ARGS) {
-      printf("*** FATAL ERROR ***  Too many cat() arguments\n");
-#if __STDC__
-      fflush(stdout);
-#endif
-      bug(2206);
-    }
-    arg[numArgs] = curArg;
-    argPos[numArgs] = pos;
-    pos += strlen(curArg);
-  } while (++numArgs, (curArg = va_arg(ap,char *)) != 0);
-  va_end(ap);           /* End var args session */
-
-  /* Allocate the memory for it */
-  result = tempAlloc((long)pos+1);
-  /* Move the strings into the newly allocated area */
-  for (i = 0; i < numArgs; ++i)
-    strcpy(result + argPos[i], arg[i]);
-  return result;
-} /* cat */
-
-
-/* 20-Oct-2013 Wolf Lammen - allow unlimited input line lengths */
-/* Input a line from the user or from a file */
-/* Returns 1 if a (possibly empty) line was successfully read, 0 if EOF */
-int linput(FILE *stream, const char* ask, vstring *target)
-{                           /* Note: "vstring *target" means "char **target" */
-  /*
-    BASIC:  linput "what"; a$
-    c:      linput(NULL, "what?", &a);
-
-    BASIC:  linput #1, a$                         (error trap on EOF)
-    c:      if (!linput(file1, NULL, &a)) break;  (break on EOF)
-
-  */
-  /* This function prints a prompt (if 'ask' is not NULL), gets a line from
-    the stream, and assigns it to target using the let(&...) function.
-    0 is returned when end-of-file is encountered.  The vstring
-    *target MUST be initialized to "" or previously assigned by let(&...)
-    before using it in linput. */
-  char f[10001]; /* Read in chunks up to 10000 characters */
-  int result = 0;
-  int eol_found = 0;
-  if (ask) {
-    printf("%s", ask);
-#if __STDC__
-    fflush(stdout);
-#endif
-  }
-  if (stream == NULL) stream = stdin;
-  while (!eol_found && fgets(f, sizeof(f), stream))
-  {
-    size_t endpos = strlen(f) - 1;
-    eol_found = (f[endpos] == '\n');
-    /* If the last line in the file has no newline, eol_found will be 0 here.
-       The fgets() above will return 0 and prevent another loop iteration. */
-    if (eol_found)
-      f[endpos] = 0; /* The return string will have any newline stripped. */
-    if (result)
-      /* Append additional parts of the line to *target */
-      /* The let() reallocates *target and copies the concatenation of the
-         old *target and the additional input f[] to it */
-      let(target /* = &(*target) */, cat(*target, f, NULL));
-    else
-      /* This is the first time through the loop, and normally
-         the only one unless the input line overflows f[] */
-      let(target, f);  /* Allocate *target and copy f to it */
-    result = 1;
-  }
-  return result;
-} /* linput */
-
-
-/* Find out the length of a string */
-long len(vstring s)
-{
-  return ((long)strlen(s));
-} /* len */
-
-
-/* Extract sin from character position start to stop into sout */
-vstring seg(vstring sin, long start, long stop)
-{
-  if (start < 1) start = 1;
-  return mid(sin, start, stop - start + 1);
-} /* seg */
-
-
-/* Extract sin from character position start for length len */
-vstring mid(vstring sin, long start, long length)
-{
-  vstring sout;
-  if (start < 1) start = 1;
-  if (length < 0) length = 0;
-  sout=tempAlloc(length + 1);
-  strncpy(sout,sin + start - 1, (size_t)length);
-/*E*/ /*??? Should db be subtracted from if length > end of string? */
-  sout[length] = 0;
-  return (sout);
-} /* mid */
-
-
-/* Extract leftmost n characters */
-vstring left(vstring sin,long n)
-{
-  return mid(sin, 1, n);
-} /* left */
-
-
-/* Extract after character n */
-vstring right(vstring sin, long n)
-{
-  return seg(sin, n, (long)(strlen(sin)));
-} /* right */
-
-
-/* Emulate VMS BASIC edit$ command */
-vstring edit(vstring sin,long control)
-#define isblank_(c) ((c == ' ') || (c == '\t'))
-    /* 11-Sep-2009 nm Added _ to fix '"isblank" redefined' compiler warning */
-#define isblankorlf_(c) ((c == ' ') || (c == '\t') || (c == '\n'))
-    /* 8-May-2015 nm added isblankorlf_ */
-{
-  /* EDIT$ (from VMS BASIC manual)
-       Syntax:  str-vbl = EDIT$(str-exp, int-exp)
-       Values   Effect
-       1        Trim parity bits
-       2        Discard all spaces and tabs
-       4        Discard characters: CR, LF, FF, ESC, RUBOUT, and NULL
-       8        Discard leading spaces and tabs
-       16       Reduce spaces and tabs to one space
-       32       Convert lowercase to uppercase
-       64       Convert [ to ( and ] to )
-       128      Discard trailing spaces and tabs
-       256      Do not alter characters inside quotes
-
-       (non-BASIC extensions)
-       512      Convert uppercase to lowercase
-       1024     Tab the line (convert spaces to equivalent tabs)
-       2048     Untab the line (convert tabs to equivalent spaces)
-       4096     Convert VT220 screen print frame graphics to -,|,+ characters
-
-       (Added 10/24/03:)
-       8192     Discard CR only (to assist DOS-to-Unix conversion)
-
-       (Added 8-May-2015 nm:)
-       16384    Discard trailing spaces, tabs, and LFs
-  */
-  vstring sout;
-  long i, j, k, m;
-  int last_char_is_blank;
-  int trim_flag, discardctrl_flag, bracket_flag, quote_flag, case_flag;
-  int alldiscard_flag, leaddiscard_flag, traildiscard_flag,
-      traildiscardLF_flag, reduce_flag;
-  int processing_inside_quote=0;
-  int lowercase_flag, tab_flag, untab_flag, screen_flag, discardcr_flag;
-  unsigned char graphicsChar;
-
-  /* Set up the flags */
-  trim_flag = control & 1;
-  alldiscard_flag = control & 2;
-  discardctrl_flag = control & 4;
-  leaddiscard_flag = control & 8;
-  reduce_flag = control & 16;
-  case_flag = control & 32;
-  bracket_flag = control & 64;
-  traildiscard_flag = control & 128;
-  traildiscardLF_flag = control & 16384;
-  quote_flag = control & 256;
-
-  /* Non-BASIC extensions */
-  lowercase_flag = control & 512;
-  tab_flag = control & 1024;
-  untab_flag = control & 2048;
-  screen_flag = control & 4096; /* Convert VT220 screen prints to |,-,+
-                                   format */
-  discardcr_flag = control & 8192; /* Discard CR's */
-
-  /* Copy string */
-  i = (long)strlen(sin) + 1;
-  if (untab_flag) i = i * 7; /* Allow for max possible length */
-  sout=tempAlloc(i);
-  strcpy(sout,sin);
-
-  /* Discard leading space/tab */
-  i=0;
-  if (leaddiscard_flag)
-    while ((sout[i] != 0) && isblank_(sout[i]))
-      sout[i++] = 0;
-
-  /* Main processing loop */
-  while (sout[i] != 0) {
-
-    /* Alter characters inside quotes ? */
-    if (quote_flag && ((sout[i] == '"') || (sout[i] == '\'')))
-       processing_inside_quote = ~ processing_inside_quote;
-    if (processing_inside_quote) {
-       /* Skip the rest of the code and continue to process next character */
-       i++; continue;
-    }
-
-    /* Discard all space/tab */
-    if ((alldiscard_flag) && isblank_(sout[i]))
-        sout[i] = 0;
-
-    /* Trim parity (eighth?) bit */
-    if (trim_flag)
-       sout[i] = sout[i] & 0x7F;
-
-    /* Discard CR,LF,FF,ESC,BS */
-    if ((discardctrl_flag) && (
-         (sout[i] == '\015') || /* CR  */
-         (sout[i] == '\012') || /* LF  */
-         (sout[i] == '\014') || /* FF  */
-         (sout[i] == '\033') || /* ESC */
-         /*(sout[i] == '\032') ||*/ /* ^Z */ /* DIFFERENCE won't work w/ this */
-         (sout[i] == '\010')))  /* BS  */
-      sout[i] = 0;
-
-    /* Discard CR */
-    if ((discardcr_flag) && (
-         (sout[i] == '\015')))  /* CR  */
-      sout[i] = 0;
-
-    /* Convert lowercase to uppercase */
-    /*
-    if ((case_flag) && (islower(sout[i])))
-       sout[i] = toupper(sout[i]);
-    */
-    /* 13-Jun-2009 nm The upper/lower case C functions have odd behavior
-       with characters > 127, at least in lcc.  So this was rewritten to
-       not use them. */
-    if ((case_flag) && (sout[i] >= 'a' && sout[i] <= 'z'))
-       sout[i] = (char)(sout[i] - ('a' - 'A'));
-
-    /* Convert [] to () */
-    if ((bracket_flag) && (sout[i] == '['))
-       sout[i] = '(';
-    if ((bracket_flag) && (sout[i] == ']'))
-       sout[i] = ')';
-
-    /* Convert uppercase to lowercase */
-    /*
-    if ((lowercase_flag) && (isupper(sout[i])))
-       sout[i] = tolower(sout[i]);
-    */
-    /* 13-Jun-2009 nm The upper/lower case C functions have odd behavior
-       with characters > 127, at least in lcc.  So this was rewritten to
-       not use them. */
-    if ((lowercase_flag) && (sout[i] >= 'A' && sout[i] <= 'Z'))
-       sout[i] = (char)(sout[i] + ('a' - 'A'));
-
-    /* Convert VT220 screen print frame graphics to +,|,- */
-    if (screen_flag) {
-      graphicsChar = (unsigned char)sout[i]; /* Need unsigned char for >127 */
-      /* vt220 */
-      if (graphicsChar >= 234 && graphicsChar <= 237) sout[i] = '+';
-      if (graphicsChar == 241) sout[i] = '-';
-      if (graphicsChar == 248) sout[i] = '|';
-      if (graphicsChar == 166) sout[i] = '|';
-      /* vt100 */
-      if (graphicsChar == 218 /*up left*/ || graphicsChar == 217 /*lo r*/
-          || graphicsChar == 191 /*up r*/ || graphicsChar == 192 /*lo l*/)
-        sout[i] = '+';
-      if (graphicsChar == 196) sout[i] = '-';
-      if (graphicsChar == 179) sout[i] = '|';
-    }
-
-    /* Process next character */
-    i++;
-  }
-  /* sout[i]=0 is the last character at this point */
-
-  /* Clean up the deleted characters */
-  for (j = 0, k = 0; j <= i; j++)
-    if (sout[j]!=0) sout[k++]=sout[j];
-  sout[k] = 0;
-  /* sout[k] = 0 is the last character at this point */
-
-  /* Discard trailing space/tab */
-  if (traildiscard_flag) {
-    --k;
-    while ((k >= 0) && isblank_(sout[k])) --k;
-    sout[++k] = 0;
-  }
-
-  /* 8-May-2015 nm */
-  /* Discard trailing space/tab and LF */
-  if (traildiscardLF_flag) {
-    --k;
-    while ((k >= 0) && isblankorlf_(sout[k])) --k;
-    sout[++k] = 0;
-  }
-
-  /* Reduce multiple space/tab to a single space */
-  if (reduce_flag) {
-    i = j = last_char_is_blank = 0;
-    while (i <= k - 1) {
-      if (!isblank_(sout[i])) {
-        sout[j++] = sout[i++];
-        last_char_is_blank = 0;
-      } else {
-        if (!last_char_is_blank)
-          sout[j++]=' '; /* Insert a space at the first occurrence of a blank */
-        last_char_is_blank = 1; /* Register that a blank is found */
-        i++; /* Process next character */
-      }
-    }
-    sout[j] = 0;
-  }
-
-  /* Untab the line */
-  if (untab_flag || tab_flag) {
-
-    /*
-    DEF FNUNTAB$(L$)      ! UNTAB LINE L$
-    I9%=1%
-    I9%=INSTR(I9%,L$,CHR$(9%))
-    WHILE I9%
-      L$=LEFT(L$,I9%-1%)+SPACE$(8%-((I9%-1%) AND 7%))+RIGHT(L$,I9%+1%)
-      I9%=INSTR(I9%,L$,CHR$(9%))
-    NEXT
-    FNUNTAB$=L$
-    FNEND
-    */
-
-    /***** old code (doesn't handle multiple lines)
-    k = (long)strlen(sout);
-    for (i = 1; i <= k; i++) {
-      if (sout[i - 1] != '\t') continue;
-      for (j = k; j >= i; j--) {
-        sout[j + 8 - ((i - 1) & 7) - 1] = sout[j];
-      }
-      for (j = i; j < i + 8 - ((i - 1) & 7); j++) {
-        sout[j - 1] = ' ';
-      }
-      k = k + 8 - ((i - 1) & 7);
-    }
-    *****/
-
-    /* Untab string containing multiple lines */ /* 9-Jul-2011 nm */
-    /* (Currently this is needed by outputStatement() in mmpars.c) */
-    k = (long)strlen(sout);
-    m = 0;  /* Position on line relative to last '\n' */
-    for (i = 1; i <= k; i++) {
-      if (sout[i - 1] == '\n') {
-        m = 0;
-        continue;
-      }
-      m++; /* Should equal i for one-line string */
-      if (sout[i - 1] != '\t') continue;
-      for (j = k; j >= i; j--) {
-        sout[j + 8 - ((m - 1) & 7) - 1] = sout[j];
-      }
-      for (j = i; j < i + 8 - ((m - 1) & 7); j++) {
-        sout[j - 1] = ' ';
-      }
-      k = k + 8 - ((m - 1) & 7);
-    }
-  }
-
-  /* Tab the line */
-  /* (Note that this does not [yet?] handle string with multiple lines) */
-  if (tab_flag) {
-
-    /*
-    DEF FNTAB$(L$)        ! TAB LINE L$
-    I9%=0%
-    FOR I9%=8% STEP 8% WHILE I9%<LEN(L$)
-      J9%=I9%
-      J9%=J9%-1% UNTIL ASCII(MID(L$,J9%,1%))<>32% OR J9%=I9%-8%
-      IF J9%<=I9%-2% THEN
-        L$=LEFT(L$,J9%)+CHR$(9%)+RIGHT(L$,I9%+1%)
-        I9%=J9%+1%
-      END IF
-    NEXT I9%
-    FNTAB$=L$
-    FNEND
-    */
-
-    k = (long)strlen(sout);
-    for (i = 8; i < k; i = i + 8) {
-      j = i;
-
-      /* 25-May-2016 nm */
-      /* gcc m*.c -o metamath.exe -O2 -Wall was giving:
-             mmvstr.c:285:9: warning: assuming signed overflow does not occur
-             when assuming that (X - c) <= X is always true [-Wstrict-overflow]
-         Here we trick gcc into turning off this optimization by moving
-         the computation of i - 2 here, then referencing m instead of i - 2
-         below.  Note that if "m = i - 2" is moved _after_ the "while", the
-         error message returns. */
-      m = i - 2;
-
-      while (sout[j - 1] == ' ' && j > i - 8) j--;
-      /*if (j <= i - 2) {*/
-      if (j <= m) {  /* 25-May-2016 nm */
-        sout[j] = '\t';
-        j = i;
-        while (sout[j - 1] == ' ' && j > i - 8 + 1) {
-          sout[j - 1] = 0;
-          j--;
-        }
-      }
-    }
-    i = k;
-    /* sout[i]=0 is the last character at this point */
-    /* Clean up the deleted characters */
-    for (j = 0, k = 0; j <= i; j++)
-      if (sout[j] != 0) sout[k++] = sout[j];
-    sout[k] = 0;
-    /* sout[k] = 0 is the last character at this point */
-  }
-
-  return (sout);
-} /* edit */
-
-
-/* Return a string of the same character */
-vstring string(long n, char c)
-{
-  vstring sout;
-  long j = 0;
-  if (n < 0) n = 0;
-  sout=tempAlloc(n + 1);
-  while (j < n) sout[j++] = c;
-  sout[j] = 0;
-  return (sout);
-} /* string */
-
-
-/* Return a string of spaces */
-vstring space(long n)
-{
-  return (string(n, ' '));
-} /* space */
-
-
-/* Return a character given its ASCII value */
-vstring chr(long n)
-{
-  vstring sout;
-  sout = tempAlloc(2);
-  sout[0] = (char)(n & 0xFF);
-  sout[1] = 0;
-  return(sout);
-} /* chr */
-
-
-/* Search for string2 in string1 starting at start_position */
-/* If there is no match, 0 is returned */
-/* If string2 is "", (length of the string) + 1 is returned */
-long instr(long start_position, vstring string1, vstring string2)
-{
-  char *sp1, *sp2;
-  long ls1, ls2;
-  long found = 0;
-  if (start_position < 1) start_position = 1;
-  ls1 = (long)strlen(string1);
-  ls2 = (long)strlen(string2);
-  if (start_position > ls1) start_position = ls1 + 1;
-  sp1 = string1 + start_position - 1;
-  while ((sp2 = strchr(sp1, string2[0])) != 0) {
-    if (strncmp(sp2, string2, (size_t)ls2) == 0) {
-      found = sp2 - string1 + 1;
-      break;
-    } else
-      sp1 = sp2 + 1;
-  }
-  return (found);
-} /* instr */
-
-
-/* 12-Jun-2011 nm Added rinstr */
-/* Search for _last_ occurrence of string2 in string1 */
-/* 1 = 1st string character; 0 = not found */
-/* ??? Future - this could be made more efficient by searching directly,
-   backwards from end of string1 */
-long rinstr(vstring string1, vstring string2)
-{
-  long pos = 0;
-  long savePos = 0;
-
-  while (1) {  /* Scan until substring no longer found */
-    pos = instr(pos + 1, string1, string2);
-    if (!pos) break;
-    savePos = pos;
-  }
-  return (savePos);
-} /* rinstr */
-
-
-/* Translate string in sin to sout based on table.
-   Table must be 256 characters long!! <- not true anymore? */
-vstring xlate(vstring sin,vstring table)
-{
-  vstring sout;
-  long len_table, len_sin;
-  long i, j;
-  long table_entry;
-  char m;
-  len_sin = (long)strlen(sin);
-  len_table = (long)strlen(table);
-  sout = tempAlloc(len_sin+1);
-  for (i = j = 0; i < len_sin; i++)
-  {
-    table_entry = 0x000000FF & (long)sin[i];
-    if (table_entry < len_table)
-      if ((m = table[table_entry])!='\0')
-        sout[j++] = m;
-  }
-  sout[j]='\0';
-  return (sout);
-} /* xlate */
-
-
-/* Returns the ascii value of a character */
-long ascii_(vstring c)
-{
-  return ((long)c[0]);
-} /* ascii_ */
-
-
-/* Returns the floating-point value of a numeric string */
-double val(vstring s)
-{
-  double v = 0;
-  char signFound = 0;
-  double power = 1.0;
-  long i;
-  for (i = (long)strlen(s); i >= 0; i--) {
-    switch (s[i]) {
-      case '.':
-        v = v / power;
-        power = 1.0;
-        break;
-      case '-':
-        signFound = 1;
-        break;
-      case '0': case '1': case '2': case '3': case '4':
-      case '5': case '6': case '7': case '8': case '9':
-        v = v + ((double)(s[i] - '0')) * power;
-        power = 10.0 * power;
-        break;
-    }
-  }
-  if (signFound) v = - v;
-  return v;
-  /*
-  return (atof(s));
-  */
-} /* val */
-
-
-/* Returns current date as an ASCII string */
-vstring date()
-{
-  vstring sout;
-  struct tm *time_structure;
-  time_t time_val;
-  char *month[12];
-
-  /* (Aggregrate initialization is not portable) */
-  /* (It must be done explicitly for portability) */
-  month[0] = "Jan";
-  month[1] = "Feb";
-  month[2] = "Mar";
-  month[3] = "Apr";
-  month[4] = "May";
-  month[5] = "Jun";
-  month[6] = "Jul";
-  month[7] = "Aug";
-  month[8] = "Sep";
-  month[9] = "Oct";
-  month[10] = "Nov";
-  month[11] = "Dec";
-
-  time(&time_val); /* Retrieve time */
-  time_structure = localtime(&time_val); /* Translate to time structure */
-  sout = tempAlloc(15); /* 8-Mar-2019 nm Changed from 12 to 15 to prevent
-                           gcc 8.3 warning (patch provided by David Starner) */
-  /* "%02d" means leading zeros with min. field width of 2 */
-  /* sprintf(sout,"%d-%s-%02d", */
-  sprintf(sout,"%d-%s-%04d", /* 10-Apr-06 nm 4-digit year */
-      time_structure->tm_mday,
-      month[time_structure->tm_mon],
-      /* time_structure->tm_year); */ /* old */
-      /* (int)((time_structure->tm_year) % 100)); */ /* Y2K */
-      (int)((time_structure->tm_year) + 1900)); /* 10-Apr-06 nm 4-digit yr */
-  return(sout);
-} /* date */
-
-
-/* Return current time as an ASCII string */
-vstring time_()
-{
-  vstring sout;
-  struct tm *time_structure;
-  time_t time_val;
-  int i;
-  char *format;
-  char *format1 = "%d:%d %s";
-  char *format2 = "%d:0%d %s";
-  char *am_pm[2];
-  /* (Aggregrate initialization is not portable) */
-  /* (It must be done explicitly for portability) */
-  am_pm[0] = "AM";
-  am_pm[1] = "PM";
-
-  time(&time_val); /* Retrieve time */
-  time_structure = localtime(&time_val); /* Translate to time structure */
-  if (time_structure->tm_hour >= 12)
-    i = 1;
-  else
-    i = 0;
-  if (time_structure->tm_hour > 12)
-    time_structure->tm_hour -= 12;
-  if (time_structure->tm_hour == 0)
-    time_structure->tm_hour = 12;
-  sout = tempAlloc(12);
-  if (time_structure->tm_min >= 10)
-    format = format1;
-  else
-    format = format2;
-  sprintf(sout,format,
-      time_structure->tm_hour,
-      time_structure->tm_min,
-      am_pm[i]);
-  return(sout);
-} /* time */
-
-
-/* Return a number as an ASCII string */
-vstring str(double f)
-{
-  /* This function converts a floating point number to a string in the */
-  /* same way that %f in printf does, except that trailing zeroes after */
-  /* the one after the decimal point are stripped; e.g., it returns 7 */
-  /* instead of 7.000000000000000. */
-  vstring s;
-  long i;
-  s = tempAlloc(50);
-  sprintf(s,"%f", f);
-  if (strchr(s, '.') != 0) { /* The string has a period in it */
-    for (i = (long)strlen(s) - 1; i > 0; i--) {  /* Scan string backwards */
-      if (s[i] != '0') break; /* 1st non-zero digit */
-      s[i] = 0; /* Delete the trailing 0 */
-    }
-    if (s[i] == '.') s[i] = 0; /* Delete trailing period */
-/*E*/INCDB1(-(49 - (long)strlen(s)));
-  }
-  return (s);
-} /* str */
-
-
-/* Return a number as an ASCII string */
-/* (This may have differed slightly from str() in BASIC but I forgot how.
-   It should be considered deprecated.) */
-vstring num1(double f)
-{
-  return (str(f));
-} /* num1 */
-
-
-/* Return a number as an ASCII string surrounded by spaces */
-/* (This should be considered deprecated.) */
-vstring num(double f)
-{
-  return (cat(" ",str(f)," ",NULL));
-} /* num */
-
-
-
-/*** NEW FUNCTIONS ADDED 11/25/98 ***/
-
-/* Emulate PROGRESS "entry" and related string functions */
-/* (PROGRESS is a 4-GL database language) */
-
-/* A "list" is a string of comma-separated elements.  Example:
-   "a,b,c" has 3 elements.  "a,b,c," has 4 elements; the last element is
-   an empty string.  ",," has 3 elements; each is an empty string.
-   In "a,b,c", the entry numbers of the elements are 1, 2 and 3 (i.e.
-   the entry numbers start a 1, not 0). */
-
-/* Returns a character string entry from a comma-separated
-   list based on an integer position. */
-/* If element is less than 1 or greater than number of elements
-   in the list, a null string is returned. */
-vstring entry(long element, vstring list)
-{
-  vstring sout;
-  long commaCount, lastComma, i, length;
-  if (element < 1) return ("");
-  lastComma = -1;
-  commaCount = 0;
-  i = 0;
-  while (list[i] != 0) {
-    if (list[i] == ',') {
-      commaCount++;
-      if (commaCount == element) {
-        break;
-      }
-      lastComma = i;
-    }
-    i++;
-  }
-  if (list[i] == 0) commaCount++;
-  if (element > commaCount) return ("");
-  length = i - lastComma - 1;
-  if (length < 1) return ("");
-  sout = tempAlloc(length + 1);
-  strncpy(sout, list + lastComma + 1, (size_t)length);
-  sout[length] = 0;
-  return (sout);
-}
-
-/* Emulate PROGRESS lookup function */
-/* Returns an integer giving the first position of an expression
-   in a comma-separated list. Returns a 0 if the expression
-   is not in the list. */
-long lookup(vstring expression, vstring list)
-{
-  long i, exprNum, exprPos;
-  char match;
-
-  match = 1;
-  i = 0;
-  exprNum = 0;
-  exprPos = 0;
-  while (list[i] != 0) {
-    if (list[i] == ',') {
-      exprNum++;
-      if (match) {
-        if (expression[exprPos] == 0) return exprNum;
-      }
-      exprPos = 0;
-      match = 1;
-      i++;
-      continue;
-    }
-    if (match) {
-      if (expression[exprPos] != list[i]) match = 0;
-    }
-    i++;
-    exprPos++;
-  }
-  exprNum++;
-  if (match) {
-    if (expression[exprPos] == 0) return exprNum;
-  }
-  return 0;
-}
-
-
-/* Emulate PROGRESS num-entries function */
-/* Returns the number of items in a comma-separated list.  If the
-   list is the empty string, return 0. */
-long numEntries(vstring list)
-{
-  long i, commaCount;
-  if (list[0] == 0) {
-    commaCount = -1; /* 26-Apr-2006 nm Return 0 if list empty */
-  } else {
-    commaCount = 0;
-    i = 0;
-    while (list[i] != 0) {
-      if (list[i] == ',') commaCount++;
-      i++;
-    }
-  }
-  return (commaCount + 1);
-}
-
-/* Returns the character position of the start of the
-   element in a list - useful for manipulating
-   the list string directly.  1 means the first string
-   character. */
-/* If element is less than 1 or greater than number of elements
-   in the list, a 0 is returned.  If entry is null, a 0 is
-   returned. */
-long entryPosition(long element, vstring list)
-{
-  long commaCount, lastComma, i;
-  if (element < 1) return 0;
-  lastComma = -1;
-  commaCount = 0;
-  i = 0;
-  while (list[i] != 0) {
-    if (list[i] == ',') {
-      commaCount++;
-      if (commaCount == element) {
-        break;
-      }
-      lastComma = i;
-    }
-    i++;
-  }
-  if (list[i] == 0) {
-    if (i == 0) return 0;
-    if (list[i - 1] == ',') return 0;
-    commaCount++;
-  }
-  if (element > commaCount) return (0);
-  if (list[lastComma + 1] == ',') return 0;
-  return (lastComma + 2);
-}
-
-
-
-/* For debugging */
-/*
-
-int main(void)
-{
-  vstringdef(s);
-  vstringdef(t);
-
-  printf("Hello\n");
-  let(&t,edit(" x y z ",2));
-  let(&s,cat(right("abc",2),left("def",len(right("xxx",2))),"ghi",t,NULL));
-  printf("%s\n",s);
-  printf("num %s\n",num(5));
-  printf("str %s\n",str(5.02));
-  printf("num1 %s\n",num1(5.02));
-  printf("time_ %s\n",time_());
-  printf("date %s\n",date());
-  printf("val %f\n",val("6.77"));
-}
-
-*/
+/*****************************************************************************/
+/*        Copyright (C) 2019  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/*!
+\file mmvstr.c - VMS-BASIC variable length string library routines header
+This is an emulation of the string functions available in VMS BASIC.
+*/
+
+/*** See the comments in mmvstr.h for an explanation of these functions ******/
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "mmvstr.h"
+/*E*/ /*Next line is need to declare "db" for debugging*/
+#include "mmdata.h"
+/* mmdata.h is also used to declare the bug() function that is called in
+   several places by mmvstr.c.  To make mmvstr.c and mmvstr.h completely
+   independent of the other programs, for use with another project, do the
+   following:
+     (1) Remove all lines beginning with the "/ *E* /" comment.
+     (2) Remove all calls to the bug() function (4 places).
+   To see an example of stand-alone usage of the mmvstr.c functions, see
+   the program lattice.c and several others included in
+     http://us.metamath.org/downloads/quantum-logic.tar.gz
+*/
+
+/*E*/long db1=0;
+
+/*!
+ * \def INCDB1
+ * updates \ref db1 if NDEBUG is not defined, is a no operation else.
+ *
+ * NDEBUG switches C assert instructions off or on.  So the handling of db1 is
+ * aligned with assert().
+ */
+#ifdef NDEBUG
+# define INCDB1(x)
+#else
+# define INCDB1(x) db1 += (x)
+#endif
+
+/*!
+ * \def MAX_ALLOC_STACK
+ *
+ * The number of \ref vstring pointers set aside for temporary string
+ * evaluation.  This number covers the needs of ordinary nested functions but
+ * it puts a limit to recurrent calls.
+ *
+ * The number given here is one greater than actually available.  One entry is
+ * reserved for the terminal null pointer marking the top of stack.
+ */
+#define MAX_ALLOC_STACK 100
+long g_tempAllocStackTop = 0;      /* Top of stack for tempAlloc function */
+long g_startTempAllocStack = 0;    /* Where to start freeing temporary allocation
+                                    when let() is called (normally 0, except in
+                                    special nested vstring functions) */
+
+/*!
+ * \brief stack for temporary text.
+ *
+ * This \ref pgStack "stack" contains \ref vstring pointers holding temporary
+ * text like fragments, boilerplate and so on.  The current top of the stack is
+ * \ref g_tempAllocStackTop.  Nested functions share this stack, each setting
+ * aside its own scope.  The scope of the most nested function begins at index
+ * \ref g_startTempAllocStack.
+ *
+ * When a nested function starts execution, it saves \ref g_startTempAllocStack
+ * and copies the \ref g_tempAllocStackTop into it, marking the begin of its own
+ * scope of temporaries.  Before returning, both values are restored again.
+ *
+ * The scope of top level functions begins at index 0
+ *.
+ * \invariant
+ * - The entry at \ref g_tempAllocStackTop is NULL.
+ */
+void *tempAllocStack[MAX_ALLOC_STACK];
+
+void freeTempAlloc(void) {
+  /* All memory previously allocated with tempAlloc is deallocated. */
+  /* EXCEPT:  When g_startTempAllocStack != 0, the freeing will start at
+     g_startTempAllocStack. */
+  long i;
+  for (i = g_startTempAllocStack; i < g_tempAllocStackTop; i++) {
+/*E*/INCDB1(-1 - (long)strlen(tempAllocStack[i]));
+/*E* /printf("%ld removing [%s]\n", db1, tempAllocStack[i]);*/
+    free(tempAllocStack[i]);
+  }
+  g_tempAllocStackTop = g_startTempAllocStack;
+} /* freeTempAlloc */
+
+/*!
+ * \fn pushTempAlloc(void *mem)
+ * \brief pushes a pointer onto the \ref tempAllocStack.
+ *
+ * In case of a stack overflow \ref bugfn "bug" is called.  This function is low level
+ * that does not ensure that invariants of \ref tempAllocStack are kept.
+ *
+ * \param mem (not null) points to either a non-mutable empty string, or
+ *   to allocated memory.  Its contents need not be valid yet, although it is
+ *   recommended to point to a non-NUL character.
+ * \pre
+ *   The stack must not be full.
+ * \post
+ *   If not full, \p mem is added on top of \ref tempAllocStack, and
+ *   \ref g_tempAllocStackTop is increased.  This function
+ *   does not ensure a NULL pointer follows the pushed pointer.  Statistics in
+ *   \ref db1 is not updated.
+ * \warning
+ *   In case of stack overflow, the caller is not notified and a memory leak
+ *   is likely.
+ */
+static void pushTempAlloc(void *mem)
+{
+  if (g_tempAllocStackTop >= (MAX_ALLOC_STACK-1)) {
+    printf("*** FATAL ERROR ***  Temporary string stack overflow\n");
+#if __STDC__
+    fflush(stdout);
+#endif
+    bug(2201);
+  }
+  tempAllocStack[g_tempAllocStackTop++] = mem;
+} /* pushTempAlloc */
+
+/*!
+ * \fn tempAlloc(long size)
+ *
+ * \brief allocates memory for size bytes and pushes it onto the
+ *   \ref tempAllocStack
+ *
+ * This low level function does NOT initialize the allocated memory.  If the
+ * allocation on the heap fails, \ref bugfn "bug" is called.  The statistic
+ * value \ref db1 is updated.
+ *
+ * \param size (> 0) number of bytes to allocate on the heap.  If the memory is
+ *   intended to hold NUL terminated text, then size must account for the final
+ *   NUL character, too.
+ * \pre
+ *   The \ref tempAllocStack must not be full.
+ * \post
+ *   The top of \ref tempAllocStack addresses memory at least the size of the
+ *   submitted parameter.
+ * \warning
+ *   In case of stack overflow, the caller is not notified and a memory leak
+ *   is likely.
+ */
+static void* tempAlloc(long size)  /* String memory allocation/deallocation */
+{
+  void* memptr = malloc((size_t)size);
+  if (!memptr || size == 0) {
+    printf("*** FATAL ERROR ***  Temporary string allocation failed\n");
+#if __STDC__
+    fflush(stdout);
+#endif
+    bug(2202);
+  }
+  pushTempAlloc(memptr);
+/*E*/INCDB1(size);
+/*E* /printf("%ld adding\n",db1);*/
+  return memptr;
+} /* tempAlloc */
+
+
+/* Put string in temporary allocation arena */
+temp_vstring makeTempAlloc(vstring s) {
+  if (s[0]) { /* Don't do it if vstring is empty */
+    pushTempAlloc(s);
+/*E*/INCDB1((long)strlen(s) + 1);
+/*E*/db-=(long)strlen(s) + 1;
+/*E* /printf("%ld temping[%s]\n", db1, s);*/
+  }
+  return s;
+} /* makeTempAlloc */
+
+
+/* String assignment */
+void let(vstring *target, const char *source) {
+
+  size_t sourceLength = strlen(source);  /* Save its length */
+  size_t targetLength = strlen(*target); /* Save its length */
+/*E*/if (targetLength) {
+/*E*/  db -= (long)targetLength+1;
+/*E*/  /* printf("%ld Deleting %s\n",db,*target); */
+/*E*/}
+/*E*/if (sourceLength) {
+/*E*/  db += (long)sourceLength+1;
+/*E*/  /* printf("%ld Adding %s\n",db,source); */
+/*E*/}
+  if (targetLength < sourceLength) { /* Old string has not enough room for new one */
+    /* Free old string space and allocate new space */
+    if (targetLength)
+      free(*target);  /* Free old space */
+    *target = malloc(sourceLength + 1); /* Allocate new space */
+    if (!*target) {
+      printf("*** FATAL ERROR ***  String memory couldn't be allocated\n");
+#if __STDC__
+      fflush(stdout);
+#endif
+      bug(2204);
+    }
+  }
+  if (sourceLength) {
+    strcpy(*target, source);
+  } else {
+    /* Empty strings could still be temporaries, so always assign a constant */
+    if (targetLength) {
+      free(*target);
+    }
+    *target = "";
+  }
+
+  freeTempAlloc(); /* Free up temporary strings used in expression computation */
+
+} /* let */
+
+/* String concatenation */
+temp_vstring cat(const char *string1, ...) {
+#define MAX_CAT_ARGS 50
+  va_list ap;   /* Declare list incrementer */
+  const char *arg[MAX_CAT_ARGS];    /* Array to store arguments */
+  size_t argPos[MAX_CAT_ARGS]; /* Array of argument positions in result */
+  int i;
+  int numArgs = 0;        /* Define "last argument" */
+
+  size_t pos = 0;
+  const char* curArg = string1;
+
+  va_start(ap, string1); /* Begin the session */
+  do {
+        /* User-provided argument list must terminate with 0 */
+    if (numArgs >= MAX_CAT_ARGS) {
+      printf("*** FATAL ERROR ***  Too many cat() arguments\n");
+#if __STDC__
+      fflush(stdout);
+#endif
+      bug(2206);
+    }
+    arg[numArgs] = curArg;
+    argPos[numArgs] = pos;
+    pos += strlen(curArg);
+  } while (++numArgs, (curArg = va_arg(ap,char *)) != 0);
+  va_end(ap);           /* End varargs session */
+
+  /* Allocate the memory for it */
+  temp_vstring result = tempAlloc((long)pos+1);
+  /* Move the strings into the newly allocated area */
+  for (i = 0; i < numArgs; ++i)
+    strcpy(result + argPos[i], arg[i]);
+  return result;
+} /* cat */
+
+
+/* Input a line from the user or from a file */
+/* Returns 1 if a (possibly empty) line was successfully read, 0 if EOF */
+/*
+  BASIC:  linput "what"; a$
+  c:      linput(NULL, "what?", &a);
+
+  BASIC:  linput #1, a$                         (error trap on EOF)
+  c:      if (!linput(file1, NULL, &a)) break;  (break on EOF)
+
+*/
+/* This function prints a prompt (if 'ask' is not NULL), gets a line from
+  the stream, and assigns it to target using the let(&...) function.
+  0 is returned when end-of-file is encountered.  The vstring
+  *target MUST be initialized to "" or previously assigned by let(&...)
+  before using it in linput. */
+int linput(FILE *stream, const char* ask, vstring *target) {
+                            /* Note: "vstring *target" means "char **target" */
+  char f[10001]; /* Read in chunks up to 10000 characters */
+  int result = 0;
+  int eol_found = 0;
+  if (ask) {
+    printf("%s", ask);
+#if __STDC__
+    fflush(stdout);
+#endif
+  }
+  if (stream == NULL) stream = stdin;
+  while (!eol_found && fgets(f, sizeof(f), stream))
+  {
+    size_t endpos = strlen(f) - 1;
+    eol_found = (f[endpos] == '\n');
+    /* If the last line in the file has no newline, eol_found will be 0 here.
+       The fgets() above will return 0 and prevent another loop iteration. */
+    if (eol_found)
+      f[endpos] = 0; /* The return string will have any newline stripped. */
+    if (result)
+      /* Append additional parts of the line to *target */
+      /* The let() reallocates *target and copies the concatenation of the
+         old *target and the additional input f[] to it */
+      let(target /* = &(*target) */, cat(*target, f, NULL));
+    else
+      /* This is the first time through the loop, and normally
+         the only one unless the input line overflows f[] */
+      let(target, f);  /* Allocate *target and copy f to it */
+    result = 1;
+  }
+  return result;
+} /* linput */
+
+
+/* Find out the length of a string */
+long len(const char *s) {
+  return (long)strlen(s);
+} /* len */
+
+
+/* Extract sin from character position start to stop into sout */
+temp_vstring seg(const char *sin, long start, long stop) {
+  if (start < 1) start = 1;
+  return mid(sin, start, stop - start + 1);
+} /* seg */
+
+
+/* Extract sin from character position start for length len */
+temp_vstring mid(const char *sin, long start, long length) {
+  if (start < 1) start = 1;
+  if (length < 0) length = 0;
+  temp_vstring sout = tempAlloc(length + 1);
+  strncpy(sout, sin + start - 1, (size_t)length);
+/*E*/ /*??? Should db be subtracted from if length > end of string? */
+  sout[length] = 0;
+  return sout;
+} /* mid */
+
+
+/* Extract leftmost n characters */
+temp_vstring left(const char *sin, long n) {
+  return mid(sin, 1, n);
+} /* left */
+
+
+/* Extract after character n */
+temp_vstring right(const char *sin, long n) {
+  return seg(sin, n, (long)(strlen(sin)));
+} /* right */
+
+
+/* Emulate VMS BASIC edit$ command */
+temp_vstring edit(const char *sin, long control) {
+
+/* Added _ to fix '"isblank" redefined' compiler warning */
+#define isblank_(c) ((c == ' ') || (c == '\t'))
+#define isblankorlf_(c) ((c == ' ') || (c == '\t') || (c == '\n'))
+
+  /* EDIT$ (from VMS BASIC manual)
+       Syntax:  str-vbl = EDIT$(str-exp, int-exp)
+       Values   Effect
+       1        Clear parity bits
+       2        Discard all spaces and tabs
+       4        Discard characters: CR, LF, FF, ESC, RUBOUT, and NULL
+       8        Discard leading spaces and tabs
+       16       Reduce spaces and tabs to one space
+       32       Convert lowercase to uppercase
+       64       Convert [ to ( and ] to )
+       128      Discard trailing spaces and tabs
+       256      Do not alter characters inside quotes
+
+       (non-BASIC extensions)
+       512      Convert uppercase to lowercase
+       1024     Tab the line (convert spaces to equivalent tabs)
+       2048     Untab the line (convert tabs to equivalent spaces)
+       4096     Convert VT220 screen print frame graphics to -,|,+ characters
+       8192     Discard CR only (to assist DOS-to-Unix conversion)
+       16384    Discard trailing spaces, tabs, and LFs
+  */
+  long i, j, k, m;
+  int last_char_is_blank;
+  int clear_parity_flag, discardctrl_flag, bracket_flag, quote_flag, uppercase_flag;
+  int alldiscard_flag, leaddiscard_flag, traildiscard_flag,
+      traildiscardLF_flag, reduce_flag;
+  int processing_inside_quote=0;
+  int lowercase_flag, tab_flag, untab_flag, screen_flag, discardcr_flag;
+  unsigned char graphicsChar;
+
+  /* Set up the flags */
+  clear_parity_flag = control & 1;
+  alldiscard_flag = control & 2;
+  discardctrl_flag = control & 4;
+  leaddiscard_flag = control & 8;
+  reduce_flag = control & 16;
+  uppercase_flag = control & 32;
+  bracket_flag = control & 64;
+  traildiscard_flag = control & 128;
+  traildiscardLF_flag = control & 16384;
+  quote_flag = control & 256;
+
+  /* Non-BASIC extensions */
+  lowercase_flag = control & 512;
+  tab_flag = control & 1024;
+  untab_flag = control & 2048;
+  screen_flag = control & 4096; /* Convert VT220 screen prints to |,-,+
+                                   format */
+  discardcr_flag = control & 8192; /* Discard CR's */
+
+  /* Copy string */
+  i = (long)strlen(sin) + 1;
+  if (untab_flag) i = i * 7; /* Allow for max possible length */
+  temp_vstring sout = tempAlloc(i);
+  strcpy(sout, sin);
+
+  /* Discard leading space/tab */
+  i = 0;
+  if (leaddiscard_flag)
+    while ((sout[i] != 0) && isblank_(sout[i]))
+      sout[i++] = 0;
+
+  /* Main processing loop */
+  while (sout[i] != 0) {
+
+    /* Alter characters inside quotes ? */
+    if (quote_flag && ((sout[i] == '"') || (sout[i] == '\'')))
+       processing_inside_quote = ~ processing_inside_quote;
+    if (processing_inside_quote) {
+       /* Skip the rest of the code and continue to process next character */
+       i++; continue;
+    }
+
+    /* Discard all space/tab */
+    if ((alldiscard_flag) && isblank_(sout[i]))
+        sout[i] = 0;
+
+    /* Clear parity (eighth?) bit */
+    if (clear_parity_flag)
+       sout[i] = sout[i] & 0x7F;
+
+    /* Discard CR,LF,FF,ESC,BS */
+    if ((discardctrl_flag) && (
+         (sout[i] == '\015') || /* CR  */
+         (sout[i] == '\012') || /* LF  */
+         (sout[i] == '\014') || /* FF  */
+         (sout[i] == '\033') || /* ESC */
+         /*(sout[i] == '\032') ||*/ /* ^Z */ /* DIFFERENCE won't work w/ this */
+         (sout[i] == '\010')))  /* BS  */
+      sout[i] = 0;
+
+    /* Discard CR */
+    if ((discardcr_flag) && (
+         (sout[i] == '\015')))  /* CR  */
+      sout[i] = 0;
+
+    /* Convert lowercase to uppercase */
+    /*
+    if ((uppercase_flag) && (islower(sout[i])))
+       sout[i] = toupper(sout[i]);
+    */
+    /* 13-Jun-2009 nm The upper/lower case C functions have odd behavior
+       with characters > 127, at least in lcc.  So this was rewritten to
+       not use them. */
+    if ((uppercase_flag) && (sout[i] >= 'a' && sout[i] <= 'z'))
+       sout[i] = (char)(sout[i] - ('a' - 'A'));
+
+    /* Convert [] to () */
+    if ((bracket_flag) && (sout[i] == '['))
+       sout[i] = '(';
+    if ((bracket_flag) && (sout[i] == ']'))
+       sout[i] = ')';
+
+    /* Convert uppercase to lowercase */
+    /*
+    if ((lowercase_flag) && (isupper(sout[i])))
+       sout[i] = tolower(sout[i]);
+    */
+    /* 13-Jun-2009 nm The upper/lower case C functions have odd behavior
+       with characters > 127, at least in lcc.  So this was rewritten to
+       not use them. */
+    if ((lowercase_flag) && (sout[i] >= 'A' && sout[i] <= 'Z'))
+       sout[i] = (char)(sout[i] + ('a' - 'A'));
+
+    /* Convert VT220 screen print frame graphics to +,|,- */
+    if (screen_flag) {
+      graphicsChar = (unsigned char)sout[i]; /* Need unsigned char for >127 */
+      /* vt220 */
+      if (graphicsChar >= 234 && graphicsChar <= 237) sout[i] = '+';
+      if (graphicsChar == 241) sout[i] = '-';
+      if (graphicsChar == 248) sout[i] = '|';
+      if (graphicsChar == 166) sout[i] = '|';
+      /* vt100 */
+      if (graphicsChar == 218 /*up left*/ || graphicsChar == 217 /*lo r*/
+          || graphicsChar == 191 /*up r*/ || graphicsChar == 192 /*lo l*/)
+        sout[i] = '+';
+      if (graphicsChar == 196) sout[i] = '-';
+      if (graphicsChar == 179) sout[i] = '|';
+    }
+
+    /* Process next character */
+    i++;
+  }
+  /* sout[i]=0 is the last character at this point */
+
+  /* Clean up the deleted characters */
+  for (j = 0, k = 0; j <= i; j++)
+    if (sout[j]!=0) sout[k++]=sout[j];
+  sout[k] = 0;
+  /* sout[k] = 0 is the last character at this point */
+
+  /* Discard trailing space/tab */
+  if (traildiscard_flag) {
+    --k;
+    while ((k >= 0) && isblank_(sout[k])) --k;
+    sout[++k] = 0;
+  }
+
+  /* Discard trailing space/tab and LF */
+  if (traildiscardLF_flag) {
+    --k;
+    while ((k >= 0) && isblankorlf_(sout[k])) --k;
+    sout[++k] = 0;
+  }
+
+  /* Reduce multiple space/tab to a single space */
+  if (reduce_flag) {
+    i = j = last_char_is_blank = 0;
+    while (i <= k - 1) {
+      if (!isblank_(sout[i])) {
+        sout[j++] = sout[i++];
+        last_char_is_blank = 0;
+      } else {
+        if (!last_char_is_blank)
+          sout[j++]=' '; /* Insert a space at the first occurrence of a blank */
+        last_char_is_blank = 1; /* Register that a blank is found */
+        i++; /* Process next character */
+      }
+    }
+    sout[j] = 0;
+  }
+
+  /* Untab the line */
+  if (untab_flag || tab_flag) {
+
+    /*
+    DEF FNUNTAB$(L$)      ! UNTAB LINE L$
+    I9%=1%
+    I9%=INSTR(I9%,L$,CHR$(9%))
+    WHILE I9%
+      L$=LEFT(L$,I9%-1%)+SPACE$(8%-((I9%-1%) AND 7%))+RIGHT(L$,I9%+1%)
+      I9%=INSTR(I9%,L$,CHR$(9%))
+    NEXT
+    FNUNTAB$=L$
+    FNEND
+    */
+
+    /***** old code (doesn't handle multiple lines)
+    k = (long)strlen(sout);
+    for (i = 1; i <= k; i++) {
+      if (sout[i - 1] != '\t') continue;
+      for (j = k; j >= i; j--) {
+        sout[j + 8 - ((i - 1) & 7) - 1] = sout[j];
+      }
+      for (j = i; j < i + 8 - ((i - 1) & 7); j++) {
+        sout[j - 1] = ' ';
+      }
+      k = k + 8 - ((i - 1) & 7);
+    }
+    *****/
+
+    /* Untab string containing multiple lines */
+    /* (Currently this is needed by outputStatement() in mmpars.c) */
+    k = (long)strlen(sout);
+    m = 0;  /* Position on line relative to last '\n' */
+    for (i = 1; i <= k; i++) {
+      if (sout[i - 1] == '\n') {
+        m = 0;
+        continue;
+      }
+      m++; /* Should equal i for one-line string */
+      if (sout[i - 1] != '\t') continue;
+      for (j = k; j >= i; j--) {
+        sout[j + 8 - ((m - 1) & 7) - 1] = sout[j];
+      }
+      for (j = i; j < i + 8 - ((m - 1) & 7); j++) {
+        sout[j - 1] = ' ';
+      }
+      k = k + 8 - ((m - 1) & 7);
+    }
+  }
+
+  /* Tab the line */
+  /* (Note that this does not [yet?] handle string with multiple lines) */
+  if (tab_flag) {
+
+    /*
+    DEF FNTAB$(L$)        ! TAB LINE L$
+    I9%=0%
+    FOR I9%=8% STEP 8% WHILE I9%<LEN(L$)
+      J9%=I9%
+      J9%=J9%-1% UNTIL ASCII(MID(L$,J9%,1%))<>32% OR J9%=I9%-8%
+      IF J9%<=I9%-2% THEN
+        L$=LEFT(L$,J9%)+CHR$(9%)+RIGHT(L$,I9%+1%)
+        I9%=J9%+1%
+      END IF
+    NEXT I9%
+    FNTAB$=L$
+    FNEND
+    */
+
+    k = (long)strlen(sout);
+    for (i = 8; i < k; i = i + 8) {
+      j = i;
+
+      /* gcc m*.c -o metamath.exe -O2 -Wall was giving:
+             mmvstr.c:285:9: warning: assuming signed overflow does not occur
+             when assuming that (X - c) <= X is always true [-Wstrict-overflow]
+         Here we trick gcc into turning off this optimization by moving
+         the computation of i - 2 here, then referencing m instead of i - 2
+         below.  Note that if "m = i - 2" is moved _after_ the "while", the
+         error message returns. */
+      m = i - 2;
+
+      while (sout[j - 1] == ' ' && j > i - 8) j--;
+      if (j <= m) {
+        sout[j] = '\t';
+        j = i;
+        while (sout[j - 1] == ' ' && j > i - 8 + 1) {
+          sout[j - 1] = 0;
+          j--;
+        }
+      }
+    }
+    i = k;
+    /* sout[i]=0 is the last character at this point */
+    /* Clean up the deleted characters */
+    for (j = 0, k = 0; j <= i; j++)
+      if (sout[j] != 0) sout[k++] = sout[j];
+    sout[k] = 0;
+    /* sout[k] = 0 is the last character at this point */
+  }
+
+  return sout;
+} /* edit */
+
+
+/* Return a string of the same character */
+temp_vstring string(long n, char c) {
+  long j = 0;
+  if (n < 0) n = 0;
+  temp_vstring sout = tempAlloc(n + 1);
+  while (j < n) sout[j++] = c;
+  sout[j] = 0;
+  return (sout);
+} /* string */
+
+
+/* Return a string of spaces */
+temp_vstring space(long n) {
+  return string(n, ' ');
+} /* space */
+
+
+/* Return a character given its ASCII value */
+temp_vstring chr(long n) {
+  temp_vstring sout = tempAlloc(2);
+  sout[0] = (char)(n & 0xFF);
+  sout[1] = 0;
+  return sout;
+} /* chr */
+
+
+long instr(long start, const char *string, const char *match) {
+  const char *sp1, *sp2;
+  long ls1, ls2;
+  long found = 0;
+  if (start < 1) start = 1;
+  ls1 = (long)strlen(string);
+  ls2 = (long)strlen(match);
+  if (start > ls1) start = ls1 + 1;
+  sp1 = string + start - 1;
+  while ((sp2 = strchr(sp1, match[0])) != 0) {
+    if (strncmp(sp2, match, (size_t)ls2) == 0) {
+      found = sp2 - string + 1;
+      break;
+    } else
+      sp1 = sp2 + 1;
+  }
+  return found;
+} /* instr */
+
+
+/* Search for _last_ occurrence of string2 in string1 */
+/* 1 = 1st string character; 0 = not found */
+/* ??? Future - this could be made more efficient by searching directly,
+   backwards from end of string1 */
+long rinstr(const char *string1, const char *string2) {
+  long pos = 0;
+  long savePos = 0;
+
+  while (1) {  /* Scan until substring no longer found */
+    pos = instr(pos + 1, string1, string2);
+    if (!pos) break;
+    savePos = pos;
+  }
+  return savePos;
+} /* rinstr */
+
+
+/* Translate string in sin to sout based on table.
+   Table must be 256 characters long!! <- not true anymore? */
+temp_vstring xlate(const char *sin, const char *table)
+{
+  long len_table, len_sin;
+  long i, j;
+  long table_entry;
+  char m;
+  len_sin = (long)strlen(sin);
+  len_table = (long)strlen(table);
+  temp_vstring sout = tempAlloc(len_sin+1);
+  for (i = j = 0; i < len_sin; i++)
+  {
+    table_entry = 0x000000FF & (long)sin[i];
+    if (table_entry < len_table)
+      if ((m = table[table_entry])!='\0')
+        sout[j++] = m;
+  }
+  sout[j]='\0';
+  return (sout);
+} /* xlate */
+
+
+/* Returns the ascii value of a character */
+long ascii_(const char *c) {
+  return (unsigned char)c[0];
+} /* ascii_ */
+
+
+/* Returns the floating-point value of a numeric string */
+double val(const char *s) {
+  double v = 0;
+  char signFound = 0;
+  double power = 1.0;
+  long i;
+  for (i = (long)strlen(s); i >= 0; i--) {
+    switch (s[i]) {
+      case '.':
+        v = v / power;
+        power = 1.0;
+        break;
+      case '-':
+        signFound = 1;
+        break;
+      case '0': case '1': case '2': case '3': case '4':
+      case '5': case '6': case '7': case '8': case '9':
+        v = v + ((double)(s[i] - '0')) * power;
+        power = 10.0 * power;
+        break;
+    }
+  }
+  if (signFound) v = - v;
+  return v;
+  /*
+  return (atof(s));
+  */
+} /* val */
+
+
+/* Returns current date as an ASCII string */
+temp_vstring date() {
+  struct tm *time_structure;
+  time_t time_val;
+  char *month[12];
+
+  /* (Aggregate initialization is not portable) */
+  /* (It must be done explicitly for portability) */
+  month[0] = "Jan";
+  month[1] = "Feb";
+  month[2] = "Mar";
+  month[3] = "Apr";
+  month[4] = "May";
+  month[5] = "Jun";
+  month[6] = "Jul";
+  month[7] = "Aug";
+  month[8] = "Sep";
+  month[9] = "Oct";
+  month[10] = "Nov";
+  month[11] = "Dec";
+
+  time(&time_val); /* Retrieve time */
+  time_structure = localtime(&time_val); /* Translate to time structure */
+  temp_vstring sout = tempAlloc(15); /* Use 15 instead of 12 to prevent gcc 8.3 warning */
+  /* "%02d" means leading zeros with min. field width of 2 */
+  /* sprintf(sout,"%d-%s-%02d", */
+  sprintf(sout,"%d-%s-%04d",
+      time_structure->tm_mday,
+      month[time_structure->tm_mon],
+      (int)((time_structure->tm_year) + 1900));
+  return sout;
+} /* date */
+
+
+/* Return current time as an ASCII string */
+temp_vstring time_() {
+  struct tm *time_structure;
+  time_t time_val;
+  int i;
+  char *format;
+  char *format1 = "%d:%d %s";
+  char *format2 = "%d:0%d %s";
+  char *am_pm[2];
+  /* (Aggregate initialization is not portable) */
+  /* (It must be done explicitly for portability) */
+  am_pm[0] = "AM";
+  am_pm[1] = "PM";
+
+  time(&time_val); /* Retrieve time */
+  time_structure = localtime(&time_val); /* Translate to time structure */
+  if (time_structure->tm_hour >= 12)
+    i = 1;
+  else
+    i = 0;
+  if (time_structure->tm_hour > 12)
+    time_structure->tm_hour -= 12;
+  if (time_structure->tm_hour == 0)
+    time_structure->tm_hour = 12;
+  temp_vstring sout = tempAlloc(12);
+  if (time_structure->tm_min >= 10)
+    format = format1;
+  else
+    format = format2;
+  sprintf(sout,format,
+      time_structure->tm_hour,
+      time_structure->tm_min,
+      am_pm[i]);
+  return sout;
+} /* time */
+
+
+/* Return a number as an ASCII string */
+temp_vstring str(double f) {
+  /* This function converts a floating point number to a string in the */
+  /* same way that %f in printf does, except that trailing zeroes after */
+  /* the one after the decimal point are stripped; e.g., it returns 7 */
+  /* instead of 7.000000000000000. */
+  long i;
+  temp_vstring s = tempAlloc(50);
+  sprintf(s,"%f", f);
+  if (strchr(s, '.') != 0) { /* The string has a period in it */
+    for (i = (long)strlen(s) - 1; i > 0; i--) {  /* Scan string backwards */
+      if (s[i] != '0') break; /* 1st non-zero digit */
+      s[i] = 0; /* Delete the trailing 0 */
+    }
+    if (s[i] == '.') s[i] = 0; /* Delete trailing period */
+/*E*/INCDB1(-(49 - (long)strlen(s)));
+  }
+  return s;
+} /* str */
+
+
+/* Return a number as an ASCII string */
+/* (This may have differed slightly from str() in BASIC but I forgot how.
+   It should be considered deprecated.) */
+temp_vstring num1(double f) {
+  return str(f);
+} /* num1 */
+
+
+/* Return a number as an ASCII string surrounded by spaces */
+/* (This should be considered deprecated.) */
+temp_vstring num(double f) {
+  return cat(" ", str(f), " ", NULL);
+} /* num */
+
+
+
+/* Emulate PROGRESS "entry" and related string functions */
+/* (PROGRESS is a 4-GL database language) */
+
+/* A "list" is a string of comma-separated elements.  Example:
+   "a,b,c" has 3 elements.  "a,b,c," has 4 elements; the last element is
+   an empty string.  ",," has 3 elements; each is an empty string.
+   In "a,b,c", the entry numbers of the elements are 1, 2 and 3 (i.e.
+   the entry numbers start a 1, not 0). */
+
+/* Returns a character string entry from a comma-separated
+   list based on an integer position. */
+/* If element is less than 1 or greater than number of elements
+   in the list, a null string is returned. */
+temp_vstring entry(long element, const char *list)
+{
+  long commaCount, lastComma, i, length;
+  if (element < 1) return ("");
+  lastComma = -1;
+  commaCount = 0;
+  i = 0;
+  while (list[i] != 0) {
+    if (list[i] == ',') {
+      commaCount++;
+      if (commaCount == element) {
+        break;
+      }
+      lastComma = i;
+    }
+    i++;
+  }
+  if (list[i] == 0) commaCount++;
+  if (element > commaCount) return ("");
+  length = i - lastComma - 1;
+  if (length < 1) return ("");
+  temp_vstring sout = tempAlloc(length + 1);
+  strncpy(sout, list + lastComma + 1, (size_t)length);
+  sout[length] = 0;
+  return sout;
+}
+
+/* Emulate PROGRESS lookup function */
+/* Returns an integer giving the first position of an expression
+   in a comma-separated list. Returns a 0 if the expression
+   is not in the list. */
+long lookup(const char *expression, const char *list) {
+  long i, exprNum, exprPos;
+  char match;
+
+  match = 1;
+  i = 0;
+  exprNum = 0;
+  exprPos = 0;
+  while (list[i] != 0) {
+    if (list[i] == ',') {
+      exprNum++;
+      if (match) {
+        if (expression[exprPos] == 0) return exprNum;
+      }
+      exprPos = 0;
+      match = 1;
+      i++;
+      continue;
+    }
+    if (match) {
+      if (expression[exprPos] != list[i]) match = 0;
+    }
+    i++;
+    exprPos++;
+  }
+  exprNum++;
+  if (match) {
+    if (expression[exprPos] == 0) return exprNum;
+  }
+  return 0;
+}
+
+
+/* Emulate PROGRESS num-entries function */
+/* Returns the number of items in a comma-separated list.  If the
+   list is the empty string, return 0. */
+long numEntries(const char *list) {
+  long i, commaCount;
+  if (list[0] == 0) {
+    commaCount = -1; /* Return 0 if list empty */
+  } else {
+    commaCount = 0;
+    i = 0;
+    while (list[i] != 0) {
+      if (list[i] == ',') commaCount++;
+      i++;
+    }
+  }
+  return (commaCount + 1);
+}
+
+/* Returns the character position of the start of the
+   element in a list - useful for manipulating
+   the list string directly.  1 means the first string
+   character. */
+/* If element is less than 1 or greater than number of elements
+   in the list, a 0 is returned.  If entry is null, a 0 is
+   returned. */
+long entryPosition(long element, const char *list) {
+  long commaCount, lastComma, i;
+  if (element < 1) return 0;
+  lastComma = -1;
+  commaCount = 0;
+  i = 0;
+  while (list[i] != 0) {
+    if (list[i] == ',') {
+      commaCount++;
+      if (commaCount == element) {
+        break;
+      }
+      lastComma = i;
+    }
+    i++;
+  }
+  if (list[i] == 0) {
+    if (i == 0) return 0;
+    if (list[i - 1] == ',') return 0;
+    commaCount++;
+  }
+  if (element > commaCount) return (0);
+  if (list[lastComma + 1] == ',') return 0;
+  return (lastComma + 2);
+}
+
+
+
+/* For debugging */
+/*
+
+int main(void)
+{
+  vstring_def(s);
+  vstring_def(t);
+
+  printf("Hello\n");
+  let(&t,edit(" x y z ",2));
+  let(&s,cat(right("abc",2),left("def",len(right("xxx",2))),"ghi",t,NULL));
+  printf("%s\n",s);
+  printf("num %s\n",num(5));
+  printf("str %s\n",str(5.02));
+  printf("num1 %s\n",num1(5.02));
+  printf("time_ %s\n",time_());
+  printf("date %s\n",date());
+  printf("val %f\n",val("6.77"));
+}
+
+*/
diff --git a/src/mmvstr.h b/src/mmvstr.h
new file mode 100644
index 0000000..bc8203b
--- /dev/null
+++ b/src/mmvstr.h
@@ -0,0 +1,639 @@
+/*****************************************************************************/
+/*        Copyright (C) 2011  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMVSTR_H_
+#define METAMATH_MMVSTR_H_
+
+#include <stdio.h>
+
+/**************************************************************************//**
+ * \file mmvstr.h
+ * \brief VMS-BASIC variable length string library routines header
+ *
+ * Variable-length string handler
+ * ------------------------------
+ *
+ * This collection of string-handling functions emulate most of the
+ * string functions of [VMS BASIC][1]. The objects manipulated by these
+ * functions are strings of a special type called \ref vstring which have no
+ * pre-defined upper length limit but are dynamically allocated and
+ * deallocated as needed.  To use the vstring functions within a program,
+ * all vstrings must be initially set to the null string when declared or
+ * before first used, for example:
+ *
+ *     vstring_def(string1);
+ *     vstring stringArray[] = {"", "", ""};
+ *
+ *     vstring bigArray[100][10]; /- Must be initialized before using -/
+ *     int i, j;
+ *     for (i = 0; i < 100; i++)
+ *       for (j = 0; j < 10; j++)
+ *         bigArray[i][j] = ""; /- Initialize -/
+ *
+ *
+ *  After initialization, vstrings should be assigned with the `let(&`
+ * function only; for example the statements
+ *
+ *     let(&string1, "abc");
+ *     let(&string1, string2);
+ *     let(&string1, left(string2, 3));
+ *
+ * all assign the second argument to `string1`.  The \ref let function must
+ * _not_ be used to initialize a vstring the first time.
+ *
+ * Any local vstrings in a function must be deallocated before returning
+ * from the function, otherwise there will be memory leakage and eventual
+ * memory overflow.  To deallocate, assign the vstring to "" with
+ * \ref free_vstring :
+ *
+ *     void abc(void) {
+ *       vstring_def(xyz);
+ *       ...
+ *       free_vstring(xyz);
+ *     }
+ *
+ * The 'cat' function emulates the '+' concatenation operator in BASIC.
+ * It has a variable number of arguments, and the last argument should always
+ * be NULL.  For example,
+ *
+ *     let(&string1,cat("abc","def",NULL));
+ *
+ * assigns "abcdef" to `string1`.  Warning: 0 will work instead of NULL on the
+ * VAX but not on the Macintosh, so always use NULL.
+ *
+ * All other functions are generally used exactly like their BASIC
+ * equivalents.  For example, the BASIC statement
+ *
+ *     let string1$=left$("def",len(right$("xxx",2)))+"ghi"+string2$
+ *
+ * is emulated in C as
+ *
+ *     let(&string1,cat(left("def",len(right("xxx",2))),"ghi",string2,NULL));
+ *
+ * Note that ANSI C does not allow "$" as part of an identifier
+ * name, so the names in C have had the "$" suffix removed.
+ *
+ *      The string arguments of the vstring functions may be either standard C
+ * strings or vstrings (except that the first argument of the `let(&` function
+ * must be a vstring).  The standard C string functions may use vstrings or
+ * vstring functions as their string arguments, as long as the vstring variable
+ * itself (which is a char * pointer) is not modified and no attempt is made to
+ * increase the length of a vstring.  Caution must be exercised when
+ * assigning standard C string pointers to vstrings or the results of
+ * vstring functions, as the memory space may be deallocated when the
+ * `let(&` function is next executed.  For example,
+ *
+ *     char *stdstr; /- A standard c string pointer -/
+ *      ...
+ *     stdstr=left("abc",2);
+ *
+ * will assign "ab" to 'stdstr', but this assignment will be lost when the
+ * next 'let(&' function is executed.  To be safe, use 'strcpy':
+ *
+ *     char stdstr1[80]; /- A fixed length standard c string -/
+ *      ...
+ *     strcpy(stdstr1,left("abc",2));
+ *
+ * Here, of course, the user must ensure that the string copied to `stdstr1`
+ * does not exceed 79 characters in length.
+ *
+ * The vstring functions allocate temporary memory whenever they are called.
+ * This temporary memory is deallocated whenever a `let(&` assignment is
+ * made.  The user should be aware of this when using vstring functions
+ * outside of `let(&` assignments; for example
+ *
+ *     for (i=0; i<10000; i++)
+ *       print2("%s\n",left(string1,70));
+ *
+ * will allocate another 70 bytes or so of memory each pass through the loop.
+ * If necessary, \ref freeTempAlloc can be used periodically to clear
+ * this temporary memory:
+ *
+ *     for (i=0; i<10000; i++) {
+ *       print2("%s\n",left(string1,70));
+ *       freeTempAlloc();
+ *     }
+ *
+ * It should be noted that the \ref linput function assigns its target string
+ * with `let(&` and thus has the same effect as `let(&`.
+ *
+ * [1]: http://bitsavers.org/pdf/dec/vax/lang/basic/AA-HY16B-TE_VAX_BASIC_Reference_Manual_Feb90.pdf
+ *
+ *****************************************************************************/
+
+/*!
+ * \typedef vstring
+ * \brief contains NUL terminated,  character oriented data
+ *
+ * A vstring is like a C string, but it contains a control block allowing
+ * for memory allocation. New vstrings should always be constructed from the
+ * `vstring_def` macro.
+ *
+ * - A vstring is never NULL;
+ * - If the text is empty, i.e. the pointer points to the terminating 0x00
+ *   character, then its contents is not allocated memory and not mutable
+ *   instead;
+ * - If not empty, i.e. the pointer points to a character different from
+ *   0x00, then this is never a true left portion of another \ref vstring.
+ * - Although not required under all circumstances, it is highly recommended to
+ *   uniquely point to some allocated memory only.
+ *
+ * You can use a vstring to read the associated text, but you must never write
+ * to memory pointed to by a vstring directly, nor may you change the pointer's
+ * value.  Declaration, definition and write access to a vstring, or the text it
+ * points to, is exclusively done through dedicated functions.  Although the
+ * encoding of the text (or whatever data it is) requires only the existence of
+ * exactly one 0x00 at the end, using ASCII, or at least UTF-8, is recommended
+ * to allow various print instructions.
+ */
+typedef char* vstring;
+
+/*!
+ * \brief A vstring that is allocated in temporary storage.
+ *   These strings will be deallocated after the next call to `let`.
+ *
+ * This alias for \ref vstring is used to mark an entry in the
+ * \ref tempAllocStack.  Entries in this stack are subject to automatic
+ * deallocation by \ref let or \ref freeTempAlloc.
+ *
+ * Unlike \ref vstring this type knows no exceptional handling of empty
+ * strings.  If an empty string is generated as a temporary in the course of a
+ * construction of a final \ref vstring, it is allocated on the heap as usual.
+ *
+ * If returned by a function, it is already pushed on the \ref tempAllocStack.
+ *
+ * A `temp_vstring` should never be used as the first argument of a `let`.
+ * This code is INCORRECT:
+ *
+ *     temp_vstring foo = left("foobar", 3);
+ *     let(&foo, "bar"); // this will cause a double free
+ *
+ * It is okay (and quite common) to use a temp_vstring as the second argument,
+ * however. It is best not to hold on to the value, though, because the `let`
+ * will free it. This code is INCORRECT:
+ *
+ *     vstring_def(x);
+ *     temp_vstring foobar = cat("foo", "bar");
+ *     let(&x, foobar); // frees foobar
+ *     let(&x, foobar); // dangling reference
+ *
+ * There is a converse problem when `temp_vstring`s are used without `let`:
+ *
+ *     for (int i = 0; i < 100000; i++) {
+ *       vstring_def(x);
+ *       if (strlen(space(i)) == 99999) break;
+ *     }
+ *
+ * We don't need to deallocate the string returned by `space()` directly,
+ * because it returns a `temp_vstring`, but because there is no `let` in
+ * this function, we end up allocating a bunch of temporaries and
+ * effectively get a memory leak. (There is space for about 100
+ * temporaries so this loop will cause a crash.) To solve this problem,
+ * we can either use a dummy `let()` statement in the loop, or call
+ * `freeTempAlloc` directly:
+ *
+ *     for (int i = 0; i < 100000; i++) {
+ *       vstring_def(x);
+ *       if (strlen(space(i)) == 99999) break;
+ *       freeTempAlloc();
+ *     }
+ */
+typedef vstring temp_vstring;
+
+/*!
+ * \def vstring_def
+ * \brief creates a new \ref vstring variable.
+ *
+ * declares a \ref vstring variable and initiates it with empty text ("").
+ * If it remains unmodified, freeing of __x__ is possible, but not required.
+ *
+ * \param[in] x plain C variable name without quote characters.
+ * \pre
+ *   the variable has not been declared before in the current scope.
+ * \post
+ *   initialized with empty text.  No administrative data is added, in
+ *   conformance with the semantics of a \ref vstring.
+ */
+#define vstring_def(x) vstring x = ""
+
+/*!
+ * \def free_vstring
+ * \brief deallocates a \ref vstring variable and sets it to the empty string.
+ *
+ * Multiple invocations on the same variable is possible.  Can be reused again
+ * without a call to \ref vstring_def.
+ *
+ * Side effect: Frees and pops off entries on and beyond index
+ * \ref g_startTempAllocStack from the \ref tempAllocStack.
+ *
+ * \param[in,out] x (not null) an initialized \ref vstring variable.  According to the
+ * semantics of a \ref vstring, \p x is not deallocated, if it points to an
+ * empty string. 
+ * \pre
+ *   \p x was declared and initialized before.
+ * \post
+ *   \p x initialized with empty text.  Entries on and beyond index
+ * \ref g_startTempAllocStack are freed and popped off the \ref tempAllocStack.
+ */
+#define free_vstring(x) let(&x, "")
+
+/*!
+ * \fn freeTempAlloc
+ * \brief Free space allocated for temporary vstring instances.
+ *
+ * Temporary \ref vstring in \ref tempAllocStack are used for example to
+ * construct final text from patterns, boilerplate etc. along with data to be
+ * filled in.
+ *
+ * This function frees all entries beginning with \ref g_startTempAllocStack.
+ * It is usually called automatically by let(), but can also be invoked
+ * directly to avoid buildup of temporary strings.
+ *
+ * \pre
+ *   All references freed in \ref tempAllocStack can be safely discarded
+ *   without risking a memory leak.
+ * \post
+ * - Entries in \ref tempAllocStack from index \ref g_startTempAllocStack on
+ *   are freed.  The top of stack \ref g_tempAllocStackTop is back to
+ *   \ref g_startTempAllocStack again, so the current scope of temporaries is
+ *   empty;
+ * - db1 is updated, if NDEBUG is not defined.
+ */
+void freeTempAlloc(void);
+
+/*!
+ * \fn let(vstring *target, const char *source)
+ * \brief emulation of BASIC string assignment
+ *
+ * assigns to text to a \ref vstring pointer.  This includes a bit of memory
+ * management.  Not only is the space of the destination of the assignment
+ * reallocated if its previous size was too small.  But in addition the
+ * \ref pgStack "stack" \ref tempAllocStack is freed of intermediate values
+ * again.  Every entry on and beyond \ref g_startTempAllocStack is considered
+ * to be consumed and subject to deallocation.
+ *
+ * This deallocation procedure is embedded in this operation, since often the
+ * final string is composed of some fragments, that now can be disposed of.  In
+ * fact, this function must ALWAYS be called to assign to a vstring in order
+ * for the memory cleanup routines, etc. to work properly.  A new vstring
+ * should be initialized to "" (the empty string), and the \ref vstring_def
+ * macro handles creation of such variables.
+ *
+ * Possible failures: Out of memory condition.
+ *
+ * \param[in,out] target (not null) address of a \ref vstring receiving a copy of
+ *   the source string.  Its current value, if not empty, must never point to a
+ *   true tail of another \ref vstring.  You must not assign to any of the
+ *   temporary strings in \ref tempAllocStack.
+ * \param[in] source (not null) NUL terminated string to be copied from.
+ *
+ * \warning `source` must not point into `target` (but this is unlikely to arise if
+ *  `source` is calculated using `temp_vstring` operations from `target`).
+ * \pre
+ * - \ref g_startTempAllocStack contains the starting index of entries in
+ *   \ref tempAllocStack, that is going to be deallocated.
+ * - The \p target of this function must either be empty, or uniquely point
+ *   to a \ref vstring, but not to any of the \ref temp_vstring;
+ * - The \p target need not provide enough space for the source.  If
+ *   necessary, it is reallocated;
+ * \post
+ * - Entries in \ref tempAllocStack from \ref g_startTempAllocStack (on entry
+ *   to the function) are deallocated;
+ * - The stack pointer in \ref g_tempAllocStackTop is set to
+ *   \ref g_startTempAllocStack (on entry to the function);
+ * - If the assigned value is the empty string, but the \p target not, it is
+ *   freed and assigned to a constant "";
+ * - \ref db is updated.
+ * \bug In an out-of-memory situation the program is not exited
+ */
+void let(vstring *target, const char *source);
+
+/*!
+ * \fn temp_vstring cat(const char * string1, ...)
+ * \brief concatenates several NUL terminated strings to a single string.
+ *
+ * \note last argument MUST be NULL, e.g.
+ *   `let(&abc, cat("Hello", " ", left("worldx", 5), "!", NULL);`
+ *   Also the first string must not be `NULL`, i.e. `cat(NULL)` alone is invalid.
+ *
+ * Up to MAX_CAT_ARGS - 1 (49) NUL terminated strings submitted as parameters
+ * are concatenated to form a single NUL terminated string.  The parameters
+ * terminate with a NULL pointer, a single NULL pointer is not allowed, though.
+ * The resulting string is pushed on \ref tempAllocStack.
+ * \param[in] string1 (not null) a pointer to a NUL terminated string.
+ *   The following parameters are pointers to NUL terminated strings as well,
+ *   except for the last parameter that must be NULL.  It is allowed to
+ *   duplicate parameters.
+ * \return the concatenated \ref temp_vstring terminated by a NUL character.
+ * \post the resulting string is pushed onto the \ref tempAllocStack.
+ *   \ref db is updated.
+ * \bug a stack overflow of \ref tempAllocStack is not handled correctly.
+ */
+temp_vstring cat(const char * string1, ...);
+
+/*!
+ * Emulation of BASIC linput (line input) statement; returns NULL if EOF
+ * \note linput assigns target string with `let(&target,...)`
+ *
+ *     BASIC:  linput "what";a$
+ *     c:      linput(NULL,"what?",&a);
+ *
+ *     BASIC:  linput #1,a$                        (error trap on EOF)
+ *     c:      if (!linput(file1,NULL,&a)) break;  (break on EOF)
+ *
+ * \returns whether a (possibly empty) line was successfully read
+ */
+int linput(FILE *stream, const char *ask, vstring *target);
+
+/* Emulation of BASIC string functions */
+/* Indices are 1-based */
+
+/*!
+ * \fn temp_vstring seg(const char *sin, long start, long stop)
+ * Extracts a substring from a source and pushes it on \ref tempAllocStack.
+ * Note: The bounding indices are 1-based and inclusive.
+ *
+ * \param[in] sin (not null) pointer to the NUL-terminated source text.
+ * \param[in] start offset of the first byte of the substring, counted in bytes from
+ *   the first one of \p sin, a 1-based index.  A value less than 1 is
+ *   internally corrected to 1, but it must not point beyond the terminating
+ *   NUL of \p sin, if \p start <= \p stop.
+ * \param[in] stop offset of the last byte of the substring, counted in bytes from
+ *   the first one of \p sin, a 1-based index.  The natural bounds of this
+ *   value are \p start - 1 and the length of \p sin.  Values outside of this
+ *   range are internally corrected to the closer of these limits.  If \p stop
+ *   < \p start the empty string is returned.
+ * \attention the indices are 1-based: seg("hello", 2, 3) == "el"!
+ * \return a pointer to new allocated \ref temp_vstring referencing the
+ *   requested substring, that is also pushed onto the top of
+ *   \ref tempAllocStack
+ * \pre
+ *   \p start <= length(\p sin).
+ * \post
+ *   A pointer to the substring is pushed on \ref tempAllocStack, even if it
+ *   empty;
+ * \warning not UTF-8 safe.
+ * \bug a stack overflow of \ref tempAllocStack is not handled correctly;
+ */
+temp_vstring seg(const char *sin, long start, long stop);
+
+/*!
+ * \fn temp_vstring mid(const char *sin, long start, long length)
+ * Extracts a substring from a source and pushes it on \ref tempAllocStack
+ *
+ * \param[in] sin (not null) pointer to the NUL-terminated source text.
+ * \param[in] start offset of the substring in bytes from the first byte of
+ *   \p sin, 1-based.  A value less than 1 is internally corrected to 1, but it
+ *   must never point beyond the terminating NUL of \p sin.
+ * \param[in] length length of substring in bytes.  Negative values are
+ *   corrected to 0.  If \p start + \p length exceeds the length of \p sin,
+ *   then only the portion up to the terminating NUL is taken.
+ * \attention the index \p start is 1-based: mid("hello", 2, 1) == "e"!
+ * \return a pointer to new allocated \ref temp_vstring referencing the
+ *   requested substring, that is also pushed onto the top of
+ *   \ref tempAllocStack
+ * \pre
+ *   \p start <= length(\p sin).  This must hold even if the requested length is
+ *   0, because its implementation in C requires the validity of the pointer,
+ *   even if it is not dereferenced.
+ * \post
+ *   A pointer to the substring is pushed on \ref tempAllocStack, even if it
+ *   is empty;
+ * \warning not UTF-8 safe.
+ * \bug a stack overflow of \ref tempAllocStack is not handled correctly;
+ */
+temp_vstring mid(const char *sin, long start, long length);
+
+/*!
+ * \fn temp_vstring left(const char *sin, long n)
+ * \brief Extract leftmost n characters.
+ *
+ * Copies the leftmost n bytes of a NUL terminated string to a temporary.
+ * If the source contains UTF-8 encoded text, care has to be taken that a
+ * multi-byte character is not split in this process.
+ *
+ * \param[in] sin (not null) pointer to a NUL terminated string to be copied from.
+ * \param[in] n count of bytes to be copied from the source.  The natural bounds of
+ *   this value is 0 and the length of \p sin in bytes.  Any value outside of
+ *   this range is corrected to the closer one of these limits.
+ * \return a pointer to new allocated \ref temp_vstring referencing the
+ *   requested portion, that is also pushed onto the top of \ref tempAllocStack
+ * \post
+ *   A pointer to the substring is pushed on \ref tempAllocStack, even if it
+ *   is empty.
+ * \warning not UTF-8 safe.
+ * \bug a stack overflow of \ref tempAllocStack is not handled correctly.
+ */
+temp_vstring left(const char *sin, long n);
+
+/*!
+ * \fn temp_vstring right(const char *sin, long n)
+ * \brief Extract the substring starting with position n.
+ *
+ * Copies a NUL terminated string starting with the character at position __n__
+ * to a temporary.  If the source contains UTF-8 encoded text, care has to be
+ * taken that a multi-byte character is not split in this process.
+ *
+ * \param[in] sin (not null) pointer to a NUL terminated string to be copied
+ *   from.
+ * \param[in] n 1-based index of the first not skipped character at the
+ *   beginning of the source.  A value less than 1 is internally corrected to 1.
+ * \return a pointer to new allocated \ref temp_vstring referencing the
+ *   requested portion, that is also pushed onto the top of \ref tempAllocStack
+ * \attention the index \p n is 1-based: right("hello", 2) == "ello"!
+ * \pre
+ *   \p n <= length(\p sin)
+ * \post
+ *   A pointer to the substring is pushed on \ref tempAllocStack, even if it
+ *   is empty.
+ * \warning not UTF-8 safe.
+ * \bug a stack overflow of \a tempAllocStack is not handled correctly.
+ */
+temp_vstring right(const char *sin, long n);
+
+/*!
+ * \fn temp_vstring edit(const char *sin, long control)
+ * perform a combination of transformations on \p sin based on the set bits in
+ * \p control.  This is an ASCII based transformation.
+ *      Bit    |  Effect
+ *      -----  |  ------
+ *      1      |  Clear parity bits
+ *      2      |  Discard all spaces and tabs
+ *      4      |  Discard characters: CR, LF, FF, ESC, RUBOUT, and NULL
+ *      8      |  Discard leading spaces and tabs
+ *      16     |  Reduce spaces and tabs to one space
+ *      32     |  Convert lowercase to uppercase
+ *      64     |  Convert [ to ( and ] to )
+ *      128    |  Discard trailing spaces and tabs
+ *      256    |  Do not alter characters inside quotes
+ *      512    |  Convert uppercase to lowercase
+ *      1024   |  Tab the line (convert spaces to equivalent tabs)
+ *      2048   |  Untab the line (convert tabs to equivalent spaces)
+ *      4096   |  Convert VT220 screen print frame graphics to -,|,+ characters
+ *      8192   |  Discard CR only (to assist DOS-to-Unix conversion)
+ *      16384  |  Discard trailing spaces, tabs, and LFs
+ * \param[in] sin (not null) NUL terminated string to convert
+ * \param[in] control a combination of set bit requesting the desired
+ *   transformation(s)
+ * \return the transformed \p sin ready for pushing on \ref tempAllocStack.
+ */
+temp_vstring edit(const char *sin, long control);
+
+/*!
+ * \fn temp_vstring space(long n)
+ * pushes a NUL terminated string of \p n characters onto
+ * \ref tempAllocStack.
+ * \param[in] n the count of spaces, one less than the memory to allocate in
+ *   bytes.
+ * \return a pointer to new allocated \ref temp_vstring referencing the
+ *   requested contents, also pushed onto the top of \ref tempAllocStack
+ * \post The returned string is NUL terminated
+ * \bug a stack overflow of \ref tempAllocStack is not handled correctly.
+ */
+temp_vstring space(long n);
+
+/*!
+ * \fn temp_vstring string(long n, char c)
+ * pushes a NUL terminated string of \p n characters \p c onto
+ * \ref tempAllocStack.
+ * \param[in] n one less than the memory to allocate in bytes.
+ * \param[in] c character to fill the allocated memory with.  It is padded to
+ *   the right with a NUL character.
+ * \attention The choice of NUL for \p c returns the empty string in a block
+ *   of \p n + 1 allocated bytes.
+ * \post The returned string is NUL terminated
+ * \return a pointer to new allocated \ref temp_vstring referencing the
+ *   requested contents, also pushed onto the top of \ref tempAllocStack
+ * \bug a stack overflow of \ref tempAllocStack is not handled correctly.
+ */
+temp_vstring string(long n, char c);
+
+/*!
+ * \fn temp_vstring chr(long n)
+ * \brief create a temporary string containing a single byte.
+ *
+ * create a NUL terminated string containing only the least significant byte of
+ * \p n.  If this byte is 0x00, an empty string is the result.  If the most
+ * significant bit of this byte is set, the returned string is not an ASCII
+ * or UTF-8 string.
+ * \param[in] n The eight least significant bits are converted into a
+ *   character used to build a string from.
+ * \post A string containing a single character different from NUL, or
+ *   the empty string else, is pushed onto the \ref tempAllocStack.
+ * \return a pointer to new allocated \ref temp_vstring referencing the
+ *   requested contents, also pushed onto the top of \ref tempAllocStack
+ * \warning
+ *   the resulting string need not contain exactly 1 character, and if it does,
+ *   this character need not be ASCII or UTF-8.  If CHAR_BITS is not 8
+ *   (extremely rare nowadays) there might be a portability issue.
+ * \bug a stack overflow of \ref tempAllocStack is not handled correctly.
+ */
+temp_vstring chr(long n);
+
+temp_vstring xlate(const char *sin, const char *table);
+
+temp_vstring date(void);
+
+temp_vstring time_(void);
+
+temp_vstring num(double x);
+
+temp_vstring num1(double x);
+
+temp_vstring str(double x);
+
+long len(const char *s);
+
+/*!
+ * \fn long instr(long start, const char *string, const char *match)
+ * Search for \p match in \p string starting at \p start.
+ * \param[in] start 1-based position (including) the search begins.  A value <=
+ *   0 is corrected to 1.  A value beyond the terminating NUL in \p string
+ *   is corrected to the terminating NUL.
+ * \param[in] string NUL-terminated string to be searched.
+ * \param[in] match NUL-terminated match to be found in \p string.  If
+ *   this is the empty string, the length of \p string is returned.
+ * \return the 1-based position of the first hit, or 0 if not found.
+ */
+long instr(long start, const char *string, const char *match);
+
+long rinstr(const char *string1, const char *string2);
+
+long ascii_(const char *c);
+
+double val(const char *s);
+
+
+/* Emulation of Progress 4GL string functions */
+
+temp_vstring entry(long element, const char *list);
+
+long lookup(const char *expression, const char *list);
+
+long numEntries(const char *list);
+
+long entryPosition(long element, const char *list);
+
+
+/* Routines may/may not be written (lowest priority):
+vstring place$(vstring sout);
+vstring prod$(vstring sout);
+vstring quo$(vstring sout);
+*/
+
+/******* Special purpose routines for better
+      memory allocation (use with caution) *******/
+
+/*!
+ * \var g_tempAllocStackTop
+ * \brief Top of stack for temporary text.
+ *
+ * Refers to the \ref pgStack "stack" in \ref tempAllocStack for temporary
+ * text.  The current top index referencing the next free entry is kept in
+ * this variable.
+ *
+ * This value is made public for setting up scopes of temporary memory for
+ * nested functions.  Each such function allocates/frees scratch memory
+ * independently.  Once a nested function is done, the caller's context must
+ * be restored again, so it sees "its" temporaries untempered with.  To this
+ * end the called nested function saves administrative stack data.  Upon finish
+ * it restores those values.
+ */
+extern long g_tempAllocStackTop;   /* Top of stack for tempAlloc function */
+
+/*!
+ * \var g_startTempAllocStack
+ * \brief references the first entry of the current scope of temporaries.
+ *
+ * Refers to the \ref pgStack "stack" in \ref tempAllocStack for temporary
+ * text.  Nested functions maintain their own scope of temporary data.  The
+ * index referencing the first index of the current scope is kept in this
+ * variable.
+ *
+ * This value is made public for setting up scopes of temporary memory for
+ * nested functions.  Each such function allocates/frees scratch memory
+ * independently.  Once a nested function is done, the caller's context must
+ * be restored again, so it sees "its" temporaries untempered with.  To this
+ * end the called nested function saves administrative stack data.  Upon finish
+ * it restores those values.
+ *
+ * \invariant
+ *   \ref g_startTempAllocStack <= \ref g_tempAllocStackTop.
+ */
+extern long g_startTempAllocStack; /* Where to start freeing temporary allocation
+    when let() is called (normally 0, except for nested vstring functions) */
+
+/*! \brief Make string have temporary allocation to be released by next let().
+
+  This function effectively changes the type of `s`
+  from `vstring` (an owned pointer) to `temp_vstring` (a temporary to be
+  freed by the next `let()`). See `temp_vstring` for information on what
+  you can do with temporary strings.
+  In particular, after makeTempAlloc() is called, the vstring may NOT be
+  assigned again with let(). */
+temp_vstring makeTempAlloc(vstring s);
+
+#endif /* METAMATH_MMVSTR_H_ */
diff --git a/mmword.c b/src/mmword.c
similarity index 87%
rename from mmword.c
rename to src/mmword.c
index 0497007..a068db4 100644
--- a/mmword.c
+++ b/src/mmword.c
@@ -1,612 +1,613 @@
-/*****************************************************************************/
-/*        Copyright (C) 2012  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* This file implements the UPDATE command of the TOOLS utility.  revise() is
-   the main external call; see the comments preceding it. */
-
-/* The UPDATE command of TOOLS (mmword.c) was custom-written in accordance
-   with the version control requirements of a company that used it.  It
-   documents the differences between two versions of a program as C-style
-   comments embedded in the newer version.  The best way to determine whether
-   it suits your similar needs is just to run it and look at its output. */
-
-/* Very old history: this was called mmword.c because it was intended to
-   produce RTF output for Word, analogous to the existing LaTeX output.
-   Microsoft never responded to a request for the RTF specification, as they
-   promised in the circa 1990 manual accompanying Word.  Thus it remained an
-   empty shell.  When the need for UPDATE arose, the mmword.c shell was used in
-   order to avoid the nuisance of changing some compilation setups and scripts
-   existing at that time. */
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <setjmp.h>
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mminou.h"
-#include "mmword.h"
-
-/* Set to 79, 80, etc. - length of line after tag is added */
-#define LINE_LENGTH 80
-
-
-   /* 7000 ! ***** "DIFF" Option ***** from DO LIST */
-/*  gosub_7000(f1_name, f2_name, f3_name, &f3_fp, m); */
-
-
-char strcmpe(vstring s1, vstring s2);
-vstring stripAndTag(vstring line, vstring tag, flag tagBlankLines);
-
-long r0,r1,r2,i0,i1_,i2_,d,t,i9;
-FILE *f1_fp_;
-FILE *f2_fp_;
-FILE *f3_fp_;
-char eof1, eof2;
-vstring ctlz_ = "";
-vstring l1_ = "";
-vstring l2_ = "";
-vstring tmp_ = "";
-vstring tmpLine = "";
-vstring addTag_ = "";
-vstring delStartTag_ = "";
-vstring delEndTag_ = "";
-flag printedAtLeastOne;
-     /*  Declare input and save buffers */
-#define MAX_LINES 10000
-#define MAX_BUF 1000
-     vstring line1_[MAX_LINES];
-     vstring line2_[MAX_LINES];
-     vstring reserve1_[MAX_BUF];
-     vstring reserve2_[MAX_BUF];
-
-
-/* revise() is called by the UPDATE command of TOOLs.  The idea is to
-   keep all past history of a file in the file itself, in the form of
-   comments.  In mmcmds.c, see the parsing of the UPDATE command for a
-   partial explanation of its arguments.  UPDATE was written for a
-   proprietary language with C-style comments (where nested comments were
-   allowed) and it may not be generally useful without some modification. */
-void revise(FILE *f1_fp, FILE *f2_fp, FILE *f3_fp, vstring addTag, long m)
-{
-  /******** Figure out the differences (DO LIST subroutine) ******/
-  vstring blanksPrefix = "";
-  long tmpi;
-  long i, j;
-
-  f1_fp_ = f1_fp;
-  f2_fp_ = f2_fp;
-  f3_fp_ = f3_fp;
-  let(&addTag_, addTag);
-  let(&delStartTag_, "/******* Start of deleted section *******");
-  let(&delEndTag_, "******* End of deleted section *******/");
-
-
-  /* Initialize vstring arrays */
-  for (i = 0; i < MAX_LINES; i++) {
-    line1_[i] = "";
-    line2_[i] = "";
-  }
-  for (i = 0; i < MAX_BUF; i++) {
-    reserve1_[i] = "";
-    reserve2_[i] = "";
-  }
-
-  if (m < 1) m = 1;
-
-  r0=r1=r2=i0=i1_=i2_=d=t=i=j=i9=0;
-
-
-  let(&ctlz_,chr(26));  /* End-of-file character */
-
-  let(&l1_,ctlz_);
-  let(&l2_,ctlz_);
-  eof1=eof2=0;  /* End-of-file flags */
-  d=0;
-
-l7100:  /*
-          Lines 7100 through 7300 are a modified version of the compare loop
-          In DEC's FILCOM.BAS program.
-        */
-
-        if (!strcmpe(l1_,l2_)) { /* The lines are the same */
-                if (strcmpe(l2_,ctlz_)) {
-                  fprintf(f3_fp_, "%s\n", l2_); /* Use edited version
-                                of line when they are the same */
-                }
-                gosub_7320();
-                gosub_7330();
-                if (strcmpe(l1_,ctlz_) || strcmpe(l2_,ctlz_) ) {
-                        goto l7100;
-                } else {
-                        fclose(f1_fp_);
-                        fclose(f2_fp_);
-                        fclose(f3_fp_);
-
-                      /* Deallocate string memory */
-                      for (i = 0; i < MAX_LINES; i++) {
-                        let(&(line1_[i]), "");
-                        let(&(line2_[i]), "");
-                      }
-                      for (i = 0; i < MAX_BUF; i++) {
-                        let(&(reserve1_[i]), "");
-                        let(&(reserve2_[i]), "");
-                      }
-                      let(&ctlz_, "");
-                      let(&l1_, "");
-                      let(&l2_, "");
-                      let(&tmp_, "");
-                      let(&tmpLine, "");
-                      let(&addTag_, "");
-                      let(&delStartTag_, "");
-                      let(&delEndTag_, "");
-                      let(&blanksPrefix, "");
-
-                        return;
-                }
-        }
-        d=d+1;  /* Number of difference sections found so far
-                         (For user information only) */
-        i1_=i2_=m-1;
-        let(&line1_[0],l1_);
-        let(&line2_[0],l2_);
-        for (i0 = 1; i0 < m; i0++) {
-          gosub_7320();
-          let(&line1_[i0],l1_);
-        }
-        for (i0 = 1; i0 < m; i0++) {
-          gosub_7330();
-          let(&line2_[i0],l2_);
-        }
-l7130:  gosub_7320();
-        i1_=i1_+1;
-        if (i1_ >= MAX_LINES) {
-          printf("*** FATAL *** Overflow#1\n");
-#if __STDC__
-          fflush(stdout);
-#endif
-          exit(0);
-        }
-        let(&line1_[i1_],l1_);
-        t=0;
-        i=0;
-l7140:  if (strcmpe(line1_[i1_+t-m+1], line2_[i+t])) {
-                t=0;
-        } else {
-
-                /* If lines "match", ensure we use the EDITED version for the
-                   final output */
-                let(&line1_[i1_+t-m+1], line2_[i+t]);
-
-                t=t+1;
-                if (t==m) {
-                        goto l7200;
-                } else {
-                        goto l7140;
-                }
-        }
-
-        i=i+1;
-        if (i<=i2_-m+1) {
-                goto l7140;
-        }
-        gosub_7330();
-        i2_=i2_+1;
-        if (i2_ >= MAX_LINES) {
-          printf("*** FATAL *** Overflow#2\n");
-#if __STDC__
-          fflush(stdout);
-#endif
-          exit(0);
-        }
-        let(&line2_[i2_],l2_);
-        t=0;
-        i=0;
-l7170:
-        if (strcmpe(line1_[i+t], line2_[i2_+t-m+1])) {
-                t=0;
-        } else {
-
-                /* If lines "match", ensure we use the EDITED version for the
-                   final output */
-                let(&line1_[i+t], line2_[i2_+t-m+1]);
-
-                t=t+1;
-                if (t==m) {
-                        goto l7220;
-                } else {
-                        goto l7170;
-                }
-        }
-        i=i+1;
-        if (i<=i1_-m+1) {
-                goto l7170;
-        }
-        goto l7130;
-l7200:  i=i+m-1;
-        if (r2) {
-          for (j=r2-1; j>=0; j--) {
-                let(&reserve2_[j+i2_-i],reserve2_[j]);
-          }
-        }
-        for (j=1; j<=i2_-i; j++) {
-          let(&reserve2_[j-1],line2_[j+i]);
-        }
-        r2=r2+i2_-i;
-        if (r2 >= MAX_BUF) {
-          printf("*** FATAL *** Overflow#3\n");
-#if __STDC__
-          fflush(stdout);
-#endif
-          exit(0);
-        }
-        i2_=i;
-        goto l7240;
-l7220:  i=i+m-1;
-        if (r1) {
-          for (j=r1-1; j>=0; j--) {
-                let(&reserve1_[j+i1_-i],reserve1_[j]);
-          }
-        }
-
-        for (j=1; j<=i1_-i; j++) {
-          let(&reserve1_[j-1],line1_[j+i]);
-        }
-        r1=r1+i1_-i;
-        if (r1 >= MAX_BUF) {
-          printf("*** FATAL *** Overflow#4\n");
-#if __STDC__
-          fflush(stdout);
-#endif
-          exit(0);
-        }
-        i1_=i;
-        goto l7240;
-l7240: /* */
-
-       printedAtLeastOne = 0;
-       for (i=0; i<=i1_-m; i++) {
-         if (strcmpe(line1_[i],ctlz_)) {
-           if (!printedAtLeastOne) {
-             printedAtLeastOne = 1;
-
-             /* Put out any blank lines before delStartTag_ */
-             while (((vstring)(line1_[i]))[0] == '\n') {
-               fprintf(f3_fp_, "\n");
-               let(&(line1_[i]), right(line1_[i], 2));
-             }
-
-             /* Find the beginning blank space */
-             tmpi = 0;
-             while (((vstring)(line1_[i]))[tmpi] == ' ') tmpi++;
-             let(&blanksPrefix, space(tmpi));
-             let(&tmpLine, "");
-             tmpLine = stripAndTag(cat(blanksPrefix, delStartTag_, NULL),
-                 addTag_, 0);
-             fprintf(f3_fp_, "%s\n", tmpLine);
-           }
-           fprintf(f3_fp_, "%s\n", line1_[i]);
-                                     /* Output original deleted lines */
-           /*let(&tmp_, "");*/ /* Clear vstring stack */
-         }
-       }
-       if (printedAtLeastOne) {
-         let(&tmpLine, "");
-         tmpLine = stripAndTag(cat(blanksPrefix, delEndTag_, NULL), addTag_
-             ,0);
-         fprintf(f3_fp_, "%s\n", tmpLine);
-       }
-       for (i=0; i<=i1_-m; i++) {
-         if (i<=i2_-m) {
-           if (strcmpe(line2_[i],ctlz_)) {
-             let(&tmpLine, "");
-             if (i == 0) {
-               tmpLine = stripAndTag(line2_[i], addTag_, 0);
-             } else {
-               /* Put tags on blank lines *inside* of new section */
-               tmpLine = stripAndTag(line2_[i], addTag_, 1);
-             }
-             fprintf(f3_fp_, "%s\n", tmpLine);
-                                     /* Output tagged edited lines */
-             /*let(&tmp_, "");*/ /* Clear vstring stack */
-           }
-         }
-       }
-       for (i=i1_-m+1; i<=i2_-m; i++) {
-         if (strcmpe(line2_[i],ctlz_)) {
-           let(&tmpLine, "");
-           if (i == 0) {
-             tmpLine = stripAndTag(line2_[i], addTag_, 0);
-           } else {
-             /* Put tags on blank lines *inside* of new section */
-             tmpLine = stripAndTag(line2_[i], addTag_, 1);
-           }
-           fprintf(f3_fp_, "%s\n", tmpLine);
-                                     /* Print remaining edited lines */
-           /*let(&tmp_, "");*/ /* Clear vstring stack */
-         }
-       }
-       for (i=0; i<=m-1; i++) {
-         let(&l1_,line1_[i1_-m+1+i]);
-         if (strcmpe(l1_,ctlz_)) {
-           fprintf(f3_fp_,"%s\n",l1_);  /*  Print remaining matching lines */
-         }
-       }
-
-       let(&l1_,ctlz_);
-       let(&l2_,ctlz_);
-       goto l7100;
-
-}
-
-
-
-void gosub_7320()
-{
-        /* Subroutine:  get next L1_ from original file */
-  vstring tmpLin = "";
-  if (r1) {     /*  Get next line from save array */
-    let(&l1_,reserve1_[0]);
-    r1=r1-1;
-    for (i9=0; i9<=r1-1; i9++) {
-      let(&reserve1_[i9],reserve1_[i9+1]);
-    }
-  } else {              /* Get next line from input file */
-    if (eof1) {
-      let(&l1_,ctlz_);
-    } else {
-     next_l1:
-      if (!linput(f1_fp_,NULL,&l1_)) { /*linput returns 0 if EOF */
-        eof1 = 1;
-        /* Note that we will discard any blank lines before EOF; this
-           should be OK though */
-        let(&l1_,ctlz_);
-        let(&tmpLin, ""); /* Deallocate */
-        return;
-      }
-      let(&l1_, edit(l1_, 4 + 128 + 2048)); /* Trim garb, trail space; untab */
-      if (!l1_[0]) { /* Ignore blank lines for comparison */
-        let(&tmpLin, cat(tmpLin, "\n", NULL)); /* Blank line */
-        goto next_l1;
-      }
-    }
-  }
-  let(&l1_, cat(tmpLin, l1_, NULL)); /* Add any blank lines */
-  let(&tmpLin, ""); /* Deallocate */
-  return;
-}
-
-void gosub_7330() {
-        /*  Subroutine:  get next L2_ from edited file */
-  vstring tmpLin = "";
-  vstring tmpStrPtr; /* pointer only */
-  flag stripDeletedSectionMode;
-  if (r2) {     /*  Get next line from save array */
-    let(&l2_,reserve2_[0]);
-    r2=r2-1;
-    for (i9 = 0; i9 < r2; i9++) {
-      let(&reserve2_[i9],reserve2_[i9+1]);
-    }
-  } else {              /*  Get next line from input file */
-    if (eof2) {
-      let(&l2_,ctlz_);
-    } else {
-     stripDeletedSectionMode = 0;
-     next_l2:
-      if (!linput(f2_fp_,NULL,&l2_)) { /* linput returns 0 if EOF */
-        eof2 = 1;
-        /* Note that we will discard any blank lines before EOF; this
-           should be OK though */
-        let(&l2_, ctlz_);
-        let(&tmpLin, ""); /* Deallocate */
-        return;
-      }
-      let(&l2_, edit(l2_, 4 + 128 + 2048)); /* Trim garb, trail space; untab */
-      if (!strcmp(edit(delStartTag_, 2), left(edit(l2_, 2 + 4),
-          (long)strlen(edit(delStartTag_, 2))))) {
-        if (getRevision(l2_) == getRevision(addTag_)) {
-          /* We should strip out deleted section from previous run */
-          /* (The diff algorithm will put them back from orig. file) */
-          stripDeletedSectionMode = 1;
-          goto next_l2;
-        }
-      }
-      if (stripDeletedSectionMode) {
-        if (!strcmp(edit(delEndTag_, 2), left(edit(l2_, 2 + 4),
-            (long)strlen(edit(delEndTag_, 2))))  &&
-            getRevision(l2_) == getRevision(addTag_) ) {
-          stripDeletedSectionMode = 0;
-        }
-        goto next_l2;
-      }
-
-      /* Strip off tags that match *this* revision (so previous out-of-sync
-         runs will be corrected) */
-      if (getRevision(l2_) == getRevision(addTag_)) {
-        tmpStrPtr = l2_;
-        l2_ = stripAndTag(l2_, "", 0);
-        let(&tmpStrPtr, ""); /* deallocate old l2_ */
-      }
-
-      if (!l2_[0]) { /* Ignore blank lines for comparison */
-        let(&tmpLin, cat(tmpLin, "\n", NULL)); /* Blank line */
-        goto next_l2;
-      }
-    }
-  }
-  let(&l2_, cat(tmpLin, l2_, NULL)); /* Add any blank lines */
-  let(&tmpLin, ""); /* Deallocate */
-  return;
-
-}
-
-
-/* Return 0 if difference lines are the same, non-zero otherwise */
-char strcmpe(vstring s1, vstring s2)
-{
-  flag cmpflag;
-
-  /* Option flags - make global if we want to use them */
-  flag ignoreSpaces = 1;
-  flag ignoreSameLineComments = 1;
-
-  vstring tmps1 = "";
-  vstring tmps2 = "";
-  long i;
-  long i2;
-  long i3;
-  let(&tmps1, s1);
-  let(&tmps2, s2);
-
-  if (ignoreSpaces) {
-    let(&tmps1, edit(tmps1, 2 + 4));
-    let(&tmps2, edit(tmps2, 2 + 4));
-  }
-
-  if (ignoreSameLineComments) {
-    while (1) {
-      i = instr(1, tmps1, "/*");
-      if (i == 0) break;
-      i2 = instr(i + 2, tmps1, "*/");
-      if (i2 == 0) break;
-      i3 = instr(i + 2, tmps1, "/*");
-      if (i3 != 0 && i3 < i2) break; /*i = i3;*/ /* Nested comment */
-      if (i2 - i > 7) break; /* only ignore short comments (tags) */
-      let(&tmps1, cat(left(tmps1, i - 1), right(tmps1, i2 + 2), NULL));
-    }
-    while (1) {
-      i = instr(1, tmps2, "/*");
-      if (i == 0) break;
-      i2 = instr(i + 2, tmps2, "*/");
-      if (i2 == 0) break;
-      i3 = instr(i + 2, tmps2, "/*");
-      if (i3 != 0 && i3 < i2) break; /*i = i3;*/ /* Nested comment */
-      if (i2 - i > 7) break; /* only ignore short comments (tags) */
-      let(&tmps2, cat(left(tmps2, i - 1), right(tmps2, i2 + 2), NULL));
-    }
-  }
-
-  cmpflag = !!strcmp(tmps1, tmps2);
-  let(&tmps1, ""); /* Deallocate string */
-  let(&tmps2, ""); /* Deallocate string */
-  return (cmpflag);
-}
-
-
-/* Strip any old tag from line and put new tag on it */
-/* (Caller must deallocate returned string) */
-vstring stripAndTag(vstring line, vstring tag, flag tagBlankLines)
-{
-  long i, j, k, n;
-  vstring line1 = "", comment = "";
-  long lineLength = LINE_LENGTH;
-  flag validTag;
-  i = 0;
-  let(&line1, edit(line, 128 + 2048)); /* Trim trailing spaces and untab */
-  /* Get last comment on line */
-  while (1) {
-    j = instr(i + 1, line1, "/*");
-    if (j == 0) break;
-    i = j;
-  }
-  j = instr(i, line1, "*/");
-  if (i && j == (signed)(strlen(line1)) - 1) {
-    let(&comment, seg(line1, i + 2, j - 1));
-    validTag = 1;
-    for (k = 0; k < (signed)(strlen(comment)); k++) {
-      /* Check for valid characters that can appear in a tag */
-      if (instr(1, " 1234567890#", mid(comment, k + 1, 1))) continue;
-      validTag = 0;
-      break;
-    }
-    if (validTag) let(&line1, edit(left(line1, i - 1), 128));
-    let(&comment, ""); /* deallocate */
-  }
-
-  /* Count blank lines concatenated to the beginning of this line */
-  n = 0;
-  while (line1[n] == '\n') n++;
-
-  /* Add the tag */
-  if (tag[0]) { /* Non-blank tag */
-    if ((long)strlen(line1) - n < lineLength - 1 - (long)strlen(tag))
-      let(&line1, cat(line1,
-          space(lineLength - 1 - (long)strlen(tag) - (long)strlen(line1) + n),
-          NULL));
-    let(&line1, cat(line1, " ", tag, NULL));
-    if ((signed)(strlen(line1)) - n > lineLength) {
-      print2(
-"Warning: The following line has > %ld characters after tag is added:\n",
-          lineLength);
-      print2("%s\n", line1);
-    }
-  }
-
-  /* Add tags to blank lines if tagBlankLines is set */
-  /* (Used for blank lines inside of new edited file sections) */
-  if (tagBlankLines && n > 0) {
-    let(&line1, right(line1, n + 1));
-    for (i = 1; i <= n; i++) {
-      let(&line1, cat(space(lineLength - (long)strlen(tag)), tag, "\n",
-          line1, NULL));
-    }
-  }
-
-  return line1;
-}
-
-/* Get the largest revision number tag in a file */
-/* Tags are assumed to be of format nn or #nn in comment at end of line */
-/* Used to determine default argument for tag question */
-long highestRevision(vstring fileName)
-{
-  vstring str1 = "";
-  long revision;
-  long largest = 0;
-  FILE *fp;
-
-  fp = fopen(fileName, "r");
-  if (!fp) return 0;
-  while (linput(fp, NULL, &str1)) {
-    revision = getRevision(str1);
-    if (revision > largest) largest = revision;
-  }
-  let(&str1, "");
-  fclose(fp);
-  return largest;
-}
-
-/* Get numeric revision from the tag on a line (returns 0 if none) */
-/* Tags are assumed to be of format nn or #nn in comment at end of line */
-long getRevision(vstring line)
-{
-  vstring str1 = "";
-  vstring str2 = "";
-  vstring tag = "";
-  long revision;
-
-  if (instr(1, line, "/*") == 0) return 0; /* Speedup - no comment in line */
-  let(&str1, edit(line, 2)); /* This line has the tag not stripped */
-  let(&str2, "");
-  str2 = stripAndTag(str1, "", 0); /* Strip old tag & add dummy new one */
-  let(&str2, edit(str2, 2)); /* This line has the tag stripped */
-  if (!strcmp(str1, str2)) {
-    revision = 0;  /* No tag */
-  } else {
-    let(&tag, edit(seg(str1, (long)strlen(str2) + 3,
-        (long)strlen(str1) - 2), 2));
-    if (tag[0] == '#') let(&tag, right(tag, 2)); /* Remove any # */
-    revision = (long)(val(tag));
-  }
-  let(&tag, "");
-  let(&str1, "");
-  let(&str2, "");
-  return revision;
-}
-
+/*****************************************************************************/
+/*        Copyright (C) 2012  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* This file implements the UPDATE command of the TOOLS utility.  revise() is
+   the main external call; see the comments preceding it. */
+
+/* The UPDATE command of TOOLS (mmword.c) was custom-written in accordance
+   with the version control requirements of a company that used it.  It
+   documents the differences between two versions of a program as C-style
+   comments embedded in the newer version.  The best way to determine whether
+   it suits your similar needs is just to run it and look at its output. */
+
+/* Very old history: this was called mmword.c because it was intended to
+   produce RTF output for Word, analogous to the existing LaTeX output.
+   Microsoft never responded to a request for the RTF specification, as they
+   promised in the circa 1990 manual accompanying Word.  Thus it remained an
+   empty shell.  When the need for UPDATE arose, the mmword.c shell was used in
+   order to avoid the nuisance of changing some compilation setups and scripts
+   existing at that time. */
+
+#include <string.h>
+#include <stdlib.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mminou.h"
+#include "mmword.h"
+
+/* Set to 79, 80, etc. - length of line after tag is added */
+#define LINE_LENGTH 80
+
+
+   /* 7000 ! ***** "DIFF" Option ***** from DO LIST */
+/*  gosub_7000(f1_name, f2_name, f3_name, &f3_fp, m); */
+
+
+char strcmpe(vstring s1, vstring s2);
+vstring stripAndTag(vstring line, vstring tag, flag tagBlankLines);
+
+long r0,r1,r2,i0,i1_,i2_,d,t,i9;
+FILE *f1_fp_;
+FILE *f2_fp_;
+FILE *f3_fp_;
+char eof1, eof2;
+vstring_def(ctlz_);
+vstring_def(l1_);
+vstring_def(l2_);
+vstring_def(tmp_);
+vstring_def(tmpLine);
+vstring_def(addTag_);
+vstring_def(delStartTag_);
+vstring_def(delEndTag_);
+flag printedAtLeastOne;
+     /*  Declare input and save buffers */
+#define MAX_LINES 10000
+#define MAX_BUF 1000
+     vstring line1_[MAX_LINES];
+     vstring line2_[MAX_LINES];
+     vstring reserve1_[MAX_BUF];
+     vstring reserve2_[MAX_BUF];
+
+
+/* These two functions emulate 2 GOSUBs in BASIC, that are part of a
+   translation of a very old BASIC program (by nm) that implemented a
+   difference algorithm (like Unix diff). */
+void gosub_7320(void);
+void gosub_7330(void);
+
+/* revise() is called by the UPDATE command of TOOLs.  The idea is to
+   keep all past history of a file in the file itself, in the form of
+   comments.  In mmcmds.c, see the parsing of the UPDATE command for a
+   partial explanation of its arguments.  UPDATE was written for a
+   proprietary language with C-style comments (where nested comments were
+   allowed) and it may not be generally useful without some modification. */
+void revise(FILE *f1_fp, FILE *f2_fp, FILE *f3_fp, vstring addTag, long m)
+{
+  /******** Figure out the differences (DO LIST subroutine) ******/
+  vstring_def(blanksPrefix);
+  long tmpi;
+  long i, j;
+
+  f1_fp_ = f1_fp;
+  f2_fp_ = f2_fp;
+  f3_fp_ = f3_fp;
+  let(&addTag_, addTag);
+  let(&delStartTag_, "/******* Start of deleted section *******");
+  let(&delEndTag_, "******* End of deleted section *******/");
+
+
+  /* Initialize vstring arrays */
+  for (i = 0; i < MAX_LINES; i++) {
+    line1_[i] = "";
+    line2_[i] = "";
+  }
+  for (i = 0; i < MAX_BUF; i++) {
+    reserve1_[i] = "";
+    reserve2_[i] = "";
+  }
+
+  if (m < 1) m = 1;
+
+  r0=r1=r2=i0=i1_=i2_=d=t=i=j=i9=0;
+
+
+  let(&ctlz_,chr(26));  /* End-of-file character */
+
+  let(&l1_,ctlz_);
+  let(&l2_,ctlz_);
+  eof1=eof2=0;  /* End-of-file flags */
+  d=0;
+
+l7100:  /*
+          Lines 7100 through 7300 are a modified version of the compare loop
+          In DEC's FILCOM.BAS program.
+        */
+
+        if (!strcmpe(l1_,l2_)) { /* The lines are the same */
+                if (strcmpe(l2_,ctlz_)) {
+                  fprintf(f3_fp_, "%s\n", l2_); /* Use edited version
+                                of line when they are the same */
+                }
+                gosub_7320();
+                gosub_7330();
+                if (strcmpe(l1_,ctlz_) || strcmpe(l2_,ctlz_) ) {
+                        goto l7100;
+                } else {
+                        fclose(f1_fp_);
+                        fclose(f2_fp_);
+                        fclose(f3_fp_);
+
+                      /* Deallocate string memory */
+                      for (i = 0; i < MAX_LINES; i++) {
+                        free_vstring(line1_[i]);
+                        free_vstring(line2_[i]);
+                      }
+                      for (i = 0; i < MAX_BUF; i++) {
+                        free_vstring(reserve1_[i]);
+                        free_vstring(reserve2_[i]);
+                      }
+                      free_vstring(ctlz_);
+                      free_vstring(l1_);
+                      free_vstring(l2_);
+                      free_vstring(tmp_);
+                      free_vstring(tmpLine);
+                      free_vstring(addTag_);
+                      free_vstring(delStartTag_);
+                      free_vstring(delEndTag_);
+                      free_vstring(blanksPrefix);
+
+                        return;
+                }
+        }
+        d=d+1;  /* Number of difference sections found so far
+                         (For user information only) */
+        i1_=i2_=m-1;
+        let(&line1_[0],l1_);
+        let(&line2_[0],l2_);
+        for (i0 = 1; i0 < m; i0++) {
+          gosub_7320();
+          let(&line1_[i0],l1_);
+        }
+        for (i0 = 1; i0 < m; i0++) {
+          gosub_7330();
+          let(&line2_[i0],l2_);
+        }
+l7130:  gosub_7320();
+        i1_=i1_+1;
+        if (i1_ >= MAX_LINES) {
+          printf("*** FATAL *** Overflow#1\n");
+#if __STDC__
+          fflush(stdout);
+#endif
+          exit(0);
+        }
+        let(&line1_[i1_],l1_);
+        t=0;
+        i=0;
+l7140:  if (strcmpe(line1_[i1_+t-m+1], line2_[i+t])) {
+                t=0;
+        } else {
+
+                /* If lines "match", ensure we use the EDITED version for the
+                   final output */
+                let(&line1_[i1_+t-m+1], line2_[i+t]);
+
+                t=t+1;
+                if (t==m) {
+                        goto l7200;
+                } else {
+                        goto l7140;
+                }
+        }
+
+        i=i+1;
+        if (i<=i2_-m+1) {
+                goto l7140;
+        }
+        gosub_7330();
+        i2_=i2_+1;
+        if (i2_ >= MAX_LINES) {
+          printf("*** FATAL *** Overflow#2\n");
+#if __STDC__
+          fflush(stdout);
+#endif
+          exit(0);
+        }
+        let(&line2_[i2_],l2_);
+        t=0;
+        i=0;
+l7170:
+        if (strcmpe(line1_[i+t], line2_[i2_+t-m+1])) {
+                t=0;
+        } else {
+
+                /* If lines "match", ensure we use the EDITED version for the
+                   final output */
+                let(&line1_[i+t], line2_[i2_+t-m+1]);
+
+                t=t+1;
+                if (t==m) {
+                        goto l7220;
+                } else {
+                        goto l7170;
+                }
+        }
+        i=i+1;
+        if (i<=i1_-m+1) {
+                goto l7170;
+        }
+        goto l7130;
+l7200:  i=i+m-1;
+        if (r2) {
+          for (j=r2-1; j>=0; j--) {
+                let(&reserve2_[j+i2_-i],reserve2_[j]);
+          }
+        }
+        for (j=1; j<=i2_-i; j++) {
+          let(&reserve2_[j-1],line2_[j+i]);
+        }
+        r2=r2+i2_-i;
+        if (r2 >= MAX_BUF) {
+          printf("*** FATAL *** Overflow#3\n");
+#if __STDC__
+          fflush(stdout);
+#endif
+          exit(0);
+        }
+        i2_=i;
+        goto l7240;
+l7220:  i=i+m-1;
+        if (r1) {
+          for (j=r1-1; j>=0; j--) {
+                let(&reserve1_[j+i1_-i],reserve1_[j]);
+          }
+        }
+
+        for (j=1; j<=i1_-i; j++) {
+          let(&reserve1_[j-1],line1_[j+i]);
+        }
+        r1=r1+i1_-i;
+        if (r1 >= MAX_BUF) {
+          printf("*** FATAL *** Overflow#4\n");
+#if __STDC__
+          fflush(stdout);
+#endif
+          exit(0);
+        }
+        i1_=i;
+        goto l7240;
+l7240: /* */
+
+       printedAtLeastOne = 0;
+       for (i=0; i<=i1_-m; i++) {
+         if (strcmpe(line1_[i],ctlz_)) {
+           if (!printedAtLeastOne) {
+             printedAtLeastOne = 1;
+
+             /* Put out any blank lines before delStartTag_ */
+             while (((vstring)(line1_[i]))[0] == '\n') {
+               fprintf(f3_fp_, "\n");
+               let(&(line1_[i]), right(line1_[i], 2));
+             }
+
+             /* Find the beginning blank space */
+             tmpi = 0;
+             while (((vstring)(line1_[i]))[tmpi] == ' ') tmpi++;
+             let(&blanksPrefix, space(tmpi));
+             free_vstring(tmpLine);
+             tmpLine = stripAndTag(cat(blanksPrefix, delStartTag_, NULL),
+                 addTag_, 0);
+             fprintf(f3_fp_, "%s\n", tmpLine);
+           }
+           fprintf(f3_fp_, "%s\n", line1_[i]);
+                                     /* Output original deleted lines */
+           /*freeTempAlloc();*/ /* Clear vstring stack */
+         }
+       }
+       if (printedAtLeastOne) {
+         free_vstring(tmpLine);
+         tmpLine = stripAndTag(cat(blanksPrefix, delEndTag_, NULL), addTag_
+             ,0);
+         fprintf(f3_fp_, "%s\n", tmpLine);
+       }
+       for (i=0; i<=i1_-m; i++) {
+         if (i<=i2_-m) {
+           if (strcmpe(line2_[i],ctlz_)) {
+             free_vstring(tmpLine);
+             if (i == 0) {
+               tmpLine = stripAndTag(line2_[i], addTag_, 0);
+             } else {
+               /* Put tags on blank lines *inside* of new section */
+               tmpLine = stripAndTag(line2_[i], addTag_, 1);
+             }
+             fprintf(f3_fp_, "%s\n", tmpLine);
+                                     /* Output tagged edited lines */
+             /*freeTempAlloc();*/ /* Clear vstring stack */
+           }
+         }
+       }
+       for (i=i1_-m+1; i<=i2_-m; i++) {
+         if (strcmpe(line2_[i],ctlz_)) {
+           free_vstring(tmpLine);
+           if (i == 0) {
+             tmpLine = stripAndTag(line2_[i], addTag_, 0);
+           } else {
+             /* Put tags on blank lines *inside* of new section */
+             tmpLine = stripAndTag(line2_[i], addTag_, 1);
+           }
+           fprintf(f3_fp_, "%s\n", tmpLine);
+                                     /* Print remaining edited lines */
+           /*freeTempAlloc();*/ /* Clear vstring stack */
+         }
+       }
+       for (i=0; i<=m-1; i++) {
+         let(&l1_,line1_[i1_-m+1+i]);
+         if (strcmpe(l1_,ctlz_)) {
+           fprintf(f3_fp_,"%s\n",l1_);  /*  Print remaining matching lines */
+         }
+       }
+
+       let(&l1_,ctlz_);
+       let(&l2_,ctlz_);
+       goto l7100;
+
+}
+
+
+
+void gosub_7320() {
+        /* Subroutine:  get next L1_ from original file */
+  vstring_def(tmpLin);
+  if (r1) {     /*  Get next line from save array */
+    let(&l1_,reserve1_[0]);
+    r1=r1-1;
+    for (i9=0; i9<=r1-1; i9++) {
+      let(&reserve1_[i9],reserve1_[i9+1]);
+    }
+  } else {              /* Get next line from input file */
+    if (eof1) {
+      let(&l1_,ctlz_);
+    } else {
+     next_l1:
+      if (!linput(f1_fp_,NULL,&l1_)) { /*linput returns 0 if EOF */
+        eof1 = 1;
+        /* Note that we will discard any blank lines before EOF; this
+           should be OK though */
+        let(&l1_,ctlz_);
+        free_vstring(tmpLin); /* Deallocate */
+        return;
+      }
+      let(&l1_, edit(l1_, 4 + 128 + 2048)); /* Trim garb, trail space; untab */
+      if (!l1_[0]) { /* Ignore blank lines for comparison */
+        let(&tmpLin, cat(tmpLin, "\n", NULL)); /* Blank line */
+        goto next_l1;
+      }
+    }
+  }
+  let(&l1_, cat(tmpLin, l1_, NULL)); /* Add any blank lines */
+  free_vstring(tmpLin); /* Deallocate */
+  return;
+}
+
+void gosub_7330() {
+        /*  Subroutine:  get next L2_ from edited file */
+  vstring_def(tmpLin);
+  vstring tmpStrPtr; /* pointer only */
+  flag stripDeletedSectionMode;
+  if (r2) {     /*  Get next line from save array */
+    let(&l2_,reserve2_[0]);
+    r2=r2-1;
+    for (i9 = 0; i9 < r2; i9++) {
+      let(&reserve2_[i9],reserve2_[i9+1]);
+    }
+  } else {              /*  Get next line from input file */
+    if (eof2) {
+      let(&l2_,ctlz_);
+    } else {
+     stripDeletedSectionMode = 0;
+     next_l2:
+      if (!linput(f2_fp_,NULL,&l2_)) { /* linput returns 0 if EOF */
+        eof2 = 1;
+        /* Note that we will discard any blank lines before EOF; this
+           should be OK though */
+        let(&l2_, ctlz_);
+        free_vstring(tmpLin); /* Deallocate */
+        return;
+      }
+      let(&l2_, edit(l2_, 4 + 128 + 2048)); /* Trim garb, trail space; untab */
+      if (!strcmp(edit(delStartTag_, 2), left(edit(l2_, 2 + 4),
+          (long)strlen(edit(delStartTag_, 2))))) {
+        if (getRevision(l2_) == getRevision(addTag_)) {
+          /* We should strip out deleted section from previous run */
+          /* (The diff algorithm will put them back from orig. file) */
+          stripDeletedSectionMode = 1;
+          goto next_l2;
+        }
+      }
+      if (stripDeletedSectionMode) {
+        if (!strcmp(edit(delEndTag_, 2), left(edit(l2_, 2 + 4),
+            (long)strlen(edit(delEndTag_, 2))))  &&
+            getRevision(l2_) == getRevision(addTag_) ) {
+          stripDeletedSectionMode = 0;
+        }
+        goto next_l2;
+      }
+
+      /* Strip off tags that match *this* revision (so previous out-of-sync
+         runs will be corrected) */
+      if (getRevision(l2_) == getRevision(addTag_)) {
+        tmpStrPtr = l2_;
+        l2_ = stripAndTag(l2_, "", 0);
+        free_vstring(tmpStrPtr); /* deallocate old l2_ */
+      }
+
+      if (!l2_[0]) { /* Ignore blank lines for comparison */
+        let(&tmpLin, cat(tmpLin, "\n", NULL)); /* Blank line */
+        goto next_l2;
+      }
+    }
+  }
+  let(&l2_, cat(tmpLin, l2_, NULL)); /* Add any blank lines */
+  free_vstring(tmpLin); /* Deallocate */
+  return;
+
+}
+
+
+/* Return 0 if difference lines are the same, non-zero otherwise */
+char strcmpe(vstring s1, vstring s2)
+{
+  flag cmpflag;
+
+  /* Option flags - make global if we want to use them */
+  flag ignoreSpaces = 1;
+  flag ignoreSameLineComments = 1;
+
+  vstring_def(tmps1);
+  vstring_def(tmps2);
+  long i;
+  long i2;
+  long i3;
+  let(&tmps1, s1);
+  let(&tmps2, s2);
+
+  if (ignoreSpaces) {
+    let(&tmps1, edit(tmps1, 2 + 4));
+    let(&tmps2, edit(tmps2, 2 + 4));
+  }
+
+  if (ignoreSameLineComments) {
+    while (1) {
+      i = instr(1, tmps1, "/*");
+      if (i == 0) break;
+      i2 = instr(i + 2, tmps1, "*/");
+      if (i2 == 0) break;
+      i3 = instr(i + 2, tmps1, "/*");
+      if (i3 != 0 && i3 < i2) break; /*i = i3;*/ /* Nested comment */
+      if (i2 - i > 7) break; /* only ignore short comments (tags) */
+      let(&tmps1, cat(left(tmps1, i - 1), right(tmps1, i2 + 2), NULL));
+    }
+    while (1) {
+      i = instr(1, tmps2, "/*");
+      if (i == 0) break;
+      i2 = instr(i + 2, tmps2, "*/");
+      if (i2 == 0) break;
+      i3 = instr(i + 2, tmps2, "/*");
+      if (i3 != 0 && i3 < i2) break; /*i = i3;*/ /* Nested comment */
+      if (i2 - i > 7) break; /* only ignore short comments (tags) */
+      let(&tmps2, cat(left(tmps2, i - 1), right(tmps2, i2 + 2), NULL));
+    }
+  }
+
+  cmpflag = !!strcmp(tmps1, tmps2);
+  free_vstring(tmps1); /* Deallocate string */
+  free_vstring(tmps2); /* Deallocate string */
+  return (cmpflag);
+}
+
+
+/* Strip any old tag from line and put new tag on it */
+/* (Caller must deallocate returned string) */
+vstring stripAndTag(vstring line, vstring tag, flag tagBlankLines)
+{
+  long i, j, k, n;
+  vstring_def(line1);
+  vstring_def(comment);
+  long lineLength = LINE_LENGTH;
+  flag validTag;
+  i = 0;
+  let(&line1, edit(line, 128 + 2048)); /* Trim trailing spaces and untab */
+  /* Get last comment on line */
+  while (1) {
+    j = instr(i + 1, line1, "/*");
+    if (j == 0) break;
+    i = j;
+  }
+  j = instr(i, line1, "*/");
+  if (i && j == (signed)(strlen(line1)) - 1) {
+    let(&comment, seg(line1, i + 2, j - 1));
+    validTag = 1;
+    for (k = 0; k < (signed)(strlen(comment)); k++) {
+      /* Check for valid characters that can appear in a tag */
+      if (instr(1, " 1234567890#", mid(comment, k + 1, 1))) continue;
+      validTag = 0;
+      break;
+    }
+    if (validTag) let(&line1, edit(left(line1, i - 1), 128));
+    free_vstring(comment); /* deallocate */
+  }
+
+  /* Count blank lines concatenated to the beginning of this line */
+  n = 0;
+  while (line1[n] == '\n') n++;
+
+  /* Add the tag */
+  if (tag[0]) { /* Non-blank tag */
+    if ((long)strlen(line1) - n < lineLength - 1 - (long)strlen(tag))
+      let(&line1, cat(line1,
+          space(lineLength - 1 - (long)strlen(tag) - (long)strlen(line1) + n),
+          NULL));
+    let(&line1, cat(line1, " ", tag, NULL));
+    if ((signed)(strlen(line1)) - n > lineLength) {
+      print2(
+"Warning: The following line has > %ld characters after tag is added:\n",
+          lineLength);
+      print2("%s\n", line1);
+    }
+  }
+
+  /* Add tags to blank lines if tagBlankLines is set */
+  /* (Used for blank lines inside of new edited file sections) */
+  if (tagBlankLines && n > 0) {
+    let(&line1, right(line1, n + 1));
+    for (i = 1; i <= n; i++) {
+      let(&line1, cat(space(lineLength - (long)strlen(tag)), tag, "\n",
+          line1, NULL));
+    }
+  }
+
+  return line1;
+}
+
+/* Get the largest revision number tag in a file */
+/* Tags are assumed to be of format nn or #nn in comment at end of line */
+/* Used to determine default argument for tag question */
+long highestRevision(vstring fileName)
+{
+  vstring_def(str1);
+  long revision;
+  long largest = 0;
+  FILE *fp;
+
+  fp = fopen(fileName, "r");
+  if (!fp) return 0;
+  while (linput(fp, NULL, &str1)) {
+    revision = getRevision(str1);
+    if (revision > largest) largest = revision;
+  }
+  free_vstring(str1);
+  fclose(fp);
+  return largest;
+}
+
+/* Get numeric revision from the tag on a line (returns 0 if none) */
+/* Tags are assumed to be of format nn or #nn in comment at end of line */
+long getRevision(vstring line)
+{
+  vstring_def(str1);
+  vstring_def(str2);
+  vstring_def(tag);
+  long revision;
+
+  if (instr(1, line, "/*") == 0) return 0; /* Speedup - no comment in line */
+  let(&str1, edit(line, 2)); /* This line has the tag not stripped */
+  free_vstring(str2);
+  str2 = stripAndTag(str1, "", 0); /* Strip old tag & add dummy new one */
+  let(&str2, edit(str2, 2)); /* This line has the tag stripped */
+  if (!strcmp(str1, str2)) {
+    revision = 0;  /* No tag */
+  } else {
+    let(&tag, edit(seg(str1, (long)strlen(str2) + 3,
+        (long)strlen(str1) - 2), 2));
+    if (tag[0] == '#') let(&tag, right(tag, 2)); /* Remove any # */
+    revision = (long)(val(tag));
+  }
+  free_vstring(tag);
+  free_vstring(str1);
+  free_vstring(str2);
+  return revision;
+}
+
diff --git a/mmword.h b/src/mmword.h
similarity index 51%
rename from mmword.h
rename to src/mmword.h
index 1d4182a..d1da82b 100644
--- a/mmword.h
+++ b/src/mmword.h
@@ -1,33 +1,30 @@
-/*****************************************************************************/
-/*        Copyright (C) 2012  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-#ifndef METAMATH_MMWORD_H_
-#define METAMATH_MMWORD_H_
-
-#include "mmvstr.h"
-
-/* Tag file changes with revision number tags */
-void revise(FILE *f1_fp, FILE *f2_fp, FILE *f3_fp, vstring addTag, long m);
-
-
-/* Get the largest revision number tag in a file */
-/* Tags are assumed to be of format nn or #nn in comment at end of line */
-/* Used to determine default argument for tag question */
-long highestRevision(vstring fileName);
-
-
-/* Get numeric revision from the tag on a line (returns 0 if none) */
-/* Tags are assumed to be of format nn or #nn in comment at end of line */
-long getRevision(vstring line);
-
-
-/* These two functions emulate 2 GOSUBs in BASIC, that are part of a
-   translation of a very old BASIC program (by nm) that implemented a
-   difference algorithm (like Unix diff). */
-void gosub_7320(void);
-void gosub_7330(void);
-
-#endif /* METAMATH_MMWORD_H_ */
+/*****************************************************************************/
+/*        Copyright (C) 2012  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMWORD_H_
+#define METAMATH_MMWORD_H_
+
+/*! \file */
+
+#include "mmvstr.h"
+
+/*! Tag file changes with revision number tags */
+void revise(FILE *f1_fp, FILE *f2_fp, FILE *f3_fp, vstring addTag, long m);
+
+
+/*! \brief Get the largest revision number tag in a file
+
+   Tags are assumed to be of format nn or #nn in comment at end of line.
+   Used to determine default argument for tag question. */
+long highestRevision(vstring fileName);
+
+
+/*! \brief Get numeric revision from the tag on a line (returns 0 if none)
+
+   Tags are assumed to be of format nn or #nn in comment at end of line */
+long getRevision(vstring line);
+
+#endif /* METAMATH_MMWORD_H_ */
diff --git a/mmwtex.c b/src/mmwtex.c
similarity index 65%
rename from mmwtex.c
rename to src/mmwtex.c
index 03eabdd..55f38bc 100644
--- a/mmwtex.c
+++ b/src/mmwtex.c
@@ -1,7003 +1,5656 @@
-/*****************************************************************************/
-/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
-/*            License terms:  GNU General Public License                     */
-/*****************************************************************************/
-/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
-
-/* This module processes LaTeX and HTML output. */
-
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include "mmutil.h"
-#include "mmvstr.h"
-#include "mmdata.h"
-#include "mminou.h"
-#include "mmpars.h" /* For rawSourceError and mathSrchCmp and lookupLabel */
-#include "mmwtex.h"
-#include "mmcmdl.h" /* For g_texFileName */
-
-/* 6/27/99 - Now, all LaTeX and HTML definitions are taken from the source
-   file (read in the by READ... command).  In the source file, there should
-   be a single comment $( ... $) containing the keyword $t.  The definitions
-   start after the $t and end at the $).  Between $t and $), the definition
-   source should exist.  See the file set.mm for an example. */
-
-flag g_oldTexFlag = 0; /* Use TeX macros in output (obsolete) */
-
-flag g_htmlFlag = 0;  /* HTML flag: 0 = TeX, 1 = HTML */
-flag g_altHtmlFlag = 0;  /* Use "althtmldef" instead of "htmldef".  This is
-    intended to allow the generation of pages with the Unicode font
-    instead of the individual GIF files. */
-flag g_briefHtmlFlag = 0;  /* Output statement lists only, for statement display
-                in other HTML pages, such as the Proof Explorer home page */
-long g_extHtmlStmt = 0; /* At this statement and above, use the exthtmlxxx
-    variables for title, links, etc.  This was put in to allow proper
-    generation of the Hilbert Space Explorer extension to the set.mm
-    database. */
-
-/* 5-Aug-2020 nm */
-/* Globals to hold mathbox information.  They should be re-initialized
-   by the ERASE command (eraseSource()).  g_mathboxStmt = 0 indicates
-   it and the other variables haven't been initialized. */
-long g_mathboxStmt = 0; /* At this statement and above, use SANDBOX_COLOR
-    background for theorem, mmrecent, & mmbiblio lists */
-    /* 0 means it hasn't been looked up yet; g_statements + 1 means
-       there is no mathbox */
-long g_mathboxes = 0; /* # of mathboxes */
-/* The following 3 strings are 0-based e.g. g_mathboxStart[0] is for
-   mathbox #1 */
-nmbrString *g_mathboxStart = NULL_NMBRSTRING; /* Start stmt vs. mathbox # */
-nmbrString *g_mathboxEnd = NULL_NMBRSTRING; /* End stmt vs. mathbox # */
-pntrString *g_mathboxUser = NULL_PNTRSTRING; /* User name vs. mathbox # */
-
-/* This is the list of characters causing the space before the opening "`"
-   in a math string in a comment to be removed for HTML output. */
-#define OPENING_PUNCTUATION "(['\""
-/* This is the list of characters causing the space after the closing "`"
-   in a math string in a comment to be removed for HTML output. */
-#define CLOSING_PUNCTUATION ".,;)?!:]'\"_-"
-
-/* Tex output file */
-FILE *g_texFilePtr = NULL;
-flag g_texFileOpenFlag = 0;
-
-/********* 15-Aug-2020 nm Obsolete
-/@ Tex dictionary @/
-FILE *tex_dict_fp;
-vstring tex_dict_fn = "";
-********/
-
-/* Global variables */
-flag g_texDefsRead = 0;
-struct texDef_struct *g_TexDefs; /* 27-Oct-2012 nm Made global for "erase" */
-
-/* Variables local to this module (except some $t variables) */
-long numSymbs;
-#define DOLLAR_SUBST 2
-/*char dollarSubst = 2;*/
-    /* Substitute character for $ in converting to obsolete $l,$m,$n
-       comments - Use '$' instead of non-printable ASCII 2 for debugging */
-
-/* Variables set by the language in the set.mm etc. $t statement */
-/* Some of these are global; see mmwtex.h */
-vstring g_htmlCSS = ""; /* Set by g_htmlCSS commands */  /* 14-Jan-2016 nm */
-vstring g_htmlFont = ""; /* Set by htmlfont commands */  /* 14-Jan-2016 nm */
-vstring g_htmlVarColor = ""; /* Set by htmlvarcolor commands */
-vstring htmlExtUrl = ""; /* Set by htmlexturl command */
-vstring htmlTitle = ""; /* Set by htmltitle command */
-  /* 16-Aug-2016 nm */
-  vstring htmlTitleAbbr = ""; /* Extracted from htmlTitle */
-vstring g_htmlHome = ""; /* Set by htmlhome command */
-  /* 16-Aug-2016 nm */
-  /* Future - assign these in the $t set.mm comment instead of g_htmlHome */
-  vstring g_htmlHomeHREF = ""; /* Extracted from g_htmlHome */
-  vstring g_htmlHomeIMG = ""; /* Extracted from g_htmlHome */
-vstring g_htmlBibliography = ""; /* Optional; set by htmlbibliography command */
-vstring extHtmlLabel = ""; /* Set by exthtmllabel command - where extHtml starts */
-vstring g_extHtmlTitle = ""; /* Set by exthtmltitle command (global!) */
-  vstring g_extHtmlTitleAbbr = ""; /* Extracted from htmlTitle */
-vstring extHtmlHome = ""; /* Set by exthtmlhome command */
-  /* 16-Aug-2016 nm */
-  /* Future - assign these in the $t set.mm comment instead of g_htmlHome */
-  vstring extHtmlHomeHREF = ""; /* Extracted from extHtmlHome */
-  vstring extHtmlHomeIMG = ""; /* Extracted from extHtmlHome */
-vstring extHtmlBibliography = ""; /* Optional; set by exthtmlbibliography command */
-vstring htmlDir = ""; /* Directory for GIF version, set by htmldir command */
-vstring altHtmlDir = ""; /* Directory for Unicode Font version, set by
-                            althtmldir command */
-
-/* 29-Jul-2008 nm Sandbox stuff */
-vstring sandboxHome = "";
-  vstring sandboxHomeHREF = ""; /* Extracted from extHtmlHome */
-  vstring sandboxHomeIMG = ""; /* Extracted from extHtmlHome */
-vstring sandboxTitle = "";
-  /* 16-Aug-2016 nm */
-  vstring sandboxTitleAbbr = "";
-
-/* Variables holding all HTML <a name..> tags from bibiography pages  */
-vstring g_htmlBibliographyTags = "";
-vstring extHtmlBibliographyTags = "";
-
-
-/* 17-Nov-2015 nm Added */
-void eraseTexDefs(void) {
-  /* Deallocate the texdef/htmldef storage */ /* Added 27-Oct-2012 nm */
-  long i;
-  if (g_texDefsRead) {  /* If not (already deallocated or never allocated) */
-    g_texDefsRead = 0;
-
-    for (i = 0; i < numSymbs; i++) {  /* Deallocate structure member i */
-      let(&(g_TexDefs[i].tokenName), "");
-      let(&(g_TexDefs[i].texEquiv), "");
-    }
-    free(g_TexDefs); /* Deallocate the structure */
-  }
-  return;
-} /* eraseTexDefs */
-
-
-/* Returns 2 if there were severe parsing errors, 1 if there were warnings but
-   no errors, 0 if no errors or warnings */
-flag readTexDefs(
-  flag errorsOnly,  /* 1 = suppress non-error messages */
-  flag noGifCheck   /* 1 = don't check for missing GIFs */)
-{
-
-  char *fileBuf;
-  char *startPtr;
-  long lineNumOffset = 0;
-  char *fbPtr;
-  char *tmpPtr;
-  char *tmpPtr2;
-  long charCount;
-  long i, j, k, p;
-  long lineNum;
-  long tokenLength;
-  char zapChar;
-  long cmd;
-  long parsePass;
-  vstring token = "";
-  vstring partialToken = ""; /* 6-Aug-2011 nm */
-  FILE *tmpFp;
-  static flag saveHtmlFlag = -1; /* -1 to force 1st read */  /* 17-Nov-2015 nm */
-  static flag saveAltHtmlFlag = 1; /* -1 to force 1st read */ /* 17-Nov-2015 nm */
-  flag warningFound = 0; /* 1 if a warning was found */
-  char *dollarTStmtPtr = NULL; /* Pointer to label section of statement with
-                              the $t comment */ /* 2-Feb-2018 nm */
-
-  /* bsearch returned values for use in error-checking */
-  void *g_mathKeyPtr; /* bsearch returned value for math symbol lookup */
-  void *texDefsPtr; /* For binary search */
-
-  /* 17-Nov-2015 nm */
-  /* if (g_texDefsRead) { */
-  if (saveHtmlFlag != g_htmlFlag || saveAltHtmlFlag != g_altHtmlFlag
-      || !g_texDefsRead) {   /* 3-Jan-2016 nm */
-    /* One or both changed - we need to erase and re-read */
-    eraseTexDefs();
-    saveHtmlFlag = g_htmlFlag; /* Save for next call to readTexDefs() */
-    saveAltHtmlFlag = g_altHtmlFlag;  /* Save for next call to readTexDefs() */
-    if (g_htmlFlag == 0 /* Tex */ && g_altHtmlFlag == 1) {
-      bug(2301); /* Nonsensical combination */
-    }
-  } else {
-    /* Nothing changed; don't need to read again */
-    return 0; /* No errors */
-  }
-  /* } */
-
-  /* Initial values below will be overridden if a user assignment exists in the
-     $t comment of the xxx.mm input file */
-  let(&htmlTitle, "Metamath Test Page"); /* Set by htmltitle command in $t
-                                                 comment */
-  /* let(&g_htmlHome, "<A HREF=\"http://metamath.org\">Home</A>"); */
-  /* 16-Aug-2016 nm */
-  let(&g_htmlHome, cat("<A HREF=\"mmset.html\"><FONT SIZE=-2 FACE=sans-serif>",
-    "<IMG SRC=\"mm.gif\" BORDER=0 ALT=",
-    "\"Home\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE STYLE=\"margin-bottom:0px\">",
-    "Home</FONT></A>", NULL));
-                                     /* Set by htmlhome command in $t comment */
-
-  if (errorsOnly == 0) {
-    print2("Reading definitions from $t statement of %s...\n", g_input_fn);
-  }
-  /******* 10/14/02 Rewrote this section so xxx.mm is not read again *******/
-  /* Find the comment with the $t */
-  fileBuf = ""; /* This used to point to the input file buffer of an external
-                   latex.def file; now it's from the xxx.mm $t comment, so we
-                   make it a normal string */
-  let(&fileBuf, "");
-  /* Note that g_Statement[g_statements + 1] is a special (empty) statement whose
-     labelSection holds any comment after the last statement. */
-  for (i = 1; i <= g_statements + 1; i++) {
-    /* We do low-level stuff on the xxx.mm input file buffer for speed */
-    tmpPtr = g_Statement[i].labelSectionPtr;
-    j = g_Statement[i].labelSectionLen;
-    /* Note that for g_Statement[g_statements + 1], the lineNum is one plus the
-       number of lines in the file */
-    if (!fileBuf[0]) lineNumOffset = g_Statement[i].lineNum; /* Save for later */
-        /* (Don't save if we're in scan to end for double $t detection) */
-    zapChar = tmpPtr[j]; /* Save the original character */
-    tmpPtr[j] = 0; /* Create an end-of-string */
-    if (instr(1, tmpPtr, "$t")) {
-      /* Found a $t comment */
-      /* Make sure this isn't a second one in another statement */
-      /* (A second one in the labelSection of one statement will trigger
-         an error below.) */
-      if (fileBuf[0]) {
-        print2(
-  "?Error: There are two comments containing a $t keyword in \"%s\".\n",
-            g_input_fn);
-        let(&fileBuf, "");
-        return 2;
-      }
-      let(&fileBuf, tmpPtr);
-      tmpPtr[j] = zapChar;
-      dollarTStmtPtr = g_Statement[i].labelSectionPtr; /* 2-Feb-2018 nm */
-      /* break; */ /* Continue to end to detect double $t */
-    }
-    tmpPtr[j] = zapChar; /* Restore the xxx.mm input file buffer */
-  } /* next i */
-  /* If the $t wasn't found, fileBuf will be "", causing error message below. */
-  /* Compute line number offset of beginning of g_Statement[i].labelSection for
-     use in error messages */
-  j = (long)strlen(fileBuf);
-  for (i = 0; i < j; i++) {
-    if (fileBuf[i] == '\n') lineNumOffset--;
-  }
-  /******* End of 10/14/02 rewrite *******/
-
-
-#define LATEXDEF 1
-#define HTMLDEF 2
-#define HTMLVARCOLOR 3
-#define HTMLTITLE 4
-#define HTMLHOME 5
-/* Added 12/27/01 */
-#define ALTHTMLDEF 6
-#define EXTHTMLTITLE 7
-#define EXTHTMLHOME 8
-#define EXTHTMLLABEL 9
-#define HTMLDIR 10
-#define ALTHTMLDIR 11
-/* Added 10/9/02 */
-#define HTMLBIBLIOGRAPHY 12
-#define EXTHTMLBIBLIOGRAPHY 13
-/* Added 14-Jan-2016 nm */
-#define HTMLCSS 14
-#define HTMLFONT 15
-/* Added 26-Dec-2020 nm */
-#define HTMLEXTURL 16
-
-  startPtr = fileBuf;
-
-  /* 6/27/99 Find $t command */
-  while (1) {
-    if (startPtr[0] == '$') {
-      if (startPtr[1] == 't') {
-        startPtr++;
-        break;
-      }
-    }
-    if (startPtr[0] == 0) break;
-    startPtr++;
-  }
-  if (startPtr[0] == 0) {
-    print2("?Error: There is no $t command in the file \"%s\".\n", g_input_fn);
-    print2(
-"The file should have exactly one comment of the form $(...$t...$) with\n");
-    print2("the LaTeX and HTML definitions between $t and $).\n");
-    let(&fileBuf, "");  /* was: free(fileBuf); */
-    return 2;
-  }
-  startPtr++; /* Move to 1st char after $t */
-
-  /* 6/27/99 Search for the ending $) and zap the $) to be end-of-string */
-  tmpPtr = startPtr;
-  while (1) {
-    if (tmpPtr[0] == '$') {
-      if (tmpPtr[1] == ')') {
-        break;
-      }
-    }
-    if (tmpPtr[0] == 0) break;
-    tmpPtr++;
-  }
-  if (tmpPtr[0] == 0) {
-    print2(
-  "?Error: There is no $) comment closure after the $t keyword in \"%s\".\n",
-        g_input_fn);
-    let(&fileBuf, "");  /* was: free(fileBuf); */
-    return 2;
-  }
-
-  /* 10/9/02 Make sure there aren't two comments with $t commands */
-  tmpPtr2 = tmpPtr;
-  while (1) {
-    if (tmpPtr2[0] == '$') {
-      if (tmpPtr2[1] == 't') {
-        print2(
-  "?Error: There are two comments containing a $t keyword in \"%s\".\n",
-            g_input_fn);
-        let(&fileBuf, "");  /* was: free(fileBuf); */
-        return 2;
-      }
-    }
-    if (tmpPtr2[0] == 0) break;
-    tmpPtr2++;
-  }
-
-   /* Force end of string at the $ in $) */
-  tmpPtr[0] = '\n';
-  tmpPtr[1] = 0;
-
-  charCount = tmpPtr + 1 - fileBuf; /* For bugcheck */
-
-  for (parsePass = 1; parsePass <= 2; parsePass++) {
-    /* Pass 1 - Count the number of symbols defined and alloc g_TexDefs array */
-    /* Pass 2 - Assign the texDefs array */
-    numSymbs = 0;
-    fbPtr = startPtr;
-
-    while (1) {
-
-      /* Get next token */
-      fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
-      tokenLength = texDefTokenLen(fbPtr);
-
-      /* Process token - command */
-      if (!tokenLength) break; /* End of file */
-      zapChar = fbPtr[tokenLength]; /* Char to restore after zapping source */
-      fbPtr[tokenLength] = 0; /* Create end of string */
-      cmd = lookup(fbPtr,
-          "latexdef,htmldef,htmlvarcolor,htmltitle,htmlhome"
-          ",althtmldef,exthtmltitle,exthtmlhome,exthtmllabel,htmldir"
-          ",althtmldir,htmlbibliography,exthtmlbibliography"
-          ",htmlcss,htmlfont,htmlexturl"   /* 26-Dec-2020 nm */
-          );
-      fbPtr[tokenLength] = zapChar;
-      if (cmd == 0) {
-        lineNum = lineNumOffset;
-        for (i = 0; i < (fbPtr - fileBuf); i++) {
-          if (fileBuf[i] == '\n') lineNum++;
-        }
-        rawSourceError(/*fileBuf*/g_sourcePtr,  /* 2-Feb-2018 nm */
-            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf), tokenLength,
-            /*lineNum, g_input_fn,*/   /* 2-Feb-2018 nm These arguments are now
-                computed in rawSourceError() */
-            cat("Expected \"latexdef\", \"htmldef\", \"htmlvarcolor\",",
-            " \"htmltitle\", \"htmlhome\", \"althtmldef\",",
-            " \"exthtmltitle\", \"exthtmlhome\", \"exthtmllabel\",",
-            " \"htmldir\", \"althtmldir\",",
-            " \"htmlbibliography\", \"exthtmlbibliography\",",
-            " \"htmlcss\", \"htmlfont\",",    /* 14-Jan-2016 nm */
-            " or \"htmlexturl\" here.",       /* 26-Dec-2020 nm */
-            NULL));
-        let(&fileBuf, "");  /* was: free(fileBuf); */
-        return 2;
-      }
-      fbPtr = fbPtr + tokenLength;
-
-      if (cmd != HTMLVARCOLOR && cmd != HTMLTITLE && cmd != HTMLHOME
-          && cmd != EXTHTMLTITLE && cmd != EXTHTMLHOME && cmd != EXTHTMLLABEL
-          && cmd != HTMLDIR && cmd != ALTHTMLDIR
-          && cmd != HTMLBIBLIOGRAPHY && cmd != EXTHTMLBIBLIOGRAPHY
-          && cmd != HTMLCSS /* 14-Jan-2016 nm */
-          && cmd != HTMLFONT /* 14-Jan-2016 nm */
-          && cmd != HTMLEXTURL /* 26-Dec-2020 nm */
-          ) {
-         /* Get next token - string in quotes */
-        fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
-        tokenLength = texDefTokenLen(fbPtr);
-
-        /* Process token - string in quotes */
-        if (fbPtr[0] != '\"' && fbPtr[0] != '\'') {
-          if (!tokenLength) { /* Abnormal end-of-file */
-            fbPtr--; /* Format for error message */
-            tokenLength++;
-          }
-          lineNum = lineNumOffset;
-          for (i = 0; i < (fbPtr - fileBuf); i++) {
-            if (fileBuf[i] == '\n') lineNum++;
-          }
-          rawSourceError(/*fileBuf*/g_sourcePtr,  /* 2-Feb-2018 nm */
-            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
-              tokenLength, /*lineNum, g_input_fn,*/
-              "Expected a quoted string here.");
-          let(&fileBuf, "");  /* was: free(fileBuf); */
-          return 2;
-        }
-        if (parsePass == 2) {
-          zapChar = fbPtr[tokenLength - 1]; /* Chr to restore after zapping src */
-          fbPtr[tokenLength - 1] = 0; /* Create end of string */
-          let(&token, fbPtr + 1); /* Get ASCII token; note that leading and
-              trailing quotes are omitted. */
-          fbPtr[tokenLength - 1] = zapChar;
-
-          /* Change double internal quotes to single quotes */
-          /* 6-Aug-2011 nm Do this only for double quotes matching the
-             outer quotes.  fbPtr[0] is the quote character. */
-          if (fbPtr[0] != '\"' && fbPtr[0] != '\'') bug(2329);
-          j = (long)strlen(token);
-          for (i = 0; i < j - 1; i++) {
-            if (token[i] == fbPtr[0] &&
-                token[i + 1] == fbPtr[0]) {
-              let(&token, cat(left(token,
-                  i + 1), right(token, i + 3), NULL));
-              j--;
-            }
-          }
-
-          if ((cmd == LATEXDEF && !g_htmlFlag)
-              || (cmd == HTMLDEF && g_htmlFlag && !g_altHtmlFlag)
-              || (cmd == ALTHTMLDEF && g_htmlFlag && g_altHtmlFlag)) {
-            g_TexDefs[numSymbs].tokenName = "";
-            let(&(g_TexDefs[numSymbs].tokenName), token);
-          }
-        } /* if (parsePass == 2) */
-
-        fbPtr = fbPtr + tokenLength;
-      } /* if (cmd != HTMLVARCOLOR && cmd != HTMLTITLE && cmd != HTMLHOME...) */
-
-      if (cmd != HTMLVARCOLOR && cmd != HTMLTITLE && cmd != HTMLHOME
-          && cmd != EXTHTMLTITLE && cmd != EXTHTMLHOME && cmd != EXTHTMLLABEL
-          && cmd != HTMLDIR && cmd != ALTHTMLDIR
-          && cmd != HTMLBIBLIOGRAPHY && cmd != EXTHTMLBIBLIOGRAPHY
-          && cmd != HTMLCSS /* 14-Jan-2016 nm */
-          && cmd != HTMLFONT /* 14-Jan-2016 nm */
-          && cmd != HTMLEXTURL /* 26-Dec-2020 nm */
-          ) {
-        /* Get next token -- "as" */
-        fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
-        tokenLength = texDefTokenLen(fbPtr);
-        zapChar = fbPtr[tokenLength]; /* Char to restore after zapping source */
-        fbPtr[tokenLength] = 0; /* Create end of string */
-        if (strcmp(fbPtr, "as")) {
-          if (!tokenLength) { /* Abnormal end-of-file */
-            fbPtr--; /* Format for error message */
-            tokenLength++;
-          }
-          lineNum = lineNumOffset;
-          for (i = 0; i < (fbPtr - fileBuf); i++) {
-            if (fileBuf[i] == '\n') lineNum++;
-          }
-          rawSourceError(/*fileBuf*/g_sourcePtr,  /* 2-Feb-2018 nm */
-            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
-              tokenLength, /*lineNum, g_input_fn,*/
-              "Expected the keyword \"as\" here.");
-          let(&fileBuf, "");  /* was: free(fileBuf); */
-          return 2;
-        }
-        fbPtr[tokenLength] = zapChar;
-        fbPtr = fbPtr + tokenLength;
-      } /* if (cmd != HTMLVARCOLOR && ... */
-
-      if (parsePass == 2) {
-        /* Initialize LaTeX/HTML equivalent */
-        let(&token, "");
-      }
-
-      /* Scan   "<string>" + "<string>" + ...   until ";" found */
-      while (1) {
-
-        /* Get next token - string in quotes */
-        fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
-        tokenLength = texDefTokenLen(fbPtr);
-        if (fbPtr[0] != '\"' && fbPtr[0] != '\'') {
-          if (!tokenLength) { /* Abnormal end-of-file */
-            fbPtr--; /* Format for error message */
-            tokenLength++;
-          }
-          lineNum = lineNumOffset;
-          for (i = 0; i < (fbPtr - fileBuf); i++) {
-            if (fileBuf[i] == '\n') lineNum++;
-          }
-          rawSourceError(/*fileBuf*/g_sourcePtr,  /* 2-Feb-2018 nm */
-            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
-              tokenLength, /*lineNum, g_input_fn,*/
-              "Expected a quoted string here.");
-          let(&fileBuf, "");  /* was: free(fileBuf); */
-          return 2;
-        }
-        if (parsePass == 2) {
-          zapChar = fbPtr[tokenLength - 1]; /* Chr to restore after zapping src */
-          fbPtr[tokenLength - 1] = 0; /* Create end of string */
-          /* let(&token, cat(token, fbPtr + 1, NULL)); old before 6-Aug-2011 */
-           /* 6-Aug-2011 nm */
-          let(&partialToken, fbPtr + 1); /* Get ASCII token; note that leading
-              and trailing quotes are omitted. */
-          fbPtr[tokenLength - 1] = zapChar;
-
-          /* 6-Aug-2011 nm */
-          /* Change double internal quotes to single quotes */
-          /* Do this only for double quotes matching the
-             outer quotes.  fbPtr[0] is the quote character. */
-          if (fbPtr[0] != '\"' && fbPtr[0] != '\'') bug(2330);
-          j = (long)strlen(partialToken);
-          for (i = 0; i < j - 1; i++) {
-            if (token[i] == fbPtr[0] &&
-                token[i + 1] == fbPtr[0]) {
-              let(&partialToken, cat(left(partialToken,
-                  i + 1), right(token, i + 3), NULL));
-              j--;
-            }
-          }
-
-          /* 6-Aug-2011 nm */
-          /* Check that string is on a single line */
-          tmpPtr2 = strchr(partialToken, '\n');
-          if (tmpPtr2 != NULL) {
-
-            /* 26-Dec-2011 nm - added to initialize lineNum */
-            lineNum = lineNumOffset;
-            for (i = 0; i < (fbPtr - fileBuf); i++) {
-              if (fileBuf[i] == '\n') lineNum++;
-            }
-
-            rawSourceError(/*fileBuf*/g_sourcePtr,  /* 2-Feb-2018 nm */
-                /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
-                tmpPtr2 - partialToken + 1 /*tokenLength on current line*/,
-                /*lineNum, g_input_fn,*/
-                "String should be on a single line.");
-          }
-
-          /* 6-Aug-2011 nm */
-          /* Combine the string part to the main token we're building */
-          let(&token, cat(token, partialToken, NULL));
-
-        } /* (parsePass == 2) */
-
-        fbPtr = fbPtr + tokenLength;
-
-
-        /* Get next token - "+" or ";" */
-        fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
-        tokenLength = texDefTokenLen(fbPtr);
-        if ((fbPtr[0] != '+' && fbPtr[0] != ';') || tokenLength != 1) {
-          if (!tokenLength) { /* Abnormal end-of-file */
-            fbPtr--; /* Format for error message */
-            tokenLength++;
-          }
-          lineNum = lineNumOffset;
-          for (i = 0; i < (fbPtr - fileBuf); i++) {
-            if (fileBuf[i] == '\n') {
-              lineNum++;
-            }
-          }
-          rawSourceError(/*fileBuf*/g_sourcePtr,  /* 2-Feb-2018 nm */
-            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
-              tokenLength, /*lineNum, g_input_fn,*/
-              "Expected \"+\" or \";\" here.");
-          let(&fileBuf, "");  /* was: free(fileBuf); */
-         return 2;
-        }
-        fbPtr = fbPtr + tokenLength;
-
-        if (fbPtr[-1] == ';') break;
-
-      } /* End while */
-
-
-      if (parsePass == 2) {
-
-        /* 6-Aug-2011 nm This was moved above (and modified) because
-           each string part may have different outer quotes, so we can't
-           do the whole string at once like the attempt below. */
-        /* old before 6-Aug-2011 */
-        /* Change double internal quotes to single quotes */
-        /*
-        j = (long)strlen(token);
-        for (i = 0; i < j - 1; i++) {
-          if ((token[i] == '\"' &&
-              token[i + 1] == '\"') ||
-              (token[i] == '\'' &&
-              token[i + 1] == '\'')) {
-            let(&token, cat(left(token,
-                i + 1), right(token, i + 3), NULL));
-            j--;
-          }
-        }
-        */
-        /* end of old before 6-Aug-2011 */
-
-        if ((cmd == LATEXDEF && !g_htmlFlag)
-            || (cmd == HTMLDEF && g_htmlFlag && !g_altHtmlFlag)
-            || (cmd == ALTHTMLDEF && g_htmlFlag && g_altHtmlFlag)) {
-          g_TexDefs[numSymbs].texEquiv = "";
-          let(&(g_TexDefs[numSymbs].texEquiv), token);
-        }
-        if (cmd == HTMLVARCOLOR) {
-          let(&g_htmlVarColor, cat(g_htmlVarColor, " ", token, NULL));
-        }
-        if (cmd == HTMLTITLE) {
-          let(&htmlTitle, token);
-        }
-        if (cmd == HTMLHOME) {
-          let(&g_htmlHome, token);
-        }
-        if (cmd == EXTHTMLTITLE) {
-          let(&g_extHtmlTitle, token);
-        }
-        if (cmd == EXTHTMLHOME) {
-          let(&extHtmlHome, token);
-        }
-        if (cmd == EXTHTMLLABEL) {
-          let(&extHtmlLabel, token);
-        }
-        if (cmd == HTMLDIR) {
-          let(&htmlDir, token);
-        }
-        if (cmd == ALTHTMLDIR) {
-          let(&altHtmlDir, token);
-        }
-        if (cmd == HTMLBIBLIOGRAPHY) {
-          let(&g_htmlBibliography, token);
-        }
-        if (cmd == EXTHTMLBIBLIOGRAPHY) {
-          let(&extHtmlBibliography, token);
-        }
-        if (cmd == HTMLCSS) {
-          let(&g_htmlCSS, token);
-          /* 14-Jan-2016 nm User's CSS */ /* 13-Dec-2018 nm Moved up to here */
-          /* Convert characters "\n" to new line - maybe do for other fields too? */
-          do {
-            p = instr(1, g_htmlCSS, "\\n");
-            if (p != 0) {
-              let(&g_htmlCSS, cat(left(g_htmlCSS, p - 1), "\n",
-                  right(g_htmlCSS, p + 2), NULL));
-            }
-          } while (p != 0);
-        }
-        if (cmd == HTMLFONT) {
-          let(&g_htmlFont, token);
-        }
-        if (cmd == HTMLEXTURL) {
-          let(&htmlExtUrl, token);  /* 26-Dec-2020 nm */
-        }
-      }
-
-      if ((cmd == LATEXDEF && !g_htmlFlag)
-          || (cmd == HTMLDEF && g_htmlFlag && !g_altHtmlFlag)
-          || (cmd == ALTHTMLDEF && g_htmlFlag && g_altHtmlFlag)) {
-        numSymbs++;
-      }
-
-    } /* End while */
-
-    if (fbPtr != fileBuf + charCount) bug(2305);
-
-    if (parsePass == 1 ) {
-      if (errorsOnly == 0) {
-        print2("%ld typesetting statements were read from \"%s\".\n",
-            numSymbs, g_input_fn);
-      }
-      g_TexDefs = malloc((size_t)numSymbs * sizeof(struct texDef_struct));
-      if (!g_TexDefs) outOfMemory("#99 (TeX symbols)");
-    }
-
-  } /* next parsePass */
-
-
-  /* Sort the tokens for later lookup */
-  qsort(g_TexDefs, (size_t)numSymbs, sizeof(struct texDef_struct), texSortCmp);
-
-  /* Check for duplicate definitions */
-  for (i = 1; i < numSymbs; i++) {
-    if (!strcmp(g_TexDefs[i].tokenName, g_TexDefs[i - 1].tokenName)) {
-      printLongLine(cat("?Warning: Token ", g_TexDefs[i].tokenName,
-          " is defined more than once in ",
-          g_htmlFlag
-            ? (g_altHtmlFlag ? "an althtmldef" : "an htmldef") /* 4-Jul-2020 nm */
-            : "a latexdef",
-          " statement.", NULL),
-          "", " ");
-      warningFound = 1;
-    }
-  }
-
-  /* Check to make sure all definitions are for a real math token */
-  for (i = 0; i < numSymbs; i++) {
-    /* Note:  g_mathKey, g_mathTokens, and mathSrchCmp are assigned or defined
-       in mmpars.c. */
-    g_mathKeyPtr = (void *)bsearch(g_TexDefs[i].tokenName, g_mathKey,
-        (size_t)g_mathTokens, sizeof(long), mathSrchCmp);
-    if (!g_mathKeyPtr) {
-      printLongLine(cat("?Warning: The token \"", g_TexDefs[i].tokenName,
-          "\", which was defined in ",
-          g_htmlFlag
-            ? (g_altHtmlFlag ? "an althtmldef" : "an htmldef")  /* 4-Jul-2020 nm */
-            : "a latexdef",
-          " statement, was not declared in any $v or $c statement.", NULL),
-          "", " ");
-      warningFound = 1;
-    }
-  }
-
-  /* Check to make sure all math tokens have typesetting definitions */
-  for (i = 0; i < g_mathTokens; i++) {
-    texDefsPtr = (void *)bsearch(g_MathToken[i].tokenName, g_TexDefs,
-        (size_t)numSymbs, sizeof(struct texDef_struct), texSrchCmp);
-    if (!texDefsPtr) {
-      printLongLine(cat("?Warning: The token \"", g_MathToken[i].tokenName,
-       "\", which was defined in a $v or $c statement, was not declared in ",
-          g_htmlFlag
-            ? (g_altHtmlFlag ? "an althtmldef" : "an htmldef") /* 4-Jul-2020 nm */
-            : "a latexdef",
-          " statement.", NULL),
-          "", " ");
-      warningFound = 1;
-    }
-  }
-
-  /* 26-Jun-2011 nm Added this check */
-  /* Check to make sure all GIFs are present */
-  if (g_htmlFlag) {
-    for (i = 0; i < numSymbs; i++) {
-      tmpPtr = g_TexDefs[i].texEquiv;
-      k = 0;
-      while (1) {
-        j = instr(k + 1, tmpPtr, "IMG SRC=");
-                   /* Note that only an exact match with
-                      "IMG SRC=" is currently handled */
-        if (j == 0) break;
-        k = instr(j + 9, g_TexDefs[i].texEquiv, mid(tmpPtr, j + 8, 1));
-                                           /* Get position of trailing quote */
-                                    /* Future:  use strchr instead of mid()
-                                       for efficiency? */
-        let(&token, seg(tmpPtr, j + 9, k - 1));  /* Get name of .gif (.png) */
-        if (k == 0) break;  /* Future: we may want to issue "missing
-                                     trailing quote" warning */
-           /* (We test k after the let() so that the temporary string stack
-              entry created by mid() is emptied and won't overflow */
-        if (noGifCheck == 0) { /* 17-Nov-2015 nm */
-          tmpFp = fopen(token, "r"); /* See if it exists */
-          if (!tmpFp) {
-            printLongLine(cat("?Warning: The file \"", token,
-                "\", which is referenced in an htmldef",
-                " statement, was not found.", NULL),
-                "", " ");
-            warningFound = 1;
-          } else {
-            fclose(tmpFp);
-          }
-        }
-      }
-    }
-  }
-
-
-  /* Look up the extended database start label */
-  if (extHtmlLabel[0]) {
-    for (i = 1; i <= g_statements; i++) {
-      if (!strcmp(extHtmlLabel, g_Statement[i].labelName)) break;
-    }
-    if (i > g_statements) {
-      printLongLine(cat("?Warning: There is no statement with label \"",
-          extHtmlLabel,
-          "\" (specified by exthtmllabel in the database source $t comment).  ",
-          "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
-      warningFound = 1;
-    }
-    g_extHtmlStmt = i;
-  } else {
-    /* There is no extended database; set threshold to beyond end of db */
-    g_extHtmlStmt = g_statements + 1;
-  }
-
-  /* 29-Jul-2008 nm Sandbox stuff */
-  /* 24-Jul-2009 nm Changed name of sandbox to "mathbox" */
-  /* 28-Jun-2011 nm Use lookupLabel for speedup */
-  /* replaced w/ function call 17-Jul-2020
-  if (g_mathboxStmt == 0) { /@ Look up "mathbox" label if it hasn't been @/
-    g_mathboxStmt = lookupLabel("mathbox");
-    if (g_mathboxStmt == -1)
-      g_mathboxStmt = g_statements + 1;  /@ Default beyond db end if none @/
-  }
-  */
-  assignMathboxInfo(); /* 17-Jul-2020 nm */
-  /*
-  for (i = 1; i <= g_statements; i++) {
-    /@ For now (and probably forever) the sandbox start theorem is
-       hardcoded to "sandbox", like in set.mm @/
-    /@ if (!strcmp("sandbox", g_Statement[i].labelName)) { @/
-    /@ 24-Jul-2009 nm Changed name of sandbox to "mathbox" @/
-    if (!strcmp("mathbox", g_Statement[i].labelName)) {
-      g_mathboxStmt = i;
-      break;
-    }
-  }
-  */
-  /* In case there is not extended (Hilbert Space Explorer) section,
-     but there is a sandbox section, make the extended section "empty". */
-  if (g_extHtmlStmt == g_statements + 1) g_extHtmlStmt = g_mathboxStmt;
-  let(&sandboxHome, cat("<A HREF=\"mmtheorems.html#sandbox:bighdr\">",
-    "<FONT SIZE=-2 FACE=sans-serif>",
-    "<IMG SRC=\"_sandbox.gif\" BORDER=0 ALT=",
-    "\"Table of Contents\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE>",
-    "Table of Contents</FONT></A>", NULL));
-  let(&sandboxHomeHREF, "mmtheorems.html#sandbox:bighdr");
-  let(&sandboxHomeIMG, "_sandbox.gif");
-  let(&sandboxTitleAbbr, "Users' Mathboxes");
-  /*let(&sandboxTitle, "User Sandbox");*/
-  /* 24-Jul-2009 nm Changed name of sandbox to "mathbox" */
-  let(&sandboxTitle, "Users' Mathboxes");
-
-  /* 16-Aug-2016 nm */
-  /*
-  let(&sandboxHomeHREF, "mmtheorems.html#sandbox:bighdr");
-  let(&sandboxHomeIMG, "_sandbox.gif");
-  let(&sandboxTitleAbbr, "Mathboxes");
-  */
-
-  /* 16-Aug-2016 nm */
-  /* Extract derived variables from the $t variables */
-  /* (In the future, it might be better to do this directly in the $t.) */
-  i = instr(1, g_htmlHome, "HREF=\"") + 5;
-  if (i == 5) {
-    printLongLine(
-        "?Warning: In the $t comment, htmlhome has no 'HREF=\"'.", "", " ");
-    warningFound = 1;
-  }
-  j = instr(i + 1, g_htmlHome, "\"");
-  let(&g_htmlHomeHREF, seg(g_htmlHome, i + 1, j - 1));
-  i = instr(1, g_htmlHome, "IMG SRC=\"") + 8;
-  if (i == 8) {
-    printLongLine(
-        "?Warning: In the $t comment, htmlhome has no 'IMG SRC=\"'.", "", " ");
-    warningFound = 1;
-  }
-  j = instr(i + 1, g_htmlHome, "\"");
-  let(&g_htmlHomeIMG, seg(g_htmlHome, i + 1, j - 1));
-
-
-  /* 16-Aug-2016 nm */
-  /* Compose abbreviated title from capital letters */
-  j = (long)strlen(htmlTitle);
-  let(&htmlTitleAbbr, "");
-  for (i = 1; i <= j; i++) {
-    if (htmlTitle[i - 1] >= 'A' && htmlTitle[i -1] <= 'Z') {
-      let(&htmlTitleAbbr, cat(htmlTitleAbbr, chr(htmlTitle[i - 1]), NULL));
-    }
-  }
-  let(&htmlTitleAbbr, cat(htmlTitleAbbr, " Home", NULL));
-
-  /* 18-Aug-2016 nm Added conditional test for extended section */
-  if (g_extHtmlStmt < g_statements + 1 /* If extended section exists */
-      /* nm 8-Dec-2017 nm */
-      && g_extHtmlStmt != g_mathboxStmt) { /* and is not an empty dummy section */
-    i = instr(1, extHtmlHome, "HREF=\"") + 5;
-    if (i == 5) {
-      printLongLine(
-          "?Warning: In the $t comment, exthtmlhome has no 'HREF=\"'.", "", " ");
-      warningFound = 1;
-    }
-    j = instr(i + 1, extHtmlHome, "\"");
-    let(&extHtmlHomeHREF, seg(extHtmlHome, i + 1, j - 1));
-    i = instr(1, extHtmlHome, "IMG SRC=\"") + 8;
-    if (i == 8) {
-      printLongLine(
-          "?Warning: In the $t comment, exthtmlhome has no 'IMG SRC=\"'.", "", " ");
-      warningFound = 1;
-    }
-    j = instr(i + 1, extHtmlHome, "\"");
-    let(&extHtmlHomeIMG, seg(extHtmlHome, i + 1, j - 1));
-    /* Compose abbreviated title from capital letters */
-    j = (long)strlen(g_extHtmlTitle);
-    let(&g_extHtmlTitleAbbr, "");
-    for (i = 1; i <= j; i++) {
-      if (g_extHtmlTitle[i - 1] >= 'A' && g_extHtmlTitle[i -1] <= 'Z') {
-        let(&g_extHtmlTitleAbbr, cat(g_extHtmlTitleAbbr,
-            chr(g_extHtmlTitle[i - 1]), NULL));
-      }
-    }
-    let(&g_extHtmlTitleAbbr, cat(g_extHtmlTitleAbbr, " Home", NULL));
-  }
-
-  /* 16-Aug-2016 nm For debugging - remove later */
-  /*
-  printf("h=%s i=%s a=%s\n",g_htmlHomeHREF,g_htmlHomeIMG,htmlTitleAbbr);
-  printf("eh=%s ei=%s ea=%s\n",extHtmlHomeHREF,extHtmlHomeIMG,g_extHtmlTitleAbbr);
-  */
-
-
-
-  let(&token, ""); /* Deallocate */
-  let(&partialToken, ""); /* Deallocate */  /* 6-Aug-2011 nm */
-  let(&fileBuf, "");  /* was: free(fileBuf); */
-  g_texDefsRead = 1;  /* Set global flag that it's been read in */
-  return warningFound; /* Return indicator that parsing passed (0) or
-                           had warning(s) (1) */
-
-} /* readTexDefs */
-
-/* This function returns the length of the white space starting at ptr.
-   Comments are considered white space.  ptr should point to the first character
-   of the white space.  If ptr does not point to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned. */
-long texDefWhiteSpaceLen(char *ptr)
-{
-  long i = 0;
-  char tmpchr;
-  char *ptr1;
-  while (1) {
-    tmpchr = ptr[i];
-    if (!tmpchr) return (i); /* End of string */
-    if (isalnum((unsigned char)(tmpchr))) return (i); /* Alphanumeric string */
-
-    /* 6-Aug-2011 nm Removed this undocumented feature */
-    /*
-    if (ptr[i] == '!') { /@ Comment to end-of-line @/
-      ptr1 = strchr(ptr + i + 1, '\n');
-      if (!ptr1) bug(2306);
-      i = ptr1 - ptr + 1;
-      continue;
-    }
-    */
-
-    if (tmpchr == '/') { /* Embedded c-style comment - used to ignore
-        comments inside of Metamath comment for LaTeX/HTML definitions */
-      if (ptr[i + 1] == '*') {
-        while (1) {
-          ptr1 = strchr(ptr + i + 2, '*');
-          if (!ptr1) {
-            return(i + (long)strlen(&ptr[i])); /* Unterminated comment - goto EOF */
-          }
-          if (ptr1[1] == '/') break;
-          i = ptr1 - ptr;
-        }
-        i = ptr1 - ptr + 2;
-        continue;
-      } else {
-        return(i);
-      }
-    }
-    if (isgraph((unsigned char)tmpchr)) return (i);
-    i++;
-  }
-  bug(2307);
-  return(0); /* Dummy return - never executed */
-} /* texDefWhiteSpaceLen */
-
-
-/* This function returns the length of the token (non-white-space) starting at
-   ptr.  Comments are considered white space.  ptr should point to the first
-   character of the token.  If ptr points to a white space character, 0
-   is returned.  If ptr points to a null character, 0 is returned.  If ptr
-   points to a quoted string, the quoted string is returned.  A non-alphanumeric\
-   characters ends a token and is a single token. */
-long texDefTokenLen(char *ptr)
-{
-  long i = 0;
-  char tmpchr;
-  char *ptr1;
-  tmpchr = ptr[i];
-  if (tmpchr == '\"') {
-    while (1) {
-      ptr1 = strchr(ptr + i + 1, '\"');
-      if (!ptr1) {
-        return(i + (long)strlen(&ptr[i])); /* Unterminated quote - goto EOF */
-      }
-      if (ptr1[1] != '\"') return(ptr1 - ptr + 1); /* Double quote is literal */
-      i = ptr1 - ptr + 1;
-    }
-  }
-  if (tmpchr == '\'') {
-    while (1) {
-      ptr1 = strchr(ptr + i + 1, '\'');
-      if (!ptr1) {
-        return(i + (long)strlen(&ptr[i])); /* Unterminated quote - goto EOF */
-      }
-      if (ptr1[1] != '\'') return(ptr1 - ptr + 1); /* Double quote is literal */
-      i = ptr1 - ptr + 1;
-    }
-  }
-  if (ispunct((unsigned char)tmpchr)) return (1); /* Single-char token */
-  while (1) {
-    tmpchr = ptr[i];
-    if (!isalnum((unsigned char)tmpchr)) return (i); /* End of alphnum. token */
-    i++;
-  }
-  bug(2308);
-  return(0); /* Dummy return - never executed */
-} /* texDefTokenLen */
-
-/* Token comparison for qsort */
-int texSortCmp(const void *key1, const void *key2)
-{
-  /* Returns -1 if key1 < key2, 0 if equal, 1 if key1 > key2 */
-  /* Note:  ptr->fld == (*ptr).fld
-            str.fld == (&str)->fld   */
-  return (strcmp( ((struct texDef_struct *)key1)->tokenName,
-      ((struct texDef_struct *)key2)->tokenName));
-} /* texSortCmp */
-
-
-/* Token comparison for bsearch */
-int texSrchCmp(const void *key, const void *data)
-{
-  /* Returns -1 if key < data, 0 if equal, 1 if key > data */
-  return (strcmp(key,
-      ((struct texDef_struct *)data)->tokenName));
-} /* texSrchCmp */
-
-/* Convert ascii to a string of \tt tex; must not have control chars */
-/* (The caller must surround it by {\tt }) */
-/* ***Note:  The caller must deallocate returned string */
-vstring asciiToTt(vstring s)
-{
-
-  vstring ttstr = "";
-  vstring tmp = "";
-  long i, j, k;
-
-  let(&ttstr, s); /* In case the input s is temporarily allocated */
-  j = (long)strlen(ttstr);
-
-  /* Put special \tt font characters in a form that TeX can understand */
-  for (i = 0; i < j; i++) {
-    k = 1;
-    if (!g_htmlFlag) {
-      switch (ttstr[i]) {
-        /* For all unspecified cases, TeX will accept the character 'as is' */
-        case ' ':
-        case '$':
-        case '%':
-        case '#':
-        case '{':
-        case '}':
-        case '&':
-          let(&ttstr,cat(left(ttstr,i),"\\",right(ttstr,i+1),NULL));
-          k = 2;
-          break;
-        case '^':
-          let(&ttstr,cat(left(ttstr,i),"\\^{ }",right(ttstr,i+2),NULL));
-          k = 5;
-          break;
-        case '\\':
-        case '|':
-        case '<':
-        case '>':
-        case '"':
-        case '~':
-        case '_':
-          /* Note:  this conversion will work for any character, but
-             results in the most TeX source code. */
-          let(&ttstr,cat(left(ttstr,i),"\\char`\\",right(ttstr,i+1),NULL));
-          k = 8;
-          break;
-      } /* End switch mtoken[i] */
-    } else {
-      switch (ttstr[i]) {
-        /* For all unspecified cases, HTML will accept the character 'as is' */
-        /* 1-Oct-04 Don't convert to &amp; anymore but leave as is.  This
-           will allow the user to insert HTML entities for Unicode etc.
-           directly in the database source. */
-        /*
-        case '&':
-          let(&ttstr,cat(left(ttstr,i),"&amp;",right(ttstr,i+2),NULL));
-          k = 5;
-          break;
-        */
-        case '<':
-          /* 11/15/02 Leave in some special HTML tags (case must match) */
-          /* This was done specially for the set.mm inf3 comment */
-          /*
-          if (!strcmp(mid(ttstr, i + 1, 5), "<PRE>")) {
-            let(&ttstr, ttstr); /@ Purge stack to prevent overflow by 'mid' @/
-            i = i + 5;
-            break;
-          }
-          if (!strcmp(mid(ttstr, i + 1, 6), "</PRE>")) {
-            let(&ttstr, ttstr); /@ Purge stack to prevent overflow by 'mid' @/
-            i = i + 6;
-            break;
-          }
-          */
-          /* 26-Dec-2011 nm - changed <PRE> to more general <HTML> */
-          if (!strcmp(mid(ttstr, i + 1, 6), "<HTML>")) {
-            let(&ttstr, ttstr); /* Purge stack to prevent overflow by 'mid' */
-            i = i + 6;
-            break;
-          }
-          if (!strcmp(mid(ttstr, i + 1, 7), "</HTML>")) {
-            let(&ttstr, ttstr); /* Purge stack to prevent overflow by 'mid' */
-            i = i + 7;
-            break;
-          }
-          let(&ttstr,cat(left(ttstr,i),"&lt;",right(ttstr,i+2),NULL));
-          k = 4;
-          break;
-        case '>':
-          let(&ttstr,cat(left(ttstr,i),"&gt;",right(ttstr,i+2),NULL));
-          k = 4;
-          break;
-        case '"':
-          let(&ttstr,cat(left(ttstr,i),"&quot;",right(ttstr,i+2),NULL));
-          k = 6;
-          break;
-      } /* End switch mtoken[i] */
-    }
-
-    if (k > 1) { /* Adjust iteration and length */
-      i = i + k - 1;
-      j = j + k - 1;
-    }
-  } /* Next i */
-
-  let(&tmp, "");  /* Deallocate */
-  return(ttstr);
-} /* asciiToTt */
-
-
-/* Convert ascii token to TeX equivalent */
-/* The "$" math delimiter is not placed around the returned arg. here */
-/* *** Note: The caller must deallocate the returned string */
-vstring tokenToTex(vstring mtoken, long statemNum /*for error msgs*/)
-{
-  vstring tex = "";
-  vstring tmpStr;
-  long i, j, k;
-  void *texDefsPtr; /* For binary search */
-  flag saveOutputToString;
-
-  if (!g_texDefsRead) {
-    bug(2320); /* This shouldn't be called if definitions weren't read */
-  }
-
-  texDefsPtr = (void *)bsearch(mtoken, g_TexDefs, (size_t)numSymbs,
-      sizeof(struct texDef_struct), texSrchCmp);
-  if (texDefsPtr) { /* Found it */
-    let(&tex, ((struct texDef_struct *)texDefsPtr)->texEquiv);
-  } else {
-    /* 9/5/99 If it wasn't found, give user a warning... */
-    saveOutputToString = g_outputToString;
-    g_outputToString = 0;
-    /* if (statemNum <= 0 || statemNum > g_statements) bug(2331); */ /* OLD */
-    /* 19-Sep-2012 nm It is possible for statemNum to be 0 when
-       tokenToTex() is called (via getTexLongMath()) from
-       printTexLongMath(), when its hypStmt argument is 0 (= not associated
-       with a statement).  (Reported by Wolf Lammen.) */
-    if (statemNum < 0 || statemNum > g_statements) bug(2331);
-    if (statemNum > 0) {   /* Include statement label in error message */
-      printLongLine(cat("?Warning: In the comment for statement \"",
-          g_Statement[statemNum].labelName,
-          "\", math symbol token \"", mtoken,
-          "\" does not have a LaTeX and/or an HTML definition.", NULL),
-          "", " ");
-    } else { /* There is no statement associated with the error message */
-      printLongLine(cat("?Warning: Math symbol token \"", mtoken,
-          "\" does not have a LaTeX and/or an HTML definition.", NULL),
-          "", " ");
-    }
-    g_outputToString = saveOutputToString;
-    /* ... but we'll still leave in the old default conversion anyway: */
-
-    /* If it wasn't found, use built-in conversion rules */
-    let(&tex, mtoken);
-
-    /* First, see if it's a tilde followed by a letter */
-    /* If so, remove the tilde.  (This is actually obsolete.) */
-    /* (The tilde was an escape in the obsolete syntax.) */
-    if (tex[0] == '~') {
-      if (isalpha((unsigned char)(tex[1]))) {
-        let(&tex, right(tex, 2)); /* Remove tilde */
-      }
-    }
-
-    /* Next, convert punctuation characters to tt font */
-    j = (long)strlen(tex);
-    for (i = 0; i < j; i++) {
-      if (ispunct((unsigned char)(tex[i]))) {
-        tmpStr = asciiToTt(chr(tex[i]));
-        if (!g_htmlFlag)
-          let(&tmpStr, cat("{\\tt ", tmpStr, "}", NULL));
-        k = (long)strlen(tmpStr);
-        let(&tex,
-            cat(left(tex, i), tmpStr, right(tex, i + 2), NULL));
-        i = i + k - 1; /* Adjust iteration */
-        j = j + k - 1; /* Adjust length */
-        let(&tmpStr, ""); /* Deallocate */
-      }
-    } /* Next i */
-
-    /* Make all letters Roman; put inside mbox */
-    if (!g_htmlFlag)
-      let(&tex, cat("\\mbox{\\rm ", tex, "}", NULL));
-
-  } /* End if */
-
-  return (tex);
-} /* tokenToTex */
-
-
-/* Converts a comment section in math mode to TeX.  Each math token
-   MUST be separated by white space.   TeX "$" does not surround the output. */
-vstring asciiMathToTex(vstring mathComment, long statemNum)
-{
-
-  vstring tex;
-  vstring texLine = "";
-  vstring lastTex = "";
-  vstring token = "";
-  flag alphnew, alphold, unknownnew, unknownold;
-  /* flag firstToken; */  /* Not used */
-  long i;
-  vstring srcptr;
-
-  srcptr = mathComment;
-
-  let(&texLine, "");
-  let(&lastTex, "");
-  /* firstToken = 1; */
-  while(1) {
-    i = whiteSpaceLen(srcptr);
-    srcptr = srcptr + i;
-    i = tokenLen(srcptr);
-    if (!i) break; /* Done */
-    let(&token, space(i));
-    memcpy(token, srcptr, (size_t)i);
-    srcptr = srcptr + i;
-    tex = tokenToTex(token, statemNum); /* Convert token to TeX */
-              /* tokenToTex allocates tex; we must deallocate it */
-
-    if (!g_htmlFlag) {
-      /* If this token and previous token begin with letter, add a thin
-           space between them */
-      /* Also, anything not in table will have space added */
-      alphnew = !!isalpha((unsigned char)(tex[0])); /* 31-Aug-2012 nm Added
-            "!!" here and below because isalpha returns an integer, whose
-            unspecified non-zero value could be truncated to 0 when
-            converted to char.  Thanks to Wolf Lammen for pointing this out. */
-      unknownnew = 0;
-      if (!strcmp(left(tex, 10), "\\mbox{\\rm ")) { /* Token not in table */
-        unknownnew = 1;
-      }
-      alphold = !!isalpha((unsigned char)(lastTex[0]));
-      unknownold = 0;
-      if (!strcmp(left(lastTex, 10), "\\mbox{\\rm ")) { /* Token not in table*/
-        unknownold = 1;
-      }
-   /*if ((alphold && alphnew) || unknownold || (unknownnew && !firstToken)) {*/
-      /* Put thin space only between letters and/or unknowns  11/3/94 */
-      if ((alphold || unknownold) && (alphnew || unknownnew)) {
-        /* Put additional thin space between two letters */
-        let(&texLine, cat(texLine, "\\,", tex, " ", NULL));
-      } else {
-        let(&texLine, cat(texLine, tex, " ", NULL));
-      }
-    } else {
-      let(&texLine, cat(texLine, tex, NULL));
-    }
-    let(&lastTex, ""); /* Deallocate */
-    lastTex = tex; /* Pass deallocation responsibility for tex to lastTex */
-
-    /* firstToken = 0; */
-
-  } /* End while (1) */
-  let(&lastTex, ""); /* Deallocate */
-  let(&token, ""); /* Deallocate */
-
-  return (texLine);
-
-} /* asciiMathToTex */
-
-
-/* Gets the next section of a comment that is in the current mode (text,
-   label, or math).  If 1st char. is not "$" (DOLLAR_SUBST), text mode is
-   assumed.  mode = 0 means end of comment reached.  srcptr is left at 1st
-   char. of start of next comment section. */
-vstring getCommentModeSection(vstring *srcptr, char *mode)
-{
-  vstring modeSection = "";
-  vstring ptr; /* Not allocated */
-  flag addMode = 0;
-  if (!g_outputToString) bug(2319);  /* 10/10/02 */
-
-  if ((*srcptr)[0] != DOLLAR_SUBST /*'$'*/) {
-    if ((*srcptr)[0] == 0) { /* End of string */
-      *mode = 0; /* End of comment */
-      return ("");
-    } else {
-      *mode = 'n'; /* Normal text */
-      addMode = 1;
-    }
-  } else {
-    switch ((*srcptr)[1]) {
-      case 'l':
-      case 'm':
-      case 'n':
-        *mode = (*srcptr)[1];
-        break;
-      case ')':  /* Obsolete */
-        bug(2317);
-        /* Leave old code in case user continues through the bug */
-        *mode = 0; /* End of comment */
-        return ("");
-        break;
-      default:
-        *mode = 'n';
-        break;
-    }
-  }
-
-  ptr = (*srcptr) + 1;
-  while (1) {
-    if (ptr[0] == DOLLAR_SUBST /*'$'*/) {
-      switch (ptr[1]) {
-        case 'l':
-        case 'm':
-        case 'n':
-        case ')':  /* Obsolete (will never happen) */
-          if (ptr[1] == ')') bug(2318);
-          let(&modeSection, space(ptr - (*srcptr)));
-          memcpy(modeSection, *srcptr, (size_t)(ptr - (*srcptr)));
-          if (addMode) {
-            let(&modeSection, cat(chr(DOLLAR_SUBST), "n", /*"$n"*/ modeSection,
-                NULL));
-          }
-          *srcptr = ptr;
-          return (modeSection);
-          break;
-      }
-    } else {
-      if (ptr[0] == 0) {
-          let(&modeSection, space(ptr - (*srcptr)));
-          memcpy(modeSection, *srcptr, (size_t)(ptr - (*srcptr)));
-          if (addMode) {
-            let(&modeSection, cat(chr(DOLLAR_SUBST), "n", /*"$n"*/ modeSection,
-                NULL));
-          }
-          *srcptr = ptr;
-          return (modeSection);
-      }
-    }
-    ptr++;
-  } /* End while */
-  return(NULL); /* Dummy return - never executes */
-} /* getCommentModeSection */
-
-
-/* The texHeaderFlag means this:
-    If !g_htmlFlag (i.e. TeX mode), then 1 means print header
-    If g_htmlFlag, then 1 means include "Previous Next" links on page,
-    based on the global g_showStatement variable
-*/
-void printTexHeader(flag texHeaderFlag)
-{
-
-  long i, j, k;
-  vstring tmpStr = "";
-
-  /* 2-Aug-2009 nm - "Mathbox for <username>" mod */
-  vstring localSandboxTitle = "";
-  vstring hugeHdr = ""; /* 21-Jun-2014 nm */
-  vstring bigHdr = "";
-  vstring smallHdr = "";
-  vstring tinyHdr = "";  /* 21-Aug-2017 nm */
-  vstring hugeHdrComment = ""; /* 8-May-2015 nm */
-  vstring bigHdrComment = ""; /* 8-May-2015 nm */
-  vstring smallHdrComment = ""; /* 8-May-2015 nm */
-  vstring tinyHdrComment = ""; /* 21-Aug-2017 nm */
-
-  /*if (!g_texDefsRead) {*/ /* 17-Nov-2015 nm Now done in readTexDefs() */
-  if (2/*error*/ == readTexDefs(0/*errorsOnly=0*/, 0 /*noGifCheck=0*/)) {
-    print2(
-       "?There was an error in the $t comment's LaTeX/HTML definitions.\n");
-    return;
-  }
-  /*}*/
-
-  g_outputToString = 1;  /* Redirect print2 and printLongLine to g_printString */
-  /*let(&g_printString, "");*/ /* May have stuff to be printed 7/4/98 */
-  if (!g_htmlFlag) {
-    print2("%s This LaTeX file was created by Metamath on %s %s.\n",
-       "%", date(), time_());
-
-    /* 14-Sep-2010 nm Added OLD_TEX (g_oldTexFlag) */
-    if (texHeaderFlag && !g_oldTexFlag) {
-      print2("\\documentclass{article}\n");
-      print2("\\usepackage{graphicx} %% For rotated iota\n"); /* 29-Nov-2013 nm */
-      print2("\\usepackage{amssymb}\n");
-      print2("\\usepackage{amsmath} %% For \\begin{align}...\n");
-      print2("\\usepackage{amsthm}\n");
-      print2("\\theoremstyle{plain}\n");
-      print2("\\newtheorem{theorem}{Theorem}[section]\n");
-      print2("\\newtheorem{definition}[theorem]{Definition}\n");
-      print2("\\newtheorem{lemma}[theorem]{Lemma}\n");
-      print2("\\newtheorem{axiom}{Axiom}\n");
-      print2("\\allowdisplaybreaks[1] %% Allow page breaks in {align}\n");
-      print2("\\usepackage[plainpages=false,pdfpagelabels]{hyperref}\n");
-      print2("\\hypersetup{colorlinks} %% Get rid of boxes around links\n");
-                                                        /* 2-May-2017 */
-      print2("\\begin{document}\n");
-      print2("\n");
-    }
-
-    if (texHeaderFlag && g_oldTexFlag) {
-      /******* LaTeX 2.09
-      print2(
-"\\documentstyle[leqno]{article}\n");
-      *******/
-      /* LaTeX 2e */
-      print2(
-    "\\documentclass[leqno]{article}\n");
-      /* LaTeX 2e */
-      print2("\\usepackage{graphicx}\n"); /* 29-Nov-2013 nm For rotated iota */
-      print2(
-    "\\usepackage{amssymb}\n");
-      print2(
-"\\raggedbottom\n");
-      print2(
-"\\raggedright\n");
-      print2(
-"%%\\title{Your title here}\n");
-      print2(
-"%%\\author{Your name here}\n");
-      print2(
-"\\begin{document}\n");
-      /******* LaTeX 2.09
-      print2(
-"\\input amssym.def  %% Load in AMS fonts\n");
-      print2(
-"\\input amssym.tex  %% Load in AMS fonts\n");
-      *******/
-      print2(
-"%%\\maketitle\n");
-      print2(
-"\\newbox\\mlinebox\n");
-      print2(
-"\\newbox\\mtrialbox\n");
-      print2(
-"\\newbox\\startprefix  %% Prefix for first line of a formula\n");
-      print2(
-"\\newbox\\contprefix  %% Prefix for continuation line of a formula\n");
-      print2(
-"\\def\\startm{  %% Initialize formula line\n");
-      print2(
-"  \\setbox\\mlinebox=\\hbox{\\unhcopy\\startprefix}\n");
-      print2(
-"}\n");
-      print2(
-"\\def\\m#1{  %% Add a symbol to the formula\n");
-      print2(
-"  \\setbox\\mtrialbox=\\hbox{\\unhcopy\\mlinebox $\\,#1$}\n");
-      print2(
-"  \\ifdim\\wd\\mtrialbox>\\hsize\n");
-      print2(
-"    \\box\\mlinebox\n");
-      print2(
-"    \\setbox\\mlinebox=\\hbox{\\unhcopy\\contprefix $\\,#1$}\n");
-      print2(
-"  \\else\n");
-      print2(
-"    \\setbox\\mlinebox=\\hbox{\\unhbox\\mtrialbox}\n");
-      print2(
-"  \\fi\n");
-      print2(
-"}\n");
-      print2(
-"\\def\\endm{  %% Output the last line of a formula\n");
-      print2(
-"  \\box\\mlinebox\n");
-      print2(
-"}\n");
-    }
-  } else { /* g_htmlFlag */
-
-    print2(
-        "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n");
-    print2(     "    \"http://www.w3.org/TR/html4/loose.dtd\">\n");
-    print2("<HTML LANG=\"EN-US\">\n");
-    print2("<HEAD>\n");
-    print2("%s%s\n", "<META HTTP-EQUIV=\"Content-Type\" ",
-        "CONTENT=\"text/html; charset=iso-8859-1\">");
-    /* 13-Aug-2016 nm */
-    /* Improve mobile device display per David A. Wheeler */
-    print2(
-"<META NAME=\"viewport\" CONTENT=\"width=device-width, initial-scale=1.0\">\n"
-        );
-
-    print2("<STYLE TYPE=\"text/css\">\n");
-    print2("<!--\n");
-    /* 15-Apr-2015 nm - align math symbol images to text */
-    /* 18-Oct-2015 nm Image alignment fix */
-    /* 10-Jan-2016 nm Optional information but takes unnecessary file space */
-    /* (change @ to * if uncommenting)
-    print2(
-        "/@ Math symbol images will be shifted down 4 pixels to align with\n");
-    print2(
-        "   normal text for compatibility with various browsers.  The old\n");
-    print2(
-        "   ALIGN=TOP for math symbol images did not align in all browsers\n");
-    print2(
-        "   and should be deleted.  All other images must override this\n");
-    print2(
-        "   shift with STYLE=\"margin-bottom:0px\". @/\n");
-    */
-    print2("img { margin-bottom: -4px }\n");
-#ifndef RAINBOW_OPTION
-    /* Print style sheet for pink number that goes after statement label */
-    print2(".p { font-family: \"Arial Narrow\";\n");
-    print2("     font-size: x-small;\n");
-    /* Strip off quotes from color (css doesn't like them) */
-    printLongLine(cat("     color: ", seg(PINK_NUMBER_COLOR, 2,
-        (long)strlen(PINK_NUMBER_COLOR) - 1), ";", NULL), "", "&");
-    print2("   }\n");
-#else
-    /* Print style sheet for rainbow-colored number that goes after
-       statement label */
-    print2(".r { font-family: \"Arial Narrow\";\n");
-    print2("     font-size: x-small;\n");
-    /* There is no color */
-    print2("   }\n");
-#endif
-
-#ifdef INDENT_HTML_PROOFS
-    /* nm 3-Feb-04 Experiment to indent web proof displays */
-    /* Print style sheet for HTML proof indentation number */
-    /* ??? Future - combine with above style sheet */
-    print2(".i { font-family: \"Arial Narrow\";\n");
-    print2("     font-size: x-small;\n");
-    print2("     color: gray;\n");
-    print2("   }\n");
-#endif
-    print2("-->\n");
-    print2("</STYLE>\n");
-    printLongLine(g_htmlCSS, "", " ");
-
-    /*
-    /@ 12-Jan-2016 nm Per Mario Carneiro's suggestion @/
-    print2(".set { color: red; font-style: italic }\n");
-    print2(".class { color: #C3C; font-style: italic }\n");
-    print2(".wff { color: blue; font-style: italic }\n");
-    /@ .symvar = use symbol as class variable @/
-    print2(".symvar { text-decoration: underline dotted; color: #C3C}\n");
-    print2(".typecode { color: gray }\n");
-    print2(".hidden { color: gray }\n");
-    /@ 10-Jan-2016 nm Experiment to always include a style sheet @/
-    /@   Todo: decide on name and directory for .css's @/
-    print2("<LINK href=\"mmstyle.css\" title=\"mmstyle\"\n");
-    print2("      rel=\"stylesheet\" type=\"text/css\">\n");
-    print2("<LINK href=\"mmstylealt.css\" title=\"mmstylealt\"\n");
-    print2("      rel=\"alternate stylesheet\" type=\"text/css\">\n");
-    */
-
-    /*
-    print2("<META NAME=\"ROBOTS\" CONTENT=\"NONE\">\n");
-    print2("<META NAME=\"GENERATOR\" CONTENT=\"Metamath\">\n");
-    */
-    /*
-    if (g_showStatement < g_extHtmlStmt) {
-      print2("%s\n", cat("<TITLE>", htmlTitle, " - ",
-          / * Strip off ".html" * /
-          left(g_texFileName, (long)strlen(g_texFileName) - 5),
-          / *left(g_texFileName, instr(1, g_texFileName, ".htm") - 1),* /
-          "</TITLE>", NULL));
-    } else {
-      print2("%s\n", cat("<TITLE>", g_extHtmlTitle, " - ",
-          / * Strip off ".html" * /
-          left(g_texFileName, (long)strlen(g_texFileName) - 5),
-          / *left(g_texFileName, instr(1, g_texFileName, ".htm") - 1),* /
-          "</TITLE>", NULL));
-    }
-    */
-    /* 4-Jun-06 nm - Put theorem name before "Metamath Proof Explorer" etc. */
-    if (g_showStatement < g_extHtmlStmt) {
-      print2("%s\n", cat("<TITLE>",
-          /* Strip off ".html" */
-          left(g_texFileName, (long)strlen(g_texFileName) - 5),
-          /*left(g_texFileName, instr(1, g_texFileName, ".htm") - 1),*/
-          " - ", htmlTitle,
-          "</TITLE>", NULL));
-    /*} else {*/
-    } else if (g_showStatement < g_mathboxStmt) { /* 29-Jul-2008 nm Sandbox stuff */
-      print2("%s\n", cat("<TITLE>",
-          /* Strip off ".html" */
-          left(g_texFileName, (long)strlen(g_texFileName) - 5),
-          /*left(g_texFileName, instr(1, g_texFileName, ".htm") - 1),*/
-          " - ", g_extHtmlTitle,
-          "</TITLE>", NULL));
-
-    /* 29-Jul-2008 nm Sandbox stuff */
-    } else {
-
-      /* 2-Aug-2009 nm - "Mathbox for <username>" mod */
-      /* Scan from this statement backwards until a big header is found */
-      for (i = g_showStatement; i > g_mathboxStmt; i--) {
-        if (g_Statement[i].type == a_ || g_Statement[i].type == p_) {
-                                                            /* 18-Dec-2016 nm */
-          /* Note: only bigHdr is used; the other 5 returned strings are
-             ignored */
-          getSectionHeadings(i, &hugeHdr, &bigHdr, &smallHdr,
-              &tinyHdr,
-              /* 5-May-2015 nm */
-              &hugeHdrComment, &bigHdrComment, &smallHdrComment,
-              &tinyHdrComment,
-              0, /* fineResolution */
-              0  /* fullComment */);
-          if (bigHdr[0] != 0) break;
-        } /* 18-Dec-2016 nm */
-      } /* next i */
-      if (bigHdr[0]) {
-        /* A big header was found; use it for the page title */
-        let(&localSandboxTitle, bigHdr);
-      } else {
-        /* A big header was not found (should not happen if set.mm is
-           formatted right, but use default just in case) */
-        let(&localSandboxTitle, sandboxTitle);
-      }
-      let(&hugeHdr, "");   /* Deallocate memory */
-      let(&bigHdr, "");   /* Deallocate memory */
-      let(&smallHdr, ""); /* Deallocate memory */
-      let(&tinyHdr, ""); /* Deallocate memory */
-      let(&hugeHdrComment, "");   /* Deallocate memory */
-      let(&bigHdrComment, "");   /* Deallocate memory */
-      let(&smallHdrComment, ""); /* Deallocate memory */
-      let(&tinyHdrComment, ""); /* Deallocate memory */
-      /* 2-Aug-2009 nm - end of "Mathbox for <username>" mod */
-
-      printLongLine(cat("<TITLE>",
-          /* Strip off ".html" */
-          left(g_texFileName, (long)strlen(g_texFileName) - 5),
-          /*left(g_texFileName, instr(1, g_texFileName, ".htm") - 1),*/
-          /*" - ", sandboxTitle,*/
-          /* 2-Aug-2009 nm - "Mathbox for <username>" mod */
-          " - ", localSandboxTitle,
-          "</TITLE>", NULL), "", "\"");
-
-    }
-    /* Icon for bookmark */
-    print2("%s%s\n", "<LINK REL=\"shortcut icon\" HREF=\"favicon.ico\" ",
-        "TYPE=\"image/x-icon\">");
-    /*
-    print2("<META HTML-EQUIV=\"Keywords\"\n");
-    print2("CONTENT=\"%s\">\n", htmlTitle);
-    */
-
-    print2("</HEAD>\n");
-    /*print2("<BODY BGCOLOR=\"#D2FFFF\">\n");*/
-    /*print2("<BODY BGCOLOR=%s>\n", MINT_BACKGROUND_COLOR);*/
-    print2("<BODY BGCOLOR=\"#FFFFFF\">\n");
-
-    /* 16-Aug-2016 nm Major revision of header */
-    print2("<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH=\"100%s\">\n",
-         "%");
-    print2("  <TR>\n");
-    print2("    <TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%s\"><A HREF=\n", "%");
-    print2("    \"%s\"><IMG SRC=\"%s\"\n",
-        (g_showStatement < g_extHtmlStmt ? g_htmlHomeHREF :
-             (g_showStatement < g_mathboxStmt ? extHtmlHomeHREF :
-             sandboxHomeHREF)),
-        /* Note that we assume that the upper-left image is 32x32 */
-        (g_showStatement < g_extHtmlStmt ? g_htmlHomeIMG :
-             (g_showStatement < g_mathboxStmt ? extHtmlHomeIMG :
-             sandboxHomeIMG)));
-    print2("      BORDER=0\n");
-    print2("      ALT=\"%s\"\n",
-        (g_showStatement < g_extHtmlStmt ? htmlTitleAbbr :
-             (g_showStatement < g_mathboxStmt ? g_extHtmlTitleAbbr :
-             sandboxTitleAbbr)));
-    print2("      TITLE=\"%s\"\n",
-        (g_showStatement < g_extHtmlStmt ? htmlTitleAbbr :
-             (g_showStatement < g_mathboxStmt ? g_extHtmlTitleAbbr :
-             sandboxTitleAbbr)));
-    print2(
-      "      HEIGHT=32 WIDTH=32 ALIGN=TOP STYLE=\"margin-bottom:0px\"></A>\n");
-    print2("    </TD>\n");
-    print2(
-"    <TD ALIGN=CENTER COLSPAN=2 VALIGN=TOP><FONT SIZE=\"+3\" COLOR=%s><B>\n",
-                              /* Change COLSPAN=1 -> 2 */ /* 27-Dec-2020 nm */
-      GREEN_TITLE_COLOR);
-    /* Allow plenty of room for long titles (although over 79 chars. will
-       trigger bug 1505). */
-    print2("%s\n",
-        (g_showStatement < g_extHtmlStmt ? htmlTitle :
-             (g_showStatement < g_mathboxStmt ? g_extHtmlTitle :
-             localSandboxTitle)));
-    print2("      </B></FONT></TD>\n");
-    /* print2("    </TD>\n"); */ /* 26-Dec-2016 nm Delete extra </TD> */
-
-
-    /*
-    print2("<TABLE BORDER=0 WIDTH=\"100%s\"><TR>\n", "%");
-    print2("<TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%s\"\n", "%");
-    */
-
-    /*
-    print2("<A HREF=\"mmexplorer.html\">\n");
-    print2("<IMG SRC=\"cowboy.gif\"\n");
-    print2("ALT=\"[Image of cowboy]Home Page\"\n");
-    print2("WIDTH=27 HEIGHT=32 ALIGN=LEFT><FONT SIZE=-1 FACE=sans-serif>Home\n");
-    print2("</FONT></A>");
-    */
-    /*
-    if (g_showStatement < g_extHtmlStmt) {
-      printLongLine(cat("ROWSPAN=2>", g_htmlHome, "</TD>", NULL), "", "\"");
-    /@} else {@/
-    } else if (g_showStatement < g_mathboxStmt) { /@ 29-Jul-2008 nm Sandbox stuff @/
-      printLongLine(cat("ROWSPAN=2>", extHtmlHome, "</TD>", NULL), "", "\"");
-
-    /@ 29-Jul-2008 nm Sandbox stuff @/
-    } else {
-      printLongLine(cat("ROWSPAN=2>", sandboxHome, "</TD>", NULL), "", "\"");
-
-    }
-
-    if (g_showStatement < g_extHtmlStmt) {
-      printLongLine(cat(
-          "<TD ALIGN=CENTER VALIGN=TOP ROWSPAN=2><FONT SIZE=\"+3\" COLOR=",
-          GREEN_TITLE_COLOR, "><B>", htmlTitle, "</B></FONT>", NULL), "", "\"");
-    /@} else {@/
-    } else if (g_showStatement < g_mathboxStmt) { /@ 29-Jul-2008 nm Sandbox stuff @/
-      printLongLine(cat(
-          "<TD ALIGN=CENTER VALIGN=TOP ROWSPAN=2><FONT SIZE=\"+3\" COLOR=",
-          GREEN_TITLE_COLOR, "><B>", g_extHtmlTitle, "</B></FONT>", NULL), "", "\"");
-
-    /@ 29-Jul-2008 nm Sandbox stuff @/
-    } else {
-      printLongLine(cat(
-          "<TD ALIGN=CENTER VALIGN=TOP ROWSPAN=2><FONT SIZE=\"+3\" COLOR=",
-          GREEN_TITLE_COLOR, "><B>",
-          /@sandboxTitle,@/
-          /@ 2-Aug-2009 nm - "Mathbox for <username>" mod @/
-          localSandboxTitle,
-           "</B></FONT>", NULL), "", "\"");
-
-    }
-    */
-
-
-    if (texHeaderFlag) {  /* For HTML, 1 means to put prev/next links */
-      /* Put Previous/Next links into web page */
-      print2("    <TD ALIGN=RIGHT VALIGN=TOP WIDTH=\"25%s\">\n", "%");
-      print2("      <FONT SIZE=-1 FACE=sans-serif>\n");
-      /* Find the previous statement with a web page */
-      j = 0;
-      k = 0;
-      for (i = g_showStatement - 1; i >= 1; i--) {
-        if ((g_Statement[i].type == (char)p_ ||
-            g_Statement[i].type == (char)a_ )
-            /* Skip dummy "xxx..." statements.  We rely on lazy
-               evaluation to prevent array bound overflow. */
-            /* 16-Aug-2016 nm Took out this obsolete feature */
-            /*
-            && (g_Statement[i].labelName[0] != 'x'
-              || g_Statement[i].labelName[1] != 'x'
-              || g_Statement[i].labelName[2] != 'x')
-            */
-            ) {
-          j = i;
-          break;
-        }
-      }
-      if (j == 0) {
-        k = 1; /* First statement flag */
-        /* For the first statement, wrap to last one */
-        for (i = g_statements; i >= 1; i--) {
-          if ((g_Statement[i].type == (char)p_ ||
-              g_Statement[i].type == (char)a_ )
-              /* Skip dummy "xxx..." statements.  We rely on lazy
-                 evaluation to prevent array bound overflow. */
-              /* 16-Aug-2016 nm Took out this obsolete feature */
-              /*
-              && (g_Statement[i].labelName[0] != 'x'
-                || g_Statement[i].labelName[1] != 'x'
-                || g_Statement[i].labelName[2] != 'x')
-              */
-              ) {
-            j = i;
-            break;
-          }
-        }
-      }
-      if (j == 0) bug(2314);
-      print2("      <A HREF=\"%s.html\">\n",
-          g_Statement[j].labelName);
-      if (!k) {
-        print2("      &lt; Previous</A>&nbsp;&nbsp;\n");
-      } else {
-        print2("      &lt; Wrap</A>&nbsp;&nbsp;\n");
-      }
-      /* Find the next statement with a web page */
-      j = 0;
-      k = 0;
-      for (i = g_showStatement + 1; i <= g_statements; i++) {
-        if ((g_Statement[i].type == (char)p_ ||
-            g_Statement[i].type == (char)a_ )
-            /* Skip dummy "xxx..." statements.  We rely on lazy
-               evaluation to prevent array bound overflow. */
-            /* 16-Aug-2016 nm Took out this obsolete feature */
-            /*
-            && (g_Statement[i].labelName[0] != 'x'
-              || g_Statement[i].labelName[1] != 'x'
-              || g_Statement[i].labelName[2] != 'x')
-            */
-            ) {
-          j = i;
-          break;
-        }
-      }
-      if (j == 0) {
-        k = 1; /* Last statement flag */
-        /* For the last statement, wrap to first one */
-        for (i = 1; i <= g_statements; i++) {
-          if ((g_Statement[i].type == (char)p_ ||
-              g_Statement[i].type == (char)a_ )
-              /* Skip dummy "xxx..." statements.  We rely on lazy
-                 evaluation to prevent array bound overflow. */
-              /* 16-Aug-2016 nm Took out this obsolete feature */
-              /*
-              && (g_Statement[i].labelName[0] != 'x'
-                || g_Statement[i].labelName[1] != 'x'
-                || g_Statement[i].labelName[2] != 'x')
-              */
-              ) {
-            j = i;
-            break;
-          }
-        }
-      }
-      if (j == 0) bug(2315);
-      /*print2("<A HREF=\"%s.html\">Next</A></FONT>\n",*/
-      if (!k) {
-        print2("      <A HREF=\"%s.html\">Next &gt;</A>\n",
-            g_Statement[j].labelName);
-      } else {
-        print2("      <A HREF=\"%s.html\">Wrap &gt;</A>\n",
-            g_Statement[j].labelName);
-      }
-
-      print2("      </FONT><FONT FACE=sans-serif SIZE=-2>\n");
-
-      /* ??? Is the closing </FONT> printed if there is no altHtml?
-         This should be tested.  8/9/03 ndm */
-
-      /* 15-Aug-04 nm Compute the theorem list page number.  ??? Temporarily
-         we assume it to be 100 (hardcoded).  Todo: This should be fixed to use
-         the same as the THEOREMS_PER_PAGE in WRITE THEOREMS (have a SET
-         global variable in place of THEOREMS_PER_PAGE?) */
-      i = ((g_Statement[g_showStatement].pinkNumber - 1) / 100) + 1; /* Page # */
-      /* let(&tmpStr, cat("mmtheorems", (i == 1) ? "" : str(i), ".html#", */
-      /* 18-Jul-2015 nm - all thm pages now have page num after mmtheorems
-         since mmtheorems.html is now just the table of contents */
-      let(&tmpStr, cat("mmtheorems", str((double)i), ".html#",
-          g_Statement[g_showStatement].labelName, NULL)); /* Link to page/stmt */
-      /*print2("      <BR><A HREF=\"%s\">Nearby theorems</A>\n",
-            tmpStr);*/
-      /* 3-May-2017 nm Break up lines w/ long labels to prevent bug 1505 */
-      printLongLine(cat("      <BR><A HREF=\"", tmpStr,
-            "\">Nearby theorems</A>", NULL), " ", " ");
-
-      /* Print the GIF/Unicode Font choice, if directories are specified */
-      /*
-      if (htmlDir[0]) {
-
-        if (g_altHtmlFlag) {
-          print2("      <BR><A HREF=\"%s%s\">GIF version</A>\n",
-                htmlDir, g_texFileName);
-
-        } else {
-          print2("      <BR><A HREF=\"%s%s\">Unicode version</A>\n",
-                altHtmlDir, g_texFileName);
-
-        }
-      }
-      */
-      print2("      </FONT>\n");
-      print2("    </TD>\n");
-      print2("  </TR>\n");
-      print2("  <TR>\n");
-      print2("    <TD COLSPAN=2 ALIGN=LEFT VALIGN=TOP><FONT SIZE=-2\n");
-      print2("      FACE=sans-serif>\n");
-      print2("      <A HREF=\"../mm.html\">Mirrors</A>&nbsp; &gt;\n");
-      print2("      &nbsp;<A HREF=\"../index.html\">Home</A>&nbsp; &gt;\n");
-      print2("      &nbsp;<A HREF=\"%s\">%s</A>&nbsp; &gt;\n",
-          (g_showStatement < g_extHtmlStmt ? g_htmlHomeHREF :
-               (g_showStatement < g_mathboxStmt ? extHtmlHomeHREF :
-               g_htmlHomeHREF)),
-          (g_showStatement < g_extHtmlStmt ? htmlTitleAbbr :
-               (g_showStatement < g_mathboxStmt ? g_extHtmlTitleAbbr :
-               htmlTitleAbbr)));
-      print2("      &nbsp;<A HREF=\"mmtheorems.html\">Th. List</A>&nbsp; &gt;\n");
-      if (g_showStatement >= g_mathboxStmt) {
-        print2("      &nbsp;<A HREF=\"mmtheorems.html#sandbox:bighdr\">\n");
-        print2("      Mathboxes</A>&nbsp; &gt;\n");
-      }
-      print2("      &nbsp;%s\n",
-          /* Strip off ".html" */
-          left(g_texFileName, (long)strlen(g_texFileName) - 5));
-      print2("      </FONT>\n");
-      print2("    </TD>\n");
-      print2("    <TD COLSPAN=2 ALIGN=RIGHT VALIGN=TOP>\n");
-                              /* Change COLSPAN=1 -> 2 */ /* 27-Dec-2020 nm */
-      print2("      <FONT SIZE=-2 FACE=sans-serif>\n");
-
-
-      /**** Deleted 26-Dec-2020 nm
-      /@ 3-Jun-2018 nm Add link to Thierry Arnoux's site @/
-      /@ This was added to version 0.162 to become 0.162-thierry @/
-      /@@@@@@ THIS IS TEMPORARY AND MAY BE CHANGED OR DELETED @@@@@@@@@/
-      printLongLine(cat("      <A HREF=\"",
-          "http://metamath.tirix.org/", g_texFileName,
-          "\">Structured version</A>&nbsp;&nbsp;", NULL), "", " ");
-      /@@@@@@ END OF LINKING TO THIERRY ARNOUX'S SITE @@@@@@@@@@@@/
-      ***** End of 26-Dec-2020 deletion *****/
-
-      /* 26-Dec-2020 nm Add link(s) specified by htmlexturl in $t statement */
-      /* The position of the theorem name is indicated with "*" in the
-         htmlexturl $t variable.  If a literal "*" is part of the URL,
-         use the alternate URL encoding "%2A" */
-      /* Example:
-          htmlexturl '<A HREF="http://metamath.tirix.org/*.html">' +
-              'Structured version</A>&nbsp;&nbsp;' +
-              '<A HREF="https://expln.github.io/metamath/asrt/*.html">' +
-              'ASCII version</A>&nbsp;&nbsp;';
-      */
-      let(&tmpStr, htmlExtUrl);
-      i = 1;
-      while (1) {
-        i = instr(i, tmpStr, "*");
-        if (i == 0) break;
-        let(&tmpStr, cat(left(tmpStr, i - 1),
-            g_Statement[g_showStatement].labelName,
-            right(tmpStr, i + 1), NULL));
-      }
-      printLongLine(tmpStr, "", " ");
-      /* End of 26-Dec-2020 mod */
-
-      /* Print the GIF/Unicode Font choice, if directories are specified */
-      if (htmlDir[0]) {
-
-        if (g_altHtmlFlag) {
-          print2("      <A HREF=\"%s%s\">GIF version</A>\n",
-                htmlDir, g_texFileName);
-
-        } else {
-          print2("      <A HREF=\"%s%s\">Unicode version</A>\n",
-                altHtmlDir, g_texFileName);
-
-        }
-      }
-
-    } else { /* texHeaderFlag=0 for HTML means not to put prev/next links */
-      /*print2("      </TD><TD ALIGN=RIGHT VALIGN=TOP\n");*/
-      /* 14-Oct-2019 there is no table open (mmascii, mmdefinitions), so don't
-         add </TD> which caused HTML validation failure */
-      print2("      <TD ALIGN=RIGHT VALIGN=TOP\n");
-      print2("       ><FONT FACE=sans-serif SIZE=-2>\n", "%");
-
-      /* Print the GIF/Unicode Font choice, if directories are specified */
-      if (htmlDir[0]) {
-        print2("\n");
-        if (g_altHtmlFlag) {
-          print2("This is the Unicode version.<BR>\n");
-          print2("<A HREF=\"%s%s\">Change to GIF version</A>\n",
-              htmlDir, g_texFileName);
-        } else {
-          print2("This is the GIF version.<BR>\n");
-          print2("<A HREF=\"%s%s\">Change to Unicode version</A>\n",
-              altHtmlDir, g_texFileName);
-        }
-      }
-      else {
-        print2("&nbsp;\n");
-      }
-
-    }
-
-    print2("      </FONT>\n");
-    print2("    </TD>\n");
-    print2("  </TR>\n");
-    print2("</TABLE>\n");
-
-
-
-    print2("<HR NOSHADE SIZE=1>\n");
-
-  } /* g_htmlFlag */
-  fprintf(g_texFilePtr, "%s", g_printString);
-  g_outputToString = 0;
-  let(&g_printString, "");
-
-  /* Deallocate strings */
-  let(&tmpStr, "");
-
-} /* printTexHeader */
-
-/* Prints an embedded comment in TeX or HTML.  The commentPtr must point to the first
-   character after the "$(" in the comment.  The printout ends when the first
-   "$)" or null character is encountered.   commentPtr must not be a temporary
-   allocation.   htmlCenterFlag, if 1, means to center the HTML and add a
-   "Description:" prefix. */
-/* The output is printed to the global g_texFilePtr. */
-/* Note: the global long "g_showStatement" is referenced to determine whether
-   to read bibliography from mmset.html or mmhilbert.html (or other
-   g_htmlBibliography or extHtmlBibliography file pair). */
-/* Returns 1 if an error or warning message was printed */ /* 17-Nov-2015 */
-flag printTexComment(vstring commentPtr, flag htmlCenterFlag,
-    /* 17-Nov-2015 nm */
-    long actionBits, /* see below */
-        /* 13-Dec-2018 */
-        /* Indicators for actionBits:
-            #define ERRORS_ONLY 1 - just report errors, don't print output
-            #define PROCESS_SYMBOLS 2
-            #define PROCESS_LABELS 4
-            #define ADD_COLORED_LABEL_NUMBER 8
-            #define PROCESS_BIBREFS 16
-            #define PROCESS_UNDERSCORES 32
-            #define CONVERT_TO_HTML 64 - convert '<' to '&gt;' unless
-                     <HTML>, </HTML> present
-            #define METAMATH_COMMENT 128 - $) terminates string
-            #define PROCESS_EVERYTHING PROCESS_SYMBOLS + PROCESS_LABELS \
-             + ADD_COLORED_LABEL_NUMBER + PROCESS_BIBREFS \
-             + PROCESS_UNDERSCORES + CONVERT_HTML + METAMATH_COMMENT \
-        */
-        /* 10-Dec-2018 nm - expanded meaning of errorsOnly for MARKUP commmand:
-             2 = process as if in <HTML>...</HTML> preformatted mode but
-                 don't strip <HTML>...</HTML> tags
-             3 = same as 2, but convert ONLY math symbols
-           (These new values were added instead of addina a new argument,
-           so as not to have to modify ~60 other calls to this function) */
-
-    flag noFileCheck)  /* 1 = ignore missing external files (gifs, bib, etc.) */
-{
-  vstring cmtptr; /* Not allocated */
-  vstring srcptr; /* Not allocated */
-  vstring lineStart; /* Not allocated */
-  vstring tmpStr = "";
-  vstring modeSection; /* Not allocated */
-  vstring sourceLine = "";
-  vstring outputLine = "";
-  vstring tmp = "";
-  flag textMode, mode, lastLineFlag, displayMode;
-  vstring tmpComment = ""; /* Added 10/10/02 */
-  /* 11/15/02 A comment section with <PRE>...</PRE> is formatted into a
-              monospaced text table */
-  /* 26-Dec-2011 nm - changed <PRE> to more general <HTML> */
-  /*flag preformattedMode = 0; /@ HTML <PRE> preformatted mode @/ */
-  flag preformattedMode = 0; /* HTML <HTML> preformatted mode */
-
-  /* 10/10/02 For bibliography hyperlinks */
-  vstring bibTag = "";
-  vstring bibFileName = "";
-  vstring bibFileContents = "";
-  vstring bibFileContentsUpper = ""; /* Uppercase version */
-  vstring bibTags = "";
-  long pos1, pos2, htmlpos1, htmlpos2, saveScreenWidth;
-  flag tmpMathMode; /* 15-Feb-2014 nm */
-
-  /* Variables for converting ` ` and ~ to old $m,$n and $l,$n formats in
-     order to re-use the old code */
-  /* Note that DOLLAR_SUBST will replace the old $. */
-  vstring cmt = "";
-  vstring cmtMasked = ""; /* cmt with math syms blanked */ /* 20-Aug-2014 nm */
-          /* 20-Oct-2018 - also mask ~ label */
-  vstring tmpMasked = ""; /* tmp with math syms blanked */ /* 20-Aug-2014 nm */
-  vstring tmpStrMasked = ""; /* tmpStr w/ math syms blanked */ /* 20-Aug-2014 */
-  long i, clen;
-  flag returnVal = 0; /* 1 means error/warning */ /* 17-Nov-2015 nm */
-
-  /* 13-Dec-2018 nm */
-  /* Internal flags derived from actionBits argument, for MARKUP command use */
-  flag errorsOnly;
-  flag processSymbols;
-  flag processLabels;
-  flag addColoredLabelNumber;
-  flag processBibrefs;
-  flag processUnderscores;
-  flag convertToHtml;
-  flag metamathComment;
-
-  /* Assign local Booleans for actionBits mask */
-  errorsOnly = (actionBits & ERRORS_ONLY ) != 0;
-  processSymbols = (actionBits & PROCESS_SYMBOLS ) != 0;
-  processLabels = (actionBits & PROCESS_LABELS ) != 0;
-  addColoredLabelNumber = (actionBits & ADD_COLORED_LABEL_NUMBER ) != 0;
-  processBibrefs = (actionBits & PROCESS_BIBREFS ) != 0;
-  processUnderscores = (actionBits & PROCESS_UNDERSCORES ) != 0;
-  convertToHtml = (actionBits & CONVERT_TO_HTML ) != 0;
-  metamathComment = (actionBits & METAMATH_COMMENT ) != 0;
-
-  /* We must let this procedure handle switching output to string mode */
-  if (g_outputToString) bug(2309);
-  /* The LaTeX (or HTML) file must be open */
-  if (errorsOnly == 0) {   /* 17-Nov-2015 nm */
-    if (!g_texFilePtr) bug(2321);
-  }
-
-  cmtptr = commentPtr;
-
-  if (!g_texDefsRead) {
-    return returnVal; /* TeX defs were not read (error was detected
-                               and flagged to the user elsewhere) */
-  }
-
-  /* Convert line to the old $m..$n and $l..$n formats (using DOLLAR_SUBST
-     instead of "$") - the old syntax is obsolete but we do this conversion
-     to re-use some old code */
-  if (metamathComment != 0) {
-    i = instr(1, cmtptr, "$)");      /* If it points to source buffer */
-    if (!i) i = (long)strlen(cmtptr) + 1;  /* If it's a stand-alone string */
-  } else {
-    i = (long)strlen(cmtptr) + 1;
-  }
-  let(&cmt, left(cmtptr, i - 1));
-
-  /* All actions on cmt should be mirrored on cmdMasked, except that
-     math symbols are replaced with blanks in cmdMasked */
-  let(&cmtMasked, cmt); /* 20-Aug-2014 nm */
-
-
-  /* This section is independent and can be removed without side effects */
-  if (g_htmlFlag) {
-    /* Convert special characters <, &, etc. to HTML entities */
-    /* But skip converting math symbols inside ` ` */
-    /* 26-Dec-2011 nm Detect preformatted HTML (this is crude, since it
-       will apply to whole comment - perhaps fine-tune this later) */
-    if (convertToHtml != 0) { /* 13-Dec-2018 nm */
-      if (instr(1, cmt, "<HTML>") != 0) preformattedMode = 1;
-    } else {
-      preformattedMode = 1; /* For MARKUP command - don't convert HTML */
-    }
-    mode = 1; /* 1 normal, -1 math token */
-    let(&tmp, "");
-    let(&tmpMasked, "");
-    while (1) {
-      pos1 = 0;
-      while (1) {
-        pos1 = instr(pos1 + 1, cmt, "`");
-        if (!pos1) break;
-        if (cmt[pos1] == '`') {
-          pos1++;  /* Skip `` escape */
-          continue;
-        }
-        break;
-      }
-      if (!pos1) pos1 = (long)strlen(cmt) + 1;
-      if (mode == 1 && preformattedMode == 0) {
-        let(&tmpStr, "");
-        /* asciiToTt() is where "<" is converted to "&lt;" etc. */
-        tmpStr = asciiToTt(left(cmt, pos1));
-        let(&tmpStrMasked, tmpStr);
-      } else {
-        let(&tmpStr, left(cmt, pos1));
-        /* 20-Aug-2014 nm */
-        if (mode == -1) { /* Math mode */
-          /* Replace math symbols with spaces to prevent confusing them
-             with markup in sections below */
-          let(&tmpStrMasked, cat(space(pos1 - 1),
-              mid(cmtMasked, pos1, 1), NULL));
-        } else { /* Preformatted mode but not math mode */
-          let(&tmpStrMasked, left(cmtMasked, pos1));
-        }
-      }
-      let(&tmp, cat(tmp, tmpStr, NULL));
-      let(&tmpMasked, cat(tmpMasked, tmpStrMasked, NULL));
-      let(&cmt, right(cmt, pos1 + 1));
-      let(&cmtMasked, right(cmtMasked, pos1 + 1));
-      if (!cmt[0]) break;
-      mode = (char)(-mode);
-    }
-    let(&cmt, tmp);
-    let(&cmtMasked, tmpMasked);
-    let(&tmpStr, ""); /* Deallocate */
-    let(&tmpStrMasked, "");
-  }
-
-
-  /* 10/10/02 Add leading and trailing HTML markup to comment here
-     (instead of in caller).  Also convert special characters. */
-  if (g_htmlFlag) {
-    /* This used to be done in mmcmds.c */
-    if (htmlCenterFlag) {  /* Note:  this should be 0 in MARKUP command */
-      let(&cmt, cat("<CENTER><TABLE><TR><TD ALIGN=LEFT><B>Description: </B>",
-          cmt, "</TD></TR></TABLE></CENTER>", NULL));
-      let(&cmtMasked,
-          cat("<CENTER><TABLE><TR><TD ALIGN=LEFT><B>Description: </B>",
-          cmtMasked, "</TD></TR></TABLE></CENTER>", NULL));
-    }
-  }
-
-
-  /* Added Oct-20-2018 nm */
-  /* Mask out _ (underscore) in labels so they won't become subscripts
-     (reported by Benoit Jubin) */
-  /* This section is independent and can be removed without side effects */
-  if (g_htmlFlag != 0
-      /* && processSymbols != 0*/ /* Mode 3 processes symbols only */
-         /* (Actually we should do this all the time; this is the mask) */
-      ) {
-    pos1 = 0;
-    while (1) {   /* Look for label start */
-      pos1 = instr(pos1 + 1, cmtMasked, "~");
-      if (!pos1) break;
-      if (cmtMasked[pos1] == '~') {
-        pos1++;  /* Skip ~~ escape */
-        continue;
-      }
-      /* Skip whitespace after ~ */
-      while (1) {
-        if (cmtMasked[pos1] == 0) break;  /* End of line */
-        if (isspace((unsigned char)(cmtMasked[pos1]))) {
-          pos1++;
-          continue;
-        } else { /* Found start of label */
-          break;
-        }
-      }
-      /* Skip non-whitespace after ~ find end of label */
-      while (1) {
-        if (cmtMasked[pos1] == 0) break;  /* End of line */
-        if (!(isspace((unsigned char)(cmtMasked[pos1])))) {
-          if (cmtMasked[pos1] == '_') {
-            /* Put an "?" in place of label character in mask */
-            cmtMasked[pos1] = '?';
-          }
-          pos1++;
-          continue;
-        } else { /* Found end of label */
-          break;
-        }
-      }  /* while (1) */
-    } /* while (1) */
-  } /* if g_htmlFlag */
-
-
-  /* 5-Dec-03 Handle dollar signs in comments converted to LaTeX */
-  /* This section is independent and can be removed without side effects */
-  /* This must be done before the underscores below so subscript $'s */
-  /* won't be converted to \$'s */
-  if (!g_htmlFlag) {  /* LaTeX */
-
-    /* MARKUP command doesn't handle LaTeX */
-    /* 25-Jan-2019 nm - this gets set by PROCESS_EVERYTHING in many calls;
-       it's not specific to MARKUP and not a bug. */
-    /* if (metamathComment != 0) bug(2343); */
-
-    pos1 = 0;
-    while (1) {
-      pos1 = instr(pos1 + 1, cmt, "$");
-      if (!pos1) break;
-      /*
-      /@ Don't modify anything inside of <PRE>...</PRE> tags @/
-      if (pos1 > instr(1, cmt, "<PRE>") && pos1 < instr(1, cmt, "</PRE>"))
-        continue;
-      */
-      /* Don't modify anything inside of <HTML>...</HTML> tags */
-      if (pos1 > instr(1, cmt, "<HTML>") && pos1 < instr(1, cmt, "</HTML>"))
-        continue;
-      let(&cmt, cat(left(cmt, pos1 - 1), "\\$",
-          right(cmt, pos1 + 1), NULL));
-      let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "\\$",
-          right(cmtMasked, pos1 + 1), NULL));
-      pos1 = pos1 + 1; /* Adjust for 2-1 extra chars in "let" above */
-    } /* while (1) */
-  }
-
-  /* 15-Feb-2014 nm */
-  /* 1-May-2017 nm - moved this section up to BEFORE the underscore handling
-     below, so that "{\em...}" won't be converted to "\}\em...\}" */
-  /* Convert any remaining special characters for LaTeX */
-  /* This section is independent and can be removed without side effects */
-  if (!g_htmlFlag) { /* i.e. LaTeX mode */
-    /* At this point, the comment begins e.g "\begin{lemma}\label{lem:abc}" */
-    pos1 = instr(1, cmt, "} ");
-    if (pos1) {
-      pos1++; /* Start after the "}" */
-    } else {
-      pos1 = 1; /* If not found, start from beginning of line */
-    }
-    pos2 = (long)strlen(cmt);
-    tmpMathMode = 0;
-    for (pos1 = pos1 + 0; pos1 <= pos2; pos1++) {
-      /* Don't modify anything inside of math symbol strings
-         (imperfect - only works if `...` is not split across lines?) */
-      if (cmt[pos1 - 1] == '`') tmpMathMode = (flag)(1 - tmpMathMode);
-      if (tmpMathMode) continue;
-      if (pos1 > 1) {
-        if (cmt[pos1 - 1] == '_' && cmt[pos1 - 2] == '$') {
-          /* The _ is part of "$_{...}$" earlier conversion */
-          continue;
-        }
-      }
-      /* $%#{}&^\\|<>"~_ are converted by asciiToTt() */
-      /* Omit \ and $ since they be part of an earlier converston */
-      /* Omit ~ since it is part of label ref */
-      /* Omit " since it legal */
-      /* Because converting to \char` causes later math mode problems due to `,
-         we change |><_ to /)(- (an ugly workaround) */
-      switch(cmt[pos1 - 1]) {
-        case '|': cmt[pos1 - 1] = '/'; break;
-        case '<': cmt[pos1 - 1] = '{'; break;
-        case '>': cmt[pos1 - 1] = '}'; break;
-        case '_': cmt[pos1 - 1] = '-'; break;
-      }
-      if (strchr("%#{}&^|<>_", cmt[pos1 - 1]) != NULL) {
-        let(&tmpStr, "");
-        tmpStr = asciiToTt(chr(cmt[pos1 - 1]));
-        let(&cmt, cat(left(cmt, pos1 - 1), tmpStr,
-            right(cmt, pos1 + 1), NULL));
-        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), tmpStr,
-            right(cmtMasked, pos1 + 1), NULL));
-        pos1 += (long)strlen(tmpStr) - 1;
-        pos2 += (long)strlen(tmpStr) - 1;
-      }
-    } /* Next pos1 */
-  } /* if (!g_htmlFlag) */
-
-  /* 10-Oct-02 Handle underscores in comments converted to HTML:  Convert _abc_
-     to <I>abc</I> for book titles, etc.; convert a_n to a<SUB>n</SUB> for
-     subscripts */
-  /* 5-Dec-03 Added LaTeX handling */
-  /* This section is independent and can be removed without side effects */
-  if (g_htmlFlag != 0     /* 5-Dec-03 */
-      && processUnderscores != 0) {  /* 13-Dec-2018 nm */
-    pos1 = 0;
-    while (1) {
-      /* pos1 = instr(pos1 + 1, cmt, "_"); */
-      /* 20-Aug-2014 nm Only look at non-math part of comment */
-      pos1 = instr(pos1 + 1, cmtMasked, "_");
-      if (!pos1) break;
-      /*
-      /@ Don't modify anything inside of <PRE>...</PRE> tags @/
-      if (pos1 > instr(1, cmt, "<PRE>") && pos1 < instr(1, cmt, "</PRE>"))
-        continue;
-      */
-      /* Don't modify anything inside of <HTML>...</HTML> tags */
-      if (pos1 > instr(1, cmt, "<HTML>") && pos1 < instr(1, cmt, "</HTML>"))
-        continue;
-      /* 23-Jul-2006 nm Don't modify anything inside of math symbol strings
-         (imperfect - only works if `...` is not split across lines) */
-      /* 20-Aug-2014 nm No longer necessary since we now use cmtMasked */
-      /*
-      if (pos1 > instr(1, cmt, "`") && pos1 < instr(instr(1, cmt, "`") + 1,
-          cmt, "`"))
-        continue;
-      */
-
-      /* 5-Apr-2007 nm Don't modify external hyperlinks containing "_" */
-      pos2 = pos1 - 1;
-      while (1) { /* Get to previous whitespace */
-        if (pos2 == 0 || isspace((unsigned char)(cmt[pos2]))) break;
-        pos2--;
-      }
-      if (!strcmp(mid(cmt, pos2 + 2, 7), "http://")) {
-        continue;
-      }
-      /* 20-Aug-2014 nm Add https */
-      if (!strcmp(mid(cmt, pos2 + 2, 8), "https://")) {
-        continue;
-      }
-      /* 20-Oct-2018 nm Add mm */
-      if (!strcmp(mid(cmt, pos2 + 2, 2), "mm")) {
-        continue;
-      }
-
-      /* Opening "_" must be <whitespace>_<alphanum> for <I> tag */
-      if (pos1 > 1) {
-        /* Check for not whitespace and not opening punctuation */
-        if (!isspace((unsigned char)(cmt[pos1 - 2]))
-            && strchr(OPENING_PUNCTUATION, cmt[pos1 - 2]) == NULL) {
-          /* Check for not whitespace and not closing punctuation */
-          if (!isspace((unsigned char)(cmt[pos1]))
-            && strchr(CLOSING_PUNCTUATION, cmt[pos1]) == NULL) {
-
-            /* 28-Sep-03 - Added subscript handling */
-            /* Found <nonwhitespace>_<nonwhitespace> - assume subscript */
-            /* Locate the whitepace (or end of string) that closes subscript */
-            /* Note:  This algorithm is not perfect in that the subscript
-               is assumed to end at closing punctuation, which theoretically
-               could be part of the subscript itself, such as a subscript
-               with a comma in it. */
-            pos2 = pos1 + 1;
-            while (1) {
-              if (!cmt[pos2]) break; /* End of string */
-              /* Look for whitespace or closing punctuation */
-              if (isspace((unsigned char)(cmt[pos2]))
-                  || strchr(OPENING_PUNCTUATION, cmt[pos2]) != NULL
-                  || strchr(CLOSING_PUNCTUATION, cmt[pos2]) != NULL) break;
-              pos2++; /* Move forward through subscript */
-            }
-            pos2++; /* Adjust for left, seg, etc. that start at 1 not 0 */
-            if (g_htmlFlag) {  /* HTML */
-              /* Put <SUB>...</SUB> around subscript */
-              let(&cmt, cat(left(cmt, pos1 - 1),
-                  "<SUB><FONT SIZE=\"-1\">",
-                  seg(cmt, pos1 + 1, pos2 - 1), /* Skip (delete) "_" */
-                  "</FONT></SUB>", right(cmt, pos2), NULL));
-              let(&cmtMasked, cat(left(cmtMasked, pos1 - 1),
-                  "<SUB><FONT SIZE=\"-1\">",
-                  seg(cmtMasked, pos1 + 1, pos2 - 1), /* Skip (delete) "_" */
-                  "</FONT></SUB>", right(cmtMasked, pos2), NULL));
-              pos1 = pos2 + 33; /* Adjust for 34-1 extra chars in "let" above */
-            } else {  /* LaTeX */
-              /* Put _{...} around subscript */
-              let(&cmt, cat(left(cmt, pos1 - 1), "$_{",
-                  seg(cmt, pos1 + 1, pos2 - 1),  /* Skip (delete) "_" */
-                  "}$", right(cmt, pos2), NULL));
-              let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "$_{",
-                  seg(cmtMasked, pos1 + 1, pos2 - 1),  /* Skip (delete) "_" */
-                  "}$", right(cmtMasked, pos2), NULL));
-              pos1 = pos2 + 4; /* Adjust for 5-1 extra chars in "let" above */
-            }
-            continue;
-            /* 23-Sep-03 - End of subscript handling */
-
-          } else {
-            /* Found <nonwhitespace>_<whitespace> - not an opening "_" */
-            /* Do nothing in this case */
-            continue;
-          }
-        }
-      }
-      if (!isalnum((unsigned char)(cmt[pos1]))) continue;
-      /* pos2 = instr(pos1 + 1, cmt, "_"); */
-      /* 20-Aug-2014 nm Only look at non-math part of comment */
-      pos2 = instr(pos1 + 1, cmtMasked, "_");
-      if (!pos2) break;
-      /* Closing "_" must be <alphanum>_<nonalphanum> */
-      if (!isalnum((unsigned char)(cmt[pos2 - 2]))) continue;
-      if (isalnum((unsigned char)(cmt[pos2]))) continue;
-      if (g_htmlFlag) {  /* HTML */
-        let(&cmt, cat(left(cmt, pos1 - 1), "<I>",
-            seg(cmt, pos1 + 1, pos2 - 1),
-            "</I>", right(cmt, pos2 + 1), NULL));
-        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "<I>",
-            seg(cmtMasked, pos1 + 1, pos2 - 1),
-            "</I>", right(cmtMasked, pos2 + 1), NULL));
-        pos1 = pos2 + 5; /* Adjust for 7-2 extra chars in "let" above */
-      } else {  /* LaTeX */
-        let(&cmt, cat(left(cmt, pos1 - 1), "{\\em ",
-            seg(cmt, pos1 + 1, pos2 - 1),
-            "}", right(cmt, pos2 + 1), NULL));
-        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "{\\em ",
-            seg(cmtMasked, pos1 + 1, pos2 - 1),
-            "}", right(cmtMasked, pos2 + 1), NULL));
-        pos1 = pos2 + 4; /* Adjust for 6-2 extra chars in "let" above */
-      }
-    }
-  }
-
-  /* 1-May-2017 nm */
-  /* Convert opening double quote to `` for LaTeX */
-  /* This section is independent and can be removed without side effects */
-  if (!g_htmlFlag) { /* If LaTeX mode */
-    i = 1; /* Even/odd counter: 1 = left quote, 0 = right quote */
-    pos1 = 0;
-    while (1) {
-      /* cmtMasked has math symbols blanked */
-      pos1 = instr(pos1 + 1, cmtMasked, "\"");
-      if (pos1 == 0) break;
-      if (i == 1) {
-        /* Warning:  "`" needs to be escaped (i.e. repeated) to prevent it
-           from being treated as a math symbol delimiter below.  So "````"
-           will become "``" in the LaTeX output. */
-        let(&cmt, cat(left(cmt, pos1 - 1), "````",
-            right(cmt, pos1 + 1), NULL));
-        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "````",
-            right(cmtMasked, pos1 + 1), NULL));
-      }
-      i = 1 - i; /* Count to next even or odd */
-    }
-  }
-
-  /* 10/10/02 Put bibliography hyperlinks in comments converted to HTML:
-        [Monk2] becomes <A HREF="mmset.html#monk2>[Monk2]</A> etc. */
-  /* This section is independent and can be removed without side effects */
-  if (g_htmlFlag
-      && processBibrefs != 0 /* 13-Dec-2018 nm */
-      ) {
-    /* Assign local tag list and local HTML file name */
-    if (g_showStatement < g_extHtmlStmt) {
-      let(&bibTags, g_htmlBibliographyTags);
-      let(&bibFileName, g_htmlBibliography);
-    /*} else {*/
-    } else if (g_showStatement < g_mathboxStmt) { /* 29-Jul-2008 nm Sandbox stuff */
-      let(&bibTags, extHtmlBibliographyTags);
-      let(&bibFileName, extHtmlBibliography);
-
-    /* 29-Jul-2008 nm Sandbox stuff */
-    } else {
-      let(&bibTags, g_htmlBibliographyTags);  /* Go back to Mm Prf Explorer */
-      let(&bibFileName, g_htmlBibliography);
-
-    }
-    if (bibFileName[0]) {
-      /* The user specified a bibliography file in the xxx.mm $t comment
-         (otherwise we don't do anything) */
-      pos1 = 0;
-      while (1) {
-        /* Look for any bibliography tags to convert to hyperlinks */
-        /* The biblio tag should be in brackets e.g. "[Monk2]" */
-        /* pos1 = instr(pos1 + 1, cmt, "["); */
-        /* 20-Aug-2014 nm Only look at non-math part of comment */
-        pos1 = instr(pos1 + 1, cmtMasked, "[");
-        if (!pos1) break; /* 3-Feb-2019 nm Moved this to before block below */
-
-        /* 9-Dec-2018 nm */
-        /* Escape a double [[ */
-        if (cmtMasked[pos1] == '[') {  /* This is the char after "[" above */
-          /* Remove the first "[" */
-          let(&cmt, cat(left(cmt, pos1 - 1),
-              right(cmt, pos1 + 1), NULL));
-          let(&cmtMasked, cat(left(cmtMasked, pos1 - 1),
-              right(cmtMasked, pos1 + 1), NULL));
-          /* The pos1-th position (starting at 1) is now the "[" that remains */
-          continue;
-        }
-
-        /* pos2 = instr(pos1 + 1, cmt, "]"); */
-        /* 20-Aug-2014 nm Only look at non-math part of comment */
-        pos2 = instr(pos1 + 1, cmtMasked, "]");
-        if (!pos2) break;
-
-        /* 30-Jun-2011 nm */
-        /* See if we are in math mode */
-        /* clen = (long)strlen(cmt); */ /* 18-Sep-2013 never used */
-        /* 20-Aug-2014 nm Deleted since cmtMasked takes care of it */
-        /*
-        mode = 0; /@ 0 = normal, 1 = math @/
-        for (i = 0; i < pos1; i++) {
-          if (cmt[i] == '`' && cmt[i + 1] != '`') {
-            mode = (char)(1 - mode);
-          }
-        }
-        if (mode) continue; /@ Don't process [...] brackets in math mode @/
-        */
-
-        /* let(&bibTag, seg(cmt, pos1, pos2)); */
-        /* 20-Aug-2014 nm Get bibTag from cmtMasked as extra precaution */
-        let(&bibTag, seg(cmtMasked, pos1, pos2));
-        /* There should be no white space in the tag */
-        if ((signed)(strcspn(bibTag, " \n\r\t\f")) < pos2 - pos1 + 1) continue;
-        /* OK, we have a good tag.  If the file with bibliography has not been
-           read in yet, let's do so here for error-checking. */
-
-        /* Start of error-checking */
-        if (noFileCheck == 0) {  /* 17-Nov-2015 nm */
-          if (!bibTags[0]) {
-            /* The bibliography file has not be read in yet. */
-            let(&bibFileContents, "");
-            if (errorsOnly == 0) { /* 17-Nov-2015 nm */
-              print2("Reading HTML bibliographic tags from file \"%s\"...\n",
-                  bibFileName);
-            }
-            bibFileContents = readFileToString(bibFileName, 0,
-                &i /* charCount; not used here */); /* 31-Dec-2017 nm */
-            if (!bibFileContents) {
-              /* The file was not found or had some problem (use verbose mode = 1
-                 in 2nd argument of readFileToString for debugging). */
-              printLongLine(cat("?Warning: Couldn't open or read the file \"",
-                  bibFileName,
-                  "\".  The bibliographic hyperlinks will not be checked for",
-                  " correctness.  The first one is \"", bibTag,
-                  "\" in the comment for statement \"",
-                  g_Statement[g_showStatement].labelName, "\".",
-                  NULL), "", " ");
-              returnVal = 1; /* Error/warning printed */ /* 17-Nov-2015 nm */
-              bibFileContents = ""; /* Restore to normal string */
-              let(&bibTags, "?"); /* Assign to a nonsense tag that won't match
-                  but tells us an attempt was already made to read the file */
-            } else {
-              /* Note: In an <A NAME=...> tag, HTML is case-insensitive for A and
-                 NAME but case-sensitive for the token after the = */
-              /* Strip all whitespace */
-              let(&bibFileContents, edit(bibFileContents, 2));
-              /* Uppercase version for HTML tag search */
-              let(&bibFileContentsUpper, edit(bibFileContents, 32));
-              htmlpos1 = 0;
-              while (1) {  /* Look for all <A NAME=...></A> HTML tags */
-                htmlpos1 = instr(htmlpos1 + 1, bibFileContentsUpper, "<ANAME=");
-                /* Note stripped space after <A... - not perfectly robust but
-                   good enough if HTML file is legal since <ANAME is not an HTML
-                   tag (let's not get into a regex discussion though...) */
-                if (!htmlpos1) break;
-                htmlpos1 = htmlpos1 + 7;  /* Point ot beginning of tag name */
-                /* Extract tag, ignoring any surrounding quotes */
-                if (bibFileContents[htmlpos1 - 1] == '\''
-                    || bibFileContents[htmlpos1 - 1] == '"') htmlpos1++;
-                htmlpos2 = instr(htmlpos1, bibFileContents, ">");
-                if (!htmlpos2) break;
-                htmlpos2--; /* Move to character before ">" */
-                if (bibFileContents[htmlpos2 - 1] == '\''
-                    || bibFileContents[htmlpos2 - 1] == '"') htmlpos2--;
-                if (htmlpos2 <= htmlpos1) continue;  /* Ignore bad HTML syntax */
-                let(&tmp, cat("[",
-                    seg(bibFileContents, htmlpos1, htmlpos2), "]", NULL));
-                /* Check if tag is already in list */
-                if (instr(1, bibTags, tmp)) {
-                  printLongLine(cat("?Error: There two occurrences of",
-                      " bibliographic reference \"",
-                      seg(bibFileContents, htmlpos1, htmlpos2),
-                      "\" in the file \"", bibFileName, "\".", NULL), "", " ");
-                  returnVal = 1; /* Error/warning printed */ /* 17-Nov-2015 nm */
-                }
-                /* Add tag to tag list */
-                let(&bibTags, cat(bibTags, tmp, NULL));
-              } /* end while */
-              if (!bibTags[0]) {
-                /* No tags found; put dummy partial tag meaning "file read" */
-                let(&bibTags, "[");
-              }
-            } /* end if (!bibFIleContents) */
-          } /* end if (noFileCheck == 0) */
-          /* Assign to permanent tag list for next time */
-          if (g_showStatement < g_extHtmlStmt) {
-            let(&g_htmlBibliographyTags, bibTags);
-          /*} else {*/
-          } else if (g_showStatement < g_mathboxStmt) {
-                                             /* 29-Jul-2008 nm Sandbox stuff */
-            let(&extHtmlBibliographyTags, bibTags);
-
-          /* 29-Jul-2008 nm Sandbox stuff */
-          } else {
-            let(&g_htmlBibliographyTags, bibTags);
-
-          }
-          /* Done reading in HTML file with bibliography */
-        } /* end if (!bibTags[0]) */
-        /* See if the tag we found is in the bibliography file */
-        if (bibTags[0] == '[') {
-          /* We have a tag list from the bibliography file */
-          if (!instr(1, bibTags, bibTag)) {
-            printLongLine(cat("?Error: The bibliographic reference \"", bibTag,
-                "\" in statement \"", g_Statement[g_showStatement].labelName,
-                "\" was not found as an <A NAME=\"",
-                seg(bibTag, 2, pos2 - pos1),
-                "\"></A> anchor in the file \"", bibFileName, "\".", NULL),
-                "", " ");
-            returnVal = 1; /* Error/warning printed */ /* 17-Nov-2015 nm */
-          }
-        }
-        /* End of error-checking */
-
-        /* Make an HTML reference for the tag */
-        let(&tmp, cat("[<A HREF=\"",
-            bibFileName, "#", seg(bibTag, 2, pos2 - pos1), "\">",
-            seg(bibTag, 2, pos2 - pos1), "</A>]", NULL));
-        let(&cmt, cat(left(cmt, pos1 - 1), tmp, right(cmt,
-            pos2 + 1), NULL));
-        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), tmp, right(cmtMasked,
-            pos2 + 1), NULL));
-        pos1 = pos1 + (long)strlen(tmp) - (long)strlen(bibTag); /* Adjust comment position */
-      } /* end while(1) */
-    } /* end if (bibFileName[0]) */
-  } /* end of if (g_htmlFlag) */
-  /* 10/10/02 End of bibliography hyperlinks */
-
-  /* All actions on cmt should be mirrored on cmdMasked, except that
-     math symbols are replaced with blanks in cmdMasked */
-  if (strlen(cmt) != strlen(cmtMasked)) bug(2334); /* Should be in sync */
-
-  /* Starting here, we no longer use cmtMasked, so syncing it with cmt
-     isn't important anymore. */
-
-  clen = (long)strlen(cmt);
-  mode = 'n';
-  for (i = 0; i < clen; i++) {
-    if (cmt[i] == '`') {
-      if (cmt[i + 1] == '`') {
-        if (processSymbols != 0) { /* 13-Dec-2018 nm */
-          /* Escaped ` = actual ` */
-          let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
-          clen--;
-        }
-      } else {
-        /* 13-Dec-2018 nm We will still enter and exit math mode when
-           processSymbols=0 so as to skip ~ in math symbols.  However,
-           we don't insert the "DOLLAR_SUBST mode" so that later on
-           it will look like normal text */
-        /* Enter or exit math mode */
-        if (mode != 'm') {
-          mode = 'm';
-        } else {
-          mode = 'n';
-        }
-
-        if (processSymbols != 0) {  /* 13-Dec-2018 nm */
-          let(&cmt, cat(left(cmt, i), chr(DOLLAR_SUBST) /*$*/, chr(mode),
-              right(cmt, i+2), NULL));
-          clen++;
-          i++;
-        }
-
-        /* 10/10/02 */
-        /* If symbol is preceded by opening punctuation and a space, take out
-           the space so it looks better. */
-        if (mode == 'm'
-            && processSymbols != 0 /* 13-Dec-2018 nm */
-            ) {
-          let(&tmp, mid(cmt, i - 2, 2));
-          if (!strcmp("( ", tmp)) {
-            let(&cmt, cat(left(cmt, i - 2), right(cmt, i), NULL));
-            clen = clen - 1;
-          }
-          /* We include quotes since symbols are often enclosed in them. */
-          let(&tmp, mid(cmt, i - 8, 8));
-          if (!strcmp("&quot; ", right(tmp, 2))
-              && strchr("( ", tmp[0]) != NULL) {
-            let(&cmt, cat(left(cmt, i - 2), right(cmt, i), NULL));
-            clen = clen - 1;
-          }
-          let(&tmp, "");
-        }
-        /* If symbol is followed by a space and closing punctuation, take out
-           the space so it looks better. */
-        if (mode == 'n'
-            && processSymbols != 0 /* 13-Dec-2018 nm */
-            ) {
-          /* (Why must it be i + 2 here but i + 1 in label version below?
-             Didn't investigate but seems strange.) */
-          let(&tmp, mid(cmt, i + 2, 2));
-          if (tmp[0] == ' ' && strchr(CLOSING_PUNCTUATION, tmp[1]) != NULL) {
-            let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
-            clen = clen - 1;
-          }
-          /* We include quotes since symbols are often enclosed in them. */
-          let(&tmp, mid(cmt, i + 2, 8));
-          if (strlen(tmp) < 8)
-              let(&tmp, cat(tmp, space(8 - (long)strlen(tmp)), NULL));
-          if (!strcmp(" &quot;", left(tmp, 7))
-              && strchr(CLOSING_PUNCTUATION, tmp[7]) != NULL) {
-            let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
-            clen = clen - 1;
-          }
-          let(&tmp, "");
-        }
-
-      }
-    }
-    if (cmt[i] == '~' && mode != 'm') {
-      if (cmt[i + 1] == '~' /* Escaped ~ */
-          || processLabels == 0 /* 13-Dec-2018 nm */
-          ) {
-        if (processLabels != 0) {  /* 13-Dec-2018 nm */
-          /* Escaped ~ = actual ~ */
-          let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
-          clen--;
-        }
-      } else {
-        /* Enter or exit label mode */
-        if (mode != 'l') {
-          mode = 'l';
-          /* 9/5/99 - If there is whitespace after the ~, then remove
-             all whitespace immediately after the ~ to join the ~ to
-             the label.  This enhances the Metamath syntax so that
-             whitespace is now allowed between the ~ and the label, which
-             makes it easier to do global substitutions of labels in a
-             text editor. */
-          while (isspace((unsigned char)(cmt[i + 1])) && clen > i + 1) {
-            let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
-            clen--;
-          }
-        } else {
-          /* This really should never happen */
-          /* 1-Oct-04 If you see this bug, the most likely cause is a tilde
-             character in a comment that does not prefix a label or hyperlink.
-             The most common problem is the "~" inside a hyperlink that
-             specifies a user's directory.  To fix it, use a double tilde
-             "~~" to escape it, which will become a single tilde on output.
-             (At some future point this bug should be converted to a
-             meaningful error message that doesn't abort the program.) */
-          /* bug(2310); */
-          /* 2-Oct-2015 nm Changed to error message */
-          g_outputToString = 0;
-          printLongLine(cat("?Warning: There is a \"~\" inside of a label",
-              " in the comment of statement \"",
-              g_Statement[g_showStatement].labelName,
-              "\".  Use \"~~\" to escape \"~\" in an http reference.",
-              NULL), "", " ");
-          returnVal = 1; /* Error/warning printed */ /* 17-Nov-2015 nm */
-          g_outputToString = 1;
-          mode = 'n';
-        }
-        let(&cmt, cat(left(cmt, i), chr(DOLLAR_SUBST) /*$*/, chr(mode),
-            right(cmt, i+2), NULL));
-        clen++;
-        i++;
-
-        /* 10/10/02 */
-        /* If label is preceded by opening punctuation and space, take out
-           the space so it looks better. */
-        let(&tmp, mid(cmt, i - 2, 2));
-        /*printf("tmp#%s#\n",tmp);*/
-        if (!strcmp("( ", tmp) || !strcmp("[ ", tmp)) {
-          let(&cmt, cat(left(cmt, i - 2), right(cmt, i), NULL));
-          clen = clen - 1;
-        }
-        let(&tmp, "");
-
-      }
-    }
-
-    if (processLabels == 0 && mode == 'l') {
-      /* We should have prevented it from ever getting into label mode */
-      bug(2344); /* 13-Dec-2018 nm */
-    }
-
-    if ((isspace((unsigned char)(cmt[i]))
-            || cmt[i] == '<') /* 17-Nov-2012 nm If the label ends the comment,
-               "</TD>" with no space will be appended before this section. */
-        && mode == 'l') {
-      /* Whitespace exits label mode */
-      mode = 'n';
-      let(&cmt, cat(left(cmt, i), chr(DOLLAR_SUBST) /*$*/, chr(mode),
-          right(cmt, i+1), NULL));
-      clen = clen + 2;
-      i = i + 2;
-
-      /* 10/10/02 */
-      /* If label is followed by space and end punctuation, take out the space
-         so it looks better. */
-      let(&tmp, mid(cmt, i + 1, 2));
-      if (tmp[0] == ' ' && strchr(CLOSING_PUNCTUATION, tmp[1]) != NULL) {
-        let(&cmt, cat(left(cmt, i), right(cmt, i + 2), NULL));
-        clen = clen - 1;
-      }
-      let(&tmp, "");
-
-    }
-    /* clen should always remain comment length - do a sanity check here */
-    if ((signed)(strlen(cmt)) != clen) {
-      bug(2311);
-    }
-  } /* Next i */
-  /* End convert line to the old $m..$n and $l..$n */
-
-
-  /* 29-May-2017 nm */
-  /* Put <HTML> and </HTML> at beginning of line so preformattedMode won't
-     be switched on or off in the middle of processing a line */
-  /* This also fixes the problem where multiple <HTML>...</HTML> on one
-     line aren't all removed in HTML output, causing w3c validation errors */
-  /* Note:  "Q<HTML><sup>2</sup></HTML>." will have a space around "2" because
-     of this fix.  Instead use "<HTML>Q<sup>2</sup>.</HTML>" or just "Q^2." */
-  pos1 = -1; /* So -1 + 2 = 1 = start of string for instr() */
-  while (1) {
-    pos1 = instr(pos1 + 2/*skip new \n*/, cmt, "<HTML>");
-    if (pos1 == 0
-      || convertToHtml == 0 /* Don't touch <HTML> in MARKUP command */
-                                 /* 13-Dec-2018 nm */
-      ) break;
-
-    /* If <HTML> begins a line (after stripping spaces), don't put a \n so
-       that we don't trigger new paragraph mode */
-    let(&tmpStr, edit(left(cmt, pos1 - 1), 2/*discard spaces & tabs*/));
-    i = (long)strlen(tmpStr);
-    if (i == 0) continue;
-    if (tmpStr[i - 1] == '\n') continue;
-
-    let(&cmt, cat(left(cmt, pos1 - 1), "\n", right(cmt, pos1), NULL));
-  }
-  pos1 = -1; /* So -1 + 2 = 1 = start of string for instr() */
-  while (1) {
-    pos1 = instr(pos1 + 2/*skip new \n*/, cmt, "</HTML>");
-    if (pos1 == 0
-      || convertToHtml == 0 /* Don't touch </HTML> in MARKUP command */
-                                 /* 13-Dec-2018 nm */
-      ) break;
-
-    /* If </HTML> begins a line (after stripping spaces), don't put a \n so
-       that we don't trigger new paragraph mode */
-    let(&tmpStr, edit(left(cmt, pos1 - 1), 2/*discard spaces & tabs*/));
-    i = (long)strlen(tmpStr);
-    if (i == 0) continue;
-    if (tmpStr[i - 1] == '\n') continue;
-
-    let(&cmt, cat(left(cmt, pos1 - 1), "\n", right(cmt, pos1), NULL));
-  }
-
-
-  cmtptr = cmt; /* cmtptr is for scanning cmt */
-
-  g_outputToString = 1; /* Redirect print2 and printLongLine to g_printString */
-  /*let(&g_printString, "");*/ /* May have stuff to be printed 7/4/98 */
-
-  while (1) {
-    /* Get a "line" of text, up to the next new-line.  New-lines embedded
-       in $m and $l sections are ignored, so that these sections will not
-       be dangling. */
-    lineStart = cmtptr;
-    textMode = 1;
-    lastLineFlag = 0;
-    while (1) {
-      if (cmtptr[0] == 0) {
-        lastLineFlag = 1;
-        break;
-      }
-      if (cmtptr[0] == '\n' && textMode) break;
-      /* if (cmtptr[0] == '$') { */
-      if (cmtptr[0] == DOLLAR_SUBST) {  /* 14-Feb-2014 nm */
-        if (cmtptr[1] == ')') {
-          bug(2312); /* Obsolete (should never happen) */
-          lastLineFlag = 1;
-          break;
-        }
-      }
-      if (cmtptr[0] == DOLLAR_SUBST /*'$'*/) {
-        cmtptr++;
-        if (cmtptr[0] == 'm') textMode = 0; /* Math mode */
-        if (cmtptr[0] == 'l') textMode = 0; /* Label mode */
-        if (cmtptr[0] == 'n') textMode = 1; /* Normal mode */
-      }
-      cmtptr++;
-    }
-    let(&sourceLine, space(cmtptr - lineStart));
-    memcpy(sourceLine, lineStart, (size_t)(cmtptr - lineStart));
-    cmtptr++;  /* Get past new-line to prepare for next line's scan */
-
-    /* If the line contains only math mode text, use TeX display mode. */
-    displayMode = 0;
-    let(&tmpStr, edit(sourceLine, 8 + 128)); /* Trim spaces */
-    if (!strcmp(right(tmpStr, (long)strlen(tmpStr) - 1), cat(chr(DOLLAR_SUBST), "n",
-        NULL))) let(&tmpStr, left(tmpStr, (long)strlen(tmpStr) - 2)); /* Strip $n */
-    srcptr = tmpStr;
-    modeSection = getCommentModeSection(&srcptr, &mode);
-    let(&modeSection, ""); /* Deallocate */
-    if (mode == 'm') {
-      modeSection = getCommentModeSection(&srcptr, &mode);
-      let(&modeSection, ""); /* Deallocate */
-      /* 9/9/99  displayMode is obsolete.  Because it depends on a manual
-         user edit, by default it will create a LaTeX error. Turn it off. */
-      /*if (mode == 0) displayMode = 1;*/ /* No text after math mode text */
-    }
-    let(&tmpStr, ""); /* Deallocate */
-
-
-    /* Convert all sections of the line to text, math, or labels */
-    let(&outputLine, "");
-    srcptr = sourceLine;
-    while (1) {
-      modeSection = getCommentModeSection(&srcptr, &mode);
-      if (!mode) break; /* Done */
-      let(&modeSection, right(modeSection, 3)); /* Remove mode-change command */
-      switch (mode) {
-        case 'n': /* Normal text */
-          let(&outputLine, cat(outputLine, modeSection, NULL));
-          break;
-        case 'l': /* Label mode */
-
-          if (processLabels == 0) { /* 13-Dec-2018 nm */
-            /* Labels should be treated as normal text */
-            bug(2345);
-          }
-
-          let(&modeSection, edit(modeSection, 8 + 128 + 16));  /* Discard
-                      leading and trailing blanks; reduce spaces to one space */
-          let(&tmpStr, "");
-          tmpStr = asciiToTt(modeSection);
-          if (!tmpStr[0]) { /* Can't be blank */
-            /* bug(2313); */ /* 2-Oct-2015 nm Commented out */
-
-            /* 2-Oct-2015 nm */
-            /* This can happen if ~ is followed by ` (start of math string) */
-            g_outputToString = 0;
-            printLongLine(cat("?Error: There is a \"~\" with no label",
-                " in the comment of statement \"",
-                g_Statement[g_showStatement].labelName,
-                "\".  Check that \"`\" inside of a math symbol is",
-                " escaped with \"``\".",
-                NULL), "", " ");
-            returnVal = 1; /* Error/warning printed */ /* 17-Nov-2015 nm */
-            g_outputToString = 1;
-
-          }
-
-          /* 10/10/02 - obsolete */
-          /* 9/5/99 - Make sure the label in a comment corresponds to a real
-             statement so we won't have broken HTML links (or misleading
-             LaTeX information) - since label references in comments are
-             rare, we just do a linear scan instead of binary search */
-          /******* 10/10/02
-          for (i = 1; i <= g_statements; i++) {
-            if (!strcmp(g_Statement[i].labelName, tmpStr)) break;
-          }
-          if (i > g_statements) {
-            g_outputToString = 0;
-            printLongLine(cat("?Error: Statement \"", tmpStr,
-               "\" (referenced in comment) does not exist.", NULL), "", " ");
-            g_outputToString = 1;
-            returnVal = 1;
-          }
-          *******/
-
-          if (!strcmp("http://", left(tmpStr, 7))
-              || !strcmp("https://", left(tmpStr, 8)) /* 20-Aug-2014 nm */
-              || !strcmp("mm", left(tmpStr, 2)) /* 20-Oct-2018 nm */
-              ) {
-            /* 4/13/04 nm - If the "label" begins with 'http://', then
-               assume it is an external hyperlink and not a real label.
-               This is kind of a syntax kludge but it is easy to do.
-               20-Oct-2018 nm - Added starting with 'mm', which is illegal
-               for set.mm labels - e.g. mmtheorems.html#abc */
-
-            if (g_htmlFlag) {
-              let(&outputLine, cat(outputLine, "<A HREF=\"", tmpStr,
-                  "\">", tmpStr, "</A>", tmp, NULL));
-            } else {
-
-              /* 1-May-2017 nm */
-              /* Generate LaTeX version of the URL */
-              i = instr(1, tmpStr, "\\char`\\~");
-              /* The url{} function automatically converts ~ to LaTeX */
-              if (i != 0) {
-                let(&tmpStr, cat(left(tmpStr, i - 1), right(tmpStr, i + 7),
-                    NULL));
-              }
-              let(&outputLine, cat(outputLine, "\\url{", tmpStr,
-                  "}", tmp, NULL));
-
-            }
-          } else {
-            /* 10/10/02 Do binary search through just $a's and $p's (there
-               are no html pages for local labels) */
-            i = lookupLabel(tmpStr);
-            if (i < 0) {
-              g_outputToString = 0;
-              printLongLine(cat("?Warning: The label token \"", tmpStr,
-                  "\" (referenced in comment of statement \"",
-                  g_Statement[g_showStatement].labelName,
-                  "\") is not a $a or $p statement label.", NULL), "", " ");
-              g_outputToString = 1;
-              returnVal = 1; /* Error/warning printed */ /* 17-Nov-2015 nm */
-            }
-
-            if (!g_htmlFlag) {
-              let(&outputLine, cat(outputLine, "{\\tt ", tmpStr,
-                 "}", NULL));
-            } else {
-              let(&tmp, "");
-              if (addColoredLabelNumber != 0) { /* 13-Dec-2018 nm */
-                /* When the error above occurs, i < 0 will cause pinkHTML()
-                   to issue "(future)" for pleasant readability */
-                tmp = pinkHTML(i);
-              }
-              if (i < 0) {
-                /* Error output - prevent broken link */
-                let(&outputLine, cat(outputLine, "<FONT COLOR=blue ",
-                    ">", tmpStr, "</FONT>", tmp, NULL));
-              } else {
-                /* Normal output - put hyperlink to the statement */
-                let(&outputLine, cat(outputLine, "<A HREF=\"", tmpStr,
-                    ".html\">", tmpStr, "</A>", tmp, NULL));
-              }
-            }
-          } /* if (!strcmp("http://", left(tmpStr, 7))) ... else */
-          let(&tmpStr, ""); /* Deallocate */
-          break;
-        case 'm': /* Math mode */
-
-          if (processSymbols == 0) { /* 13-Dec-2018 nm */
-            /* Math symbols should be treated as normal text */
-            bug(2346);
-          }
-
-          let(&tmpStr, "");
-          tmpStr = asciiMathToTex(modeSection, g_showStatement);
-          if (!g_htmlFlag) {
-            if (displayMode) {
-              /* It the user's responsibility to establish equation environment
-                 in displayMode. */
-              let(&outputLine, cat(outputLine, /*"\\[",*/ edit(tmpStr, 128),
-                /*"\\]",*/ NULL));  /* edit = remove trailing spaces */
-            } else {
-              let(&outputLine, cat(outputLine, "$", edit(tmpStr, 128),
-                "$", NULL));  /* edit = remove trailing spaces */
-            }
-          } else {
-            /* 10/25/02 Trim leading, trailing spaces in case punctuation
-               surrounds the math symbols in the comment */
-            let(&tmpStr, edit(tmpStr, 8 + 128));
-            /* 14-Jan-2016 nm */
-            /* Enclose math symbols in a span to be used for font selection */
-            let(&tmpStr, cat(
-                (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
-                                               /* 14-Jan-2016 nm */
-                tmpStr,
-                (g_altHtmlFlag ? "</SPAN>" : ""),    /* 14-Jan-2016 nm */
-                NULL));
-            let(&outputLine, cat(outputLine, tmpStr, NULL)); /* html */
-          }
-          let(&tmpStr, ""); /* Deallocate */
-          break;
-      } /* End switch(mode) */
-      let(&modeSection, ""); /* Deallocate */
-    }
-    let(&outputLine, edit(outputLine, 128)); /* remove trailing spaces */
-
-    if (g_htmlFlag) {
-      /* Change blank lines into paragraph breaks except in <HTML> mode */
-      if (!outputLine[0]) { /* Blank line */
-        if (preformattedMode == 0
-            && convertToHtml == 1 /* Not MARKUP command */ /* 13-Dec-2018 nm */
-            ) {  /* Make it a paragraph break */
-          let(&outputLine,
-              /* "<P>"); */
-              /* 9-May-2015 nm Prevent space after last paragraph */
-              "<P STYLE=\"margin-bottom:0em\">");
-        }
-      }
-      /* 11/15/02 If a statement comment has a section embedded in
-         <PRE>...</PRE>, we make it a table with monospaced text and a
-         background color */
-      /* pos1 = instr(1, outputLine, "<PRE>"); */
-      /* 26-Dec-2011 nm - changed <PRE> to more general <HTML> */
-      pos1 = instr(1, outputLine, "<HTML>");
-      if (pos1 != 0
-          && convertToHtml == 1 /* 13-Dec-2018 nm */
-          ) {
-        /* 26-Dec-2011 nm - The line below is probably redundant since we
-           set preformattedMode ealier.  Maybe add a bug check to make sure
-           it is 1 here. */
-        preformattedMode = 1; /* So we don't put <P> for blank lines */
-        /* 26-Dec-2011 nm - Took out fancy table for simplicity
-        let(&outputLine, cat(left(outputLine, pos1 - 1),
-            "<P><CENTER><TABLE BORDER=0 CELLSPACING=0 CELLPADDING=10 BGCOLOR=",
-            /@ MINT_BACKGROUND_COLOR, @/
-            "\"#F0F0F0\"", /@ Very light gray @/
-            "><TR><TD ALIGN=LEFT>", right(outputLine, pos1), NULL));
-        */
-        /* 26-Dec-2011 nm - Strip out the "<HTML>" string */
-        let(&outputLine, cat(left(outputLine, pos1 - 1),
-            right(outputLine, pos1 + 6), NULL));
-      }
-      /* pos1 = instr(1, outputLine, "</PRE>"); */
-      pos1 = instr(1, outputLine, "</HTML>");
-      if (pos1 != 0
-          && convertToHtml == 1 /* 13-Dec-2018 nm */
-          ) {
-        preformattedMode = 0;
-        /* 26-Dec-2011 nm - Took out fancy table for simplicity
-        let(&outputLine, cat(left(outputLine, pos1 + 5),
-            "</TD></TR></TABLE></CENTER>", right(outputLine, pos1 + 6), NULL));
-        */
-        /* 26-Dec-2011 nm - Strip out the "</HTML>" string */
-        let(&outputLine, cat(left(outputLine, pos1 - 1),
-            right(outputLine, pos1 + 7), NULL));
-      }
-    }
-
-    if (!g_htmlFlag) { /* LaTeX */
-      /* Convert <PRE>...</PRE> HTML tags to LaTeX */
-      /* 26-Dec-2011 nm - leave this in for now */
-      while (1) {
-        pos1 = instr(1, outputLine, "<PRE>");
-        if (pos1) {
-          let(&outputLine, cat(left(outputLine, pos1 - 1), "\\begin{verbatim} ",
-              right(outputLine, pos1 + 5), NULL));
-        } else {
-          break;
-        }
-      }
-      while (1) {
-        pos1 = instr(1, outputLine, "</PRE>");
-        if (pos1) {
-          let(&outputLine, cat(left(outputLine, pos1 - 1), "\\end{verbatim} ",
-              right(outputLine, pos1 + 6), NULL));
-        } else {
-          break;
-        }
-      }
-      /* 26-Dec-2011 nm - strip out <HTML>, </HTML> */
-      /* The HTML part may screw up LaTeX; maybe we should just take out
-         any HTML code completely in the future? */
-      while (1) {
-        pos1 = instr(1, outputLine, "<HTML>");
-        if (pos1) {
-          let(&outputLine, cat(left(outputLine, pos1 - 1),
-              right(outputLine, pos1 + 6), NULL));
-        } else {
-          break;
-        }
-      }
-      while (1) {
-        pos1 = instr(1, outputLine, "</HTML>");
-        if (pos1) {
-          let(&outputLine, cat(left(outputLine, pos1 - 1),
-              right(outputLine, pos1 + 7), NULL));
-        } else {
-          break;
-        }
-      }
-    }
-
-    saveScreenWidth = g_screenWidth;
-    /* 26-Dec-2011 nm - in <PRE> mode, we don't want to wrap the HTML
-       output with spurious newlines */
-    /*if (preformattedMode) g_screenWidth = PRINTBUFFERSIZE - 2;*/
-    /* 19-Jun-2010 nm PRINTBUFFERSIZE was removed.  Any large value will
-       do; we just need to accomodate the worst case line length that will
-       result from converting ~ label, [author], ` math ` to HTML */
-    if (preformattedMode) g_screenWidth = 50000;
-    if (errorsOnly == 0) {
-      printLongLine(outputLine, "", g_htmlFlag ? "\"" : "\\");
-    }
-    g_screenWidth = saveScreenWidth;
-
-    let(&tmp, ""); /* Clear temporary allocation stack */
-
-    if (lastLineFlag) break; /* Done */
-  } /* end while(1) */
-
-  if (g_htmlFlag) {
-    if (convertToHtml != 0) { /* Not MARKUP command */ /* 13-Dec-2018 nm */
-      print2("\n"); /* Don't change what the previous code did */
-    } else {
-      /* 13-Dec-2018 nm */
-      /* Add newline if string is not empty and has no newline at end */
-      if (g_printString[0] != 0) {
-        i = (long)strlen(g_printString);
-        if (g_printString[i - 1] != '\n')  {
-          print2("\n");
-        } else {
-          /* 13-Dec-2018 nm */
-          /* There is an extra \n added by something previous.  Until
-             we figure out what, take it off so that MARKUP output will
-             equal input when no processing qualifiers are used. */
-          if (i > 1) {
-            if (g_printString[i - 2] == '\n') {
-              let(&g_printString, left(g_printString, i - 1));
-            }
-          }
-        }
-      }
-    }
-  } else { /* LaTeX mode */
-    if (!g_oldTexFlag) {
-      /* 14-Sep-2010 nm Suppress blank line for LaTeX */
-      /* print2("\n"); */
-    } else {
-      print2("\n");
-    }
-  }
-
-  g_outputToString = 0; /* Restore normal output */
-  if (errorsOnly == 0) { /* 17-Nov-2015 nm */
-    fprintf(g_texFilePtr, "%s", g_printString);
-  }
-
-  let(&g_printString, ""); /* Deallocate strings */
-  let(&sourceLine, "");
-  let(&outputLine, "");
-  let(&cmt, "");
-  let(&cmtMasked, "");
-  let(&tmpComment, "");
-  let(&tmp, "");
-  let(&tmpMasked, "");
-  let(&tmpStr, "");
-  let(&tmpStrMasked, "");
-  let(&bibTag, "");
-  let(&bibFileName, "");
-  let(&bibFileContents, "");
-  let(&bibFileContentsUpper, "");
-  let(&bibTags, "");
-
-  return returnVal; /* 1 if error/warning found */
-
-} /* printTexComment */
-
-
-
-void printTexLongMath(nmbrString *mathString,
-    vstring startPrefix, /* Start prefix in "screen display" mode e.g.
-         "abc $p"; it is converted to the appropriate format.  Non-zero
-         length means proof step in HTML mode, as opposed to assertion etc. */
-    vstring contPrefix, /* Prefix for continuation lines.  Not used in
-         HTML mode.  Warning:  contPrefix must not be temporarily allocated
-         (as a cat, left, etc. argument) by caller */
-    long hypStmt, /* hypStmt, if non-zero, is the statement number to be
-                     referenced next to the hypothesis link in html */
-    long indentationLevel) /* nm 3-Feb-04 Indentation amount of proof step -
-                              note that this is 0 for last step of proof */
-{
-/* 23-Apr-04 nm Changed "top" level from 0 to 1 - hopefully slightly less
-   confusing since before user had to scroll to bottom to know that it
-   started at 0 and not 1 */
-#define INDENTATION_OFFSET 1
-  long i;
-  long pos;
-  vstring tex = "";
-  vstring texLine = "";
-  vstring sPrefix = ""; /* 7/3/98 */
-  vstring htmStep = ""; /* 7/4/98 */
-  vstring htmStepTag = ""; /* 30-May-2015 nm */
-  vstring htmHyp = ""; /* 7/4/98 */
-  vstring htmRef = ""; /* 7/4/98 */
-  vstring htmLocLab = ""; /* 7/4/98 */
-  vstring tmp = "";  /* 10/10/02 */
-  vstring descr = ""; /* 19-Nov-2007 nm */
-  char refType = '?'; /* 14-Sep-2010 nm  'e' means $e, etc. */
-
-  let(&sPrefix, startPrefix); /* 7/3/98 Save it; it may be temp alloc */
-
-  if (!g_texDefsRead) return; /* TeX defs were not read (error was printed) */
-  g_outputToString = 1; /* Redirect print2 and printLongLine to g_printString */
-  /* May have stuff to be printed 7/4/98 */
-  /*if (!g_htmlFlag) let(&g_printString, "");*/ /* Removed 6-Dec-03 */
-
-  /* Note that the "tex" assignment below will be used only when !g_htmlFlag
-     and g_oldTexFlag, or when g_htmlFlag and len(sPrefix)>0 */
-  let(&tex, "");
-  tex = asciiToTt(sPrefix); /* asciiToTt allocates; we must deallocate */
-      /* Example: sPrefix = " 4 2,3 ax-mp  $a " */
-      /*          tex = "\ 4\ 2,3\ ax-mp\ \ \$a\ " in !g_htmlFlag mode */
-      /*          tex = " 4 2,3 ax-qmp  $a " in g_htmlFlag mode */
-  let(&texLine, "");
-
-  /* 14-Sep-2010 nm Get statement type of proof step reference */
-  i = instr(1, sPrefix, "$");
-  if (i) refType = sPrefix[i]; /* Character after the "$" */
-
-  /* 14-Sep-2010 nm Moved this code from below to use for !g_oldTexFlag */
-  if (g_htmlFlag || !g_oldTexFlag) {
-
-    /* Process a proof step prefix */
-    if (strlen(sPrefix)) { /* It's a proof step */
-      /* Make each token a separate table column for HTML */
-      /* This is a kludge that only works with /LEMMON style proofs! */
-      /* 14-Sep-2010 nm Note that asciiToTt() above puts "\ " when not in
-         g_htmlFlag mode, so use sPrefix instead of tex so it will work in
-         !g_oldTexFlag mode */
-
-      /* 1-May-2017 nm Fix for non-/LEMMON format used by TeX mode */
-      /* In HTML mode, sPrefix has two possible formats:
-           "2 ax-1  $a "
-           "3 1,2 ax-mp  $a "
-         In LaTeX mode (!g_htmlFlag), sPrefix has one format:
-           "8   maj=ax-1  $a "
-           "9 a1i=ax-mp $a " */
-      /* Later on 1-May-2017: the LaTeX mode now returns same as HTML mode. */
-
-      /* let(&tex, edit(tex, 8 + 16 + 128)); */
-      let(&tex, edit(sPrefix, 8/*no leading spaces*/
-           + 16/*reduce spaces and tabs*/
-           + 128/*no trailing spaces*/));
-
-      i = 0;
-      pos = 1;
-      while (pos) {
-        pos = instr(1, tex, " ");
-        if (pos) {
-          if (i > 3) { /* 2/8/02 - added for extra safety for the future */
-            bug(2316);
-          }
-          /* Deleted 26-Jun-2017 nm - this check is too conservative; we
-             could have starting tex="2 1 a4s @2: $p" which will result
-             in pos=4, i=3.  "show proof drsb1/tex" triggered this bug. */
-          /*
-          if (!g_htmlFlag && i > 2) { /@ 1-May-2017 nm @/
-            bug(2341);
-          }
-          */
-          if (i == 0) let(&htmStep, left(tex, pos - 1));
-          if (i == 1) let(&htmHyp, left(tex, pos - 1));
-          if (i == 2) let(&htmRef, left(tex, pos - 1));
-          /* 26-Jun-2017 nm */
-          if (i == 3) let(&htmLocLab, left(tex, pos - 1));
-
-          let(&tex, right(tex, pos + 1));
-          i++;
-        }
-      }
-
-      /* 26-Jun-2017 nm */
-      if (i == 3 && htmRef[0] == '@') {
-        /* The referenced statement has no hypotheses but has a local
-           label e.g."2 a4s @2: $p" */
-        let(&htmLocLab, htmRef);
-        let(&htmRef, htmHyp);
-        let(&htmHyp, "");
-      }
-
-      if (i < 3) {
-        /* The referenced statement has no hypotheses e.g.
-           "4 ax-1 $a" */
-        let(&htmRef, htmHyp);
-        let(&htmHyp, "");
-
-        /* 1-May-2017 nm Fix bug reported by Ari Ferrera*/
-        /* Change "maj=ax-1" to "ax-1" so \ref{} produced by
-           "show proof .../tex" will match \label{} produced by
-           "show statement .../tex" */
-        /* Later on 1-May-2017:  earlier we set the noIndentFlag (Lemmon
-           proof) in the SHOW PROOF.../TEX call in metamath.c, so the
-           hypothesis ref list will be available just like in the HTML
-           output. */
-        /* 1-May-2017 nm - Delete the below if we keep the noIndentFlag solution. */
-        /*
-        if (!g_htmlFlag) {
-          pos = instr(1, htmRef, "=");
-          if (!pos) bug(2342);
-          let(&htmRef, right(htmRef, pos + 1));
-        }
-        */
-        /* We now consider "=" a bug since the call via typeProof() in
-           metamath.c now always has noIndentFlag = 1. */
-        if (!g_htmlFlag) {
-          pos = instr(1, htmRef, "=");
-          if (pos) bug(2342);
-        }
-
-
-      }
-    } /* if (strlen(sPrefix)) (end processing proof step prefix) */
-  }
-
-  if (!g_htmlFlag) {
-
-    /* 27-Jul-05 nm Added SIMPLE_TEX */
-    if (!g_oldTexFlag) {
-      /* 14-Sep-2010 nm Old 27-Jul-05 version commented out: */
-      /* printLongLine(cat("\\texttt{", tex, "}", NULL), "", " ");  */
-      /* let(&tex, ""); */ /* Deallocate */
-      /* tex = asciiToTt(contPrefix); */
-      /* printLongLine(cat("\\texttt{", tex, "}", NULL), "", " "); */
-      /* print2("\\begin{eqnarray}\n"); */
-    } else {
-      /* 9/2/99 Trim down long start prefixes so they won't overflow line,
-         by putting their tokens into \m macros */
-#define TRIMTHRESHOLD 60
-      i = (long)strlen(tex);
-      while (i > TRIMTHRESHOLD) {
-        if (tex[i] == '\\') {
-          /* Move to math part */
-          let(&texLine, cat("\\m{\\mbox{\\tt", right(tex, i + 1), "}}",
-              texLine, NULL));
-          /* Take off of prefix part */
-          let(&tex, left(tex, i));
-        }
-        i--;
-      }
-
-      printLongLine(cat(
-          "\\setbox\\startprefix=\\hbox{\\tt ", tex, "}", NULL), "", "\\");
-      let(&tex, ""); /* Deallocate */
-      tex = asciiToTt(contPrefix);
-      printLongLine(cat(
-          "\\setbox\\contprefix=\\hbox{\\tt ", tex, "}", NULL), "", "\\");
-      print2("\\startm\n");
-    }
-  } else { /* g_htmlFlag */
-    if (strlen(sPrefix)) { /* It's a proof step */
-
-      if (htmHyp[0] == 0)
-        let(&htmHyp, "&nbsp;");  /* Insert blank field for Lemmon ref w/out hyp */
-
-      /*** Start of 9-Sep-2010 ***/
-      /* 9-Sep-2010 Stefan Allen - put hyperlinks on hypothesis
-         label references in SHOW STATEMENT * /HTML, ALT_HTML output */
-      /*Add hyperlink references to the proof */
-      /* let(&htmStep, cat("<A NAME=\"",htmStep,"\">",htmStep,"</A>",NULL)); */
-      /* 30-May-2015 nm Use a separate tag to put into the math cell,
-         so it will link to the top of the math cell */
-      let(&htmStepTag, cat("<A NAME=\"", htmStep, "\">","</A>", NULL));
-      i = 1;
-      pos = 1;
-      while (pos && strcmp(htmHyp, "&nbsp;")) {
-        pos = instr(i,htmHyp, ",");
-        if (!pos) pos = len(htmHyp) + 1;
-        let(&htmHyp, cat(left(htmHyp, i - 1),
-            "<A HREF=\"#",
-            seg(htmHyp, i, pos - 1),
-            "\">",
-            seg(htmHyp, i, pos - 1),
-            "</A>",
-            right(htmHyp, pos),
-            NULL));
-        /* Break out of loop if we hit the end */
-        pos += 16 + len(seg(htmHyp, i, pos - 1)) + 1;
-        if (!instr(i, htmHyp, ",")) break;
-        i = pos;
-      }
-      /*** End of 9-Sep-2010 ***/
-
-      /* 2/8/02 Add a space after each comma so very long hypotheses
-         lists will wrap in an HTML table cell, e.g. gomaex3 in ql.mm */
-      pos = instr(1, htmHyp, ",");
-      while (pos) {
-        let(&htmHyp, cat(left(htmHyp, pos), " ", right(htmHyp, pos + 1), NULL));
-        pos = instr(pos + 1, htmHyp, ",");
-      }
-
-      /* if (!strcmp(tex, "$e") || !strcmp(tex, "$f")) { */ /* Old */
-      if (refType == 'e' || refType == 'f') { /* 14-Sep-2010 nm Speedup */
-        /* A hypothesis - don't include link */
-        printLongLine(cat("<TR ALIGN=LEFT><TD>", htmStep, "</TD><TD>",
-            htmHyp, "</TD><TD>", htmRef,
-            "</TD><TD>",
-            htmStepTag, /* 30-May-2015 nm */
-                /* Put the <A NAME=...></A> tag at start of math symbol cell */
-            NULL), "", "\"");
-      } else {
-        if (hypStmt <= 0) {
-          printLongLine(cat("<TR ALIGN=LEFT><TD>", htmStep, "</TD><TD>",
-              htmHyp, "</TD><TD><A HREF=\"", htmRef, ".html\">", htmRef,
-              "</A></TD><TD>",
-              htmStepTag, /* 30-May-2015 nm */
-                /* Put the <A NAME=...></A> tag at start of math symbol cell */
-              NULL), "", "\"");
-        } else {
-          /* Include step number reference.  The idea is that this will
-             help the user to recognized "important" (vs. early trivial
-             logic) steps.  This prints a small pink statement number
-             after the hypothesis statement label. */
-          let(&tmp, "");
-          tmp = pinkHTML(hypStmt);
-          /* Special case for pink number here: to make table more compact,
-             we allow the pink number to break off of the href by changing
-             PINK_NBSP to a space.  Elsewhere we don't allow such line
-             breaks. */
-          /* 10/14/02 I decided I don't like it so I took it out. */
-          /*******
-          let(&tmp, cat(" ", right(tmp, (long)strlen(PINK_NBSP) + 1), NULL));
-          *******/
-
-#define TOOLTIP
-#ifdef TOOLTIP
-          /* 19-Nov-2007 nm Get description for mod below */
-          let(&descr, ""); /* Deallocate previous description */
-          descr = getDescription(hypStmt);
-          let(&descr, edit(descr, 4 + 16)); /* Discard lf/cr; reduce spaces */
-#define MAX_DESCR_LEN 87
-          if (strlen(descr) > MAX_DESCR_LEN) { /* Truncate long lines */
-            i = MAX_DESCR_LEN - 3;
-            while (i >= 0) { /* Get to previous word boundary */
-              if (descr[i] == ' ') break;
-              i--;
-            }
-            let(&descr, cat(left(descr, i), "...", NULL));
-          }
-          i = 0;
-          while (descr[i] != 0) { /* Convert double quote to single */
-            descr[i] = (char)(descr[i] == '"' ? '\'' : descr[i]);
-            i++;
-          }
-#endif
-
-          printLongLine(cat("<TR ALIGN=LEFT><TD>", htmStep, "</TD><TD>",
-              htmHyp, "</TD><TD><A HREF=\"", htmRef, ".html\"",
-
-#ifdef TOOLTIP
-              /* 19-Nov-2007 nm Put in a TITLE entry for mouseover tooltip,
-                 as suggested by Reinder Verlinde */
-              " TITLE=\"", descr, "\"",
-#endif
-
-              ">", htmRef,
-              "</A>", tmp,
-              "</TD><TD>",
-              htmStepTag, /* 30-May-2015 nm */
-                /* Put the <A NAME=...></A> tag at start of math symbol cell */
-              NULL), "", "\"");
-        }
-      }
-#ifdef INDENT_HTML_PROOFS
-      /* nm 3-Feb-04 Experiment to indent web proof displays */
-      let(&tmp, "");
-      for (i = 1; i <= indentationLevel; i++) {
-        let(&tmp, cat(tmp, ". ", NULL));
-      }
-      let(&tmp, cat("<SPAN CLASS=i>",
-          tmp,
-          str((double)(indentationLevel + INDENTATION_OFFSET)), "</SPAN>",
-          NULL));
-      printLongLine(tmp, "", "\"");
-      let(&tmp, "");
-#endif
-    } /* strlen(sPrefix) */
-  } /* g_htmlFlag */
-  let(&tex, ""); /* Deallocate */
-  let(&sPrefix, ""); /* Deallocate */
-
-  let(&tex, "");
-  tex = getTexLongMath(mathString, hypStmt); /* 20-Sep-03 */
-  let(&texLine, cat(texLine, tex, NULL));
-
-  if (!g_htmlFlag) {  /* LaTeX */
-    /* 27-Jul-05 nm Added for new LaTeX (!g_oldTexFlag) */
-    if (!g_oldTexFlag) {
-      /* 14-Sep-2010 nm */
-      if (refType == 'e' || refType == 'f') {
-        /* A hypothesis - don't include \ref{} */
-        printLongLine(cat("  ",
-            /* If not first step, so print "\\" LaTeX line break */
-            !strcmp(htmStep, "1") ? "" : "\\\\ ",
-            htmStep,  /* Step number */
-            " && ",
-            " & ",
-            texLine,
-            /* Don't put space to help prevent bad line break */
-            "&\\text{Hyp~",
-            /* The following puts a hypothesis number such as "2" if
-               $e label is "abc.2"; if no ".", will be whole label */
-            right(htmRef, instr(1, htmRef, ".") + 1),
-            "}\\notag%",
-            /* Add full label as LaTeX comment - note lack of space after
-               "%" above to prevent bad line break */
-            htmRef, NULL),
-            "    \\notag \\\\ && & \\qquad ",  /* Continuation line prefix */
-            " ");
-      } else {
-        printLongLine(cat("  ",
-            /* If not first step, so print "\\" LaTeX line break */
-            !strcmp(htmStep, "1") ? "" : "\\\\ ",
-            htmStep,  /* Step number */
-            " && ",
-
-            /* Local label if any e.g. "@2:" */ /* 26-Jun-2017 nm */
-            (htmLocLab[0] != 0) ? cat(htmLocLab, "\\ ", NULL) : "",
-
-            " & ",
-            texLine,
-            /* Don't put space to help prevent bad line break */
-
-            /* 1-May-2017 nm Surround \ref with \mbox for non-math-mode
-               symbolic labels (due to \tag{..} in mmcmds.c).  Also,
-               move hypotheses to after referenced label */
-            "&",
-            "(",
-
-            /* Don't make local label a \ref */ /* 26-Jun-2017 nm */
-            (htmRef[0] != '@') ?
-                cat("\\mbox{\\ref{eq:", htmRef, "}}", NULL)
-                : htmRef,
-
-            htmHyp[0] ? "," : "",
-            htmHyp,
-            ")\\notag", NULL),
-            /* before 1-May-2017:
-            "&", htmHyp, htmHyp[0] ? "," : "",
-            "(\\ref{eq:", htmRef, "})\\notag", NULL),
-            */
-
-            "    \\notag \\\\ && & \\qquad ",  /* Continuation line prefix */
-            " ");
-      }
-      /* 14-Sep-2010 nm - commented out below */
-      /* printLongLine(texLine, "", " "); */
-      /* print2("\\end{eqnarray}\n"); */
-      /* 6 && \vdash& ( B \ton_3 B ) \ton_3 A & (\ref{eq:q2}),4,5 \notag \\ */
-      /*print2(" & (\\ref{eq:%s}%s \\notag\n",???,??? );*/
-    } else {
-      printLongLine(texLine, "", "\\");
-      print2("\\endm\n");
-    }
-  } else {  /* HTML */
-    printLongLine(cat(texLine, "</TD></TR>", NULL), "", "\"");
-  }
-
-  g_outputToString = 0; /* Restore normal output */
-  fprintf(g_texFilePtr, "%s", g_printString);
-  let(&g_printString, "");
-
-  let(&descr, ""); /*Deallocate */  /* 17-Nov-2007 nm */
-  let(&htmStep, ""); /* Deallocate */
-  let(&htmStepTag, ""); /* Deallocate */
-  let(&htmHyp, ""); /* Deallocate */
-  let(&htmRef, ""); /* Deallocate */
-  let(&htmLocLab, ""); /* Deallocate */ /* 26-Jun-2017 nm */
-  let(&tmp, ""); /* Deallocate */
-  let(&texLine, ""); /* Deallocate */
-  let(&tex, ""); /* Deallocate */
-} /* printTexLongMath */
-
-void printTexTrailer(flag texTrailerFlag) {
-
-  if (texTrailerFlag) {
-    g_outputToString = 1; /* Redirect print2 and printLongLine to g_printString */
-    if (!g_htmlFlag) let(&g_printString, "");
-        /* May have stuff to be printed 7/4/98 */
-    if (!g_htmlFlag) {
-      print2("\\end{document}\n");
-    } else {
-      /*******  10/10/02 Moved to mmcmds.c so it can be printed immediately
-                after proof; made g_htmlVarColor global for this
-      print2("<FONT SIZE=-1 FACE=sans-serif>Colors of variables:\n");
-      printLongLine(cat(g_htmlVarColor, "</FONT>", NULL), "", " ");
-      *******/
-      print2("</TABLE></CENTER>\n");
-      print2("<TABLE BORDER=0 WIDTH=\"100%s\">\n", "%");
-      print2("<TR><TD WIDTH=\"25%s\">&nbsp;</TD>\n", "%");
-      print2("<TD ALIGN=CENTER VALIGN=BOTTOM>\n");
-      print2("<FONT SIZE=-2 FACE=sans-serif>\n");
-      /*
-      print2("<A HREF=\"definitions.html\">Definition list</A> |\n");
-      print2("<A HREF=\"theorems.html\">Theorem list</A><BR>\n");
-      */
-      /*
-      print2("Copyright &copy; 2002 \n");
-      print2("The Metamath Home Page is\n");
-      */
-      /*
-      print2("<A HREF=\"http://metamath.org\">metamath.org mirrors</A>\n");
-      */
-      print2("Copyright terms:\n");
-      print2("<A HREF=\"../copyright.html#pd\">Public domain</A>\n");
-      print2("</FONT></TD><TD ALIGN=RIGHT VALIGN=BOTTOM WIDTH=\"25%s\">\n",
-          "%");
-      print2("<FONT SIZE=-2 FACE=sans-serif>\n");
-      print2("<A HREF=\"http://validator.w3.org/check?uri=referer\">\n");
-      print2("W3C validator</A>\n");
-      print2("</FONT></TD></TR></TABLE>\n");
-
-      /* Todo: Decide to use or not use this */
-      /*
-      print2("<SCRIPT SRC=\"http://www.google-analytics.com/urchin.js\"\n");
-      print2("  TYPE=\"text/javascript\">\n");
-      print2("</SCRIPT>\n");
-      print2("<SCRIPT TYPE=\"text/javascript\">\n");
-      print2("  _uacct = \"UA-1862729-1\";\n");
-      print2("  urchinTracker();\n");
-      print2("</SCRIPT>\n");
-      */
-
-      print2("</BODY></HTML>\n");
-    }
-    g_outputToString = 0; /* Restore normal output */
-    fprintf(g_texFilePtr, "%s", g_printString);
-    let(&g_printString, "");
-  }
-
-} /* printTexTrailer */
-
-
-/* Added 4-Dec-03 - WRITE THEOREM_LIST command:  Write out theorem list
-   into mmtheorems.html, mmtheorems1.html,... */
-void writeTheoremList(long theoremsPerPage, flag showLemmas, flag noVersioning)
-{
-  nmbrString *nmbrStmtNmbr = NULL_NMBRSTRING;
-  long pages, page, assertion, assertions, lastAssertion;
-  long s, p, i1, i2;
-  vstring str1 = "";
-  vstring str3 = "";
-  vstring str4 = "";
-  vstring prevNextLinks = "";
-  long partCntr;        /* Counter for hugeHdr */ /* 21-Jun-2014 */
-  long sectionCntr;     /* Counter for bigHdr */ /* 21-Jun-2014 */
-  long subsectionCntr;  /* Counter for smallHdr */ /* 21-Jun-2014 */
-  long subsubsectionCntr;  /* Counter for tinyHdr */ /* 21-Aug-2017 */
-  vstring outputFileName = "";
-  FILE *outputFilePtr;
-  long passNumber; /* 18-Oct-2015 1/2 for summary/detailed table of contents */
-
-  /* 31-Jul-2006 for table of contents mod */
-  vstring hugeHdr = ""; /* 21-Jun-2014 nm */
-  vstring bigHdr = "";
-  vstring smallHdr = "";
-  vstring tinyHdr = ""; /* 21-Aug-2017 nm */
-  vstring hugeHdrComment = ""; /* 8-May-2015 nm */
-  vstring bigHdrComment = ""; /* 8-May-2015 nm */
-  vstring smallHdrComment = ""; /* 8-May-2015 nm */
-  vstring tinyHdrComment = ""; /* 21-Aug-2017 nm */
-  long stmt, i;
-  pntrString *pntrHugeHdr = NULL_PNTRSTRING;
-  pntrString *pntrBigHdr = NULL_PNTRSTRING;
-  pntrString *pntrSmallHdr = NULL_PNTRSTRING;
-  pntrString *pntrTinyHdr = NULL_PNTRSTRING; /* 21-Aug-2017 nm */
-  pntrString *pntrHugeHdrComment = NULL_PNTRSTRING; /* 8-May-2015 nm */
-  pntrString *pntrBigHdrComment = NULL_PNTRSTRING; /* 8-May-2015 nm */
-  pntrString *pntrSmallHdrComment = NULL_PNTRSTRING; /* 8-May-2015 nm */
-  pntrString *pntrTinyHdrComment = NULL_PNTRSTRING; /* 21-Aug-2017 nm */
-  vstring hdrCommentMarker = ""; /* 4-Aug-2018 nm */
-  vstring hdrCommentAnchor = ""; /* 20-Oct-2018 nm */
-  flag hdrCommentAnchorDone = 0; /* 20-Oct-2018 nm */
-
-  /* Populate the statement map */
-  /* ? ? ? Future:  is assertions same as g_Statement[g_statements].pinkNumber? */
-  nmbrLet(&nmbrStmtNmbr, nmbrSpace(g_statements + 1));
-  assertions = 0; /* Number of $p's + $a's */
-  for (s = 1; s <= g_statements; s++) {
-    if (g_Statement[s].type == a_ || g_Statement[s].type == p_) {
-      assertions++; /* Corresponds to pink number */
-      nmbrStmtNmbr[assertions] = s;
-    }
-  }
-  if (assertions != g_Statement[g_statements].pinkNumber) bug(2328);
-
-  /* 31-Jul-2006 nm Table of contents mod */
-  /* Allocate array for section headers found */
-  pntrLet(&pntrHugeHdr, pntrSpace(g_statements + 1));
-  pntrLet(&pntrBigHdr, pntrSpace(g_statements + 1));
-  pntrLet(&pntrSmallHdr, pntrSpace(g_statements + 1));
-  pntrLet(&pntrTinyHdr, pntrSpace(g_statements + 1)); /* 21-Aug-2017 nm */
-  pntrLet(&pntrHugeHdrComment, pntrSpace(g_statements + 1)); /* 8-May-2015 nm */
-  pntrLet(&pntrBigHdrComment, pntrSpace(g_statements + 1)); /* 8-May-2015 nm */
-  pntrLet(&pntrSmallHdrComment, pntrSpace(g_statements + 1)); /* 8-May-2015 nm */
-  pntrLet(&pntrTinyHdrComment, pntrSpace(g_statements + 1)); /* 21-Aug-2017 nm */
-
-  pages = ((assertions - 1) / theoremsPerPage) + 1;
-  /* for (page = 1; page <= pages; page++) { */
-  /* 8-May-2015 nm */
-  for (page = 0; page <= pages; page++) {
-    /* Open file */
-    let(&outputFileName,
-        /* cat("mmtheorems", (page > 1) ? str(page) : "", ".html", NULL)); */
-        /* 8-May-2015 nm */
-        cat("mmtheorems", (page > 0) ? str((double)page) : "", ".html", NULL));
-    print2("Creating %s\n", outputFileName);
-    outputFilePtr = fSafeOpen(outputFileName, "w", noVersioning);
-    if (!outputFilePtr) goto TL_ABORT; /* Couldn't open it (error msg was provided)*/
-
-    /* Output header */
-    /* TODO 14-Jan-2016: why aren't we using printTexHeader? */
-
-    g_outputToString = 1;
-    print2(
-        "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n");
-    print2(     "    \"http://www.w3.org/TR/html4/loose.dtd\">\n");
-    print2("<HTML LANG=\"EN-US\">\n");
-    print2("<HEAD>\n");
-    print2("%s%s\n", "<META HTTP-EQUIV=\"Content-Type\" ",
-        "CONTENT=\"text/html; charset=iso-8859-1\">");
-    /* 13-Aug-2016 nm */
-    /* Improve mobile device display per David A. Wheeler */
-    print2(
-"<META NAME=\"viewport\" CONTENT=\"width=device-width, initial-scale=1.0\">\n"
-        );
-
-    print2("<STYLE TYPE=\"text/css\">\n");
-    print2("<!--\n");
-    /* 15-Apr-2015 nm - align math symbol images to text */
-    print2("img { margin-bottom: -4px }\n");
-#ifndef RAINBOW_OPTION
-    /* Print style sheet for pink number that goes after statement label */
-    print2(".p { font-family: \"Arial Narrow\";\n");
-    print2("     font-size: x-small;\n");
-    /* Strip off quotes from color (css doesn't like them) */
-    printLongLine(cat("     color: ", seg(PINK_NUMBER_COLOR, 2,
-        (long)strlen(PINK_NUMBER_COLOR) - 1), ";", NULL), "", "&");
-    print2("   }\n");
-#else
-    /* Print style sheet for colored number that goes after statement label */
-    print2(".r { font-family: \"Arial Narrow\";\n");
-    print2("     font-size: x-small;\n");
-    print2("   }\n");
-#endif
-    print2("-->\n");
-    print2("</STYLE>\n");
-    printLongLine(g_htmlCSS, "", " ");
-
-    /*
-    print2("%s\n", cat("<TITLE>", htmlTitle, " - ",
-        / * Strip off ".html" * /
-        left(outputFileName, (long)strlen(outputFileName) - 5),
-        "</TITLE>", NULL));
-    */
-    /* 4-Jun-06 nm - Put page name before "Metamath Proof Explorer" etc. */
-    /* 15-Nov-2015 nm - Change print2() to printLongLine() */
-    printLongLine(cat("<TITLE>",
-        /* Strip off ".html" */
-        /*
-        left(outputFileName, (long)strlen(outputFileName) - 5),
-        */
-
-        /* "List p. ", str(page), */ /* 21-Jun-2014 */
-        /* 9-May-2015 nm */
-        ((page == 0)
-            ? "TOC of Theorem List"
-            : cat("P. ", str((double)page), " of Theorem List", NULL)),
-
-        " - ",
-        htmlTitle,
-        "</TITLE>",
-        NULL), "", "\"");
-    /* Icon for bookmark */
-    print2("%s%s\n", "<LINK REL=\"shortcut icon\" HREF=\"favicon.ico\" ",
-        "TYPE=\"image/x-icon\">");
-
-    /* 18-Oct-2015 nm Image alignment fix */
-    print2(
-        "<STYLE TYPE=\"text/css\">\n");
-    print2(
-        "<!--\n");
-    /* Optional information but takes unnecessary file space */
-    /* (change @ to * if uncommenting)
-    print2(
-        "/@ Math symbol GIFs will be shifted down 4 pixels to align with\n");
-    print2(
-        "   normal text for compatibility with various browsers.  The old\n");
-    print2(
-        "   ALIGN=TOP for math symbol images did not align in all browsers\n");
-    print2(
-        "   and should be deleted.  All other images must override this\n");
-    print2(
-        "   shift with STYLE=\"margin-bottom:0px\". @/\n");
-    */
-    print2(
-        "img { margin-bottom: -4px }\n");
-    print2(
-        "-->\n");
-    print2(
-        "</STYLE>\n");
-
-    print2("</HEAD>\n");
-    print2("<BODY BGCOLOR=\"#FFFFFF\">\n");
-    print2("<TABLE BORDER=0 WIDTH=\"100%s\"><TR>\n", "%");
-    print2("<TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%s\"\n", "%");
-    printLongLine(cat("ROWSPAN=2>", g_htmlHome, "</TD>", NULL), "", "\"");
-    printLongLine(cat(
-        "<TD NOWRAP ALIGN=CENTER ROWSPAN=2><FONT SIZE=\"+3\" COLOR=",
-        GREEN_TITLE_COLOR, "><B>", htmlTitle, "</B></FONT>",
-        "<BR><FONT SIZE=\"+2\" COLOR=",      /* 21-Jun-2014 */
-        GREEN_TITLE_COLOR,                   /* 21-Jun-2014 */
-        /* "><B>Statement List (", */
-        /* 9-May-2015 nm */
-        "><B>Theorem List (",
-        ((page == 0)
-            ? "Table of Contents"
-            /* : cat("(p. ", str(page), " of ", str(pages), NULL)), */
-            /* 9-May-2015 nm Remove stray "(" */
-            : cat("p. ", str((double)page), " of ", str((double)pages), NULL)),
-        ")</B></FONT>",
-        NULL), "", "\"");
-
-
-    /* Put Previous/Next links into web page */
-    print2("</TD><TD NOWRAP ALIGN=RIGHT VALIGN=TOP WIDTH=\"25%c\"><FONT\n", '%');
-    print2(" SIZE=-1 FACE=sans-serif>\n");
-
-    /* Output title with current page */
-    /* Output previous and next */
-
-
-    /* 21-Jun-2014 nm Assign prevNextLinks once here since it is used 3 times */
-    let(&prevNextLinks, cat("<A HREF=\"mmtheorems",
-        /*
-        (page > 1)
-            ? ((page - 1 > 1) ? str(page - 1) : "")
-            : ((pages > 1) ? str(pages) : ""),
-        */
-        /* 8-May-2015 */
-        (page > 0)
-            ? ((page - 1 > 0) ? str((double)page - 1) : "")
-            : ((pages > 0) ? str((double)pages) : ""),
-        ".html\">", NULL));
-    /* if (page > 1) { */
-    /* 8-May-2015 */
-    if (page > 0) {
-      let(&prevNextLinks, cat(prevNextLinks,
-          "&lt; Previous</A>&nbsp;&nbsp;", NULL));
-    } else {
-      let(&prevNextLinks, cat(prevNextLinks, "&lt; Wrap</A>&nbsp;&nbsp;", NULL));
-    }
-    let(&prevNextLinks, cat(prevNextLinks, "<A HREF=\"mmtheorems",
-        (page < pages)
-            ? str((double)page + 1)
-            : "",
-        ".html\">", NULL));
-    if (page < pages) {
-      let(&prevNextLinks, cat(prevNextLinks, "Next &gt;</A>", NULL));
-    } else {
-      let(&prevNextLinks, cat(prevNextLinks, "Wrap &gt;</A>", NULL));
-    }
-
-    printLongLine(prevNextLinks,
-        " ",  /* Start continuation line with space */
-        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-    /********* old code that the above printLongLine replaces
-    let(&str1, cat("<A HREF=\"mmtheorems",
-        (page > 1)
-            ? ((page - 1 > 1) ? str(page - 1) : "")
-            : ((pages > 1) ? str(pages) : ""),
-        ".html\">", NULL));
-    if (page > 1) {
-      print2("%s&lt; Previous</A>&nbsp;&nbsp;\n", str1);
-    } else {
-      print2("%s&lt; Wrap</A>&nbsp;&nbsp;\n", str1);
-    }
-    let(&str1, cat("<A HREF=\"mmtheorems",
-        (page < pages)
-            ? str(page + 1)
-            : "",
-        ".html\">", NULL));
-    if (page < pages) {
-      print2("%sNext &gt;</A>\n", str1);
-    } else {
-      print2("%sWrap &gt;</A>\n", str1);
-    }
-    ******************************/
-
-
-    /* Finish up header */
-    /* Print the GIF/Unicode Font choice, if directories are specified */
-    if (htmlDir[0]) {
-      if (g_altHtmlFlag) {
-        /* 4-Aug-2018 nm */
-        print2("</FONT></TD></TR><TR><TD ALIGN=RIGHT><FONT FACE=sans-serif\n");
-        print2("SIZE=-2>Bad symbols? Try the\n");
-        print2("<BR><A HREF=\"%s%s\">GIF\n",
-            htmlDir, outputFileName);
-        print2("version</A>.</FONT></TD>\n");
-        /* 4-Aug-2018 nm - removed Firefox and IE browser references
-        print2("</FONT></TD></TR><TR><TD ALIGN=RIGHT><FONT FACE=sans-serif\n");
-        print2("SIZE=-2>Bad symbols?\n");
-        print2("Use <A HREF=\"http://mozilla.org\">Firefox</A><BR>\n");
-        print2("(or <A HREF=\"%s%s\">GIF version</A> for IE).</FONT></TD>\n",
-            htmlDir, outputFileName);
-        */
-      } else {
-        print2("</FONT></TD></TR><TR><TD ALIGN=RIGHT><FONT FACE=sans-serif\n");
-        print2("SIZE=-2>Browser slow? Try the\n");
-        print2("<BR><A HREF=\"%s%s\">Unicode\n",
-            altHtmlDir, outputFileName);
-        print2("version</A>.</FONT></TD>\n");
-      }
-    }
-    /*print2("</TR></TABLE>\n");*/
-    /*print2("<HR NOSHADE SIZE=1>\n");*/
-
-    /* 4-Aug-2018 nm */
-    /* Make breadcrumb font to match other pages */
-    print2("<TR>\n");
-    print2(
-      "<TD COLSPAN=3 ALIGN=LEFT VALIGN=TOP><FONT SIZE=-2 FACE=sans-serif>\n");
-    print2("<BR>\n");  /* Add a little more vertical space */
-
-    /* Print some useful links */
-    /* print2("<CENTER>\n"); */
-    print2("<A HREF=\"../mm.html\">Mirrors</A>\n");
-    print2("&nbsp;&gt;&nbsp;<A HREF=\"../index.html\">\n");
-    print2("Metamath Home Page</A>\n");
-
-    /* print2("&nbsp;&gt;&nbsp;<A HREF=\"mmset.html\">\n"); */
-    /* 15-Apr-2015 nm */
-    /* Normally, g_htmlBibliography in the .mm file will have the
-       project home page, and we depend on this here rather than
-       extracting from g_htmlHome */
-    print2("&nbsp;&gt;&nbsp;<A HREF=\"%s\">\n", g_htmlBibliography);
-
-    /* print2("MPE Home Page</A>\n"); */
-    /* 15-Apr-2015 nm */
-    /* Put a meaningful abbreviation for the project home page
-       by extracting capital letters from title */
-    let(&str1, "");
-    s = (long)strlen(htmlTitle);
-    for (i = 0; i < s; i++) {
-      if (htmlTitle[i] >= 'A' && htmlTitle[i] <= 'Z') {
-        let(&str1, cat(str1, chr(htmlTitle[i]), NULL));
-      }
-    }
-    print2("%s Home Page</A>\n", str1);
-
-    /* if (page != 1) { */
-    /* 8-May-2015 nm */
-    if (page != 0) {
-      print2("&nbsp;&gt;&nbsp;<A HREF=\"mmtheorems.html\">\n");
-      /* print2("Statement List Contents</A>\n"); */
-      /* 9-May-2015 nm */
-      print2("Theorem List Contents</A>\n");
-    } else {
-      print2("&nbsp;&gt;&nbsp;\n");
-      /* print2("Statement List Contents\n"); */
-      /* 9-May-2015 nm */
-      print2("Theorem List Contents\n");
-    }
-
-    /*****
-    /@ 15-Apr-2015 nm @/
-    /@ Use "g_extHtmlStmt <= g_statements" as an indicator that we're doing
-       Metamath Proof Explorer; the others don't have mmrecent.html pages @/
-    /@if (g_extHtmlStmt <= g_statements) {@/ /@ g_extHtmlStmt = g_statements + 1
-                                              unless mmset.html @/
-    /@ 8-Dec-2017 nm @/
-    if (g_extHtmlStmt < g_mathboxStmt) { /@ g_extHtmlStmt >= g_mathboxStmt
-                                              unless mmset.html @/
-    *****/
-    /* 30-Nov-2019 nm */
-    /* Assume there is a Most Recent page when the .mm has a mathbox stmt
-       (currently set.mm and iset.mm)  */
-    if (g_mathboxStmt < g_statements + 1) {
-      print2("&nbsp;&gt;&nbsp;<A HREF=\"mmrecent.html\">\n");
-      print2("Recent Proofs</A>\n");
-    }
-
-
-    print2("&nbsp; &nbsp; &nbsp; <B><FONT COLOR=%s>\n", GREEN_TITLE_COLOR);
-    print2("This page:</FONT></B> \n");
-    /* 8-May-2015 nm Deleted, since ToC is now separate page: */
-    /*
-    if (page == 1) {
-      print2("<A HREF=\"#mmstmtlst\">Start of list</A>&nbsp; &nbsp; \n");
-    }
-    */
-
-    /* 18-Oct-2015 nm */
-    if (page == 0) {
-      print2(
-          "&nbsp;<A HREF=\"#mmdtoc\">Detailed Table of Contents</A>&nbsp;\n");
-    }
-
-    print2("<A HREF=\"#mmpglst\">Page List</A>\n");
-
-    /* 4-Aug-2018 nm */
-    /* Change breadcrumb font to match other pages */
-    print2("</FONT>\n");
-    print2("</TD>\n");
-    print2("</TR></TABLE>\n");
-
-    /* print2("</CENTER>\n"); */
-    print2("<HR NOSHADE SIZE=1>\n");
-
-    /* Write out HTML page so far */
-    fprintf(outputFilePtr, "%s", g_printString);
-    g_outputToString = 0;
-    let(&g_printString, "");
-
-    /***** 21-Jun-2014 Moved to bottom of page ********
-    /@ Output links to the other pages @/
-    fprintf(outputFilePtr, "Jump to page: \n");
-    for (p = 1; p <= pages; p++) {
-
-      /@ Construct the pink number range @/
-      let(&str3, "");
-      str3 = pinkRangeHTML(
-          nmbrStmtNmbr[(p - 1) @ theoremsPerPage + 1],
-          (p < pages) ?
-            nmbrStmtNmbr[p @ theoremsPerPage] :
-            nmbrStmtNmbr[assertions]);
-
-      /@ 31-Jul-2006 nm Change "1" to "Contents + 1" @/
-      if (p == page) {
-        let(&str1,
-            (p == 1) ? "Contents + 1" : str(p) /@ 31-Jul-2006 nm @/
-            ); /@ Current page shouldn't have link to self @/
-      } else {
-        let(&str1, cat("<A HREF=\"mmtheorems",
-            (p == 1) ? "" : str(p),
-            /@ (p == 1) ? ".html#mmtc\">" : ".html\">", @/ /@ 31-Aug-2006 nm @/
-            ".html\">", /@ 8-Feb-2007 nm Friendlier, because you can start
-                  scrolling through the page before it finishes loading,
-                  without its jumping to #mmtc (start of Table of Contents)
-                  when it's done. @/
-            (p == 1) ? "Contents + 1" : str(p) /@ 31-Jul-2006 nm @/
-            , "</A>", NULL));
-      }
-      let(&str1, cat(str1, PINK_NBSP, str3, NULL));
-      fprintf(outputFilePtr, "%s\n", str1);
-    }
-    ******** end of 21-Jun-2014 move *******/
-
-    /* 31-Jul-2006 nm Add table of contents to first WRITE THEOREM page */
-    /* if (page == 1) { */ /* We're on page 1 */
-    /* 8-May-2015 nm */
-    if (page == 0) {  /* We're on ToC page */
-
-      /* Pass 1: table of contents summary; pass 2: detail */ /* 18-Oct-2015 */
-      for (passNumber = 1; passNumber <= 2; passNumber++) {
-
-        g_outputToString = 1;
-
-        /* 18-Oct-2015 deleted
-        print2(
-        "<P><CENTER><A NAME=\"mmtc\"></A><B>Table of Contents</B></CENTER>\n");
-        */
-        /* 18-Oct-2015 nm */
-        if (passNumber == 1) {
-
-/* 24-Oct-2018 nm Temporary hopefully */
-/* 31-Oct-2018 nm Implemented workaround; see below under this date */
-/*
-print2("<P><CENTER><B><FONT COLOR=RED>The Chrome browser has a\n");
-print2("bug that hyperlinks to incorrect anchors.\n");
-print2("If you click on PART 2 in the summary, it should go to PART 2 in\n");
-print2("the detailed section.  If it goes to PART 1, your browser has the bug.\n");
-print2("If your Chrome version works, let me (Norm Megill) know\n");
-print2("the version number so I can mention it here.  Otherwise you will\n");
-print2("need another browser to navigate. This page works with Firefox\n");
-print2("and Internet Explorer and passes validator.w3.org.\n");
-print2("</FONT></B></CENTER>\n");
-*/
-
-
-          print2(
-              "<P><CENTER><B>Table of Contents Summary</B></CENTER>\n");
-        } else {
-          print2(
-  "<P><CENTER><A NAME=\"mmdtoc\"></A><B>Detailed Table of Contents</B><BR>\n");
-
-          /* 4-Aug-2018 nm */
-          print2(
-           "<B>(* means the section header has a description)</B></CENTER>\n");
-
-        }
-
-        fprintf(outputFilePtr, "%s", g_printString);
-
-        g_outputToString = 0;
-        let(&g_printString, "");
-
-        let(&hugeHdr, "");
-        let(&bigHdr, "");
-        let(&smallHdr, "");
-        let(&tinyHdr, "");
-        let(&hugeHdrComment, "");
-        let(&bigHdrComment, "");
-        let(&smallHdrComment, "");
-        let(&tinyHdrComment, "");
-        partCntr = 0;    /* Initialize counters */    /* 21-Jun-2014 */
-        sectionCntr = 0;
-        subsectionCntr = 0;
-        subsubsectionCntr = 0; /* 21-Aug-2017 nm */
-        for (stmt = 1; stmt <= g_statements; stmt++) {
-
-          /* 18-Dec-2016 nm moved to below the "if"
-          getSectionHeadings(stmt, &hugeHdr, &bigHdr, &smallHdr,
-              /@ 5-May-2015 nm @/
-              &hugeHdrComment, &bigHdrComment, &smallHdrComment);
-          */
-
-          /* Output the headers for $a and $p statements */
-          if (g_Statement[stmt].type == p_ || g_Statement[stmt].type == a_) {
-            hdrCommentAnchorDone = 0; /* 20-Oct-2018 nm */
-            getSectionHeadings(stmt, &hugeHdr, &bigHdr, &smallHdr,
-                &tinyHdr, /* 21-Aug-2017 nm */
-                /* 5-May-2015 nm */
-                &hugeHdrComment, &bigHdrComment, &smallHdrComment,
-                &tinyHdrComment,
-                0, /* fineResolution */
-                0  /* fullComment */);
-            if (hugeHdr[0] || bigHdr[0] || smallHdr[0] || tinyHdr[0]) {
-              /* Write to the table of contents */
-              g_outputToString = 1;
-              i = ((g_Statement[stmt].pinkNumber - 1) / theoremsPerPage)
-                  + 1; /* Page # */
-              /* let(&str3, cat("mmtheorems", (i == 1) ? "" : str(i), ".html#", */
-                          /* Note that page 1 has no number after mmtheorems */
-              /* 8-May-2015 nm */
-              let(&str3, cat("mmtheorems", str((double)i), ".html#",
-                  /* g_Statement[stmt].labelName, NULL)); */
-                  "mm", str((double)(g_Statement[stmt].pinkNumber)), NULL));
-                     /* Link to page/location - no theorem can be named "mm*" */
-              let(&str4, "");
-              str4 = pinkHTML(stmt);
-              /*let(&str4, right(str4, (long)strlen(PINK_NBSP) + 1));*/
-                                                          /* Discard "&nbsp;" */
-              if (hugeHdr[0]) {    /* 21-Jun-2014 nm */
-
-                /* Create part number */
-                partCntr++;
-                sectionCntr = 0;
-                subsectionCntr = 0;
-                subsubsectionCntr = 0; /* 21-Aug-2017 nm */
-                let(&hugeHdr, cat("PART ", str((double)partCntr), "&nbsp;&nbsp;",
-                    hugeHdr, NULL));
-
-                /* 4-Aug-2018 nm */
-                /* Put an asterisk before the header if the header has
-                   a comment */
-                if (hugeHdrComment[0] != 0 && passNumber == 2) {
-                  let(&hdrCommentMarker, "*");
-                  /* 20-Oct-2018 nm */
-                  if (hdrCommentAnchorDone == 0) {
-                    let(&hdrCommentAnchor, cat(
-                        "<A NAME=\"",
-                        g_Statement[stmt].labelName, "\"></A>",
-
-                        /* 27-Aug-2019 nm "&#8203;" is a "zero-width" space to
-                           workaround Chrome bug that jumps to wrong anchor. */
-                        "&#8203;",
-
-                        NULL));
-                    hdrCommentAnchorDone = 1;
-                  } else {
-                    let(&hdrCommentAnchor, "");
-                  }
-                } else {
-                  let(&hdrCommentMarker, "");
-                  let(&hdrCommentAnchor, "");
-                }
-
-                printLongLine(cat(
-
-                    /* 18-Oct-2015 nm */
-                    /* In detailed section, add an anchor to reach it from
-                       summary section */
-                    (passNumber == 2) ?
-                        cat("<A NAME=\"dtl:", str((double)partCntr),
-                            "\"></A>",
-
-                            /* 27-Aug-2019 nm "&#8203;" is a "zero-width" space to
-                               workaround Chrome bug that jumps to wrong anchor. */
-                            "&#8203;",
-
-                            NULL) : "",
-
-                    /* 29-Jul-2008 nm Add an anchor to the "sandbox" theorem
-                       for use by mmrecent.html */
-                    /* 21-Jun-2014 nm We use "sandbox:bighdr" for both here and
-                       below so that either huge or big header type could
-                       be used to start mathbox sections */
-                    (stmt == g_mathboxStmt && bigHdr[0] == 0
-                          /* 4-Aug-2018 nm */
-                          && passNumber == 1 /* Only in summary TOC */
-                          ) ?
-                        /* Note the colon so it won't conflict w/ theorem
-                           name anchor */
-                        /*"<A NAME=\"sandbox:bighdr\"></A>" : "",*/
-                        /* 27-Aug-2019 nm "&#8203;" is a "zero-width" space to
-                           workaround Chrome bug that jumps to wrong anchor. */
-                        "<A NAME=\"sandbox:bighdr\"></A>&#8203;" : "",
-
-                    hdrCommentAnchor, /* 20-Oct-2018 nm */
-                    "<A HREF=\"",
-
-                    /* 18-Oct-2015 nm */
-                    (passNumber == 1) ?
-                        cat("#dtl:", str((double)partCntr), NULL)  /* Link to detailed toc */
-                        : cat(str3, "h", NULL), /* Link to thm list */
-
-                    "\"><B>",
-                    hdrCommentMarker, /* 4-Aug-2018 nm */
-                    hugeHdr, "</B></A>",
-                    /*
-                    " &nbsp; <A HREF=\"",
-                    g_Statement[stmt].labelName, ".html\">",
-                    g_Statement[stmt].labelName, "</A>",
-                    str4,
-                    */
-                    "<BR>", NULL),
-                    " ",  /* Start continuation line with space */
-                    "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-                if (passNumber == 2) { /* 18-Oct-2015 nm */
-                  /* Assign to array for use during theorem output */
-                  let((vstring *)(&pntrHugeHdr[stmt]), hugeHdr);
-                  let((vstring *)(&pntrHugeHdrComment[stmt]), hugeHdrComment);
-                }
-                let(&hugeHdr, "");
-                let(&hugeHdrComment, "");
-              }
-              if (bigHdr[0]) {
-
-                /* Create section number */  /* 21-Jun-2014 */
-                sectionCntr++;
-                subsectionCntr = 0;
-                subsubsectionCntr = 0; /* 21-Aug-2017 nm */
-                let(&bigHdr, cat(str((double)partCntr), ".", str((double)sectionCntr),
-                    "&nbsp;&nbsp;",
-                    bigHdr, NULL));
-
-                /* 4-Aug-2018 nm */
-                /* Put an asterisk before the header if the header has
-                   a comment */
-                if (bigHdrComment[0] != 0 && passNumber == 2) {
-                  let(&hdrCommentMarker, "*");
-                  /* 20-Oct-2018 nm */
-                  if (hdrCommentAnchorDone == 0) {
-                    let(&hdrCommentAnchor, cat(
-                        "<A NAME=\"",
-                        g_Statement[stmt].labelName, "\"></A>",
-
-                        /* 27-Aug-2019 nm "&#8203;" is a "zero-width" space to
-                           workaround Chrome bug that jumps to wrong anchor. */
-                        "&#8203;",
-
-                        NULL));
-                    hdrCommentAnchorDone = 1;
-                  } else {
-                    let(&hdrCommentAnchor, "");
-                  }
-                } else {
-                  let(&hdrCommentMarker, "");
-                  let(&hdrCommentAnchor, "");
-                }
-
-                printLongLine(cat(
-                    "&nbsp; &nbsp; &nbsp; ", /* Indentation spacing */
-
-                    /* 18-Oct-2015 nm */
-                    /* In detailed section, add an anchor to reach it from
-                       summary section */
-                    (passNumber == 2) ?
-                        cat("<A NAME=\"dtl:", str((double)partCntr), ".",
-                            str((double)sectionCntr), "\"></A>",
-
-                            /* 27-Aug-2019 nm "&#8203;" is a "zero-width" space to
-                               workaround Chrome bug that jumps to wrong anchor. */
-                            "&#8203;",
-
-                            NULL)
-                        : "",
-
-                    /* 29-Jul-2008 nm Add an anchor to the "sandbox" theorem
-                       for use by mmrecent.html */
-                    (stmt == g_mathboxStmt
-                          /* 4-Aug-2018 nm */
-                          && passNumber == 1 /* Only in summary TOC */
-                          ) ?
-                        /* Note the colon so it won't conflict w/ theorem
-                           name anchor */
-                        /*"<A NAME=\"sandbox:bighdr\"></A>" : "",*/
-                        /* 27-Aug-2019 nm "&#8203;" is a "zero-width" space to
-                           workaround Chrome bug that jumps to wrong anchor. */
-                        "<A NAME=\"sandbox:bighdr\"></A>&#8203;" : "",
-                    hdrCommentAnchor, /* 20-Oct-2018 nm */
-                    "<A HREF=\"",
-
-                    /* 18-Oct-2015 nm */
-                    (passNumber == 1) ?
-                         cat("#dtl:", str((double)partCntr), ".",
-                             str((double)sectionCntr),
-                             NULL)   /* Link to detailed toc */
-                        : cat(str3, "b", NULL), /* Link to thm list */
-
-                    "\"><B>",
-                    hdrCommentMarker, /* 4-Aug-2018 */
-                    bigHdr, "</B></A>",
-                    /*
-                    " &nbsp; <A HREF=\"",
-                    g_Statement[stmt].labelName, ".html\">",
-                    g_Statement[stmt].labelName, "</A>",
-                    str4,
-                    */
-                    "<BR>", NULL),
-                    " ",  /* Start continuation line with space */
-                    "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-                if (passNumber == 2) { /* 18-Oct-2015 nm */
-                  /* Assign to array for use during theorem list output */
-                  let((vstring *)(&pntrBigHdr[stmt]), bigHdr);
-                  let((vstring *)(&pntrBigHdrComment[stmt]), bigHdrComment);
-                }
-                let(&bigHdr, "");
-                let(&bigHdrComment, "");
-              }
-              if (smallHdr[0]
-                  && passNumber == 2) {  /* Skip in pass 1 (summary) */
-
-                /* Create subsection number */  /* 21-Jun-2014 */
-                subsectionCntr++;
-                subsubsectionCntr = 0; /* 21-Aug-2017 nm */
-                let(&smallHdr, cat(str((double)partCntr), ".",
-                    str((double)sectionCntr),
-                    ".", str((double)subsectionCntr), "&nbsp;&nbsp;",
-                    smallHdr, NULL));
-
-                /* 4-Aug-2018 nm */
-                /* Put an asterisk before the header if the header has
-                   a comment */
-                if (smallHdrComment[0] != 0 && passNumber == 2) {
-                  let(&hdrCommentMarker, "*");
-                  /* 20-Oct-2018 nm */
-                  if (hdrCommentAnchorDone == 0) {
-                    let(&hdrCommentAnchor, cat("<A NAME=\"",
-                        g_Statement[stmt].labelName, "\"></A>",
-
-                        /* 27-Aug-2019 nm "&#8203;" is a "zero-width" space to
-                           workaround Chrome bug that jumps to wrong anchor. */
-                        "&#8203;",
-
-                        NULL));
-                    hdrCommentAnchorDone = 1;
-                  } else {
-                    let(&hdrCommentAnchor, "");
-                  }
-                } else {
-                  let(&hdrCommentMarker, "");
-                  let(&hdrCommentAnchor, "");
-                }
-
-                printLongLine(cat("&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ",
-
-                    /* 23-May-2008 nm Add an anchor to the "sandbox" theorem
-                       for use by mmrecent.html */
-                    /*
-                    !strcmp(g_Statement[stmt].labelName, "sandbox") ?
-                        "<A NAME=\"sandbox:smallhdr\"></A>&#8203;" : "",
-                    */
-
-                    hdrCommentAnchor, /* 20-Oct-2018 nm */
-                    "<A HREF=\"", str3, "s\">",
-                    hdrCommentMarker, /* 4-Aug-2018 */
-                    smallHdr, "</A>",
-                    " &nbsp; <A HREF=\"",
-                    g_Statement[stmt].labelName, ".html\">",
-                    g_Statement[stmt].labelName, "</A>",
-                    str4,
-                    "<BR>", NULL),
-                    " ",  /* Start continuation line with space */
-                    "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-                /* Assign to array for use during theorem output */
-                let((vstring *)(&pntrSmallHdr[stmt]), smallHdr);
-                let(&smallHdr, "");
-                let((vstring *)(&pntrSmallHdrComment[stmt]), smallHdrComment);
-                let(&smallHdrComment, "");
-              }
-
-              /* Added 21-Aug-2017 nm */
-              if (tinyHdr[0]
-                  && passNumber == 2) {  /* Skip in pass 1 (summary) */
-
-                /* Create subsection number */  /* 21-Jun-2014 */
-                subsubsectionCntr++;
-                let(&tinyHdr, cat(str((double)partCntr), ".",
-                    str((double)sectionCntr),
-                    ".", str((double)subsectionCntr),
-                    ".", str((double)subsubsectionCntr), "&nbsp;&nbsp;",
-                    tinyHdr, NULL));
-
-                /* 4-Aug-2018 nm */
-                /* Put an asterisk before the header if the header has
-                   a comment */
-                if (tinyHdrComment[0] != 0 && passNumber == 2) {
-                  let(&hdrCommentMarker, "*");
-                  /* 20-Oct-2018 nm */
-                  if (hdrCommentAnchorDone == 0) {
-                    let(&hdrCommentAnchor, cat("<A NAME=\"",
-                        g_Statement[stmt].labelName, "\"></A> ",
-
-                        /* 27-Aug-2019 nm "&#8203;" is a "zero-width" space to
-                           workaround Chrome bug that jumps to wrong anchor. */
-                        "&#8203;",
-
-                        NULL));
-                    hdrCommentAnchorDone = 1;
-                  } else {
-                    let(&hdrCommentAnchor, "");
-                  }
-                } else {
-                  let(&hdrCommentMarker, "");
-                  let(&hdrCommentAnchor, "");
-                }
-
-                printLongLine(cat(
-         "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ",
-                    /* 23-May-2008 nm Add an anchor to the "sandbox" theorem
-                       for use by mmrecent.html */
-                    /*
-                    !strcmp(g_Statement[stmt].labelName, "sandbox") ?
-                        "<A NAME=\"sandbox:tinyhdr\"></A>&#8203;" : "",
-                    */
-
-                    hdrCommentAnchor, /* 20-Oct-2018 nm */
-                    "<A HREF=\"", str3, "s\">",
-                    hdrCommentMarker, /* 4-Aug-2018 */
-                    tinyHdr, "</A>",
-                    " &nbsp; <A HREF=\"",
-                    g_Statement[stmt].labelName, ".html\">",
-                    g_Statement[stmt].labelName, "</A>",
-                    str4,
-                    "<BR>", NULL),
-                    " ",  /* Start continuation line with space */
-                    "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-                /* Assign to array for use during theorem output */
-                let((vstring *)(&pntrTinyHdr[stmt]), tinyHdr);
-                let(&tinyHdr, "");
-                let((vstring *)(&pntrTinyHdrComment[stmt]), tinyHdrComment);
-                let(&tinyHdrComment, "");
-              }
-              /* (End of 21-Aug-2017 addition) */
-
-              fprintf(outputFilePtr, "%s", g_printString);
-              g_outputToString = 0;
-              let(&g_printString, "");
-            } /* if huge or big or small or tiny header */
-          } /* if $a or $p */
-        } /* next stmt */
-        /* 8-May-2015 nm Do we need the HR below? */
-        fprintf(outputFilePtr, "<HR NOSHADE SIZE=1>\n");
-
-      } /* next passNumber */ /* 18-Oct-2015 nm */
-
-    } /* if page 0 */
-    /* End of 31-Jul-2006 added code for table of contents mod */
-
-    /* 8-May-2015 nm Just skip over instead of a big if indent */
-    if (page == 0) goto SKIP_LIST;
-
-
-    /* Put in color key */
-    g_outputToString = 1;
-    print2("<A NAME=\"mmstmtlst\"></A>\n");
-    /*if (g_extHtmlStmt <= g_statements) {*/ /* g_extHtmlStmt = g_statements + 1 in ql.mm */
-    /* 8-Dec-2017 nm */
-    if (g_extHtmlStmt < g_mathboxStmt) { /* g_extHtmlStmt >= g_mathboxStmt in ql.mm */
-      /* ?? Currently this is customized for set.mm only!! */
-      print2("<P>\n");
-      print2("<CENTER><TABLE CELLSPACING=0 CELLPADDING=5\n");
-      print2("SUMMARY=\"Color key\"><TR>\n");
-      print2("\n");
-      print2("<TD>Color key:&nbsp;&nbsp;&nbsp;</TD>\n");
-      print2("<TD BGCOLOR=%s NOWRAP><A\n", MINT_BACKGROUND_COLOR);
-      print2("HREF=\"mmset.html\"><IMG SRC=\"mm.gif\" BORDER=0\n");
-      print2("ALT=\"Metamath Proof Explorer\" HEIGHT=32 WIDTH=32\n");
-      print2("ALIGN=MIDDLE> &nbsp;Metamath Proof Explorer</A>\n");
-
-      let(&str3, "");
-      if (g_Statement[g_extHtmlStmt].pinkNumber <= 0) bug(2332);
-      str3 = pinkRangeHTML(nmbrStmtNmbr[1],
-          nmbrStmtNmbr[g_Statement[g_extHtmlStmt].pinkNumber - 1]);
-      printLongLine(cat("<BR>(", str3, ")", NULL),
-        " ",  /* Start continuation line with space */
-        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-      print2("</TD>\n");
-      print2("\n");
-      print2("<TD WIDTH=10>&nbsp;</TD>\n");
-      print2("\n");
-
-      /* Hilbert Space Explorer */
-      print2("<TD BGCOLOR=%s NOWRAP><A\n", PURPLISH_BIBLIO_COLOR);
-      print2(" HREF=\"mmhil.html\"><IMG SRC=\"atomic.gif\"\n");
-      print2(
- "BORDER=0 ALT=\"Hilbert Space Explorer\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE>\n");
-      print2("&nbsp;Hilbert Space Explorer</A>\n");
-
-      let(&str3, "");
-      /* str3 = pinkRangeHTML(g_extHtmlStmt, nmbrStmtNmbr[assertions]); */
-      if (g_Statement[g_mathboxStmt].pinkNumber <= 0) bug(2333);
-      str3 = pinkRangeHTML(g_extHtmlStmt,
-         nmbrStmtNmbr[g_Statement[g_mathboxStmt].pinkNumber - 1]);
-      printLongLine(cat("<BR>(", str3, ")", NULL),
-        " ",  /* Start continuation line with space */
-        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-      print2("</TD>\n");
-      print2("\n");
-      print2("<TD WIDTH=10>&nbsp;</TD>\n");
-      print2("\n");
-
-
-      /* 29-Jul-2008 nm Sandbox stuff */
-      print2("<TD BGCOLOR=%s NOWRAP><A\n", SANDBOX_COLOR);
-      print2(
-   /*" HREF=\"mmtheorems.html#sandbox:bighdr\"><IMG SRC=\"_sandbox.gif\"\n");*/
-      /* 24-Jul-2009 nm Changed name of sandbox to "mathbox" */
-       " HREF=\"mathbox.html\"><IMG SRC=\"_sandbox.gif\"\n");
-      print2(
-     /*"BORDER=0 ALT=\"User Sandboxes\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE>\n");*/
-         /* 24-Jul-2009 nm Changed name of sandbox to "mathbox" */
-         "BORDER=0 ALT=\"Users' Mathboxes\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE>\n");
-      /*print2("&nbsp;User Sandboxes</A>\n");*/
-      /* 24-Jul-2009 nm Changed name of sandbox to "mathbox" */
-      print2("&nbsp;Users' Mathboxes</A>\n");
-
-      let(&str3, "");
-      str3 = pinkRangeHTML(g_mathboxStmt, nmbrStmtNmbr[assertions]);
-      printLongLine(cat("<BR>(", str3, ")", NULL),
-        " ",  /* Start continuation line with space */
-        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-      print2("</TD>\n");
-      print2("\n");
-      print2("<TD WIDTH=10>&nbsp;</TD>\n");
-      print2("\n");
-
-
-      print2("</TR></TABLE></CENTER>\n");
-    } /* end if (g_extHtmlStmt < g_mathboxStmt) */
-
-    /* Write out HTML page so far */
-    fprintf(outputFilePtr, "%s", g_printString);
-    g_outputToString = 0;
-    let(&g_printString, "");
-
-
-    /* Write the main table header */
-    g_outputToString = 1;
-    print2("\n");
-    print2("<P><CENTER>\n");
-    print2("<TABLE BORDER CELLSPACING=0 CELLPADDING=3 BGCOLOR=%s\n",
-        MINT_BACKGROUND_COLOR);
-    /* print2("SUMMARY=\"Statement List for %s\">\n", htmlTitle); */
-    /* 9-May-2015 nm */
-    print2("SUMMARY=\"Theorem List for %s\">\n", htmlTitle);
-    let(&str3, "");
-    if (page < 1) bug(2335); /* Page 0 ToC should have been skipped */
-    str3 = pinkHTML(nmbrStmtNmbr[(page - 1) * theoremsPerPage + 1]);
-    let(&str3, right(str3, (long)strlen(PINK_NBSP) + 1)); /* Discard "&nbsp;" */
-    let(&str4, "");
-    str4 = pinkHTML((page < pages) ?
-        nmbrStmtNmbr[page * theoremsPerPage] :
-        nmbrStmtNmbr[assertions]);
-    let(&str4, right(str4, (long)strlen(PINK_NBSP) + 1)); /* Discard "&nbsp;" */
-    /* printLongLine(cat("<CAPTION><B>Statement List for ", htmlTitle, */
-    /* 9-May-2015 nm */
-    printLongLine(cat("<CAPTION><B>Theorem List for ", htmlTitle,
-        " - </B>", str3, "<B>-</B>", str4,
-        /* 21-Jun-2014 - redundant, since it's in the title now
-        "<B>",
-        " - Page ",
-        str(page), " of ",
-        str(pages),
-        "</B>",
-        */
-        " &nbsp; *Has distinct variable group(s)"
-        "</CAPTION>",NULL),
-        " ",  /* Start continuation line with space */
-        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-    print2("\n");
-    print2("<TR><TH>Type</TH><TH>Label</TH><TH>Description</TH></TR>\n");
-    print2("<TR><TH COLSPAN=3>Statement</TH></TR>\n");
-    print2("\n");
-    print2("<TR BGCOLOR=white><TD COLSPAN=3><FONT SIZE=-3>&nbsp;</FONT></TD></TR>\n");
-    print2("\n");
-    fprintf(outputFilePtr, "%s", g_printString);
-    g_outputToString = 0;
-    let(&g_printString, "");
-
-    /* Find the last assertion that will be printed on the page, so
-       we will know when a separator between theorems is not needed */
-    lastAssertion = 0;
-    for (assertion = (page - 1) * theoremsPerPage + 1;
-        assertion <= page * theoremsPerPage; assertion++) {
-      if (assertion > assertions) break; /* We're beyond the end */
-
-      /* nm 22-Jan-04 Don't count statements whose names begin with "xxx"
-         because they will not be output */
-      /* 9-May-2015 nm Can this be deleted?  Do we need it anymore? */
-      /* Deleted 4-Aug-2018 nm
-      if (strcmp("xxx", left(g_Statement[s].labelName, 3))) {
-        lastAssertion = assertion;
-      }
-      let(&str1, ""); /@ Purge string stack if too many left()'s @/
-      */
-
-      /* 4-Aug-2018 nm The above was replaced with: */
-      lastAssertion = assertion;
-    }
-
-    /* Output theorems on the page */
-    for (assertion = (page - 1) * theoremsPerPage + 1;
-        assertion <= page * theoremsPerPage; assertion++) {
-      if (assertion > assertions) break; /* We're beyond the end */
-
-      s = nmbrStmtNmbr[assertion]; /* Statement number */
-      /* Output only $p's, not $a's */
-      /*if (g_Statement[s].type != p_) continue;*/ /* Now do everything */
-
-      /********* Deleted 3-May-2017 nm
-      /@ nm 22-Jan-04 Skip statements whose labels begin "xxx" - this
-         means they are temporary placeholders created by
-         WRITE SOURCE / CLEAN in writeSource() in mmcmds.c @/
-      let(&str1, ""); /@ Purge string stack if too many left()'s @/
-      /@ 9-May-2015 nm Can this be deleted?  Do we need it anymore? @/
-      if (!strcmp("xxx", left(g_Statement[s].labelName, 3))) continue;
-      ******/
-
-      /* Construct the statement type label */
-      if (g_Statement[s].type == p_) {
-        let(&str1, "Theorem");
-      } else if (!strcmp("ax-", left(g_Statement[s].labelName, 3))) {
-        let(&str1, "<B><FONT COLOR=red>Axiom</FONT></B>");
-      } else if (!strcmp("df-", left(g_Statement[s].labelName, 3))) {
-        let(&str1, "<B><FONT COLOR=blue>Definition</FONT></B>");
-      } else {
-        let(&str1, "<B><FONT COLOR=\"#00CC00\">Syntax</FONT></B>");
-      }
-
-      if (s == s + 0) goto skip_date;
-      /* OBSOLETE */
-      /* Get the date in the comment section after the statement */
-      let(&str1, space(g_Statement[s + 1].labelSectionLen));
-      memcpy(str1, g_Statement[s + 1].labelSectionPtr,
-          (size_t)(g_Statement[s + 1].labelSectionLen));
-      let(&str1, edit(str1, 2)); /* Discard spaces and tabs */
-      i1 = instr(1, str1, "$([");
-      i2 = instr(i1, str1, "]$)");
-      if (i1 && i2) {
-        let(&str1, seg(str1, i1 + 3, i2 - 1));
-      } else {
-        let(&str1, "");
-      }
-     skip_date:
-
-      let(&str3, "");
-      str3 = getDescription(s);
-      let(&str4, "");
-      str4 = pinkHTML(s); /* Get little pink number */
-      /* Output the description comment */
-      /* Break up long lines for text editors with printLongLine */
-      let(&g_printString, "");
-      g_outputToString = 1;
-      print2("\n"); /* Blank line for HTML source human readability */
-
-      /* 31-Jul-2006 nm Table of contents mod */
-      if (((vstring)(pntrHugeHdr[s]))[0]) { /* There is a major part break */
-        printLongLine(cat(                                  /* 21-Jun-2014 */
-                 /* The header */
-                 "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3",
-                 /* " ALIGN=CENTER><FONT SIZE=\"+1\"><B>", */
-                 /* 9-May-2015 nm */
-                 "><CENTER><FONT SIZE=\"+1\"><B>",
-                 "<A NAME=\"mm", str((double)(g_Statement[s].pinkNumber)),
-                     "h\"></A>",   /* Anchor for table of contents */
-                 (vstring)(pntrHugeHdr[s]),
-                 /* "</B></FONT></TD></TR>", */
-                 /* 9-May-2015 nm */
-                 "</B></FONT></CENTER>",
-                 NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-        /* 8-May-2015 nm */
-        /* The comment part of the header, if any */
-        if (((vstring)(pntrHugeHdrComment[s]))[0]) {
-
-          /* Open the table row */
-          /* print2("%s\n", "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3>"); */\
-          /* 9-May-2015 nm - keep comment in same table cell */
-          print2("%s\n", "<P STYLE=\"margin-bottom:0em\">");
-
-          /* We are currently printing to g_printString to allow use of
-             printLongLine(); however, the rendering function
-             printTexComment uses g_printString internally, so we have to
-             flush the current g_printString and turn off g_outputToString mode
-             in order to call the rendering function printTexComment. */
-          /* (Question:  why do the calls to printTexComment for statement
-             descriptions, later, not need to flush the g_printString?  Is the
-             flushing code here redundant?) */
-          /* Clear out the g_printString output in prep for printTexComment */
-          g_outputToString = 0;
-          fprintf(outputFilePtr, "%s", g_printString);
-          let(&g_printString, "");
-          g_showStatement = s; /* For printTexComment */
-          g_texFilePtr = outputFilePtr; /* For printTexComment */
-          /* 8-May-2015 ???Future - make this just return a string??? */
-          /* printTexComment((vstring)(pntrHugeHdrComment[s]), 0); */
-          /* 17-Nov-2015 nm Add 3rd & 4th arguments */
-          printTexComment(  /* Sends result to g_texFilePtr */
-              (vstring)(pntrHugeHdrComment[s]),
-              0, /* 1 = htmlCenterFlag */
-              PROCESS_EVERYTHING, /* actionBits */ /* 13-Dec-2018 nm */
-              0 /* 1 = noFileCheck */);
-          g_texFilePtr = NULL;
-          g_outputToString = 1; /* Restore after printTexComment */
-
-          /* Close the table row */
-          /* print2("%s\n", "</TD></TR>"); */ /* 9-May-2015 nm */
-        }
-
-        /* 9-May-2015 nm */
-        /* Close the table row */
-        print2("%s\n", "</TD></TR>");
-
-        printLongLine(cat(                                  /* 21-Jun-2014 */
-                 /* Separator row */
-                 "<TR BGCOLOR=white><TD COLSPAN=3>",
-                 "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>",
-                 NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-      }
-      if (((vstring)(pntrBigHdr[s]))[0]) { /* There is a major section break */
-        printLongLine(cat(
-                 /* The header */
-                 "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3",
-                 /* " ALIGN=CENTER><FONT SIZE=\"+1\"><B>", */
-                 /* 9-May-2015 nm */
-                 "><CENTER><FONT SIZE=\"+1\"><B>",
-                 "<A NAME=\"mm", str((double)(g_Statement[s].pinkNumber)),
-                     "b\"></A>",      /* Anchor for table of contents */
-                 (vstring)(pntrBigHdr[s]),
-                 /* "</B></FONT></TD></TR>", */
-                 /* 9-May-2015 nm */
-                 "</B></FONT></CENTER>",
-                 NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-        /* 8-May-2015 nm */
-        /* The comment part of the header, if any */
-        if (((vstring)(pntrBigHdrComment[s]))[0]) {
-
-          /* Open the table row */
-          /* print2("%s\n", "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3>"); */\
-          /* 9-May-2015 nm - keep comment in same table cell */
-          print2("%s\n", "<P STYLE=\"margin-bottom:0em\">");
-
-          /* We are currently printing to g_printString to allow use of
-             printLongLine(); however, the rendering function
-             printTexComment uses g_printString internally, so we have to
-             flush the current g_printString and turn off g_outputToString mode
-             in order to call the rendering function printTexComment. */
-          /* (Question:  why do the calls to printTexComment for statement
-             descriptions, later, not need to flush the g_printString?  Is the
-             flushing code here redundant?) */
-          /* Clear out the g_printString output in prep for printTexComment */
-          g_outputToString = 0;
-          fprintf(outputFilePtr, "%s", g_printString);
-          let(&g_printString, "");
-          g_showStatement = s; /* For printTexComment */
-          g_texFilePtr = outputFilePtr; /* For printTexComment */
-          /* 8-May-2015 ???Future - make this just return a string??? */
-          /* printTexComment((vstring)(pntrBigHdrComment[s]), 0); */
-          /* 17-Nov-2015 nm Add 3rd & 4th arguments */
-          printTexComment(  /* Sends result to g_texFilePtr */
-              (vstring)(pntrBigHdrComment[s]),
-              0, /* 1 = htmlCenterFlag */
-              PROCESS_EVERYTHING, /* actionBits */ /* 13-Dec-2018 nm */
-              0  /* 1 = noFileCheck */);
-          g_texFilePtr = NULL;
-          g_outputToString = 1; /* Restore after printTexComment */
-
-          /* Close the table row */
-          /* print2("%s\n", "</TD></TR>"); */ /* 9-May-2015 nm */
-        }
-
-        /* 9-May-2015 nm */
-        /* Close the table row */
-        print2("%s\n", "</TD></TR>");
-
-        printLongLine(cat(                                  /* 21-Jun-2014 */
-                 /* Separator row */
-                 "<TR BGCOLOR=white><TD COLSPAN=3>",
-                 "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>",
-                 NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-      }
-      if (((vstring)(pntrSmallHdr[s]))[0]) { /* There is a minor sec break */
-        printLongLine(cat(
-                 /* The header */
-                 "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3",
-                 /* " ALIGN=CENTER><B>", */
-                 /* 9-May-2015 nm */
-                 "><CENTER><B>",
-                 "<A NAME=\"mm", str((double)(g_Statement[s].pinkNumber)),
-                     "s\"></A>",    /* Anchor for table of contents */
-                 (vstring)(pntrSmallHdr[s]),
-                 /* "</B></TD></TR>", */
-                 /* 9-May-2015 nm */
-                 "</B></CENTER>",
-                 NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-        /* 8-May-2015 nm */
-        /* The comment part of the header, if any */
-        if (((vstring)(pntrSmallHdrComment[s]))[0]) {
-
-          /* Open the table row */
-          /* print2("%s\n", "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3>"); */\
-          /* 9-May-2015 nm - keep comment in same table cell */
-          print2("%s\n", "<P STYLE=\"margin-bottom:0em\">");
-
-          /* We are currently printing to g_printString to allow use of
-             printLongLine(); however, the rendering function
-             printTexComment uses g_printString internally, so we have to
-             flush the current g_printString and turn off g_outputToString mode
-             in order to call the rendering function printTexComment. */
-          /* (Question:  why do the calls to printTexComment for statement
-             descriptions, later, not need to flush the g_printString?  Is the
-             flushing code here redundant?) */
-          /* Clear out the g_printString output in prep for printTexComment */
-          g_outputToString = 0;
-          fprintf(outputFilePtr, "%s", g_printString);
-          let(&g_printString, "");
-          g_showStatement = s; /* For printTexComment */
-          g_texFilePtr = outputFilePtr; /* For printTexComment */
-          /* 8-May-2015 ???Future - make this just return a string??? */
-          /* printTexComment((vstring)(pntrSmallHdrComment[s]), 0); */
-          /* 17-Nov-2015 nm Add 3rd & 4th arguments */
-          printTexComment(  /* Sends result to g_texFilePtr */
-              (vstring)(pntrSmallHdrComment[s]),
-              0, /* 1 = htmlCenterFlag */
-              PROCESS_EVERYTHING, /* actionBits */ /* 13-Dec-2018 nm */
-              0  /* 1 = noFileCheck */);
-          g_texFilePtr = NULL;
-          g_outputToString = 1; /* Restore after printTexComment */
-
-          /* Close the table row */
-          /* print2("%s\n", "</TD></TR>"); */ /* 9-May-2015 nm */
-        }
-
-        /* 9-May-2015 nm */
-        /* Close the table row */
-        print2("%s\n", "</TD></TR>");
-
-        printLongLine(cat(                                  /* 21-Jun-2014 */
-                 /* Separator row */
-                 "<TR BGCOLOR=white><TD COLSPAN=3>",
-                 "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>",
-                 NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-      }
-
-      /* Added 21-Aug-2017 nm */
-      if (((vstring)(pntrTinyHdr[s]))[0]) { /* There is a subsubsection break */
-        printLongLine(cat(
-                 /* The header */
-                 "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3",
-                 /* " ALIGN=CENTER><B>", */
-                 /* 9-May-2015 nm */
-                 "><CENTER><B>",
-                 "<A NAME=\"mm", str((double)(g_Statement[s].pinkNumber)),
-                     "s\"></A>",    /* Anchor for table of contents */
-                 (vstring)(pntrTinyHdr[s]),
-                 /* "</B></TD></TR>", */
-                 /* 9-May-2015 nm */
-                 "</B></CENTER>",
-                 NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-        /* 8-May-2015 nm */
-        /* The comment part of the header, if any */
-        if (((vstring)(pntrTinyHdrComment[s]))[0]) {
-
-          /* Open the table row */
-          /* print2("%s\n", "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3>"); */\
-          /* 9-May-2015 nm - keep comment in same table cell */
-          print2("%s\n", "<P STYLE=\"margin-bottom:0em\">");
-
-          /* We are currently printing to g_printString to allow use of
-             printLongLine(); however, the rendering function
-             printTexComment uses g_printString internally, so we have to
-             flush the current g_printString and turn off g_outputToString mode
-             in order to call the rendering function printTexComment. */
-          /* (Question:  why do the calls to printTexComment for statement
-             descriptions, later, not need to flush the g_printString?  Is the
-             flushing code here redundant?) */
-          /* Clear out the g_printString output in prep for printTexComment */
-          g_outputToString = 0;
-          fprintf(outputFilePtr, "%s", g_printString);
-          let(&g_printString, "");
-          g_showStatement = s; /* For printTexComment */
-          g_texFilePtr = outputFilePtr; /* For printTexComment */
-          /* 8-May-2015 ???Future - make this just return a string??? */
-          /* printTexComment((vstring)(pntrTinyHdrComment[s]), 0); */
-          /* 17-Nov-2015 nm Add 3rd & 4th arguments */
-          printTexComment(  /* Sends result to g_texFilePtr */
-              (vstring)(pntrTinyHdrComment[s]),
-              0, /* 1 = htmlCenterFlag */
-              PROCESS_EVERYTHING, /* actionBits */ /* 13-Dec-2018 nm */
-              0  /* 1 = noFileCheck */);
-          g_texFilePtr = NULL;
-          g_outputToString = 1; /* Restore after printTexComment */
-
-          /* Close the table row */
-          /* print2("%s\n", "</TD></TR>"); */ /* 9-May-2015 nm */
-        }
-
-        /* 9-May-2015 nm */
-        /* Close the table row */
-        print2("%s\n", "</TD></TR>");
-
-        printLongLine(cat(                                  /* 21-Jun-2014 */
-                 /* Separator row */
-                 "<TR BGCOLOR=white><TD COLSPAN=3>",
-                 "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>",
-                 NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-      }
-      /* (End of 21-Aug-2017 addition) */
-
-      printLongLine(cat(
-            (s < g_extHtmlStmt)
-               ? "<TR>"
-               : (s < g_mathboxStmt)
-                   ? cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL)
-                   /* 29-Jul-2008 nm Sandbox stuff */
-                   : cat("<TR BGCOLOR=", SANDBOX_COLOR, ">", NULL),
-            "<TD NOWRAP>",  /* IE breaks up the date */
-            str1, /* Date */
-            "</TD><TD ALIGN=CENTER><A HREF=\"",
-            g_Statement[s].labelName, ".html\">",
-            g_Statement[s].labelName, "</A>",
-            str4,
-
-            /* 5-Jan-2014 nm */
-            /* Add asterisk if statement has distinct var groups */
-            (nmbrLen(g_Statement[s].reqDisjVarsA) > 0) ? "*" : "",
-
-            "</TD><TD ALIGN=LEFT>",
-            /* 15-Aug-04 nm - Add anchor for hyperlinking to the table row */
-            "<A NAME=\"", g_Statement[s].labelName, "\"></A>",
-
-            NULL),  /* Description */
-          " ",  /* Start continuation line with space */
-          "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-
-      g_showStatement = s; /* For printTexComment */
-      g_outputToString = 0; /* For printTexComment */
-      g_texFilePtr = outputFilePtr; /* For printTexComment */
-      /* 18-Sep-03 ???Future - make this just return a string??? */
-      /* printTexComment(str3, 0); */ /* Sends result to g_texFilePtr */
-      /* 17-Nov-2015 nm Add 3rd & 4th arguments */
-      printTexComment(  /* Sends result to g_texFilePtr */
-          str3,
-          0, /* 1 = htmlCenterFlag */
-          PROCESS_EVERYTHING, /* actionBits */ /* 13-Dec-2018 nm */
-          0  /* 1 = noFileCheck */);
-      g_texFilePtr = NULL;
-      g_outputToString = 1; /* Restore after printTexComment */
-
-      /* Get HTML hypotheses => assertion */
-      let(&str4, "");
-      str4 = getTexOrHtmlHypAndAssertion(s); /* In mmwtex.c */
-
-      /* 19-Aug-05 nm Suppress the math content of lemmas, which can
-         be very big and not interesting */
-      if (!strcmp(left(str3, 10), "Lemma for ")
-          && !showLemmas) {  /* 10-Oct-2012 nm */
-        /* Suppress the table row with the math content */
-        print2(" <I>[Auxiliary lemma - not displayed.]</I></TD></TR>\n");
-      } else {
-        /* Output the table row with the math content */
-        printLongLine(cat("</TD></TR><TR",
-
-              /*
-              (s < g_extHtmlStmt) ?
-                   ">" :
-                   cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
-              */
-
-              /* 29-Jul-2008 nm Sandbox stuff */
-              (s < g_extHtmlStmt)
-                 ? ">"
-                 : (s < g_mathboxStmt)
-                     ? cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL)
-                     : cat(" BGCOLOR=", SANDBOX_COLOR, ">", NULL),
-
-
-            /*** old
-            "<TD BGCOLOR=white>&nbsp;</TD><TD COLSPAN=2 ALIGN=CENTER>",
-            str4, "</TD></TR>", NULL),
-            ****/
-            /* 27-Oct-03 nm */
-            "<TD COLSPAN=3 ALIGN=CENTER>",
-            str4, "</TD></TR>", NULL),
-
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-      }
-
-      g_outputToString = 0;
-      fprintf(outputFilePtr, "%s", g_printString);
-      let(&g_printString, "");
-
-      if (assertion != lastAssertion) {
-        /* Put separator row if not last theorem */
-        g_outputToString = 1;
-        printLongLine(cat("<TR BGCOLOR=white><TD COLSPAN=3>",
-            "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>", NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-        g_outputToString = 0;
-        fprintf(outputFilePtr, "%s", g_printString);
-        let(&g_printString, "");
-      }
-    } /* next assertion */
-
-    /* Output trailer */
-    g_outputToString = 1;
-    print2("</TABLE></CENTER>\n");
-    print2("\n");
-
-    /* 8-May-2015 */
-   SKIP_LIST: /* (skipped when page == 0) */
-    g_outputToString = 1; /* To compensate for skipped assignment above */
-
-
-    /* 21-Jun-2014 nm Put extra Prev/Next hyperlinks here for convenience */
-    print2("<TABLE BORDER=0 WIDTH=\"100%c\">\n", '%');
-    print2("  <TR>\n");
-    print2("    <TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%c\">\n", '%');
-    print2("      &nbsp;\n");
-    /*
-    print2("      <FONT SIZE=-1 FACE=sans-serif>\n");
-    print2("      <A HREF=\"mmset.html\">MPE Home</A>&nbsp;&nbsp;\n");
-    print2(
-  "      <A HREF=\"mmtheorems.html#mmtc\">Table of contents</A></FONT>\n");
-    */
-    print2("    </TD>\n");
-    print2("    <TD NOWRAP ALIGN=CENTER>&nbsp;</TD>\n");
-    print2("    <TD NOWRAP ALIGN=RIGHT VALIGN=TOP WIDTH=\"25%c\"><FONT\n", '%');
-    print2("      SIZE=-1 FACE=sans-serif>\n");
-    printLongLine(cat("      ", prevNextLinks, NULL),
-        " ",  /* Start continuation line with space */
-        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-    print2("      </FONT></TD>\n");
-    print2("  </TR>\n");
-    print2("</TABLE>\n");
-
-
-    print2("<HR NOSHADE SIZE=1>\n");
-
-
-    g_outputToString = 0;
-    fprintf(outputFilePtr, "%s", g_printString);
-    let(&g_printString, "");
-
-
-    fprintf(outputFilePtr, "<A NAME=\"mmpglst\"></A>\n");
-    fprintf(outputFilePtr, "<CENTER>\n");
-    fprintf(outputFilePtr, "<B>Page List</B>\n");
-    fprintf(outputFilePtr, "</CENTER>\n");
-
-
-    /* 21-Jun-2014 nm Moved page list to bottom */
-    /* Output links to the other pages */
-    fprintf(outputFilePtr, "Jump to page: \n");
-    /* for (p = 1; p <= pages; p++) { */
-    /* 8-May-2015 */
-    for (p = 0; p <= pages; p++) {
-
-      /* Construct the pink number range */
-      let(&str3, "");
-      if (p > 0) {   /* 8-May-2015 */
-        str3 = pinkRangeHTML(
-            nmbrStmtNmbr[(p - 1) * theoremsPerPage + 1],
-            (p < pages) ?
-              nmbrStmtNmbr[p * theoremsPerPage] :
-              nmbrStmtNmbr[assertions]);
-      }
-
-      /* 31-Jul-2006 nm Change "1" to "Contents + 1" */
-      if (p == page) {
-        let(&str1,
-            /* (p == 1) ? "Contents + 1" : str(p) */ /* 31-Jul-2006 nm */
-            /* 8-May-2015 nm */
-            (p == 0) ? "Contents" : str((double)p)
-            ); /* Current page shouldn't have link to self */
-      } else {
-        let(&str1, cat("<A HREF=\"mmtheorems",
-            /* (p == 1) ? "" : str(p), */
-            /* 8-May-2015 nm */
-            (p == 0) ? "" : str((double)p),
-            /* (p == 1) ? ".html#mmtc\">" : ".html\">", */ /* 31-Aug-2006 nm */
-            ".html\">", /* 8-Feb-2007 nm Friendlier, because you can start
-                  scrolling through the page before it finishes loading,
-                  without its jumping to #mmtc (start of Table of Contents)
-                  when it's done. */
-            /* (p == 1) ? "Contents + 1" : str(p) */ /* 31-Jul-2006 nm */
-            /* 8-May-2015 nm */
-            (p == 0) ? "Contents" : str((double)p)
-            , "</A>", NULL));
-      }
-      let(&str1, cat(str1, PINK_NBSP, str3, NULL));
-      fprintf(outputFilePtr, "%s\n", str1);
-    }
-    /* End of 21-Jun-2014 */
-
-
-    g_outputToString = 1;
-    print2("<HR NOSHADE SIZE=1>\n");
-    /* nm 22-Jan-04 Take out date because it causes an unnecessary incremental
-       site update */
-    /*
-    print2(" <CENTER><I>\n");
-    print2("This page was last updated on %s.\n", date());
-    print2("</I></CENTER>\n");
-    */
-
-    /*
-    print2("<CENTER><FONT SIZE=-2 FACE=ARIAL>\n");
-    print2("<A HREF=\"http://metamath.org\">metamath.org mirrors</A>\n");
-    print2("</FONT></CENTER>\n");
-    */
-    /* Add a "Previous" and "Next" links to bottom of page for convenience */
-    /************ 21-Jun-2014 nm Done above, put into prevNextLinks string
-    let(&str1, cat("<A HREF=\"mmtheorems",
-        (page > 1)
-            ? ((page - 1 > 1) ? str(page - 1) : "")
-            : ((pages > 1) ? str(pages) : ""),
-        ".html\">", NULL));
-    if (page > 1) {
-      let(&str1, cat(str1, "&lt; Previous</A>&nbsp;&nbsp;", NULL));
-    } else {
-      let(&str1, cat(str1, "&lt; Wrap</A>&nbsp;&nbsp;", NULL));
-    }
-    let(&str1, cat(str1, "<A HREF=\"mmtheorems",
-        (page < pages)
-            ? str(page + 1)
-            : "",
-        ".html\">", NULL));
-    if (page < pages) {
-      let(&str1, cat(str1, "Next &gt;</A>", NULL));
-    } else {
-      let(&str1, cat(str1, "Wrap &gt;</A>", NULL));
-    }
-    *************/
-
-    print2("<TABLE BORDER=0 WIDTH=\"100%c\">\n", '%');
-    print2("  <TR>\n");
-  /*print2("    <TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%c\">&nbsp;</TD>\n", '%');*/
-    /* 31-Aug-2006 nm Changed above line to the 4 following lines: */
-    print2("    <TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%c\">\n", '%');
-    print2("      &nbsp;\n");
-    /*
-    print2("      <FONT SIZE=-1 FACE=sans-serif>\n");
-    print2("      <A HREF=\"mmset.html\">MPE Home</A>&nbsp;&nbsp;\n");
-    print2(
-  "      <A HREF=\"mmtheorems.html#mmtc\">Table of contents</A></FONT>\n");
-    */
-    print2("    </TD>\n");
-    print2("    <TD NOWRAP ALIGN=CENTER><FONT SIZE=-2\n");
-    print2("      FACE=ARIAL>\n");
-    /*
-    print2("      <A HREF=\"http://metamath.org\">metamath.org mirrors</A>\n");
-    */
-    print2("Copyright terms:\n");
-    print2("<A HREF=\"../copyright.html#pd\">Public domain</A>\n");
-    print2("      </FONT></TD>\n");
-    print2("    <TD NOWRAP ALIGN=RIGHT VALIGN=TOP WIDTH=\"25%c\"><FONT\n", '%');
-    print2("      SIZE=-1 FACE=sans-serif>\n");
-    /*printLongLine(cat("      ", str1, NULL),*/
-    printLongLine(cat("      ", prevNextLinks, NULL), /* 21-Jun-2014 */
-        " ",  /* Start continuation line with space */
-        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-    print2("      </FONT></TD>\n");
-    print2("  </TR>\n");
-    print2("</TABLE>\n");
-
-    /* Todo: Decide to use or not use this */
-    /*
-    print2("<SCRIPT SRC=\"http://www.google-analytics.com/urchin.js\"\n");
-    print2("  TYPE=\"text/javascript\">\n");
-    print2("</SCRIPT>\n");
-    print2("<SCRIPT TYPE=\"text/javascript\">\n");
-    print2("  _uacct = \"UA-1862729-1\";\n");
-    print2("  urchinTracker();\n");
-    print2("</SCRIPT>");
-    */
-
-    print2("</BODY></HTML>\n");
-    g_outputToString = 0;
-    fprintf(outputFilePtr, "%s", g_printString);
-    let(&g_printString, "");
-
-    /* Close file */
-    fclose(outputFilePtr);
-  } /* next page */
-
- TL_ABORT:
-  /* Deallocate memory */
-  let(&str1, "");
-  let(&str3, "");
-  let(&str4, "");
-  let(&prevNextLinks, "");
-  let(&outputFileName, "");
-  let(&hugeHdr, "");
-  let(&bigHdr, "");
-  let(&smallHdr, "");
-  let(&tinyHdr, ""); /* 21-Aug-2017 nm */
-  let(&hdrCommentMarker, ""); /* 4-Aug-2018 */
-  for (i = 0; i <= g_statements; i++) let((vstring *)(&pntrHugeHdr[i]), "");
-  pntrLet(&pntrHugeHdr, NULL_PNTRSTRING);
-  for (i = 0; i <= g_statements; i++) let((vstring *)(&pntrBigHdr[i]), "");
-  pntrLet(&pntrBigHdr, NULL_PNTRSTRING);
-  for (i = 0; i <= g_statements; i++) let((vstring *)(&pntrSmallHdr[i]), "");
-  pntrLet(&pntrSmallHdr, NULL_PNTRSTRING);
-  /* 21-Aug-2017 nm */
-  for (i = 0; i <= g_statements; i++) let((vstring *)(&pntrTinyHdr[i]), "");
-  pntrLet(&pntrTinyHdr, NULL_PNTRSTRING);
-
-} /* writeTheoremList */
-
-
-/* 12-Sep-2020 nm - added fullComment, which puts the entire header
-   (including any comment and $( $) keywords) into xxxHdrTitle and
-   xxxHdrComment.  If there is no comment below header, xxxHdrComment
-   will be "$)".   No \n follows "$)".  Any leading whitespace will
-   be prefixed before the "$(".  This mode is used for /EXTRACT
-   and was added to make white space match original source. */
-/* 24-Aug-2020 nm - added fineResolution flag, where "header area" is
-   just the labelSection (text before statement) instead of all of the
-   content between a $a/$p and next $a/$p.  This is used by
-   WRITE SOURCE ... /EXTRACT. */
-/* 18-Dec-2016 nm - use true "header area" as described below, and
-   ensure statement argument is $p or $a */
-/* 2-Aug-2009 nm - broke this function out from writeTheoremList() */
-/* 21-Jun-2014 nm - added hugeHdrTitle */
-/* 21-Aug-2017 nm - added tinyHdrTitle */
-/* 8-May-2015 nm - added hugeHdrComment, bigHdrComment, smallHdrComment */
-/* 21-Aug-2017 nm - added tinyHdrComment */
-/* This function extracts any section headers in the comment sections
-   prior to the label of statement stmt.   If a huge (####...) header isn't
-   found, *hugeHdrTitle will be set to the empty string.
-   If a big (#*#*...) header isn't found (or isn't after the last huge header),
-   *bigHdrTitle will be set to the empty string.  If a small
-   (=-=-...) header isn't found (or isn't after the last huge header or
-   the last big header), *smallHdrTitle will be set to the empty string.
-   In all 3 cases, only the last occurrence of a header is considered.
-   If a tiny
-   (-.-....) header isn't found (or isn't after the last huge header or
-   the last big header or the last small header), *tinyHdrTitle will be
-   set to the empty string.
-   In all 4 cases, only the last occurrence of a header is considered. */
-/*
-20-Jun-2015 metamath Google group email:
-https://groups.google.com/g/metamath/c/QE0lwg9f5Ho/m/J8ekl_lH1I8J
-
-There are 3 kinds of section headers, big (####...), medium (#*#*#*...),
-and small (=-=-=-).
-
-Call the collection of (outside-of-statement) comments between two
-successive $a/$p statements (i.e. those statements that generate web
-pages) a "header area".  The algorithm scans the header area for the
-_last_ header of each type (big, medium, small) and discards all others.
-Then, if there is a medium and it appears before a big, the medium is
-discarded.  If there is a small and it appears before a big or medium,
-the small is discarded.  In other words, a maximum of one header of
-each type is kept, in the order big, medium, and small.
-
-There are two reasons for doing this:  (1) it disregards headers used
-for other purposes such as the headers for the set.mm-specific
-information at the top that is not of general interest and (2) it
-ignores headers for empty sections; for example, a mathbox user might
-have a bunch of headers for sections planned for the future, but we
-ignore them if those sections are empty (no $a or $p in them).
-
- 6-Aug-2019 nm: added error checking; added error checking
-21-Aug-2017 nm: added "tiny" to "big, medium, small".
-
-*/
-/*void getSectionHeadings(long stmt, vstring *hugeHdrTitle, vstring *bigHdrTitle,*/
-/* Return 1 if error found, 0 otherwise */ /* 6-Aug-2019 nm */
-flag getSectionHeadings(long stmt,
-    vstring *hugeHdrTitle,
-    vstring *bigHdrTitle,
-    vstring *smallHdrTitle,
-    vstring *tinyHdrTitle,  /* 21-Aug-2017 nm */
-    /* 8-May-2015 nm Added huge,big,smallHdrComment */
-    vstring *hugeHdrComment,
-    vstring *bigHdrComment,
-    vstring *smallHdrComment,
-    vstring *tinyHdrComment,  /* 21-Aug-2017 nm */
-    flag fineResolution,  /* 24-Aug-2020 nm */
-    flag fullComment  /* 12-Sep-2020 nm */
-    )
-{  /* 21-Aug-2017 nm */
-
-  /* 31-Jul-2006 for table of contents mod */
-  vstring labelStr = "";
-  long pos, pos1, pos2, pos3, pos4;
-  flag errorFound = 0; /* 6-Aug-2019 nm */
-  flag saveOutputToString; /* 6-Aug-2019 nm */
-
-  /* 6-Aug-2019 nm */
-  /* Print any error messages to screen */
-  saveOutputToString = g_outputToString; /* To restore when returning */
-  g_outputToString = 0;
-
-  /* 24-Aug-2020 nm */
-  /* (This initialization seems to be done redundantly by caller elsewhere,
-     but for  WRITE SOURCE ... / EXTRACT we need to do it explicitly.) */
-  let(&(*hugeHdrTitle), "");
-  let(&(*bigHdrTitle), "");
-  let(&(*smallHdrTitle), "");
-  let(&(*tinyHdrTitle), "");
-  let(&(*hugeHdrComment), "");
-  let(&(*bigHdrComment), "");
-  let(&(*smallHdrComment), "");
-  let(&(*tinyHdrComment), "");
-
-  /* 18-Dec-2016 nm */
-  /* We now process only $a or $p statements */
-  if (fineResolution == 0) { /* 24-Aug-2020 nm */
-    if (g_Statement[stmt].type != a_ && g_Statement[stmt].type != p_) bug(2340);
-  }
-
-  /* 18-Dec-2016 nm */
-  /* Get header area between this statement and the statement after the
-     previous $a or $p statement */
-  /* pos3 and pos4 are used temporarily here; not related to later use */
-  if (fineResolution == 0) {
-    pos3 = g_Statement[stmt].headerStartStmt;  /* Statement immediately after the
-                previous $a or $p statement (will be this statement if previous
-                statement is $a or $p) */
-  } else {
-    /* 24-Aug-2020 nm */
-    pos3 = stmt; /* For WRITE SOURCE ... / EXTRACT, we want every statement
-                    treated equally */
-  }
-  if (pos3 == 0 || pos3 > stmt) bug(2241);
-  pos4 = (g_Statement[stmt].labelSectionPtr
-        - g_Statement[pos3].labelSectionPtr)
-        + g_Statement[stmt].labelSectionLen;  /* Length of the header area */
-  let(&labelStr, space(pos4));
-  memcpy(labelStr, g_Statement[pos3].labelSectionPtr,
-      (size_t)(pos4));
-
-  /* Old code before 18-Dec-2016
-  /@ Get headers from comment section between statements @/
-  let(&labelStr, space(g_Statement[stmt].labelSectionLen));
-  memcpy(labelStr, g_Statement[stmt].labelSectionPtr,
-      (size_t)(g_Statement[stmt].labelSectionLen));
-  */
-
-  pos = 0;
-  pos2 = 0;
-  while (1) {  /* Find last "huge" header, if any */
-    /* 4-Nov-2007 nm:  Obviously, the match below will not work if the
-       $( line has a trailing space, which some editors might insert.
-       The symptom is a missing table of contents entry.  But to detect
-       this (and for the #*#* and =-=- matches below) would take a little work
-       and perhaps slow things down, and I don't think it is worth it.  I
-       put a note in HELP WRITE THEOREM_LIST. */
-    pos1 = pos; /* 23-May-2008 */
-    pos = instr(pos + 1, labelStr, "$(\n" HUGE_DECORATION);
-
-    /* 23-May-2008 nm Tolerate one space after "$(", to handle case of
-      one space added to the end of each line with TOOLS to make global
-      label changes are easier (still a kludge; this should be made
-      white-space insensitive some day) */
-    pos1 = instr(pos1 + 1, labelStr, "$( \n" HUGE_DECORATION);
-    if (pos1 > pos) pos = pos1;
-
-    if (!pos) break;
-    if (pos) pos2 = pos;
-  } /* while(1) */
-  if (pos2) { /* Extract "huge" header */
-    pos1 = pos2; /* Save "$(" position */ /* 12-Sep-2020 nm */
-    pos = instr(pos2 + 4, labelStr, "\n"); /* Get to end of #### line */
-    pos2 = instr(pos + 1, labelStr, "\n"); /* Find end of title line */
-
-    /* Error check - can't have more than 1 title line */ /* 6-Aug-2019 nm */
-    if (strcmp(mid(labelStr, pos2 + 1, 4), HUGE_DECORATION)) {
-      print2(
-       "?Warning: missing closing \"%s\" decoration above statement \"%s\".\n",
-          HUGE_DECORATION, g_Statement[stmt].labelName);
-      errorFound = 1;
-    }
-
-    pos3 = instr(pos2 + 1, labelStr, "\n"); /* Get to end of 2nd #### line */
-    while (labelStr[(pos3 - 1) + 1] == '\n') pos3++; /* Skip 1st blank lines */
-    pos4 = instr(pos3, labelStr, "$)"); /* Get to end of title comment */
-    if (fullComment == 0) {  /* 12-Sep-2020 nm */
-      let(&(*hugeHdrTitle), seg(labelStr, pos + 1, pos2 - 1));
-      let(&(*hugeHdrTitle), edit((*hugeHdrTitle), 8 + 128));
-                                                /* Trim leading, trailing sp */
-      let(&(*hugeHdrComment), seg(labelStr, pos3 + 1, pos4 - 2));
-      let(&(*hugeHdrComment), edit((*hugeHdrComment), 8 + 16384));
-          /* Trim leading sp, trailing sp & lf */
-    } else {
-      /* 12-Sep-2020 nm */
-      /* Put entire comment in hugeHdrTitle and hugeHdrComment for /EXTRACT */
-      /* Search backwards for non-space or beginning of string: */
-      pos = pos1; /* pos1 is the "$" in "$(" */
-      pos--;
-      while (pos > 0) {
-        if (labelStr[pos - 1] != ' '
-              && labelStr[pos - 1] != '\n') break;
-        pos--;
-      }
-      /* pos + 1 is the start of whitespace preceding "$(" */
-      /* pos4 is the "$" in "$)" */
-      /* pos3 is the \n after the 2nd decoration line */
-      let(&(*hugeHdrTitle), seg(labelStr, pos + 1, pos3));
-      let(&(*hugeHdrComment), seg(labelStr, pos3 + 1, pos4 + 1));
-    }
-  }
-  /* pos = 0; */ /* Leave pos alone so that we start with "huge" header pos,
-                    to ignore any earlier "tiny" or "small" or "big" header */
-  pos2 = 0;
-  while (1) {  /* Find last "big" header, if any */
-    /* nm 4-Nov-2007:  Obviously, the match below will not work if the
-       $( line has a trailing space, which some editors might insert.
-       The symptom is a missing table of contents entry.  But to detect
-       this (and for the =-=- match below) would take a little work and
-       perhaps slow things down, and I don't think it is worth it.  I
-       put a note in HELP WRITE THEOREM_LIST. */
-    pos1 = pos; /* 23-May-2008 */
-    pos = instr(pos + 1, labelStr, "$(\n" BIG_DECORATION);
-
-    /* 23-May-2008 nm Tolerate one space after "$(", to handle case of
-      one space added to the end of each line with TOOLS to make global
-      label changes are easier (still a kludge; this should be made
-      white-space insensitive some day) */
-    pos1 = instr(pos1 + 1, labelStr, "$( \n" BIG_DECORATION);
-    if (pos1 > pos) pos = pos1;
-
-    if (!pos) break;
-    if (pos) pos2 = pos;
-  }
-  if (pos2) { /* Extract "big" header */
-    pos1 = pos2; /* Save "$(" position */ /* 12-Sep-2020 nm */
-    pos = instr(pos2 + 4, labelStr, "\n"); /* Get to end of #*#* line */
-    pos2 = instr(pos + 1, labelStr, "\n"); /* Find end of title line */
-
-    /* Error check - can't have more than 1 title line */ /* 6-Aug-2019 nm */
-    if (strcmp(mid(labelStr, pos2 + 1, 4), BIG_DECORATION)) {
-      print2(
-       "?Warning: missing closing \"%s\" decoration above statement \"%s\".\n",
-          BIG_DECORATION, g_Statement[stmt].labelName);
-      errorFound = 1;
-    }
-
-    pos3 = instr(pos2 + 1, labelStr, "\n"); /* Get to end of 2nd #*#* line */
-    while (labelStr[(pos3 - 1) + 1] == '\n') pos3++; /* Skip 1st blank lines */
-    pos4 = instr(pos3, labelStr, "$)"); /* Get to end of title comment */
-    if (fullComment == 0) {  /* 12-Sep-2020 nm */
-      let(&(*bigHdrTitle), seg(labelStr, pos + 1, pos2 - 1));
-      let(&(*bigHdrTitle), edit((*bigHdrTitle), 8 + 128));
-                                                  /* Trim leading, trailing sp */
-      let(&(*bigHdrComment), seg(labelStr, pos3 + 1, pos4 - 2));
-      let(&(*bigHdrComment), edit((*bigHdrComment), 8 + 16384));
-          /* Trim leading sp, trailing sp & lf */
-    } else {
-      /* 12-Sep-2020 nm */
-      /* Put entire comment in bigHdrTitle and bigHdrComment for /EXTRACT */
-      /* Search backwards for non-space or beginning of string: */
-      pos = pos1; /* pos1 is the "$" in "$(" */
-      pos--;
-      while (pos > 0) {
-        if (labelStr[pos - 1] != ' '
-              && labelStr[pos - 1] != '\n') break;
-        pos--;
-      }
-      /* pos + 1 is the start of whitespace preceding "$(" */
-      /* pos4 is the "$" in "$)" */
-      /* pos3 is the \n after the 2nd decoration line */
-      let(&(*bigHdrTitle), seg(labelStr, pos + 1, pos3));
-      let(&(*bigHdrComment), seg(labelStr, pos3 + 1, pos4 + 1));
-    }
-  }
-  /* pos = 0; */ /* Leave pos alone so that we start with "big" header pos,
-                    to ignore any earlier "tiny" or "small" header */
-  pos2 = 0;
-  while (1) {  /* Find last "small" header, if any */
-    pos1 = pos; /* 23-May-2008 */
-    pos = instr(pos + 1, labelStr, "$(\n" SMALL_DECORATION);
-
-    /* 23-May-2008 nm Tolerate one space after "$(", to handle case of
-      one space added to the end of each line with TOOLS to make global
-      label changes are easier (still a kludge; this should be made
-      white-space insensitive some day) */
-    pos1 = instr(pos1 + 1, labelStr, "$( \n" SMALL_DECORATION);
-    if (pos1 > pos) pos = pos1;
-
-    if (!pos) break;
-    if (pos) pos2 = pos;
-  }
-  if (pos2) { /* Extract "small" header */
-    pos1 = pos2; /* Save "$(" position */ /* 12-Sep-2020 nm */
-    pos = instr(pos2 + 4, labelStr, "\n"); /* Get to end of =-=- line */
-    pos2 = instr(pos + 1, labelStr, "\n"); /* Find end of title line */
-
-    /* Error check - can't have more than 1 title line */ /* 6-Aug-2019 nm */
-    if (strcmp(mid(labelStr, pos2 + 1, 4), SMALL_DECORATION)) {
-      print2(
-       "?Warning: missing closing \"%s\" decoration above statement \"%s\".\n",
-          SMALL_DECORATION, g_Statement[stmt].labelName);
-      errorFound = 1;
-    }
-
-    pos3 = instr(pos2 + 1, labelStr, "\n"); /* Get to end of 2nd =-=- line */
-    while (labelStr[(pos3 - 1) + 1] == '\n') pos3++; /* Skip 1st blank lines */
-    pos4 = instr(pos3, labelStr, "$)"); /* Get to end of title comment */
-    if (fullComment == 0) {  /* 12-Sep-2020 nm */
-      let(&(*smallHdrTitle), seg(labelStr, pos + 1, pos2 - 1));
-      let(&(*smallHdrTitle), edit((*smallHdrTitle), 8 + 128));
-                                                /* Trim leading, trailing sp */
-      let(&(*smallHdrComment), seg(labelStr, pos3 + 1, pos4 - 2));
-      let(&(*smallHdrComment), edit((*smallHdrComment), 8 + 16384));
-          /* Trim leading sp, trailing sp & lf */
-    } else {
-      /* 12-Sep-2020 nm */
-      /* Put entire comment in smallHdrTitle and smallHdrComment for /EXTRACT */
-      /* Search backwards for non-space or beginning of string: */
-      pos = pos1; /* pos1 is the "$" in "$(" */
-      pos--;
-      while (pos > 0) {
-        if (labelStr[pos - 1] != ' '
-              && labelStr[pos - 1] != '\n') break;
-        pos--;
-      }
-      /* pos + 1 is the start of whitespace preceding "$(" */
-      /* pos4 is the "$" in "$)" */
-      /* pos3 is the \n after the 2nd decoration line */
-      let(&(*smallHdrTitle), seg(labelStr, pos + 1, pos3));
-      let(&(*smallHdrComment), seg(labelStr, pos3 + 1, pos4 + 1));
-    }
-  }
-
-  /* Added 21-Aug-2017 nm */
-  /* pos = 0; */ /* Leave pos alone so that we start with "small" header pos,
-                    to ignore any earlier "tiny" header */
-  pos2 = 0;
-  while (1) {  /* Find last "tiny" header, if any */
-    pos1 = pos; /* 23-May-2008 */
-    pos = instr(pos + 1, labelStr, "$(\n" TINY_DECORATION);
-
-    /* 23-May-2008 nm Tolerate one space after "$(", to handle case of
-      one space added to the end of each line with TOOLS to make global
-      label changes are easier (still a kludge; this should be made
-      white-space insensitive some day) */
-    pos1 = instr(pos1 + 1, labelStr, "$( \n" TINY_DECORATION);
-    if (pos1 > pos) pos = pos1;
-
-    if (!pos) break;
-    if (pos) pos2 = pos;
-  }
-  if (pos2) { /* Extract "tiny" header */
-    pos1 = pos2; /* Save "$(" position */ /* 12-Sep-2020 nm */
-    pos = instr(pos2 + 4, labelStr, "\n"); /* Get to end of -.-. line */
-    pos2 = instr(pos + 1, labelStr, "\n"); /* Find end of title line */
-
-    /* Error check - can't have more than 1 title line */ /* 6-Aug-2019 nm */
-    if (strcmp(mid(labelStr, pos2 + 1, 4), TINY_DECORATION)) {
-      print2(
-       "?Warning: missing closing \"%s\" decoration above statement \"%s\".\n",
-          TINY_DECORATION, g_Statement[stmt].labelName);
-      errorFound = 1;
-    }
-
-    pos3 = instr(pos2 + 1, labelStr, "\n"); /* Get to end of 2nd -.-. line */
-    while (labelStr[(pos3 - 1) + 1] == '\n') pos3++; /* Skip 1st blank lines */
-    pos4 = instr(pos3, labelStr, "$)"); /* Get to end of title comment */
-    if (fullComment == 0) {  /* 12-Sep-2020 nm */
-      let(&(*tinyHdrTitle), seg(labelStr, pos + 1, pos2 - 1));
-      let(&(*tinyHdrTitle), edit((*tinyHdrTitle), 8 + 128));
-                                                  /* Trim leading, trailing sp */
-      let(&(*tinyHdrComment), seg(labelStr, pos3 + 1, pos4 - 2));
-      let(&(*tinyHdrComment), edit((*tinyHdrComment), 8 + 16384));
-          /* Trim leading sp, trailing sp & lf */
-    } else {
-      /* 12-Sep-2020 nm */
-      /* Put entire comment in tinyHdrTitle and tinyHdrComment for /EXTRACT */
-      /* Search backwards for non-space or beginning of string: */
-      pos = pos1; /* pos1 is the "$" in "$(" */
-      pos--;
-      while (pos > 0) {
-        if (labelStr[pos - 1] != ' '
-              && labelStr[pos - 1] != '\n') break;
-        pos--;
-      }
-      /* pos + 1 is the start of whitespace preceding "$(" */
-      /* pos4 is the "$" in "$)" */
-      /* pos3 is the \n after the 2nd decoration line */
-      let(&(*tinyHdrTitle), seg(labelStr, pos + 1, pos3));
-      let(&(*tinyHdrComment), seg(labelStr, pos3 + 1, pos4 + 1));
-    }
-  }
-  /* (End of 21-Aug-2017 addition) */
-
-  /* 6-Aug-2019 nm */
-  if (errorFound == 1) {
-    print2("  (Note that section titles may not be longer than one line.)\n");
-  }
-  /* Restore output stream */
-  g_outputToString = saveOutputToString;
-
-  let(&labelStr, "");  /* Deallocate string memory */
-  return errorFound;  /* 6-Aug-2019 nm */
-} /* getSectionHeadings */
-
-
-/* Returns the pink number printed next to statement labels in HTML output */
-/* The pink number only counts $a and $p statements, unlike the statement
-   number which also counts $f, $e, $c, $v, ${, $} */
-/* 10/10/02 This is no longer used? */
-#ifdef DUMMY  /* For commenting it out */
-long pinkNumber(long statemNum)
-{
-  long statemMap = 0;
-  long i;
-  /* Statement map for number of g_showStatement - this makes the statement
-     number more meaningful, by counting only $a and $p. */
-  /* ???This could be done once if we want to speed things up, but
-     be careful because it will have to be redone if ERASE then READ.
-     For the future it could be added to the g_Statement[] structure. */
-  statemMap = 0;
-  for (i = 1; i <= statemNum; i++) {
-    if (g_Statement[i].type == a_ || g_Statement[i].type == p_)
-      statemMap++;
-  }
-  return statemMap;
-} /* pinkNumber */
-#endif
-
-/* Added 10/10/02 */
-/* Returns HTML for the pink number to print after the statement labels
-   in HTML output.  (Note that "pink" means "rainbow colored" number now.) */
-/* Warning: The caller must deallocate the returned vstring (i.e. this
-   function cannot be used in let statements but must be assigned to
-   a local vstring for local deallocation) */
-vstring pinkHTML(long statemNum)
-{
-  long statemMap;
-  vstring htmlCode = "";
-  vstring hexValue = "";
-
-  /* The pink number only counts $a and $p statements, unlike the statement
-     number which also counts $f, $e, $c, $v, ${, $} */
-  /* 10/25/02 Added pinkNumber to the g_Statement[] structure for speedup. */
-  /*
-  statemMap = 0;
-  for (i = 1; i <= statemNum; i++) {
-    if (g_Statement[i].type == a_ || g_Statement[i].type == p_)
-      statemMap++;
-  }
-  */
-  if (statemNum > 0) {
-    statemMap = g_Statement[statemNum].pinkNumber;
-  } else {
-    /* -1 means the label wasn't found */
-    statemMap = -1;
-  }
-
-  /* Note: we put "(future)" when the label wasn't found (an error message
-     was also generated previously) */
-
-  /* Without style sheet */
-  /*
-  let(&htmlCode, cat(PINK_NBSP,
-      "<FONT FACE=\"Arial Narrow\" SIZE=-2 COLOR=", PINK_NUMBER_COLOR, ">",
-      (statemMap != -1) ? str(statemMap) : "(future)", "</FONT>", NULL));
-  */
-
-#ifndef RAINBOW_OPTION
-  /* With style sheet */
-  let(&htmlCode, cat(PINK_NBSP,
-      "<SPAN CLASS=p>",
-      (statemMap != -1) ? str((double)statemMap) : "(future)", "</SPAN>", NULL));
-#endif
-
-#ifdef RAINBOW_OPTION
-  /* ndm 10-Jan-04 With style sheet and explicit color */
-  let(&hexValue, "");
-  hexValue = spectrumToRGB(statemMap, g_Statement[g_statements].pinkNumber);
-  let(&htmlCode, cat(PINK_NBSP,
-      "<SPAN CLASS=r STYLE=\"color:#", hexValue, "\">",
-      (statemMap != -1) ? str((double)statemMap) : "(future)", "</SPAN>", NULL));
-#endif
-  let(&hexValue, "");
-
-  return htmlCode;
-} /* pinkHTML */
-
-
-/* Added 25-Aug-04 */
-/* Returns HTML for a range of pink numbers separated by a "-". */
-/* Warning: The caller must deallocate the returned vstring (i.e. this
-   function cannot be used in let statements but must be assigned to
-   a local vstring for local deallocation) */
-vstring pinkRangeHTML(long statemNum1, long statemNum2)
-{
-  vstring htmlCode = "";
-  vstring str3 = "";
-  vstring str4 = "";
-
-  /* Construct the HTML for a pink number range */
-  let(&str3, "");
-  str3 = pinkHTML(statemNum1);
-  let(&str3, right(str3, (long)strlen(PINK_NBSP) + 1)); /* Discard "&nbsp;" */
-  let(&str4, "");
-  str4 = pinkHTML(statemNum2);
-  let(&str4, right(str4, (long)strlen(PINK_NBSP) + 1)); /* Discard "&nbsp;" */
-  let(&htmlCode, cat(str3, "-", str4, NULL));
-  let(&str3, ""); /* Deallocate */
-  let(&str4, ""); /* Deallocate */
-  return htmlCode;
-} /* pinkRangeHTML */
-
-
-#ifdef RAINBOW_OPTION
-/* 20-Aug-2006 nm This section was revised so that all colors have
-   the same grayscale brightness. */
-
-/* This function converts a "spectrum" color (1 to maxColor) to an
-   RBG value in hex notation for HTML.  The caller must deallocate the
-   returned vstring to prevent memory leaks.  color = 1 (red) to maxColor
-   (violet).  A special case is the color -1, which just returns black. */
-/* ndm 10-Jan-04 */
-vstring spectrumToRGB(long color, long maxColor) {
-  vstring str1 = "";
-  double fraction, fractionInPartition;
-  long j, red, green, blue, partition;
-/* Change PARTITIONS whenever the table below has entries added or removed! */
-#define PARTITIONS 28
-  static double redRef[PARTITIONS +  1];    /* 20-Aug-2006 nm Made these */
-  static double greenRef[PARTITIONS +  1];  /*                static for */
-  static double blueRef[PARTITIONS +  1];   /*                speedup */
-  static long i = -1;                       /*                below */
-
-  if (i > -1) goto SKIP_INIT; /* 20-Aug-2006 nm - Speedup */
-  i = -1; /* For safety */
-
-#define L53empirical
-#ifdef L53empirical
-  /* Here, we use the maximum saturation possible for a fixed L*a*b color L
-     (lightness) value of 53, which corresponds to 50% gray scale.
-     Each pure color had either brightness reduced or saturation reduced,
-     as required, to achieve L = 53.
-
-     The partitions in the 'ifdef L53obsolete' below were divided into 1000
-     subpartitions, then new partitions were determined by reselecting
-     partition boundaries based on where their color difference was
-     distinguishable (i.e. could semi-comfortably read letters of one color
-     with the other as a background, on an LCD display).  Some human judgment
-     was involved, and it is probably not completely uniform or optimal.
-
-     A Just Noticeable Difference (JND) algorithm for spacing might be more
-     accurate, especially if averaged over several subjects and different
-     monitors.  I wrote a program for that - asking the user to identify a word
-     in one hue with an adjacent hue as a background, in order to score a
-     point - but it was taking too much time, and I decided life is too short.
-     I think this is "good enough" though, perhaps not even noticeably
-     non-optimum.
-
-     The comment at the end of each line is the hue 0-360 mapped linearly
-     to 1-1043 (345 = 1000, i.e. "extreme" purple or almost red).  Partitions
-     at the end can be commented out if we want to stop at violet instead of
-     almost wrapping around to red via the purples, in order to more accurately
-     emulate the color spectrum.  Be sure to update the PARTITIONS constant
-     above if a partition is commented out, to avoid a bug trap. */
-  i++; redRef[i] = 251; greenRef[i] = 0; blueRef[i] = 0;       /* 1 */
-  i++; redRef[i] = 247; greenRef[i] = 12; blueRef[i] = 0;      /* 10 */
-  i++; redRef[i] = 238; greenRef[i] = 44; blueRef[i] = 0;      /* 34 */
-  i++; redRef[i] = 222; greenRef[i] = 71; blueRef[i] = 0;      /* 58 */
-  i++; redRef[i] = 203; greenRef[i] = 89; blueRef[i] = 0;      /* 79 */
-  i++; redRef[i] = 178; greenRef[i] = 108; blueRef[i] = 0;     /* 109 */
-  i++; redRef[i] = 154; greenRef[i] = 122; blueRef[i] = 0;     /* 140 */
-  i++; redRef[i] = 127; greenRef[i] = 131; blueRef[i] = 0;     /* 181 */
-  i++; redRef[i] = 110; greenRef[i] = 136; blueRef[i] = 0;     /* 208 */
-  i++; redRef[i] = 86; greenRef[i] = 141; blueRef[i] = 0;      /* 242 */
-  i++; redRef[i] = 60; greenRef[i] = 144; blueRef[i] = 0;      /* 276 */
-  i++; redRef[i] = 30; greenRef[i] = 147; blueRef[i] = 0;      /* 313 */
-  i++; redRef[i] = 0; greenRef[i] = 148; blueRef[i] = 22;      /* 375 */
-  i++; redRef[i] = 0; greenRef[i] = 145; blueRef[i] = 61;      /* 422 */
-  i++; redRef[i] = 0; greenRef[i] = 145; blueRef[i] = 94;      /* 462 */
-  i++; redRef[i] = 0; greenRef[i] = 143; blueRef[i] = 127;     /* 504 */
-  i++; redRef[i] = 0; greenRef[i] = 140; blueRef[i] = 164;     /* 545 */
-  i++; redRef[i] = 0; greenRef[i] = 133; blueRef[i] = 218;     /* 587 */
-  i++; redRef[i] = 3; greenRef[i] = 127; blueRef[i] = 255;     /* 612 */
-  i++; redRef[i] = 71; greenRef[i] = 119; blueRef[i] = 255;    /* 652 */
-  i++; redRef[i] = 110; greenRef[i] = 109; blueRef[i] = 255;   /* 698 */
-  i++; redRef[i] = 137; greenRef[i] = 99; blueRef[i] = 255;    /* 740 */
-  i++; redRef[i] = 169; greenRef[i] = 78; blueRef[i] = 255;    /* 786 */
-  i++; redRef[i] = 186; greenRef[i] = 57; blueRef[i] = 255;    /* 808 */
-  i++; redRef[i] = 204; greenRef[i] = 33; blueRef[i] = 249;    /* 834 */
-  i++; redRef[i] = 213; greenRef[i] = 16; blueRef[i] = 235;    /* 853 */
-  i++; redRef[i] = 221; greenRef[i] = 0; blueRef[i] = 222;     /* 870 */
-  i++; redRef[i] = 233; greenRef[i] = 0; blueRef[i] = 172;     /* 916 */
-  i++; redRef[i] = 239; greenRef[i] = 0; blueRef[i] = 132;     /* 948 */
-  /*i++; redRef[i] = 242; greenRef[i] = 0; blueRef[i] = 98;*/  /* 973 */
-  /*i++; redRef[i] = 244; greenRef[i] = 0; blueRef[i] = 62;*/  /* 1000 */
-#endif
-
-#ifdef L53obsolete
-  /* THIS IS OBSOLETE AND FOR HISTORICAL REFERENCE ONLY; IT MAY BE DELETED. */
-  /* Here, we use the maximum saturation possible for a given L value of 53.
-     Each pure color has either brightness reduced or saturation reduced,
-     as appropriate, until the LAB color L (lightness) value is 53.  The
-     comment at the end of each line is the hue (0 to 360).
-
-     Unfortunately, equal hue differences are not equally distinguishable.
-     The commented-out lines were an unsuccessful attempt to make them more
-     uniform before the final empirical table above was determined. */
-  i++; redRef[i] = 251; greenRef[i] = 0; blueRef[i] = 0; /* 0 r */
-  i++; redRef[i] = 234; greenRef[i] = 59; blueRef[i] = 0; /* 15 */
-  i++; redRef[i] = 196; greenRef[i] = 98; blueRef[i] = 0; /* 30 */
-  i++; redRef[i] = 160; greenRef[i] = 120; blueRef[i] = 0; /* 45 */
-  /*i++; redRef[i] = 131; greenRef[i] = 131; blueRef[i] = 0;*/ /* 60 */
-  i++; redRef[i] = 104; greenRef[i] = 138; blueRef[i] = 0; /* 75 */
-  /*i++; redRef[i] = 72; greenRef[i] = 144; blueRef[i] = 0;*/ /* 90 */
-  /*i++; redRef[i] = 37; greenRef[i] = 147; blueRef[i] = 0;*/ /* 105 */
-  i++; redRef[i] = 0; greenRef[i] = 148; blueRef[i] = 0; /* 120 g */
-  /*i++; redRef[i] = 0; greenRef[i] = 148; blueRef[i] = 37;*/ /* 135 */
-  /*i++; redRef[i] = 0; greenRef[i] = 145; blueRef[i] = 73;*/ /* 150 */
-  i++; redRef[i] = 0; greenRef[i] = 145; blueRef[i] = 109; /* 165 */
-  /*i++; redRef[i] = 0; greenRef[i] = 142; blueRef[i] = 142;*/ /* 180 */
-  /*i++; redRef[i] = 0; greenRef[i] = 139; blueRef[i] = 185;*/ /* 195 */
-  i++; redRef[i] = 0; greenRef[i] = 128; blueRef[i] = 255; /* 210 */
-  /*i++; redRef[i] = 73; greenRef[i] = 119; blueRef[i] = 255;*/ /* 225 */
-  i++; redRef[i] = 110; greenRef[i] = 110; blueRef[i] = 255; /* 240 b */
-  i++; redRef[i] = 138; greenRef[i] = 99; blueRef[i] = 255; /* 255 */
-  i++; redRef[i] = 168; greenRef[i] = 81; blueRef[i] = 255; /* 270 */
-  i++; redRef[i] = 201; greenRef[i] = 40; blueRef[i] = 255; /* 285 */
-  i++; redRef[i] = 222; greenRef[i] = 0; blueRef[i] = 222; /* 300 */
-  i++; redRef[i] = 233; greenRef[i] = 0; blueRef[i] = 175; /* 315 */
-  i++; redRef[i] = 241; greenRef[i] = 0; blueRef[i] = 120; /* 330 */
-  /*i++; redRef[i] = 245; greenRef[i] = 0; blueRef[i] = 61;*/ /* 345 */
-#endif
-
-#ifdef L68obsolete
-  /* THIS IS OBSOLETE AND FOR HISTORICAL REFERENCE ONLY; IT MAY BE DELETED. */
-  /* Each pure color has either brightness reduced or saturation reduced,
-     as appropriate, until the LAB color L (lightness) value is 68.
-
-     L = 68 was for the original pink color #FA8072, but the purples end
-     up too light to be seen easily on some monitors */
-  i++; redRef[i] = 255; greenRef[i] = 122; blueRef[i] = 122; /* 0 r */
-  i++; redRef[i] = 255; greenRef[i] = 127; blueRef[i] = 0; /* 30 */
-  i++; redRef[i] = 207; greenRef[i] = 155; blueRef[i] = 0; /* 45 */
-  i++; redRef[i] = 170; greenRef[i] = 170; blueRef[i] = 0; /* 60 */
-  i++; redRef[i] = 93; greenRef[i] = 186; blueRef[i] = 0; /* 90 */
-  i++; redRef[i] = 0; greenRef[i] = 196; blueRef[i] = 0; /* 120 g */
-  i++; redRef[i] = 0; greenRef[i] = 190; blueRef[i] = 94; /* 150 */
-  i++; redRef[i] = 0; greenRef[i] = 185; blueRef[i] = 185; /* 180 */
-  i++; redRef[i] = 87; greenRef[i] = 171; blueRef[i] = 255; /* 210 */
-  i++; redRef[i] = 156; greenRef[i] = 156; blueRef[i] = 255; /* 240 b */
-  i++; redRef[i] = 197; greenRef[i] = 140; blueRef[i] = 255; /* 270 */
-  /*i++; redRef[i] = 223; greenRef[i] = 126; blueRef[i] = 255;*/ /* 285 */
-  i++; redRef[i] = 255; greenRef[i] = 100; blueRef[i] = 255; /* 300 */
-  i++; redRef[i] = 255; greenRef[i] = 115; blueRef[i] = 185; /* 330 */
-#endif
-
-#ifdef L53S57obsolete
-  /* THIS IS OBSOLETE AND FOR HISTORICAL REFERENCE ONLY; IT MAY BE DELETED. */
-  /* Saturation is constant 57%; LAB color L (lightness) is constant 53.
-     This looks nice - colors are consistent pastels due to holding saturation
-     constant (the maximum possible due to blue) - but we lose some of the
-     distinguishability provided by saturated colors */
-  i++; redRef[i] = 206; greenRef[i] = 89; blueRef[i] = 89; /* 0 r */
-  i++; redRef[i] = 184; greenRef[i] = 105; blueRef[i] = 79; /* 15 */
-  i++; redRef[i] = 164; greenRef[i] = 117; blueRef[i] = 71; /* 30 */
-  i++; redRef[i] = 145; greenRef[i] = 124; blueRef[i] = 62; /* 45 */
-  /*i++; redRef[i] = 130; greenRef[i] = 130; blueRef[i] = 56;*/ /* 60 */
-  /*i++; redRef[i] = 116; greenRef[i] = 135; blueRef[i] = 58;*/ /* 75 */
-  i++; redRef[i] = 98; greenRef[i] = 137; blueRef[i] = 59; /* 90 */
-  /*i++; redRef[i] = 80; greenRef[i] = 140; blueRef[i] = 60;*/ /* 105 */
-  i++; redRef[i] = 62; greenRef[i] = 144; blueRef[i] = 62; /* 120 g */
-  /*i++; redRef[i] = 61; greenRef[i] = 143; blueRef[i] = 82;*/ /* 135 */
-  i++; redRef[i] = 61; greenRef[i] = 142; blueRef[i] = 102; /* 150 */
-  /*i++; redRef[i] = 60; greenRef[i] = 139; blueRef[i] = 119;*/ /* 165 */
-  /*i++; redRef[i] = 60; greenRef[i] = 140; blueRef[i] = 140;*/ /* 180 */
-  /*i++; redRef[i] = 68; greenRef[i] = 136; blueRef[i] = 159;*/ /* 195 */
-  i++; redRef[i] = 80; greenRef[i] = 132; blueRef[i] = 185; /* 210 */
-  /*i++; redRef[i] = 93; greenRef[i] = 124; blueRef[i] = 216;*/ /* 225 */
-  i++; redRef[i] = 110; greenRef[i] = 110; blueRef[i] = 255; /* 240 b */
-  i++; redRef[i] = 139; greenRef[i] = 104; blueRef[i] = 242; /* 255 */
-  i++; redRef[i] = 159; greenRef[i] = 96; blueRef[i] = 223; /* 270 */
-  i++; redRef[i] = 178; greenRef[i] = 89; blueRef[i] = 207; /* 285 */
-  i++; redRef[i] = 191; greenRef[i] = 82; blueRef[i] = 191; /* 300 */
-  i++; redRef[i] = 194; greenRef[i] = 83; blueRef[i] = 166; /* 315 */
-  i++; redRef[i] = 199; greenRef[i] = 86; blueRef[i] = 142; /* 330 */
-  /*i++; redRef[i] = 202; greenRef[i] = 87; blueRef[i] = 116;*/ /* 345 */
-#endif
-
-  if (i != PARTITIONS) { /* Double-check future edits */
-    print2("? %ld partitions but PARTITIONS = %ld\n", i, (long)PARTITIONS);
-    bug(2326); /* Don't go further to prevent out-of-range references */
-  }
-
- SKIP_INIT:
-  if (color == -1) {
-    let(&str1, "000000"); /* Return black for "(future)" color for labels with
-                             missing theorems in comments */
-    return str1;
-  }
-
-  if (color < 1 || color > maxColor) {
-    bug(2327);
-  }
-  fraction = (1.0 * ((double)color - 1)) / (double)maxColor;
-                                   /* Fractional position in "spectrum" */
-  partition = (long)(PARTITIONS * fraction);  /* Partition number (integer) */
-  if (partition >= PARTITIONS) bug(2325); /* Roundoff error? */
-  fractionInPartition = 1.0 * (fraction - (1.0 * (double)partition) / PARTITIONS)
-      * PARTITIONS; /* The fraction of this partition it covers */
-  red = (long)(1.0 * (redRef[partition] +
-          fractionInPartition *
-              (redRef[partition + 1] - redRef[partition])));
-  green = (long)(1.0 * (greenRef[partition] +
-          fractionInPartition *
-              (greenRef[partition + 1] - greenRef[partition])));
-  blue = (long)(1.0 * (blueRef[partition] +
-          fractionInPartition *
-              (blueRef[partition + 1] - blueRef[partition])));
-  /* debug */
-  /* i=1;if (g_outputToString==0) {i=0;g_outputToString=1;} */
-  /*   print2("p%ldc%ld\n", partition, color); g_outputToString=i; */
-  /*printf("red %ld green %ld blue %ld\n", red, green, blue);*/
-
-  if (red < 0 || green < 0 || blue < 0
-      || red > 255 || green > 255 || blue > 255) {
-    print2("%ld %ld %ld\n", red, green, blue);
-    bug(2323);
-  }
-  let(&str1, "      ");
-  j = sprintf(str1, "%02X%02X%02X", (unsigned int)red, (unsigned int)green,
-      (unsigned int)blue);
-  if (j != 6) bug(2324);
-  /* debug */
-  /*printf("<FONT COLOR='#%02X%02X%02X'>a </FONT>\n", red, green, blue);*/
-  return str1;
-} /* spectrumToRGB */
-#endif    /* #ifdef RAINBOW_OPTION */
-
-
-/* Added 20-Sep-03 (broken out of printTexLongMath() for better
-   modularization) */
-/* Returns the HTML code for GIFs (!g_altHtmlFlag) or Unicode (g_altHtmlFlag),
-   or LaTeX when !g_htmlFlag, for the math string (hypothesis or conclusion) that
-   is passed in. */
-/* Warning: The caller must deallocate the returned vstring. */
-vstring getTexLongMath(nmbrString *mathString, long statemNum)
-{
-  long pos;
-  vstring tex = "";
-  vstring texLine = "";
-  vstring lastTex = "";
-  flag alphnew, alphold, unknownnew, unknownold;
-
-  if (!g_texDefsRead) bug(2322); /* TeX defs were not read */
-  let(&texLine, "");
-
-  let(&lastTex, "");
-  for (pos = 0; pos < nmbrLen(mathString); pos++) {
-    let(&tex, "");
-    tex = tokenToTex(g_MathToken[mathString[pos]].tokenName, statemNum);
-              /* tokenToTex allocates tex; we must deallocate it */
-    if (!g_htmlFlag) {  /* LaTeX */
-      /* If this token and previous token begin with letter, add a thin
-           space between them */
-      /* Also, anything not in table will have space added */
-      alphnew = !!isalpha((unsigned char)(tex[0]));
-      unknownnew = 0;
-      if (!strcmp(left(tex, 10), "\\mbox{\\rm ")) { /* Token not in table */
-        unknownnew = 1;
-      }
-      alphold = !!isalpha((unsigned char)(lastTex[0]));
-      unknownold = 0;
-      if (!strcmp(left(lastTex, 10), "\\mbox{\\rm ")) { /* Token not in table*/
-        unknownold = 1;
-      }
-      /*if ((alphold && alphnew) || unknownold || (unknownnew && pos > 0)) {*/
-      /* Put thin space only between letters and/or unknowns  11/3/94 */
-      if ((alphold || unknownold) && (alphnew || unknownnew)) {
-        /* Put additional thin space between two letters */
-        /* 27-Jul-05 nm Added for new LaTeX output */
-        if (!g_oldTexFlag) {
-          let(&texLine, cat(texLine, "\\,", tex, " ", NULL));
-        } else {
-          let(&texLine, cat(texLine, "\\m{\\,", tex, "}", NULL));
-        }
-      } else {
-        /* 27-Jul-05 nm Added for new LaTeX output */
-        if (!g_oldTexFlag) {
-          let(&texLine, cat(texLine, "", tex, " ", NULL));
-        } else {
-          let(&texLine, cat(texLine, "\\m{", tex, "}", NULL));
-        }
-      }
-    } else {  /* HTML */
-
-      /* 7/27/03 When we have something like "E. x e. om x = y", the lack of
-         space between om and x looks ugly in HTML.  This kludge adds it in
-         for restricted quantifiers not followed by parenthesis, in order
-         to make the web page look a little nicer.  E.g. onminex. */
-      /* Note that the space is put between the pos-1 and the pos tokens */
-      if (pos >=4) {
-        if (!strcmp(g_MathToken[mathString[pos - 2]].tokenName, "e.")
-            && (!strcmp(g_MathToken[mathString[pos - 4]].tokenName, "E.")
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "A.")
-
-              /* 20-Oct-2018 nm per Benoit's 3-Sep, 18-Sep, 9-Oct emails */
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "prod_")
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "E*")
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "iota_")
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "Disj_")
-
-              /* 6-Apr-04 nm - indexed E! */
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "E!")
-              /* 12-Nov-05 nm - finite sums */
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "sum_")
-              /* 30-Sep-06 nm - infinite cartesian product */
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "X_")
-              /* 23-Jan-04 nm - indexed union, intersection */
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "U_")
-              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "|^|_"))
-            /* 23-Jan-04 nm - add space even for parenthesized arg */
-            /*&& strcmp(g_MathToken[mathString[pos]].tokenName, "(")*/
-            && strcmp(g_MathToken[mathString[pos]].tokenName, ")")
-            /* It also shouldn't be restricted _to_ an expression in parens. */
-            && strcmp(g_MathToken[mathString[pos - 1]].tokenName, "(")
-            /* ...or restricted _to_ a union or intersection 1-Feb-05 */
-            && strcmp(g_MathToken[mathString[pos - 1]].tokenName, "U.")
-            && strcmp(g_MathToken[mathString[pos - 1]].tokenName, "|^|")
-            /* ...or restricted _to_ an expression in braces */
-            && strcmp(g_MathToken[mathString[pos - 1]].tokenName, "{")) {
-          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
-        }
-      }
-      /* This one puts a space between the 2 x's in a case like
-         "E. x x = y".  E.g. cla4egf */
-      if (pos >=2) {
-        /* Match a token starting with a letter */
-        if (isalpha((unsigned char)(g_MathToken[mathString[pos]].tokenName[0]))) {
-          /* and make sure its length is 1 */
-          if (!(g_MathToken[mathString[pos]].tokenName[1])) {
-
-            /* 20-Sep-2017 nm */
-            /* Make sure previous token is a letter also, to prevent unneeded
-               space in "ran ( A ..." (e.g. rncoeq, dfiun3g) */
-            /* Match a token starting with a letter */
-            if (isalpha((unsigned char)(g_MathToken[mathString[pos - 1]].tokenName[0]))) {
-              /* and make sure its length is 1 */
-              if (!(g_MathToken[mathString[pos - 1]].tokenName[1])) {
-
-                /* See if it's 1st letter in a quantified expression */
-                if (!strcmp(g_MathToken[mathString[pos - 2]].tokenName, "E.")
-                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "A.")
-                    /* 26-Dec-2016 nm - "not free in" binder */
-                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "F/")
-                    /* 6-Apr-04 nm added E!, E* */
-                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "E!")
-      /* 4-Jun-06 nm added dom, ran for space btwn A,x in "E! x e. dom A x A y" */
-                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "ran")
-                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "dom")
-                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "E*")) {
-                  let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
-                }
-
-              } /* 20-Sep-2017 nm */
-            } /* 20-Sep-2017 nm */
-
-          }
-        }
-      }
-      /* This one puts a space after a letter followed by a word token
-         e.g. "A" and "suc" in "A. x e. U. A suc" in limuni2  1-Feb-05 */
-      if (pos >= 1) {
-        /* See if the next token is "suc" */
-        if (!strcmp(g_MathToken[mathString[pos]].tokenName, "suc")) {
-          /* Match a token starting with a letter for the current token */
-          if (isalpha(
-              (unsigned char)(g_MathToken[mathString[pos - 1]].tokenName[0]))) {
-            /* and make sure its length is 1 */
-            if (!(g_MathToken[mathString[pos - 1]].tokenName[1])) {
-              let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
-            }
-          }
-        }
-      }
-      /* This one puts a space before any "-." that doesn't come after
-         a parentheses e.g. ax-6 has both cases */
-      if (pos >=1) {
-        /* See if we have a non-parenthesis followed by not */
-        if (strcmp(g_MathToken[mathString[pos - 1]].tokenName, "(")
-            && !strcmp(g_MathToken[mathString[pos]].tokenName, "-.")) {
-          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
-        }
-      }
-      /* nm 9-Feb-04 This one puts a space between "S" and "(" in df-iso. */
-      if (pos >=4) {
-        if (!strcmp(g_MathToken[mathString[pos - 4]].tokenName, "Isom")
-            && !strcmp(g_MathToken[mathString[pos - 2]].tokenName, ",")
-            && !strcmp(g_MathToken[mathString[pos]].tokenName, "(")) {
-          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
-        }
-      }
-      /* nm 11-Aug-04 This one puts a space between "}" and "(" in
-         funcnvuni proof. */
-      if (pos >=1) {
-        /* See if we have "}" followed by "(" */
-        if (!strcmp(g_MathToken[mathString[pos - 1]].tokenName, "}")
-            && !strcmp(g_MathToken[mathString[pos]].tokenName, "(")) {
-          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
-        }
-      }
-      /* 7-Mar-2016 nm This one puts a space between "}" and "{" in
-         konigsberg proof. */
-      if (pos >=1) {
-        /* See if we have "}" followed by "(" */
-        if (!strcmp(g_MathToken[mathString[pos - 1]].tokenName, "}")
-            && !strcmp(g_MathToken[mathString[pos]].tokenName, "{")) {
-          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
-        }
-      }
-      /* 7/27/03 end */
-
-      let(&texLine, cat(texLine, tex, NULL));
-    } /* if !g_htmlFlag */
-    let(&lastTex, tex); /* Save for next pass */
-  } /* Next pos */
-
-  /* 8/9/03 Discard redundant white space to reduce HTML file size */
-  let(&texLine, edit(texLine, 8 + 16 + 128));
-
-  /* 1-Feb-2016 nm */
-  /* Enclose math symbols in a span to be used for font selection */
-  let(&texLine, cat(
-      (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
-      texLine,
-      (g_altHtmlFlag ? "</SPAN>" : ""), NULL));
-
-  let(&tex, "");
-  let(&lastTex, "");
-  return texLine;
-} /* getTexLongMath */
-
-
-/* Added 18-Sep-03 (broken out of metamath.c for better modularization) */
-/* Returns the TeX, or HTML code for GIFs (!g_altHtmlFlag) or Unicode
-   (g_altHtmlFlag), for a statement's hypotheses and assertion in the form
-   hyp & ... & hyp => assertion */
-/* Warning: The caller must deallocate the returned vstring (i.e. this
-   function cannot be used in let statements but must be assigned to
-   a local vstring for local deallocation) */
-vstring getTexOrHtmlHypAndAssertion(long statemNum)
-{
-  long reqHyps, essHyps, n;
-  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated directly */
-  vstring texOrHtmlCode = "";
-  vstring str2 = "";
-  /* Count the number of essential hypotheses essHyps */
-  essHyps = 0;
-  reqHyps = nmbrLen(g_Statement[statemNum].reqHypList);
-  let(&texOrHtmlCode, "");
-  for (n = 0; n < reqHyps; n++) {
-    if (g_Statement[g_Statement[statemNum].reqHypList[n]].type
-        == (char)e_) {
-      essHyps++;
-      if (texOrHtmlCode[0]) { /* Add '&' between hypotheses */
-        if (!g_htmlFlag) {
-          /* Hard-coded for set.mm! */
-          let(&texOrHtmlCode, cat(texOrHtmlCode,
-                 "\\quad\\&\\quad "
-              ,NULL));
-        } else {
-          if (g_altHtmlFlag) {
-            /* Hard-coded for set.mm! */
-            let(&texOrHtmlCode, cat(texOrHtmlCode,
-                /* 8/8/03 - Changed from Symbol to Unicode */
-/* "&nbsp;&nbsp;&nbsp;<FONT FACE=\"Symbol\"> &#38;</FONT>&nbsp;&nbsp;&nbsp;" */
-                "<SPAN ", g_htmlFont, ">",  /* 1-Feb-2016 nm */
-                "&nbsp;&nbsp;&nbsp; &amp;&nbsp;&nbsp;&nbsp;",
-                "</SPAN>",   /* 1-Feb-2016 nm */
-                NULL));
-          } else {
-            /* Hard-coded for set.mm! */
-            let(&texOrHtmlCode, cat(texOrHtmlCode,
-          "&nbsp;&nbsp;&nbsp;<IMG SRC='amp.gif' WIDTH=12 HEIGHT=19 ALT='&amp;'"
-                ," ALIGN=TOP>&nbsp;&nbsp;&nbsp;"
-                ,NULL));
-          }
-        }
-      } /* if texOrHtmlCode[0] */
-      /* Construct HTML hypothesis */
-      nmbrTmpPtr = g_Statement[g_Statement[statemNum].reqHypList[n]].mathString;
-      let(&str2, "");
-      str2 = getTexLongMath(nmbrTmpPtr, statemNum);
-      let(&texOrHtmlCode, cat(texOrHtmlCode, str2, NULL));
-    }
-  }
-  if (essHyps) {  /* Add big arrow if there were hypotheses */
-    if (!g_htmlFlag) {
-      /* Hard-coded for set.mm! */
-      let(&texOrHtmlCode, cat(texOrHtmlCode,
-                 "\\quad\\Rightarrow\\quad "
-          ,NULL));
-    } else {
-      if (g_altHtmlFlag) {
-        /* Hard-coded for set.mm! */
-        let(&texOrHtmlCode, cat(texOrHtmlCode,
-            /* 8/8/03 - Changed from Symbol to Unicode */
-/* "&nbsp;&nbsp;&nbsp;<FONT FACE=\"Symbol\"> &#222;</FONT>&nbsp;&nbsp;&nbsp;" */
- /* 29-Aug-2008 nm - added sans-serif to work around FF3 bug that produces
-      huge character heights otherwise */
-          /*  "&nbsp;&nbsp;&nbsp; &#8658;&nbsp;&nbsp;&nbsp;" */
-      "&nbsp;&nbsp;&nbsp; <FONT FACE=sans-serif>&#8658;</FONT>&nbsp;&nbsp;&nbsp;"
-            ,NULL));
-      } else {
-        /* Hard-coded for set.mm! */
-        let(&texOrHtmlCode, cat(texOrHtmlCode,
-          "&nbsp;&nbsp;&nbsp;<IMG SRC='bigto.gif' WIDTH=15 HEIGHT=19 ALT='=&gt;'"
-            ," ALIGN=TOP>&nbsp;&nbsp;&nbsp;"
-            ,NULL));
-      }
-    }
-  }
-  /* Construct TeX or HTML assertion */
-  nmbrTmpPtr = g_Statement[statemNum].mathString;
-  let(&str2, "");
-  str2 = getTexLongMath(nmbrTmpPtr, statemNum);
-  let(&texOrHtmlCode, cat(texOrHtmlCode, str2, NULL));
-
-  /* Deallocate memory */
-  let(&str2, "");
-  return texOrHtmlCode;
-}  /* getTexOrHtmlHypAndAssertion */
-
-
-
-
-/* Added 17-Nov-2015 (broken out of metamath.c for better modularization) */
-/* Called by the WRITE BIBLIOGRPAHY command and also by VERIFY MARKUP
-   for error checking */
-/* Returns 0 if OK, 1 if warning(s), 2 if any error */
-flag writeBibliography(vstring bibFile,
-    vstring labelMatch, /* Normally "*" except when called by verifyMarkup() */
-    flag errorsOnly,  /* 1 = no output, just warning msgs if any */
-    flag noFileCheck) /* 1 = ignore missing external files (mmbiblio.html) */
-{
-  flag errFlag;
-  FILE *list1_fp = NULL;
-  FILE *list2_fp = NULL;
-  long lines, p2, i, j, jend, k, l, m, n, p, q, s, pass1refs;
-  vstring str1 = "", str2 = "", str3 = "", str4 = "", newstr = "", oldstr = "";
-  pntrString *pntrTmp = NULL_PNTRSTRING;
-  flag warnFlag;
-
-  n = 0; /* 14-Jan-2016 nm Old gcc 4.6.3 wrongly says may be uninit ln 5506 */
-  pass1refs = 0; /* 25-Jan-2016 gcc 4.5.3 wrongly says may be uninit */
-  if (noFileCheck == 1 && errorsOnly == 0) {
-    bug(2336); /* If we aren't opening files, a non-error run can't work */
-  }
-  /* 10/10/02 */
-  /* This utility builds the bibliographical cross-references to various
-     textbooks and updates the user-specified file normally called
-     mmbiblio.html. */
-  warnFlag = 0; /* 1 means warning was found, 2 that error was found */
-  errFlag = 0; /* Error flag to recover input file and to set return value 2 */
-  if (noFileCheck == 0) {
-    list1_fp = fSafeOpen(bibFile, "r", 0/*noVersioningFlag*/);
-    if (list1_fp == NULL) {
-      /* Couldn't open it (error msg was provided)*/
-      return 1;
-    }
-    if (errorsOnly == 0) {
-      fclose(list1_fp);
-      /* This will rename the input mmbiblio.html as mmbiblio.html~1 */
-      list2_fp = fSafeOpen(bibFile, "w", 0/*noVersioningFlag*/);
-      if (list2_fp == NULL) {
-          /* Couldn't open it (error msg was provided)*/
-        return 1;
-      }
-      /* Note: in older versions the "~1" string was OS-dependent, but we
-         don't support VAX or THINK C anymore...  Anyway we reopen it
-         here with the renamed file in case the OS won't let us rename
-         an opened file during the fSafeOpen for write above. */
-      list1_fp = fSafeOpen(cat(bibFile, "~1", NULL), "r", 0/*noVersioningFlag*/);
-      if (list1_fp == NULL) bug(2337);
-    }
-  }
-  if (!g_texDefsRead) {
-    g_htmlFlag = 1;
-    /* Now done in readTexDefs() *
-    if (errorsOnly == 0 ) {
-      print2("Reading definitions from $t statement of %s...\n", g_input_fn);
-    }
-    */
-    if (2/*error*/ == readTexDefs(errorsOnly, noFileCheck)) {
-      errFlag = 2; /* Error flag to recover input file */
-      goto BIB_ERROR; /* An error occurred */
-    }
-  }
-
-  /* Transfer the input file up to the special "<!-- #START# -->" comment */
-  if (noFileCheck == 0) {
-    while (1) {
-      if (!linput(list1_fp, NULL, &str1)) {
-        print2(
-  "?Error: Could not find \"<!-- #START# -->\" line in input file \"%s\".\n",
-            bibFile);
-        errFlag = 2; /* Error flag to recover input file */
-        break;
-      }
-      if (errorsOnly == 0) {
-        fprintf(list2_fp, "%s\n", str1);
-      }
-      if (!strcmp(str1, "<!-- #START# -->")) break;
-    }
-    if (errFlag) goto BIB_ERROR;
-  }
-
-  p2 = 1; /* Pass 1 or 2 flag */
-  lines = 0;
-  while (1) {
-
-    if (p2 == 2) {  /* Pass 2 */
-      /* Allocate memory for sorting */
-      pntrLet(&pntrTmp, pntrSpace(lines));
-      lines = 0;
-    }
-
-    /* Scan all $a and $p statements */
-    for (i = 1; i <= g_statements; i++) {
-      if (g_Statement[i].type != (char)p_ &&
-        g_Statement[i].type != (char)a_) continue;
-
-      /* 13-Dec-2016 nm */
-      /* Normally labelMatch is *, but may be more specific for
-         use by verifyMarkup() */
-      if (!matchesList(g_Statement[i].labelName, labelMatch, '*', '?')) {
-        continue;
-      }
-
-      /* Omit ...OLD (obsolete) and ...NEW (to be implemented) statements */
-      if (instr(1, g_Statement[i].labelName, "NEW")) continue;
-      if (instr(1, g_Statement[i].labelName, "OLD")) continue;
-      let(&str1, "");
-      str1 = getDescription(i); /* Get the statement's comment */
-      if (!instr(1, str1, "[")) continue;
-      l = (signed)(strlen(str1));
-      for (j = 0; j < l; j++) {
-        if (str1[j] == '\n') str1[j] = ' '; /* Change newlines to spaces */
-        if (str1[j] == '\r') bug(2338);
-      }
-      let(&str1, edit(str1, 8 + 128 + 16)); /* Reduce & trim whitespace */
-
-      /* 15-Apr-2015 nm */
-      /* Clear out math symbols in backquotes to prevent false matches
-         to [reference] bracket */
-      k = 0; /* Math symbol mode if 1 */
-      l = (signed)(strlen(str1));
-      for (j = 0; j < l - 1; j++) {
-        if (k == 0) {
-          if (str1[j] == '`') {
-            k = 1; /* Start of math mode */
-          }
-        } else { /* In math mode */
-          if (str1[j] == '`') { /* A backquote */
-            if (str1[j + 1] == '`') {
-              /* It is an escaped backquote */
-              str1[j] = ' ';
-              str1[j + 1] = ' ';
-            } else {
-              k = 0; /* End of math mode */
-            }
-          } else { /* Not a backquote */
-            str1[j] = ' '; /* Clear out the math mode part */
-          }
-        } /* end if k == 0 */
-      } /* next j */
-
-      /* Put spaces before page #s (up to 4 digits) for sorting */
-      j = 0;
-      while (1) {
-        j = instr(j + 1, str1, " p. "); /* Heuristic - match " p. " */
-        if (!j) break;
-        if (j) {
-          for (k = j + 4; k <= (signed)(strlen(str1)) + 1; k++) {
-            if (!isdigit((unsigned char)(str1[k - 1]))) {
-              let(&str1, cat(left(str1, j + 2),
-                  space(4 - (k - (j + 4))), right(str1, j + 3), NULL));
-              /* Add ### after page number as marker */
-              let(&str1, cat(left(str1, j + 7), "###", right(str1, j + 8),
-                  NULL));
-              break;
-            }
-          }
-        }
-      }
-      /* Process any bibliographic references in comment */
-      j = 0;
-      n = 0;
-      while (1) {
-        j = instr(j + 1, str1, "["); /* Find reference (not robust) */
-        if (!j) break;
-
-        /* 13-Dec-2016 nm Fix mmbiblio.html corruption caused by
-           "[Copying an angle]" in df-ibcg in
-           set.mm.2016-09-18-JB-mbox-cleanup */
-        /* Skip if there is no trailing "]" */
-        jend = instr(j, str1, "]");
-        if (!jend) break;
-        /* Skip bracketed text with spaces */
-        /* This is a somewhat ugly workaround that lets us tolerate user
-           comments in [...] is there is at least one space.
-           The printTexComment() above handles this case with the
-           "strcspn(bibTag, " \n\r\t\f")" above; here we just have to look
-           for space since we already reduced \n \t to space (\f is probably
-           overkill, and any \r's are removed during the READ command) */
-        if (instr(1, seg(str1, j, jend), " ")) continue;
-        /* 22-Feb-2019 nm */
-        /* Skip escaped bracket "[[" */
-        if (str1[j] == '[') {  /* (j+1)th character in str1 */
-          j++; /* Skip over 2nd "[" */
-          continue;
-        }
-        if (!isalnum((unsigned char)(str1[j]))) continue; /* Not start of reference */
-        n++;
-        /* Backtrack from [reference] to a starting keyword */
-        m = 0;
-        let(&str2, edit(str1, 32)); /* to uppercase */
-
-        /* (The string search below is rather inefficient; maybe improve
-           the algorithm if speed becomes a problem.) */
-        for (k = j - 1; k >= 1; k--) {
-          /* **IMPORTANT** Make sure to update mmhlpb.c HELP WRITE BIBLIOGRAPHY
-             if new items are added to this list. */
-          if (0
-              /* 3-Jun-2018 nm Added PROOF, STATEMENT */
-              /* 12-Apr-2020 nm Added CLAIM */
-              /* 8-Aug-2020 nm Added CONJECTURE, RESULT */
-              /* 23-Aug-2020 nm Added CONCLUSION FACT INTRODUCTION PARAGRAPH
-                      SCOLIA SCOLION SUBSECTION TABLE */
-              /* Do not add SCHEMA but use AXIOM SCHEMA or THEOREM SCHEMA */
-              /* Put the most frequent ones first to speed up search;
-                 TODO: count occurrences in mmbiblio.html to find optimal order */
-              || !strcmp(mid(str2, k, (long)strlen("THEOREM")), "THEOREM")
-              || !strcmp(mid(str2, k, (long)strlen("EQUATION")), "EQUATION")
-              || !strcmp(mid(str2, k, (long)strlen("DEFINITION")), "DEFINITION")
-              || !strcmp(mid(str2, k, (long)strlen("LEMMA")), "LEMMA")
-              || !strcmp(mid(str2, k, (long)strlen("EXERCISE")), "EXERCISE")
-              || !strcmp(mid(str2, k, (long)strlen("AXIOM")), "AXIOM")
-              || !strcmp(mid(str2, k, (long)strlen("CLAIM")), "CLAIM")
-              || !strcmp(mid(str2, k, (long)strlen("CHAPTER")), "CHAPTER")
-              || !strcmp(mid(str2, k, (long)strlen("COMPARE")), "COMPARE")
-              || !strcmp(mid(str2, k, (long)strlen("CONDITION")), "CONDITION")
-              || !strcmp(mid(str2, k, (long)strlen("CONJECTURE")), "CONJECTURE")
-              || !strcmp(mid(str2, k, (long)strlen("COROLLARY")), "COROLLARY")
-              || !strcmp(mid(str2, k, (long)strlen("EXAMPLE")), "EXAMPLE")
-              || !strcmp(mid(str2, k, (long)strlen("FIGURE")), "FIGURE")
-              || !strcmp(mid(str2, k, (long)strlen("ITEM")), "ITEM")
-              || !strcmp(mid(str2, k, (long)strlen("LEMMAS")), "LEMMAS")
-              || !strcmp(mid(str2, k, (long)strlen("LINE")), "LINE")
-              || !strcmp(mid(str2, k, (long)strlen("LINES")), "LINES")
-              || !strcmp(mid(str2, k, (long)strlen("NOTATION")), "NOTATION")
-              || !strcmp(mid(str2, k, (long)strlen("NOTE")), "NOTE")
-              || !strcmp(mid(str2, k, (long)strlen("OBSERVATION")), "OBSERVATION")
-              || !strcmp(mid(str2, k, (long)strlen("PART")), "PART")
-              || !strcmp(mid(str2, k, (long)strlen("POSTULATE")), "POSTULATE")
-              || !strcmp(mid(str2, k, (long)strlen("PROBLEM")), "PROBLEM")
-              || !strcmp(mid(str2, k, (long)strlen("PROPERTY")), "PROPERTY")
-              || !strcmp(mid(str2, k, (long)strlen("PROPOSITION")), "PROPOSITION")
-              || !strcmp(mid(str2, k, (long)strlen("REMARK")), "REMARK")
-              || !strcmp(mid(str2, k, (long)strlen("RESULT")), "RESULT")
-              || !strcmp(mid(str2, k, (long)strlen("RULE")), "RULE")
-              || !strcmp(mid(str2, k, (long)strlen("SCHEME")), "SCHEME")
-              || !strcmp(mid(str2, k, (long)strlen("SECTION")), "SECTION")
-              || !strcmp(mid(str2, k, (long)strlen("PROOF")), "PROOF")
-              || !strcmp(mid(str2, k, (long)strlen("STATEMENT")), "STATEMENT")
-              || !strcmp(mid(str2, k, (long)strlen("CONCLUSION")), "CONCLUSION")
-              || !strcmp(mid(str2, k, (long)strlen("FACT")), "FACT")
-              || !strcmp(mid(str2, k, (long)strlen("INTRODUCTION")), "INTRODUCTION")
-              || !strcmp(mid(str2, k, (long)strlen("PARAGRAPH")), "PARAGRAPH")
-              || !strcmp(mid(str2, k, (long)strlen("SCOLIA")), "SCOLIA")
-              || !strcmp(mid(str2, k, (long)strlen("SCOLION")), "SCOLION")
-              || !strcmp(mid(str2, k, (long)strlen("SUBSECTION")), "SUBSECTION")
-              || !strcmp(mid(str2, k, (long)strlen("TABLE")), "TABLE")
-              ) {
-            m = k;
-            break;
-          }
-          let(&str3, ""); /* Clear tmp alloc stack created by "mid" */
-        }
-        if (!m) {
-          if (p2 == 1) {
-            print2(
-             "?Warning: Bibliography keyword missing in comment for \"%s\".\n",
-                g_Statement[i].labelName);
-            print2(
-                "    (See HELP WRITE BIBLIOGRAPHY for list of keywords.)\n");
-            warnFlag = 1;
-          }
-          continue; /* Not a bib ref - ignore */
-        }
-        /* m is at the start of a keyword */
-        p = instr(m, str1, "["); /* Start of bibliograpy reference */
-        q = instr(p, str1, "]"); /* End of bibliography reference */
-        if (q == 0) {
-          if (p2 == 1) {
-            print2(
-        "?Warning: Bibliography reference not found in HTML file in \"%s\".\n",
-              g_Statement[i].labelName);
-            warnFlag = 1;
-          }
-          continue; /* Pretend it is not a bib ref - ignore */
-        }
-        s = instr(q, str1, "###"); /* Page number marker */
-        if (!s) {
-          if (p2 == 1) {
-            print2(
-          "?Warning: No page number after [<author>] bib ref in \"%s\".\n",
-              g_Statement[i].labelName);
-            warnFlag = 1;
-          }
-          continue; /* No page number given - ignore */
-        }
-        /* Now we have a real reference; increment reference count */
-        lines++;
-        if (p2 == 1) continue; /* In 1st pass, we just count refs */
-
-        let(&str2, seg(str1, m, p - 1));     /* "Theorem #" */
-        let(&str3, seg(str1, p + 1, q - 1));  /* "[bibref]" w/out [] */
-        let(&str4, seg(str1, q + 1, s - 1)); /* " p. nnnn" */
-        str2[0] = (char)(toupper((unsigned char)(str2[0])));
-        /* Eliminate noise like "of" in "Theorem 1 of [bibref]" */
-        for (k = (long)strlen(str2); k >=1; k--) {
-          if (0
-              || !strcmp(mid(str2, k, (long)strlen(" of ")), " of ")
-              || !strcmp(mid(str2, k, (long)strlen(" in ")), " in ")
-              || !strcmp(mid(str2, k, (long)strlen(" from ")), " from ")
-              || !strcmp(mid(str2, k, (long)strlen(" on ")), " on ")
-              ) {
-            let(&str2, left(str2, k - 1));
-            break;
-          }
-          let(&str2, str2);
-        }
-
-        let(&newstr, "");
-        newstr = pinkHTML(i); /* Get little pink number */
-        let(&oldstr, cat(
-            /* Construct the sorting key */
-            /* The space() helps Th. 9 sort before Th. 10 on same page */
-            str3, " ", str4, space(20 - (long)strlen(str2)), str2,
-            "|||",  /* ||| means end of sort key */
-            /* Construct just the statement href for combining dup refs */
-            "<A HREF=\"", g_Statement[i].labelName,
-            ".html\">", g_Statement[i].labelName, "</A>",
-            newstr,
-            "&&&",  /* &&& means end of statement href */
-            /* Construct actual HTML table row (without ending tag
-               so duplicate references can be added) */
-
-            /*
-            (i < g_extHtmlStmt) ?
-               "<TR>" :
-               cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL),
-            */
-
-            /* 29-Jul-2008 nm Sandbox stuff */
-            (i < g_extHtmlStmt)
-               ? "<TR>"
-               : (i < g_mathboxStmt)
-                   ? cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL)
-                   : cat("<TR BGCOLOR=", SANDBOX_COLOR, ">", NULL),
-
-            "<TD NOWRAP>[<A HREF=\"",
-
-            /*
-            (i < g_extHtmlStmt) ?
-               g_htmlBibliography :
-               extHtmlBibliography,
-            */
-
-            /* 29-Jul-2008 nm Sandbox stuff */
-            (i < g_extHtmlStmt)
-               ? g_htmlBibliography
-               : (i < g_mathboxStmt)
-                   ? extHtmlBibliography
-                   /* Note that the sandbox uses the mmset.html
-                      bibliography */
-                   : g_htmlBibliography,
-
-            "#",
-            str3,
-            "\">", str3, "</A>]", str4,
-            "</TD><TD>", str2, "</TD><TD><A HREF=\"",
-            g_Statement[i].labelName,
-            ".html\">", g_Statement[i].labelName, "</A>",
-            newstr, NULL));
-        /* Put construction into string array for sorting */
-        let((vstring *)(&pntrTmp[lines - 1]), oldstr);
-      } /* while(1) */
-    } /* next i */
-
-    /* 'lines' should be the same in both passes */
-    if (p2 == 1) {
-      pass1refs = lines;
-    } else {
-      if (pass1refs != lines) bug(2339);
-    }
-
-    if (errorsOnly == 0 && p2 == 2) {
-      /*
-      print2("Pass %ld finished.  %ld references were processed.\n", p2, lines);
-      */
-      print2("%ld references were processed.\n", lines);
-    }
-    if (p2 == 2) break;
-    p2++;    /* Increment from pass 1 to pass 2 */
-  } /* while(1) */
-
-  /* Sort */
-  g_qsortKey = "";
-  qsort(pntrTmp, (size_t)lines, sizeof(void *), qsortStringCmp);
-
-  /* Combine duplicate references */
-  let(&str1, "");  /* Last biblio ref */
-  for (i = 0; i < lines; i++) {
-    j = instr(1, (vstring)(pntrTmp[i]), "|||");
-    let(&str2, left((vstring)(pntrTmp[i]), j - 1));
-    if (!strcmp(str1, str2)) {
-      /* n++; */ /* 17-Nov-2015 nm Deleted - why was this here? */
-      /* Combine last with this */
-      k = instr(j, (vstring)(pntrTmp[i]), "&&&");
-      /* Extract statement href */
-      let(&str3, seg((vstring)(pntrTmp[i]), j + 3, k -1));
-      let((vstring *)(&pntrTmp[i]),
-          cat((vstring)(pntrTmp[i - 1]), " &nbsp;", str3, NULL));
-      let((vstring *)(&pntrTmp[i - 1]), ""); /* Clear previous line */
-    }
-    let(&str1, str2);
-  }
-
-  /* Write output */
-  if (noFileCheck == 0 && errorsOnly == 0) {
-    n = 0; /* Table rows written */
-    for (i = 0; i < lines; i++) {
-      j = instr(1, (vstring)(pntrTmp[i]), "&&&");
-      if (j) {  /* Don't print blanked out combined lines */
-        n++;
-        /* Take off prefixes and reduce spaces */
-        let(&str1, edit(right((vstring)(pntrTmp[i]), j + 3), 16));
-        j = 1;
-        /* Break up long lines for text editors */
-        let(&g_printString, "");
-        g_outputToString = 1;
-        printLongLine(cat(str1, "</TD></TR>", NULL),
-            " ",  /* Start continuation line with space */
-            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
-        g_outputToString = 0;
-        fprintf(list2_fp, "%s", g_printString);
-        let(&g_printString, "");
-      }
-    }
-  }
-
-
-  /* Discard the input file up to the special "<!-- #END# -->" comment */
-  if (noFileCheck == 0) {
-    while (1) {
-      if (!linput(list1_fp, NULL, &str1)) {
-        print2(
-  "?Error: Could not find \"<!-- #END# -->\" line in input file \"%s\".\n",
-            bibFile);
-        errFlag = 2; /* Error flag to recover input file */
-        break;
-      }
-      if (!strcmp(str1, "<!-- #END# -->")) {
-        if (errorsOnly == 0) {
-          fprintf(list2_fp, "%s\n", str1);
-        }
-        break;
-      }
-    }
-    if (errFlag) goto BIB_ERROR;
-  }
-
-  if (noFileCheck == 0 && errorsOnly == 0) {
-    /* Transfer the rest of the input file */
-    while (1) {
-      if (!linput(list1_fp, NULL, &str1)) {
-        break;
-      }
-
-      /* Update the date stamp at the bottom of the HTML page. */
-      /* This is just a nicety; no error check is done. */
-      if (!strcmp("This page was last updated on ", left(str1, 30))) {
-        let(&str1, cat(left(str1, 30), date(), ".", NULL));
-      }
-
-      fprintf(list2_fp, "%s\n", str1);
-    }
-
-    print2("%ld table rows were written.\n", n);
-    /* Deallocate string array */
-    for (i = 0; i < lines; i++) let((vstring *)(&pntrTmp[i]), "");
-    pntrLet(&pntrTmp,NULL_PNTRSTRING);
-  }
-
-
- BIB_ERROR:
-  if (noFileCheck == 0) {
-    fclose(list1_fp);
-    if (errorsOnly == 0) {
-      fclose(list2_fp);
-    }
-    if (errorsOnly == 0) {
-      if (errFlag) {
-        /* Recover input files in case of error */
-        remove(bibFile);  /* Delete output file */
-        rename(cat(bibFile, "~1", NULL), g_fullArg[2]);
-            /* Restore input file name */
-        print2("?The file \"%s\" was not modified.\n", g_fullArg[2]);
-      }
-    }
-  }
-  if (errFlag == 2) warnFlag = 2;
-  return warnFlag;
-}  /* writeBibliography */
-
-
-/* 5-Aug-2020 nm */
-/* Returns 1 if stmt1 and stmt2 are in different mathboxes, 0 if
-   they are in the same mathbox or if one of them is not in a mathbox. */
-flag inDiffMathboxes(long stmt1, long stmt2) {
-  long mbox1, mbox2;
-  mbox1 = getMathboxNum(stmt1);
-  mbox2 = getMathboxNum(stmt2);
-  if (mbox1 == 0 || mbox2 == 0) return 0;
-  if (mbox1 != mbox2) return 1;
-  return 0;
-}
-
-/* 5-Aug-2020 nm */
-/* Returns the user of the mathbox that a statement is in, or ""
-   if the statement is not in a mathbox. */
-/* Caller should NOT deallocate returned string (it points directly to
-   g_mathboxUser[] entry) */
-vstring getMathboxUser(long stmt) {
-  long mbox;
-  mbox = getMathboxNum(stmt);
-  if (mbox == 0) return "";
-  return g_mathboxUser[mbox - 1];
-}
-
-/* 5-Aug-2020 nm */
-/* Given a statement number, find out what mathbox it's in (numbered starting
-   at 1) mainly for error messages; if it's not in a mathbox, return 0. */
-/* We assume the number of mathboxes is small enough that a linear search
-   won't slow things too much. */
-long getMathboxNum(long stmt) {
-  long mbox;
-  assignMathboxInfo(); /* In case it's not yet initialized */
-  for (mbox = 0; mbox < g_mathboxes; mbox++) {
-    if (stmt < g_mathboxStart[mbox]) break;
-  }
-  return mbox;
-} /* getMathboxNum */
-
-
-/* 5-Aug-2020 nm */
-/* Assign the global variable g_mathboxStmt, the statement number with the
-   label "mathbox", as well as g_mathboxes, g_mathboxStart[], g_mathboxEnd[],
-   and g_mathboxUser[].  For speed, we do the lookup only if it hasn't been
-   done yet.   Note that the ERASE command (eraseSource()) should set
-   g_mathboxStmt to zero as well as deallocate the strings. */
-/* This function will just return if g_mathboxStmt is already nonzero. */
-#define MB_LABEL "mathbox"
-void assignMathboxInfo(void) {
-  if (g_mathboxStmt == 0) { /* Look up "mathbox" label if it hasn't been */
-    g_mathboxStmt = lookupLabel(MB_LABEL);
-    if (g_mathboxStmt == -1) { /* There are no mathboxes */
-      g_mathboxStmt = g_statements + 1;  /* Default beyond db end if none */
-      g_mathboxes = 0;
-    } else {
-      /* Population mathbox information variables */
-      g_mathboxes = getMathboxLoc(&g_mathboxStart, &g_mathboxEnd,
-          &g_mathboxUser);
-    }
-  }
-  return;
-} /* assignMathboxInfo */
-
-
-/* 5-Aug-2020 nm */ /* (This was originally written to be able to deal with
-   local mathboxStart,End,User, which were later made global.  But there should
-   be no significant slowdown so we've kept this ability.) */
-/* 17-Jul-2020 nm */
-/* Returns the number of mathboxes, while assigning start statement, end
-   statement, and mathbox name. */
-#define MB_TAG "Mathbox for "
-long getMathboxLoc(nmbrString **mathboxStart, nmbrString **mathboxEnd,
-    pntrString **mathboxUser) {
-  long m, p, q, tagLen, stmt;
-  long mathboxes = 0;
-  vstring comment = "";
-  vstring user = "";
-  assignMathboxInfo(); /* Assign g_mathboxStmt */
-  tagLen = (long)strlen(MB_TAG);
-  /* Ensure lists are initialized */
-  if (pntrLen((pntrString *)(*mathboxUser)) != 0) bug(2347);
-  if (nmbrLen((nmbrString *)(*mathboxStart)) != 0) bug(2348);
-  if (nmbrLen((nmbrString *)(*mathboxEnd)) != 0) bug(2349);
-  for (stmt = g_mathboxStmt + 1; stmt <= g_statements; stmt++) {
-    /* Heuristic to match beginning of mathbox */
-    let(&comment, left(g_Statement[stmt].labelSectionPtr,
-        g_Statement[stmt].labelSectionLen));
-    p = 0;
-    /* This loop will skip empty mathboxes i.e. it will get the last
-       "Mathbox for " in the label section comment(s) */
-    while (1) {
-      q = instr(p + 1, comment, MB_TAG);
-      if (q == 0) break;
-      p = q; /* Save last "Mathbox for " */
-    }
-    if (p == 0) continue; /* No "Mathbox for " in this statement's comment */
-
-    /* Found a mathbox; assign user and start statement */
-    mathboxes++;
-    q = instr(p, comment, "\n");
-    if (q == 0) bug(2350); /* No end of line */
-    let(&user, seg(comment, p + tagLen, q - 1));
-    pntrLet(&(*mathboxUser), pntrAddElement(*mathboxUser));
-    (*mathboxUser)[mathboxes - 1] = "";
-    let((vstring *)(&((*mathboxUser)[mathboxes - 1])), user);
-    nmbrLet(&(*mathboxStart), nmbrAddElement(*mathboxStart, stmt));
-  } /* next stmt */
-  if (mathboxes == 0) goto RETURN_POINT;
-  /* Assign end statements */
-  nmbrLet(&(*mathboxEnd), nmbrSpace(mathboxes)); /* Pre-allocate */
-  for (m = 0; m < mathboxes - 1; m++) {
-    (*mathboxEnd)[m] = (*mathboxStart)[m + 1] - 1;
-  }
-  (*mathboxEnd)[mathboxes - 1] = g_statements; /* Assumed end of last mathbox */
- RETURN_POINT:
-  let(&comment, "");
-  let(&user, "");
-  return mathboxes;
-} /* getMathboxLoc */
+/*****************************************************************************/
+/*        Copyright (C) 2021  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+/* This module processes LaTeX and HTML output. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "mmvstr.h"
+#include "mmdata.h"
+#include "mminou.h"
+#include "mmpars.h" /* For rawSourceError and mathSrchCmp and lookupLabel */
+#include "mmwtex.h"
+#include "mmcmdl.h" /* For g_texFileName */
+
+/* All LaTeX and HTML definitions are taken from the source
+   file (read in the by READ... command).  In the source file, there should
+   be a single comment $( ... $) containing the keyword $t.  The definitions
+   start after the $t and end at the $).  Between $t and $), the definition
+   source should exist.  See the file set.mm for an example. */
+
+flag g_oldTexFlag = 0; /* Use TeX macros in output (obsolete) */
+
+flag g_htmlFlag = 0;  /* HTML flag: 0 = TeX, 1 = HTML */
+flag g_altHtmlFlag = 0;  /* Use "althtmldef" instead of "htmldef".  This is
+    intended to allow the generation of pages with the Unicode font
+    instead of the individual GIF files. */
+flag g_briefHtmlFlag = 0;  /* Output statement lists only, for statement display
+                in other HTML pages, such as the Proof Explorer home page */
+long g_extHtmlStmt = 0; /* At this statement and above, use the exthtmlxxx
+    variables for title, links, etc.  This was put in to allow proper
+    generation of the Hilbert Space Explorer extension to the set.mm
+    database. */
+
+/* Globals to hold mathbox information.  They should be re-initialized
+   by the ERASE command (eraseSource()).  g_mathboxStmt = 0 indicates
+   it and the other variables haven't been initialized. */
+long g_mathboxStmt = 0; /* At this statement and above, use SANDBOX_COLOR
+    background for theorem, mmrecent, & mmbiblio lists */
+    /* 0 means it hasn't been looked up yet; g_statements + 1 means
+       there is no mathbox */
+long g_mathboxes = 0; /* # of mathboxes */
+/* The following 3 strings are 0-based e.g. g_mathboxStart[0] is for
+   mathbox #1 */
+nmbrString_def(g_mathboxStart); /* Start stmt vs. mathbox # */
+nmbrString_def(g_mathboxEnd); /* End stmt vs. mathbox # */
+pntrString_def(g_mathboxUser); /* User name vs. mathbox # */
+
+/* This is the list of characters causing the space before the opening "`"
+   in a math string in a comment to be removed for HTML output. */
+#define OPENING_PUNCTUATION "(['\""
+/* This is the list of characters causing the space after the closing "`"
+   in a math string in a comment to be removed for HTML output. */
+#define CLOSING_PUNCTUATION ".,;)?!:]'\"_-"
+
+/* Tex output file */
+FILE *g_texFilePtr = NULL;
+flag g_texFileOpenFlag = 0;
+
+/* Global variables */
+flag g_texDefsRead = 0;
+struct texDef_struct *g_TexDefs;
+
+/* Variables local to this module (except some $t variables) */
+long numSymbs;
+#define DOLLAR_SUBST 2
+    /* Substitute character for $ in converting to obsolete $l,$m,$n
+       comments - Use '$' instead of non-printable ASCII 2 for debugging */
+
+/* Variables set by the language in the set.mm etc. $t statement */
+/* Some of these are global; see mmwtex.h */
+vstring_def(g_htmlCSS); /* Set by g_htmlCSS commands */
+vstring_def(g_htmlFont); /* Set by htmlfont commands */
+vstring_def(g_htmlVarColor); /* Set by htmlvarcolor commands */
+vstring_def(htmlExtUrl); /* Set by htmlexturl command */
+vstring_def(htmlTitle); /* Set by htmltitle command */
+  vstring_def(htmlTitleAbbr); /* Extracted from htmlTitle */
+vstring_def(g_htmlHome); /* Set by htmlhome command */
+  /* Future - assign these in the $t set.mm comment instead of g_htmlHome */
+  vstring_def(g_htmlHomeHREF); /* Extracted from g_htmlHome */
+  vstring_def(g_htmlHomeIMG); /* Extracted from g_htmlHome */
+vstring_def(g_htmlBibliography); /* Optional; set by htmlbibliography command */
+vstring_def(extHtmlLabel); /* Set by exthtmllabel command - where extHtml starts */
+vstring_def(g_extHtmlTitle); /* Set by exthtmltitle command (global!) */
+  vstring_def(g_extHtmlTitleAbbr); /* Extracted from htmlTitle */
+vstring_def(extHtmlHome); /* Set by exthtmlhome command */
+  /* Future - assign these in the $t set.mm comment instead of g_htmlHome */
+  vstring_def(extHtmlHomeHREF); /* Extracted from extHtmlHome */
+  vstring_def(extHtmlHomeIMG); /* Extracted from extHtmlHome */
+vstring_def(extHtmlBibliography); /* Optional; set by exthtmlbibliography command */
+vstring_def(htmlDir); /* Directory for GIF version, set by htmldir command */
+vstring_def(altHtmlDir); /* Directory for Unicode Font version, set by
+                            althtmldir command */
+
+/* Sandbox stuff */
+vstring_def(sandboxHome);
+  vstring_def(sandboxHomeHREF); /* Extracted from extHtmlHome */
+  vstring_def(sandboxHomeIMG); /* Extracted from extHtmlHome */
+vstring_def(sandboxTitle);
+  vstring_def(sandboxTitleAbbr);
+
+/* Variables holding all HTML <a name..> tags from bibliography pages  */
+vstring_def(g_htmlBibliographyTags);
+vstring_def(extHtmlBibliographyTags);
+
+
+void eraseTexDefs(void) {
+  /* Deallocate the texdef/htmldef storage */
+  long i;
+  if (g_texDefsRead) {  /* If not (already deallocated or never allocated) */
+    g_texDefsRead = 0;
+
+    for (i = 0; i < numSymbs; i++) {  /* Deallocate structure member i */
+      free_vstring(g_TexDefs[i].tokenName);
+      free_vstring(g_TexDefs[i].texEquiv);
+    }
+    free(g_TexDefs); /* Deallocate the structure */
+  }
+  return;
+} /* eraseTexDefs */
+
+
+/* Returns 2 if there were severe parsing errors, 1 if there were warnings but
+   no errors, 0 if no errors or warnings */
+flag readTexDefs(
+  flag errorsOnly,  /* 1 = suppress non-error messages */
+  flag gifCheck   /* 1 = check for missing GIFs */)
+{
+
+  char *startPtr;
+  long lineNumOffset = 0;
+  char *fbPtr;
+  char *tmpPtr;
+  char *tmpPtr2;
+  long charCount;
+  long i, j, k, p;
+  long lineNum;
+  long tokenLength;
+  char zapChar;
+  long cmd;
+  long parsePass;
+  vstring_def(token);
+  vstring_def(partialToken);
+  FILE *tmpFp;
+  static flag saveHtmlFlag = -1; /* -1 to force 1st read */
+  static flag saveAltHtmlFlag = 1; /* -1 to force 1st read */
+  flag warningFound = 0; /* 1 if a warning was found */
+  char *dollarTStmtPtr = NULL; /* Pointer to label section of statement with
+                              the $t comment */
+
+  /* bsearch returned values for use in error-checking */
+  void *g_mathKeyPtr; /* bsearch returned value for math symbol lookup */
+  void *texDefsPtr; /* For binary search */
+
+  if (saveHtmlFlag != g_htmlFlag || saveAltHtmlFlag != g_altHtmlFlag
+      || !g_texDefsRead) {
+    /* One or both changed - we need to erase and re-read */
+    eraseTexDefs();
+    saveHtmlFlag = g_htmlFlag; /* Save for next call to readTexDefs() */
+    saveAltHtmlFlag = g_altHtmlFlag;  /* Save for next call to readTexDefs() */
+    if (g_htmlFlag == 0 /* Tex */ && g_altHtmlFlag == 1) {
+      bug(2301); /* Nonsensical combination */
+    }
+  } else {
+    /* Nothing changed; don't need to read again */
+    return 0; /* No errors */
+  }
+
+  /* Initial values below will be overridden if a user assignment exists in the
+     $t comment of the xxx.mm input file */
+  let(&htmlTitle, "Metamath Test Page"); /* Set by htmltitle command in $t
+                                                 comment */
+  let(&g_htmlHome, cat("<A HREF=\"mmset.html\"><FONT SIZE=-2 FACE=sans-serif>",
+    "<IMG SRC=\"mm.gif\" BORDER=0 ALT=",
+    "\"Home\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE STYLE=\"margin-bottom:0px\">",
+    "Home</FONT></A>", NULL));
+                                     /* Set by htmlhome command in $t comment */
+
+  if (errorsOnly == 0) {
+    print2("Reading definitions from $t statement of %s...\n", g_input_fn);
+  }
+
+  /* Find the comment with the $t */
+  vstring_def(fileBuf); /* This used to point to the input file buffer of an external
+                   latex.def file; now it's from the xxx.mm $t comment, so we
+                   make it a normal string */
+
+  /* Note that g_Statement[g_statements + 1] is a special (empty) statement whose
+     labelSection holds any comment after the last statement. */
+  for (i = 1; i <= g_statements + 1; i++) {
+    /* We do low-level stuff on the xxx.mm input file buffer for speed */
+    tmpPtr = g_Statement[i].labelSectionPtr;
+    j = g_Statement[i].labelSectionLen;
+    /* Note that for g_Statement[g_statements + 1], the lineNum is one plus the
+       number of lines in the file */
+    if (!fileBuf[0]) lineNumOffset = g_Statement[i].lineNum; /* Save for later */
+        /* (Don't save if we're in scan to end for double $t detection) */
+    zapChar = tmpPtr[j]; /* Save the original character */
+    tmpPtr[j] = 0; /* Create an end-of-string */
+    if (instr(1, tmpPtr, "$t")) {
+      /* Found a $t comment */
+      /* Make sure this isn't a second one in another statement */
+      /* (A second one in the labelSection of one statement will trigger
+         an error below.) */
+      if (fileBuf[0]) {
+        print2("?Error: There are two comments containing a $t keyword in \"%s\".\n",
+            g_input_fn);
+        free_vstring(fileBuf);
+        return 2;
+      }
+      let(&fileBuf, tmpPtr);
+      tmpPtr[j] = zapChar;
+      dollarTStmtPtr = g_Statement[i].labelSectionPtr;
+      /* break; */ /* Continue to end to detect double $t */
+    }
+    tmpPtr[j] = zapChar; /* Restore the xxx.mm input file buffer */
+  } /* next i */
+  /* If the $t wasn't found, fileBuf will be "", causing error message below. */
+  /* Compute line number offset of beginning of g_Statement[i].labelSection for
+     use in error messages */
+  j = (long)strlen(fileBuf);
+  for (i = 0; i < j; i++) {
+    if (fileBuf[i] == '\n') lineNumOffset--;
+  }
+
+
+#define LATEXDEF 1
+#define HTMLDEF 2
+#define HTMLVARCOLOR 3
+#define HTMLTITLE 4
+#define HTMLHOME 5
+#define ALTHTMLDEF 6
+#define EXTHTMLTITLE 7
+#define EXTHTMLHOME 8
+#define EXTHTMLLABEL 9
+#define HTMLDIR 10
+#define ALTHTMLDIR 11
+#define HTMLBIBLIOGRAPHY 12
+#define EXTHTMLBIBLIOGRAPHY 13
+#define HTMLCSS 14
+#define HTMLFONT 15
+#define HTMLEXTURL 16
+
+  startPtr = fileBuf;
+
+  /* Find $t command */
+  while (1) {
+    if (startPtr[0] == '$') {
+      if (startPtr[1] == 't') {
+        startPtr++;
+        break;
+      }
+    }
+    if (startPtr[0] == 0) break;
+    startPtr++;
+  }
+  if (startPtr[0] == 0) {
+    print2("?Error: There is no $t command in the file \"%s\".\n", g_input_fn);
+    print2(
+"The file should have exactly one comment of the form $(...$t...$) with\n");
+    print2("the LaTeX and HTML definitions between $t and $).\n");
+    free_vstring(fileBuf);
+    return 2;
+  }
+  startPtr++; /* Move to 1st char after $t */
+
+  /* Search for the ending $) and zap the $) to be end-of-string */
+  tmpPtr = startPtr;
+  while (1) {
+    if (tmpPtr[0] == '$') {
+      if (tmpPtr[1] == ')') {
+        break;
+      }
+    }
+    if (tmpPtr[0] == 0) break;
+    tmpPtr++;
+  }
+  if (tmpPtr[0] == 0) {
+    print2(
+  "?Error: There is no $) comment closure after the $t keyword in \"%s\".\n",
+        g_input_fn);
+    free_vstring(fileBuf);
+    return 2;
+  }
+
+  /* Make sure there aren't two comments with $t commands */
+  tmpPtr2 = tmpPtr;
+  while (1) {
+    if (tmpPtr2[0] == '$') {
+      if (tmpPtr2[1] == 't') {
+        print2(
+  "?Error: There are two comments containing a $t keyword in \"%s\".\n",
+            g_input_fn);
+        free_vstring(fileBuf);
+        return 2;
+      }
+    }
+    if (tmpPtr2[0] == 0) break;
+    tmpPtr2++;
+  }
+
+   /* Force end of string at the $ in $) */
+  tmpPtr[0] = '\n';
+  tmpPtr[1] = 0;
+
+  charCount = tmpPtr + 1 - fileBuf; /* For bug check */
+
+  for (parsePass = 1; parsePass <= 2; parsePass++) {
+    /* Pass 1 - Count the number of symbols defined and alloc g_TexDefs array */
+    /* Pass 2 - Assign the texDefs array */
+    numSymbs = 0;
+    fbPtr = startPtr;
+
+    while (1) {
+
+      /* Get next token */
+      fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
+      tokenLength = texDefTokenLen(fbPtr);
+
+      /* Process token - command */
+      if (!tokenLength) break; /* End of file */
+      zapChar = fbPtr[tokenLength]; /* Char to restore after zapping source */
+      fbPtr[tokenLength] = 0; /* Create end of string */
+      cmd = lookup(fbPtr,
+          "latexdef,htmldef,htmlvarcolor,htmltitle,htmlhome"
+          ",althtmldef,exthtmltitle,exthtmlhome,exthtmllabel,htmldir"
+          ",althtmldir,htmlbibliography,exthtmlbibliography"
+          ",htmlcss,htmlfont,htmlexturl"
+          );
+      fbPtr[tokenLength] = zapChar;
+      if (cmd == 0) {
+        lineNum = lineNumOffset;
+        for (i = 0; i < (fbPtr - fileBuf); i++) {
+          if (fileBuf[i] == '\n') lineNum++;
+        }
+        rawSourceError(/*fileBuf*/g_sourcePtr,
+            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf), tokenLength,
+            cat("Expected \"latexdef\", \"htmldef\", \"htmlvarcolor\",",
+            " \"htmltitle\", \"htmlhome\", \"althtmldef\",",
+            " \"exthtmltitle\", \"exthtmlhome\", \"exthtmllabel\",",
+            " \"htmldir\", \"althtmldir\",",
+            " \"htmlbibliography\", \"exthtmlbibliography\",",
+            " \"htmlcss\", \"htmlfont\",",
+            " or \"htmlexturl\" here.",
+            NULL));
+        free_vstring(fileBuf);
+        return 2;
+      }
+      fbPtr = fbPtr + tokenLength;
+
+      if (cmd != HTMLVARCOLOR && cmd != HTMLTITLE && cmd != HTMLHOME
+          && cmd != EXTHTMLTITLE && cmd != EXTHTMLHOME && cmd != EXTHTMLLABEL
+          && cmd != HTMLDIR && cmd != ALTHTMLDIR
+          && cmd != HTMLBIBLIOGRAPHY && cmd != EXTHTMLBIBLIOGRAPHY
+          && cmd != HTMLCSS && cmd != HTMLFONT && cmd != HTMLEXTURL) {
+         /* Get next token - string in quotes */
+        fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
+        tokenLength = texDefTokenLen(fbPtr);
+
+        /* Process token - string in quotes */
+        if (fbPtr[0] != '\"' && fbPtr[0] != '\'') {
+          if (!tokenLength) { /* Abnormal end-of-file */
+            fbPtr--; /* Format for error message */
+            tokenLength++;
+          }
+          lineNum = lineNumOffset;
+          for (i = 0; i < (fbPtr - fileBuf); i++) {
+            if (fileBuf[i] == '\n') lineNum++;
+          }
+          rawSourceError(/*fileBuf*/g_sourcePtr,
+            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
+              tokenLength,
+              "Expected a quoted string here.");
+          free_vstring(fileBuf);
+          return 2;
+        }
+        if (parsePass == 2) {
+          zapChar = fbPtr[tokenLength - 1]; /* Chr to restore after zapping src */
+          fbPtr[tokenLength - 1] = 0; /* Create end of string */
+          let(&token, fbPtr + 1); /* Get ASCII token; note that leading and
+              trailing quotes are omitted. */
+          fbPtr[tokenLength - 1] = zapChar;
+
+          /* Change double internal quotes to single quotes */
+          /* Do this only for double quotes matching the
+             outer quotes.  fbPtr[0] is the quote character. */
+          if (fbPtr[0] != '\"' && fbPtr[0] != '\'') bug(2329);
+          j = (long)strlen(token);
+          for (i = 0; i < j - 1; i++) {
+            if (token[i] == fbPtr[0] &&
+                token[i + 1] == fbPtr[0]) {
+              let(&token, cat(left(token,
+                  i + 1), right(token, i + 3), NULL));
+              j--;
+            }
+          }
+
+          if ((cmd == LATEXDEF && !g_htmlFlag)
+              || (cmd == HTMLDEF && g_htmlFlag && !g_altHtmlFlag)
+              || (cmd == ALTHTMLDEF && g_htmlFlag && g_altHtmlFlag)) {
+            g_TexDefs[numSymbs].tokenName = "";
+            let(&(g_TexDefs[numSymbs].tokenName), token);
+          }
+        } /* if (parsePass == 2) */
+
+        fbPtr = fbPtr + tokenLength;
+      } /* if (cmd != HTMLVARCOLOR && cmd != HTMLTITLE && cmd != HTMLHOME...) */
+
+      if (cmd != HTMLVARCOLOR && cmd != HTMLTITLE && cmd != HTMLHOME
+          && cmd != EXTHTMLTITLE && cmd != EXTHTMLHOME && cmd != EXTHTMLLABEL
+          && cmd != HTMLDIR && cmd != ALTHTMLDIR
+          && cmd != HTMLBIBLIOGRAPHY && cmd != EXTHTMLBIBLIOGRAPHY
+          && cmd != HTMLCSS && cmd != HTMLFONT && cmd != HTMLEXTURL) {
+        /* Get next token -- "as" */
+        fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
+        tokenLength = texDefTokenLen(fbPtr);
+        zapChar = fbPtr[tokenLength]; /* Char to restore after zapping source */
+        fbPtr[tokenLength] = 0; /* Create end of string */
+        if (strcmp(fbPtr, "as")) {
+          if (!tokenLength) { /* Abnormal end-of-file */
+            fbPtr--; /* Format for error message */
+            tokenLength++;
+          }
+          lineNum = lineNumOffset;
+          for (i = 0; i < (fbPtr - fileBuf); i++) {
+            if (fileBuf[i] == '\n') lineNum++;
+          }
+          rawSourceError(/*fileBuf*/g_sourcePtr,
+            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
+              tokenLength,
+              "Expected the keyword \"as\" here.");
+          free_vstring(fileBuf);
+          return 2;
+        }
+        fbPtr[tokenLength] = zapChar;
+        fbPtr = fbPtr + tokenLength;
+      } /* if (cmd != HTMLVARCOLOR && ... */
+
+      if (parsePass == 2) {
+        /* Initialize LaTeX/HTML equivalent */
+        let(&token, "");
+      }
+
+      /* Scan   "<string>" + "<string>" + ...   until ";" found */
+      while (1) {
+
+        /* Get next token - string in quotes */
+        fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
+        tokenLength = texDefTokenLen(fbPtr);
+        if (fbPtr[0] != '\"' && fbPtr[0] != '\'') {
+          if (!tokenLength) { /* Abnormal end-of-file */
+            fbPtr--; /* Format for error message */
+            tokenLength++;
+          }
+          lineNum = lineNumOffset;
+          for (i = 0; i < (fbPtr - fileBuf); i++) {
+            if (fileBuf[i] == '\n') lineNum++;
+          }
+          rawSourceError(/*fileBuf*/g_sourcePtr,
+            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
+              tokenLength,
+              "Expected a quoted string here.");
+          free_vstring(fileBuf);
+          return 2;
+        }
+        if (parsePass == 2) {
+          zapChar = fbPtr[tokenLength - 1]; /* Chr to restore after zapping src */
+          fbPtr[tokenLength - 1] = 0; /* Create end of string */
+          let(&partialToken, fbPtr + 1); /* Get ASCII token; note that leading
+              and trailing quotes are omitted. */
+          fbPtr[tokenLength - 1] = zapChar;
+
+          /* Change double internal quotes to single quotes */
+          /* Do this only for double quotes matching the
+             outer quotes.  fbPtr[0] is the quote character. */
+          if (fbPtr[0] != '\"' && fbPtr[0] != '\'') bug(2330);
+          j = (long)strlen(partialToken);
+          for (i = 0; i < j - 1; i++) {
+            if (token[i] == fbPtr[0] &&
+                token[i + 1] == fbPtr[0]) {
+              let(&partialToken, cat(left(partialToken,
+                  i + 1), right(token, i + 3), NULL));
+              j--;
+            }
+          }
+
+          /* Check that string is on a single line */
+          tmpPtr2 = strchr(partialToken, '\n');
+          if (tmpPtr2 != NULL) {
+            lineNum = lineNumOffset;
+            for (i = 0; i < (fbPtr - fileBuf); i++) {
+              if (fileBuf[i] == '\n') lineNum++;
+            }
+
+            rawSourceError(/*fileBuf*/g_sourcePtr,
+                /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
+                tmpPtr2 - partialToken + 1 /*tokenLength on current line*/,
+                "String should be on a single line.");
+          }
+
+          /* Combine the string part to the main token we're building */
+          let(&token, cat(token, partialToken, NULL));
+
+        } /* (parsePass == 2) */
+
+        fbPtr = fbPtr + tokenLength;
+
+
+        /* Get next token - "+" or ";" */
+        fbPtr = fbPtr + texDefWhiteSpaceLen(fbPtr);
+        tokenLength = texDefTokenLen(fbPtr);
+        if ((fbPtr[0] != '+' && fbPtr[0] != ';') || tokenLength != 1) {
+          if (!tokenLength) { /* Abnormal end-of-file */
+            fbPtr--; /* Format for error message */
+            tokenLength++;
+          }
+          lineNum = lineNumOffset;
+          for (i = 0; i < (fbPtr - fileBuf); i++) {
+            if (fileBuf[i] == '\n') {
+              lineNum++;
+            }
+          }
+          rawSourceError(/*fileBuf*/g_sourcePtr,
+            /*fbPtr*/dollarTStmtPtr + (fbPtr - fileBuf),
+              tokenLength, /*lineNum, g_input_fn,*/
+              "Expected \"+\" or \";\" here.");
+          free_vstring(fileBuf);
+         return 2;
+        }
+        fbPtr = fbPtr + tokenLength;
+
+        if (fbPtr[-1] == ';') break;
+
+      } /* End while */
+
+
+      if (parsePass == 2) {
+        if ((cmd == LATEXDEF && !g_htmlFlag)
+            || (cmd == HTMLDEF && g_htmlFlag && !g_altHtmlFlag)
+            || (cmd == ALTHTMLDEF && g_htmlFlag && g_altHtmlFlag)) {
+          g_TexDefs[numSymbs].texEquiv = "";
+          let(&(g_TexDefs[numSymbs].texEquiv), token);
+        }
+        if (cmd == HTMLVARCOLOR) {
+          let(&g_htmlVarColor, cat(g_htmlVarColor, " ", token, NULL));
+        }
+        if (cmd == HTMLTITLE) {
+          let(&htmlTitle, token);
+        }
+        if (cmd == HTMLHOME) {
+          let(&g_htmlHome, token);
+        }
+        if (cmd == EXTHTMLTITLE) {
+          let(&g_extHtmlTitle, token);
+        }
+        if (cmd == EXTHTMLHOME) {
+          let(&extHtmlHome, token);
+        }
+        if (cmd == EXTHTMLLABEL) {
+          let(&extHtmlLabel, token);
+        }
+        if (cmd == HTMLDIR) {
+          let(&htmlDir, token);
+        }
+        if (cmd == ALTHTMLDIR) {
+          let(&altHtmlDir, token);
+        }
+        if (cmd == HTMLBIBLIOGRAPHY) {
+          let(&g_htmlBibliography, token);
+        }
+        if (cmd == EXTHTMLBIBLIOGRAPHY) {
+          let(&extHtmlBibliography, token);
+        }
+        if (cmd == HTMLCSS) {
+          let(&g_htmlCSS, token);
+          /* User's CSS */
+          /* Convert characters "\n" to new line - maybe do for other fields too? */
+          do {
+            p = instr(1, g_htmlCSS, "\\n");
+            if (p != 0) {
+              let(&g_htmlCSS, cat(left(g_htmlCSS, p - 1), "\n",
+                  right(g_htmlCSS, p + 2), NULL));
+            }
+          } while (p != 0);
+        }
+        if (cmd == HTMLFONT) {
+          let(&g_htmlFont, token);
+        }
+        if (cmd == HTMLEXTURL) {
+          let(&htmlExtUrl, token);
+        }
+      }
+
+      if ((cmd == LATEXDEF && !g_htmlFlag)
+          || (cmd == HTMLDEF && g_htmlFlag && !g_altHtmlFlag)
+          || (cmd == ALTHTMLDEF && g_htmlFlag && g_altHtmlFlag)) {
+        numSymbs++;
+      }
+
+    } /* End while */
+
+    if (fbPtr != fileBuf + charCount) bug(2305);
+
+    if (parsePass == 1 ) {
+      if (errorsOnly == 0) {
+        print2("%ld typesetting statements were read from \"%s\".\n",
+            numSymbs, g_input_fn);
+      }
+      g_TexDefs = malloc((size_t)numSymbs * sizeof(struct texDef_struct));
+      if (!g_TexDefs) outOfMemory("#99 (TeX symbols)");
+    }
+
+  } /* next parsePass */
+
+
+  /* Sort the tokens for later lookup */
+  qsort(g_TexDefs, (size_t)numSymbs, sizeof(struct texDef_struct), texSortCmp);
+
+  /* Check for duplicate definitions */
+  for (i = 1; i < numSymbs; i++) {
+    if (!strcmp(g_TexDefs[i].tokenName, g_TexDefs[i - 1].tokenName)) {
+      printLongLine(cat("?Warning: Token ", g_TexDefs[i].tokenName,
+          " is defined more than once in ",
+          g_htmlFlag
+            ? (g_altHtmlFlag ? "an althtmldef" : "an htmldef")
+            : "a latexdef",
+          " statement.", NULL),
+          "", " ");
+      warningFound = 1;
+    }
+  }
+
+  /* Check to make sure all definitions are for a real math token */
+  for (i = 0; i < numSymbs; i++) {
+    /* Note:  g_mathKey, g_mathTokens, and mathSrchCmp are assigned or defined
+       in mmpars.c. */
+    g_mathKeyPtr = (void *)bsearch(g_TexDefs[i].tokenName, g_mathKey,
+        (size_t)g_mathTokens, sizeof(long), mathSrchCmp);
+    if (!g_mathKeyPtr) {
+      printLongLine(cat("?Warning: The token \"", g_TexDefs[i].tokenName,
+          "\", which was defined in ",
+          g_htmlFlag
+            ? (g_altHtmlFlag ? "an althtmldef" : "an htmldef")
+            : "a latexdef",
+          " statement, was not declared in any $v or $c statement.", NULL),
+          "", " ");
+      warningFound = 1;
+    }
+  }
+
+  /* Check to make sure all math tokens have typesetting definitions */
+  for (i = 0; i < g_mathTokens; i++) {
+    texDefsPtr = (void *)bsearch(g_MathToken[i].tokenName, g_TexDefs,
+        (size_t)numSymbs, sizeof(struct texDef_struct), texSrchCmp);
+    if (!texDefsPtr) {
+      printLongLine(cat("?Warning: The token \"", g_MathToken[i].tokenName,
+       "\", which was defined in a $v or $c statement, was not declared in ",
+          g_htmlFlag
+            ? (g_altHtmlFlag ? "an althtmldef" : "an htmldef")
+            : "a latexdef",
+          " statement.", NULL),
+          "", " ");
+      warningFound = 1;
+    }
+  }
+
+  /* Check to make sure all GIFs are present */
+  if (g_htmlFlag) {
+    for (i = 0; i < numSymbs; i++) {
+      tmpPtr = g_TexDefs[i].texEquiv;
+      k = 0;
+      while (1) {
+        j = instr(k + 1, tmpPtr, "IMG SRC=");
+                   /* Note that only an exact match with
+                      "IMG SRC=" is currently handled */
+        if (j == 0) break;
+        k = instr(j + 9, g_TexDefs[i].texEquiv, mid(tmpPtr, j + 8, 1));
+                                           /* Get position of trailing quote */
+                                    /* Future:  use strchr instead of mid()
+                                       for efficiency? */
+        let(&token, seg(tmpPtr, j + 9, k - 1));  /* Get name of .gif (.png) */
+        if (k == 0) break;  /* Future: we may want to issue "missing
+                                     trailing quote" warning */
+           /* (We test k after the let() so that the temporary string stack
+              entry created by mid() is emptied and won't overflow */
+        if (gifCheck) {
+          tmpFp = fopen(token, "r"); /* See if it exists */
+          if (!tmpFp) {
+            printLongLine(cat("?Warning: The file \"", token,
+                "\", which is referenced in an htmldef",
+                " statement, was not found.", NULL),
+                "", " ");
+            warningFound = 1;
+          } else {
+            fclose(tmpFp);
+          }
+        }
+      }
+    }
+  }
+
+
+  /* Look up the extended database start label */
+  if (extHtmlLabel[0]) {
+    for (i = 1; i <= g_statements; i++) {
+      if (!strcmp(extHtmlLabel, g_Statement[i].labelName)) break;
+    }
+    if (i > g_statements) {
+      printLongLine(cat("?Warning: There is no statement with label \"",
+          extHtmlLabel,
+          "\" (specified by exthtmllabel in the database source $t comment).  ",
+          "Use SHOW LABELS for a list of valid labels.", NULL), "", " ");
+      warningFound = 1;
+    }
+    g_extHtmlStmt = i;
+  } else {
+    /* There is no extended database; set threshold to beyond end of db */
+    g_extHtmlStmt = g_statements + 1;
+  }
+
+  assignMathboxInfo();
+  /* In case there is not extended (Hilbert Space Explorer) section,
+     but there is a sandbox section, make the extended section "empty". */
+  if (g_extHtmlStmt == g_statements + 1) g_extHtmlStmt = g_mathboxStmt;
+  let(&sandboxHome, cat("<A HREF=\"mmtheorems.html#sandbox:bighdr\">",
+    "<FONT SIZE=-2 FACE=sans-serif>",
+    "<IMG SRC=\"_sandbox.gif\" BORDER=0 ALT=",
+    "\"Table of Contents\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE>",
+    "Table of Contents</FONT></A>", NULL));
+  let(&sandboxHomeHREF, "mmtheorems.html#sandbox:bighdr");
+  let(&sandboxHomeIMG, "_sandbox.gif");
+  let(&sandboxTitleAbbr, "Users' Mathboxes");
+  let(&sandboxTitle, "Users' Mathboxes");
+
+  /* Extract derived variables from the $t variables */
+  /* (In the future, it might be better to do this directly in the $t.) */
+  i = instr(1, g_htmlHome, "HREF=\"") + 5;
+  if (i == 5) {
+    printLongLine(
+        "?Warning: In the $t comment, htmlhome has no 'HREF=\"'.", "", " ");
+    warningFound = 1;
+  }
+  j = instr(i + 1, g_htmlHome, "\"");
+  let(&g_htmlHomeHREF, seg(g_htmlHome, i + 1, j - 1));
+  i = instr(1, g_htmlHome, "IMG SRC=\"") + 8;
+  if (i == 8) {
+    printLongLine(
+        "?Warning: In the $t comment, htmlhome has no 'IMG SRC=\"'.", "", " ");
+    warningFound = 1;
+  }
+  j = instr(i + 1, g_htmlHome, "\"");
+  let(&g_htmlHomeIMG, seg(g_htmlHome, i + 1, j - 1));
+
+
+  /* Compose abbreviated title from capital letters */
+  j = (long)strlen(htmlTitle);
+  let(&htmlTitleAbbr, "");
+  for (i = 1; i <= j; i++) {
+    if (htmlTitle[i - 1] >= 'A' && htmlTitle[i -1] <= 'Z') {
+      let(&htmlTitleAbbr, cat(htmlTitleAbbr, chr(htmlTitle[i - 1]), NULL));
+    }
+  }
+  let(&htmlTitleAbbr, cat(htmlTitleAbbr, " Home", NULL));
+
+  if (g_extHtmlStmt < g_statements + 1 /* If extended section exists */
+      && g_extHtmlStmt != g_mathboxStmt) { /* and is not an empty dummy section */
+    i = instr(1, extHtmlHome, "HREF=\"") + 5;
+    if (i == 5) {
+      printLongLine(
+          "?Warning: In the $t comment, exthtmlhome has no 'HREF=\"'.", "", " ");
+      warningFound = 1;
+    }
+    j = instr(i + 1, extHtmlHome, "\"");
+    let(&extHtmlHomeHREF, seg(extHtmlHome, i + 1, j - 1));
+    i = instr(1, extHtmlHome, "IMG SRC=\"") + 8;
+    if (i == 8) {
+      printLongLine(
+          "?Warning: In the $t comment, exthtmlhome has no 'IMG SRC=\"'.", "", " ");
+      warningFound = 1;
+    }
+    j = instr(i + 1, extHtmlHome, "\"");
+    let(&extHtmlHomeIMG, seg(extHtmlHome, i + 1, j - 1));
+    /* Compose abbreviated title from capital letters */
+    j = (long)strlen(g_extHtmlTitle);
+    let(&g_extHtmlTitleAbbr, "");
+    for (i = 1; i <= j; i++) {
+      if (g_extHtmlTitle[i - 1] >= 'A' && g_extHtmlTitle[i -1] <= 'Z') {
+        let(&g_extHtmlTitleAbbr, cat(g_extHtmlTitleAbbr,
+            chr(g_extHtmlTitle[i - 1]), NULL));
+      }
+    }
+    let(&g_extHtmlTitleAbbr, cat(g_extHtmlTitleAbbr, " Home", NULL));
+  }
+
+
+  free_vstring(token); /* Deallocate */
+  free_vstring(partialToken); /* Deallocate */
+  free_vstring(fileBuf);
+  g_texDefsRead = 1;  /* Set global flag that it's been read in */
+  return warningFound; /* Return indicator that parsing passed (0) or
+                           had warning(s) (1) */
+
+} /* readTexDefs */
+
+/* This function returns the length of the white space starting at ptr.
+   Comments are considered white space.  ptr should point to the first character
+   of the white space.  If ptr does not point to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned. */
+long texDefWhiteSpaceLen(char *ptr)
+{
+  long i = 0;
+  char tmpchr;
+  char *ptr1;
+  while (1) {
+    tmpchr = ptr[i];
+    if (!tmpchr) return i; /* End of string */
+    if (isalnum((unsigned char)(tmpchr))) return i; /* Alphanumeric string */
+
+    if (tmpchr == '/') { /* Embedded c-style comment - used to ignore
+        comments inside of Metamath comment for LaTeX/HTML definitions */
+      if (ptr[i + 1] == '*') {
+        while (1) {
+          ptr1 = strchr(ptr + i + 2, '*');
+          if (!ptr1) {
+            return i + (long)strlen(&ptr[i]); /* Unterminated comment - goto EOF */
+          }
+          if (ptr1[1] == '/') break;
+          i = ptr1 - ptr;
+        }
+        i = ptr1 - ptr + 2;
+        continue;
+      } else {
+        return i;
+      }
+    }
+    if (isgraph((unsigned char)tmpchr)) return i;
+    i++;
+  }
+  bug(2307);
+  return 0; /* Dummy return - never executed */
+} /* texDefWhiteSpaceLen */
+
+
+/* This function returns the length of the token (non-white-space) starting at
+   ptr.  Comments are considered white space.  ptr should point to the first
+   character of the token.  If ptr points to a white space character, 0
+   is returned.  If ptr points to a null character, 0 is returned.  If ptr
+   points to a quoted string, the quoted string is returned.  A non-alphanumeric\
+   characters ends a token and is a single token. */
+long texDefTokenLen(char *ptr)
+{
+  long i = 0;
+  char tmpchr;
+  char *ptr1;
+  tmpchr = ptr[i];
+  if (tmpchr == '\"') {
+    while (1) {
+      ptr1 = strchr(ptr + i + 1, '\"');
+      if (!ptr1) {
+        return i + (long)strlen(&ptr[i]); /* Unterminated quote - goto EOF */
+      }
+      if (ptr1[1] != '\"') return ptr1 - ptr + 1; /* Double quote is literal */
+      i = ptr1 - ptr + 1;
+    }
+  }
+  if (tmpchr == '\'') {
+    while (1) {
+      ptr1 = strchr(ptr + i + 1, '\'');
+      if (!ptr1) {
+        return i + (long)strlen(&ptr[i]); /* Unterminated quote - goto EOF */
+      }
+      if (ptr1[1] != '\'') return ptr1 - ptr + 1; /* Double quote is literal */
+      i = ptr1 - ptr + 1;
+    }
+  }
+  if (ispunct((unsigned char)tmpchr)) return 1; /* Single-char token */
+  while (1) {
+    tmpchr = ptr[i];
+    if (!isalnum((unsigned char)tmpchr)) return i; /* End of alphanumeric token */
+    i++;
+  }
+  bug(2308);
+  return 0; /* Dummy return - never executed */
+} /* texDefTokenLen */
+
+/* Token comparison for qsort */
+int texSortCmp(const void *key1, const void *key2)
+{
+  /* Returns -1 if key1 < key2, 0 if equal, 1 if key1 > key2 */
+  /* Note:  ptr->fld == (*ptr).fld
+            str.fld == (&str)->fld   */
+  return strcmp(((struct texDef_struct *)key1)->tokenName,
+      ((struct texDef_struct *)key2)->tokenName);
+} /* texSortCmp */
+
+
+/* Token comparison for bsearch */
+int texSrchCmp(const void *key, const void *data)
+{
+  /* Returns -1 if key < data, 0 if equal, 1 if key > data */
+  return strcmp(key,
+      ((struct texDef_struct *)data)->tokenName);
+} /* texSrchCmp */
+
+/* Convert ascii to a string of \tt tex; must not have control chars */
+/* (The caller must surround it by {\tt }) */
+/* ***Note:  The caller must deallocate returned string */
+vstring asciiToTt(vstring s)
+{
+
+  vstring_def(ttstr);
+  vstring_def(tmp);
+  long i, j, k;
+
+  let(&ttstr, s); /* In case the input s is temporarily allocated */
+  j = (long)strlen(ttstr);
+
+  /* Put special \tt font characters in a form that TeX can understand */
+  for (i = 0; i < j; i++) {
+    k = 1;
+    if (!g_htmlFlag) {
+      switch (ttstr[i]) {
+        /* For all unspecified cases, TeX will accept the character 'as is' */
+        case ' ':
+        case '$':
+        case '%':
+        case '#':
+        case '{':
+        case '}':
+        case '&':
+          let(&ttstr,cat(left(ttstr,i),"\\",right(ttstr,i+1),NULL));
+          k = 2;
+          break;
+        case '^':
+          let(&ttstr,cat(left(ttstr,i),"\\^{ }",right(ttstr,i+2),NULL));
+          k = 5;
+          break;
+        case '\\':
+        case '|':
+        case '<':
+        case '>':
+        case '"':
+        case '~':
+        case '_':
+          /* Note:  this conversion will work for any character, but
+             results in the most TeX source code. */
+          let(&ttstr,cat(left(ttstr,i),"\\char`\\",right(ttstr,i+1),NULL));
+          k = 8;
+          break;
+      } /* End switch mtoken[i] */
+    } else {
+      switch (ttstr[i]) {
+        /* For all unspecified cases, HTML will accept the character 'as is' */
+        /* Don't convert to &amp; but leave as is.  This
+           will allow the user to insert HTML entities for Unicode etc.
+           directly in the database source. */
+        /* case '&': ... */
+        case '<':
+          /* Leave in HTML tags (case must match) */
+          if (!strcmp(mid(ttstr, i + 1, 6), "<HTML>")) {
+            let(&ttstr, ttstr); /* Purge stack to prevent overflow by 'mid' */
+            i = i + 6;
+            break;
+          }
+          if (!strcmp(mid(ttstr, i + 1, 7), "</HTML>")) {
+            let(&ttstr, ttstr); /* Purge stack to prevent overflow by 'mid' */
+            i = i + 7;
+            break;
+          }
+          let(&ttstr,cat(left(ttstr,i),"&lt;",right(ttstr,i+2),NULL));
+          k = 4;
+          break;
+        case '>':
+          let(&ttstr,cat(left(ttstr,i),"&gt;",right(ttstr,i+2),NULL));
+          k = 4;
+          break;
+        case '"':
+          let(&ttstr,cat(left(ttstr,i),"&quot;",right(ttstr,i+2),NULL));
+          k = 6;
+          break;
+      } /* End switch mtoken[i] */
+    }
+
+    if (k > 1) { /* Adjust iteration and length */
+      i = i + k - 1;
+      j = j + k - 1;
+    }
+  } /* Next i */
+
+  free_vstring(tmp);  /* Deallocate */
+  return ttstr;
+} /* asciiToTt */
+
+
+/* Convert ascii token to TeX equivalent */
+/* The "$" math delimiter is not placed around the returned arg. here */
+/* *** Note: The caller must deallocate the returned string */
+vstring tokenToTex(vstring mtoken, long statemNum /*for error msgs*/)
+{
+  vstring_def(tex);
+  vstring tmpStr;
+  long i, j, k;
+  void *texDefsPtr; /* For binary search */
+  flag saveOutputToString;
+
+  if (!g_texDefsRead) {
+    bug(2320); /* This shouldn't be called if definitions weren't read */
+  }
+
+  texDefsPtr = (void *)bsearch(mtoken, g_TexDefs, (size_t)numSymbs,
+      sizeof(struct texDef_struct), texSrchCmp);
+  if (texDefsPtr) { /* Found it */
+    let(&tex, ((struct texDef_struct *)texDefsPtr)->texEquiv);
+  } else {
+    /* If it wasn't found, give user a warning... */
+    saveOutputToString = g_outputToString;
+    g_outputToString = 0;
+    /* It is possible for statemNum to be 0 when
+       tokenToTex() is called (via getTexLongMath()) from
+       printTexLongMath(), when its hypStmt argument is 0 (= not associated
+       with a statement).  (Reported by Wolf Lammen.) */
+    if (statemNum < 0 || statemNum > g_statements) bug(2331);
+    if (statemNum > 0) {   /* Include statement label in error message */
+      printLongLine(cat("?Warning: In the comment for statement \"",
+          g_Statement[statemNum].labelName,
+          "\", math symbol token \"", mtoken,
+          "\" does not have a LaTeX and/or an HTML definition.", NULL),
+          "", " ");
+    } else { /* There is no statement associated with the error message */
+      printLongLine(cat("?Warning: Math symbol token \"", mtoken,
+          "\" does not have a LaTeX and/or an HTML definition.", NULL),
+          "", " ");
+    }
+    g_outputToString = saveOutputToString;
+    /* ... but we'll still leave in the old default conversion anyway: */
+
+    /* If it wasn't found, use built-in conversion rules */
+    let(&tex, mtoken);
+
+    /* First, see if it's a tilde followed by a letter */
+    /* If so, remove the tilde.  (This is actually obsolete.) */
+    /* (The tilde was an escape in the obsolete syntax.) */
+    if (tex[0] == '~') {
+      if (isalpha((unsigned char)(tex[1]))) {
+        let(&tex, right(tex, 2)); /* Remove tilde */
+      }
+    }
+
+    /* Next, convert punctuation characters to tt font */
+    j = (long)strlen(tex);
+    for (i = 0; i < j; i++) {
+      if (ispunct((unsigned char)(tex[i]))) {
+        tmpStr = asciiToTt(chr(tex[i]));
+        if (!g_htmlFlag)
+          let(&tmpStr, cat("{\\tt ", tmpStr, "}", NULL));
+        k = (long)strlen(tmpStr);
+        let(&tex,
+            cat(left(tex, i), tmpStr, right(tex, i + 2), NULL));
+        i = i + k - 1; /* Adjust iteration */
+        j = j + k - 1; /* Adjust length */
+        free_vstring(tmpStr); /* Deallocate */
+      }
+    } /* Next i */
+
+    /* Make all letters Roman; put inside mbox */
+    if (!g_htmlFlag)
+      let(&tex, cat("\\mbox{\\rm ", tex, "}", NULL));
+
+  } /* End if */
+
+  return tex;
+} /* tokenToTex */
+
+
+/* Converts a comment section in math mode to TeX.  Each math token
+   MUST be separated by white space.   TeX "$" does not surround the output. */
+vstring asciiMathToTex(vstring mathComment, long statemNum)
+{
+
+  vstring tex;
+  vstring_def(texLine);
+  vstring_def(lastTex);
+  vstring_def(token);
+  flag alphnew, alphold, unknownnew, unknownold;
+  long i;
+  vstring srcptr;
+
+  srcptr = mathComment;
+
+  free_vstring(texLine);
+  free_vstring(lastTex);
+  while(1) {
+    i = whiteSpaceLen(srcptr);
+    srcptr = srcptr + i;
+    i = tokenLen(srcptr);
+    if (!i) break; /* Done */
+    let(&token, space(i));
+    memcpy(token, srcptr, (size_t)i);
+    srcptr = srcptr + i;
+    tex = tokenToTex(token, statemNum); /* Convert token to TeX */
+              /* tokenToTex allocates tex; we must deallocate it */
+
+    if (!g_htmlFlag) {
+      /* If this token and previous token begin with letter, add a thin
+           space between them */
+      /* Also, anything not in table will have space added */
+      /* Use "!!" here and below because isalpha returns an integer, whose
+        unspecified non-zero value could be truncated to 0 when
+        converted to char.  Thanks to Wolf Lammen for pointing this out. */
+      alphnew = !!isalpha((unsigned char)(tex[0]));
+      unknownnew = 0;
+      if (!strcmp(left(tex, 10), "\\mbox{\\rm ")) { /* Token not in table */
+        unknownnew = 1;
+      }
+      alphold = !!isalpha((unsigned char)(lastTex[0]));
+      unknownold = 0;
+      if (!strcmp(left(lastTex, 10), "\\mbox{\\rm ")) { /* Token not in table*/
+        unknownold = 1;
+      }
+      /* Put thin space only between letters and/or unknowns */
+      if ((alphold || unknownold) && (alphnew || unknownnew)) {
+        /* Put additional thin space between two letters */
+        let(&texLine, cat(texLine, "\\,", tex, " ", NULL));
+      } else {
+        let(&texLine, cat(texLine, tex, " ", NULL));
+      }
+    } else {
+      let(&texLine, cat(texLine, tex, NULL));
+    }
+    free_vstring(lastTex); /* Deallocate */
+    lastTex = tex; /* Pass deallocation responsibility for tex to lastTex */
+  } /* End while (1) */
+
+  free_vstring(lastTex); /* Deallocate */
+  free_vstring(token); /* Deallocate */
+
+  return texLine;
+} /* asciiMathToTex */
+
+
+/* Gets the next section of a comment that is in the current mode (text,
+   label, or math).  If 1st char. is not "$" (DOLLAR_SUBST), text mode is
+   assumed.  mode = 0 means end of comment reached.  srcptr is left at 1st
+   char. of start of next comment section. */
+vstring getCommentModeSection(vstring *srcptr, char *mode)
+{
+  vstring_def(modeSection);
+  vstring ptr; /* Not allocated */
+  flag addMode = 0;
+  if (!g_outputToString) bug(2319);
+
+  if ((*srcptr)[0] != DOLLAR_SUBST /*'$'*/) {
+    if ((*srcptr)[0] == 0) { /* End of string */
+      *mode = 0; /* End of comment */
+      return "";
+    } else {
+      *mode = 'n'; /* Normal text */
+      addMode = 1;
+    }
+  } else {
+    switch ((*srcptr)[1]) {
+      case 'l':
+      case 'm':
+      case 'n':
+        *mode = (*srcptr)[1];
+        break;
+      case ')':  /* Obsolete */
+        bug(2317);
+        /* Leave old code in case user continues through the bug */
+        *mode = 0; /* End of comment */
+        return "";
+        break;
+      default:
+        *mode = 'n';
+        break;
+    }
+  }
+
+  ptr = (*srcptr) + 1;
+  while (1) {
+    if (ptr[0] == DOLLAR_SUBST /*'$'*/) {
+      switch (ptr[1]) {
+        case 'l':
+        case 'm':
+        case 'n':
+        case ')':  /* Obsolete (will never happen) */
+          if (ptr[1] == ')') bug(2318);
+          let(&modeSection, space(ptr - (*srcptr)));
+          memcpy(modeSection, *srcptr, (size_t)(ptr - (*srcptr)));
+          if (addMode) {
+            let(&modeSection, cat(chr(DOLLAR_SUBST), "n", /*"$n"*/ modeSection,
+                NULL));
+          }
+          *srcptr = ptr;
+          return modeSection;
+          break;
+      }
+    } else {
+      if (ptr[0] == 0) {
+          let(&modeSection, space(ptr - (*srcptr)));
+          memcpy(modeSection, *srcptr, (size_t)(ptr - (*srcptr)));
+          if (addMode) {
+            let(&modeSection, cat(chr(DOLLAR_SUBST), "n", /*"$n"*/ modeSection,
+                NULL));
+          }
+          *srcptr = ptr;
+          return modeSection;
+      }
+    }
+    ptr++;
+  } /* End while */
+  return NULL; /* Dummy return - never executes */
+} /* getCommentModeSection */
+
+
+/* The texHeaderFlag means this:
+    If !g_htmlFlag (i.e. TeX mode), then 1 means print header
+    If g_htmlFlag, then 1 means include "Previous Next" links on page,
+    based on the global g_showStatement variable
+*/
+void printTexHeader(flag texHeaderFlag)
+{
+
+  long i, j, k;
+  vstring_def(tmpStr);
+
+  /* "Mathbox for <username>" mod */
+  vstring_def(localSandboxTitle);
+  vstring_def(hugeHdr);
+  vstring_def(bigHdr);
+  vstring_def(smallHdr);
+  vstring_def(tinyHdr);
+  vstring_def(hugeHdrComment);
+  vstring_def(bigHdrComment);
+  vstring_def(smallHdrComment);
+  vstring_def(tinyHdrComment);
+
+  if (2/*error*/ == readTexDefs(0/*errorsOnly=0*/, 1 /*gifCheck=1*/)) {
+    print2(
+       "?There was an error in the $t comment's LaTeX/HTML definitions.\n");
+    return;
+  }
+  /*}*/
+
+  g_outputToString = 1;  /* Redirect print2 and printLongLine to g_printString */
+  if (!g_htmlFlag) {
+    print2("%s This LaTeX file was created by Metamath on %s %s.\n",
+       "%", date(), time_());
+
+    if (texHeaderFlag && !g_oldTexFlag) {
+      print2("\\documentclass{article}\n");
+      print2("\\usepackage{graphicx} %% For rotated iota\n");
+      print2("\\usepackage{amssymb}\n");
+      print2("\\usepackage{amsmath} %% For \\begin{align}...\n");
+      print2("\\usepackage{amsthm}\n");
+      print2("\\theoremstyle{plain}\n");
+      print2("\\newtheorem{theorem}{Theorem}[section]\n");
+      print2("\\newtheorem{definition}[theorem]{Definition}\n");
+      print2("\\newtheorem{lemma}[theorem]{Lemma}\n");
+      print2("\\newtheorem{axiom}{Axiom}\n");
+      print2("\\allowdisplaybreaks[1] %% Allow page breaks in {align}\n");
+      print2("\\usepackage[plainpages=false,pdfpagelabels]{hyperref}\n");
+      print2("\\hypersetup{colorlinks} %% Get rid of boxes around links\n");
+      print2("\\begin{document}\n");
+      print2("\n");
+    }
+
+    if (texHeaderFlag && g_oldTexFlag) {
+      /* LaTeX 2e */
+      print2("\\documentclass[leqno]{article}\n");
+      /* LaTeX 2e */
+      print2("\\usepackage{graphicx}\n"); /* For rotated iota */
+      print2("\\usepackage{amssymb}\n");
+      print2("\\raggedbottom\n");
+      print2("\\raggedright\n");
+      print2("%%\\title{Your title here}\n");
+      print2("%%\\author{Your name here}\n");
+      print2("\\begin{document}\n");
+      print2("%%\\maketitle\n");
+      print2("\\newbox\\mlinebox\n");
+      print2("\\newbox\\mtrialbox\n");
+      print2("\\newbox\\startprefix  %% Prefix for first line of a formula\n");
+      print2("\\newbox\\contprefix  %% Prefix for continuation line of a formula\n");
+      print2("\\def\\startm{  %% Initialize formula line\n");
+      print2("  \\setbox\\mlinebox=\\hbox{\\unhcopy\\startprefix}\n");
+      print2("}\n");
+      print2("\\def\\m#1{  %% Add a symbol to the formula\n");
+      print2("  \\setbox\\mtrialbox=\\hbox{\\unhcopy\\mlinebox $\\,#1$}\n");
+      print2("  \\ifdim\\wd\\mtrialbox>\\hsize\n");
+      print2("    \\box\\mlinebox\n");
+      print2("    \\setbox\\mlinebox=\\hbox{\\unhcopy\\contprefix $\\,#1$}\n");
+      print2("  \\else\n");
+      print2("    \\setbox\\mlinebox=\\hbox{\\unhbox\\mtrialbox}\n");
+      print2("  \\fi\n");
+      print2("}\n");
+      print2("\\def\\endm{  %% Output the last line of a formula\n");
+      print2("  \\box\\mlinebox\n");
+      print2("}\n");
+    }
+  } else { /* g_htmlFlag */
+
+    print2("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n");
+    print2(     "    \"http://www.w3.org/TR/html4/loose.dtd\">\n");
+    print2("<HTML LANG=\"EN-US\">\n");
+    print2("<HEAD>\n");
+    print2("%s%s\n", "<META HTTP-EQUIV=\"Content-Type\" ",
+        "CONTENT=\"text/html; charset=iso-8859-1\">");
+    /* Improve mobile device display per David A. Wheeler */
+    print2("<META NAME=\"viewport\" CONTENT=\"width=device-width, initial-scale=1.0\">\n");
+
+    print2("<STYLE TYPE=\"text/css\">\n");
+    print2("<!--\n");
+    /* Optional information but takes unnecessary file space */
+    /* (change @ to * if uncommenting)
+    print2(
+        "/@ Math symbol images will be shifted down 4 pixels to align with\n");
+    print2(
+        "   normal text for compatibility with various browsers.  The old\n");
+    print2(
+        "   ALIGN=TOP for math symbol images did not align in all browsers\n");
+    print2(
+        "   and should be deleted.  All other images must override this\n");
+    print2(
+        "   shift with STYLE=\"margin-bottom:0px\". @/\n");
+    */
+    print2("img { margin-bottom: -4px }\n");
+    /* Print style sheet for rainbow-colored number that goes after
+       statement label */
+    print2(".r { font-family: \"Arial Narrow\";\n");
+    print2("     font-size: x-small;\n");
+    /* There is no color */
+    print2("   }\n");
+
+    /* Indent web proof displays */
+    /* Print style sheet for HTML proof indentation number */
+    /* ??? Future - combine with above style sheet */
+    print2(".i { font-family: \"Arial Narrow\";\n");
+    print2("     font-size: x-small;\n");
+    print2("     color: gray;\n");
+    print2("   }\n");
+    print2("-->\n");
+    print2("</STYLE>\n");
+    printLongLine(g_htmlCSS, "", " ");
+
+    /* Put theorem name before "Metamath Proof Explorer" etc. */
+    if (g_showStatement < g_extHtmlStmt) {
+      print2("%s\n", cat("<TITLE>",
+          /* Strip off ".html" */
+          left(g_texFileName, (long)strlen(g_texFileName) - 5),
+          " - ", htmlTitle,
+          "</TITLE>", NULL));
+    } else if (g_showStatement < g_mathboxStmt) { /* Sandbox stuff */
+      print2("%s\n", cat("<TITLE>",
+          /* Strip off ".html" */
+          left(g_texFileName, (long)strlen(g_texFileName) - 5),
+          " - ", g_extHtmlTitle,
+          "</TITLE>", NULL));
+
+    } else {
+      /* "Mathbox for <username>" */
+      /* Scan from this statement backwards until a big header is found */
+      for (i = g_showStatement; i > g_mathboxStmt; i--) {
+        if (g_Statement[i].type == a_ || g_Statement[i].type == p_) {
+          /* Note: only bigHdr is used; the other 5 returned strings are
+             ignored */
+          getSectionHeadings(i, &hugeHdr, &bigHdr, &smallHdr,
+              &tinyHdr,
+              &hugeHdrComment, &bigHdrComment, &smallHdrComment,
+              &tinyHdrComment,
+              0, /* fineResolution */
+              0  /* fullComment */);
+          if (bigHdr[0] != 0) break;
+        }
+      } /* next i */
+      if (bigHdr[0]) {
+        /* A big header was found; use it for the page title */
+        let(&localSandboxTitle, bigHdr);
+      } else {
+        /* A big header was not found (should not happen if set.mm is
+           formatted right, but use default just in case) */
+        let(&localSandboxTitle, sandboxTitle);
+      }
+      free_vstring(hugeHdr);   /* Deallocate memory */
+      free_vstring(bigHdr);   /* Deallocate memory */
+      free_vstring(smallHdr); /* Deallocate memory */
+      free_vstring(tinyHdr); /* Deallocate memory */
+      free_vstring(hugeHdrComment);   /* Deallocate memory */
+      free_vstring(bigHdrComment);   /* Deallocate memory */
+      free_vstring(smallHdrComment); /* Deallocate memory */
+      free_vstring(tinyHdrComment); /* Deallocate memory */
+
+      printLongLine(cat("<TITLE>",
+          /* Strip off ".html" */
+          left(g_texFileName, (long)strlen(g_texFileName) - 5),
+          " - ", localSandboxTitle,
+          "</TITLE>", NULL), "", "\"");
+
+    }
+    /* Icon for bookmark */
+    print2("%s%s\n", "<LINK REL=\"shortcut icon\" HREF=\"favicon.ico\" ",
+        "TYPE=\"image/x-icon\">");
+
+    print2("</HEAD>\n");
+    print2("<BODY BGCOLOR=\"#FFFFFF\">\n");
+
+    print2("<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH=\"100%s\">\n",
+         "%");
+    print2("  <TR>\n");
+    print2("    <TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%s\"><A HREF=\n", "%");
+    print2("    \"%s\"><IMG SRC=\"%s\"\n",
+        (g_showStatement < g_extHtmlStmt ? g_htmlHomeHREF :
+             (g_showStatement < g_mathboxStmt ? extHtmlHomeHREF :
+             sandboxHomeHREF)),
+        /* Note that we assume that the upper-left image is 32x32 */
+        (g_showStatement < g_extHtmlStmt ? g_htmlHomeIMG :
+             (g_showStatement < g_mathboxStmt ? extHtmlHomeIMG :
+             sandboxHomeIMG)));
+    print2("      BORDER=0\n");
+    print2("      ALT=\"%s\"\n",
+        (g_showStatement < g_extHtmlStmt ? htmlTitleAbbr :
+             (g_showStatement < g_mathboxStmt ? g_extHtmlTitleAbbr :
+             sandboxTitleAbbr)));
+    print2("      TITLE=\"%s\"\n",
+        (g_showStatement < g_extHtmlStmt ? htmlTitleAbbr :
+             (g_showStatement < g_mathboxStmt ? g_extHtmlTitleAbbr :
+             sandboxTitleAbbr)));
+    print2(
+      "      HEIGHT=32 WIDTH=32 ALIGN=TOP STYLE=\"margin-bottom:0px\"></A>\n");
+    print2("    </TD>\n");
+    print2(
+"    <TD ALIGN=CENTER COLSPAN=2 VALIGN=TOP><FONT SIZE=\"+3\" COLOR=%s><B>\n",
+      GREEN_TITLE_COLOR);
+    /* Allow plenty of room for long titles (although over 79 chars. will
+       trigger bug 1505). */
+    print2("%s\n",
+        (g_showStatement < g_extHtmlStmt ? htmlTitle :
+             (g_showStatement < g_mathboxStmt ? g_extHtmlTitle :
+             localSandboxTitle)));
+    print2("      </B></FONT></TD>\n");
+
+    if (texHeaderFlag) {  /* For HTML, 1 means to put prev/next links */
+      /* Put Previous/Next links into web page */
+      print2("    <TD ALIGN=RIGHT VALIGN=TOP WIDTH=\"25%s\">\n", "%");
+      print2("      <FONT SIZE=-1 FACE=sans-serif>\n");
+      /* Find the previous statement with a web page */
+      j = 0;
+      k = 0;
+      for (i = g_showStatement - 1; i >= 1; i--) {
+        if (g_Statement[i].type == (char)p_ ||
+            g_Statement[i].type == (char)a_) {
+          j = i;
+          break;
+        }
+      }
+      if (j == 0) {
+        k = 1; /* First statement flag */
+        /* For the first statement, wrap to last one */
+        for (i = g_statements; i >= 1; i--) {
+          if (g_Statement[i].type == (char)p_ ||
+              g_Statement[i].type == (char)a_ ) {
+            j = i;
+            break;
+          }
+        }
+      }
+      if (j == 0) bug(2314);
+      print2("      <A HREF=\"%s.html\">\n",
+          g_Statement[j].labelName);
+      if (!k) {
+        print2("      &lt; Previous</A>&nbsp;&nbsp;\n");
+      } else {
+        print2("      &lt; Wrap</A>&nbsp;&nbsp;\n");
+      }
+      /* Find the next statement with a web page */
+      j = 0;
+      k = 0;
+      for (i = g_showStatement + 1; i <= g_statements; i++) {
+        if (g_Statement[i].type == (char)p_ ||
+            g_Statement[i].type == (char)a_) {
+          j = i;
+          break;
+        }
+      }
+      if (j == 0) {
+        k = 1; /* Last statement flag */
+        /* For the last statement, wrap to first one */
+        for (i = 1; i <= g_statements; i++) {
+          if (g_Statement[i].type == (char)p_ ||
+              g_Statement[i].type == (char)a_) {
+            j = i;
+            break;
+          }
+        }
+      }
+      if (j == 0) bug(2315);
+      if (!k) {
+        print2("      <A HREF=\"%s.html\">Next &gt;</A>\n",
+            g_Statement[j].labelName);
+      } else {
+        print2("      <A HREF=\"%s.html\">Wrap &gt;</A>\n",
+            g_Statement[j].labelName);
+      }
+
+      print2("      </FONT><FONT FACE=sans-serif SIZE=-2>\n");
+
+      /* 8-Sep-03 nm - ??? Is the closing </FONT> printed if there is no
+         altHtml?  This should be tested. */
+
+      /* Compute the theorem list page number.  ??? Temporarily
+         we assume it to be 100 (hardcoded).  Todo: This should be fixed to use
+         the same as the THEOREMS_PER_PAGE in WRITE THEOREMS (have a SET
+         global variable in place of THEOREMS_PER_PAGE?) */
+      i = ((g_Statement[g_showStatement].pinkNumber - 1) / 100) + 1; /* Page # */
+      /* All thm pages now have page num after mmtheorems
+         since mmtheorems.html is now just the table of contents */
+      let(&tmpStr, cat("mmtheorems", str((double)i), ".html#",
+          g_Statement[g_showStatement].labelName, NULL)); /* Link to page/stmt */
+      /* Break up lines w/ long labels to prevent bug 1505 */
+      printLongLine(cat("      <BR><A HREF=\"", tmpStr,
+            "\">Nearby theorems</A>", NULL), " ", " ");
+
+      print2("      </FONT>\n");
+      print2("    </TD>\n");
+      print2("  </TR>\n");
+      print2("  <TR>\n");
+      print2("    <TD COLSPAN=2 ALIGN=LEFT VALIGN=TOP><FONT SIZE=-2\n");
+      print2("      FACE=sans-serif>\n");
+      print2("      <A HREF=\"../mm.html\">Mirrors</A>&nbsp; &gt;\n");
+      print2("      &nbsp;<A HREF=\"../index.html\">Home</A>&nbsp; &gt;\n");
+      print2("      &nbsp;<A HREF=\"%s\">%s</A>&nbsp; &gt;\n",
+          (g_showStatement < g_extHtmlStmt ? g_htmlHomeHREF :
+               (g_showStatement < g_mathboxStmt ? extHtmlHomeHREF :
+               g_htmlHomeHREF)),
+          (g_showStatement < g_extHtmlStmt ? htmlTitleAbbr :
+               (g_showStatement < g_mathboxStmt ? g_extHtmlTitleAbbr :
+               htmlTitleAbbr)));
+      print2("      &nbsp;<A HREF=\"mmtheorems.html\">Th. List</A>&nbsp; &gt;\n");
+      if (g_showStatement >= g_mathboxStmt) {
+        print2("      &nbsp;<A HREF=\"mmtheorems.html#sandbox:bighdr\">\n");
+        print2("      Mathboxes</A>&nbsp; &gt;\n");
+      }
+      print2("      &nbsp;%s\n",
+          /* Strip off ".html" */
+          left(g_texFileName, (long)strlen(g_texFileName) - 5));
+      print2("      </FONT>\n");
+      print2("    </TD>\n");
+      print2("    <TD COLSPAN=2 ALIGN=RIGHT VALIGN=TOP>\n");
+      print2("      <FONT SIZE=-2 FACE=sans-serif>\n");
+
+      /* Add link(s) specified by htmlexturl in $t statement */
+      /* The position of the theorem name is indicated with "*" in the
+         htmlexturl $t variable.  If a literal "*" is part of the URL,
+         use the alternate URL encoding "%2A" */
+      /* Example: (take out space in "/ *" below that was put there to prevent
+                   compiler warnings)
+          htmlexturl '<A HREF="http://metamath.tirix.org/ *.html">' +
+              'Structured version</A>&nbsp;&nbsp;' +
+              '<A HREF="https://expln.github.io/metamath/asrt/ *.html">' +
+              'ASCII version</A>&nbsp;&nbsp;';
+      */
+      let(&tmpStr, htmlExtUrl);
+      i = 1;
+      while (1) {
+        i = instr(i, tmpStr, "*");
+        if (i == 0) break;
+        let(&tmpStr, cat(left(tmpStr, i - 1),
+            g_Statement[g_showStatement].labelName,
+            right(tmpStr, i + 1), NULL));
+      }
+      printLongLine(tmpStr, "", " ");
+
+      /* Print the GIF/Unicode Font choice, if directories are specified */
+      if (htmlDir[0]) {
+
+        if (g_altHtmlFlag) {
+          print2("      <A HREF=\"%s%s\">GIF version</A>\n",
+                htmlDir, g_texFileName);
+
+        } else {
+          print2("      <A HREF=\"%s%s\">Unicode version</A>\n",
+                altHtmlDir, g_texFileName);
+
+        }
+      }
+
+    } else { /* texHeaderFlag=0 for HTML means not to put prev/next links */
+      /* there is no table open (mmascii, mmdefinitions), so don't
+         add </TD> which caused HTML validation failure */
+      print2("      <TD ALIGN=RIGHT VALIGN=TOP\n");
+      print2("       ><FONT FACE=sans-serif SIZE=-2>\n", "%");
+
+      /* Print the GIF/Unicode Font choice, if directories are specified */
+      if (htmlDir[0]) {
+        print2("\n");
+        if (g_altHtmlFlag) {
+          print2("This is the Unicode version.<BR>\n");
+          print2("<A HREF=\"%s%s\">Change to GIF version</A>\n",
+              htmlDir, g_texFileName);
+        } else {
+          print2("This is the GIF version.<BR>\n");
+          print2("<A HREF=\"%s%s\">Change to Unicode version</A>\n",
+              altHtmlDir, g_texFileName);
+        }
+      }
+      else {
+        print2("&nbsp;\n");
+      }
+
+    }
+
+    print2("      </FONT>\n");
+    print2("    </TD>\n");
+    print2("  </TR>\n");
+    print2("</TABLE>\n");
+
+
+
+    print2("<HR NOSHADE SIZE=1>\n");
+
+  } /* g_htmlFlag */
+  fprintf(g_texFilePtr, "%s", g_printString);
+  g_outputToString = 0;
+  free_vstring(g_printString);
+
+  /* Deallocate strings */
+  free_vstring(tmpStr);
+
+} /* printTexHeader */
+
+/* Prints an embedded comment in TeX or HTML.  The commentPtr must point to the first
+   character after the "$(" in the comment.  The printout ends when the first
+   "$)" or null character is encountered.   commentPtr must not be a temporary
+   allocation.   htmlCenterFlag, if 1, means to center the HTML and add a
+   "Description:" prefix. */
+/* The output is printed to the global g_texFilePtr. */
+/* Note: the global long "g_showStatement" is referenced to determine whether
+   to read bibliography from mmset.html or mmhilbert.html (or other
+   g_htmlBibliography or extHtmlBibliography file pair). */
+/* Returns 1 if an error or warning message was printed */
+flag printTexComment(vstring commentPtr, flag htmlCenterFlag,
+    long actionBits, /* see below */
+        /* Indicators for actionBits:
+            #define ERRORS_ONLY 1 - just report errors, don't print output
+            #define PROCESS_SYMBOLS 2
+            #define PROCESS_LABELS 4
+            #define ADD_COLORED_LABEL_NUMBER 8
+            #define PROCESS_BIBREFS 16
+            #define PROCESS_UNDERSCORES 32
+            #define CONVERT_TO_HTML 64 - convert '<' to '&gt;' unless
+                     <HTML>, </HTML> present
+            #define METAMATH_COMMENT 128 - $) terminates string
+            #define PROCESS_EVERYTHING PROCESS_SYMBOLS + PROCESS_LABELS \
+             + ADD_COLORED_LABEL_NUMBER + PROCESS_BIBREFS \
+             + PROCESS_UNDERSCORES + CONVERT_HTML + METAMATH_COMMENT \
+        */
+        /* 10-Dec-2018 nm - expanded meaning of errorsOnly for MARKUP command:
+             2 = process as if in <HTML>...</HTML> preformatted mode but
+                 don't strip <HTML>...</HTML> tags
+             3 = same as 2, but convert ONLY math symbols
+           (These new values were added instead of adding a new argument,
+           so as not to have to modify ~60 other calls to this function) */
+
+    flag fileCheck)  /* 1 = check external files (gifs and bib) */
+{
+  vstring cmtptr; /* Not allocated */
+  vstring srcptr; /* Not allocated */
+  vstring lineStart; /* Not allocated */
+  vstring_def(tmpStr);
+  vstring modeSection; /* Not allocated */
+  vstring_def(sourceLine);
+  vstring_def(outputLine);
+  vstring_def(tmp);
+  flag textMode, mode, lastLineFlag, displayMode;
+  vstring_def(tmpComment);
+  flag preformattedMode = 0; /* HTML <HTML> preformatted mode */
+
+  /* For bibliography hyperlinks */
+  vstring_def(bibTag);
+  vstring_def(bibFileName);
+  vstring_def(bibFileContents);
+  vstring_def(bibFileContentsUpper); /* Uppercase version */
+  vstring_def(bibTags);
+  long pos1, pos2, htmlpos1, htmlpos2, saveScreenWidth;
+  flag tmpMathMode;
+
+  /* Variables for converting ` ` and ~ to old $m,$n and $l,$n formats in
+     order to re-use the old code */
+  /* Note that DOLLAR_SUBST will replace the old $. */
+  vstring_def(cmt);
+  vstring_def(cmtMasked); /* cmt with math syms blanked */ /* also mask ~ label */
+  vstring_def(tmpMasked); /* tmp with math syms blanked */
+  vstring_def(tmpStrMasked); /* tmpStr w/ math syms blanked */
+  long i, clen;
+  flag returnVal = 0; /* 1 means error/warning */
+
+  /* Internal flags derived from actionBits argument, for MARKUP command use */
+  flag errorsOnly;
+  flag processSymbols;
+  flag processLabels;
+  flag addColoredLabelNumber;
+  flag processBibrefs;
+  flag processUnderscores;
+  flag convertToHtml;
+  flag metamathComment;
+
+  /* Assign local Booleans for actionBits mask */
+  errorsOnly = (actionBits & ERRORS_ONLY ) != 0;
+  processSymbols = (actionBits & PROCESS_SYMBOLS ) != 0;
+  processLabels = (actionBits & PROCESS_LABELS ) != 0;
+  addColoredLabelNumber = (actionBits & ADD_COLORED_LABEL_NUMBER ) != 0;
+  processBibrefs = (actionBits & PROCESS_BIBREFS ) != 0;
+  processUnderscores = (actionBits & PROCESS_UNDERSCORES ) != 0;
+  convertToHtml = (actionBits & CONVERT_TO_HTML ) != 0;
+  metamathComment = (actionBits & METAMATH_COMMENT ) != 0;
+
+  /* We must let this procedure handle switching output to string mode */
+  if (g_outputToString) bug(2309);
+  /* The LaTeX (or HTML) file must be open */
+  if (errorsOnly == 0) {
+    if (!g_texFilePtr) bug(2321);
+  }
+
+  cmtptr = commentPtr;
+
+  if (!g_texDefsRead) {
+    return returnVal; /* TeX defs were not read (error was detected
+                               and flagged to the user elsewhere) */
+  }
+
+  /* Convert line to the old $m..$n and $l..$n formats (using DOLLAR_SUBST
+     instead of "$") - the old syntax is obsolete but we do this conversion
+     to re-use some old code */
+  if (metamathComment != 0) {
+    i = instr(1, cmtptr, "$)");      /* If it points to source buffer */
+    if (!i) i = (long)strlen(cmtptr) + 1;  /* If it's a stand-alone string */
+  } else {
+    i = (long)strlen(cmtptr) + 1;
+  }
+  let(&cmt, left(cmtptr, i - 1));
+
+  /* All actions on cmt should be mirrored on cmdMasked, except that
+     math symbols are replaced with blanks in cmdMasked */
+  let(&cmtMasked, cmt);
+
+
+  /* This section is independent and can be removed without side effects */
+  if (g_htmlFlag) {
+    /* Convert special characters <, &, etc. to HTML entities */
+    /* But skip converting math symbols inside ` ` */
+    /* Detect preformatted HTML (this is crude, since it
+       will apply to whole comment - perhaps fine-tune this later) */
+    if (convertToHtml != 0) {
+      if (instr(1, cmt, "<HTML>") != 0) preformattedMode = 1;
+    } else {
+      preformattedMode = 1; /* For MARKUP command - don't convert HTML */
+    }
+    mode = 1; /* 1 normal, -1 math token */
+    let(&tmp, "");
+    let(&tmpMasked, "");
+    while (1) {
+      pos1 = 0;
+      while (1) {
+        pos1 = instr(pos1 + 1, cmt, "`");
+        if (!pos1) break;
+        if (cmt[pos1] == '`') {
+          pos1++;  /* Skip `` escape */
+          continue;
+        }
+        break;
+      }
+      if (!pos1) pos1 = (long)strlen(cmt) + 1;
+      if (mode == 1 && preformattedMode == 0) {
+        free_vstring(tmpStr);
+        /* asciiToTt() is where "<" is converted to "&lt;" etc. */
+        tmpStr = asciiToTt(left(cmt, pos1));
+        let(&tmpStrMasked, tmpStr);
+      } else {
+        let(&tmpStr, left(cmt, pos1));
+        if (mode == -1) { /* Math mode */
+          /* Replace math symbols with spaces to prevent confusing them
+             with markup in sections below */
+          let(&tmpStrMasked, cat(space(pos1 - 1),
+              mid(cmtMasked, pos1, 1), NULL));
+        } else { /* Preformatted mode but not math mode */
+          let(&tmpStrMasked, left(cmtMasked, pos1));
+        }
+      }
+      let(&tmp, cat(tmp, tmpStr, NULL));
+      let(&tmpMasked, cat(tmpMasked, tmpStrMasked, NULL));
+      let(&cmt, right(cmt, pos1 + 1));
+      let(&cmtMasked, right(cmtMasked, pos1 + 1));
+      if (!cmt[0]) break;
+      mode = (char)(-mode);
+    }
+    let(&cmt, tmp);
+    let(&cmtMasked, tmpMasked);
+    free_vstring(tmpStr); /* Deallocate */
+    free_vstring(tmpStrMasked);
+  }
+
+
+  /* Add leading and trailing HTML markup to comment here
+     (instead of in caller).  Also convert special characters. */
+  if (g_htmlFlag) {
+    /* This used to be done in mmcmds.c */
+    if (htmlCenterFlag) {  /* Note:  this should be 0 in MARKUP command */
+      let(&cmt, cat("<CENTER><TABLE><TR><TD ALIGN=LEFT><B>Description: </B>",
+          cmt, "</TD></TR></TABLE></CENTER>", NULL));
+      let(&cmtMasked,
+          cat("<CENTER><TABLE><TR><TD ALIGN=LEFT><B>Description: </B>",
+          cmtMasked, "</TD></TR></TABLE></CENTER>", NULL));
+    }
+  }
+
+
+  /* Mask out _ (underscore) in labels so they won't become subscripts
+     (reported by Benoit Jubin) */
+  /* This section is independent and can be removed without side effects */
+  if (g_htmlFlag != 0) {
+    pos1 = 0;
+    while (1) {   /* Look for label start */
+      pos1 = instr(pos1 + 1, cmtMasked, "~");
+      if (!pos1) break;
+      if (cmtMasked[pos1] == '~') {
+        pos1++;  /* Skip ~~ escape */
+        continue;
+      }
+      /* Skip whitespace after ~ */
+      while (1) {
+        if (cmtMasked[pos1] == 0) break;  /* End of line */
+        if (isspace((unsigned char)(cmtMasked[pos1]))) {
+          pos1++;
+          continue;
+        } else { /* Found start of label */
+          break;
+        }
+      }
+      /* Skip non-whitespace after ~ find end of label */
+      while (1) {
+        if (cmtMasked[pos1] == 0) break;  /* End of line */
+        if (!(isspace((unsigned char)(cmtMasked[pos1])))) {
+          if (cmtMasked[pos1] == '_') {
+            /* Put an "?" in place of label character in mask */
+            cmtMasked[pos1] = '?';
+          }
+          pos1++;
+          continue;
+        } else { /* Found end of label */
+          break;
+        }
+      }  /* while (1) */
+    } /* while (1) */
+  } /* if g_htmlFlag */
+
+
+  /* Handle dollar signs in comments converted to LaTeX */
+  /* This section is independent and can be removed without side effects */
+  /* This must be done before the underscores below so subscript $'s */
+  /* won't be converted to \$'s */
+  if (!g_htmlFlag) {  /* LaTeX */
+    pos1 = 0;
+    while (1) {
+      pos1 = instr(pos1 + 1, cmt, "$");
+      if (!pos1) break;
+      /* Don't modify anything inside of <HTML>...</HTML> tags */
+      if (pos1 > instr(1, cmt, "<HTML>") && pos1 < instr(1, cmt, "</HTML>"))
+        continue;
+      let(&cmt, cat(left(cmt, pos1 - 1), "\\$",
+          right(cmt, pos1 + 1), NULL));
+      let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "\\$",
+          right(cmtMasked, pos1 + 1), NULL));
+      pos1 = pos1 + 1; /* Adjust for 2-1 extra chars in "let" above */
+    } /* while (1) */
+  }
+
+  /* This section comes BEFORE the underscore handling
+     below, so that "{\em...}" won't be converted to "\}\em...\}" */
+  /* Convert any remaining special characters for LaTeX */
+  /* This section is independent and can be removed without side effects */
+  if (!g_htmlFlag) { /* i.e. LaTeX mode */
+    /* At this point, the comment begins e.g "\begin{lemma}\label{lem:abc}" */
+    pos1 = instr(1, cmt, "} ");
+    if (pos1) {
+      pos1++; /* Start after the "}" */
+    } else {
+      pos1 = 1; /* If not found, start from beginning of line */
+    }
+    pos2 = (long)strlen(cmt);
+    tmpMathMode = 0;
+    for (pos1 = pos1 + 0; pos1 <= pos2; pos1++) {
+      /* Don't modify anything inside of math symbol strings
+         (imperfect - only works if `...` is not split across lines?) */
+      if (cmt[pos1 - 1] == '`') tmpMathMode = (flag)(1 - tmpMathMode);
+      if (tmpMathMode) continue;
+      if (pos1 > 1) {
+        if (cmt[pos1 - 1] == '_' && cmt[pos1 - 2] == '$') {
+          /* The _ is part of "$_{...}$" earlier conversion */
+          continue;
+        }
+      }
+      /* $%#{}&^\\|<>"~_ are converted by asciiToTt() */
+      /* Omit \ and $ since they be part of an earlier conversion */
+      /* Omit ~ since it is part of label ref */
+      /* Omit " since it legal */
+      /* Because converting to \char` causes later math mode problems due to `,
+         we change |><_ to /)(- (an ugly workaround) */
+      switch(cmt[pos1 - 1]) {
+        case '|': cmt[pos1 - 1] = '/'; break;
+        case '<': cmt[pos1 - 1] = '{'; break;
+        case '>': cmt[pos1 - 1] = '}'; break;
+        case '_': cmt[pos1 - 1] = '-'; break;
+      }
+      if (strchr("%#{}&^|<>_", cmt[pos1 - 1]) != NULL) {
+        free_vstring(tmpStr);
+        tmpStr = asciiToTt(chr(cmt[pos1 - 1]));
+        let(&cmt, cat(left(cmt, pos1 - 1), tmpStr,
+            right(cmt, pos1 + 1), NULL));
+        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), tmpStr,
+            right(cmtMasked, pos1 + 1), NULL));
+        pos1 += (long)strlen(tmpStr) - 1;
+        pos2 += (long)strlen(tmpStr) - 1;
+      }
+    } /* Next pos1 */
+  } /* if (!g_htmlFlag) */
+
+  /* Handle underscores in comments converted to HTML:  Convert _abc_
+     to <I>abc</I> for book titles, etc.; convert a_n to a<SUB>n</SUB> for
+     subscripts */
+  /* This section is independent and can be removed without side effects */
+  if (g_htmlFlag != 0 && processUnderscores != 0) {
+    pos1 = 0;
+    while (1) {
+      /* Only look at non-math part of comment */
+      pos1 = instr(pos1 + 1, cmtMasked, "_");
+      if (!pos1) break;
+      /* Don't modify anything inside of <HTML>...</HTML> tags */
+      if (pos1 > instr(1, cmt, "<HTML>") && pos1 < instr(1, cmt, "</HTML>"))
+        continue;
+
+      /* Don't modify external hyperlinks containing "_" */
+      pos2 = pos1 - 1;
+      while (1) { /* Get to previous whitespace */
+        if (pos2 == 0 || isspace((unsigned char)(cmt[pos2]))) break;
+        pos2--;
+      }
+      if (!strcmp(mid(cmt, pos2 + 2, 7), "http://")) {
+        continue;
+      }
+      if (!strcmp(mid(cmt, pos2 + 2, 8), "https://")) {
+        continue;
+      }
+      if (!strcmp(mid(cmt, pos2 + 2, 2), "mm")) {
+        continue;
+      }
+
+      /* Opening "_" must be <whitespace>_<alphanum> for <I> tag */
+      if (pos1 > 1) {
+        /* Check for not whitespace and not opening punctuation */
+        if (!isspace((unsigned char)(cmt[pos1 - 2]))
+            && strchr(OPENING_PUNCTUATION, cmt[pos1 - 2]) == NULL) {
+          /* Check for not whitespace and not closing punctuation */
+          if (!isspace((unsigned char)(cmt[pos1]))
+            && strchr(CLOSING_PUNCTUATION, cmt[pos1]) == NULL) {
+
+            /* Found <nonwhitespace>_<nonwhitespace> - assume subscript */
+            /* Locate the whitespace (or end of string) that closes subscript */
+            /* Note:  This algorithm is not perfect in that the subscript
+               is assumed to end at closing punctuation, which theoretically
+               could be part of the subscript itself, such as a subscript
+               with a comma in it. */
+            pos2 = pos1 + 1;
+            while (1) {
+              if (!cmt[pos2]) break; /* End of string */
+              /* Look for whitespace or closing punctuation */
+              if (isspace((unsigned char)(cmt[pos2]))
+                  || strchr(OPENING_PUNCTUATION, cmt[pos2]) != NULL
+                  || strchr(CLOSING_PUNCTUATION, cmt[pos2]) != NULL) break;
+              pos2++; /* Move forward through subscript */
+            }
+            pos2++; /* Adjust for left, seg, etc. that start at 1 not 0 */
+            if (g_htmlFlag) {  /* HTML */
+              /* Put <SUB>...</SUB> around subscript */
+              let(&cmt, cat(left(cmt, pos1 - 1),
+                  "<SUB><FONT SIZE=\"-1\">",
+                  seg(cmt, pos1 + 1, pos2 - 1), /* Skip (delete) "_" */
+                  "</FONT></SUB>", right(cmt, pos2), NULL));
+              let(&cmtMasked, cat(left(cmtMasked, pos1 - 1),
+                  "<SUB><FONT SIZE=\"-1\">",
+                  seg(cmtMasked, pos1 + 1, pos2 - 1), /* Skip (delete) "_" */
+                  "</FONT></SUB>", right(cmtMasked, pos2), NULL));
+              pos1 = pos2 + 33; /* Adjust for 34-1 extra chars in "let" above */
+            } else {  /* LaTeX */
+              /* Put _{...} around subscript */
+              let(&cmt, cat(left(cmt, pos1 - 1), "$_{",
+                  seg(cmt, pos1 + 1, pos2 - 1),  /* Skip (delete) "_" */
+                  "}$", right(cmt, pos2), NULL));
+              let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "$_{",
+                  seg(cmtMasked, pos1 + 1, pos2 - 1),  /* Skip (delete) "_" */
+                  "}$", right(cmtMasked, pos2), NULL));
+              pos1 = pos2 + 4; /* Adjust for 5-1 extra chars in "let" above */
+            }
+            continue;
+
+          } else {
+            /* Found <nonwhitespace>_<whitespace> - not an opening "_" */
+            /* Do nothing in this case */
+            continue;
+          }
+        }
+      }
+      if (!isalnum((unsigned char)(cmt[pos1]))) continue;
+      /* Only look at non-math part of comment */
+      pos2 = instr(pos1 + 1, cmtMasked, "_");
+      if (!pos2) break;
+      /* Closing "_" must be <alphanum>_<nonalphanum> */
+      if (!isalnum((unsigned char)(cmt[pos2 - 2]))) continue;
+      if (isalnum((unsigned char)(cmt[pos2]))) continue;
+      if (g_htmlFlag) {  /* HTML */
+        let(&cmt, cat(left(cmt, pos1 - 1), "<I>",
+            seg(cmt, pos1 + 1, pos2 - 1),
+            "</I>", right(cmt, pos2 + 1), NULL));
+        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "<I>",
+            seg(cmtMasked, pos1 + 1, pos2 - 1),
+            "</I>", right(cmtMasked, pos2 + 1), NULL));
+        pos1 = pos2 + 5; /* Adjust for 7-2 extra chars in "let" above */
+      } else {  /* LaTeX */
+        let(&cmt, cat(left(cmt, pos1 - 1), "{\\em ",
+            seg(cmt, pos1 + 1, pos2 - 1),
+            "}", right(cmt, pos2 + 1), NULL));
+        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "{\\em ",
+            seg(cmtMasked, pos1 + 1, pos2 - 1),
+            "}", right(cmtMasked, pos2 + 1), NULL));
+        pos1 = pos2 + 4; /* Adjust for 6-2 extra chars in "let" above */
+      }
+    }
+  }
+
+  /* Convert opening double quote to `` for LaTeX */
+  /* This section is independent and can be removed without side effects */
+  if (!g_htmlFlag) { /* If LaTeX mode */
+    i = 1; /* Even/odd counter: 1 = left quote, 0 = right quote */
+    pos1 = 0;
+    while (1) {
+      /* cmtMasked has math symbols blanked */
+      pos1 = instr(pos1 + 1, cmtMasked, "\"");
+      if (pos1 == 0) break;
+      if (i == 1) {
+        /* Warning:  "`" needs to be escaped (i.e. repeated) to prevent it
+           from being treated as a math symbol delimiter below.  So "````"
+           will become "``" in the LaTeX output. */
+        let(&cmt, cat(left(cmt, pos1 - 1), "````",
+            right(cmt, pos1 + 1), NULL));
+        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), "````",
+            right(cmtMasked, pos1 + 1), NULL));
+      }
+      i = 1 - i; /* Count to next even or odd */
+    }
+  }
+
+  /* Put bibliography hyperlinks in comments converted to HTML:
+        [Monk2] becomes <A HREF="mmset.html#monk2>[Monk2]</A> etc. */
+  /* This section is independent and can be removed without side effects */
+  if (g_htmlFlag && processBibrefs != 0) {
+    /* Assign local tag list and local HTML file name */
+    if (g_showStatement < g_extHtmlStmt) {
+      let(&bibTags, g_htmlBibliographyTags);
+      let(&bibFileName, g_htmlBibliography);
+    } else if (g_showStatement < g_mathboxStmt) { /* Sandbox stuff */
+      let(&bibTags, extHtmlBibliographyTags);
+      let(&bibFileName, extHtmlBibliography);
+    } else {
+      /* Sandbox stuff */
+      let(&bibTags, g_htmlBibliographyTags);  /* Go back to Mm Prf Explorer */
+      let(&bibFileName, g_htmlBibliography);
+    }
+
+    if (bibFileName[0]) {
+      /* The user specified a bibliography file in the xxx.mm $t comment
+         (otherwise we don't do anything) */
+      pos1 = 0;
+      while (1) {
+        /* Look for any bibliography tags to convert to hyperlinks */
+        /* The biblio tag should be in brackets e.g. "[Monk2]" */
+        /* Only look at non-math part of comment */
+        pos1 = instr(pos1 + 1, cmtMasked, "[");
+        if (!pos1) break;
+
+        /* Escape a double [[ */
+        if (cmtMasked[pos1] == '[') {  /* This is the char after "[" above */
+          /* Remove the first "[" */
+          let(&cmt, cat(left(cmt, pos1 - 1),
+              right(cmt, pos1 + 1), NULL));
+          let(&cmtMasked, cat(left(cmtMasked, pos1 - 1),
+              right(cmtMasked, pos1 + 1), NULL));
+          /* The pos1-th position (starting at 1) is now the "[" that remains */
+          continue;
+        }
+
+        /* Only look at non-math part of comment */
+        pos2 = instr(pos1 + 1, cmtMasked, "]");
+        if (!pos2) break;
+
+        /* Get bibTag from cmtMasked as extra precaution */
+        let(&bibTag, seg(cmtMasked, pos1, pos2));
+        /* There should be no white space in the tag */
+        if ((signed)(strcspn(bibTag, " \n\r\t\f")) < pos2 - pos1 + 1) continue;
+        /* OK, we have a good tag.  If the file with bibliography has not been
+           read in yet, let's do so here for error-checking. */
+
+        /* Start of error-checking */
+        if (fileCheck) {
+          if (!bibTags[0]) {
+            /* The bibliography file has not be read in yet. */
+            free_vstring(bibFileContents);
+            if (errorsOnly == 0) {
+              print2("Reading HTML bibliographic tags from file \"%s\"...\n",
+                  bibFileName);
+            }
+            bibFileContents = readFileToString(bibFileName, 0,
+                &i /* charCount; not used here */);
+            if (!bibFileContents) {
+              /* The file was not found or had some problem (use verbose mode = 1
+                 in 2nd argument of readFileToString for debugging). */
+              printLongLine(cat("?Warning: Couldn't open or read the file \"",
+                  bibFileName,
+                  "\".  The bibliographic hyperlinks will not be checked for",
+                  " correctness.  The first one is \"", bibTag,
+                  "\" in the comment for statement \"",
+                  g_Statement[g_showStatement].labelName, "\".",
+                  NULL), "", " ");
+              returnVal = 1; /* Error/warning printed */
+              bibFileContents = ""; /* Restore to normal string */
+              let(&bibTags, "?"); /* Assign to a nonsense tag that won't match
+                  but tells us an attempt was already made to read the file */
+            } else {
+              /* Note: In an <A NAME=...> tag, HTML is case-insensitive for A and
+                 NAME but case-sensitive for the token after the = */
+              /* Strip all whitespace */
+              let(&bibFileContents, edit(bibFileContents, 2));
+              /* Uppercase version for HTML tag search */
+              let(&bibFileContentsUpper, edit(bibFileContents, 32));
+              htmlpos1 = 0;
+              while (1) {  /* Look for all <A NAME=...></A> HTML tags */
+                htmlpos1 = instr(htmlpos1 + 1, bibFileContentsUpper, "<ANAME=");
+                /* Note stripped space after <A... - not perfectly robust but
+                   good enough if HTML file is legal since <ANAME is not an HTML
+                   tag (let's not get into a regex discussion though...) */
+                if (!htmlpos1) break;
+                htmlpos1 = htmlpos1 + 7;  /* Point to beginning of tag name */
+                /* Extract tag, ignoring any surrounding quotes */
+                if (bibFileContents[htmlpos1 - 1] == '\''
+                    || bibFileContents[htmlpos1 - 1] == '"') htmlpos1++;
+                htmlpos2 = instr(htmlpos1, bibFileContents, ">");
+                if (!htmlpos2) break;
+                htmlpos2--; /* Move to character before ">" */
+                if (bibFileContents[htmlpos2 - 1] == '\''
+                    || bibFileContents[htmlpos2 - 1] == '"') htmlpos2--;
+                if (htmlpos2 <= htmlpos1) continue;  /* Ignore bad HTML syntax */
+                let(&tmp, cat("[",
+                    seg(bibFileContents, htmlpos1, htmlpos2), "]", NULL));
+                /* Check if tag is already in list */
+                if (instr(1, bibTags, tmp)) {
+                  printLongLine(cat("?Error: There two occurrences of",
+                      " bibliographic reference \"",
+                      seg(bibFileContents, htmlpos1, htmlpos2),
+                      "\" in the file \"", bibFileName, "\".", NULL), "", " ");
+                  returnVal = 1; /* Error/warning printed */
+                }
+                /* Add tag to tag list */
+                let(&bibTags, cat(bibTags, tmp, NULL));
+              } /* end while */
+              if (!bibTags[0]) {
+                /* No tags found; put dummy partial tag meaning "file read" */
+                let(&bibTags, "[");
+              }
+            } /* end if (!bibFileContents) */
+          } /* end if (noFileCheck == 0) */
+          /* Assign to permanent tag list for next time */
+          if (g_showStatement < g_extHtmlStmt) {
+            let(&g_htmlBibliographyTags, bibTags);
+          /*} else {*/
+          } else if (g_showStatement < g_mathboxStmt) {
+            let(&extHtmlBibliographyTags, bibTags);
+
+          } else {
+            let(&g_htmlBibliographyTags, bibTags);
+
+          }
+          /* Done reading in HTML file with bibliography */
+        } /* end if (!bibTags[0]) */
+        /* See if the tag we found is in the bibliography file */
+        if (bibTags[0] == '[') {
+          /* We have a tag list from the bibliography file */
+          if (!instr(1, bibTags, bibTag)) {
+            printLongLine(cat("?Error: The bibliographic reference \"", bibTag,
+                "\" in statement \"", g_Statement[g_showStatement].labelName,
+                "\" was not found as an <A NAME=\"",
+                seg(bibTag, 2, pos2 - pos1),
+                "\"></A> anchor in the file \"", bibFileName, "\".", NULL),
+                "", " ");
+            returnVal = 1; /* Error/warning printed */
+          }
+        }
+        /* End of error-checking */
+
+        /* Make an HTML reference for the tag */
+        let(&tmp, cat("[<A HREF=\"",
+            bibFileName, "#", seg(bibTag, 2, pos2 - pos1), "\">",
+            seg(bibTag, 2, pos2 - pos1), "</A>]", NULL));
+        let(&cmt, cat(left(cmt, pos1 - 1), tmp, right(cmt,
+            pos2 + 1), NULL));
+        let(&cmtMasked, cat(left(cmtMasked, pos1 - 1), tmp, right(cmtMasked,
+            pos2 + 1), NULL));
+        pos1 = pos1 + (long)strlen(tmp) - (long)strlen(bibTag); /* Adjust comment position */
+      } /* end while(1) */
+    } /* end if (bibFileName[0]) */
+  } /* end of if (g_htmlFlag) */
+
+  /* All actions on cmt should be mirrored on cmdMasked, except that
+     math symbols are replaced with blanks in cmdMasked */
+  if (strlen(cmt) != strlen(cmtMasked)) bug(2334); /* Should be in sync */
+
+  /* Starting here, we no longer use cmtMasked, so syncing it with cmt
+     isn't important anymore. */
+
+  clen = (long)strlen(cmt);
+  mode = 'n';
+  for (i = 0; i < clen; i++) {
+    if (cmt[i] == '`') {
+      if (cmt[i + 1] == '`') {
+        if (processSymbols != 0) {
+          /* Escaped ` = actual ` */
+          let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
+          clen--;
+        }
+      } else {
+        /* We will still enter and exit math mode when
+           processSymbols=0 so as to skip ~ in math symbols.  However,
+           we don't insert the "DOLLAR_SUBST mode" so that later on
+           it will look like normal text */
+        /* Enter or exit math mode */
+        if (mode != 'm') {
+          mode = 'm';
+        } else {
+          mode = 'n';
+        }
+
+        if (processSymbols != 0) {
+          let(&cmt, cat(left(cmt, i), chr(DOLLAR_SUBST) /*$*/, chr(mode),
+              right(cmt, i+2), NULL));
+          clen++;
+          i++;
+        }
+
+        /* If symbol is preceded by opening punctuation and a space, take out
+           the space so it looks better. */
+        if (mode == 'm' && processSymbols != 0) {
+          let(&tmp, mid(cmt, i - 2, 2));
+          if (!strcmp("( ", tmp)) {
+            let(&cmt, cat(left(cmt, i - 2), right(cmt, i), NULL));
+            clen = clen - 1;
+          }
+          /* We include quotes since symbols are often enclosed in them. */
+          let(&tmp, mid(cmt, i - 8, 8));
+          if (!strcmp("&quot; ", right(tmp, 2))
+              && strchr("( ", tmp[0]) != NULL) {
+            let(&cmt, cat(left(cmt, i - 2), right(cmt, i), NULL));
+            clen = clen - 1;
+          }
+          free_vstring(tmp);
+        }
+        /* If symbol is followed by a space and closing punctuation, take out
+           the space so it looks better. */
+        if (mode == 'n' && processSymbols != 0) {
+          /* (Why must it be i + 2 here but i + 1 in label version below?
+             Didn't investigate but seems strange.) */
+          let(&tmp, mid(cmt, i + 2, 2));
+          if (tmp[0] == ' ' && strchr(CLOSING_PUNCTUATION, tmp[1]) != NULL) {
+            let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
+            clen = clen - 1;
+          }
+          /* We include quotes since symbols are often enclosed in them. */
+          let(&tmp, mid(cmt, i + 2, 8));
+          if (strlen(tmp) < 8)
+              let(&tmp, cat(tmp, space(8 - (long)strlen(tmp)), NULL));
+          if (!strcmp(" &quot;", left(tmp, 7))
+              && strchr(CLOSING_PUNCTUATION, tmp[7]) != NULL) {
+            let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
+            clen = clen - 1;
+          }
+          free_vstring(tmp);
+        }
+
+      }
+    }
+    if (cmt[i] == '~' && mode != 'm') {
+      if (cmt[i + 1] == '~' /* Escaped ~ */ || processLabels == 0) {
+        if (processLabels != 0) {
+          /* Escaped ~ = actual ~ */
+          let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
+          clen--;
+        }
+      } else {
+        /* Enter or exit label mode */
+        if (mode != 'l') {
+          mode = 'l';
+          /* If there is whitespace after the ~, then remove
+             all whitespace immediately after the ~ to join the ~ to
+             the label.  This enhances the Metamath syntax so that
+             whitespace is now allowed between the ~ and the label, which
+             makes it easier to do global substitutions of labels in a
+             text editor. */
+          while (isspace((unsigned char)(cmt[i + 1])) && clen > i + 1) {
+            let(&cmt, cat(left(cmt, i + 1), right(cmt, i + 3), NULL));
+            clen--;
+          }
+        } else {
+          /* If you see this bug, the most likely cause is a tilde
+             character in a comment that does not prefix a label or hyperlink.
+             The most common problem is the "~" inside a hyperlink that
+             specifies a user's directory.  To fix it, use a double tilde
+             "~~" to escape it, which will become a single tilde on output. */
+          g_outputToString = 0;
+          printLongLine(cat("?Warning: There is a \"~\" inside of a label",
+              " in the comment of statement \"",
+              g_Statement[g_showStatement].labelName,
+              "\".  Use \"~~\" to escape \"~\" in an http reference.",
+              NULL), "", " ");
+          returnVal = 1; /* Error/warning printed */
+          g_outputToString = 1;
+          mode = 'n';
+        }
+        let(&cmt, cat(left(cmt, i), chr(DOLLAR_SUBST) /*$*/, chr(mode),
+            right(cmt, i+2), NULL));
+        clen++;
+        i++;
+
+        /* If label is preceded by opening punctuation and space, take out
+           the space so it looks better. */
+        let(&tmp, mid(cmt, i - 2, 2));
+        /*printf("tmp#%s#\n",tmp);*/
+        if (!strcmp("( ", tmp) || !strcmp("[ ", tmp)) {
+          let(&cmt, cat(left(cmt, i - 2), right(cmt, i), NULL));
+          clen = clen - 1;
+        }
+        free_vstring(tmp);
+
+      }
+    }
+
+    if (processLabels == 0 && mode == 'l') {
+      /* We should have prevented it from ever getting into label mode */
+      bug(2344);
+    }
+
+    if ((isspace((unsigned char)(cmt[i]))
+            || cmt[i] == '<') /* If the label ends the comment,
+               "</TD>" with no space will be appended before this section. */
+        && mode == 'l') {
+      /* Whitespace exits label mode */
+      mode = 'n';
+      let(&cmt, cat(left(cmt, i), chr(DOLLAR_SUBST) /*$*/, chr(mode),
+          right(cmt, i+1), NULL));
+      clen = clen + 2;
+      i = i + 2;
+
+      /* If label is followed by space and end punctuation, take out the space
+         so it looks better. */
+      let(&tmp, mid(cmt, i + 1, 2));
+      if (tmp[0] == ' ' && strchr(CLOSING_PUNCTUATION, tmp[1]) != NULL) {
+        let(&cmt, cat(left(cmt, i), right(cmt, i + 2), NULL));
+        clen = clen - 1;
+      }
+      free_vstring(tmp);
+
+    }
+    /* clen should always remain comment length - do a sanity check here */
+    if ((signed)(strlen(cmt)) != clen) {
+      bug(2311);
+    }
+  } /* Next i */
+  /* End convert line to the old $m..$n and $l..$n */
+
+
+  /* Put <HTML> and </HTML> at beginning of line so preformattedMode won't
+     be switched on or off in the middle of processing a line */
+  /* This also fixes the problem where multiple <HTML>...</HTML> on one
+     line aren't all removed in HTML output, causing w3c validation errors */
+  /* Note:  "Q<HTML><sup>2</sup></HTML>." will have a space around "2" because
+     of this fix.  Instead use "<HTML>Q<sup>2</sup>.</HTML>" or just "Q^2." */
+  pos1 = -1; /* So -1 + 2 = 1 = start of string for instr() */
+  while (1) {
+    pos1 = instr(pos1 + 2/*skip new \n*/, cmt, "<HTML>");
+    if (pos1 == 0
+      || convertToHtml == 0 /* Don't touch <HTML> in MARKUP command */
+      ) break;
+
+    /* If <HTML> begins a line (after stripping spaces), don't put a \n so
+       that we don't trigger new paragraph mode */
+    let(&tmpStr, edit(left(cmt, pos1 - 1), 2/*discard spaces & tabs*/));
+    i = (long)strlen(tmpStr);
+    if (i == 0) continue;
+    if (tmpStr[i - 1] == '\n') continue;
+
+    let(&cmt, cat(left(cmt, pos1 - 1), "\n", right(cmt, pos1), NULL));
+  }
+  pos1 = -1; /* So -1 + 2 = 1 = start of string for instr() */
+  while (1) {
+    pos1 = instr(pos1 + 2/*skip new \n*/, cmt, "</HTML>");
+    if (pos1 == 0
+      || convertToHtml == 0 /* Don't touch </HTML> in MARKUP command */
+      ) break;
+
+    /* If </HTML> begins a line (after stripping spaces), don't put a \n so
+       that we don't trigger new paragraph mode */
+    let(&tmpStr, edit(left(cmt, pos1 - 1), 2/*discard spaces & tabs*/));
+    i = (long)strlen(tmpStr);
+    if (i == 0) continue;
+    if (tmpStr[i - 1] == '\n') continue;
+
+    let(&cmt, cat(left(cmt, pos1 - 1), "\n", right(cmt, pos1), NULL));
+  }
+
+
+  cmtptr = cmt; /* cmtptr is for scanning cmt */
+
+  g_outputToString = 1; /* Redirect print2 and printLongLine to g_printString */
+
+  while (1) {
+    /* Get a "line" of text, up to the next new-line.  New-lines embedded
+       in $m and $l sections are ignored, so that these sections will not
+       be dangling. */
+    lineStart = cmtptr;
+    textMode = 1;
+    lastLineFlag = 0;
+    while (1) {
+      if (cmtptr[0] == 0) {
+        lastLineFlag = 1;
+        break;
+      }
+      if (cmtptr[0] == '\n' && textMode) break;
+      /* if (cmtptr[0] == '$') { */
+      if (cmtptr[0] == DOLLAR_SUBST) {
+        if (cmtptr[1] == ')') {
+          bug(2312); /* Obsolete (should never happen) */
+          lastLineFlag = 1;
+          break;
+        }
+      }
+      if (cmtptr[0] == DOLLAR_SUBST /*'$'*/) {
+        cmtptr++;
+        if (cmtptr[0] == 'm') textMode = 0; /* Math mode */
+        if (cmtptr[0] == 'l') textMode = 0; /* Label mode */
+        if (cmtptr[0] == 'n') textMode = 1; /* Normal mode */
+      }
+      cmtptr++;
+    }
+    let(&sourceLine, space(cmtptr - lineStart));
+    memcpy(sourceLine, lineStart, (size_t)(cmtptr - lineStart));
+    cmtptr++;  /* Get past new-line to prepare for next line's scan */
+
+    /* If the line contains only math mode text, use TeX display mode. */
+    displayMode = 0;
+    let(&tmpStr, edit(sourceLine, 8 + 128)); /* Trim spaces */
+    if (!strcmp(right(tmpStr, (long)strlen(tmpStr) - 1), cat(chr(DOLLAR_SUBST), "n",
+        NULL))) let(&tmpStr, left(tmpStr, (long)strlen(tmpStr) - 2)); /* Strip $n */
+    srcptr = tmpStr;
+    modeSection = getCommentModeSection(&srcptr, &mode);
+    free_vstring(modeSection); /* Deallocate */
+    if (mode == 'm') {
+      modeSection = getCommentModeSection(&srcptr, &mode);
+      free_vstring(modeSection); /* Deallocate */
+    }
+    free_vstring(tmpStr); /* Deallocate */
+
+
+    /* Convert all sections of the line to text, math, or labels */
+    let(&outputLine, "");
+    srcptr = sourceLine;
+    while (1) {
+      modeSection = getCommentModeSection(&srcptr, &mode);
+      if (!mode) break; /* Done */
+      let(&modeSection, right(modeSection, 3)); /* Remove mode-change command */
+      switch (mode) {
+        case 'n': /* Normal text */
+          let(&outputLine, cat(outputLine, modeSection, NULL));
+          break;
+        case 'l': /* Label mode */
+
+          if (processLabels == 0) {
+            /* Labels should be treated as normal text */
+            bug(2345);
+          }
+
+          let(&modeSection, edit(modeSection, 8 + 128 + 16));  /* Discard
+                      leading and trailing blanks; reduce spaces to one space */
+          free_vstring(tmpStr);
+          tmpStr = asciiToTt(modeSection);
+          if (!tmpStr[0]) {
+            /* This can happen if ~ is followed by ` (start of math string) */
+            g_outputToString = 0;
+            printLongLine(cat("?Error: There is a \"~\" with no label",
+                " in the comment of statement \"",
+                g_Statement[g_showStatement].labelName,
+                "\".  Check that \"`\" inside of a math symbol is",
+                " escaped with \"``\".",
+                NULL), "", " ");
+            returnVal = 1; /* Error/warning printed */
+            g_outputToString = 1;
+
+          }
+
+          if (!strcmp("http://", left(tmpStr, 7))
+              || !strcmp("https://", left(tmpStr, 8))
+              || !strcmp("mm", left(tmpStr, 2))) {
+            /* If the "label" begins with 'http://', then
+               assume it is an external hyperlink and not a real label.
+               This is kind of a syntax kludge but it is easy to do.
+               Added starting with 'mm', which is illegal
+               for set.mm labels - e.g. mmtheorems.html#abc */
+
+            if (g_htmlFlag) {
+              let(&outputLine, cat(outputLine, "<A HREF=\"", tmpStr,
+                  "\">", tmpStr, "</A>", tmp, NULL));
+            } else {
+
+              /* Generate LaTeX version of the URL */
+              i = instr(1, tmpStr, "\\char`\\~");
+              /* The url{} function automatically converts ~ to LaTeX */
+              if (i != 0) {
+                let(&tmpStr, cat(left(tmpStr, i - 1), right(tmpStr, i + 7),
+                    NULL));
+              }
+              let(&outputLine, cat(outputLine, "\\url{", tmpStr,
+                  "}", tmp, NULL));
+
+            }
+          } else {
+            /* Do binary search through just $a's and $p's (there
+               are no html pages for local labels) */
+            i = lookupLabel(tmpStr);
+            if (i < 0) {
+              g_outputToString = 0;
+              printLongLine(cat("?Warning: The label token \"", tmpStr,
+                  "\" (referenced in comment of statement \"",
+                  g_Statement[g_showStatement].labelName,
+                  "\") is not a $a or $p statement label.", NULL), "", " ");
+              g_outputToString = 1;
+              returnVal = 1; /* Error/warning printed */
+            }
+
+            if (!g_htmlFlag) {
+              let(&outputLine, cat(outputLine, "{\\tt ", tmpStr,
+                 "}", NULL));
+            } else {
+              free_vstring(tmp);
+              if (addColoredLabelNumber != 0) {
+                /* When the error above occurs, i < 0 will cause pinkHTML()
+                   to issue "(future)" for pleasant readability */
+                tmp = pinkHTML(i);
+              }
+              if (i < 0) {
+                /* Error output - prevent broken link */
+                let(&outputLine, cat(outputLine, "<FONT COLOR=blue ",
+                    ">", tmpStr, "</FONT>", tmp, NULL));
+              } else {
+                /* Normal output - put hyperlink to the statement */
+                let(&outputLine, cat(outputLine, "<A HREF=\"", tmpStr,
+                    ".html\">", tmpStr, "</A>", tmp, NULL));
+              }
+            }
+          } /* if (!strcmp("http://", left(tmpStr, 7))) ... else */
+          free_vstring(tmpStr); /* Deallocate */
+          break;
+        case 'm': /* Math mode */
+
+          if (processSymbols == 0) {
+            /* Math symbols should be treated as normal text */
+            bug(2346);
+          }
+
+          free_vstring(tmpStr);
+          tmpStr = asciiMathToTex(modeSection, g_showStatement);
+          if (!g_htmlFlag) {
+            if (displayMode) {
+              /* It the user's responsibility to establish equation environment
+                 in displayMode. */
+              let(&outputLine, cat(outputLine, /*"\\[",*/ edit(tmpStr, 128),
+                /*"\\]",*/ NULL));  /* edit = remove trailing spaces */
+            } else {
+              let(&outputLine, cat(outputLine, "$", edit(tmpStr, 128),
+                "$", NULL));  /* edit = remove trailing spaces */
+            }
+          } else {
+            /* Trim leading, trailing spaces in case punctuation
+               surrounds the math symbols in the comment */
+            let(&tmpStr, edit(tmpStr, 8 + 128));
+            /* Enclose math symbols in a span to be used for font selection */
+            let(&tmpStr, cat(
+                (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
+                tmpStr,
+                (g_altHtmlFlag ? "</SPAN>" : ""),
+                NULL));
+            let(&outputLine, cat(outputLine, tmpStr, NULL)); /* html */
+          }
+          free_vstring(tmpStr); /* Deallocate */
+          break;
+      } /* End switch(mode) */
+      free_vstring(modeSection); /* Deallocate */
+    }
+    let(&outputLine, edit(outputLine, 128)); /* remove trailing spaces */
+
+    if (g_htmlFlag) {
+      /* Change blank lines into paragraph breaks except in <HTML> mode */
+      if (!outputLine[0]) { /* Blank line */
+        if (preformattedMode == 0
+            && convertToHtml == 1 /* Not MARKUP command */
+            ) {  /* Make it a paragraph break */
+          let(&outputLine,
+              /* Prevent space after last paragraph */
+              "<P STYLE=\"margin-bottom:0em\">");
+        }
+      }
+      /* If a statement comment has a section embedded in
+         <HTML>...</HTML>, we keep the contents verbatim */
+      pos1 = instr(1, outputLine, "<HTML>");
+      if (pos1 != 0 && convertToHtml == 1) {
+        /* The line below is probably redundant since we
+           set preformattedMode earlier.  Maybe add a bug check to make sure
+           it is 1 here. */
+        preformattedMode = 1; /* So we don't put <P> for blank lines */
+        /* Strip out the "<HTML>" string */
+        let(&outputLine, cat(left(outputLine, pos1 - 1),
+            right(outputLine, pos1 + 6), NULL));
+      }
+      pos1 = instr(1, outputLine, "</HTML>");
+      if (pos1 != 0 && convertToHtml == 1) {
+        preformattedMode = 0;
+        /* Strip out the "</HTML>" string */
+        let(&outputLine, cat(left(outputLine, pos1 - 1),
+            right(outputLine, pos1 + 7), NULL));
+      }
+    }
+
+    if (!g_htmlFlag) { /* LaTeX */
+      /* Convert <PRE>...</PRE> HTML tags to LaTeX */
+      /* leave this in for now */
+      while (1) {
+        pos1 = instr(1, outputLine, "<PRE>");
+        if (pos1) {
+          let(&outputLine, cat(left(outputLine, pos1 - 1), "\\begin{verbatim} ",
+              right(outputLine, pos1 + 5), NULL));
+        } else {
+          break;
+        }
+      }
+      while (1) {
+        pos1 = instr(1, outputLine, "</PRE>");
+        if (pos1) {
+          let(&outputLine, cat(left(outputLine, pos1 - 1), "\\end{verbatim} ",
+              right(outputLine, pos1 + 6), NULL));
+        } else {
+          break;
+        }
+      }
+      /* strip out <HTML>, </HTML> */
+      /* The HTML part may screw up LaTeX; maybe we should just take out
+         any HTML code completely in the future? */
+      while (1) {
+        pos1 = instr(1, outputLine, "<HTML>");
+        if (pos1) {
+          let(&outputLine, cat(left(outputLine, pos1 - 1),
+              right(outputLine, pos1 + 6), NULL));
+        } else {
+          break;
+        }
+      }
+      while (1) {
+        pos1 = instr(1, outputLine, "</HTML>");
+        if (pos1) {
+          let(&outputLine, cat(left(outputLine, pos1 - 1),
+              right(outputLine, pos1 + 7), NULL));
+        } else {
+          break;
+        }
+      }
+    }
+
+    saveScreenWidth = g_screenWidth;
+    /* in <PRE> mode, we don't want to wrap the HTML
+       output with spurious newlines. Any large value will
+       do; we just need to accommodate the worst case line length that will
+       result from converting ~ label, [author], ` math ` to HTML */
+    if (preformattedMode) g_screenWidth = 50000;
+    if (errorsOnly == 0) {
+      printLongLine(outputLine, "", g_htmlFlag ? "\"" : "\\");
+    }
+    g_screenWidth = saveScreenWidth;
+
+    freeTempAlloc(); /* Clear temporary allocation stack */
+
+    if (lastLineFlag) break; /* Done */
+  } /* end while(1) */
+
+  if (g_htmlFlag) {
+    if (convertToHtml != 0) { /* Not MARKUP command */
+      print2("\n"); /* Don't change what the previous code did */
+    } else {
+      /* Add newline if string is not empty and has no newline at end */
+      if (g_printString[0] != 0) {
+        i = (long)strlen(g_printString);
+        if (g_printString[i - 1] != '\n')  {
+          print2("\n");
+        } else {
+          /* There is an extra \n added by something previous.  Until
+             we figure out what, take it off so that MARKUP output will
+             equal input when no processing qualifiers are used. */
+          if (i > 1) {
+            if (g_printString[i - 2] == '\n') {
+              let(&g_printString, left(g_printString, i - 1));
+            }
+          }
+        }
+      }
+    }
+  } else { /* LaTeX mode */
+    if (!g_oldTexFlag) {
+      /* Suppress blank line for LaTeX */
+      /* print2("\n"); */
+    } else {
+      print2("\n");
+    }
+  }
+
+  g_outputToString = 0; /* Restore normal output */
+  if (errorsOnly == 0) {
+    fprintf(g_texFilePtr, "%s", g_printString);
+  }
+
+  free_vstring(g_printString); /* Deallocate strings */
+  free_vstring(sourceLine);
+  free_vstring(outputLine);
+  free_vstring(cmt);
+  free_vstring(cmtMasked);
+  free_vstring(tmpComment);
+  free_vstring(tmp);
+  free_vstring(tmpMasked);
+  free_vstring(tmpStr);
+  free_vstring(tmpStrMasked);
+  free_vstring(bibTag);
+  free_vstring(bibFileName);
+  free_vstring(bibFileContents);
+  free_vstring(bibFileContentsUpper);
+  free_vstring(bibTags);
+
+  return returnVal; /* 1 if error/warning found */
+
+} /* printTexComment */
+
+
+
+void printTexLongMath(nmbrString *mathString,
+    vstring startPrefix, /* Start prefix in "screen display" mode e.g.
+         "abc $p"; it is converted to the appropriate format.  Non-zero
+         length means proof step in HTML mode, as opposed to assertion etc. */
+    vstring contPrefix, /* Prefix for continuation lines.  Not used in
+         HTML mode.  Warning:  contPrefix must not be temporarily allocated
+         (as a cat, left, etc. argument) by caller */
+    long hypStmt, /* hypStmt, if non-zero, is the statement number to be
+                     referenced next to the hypothesis link in html */
+    long indentationLevel) /* Indentation amount of proof step -
+                              note that this is 0 for last step of proof */
+{
+#define INDENTATION_OFFSET 1
+  long i;
+  long pos;
+  vstring_def(tex);
+  vstring_def(texLine);
+  vstring_def(sPrefix);
+  vstring_def(htmStep);
+  vstring_def(htmStepTag);
+  vstring_def(htmHyp);
+  vstring_def(htmRef);
+  vstring_def(htmLocLab);
+  vstring_def(tmp);
+  vstring_def(descr);
+  char refType = '?'; /* 'e' means $e, etc. */
+
+  let(&sPrefix, startPrefix); /* Save it; it may be temp alloc */
+
+  if (!g_texDefsRead) return; /* TeX defs were not read (error was printed) */
+  g_outputToString = 1; /* Redirect print2 and printLongLine to g_printString */
+
+  /* Note that the "tex" assignment below will be used only when !g_htmlFlag
+     and g_oldTexFlag, or when g_htmlFlag and len(sPrefix)>0 */
+  free_vstring(tex);
+  tex = asciiToTt(sPrefix); /* asciiToTt allocates; we must deallocate */
+      /* Example: sPrefix = " 4 2,3 ax-mp  $a " */
+      /*          tex = "\ 4\ 2,3\ ax-mp\ \ \$a\ " in !g_htmlFlag mode */
+      /*          tex = " 4 2,3 ax-mp  $a " in g_htmlFlag mode */
+  let(&texLine, "");
+
+  /* Get statement type of proof step reference */
+  i = instr(1, sPrefix, "$");
+  if (i) refType = sPrefix[i]; /* Character after the "$" */
+
+  if (g_htmlFlag || !g_oldTexFlag) {
+
+    /* Process a proof step prefix */
+    if (strlen(sPrefix)) { /* It's a proof step */
+      /* Make each token a separate table column for HTML */
+      /* This is a kludge that only works with /LEMMON style proofs! */
+      /* Note that asciiToTt() above puts "\ " when not in
+         g_htmlFlag mode, so use sPrefix instead of tex so it will work in
+         !g_oldTexFlag mode */
+
+      /* In HTML mode, sPrefix has two possible formats:
+           "2 ax-1  $a "
+           "3 1,2 ax-mp  $a "
+         In LaTeX mode (!g_htmlFlag), sPrefix has one format:
+           "8   maj=ax-1  $a "
+           "9 a1i=ax-mp $a " */
+
+      let(&tex, edit(sPrefix, 8/*no leading spaces*/
+           + 16/*reduce spaces and tabs*/
+           + 128/*no trailing spaces*/));
+
+      i = 0;
+      pos = 1;
+      while (pos) {
+        pos = instr(1, tex, " ");
+        if (pos) {
+          if (i > 3) { /* added for extra safety for the future */
+            bug(2316);
+          }
+          if (i == 0) let(&htmStep, left(tex, pos - 1));
+          if (i == 1) let(&htmHyp, left(tex, pos - 1));
+          if (i == 2) let(&htmRef, left(tex, pos - 1));
+          if (i == 3) let(&htmLocLab, left(tex, pos - 1));
+
+          let(&tex, right(tex, pos + 1));
+          i++;
+        }
+      }
+
+      if (i == 3 && htmRef[0] == '@') {
+        /* The referenced statement has no hypotheses but has a local
+           label e.g."2 a4s @2: $p" */
+        let(&htmLocLab, htmRef);
+        let(&htmRef, htmHyp);
+        let(&htmHyp, "");
+      }
+
+      if (i < 3) {
+        /* The referenced statement has no hypotheses e.g.
+           "4 ax-1 $a" */
+        let(&htmRef, htmHyp);
+        let(&htmHyp, "");
+
+        /* Change "maj=ax-1" to "ax-1" so \ref{} produced by
+           "show proof .../tex" will match \label{} produced by
+           "show statement .../tex" */
+        /* Earlier we set the noIndentFlag (Lemmon
+           proof) in the SHOW PROOF.../TEX call in metamath.c, so the
+           hypothesis ref list will be available just like in the HTML
+           output. */
+        /* We now consider "=" a bug since the call via typeProof() in
+           metamath.c now always has noIndentFlag = 1. */
+        if (!g_htmlFlag) {
+          pos = instr(1, htmRef, "=");
+          if (pos) bug(2342);
+        }
+      }
+    } /* if (strlen(sPrefix)) (end processing proof step prefix) */
+  }
+
+  if (!g_htmlFlag) {
+    if (g_oldTexFlag) {
+      /* Trim down long start prefixes so they won't overflow line,
+         by putting their tokens into \m macros */
+#define TRIMTHRESHOLD 60
+      i = (long)strlen(tex);
+      while (i > TRIMTHRESHOLD) {
+        if (tex[i] == '\\') {
+          /* Move to math part */
+          let(&texLine, cat("\\m{\\mbox{\\tt", right(tex, i + 1), "}}",
+              texLine, NULL));
+          /* Take off of prefix part */
+          let(&tex, left(tex, i));
+        }
+        i--;
+      }
+
+      printLongLine(cat(
+          "\\setbox\\startprefix=\\hbox{\\tt ", tex, "}", NULL), "", "\\");
+      free_vstring(tex); /* Deallocate */
+      tex = asciiToTt(contPrefix);
+      printLongLine(cat(
+          "\\setbox\\contprefix=\\hbox{\\tt ", tex, "}", NULL), "", "\\");
+      print2("\\startm\n");
+    }
+  } else { /* g_htmlFlag */
+    if (strlen(sPrefix)) { /* It's a proof step */
+
+      if (htmHyp[0] == 0)
+        let(&htmHyp, "&nbsp;");  /* Insert blank field for Lemmon ref w/out hyp */
+
+      /* Put hyperlinks on hypothesis
+         label references in SHOW STATEMENT * /HTML, ALT_HTML output */
+      /* Use a separate tag to put into the math cell,
+         so it will link to the top of the math cell */
+      let(&htmStepTag, cat("<A NAME=\"", htmStep, "\">","</A>", NULL));
+      i = 1;
+      pos = 1;
+      while (pos && strcmp(htmHyp, "&nbsp;")) {
+        pos = instr(i,htmHyp, ",");
+        if (!pos) pos = len(htmHyp) + 1;
+        let(&htmHyp, cat(left(htmHyp, i - 1),
+            "<A HREF=\"#",
+            seg(htmHyp, i, pos - 1),
+            "\">",
+            seg(htmHyp, i, pos - 1),
+            "</A>",
+            right(htmHyp, pos),
+            NULL));
+        /* Break out of loop if we hit the end */
+        pos += 16 + len(seg(htmHyp, i, pos - 1)) + 1;
+        if (!instr(i, htmHyp, ",")) break;
+        i = pos;
+      }
+
+      /* Add a space after each comma so very long hypotheses
+         lists will wrap in an HTML table cell, e.g. gomaex3 in ql.mm */
+      pos = instr(1, htmHyp, ",");
+      while (pos) {
+        let(&htmHyp, cat(left(htmHyp, pos), " ", right(htmHyp, pos + 1), NULL));
+        pos = instr(pos + 1, htmHyp, ",");
+      }
+
+      if (refType == 'e' || refType == 'f') {
+        /* A hypothesis - don't include link */
+        printLongLine(cat("<TR ALIGN=LEFT><TD>", htmStep, "</TD><TD>",
+            htmHyp, "</TD><TD>", htmRef,
+            "</TD><TD>",
+            htmStepTag, /* Put the <A NAME=...></A> tag at start of math symbol cell */
+            NULL), "", "\"");
+      } else {
+        if (hypStmt <= 0) {
+          printLongLine(cat("<TR ALIGN=LEFT><TD>", htmStep, "</TD><TD>",
+              htmHyp, "</TD><TD><A HREF=\"", htmRef, ".html\">", htmRef,
+              "</A></TD><TD>",
+              htmStepTag, /* Put the <A NAME=...></A> tag at start of math symbol cell */
+              NULL), "", "\"");
+        } else {
+          /* Include step number reference.  The idea is that this will
+             help the user to recognized "important" (vs. early trivial
+             logic) steps.  This prints a small pink statement number
+             after the hypothesis statement label. */
+          free_vstring(tmp);
+          tmp = pinkHTML(hypStmt);
+
+          /* Get description for mod below */
+          free_vstring(descr); /* Deallocate previous description */
+          descr = getDescription(hypStmt);
+          let(&descr, edit(descr, 4 + 16)); /* Discard lf/cr; reduce spaces */
+#define MAX_DESCR_LEN 87
+          if (strlen(descr) > MAX_DESCR_LEN) { /* Truncate long lines */
+            i = MAX_DESCR_LEN - 3;
+            while (i >= 0) { /* Get to previous word boundary */
+              if (descr[i] == ' ') break;
+              i--;
+            }
+            let(&descr, cat(left(descr, i), "...", NULL));
+          }
+          i = 0;
+          while (descr[i] != 0) { /* Convert double quote to single */
+            descr[i] = (char)(descr[i] == '"' ? '\'' : descr[i]);
+            i++;
+          }
+
+          printLongLine(cat("<TR ALIGN=LEFT><TD>", htmStep, "</TD><TD>",
+              htmHyp, "</TD><TD><A HREF=\"", htmRef, ".html\"",
+              /* Put in a TITLE entry for mouseover tooltip,
+                 as suggested by Reinder Verlinde */
+              " TITLE=\"", descr, "\"",
+              ">", htmRef,
+              "</A>", tmp,
+              "</TD><TD>",
+              htmStepTag, /* Put the <A NAME=...></A> tag at start of math symbol cell */
+              NULL), "", "\"");
+        }
+      }
+      /* Indent web proof displays */
+      let(&tmp, "");
+      for (i = 1; i <= indentationLevel; i++) {
+        let(&tmp, cat(tmp, ". ", NULL));
+      }
+      let(&tmp, cat("<SPAN CLASS=i>",
+          tmp,
+          str((double)(indentationLevel + INDENTATION_OFFSET)), "</SPAN>",
+          NULL));
+      printLongLine(tmp, "", "\"");
+      free_vstring(tmp);
+    } /* strlen(sPrefix) */
+  } /* g_htmlFlag */
+  free_vstring(sPrefix); /* Deallocate */
+
+  free_vstring(tex);
+  tex = getTexLongMath(mathString, hypStmt);
+  let(&texLine, cat(texLine, tex, NULL));
+
+  if (!g_htmlFlag) {  /* LaTeX */
+    if (!g_oldTexFlag) {
+      if (refType == 'e' || refType == 'f') {
+        /* A hypothesis - don't include \ref{} */
+        printLongLine(cat("  ",
+            /* If not first step, so print "\\" LaTeX line break */
+            !strcmp(htmStep, "1") ? "" : "\\\\ ",
+            htmStep,  /* Step number */
+            " && ",
+            " & ",
+            texLine,
+            /* Don't put space to help prevent bad line break */
+            "&\\text{Hyp~",
+            /* The following puts a hypothesis number such as "2" if
+               $e label is "abc.2"; if no ".", will be whole label */
+            right(htmRef, instr(1, htmRef, ".") + 1),
+            "}\\notag%",
+            /* Add full label as LaTeX comment - note lack of space after
+               "%" above to prevent bad line break */
+            htmRef, NULL),
+            "    \\notag \\\\ && & \\qquad ",  /* Continuation line prefix */
+            " ");
+      } else {
+        printLongLine(cat("  ",
+            /* If not first step, so print "\\" LaTeX line break */
+            !strcmp(htmStep, "1") ? "" : "\\\\ ",
+            htmStep,  /* Step number */
+            " && ",
+
+            /* Local label if any e.g. "@2:" */
+            (htmLocLab[0] != 0) ? cat(htmLocLab, "\\ ", NULL) : "",
+
+            " & ",
+            texLine,
+            /* Don't put space to help prevent bad line break */
+
+            /* Surround \ref with \mbox for non-math-mode
+               symbolic labels (due to \tag{..} in mmcmds.c).  Also,
+               move hypotheses to after referenced label */
+            "&",
+            "(",
+
+            /* Don't make local label a \ref */
+            (htmRef[0] != '@') ?
+                cat("\\mbox{\\ref{eq:", htmRef, "}}", NULL)
+                : htmRef,
+
+            htmHyp[0] ? "," : "",
+            htmHyp,
+            ")\\notag", NULL),
+
+            "    \\notag \\\\ && & \\qquad ",  /* Continuation line prefix */
+            " ");
+      }
+    } else {
+      printLongLine(texLine, "", "\\");
+      print2("\\endm\n");
+    }
+  } else {  /* HTML */
+    printLongLine(cat(texLine, "</TD></TR>", NULL), "", "\"");
+  }
+
+  g_outputToString = 0; /* Restore normal output */
+  fprintf(g_texFilePtr, "%s", g_printString);
+  free_vstring(g_printString);
+
+  free_vstring(descr); /*Deallocate */
+  free_vstring(htmStep); /* Deallocate */
+  free_vstring(htmStepTag); /* Deallocate */
+  free_vstring(htmHyp); /* Deallocate */
+  free_vstring(htmRef); /* Deallocate */
+  free_vstring(htmLocLab); /* Deallocate */
+  free_vstring(tmp); /* Deallocate */
+  free_vstring(texLine); /* Deallocate */
+  free_vstring(tex); /* Deallocate */
+} /* printTexLongMath */
+
+void printTexTrailer(flag texTrailerFlag) {
+
+  if (texTrailerFlag) {
+    g_outputToString = 1; /* Redirect print2 and printLongLine to g_printString */
+    if (!g_htmlFlag) let(&g_printString, "");
+        /* May have stuff to be printed */
+    if (!g_htmlFlag) {
+      print2("\\end{document}\n");
+    } else {
+      print2("</TABLE></CENTER>\n");
+      print2("<TABLE BORDER=0 WIDTH=\"100%s\">\n", "%");
+      print2("<TR><TD WIDTH=\"25%s\">&nbsp;</TD>\n", "%");
+      print2("<TD ALIGN=CENTER VALIGN=BOTTOM>\n");
+      print2("<FONT SIZE=-2 FACE=sans-serif>\n");
+      print2("Copyright terms:\n");
+      print2("<A HREF=\"../copyright.html#pd\">Public domain</A>\n");
+      print2("</FONT></TD><TD ALIGN=RIGHT VALIGN=BOTTOM WIDTH=\"25%s\">\n", "%");
+      print2("<FONT SIZE=-2 FACE=sans-serif>\n");
+      print2("<A HREF=\"http://validator.w3.org/check?uri=referer\">\n");
+      print2("W3C validator</A>\n");
+      print2("</FONT></TD></TR></TABLE>\n");
+      print2("</BODY></HTML>\n");
+    }
+    g_outputToString = 0; /* Restore normal output */
+    fprintf(g_texFilePtr, "%s", g_printString);
+    free_vstring(g_printString);
+  }
+
+} /* printTexTrailer */
+
+
+/* WRITE THEOREM_LIST command:  Write out theorem list
+   into mmtheorems.html, mmtheorems1.html,... */
+void writeTheoremList(long theoremsPerPage, flag showLemmas, flag noVersioning)
+{
+  nmbrString_def(nmbrStmtNmbr);
+  long pages, page, assertion, assertions, lastAssertion;
+  long s, p, i1, i2;
+  vstring_def(str1);
+  vstring_def(str3);
+  vstring_def(str4);
+  vstring_def(prevNextLinks);
+  long partCntr;        /* Counter for hugeHdr */
+  long sectionCntr;     /* Counter for bigHdr */
+  long subsectionCntr;  /* Counter for smallHdr */
+  long subsubsectionCntr;  /* Counter for tinyHdr */
+  vstring_def(outputFileName);
+  FILE *outputFilePtr;
+  long passNumber; /* for summary/detailed table of contents */
+
+  /* for table of contents */
+  vstring_def(hugeHdr);
+  vstring_def(bigHdr);
+  vstring_def(smallHdr);
+  vstring_def(tinyHdr);
+  vstring_def(hugeHdrComment);
+  vstring_def(bigHdrComment);
+  vstring_def(smallHdrComment);
+  vstring_def(tinyHdrComment);
+  long stmt, i;
+  pntrString_def(pntrHugeHdr);
+  pntrString_def(pntrBigHdr);
+  pntrString_def(pntrSmallHdr);
+  pntrString_def(pntrTinyHdr);
+  pntrString_def(pntrHugeHdrComment);
+  pntrString_def(pntrBigHdrComment);
+  pntrString_def(pntrSmallHdrComment);
+  pntrString_def(pntrTinyHdrComment);
+  vstring_def(hdrCommentMarker);
+  vstring_def(hdrCommentAnchor);
+  flag hdrCommentAnchorDone = 0;
+
+  /* Populate the statement map */
+  /* ? ? ? Future:  is assertions same as g_Statement[g_statements].pinkNumber? */
+  nmbrLet(&nmbrStmtNmbr, nmbrSpace(g_statements + 1));
+  assertions = 0; /* Number of $p's + $a's */
+  for (s = 1; s <= g_statements; s++) {
+    if (g_Statement[s].type == a_ || g_Statement[s].type == p_) {
+      assertions++; /* Corresponds to pink number */
+      nmbrStmtNmbr[assertions] = s;
+    }
+  }
+  if (assertions != g_Statement[g_statements].pinkNumber) bug(2328);
+
+  /* Table of contents */
+  /* Allocate array for section headers found */
+  pntrLet(&pntrHugeHdr, pntrSpace(g_statements + 1));
+  pntrLet(&pntrBigHdr, pntrSpace(g_statements + 1));
+  pntrLet(&pntrSmallHdr, pntrSpace(g_statements + 1));
+  pntrLet(&pntrTinyHdr, pntrSpace(g_statements + 1));
+  pntrLet(&pntrHugeHdrComment, pntrSpace(g_statements + 1));
+  pntrLet(&pntrBigHdrComment, pntrSpace(g_statements + 1));
+  pntrLet(&pntrSmallHdrComment, pntrSpace(g_statements + 1));
+  pntrLet(&pntrTinyHdrComment, pntrSpace(g_statements + 1));
+
+  pages = ((assertions - 1) / theoremsPerPage) + 1;
+  for (page = 0; page <= pages; page++) {
+    /* Open file */
+    let(&outputFileName,
+        cat("mmtheorems", (page > 0) ? str((double)page) : "", ".html", NULL));
+    print2("Creating %s\n", outputFileName);
+    outputFilePtr = fSafeOpen(outputFileName, "w", noVersioning);
+    if (!outputFilePtr) goto TL_ABORT; /* Couldn't open it (error msg was provided)*/
+
+    /* Output header */
+    /* TODO 14-Jan-2016: why aren't we using printTexHeader? */
+
+    g_outputToString = 1;
+    print2("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n");
+    print2(     "    \"http://www.w3.org/TR/html4/loose.dtd\">\n");
+    print2("<HTML LANG=\"EN-US\">\n");
+    print2("<HEAD>\n");
+    print2("%s%s\n", "<META HTTP-EQUIV=\"Content-Type\" ",
+        "CONTENT=\"text/html; charset=iso-8859-1\">");
+    /* Improve mobile device display per David A. Wheeler */
+    print2("<META NAME=\"viewport\" "
+      "CONTENT=\"width=device-width, initial-scale=1.0\">\n");
+
+    print2("<STYLE TYPE=\"text/css\">\n");
+    print2("<!--\n");
+    /* align math symbol images to text */
+    print2("img { margin-bottom: -4px }\n");
+    /* Print style sheet for colored number that goes after statement label */
+    print2(".r { font-family: \"Arial Narrow\";\n");
+    print2("     font-size: x-small;\n");
+    print2("   }\n");
+    print2("-->\n");
+    print2("</STYLE>\n");
+    printLongLine(g_htmlCSS, "", " ");
+
+    /*
+    print2("%s\n", cat("<TITLE>", htmlTitle, " - ",
+        / * Strip off ".html" * /
+        left(outputFileName, (long)strlen(outputFileName) - 5),
+        "</TITLE>", NULL));
+    */
+    /* Put page name before "Metamath Proof Explorer" etc. */
+    printLongLine(cat("<TITLE>",
+        ((page == 0)
+            ? "TOC of Theorem List"
+            : cat("P. ", str((double)page), " of Theorem List", NULL)),
+
+        " - ",
+        htmlTitle,
+        "</TITLE>",
+        NULL), "", "\"");
+    /* Icon for bookmark */
+    print2("%s%s\n", "<LINK REL=\"shortcut icon\" HREF=\"favicon.ico\" ",
+        "TYPE=\"image/x-icon\">");
+
+    /* Image alignment fix */
+    print2("<STYLE TYPE=\"text/css\">\n");
+    print2("<!--\n");
+    /* Optional information but takes unnecessary file space */
+    /* (change @ to * if uncommenting)
+    print2("/@ Math symbol GIFs will be shifted down 4 pixels to align with\n");
+    print2("   normal text for compatibility with various browsers.  The old\n");
+    print2("   ALIGN=TOP for math symbol images did not align in all browsers\n");
+    print2("   and should be deleted.  All other images must override this\n");
+    print2("   shift with STYLE=\"margin-bottom:0px\". @/\n");
+    */
+    print2("img { margin-bottom: -4px }\n");
+    print2("-->\n");
+    print2("</STYLE>\n");
+
+    print2("</HEAD>\n");
+    print2("<BODY BGCOLOR=\"#FFFFFF\">\n");
+    print2("<TABLE BORDER=0 WIDTH=\"100%s\"><TR>\n", "%");
+    print2("<TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%s\"\n", "%");
+    printLongLine(cat("ROWSPAN=2>", g_htmlHome, "</TD>", NULL), "", "\"");
+    printLongLine(cat(
+        "<TD NOWRAP ALIGN=CENTER ROWSPAN=2><FONT SIZE=\"+3\" COLOR=",
+        GREEN_TITLE_COLOR, "><B>", htmlTitle, "</B></FONT>",
+        "<BR><FONT SIZE=\"+2\" COLOR=",
+        GREEN_TITLE_COLOR,
+        "><B>Theorem List (",
+        ((page == 0)
+            ? "Table of Contents"
+            : cat("p. ", str((double)page), " of ", str((double)pages), NULL)),
+        ")</B></FONT>",
+        NULL), "", "\"");
+
+
+    /* Put Previous/Next links into web page */
+    print2("</TD><TD NOWRAP ALIGN=RIGHT VALIGN=TOP WIDTH=\"25%c\"><FONT\n", '%');
+    print2(" SIZE=-1 FACE=sans-serif>\n");
+
+    /* Output title with current page */
+    /* Output previous and next */
+
+
+    /* Assign prevNextLinks once here since it is used 3 times */
+    let(&prevNextLinks, cat("<A HREF=\"mmtheorems",
+        (page > 0)
+            ? ((page - 1 > 0) ? str((double)page - 1) : "")
+            : ((pages > 0) ? str((double)pages) : ""),
+        ".html\">", NULL));
+    if (page > 0) {
+      let(&prevNextLinks, cat(prevNextLinks,
+          "&lt; Previous</A>&nbsp;&nbsp;", NULL));
+    } else {
+      let(&prevNextLinks, cat(prevNextLinks, "&lt; Wrap</A>&nbsp;&nbsp;", NULL));
+    }
+    let(&prevNextLinks, cat(prevNextLinks, "<A HREF=\"mmtheorems",
+        (page < pages)
+            ? str((double)page + 1)
+            : "",
+        ".html\">", NULL));
+    if (page < pages) {
+      let(&prevNextLinks, cat(prevNextLinks, "Next &gt;</A>", NULL));
+    } else {
+      let(&prevNextLinks, cat(prevNextLinks, "Wrap &gt;</A>", NULL));
+    }
+
+    printLongLine(prevNextLinks,
+        " ",  /* Start continuation line with space */
+        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+    /* Finish up header */
+    /* Print the GIF/Unicode Font choice, if directories are specified */
+    if (htmlDir[0]) {
+      if (g_altHtmlFlag) {
+        print2("</FONT></TD></TR><TR><TD ALIGN=RIGHT><FONT FACE=sans-serif\n");
+        print2("SIZE=-2>Bad symbols? Try the\n");
+        print2("<BR><A HREF=\"%s%s\">GIF\n", htmlDir, outputFileName);
+        print2("version</A>.</FONT></TD>\n");
+      } else {
+        print2("</FONT></TD></TR><TR><TD ALIGN=RIGHT><FONT FACE=sans-serif\n");
+        print2("SIZE=-2>Browser slow? Try the\n");
+        print2("<BR><A HREF=\"%s%s\">Unicode\n", altHtmlDir, outputFileName);
+        print2("version</A>.</FONT></TD>\n");
+      }
+    }
+
+    /* Make breadcrumb font to match other pages */
+    print2("<TR>\n");
+    print2(
+      "<TD COLSPAN=3 ALIGN=LEFT VALIGN=TOP><FONT SIZE=-2 FACE=sans-serif>\n");
+    print2("<BR>\n");  /* Add a little more vertical space */
+
+    /* Print some useful links */
+    print2("<A HREF=\"../mm.html\">Mirrors</A>\n");
+    print2("&nbsp;&gt;&nbsp;<A HREF=\"../index.html\">\n");
+    print2("Metamath Home Page</A>\n");
+
+    /* Normally, g_htmlBibliography in the .mm file will have the
+       project home page, and we depend on this here rather than
+       extracting from g_htmlHome */
+    print2("&nbsp;&gt;&nbsp;<A HREF=\"%s\">\n", g_htmlBibliography);
+
+    /* Put a meaningful abbreviation for the project home page
+       by extracting capital letters from title */
+    let(&str1, "");
+    s = (long)strlen(htmlTitle);
+    for (i = 0; i < s; i++) {
+      if (htmlTitle[i] >= 'A' && htmlTitle[i] <= 'Z') {
+        let(&str1, cat(str1, chr(htmlTitle[i]), NULL));
+      }
+    }
+    print2("%s Home Page</A>\n", str1);
+
+    if (page != 0) {
+      print2("&nbsp;&gt;&nbsp;<A HREF=\"mmtheorems.html\">\n");
+      print2("Theorem List Contents</A>\n");
+    } else {
+      print2("&nbsp;&gt;&nbsp;\n");
+      print2("Theorem List Contents\n");
+    }
+
+    /* Assume there is a Most Recent page when the .mm has a mathbox stmt
+       (currently set.mm and iset.mm)  */
+    if (g_mathboxStmt < g_statements + 1) {
+      print2("&nbsp;&gt;&nbsp;<A HREF=\"mmrecent.html\">\n");
+      print2("Recent Proofs</A>\n");
+    }
+
+
+    print2("&nbsp; &nbsp; &nbsp; <B><FONT COLOR=%s>\n", GREEN_TITLE_COLOR);
+    print2("This page:</FONT></B> \n");
+
+    if (page == 0) {
+      print2("&nbsp;<A HREF=\"#mmdtoc\">Detailed Table of Contents</A>&nbsp;\n");
+    }
+
+    print2("<A HREF=\"#mmpglst\">Page List</A>\n");
+
+    /* Change breadcrumb font to match other pages */
+    print2("</FONT>\n");
+    print2("</TD>\n");
+    print2("</TR></TABLE>\n");
+
+    print2("<HR NOSHADE SIZE=1>\n");
+
+    /* Write out HTML page so far */
+    fprintf(outputFilePtr, "%s", g_printString);
+    g_outputToString = 0;
+    free_vstring(g_printString);
+
+    /* Add table of contents to first WRITE THEOREM page */
+    if (page == 0) {  /* We're on ToC page */
+
+      /* Pass 1: table of contents summary; pass 2: detail */
+      for (passNumber = 1; passNumber <= 2; passNumber++) {
+        g_outputToString = 1;
+        if (passNumber == 1) {
+          print2("<P><CENTER><B>Table of Contents Summary</B></CENTER>\n");
+        } else {
+          print2("<P><CENTER><A NAME=\"mmdtoc\"></A><B>Detailed Table of Contents</B><BR>\n");
+          print2("<B>(* means the section header has a description)</B></CENTER>\n");
+        }
+
+        fprintf(outputFilePtr, "%s", g_printString);
+
+        g_outputToString = 0;
+        free_vstring(g_printString);
+
+        free_vstring(hugeHdr);
+        free_vstring(bigHdr);
+        free_vstring(smallHdr);
+        free_vstring(tinyHdr);
+        free_vstring(hugeHdrComment);
+        free_vstring(bigHdrComment);
+        free_vstring(smallHdrComment);
+        free_vstring(tinyHdrComment);
+        partCntr = 0;    /* Initialize counters */
+        sectionCntr = 0;
+        subsectionCntr = 0;
+        subsubsectionCntr = 0;
+        for (stmt = 1; stmt <= g_statements; stmt++) {
+          /* Output the headers for $a and $p statements */
+          if (g_Statement[stmt].type == p_ || g_Statement[stmt].type == a_) {
+            hdrCommentAnchorDone = 0;
+            getSectionHeadings(stmt, &hugeHdr, &bigHdr, &smallHdr,
+                &tinyHdr,
+                &hugeHdrComment, &bigHdrComment, &smallHdrComment,
+                &tinyHdrComment,
+                0, /* fineResolution */
+                0  /* fullComment */);
+            if (hugeHdr[0] || bigHdr[0] || smallHdr[0] || tinyHdr[0]) {
+              /* Write to the table of contents */
+              g_outputToString = 1;
+              i = ((g_Statement[stmt].pinkNumber - 1) / theoremsPerPage)
+                  + 1; /* Page # */
+              /* let(&str3, cat("mmtheorems", (i == 1) ? "" : str(i), ".html#", */
+                          /* Note that page 1 has no number after mmtheorems */
+              let(&str3, cat("mmtheorems", str((double)i), ".html#",
+                  /* g_Statement[stmt].labelName, NULL)); */
+                  "mm", str((double)(g_Statement[stmt].pinkNumber)), NULL));
+                     /* Link to page/location - no theorem can be named "mm*" */
+              free_vstring(str4);
+              str4 = pinkHTML(stmt);
+              if (hugeHdr[0]) {
+
+                /* Create part number */
+                partCntr++;
+                sectionCntr = 0;
+                subsectionCntr = 0;
+                subsubsectionCntr = 0;
+                let(&hugeHdr, cat("PART ", str((double)partCntr), "&nbsp;&nbsp;",
+                    hugeHdr, NULL));
+
+                /* Put an asterisk before the header if the header has a comment */
+                if (hugeHdrComment[0] != 0 && passNumber == 2) {
+                  let(&hdrCommentMarker, "*");
+                  if (hdrCommentAnchorDone == 0) {
+                    let(&hdrCommentAnchor, cat(
+                        "<A NAME=\"",
+                        g_Statement[stmt].labelName, "\"></A>",
+
+                        /* "&#8203;" is a "zero-width" space to
+                           workaround Chrome bug that jumps to wrong anchor. */
+                        "&#8203;",
+
+                        NULL));
+                    hdrCommentAnchorDone = 1;
+                  } else {
+                    let(&hdrCommentAnchor, "");
+                  }
+                } else {
+                  let(&hdrCommentMarker, "");
+                  let(&hdrCommentAnchor, "");
+                }
+
+                printLongLine(cat(
+                    /* In detailed section, add an anchor to reach it from
+                       summary section */
+                    (passNumber == 2) ?
+                        cat("<A NAME=\"dtl:", str((double)partCntr),
+                            "\"></A>",
+                            /* "&#8203;" is a "zero-width" space to
+                               workaround Chrome bug that jumps to wrong anchor. */
+                            "&#8203;",
+                            NULL) : "",
+
+                    /* Add an anchor to the "sandbox" theorem
+                       for use by mmrecent.html */
+                    /* We use "sandbox:bighdr" for both here and
+                       below so that either huge or big header type could
+                       be used to start mathbox sections */
+                    (stmt == g_mathboxStmt && bigHdr[0] == 0
+                          && passNumber == 1 /* Only in summary TOC */
+                          ) ?
+                        /* Note the colon so it won't conflict w/ theorem
+                           name anchor */
+                        /* "&#8203;" is a "zero-width" space to
+                           workaround Chrome bug that jumps to wrong anchor. */
+                        "<A NAME=\"sandbox:bighdr\"></A>&#8203;" : "",
+
+                    hdrCommentAnchor,
+                    "<A HREF=\"",
+
+                    (passNumber == 1) ?
+                        cat("#dtl:", str((double)partCntr), NULL)  /* Link to detailed toc */
+                        : cat(str3, "h", NULL), /* Link to thm list */
+
+                    "\"><B>",
+                    hdrCommentMarker,
+                    hugeHdr, "</B></A>",
+                    "<BR>", NULL),
+                    " ",  /* Start continuation line with space */
+                    "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+                if (passNumber == 2) {
+                  /* Assign to array for use during theorem output */
+                  let((vstring *)(&pntrHugeHdr[stmt]), hugeHdr);
+                  let((vstring *)(&pntrHugeHdrComment[stmt]), hugeHdrComment);
+                }
+                free_vstring(hugeHdr);
+                free_vstring(hugeHdrComment);
+              }
+              if (bigHdr[0]) {
+
+                /* Create section number */
+                sectionCntr++;
+                subsectionCntr = 0;
+                subsubsectionCntr = 0;
+                let(&bigHdr, cat(str((double)partCntr), ".", str((double)sectionCntr),
+                    "&nbsp;&nbsp;",
+                    bigHdr, NULL));
+
+                /* Put an asterisk before the header if the header has
+                   a comment */
+                if (bigHdrComment[0] != 0 && passNumber == 2) {
+                  let(&hdrCommentMarker, "*");
+                  if (hdrCommentAnchorDone == 0) {
+                    let(&hdrCommentAnchor, cat(
+                        "<A NAME=\"",
+                        g_Statement[stmt].labelName, "\"></A>",
+
+                        /* "&#8203;" is a "zero-width" space to
+                           workaround Chrome bug that jumps to wrong anchor. */
+                        "&#8203;",
+
+                        NULL));
+                    hdrCommentAnchorDone = 1;
+                  } else {
+                    let(&hdrCommentAnchor, "");
+                  }
+                } else {
+                  let(&hdrCommentMarker, "");
+                  let(&hdrCommentAnchor, "");
+                }
+
+                printLongLine(cat(
+                    "&nbsp; &nbsp; &nbsp; ", /* Indentation spacing */
+
+                    /* In detailed section, add an anchor to reach it from
+                       summary section */
+                    (passNumber == 2) ?
+                        cat("<A NAME=\"dtl:", str((double)partCntr), ".",
+                            str((double)sectionCntr), "\"></A>",
+
+                            /* "&#8203;" is a "zero-width" space to
+                               workaround Chrome bug that jumps to wrong anchor. */
+                            "&#8203;",
+
+                            NULL)
+                        : "",
+
+                    /* Add an anchor to the "sandbox" theorem
+                       for use by mmrecent.html */
+                    (stmt == g_mathboxStmt
+                          && passNumber == 1 /* Only in summary TOC */
+                          ) ?
+                        /* Note the colon so it won't conflict w/ theorem
+                           name anchor */
+                        /* "&#8203;" is a "zero-width" space to
+                           workaround Chrome bug that jumps to wrong anchor. */
+                        "<A NAME=\"sandbox:bighdr\"></A>&#8203;" : "",
+                    hdrCommentAnchor,
+                    "<A HREF=\"",
+
+                    (passNumber == 1) ?
+                         cat("#dtl:", str((double)partCntr), ".",
+                             str((double)sectionCntr),
+                             NULL)   /* Link to detailed toc */
+                        : cat(str3, "b", NULL), /* Link to thm list */
+
+                    "\"><B>",
+                    hdrCommentMarker,
+                    bigHdr, "</B></A>",
+                    "<BR>", NULL),
+                    " ",  /* Start continuation line with space */
+                    "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+                if (passNumber == 2) {
+                  /* Assign to array for use during theorem list output */
+                  let((vstring *)(&pntrBigHdr[stmt]), bigHdr);
+                  let((vstring *)(&pntrBigHdrComment[stmt]), bigHdrComment);
+                }
+                free_vstring(bigHdr);
+                free_vstring(bigHdrComment);
+              }
+              if (smallHdr[0]
+                  && passNumber == 2) {  /* Skip in pass 1 (summary) */
+
+                /* Create subsection number */
+                subsectionCntr++;
+                subsubsectionCntr = 0;
+                let(&smallHdr, cat(str((double)partCntr), ".",
+                    str((double)sectionCntr),
+                    ".", str((double)subsectionCntr), "&nbsp;&nbsp;",
+                    smallHdr, NULL));
+
+                /* Put an asterisk before the header if the header has
+                   a comment */
+                if (smallHdrComment[0] != 0 && passNumber == 2) {
+                  let(&hdrCommentMarker, "*");
+                  if (hdrCommentAnchorDone == 0) {
+                    let(&hdrCommentAnchor, cat("<A NAME=\"",
+                        g_Statement[stmt].labelName, "\"></A>",
+
+                        /* "&#8203;" is a "zero-width" space to
+                           workaround Chrome bug that jumps to wrong anchor. */
+                        "&#8203;",
+
+                        NULL));
+                    hdrCommentAnchorDone = 1;
+                  } else {
+                    let(&hdrCommentAnchor, "");
+                  }
+                } else {
+                  let(&hdrCommentMarker, "");
+                  let(&hdrCommentAnchor, "");
+                }
+
+                printLongLine(cat("&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ",
+                    hdrCommentAnchor,
+                    "<A HREF=\"", str3, "s\">",
+                    hdrCommentMarker,
+                    smallHdr, "</A>",
+                    " &nbsp; <A HREF=\"",
+                    g_Statement[stmt].labelName, ".html\">",
+                    g_Statement[stmt].labelName, "</A>",
+                    str4,
+                    "<BR>", NULL),
+                    " ",  /* Start continuation line with space */
+                    "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+                /* Assign to array for use during theorem output */
+                let((vstring *)(&pntrSmallHdr[stmt]), smallHdr);
+                free_vstring(smallHdr);
+                let((vstring *)(&pntrSmallHdrComment[stmt]), smallHdrComment);
+                free_vstring(smallHdrComment);
+              }
+
+              if (tinyHdr[0] && passNumber == 2) {  /* Skip in pass 1 (summary) */
+
+                /* Create subsection number */
+                subsubsectionCntr++;
+                let(&tinyHdr, cat(str((double)partCntr), ".",
+                    str((double)sectionCntr),
+                    ".", str((double)subsectionCntr),
+                    ".", str((double)subsubsectionCntr), "&nbsp;&nbsp;",
+                    tinyHdr, NULL));
+
+                /* Put an asterisk before the header if the header has
+                   a comment */
+                if (tinyHdrComment[0] != 0 && passNumber == 2) {
+                  let(&hdrCommentMarker, "*");
+                  if (hdrCommentAnchorDone == 0) {
+                    let(&hdrCommentAnchor, cat("<A NAME=\"",
+                        g_Statement[stmt].labelName, "\"></A> ",
+
+                        /* "&#8203;" is a "zero-width" space to
+                           workaround Chrome bug that jumps to wrong anchor. */
+                        "&#8203;",
+
+                        NULL));
+                    hdrCommentAnchorDone = 1;
+                  } else {
+                    let(&hdrCommentAnchor, "");
+                  }
+                } else {
+                  let(&hdrCommentMarker, "");
+                  let(&hdrCommentAnchor, "");
+                }
+
+                printLongLine(cat(
+                    "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ",
+                    hdrCommentAnchor,
+                    "<A HREF=\"", str3, "s\">",
+                    hdrCommentMarker,
+                    tinyHdr, "</A>",
+                    " &nbsp; <A HREF=\"",
+                    g_Statement[stmt].labelName, ".html\">",
+                    g_Statement[stmt].labelName, "</A>",
+                    str4,
+                    "<BR>", NULL),
+                    " ",  /* Start continuation line with space */
+                    "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+                /* Assign to array for use during theorem output */
+                let((vstring *)(&pntrTinyHdr[stmt]), tinyHdr);
+                free_vstring(tinyHdr);
+                let((vstring *)(&pntrTinyHdrComment[stmt]), tinyHdrComment);
+                free_vstring(tinyHdrComment);
+              }
+
+              fprintf(outputFilePtr, "%s", g_printString);
+              g_outputToString = 0;
+              free_vstring(g_printString);
+            } /* if huge or big or small or tiny header */
+          } /* if $a or $p */
+        } /* next stmt */
+        /* 8-May-2015 nm Do we need the HR below? */
+        fprintf(outputFilePtr, "<HR NOSHADE SIZE=1>\n");
+
+      } /* next passNumber */
+
+    } /* if page 0 */
+    /* End table of contents */
+
+    /* Just skip over instead of a big if indent */
+    if (page == 0) goto SKIP_LIST;
+
+
+    /* Put in color key */
+    g_outputToString = 1;
+    print2("<A NAME=\"mmstmtlst\"></A>\n");
+    if (g_extHtmlStmt < g_mathboxStmt) { /* g_extHtmlStmt >= g_mathboxStmt in ql.mm */
+      /* ?? Currently this is customized for set.mm only!! */
+      print2("<P>\n");
+      print2("<CENTER><TABLE CELLSPACING=0 CELLPADDING=5\n");
+      print2("SUMMARY=\"Color key\"><TR>\n");
+      print2("\n");
+      print2("<TD>Color key:&nbsp;&nbsp;&nbsp;</TD>\n");
+      print2("<TD BGCOLOR=%s NOWRAP><A\n", MINT_BACKGROUND_COLOR);
+      print2("HREF=\"mmset.html\"><IMG SRC=\"mm.gif\" BORDER=0\n");
+      print2("ALT=\"Metamath Proof Explorer\" HEIGHT=32 WIDTH=32\n");
+      print2("ALIGN=MIDDLE> &nbsp;Metamath Proof Explorer</A>\n");
+
+      free_vstring(str3);
+      if (g_Statement[g_extHtmlStmt].pinkNumber <= 0) bug(2332);
+      str3 = pinkRangeHTML(nmbrStmtNmbr[1],
+          nmbrStmtNmbr[g_Statement[g_extHtmlStmt].pinkNumber - 1]);
+      printLongLine(cat("<BR>(", str3, ")", NULL),
+        " ",  /* Start continuation line with space */
+        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+      print2("</TD>\n");
+      print2("\n");
+      print2("<TD WIDTH=10>&nbsp;</TD>\n");
+      print2("\n");
+
+      /* Hilbert Space Explorer */
+      print2("<TD BGCOLOR=%s NOWRAP><A\n", PURPLISH_BIBLIO_COLOR);
+      print2(" HREF=\"mmhil.html\"><IMG SRC=\"atomic.gif\"\n");
+      print2(
+ "BORDER=0 ALT=\"Hilbert Space Explorer\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE>\n");
+      print2("&nbsp;Hilbert Space Explorer</A>\n");
+
+      free_vstring(str3);
+      if (g_Statement[g_mathboxStmt].pinkNumber <= 0) bug(2333);
+      str3 = pinkRangeHTML(g_extHtmlStmt,
+         nmbrStmtNmbr[g_Statement[g_mathboxStmt].pinkNumber - 1]);
+      printLongLine(cat("<BR>(", str3, ")", NULL),
+        " ",  /* Start continuation line with space */
+        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+      print2("</TD>\n");
+      print2("\n");
+      print2("<TD WIDTH=10>&nbsp;</TD>\n");
+      print2("\n");
+
+
+      /* Mathbox stuff */
+      print2("<TD BGCOLOR=%s NOWRAP><A\n", SANDBOX_COLOR);
+      print2(" HREF=\"mathbox.html\"><IMG SRC=\"_sandbox.gif\"\n");
+      print2("BORDER=0 ALT=\"Users' Mathboxes\" HEIGHT=32 WIDTH=32 ALIGN=MIDDLE>\n");
+      print2("&nbsp;Users' Mathboxes</A>\n");
+
+      free_vstring(str3);
+      str3 = pinkRangeHTML(g_mathboxStmt, nmbrStmtNmbr[assertions]);
+      printLongLine(cat("<BR>(", str3, ")", NULL),
+        " ",  /* Start continuation line with space */
+        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+      print2("</TD>\n");
+      print2("\n");
+      print2("<TD WIDTH=10>&nbsp;</TD>\n");
+      print2("\n");
+
+
+      print2("</TR></TABLE></CENTER>\n");
+    } /* end if (g_extHtmlStmt < g_mathboxStmt) */
+
+    /* Write out HTML page so far */
+    fprintf(outputFilePtr, "%s", g_printString);
+    g_outputToString = 0;
+    let(&g_printString, "");
+
+
+    /* Write the main table header */
+    g_outputToString = 1;
+    print2("\n");
+    print2("<P><CENTER>\n");
+    print2("<TABLE BORDER CELLSPACING=0 CELLPADDING=3 BGCOLOR=%s\n",
+        MINT_BACKGROUND_COLOR);
+    print2("SUMMARY=\"Theorem List for %s\">\n", htmlTitle);
+    free_vstring(str3);
+    if (page < 1) bug(2335); /* Page 0 ToC should have been skipped */
+    str3 = pinkHTML(nmbrStmtNmbr[(page - 1) * theoremsPerPage + 1]);
+    let(&str3, right(str3, (long)strlen(PINK_NBSP) + 1)); /* Discard "&nbsp;" */
+    free_vstring(str4);
+    str4 = pinkHTML((page < pages) ?
+        nmbrStmtNmbr[page * theoremsPerPage] :
+        nmbrStmtNmbr[assertions]);
+    let(&str4, right(str4, (long)strlen(PINK_NBSP) + 1)); /* Discard "&nbsp;" */
+    printLongLine(cat("<CAPTION><B>Theorem List for ", htmlTitle,
+        " - </B>", str3, "<B>-</B>", str4,
+        " &nbsp; *Has distinct variable group(s)"
+        "</CAPTION>",NULL),
+        " ",  /* Start continuation line with space */
+        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+    print2("\n");
+    print2("<TR><TH>Type</TH><TH>Label</TH><TH>Description</TH></TR>\n");
+    print2("<TR><TH COLSPAN=3>Statement</TH></TR>\n");
+    print2("\n");
+    print2("<TR BGCOLOR=white><TD COLSPAN=3><FONT SIZE=-3>&nbsp;</FONT></TD></TR>\n");
+    print2("\n");
+    fprintf(outputFilePtr, "%s", g_printString);
+    g_outputToString = 0;
+    free_vstring(g_printString);
+
+    /* Find the last assertion that will be printed on the page, so
+       we will know when a separator between theorems is not needed */
+    lastAssertion = 0;
+    for (assertion = (page - 1) * theoremsPerPage + 1;
+        assertion <= page * theoremsPerPage; assertion++) {
+      if (assertion > assertions) break; /* We're beyond the end */
+
+      lastAssertion = assertion;
+    }
+
+    /* Output theorems on the page */
+    for (assertion = (page - 1) * theoremsPerPage + 1;
+        assertion <= page * theoremsPerPage; assertion++) {
+      if (assertion > assertions) break; /* We're beyond the end */
+
+      s = nmbrStmtNmbr[assertion]; /* Statement number */
+
+      /* Construct the statement type label */
+      if (g_Statement[s].type == p_) {
+        let(&str1, "Theorem");
+      } else if (!strcmp("ax-", left(g_Statement[s].labelName, 3))) {
+        let(&str1, "<B><FONT COLOR=red>Axiom</FONT></B>");
+      } else if (!strcmp("df-", left(g_Statement[s].labelName, 3))) {
+        let(&str1, "<B><FONT COLOR=blue>Definition</FONT></B>");
+      } else {
+        let(&str1, "<B><FONT COLOR=\"#00CC00\">Syntax</FONT></B>");
+      }
+
+      if (s == s + 0) goto skip_date;
+      /* OBSOLETE */
+      /* Get the date in the comment section after the statement */
+      let(&str1, space(g_Statement[s + 1].labelSectionLen));
+      memcpy(str1, g_Statement[s + 1].labelSectionPtr,
+          (size_t)(g_Statement[s + 1].labelSectionLen));
+      let(&str1, edit(str1, 2)); /* Discard spaces and tabs */
+      i1 = instr(1, str1, "$([");
+      i2 = instr(i1, str1, "]$)");
+      if (i1 && i2) {
+        let(&str1, seg(str1, i1 + 3, i2 - 1));
+      } else {
+        let(&str1, "");
+      }
+     skip_date:
+
+      free_vstring(str3);
+      str3 = getDescription(s);
+      free_vstring(str4);
+      str4 = pinkHTML(s); /* Get little pink number */
+      /* Output the description comment */
+      /* Break up long lines for text editors with printLongLine */
+      free_vstring(g_printString);
+      g_outputToString = 1;
+      print2("\n"); /* Blank line for HTML source human readability */
+
+      /* Table of contents */
+      if (((vstring)(pntrHugeHdr[s]))[0]) { /* There is a major part break */
+        printLongLine(cat(
+                 /* The header */
+                 "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3",
+                 "><CENTER><FONT SIZE=\"+1\"><B>",
+                 "<A NAME=\"mm", str((double)(g_Statement[s].pinkNumber)),
+                     "h\"></A>",   /* Anchor for table of contents */
+                 (vstring)(pntrHugeHdr[s]),
+                 "</B></FONT></CENTER>",
+                 NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+        /* The comment part of the header, if any */
+        if (((vstring)(pntrHugeHdrComment[s]))[0]) {
+
+          /* Open the table row */
+          /* keep comment in same table cell */
+          print2("%s\n", "<P STYLE=\"margin-bottom:0em\">");
+
+          /* We are currently printing to g_printString to allow use of
+             printLongLine(); however, the rendering function
+             printTexComment uses g_printString internally, so we have to
+             flush the current g_printString and turn off g_outputToString mode
+             in order to call the rendering function printTexComment. */
+          /* (Question:  why do the calls to printTexComment for statement
+             descriptions, later, not need to flush the g_printString?  Is the
+             flushing code here redundant?) */
+          /* Clear out the g_printString output in prep for printTexComment */
+          g_outputToString = 0;
+          fprintf(outputFilePtr, "%s", g_printString);
+          let(&g_printString, "");
+          g_showStatement = s; /* For printTexComment */
+          g_texFilePtr = outputFilePtr; /* For printTexComment */
+          printTexComment(  /* Sends result to g_texFilePtr */
+              (vstring)(pntrHugeHdrComment[s]),
+              0, /* 1 = htmlCenterFlag */
+              PROCESS_EVERYTHING, /* actionBits */
+              1 /* 1 = fileCheck */);
+          g_texFilePtr = NULL;
+          g_outputToString = 1; /* Restore after printTexComment */
+        }
+
+        /* Close the table row */
+        print2("%s\n", "</TD></TR>");
+
+        printLongLine(cat(
+                 /* Separator row */
+                 "<TR BGCOLOR=white><TD COLSPAN=3>",
+                 "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>",
+                 NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+      }
+      if (((vstring)(pntrBigHdr[s]))[0]) { /* There is a major section break */
+        printLongLine(cat(
+                 /* The header */
+                 "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3",
+                 "><CENTER><FONT SIZE=\"+1\"><B>",
+                 "<A NAME=\"mm", str((double)(g_Statement[s].pinkNumber)),
+                     "b\"></A>",      /* Anchor for table of contents */
+                 (vstring)(pntrBigHdr[s]),
+                 "</B></FONT></CENTER>",
+                 NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+        /* The comment part of the header, if any */
+        if (((vstring)(pntrBigHdrComment[s]))[0]) {
+
+          /* Open the table row */
+          /* keep comment in same table cell */
+          print2("%s\n", "<P STYLE=\"margin-bottom:0em\">");
+
+          /* We are currently printing to g_printString to allow use of
+             printLongLine(); however, the rendering function
+             printTexComment uses g_printString internally, so we have to
+             flush the current g_printString and turn off g_outputToString mode
+             in order to call the rendering function printTexComment. */
+          /* (Question:  why do the calls to printTexComment for statement
+             descriptions, later, not need to flush the g_printString?  Is the
+             flushing code here redundant?) */
+          /* Clear out the g_printString output in prep for printTexComment */
+          g_outputToString = 0;
+          fprintf(outputFilePtr, "%s", g_printString);
+          free_vstring(g_printString);
+          g_showStatement = s; /* For printTexComment */
+          g_texFilePtr = outputFilePtr; /* For printTexComment */
+          printTexComment(  /* Sends result to g_texFilePtr */
+              (vstring)(pntrBigHdrComment[s]),
+              0, /* 1 = htmlCenterFlag */
+              PROCESS_EVERYTHING, /* actionBits */
+              1  /* 1 = fileCheck */);
+          g_texFilePtr = NULL;
+          g_outputToString = 1; /* Restore after printTexComment */
+        }
+
+        /* Close the table row */
+        print2("%s\n", "</TD></TR>");
+
+        printLongLine(cat(
+                 /* Separator row */
+                 "<TR BGCOLOR=white><TD COLSPAN=3>",
+                 "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>",
+                 NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+      }
+      if (((vstring)(pntrSmallHdr[s]))[0]) { /* There is a minor sec break */
+        printLongLine(cat(
+                 /* The header */
+                 "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3",
+                 "><CENTER><B>",
+                 "<A NAME=\"mm", str((double)(g_Statement[s].pinkNumber)),
+                     "s\"></A>",    /* Anchor for table of contents */
+                 (vstring)(pntrSmallHdr[s]),
+                 "</B></CENTER>",
+                 NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+        /* The comment part of the header, if any */
+        if (((vstring)(pntrSmallHdrComment[s]))[0]) {
+
+          /* Open the table row */
+          /* keep comment in same table cell */
+          print2("%s\n", "<P STYLE=\"margin-bottom:0em\">");
+
+          /* We are currently printing to g_printString to allow use of
+             printLongLine(); however, the rendering function
+             printTexComment uses g_printString internally, so we have to
+             flush the current g_printString and turn off g_outputToString mode
+             in order to call the rendering function printTexComment. */
+          /* (Question:  why do the calls to printTexComment for statement
+             descriptions, later, not need to flush the g_printString?  Is the
+             flushing code here redundant?) */
+          /* Clear out the g_printString output in prep for printTexComment */
+          g_outputToString = 0;
+          fprintf(outputFilePtr, "%s", g_printString);
+          free_vstring(g_printString);
+          g_showStatement = s; /* For printTexComment */
+          g_texFilePtr = outputFilePtr; /* For printTexComment */
+          printTexComment(  /* Sends result to g_texFilePtr */
+              (vstring)(pntrSmallHdrComment[s]),
+              0, /* 1 = htmlCenterFlag */
+              PROCESS_EVERYTHING, /* actionBits */
+              1  /* 1 = fileCheck */);
+          g_texFilePtr = NULL;
+          g_outputToString = 1; /* Restore after printTexComment */
+        }
+
+        /* Close the table row */
+        print2("%s\n", "</TD></TR>");
+
+        printLongLine(cat(
+                 /* Separator row */
+                 "<TR BGCOLOR=white><TD COLSPAN=3>",
+                 "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>",
+                 NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+      }
+
+      if (((vstring)(pntrTinyHdr[s]))[0]) { /* There is a subsubsection break */
+        printLongLine(cat(
+                 /* The header */
+                 "<TR BGCOLOR=\"#FFFFF2\"><TD COLSPAN=3",
+                 "><CENTER><B>",
+                 "<A NAME=\"mm", str((double)(g_Statement[s].pinkNumber)),
+                     "s\"></A>",    /* Anchor for table of contents */
+                 (vstring)(pntrTinyHdr[s]),
+                 "</B></CENTER>",
+                 NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+        /* The comment part of the header, if any */
+        if (((vstring)(pntrTinyHdrComment[s]))[0]) {
+
+          /* Open the table row */
+          /* keep comment in same table cell */
+          print2("%s\n", "<P STYLE=\"margin-bottom:0em\">");
+
+          /* We are currently printing to g_printString to allow use of
+             printLongLine(); however, the rendering function
+             printTexComment uses g_printString internally, so we have to
+             flush the current g_printString and turn off g_outputToString mode
+             in order to call the rendering function printTexComment. */
+          /* (Question:  why do the calls to printTexComment for statement
+             descriptions, later, not need to flush the g_printString?  Is the
+             flushing code here redundant?) */
+          /* Clear out the g_printString output in prep for printTexComment */
+          g_outputToString = 0;
+          fprintf(outputFilePtr, "%s", g_printString);
+          free_vstring(g_printString);
+          g_showStatement = s; /* For printTexComment */
+          g_texFilePtr = outputFilePtr; /* For printTexComment */
+          printTexComment(  /* Sends result to g_texFilePtr */
+              (vstring)(pntrTinyHdrComment[s]),
+              0, /* 1 = htmlCenterFlag */
+              PROCESS_EVERYTHING, /* actionBits */
+              1  /* 1 = fileCheck */);
+          g_texFilePtr = NULL;
+          g_outputToString = 1; /* Restore after printTexComment */
+        }
+
+        /* Close the table row */
+        print2("%s\n", "</TD></TR>");
+
+        printLongLine(cat(
+                 /* Separator row */
+                 "<TR BGCOLOR=white><TD COLSPAN=3>",
+                 "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>",
+                 NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+      }
+
+      printLongLine(cat(
+            (s < g_extHtmlStmt)
+               ? "<TR>"
+               : (s < g_mathboxStmt)
+                   ? cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL)
+                   : cat("<TR BGCOLOR=", SANDBOX_COLOR, ">", NULL),
+            "<TD NOWRAP>",  /* IE breaks up the date */
+            str1, /* Date */
+            "</TD><TD ALIGN=CENTER><A HREF=\"",
+            g_Statement[s].labelName, ".html\">",
+            g_Statement[s].labelName, "</A>",
+            str4,
+
+            /* Add asterisk if statement has distinct var groups */
+            (nmbrLen(g_Statement[s].reqDisjVarsA) > 0) ? "*" : "",
+
+            "</TD><TD ALIGN=LEFT>",
+            /* Add anchor for hyperlinking to the table row */
+            "<A NAME=\"", g_Statement[s].labelName, "\"></A>",
+
+            NULL),  /* Description */
+          " ",  /* Start continuation line with space */
+          "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+
+      g_showStatement = s; /* For printTexComment */
+      g_outputToString = 0; /* For printTexComment */
+      g_texFilePtr = outputFilePtr; /* For printTexComment */
+      printTexComment(  /* Sends result to g_texFilePtr */
+          str3,
+          0, /* 1 = htmlCenterFlag */
+          PROCESS_EVERYTHING, /* actionBits */
+          1  /* 1 = fileCheck */);
+      g_texFilePtr = NULL;
+      g_outputToString = 1; /* Restore after printTexComment */
+
+      /* Get HTML hypotheses => assertion */
+      free_vstring(str4);
+      str4 = getTexOrHtmlHypAndAssertion(s); /* In mmwtex.c */
+
+      /* Suppress the math content of lemmas, which can
+         be very big and not interesting */
+      if (!strcmp(left(str3, 10), "Lemma for ") && !showLemmas) {
+        /* Suppress the table row with the math content */
+        print2(" <I>[Auxiliary lemma - not displayed.]</I></TD></TR>\n");
+      } else {
+        /* Output the table row with the math content */
+        printLongLine(cat("</TD></TR><TR",
+              (s < g_extHtmlStmt)
+                ? ">"
+                : (s < g_mathboxStmt)
+                  ? cat(" BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL)
+                  : cat(" BGCOLOR=", SANDBOX_COLOR, ">", NULL),
+              "<TD COLSPAN=3 ALIGN=CENTER>",
+              str4, "</TD></TR>", NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+      }
+
+      g_outputToString = 0;
+      fprintf(outputFilePtr, "%s", g_printString);
+      free_vstring(g_printString);
+
+      if (assertion != lastAssertion) {
+        /* Put separator row if not last theorem */
+        g_outputToString = 1;
+        printLongLine(cat("<TR BGCOLOR=white><TD COLSPAN=3>",
+            "<FONT SIZE=-3>&nbsp;</FONT></TD></TR>", NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+        g_outputToString = 0;
+        fprintf(outputFilePtr, "%s", g_printString);
+        free_vstring(g_printString);
+      }
+    } /* next assertion */
+
+    /* Output trailer */
+    g_outputToString = 1;
+    print2("</TABLE></CENTER>\n");
+    print2("\n");
+
+   SKIP_LIST: /* (skipped when page == 0) */
+    g_outputToString = 1; /* To compensate for skipped assignment above */
+
+
+    /* Put extra Prev/Next hyperlinks here for convenience */
+    print2("<TABLE BORDER=0 WIDTH=\"100%c\">\n", '%');
+    print2("  <TR>\n");
+    print2("    <TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%c\">\n", '%');
+    print2("      &nbsp;\n");
+    print2("    </TD>\n");
+    print2("    <TD NOWRAP ALIGN=CENTER>&nbsp;</TD>\n");
+    print2("    <TD NOWRAP ALIGN=RIGHT VALIGN=TOP WIDTH=\"25%c\"><FONT\n", '%');
+    print2("      SIZE=-1 FACE=sans-serif>\n");
+    printLongLine(cat("      ", prevNextLinks, NULL),
+        " ",  /* Start continuation line with space */
+        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+    print2("      </FONT></TD>\n");
+    print2("  </TR>\n");
+    print2("</TABLE>\n");
+
+
+    print2("<HR NOSHADE SIZE=1>\n");
+
+
+    g_outputToString = 0;
+    fprintf(outputFilePtr, "%s", g_printString);
+    free_vstring(g_printString);
+
+
+    fprintf(outputFilePtr, "<A NAME=\"mmpglst\"></A>\n");
+    fprintf(outputFilePtr, "<CENTER>\n");
+    fprintf(outputFilePtr, "<B>Page List</B>\n");
+    fprintf(outputFilePtr, "</CENTER>\n");
+
+
+    /* Output links to the other pages */
+    fprintf(outputFilePtr, "Jump to page: \n");
+    for (p = 0; p <= pages; p++) {
+
+      /* Construct the pink number range */
+      free_vstring(str3);
+      if (p > 0) {
+        str3 = pinkRangeHTML(
+            nmbrStmtNmbr[(p - 1) * theoremsPerPage + 1],
+            (p < pages) ?
+              nmbrStmtNmbr[p * theoremsPerPage] :
+              nmbrStmtNmbr[assertions]);
+      }
+
+      if (p == page) {
+        /* Current page shouldn't have link to self */
+        let(&str1, (p == 0) ? "Contents" : str((double)p));
+      } else {
+        let(&str1, cat("<A HREF=\"mmtheorems",
+            (p == 0) ? "" : str((double)p),
+
+            /* Friendlier, because you can start
+              scrolling through the page before it finishes loading,
+              without its jumping to #mmtc (start of Table of Contents)
+              when it's done. */
+            /* (p == 1) ? ".html#mmtc\">" : ".html\">", */
+            ".html\">",
+
+            (p == 0) ? "Contents" : str((double)p),
+            "</A>", NULL));
+      }
+      let(&str1, cat(str1, PINK_NBSP, str3, NULL));
+      fprintf(outputFilePtr, "%s\n", str1);
+    }
+
+
+    g_outputToString = 1;
+    print2("<HR NOSHADE SIZE=1>\n");
+    print2("<TABLE BORDER=0 WIDTH=\"100%c\">\n", '%');
+    print2("  <TR>\n");
+    print2("    <TD ALIGN=LEFT VALIGN=TOP WIDTH=\"25%c\">\n", '%');
+    print2("      &nbsp;\n");
+    print2("    </TD>\n");
+    print2("    <TD NOWRAP ALIGN=CENTER><FONT SIZE=-2\n");
+    print2("      FACE=ARIAL>\n");
+    print2("Copyright terms:\n");
+    print2("<A HREF=\"../copyright.html#pd\">Public domain</A>\n");
+    print2("      </FONT></TD>\n");
+    print2("    <TD NOWRAP ALIGN=RIGHT VALIGN=TOP WIDTH=\"25%c\"><FONT\n", '%');
+    print2("      SIZE=-1 FACE=sans-serif>\n");
+    printLongLine(cat("      ", prevNextLinks, NULL),
+        " ",  /* Start continuation line with space */
+        "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+    print2("      </FONT></TD>\n");
+    print2("  </TR>\n");
+    print2("</TABLE>\n");
+    print2("</BODY></HTML>\n");
+    g_outputToString = 0;
+    fprintf(outputFilePtr, "%s", g_printString);
+    free_vstring(g_printString);
+
+    /* Close file */
+    fclose(outputFilePtr);
+  } /* next page */
+
+ TL_ABORT:
+  /* Deallocate memory */
+  free_vstring(str1);
+  free_vstring(str3);
+  free_vstring(str4);
+  free_vstring(prevNextLinks);
+  free_vstring(outputFileName);
+  free_vstring(hugeHdr);
+  free_vstring(bigHdr);
+  free_vstring(smallHdr);
+  free_vstring(tinyHdr);
+  free_vstring(hdrCommentMarker);
+  for (i = 0; i <= g_statements; i++) free_vstring(*(vstring *)(&pntrHugeHdr[i]));
+  free_pntrString(pntrHugeHdr);
+  for (i = 0; i <= g_statements; i++) free_vstring(*(vstring *)(&pntrBigHdr[i]));
+  free_pntrString(pntrBigHdr);
+  for (i = 0; i <= g_statements; i++) free_vstring(*(vstring *)(&pntrSmallHdr[i]));
+  free_pntrString(pntrSmallHdr);
+  for (i = 0; i <= g_statements; i++) free_vstring(*(vstring *)(&pntrTinyHdr[i]));
+  free_pntrString(pntrTinyHdr);
+
+} /* writeTheoremList */
+
+
+/* This function extracts any section headers in the comment sections
+   prior to the label of statement stmt.   If a huge (####...) header isn't
+   found, *hugeHdrTitle will be set to the empty string.
+   If a big (#*#*...) header isn't found (or isn't after the last huge header),
+   *bigHdrTitle will be set to the empty string.  If a small
+   (=-=-...) header isn't found (or isn't after the last huge header or
+   the last big header), *smallHdrTitle will be set to the empty string.
+   In all 3 cases, only the last occurrence of a header is considered.
+   If a tiny
+   (-.-....) header isn't found (or isn't after the last huge header or
+   the last big header or the last small header), *tinyHdrTitle will be
+   set to the empty string.
+   In all 4 cases, only the last occurrence of a header is considered. */
+/*
+20-Jun-2015 metamath Google group email:
+https://groups.google.com/g/metamath/c/QE0lwg9f5Ho/m/J8ekl_lH1I8J
+
+There are 4 kinds of section headers, big (####...), medium (#*#*#*...),
+small (=-=-=-), and tiny (-.-.-.).
+
+Call the collection of (outside-of-statement) comments between two
+successive $a/$p statements (i.e. those statements that generate web
+pages) a "header area".  The algorithm scans the header area for the
+_last_ header of each type (big, medium, small, tiny) and discards all
+others. Then, if there is a medium and it appears before a big, the
+medium is discarded.  If there is a small and it appears before a big or
+medium, the small is discarded.  In other words, a maximum of one header
+of each type is kept, in the order big, medium, small, and tiny.
+
+There are two reasons for doing this:  (1) it disregards headers used
+for other purposes such as the headers for the set.mm-specific
+information at the top that is not of general interest and (2) it
+ignores headers for empty sections; for example, a mathbox user might
+have a bunch of headers for sections planned for the future, but we
+ignore them if those sections are empty (no $a or $p in them).
+*/
+/* Return 1 if error found, 0 otherwise */
+flag getSectionHeadings(long stmt,
+    vstring *hugeHdrTitle,
+    vstring *bigHdrTitle,
+    vstring *smallHdrTitle,
+    vstring *tinyHdrTitle,
+    vstring *hugeHdrComment,
+    vstring *bigHdrComment,
+    vstring *smallHdrComment,
+    vstring *tinyHdrComment,
+    flag fineResolution,
+    flag fullComment) {
+
+  /* for table of contents */
+  vstring_def(labelStr);
+  long pos, pos1, pos2, pos3, pos4;
+  flag errorFound = 0;
+  flag saveOutputToString;
+
+  /* Print any error messages to screen */
+  saveOutputToString = g_outputToString; /* To restore when returning */
+  g_outputToString = 0;
+
+  /* (This initialization seems to be done redundantly by caller elsewhere,
+     but for  WRITE SOURCE ... / EXTRACT we need to do it explicitly.) */
+  free_vstring(*hugeHdrTitle);
+  free_vstring(*bigHdrTitle);
+  free_vstring(*smallHdrTitle);
+  free_vstring(*tinyHdrTitle);
+  free_vstring(*hugeHdrComment);
+  free_vstring(*bigHdrComment);
+  free_vstring(*smallHdrComment);
+  free_vstring(*tinyHdrComment);
+
+  /* We now process only $a or $p statements */
+  if (fineResolution == 0) {
+    if (g_Statement[stmt].type != a_ && g_Statement[stmt].type != p_) bug(2340);
+  }
+
+  /* Get header area between this statement and the statement after the
+     previous $a or $p statement */
+  /* pos3 and pos4 are used temporarily here; not related to later use */
+  if (fineResolution == 0) {
+    pos3 = g_Statement[stmt].headerStartStmt;  /* Statement immediately after the
+                previous $a or $p statement (will be this statement if previous
+                statement is $a or $p) */
+  } else {
+    pos3 = stmt; /* For WRITE SOURCE ... / EXTRACT, we want every statement
+                    treated equally */
+  }
+  if (pos3 == 0 || pos3 > stmt) bug(2241);
+  pos4 = (g_Statement[stmt].labelSectionPtr
+        - g_Statement[pos3].labelSectionPtr)
+        + g_Statement[stmt].labelSectionLen;  /* Length of the header area */
+  let(&labelStr, space(pos4));
+  memcpy(labelStr, g_Statement[pos3].labelSectionPtr,
+      (size_t)(pos4));
+
+  pos = 0;
+  pos2 = 0;
+  while (1) {  /* Find last "huge" header, if any */
+    /* 4-Nov-2007 nm:  Obviously, the match below will not work if the
+       $( line has a trailing space, which some editors might insert.
+       The symptom is a missing table of contents entry.  But to detect
+       this (and for the #*#* and =-=- matches below) would take a little work
+       and perhaps slow things down, and I don't think it is worth it.  I
+       put a note in HELP WRITE THEOREM_LIST. */
+    pos1 = pos;
+    pos = instr(pos + 1, labelStr, "$(\n" HUGE_DECORATION);
+
+    /* 23-May-2008 nm Tolerate one space after "$(", to handle case of
+      one space added to the end of each line with TOOLS to make global
+      label changes are easier (still a kludge; this should be made
+      white-space insensitive some day) */
+    pos1 = instr(pos1 + 1, labelStr, "$( \n" HUGE_DECORATION);
+    if (pos1 > pos) pos = pos1;
+
+    if (!pos) break;
+    if (pos) pos2 = pos;
+  } /* while(1) */
+  if (pos2) { /* Extract "huge" header */
+    pos1 = pos2; /* Save "$(" position */
+    pos = instr(pos2 + 4, labelStr, "\n"); /* Get to end of #### line */
+    pos2 = instr(pos + 1, labelStr, "\n"); /* Find end of title line */
+
+    /* Error check - can't have more than 1 title line */
+    if (strcmp(mid(labelStr, pos2 + 1, 4), HUGE_DECORATION)) {
+      print2(
+       "?Warning: missing closing \"%s\" decoration above statement \"%s\".\n",
+          HUGE_DECORATION, g_Statement[stmt].labelName);
+      errorFound = 1;
+    }
+
+    pos3 = instr(pos2 + 1, labelStr, "\n"); /* Get to end of 2nd #### line */
+    while (labelStr[(pos3 - 1) + 1] == '\n') pos3++; /* Skip 1st blank lines */
+    pos4 = instr(pos3, labelStr, "$)"); /* Get to end of title comment */
+    if (fullComment == 0) {
+      let(&(*hugeHdrTitle), seg(labelStr, pos + 1, pos2 - 1));
+      let(&(*hugeHdrTitle), edit((*hugeHdrTitle), 8 + 128));
+                                                /* Trim leading, trailing sp */
+      let(&(*hugeHdrComment), seg(labelStr, pos3 + 1, pos4 - 2));
+      let(&(*hugeHdrComment), edit((*hugeHdrComment), 8 + 16384));
+          /* Trim leading sp, trailing sp & lf */
+    } else {
+      /* Put entire comment in hugeHdrTitle and hugeHdrComment for /EXTRACT */
+      /* Search backwards for non-space or beginning of string: */
+      pos = pos1; /* pos1 is the "$" in "$(" */
+      pos--;
+      while (pos > 0) {
+        if (labelStr[pos - 1] != ' '
+              && labelStr[pos - 1] != '\n') break;
+        pos--;
+      }
+      /* pos + 1 is the start of whitespace preceding "$(" */
+      /* pos4 is the "$" in "$)" */
+      /* pos3 is the \n after the 2nd decoration line */
+      let(&(*hugeHdrTitle), seg(labelStr, pos + 1, pos3));
+      let(&(*hugeHdrComment), seg(labelStr, pos3 + 1, pos4 + 1));
+    }
+  }
+  /* pos = 0; */ /* Leave pos alone so that we start with "huge" header pos,
+                    to ignore any earlier "tiny" or "small" or "big" header */
+  pos2 = 0;
+  while (1) {  /* Find last "big" header, if any */
+    pos1 = pos;
+    pos = instr(pos + 1, labelStr, "$(\n" BIG_DECORATION);
+    pos1 = instr(pos1 + 1, labelStr, "$( \n" BIG_DECORATION);
+    if (pos1 > pos) pos = pos1;
+
+    if (!pos) break;
+    if (pos) pos2 = pos;
+  }
+  if (pos2) { /* Extract "big" header */
+    pos1 = pos2; /* Save "$(" position */
+    pos = instr(pos2 + 4, labelStr, "\n"); /* Get to end of #*#* line */
+    pos2 = instr(pos + 1, labelStr, "\n"); /* Find end of title line */
+
+    /* Error check - can't have more than 1 title line */
+    if (strcmp(mid(labelStr, pos2 + 1, 4), BIG_DECORATION)) {
+      print2(
+       "?Warning: missing closing \"%s\" decoration above statement \"%s\".\n",
+          BIG_DECORATION, g_Statement[stmt].labelName);
+      errorFound = 1;
+    }
+
+    pos3 = instr(pos2 + 1, labelStr, "\n"); /* Get to end of 2nd #*#* line */
+    while (labelStr[(pos3 - 1) + 1] == '\n') pos3++; /* Skip 1st blank lines */
+    pos4 = instr(pos3, labelStr, "$)"); /* Get to end of title comment */
+    if (fullComment == 0) {
+      let(&(*bigHdrTitle), seg(labelStr, pos + 1, pos2 - 1));
+      let(&(*bigHdrTitle), edit((*bigHdrTitle), 8 + 128));
+                                                  /* Trim leading, trailing sp */
+      let(&(*bigHdrComment), seg(labelStr, pos3 + 1, pos4 - 2));
+      let(&(*bigHdrComment), edit((*bigHdrComment), 8 + 16384));
+          /* Trim leading sp, trailing sp & lf */
+    } else {
+      /* Put entire comment in bigHdrTitle and bigHdrComment for /EXTRACT */
+      /* Search backwards for non-space or beginning of string: */
+      pos = pos1; /* pos1 is the "$" in "$(" */
+      pos--;
+      while (pos > 0) {
+        if (labelStr[pos - 1] != ' '
+              && labelStr[pos - 1] != '\n') break;
+        pos--;
+      }
+      /* pos + 1 is the start of whitespace preceding "$(" */
+      /* pos4 is the "$" in "$)" */
+      /* pos3 is the \n after the 2nd decoration line */
+      let(&(*bigHdrTitle), seg(labelStr, pos + 1, pos3));
+      let(&(*bigHdrComment), seg(labelStr, pos3 + 1, pos4 + 1));
+    }
+  }
+  /* pos = 0; */ /* Leave pos alone so that we start with "big" header pos,
+                    to ignore any earlier "tiny" or "small" header */
+  pos2 = 0;
+  while (1) {  /* Find last "small" header, if any */
+    pos1 = pos;
+    pos = instr(pos + 1, labelStr, "$(\n" SMALL_DECORATION);
+    pos1 = instr(pos1 + 1, labelStr, "$( \n" SMALL_DECORATION);
+    if (pos1 > pos) pos = pos1;
+
+    if (!pos) break;
+    if (pos) pos2 = pos;
+  }
+  if (pos2) { /* Extract "small" header */
+    pos1 = pos2; /* Save "$(" position */
+    pos = instr(pos2 + 4, labelStr, "\n"); /* Get to end of =-=- line */
+    pos2 = instr(pos + 1, labelStr, "\n"); /* Find end of title line */
+
+    /* Error check - can't have more than 1 title line */
+    if (strcmp(mid(labelStr, pos2 + 1, 4), SMALL_DECORATION)) {
+      print2(
+       "?Warning: missing closing \"%s\" decoration above statement \"%s\".\n",
+          SMALL_DECORATION, g_Statement[stmt].labelName);
+      errorFound = 1;
+    }
+
+    pos3 = instr(pos2 + 1, labelStr, "\n"); /* Get to end of 2nd =-=- line */
+    while (labelStr[(pos3 - 1) + 1] == '\n') pos3++; /* Skip 1st blank lines */
+    pos4 = instr(pos3, labelStr, "$)"); /* Get to end of title comment */
+    if (fullComment == 0) {
+      let(&(*smallHdrTitle), seg(labelStr, pos + 1, pos2 - 1));
+      let(&(*smallHdrTitle), edit((*smallHdrTitle), 8 + 128));
+                                                /* Trim leading, trailing sp */
+      let(&(*smallHdrComment), seg(labelStr, pos3 + 1, pos4 - 2));
+      let(&(*smallHdrComment), edit((*smallHdrComment), 8 + 16384));
+          /* Trim leading sp, trailing sp & lf */
+    } else {
+      /* Put entire comment in smallHdrTitle and smallHdrComment for /EXTRACT */
+      /* Search backwards for non-space or beginning of string: */
+      pos = pos1; /* pos1 is the "$" in "$(" */
+      pos--;
+      while (pos > 0) {
+        if (labelStr[pos - 1] != ' '
+              && labelStr[pos - 1] != '\n') break;
+        pos--;
+      }
+      /* pos + 1 is the start of whitespace preceding "$(" */
+      /* pos4 is the "$" in "$)" */
+      /* pos3 is the \n after the 2nd decoration line */
+      let(&(*smallHdrTitle), seg(labelStr, pos + 1, pos3));
+      let(&(*smallHdrComment), seg(labelStr, pos3 + 1, pos4 + 1));
+    }
+  }
+
+  /* pos = 0; */ /* Leave pos alone so that we start with "small" header pos,
+                    to ignore any earlier "tiny" header */
+  pos2 = 0;
+  while (1) {  /* Find last "tiny" header, if any */
+    pos1 = pos;
+    pos = instr(pos + 1, labelStr, "$(\n" TINY_DECORATION);
+    pos1 = instr(pos1 + 1, labelStr, "$( \n" TINY_DECORATION);
+    if (pos1 > pos) pos = pos1;
+
+    if (!pos) break;
+    if (pos) pos2 = pos;
+  }
+  if (pos2) { /* Extract "tiny" header */
+    pos1 = pos2; /* Save "$(" position */
+    pos = instr(pos2 + 4, labelStr, "\n"); /* Get to end of -.-. line */
+    pos2 = instr(pos + 1, labelStr, "\n"); /* Find end of title line */
+
+    /* Error check - can't have more than 1 title line */
+    if (strcmp(mid(labelStr, pos2 + 1, 4), TINY_DECORATION)) {
+      print2(
+       "?Warning: missing closing \"%s\" decoration above statement \"%s\".\n",
+          TINY_DECORATION, g_Statement[stmt].labelName);
+      errorFound = 1;
+    }
+
+    pos3 = instr(pos2 + 1, labelStr, "\n"); /* Get to end of 2nd -.-. line */
+    while (labelStr[(pos3 - 1) + 1] == '\n') pos3++; /* Skip 1st blank lines */
+    pos4 = instr(pos3, labelStr, "$)"); /* Get to end of title comment */
+    if (fullComment == 0) {
+      let(&(*tinyHdrTitle), seg(labelStr, pos + 1, pos2 - 1));
+      let(&(*tinyHdrTitle), edit((*tinyHdrTitle), 8 + 128));
+                                                  /* Trim leading, trailing sp */
+      let(&(*tinyHdrComment), seg(labelStr, pos3 + 1, pos4 - 2));
+      let(&(*tinyHdrComment), edit((*tinyHdrComment), 8 + 16384));
+          /* Trim leading sp, trailing sp & lf */
+    } else {
+      /* Put entire comment in tinyHdrTitle and tinyHdrComment for /EXTRACT */
+      /* Search backwards for non-space or beginning of string: */
+      pos = pos1; /* pos1 is the "$" in "$(" */
+      pos--;
+      while (pos > 0) {
+        if (labelStr[pos - 1] != ' '
+              && labelStr[pos - 1] != '\n') break;
+        pos--;
+      }
+      /* pos + 1 is the start of whitespace preceding "$(" */
+      /* pos4 is the "$" in "$)" */
+      /* pos3 is the \n after the 2nd decoration line */
+      let(&(*tinyHdrTitle), seg(labelStr, pos + 1, pos3));
+      let(&(*tinyHdrComment), seg(labelStr, pos3 + 1, pos4 + 1));
+    }
+  }
+
+  if (errorFound == 1) {
+    print2("  (Note that section titles may not be longer than one line.)\n");
+  }
+  /* Restore output stream */
+  g_outputToString = saveOutputToString;
+
+  free_vstring(labelStr);  /* Deallocate string memory */
+  return errorFound;
+} /* getSectionHeadings */
+
+
+/* Returns HTML for the pink number to print after the statement labels
+   in HTML output.  (Note that "pink" means "rainbow colored" number now.) */
+/* Warning: The caller must deallocate the returned vstring (i.e. this
+   function cannot be used in let statements but must be assigned to
+   a local vstring for local deallocation) */
+vstring pinkHTML(long statemNum)
+{
+  long statemMap;
+  vstring_def(htmlCode);
+  vstring_def(hexValue);
+
+  if (statemNum > 0) {
+    statemMap = g_Statement[statemNum].pinkNumber;
+  } else {
+    /* -1 means the label wasn't found */
+    statemMap = -1;
+  }
+
+  /* Note: we put "(future)" when the label wasn't found (an error message
+     was also generated previously) */
+
+  /* With style sheet and explicit color */
+  free_vstring(hexValue);
+  hexValue = spectrumToRGB(statemMap, g_Statement[g_statements].pinkNumber);
+  let(&htmlCode, cat(PINK_NBSP,
+      "<SPAN CLASS=r STYLE=\"color:#", hexValue, "\">",
+      (statemMap != -1) ? str((double)statemMap) : "(future)", "</SPAN>", NULL));
+  free_vstring(hexValue);
+
+  return htmlCode;
+} /* pinkHTML */
+
+
+/* Returns HTML for a range of pink numbers separated by a "-". */
+/* Warning: The caller must deallocate the returned vstring (i.e. this
+   function cannot be used in let statements but must be assigned to
+   a local vstring for local deallocation) */
+vstring pinkRangeHTML(long statemNum1, long statemNum2) {
+  vstring_def(htmlCode);
+  vstring_def(str3);
+  vstring_def(str4);
+
+  /* Construct the HTML for a pink number range */
+  free_vstring(str3);
+  str3 = pinkHTML(statemNum1);
+  let(&str3, right(str3, (long)strlen(PINK_NBSP) + 1)); /* Discard "&nbsp;" */
+  free_vstring(str4);
+  str4 = pinkHTML(statemNum2);
+  let(&str4, right(str4, (long)strlen(PINK_NBSP) + 1)); /* Discard "&nbsp;" */
+  let(&htmlCode, cat(str3, "-", str4, NULL));
+  free_vstring(str3); /* Deallocate */
+  free_vstring(str4); /* Deallocate */
+  return htmlCode;
+} /* pinkRangeHTML */
+
+
+/* This function converts a "spectrum" color (1 to maxColor) to an
+   RBG value in hex notation for HTML.  The caller must deallocate the
+   returned vstring to prevent memory leaks.  color = 1 (red) to maxColor
+   (violet).  A special case is the color -1, which just returns black. */
+vstring spectrumToRGB(long color, long maxColor) {
+  vstring_def(str1);
+  double fraction, fractionInPartition;
+  long j, red, green, blue, partition;
+/* Change PARTITIONS whenever the table below has entries added or removed! */
+#define PARTITIONS 28
+  static double redRef[PARTITIONS +  1];    /* Made these */
+  static double greenRef[PARTITIONS +  1];  /* static for */
+  static double blueRef[PARTITIONS +  1];   /* speedup */
+  static long i = -1;                       /* below */
+
+  if (i > -1) goto SKIP_INIT;
+  i = -1; /* For safety */
+
+  /* Here, we use the maximum saturation possible for a fixed L*a*b color L
+     (lightness) value of 53, which corresponds to 50% gray scale.
+     Each pure color had either brightness reduced or saturation reduced,
+     as required, to achieve L = 53.
+
+     An initial partition was formed from hues 0, 15, 30, ..., 345, then the
+     result was divided into 1000 subpartitions, and the new partitions were
+     determined by reselecting partition boundaries based on where their color
+     difference was distinguishable (i.e. could semi-comfortably read letters
+     of one color with the other as a background, on an LCD display).  Some
+     human judgment was involved, and it is probably not completely uniform or
+     optimal.
+
+     A Just Noticeable Difference (JND) algorithm for spacing might be more
+     accurate, especially if averaged over several subjects and different
+     monitors.  I wrote a program for that - asking the user to identify a word
+     in one hue with an adjacent hue as a background, in order to score a
+     point - but it was taking too much time, and I decided life is too short.
+     I think this is "good enough" though, perhaps not even noticeably
+     non-optimum.
+
+     The comment at the end of each line is the hue 0-360 mapped linearly
+     to 1-1043 (345 = 1000, i.e. "extreme" purple or almost red).  Partitions
+     at the end can be commented out if we want to stop at violet instead of
+     almost wrapping around to red via the purples, in order to more accurately
+     emulate the color spectrum.  Be sure to update the PARTITIONS constant
+     above if a partition is commented out, to avoid a bug trap. */
+  i++; redRef[i] = 251; greenRef[i] = 0; blueRef[i] = 0;       /* 1 */
+  i++; redRef[i] = 247; greenRef[i] = 12; blueRef[i] = 0;      /* 10 */
+  i++; redRef[i] = 238; greenRef[i] = 44; blueRef[i] = 0;      /* 34 */
+  i++; redRef[i] = 222; greenRef[i] = 71; blueRef[i] = 0;      /* 58 */
+  i++; redRef[i] = 203; greenRef[i] = 89; blueRef[i] = 0;      /* 79 */
+  i++; redRef[i] = 178; greenRef[i] = 108; blueRef[i] = 0;     /* 109 */
+  i++; redRef[i] = 154; greenRef[i] = 122; blueRef[i] = 0;     /* 140 */
+  i++; redRef[i] = 127; greenRef[i] = 131; blueRef[i] = 0;     /* 181 */
+  i++; redRef[i] = 110; greenRef[i] = 136; blueRef[i] = 0;     /* 208 */
+  i++; redRef[i] = 86; greenRef[i] = 141; blueRef[i] = 0;      /* 242 */
+  i++; redRef[i] = 60; greenRef[i] = 144; blueRef[i] = 0;      /* 276 */
+  i++; redRef[i] = 30; greenRef[i] = 147; blueRef[i] = 0;      /* 313 */
+  i++; redRef[i] = 0; greenRef[i] = 148; blueRef[i] = 22;      /* 375 */
+  i++; redRef[i] = 0; greenRef[i] = 145; blueRef[i] = 61;      /* 422 */
+  i++; redRef[i] = 0; greenRef[i] = 145; blueRef[i] = 94;      /* 462 */
+  i++; redRef[i] = 0; greenRef[i] = 143; blueRef[i] = 127;     /* 504 */
+  i++; redRef[i] = 0; greenRef[i] = 140; blueRef[i] = 164;     /* 545 */
+  i++; redRef[i] = 0; greenRef[i] = 133; blueRef[i] = 218;     /* 587 */
+  i++; redRef[i] = 3; greenRef[i] = 127; blueRef[i] = 255;     /* 612 */
+  i++; redRef[i] = 71; greenRef[i] = 119; blueRef[i] = 255;    /* 652 */
+  i++; redRef[i] = 110; greenRef[i] = 109; blueRef[i] = 255;   /* 698 */
+  i++; redRef[i] = 137; greenRef[i] = 99; blueRef[i] = 255;    /* 740 */
+  i++; redRef[i] = 169; greenRef[i] = 78; blueRef[i] = 255;    /* 786 */
+  i++; redRef[i] = 186; greenRef[i] = 57; blueRef[i] = 255;    /* 808 */
+  i++; redRef[i] = 204; greenRef[i] = 33; blueRef[i] = 249;    /* 834 */
+  i++; redRef[i] = 213; greenRef[i] = 16; blueRef[i] = 235;    /* 853 */
+  i++; redRef[i] = 221; greenRef[i] = 0; blueRef[i] = 222;     /* 870 */
+  i++; redRef[i] = 233; greenRef[i] = 0; blueRef[i] = 172;     /* 916 */
+  i++; redRef[i] = 239; greenRef[i] = 0; blueRef[i] = 132;     /* 948 */
+  /*i++; redRef[i] = 242; greenRef[i] = 0; blueRef[i] = 98;*/  /* 973 */
+  /*i++; redRef[i] = 244; greenRef[i] = 0; blueRef[i] = 62;*/  /* 1000 */
+
+  if (i != PARTITIONS) { /* Double-check future edits */
+    print2("? %ld partitions but PARTITIONS = %ld\n", i, (long)PARTITIONS);
+    bug(2326); /* Don't go further to prevent out-of-range references */
+  }
+
+ SKIP_INIT:
+  if (color == -1) {
+    let(&str1, "000000"); /* Return black for "(future)" color for labels with
+                             missing theorems in comments */
+    return str1;
+  }
+
+  if (color < 1 || color > maxColor) {
+    bug(2327);
+  }
+  fraction = (1.0 * ((double)color - 1)) / (double)maxColor;
+                                   /* Fractional position in "spectrum" */
+  partition = (long)(PARTITIONS * fraction);  /* Partition number (integer) */
+  if (partition >= PARTITIONS) bug(2325); /* Round-off error? */
+  fractionInPartition = 1.0 * (fraction - (1.0 * (double)partition) / PARTITIONS)
+      * PARTITIONS; /* The fraction of this partition it covers */
+  red = (long)(1.0 * (redRef[partition] +
+          fractionInPartition *
+              (redRef[partition + 1] - redRef[partition])));
+  green = (long)(1.0 * (greenRef[partition] +
+          fractionInPartition *
+              (greenRef[partition + 1] - greenRef[partition])));
+  blue = (long)(1.0 * (blueRef[partition] +
+          fractionInPartition *
+              (blueRef[partition + 1] - blueRef[partition])));
+  /* debug */
+  /* i=1;if (g_outputToString==0) {i=0;g_outputToString=1;} */
+  /*   print2("p%ldc%ld\n", partition, color); g_outputToString=i; */
+  /*printf("red %ld green %ld blue %ld\n", red, green, blue);*/
+
+  if (red < 0 || green < 0 || blue < 0
+      || red > 255 || green > 255 || blue > 255) {
+    print2("%ld %ld %ld\n", red, green, blue);
+    bug(2323);
+  }
+  let(&str1, "      ");
+  j = sprintf(str1, "%02X%02X%02X", (unsigned int)red, (unsigned int)green,
+      (unsigned int)blue);
+  if (j != 6) bug(2324);
+  /* debug */
+  /*printf("<FONT COLOR='#%02X%02X%02X'>a </FONT>\n", red, green, blue);*/
+  return str1;
+} /* spectrumToRGB */
+
+
+/* Returns the HTML code for GIFs (!g_altHtmlFlag) or Unicode (g_altHtmlFlag),
+   or LaTeX when !g_htmlFlag, for the math string (hypothesis or conclusion) that
+   is passed in. */
+/* Warning: The caller must deallocate the returned vstring. */
+vstring getTexLongMath(nmbrString *mathString, long statemNum)
+{
+  long pos;
+  vstring_def(tex);
+  vstring_def(texLine);
+  vstring_def(lastTex);
+  flag alphnew, alphold, unknownnew, unknownold;
+
+  if (!g_texDefsRead) bug(2322); /* TeX defs were not read */
+  let(&texLine, "");
+
+  let(&lastTex, "");
+  for (pos = 0; pos < nmbrLen(mathString); pos++) {
+    free_vstring(tex);
+    tex = tokenToTex(g_MathToken[mathString[pos]].tokenName, statemNum);
+              /* tokenToTex allocates tex; we must deallocate it */
+    if (!g_htmlFlag) {  /* LaTeX */
+      /* If this token and previous token begin with letter, add a thin
+           space between them */
+      /* Also, anything not in table will have space added */
+      alphnew = !!isalpha((unsigned char)(tex[0]));
+      unknownnew = 0;
+      if (!strcmp(left(tex, 10), "\\mbox{\\rm ")) { /* Token not in table */
+        unknownnew = 1;
+      }
+      alphold = !!isalpha((unsigned char)(lastTex[0]));
+      unknownold = 0;
+      if (!strcmp(left(lastTex, 10), "\\mbox{\\rm ")) { /* Token not in table*/
+        unknownold = 1;
+      }
+      /* Put thin space only between letters and/or unknowns */
+      if ((alphold || unknownold) && (alphnew || unknownnew)) {
+        /* Put additional thin space between two letters */
+        if (!g_oldTexFlag) {
+          let(&texLine, cat(texLine, "\\,", tex, " ", NULL));
+        } else {
+          let(&texLine, cat(texLine, "\\m{\\,", tex, "}", NULL));
+        }
+      } else {
+        if (!g_oldTexFlag) {
+          let(&texLine, cat(texLine, "", tex, " ", NULL));
+        } else {
+          let(&texLine, cat(texLine, "\\m{", tex, "}", NULL));
+        }
+      }
+    } else {  /* HTML */
+
+      /* When we have something like "E. x e. om x = y", the lack of
+         space between om and x looks ugly in HTML.  This kludge adds it in
+         for restricted quantifiers not followed by parenthesis, in order
+         to make the web page look a little nicer.  E.g. onminex. */
+      /* Note that the space is put between the pos-1 and the pos tokens */
+      if (pos >=4) {
+        if (!strcmp(g_MathToken[mathString[pos - 2]].tokenName, "e.")
+            && (!strcmp(g_MathToken[mathString[pos - 4]].tokenName, "E.")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "A.")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "prod_")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "E*")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "iota_")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "Disj_")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "E!")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "sum_")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "X_")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "U_")
+              || !strcmp(g_MathToken[mathString[pos - 4]].tokenName, "|^|_"))
+            && strcmp(g_MathToken[mathString[pos]].tokenName, ")")
+            /* It also shouldn't be restricted _to_ an expression in parens. */
+            && strcmp(g_MathToken[mathString[pos - 1]].tokenName, "(")
+            /* ...or restricted _to_ a union or intersection */
+            && strcmp(g_MathToken[mathString[pos - 1]].tokenName, "U.")
+            && strcmp(g_MathToken[mathString[pos - 1]].tokenName, "|^|")
+            /* ...or restricted _to_ an expression in braces */
+            && strcmp(g_MathToken[mathString[pos - 1]].tokenName, "{")) {
+          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
+        }
+      }
+      /* This one puts a space between the 2 x's in a case like
+         "E. x x = y".  E.g. cla4egf */
+      if (pos >=2) {
+        /* Match a token starting with a letter */
+        if (isalpha((unsigned char)(g_MathToken[mathString[pos]].tokenName[0]))) {
+          /* and make sure its length is 1 */
+          if (!(g_MathToken[mathString[pos]].tokenName[1])) {
+
+            /* Make sure previous token is a letter also, to prevent unneeded
+               space in "ran ( A ..." (e.g. rncoeq, dfiun3g) */
+            /* Match a token starting with a letter */
+            if (isalpha((unsigned char)(g_MathToken[mathString[pos - 1]].tokenName[0]))) {
+              /* and make sure its length is 1 */
+              if (!(g_MathToken[mathString[pos - 1]].tokenName[1])) {
+
+                /* See if it's 1st letter in a quantified expression */
+                if (!strcmp(g_MathToken[mathString[pos - 2]].tokenName, "E.")
+                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "A.")
+                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "F/")
+                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "E!")
+                    /* space btwn A,x in "E! x e. dom A x A y" */
+                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "ran")
+                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "dom")
+                    || !strcmp(g_MathToken[mathString[pos - 2]].tokenName, "E*")) {
+                  let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
+                }
+              }
+            }
+          }
+        }
+      }
+      /* This one puts a space after a letter followed by a word token
+         e.g. "A" and "suc" in "A. x e. U. A suc" in limuni2 */
+      if (pos >= 1) {
+        /* See if the next token is "suc" */
+        if (!strcmp(g_MathToken[mathString[pos]].tokenName, "suc")) {
+          /* Match a token starting with a letter for the current token */
+          if (isalpha(
+              (unsigned char)(g_MathToken[mathString[pos - 1]].tokenName[0]))) {
+            /* and make sure its length is 1 */
+            if (!(g_MathToken[mathString[pos - 1]].tokenName[1])) {
+              let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
+            }
+          }
+        }
+      }
+      /* This one puts a space before any "-." that doesn't come after
+         a parentheses e.g. ax-6 has both cases */
+      if (pos >=1) {
+        /* See if we have a non-parenthesis followed by not */
+        if (strcmp(g_MathToken[mathString[pos - 1]].tokenName, "(")
+            && !strcmp(g_MathToken[mathString[pos]].tokenName, "-.")) {
+          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
+        }
+      }
+      /* This one puts a space between "S" and "(" in df-iso. */
+      if (pos >=4) {
+        if (!strcmp(g_MathToken[mathString[pos - 4]].tokenName, "Isom")
+            && !strcmp(g_MathToken[mathString[pos - 2]].tokenName, ",")
+            && !strcmp(g_MathToken[mathString[pos]].tokenName, "(")) {
+          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
+        }
+      }
+      /* This one puts a space between "}" and "(" in funcnvuni proof. */
+      if (pos >=1) {
+        /* See if we have "}" followed by "(" */
+        if (!strcmp(g_MathToken[mathString[pos - 1]].tokenName, "}")
+            && !strcmp(g_MathToken[mathString[pos]].tokenName, "(")) {
+          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
+        }
+      }
+      /* This one puts a space between "}" and "{" in konigsberg proof. */
+      if (pos >=1) {
+        /* See if we have "}" followed by "(" */
+        if (!strcmp(g_MathToken[mathString[pos - 1]].tokenName, "}")
+            && !strcmp(g_MathToken[mathString[pos]].tokenName, "{")) {
+          let(&texLine, cat(texLine, " ", NULL)); /* Add a space */
+        }
+      }
+
+      let(&texLine, cat(texLine, tex, NULL));
+    } /* if !g_htmlFlag */
+    let(&lastTex, tex); /* Save for next pass */
+  } /* Next pos */
+
+  /*x Discard redundant white space to reduce HTML file size */
+  let(&texLine, edit(texLine, 8 + 16 + 128));
+
+  /* Enclose math symbols in a span to be used for font selection */
+  let(&texLine, cat(
+      (g_altHtmlFlag ? cat("<SPAN ", g_htmlFont, ">", NULL) : ""),
+      texLine,
+      (g_altHtmlFlag ? "</SPAN>" : ""), NULL));
+
+  free_vstring(tex);
+  free_vstring(lastTex);
+  return texLine;
+} /* getTexLongMath */
+
+
+/* Returns the TeX, or HTML code for GIFs (!g_altHtmlFlag) or Unicode
+   (g_altHtmlFlag), for a statement's hypotheses and assertion in the form
+   hyp & ... & hyp => assertion */
+/* Warning: The caller must deallocate the returned vstring (i.e. this
+   function cannot be used in let statements but must be assigned to
+   a local vstring for local deallocation) */
+vstring getTexOrHtmlHypAndAssertion(long statemNum) {
+  long reqHyps, essHyps, n;
+  nmbrString *nmbrTmpPtr; /* Pointer only; not allocated directly */
+  vstring_def(texOrHtmlCode);
+  vstring_def(str2);
+  /* Count the number of essential hypotheses essHyps */
+  essHyps = 0;
+  reqHyps = nmbrLen(g_Statement[statemNum].reqHypList);
+  let(&texOrHtmlCode, "");
+  for (n = 0; n < reqHyps; n++) {
+    if (g_Statement[g_Statement[statemNum].reqHypList[n]].type
+        == (char)e_) {
+      essHyps++;
+      if (texOrHtmlCode[0]) { /* Add '&' between hypotheses */
+        if (!g_htmlFlag) {
+          /* Hard-coded for set.mm! */
+          let(&texOrHtmlCode, cat(texOrHtmlCode,
+                 "\\quad\\&\\quad "
+              ,NULL));
+        } else {
+          if (g_altHtmlFlag) {
+            /* Hard-coded for set.mm! */
+            let(&texOrHtmlCode, cat(texOrHtmlCode,
+                "<SPAN ", g_htmlFont, ">",
+                "&nbsp;&nbsp;&nbsp; &amp;&nbsp;&nbsp;&nbsp;",
+                "</SPAN>",
+                NULL));
+          } else {
+            /* Hard-coded for set.mm! */
+            let(&texOrHtmlCode, cat(texOrHtmlCode,
+          "&nbsp;&nbsp;&nbsp;<IMG SRC='amp.gif' WIDTH=12 HEIGHT=19 ALT='&amp;'"
+                ," ALIGN=TOP>&nbsp;&nbsp;&nbsp;"
+                ,NULL));
+          }
+        }
+      } /* if texOrHtmlCode[0] */
+      /* Construct HTML hypothesis */
+      nmbrTmpPtr = g_Statement[g_Statement[statemNum].reqHypList[n]].mathString;
+      let(&str2, "");
+      str2 = getTexLongMath(nmbrTmpPtr, statemNum);
+      let(&texOrHtmlCode, cat(texOrHtmlCode, str2, NULL));
+    }
+  }
+  if (essHyps) {  /* Add big arrow if there were hypotheses */
+    if (!g_htmlFlag) {
+      /* Hard-coded for set.mm! */
+      let(&texOrHtmlCode, cat(texOrHtmlCode,
+                 "\\quad\\Rightarrow\\quad "
+          ,NULL));
+    } else {
+      if (g_altHtmlFlag) {
+        /* Hard-coded for set.mm! */
+        let(&texOrHtmlCode, cat(texOrHtmlCode,
+            /* sans-serif to work around FF3 bug that produces
+                huge character heights otherwise */
+            "&nbsp;&nbsp;&nbsp; <FONT FACE=sans-serif>&#8658;</FONT>&nbsp;&nbsp;&nbsp;",
+            NULL));
+      } else {
+        /* Hard-coded for set.mm! */
+        let(&texOrHtmlCode, cat(texOrHtmlCode,
+            "&nbsp;&nbsp;&nbsp;<IMG SRC='bigto.gif' WIDTH=15 HEIGHT=19 ALT='=&gt;'",
+            " ALIGN=TOP>&nbsp;&nbsp;&nbsp;",
+            NULL));
+      }
+    }
+  }
+  /* Construct TeX or HTML assertion */
+  nmbrTmpPtr = g_Statement[statemNum].mathString;
+  free_vstring(str2);
+  str2 = getTexLongMath(nmbrTmpPtr, statemNum);
+  let(&texOrHtmlCode, cat(texOrHtmlCode, str2, NULL));
+
+  /* Deallocate memory */
+  free_vstring(str2);
+  return texOrHtmlCode;
+}  /* getTexOrHtmlHypAndAssertion */
+
+
+
+
+/* Called by the WRITE BIBLIOGRAPHY command and also by VERIFY MARKUP
+   for error checking */
+/* Returns 0 if OK, 1 if warning(s), 2 if any error */
+flag writeBibliography(vstring bibFile,
+    vstring labelMatch, /* Normally "*" except when called by verifyMarkup() */
+    flag errorsOnly,  /* 1 = no output, just warning msgs if any */
+    flag fileCheck) /* 1 = check external files (gifs and bib) */
+{
+  flag errFlag;
+  FILE *list1_fp = NULL;
+  FILE *list2_fp = NULL;
+  long lines, p2, i, j, jend, k, l, m, n, p, q, s, pass1refs;
+  vstring_def(str1);
+  vstring_def(str2);
+  vstring_def(str3);
+  vstring_def(str4);
+  vstring_def(newstr);
+  vstring_def(oldstr);
+  pntrString_def(pntrTmp);
+  flag warnFlag;
+
+  n = 0; /* Old gcc 4.6.3 wrongly says may be uninit ln 5506 */
+  pass1refs = 0; /* gcc 4.5.3 wrongly says may be uninit */
+  if (!fileCheck && errorsOnly == 0) {
+    bug(2336); /* If we aren't opening files, a non-error run can't work */
+  }
+
+  /* This utility builds the bibliographical cross-references to various
+     textbooks and updates the user-specified file normally called
+     mmbiblio.html. */
+  warnFlag = 0; /* 1 means warning was found, 2 that error was found */
+  errFlag = 0; /* Error flag to recover input file and to set return value 2 */
+  if (fileCheck) {
+    list1_fp = fSafeOpen(bibFile, "r", 0/*noVersioningFlag*/);
+    if (list1_fp == NULL) {
+      /* Couldn't open it (error msg was provided)*/
+      return 1;
+    }
+    if (errorsOnly == 0) {
+      fclose(list1_fp);
+      /* This will rename the input mmbiblio.html as mmbiblio.html~1 */
+      list2_fp = fSafeOpen(bibFile, "w", 0/*noVersioningFlag*/);
+      if (list2_fp == NULL) {
+          /* Couldn't open it (error msg was provided)*/
+        return 1;
+      }
+      /* Note: in older versions the "~1" string was OS-dependent, but we
+         don't support VAX or THINK C anymore...  Anyway we reopen it
+         here with the renamed file in case the OS won't let us rename
+         an opened file during the fSafeOpen for write above. */
+      list1_fp = fSafeOpen(cat(bibFile, "~1", NULL), "r", 0/*noVersioningFlag*/);
+      if (list1_fp == NULL) bug(2337);
+    }
+  }
+  if (!g_texDefsRead) {
+    g_htmlFlag = 1;
+    if (2/*error*/ == readTexDefs(errorsOnly, fileCheck)) {
+      errFlag = 2; /* Error flag to recover input file */
+      goto BIB_ERROR; /* An error occurred */
+    }
+  }
+
+  /* Transfer the input file up to the special "<!-- #START# -->" comment */
+  if (fileCheck) {
+    while (1) {
+      if (!linput(list1_fp, NULL, &str1)) {
+        print2("?Error: Could not find \"<!-- #START# -->\" line in input file \"%s\".\n",
+            bibFile);
+        errFlag = 2; /* Error flag to recover input file */
+        break;
+      }
+      if (errorsOnly == 0) {
+        fprintf(list2_fp, "%s\n", str1);
+      }
+      if (!strcmp(str1, "<!-- #START# -->")) break;
+    }
+    if (errFlag) goto BIB_ERROR;
+  }
+
+  p2 = 1; /* Pass 1 or 2 flag */
+  lines = 0;
+  while (1) {
+
+    if (p2 == 2) {  /* Pass 2 */
+      /* Allocate memory for sorting */
+      pntrLet(&pntrTmp, pntrSpace(lines));
+      lines = 0;
+    }
+
+    /* Scan all $a and $p statements */
+    for (i = 1; i <= g_statements; i++) {
+      if (g_Statement[i].type != (char)p_ &&
+        g_Statement[i].type != (char)a_) continue;
+
+      /* Normally labelMatch is *, but may be more specific for
+         use by verifyMarkup() */
+      if (!matchesList(g_Statement[i].labelName, labelMatch, '*', '?')) {
+        continue;
+      }
+
+      /* Omit ...OLD (obsolete) and ...NEW (to be implemented) statements */
+      if (instr(1, g_Statement[i].labelName, "NEW")) continue;
+      if (instr(1, g_Statement[i].labelName, "OLD")) continue;
+      let(&str1, "");
+      str1 = getDescription(i); /* Get the statement's comment */
+      if (!instr(1, str1, "[")) continue;
+      l = (signed)(strlen(str1));
+      for (j = 0; j < l; j++) {
+        if (str1[j] == '\n') str1[j] = ' '; /* Change newlines to spaces */
+        if (str1[j] == '\r') bug(2338);
+      }
+      let(&str1, edit(str1, 8 + 128 + 16)); /* Reduce & trim whitespace */
+
+      /* Clear out math symbols in backquotes to prevent false matches
+         to [reference] bracket */
+      k = 0; /* Math symbol mode if 1 */
+      l = (signed)(strlen(str1));
+      for (j = 0; j < l - 1; j++) {
+        if (k == 0) {
+          if (str1[j] == '`') {
+            k = 1; /* Start of math mode */
+          }
+        } else { /* In math mode */
+          if (str1[j] == '`') { /* A backquote */
+            if (str1[j + 1] == '`') {
+              /* It is an escaped backquote */
+              str1[j] = ' ';
+              str1[j + 1] = ' ';
+            } else {
+              k = 0; /* End of math mode */
+            }
+          } else { /* Not a backquote */
+            str1[j] = ' '; /* Clear out the math mode part */
+          }
+        } /* end if k == 0 */
+      } /* next j */
+
+      /* Put spaces before page #s (up to 4 digits) for sorting */
+      j = 0;
+      while (1) {
+        j = instr(j + 1, str1, " p. "); /* Heuristic - match " p. " */
+        if (!j) break;
+        if (j) {
+          for (k = j + 4; k <= (signed)(strlen(str1)) + 1; k++) {
+            if (!isdigit((unsigned char)(str1[k - 1]))) {
+              let(&str1, cat(left(str1, j + 2),
+                  space(4 - (k - (j + 4))), right(str1, j + 3), NULL));
+              /* Add ### after page number as marker */
+              let(&str1, cat(left(str1, j + 7), "###", right(str1, j + 8),
+                  NULL));
+              break;
+            }
+          }
+        }
+      }
+      /* Process any bibliographic references in comment */
+      j = 0;
+      n = 0;
+      while (1) {
+        j = instr(j + 1, str1, "["); /* Find reference (not robust) */
+        if (!j) break;
+
+        /* Fix mmbiblio.html corruption caused by
+           "[Copying an angle]" in df-ibcg in
+           set.mm.2016-09-18-JB-mbox-cleanup */
+        /* Skip if there is no trailing "]" */
+        jend = instr(j, str1, "]");
+        if (!jend) break;
+        /* Skip bracketed text with spaces */
+        /* This is a somewhat ugly workaround that lets us tolerate user
+           comments in [...] is there is at least one space.
+           The printTexComment() above handles this case with the
+           "strcspn(bibTag, " \n\r\t\f")" above; here we just have to look
+           for space since we already reduced \n \t to space (\f is probably
+           overkill, and any \r's are removed during the READ command) */
+        if (instr(1, seg(str1, j, jend), " ")) continue;
+        /* Skip escaped bracket "[[" */
+        if (str1[j] == '[') {  /* (j+1)th character in str1 */
+          j++; /* Skip over 2nd "[" */
+          continue;
+        }
+        if (!isalnum((unsigned char)(str1[j]))) continue; /* Not start of reference */
+        n++;
+        /* Backtrack from [reference] to a starting keyword */
+        m = 0;
+        let(&str2, edit(str1, 32)); /* to uppercase */
+
+        /* (The string search below is rather inefficient; maybe improve
+           the algorithm if speed becomes a problem.) */
+        for (k = j - 1; k >= 1; k--) {
+          /* **IMPORTANT** Make sure to update mmhlpb.c HELP WRITE BIBLIOGRAPHY
+             if new items are added to this list. */
+          if (0
+              /* Put the most frequent ones first to speed up search;
+                 TODO: count occurrences in mmbiblio.html to find optimal order */
+              || !strcmp(mid(str2, k, (long)strlen("THEOREM")), "THEOREM")
+              || !strcmp(mid(str2, k, (long)strlen("EQUATION")), "EQUATION")
+              || !strcmp(mid(str2, k, (long)strlen("DEFINITION")), "DEFINITION")
+              || !strcmp(mid(str2, k, (long)strlen("LEMMA")), "LEMMA")
+              || !strcmp(mid(str2, k, (long)strlen("EXERCISE")), "EXERCISE")
+              || !strcmp(mid(str2, k, (long)strlen("AXIOM")), "AXIOM")
+              || !strcmp(mid(str2, k, (long)strlen("CLAIM")), "CLAIM")
+              || !strcmp(mid(str2, k, (long)strlen("CHAPTER")), "CHAPTER")
+              || !strcmp(mid(str2, k, (long)strlen("COMPARE")), "COMPARE")
+              || !strcmp(mid(str2, k, (long)strlen("CONDITION")), "CONDITION")
+              || !strcmp(mid(str2, k, (long)strlen("CONJECTURE")), "CONJECTURE")
+              || !strcmp(mid(str2, k, (long)strlen("COROLLARY")), "COROLLARY")
+              || !strcmp(mid(str2, k, (long)strlen("EXAMPLE")), "EXAMPLE")
+              || !strcmp(mid(str2, k, (long)strlen("FIGURE")), "FIGURE")
+              || !strcmp(mid(str2, k, (long)strlen("ITEM")), "ITEM")
+              || !strcmp(mid(str2, k, (long)strlen("LEMMAS")), "LEMMAS")
+              || !strcmp(mid(str2, k, (long)strlen("LINE")), "LINE")
+              || !strcmp(mid(str2, k, (long)strlen("LINES")), "LINES")
+              || !strcmp(mid(str2, k, (long)strlen("NOTATION")), "NOTATION")
+              || !strcmp(mid(str2, k, (long)strlen("NOTE")), "NOTE")
+              || !strcmp(mid(str2, k, (long)strlen("OBSERVATION")), "OBSERVATION")
+              || !strcmp(mid(str2, k, (long)strlen("PART")), "PART")
+              || !strcmp(mid(str2, k, (long)strlen("POSTULATE")), "POSTULATE")
+              || !strcmp(mid(str2, k, (long)strlen("PROBLEM")), "PROBLEM")
+              || !strcmp(mid(str2, k, (long)strlen("PROPERTY")), "PROPERTY")
+              || !strcmp(mid(str2, k, (long)strlen("PROPOSITION")), "PROPOSITION")
+              || !strcmp(mid(str2, k, (long)strlen("REMARK")), "REMARK")
+              || !strcmp(mid(str2, k, (long)strlen("RESULT")), "RESULT")
+              || !strcmp(mid(str2, k, (long)strlen("RULE")), "RULE")
+              || !strcmp(mid(str2, k, (long)strlen("SCHEME")), "SCHEME")
+              || !strcmp(mid(str2, k, (long)strlen("SECTION")), "SECTION")
+              || !strcmp(mid(str2, k, (long)strlen("PROOF")), "PROOF")
+              || !strcmp(mid(str2, k, (long)strlen("STATEMENT")), "STATEMENT")
+              || !strcmp(mid(str2, k, (long)strlen("CONCLUSION")), "CONCLUSION")
+              || !strcmp(mid(str2, k, (long)strlen("FACT")), "FACT")
+              || !strcmp(mid(str2, k, (long)strlen("INTRODUCTION")), "INTRODUCTION")
+              || !strcmp(mid(str2, k, (long)strlen("PARAGRAPH")), "PARAGRAPH")
+              || !strcmp(mid(str2, k, (long)strlen("SCOLIA")), "SCOLIA")
+              || !strcmp(mid(str2, k, (long)strlen("SCOLION")), "SCOLION")
+              || !strcmp(mid(str2, k, (long)strlen("SUBSECTION")), "SUBSECTION")
+              || !strcmp(mid(str2, k, (long)strlen("TABLE")), "TABLE")
+              ) {
+            m = k;
+            break;
+          }
+          freeTempAlloc(); /* Clear tmp alloc stack created by "mid" */
+        }
+        if (!m) {
+          if (p2 == 1) {
+            print2(
+             "?Warning: Bibliography keyword missing in comment for \"%s\".\n",
+                g_Statement[i].labelName);
+            print2(
+                "    (See HELP WRITE BIBLIOGRAPHY for list of keywords.)\n");
+            warnFlag = 1;
+          }
+          continue; /* Not a bib ref - ignore */
+        }
+        /* m is at the start of a keyword */
+        p = instr(m, str1, "["); /* Start of bibliography reference */
+        q = instr(p, str1, "]"); /* End of bibliography reference */
+        if (q == 0) {
+          if (p2 == 1) {
+            print2("?Warning: Bibliography reference not found in HTML file in \"%s\".\n",
+              g_Statement[i].labelName);
+            warnFlag = 1;
+          }
+          continue; /* Pretend it is not a bib ref - ignore */
+        }
+        s = instr(q, str1, "###"); /* Page number marker */
+        if (!s) {
+          if (p2 == 1) {
+            print2("?Warning: No page number after [<author>] bib ref in \"%s\".\n",
+              g_Statement[i].labelName);
+            warnFlag = 1;
+          }
+          continue; /* No page number given - ignore */
+        }
+        /* Now we have a real reference; increment reference count */
+        lines++;
+        if (p2 == 1) continue; /* In 1st pass, we just count refs */
+
+        let(&str2, seg(str1, m, p - 1));     /* "Theorem #" */
+        let(&str3, seg(str1, p + 1, q - 1));  /* "[bibref]" w/out [] */
+        let(&str4, seg(str1, q + 1, s - 1)); /* " p. nnnn" */
+        str2[0] = (char)(toupper((unsigned char)(str2[0])));
+        /* Eliminate noise like "of" in "Theorem 1 of [bibref]" */
+        for (k = (long)strlen(str2); k >=1; k--) {
+          if (0
+              || !strcmp(mid(str2, k, (long)strlen(" of ")), " of ")
+              || !strcmp(mid(str2, k, (long)strlen(" in ")), " in ")
+              || !strcmp(mid(str2, k, (long)strlen(" from ")), " from ")
+              || !strcmp(mid(str2, k, (long)strlen(" on ")), " on ")
+              ) {
+            let(&str2, left(str2, k - 1));
+            break;
+          }
+          let(&str2, str2);
+        }
+
+        free_vstring(newstr);
+        newstr = pinkHTML(i); /* Get little pink number */
+        let(&oldstr, cat(
+            /* Construct the sorting key */
+            /* The space() helps Th. 9 sort before Th. 10 on same page */
+            str3, " ", str4, space(20 - (long)strlen(str2)), str2,
+            "|||",  /* ||| means end of sort key */
+            /* Construct just the statement href for combining dup refs */
+            "<A HREF=\"", g_Statement[i].labelName,
+            ".html\">", g_Statement[i].labelName, "</A>",
+            newstr,
+            "&&&",  /* &&& means end of statement href */
+            /* Construct actual HTML table row (without ending tag
+               so duplicate references can be added) */
+            (i < g_extHtmlStmt)
+               ? "<TR>"
+               : (i < g_mathboxStmt)
+                   ? cat("<TR BGCOLOR=", PURPLISH_BIBLIO_COLOR, ">", NULL)
+                   : cat("<TR BGCOLOR=", SANDBOX_COLOR, ">", NULL),
+
+            "<TD NOWRAP>[<A HREF=\"",
+
+            (i < g_extHtmlStmt)
+               ? g_htmlBibliography
+               : (i < g_mathboxStmt)
+                   ? extHtmlBibliography
+                   /* Note that the sandbox uses the mmset.html
+                      bibliography */
+                   : g_htmlBibliography,
+
+            "#",
+            str3,
+            "\">", str3, "</A>]", str4,
+            "</TD><TD>", str2, "</TD><TD><A HREF=\"",
+            g_Statement[i].labelName,
+            ".html\">", g_Statement[i].labelName, "</A>",
+            newstr, NULL));
+        /* Put construction into string array for sorting */
+        let((vstring *)(&pntrTmp[lines - 1]), oldstr);
+      } /* while(1) */
+    } /* next i */
+
+    /* 'lines' should be the same in both passes */
+    if (p2 == 1) {
+      pass1refs = lines;
+    } else {
+      if (pass1refs != lines) bug(2339);
+    }
+
+    if (errorsOnly == 0 && p2 == 2) {
+      /*
+      print2("Pass %ld finished.  %ld references were processed.\n", p2, lines);
+      */
+      print2("%ld references were processed.\n", lines);
+    }
+    if (p2 == 2) break;
+    p2++;    /* Increment from pass 1 to pass 2 */
+  } /* while(1) */
+
+  /* Sort */
+  g_qsortKey = "";
+  qsort(pntrTmp, (size_t)lines, sizeof(void *), qsortStringCmp);
+
+  /* Combine duplicate references */
+  let(&str1, "");  /* Last biblio ref */
+  for (i = 0; i < lines; i++) {
+    j = instr(1, (vstring)(pntrTmp[i]), "|||");
+    let(&str2, left((vstring)(pntrTmp[i]), j - 1));
+    if (!strcmp(str1, str2)) {
+      /* Combine last with this */
+      k = instr(j, (vstring)(pntrTmp[i]), "&&&");
+      /* Extract statement href */
+      let(&str3, seg((vstring)(pntrTmp[i]), j + 3, k -1));
+      let((vstring *)(&pntrTmp[i]),
+          cat((vstring)(pntrTmp[i - 1]), " &nbsp;", str3, NULL));
+      let((vstring *)(&pntrTmp[i - 1]), ""); /* Clear previous line */
+    }
+    let(&str1, str2);
+  }
+
+  /* Write output */
+  if (fileCheck && errorsOnly == 0) {
+    n = 0; /* Table rows written */
+    for (i = 0; i < lines; i++) {
+      j = instr(1, (vstring)(pntrTmp[i]), "&&&");
+      if (j) {  /* Don't print blanked out combined lines */
+        n++;
+        /* Take off prefixes and reduce spaces */
+        let(&str1, edit(right((vstring)(pntrTmp[i]), j + 3), 16));
+        j = 1;
+        /* Break up long lines for text editors */
+        let(&g_printString, "");
+        g_outputToString = 1;
+        printLongLine(cat(str1, "</TD></TR>", NULL),
+            " ",  /* Start continuation line with space */
+            "\""); /* Don't break inside quotes e.g. "Arial Narrow" */
+        g_outputToString = 0;
+        fprintf(list2_fp, "%s", g_printString);
+        free_vstring(g_printString);
+      }
+    }
+  }
+
+
+  /* Discard the input file up to the special "<!-- #END# -->" comment */
+  if (fileCheck) {
+    while (1) {
+      if (!linput(list1_fp, NULL, &str1)) {
+        print2(
+  "?Error: Could not find \"<!-- #END# -->\" line in input file \"%s\".\n",
+            bibFile);
+        errFlag = 2; /* Error flag to recover input file */
+        break;
+      }
+      if (!strcmp(str1, "<!-- #END# -->")) {
+        if (errorsOnly == 0) {
+          fprintf(list2_fp, "%s\n", str1);
+        }
+        break;
+      }
+    }
+    if (errFlag) goto BIB_ERROR;
+  }
+
+  if (fileCheck && errorsOnly == 0) {
+    /* Transfer the rest of the input file */
+    while (1) {
+      if (!linput(list1_fp, NULL, &str1)) {
+        break;
+      }
+
+      /* Update the date stamp at the bottom of the HTML page. */
+      /* This is just a nicety; no error check is done. */
+      if (!strcmp("This page was last updated on ", left(str1, 30))) {
+        let(&str1, cat(left(str1, 30), date(), ".", NULL));
+      }
+
+      fprintf(list2_fp, "%s\n", str1);
+    }
+
+    print2("%ld table rows were written.\n", n);
+    /* Deallocate string array */
+    for (i = 0; i < lines; i++) free_vstring(*(vstring *)(&pntrTmp[i]));
+    free_pntrString(pntrTmp);
+  }
+
+
+ BIB_ERROR:
+  if (fileCheck) {
+    fclose(list1_fp);
+    if (errorsOnly == 0) {
+      fclose(list2_fp);
+    }
+    if (errorsOnly == 0) {
+      if (errFlag) {
+        /* Recover input files in case of error */
+        remove(bibFile);  /* Delete output file */
+        rename(cat(bibFile, "~1", NULL), g_fullArg[2]);
+            /* Restore input file name */
+        print2("?The file \"%s\" was not modified.\n", g_fullArg[2]);
+      }
+    }
+  }
+  if (errFlag == 2) warnFlag = 2;
+  return warnFlag;
+}  /* writeBibliography */
+
+
+/* Returns 1 if stmt1 and stmt2 are in different mathboxes, 0 if
+   they are in the same mathbox or if one of them is not in a mathbox. */
+flag inDiffMathboxes(long stmt1, long stmt2) {
+  long mbox1, mbox2;
+  mbox1 = getMathboxNum(stmt1);
+  mbox2 = getMathboxNum(stmt2);
+  if (mbox1 == 0 || mbox2 == 0) return 0;
+  if (mbox1 != mbox2) return 1;
+  return 0;
+}
+
+/* Returns the user of the mathbox that a statement is in, or ""
+   if the statement is not in a mathbox. */
+/* Caller should NOT deallocate returned string (it points directly to
+   g_mathboxUser[] entry) */
+vstring getMathboxUser(long stmt) {
+  long mbox;
+  mbox = getMathboxNum(stmt);
+  if (mbox == 0) return "";
+  return g_mathboxUser[mbox - 1];
+}
+
+/* Given a statement number, find out what mathbox it's in (numbered starting
+   at 1) mainly for error messages; if it's not in a mathbox, return 0. */
+/* We assume the number of mathboxes is small enough that a linear search
+   won't slow things too much. */
+long getMathboxNum(long stmt) {
+  long mbox;
+  assignMathboxInfo(); /* In case it's not yet initialized */
+  for (mbox = 0; mbox < g_mathboxes; mbox++) {
+    if (stmt < g_mathboxStart[mbox]) break;
+  }
+  return mbox;
+} /* getMathboxNum */
+
+
+/* Assign the global variable g_mathboxStmt, the statement number with the
+   label "mathbox", as well as g_mathboxes, g_mathboxStart[], g_mathboxEnd[],
+   and g_mathboxUser[].  For speed, we do the lookup only if it hasn't been
+   done yet.   Note that the ERASE command (eraseSource()) should set
+   g_mathboxStmt to zero as well as deallocate the strings. */
+/* This function will just return if g_mathboxStmt is already nonzero. */
+#define MB_LABEL "mathbox"
+void assignMathboxInfo(void) {
+  if (g_mathboxStmt == 0) { /* Look up "mathbox" label if it hasn't been */
+    g_mathboxStmt = lookupLabel(MB_LABEL);
+    if (g_mathboxStmt == -1) { /* There are no mathboxes */
+      g_mathboxStmt = g_statements + 1;  /* Default beyond db end if none */
+      g_mathboxes = 0;
+    } else {
+      /* Population mathbox information variables */
+      g_mathboxes = getMathboxLoc(&g_mathboxStart, &g_mathboxEnd,
+          &g_mathboxUser);
+    }
+  }
+  return;
+} /* assignMathboxInfo */
+
+
+/* Returns the number of mathboxes, while assigning start statement, end
+   statement, and mathbox name. */
+#define MB_TAG "Mathbox for "
+long getMathboxLoc(nmbrString **mathboxStart, nmbrString **mathboxEnd,
+    pntrString **mathboxUser) {
+  long m, p, q, tagLen, stmt;
+  long mathboxes = 0;
+  vstring_def(comment);
+  vstring_def(user);
+  assignMathboxInfo(); /* Assign g_mathboxStmt */
+  tagLen = (long)strlen(MB_TAG);
+  /* Ensure lists are initialized */
+  if (pntrLen((pntrString *)(*mathboxUser)) != 0) bug(2347);
+  if (nmbrLen((nmbrString *)(*mathboxStart)) != 0) bug(2348);
+  if (nmbrLen((nmbrString *)(*mathboxEnd)) != 0) bug(2349);
+  for (stmt = g_mathboxStmt + 1; stmt <= g_statements; stmt++) {
+    /* Heuristic to match beginning of mathbox */
+    let(&comment, left(g_Statement[stmt].labelSectionPtr,
+        g_Statement[stmt].labelSectionLen));
+    p = 0;
+    /* This loop will skip empty mathboxes i.e. it will get the last
+       "Mathbox for " in the label section comment(s) */
+    while (1) {
+      q = instr(p + 1, comment, MB_TAG);
+      if (q == 0) break;
+      p = q; /* Save last "Mathbox for " */
+    }
+    if (p == 0) continue; /* No "Mathbox for " in this statement's comment */
+
+    /* Found a mathbox; assign user and start statement */
+    mathboxes++;
+    q = instr(p, comment, "\n");
+    if (q == 0) bug(2350); /* No end of line */
+    let(&user, seg(comment, p + tagLen, q - 1));
+    pntrLet(&(*mathboxUser), pntrAddElement(*mathboxUser));
+    (*mathboxUser)[mathboxes - 1] = "";
+    let((vstring *)(&((*mathboxUser)[mathboxes - 1])), user);
+    nmbrLet(&(*mathboxStart), nmbrAddElement(*mathboxStart, stmt));
+  } /* next stmt */
+  if (mathboxes == 0) goto RETURN_POINT;
+  /* Assign end statements */
+  nmbrLet(&(*mathboxEnd), nmbrSpace(mathboxes)); /* Pre-allocate */
+  for (m = 0; m < mathboxes - 1; m++) {
+    (*mathboxEnd)[m] = (*mathboxStart)[m + 1] - 1;
+  }
+  (*mathboxEnd)[mathboxes - 1] = g_statements; /* Assumed end of last mathbox */
+ RETURN_POINT:
+  free_vstring(comment);
+  free_vstring(user);
+  return mathboxes;
+} /* getMathboxLoc */
diff --git a/src/mmwtex.h b/src/mmwtex.h
new file mode 100644
index 0000000..fb9f057
--- /dev/null
+++ b/src/mmwtex.h
@@ -0,0 +1,218 @@
+/*****************************************************************************/
+/*        Copyright (C) 2020  NORMAN MEGILL  nm at alum.mit.edu              */
+/*            License terms:  GNU General Public License                     */
+/*****************************************************************************/
+/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
+
+#ifndef METAMATH_MMWTEX_H_
+#define METAMATH_MMWTEX_H_
+
+/*! \file */
+
+#include "mmvstr.h"
+#include "mmdata.h"
+
+/* Colors for HTML pages. */
+#define GREEN_TITLE_COLOR "\"#006633\""
+#define MINT_BACKGROUND_COLOR "\"#EEFFFA\""
+#define PINK_NUMBER_COLOR "\"#FA8072\""
+   /* =salmon; was FF6666 */
+#define PURPLISH_BIBLIO_COLOR "\"#FAEEFF\""
+#define SANDBOX_COLOR "\"#FFFFD9\""
+
+/* TeX flags */
+extern flag g_oldTexFlag; /*!< Use macros in output; obsolete; take out someday */
+
+/* HTML flags */
+extern flag g_htmlFlag;  /*!< HTML flag: 0 = TeX, 1 = HTML */
+extern flag g_altHtmlFlag;  /*!< Use "althtmldef" instead of "htmldef".  This is
+    intended to allow the generation of pages with the old Symbol font
+    instead of the individual GIF files. */
+extern flag g_briefHtmlFlag;  /*!< Output statement only, for statement display
+                in other HTML pages, such as the Proof Explorer home page */
+extern long g_extHtmlStmt; /*!< At this statement and above, use the exthtmlxxx
+    variables for title, links, etc.  This was put in to allow proper
+    generation of the Hilbert Space Explorer extension to the set.mm
+    database. */
+extern vstring g_extHtmlTitle; /*!< Title of extended section if any; set by
+    by exthtmltitle command in special $t comment of database source */
+extern vstring g_htmlVarColor; /*!< Set by htmlvarcolor commands */
+extern vstring g_htmlHome; /*!< Set by htmlhome command */
+extern vstring g_htmlBibliography; /*!< Optional; set by htmlbibliography command */
+extern vstring g_extHtmlBibliography; /*!< Optional; set by exthtmlbibliography
+                                       command */
+extern vstring g_htmlCSS; /*!< Set by htmlcss commands */
+extern vstring g_htmlFont; /*!< Optional; set by g_htmlFont command */
+
+/*! Undo readTexDefs() */
+void eraseTexDefs(void);
+
+/* TeX/HTML/ALT_HTML word-processor-specific routines */
+/*!
+  \param gifCheck The GIF check ensures that for every 'htmldef' definition containing
+   `IMG SRC="bla.gif"`, `bla.gif` must exist.
+  \returns 2 if there were severe parsing errors, 1 if there were warnings but
+    no errors, 0 if no errors or warnings
+*/
+flag readTexDefs(
+  flag errorsOnly,  /*!< 1 = suppress non-error messages */
+  flag gifCheck   /*!< 1 = check for missing GIFs */);
+
+extern flag g_texDefsRead;
+/*! for "erase" */
+struct texDef_struct {
+  vstring tokenName; /*!< ASCII token */
+  vstring texEquiv; /*!< Converted to TeX */
+};
+extern struct texDef_struct *g_TexDefs; /*!< for "erase" */
+
+
+long texDefWhiteSpaceLen(char *ptr);
+long texDefTokenLen(char *ptr);
+/*! Token comparison for qsort */
+int texSortCmp(const void *key1, const void *key2);
+/*! Token comparison for bsearch */
+int texSrchCmp(const void *key, const void *data);
+/*! Convert ascii to a string of \tt tex
+
+  (The caller must surround it by {\tt }) */
+vstring asciiToTt(vstring s);
+vstring tokenToTex(vstring mtoken, long statemNum);
+/*! Converts a comment section in math mode to TeX.  Each math token
+   MUST be separated by white space.   TeX "$" does not surround the output. */
+vstring asciiMathToTex(vstring mathComment, long statemNum);
+/*! Gets the next section of a comment that is in the current mode (text,
+   label, or math).  If 1st char. is not "$", text mode is assumed.
+   mode = 0 means end of comment reached.  srcptr is left at 1st char.
+   of start of next comment section. */
+vstring getCommentModeSection(vstring *srcptr, char *mode);
+void printTexHeader(flag texHeaderFlag);
+
+/*! Prints an embedded comment in TeX.  The commentPtr must point to the first
+   character after the "$(" in the comment.  The printout ends when the first
+   "$)" or null character is encountered.   commentPtr must not be a temporary
+   allocation.  htmlCenterFlag, if 1, means to center the HTML and add a
+   "Description:" prefix
+  \returns 1 if error/warning */
+/* void printTexComment(vstring commentPtr, char htmlCenterFlag); */
+flag printTexComment(vstring commentPtr,    /*!< Sends result to g_texFilePtr */
+    flag htmlCenterFlag, /*!< 1 = htmlCenterFlag */
+    long actionBits, /*!< see indicators below */
+    flag fileCheck /*!< 1 = fileCheck */);
+
+/* Indicators for actionBits */
+#define ERRORS_ONLY 1
+#define PROCESS_SYMBOLS 2
+#define PROCESS_LABELS 4
+#define ADD_COLORED_LABEL_NUMBER 8
+#define PROCESS_BIBREFS 16
+#define PROCESS_UNDERSCORES 32
+/* CONVERT_TO_HTML means '<' to '&lt;'; unless <HTML> in comment (and strip it) */
+#define CONVERT_TO_HTML 64
+/* METAMATH_COMMENT means $) (as well as end-of-string) terminates string. */
+#define METAMATH_COMMENT 128
+/* PROCESS_ALL is for convenience */
+#define PROCESS_EVERYTHING PROCESS_SYMBOLS + PROCESS_LABELS \
+     + ADD_COLORED_LABEL_NUMBER + PROCESS_BIBREFS \
+     + PROCESS_UNDERSCORES + CONVERT_TO_HTML + METAMATH_COMMENT
+
+void printTexLongMath(nmbrString *proofStep, vstring startPrefix,
+    vstring contPrefix, long hypStmt, long indentationLevel);
+void printTexTrailer(flag texHeaderFlag);
+
+/*! Function implementing WRITE THEOREM_LIST / THEOREMS_PER_PAGE nn */
+void writeTheoremList(long theoremsPerPage, flag showLemmas,
+    flag noVersioning);
+
+#define HUGE_DECORATION "####"
+#define BIG_DECORATION "#*#*"
+#define SMALL_DECORATION "=-=-"
+#define TINY_DECORATION "-.-."
+
+flag getSectionHeadings(long stmt, vstring *hugeHdrTitle,
+    vstring *bigHdrTitle,
+    vstring *smallHdrTitle,
+    vstring *tinyHdrTitle,
+    vstring *hugeHdrComment,
+    vstring *bigHdrComment,
+    vstring *smallHdrComment,
+    vstring *tinyHdrComment,
+    flag fineResolution, /*!< 0 = consider just successive $a/$p, 1 = all stmts */
+    flag fullComment /*!< 1 = put $( + header + comment + $) into xxxHdrTitle */
+    );
+
+/* TeX normal output */
+extern flag g_texFileOpenFlag;
+extern FILE *g_texFilePtr;
+
+/*! Pink statement number HTML code for HTML pages
+  \warning caller must deallocate returned string */
+vstring pinkHTML(long statemNum);
+
+/*! Pink statement number range HTML code for HTML pages, separated by a "-"
+  \warning caller must deallocate returned string */
+vstring pinkRangeHTML(long statemNum1, long statemNum2);
+
+#define PINK_NBSP "&nbsp;" /* Either "" or "&nbsp;" depending on taste, it is
+                  the separator between a statement href and its pink number */
+
+/*! This function converts a "spectrum" color (1 to maxColor) to an
+   RBG value in hex notation for HTML.  The caller must deallocate the
+   returned vstring.  color = 1 (red) to maxColor (violet). */
+vstring spectrumToRGB(long color, long maxColor);
+
+/*! Returns the HTML code for GIFs (!g_altHtmlFlag) or Unicode (g_altHtmlFlag),
+   or LaTeX when !g_htmlFlag, for the math string (hypothesis or conclusion) that
+   is passed in.
+  \warning The caller must deallocate the returned vstring. */
+vstring getTexLongMath(nmbrString *mathString, long statemNum);
+
+/*! Returns the TeX, or HTML code for GIFs (!g_altHtmlFlag) or Unicode
+   (g_altHtmlFlag), for a statement's hypotheses and assertion in the form
+   hyp & ... & hyp => assertion
+  \warning The caller must deallocate the returned vstring. */
+vstring getTexOrHtmlHypAndAssertion(long statemNum);
+
+/*!
+  \brief For WRITE BIBLIOGRAPHY command and error checking by VERIFY MARKUP
+
+  In addition to checking for GIFs (see readTexDefs()),
+  the `fileCheck` flag controls whether to attempt to open and parse the
+  `htmlbibliograhy` or `exthtmlbibliograhy` file to search for anchors,
+  which can be used to verify the correctness of bibliography tags.
+
+  \returns 0 if OK, 1 if error or warning found */
+flag writeBibliography(vstring bibFile,
+  vstring labelMatch, /*!< Normally "*" except by verifyMarkup() */
+  flag errorsOnly,  /*!< 1 = no output, just warning msgs if any */
+  flag fileCheck /*!< 1 = check external files (gifs and bib) */
+);
+
+/*! Globals to hold mathbox information.  They should be re-initialized
+   by the ERASE command (eraseSource()).  g_mathboxStmt = 0 indicates
+   it and the other variables haven't been initialized. */
+extern long g_mathboxStmt; /* stmt# of "mathbox" label; statements+1 if none */
+extern long g_mathboxes; /* # of mathboxes */
+/* The following 3 "strings" are 0-based e.g. g_mathboxStart[0] is for
+   mathbox #1 */
+extern nmbrString *g_mathboxStart; /*!< Start stmt vs. mathbox # */
+extern nmbrString *g_mathboxEnd; /*!< End stmt vs. mathbox # */
+extern pntrString *g_mathboxUser; /*!< User name vs. mathbox # */
+
+/*! Returns 1 if statements are in different mathboxes */
+flag inDiffMathboxes(long stmt1, long stmt2);
+/*! Returns the user of the mathbox that a statement is in, or ""
+   if the statement is not in a mathbox.
+  \attention Caller should NOT deallocate returned string (it points directly to
+   g_mathboxUser[] entry); use directly in print2() messages */
+vstring getMathboxUser(long stmt);
+/*! Returns the mathbox number (starting at 1) that stmt is in, or 0 if not
+   in a mathbox */
+long getMathboxNum(long stmt);
+/*! Populates mathbox information */
+void assignMathboxInfo(void);
+/*! Creates lists of mathbox starts and user names */
+long getMathboxLoc(nmbrString **mathboxStart, nmbrString **mathboxEnd,
+    pntrString **mathboxUser);
+
+#endif /* METAMATH_MMWTEX_H_ */