summaryrefslogtreecommitdiff
path: root/makeinfo
diff options
context:
space:
mode:
Diffstat (limited to 'makeinfo')
-rw-r--r--makeinfo/Makefile.am32
-rw-r--r--makeinfo/Makefile.in837
-rw-r--r--makeinfo/README17
-rw-r--r--makeinfo/cmds.c2184
-rw-r--r--makeinfo/cmds.h223
-rw-r--r--makeinfo/defun.c720
-rw-r--r--makeinfo/defun.h30
-rw-r--r--makeinfo/files.c783
-rw-r--r--makeinfo/files.h68
-rw-r--r--makeinfo/float.c430
-rw-r--r--makeinfo/float.h55
-rw-r--r--makeinfo/footnote.c390
-rw-r--r--makeinfo/footnote.h36
-rw-r--r--makeinfo/html.c870
-rw-r--r--makeinfo/html.h67
-rw-r--r--makeinfo/index.c1004
-rw-r--r--makeinfo/index.h92
-rw-r--r--makeinfo/insertion.c2403
-rw-r--r--makeinfo/insertion.h82
-rw-r--r--makeinfo/lang.c1811
-rw-r--r--makeinfo/lang.h141
-rw-r--r--makeinfo/macro.c1095
-rw-r--r--makeinfo/macro.h77
-rw-r--r--makeinfo/makeinfo.c4397
-rw-r--r--makeinfo/makeinfo.h395
-rw-r--r--makeinfo/multi.c646
-rw-r--r--makeinfo/multi.h26
-rw-r--r--makeinfo/node.c1947
-rw-r--r--makeinfo/node.h129
-rw-r--r--makeinfo/sectioning.c832
-rw-r--r--makeinfo/sectioning.h102
-rw-r--r--makeinfo/tests/Makefile.am40
-rw-r--r--makeinfo/tests/Makefile.in672
-rwxr-xr-xmakeinfo/tests/accent34
-rw-r--r--makeinfo/tests/accent-text.txi73
-rw-r--r--makeinfo/tests/accent.txi10
-rwxr-xr-xmakeinfo/tests/accentenc24
-rw-r--r--makeinfo/tests/accentenc.txi11
-rwxr-xr-xmakeinfo/tests/cond33
-rw-r--r--makeinfo/tests/cond.txi40
-rwxr-xr-xmakeinfo/tests/copying13
-rw-r--r--makeinfo/tests/copying.txi8
-rwxr-xr-xmakeinfo/tests/defxcond16
-rw-r--r--makeinfo/tests/defxcond.txi18
-rwxr-xr-xmakeinfo/tests/emph-option10
-rw-r--r--makeinfo/tests/emph-option.txi8
-rwxr-xr-xmakeinfo/tests/html-docdesc12
-rw-r--r--makeinfo/tests/html-docdesc.txi14
-rwxr-xr-xmakeinfo/tests/html-extrali14
-rw-r--r--makeinfo/tests/html-extrali.txi11
-rwxr-xr-xmakeinfo/tests/html-manuals13
-rwxr-xr-xmakeinfo/tests/html-min8
-rw-r--r--makeinfo/tests/html-min.txi12
-rwxr-xr-xmakeinfo/tests/html-para8
-rw-r--r--makeinfo/tests/html-para.txi24
-rwxr-xr-xmakeinfo/tests/html-title13
-rw-r--r--makeinfo/tests/html-title.txi12
-rwxr-xr-xmakeinfo/tests/html-top13
-rw-r--r--makeinfo/tests/html-top.txi7
-rw-r--r--makeinfo/tests/incl-incl.txi1
-rwxr-xr-xmakeinfo/tests/include-value22
-rw-r--r--makeinfo/tests/include-value.txi32
-rwxr-xr-xmakeinfo/tests/macro-at8
-rw-r--r--makeinfo/tests/macro-at.txi38
-rwxr-xr-xmakeinfo/tests/menu-whitespace72
-rw-r--r--makeinfo/tests/menu-whitespace.txi18
-rwxr-xr-xmakeinfo/tests/no-headers12
-rwxr-xr-xmakeinfo/tests/node-expand12
-rw-r--r--makeinfo/tests/node-expand.txi64
-rwxr-xr-xmakeinfo/tests/node-value12
-rw-r--r--makeinfo/tests/node-value.txi15
-rwxr-xr-xmakeinfo/tests/node-whitespace12
-rw-r--r--makeinfo/tests/node-whitespace.txi30
-rwxr-xr-xmakeinfo/tests/quote-args19
-rw-r--r--makeinfo/tests/quote-args.txi20
-rwxr-xr-xmakeinfo/tests/top11
-rw-r--r--makeinfo/tests/top.txi25
-rwxr-xr-xmakeinfo/tests/twofiles23
-rw-r--r--makeinfo/toc.c378
-rw-r--r--makeinfo/toc.h44
-rw-r--r--makeinfo/xml.c2252
-rw-r--r--makeinfo/xml.h159
-rw-r--r--makeinfo/xref.c632
-rw-r--r--makeinfo/xref.h29
84 files changed, 27032 insertions, 0 deletions
diff --git a/makeinfo/Makefile.am b/makeinfo/Makefile.am
new file mode 100644
index 0000000..694f915
--- /dev/null
+++ b/makeinfo/Makefile.am
@@ -0,0 +1,32 @@
+# $Id: Makefile.am,v 1.9 2006/06/21 08:08:48 akim Exp $
+# Makefile.am for texinfo/makeinfo.
+# Run automake in .. to produce Makefile.in from this.
+#
+# This file is free software; as a special exception the author 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.
+
+bin_PROGRAMS = makeinfo
+
+localedir = $(datadir)/locale
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/gnulib/lib \
+ -I$(top_builddir)/gnulib/lib \
+ -DLOCALEDIR=\"$(localedir)\"
+LDADD = ../lib/libtxi.a $(top_builddir)/gnulib/lib/libgnu.a $(LIBINTL)
+
+makeinfo_SOURCES = \
+ cmds.c cmds.h defun.c defun.h \
+ files.c files.h float.c float.h footnote.c footnote.h \
+ html.c html.h index.c index.h insertion.c insertion.h lang.c lang.h \
+ macro.c macro.h makeinfo.c makeinfo.h multi.c multi.h node.c node.h \
+ sectioning.c sectioning.h toc.c toc.h xml.c xml.h xref.c xref.h
+
+EXTRA_DIST = README
+
+SUBDIRS = tests
diff --git a/makeinfo/Makefile.in b/makeinfo/Makefile.in
new file mode 100644
index 0000000..4a19ad6
--- /dev/null
+++ b/makeinfo/Makefile.in
@@ -0,0 +1,837 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# $Id: Makefile.am,v 1.9 2006/06/21 08:08:48 akim Exp $
+# Makefile.am for texinfo/makeinfo.
+# Run automake in .. to produce Makefile.in from this.
+#
+# This file is free software; as a special exception the author 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.
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = makeinfo$(EXEEXT)
+subdir = makeinfo
+DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/gnulib/m4/alloca.m4 \
+ $(top_srcdir)/gnulib/m4/argz.m4 \
+ $(top_srcdir)/gnulib/m4/codeset.m4 \
+ $(top_srcdir)/gnulib/m4/eealloc.m4 \
+ $(top_srcdir)/gnulib/m4/environ.m4 \
+ $(top_srcdir)/gnulib/m4/error.m4 \
+ $(top_srcdir)/gnulib/m4/exitfail.m4 \
+ $(top_srcdir)/gnulib/m4/extensions.m4 \
+ $(top_srcdir)/gnulib/m4/getopt.m4 \
+ $(top_srcdir)/gnulib/m4/gettext.m4 \
+ $(top_srcdir)/gnulib/m4/gettimeofday.m4 \
+ $(top_srcdir)/gnulib/m4/glibc21.m4 \
+ $(top_srcdir)/gnulib/m4/gnulib-common.m4 \
+ $(top_srcdir)/gnulib/m4/gnulib-comp.m4 \
+ $(top_srcdir)/gnulib/m4/iconv.m4 \
+ $(top_srcdir)/gnulib/m4/include_next.m4 \
+ $(top_srcdir)/gnulib/m4/inline.m4 \
+ $(top_srcdir)/gnulib/m4/intlmacosx.m4 \
+ $(top_srcdir)/gnulib/m4/lib-ld.m4 \
+ $(top_srcdir)/gnulib/m4/lib-link.m4 \
+ $(top_srcdir)/gnulib/m4/lib-prefix.m4 \
+ $(top_srcdir)/gnulib/m4/localcharset.m4 \
+ $(top_srcdir)/gnulib/m4/longlong.m4 \
+ $(top_srcdir)/gnulib/m4/malloc.m4 \
+ $(top_srcdir)/gnulib/m4/malloca.m4 \
+ $(top_srcdir)/gnulib/m4/mbchar.m4 \
+ $(top_srcdir)/gnulib/m4/mbiter.m4 \
+ $(top_srcdir)/gnulib/m4/mbrtowc.m4 \
+ $(top_srcdir)/gnulib/m4/mbscasecmp.m4 \
+ $(top_srcdir)/gnulib/m4/mbschr.m4 \
+ $(top_srcdir)/gnulib/m4/mbslen.m4 \
+ $(top_srcdir)/gnulib/m4/mbsncasecmp.m4 \
+ $(top_srcdir)/gnulib/m4/mbsstr.m4 \
+ $(top_srcdir)/gnulib/m4/mbstate_t.m4 \
+ $(top_srcdir)/gnulib/m4/mbswidth.m4 \
+ $(top_srcdir)/gnulib/m4/memchr.m4 \
+ $(top_srcdir)/gnulib/m4/memcmp.m4 \
+ $(top_srcdir)/gnulib/m4/memcpy.m4 \
+ $(top_srcdir)/gnulib/m4/memmem.m4 \
+ $(top_srcdir)/gnulib/m4/memmove.m4 \
+ $(top_srcdir)/gnulib/m4/mempcpy.m4 \
+ $(top_srcdir)/gnulib/m4/mkstemp.m4 \
+ $(top_srcdir)/gnulib/m4/nls.m4 \
+ $(top_srcdir)/gnulib/m4/onceonly.m4 \
+ $(top_srcdir)/gnulib/m4/po.m4 \
+ $(top_srcdir)/gnulib/m4/progtest.m4 \
+ $(top_srcdir)/gnulib/m4/setenv.m4 \
+ $(top_srcdir)/gnulib/m4/stdbool.m4 \
+ $(top_srcdir)/gnulib/m4/stdint.m4 \
+ $(top_srcdir)/gnulib/m4/stdlib_h.m4 \
+ $(top_srcdir)/gnulib/m4/stpcpy.m4 \
+ $(top_srcdir)/gnulib/m4/strdup.m4 \
+ $(top_srcdir)/gnulib/m4/strerror.m4 \
+ $(top_srcdir)/gnulib/m4/string_h.m4 \
+ $(top_srcdir)/gnulib/m4/strndup.m4 \
+ $(top_srcdir)/gnulib/m4/strnlen.m4 \
+ $(top_srcdir)/gnulib/m4/sys_stat_h.m4 \
+ $(top_srcdir)/gnulib/m4/sys_time_h.m4 \
+ $(top_srcdir)/gnulib/m4/tempname.m4 \
+ $(top_srcdir)/gnulib/m4/unistd_h.m4 \
+ $(top_srcdir)/gnulib/m4/wchar.m4 \
+ $(top_srcdir)/gnulib/m4/wchar_t.m4 \
+ $(top_srcdir)/gnulib/m4/wctype.m4 \
+ $(top_srcdir)/gnulib/m4/wcwidth.m4 \
+ $(top_srcdir)/gnulib/m4/wint_t.m4 \
+ $(top_srcdir)/gnulib/m4/xalloc.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+am_makeinfo_OBJECTS = cmds.$(OBJEXT) defun.$(OBJEXT) files.$(OBJEXT) \
+ float.$(OBJEXT) footnote.$(OBJEXT) html.$(OBJEXT) \
+ index.$(OBJEXT) insertion.$(OBJEXT) lang.$(OBJEXT) \
+ macro.$(OBJEXT) makeinfo.$(OBJEXT) multi.$(OBJEXT) \
+ node.$(OBJEXT) sectioning.$(OBJEXT) toc.$(OBJEXT) \
+ xml.$(OBJEXT) xref.$(OBJEXT)
+makeinfo_OBJECTS = $(am_makeinfo_OBJECTS)
+makeinfo_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+makeinfo_DEPENDENCIES = ../lib/libtxi.a \
+ $(top_builddir)/gnulib/lib/libgnu.a $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(makeinfo_SOURCES)
+DIST_SOURCES = $(makeinfo_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALLOCA_H = @ALLOCA_H@
+AMTAR = @AMTAR@
+ARGZ_H = @ARGZ_H@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
+BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
+BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
+BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
+BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GETOPT_H = @GETOPT_H@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GLIBC21 = @GLIBC21@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@
+GNULIB_CHOWN = @GNULIB_CHOWN@
+GNULIB_DUP2 = @GNULIB_DUP2@
+GNULIB_ENVIRON = @GNULIB_ENVIRON@
+GNULIB_FCHDIR = @GNULIB_FCHDIR@
+GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@
+GNULIB_GETCWD = @GNULIB_GETCWD@
+GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@
+GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@
+GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@
+GNULIB_LCHOWN = @GNULIB_LCHOWN@
+GNULIB_LSEEK = @GNULIB_LSEEK@
+GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@
+GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@
+GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@
+GNULIB_MBSCHR = @GNULIB_MBSCHR@
+GNULIB_MBSCSPN = @GNULIB_MBSCSPN@
+GNULIB_MBSLEN = @GNULIB_MBSLEN@
+GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@
+GNULIB_MBSNLEN = @GNULIB_MBSNLEN@
+GNULIB_MBSPBRK = @GNULIB_MBSPBRK@
+GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@
+GNULIB_MBSRCHR = @GNULIB_MBSRCHR@
+GNULIB_MBSSEP = @GNULIB_MBSSEP@
+GNULIB_MBSSPN = @GNULIB_MBSSPN@
+GNULIB_MBSSTR = @GNULIB_MBSSTR@
+GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@
+GNULIB_MEMMEM = @GNULIB_MEMMEM@
+GNULIB_MEMPCPY = @GNULIB_MEMPCPY@
+GNULIB_MEMRCHR = @GNULIB_MEMRCHR@
+GNULIB_MKDTEMP = @GNULIB_MKDTEMP@
+GNULIB_MKSTEMP = @GNULIB_MKSTEMP@
+GNULIB_PUTENV = @GNULIB_PUTENV@
+GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@
+GNULIB_READLINK = @GNULIB_READLINK@
+GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@
+GNULIB_RPMATCH = @GNULIB_RPMATCH@
+GNULIB_SETENV = @GNULIB_SETENV@
+GNULIB_SLEEP = @GNULIB_SLEEP@
+GNULIB_STPCPY = @GNULIB_STPCPY@
+GNULIB_STPNCPY = @GNULIB_STPNCPY@
+GNULIB_STRCASESTR = @GNULIB_STRCASESTR@
+GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@
+GNULIB_STRDUP = @GNULIB_STRDUP@
+GNULIB_STRERROR = @GNULIB_STRERROR@
+GNULIB_STRNDUP = @GNULIB_STRNDUP@
+GNULIB_STRNLEN = @GNULIB_STRNLEN@
+GNULIB_STRPBRK = @GNULIB_STRPBRK@
+GNULIB_STRSEP = @GNULIB_STRSEP@
+GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@
+GNULIB_STRSTR = @GNULIB_STRSTR@
+GNULIB_STRTOD = @GNULIB_STRTOD@
+GNULIB_STRTOK_R = @GNULIB_STRTOK_R@
+GNULIB_UNSETENV = @GNULIB_UNSETENV@
+GNULIB_WCWIDTH = @GNULIB_WCWIDTH@
+GREP = @GREP@
+HAVE_CALLOC_POSIX = @HAVE_CALLOC_POSIX@
+HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@
+HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@
+HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@
+HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@
+HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@
+HAVE_DECL_STRERROR = @HAVE_DECL_STRERROR@
+HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@
+HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@
+HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@
+HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@
+HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@
+HAVE_DUP2 = @HAVE_DUP2@
+HAVE_FTRUNCATE = @HAVE_FTRUNCATE@
+HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@
+HAVE_GETSUBOPT = @HAVE_GETSUBOPT@
+HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
+HAVE_ISWCNTRL = @HAVE_ISWCNTRL@
+HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@
+HAVE_LSTAT = @HAVE_LSTAT@
+HAVE_MALLOC_POSIX = @HAVE_MALLOC_POSIX@
+HAVE_MEMPCPY = @HAVE_MEMPCPY@
+HAVE_MKDTEMP = @HAVE_MKDTEMP@
+HAVE_OS_H = @HAVE_OS_H@
+HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@
+HAVE_READLINK = @HAVE_READLINK@
+HAVE_REALLOC_POSIX = @HAVE_REALLOC_POSIX@
+HAVE_RPMATCH = @HAVE_RPMATCH@
+HAVE_SETENV = @HAVE_SETENV@
+HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
+HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
+HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
+HAVE_SLEEP = @HAVE_SLEEP@
+HAVE_STDINT_H = @HAVE_STDINT_H@
+HAVE_STPCPY = @HAVE_STPCPY@
+HAVE_STPNCPY = @HAVE_STPNCPY@
+HAVE_STRCASESTR = @HAVE_STRCASESTR@
+HAVE_STRCHRNUL = @HAVE_STRCHRNUL@
+HAVE_STRNDUP = @HAVE_STRNDUP@
+HAVE_STRPBRK = @HAVE_STRPBRK@
+HAVE_STRSEP = @HAVE_STRSEP@
+HAVE_STRTOD = @HAVE_STRTOD@
+HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@
+HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@
+HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@
+HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@
+HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@
+HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
+HAVE_UNISTD_H = @HAVE_UNISTD_H@
+HAVE_UNSETENV = @HAVE_UNSETENV@
+HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@
+HAVE_WCHAR_H = @HAVE_WCHAR_H@
+HAVE_WCTYPE_H = @HAVE_WCTYPE_H@
+HAVE_WINT_T = @HAVE_WINT_T@
+HAVE__BOOL = @HAVE__BOOL@
+HELP2MAN = @HELP2MAN@
+HEVEA = @HEVEA@
+INCLUDE_NEXT = @INCLUDE_NEXT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBGNU_LIBDEPS = @LIBGNU_LIBDEPS@
+LIBGNU_LTLIBDEPS = @LIBGNU_LTLIBDEPS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEXT_STDINT_H = @NEXT_STDINT_H@
+NEXT_STDLIB_H = @NEXT_STDLIB_H@
+NEXT_STRING_H = @NEXT_STRING_H@
+NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@
+NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@
+NEXT_UNISTD_H = @NEXT_UNISTD_H@
+NEXT_WCHAR_H = @NEXT_WCHAR_H@
+NEXT_WCTYPE_H = @NEXT_WCTYPE_H@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
+RANLIB = @RANLIB@
+REPLACE_CHOWN = @REPLACE_CHOWN@
+REPLACE_FCHDIR = @REPLACE_FCHDIR@
+REPLACE_GETCWD = @REPLACE_GETCWD@
+REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@
+REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@
+REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@
+REPLACE_LCHOWN = @REPLACE_LCHOWN@
+REPLACE_LSEEK = @REPLACE_LSEEK@
+REPLACE_MEMMEM = @REPLACE_MEMMEM@
+REPLACE_MKDIR = @REPLACE_MKDIR@
+REPLACE_MKSTEMP = @REPLACE_MKSTEMP@
+REPLACE_PUTENV = @REPLACE_PUTENV@
+REPLACE_STRCASESTR = @REPLACE_STRCASESTR@
+REPLACE_STRERROR = @REPLACE_STRERROR@
+REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@
+REPLACE_STRSTR = @REPLACE_STRSTR@
+REPLACE_STRTOD = @REPLACE_STRTOD@
+REPLACE_WCWIDTH = @REPLACE_WCWIDTH@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
+SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
+STDBOOL_H = @STDBOOL_H@
+STDINT_H = @STDINT_H@
+STRIP = @STRIP@
+SYS_STAT_H = @SYS_STAT_H@
+SYS_TIME_H = @SYS_TIME_H@
+TERMLIBS = @TERMLIBS@
+TEX = @TEX@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VOID_UNSETENV = @VOID_UNSETENV@
+WCHAR_H = @WCHAR_H@
+WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
+WCTYPE_H = @WCTYPE_H@
+WINT_T_SUFFIX = @WINT_T_SUFFIX@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+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@
+gl_LIBOBJS = @gl_LIBOBJS@
+gl_LTLIBOBJS = @gl_LTLIBOBJS@
+gltests_LIBOBJS = @gltests_LIBOBJS@
+gltests_LTLIBOBJS = @gltests_LTLIBOBJS@
+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 = $(datadir)/locale
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+native_tools = @native_tools@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/gnulib/lib \
+ -I$(top_builddir)/gnulib/lib \
+ -DLOCALEDIR=\"$(localedir)\"
+
+LDADD = ../lib/libtxi.a $(top_builddir)/gnulib/lib/libgnu.a $(LIBINTL)
+makeinfo_SOURCES = \
+ cmds.c cmds.h defun.c defun.h \
+ files.c files.h float.c float.h footnote.c footnote.h \
+ html.c html.h index.c index.h insertion.c insertion.h lang.c lang.h \
+ macro.c macro.h makeinfo.c makeinfo.h multi.c multi.h node.c node.h \
+ sectioning.c sectioning.h toc.c toc.h xml.c xml.h xref.c xref.h
+
+EXTRA_DIST = README
+SUBDIRS = tests
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu makeinfo/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu makeinfo/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+makeinfo$(EXEEXT): $(makeinfo_OBJECTS) $(makeinfo_DEPENDENCIES)
+ @rm -f makeinfo$(EXEEXT)
+ $(LINK) $(makeinfo_OBJECTS) $(makeinfo_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmds.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defun.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/files.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/float.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/footnote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/html.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/insertion.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lang.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macro.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/makeinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/multi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sectioning.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/toc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xref.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+ install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic ctags ctags-recursive 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-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-binPROGRAMS
+
+# 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/makeinfo/README b/makeinfo/README
new file mode 100644
index 0000000..17c4371
--- /dev/null
+++ b/makeinfo/README
@@ -0,0 +1,17 @@
+$Id: README,v 1.3 2004/04/11 17:56:46 karl Exp $
+texinfo/makeinfo/README
+
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved.
+
+makeinfo is a standalone program to convert Texinfo source into Info
+files readable with standalone info or M-x info in Emacs.
+
+makeinfo can also output other formats: plain ASCII (with --no-headers)
+or HTML (with --html) or XML (with --xml).
+
+The Emacs function M-x texinfo-format-buffer does more or less the same
+job, but makeinfo is faster and gives better error messages.
diff --git a/makeinfo/cmds.c b/makeinfo/cmds.c
new file mode 100644
index 0000000..be7ce45
--- /dev/null
+++ b/makeinfo/cmds.c
@@ -0,0 +1,2184 @@
+/* cmds.c -- Texinfo commands.
+ $Id: cmds.c,v 1.83 2008/04/09 17:07:31 karl Exp $
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ 2007, 2008 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "mbswidth.h"
+#include "cmds.h"
+#include "defun.h"
+#include "files.h"
+#include "footnote.h"
+#include "html.h"
+#include "index.h"
+#include "insertion.h"
+#include "lang.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "node.h"
+#include "sectioning.h"
+#include "toc.h"
+#include "xml.h"
+
+#ifdef TM_IN_SYS_TIME
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+/* Simple commands defined and only called here. */
+static void cm_exampleindent (void),
+ cm_firstparagraphindent (void),
+ cm_fonttextsize (void),
+ cm_frenchspacing (void),
+ cm_geq (int arg),
+ cm_leq (int arg),
+ cm_minus (int arg),
+ cm_novalidate (void),
+ cm_paragraphindent (void);
+
+/* Internals. */
+static void cm_obsolete (int arg, int start, int end),
+ not_fixed_width (int arg);
+
+/* The dispatch table. */
+COMMAND command_table[] = {
+ { "\t", insert_space, NO_BRACE_ARGS },
+ { "\n", insert_space, NO_BRACE_ARGS },
+ { " ", insert_space, NO_BRACE_ARGS },
+ { "!", cm_punct, NO_BRACE_ARGS },
+ { "\"", cm_accent_umlaut, MAYBE_BRACE_ARGS },
+ { "'", cm_accent_acute, MAYBE_BRACE_ARGS },
+ { "*", cm_asterisk, NO_BRACE_ARGS },
+ { ",", cm_accent_cedilla, MAYBE_BRACE_ARGS },
+ { "-", cm_no_op, NO_BRACE_ARGS },
+ { ".", cm_punct, NO_BRACE_ARGS },
+ { "/", cm_no_op, NO_BRACE_ARGS },
+ { ":", cm_colon, NO_BRACE_ARGS },
+ { "=", cm_accent, MAYBE_BRACE_ARGS },
+ { "?", cm_punct, NO_BRACE_ARGS },
+ { "@", insert_self, NO_BRACE_ARGS },
+ { "\\", insert_self, NO_BRACE_ARGS },
+ { "^", cm_accent_hat, MAYBE_BRACE_ARGS },
+ { "`", cm_accent_grave, MAYBE_BRACE_ARGS },
+ { "{", insert_self, NO_BRACE_ARGS },
+ { "|", cm_no_op, NO_BRACE_ARGS },
+ { "}", insert_self, NO_BRACE_ARGS },
+ { "~", cm_accent_tilde, MAYBE_BRACE_ARGS },
+ { "AA", cm_special_char, BRACE_ARGS },
+ { "AE", cm_special_char, BRACE_ARGS },
+ { "H", cm_accent, MAYBE_BRACE_ARGS },
+ { "L", cm_special_char, BRACE_ARGS },
+ { "LaTeX", cm_LaTeX, BRACE_ARGS },
+ { "O", cm_special_char, BRACE_ARGS },
+ { "OE", cm_special_char, BRACE_ARGS },
+ { "TeX", cm_TeX, BRACE_ARGS },
+ { "aa", cm_special_char, BRACE_ARGS },
+ { "abbr", cm_abbr, BRACE_ARGS },
+ { "acronym", cm_acronym, BRACE_ARGS },
+ { "ae", cm_special_char, BRACE_ARGS },
+ { "afivepaper", cm_ignore_line, NO_BRACE_ARGS },
+ { "afourlatex", cm_ignore_line, NO_BRACE_ARGS },
+ { "afourpaper", cm_ignore_line, NO_BRACE_ARGS },
+ { "afourwide", cm_ignore_line, NO_BRACE_ARGS },
+ { "alias", cm_alias, NO_BRACE_ARGS },
+ { "allowcodebreaks", cm_ignore_line, NO_BRACE_ARGS },
+ { "anchor", cm_anchor, BRACE_ARGS },
+ { "appendix", cm_appendix, NO_BRACE_ARGS },
+ { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
+ { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
+ { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
+ { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
+ { "arrow", cm_arrow, BRACE_ARGS },
+ { "asis", cm_no_op, BRACE_ARGS },
+ { "author", cm_author, NO_BRACE_ARGS },
+ { "b", cm_b, BRACE_ARGS },
+ { "bullet", cm_bullet, BRACE_ARGS },
+ { "bye", cm_bye, NO_BRACE_ARGS },
+ { "c", cm_comment, NO_BRACE_ARGS },
+ { "caption", cm_caption, BRACE_ARGS },
+ { "cartouche", cm_cartouche, NO_BRACE_ARGS },
+ { "center", cm_center, NO_BRACE_ARGS },
+ { "centerchap", cm_unnumbered, NO_BRACE_ARGS },
+ { "chapheading", cm_chapheading, NO_BRACE_ARGS },
+ { "chapter", cm_chapter, NO_BRACE_ARGS },
+ { "cindex", cm_cindex, NO_BRACE_ARGS },
+ { "cite", cm_cite, BRACE_ARGS },
+ { "clear", cm_clear, NO_BRACE_ARGS },
+ { "click", cm_click, BRACE_ARGS },
+ { "clicksequence", cm_clicksequence, BRACE_ARGS },
+ { "clickstyle", cm_clickstyle, NO_BRACE_ARGS },
+ { "code", cm_code, BRACE_ARGS },
+ { "comma", cm_comma, BRACE_ARGS },
+ { "command", cm_code, BRACE_ARGS },
+ { "comment", cm_comment, NO_BRACE_ARGS },
+ { "contents", cm_contents, NO_BRACE_ARGS },
+ { "copying", cm_copying, NO_BRACE_ARGS },
+ { "copyright", cm_copyright, BRACE_ARGS },
+ { "ctrl", cm_obsolete, BRACE_ARGS },
+ { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
+ { "defcv", cm_defun, NO_BRACE_ARGS },
+ { "defcvx", cm_defun, NO_BRACE_ARGS },
+ { "deffn", cm_defun, NO_BRACE_ARGS },
+ { "deffnx", cm_defun, NO_BRACE_ARGS },
+ { "defindex", cm_defindex, NO_BRACE_ARGS },
+ { "definfoenclose", cm_definfoenclose, NO_BRACE_ARGS },
+ { "defivar", cm_defun, NO_BRACE_ARGS },
+ { "defivarx", cm_defun, NO_BRACE_ARGS },
+ { "defmac", cm_defun, NO_BRACE_ARGS },
+ { "defmacx", cm_defun, NO_BRACE_ARGS },
+ { "defmethod", cm_defun, NO_BRACE_ARGS },
+ { "defmethodx", cm_defun, NO_BRACE_ARGS },
+ { "defop", cm_defun, NO_BRACE_ARGS },
+ { "defopt", cm_defun, NO_BRACE_ARGS },
+ { "defoptx", cm_defun, NO_BRACE_ARGS },
+ { "defopx", cm_defun, NO_BRACE_ARGS },
+ { "defspec", cm_defun, NO_BRACE_ARGS },
+ { "defspecx", cm_defun, NO_BRACE_ARGS },
+ { "deftp", cm_defun, NO_BRACE_ARGS },
+ { "deftpx", cm_defun, NO_BRACE_ARGS },
+ { "deftypecv", cm_defun, NO_BRACE_ARGS },
+ { "deftypecvx", cm_defun, NO_BRACE_ARGS },
+ { "deftypefn", cm_defun, NO_BRACE_ARGS },
+ { "deftypefnx", cm_defun, NO_BRACE_ARGS },
+ { "deftypefun", cm_defun, NO_BRACE_ARGS },
+ { "deftypefunx", cm_defun, NO_BRACE_ARGS },
+ { "deftypeivar", cm_defun, NO_BRACE_ARGS },
+ { "deftypeivarx", cm_defun, NO_BRACE_ARGS },
+ { "deftypemethod", cm_defun, NO_BRACE_ARGS },
+ { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
+ { "deftypeop", cm_defun, NO_BRACE_ARGS },
+ { "deftypeopx", cm_defun, NO_BRACE_ARGS },
+ { "deftypevar", cm_defun, NO_BRACE_ARGS },
+ { "deftypevarx", cm_defun, NO_BRACE_ARGS },
+ { "deftypevr", cm_defun, NO_BRACE_ARGS },
+ { "deftypevrx", cm_defun, NO_BRACE_ARGS },
+ { "defun", cm_defun, NO_BRACE_ARGS },
+ { "defunx", cm_defun, NO_BRACE_ARGS },
+ { "defvar", cm_defun, NO_BRACE_ARGS },
+ { "defvarx", cm_defun, NO_BRACE_ARGS },
+ { "defvr", cm_defun, NO_BRACE_ARGS },
+ { "defvrx", cm_defun, NO_BRACE_ARGS },
+ { "detailmenu", cm_detailmenu, NO_BRACE_ARGS },
+ { "dfn", cm_dfn, BRACE_ARGS },
+ { "dircategory", cm_dircategory, NO_BRACE_ARGS },
+ { "direntry", cm_direntry, NO_BRACE_ARGS },
+ { "display", cm_display, NO_BRACE_ARGS },
+ { "dmn", cm_dmn, BRACE_ARGS },
+ { "docbook", cm_docbook, NO_BRACE_ARGS },
+ { "documentdescription", cm_documentdescription, NO_BRACE_ARGS },
+ { "documentencoding", cm_documentencoding, NO_BRACE_ARGS },
+ { "documentlanguage", cm_documentlanguage, NO_BRACE_ARGS },
+ { "dotaccent", cm_accent, MAYBE_BRACE_ARGS },
+ { "dotless", cm_dotless, BRACE_ARGS },
+ { "dots", cm_dots, BRACE_ARGS },
+ { "email", cm_email, BRACE_ARGS },
+ { "emph", cm_emph, BRACE_ARGS },
+ { "end", cm_end, NO_BRACE_ARGS },
+ { "enddots", cm_enddots, BRACE_ARGS },
+ { "enumerate", cm_enumerate, NO_BRACE_ARGS },
+ { "env", cm_code, BRACE_ARGS },
+ { "equiv", cm_equiv, BRACE_ARGS },
+ { "error", cm_error, BRACE_ARGS },
+ { "euro", cm_special_char, BRACE_ARGS },
+ { "evenfooting", cm_ignore_line, NO_BRACE_ARGS },
+ { "evenfootingmarks", cm_ignore_line, NO_BRACE_ARGS },
+ { "evenheading", cm_ignore_line, NO_BRACE_ARGS },
+ { "evenheadingmarks", cm_ignore_line, NO_BRACE_ARGS },
+ { "everyfooting", cm_ignore_line, NO_BRACE_ARGS },
+ { "everyfootingmarks", cm_ignore_line, NO_BRACE_ARGS },
+ { "everyheading", cm_ignore_line, NO_BRACE_ARGS },
+ { "everyheadingmarks", cm_ignore_line, NO_BRACE_ARGS },
+ { "example", cm_example, NO_BRACE_ARGS },
+ { "exampleindent", cm_exampleindent, NO_BRACE_ARGS },
+ { "exclamdown", cm_special_char, BRACE_ARGS },
+ { "exdent", cm_exdent, NO_BRACE_ARGS },
+ { "expansion", cm_expansion, BRACE_ARGS },
+ { "file", cm_code, BRACE_ARGS },
+ { "finalout", cm_no_op, NO_BRACE_ARGS },
+ { "findex", cm_findex, NO_BRACE_ARGS },
+ { "firstparagraphindent", cm_firstparagraphindent, NO_BRACE_ARGS },
+ { "float", cm_float, NO_BRACE_ARGS },
+ { "flushleft", cm_flushleft, NO_BRACE_ARGS },
+ { "flushright", cm_flushright, NO_BRACE_ARGS },
+ { "fonttextsize", cm_fonttextsize, NO_BRACE_ARGS },
+ { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
+ { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
+ { "format", cm_format, NO_BRACE_ARGS },
+ { "frenchspacing", cm_frenchspacing, NO_BRACE_ARGS },
+ { "ftable", cm_ftable, NO_BRACE_ARGS },
+ { "geq", cm_geq, BRACE_ARGS },
+ { "group", cm_group, NO_BRACE_ARGS },
+ { "guillemetleft", cm_special_char, BRACE_ARGS },
+ { "guillemetright", cm_special_char, BRACE_ARGS },
+ { "guillemotleft", cm_special_char, BRACE_ARGS },
+ { "guillemotright", cm_special_char, BRACE_ARGS },
+ { "guilsinglleft", cm_guilsinglleft, BRACE_ARGS },
+ { "guilsinglright", cm_guilsinglright, BRACE_ARGS },
+ { "heading", cm_heading, NO_BRACE_ARGS },
+ { "headings", cm_ignore_line, NO_BRACE_ARGS },
+ { "headitem", cm_headitem, NO_BRACE_ARGS },
+ { "html", cm_html, NO_BRACE_ARGS },
+ { "hyphenation", cm_ignore_arg, BRACE_ARGS },
+ { "i", cm_i, BRACE_ARGS },
+ { "ifclear", cm_ifclear, NO_BRACE_ARGS },
+ { "ifeq", cm_ifeq, NO_BRACE_ARGS },
+ { "ifdocbook", cm_ifdocbook, NO_BRACE_ARGS },
+ { "ifhtml", cm_ifhtml, NO_BRACE_ARGS },
+ { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
+ { "ifnotdocbook", cm_ifnotdocbook, NO_BRACE_ARGS },
+ { "ifnothtml", cm_ifnothtml, NO_BRACE_ARGS },
+ { "ifnotinfo", cm_ifnotinfo, NO_BRACE_ARGS },
+ { "ifnotplaintext", cm_ifnotplaintext, NO_BRACE_ARGS },
+ { "ifnottex", cm_ifnottex, NO_BRACE_ARGS },
+ { "ifnotxml", cm_ifnotxml, NO_BRACE_ARGS },
+ { "ifplaintext", cm_ifplaintext, NO_BRACE_ARGS },
+ { "ifset", cm_ifset, NO_BRACE_ARGS },
+ { "iftex", cm_iftex, NO_BRACE_ARGS },
+ { "ifxml", cm_ifxml, NO_BRACE_ARGS },
+ { "ignore", command_name_condition, NO_BRACE_ARGS },
+ { "image", cm_image, BRACE_ARGS },
+ { "include", cm_include, NO_BRACE_ARGS },
+ { "indent", cm_indent, NO_BRACE_ARGS },
+ { "indicateurl", cm_indicate_url, BRACE_ARGS },
+ { "inforef", cm_inforef, BRACE_ARGS },
+ { "insertcopying", cm_insert_copying, NO_BRACE_ARGS },
+ { "item", cm_item, NO_BRACE_ARGS },
+ { "itemize", cm_itemize, NO_BRACE_ARGS },
+ { "itemx", cm_itemx, NO_BRACE_ARGS },
+ { "kbd", cm_kbd, BRACE_ARGS },
+ { "kbdinputstyle", cm_ignore_line, NO_BRACE_ARGS },
+ { "key", cm_key, BRACE_ARGS },
+ { "kindex", cm_kindex, NO_BRACE_ARGS },
+ { "l", cm_special_char, BRACE_ARGS },
+ { "leq", cm_leq, BRACE_ARGS },
+ { "lisp", cm_lisp, NO_BRACE_ARGS },
+ { "listoffloats", cm_listoffloats, NO_BRACE_ARGS },
+ { "lowersections", cm_lowersections, NO_BRACE_ARGS },
+ { "macro", cm_macro, NO_BRACE_ARGS },
+ { "majorheading", cm_majorheading, NO_BRACE_ARGS },
+ { "math", cm_math, BRACE_ARGS },
+ { "menu", cm_menu, NO_BRACE_ARGS },
+ { "minus", cm_minus, BRACE_ARGS },
+ { "multitable", cm_multitable, NO_BRACE_ARGS },
+ { "need", cm_ignore_line, NO_BRACE_ARGS },
+ { "node", cm_node, NO_BRACE_ARGS },
+ { "noindent", cm_noindent_cmd, NO_BRACE_ARGS },
+ { "novalidate", cm_novalidate, NO_BRACE_ARGS },
+ { "nwnode", cm_node, NO_BRACE_ARGS },
+ { "o", cm_special_char, BRACE_ARGS },
+ { "oddfooting", cm_ignore_line, NO_BRACE_ARGS },
+ { "oddfootingmarks", cm_ignore_line, NO_BRACE_ARGS },
+ { "oddheading", cm_ignore_line, NO_BRACE_ARGS },
+ { "oddheadingmarks", cm_ignore_line, NO_BRACE_ARGS },
+ { "oe", cm_special_char, BRACE_ARGS },
+ { "option", cm_code, BRACE_ARGS },
+ { "ordf", cm_special_char, BRACE_ARGS },
+ { "ordm", cm_special_char, BRACE_ARGS },
+ { "page", cm_no_op, NO_BRACE_ARGS },
+ { "pagesizes", cm_ignore_line, NO_BRACE_ARGS },
+ { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
+ { "pindex", cm_pindex, NO_BRACE_ARGS },
+ { "point", cm_point, BRACE_ARGS },
+ { "pounds", cm_special_char, BRACE_ARGS },
+ { "print", cm_print, BRACE_ARGS },
+ { "printindex", cm_printindex, NO_BRACE_ARGS },
+ { "pxref", cm_pxref, BRACE_ARGS },
+ { "questiondown", cm_special_char, BRACE_ARGS },
+ { "quotation", cm_quotation, NO_BRACE_ARGS },
+ { "quotedblbase", cm_quotedblbase, BRACE_ARGS },
+ { "quotedblleft", cm_quotedblleft, BRACE_ARGS },
+ { "quotedblright", cm_quotedblright, BRACE_ARGS },
+ { "quoteleft", cm_quoteleft, BRACE_ARGS },
+ { "quoteright", cm_quoteright, BRACE_ARGS },
+ { "quotesinglbase", cm_quotesinglbase, BRACE_ARGS },
+ { "r", cm_r, BRACE_ARGS },
+ { "raisesections", cm_raisesections, NO_BRACE_ARGS },
+ { "ref", cm_ref, BRACE_ARGS },
+ { "refill", cm_no_op, NO_BRACE_ARGS },
+ { "registeredsymbol", cm_registeredsymbol, BRACE_ARGS },
+ { "result", cm_result, BRACE_ARGS },
+ { "ringaccent", cm_accent, MAYBE_BRACE_ARGS },
+ { "rmacro", cm_rmacro, NO_BRACE_ARGS },
+ { "samp", cm_code, BRACE_ARGS },
+ { "sansserif", cm_sansserif, BRACE_ARGS },
+ { "sc", cm_sc, BRACE_ARGS },
+ { "section", cm_section, NO_BRACE_ARGS },
+ { "set", cm_set, NO_BRACE_ARGS },
+ { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
+ { "setchapterstyle", cm_obsolete, NO_BRACE_ARGS },
+ { "setcontentsaftertitlepage", cm_no_op, NO_BRACE_ARGS },
+ { "setfilename", cm_setfilename, NO_BRACE_ARGS },
+ { "setshortcontentsaftertitlepage", cm_no_op, NO_BRACE_ARGS },
+ { "settitle", cm_settitle, NO_BRACE_ARGS },
+ { "shortcaption", cm_caption, BRACE_ARGS },
+ { "shortcontents", cm_contents, NO_BRACE_ARGS },
+ { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS },
+ { "slanted", cm_slanted, BRACE_ARGS },
+ { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
+ { "smalldisplay", cm_smalldisplay, NO_BRACE_ARGS },
+ { "smallexample", cm_smallexample, NO_BRACE_ARGS },
+ { "smallformat", cm_smallformat, NO_BRACE_ARGS },
+ { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
+ { "sp", cm_sp, NO_BRACE_ARGS },
+ { "ss", cm_special_char, BRACE_ARGS },
+ { "strong", cm_strong, BRACE_ARGS },
+ { "subheading", cm_subheading, NO_BRACE_ARGS },
+ { "subsection", cm_subsection, NO_BRACE_ARGS },
+ { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
+ { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
+ { "subtitle", cm_titlepage_cmds, NO_BRACE_ARGS },
+ { "summarycontents", cm_contents, NO_BRACE_ARGS },
+ { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
+ { "synindex", cm_synindex, NO_BRACE_ARGS },
+ { "t", cm_tt, BRACE_ARGS },
+ { "tab", cm_tab, NO_BRACE_ARGS },
+ { "table", cm_table, NO_BRACE_ARGS },
+ { "tex", cm_tex, NO_BRACE_ARGS },
+ { "textdegree", cm_special_char, BRACE_ARGS },
+ { "tie", cm_tie, BRACE_ARGS },
+ { "tieaccent", cm_accent, MAYBE_BRACE_ARGS },
+ { "tindex", cm_tindex, NO_BRACE_ARGS },
+ { "title", cm_titlepage_cmds, NO_BRACE_ARGS },
+ { "titlefont", cm_titlefont, BRACE_ARGS },
+ { "titlepage", cm_titlepage, NO_BRACE_ARGS },
+ { "today", cm_today, BRACE_ARGS },
+ { "top", cm_top, NO_BRACE_ARGS },
+ { "u", cm_accent, MAYBE_BRACE_ARGS },
+ { "ubaraccent", cm_accent, MAYBE_BRACE_ARGS },
+ { "udotaccent", cm_accent, MAYBE_BRACE_ARGS },
+ { "unmacro", cm_unmacro, NO_BRACE_ARGS },
+ { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
+ { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
+ { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
+ { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
+ { "uref", cm_uref, BRACE_ARGS },
+ { "url", cm_uref, BRACE_ARGS },
+ { "v", cm_accent, MAYBE_BRACE_ARGS },
+ { "value", cm_value, BRACE_ARGS },
+ { "var", cm_var, BRACE_ARGS },
+ { "verb", cm_verb, NO_BRACE_ARGS },
+ { "verbatim", cm_verbatim, NO_BRACE_ARGS },
+ { "verbatiminclude", cm_verbatiminclude, NO_BRACE_ARGS },
+ { "vindex", cm_vindex, NO_BRACE_ARGS },
+ { "vtable", cm_vtable, NO_BRACE_ARGS },
+ { "vskip", cm_ignore_line, NO_BRACE_ARGS },
+ { "w", cm_w, BRACE_ARGS },
+ { "xml", cm_xml, NO_BRACE_ARGS },
+ { "xref", cm_xref, BRACE_ARGS },
+
+ /* Deprecated commands. These used to be for italics. */
+ { "iappendix", cm_ideprecated, NO_BRACE_ARGS },
+ { "iappendixsec", cm_ideprecated, NO_BRACE_ARGS },
+ { "iappendixsection", cm_ideprecated, NO_BRACE_ARGS },
+ { "iappendixsubsec", cm_ideprecated, NO_BRACE_ARGS },
+ { "iappendixsubsubsec", cm_ideprecated, NO_BRACE_ARGS },
+ { "ichapter", cm_ideprecated, NO_BRACE_ARGS },
+ { "isection", cm_ideprecated, NO_BRACE_ARGS },
+ { "isubsection", cm_ideprecated, NO_BRACE_ARGS },
+ { "isubsubsection", cm_ideprecated, NO_BRACE_ARGS },
+ { "iunnumbered", cm_ideprecated, NO_BRACE_ARGS },
+ { "iunnumberedsec", cm_ideprecated, NO_BRACE_ARGS },
+ { "iunnumberedsubsec", cm_ideprecated, NO_BRACE_ARGS },
+ { "iunnumberedsubsubsec", cm_ideprecated, NO_BRACE_ARGS },
+
+ /* Now @include does what this was used to. */
+ { "infoinclude", cm_obsolete, NO_BRACE_ARGS },
+ { "titlespec", cm_obsolete, NO_BRACE_ARGS },
+
+ { NULL, NULL, NO_BRACE_ARGS }
+};
+
+/* The bulk of the Texinfo commands. */
+
+/* Commands which insert their own names. */
+void
+insert_self (int arg)
+{
+ if (arg == START)
+ add_word (command);
+}
+
+void
+insert_space (int arg)
+{
+ if (arg == START)
+ {
+ if (xml && !docbook)
+ xml_insert_entity ("space");
+ else
+ add_char (' ');
+ }
+}
+
+/* Insert a comma. Useful when a literal , would break our parsing of
+ multiple arguments. */
+void
+cm_comma (int arg)
+{
+ if (arg == START)
+ add_char (',');
+}
+
+
+/* Force a line break in the output. */
+void
+cm_asterisk (void)
+{
+ if (html)
+ add_word ("<br>");
+ else if (xml && !docbook)
+ xml_insert_entity ("linebreak");
+ else if (docbook)
+ xml_asterisk ();
+ else
+ {
+ close_single_paragraph ();
+ cm_noindent ();
+ }
+}
+
+/* Insert ellipsis. */
+void
+cm_dots (int arg)
+{
+ if (arg == START)
+ {
+ if (xml && !docbook)
+ xml_insert_entity ("dots");
+ else if (docbook)
+ xml_insert_entity ("hellip");
+ else
+ if (html && !in_fixed_width_font)
+ insert_string ("<small class=\"dots\">...</small>");
+ else
+ add_word ("...");
+ }
+}
+
+/* Insert ellipsis for sentence end. */
+void
+cm_enddots (int arg)
+{
+ if (arg == START)
+ {
+ if (xml && !docbook)
+ xml_insert_entity ("enddots");
+ else if (docbook)
+ {
+ xml_insert_entity ("hellip");
+ add_char ('.');
+ }
+ else
+ if (html && !in_fixed_width_font)
+ insert_string ("<small class=\"enddots\">...</small>");
+ else
+ add_word ("...");
+ }
+}
+
+void
+cm_bullet (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&bull;");
+ else if (xml && !docbook)
+ xml_insert_entity ("bullet");
+ else if (docbook)
+ xml_insert_entity ("bull");
+ else
+ add_char ('*');
+ }
+}
+
+static void
+cm_geq (int arg)
+{
+ if (arg == START)
+ {
+ if (xml)
+ xml_insert_entity ("ge");
+ else if (html)
+ add_word ("&ge;");
+ else
+ insert_string (">=");
+ }
+}
+
+static void
+cm_leq (int arg)
+{
+ if (arg == START)
+ {
+ if (xml)
+ xml_insert_entity ("le");
+ else if (html)
+ add_word ("&le;");
+ else
+ insert_string ("<=");
+ }
+}
+
+static void
+cm_minus (int arg)
+{
+ if (arg == START)
+ {
+ if (xml)
+ xml_insert_entity ("minus");
+ else if (html)
+ add_word ("&minus;");
+ else
+ add_char ('-');
+ }
+}
+
+/* Formatting a dimension unit. */
+void
+cm_dmn (int arg)
+{
+ if (html)
+ insert_html_tag_with_attribute (arg, "span", "class=\"dmn\"");
+ else if (docbook)
+ /* No units in docbook yet. */
+ ;
+ else if (xml)
+ xml_insert_element (DIMENSION, arg);
+}
+
+/* Insert "TeX". */
+void
+cm_TeX (int arg)
+{
+ static int last_position;
+
+ if (arg == START)
+ {
+ if (xml)
+ xml_insert_entity ("tex");
+ else
+ add_word ("TeX");
+
+ last_position = output_paragraph_offset;
+ }
+ else if (last_position != output_paragraph_offset)
+ {
+ warning (_("arguments to @%s ignored"), command);
+ output_paragraph_offset = last_position;
+ }
+}
+
+/* Insert "LaTeX". */
+void
+cm_LaTeX (int arg)
+{
+ static int last_position;
+
+ if (arg == START)
+ {
+ if (xml)
+ xml_insert_entity ("latex");
+ else
+ add_word ("LaTeX");
+
+ last_position = output_paragraph_offset;
+ }
+ else if (last_position != output_paragraph_offset)
+ {
+ warning (_("arguments to @%s ignored"), command);
+ output_paragraph_offset = last_position;
+ }
+}
+
+/* Copyright symbol. */
+void
+cm_copyright (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&copy;");
+ else if (xml && !docbook)
+ xml_insert_entity ("copyright");
+ else if (docbook)
+ xml_insert_entity ("copy");
+ else
+ add_word ("(C)");
+ }
+}
+
+/* Registered symbol. */
+void
+cm_registeredsymbol (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&reg;");
+ else if (docbook)
+ xml_insert_entity ("reg");
+ else if (xml && !docbook)
+ xml_insert_entity ("registered");
+ else
+ add_word ("(R)");
+ }
+}
+
+/* Left single guillemet (single left-pointing angle quotation mark). */
+void
+cm_guilsinglleft (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&lsaquo;");
+ else if (xml && !docbook)
+ xml_insert_entity ("lsaquo");
+ else
+ add_word ("<");
+ }
+}
+
+/* Right single guillemet (single right-pointing angle quotation mark). */
+void
+cm_guilsinglright (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&rsaquo;");
+ else if (xml && !docbook)
+ xml_insert_entity ("rsaquo");
+ else
+ add_word (">");
+ }
+}
+
+/* Double low-9 quotation mark. */
+void
+cm_quotedblbase (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&bdquo;");
+ else if (docbook)
+ xml_insert_entity ("ldquor");
+ else if (xml && !docbook)
+ xml_insert_entity ("bdquo");
+ else
+ add_word ("\"");
+ }
+}
+
+/* Left double quotation mark. */
+void
+cm_quotedblleft (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&ldquo;");
+ else if (docbook)
+ xml_insert_entity ("ldquo");
+ else if (xml && !docbook)
+ xml_insert_entity ("ldquo");
+ else
+ add_word ("\"");
+ }
+}
+
+/* Right double quotation mark. */
+void
+cm_quotedblright (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&rdquo;");
+ else if (docbook)
+ xml_insert_entity ("rdquo");
+ else if (xml && !docbook)
+ xml_insert_entity ("rdquo");
+ else
+ add_word ("\"");
+ }
+}
+
+/* Left single quotation mark. */
+void
+cm_quoteleft (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&lsquo;");
+ else if (docbook)
+ xml_insert_entity ("lsquo");
+ else if (xml && !docbook)
+ xml_insert_entity ("lsquo");
+ else
+ add_word ("`");
+ }
+}
+
+/* Right single quotation mark. */
+void
+cm_quoteright (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&rsquo;");
+ else if (docbook)
+ xml_insert_entity ("rsquo");
+ else if (xml && !docbook)
+ xml_insert_entity ("rsquo");
+ else
+ add_word ("'");
+ }
+}
+
+/* Single low-9 quotation mark. */
+void
+cm_quotesinglbase (int arg)
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&sbquo;");
+ else if (docbook)
+ xml_insert_entity ("lsquor");
+ else if (xml && !docbook)
+ xml_insert_entity ("sbquo");
+ else
+ add_word (",");
+ }
+}
+
+void
+cm_today (int arg)
+{
+ static char *months[12] =
+ { N_("January"), N_("February"), N_("March"), N_("April"), N_("May"),
+ N_("June"), N_("July"), N_("August"), N_("September"), N_("October"),
+ N_("November"), N_("December") };
+ if (arg == START)
+ {
+ time_t timer = time (0);
+ struct tm *ts = localtime (&timer);
+ add_word_args ("%d %s %d", ts->tm_mday, _(months[ts->tm_mon]),
+ ts->tm_year + 1900);
+ }
+}
+
+void
+cm_comment (void)
+{
+ /* For HTML, do not output comments before HTML header is written,
+ otherwise comments before @settitle cause an empty <title> in the
+ header. */
+ if ((html && output_head_p) || xml)
+ {
+ char *line;
+ get_rest_of_line (0, &line);
+
+ if (strlen (line) > 0)
+ {
+ int save_inhibit_indentation = inhibit_paragraph_indentation;
+ int save_paragraph_is_open = paragraph_is_open;
+ int save_escape_html = escape_html;
+ int save_xml_no_para = xml_no_para;
+ int i;
+
+ inhibit_paragraph_indentation = 1;
+ escape_html = 0;
+ xml_no_para = 1;
+
+ /* @c and @comment can appear between @item and @itemx,
+ @deffn and @deffnx. */
+ xml_dont_touch_items_defs++;
+
+ /* Use insert for HTML, and XML when indentation is enabled.
+ For Docbook, use add_char. */
+ if (xml && xml_indentation_increment > 0
+ && output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] != '\n')
+ insert ('\n');
+
+ /* Crunch double hyphens in comments. */
+ add_html_block_elt ("<!-- ");
+ for (i = 0; i < strlen (line); i++)
+ if (line[i] != '-' || (i && line[i-1] != '-'))
+ add_char (line[i]);
+ add_word (" -->");
+
+ if (html)
+ add_char ('\n');
+
+ inhibit_paragraph_indentation = save_inhibit_indentation;
+ paragraph_is_open = save_paragraph_is_open;
+ escape_html = save_escape_html;
+ xml_no_para = save_xml_no_para;
+ xml_dont_touch_items_defs--;
+ }
+
+ free (line);
+ }
+ else
+ cm_ignore_line ();
+}
+
+
+
+/* We keep acronyms with two arguments around, to be able to refer to them
+ later with only one argument. */
+static ACRONYM_DESC *acronyms_stack = NULL;
+
+static void
+cm_acronym_or_abbr (int arg, int is_abbr)
+{
+ char *aa, *description;
+ unsigned len;
+
+ /* We do everything at START. */
+ if (arg == END)
+ return;
+
+ get_until_in_braces (",", &aa);
+ if (input_text[input_text_offset] == ',')
+ input_text_offset++;
+ get_until_in_braces ("}", &description);
+
+ canon_white (aa);
+ canon_white (description);
+
+ /* If not enclosed in braces, strip after comma to be compatible
+ with texinfo.tex. */
+ if (description[0] != '{' && strchr (description, ',') != NULL)
+ {
+ int i = 0;
+ while (description[i] != ',')
+ i++;
+ /* For now, just terminate the string at comma. */
+ description[i] = 0;
+ }
+
+ /* Get description out of braces. */
+ if (description[0] == '{')
+ description++;
+
+ len = strlen (description);
+ if (len && description[len-1] == '}')
+ description[len-1] = 0;
+
+ /* Save new description. */
+ if (strlen (description) > 0)
+ {
+ ACRONYM_DESC *new = xmalloc (sizeof (ACRONYM_DESC));
+
+ new->acronym = xstrdup (aa);
+ new->description = xstrdup (description);
+ new->next = acronyms_stack;
+ acronyms_stack = new;
+ }
+
+ if (html)
+ {
+ add_word (is_abbr ? "<abbr" : "<acronym");
+
+ if (strlen (description) > 0)
+ add_word_args (" title=\"%s\"", text_expansion (description));
+ else if (acronyms_stack)
+ {
+ /* No second argument, get from previous. Search order is from
+ last to first defined, so we get the most recent version of
+ the description. */
+ ACRONYM_DESC *temp = acronyms_stack;
+
+ while (temp)
+ {
+ if (STREQ (aa, temp->acronym)
+ && strlen (temp->description) > 0)
+ {
+ add_word_args (" title=\"%s\"",
+ text_expansion (temp->description));
+ break;
+ }
+ temp = temp->next;
+ }
+ }
+
+ add_char ('>');
+ execute_string ("%s", aa);
+ add_word (is_abbr ? "</abbr>" : "</acronym>");
+ }
+ else if (docbook)
+ {
+ xml_insert_element (is_abbr ? ABBREV : ACRONYM, START);
+ execute_string ("%s", aa);
+ xml_insert_element (is_abbr ? ABBREV : ACRONYM, END);
+ }
+ else if (xml)
+ {
+ xml_insert_element (is_abbr ? ABBREV : ACRONYM, START);
+
+ xml_insert_element (is_abbr ? ABBREVWORD : ACRONYMWORD, START);
+ execute_string ("%s", aa);
+ xml_insert_element (is_abbr ? ABBREVWORD : ACRONYMWORD, END);
+
+ if (strlen (description) > 0)
+ {
+ xml_insert_element (is_abbr ? ABBREVDESC : ACRONYMDESC, START);
+ execute_string ("%s", description);
+ xml_insert_element (is_abbr ? ABBREVDESC : ACRONYMDESC, END);
+ }
+
+ xml_insert_element (is_abbr ? ABBREV : ACRONYM, END);
+ }
+ else
+ execute_string ("%s", aa);
+
+ /* Put description into parenthesis after the acronym for all outputs
+ except XML. */
+ if (strlen (description) > 0 && (!xml || docbook))
+ add_word_args (" (%s)", description);
+}
+
+void
+cm_acronym (int arg)
+{
+ cm_acronym_or_abbr (arg, 0);
+}
+
+void
+cm_abbr (int arg)
+{
+ cm_acronym_or_abbr (arg, 1);
+}
+
+void
+cm_tt (int arg)
+{
+ /* @t{} is a no-op in Info. */
+ if (html)
+ insert_html_tag (arg, "tt");
+ else if (xml)
+ xml_insert_element (TT, arg);
+}
+
+void
+cm_code (int arg)
+{
+ if (arg == START)
+ in_fixed_width_font++;
+
+ if (xml)
+ {
+ if (STREQ (command, "command"))
+ xml_insert_element (COMMAND_TAG, arg);
+ else if (STREQ (command, "env"))
+ xml_insert_element (ENV, arg);
+ else if (STREQ (command, "file"))
+ xml_insert_element (FILE_TAG, arg);
+ else if (STREQ (command, "option"))
+ xml_insert_element (OPTION, arg);
+ else if (STREQ (command, "samp"))
+ {
+ if (docbook && arg == START)
+ {
+ /* Even though @samp is in_fixed_width_font, it
+ should always start a paragraph. Unfortunately,
+ in_fixed_width_font inhibits that. */
+ xml_start_para ();
+ xml_insert_entity ("lsquo");
+ }
+ xml_insert_element (SAMP, arg);
+ if (docbook && arg == END)
+ xml_insert_entity ("rsquo");
+ }
+ else
+ xml_insert_element (CODE, arg);
+ }
+ else if (html)
+ {
+ if (STREQ (command, "code"))
+ insert_html_tag (arg, "code");
+ else
+ { /* Use <samp> tag in general to get typewriter. */
+ if (arg == START)
+ { /* If @samp specifically, add quotes a la TeX output. */
+ if (STREQ (command, "samp"))
+ add_word ("&lsquo;");
+ insert_html_tag (arg, "samp");
+ }
+ insert_html_tag_with_attribute (arg, "span", "class=\"%s\"",command);
+ if (arg == END)
+ {
+ insert_html_tag (arg, "samp");
+ if (STREQ (command, "samp"))
+ add_word ("&rsquo;");
+ }
+ }
+ }
+ else
+ {
+ if (!printing_index)
+ {
+ if (arg == START)
+ add_char ('`');
+ else
+ add_meta_char ('\'');
+ }
+ }
+}
+
+void
+cm_kbd (int arg)
+{
+ if (xml)
+ xml_insert_element (KBD, arg);
+ else if (html)
+ { /* Seems like we should increment in_fixed_width_font for Info
+ format too, but then the quote-omitting special case gets
+ confused. Punt. */
+ if (arg == START)
+ in_fixed_width_font++;
+ insert_html_tag (arg, "kbd");
+ }
+ else
+ { /* People use @kbd in an example to get the "user input" font.
+ We don't want quotes in that case. */
+ if (!in_fixed_width_font)
+ cm_code (arg);
+ }
+}
+
+/* Just show a url (http://example.org/..., for example), don't link to it. */
+void
+cm_indicate_url (int arg, int start, int end)
+{
+ if (xml)
+ xml_insert_element (URL, arg);
+ else if (html)
+ {
+ if (arg == START)
+ add_word ("&lt;");
+ insert_html_tag (arg, "code");
+ if (arg != START)
+ add_word ("&gt;");
+ }
+ else
+ if (arg == START)
+ add_word ("<");
+ else
+ add_word (">");
+}
+
+void
+cm_key (int arg)
+{
+ if (xml)
+ xml_insert_element (KEY, arg);
+ else if (html)
+ add_word (arg == START ? "&lt;" : "&gt;");
+ else
+ add_char (arg == START ? '<' : '>');
+}
+
+/* Handle a command that switches to a non-fixed-width font. */
+void
+not_fixed_width (int arg)
+{
+ if (arg == START)
+ in_fixed_width_font = 0;
+}
+
+/* @var in makeinfo just uppercases the text. */
+void
+cm_var (int arg, int start_pos, int end_pos)
+{
+ if (xml)
+ xml_insert_element (VAR, arg);
+ else
+ {
+ not_fixed_width (arg);
+
+ if (html)
+ insert_html_tag (arg, "var");
+ else if (arg == END)
+ {
+ while (start_pos < end_pos)
+ {
+ unsigned char c = output_paragraph[start_pos];
+ if (strchr ("[](),", c))
+ warning (_("unlikely character %c in @var"), c);
+ output_paragraph[start_pos] = coerce_to_upper (c);
+ start_pos++;
+ }
+ }
+ }
+}
+
+void
+cm_sc (int arg, int start_pos, int end_pos)
+{
+ if (xml)
+ xml_insert_element (SC, arg);
+ else
+ {
+ not_fixed_width (arg);
+
+ if (arg == START)
+ {
+ if (html)
+ insert_html_tag_with_attribute (arg, "span", "class=\"sc\"");
+ }
+ else
+ {
+ int all_upper;
+
+ if (html)
+ start_pos += sizeof ("<span class=\"sc\">") - 1; /* skip <span> */
+
+ /* Avoid the warning below if there's no text inside @sc{}, or
+ when processing menus under --no-headers. */
+ all_upper = start_pos < end_pos;
+
+ while (start_pos < end_pos)
+ {
+ unsigned char c = output_paragraph[start_pos];
+ if (!isupper (c))
+ all_upper = 0;
+ if (!html)
+ output_paragraph[start_pos] = coerce_to_upper (c);
+ start_pos++;
+ }
+ if (all_upper)
+ warning (_("@sc argument all uppercase, thus no effect"));
+
+ if (html)
+ insert_html_tag (arg, "span");
+ }
+ }
+}
+
+void
+cm_dfn (int arg, int position)
+{
+ if (xml)
+ xml_insert_element (DFN, arg);
+ else
+ {
+ if (html)
+ insert_html_tag (arg, "dfn");
+ else if (arg == START)
+ add_char ('"');
+ else
+ add_meta_char ('"');
+ }
+}
+
+void
+cm_emph (int arg)
+{
+ if (xml)
+ xml_insert_element (EMPH, arg);
+ else if (html)
+ insert_html_tag (arg, "em");
+ else
+ add_char ('_');
+}
+
+void
+cm_verb (int arg)
+{
+ int character;
+ int delimiter = 0; /* avoid warning */
+ int seen_end = 0;
+
+ in_fixed_width_font++;
+ /* are these necessary ? */
+ last_char_was_newline = 0;
+
+ if (html)
+ add_word ("<tt>");
+
+ if (input_text_offset < input_text_length)
+ {
+ character = curchar ();
+ if (character == '{')
+ input_text_offset++;
+ else
+ line_error (_("`{' expected, but saw `%c'"), character);
+ }
+
+ if (input_text_offset < input_text_length)
+ {
+ delimiter = curchar ();
+ input_text_offset++;
+ }
+
+ while (input_text_offset < input_text_length)
+ {
+ character = curchar ();
+
+ if (character == '\n')
+ {
+ line_number++;
+ if (html)
+ add_word ("<br>\n");
+ }
+
+ else if (html && character == '<')
+ add_word ("&lt;");
+
+ else if (html && character == '&')
+ add_word ("&amp;");
+
+ else if (character == delimiter && input_text[input_text_offset+1] == '}')
+ { /* Assume no newlines in END_VERBATIM. */
+ seen_end = 1;
+ input_text_offset++;
+ break;
+ }
+
+ else
+ add_char (character);
+
+ input_text_offset++;
+ }
+
+ if (!seen_end)
+ warning (_("end of file inside verb block"));
+
+ if (input_text_offset < input_text_length)
+ {
+ character = curchar ();
+ if (character == '}')
+ input_text_offset++;
+ else
+ line_error (_("`}' expected, but saw `%c'"), character);
+ }
+
+ if (html)
+ add_word ("</tt>");
+
+ in_fixed_width_font--;
+}
+
+
+void
+cm_strong (int arg, int start_pos, int end_pos)
+{
+ if (docbook && arg == START)
+ xml_insert_element_with_attribute (B, arg, "role=\"bold\"");
+ else if (xml)
+ xml_insert_element (STRONG, arg);
+ else if (html)
+ insert_html_tag (arg, "strong");
+ else
+ add_char ('*');
+
+ if (!xml && !html && !docbook && !no_headers
+ && arg == END
+ && end_pos - start_pos >= 6
+ && (STRNCASEEQ ((char *) output_paragraph + start_pos, "*Note:", 6)
+ || STRNCASEEQ ((char *) output_paragraph + start_pos, "*Note ", 6))
+ )
+ {
+ /* Translators: "Note:" is literal here and should not be
+ translated. A document with @strong{Nota}, say, is fine. */
+ warning (_("@strong{Note...} produces a spurious cross-reference in Info; reword to avoid that"));
+ /* Adjust the output to avoid writing the bad xref. */
+ output_paragraph[start_pos + 5] = '_';
+ }
+}
+
+void
+cm_cite (int arg, int position)
+{
+ if (xml)
+ xml_insert_element (CITE, arg);
+ else if (html)
+ insert_html_tag (arg, "cite");
+ else
+ {
+ if (arg == START)
+ add_char ('`');
+ else if (!looking_at ("}'"))
+ /* In the simple case of, e.g., ... @cite{Foo}'s ...
+ don't output a double ''. */
+ add_char ('\'');
+ }
+}
+
+/* No highlighting, but argument switches fonts. */
+void
+cm_not_fixed_width (int arg, int start, int end)
+{
+ if (xml)
+ xml_insert_element (NOTFIXEDWIDTH, arg);
+ not_fixed_width (arg);
+}
+
+void
+cm_i (int arg)
+{
+ /* Make use of <lineannotation> of Docbook, if we are
+ inside an @example or similar. */
+ if (docbook && !filling_enabled && !printing_index)
+ xml_insert_element (LINEANNOTATION, arg);
+ else if (xml)
+ xml_insert_element (I, arg);
+ else if (html)
+ insert_html_tag (arg, "i");
+ else
+ not_fixed_width (arg);
+}
+
+void
+cm_slanted (int arg)
+{
+ /* Make use of <lineannotation> of Docbook, if we are
+ inside an @example or similar. */
+ if (docbook && !filling_enabled && !printing_index)
+ xml_insert_element (LINEANNOTATION, arg);
+ else if (xml)
+ xml_insert_element (SLANTED, arg);
+ else if (html)
+ insert_html_tag (arg, "i");
+ else
+ not_fixed_width (arg);
+}
+
+void
+cm_b (int arg)
+{
+ /* See cm_i comments. */
+ if (docbook && !filling_enabled && !printing_index)
+ xml_insert_element (LINEANNOTATION, arg);
+ else if (docbook && arg == START)
+ xml_insert_element_with_attribute (B, arg, "role=\"bold\"");
+ else if (xml)
+ xml_insert_element (B, arg);
+ else if (html)
+ insert_html_tag (arg, "b");
+ else
+ not_fixed_width (arg);
+}
+
+void
+cm_r (int arg)
+{
+ /* See cm_i comments. */
+ if (docbook && !filling_enabled && !printing_index)
+ xml_insert_element (LINEANNOTATION, arg);
+ else if (xml)
+ xml_insert_element (R, arg);
+ else if (html)
+ insert_html_tag_with_attribute (arg, "span", "class=\"roman\"");
+ else
+ not_fixed_width (arg);
+}
+
+void
+cm_sansserif (int arg)
+{
+ /* See cm_i comments. */
+ if (docbook && !filling_enabled && !printing_index)
+ xml_insert_element (LINEANNOTATION, arg);
+ else if (xml)
+ xml_insert_element (SANSSERIF, arg);
+ else if (html)
+ insert_html_tag_with_attribute (arg, "span", "class=\"sansserif\"");
+ else
+ not_fixed_width (arg);
+}
+
+void
+cm_titlefont (int arg)
+{
+ if (xml)
+ xml_insert_element (TITLEFONT, arg);
+ else
+ {
+ not_fixed_width (arg);
+ if (html)
+ {
+ html_title_written = 1; /* suppress title from @settitle */
+ if (arg == START)
+ add_word ("<h1 class=\"titlefont\">");
+ else
+ add_word ("</h1>\n");
+ }
+ }
+}
+
+
+/* Unfortunately, we cannot interpret @math{} contents like TeX does. We just
+ pass them through. */
+void
+cm_math (int arg)
+{
+ if (xml && !docbook)
+ xml_insert_element (MATH, arg);
+}
+
+/* Various commands are no-op's. */
+void
+cm_no_op (void)
+{
+}
+
+
+/* For proofing single chapters, etc. */
+void
+cm_novalidate (void)
+{
+ validating = 0;
+}
+
+
+/* Prevent the argument from being split across two lines. */
+void
+cm_w (int arg)
+{
+ if (arg == START)
+ non_splitting_words++;
+ else
+ {
+ if (docbook || html || xml)
+ /* This is so @w{$}Log$ doesn't end up as <dollar>Log<dollar>
+ in the output. */
+ insert_string ("<!-- /@w -->");
+
+ non_splitting_words--;
+ }
+}
+
+
+/* An unbreakable word space. Same as @w{ } for makeinfo, but different
+ for TeX (the space stretches and stretches, and does not inhibit
+ hyphenation). For XML and HTML, insert the non-breaking-space
+ character and entity, respectively */
+void
+cm_tie (int arg)
+{
+ if (arg == START)
+ if (html)
+ insert_string ("&nbsp;");
+ else if (xml)
+ insert_string ("&#xa0;");
+ else
+ {
+ cm_w (START);
+ add_char (' ');
+ }
+ else
+ if (!html && !xml) cm_w (END);
+}
+
+/* Explain that this command is obsolete, thus the user shouldn't
+ do anything with it. */
+static void
+cm_obsolete (int arg, int start, int end)
+{
+ if (arg == START)
+ warning (_("%c%s is obsolete"), COMMAND_PREFIX, command);
+}
+
+
+/* Inhibit the indentation of the next paragraph, but not of following
+ paragraphs. */
+void
+cm_noindent (void)
+{
+ if (!inhibit_paragraph_indentation)
+ inhibit_paragraph_indentation = -1;
+}
+
+void
+cm_noindent_cmd (void)
+{
+ cm_noindent ();
+ xml_no_indent = 1;
+ skip_whitespace_and_newlines();
+
+ if (xml)
+ xml_start_para ();
+ else if (html && !paragraph_is_open)
+ add_html_block_elt ("<p class=\"noindent\">");
+ else
+ {
+ paragraph_is_open = 0;
+ start_paragraph ();
+ }
+}
+
+/* Force indentation of the next paragraph. */
+void
+cm_indent (void)
+{
+ inhibit_paragraph_indentation = 0;
+ xml_no_indent = 0;
+ skip_whitespace_and_newlines();
+
+ if (xml)
+ xml_start_para ();
+ else if (html && !paragraph_is_open)
+ add_html_block_elt ("<p class=\"indent\">");
+ else
+ start_paragraph ();
+}
+
+/* I don't know exactly what to do with this. Should I allow
+ someone to switch filenames in the middle of output? Since the
+ file could be partially written, this doesn't seem to make sense.
+ Another option: ignore it, since they don't really want to
+ switch files. Finally, complain, or at least warn. It doesn't
+ really matter, anyway, since this doesn't get executed. */
+void
+cm_setfilename (void)
+{
+ char *filename;
+ get_rest_of_line (1, &filename);
+ /* warning ("`@%s %s' encountered and ignored", command, filename); */
+ if (xml)
+ add_word_args ("<setfilename>%s</setfilename>", filename);
+ free (filename);
+}
+
+void
+cm_settitle (void)
+{
+ if (xml)
+ {
+ xml_insert_element (SETTITLE, START);
+ xml_in_book_title = 1;
+ get_rest_of_line (0, &title);
+ execute_string ("%s", title);
+ xml_in_book_title = 0;
+ xml_insert_element (SETTITLE, END);
+ }
+ else
+ get_rest_of_line (0, &title);
+}
+
+
+/* Ignore argument in braces. */
+void
+cm_ignore_arg (int arg, int start_pos, int end_pos)
+{
+ if (arg == END)
+ output_paragraph_offset = start_pos;
+}
+
+/* Ignore argument on rest of line. */
+void
+cm_ignore_line (void)
+{
+ discard_until ("\n");
+}
+
+/* Insert the number of blank lines passed as argument. */
+void
+cm_sp (void)
+{
+ int lines;
+ char *line;
+
+ /* Due to tricky stuff in execute_string(), @value{} can't be expanded.
+ So there is really no reason to enable expansion for @sp parameters. */
+ get_rest_of_line (0, &line);
+
+ if (sscanf (line, "%d", &lines) != 1 || lines <= 0)
+ line_error (_("@sp requires a positive numeric argument, not `%s'"), line);
+ else
+ {
+ if (xml)
+ {
+ /* @sp can appear between @item and @itemx, @deffn and @deffnx. */
+ xml_dont_touch_items_defs++;
+ xml_insert_element_with_attribute (SP, START, "lines=\"%s\"", line);
+ /* insert_string (line);*/
+ xml_insert_element (SP, END);
+ xml_dont_touch_items_defs--;
+ }
+ else
+ {
+ /* Must disable filling since otherwise multiple newlines is like
+ multiple spaces. Must close paragraph since that's what the
+ manual says and that's what TeX does. */
+ int save_filling_enabled = filling_enabled;
+ filling_enabled = 0;
+
+ /* close_paragraph generates an extra blank line. */
+ close_single_paragraph ();
+
+ if (html)
+ add_html_block_elt ("<pre class=\"sp\">\n");
+
+ while (lines--)
+ add_char ('\n');
+
+ if (html)
+ add_html_block_elt ("</pre>\n");
+
+ filling_enabled = save_filling_enabled;
+ }
+ }
+ free (line);
+}
+
+/* @dircategory LINE outputs INFO-DIR-SECTION LINE, unless --no-headers. */
+void
+cm_dircategory (void)
+{
+ char *line;
+
+ if (html || docbook)
+ cm_ignore_line ();
+ else if (xml)
+ {
+ xml_insert_element (DIRCATEGORY, START);
+ get_rest_of_line (1, &line);
+ insert_string (line);
+ free (line);
+ xml_insert_element (DIRCATEGORY, END);
+ }
+ else
+ {
+ get_rest_of_line (1, &line);
+
+ if (!no_headers && !html)
+ {
+ /* use add_* instead of insert_* because otherwise the
+ file header ("This is ...") will end up inside the
+ dir section markers. */
+ kill_self_indent (-1); /* make sure there's no indentation */
+ cm_noindent (); /* make sure again */
+ add_word ("INFO-DIR-SECTION ");
+ add_word (line);
+ close_single_paragraph (); /* newline */
+ }
+
+ free (line);
+ }
+}
+
+/* Start a new line with just this text on it.
+ Then center the line of text.
+ */
+void
+cm_center (void)
+{
+ if (xml)
+ {
+ char *line;
+ xml_insert_element (CENTER, START);
+ get_rest_of_line (0, &line);
+ execute_string ("%s", line);
+ free (line);
+ xml_insert_element (CENTER, END);
+ }
+ else
+ {
+ int i, start, length, width;
+ char *line;
+ int save_indented_fill = indented_fill;
+ int save_filling_enabled = filling_enabled;
+ int fudge_factor = 1;
+
+ filling_enabled = indented_fill = 0;
+ cm_noindent ();
+ start = output_paragraph_offset;
+
+ if (html)
+ add_html_block_elt ("<div align=\"center\">");
+
+ inhibit_output_flushing ();
+ get_rest_of_line (0, &line);
+ execute_string ("%s", line);
+ free (line);
+ uninhibit_output_flushing ();
+ if (html)
+ add_html_block_elt ("</div>");
+
+ else
+ {
+ i = output_paragraph_offset - 1;
+ while (i > (start - 1) && output_paragraph[i] == '\n')
+ i--;
+
+ output_paragraph_offset = ++i;
+ length = output_paragraph_offset - start;
+ width = mbsnwidth ((char *)(output_paragraph + start), length, 0);
+
+ if (width < (fill_column - fudge_factor))
+ {
+ line = xmalloc (1 + length);
+ memcpy (line, (char *)(output_paragraph + start), length);
+
+ i = (fill_column - fudge_factor - width) / 2;
+ output_paragraph_offset = start;
+
+ while (i--)
+ insert (' ');
+
+ for (i = 0; i < length; i++)
+ insert (line[i]);
+
+ free (line);
+ }
+ }
+
+ insert ('\n');
+ filling_enabled = save_filling_enabled;
+ indented_fill = save_indented_fill;
+ close_single_paragraph ();
+ if (looking_at ("\n"))
+ insert ('\n');
+ }
+}
+
+
+/* Define what @click{} does. */
+static char *click_command = "@arrow";
+
+void
+cm_clickstyle (void)
+{
+ click_command = get_item_function ();
+
+ if (looking_at ("\n"))
+ {
+ input_text_offset++;
+ line_number++;
+ }
+}
+
+/* @clicksequence{File @click{} Open} */
+void
+cm_clicksequence (int arg)
+{
+ if (xml)
+ xml_insert_element (CLICKSEQUENCE, arg);
+}
+
+void
+cm_click (int arg)
+{
+ if (xml)
+ xml_insert_element (CLICK, arg);
+ else if (arg == END)
+ execute_string ("%s{}", click_command);
+}
+
+
+/* Generic right arrow, default clickstyle. */
+void
+cm_arrow (int arg)
+{
+ if (arg == END)
+ if (html || xml)
+ xml_insert_entity ("rarr");
+ else
+ add_word ("->");
+}
+
+/* Show what an expression returns. */
+void
+cm_result (int arg)
+{
+ if (arg == END)
+ if (html || xml)
+ xml_insert_entity ("rArr");
+ else
+ add_word ("=>");
+}
+
+/* What an expression expands to. */
+void
+cm_expansion (int arg)
+{
+ if (arg == END)
+ add_word (html ? "==&gt;" : "==>");
+}
+
+/* Indicates two expressions are equivalent. */
+void
+cm_equiv (int arg)
+{
+ if (arg == END)
+ add_word ("==");
+}
+
+/* What an expression may print. */
+void
+cm_print (int arg)
+{
+ if (arg == END)
+ add_word ("-|");
+}
+
+/* An error signaled. */
+void
+cm_error (int arg)
+{
+ if (arg == END)
+ add_word (html ? "error--&gt;" : "error-->");
+}
+
+/* The location of point in an example of a buffer. */
+void
+cm_point (int arg)
+{
+ if (arg == END)
+ add_word ("-!-");
+}
+
+/* @exdent: Start a new line with just this text on it.
+ The text is outdented one level if possible. */
+void
+cm_exdent (void)
+{
+ char *line;
+ int save_indent = current_indent;
+ int save_in_fixed_width_font = in_fixed_width_font;
+
+ /* Read argument. */
+ get_rest_of_line (0, &line);
+
+ /* Exdent the output. Actually this may be a no-op. */
+ if (current_indent)
+ current_indent -= default_indentation_increment;
+
+ /* @exdent arg is supposed to be in roman. */
+ in_fixed_width_font = 0;
+
+ /* The preceding newline already inserted the `current_indent'.
+ Remove one level's worth. */
+ kill_self_indent (default_indentation_increment);
+
+ if (html)
+ add_word ("<br>");
+ else if (docbook)
+ xml_insert_element (LINEANNOTATION, START);
+ else if (xml)
+ xml_insert_element (EXDENT, START);
+
+ /* Can't close_single_paragraph, then we lose preceding blank lines. */
+ flush_output ();
+ execute_string ("%s", line);
+ free (line);
+
+ if (html)
+ add_word ("<br>");
+ else if (xml)
+ {
+ xml_insert_element (docbook ? LINEANNOTATION : EXDENT, END);
+ insert ('\n');
+ }
+
+ close_single_paragraph ();
+
+ current_indent = save_indent;
+ in_fixed_width_font = save_in_fixed_width_font;
+ if (!xml)
+ start_paragraph ();
+}
+
+/*
+ Read include-filename, process the include-file:
+ verbatim_include == 0: process through reader_loop
+ verbatim_include != 0: process through handle_verbatim_environment
+ */
+static void
+handle_include (int verbatim_include)
+{
+ char *arg, *filename;
+
+ if (macro_expansion_output_stream && !executing_string)
+ me_append_before_this_command ();
+
+ if (!insertion_stack)
+ close_paragraph (); /* No blank lines etc. if not at outer level. */
+
+ if (macro_expansion_output_stream && verbatim_include)
+ {
+ me_append_before_this_command ();
+ return;
+ }
+
+ get_rest_of_line (0, &arg);
+ /* We really only want to expand @value, but it's easier to just do
+ everything. TeX will only work with @value. */
+ {
+ int save_in_fixed_width_font = in_fixed_width_font;
+ in_fixed_width_font = 1; /* do not change -- to -, etc. */
+ filename = text_expansion (arg);
+ in_fixed_width_font = save_in_fixed_width_font;
+ }
+
+ free (arg);
+
+ if (macro_expansion_output_stream && !executing_string)
+ remember_itext (input_text, input_text_offset);
+
+ pushfile ();
+
+ /* In verbose mode we print info about including another file. */
+ if (verbose_mode)
+ {
+ int i = 0;
+ FSTACK *stack = filestack;
+
+ for (i = 0, stack = filestack; stack; stack = stack->next, i++);
+
+ i *= 2;
+
+ printf ("%*s", i, "");
+ printf ("%c%s `%s'\n", COMMAND_PREFIX, command, filename);
+ fflush (stdout);
+ }
+
+ if (!find_and_load (filename, 1))
+ {
+ popfile ();
+ line_number--;
+
+ /* /wh/bar:5: @include/@verbatiminclude `foo': No such file or dir */
+ line_error ("%c%s `%s': %s", COMMAND_PREFIX, command, filename,
+ strerror (errno));
+
+ free (filename);
+ return;
+ }
+ else
+ {
+ if (macro_expansion_output_stream && !executing_string)
+ remember_itext (input_text, input_text_offset);
+
+ if (!verbatim_include)
+ reader_loop ();
+ else
+ handle_verbatim_environment (0);
+ }
+ free (filename);
+ popfile ();
+}
+
+
+/* Include file as if put in @verbatim environment */
+void
+cm_verbatiminclude (void)
+{
+ handle_include (1);
+}
+
+
+/* Remember this file, and move onto the next. */
+void
+cm_include (void)
+{
+ handle_include (0);
+}
+
+
+/* @bye: Signals end of processing. Easy to make this happen. */
+
+void
+cm_bye (void)
+{
+ discard_braces (); /* should not have any unclosed braces left */
+ input_text_offset = input_text_length;
+}
+
+/* @paragraphindent */
+
+static void
+cm_paragraphindent (void)
+{
+ char *arg;
+
+ get_rest_of_line (1, &arg);
+ if (set_paragraph_indent (arg) != 0)
+ line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
+
+ free (arg);
+}
+
+
+/* @exampleindent: change indentation of example-like environments. */
+static int
+set_example_indentation_increment (char *string)
+{
+ if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
+ ;
+ else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
+ example_indentation_increment = 0;
+ else if (sscanf (string, "%d", &example_indentation_increment) != 1)
+ return -1;
+ return 0;
+}
+
+static void
+cm_exampleindent (void)
+{
+ char *arg;
+
+ get_rest_of_line (1, &arg);
+ if (set_example_indentation_increment (arg) != 0)
+ line_error (_("Bad argument to @%s"), command);
+
+ if (input_text[input_text_offset] == '\n')
+ close_single_paragraph ();
+
+ free (arg);
+}
+
+
+/* @firstparagraphindent: suppress indentation in first paragraphs after
+ headings. */
+static int
+set_firstparagraphindent (char *string)
+{
+ if (STREQ (string, "insert") || STREQ (string, _("insert")))
+ do_first_par_indent = 1;
+ else if (STREQ (string, "none") || STREQ (string, _("none")))
+ do_first_par_indent = 0;
+ else
+ return -1;
+ return 0;
+}
+
+static void
+cm_firstparagraphindent (void)
+{
+ char *arg;
+
+ get_rest_of_line (1, &arg);
+ if (set_firstparagraphindent (arg) != 0)
+ line_error (_("Bad argument to @%s: %s"), command, arg);
+
+ free (arg);
+}
+
+/* For DocBook and XML, produce &period; for `.@:'. This gives the processing
+ software a fighting chance to treat it specially by not adding extra space.
+
+ Do this also for ?, !, and :. */
+void
+cm_colon (void)
+{
+ if (xml)
+ {
+ if (strchr (".?!:", input_text[input_text_offset-3]) != NULL)
+ {
+ /* Erase literal character that's there, except `>', which is
+ part of the XML tag. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] != '>')
+ output_paragraph_offset--;
+
+ switch (input_text[input_text_offset-3])
+ {
+ case '.':
+ xml_insert_entity ("period");
+ break;
+ case '?':
+ xml_insert_entity ("quest");
+ break;
+ case '!':
+ xml_insert_entity ("excl");
+ break;
+ case ':':
+ xml_insert_entity ("colon");
+ break;
+ }
+ }
+ }
+}
+
+/* Ending sentences explicitly. Currently, only outputs entities for XML
+ output, for other formats it calls insert_self. */
+void
+cm_punct (int arg)
+{
+ if (xml && !docbook && input_text_offset > 0)
+ {
+ switch (input_text[input_text_offset-1])
+ {
+ case '.':
+ xml_insert_entity ("eosperiod");
+ break;
+ case '?':
+ xml_insert_entity ("eosquest");
+ break;
+ case '!':
+ xml_insert_entity ("eosexcl");
+ break;
+ }
+ }
+ else
+ {
+ insert_self (arg);
+ }
+}
+
+
+/* If @frenchspacing is in effect, avoid outputting extra spaces after
+ sentence-ending periods. Actually, we explicitly do this only in one
+ tiny case (see add_char in makeinfo.c). Usually we just output
+ whatever the user gives us. */
+void
+cm_frenchspacing (void)
+{
+ char *val;
+ get_rest_of_line (1, &val);
+
+ if (STREQ (val, "on")) {
+ french_spacing = 1;
+ } else if (STREQ (val, "off")) {
+ french_spacing = 0;
+ } else {
+ line_error (_("Expected @%s on or off, not `%s'"), command, val);
+ }
+
+ if (xml && !docbook) {
+ xml_insert_element_with_attribute (FRENCHSPACING, START,
+ "val=\"%s\"", val);
+ xml_insert_element (FRENCHSPACING, END);
+ }
+}
+
+
+/* Body font size. This is only for TeX, so we're just checking
+ validity here. Don't think we should even pass it on to XML. */
+void
+cm_fonttextsize (void)
+{
+ char *val;
+ get_rest_of_line (1, &val);
+
+ if (! (STREQ (val, "10") || STREQ (val, "off"))) {
+ line_error (_("Only @%s 10 or 11 is supported, not `%s'"), command, val);
+ }
+}
diff --git a/makeinfo/cmds.h b/makeinfo/cmds.h
new file mode 100644
index 0000000..50097a7
--- /dev/null
+++ b/makeinfo/cmds.h
@@ -0,0 +1,223 @@
+/* cmds.h -- declarations for cmds.c.
+ $Id: cmds.h,v 1.15 2008/04/09 17:07:31 karl Exp $
+
+ Copyright (C) 1998, 1999, 2002, 2003, 2004, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef CMDS_H
+#define CMDS_H
+
+/* The three arguments a command can get are a flag saying whether it is
+ before argument parsing (START) or after (END), the starting position
+ of the arguments, and the ending position. */
+typedef void COMMAND_FUNCTION (); /* So we can say COMMAND_FUNCTION *foo; */
+
+/* Each command has an associated function. When the command is
+ encountered in the text, the associated function is called with START
+ as the argument. If the function expects arguments in braces, it
+ remembers itself on the stack. When the corresponding close brace is
+ encountered, the function is called with END as the argument. */
+#define START 0
+#define END 1
+
+/* Does the command expect braces? */
+#define NO_BRACE_ARGS 0
+#define BRACE_ARGS 1
+#define MAYBE_BRACE_ARGS 2
+
+typedef struct
+{
+ char *name;
+ COMMAND_FUNCTION *proc;
+ int argument_in_braces;
+} COMMAND;
+
+extern COMMAND command_table[];
+
+typedef struct acronym_desc
+{
+ struct acronym_desc *next;
+ char *acronym;
+ char *description;
+} ACRONYM_DESC;
+
+/* Texinfo commands. */
+extern void insert_self (int arg),
+ insert_space (int arg),
+ cm_ignore_line (void),
+ cm_ignore_arg (int arg, int start_pos, int end_pos),
+ cm_comment (void),
+ cm_no_op (void);
+
+/* Document structure and meta information. */
+extern void cm_setfilename (void),
+ cm_settitle (void),
+ cm_documentdescription (void),
+ cm_node (void),
+ cm_menu (void),
+ cm_detailmenu (void),
+ cm_dircategory (void),
+ cm_direntry (void),
+ cm_bye (void);
+
+/* File inclusion. */
+extern void cm_include (void),
+ cm_verbatiminclude (void);
+
+/* Cross referencing commands. */
+extern void cm_anchor (int arg),
+ cm_xref (int arg),
+ cm_pxref (int arg),
+ cm_ref (int arg),
+ cm_inforef (int arg),
+ cm_uref (int arg);
+
+/* Special insertions. */
+extern void cm_LaTeX (int arg),
+ cm_TeX (int arg),
+ cm_arrow (int arg),
+ cm_bullet (int arg),
+ cm_click (int arg),
+ cm_clicksequence (int arg),
+ cm_clickstyle (void),
+ cm_colon (void),
+ cm_comma (int arg),
+ cm_copyright (int arg),
+ cm_dots (int arg),
+ cm_enddots (int arg),
+ cm_equiv (int arg),
+ cm_error (int arg),
+ cm_expansion (int arg),
+ cm_image (int arg),
+ cm_insert_copying (void),
+ cm_guilsinglleft (int arg),
+ cm_guilsinglright (int arg),
+ cm_point (int arg),
+ cm_print (int arg),
+ cm_punct (int arg),
+ cm_quotedblbase (int arg),
+ cm_quotedblleft (int arg),
+ cm_quotedblright (int arg),
+ cm_quoteleft (int arg),
+ cm_quoteright (int arg),
+ cm_quotesinglbase (int arg),
+ cm_registeredsymbol (int arg),
+ cm_result (int arg);
+
+/* Emphasis and markup. */
+extern void cm_acronym (int arg),
+ cm_abbr (int arg),
+ cm_b (int arg),
+ cm_cite (int arg, int position),
+ cm_code (int arg),
+ cm_dfn (int arg, int position),
+ cm_dmn (int arg),
+ cm_email (int arg),
+ cm_emph (int arg),
+ cm_i (int arg),
+ cm_kbd (int arg),
+ cm_key (int arg),
+ cm_math (int arg),
+ cm_not_fixed_width (int arg, int start, int end),
+ cm_r (int arg),
+ cm_sansserif (int arg),
+ cm_sc (int arg, int start_pos, int end_pos),
+ cm_slanted (int arg),
+ cm_strong (int arg, int start_pos, int end_pos),
+ cm_tt (int arg),
+ cm_indicate_url (int arg, int start, int end),
+ cm_var (int arg, int start_pos, int end_pos),
+ cm_verb (int arg);
+
+/* Block environments. */
+extern void cm_cartouche (void),
+ cm_group (void),
+ cm_display (void),
+ cm_smalldisplay (void),
+ cm_example (void),
+ cm_smallexample (void),
+ cm_smalllisp (void),
+ cm_lisp (void),
+ cm_format (void),
+ cm_smallformat (void),
+ cm_quotation (void),
+ cm_copying (void),
+ cm_flushleft (void),
+ cm_flushright (void),
+ cm_verbatim (void),
+ cm_end (void);
+
+/* Tables, lists, enumerations. */
+extern void cm_table (void),
+ cm_ftable (void),
+ cm_vtable (void),
+ cm_itemize (void),
+ cm_enumerate (void),
+ cm_multitable (void),
+ cm_headitem (void),
+ cm_item (void),
+ cm_itemx (void),
+ cm_tab (void);
+
+extern void cm_center (void),
+ cm_exdent (void),
+ cm_indent (void),
+ cm_noindent (void),
+ cm_noindent_cmd (void);
+
+/* Line and page breaks. */
+extern void cm_asterisk (void),
+ cm_sp (void),
+ cm_page (void);
+
+/* Non breaking words. */
+extern void cm_tie (int arg),
+ cm_w (int arg);
+
+/* Title page creation. */
+extern void cm_titlepage (void),
+ cm_author (void),
+ cm_titlepage_cmds (void),
+ cm_titlefont (int arg),
+ cm_today (int arg);
+
+/* Floats. */
+extern void cm_float (void),
+ cm_caption (int arg),
+ cm_shortcaption (void),
+ cm_listoffloats (void);
+
+/* Indices. */
+extern void cm_kindex (void),
+ cm_cindex (void),
+ cm_findex (void),
+ cm_pindex (void),
+ cm_vindex (void),
+ cm_tindex (void),
+ cm_defindex (void),
+ cm_defcodeindex (void),
+ cm_synindex (void),
+ cm_printindex (void);
+
+/* Conditionals. */
+extern void cm_set (void),
+ cm_clear (void),
+ cm_ifset (void),
+ cm_ifclear (void),
+ cm_ifeq (void),
+ cm_value (int arg, int start_pos, int end_pos);
+
+#endif /* !CMDS_H */
diff --git a/makeinfo/defun.c b/makeinfo/defun.c
new file mode 100644
index 0000000..d1218a0
--- /dev/null
+++ b/makeinfo/defun.c
@@ -0,0 +1,720 @@
+/* defun.c -- @defun and friends.
+ $Id: defun.c,v 1.18 2007/09/15 23:48:45 karl Exp $
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "defun.h"
+#include "xml.h"
+#include "insertion.h"
+#include "makeinfo.h"
+#include "cmds.h"
+#include "html.h"
+
+
+#define DEFUN_SELF_DELIMITING(c) \
+ ((c) == '(' || (c) == ')' || (c) == '[' || (c) == ']')
+
+struct token_accumulator
+{
+ unsigned int length;
+ unsigned int index;
+ char **tokens;
+};
+
+static void
+initialize_token_accumulator (struct token_accumulator *accumulator)
+{
+ accumulator->length = 0;
+ accumulator->index = 0;
+ accumulator->tokens = NULL;
+}
+
+static void
+accumulate_token (struct token_accumulator *accumulator, char *token)
+{
+ if (accumulator->index >= accumulator->length)
+ {
+ accumulator->length += 10;
+ accumulator->tokens = xrealloc (accumulator->tokens,
+ (accumulator->length * sizeof (char *)));
+ }
+ accumulator->tokens[accumulator->index] = token;
+ accumulator->index += 1;
+}
+
+/* Given STRING_POINTER pointing at an open brace, skip forward and return a
+ pointer to just past the matching close brace. */
+static int
+scan_group_in_string (char **string_pointer)
+{
+ char *scan_string = (*string_pointer) + 1;
+ unsigned int level = 1;
+ int started_command = 0;
+
+ for (;;)
+ {
+ int c;
+ if (level == 0)
+ {
+ *string_pointer = scan_string;
+ return 1;
+ }
+ c = *scan_string++;
+ if (c == 0)
+ {
+ /* Tweak line_number to compensate for fact that
+ we gobbled the whole line before coming here. */
+ line_number--;
+ line_error (_("Missing `}' in @def arg"));
+ line_number++;
+ *string_pointer = scan_string - 1;
+ return 0;
+ }
+
+ if (c == '{' && !started_command)
+ level++;
+ if (c == '}' && !started_command)
+ level--;
+
+ /* remember if at @. */
+ started_command = (c == '@' && !started_command);
+ }
+}
+
+/* Return a list of tokens from the contents of STRING.
+ Commands and brace-delimited groups count as single tokens.
+ Contiguous whitespace characters are converted to a token
+ consisting of a single space. */
+static char **
+args_from_string (char *string)
+{
+ struct token_accumulator accumulator;
+ char *token_start, *token_end;
+ char *scan_string = string;
+
+ initialize_token_accumulator (&accumulator);
+
+ while (*scan_string)
+ { /* Replace arbitrary whitespace by a single space. */
+ if (whitespace (*scan_string))
+ {
+ scan_string += 1;
+ while (whitespace (*scan_string))
+ scan_string += 1;
+ accumulate_token ((&accumulator), (xstrdup (" ")));
+ continue;
+ }
+
+ /* Commands count as single tokens. */
+ if (*scan_string == COMMAND_PREFIX)
+ {
+ token_start = scan_string;
+ scan_string += 1;
+ if (self_delimiting (*scan_string))
+ scan_string += 1;
+ else
+ {
+ int c;
+ while (1)
+ {
+ c = *scan_string++;
+
+ if ((c == 0) || (c == '{') || (whitespace (c)))
+ {
+ scan_string -= 1;
+ break;
+ }
+ }
+
+ if (*scan_string == '{')
+ {
+ char *s = scan_string;
+ (void) scan_group_in_string (&s);
+ scan_string = s;
+ }
+ }
+ token_end = scan_string;
+ }
+
+ /* Parentheses and brackets are self-delimiting. */
+ else if (DEFUN_SELF_DELIMITING (*scan_string))
+ {
+ token_start = scan_string;
+ scan_string += 1;
+ token_end = scan_string;
+ }
+
+ /* Open brace introduces a group that is a single token. */
+ else if (*scan_string == '{')
+ {
+ char *s = scan_string;
+ int balanced = scan_group_in_string (&s);
+
+ token_start = scan_string + 1;
+ scan_string = s;
+ token_end = balanced ? (scan_string - 1) : scan_string;
+ }
+
+ /* Make commas separate tokens so to differentiate them from
+ parameter types in XML output. */
+ else if (*scan_string == ',')
+ {
+ token_start = scan_string;
+ scan_string += 1;
+ token_end = scan_string;
+ }
+
+ /* Otherwise a token is delimited by whitespace, parentheses,
+ brackets, or braces. A token is also ended by a command. */
+ else
+ {
+ token_start = scan_string;
+
+ for (;;)
+ {
+ int c;
+
+ c = *scan_string++;
+
+ /* Do not back up if we're looking at a }; since the only
+ valid }'s are those matched with {'s, we want to give
+ an error. If we back up, we go into an infinite loop. */
+ if (!c || whitespace (c) || DEFUN_SELF_DELIMITING (c)
+ || c == '{')
+ {
+ scan_string--;
+ break;
+ }
+
+ /* End token if we are looking at a comma, as commas are
+ delimiters too. */
+ if (c == ',')
+ {
+ scan_string--;
+ break;
+ }
+
+ /* If we encounter a command embedded within a token,
+ then end the token. */
+ if (c == COMMAND_PREFIX)
+ {
+ scan_string--;
+ break;
+ }
+ }
+ token_end = scan_string;
+ }
+
+ accumulate_token (&accumulator, substring (token_start, token_end));
+ }
+ accumulate_token (&accumulator, NULL);
+ return accumulator.tokens;
+}
+
+static void
+process_defun_args (char **defun_args, int auto_var_p)
+{
+ int pending_space = 0;
+
+ if (xml)
+ {
+ xml_process_defun_args (defun_args, auto_var_p);
+ return;
+ }
+
+ for (;;)
+ {
+ char *defun_arg = *defun_args++;
+
+ if (defun_arg == NULL)
+ break;
+
+ if (defun_arg[0] == ' ')
+ {
+ pending_space = 1;
+ continue;
+ }
+
+ if (pending_space)
+ {
+ add_char (' ');
+ pending_space = 0;
+ }
+
+ if (DEFUN_SELF_DELIMITING (defun_arg[0]))
+ {
+ /* Within @deffn and friends, texinfo.tex makes parentheses
+ sans serif and brackets bold. We use roman instead. */
+ if (html)
+ insert_html_tag (START, "");
+
+ add_char (defun_arg[0]);
+
+ if (html)
+ insert_html_tag (END, "");
+ }
+ /* else if (defun_arg[0] == '&' || defun_arg[0] == COMMAND_PREFIX) */
+ /* execute_string ("%s", defun_arg); */
+ /* else if (auto_var_p) */
+ /* execute_string ("%s", defun_arg); */
+ else
+ execute_string ("%s", defun_arg);
+ }
+}
+
+static char *
+next_nonwhite_defun_arg (char ***arg_pointer)
+{
+ char **scan = (*arg_pointer);
+ char *arg = (*scan++);
+
+ if ((arg != 0) && (*arg == ' '))
+ arg = *scan++;
+
+ if (arg == 0)
+ scan -= 1;
+
+ *arg_pointer = scan;
+
+ return (arg == 0) ? "" : arg;
+}
+
+
+/* This is needed also in insertion.c. */
+
+enum insertion_type
+get_base_type (enum insertion_type type)
+{
+ enum insertion_type base_type;
+ switch (type)
+ {
+ case defivar: base_type = defcv; break;
+ case defmac: base_type = deffn; break;
+ case defmethod: base_type = defop; break;
+ case defopt: base_type = defvr; break;
+ case defspec: base_type = deffn; break;
+ case deftypecv: base_type = deftypecv; break;
+ case deftypefun: base_type = deftypefn; break;
+ case deftypeivar: base_type = deftypeivar; break;
+ case deftypemethod: base_type = deftypemethod; break;
+ case deftypeop: base_type = deftypeop; break;
+ case deftypevar: base_type = deftypevr; break;
+ case defun: base_type = deffn; break;
+ case defvar: base_type = defvr; break;
+ default:
+ base_type = type;
+ break;
+ }
+
+ return base_type;
+}
+
+/* Make the defun type insertion.
+ TYPE says which insertion this is.
+ X_P, if nonzero, says not to start a new insertion. */
+static void
+defun_internal (enum insertion_type type, int x_p)
+{
+ enum insertion_type base_type;
+ char **defun_args, **scan_args;
+ const char *category;
+ char *defined_name;
+ char *type_name = NULL;
+ char *type_name2 = NULL;
+
+ {
+ char *line;
+
+ /* The @def.. line is the only place in Texinfo where you are
+ allowed to use unquoted braces that don't delimit arguments of
+ a command or a macro; in any other place it will trigger an
+ error message from the reader loop. The special handling of
+ this case inside `args_from_string' is an extra special hack
+ which allows this. The side effect is that if we try to expand
+ the rest of the line below, the recursive reader loop will
+ signal an error if there are brace-delimited arguments on that line.
+
+ The best solution to this would be to change the syntax of
+ @def.. commands so that it doesn't violate Texinfo's own rules.
+ But it's probably too late for this now, as it will break a lot
+ of existing manuals.
+
+ Unfortunately, this means that you can't call macros, use @value, etc.
+ inside @def.. commands, sigh. */
+ get_rest_of_line (0, &line);
+
+ /* Basic line continuation. If a line ends with \s*@\s* concatanate
+ the next line. */
+ {
+ char *next_line, *new_line;
+ int i;
+
+ line_continuation:
+ i = strlen (line) - 1;
+
+ if (i > 0 && line[i] == '@' && line[i-1] != '@')
+ {
+ get_rest_of_line (0, &next_line);
+ new_line = (char *) xmalloc (i + strlen (next_line) + 2);
+ strncpy (new_line, line, i);
+ new_line[i] = '\0';
+ free (line);
+ strcat (new_line, " ");
+ strcat (new_line, next_line);
+ line = xstrdup (new_line);
+ free (next_line);
+ free (new_line);
+
+ goto line_continuation;
+ }
+ }
+
+ defun_args = (args_from_string (line));
+ free (line);
+ }
+
+ scan_args = defun_args;
+
+ /* Get base type and category string. */
+ base_type = get_base_type (type);
+
+ /* xx all these const strings should be determined upon
+ documentlanguage argument and NOT via gettext (kama). */
+ switch (type)
+ {
+ case defun:
+ case deftypefun:
+ category = gdt("Function");
+ break;
+ case defmac:
+ category = gdt("Macro");
+ break;
+ case defspec:
+ category = gdt("Special Form");
+ break;
+ case defvar:
+ case deftypevar:
+ category = gdt("Variable");
+ break;
+ case defopt:
+ category = gdt("User Option");
+ break;
+ case defivar:
+ case deftypeivar:
+ category = gdt("Instance Variable");
+ break;
+ case defmethod:
+ case deftypemethod:
+ category = gdt("Method");
+ break;
+ default:
+ category = next_nonwhite_defun_arg (&scan_args);
+ break;
+ }
+
+ /* The class name. */
+ if ((base_type == deftypecv)
+ || (base_type == deftypefn)
+ || (base_type == deftypevr)
+ || (base_type == defcv)
+ || (base_type == defop)
+ || (base_type == deftypeivar)
+ || (base_type == deftypemethod)
+ || (base_type == deftypeop)
+ )
+ type_name = next_nonwhite_defun_arg (&scan_args);
+
+ /* The type name for typed languages. */
+ if ((base_type == deftypecv)
+ || (base_type == deftypeivar)
+ || (base_type == deftypemethod)
+ || (base_type == deftypeop)
+ )
+ type_name2 = next_nonwhite_defun_arg (&scan_args);
+
+ /* The function or whatever that's actually being defined. */
+ defined_name = next_nonwhite_defun_arg (&scan_args);
+
+ /* This hack exists solely for the purposes of formatting the Texinfo
+ manual. I couldn't think of a better way. The token might be a
+ simple @@ followed immediately by more text. If this is the case,
+ then the next defun arg is part of this one, and we should
+ concatenate them. */
+ if (*scan_args && **scan_args && !whitespace (**scan_args)
+ && STREQ (defined_name, "@@"))
+ {
+ char *tem = xmalloc (3 + strlen (scan_args[0]));
+
+ sprintf (tem, "@@%s", scan_args[0]);
+
+ free (scan_args[0]);
+ scan_args[0] = tem;
+ scan_args++;
+ defined_name = tem;
+ }
+
+ /* It's easy to write @defun foo(arg1 arg2), but a following ( is
+ misparsed by texinfo.tex and this is next to impossible to fix.
+ Warn about it. */
+ if (*scan_args && **scan_args && **scan_args == '(')
+ warning ("`%c' follows defined name `%s' instead of whitespace",
+ **scan_args, defined_name);
+
+ if (!x_p)
+ begin_insertion (type);
+
+ /* Write the definition header line.
+ This should start at the normal indentation. */
+ current_indent -= default_indentation_increment;
+ start_paragraph ();
+
+ if (!html && !xml)
+ switch (base_type)
+ {
+ case deffn:
+ case defvr:
+ case deftp:
+ execute_string (" --- %s: %s", category, defined_name);
+ break;
+ case deftypefn:
+ case deftypevr:
+ execute_string (" --- %s: %s %s", category, type_name, defined_name);
+ break;
+ case defcv:
+ execute_string (" --- %s %s %s: %s", category, gdt("of"), type_name,
+ defined_name);
+ break;
+ case deftypecv:
+ case deftypeivar:
+ execute_string (" --- %s %s %s: %s %s", category, gdt("of"), type_name,
+ type_name2, defined_name);
+ break;
+ case defop:
+ execute_string (" --- %s %s %s: %s", category, gdt("on"), type_name,
+ defined_name);
+ break;
+ case deftypeop:
+ execute_string (" --- %s %s %s: %s %s", category, gdt("on"), type_name,
+ type_name2, defined_name);
+ break;
+ case deftypemethod:
+ execute_string (" --- %s %s %s: %s %s", category, gdt("on"), type_name,
+ type_name2, defined_name);
+ break;
+ }
+ else if (html)
+ {
+ /* If this is not a @def...x version, it could only
+ be a normal version @def.... So start the table here. */
+ if (!x_p)
+ insert_string ("<div class=\"defun\">\n");
+ else
+ rollback_empty_tag ("blockquote");
+
+ /* xx The single words (on, off) used here, should depend on
+ documentlanguage and NOT on gettext --kama. */
+ switch (base_type)
+ {
+ case deffn:
+ case defvr:
+ case deftp:
+ case deftypefn:
+ case deftypevr:
+ execute_string ("--- %s: ", category);
+ break;
+
+ case defcv:
+ case deftypecv:
+ case deftypeivar:
+ execute_string ("--- %s %s %s: ", category, gdt("of"), type_name);
+ break;
+
+ case defop:
+ case deftypemethod:
+ case deftypeop:
+ execute_string ("--- %s %s %s: ", category, gdt("on"), type_name);
+ break;
+ } /* switch (base_type)... */
+
+ switch (base_type)
+ {
+ case deffn:
+ case defvr:
+ case deftp:
+ /* <var> is for the following function arguments. */
+ insert_html_tag (START, "b");
+ execute_string ("%s", defined_name);
+ insert_html_tag (END, "b");
+ insert_html_tag (START, "var");
+ break;
+ case deftypefn:
+ case deftypevr:
+ execute_string ("%s ", type_name);
+ insert_html_tag (START, "b");
+ execute_string ("%s", defined_name);
+ insert_html_tag (END, "b");
+ insert_html_tag (START, "var");
+ break;
+ case defcv:
+ case defop:
+ insert_html_tag (START, "b");
+ execute_string ("%s", defined_name);
+ insert_html_tag (END, "b");
+ insert_html_tag (START, "var");
+ break;
+ case deftypecv:
+ case deftypeivar:
+ case deftypemethod:
+ case deftypeop:
+ execute_string ("%s ", type_name2);
+ insert_html_tag (START, "b");
+ execute_string ("%s", defined_name);
+ insert_html_tag (END, "b");
+ insert_html_tag (START, "var");
+ break;
+ }
+ }
+ else if (xml)
+ xml_begin_def_term (base_type, category, defined_name, type_name,
+ type_name2);
+
+ current_indent += default_indentation_increment;
+
+ /* Now process the function arguments, if any. If these carry onto
+ the next line, they should be indented by two increments to
+ distinguish them from the body of the definition, which is indented
+ by one increment. */
+ current_indent += default_indentation_increment;
+
+ switch (base_type)
+ {
+ case deffn:
+ case defop:
+ process_defun_args (scan_args, 1);
+ break;
+
+ /* Through Makeinfo 1.67 we processed remaining args only for deftp,
+ deftypefn, and deftypemethod. But the libc manual, for example,
+ needs to say:
+ @deftypevar {char *} tzname[2]
+ And simply allowing the extra text seems far simpler than trying
+ to invent yet more defn commands. In any case, we should either
+ output it or give an error, not silently ignore it. */
+ default:
+ process_defun_args (scan_args, 0);
+ break;
+ }
+
+ current_indent -= default_indentation_increment;
+ if (!html)
+ close_single_paragraph ();
+
+ /* Make an entry in the appropriate index. (XML and
+ Docbook already got their entries, so skip them.) */
+ if (!xml)
+ switch (base_type)
+ {
+ case deffn:
+ case deftypefn:
+ execute_string ("@findex %s\n", defined_name);
+ break;
+ case defcv:
+ case deftypecv:
+ case deftypevr:
+ case defvr:
+ execute_string ("@vindex %s\n", defined_name);
+ break;
+ case deftypeivar:
+ execute_string ("@vindex %s %s %s\n", defined_name, gdt("of"),
+ type_name);
+ break;
+ case defop:
+ case deftypeop:
+ case deftypemethod:
+ execute_string ("@findex %s %s %s\n", defined_name, gdt("on"),
+ type_name);
+ break;
+ case deftp:
+ execute_string ("@tindex %s\n", defined_name);
+ break;
+ }
+
+ if (xml)
+ xml_end_def_term ();
+ else if (html)
+ {
+ inhibit_paragraph_indentation = 1;
+ no_indent = 1;
+ insert_html_tag (END, "var");
+ insert_string ("<br>\n");
+ /* Indent the definition a bit. */
+ add_html_block_elt ("<blockquote>");
+ no_indent = 0;
+ inhibit_paragraph_indentation = 0;
+ paragraph_is_open = 0;
+ }
+
+ /* Deallocate the token list. */
+ scan_args = defun_args;
+ while (1)
+ {
+ char * arg = (*scan_args++);
+ if (arg == NULL)
+ break;
+ free (arg);
+ }
+ free (defun_args);
+}
+
+/* Add an entry for a function, macro, special form, variable, or option.
+ If the name of the calling command ends in `x', then this is an extra
+ entry included in the body of an insertion of the same type. */
+void
+cm_defun (void)
+{
+ enum insertion_type type;
+ char *base_command = xstrdup (command); /* command with any `x' removed */
+ /* FIXME: is strlen(command) allways > 0? */
+ int x_p = (command[strlen (command) - 1] == 'x');
+
+ if (x_p)
+ base_command[strlen (base_command) - 1] = 0;
+
+ type = find_type_from_name (base_command);
+
+ /* If we are adding to an already existing insertion, then make sure
+ that we are already in an insertion of type TYPE. */
+ if (x_p)
+ {
+ INSERTION_ELT *i = insertion_stack;
+ /* Skip over ifclear and ifset conditionals. */
+ while (i && (i->insertion == ifset || i->insertion == ifclear))
+ i = i->next;
+
+ if (!i || i->insertion != type)
+ {
+ line_error (_("Must be in `@%s' environment to use `@%s'"),
+ base_command, command);
+ discard_until ("\n");
+ return;
+ }
+ }
+
+ defun_internal (type, x_p);
+ free (base_command);
+}
diff --git a/makeinfo/defun.h b/makeinfo/defun.h
new file mode 100644
index 0000000..fcd4ff9
--- /dev/null
+++ b/makeinfo/defun.h
@@ -0,0 +1,30 @@
+/* defun.h -- declaration for defuns.
+ $Id: defun.h,v 1.6 2007/07/01 21:20:32 karl Exp $
+
+ Copyright (C) 1999, 2005, 2007 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 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 <http://www.gnu.org/licenses/>.
+
+ Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#ifndef DEFUN_H
+#define DEFUN_H
+
+#include "insertion.h"
+
+extern enum insertion_type get_base_type (enum insertion_type);
+extern void cm_defun (void);
+
+#endif /* !DEFUN_H */
+
diff --git a/makeinfo/files.c b/makeinfo/files.c
new file mode 100644
index 0000000..49e3658
--- /dev/null
+++ b/makeinfo/files.c
@@ -0,0 +1,783 @@
+/* files.c -- file-related functions for makeinfo.
+ $Id: files.c,v 1.8 2007/07/01 21:20:32 karl Exp $
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "files.h"
+#include "html.h"
+#include "index.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "node.h"
+
+FSTACK *filestack = NULL;
+
+static int node_filename_stack_index = 0;
+static int node_filename_stack_size = 0;
+static char **node_filename_stack = NULL;
+
+/* Looking for include files. */
+
+/* Given a string containing units of information separated by colons,
+ return the next one pointed to by INDEX, or NULL if there are no more.
+ Advance INDEX to the character after the colon. */
+static char *
+extract_colon_unit (char *string, int *index)
+{
+ int start;
+ int path_sep_char = PATH_SEP[0];
+ int i = *index;
+
+ if (!string || (i >= strlen (string)))
+ return NULL;
+
+ /* Each call to this routine leaves the index pointing at a colon if
+ there is more to the path. If i > 0, then increment past the
+ `:'. If i == 0, then the path has a leading colon. Trailing colons
+ are handled OK by the `else' part of the if statement; an empty
+ string is returned in that case. */
+ if (i && string[i] == path_sep_char)
+ i++;
+
+ start = i;
+ while (string[i] && string[i] != path_sep_char) i++;
+ *index = i;
+
+ if (i == start)
+ {
+ if (string[i])
+ (*index)++;
+
+ /* Return "" in the case of a trailing `:'. */
+ return xstrdup ("");
+ }
+ else
+ {
+ char *value;
+
+ value = xmalloc (1 + (i - start));
+ memcpy (value, &string[start], (i - start));
+ value [i - start] = 0;
+
+ return value;
+ }
+}
+
+/* Return the full pathname for FILENAME by searching along PATH.
+ When found, return the stat () info for FILENAME in FINFO.
+ If PATH is NULL, only the current directory is searched.
+ If the file could not be found, return a NULL pointer. */
+char *
+get_file_info_in_path (char *filename, char *path, struct stat *finfo)
+{
+ char *dir;
+ int result, index = 0;
+
+ if (path == NULL)
+ path = ".";
+
+ /* Handle absolute pathnames. */
+ if (IS_ABSOLUTE (filename)
+ || (*filename == '.'
+ && (IS_SLASH (filename[1])
+ || (filename[1] == '.' && IS_SLASH (filename[2])))))
+ {
+ if (stat (filename, finfo) == 0)
+ return xstrdup (filename);
+ else
+ return NULL;
+ }
+
+ while ((dir = extract_colon_unit (path, &index)))
+ {
+ char *fullpath;
+
+ if (!*dir)
+ {
+ free (dir);
+ dir = xstrdup (".");
+ }
+
+ fullpath = xmalloc (2 + strlen (dir) + strlen (filename));
+ sprintf (fullpath, "%s/%s", dir, filename);
+ free (dir);
+
+ result = stat (fullpath, finfo);
+
+ if (result == 0)
+ return fullpath;
+ else
+ free (fullpath);
+ }
+ return NULL;
+}
+
+/* Prepend and append new paths to include_files_path. */
+void
+prepend_to_include_path (char *path)
+{
+ if (!include_files_path)
+ {
+ include_files_path = xstrdup (path);
+ include_files_path = xrealloc (include_files_path,
+ strlen (include_files_path) + 3); /* 3 for ":.\0" */
+ strcat (strcat (include_files_path, PATH_SEP), ".");
+ }
+ else
+ {
+ char *tmp = xstrdup (include_files_path);
+ include_files_path = xrealloc (include_files_path,
+ strlen (include_files_path) + strlen (path) + 2); /* 2 for ":\0" */
+ strcpy (include_files_path, path);
+ strcat (include_files_path, PATH_SEP);
+ strcat (include_files_path, tmp);
+ free (tmp);
+ }
+}
+
+void
+append_to_include_path (char *path)
+{
+ if (!include_files_path)
+ include_files_path = xstrdup (".");
+
+ include_files_path = (char *) xrealloc (include_files_path,
+ 2 + strlen (include_files_path) + strlen (path));
+ strcat (include_files_path, PATH_SEP);
+ strcat (include_files_path, path);
+}
+
+/* Remove the first path from the include_files_path. */
+void
+pop_path_from_include_path (void)
+{
+ int i = 0;
+ char *tmp;
+
+ if (include_files_path)
+ for (i = 0; i < strlen (include_files_path)
+ && include_files_path[i] != ':'; i++);
+
+ /* Advance include_files_path to the next char from ':' */
+ tmp = (char *) xmalloc (strlen (include_files_path) - i);
+ strcpy (tmp, (char *) include_files_path + i + 1);
+
+ free (include_files_path);
+ include_files_path = tmp;
+}
+
+/* Find and load the file named FILENAME. Return a pointer to
+ the loaded file, or NULL if it can't be loaded. If USE_PATH is zero,
+ just look for the given file (this is used in handle_delayed_writes),
+ else search along include_files_path. */
+
+char *
+find_and_load (char *filename, int use_path)
+{
+ struct stat fileinfo;
+ long file_size;
+ int file = -1, count = 0;
+ char *fullpath, *result;
+ int n, bytes_to_read;
+
+ result = fullpath = NULL;
+
+ fullpath
+ = get_file_info_in_path (filename, use_path ? include_files_path : NULL,
+ &fileinfo);
+
+ if (!fullpath)
+ goto error_exit;
+
+ filename = fullpath;
+ file_size = (long) fileinfo.st_size;
+
+ file = open (filename, O_RDONLY);
+ if (file < 0)
+ goto error_exit;
+
+ /* Load the file, with enough room for a newline and a null. */
+ result = xmalloc (file_size + 2);
+
+ /* VMS stat lies about the st_size value. The actual number of
+ readable bytes is always less than this value. The arcane
+ mysteries of VMS/RMS are too much to probe, so this hack
+ suffices to make things work. It's also needed on Cygwin. And so
+ we might as well use it everywhere. */
+ bytes_to_read = file_size;
+ while ((n = read (file, result + count, bytes_to_read)) > 0)
+ {
+ count += n;
+ bytes_to_read -= n;
+ }
+ if (0 < count && count < file_size)
+ result = xrealloc (result, count + 2); /* why waste the slack? */
+ else if (n == -1)
+error_exit:
+ {
+ if (result)
+ free (result);
+
+ if (fullpath)
+ free (fullpath);
+
+ if (file != -1)
+ close (file);
+
+ return NULL;
+ }
+ close (file);
+
+ /* Set the globals to the new file. */
+ input_text = result;
+ input_text_length = count;
+ input_filename = fullpath;
+ node_filename = xstrdup (fullpath);
+ input_text_offset = 0;
+ line_number = 1;
+ /* Not strictly necessary. This magic prevents read_token () from doing
+ extra unnecessary work each time it is called (that is a lot of times).
+ INPUT_TEXT_LENGTH is one past the actual end of the text. */
+ input_text[input_text_length] = '\n';
+ /* This, on the other hand, is always necessary. */
+ input_text[input_text_length+1] = 0;
+ return result;
+}
+
+/* Pushing and popping files. */
+static void
+push_node_filename (void)
+{
+ if (node_filename_stack_index + 1 > node_filename_stack_size)
+ node_filename_stack = xrealloc
+ (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *));
+
+ node_filename_stack[node_filename_stack_index] = node_filename;
+ node_filename_stack_index++;
+}
+
+static void
+pop_node_filename (void)
+{
+ node_filename = node_filename_stack[--node_filename_stack_index];
+}
+
+/* Save the state of the current input file. */
+void
+pushfile (void)
+{
+ FSTACK *newstack = xmalloc (sizeof (FSTACK));
+ newstack->filename = input_filename;
+ newstack->text = input_text;
+ newstack->size = input_text_length;
+ newstack->offset = input_text_offset;
+ newstack->line_number = line_number;
+ newstack->next = filestack;
+
+ filestack = newstack;
+ push_node_filename ();
+}
+
+/* Make the current file globals be what is on top of the file stack. */
+void
+popfile (void)
+{
+ FSTACK *tos = filestack;
+
+ if (!tos)
+ abort (); /* My fault. I wonder what I did? */
+
+ if (macro_expansion_output_stream)
+ {
+ maybe_write_itext (input_text, input_text_offset);
+ forget_itext (input_text);
+ }
+
+ /* Pop the stack. */
+ filestack = filestack->next;
+
+ /* Make sure that commands with braces have been satisfied. */
+ if (!executing_string && !me_executing_string)
+ discard_braces ();
+
+ /* Get the top of the stack into the globals. */
+ input_filename = tos->filename;
+ input_text = tos->text;
+ input_text_length = tos->size;
+ input_text_offset = tos->offset;
+ line_number = tos->line_number;
+ free (tos);
+
+ /* Go back to the (now) current node. */
+ pop_node_filename ();
+}
+
+/* Flush all open files on the file stack. */
+void
+flush_file_stack (void)
+{
+ while (filestack)
+ {
+ char *fname = input_filename;
+ char *text = input_text;
+ popfile ();
+ free (fname);
+ free (text);
+ }
+}
+
+/* Return the index of the first character in the filename
+ which is past all the leading directory characters. */
+static int
+skip_directory_part (char *filename)
+{
+ int i = strlen (filename) - 1;
+
+ while (i && !IS_SLASH (filename[i]))
+ i--;
+ if (IS_SLASH (filename[i]))
+ i++;
+ else if (filename[i] && HAVE_DRIVE (filename))
+ i = 2;
+
+ return i;
+}
+
+static char *
+filename_non_directory (char *name)
+{
+ return xstrdup (name + skip_directory_part (name));
+}
+
+/* Return just the simple part of the filename; i.e. the
+ filename without the path information, or extensions.
+ This conses up a new string. */
+char *
+filename_part (char *filename)
+{
+ char *basename = filename_non_directory (filename);
+
+#ifdef REMOVE_OUTPUT_EXTENSIONS
+ /* See if there is an extension to remove. If so, remove it. */
+ {
+ char *temp = strrchr (basename, '.');
+ if (temp)
+ *temp = 0;
+ }
+#endif /* REMOVE_OUTPUT_EXTENSIONS */
+ return basename;
+}
+
+/* Return the pathname part of filename. This can be NULL. */
+char *
+pathname_part (char *filename)
+{
+ char *result = NULL;
+ int i;
+
+ filename = expand_filename (filename, "");
+
+ i = skip_directory_part (filename);
+ if (i)
+ {
+ result = xmalloc (1 + i);
+ strncpy (result, filename, i);
+ result[i] = 0;
+ }
+ free (filename);
+ return result;
+}
+
+/* Return the full path to FILENAME. */
+static char *
+full_pathname (char *filename)
+{
+ int initial_character;
+ char *result;
+
+ /* No filename given? */
+ if (!filename || !*filename)
+ return xstrdup ("");
+
+ /* Already absolute? */
+ if (IS_ABSOLUTE (filename) ||
+ (*filename == '.' &&
+ (IS_SLASH (filename[1]) ||
+ (filename[1] == '.' && IS_SLASH (filename[2])))))
+ return xstrdup (filename);
+
+ initial_character = *filename;
+ if (initial_character != '~')
+ {
+ char *localdir = xmalloc (1025);
+#ifdef HAVE_GETCWD
+ if (!getcwd (localdir, 1024))
+#else
+ if (!getwd (localdir))
+#endif
+ {
+ fprintf (stderr, _("%s: getwd: %s, %s\n"),
+ progname, filename, localdir);
+ xexit (1);
+ }
+
+ strcat (localdir, "/");
+ strcat (localdir, filename);
+ result = xstrdup (localdir);
+ free (localdir);
+ }
+ else
+ { /* Does anybody know why WIN32 doesn't want to support $HOME?
+ If the reason is they don't have getpwnam, they should
+ only disable the else clause below. */
+#ifndef WIN32
+ if (IS_SLASH (filename[1]))
+ {
+ /* Return the concatenation of the environment variable HOME
+ and the rest of the string. */
+ char *temp_home;
+
+ temp_home = (char *) getenv ("HOME");
+ result = xmalloc (strlen (&filename[1])
+ + 1
+ + temp_home ? strlen (temp_home)
+ : 0);
+ *result = 0;
+
+ if (temp_home)
+ strcpy (result, temp_home);
+
+ strcat (result, &filename[1]);
+ }
+ else
+ {
+ struct passwd *user_entry;
+ int i, c;
+ char *username = xmalloc (257);
+
+ for (i = 1; (c = filename[i]); i++)
+ {
+ if (IS_SLASH (c))
+ break;
+ else
+ username[i - 1] = c;
+ }
+ if (c)
+ username[i - 1] = 0;
+
+ user_entry = getpwnam (username);
+
+ if (!user_entry)
+ return xstrdup (filename);
+
+ result = xmalloc (1 + strlen (user_entry->pw_dir)
+ + strlen (&filename[i]));
+ strcpy (result, user_entry->pw_dir);
+ strcat (result, &filename[i]);
+ }
+#endif /* not WIN32 */
+ }
+ return result;
+}
+
+/* Return the expansion of FILENAME. */
+char *
+expand_filename (char *filename, char *input_name)
+{
+ int i;
+
+ if (filename)
+ {
+ filename = full_pathname (filename);
+ if (IS_ABSOLUTE (filename)
+ || (*filename == '.' &&
+ (IS_SLASH (filename[1]) ||
+ (filename[1] == '.' && IS_SLASH (filename[2])))))
+ return filename;
+ }
+ else
+ {
+ filename = filename_non_directory (input_name);
+
+ if (!*filename)
+ {
+ free (filename);
+ filename = xstrdup ("noname.texi");
+ }
+
+ for (i = strlen (filename) - 1; i; i--)
+ if (filename[i] == '.')
+ break;
+
+ if (!i)
+ i = strlen (filename);
+
+ if (i + 6 > (strlen (filename)))
+ filename = xrealloc (filename, i + 6);
+ strcpy (filename + i, html ? ".html" : ".info");
+ return filename;
+ }
+
+ if (IS_ABSOLUTE (input_name))
+ {
+ /* Make it so that relative names work. */
+ char *result;
+
+ i = strlen (input_name) - 1;
+
+ result = xmalloc (1 + strlen (input_name) + strlen (filename));
+ strcpy (result, input_name);
+
+ while (!IS_SLASH (result[i]) && i)
+ i--;
+ if (IS_SLASH (result[i]))
+ i++;
+
+ strcpy (&result[i], filename);
+ free (filename);
+ return result;
+ }
+ return filename;
+}
+
+char *
+output_name_from_input_name (char *name)
+{
+ return expand_filename (NULL, name);
+}
+
+
+/* Modify the file name FNAME so that it fits the limitations of the
+ underlying filesystem. In particular, truncate the file name as it
+ would be truncated by the filesystem. We assume the result can
+ never be longer than the original, otherwise we couldn't be sure we
+ have enough space in the original string to modify it in place. */
+char *
+normalize_filename (char *fname)
+{
+ int maxlen;
+ char orig[PATH_MAX + 1];
+ int i;
+ char *lastdot, *p;
+
+#ifdef _PC_NAME_MAX
+ maxlen = pathconf (fname, _PC_NAME_MAX);
+ if (maxlen < 1)
+#endif
+ maxlen = PATH_MAX;
+
+ i = skip_directory_part (fname);
+ if (fname[i] == '\0')
+ return fname; /* only a directory name -- don't modify */
+ strcpy (orig, fname + i);
+
+ switch (maxlen)
+ {
+ case 12: /* MS-DOS 8+3 filesystem */
+ if (orig[0] == '.') /* leading dots are not allowed */
+ orig[0] = '_';
+ lastdot = strrchr (orig, '.');
+ if (!lastdot)
+ lastdot = orig + strlen (orig);
+ strncpy (fname + i, orig, lastdot - orig);
+ for (p = fname + i;
+ p < fname + i + (lastdot - orig) && p < fname + i + 8;
+ p++)
+ if (*p == '.')
+ *p = '_';
+ *p = '\0';
+ if (*lastdot == '.')
+ strncat (fname + i, lastdot, 4);
+ break;
+ case 14: /* old Unix systems with 14-char limitation */
+ strcpy (fname + i, orig);
+ if (strlen (fname + i) > 14)
+ fname[i + 14] = '\0';
+ break;
+ default:
+ strcpy (fname + i, orig);
+ if (strlen (fname) > maxlen - 1)
+ fname[maxlen - 1] = '\0';
+ break;
+ }
+
+ return fname;
+}
+
+/* Delayed writing functions. A few of the commands
+ needs to be handled at the end, namely @contents,
+ @shortcontents, @printindex and @listoffloats.
+ These functions take care of that. */
+static DELAYED_WRITE *delayed_writes = NULL;
+int handling_delayed_writes = 0;
+
+void
+register_delayed_write (char *delayed_command)
+{
+ DELAYED_WRITE *new;
+
+ if (!current_output_filename || !*current_output_filename)
+ {
+ /* Cannot register if we don't know what the output file is. */
+ warning (_("`%s' omitted before output filename"), delayed_command);
+ return;
+ }
+
+ if (STREQ (current_output_filename, "-"))
+ {
+ /* Do not register a new write if the output file is not seekable.
+ Let the user know about it first, though. */
+ warning (_("`%s' omitted since writing to stdout"), delayed_command);
+ return;
+ }
+
+ /* Don't complain if the user is writing /dev/null, since surely they
+ don't care, but don't register the delayed write, either. */
+ if (FILENAME_CMP (current_output_filename, NULL_DEVICE) == 0
+ || FILENAME_CMP (current_output_filename, ALSO_NULL_DEVICE) == 0)
+ return;
+
+ /* We need the HTML header in the output,
+ to get a proper output_position. */
+ if (!executing_string && html)
+ output_head ();
+ /* Get output_position updated. */
+ flush_output ();
+
+ new = xmalloc (sizeof (DELAYED_WRITE));
+ new->command = xstrdup (delayed_command);
+ new->filename = xstrdup (current_output_filename);
+ new->input_filename = xstrdup (input_filename);
+ new->position = output_position;
+ new->calling_line = line_number;
+ new->node = current_node ? xstrdup (current_node): "";
+
+ new->node_order = node_order;
+ new->index_order = index_counter;
+
+ new->next = delayed_writes;
+ delayed_writes = new;
+}
+
+void
+handle_delayed_writes (void)
+{
+ DELAYED_WRITE *temp = (DELAYED_WRITE *) reverse_list
+ ((GENERIC_LIST *) delayed_writes);
+ int position_shift_amount, line_number_shift_amount;
+ char *delayed_buf;
+
+ handling_delayed_writes = 1;
+
+ while (temp)
+ {
+ delayed_buf = find_and_load (temp->filename, 0);
+
+ if (output_paragraph_offset > 0)
+ {
+ error (_("Output buffer not empty."));
+ return;
+ }
+
+ if (!delayed_buf)
+ {
+ fs_error (temp->filename);
+ return;
+ }
+
+ output_stream = fopen (temp->filename, "w");
+ if (!output_stream)
+ {
+ fs_error (temp->filename);
+ return;
+ }
+
+ if (fwrite (delayed_buf, 1, temp->position, output_stream) != temp->position)
+ {
+ fs_error (temp->filename);
+ return;
+ }
+
+ {
+ int output_position_at_start = output_position;
+ int line_number_at_start = output_line_number;
+
+ /* In order to make warnings and errors
+ refer to the correct line number. */
+ input_filename = temp->input_filename;
+ line_number = temp->calling_line;
+
+ execute_string ("%s", temp->command);
+ flush_output ();
+
+ /* Since the output file is modified, following delayed writes
+ need to be updated by this amount. */
+ position_shift_amount = output_position - output_position_at_start;
+ line_number_shift_amount = output_line_number - line_number_at_start;
+ }
+
+ if (fwrite (delayed_buf + temp->position, 1,
+ input_text_length - temp->position, output_stream)
+ != input_text_length - temp->position
+ || fclose (output_stream) != 0)
+ fs_error (temp->filename);
+
+ /* Done with the buffer. */
+ free (delayed_buf);
+
+ /* Update positions in tag table for nodes that are defined after
+ the line this delayed write is registered. */
+ if (!html && !xml)
+ {
+ TAG_ENTRY *node;
+ for (node = tag_table; node; node = node->next_ent)
+ if (node->order > temp->node_order)
+ node->position += position_shift_amount;
+ }
+
+ /* Something similar for the line numbers in all of the defined
+ indices. */
+ {
+ int i;
+ for (i = 0; i < defined_indices; i++)
+ if (name_index_alist[i])
+ {
+ char *name = ((INDEX_ALIST *) name_index_alist[i])->name;
+ INDEX_ELT *index;
+ for (index = index_list (name); index; index = index->next)
+ if ((no_headers || STREQ (index->node, temp->node))
+ && index->entry_number > temp->index_order)
+ index->output_line += line_number_shift_amount;
+ }
+ }
+
+ /* Shift remaining delayed positions
+ by the length of this write. */
+ {
+ DELAYED_WRITE *future_write = temp->next;
+ while (future_write)
+ {
+ if (STREQ (temp->filename, future_write->filename))
+ future_write->position += position_shift_amount;
+ future_write = future_write->next;
+ }
+ }
+
+ temp = temp->next;
+ }
+}
diff --git a/makeinfo/files.h b/makeinfo/files.h
new file mode 100644
index 0000000..856c108
--- /dev/null
+++ b/makeinfo/files.h
@@ -0,0 +1,68 @@
+/* files.h -- declarations for files.c.
+ $Id: files.h,v 1.7 2007/07/01 21:20:32 karl Exp $
+
+ Copyright (C) 1998, 2002, 2004, 2007 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef FILES_H
+#define FILES_H
+
+/* A stack of file information records. If a new file is read in with
+ "@input", we remember the old input file state on this stack. */
+typedef struct fstack
+{
+ struct fstack *next;
+ char *filename;
+ char *text;
+ int size;
+ int offset;
+ int line_number;
+} FSTACK;
+extern FSTACK *filestack;
+
+extern void pushfile (void);
+extern void popfile (void);
+extern void flush_file_stack (void);
+extern char *get_file_info_in_path (char *filename, char *path,
+ struct stat *finfo);
+extern char *find_and_load (char *filename, int use_path);
+extern char *output_name_from_input_name (char *name);
+extern char *expand_filename (char *filename, char *input_name);
+extern char *filename_part (char *filename);
+extern char *pathname_part (char *filename);
+extern char *normalize_filename (char *fname);
+extern void append_to_include_path (char *path);
+extern void prepend_to_include_path (char *path);
+extern void pop_path_from_include_path (void);
+extern void register_delayed_write (char *delayed_command);
+extern void handle_delayed_writes (void);
+
+typedef struct delayed_write
+{
+ struct delayed_write *next;
+ char *command;
+ char *filename;
+ char *input_filename;
+ char *node;
+ int position;
+ int calling_line;
+
+ int node_order;
+ int index_order;
+} DELAYED_WRITE;
+
+extern int handling_delayed_writes;
+
+#endif /* !FILES_H */
diff --git a/makeinfo/float.c b/makeinfo/float.c
new file mode 100644
index 0000000..8866ffb
--- /dev/null
+++ b/makeinfo/float.c
@@ -0,0 +1,430 @@
+/* float.c -- float environment functions.
+ $Id: float.c,v 1.12 2007/07/01 21:20:32 karl Exp $
+
+ Copyright (C) 2003, 2004, 2007 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 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 <http://www.gnu.org/licenses/>.
+
+ Originally written by Alper Ersoy <dirt@gtk.org>. */
+
+#include "system.h"
+#include "makeinfo.h"
+#include "cmds.h"
+#include "files.h"
+#include "float.h"
+#include "html.h"
+#include "sectioning.h"
+#include "xml.h"
+
+static FLOAT_ELT *float_stack = NULL;
+
+void
+add_new_float (char *id, char *title, char *shorttitle,
+ char *type, char *position)
+{
+ FLOAT_ELT *new = xmalloc (sizeof (FLOAT_ELT));
+ unsigned long num_len;
+
+ new->id = id;
+ new->type = type;
+ new->title = title;
+ new->shorttitle = shorttitle;
+ new->position = position;
+ new->title_used = 0;
+ new->defining_line = line_number - 1;
+
+ new->number = current_chapter_number ();
+ /* Append dot if not @unnumbered. */
+ num_len = strlen (new->number);
+ if (num_len > 0)
+ {
+ new->number = xrealloc (new->number, num_len + 1 + 1);
+ new->number[num_len] = '.';
+ new->number[num_len+1] = '\0';
+ }
+
+ { /* Append the current float number. */
+ unsigned len = strlen (new->number) + 21; /* that's 64 bits */
+ char *s = xmalloc (len + 1);
+
+ sprintf (s, "%s%d", new->number,
+ count_floats_of_type_in_chapter (text_expansion (type),
+ new->number) + 1);
+ free (new->number);
+ new->number = xstrdup (s);
+ }
+
+ /* Plain text output needs sectioning number and its title,
+ when listing floats. */
+ if (!html && !xml && no_headers)
+ {
+ new->section = current_sectioning_number ();
+ if (strlen (new->section) == 0)
+ new->section_name = current_sectioning_name ();
+ else
+ new->section_name = "";
+ }
+
+ new->next = float_stack;
+ float_stack = new;
+}
+
+int
+count_floats_of_type_in_chapter (char *type, char *chapter)
+{
+ int i = 0;
+ int l = strlen (chapter);
+ FLOAT_ELT *temp = float_stack;
+
+ while (temp && strncmp (temp->number, chapter, l) == 0)
+ {
+ if (strlen (temp->id) > 0 && STREQ (text_expansion (temp->type), type))
+ i++;
+ temp = temp->next;
+ }
+
+ return i;
+}
+
+char *
+current_float_title (void)
+{
+ return float_stack->title;
+}
+
+char *
+current_float_shorttitle (void)
+{
+ return float_stack->shorttitle;
+}
+
+char *
+current_float_type (void)
+{
+ return float_stack->type;
+}
+
+char *
+current_float_position (void)
+{
+ return float_stack->position;
+}
+
+char *
+current_float_number (void)
+{
+ return float_stack->number;
+}
+
+char *
+current_float_id (void)
+{
+ return float_stack->id;
+}
+
+char *
+get_float_ref (char *id)
+{
+ FLOAT_ELT *temp = float_stack;
+
+ while (temp)
+ {
+ if (STREQ (id, temp->id))
+ {
+ char *s = xmalloc (strlen (temp->type) + strlen (temp->number) + 2);
+ sprintf (s, "%s %s", temp->type, temp->number);
+ return s;
+ }
+ temp = temp->next;
+ }
+
+ return NULL;
+}
+
+static int
+float_type_exists (char *check_type)
+{
+ /* Check if the requested float_type exists in the floats stack. */
+ FLOAT_ELT *temp;
+
+ for (temp = float_stack; temp; temp = temp->next)
+ if (STREQ (temp->type, check_type) && temp->id && *temp->id)
+ return 1;
+
+ return 0;
+}
+
+void
+cm_listoffloats (void)
+{
+ char *float_type;
+ get_rest_of_line (1, &float_type);
+
+ /* get_rest_of_line increments the line number by one,
+ so to make warnings/errors point to the correct line,
+ we decrement the line_number again. */
+ if (!handling_delayed_writes)
+ line_number--;
+
+ if (handling_delayed_writes && !float_type_exists (float_type))
+ warning (_("Requested float type `%s' not previously used"), float_type);
+
+ if (xml)
+ {
+ xml_insert_element_with_attribute (LISTOFFLOATS, START,
+ "type=\"%s\"", text_expansion (float_type));
+ xml_insert_element (LISTOFFLOATS, END);
+ }
+ else if (!handling_delayed_writes)
+ {
+ int command_len = sizeof ("@ ") + strlen (command) + strlen (float_type);
+ char *list_command = xmalloc (command_len + 1);
+
+ /* These are for the text following @listoffloats command.
+ Handling them with delayed writes is too late. */
+ close_paragraph ();
+ cm_noindent ();
+
+ sprintf (list_command, "@%s %s", command, float_type);
+ register_delayed_write (list_command);
+ free (list_command);
+ }
+ else if (float_type_exists (float_type))
+ {
+ FLOAT_ELT *temp = (FLOAT_ELT *) reverse_list
+ ((GENERIC_LIST *) float_stack);
+ FLOAT_ELT *new_start = temp;
+
+ if (html)
+ insert_string ("<ul class=\"listoffloats\">\n");
+ else
+ {
+ if (!no_headers)
+ insert_string ("* Menu:\n\n");
+ }
+
+ while (temp)
+ {
+ if (strlen (temp->id) > 0 && STREQ (float_type, temp->type))
+ {
+ char *caption;
+ if (strlen (temp->shorttitle) > 0)
+ caption = expansion (temp->shorttitle, 0);
+ else if (strlen (temp->title) > 0)
+ caption = expansion (temp->title, 0);
+ else
+ caption = "";
+
+ if (html)
+ {
+ /* A bit of space for HTML reabality. */
+ insert_string (" ");
+ add_html_block_elt ("<li>");
+
+ /* Simply relying on @ref command doesn't work here, because
+ commas in the caption may confuse the argument parsing. */
+ add_word ("<a href=\"");
+ add_anchor_name (temp->id, 1);
+ add_word ("\">");
+
+ if (strlen (float_type) > 0)
+ execute_string ("%s", float_type);
+
+ if (strlen (temp->id) > 0)
+ {
+ if (strlen (float_type) > 0)
+ add_char (' ');
+
+ add_word (temp->number);
+ }
+
+ if (caption)
+ {
+ if (strlen (float_type) > 0 || strlen (temp->id) > 0)
+ insert_string (": ");
+ execute_string ("%s", caption);
+ }
+
+ add_word ("</a>");
+
+ add_html_block_elt ("</li>\n");
+ }
+ else
+ {
+ char *entry;
+ char *raw_entry;
+
+ int len;
+ int aux_chars_len; /* these are asterisk, colon, etc. */
+ int column_width; /* width of the first column in menus. */
+ int number_len; /* length of Figure X.Y: etc. */
+ int i = 0;
+
+ /* Chosen widths are to match what @printindex produces. */
+ if (no_headers)
+ {
+ column_width = 43;
+ /* We have only one auxiliary character, NULL. */
+ aux_chars_len = sizeof ("");
+ }
+ else
+ {
+ column_width = 37;
+ /* We'll be adding an asterisk, followed by a space
+ and then a colon after the title, to construct a
+ proper menu item. */
+ aux_chars_len = sizeof ("* :");
+ }
+
+ /* Allocate enough space for possible expansion later. */
+ raw_entry = (char *) xmalloc (strlen (float_type)
+ + strlen (temp->number) + strlen (caption)
+ + sizeof (": "));
+
+ sprintf (raw_entry, "%s %s", float_type, temp->number);
+
+ if (strlen (caption) > 0)
+ strcat (raw_entry, ": ");
+
+ number_len = strlen (raw_entry);
+
+ len = strlen (caption) + strlen (raw_entry);
+
+ strcat (raw_entry, caption);
+ len = strlen (raw_entry);
+
+ if (len + aux_chars_len > column_width)
+ { /* Shorten long titles by looking for a space before
+ column_width - strlen (" ..."). */
+ /* -1 is for NULL, which is already in aux_chars_len. */
+ aux_chars_len += sizeof ("...") - 1;
+ len = column_width - aux_chars_len;
+ while (raw_entry[len] != ' ' && len >= 0)
+ len--;
+
+ /* Advance to the whitespace. */
+ len++;
+
+ /* If we are at the end of, say, Figure X.Y:, but
+ we have a title, then this means title does not
+ contain any whitespaces. Or it may be that we
+ went as far as the beginning. Just print as much
+ as possible of the title. */
+ if (len == 0
+ || (len == number_len && strlen (caption) > 0))
+ len = column_width - sizeof ("...");
+
+ /* Break here. */
+ raw_entry[len] = 0;
+
+ entry = xmalloc (len + aux_chars_len);
+
+ if (!no_headers)
+ strcpy (entry, "* ");
+ else
+ entry[0] = 0;
+
+ strcat (entry, raw_entry);
+ strcat (entry, "...");
+
+ if (!no_headers)
+ strcat (entry, ":");
+ }
+ else
+ {
+ entry = xmalloc (len + aux_chars_len);
+
+ if (!no_headers)
+ strcpy (entry, "* ");
+ else
+ entry[0] = 0;
+
+ strcat (entry, raw_entry);
+
+ if (!no_headers)
+ strcat (entry, ":");
+ }
+
+ insert_string (entry);
+
+ i = strlen (entry);
+ /* We insert space chars until ``column_width + four spaces''
+ is reached, to make the layout the same with what we produce
+ for @printindex. This is of course not obligatory, though
+ easier on the eye. -1 is for NULL. */
+ while (i < column_width + sizeof (" ") - 1)
+ {
+ insert (' ');
+ i++;
+ }
+
+ if (no_headers)
+ {
+ if (strlen (temp->section) > 0)
+ { /* We got your number. */
+ insert_string ((char *) _("See "));
+ insert_string (temp->section);
+ }
+ else
+ { /* Sigh, @float in an @unnumbered. :-\ */
+ insert_string ("\n ");
+ insert_string ((char *) _("See "));
+ insert_string ("``");
+ insert_string (expansion (temp->section_name, 0));
+ insert_string ("''");
+ }
+ }
+ else
+ insert_string (temp->id);
+
+ insert_string (".\n");
+
+ free (entry);
+ }
+
+ if (strlen (caption) > 0)
+ free (caption);
+ }
+ temp = temp->next;
+ }
+
+ if (html)
+ {
+ inhibit_paragraph_indentation = 1;
+ insert_string ("</ul>\n\n");
+ }
+ else
+ insert ('\n');
+
+ /* Retain the original order of float stack. */
+ temp = new_start;
+ float_stack = (FLOAT_ELT *) reverse_list ((GENERIC_LIST *) temp);
+ }
+
+ free (float_type);
+ /* Re-increment the line number, because get_rest_of_line
+ left us looking at the next line after the command. */
+ line_number++;
+}
+
+int
+current_float_used_title (void)
+{
+ return float_stack->title_used;
+}
+
+void current_float_set_title_used (void)
+{
+ float_stack->title_used = 1;
+}
diff --git a/makeinfo/float.h b/makeinfo/float.h
new file mode 100644
index 0000000..d8a0753
--- /dev/null
+++ b/makeinfo/float.h
@@ -0,0 +1,55 @@
+/* float.h -- declarations for the float environment.
+ $Id: float.h,v 1.8 2007/07/01 21:20:32 karl Exp $
+
+ Copyright (C) 2003, 2004, 2007 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 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 <http://www.gnu.org/licenses/>.
+
+ Written by Alper Ersoy <dirt@gtk.org>. */
+
+#ifndef FLOAT_H
+#define FLOAT_H
+
+typedef struct float_elt
+{
+ struct float_elt *next;
+ char *id;
+ char *type;
+ char *title;
+ char *shorttitle;
+ char *position;
+ char *number;
+ char *section;
+ char *section_name;
+ short title_used;
+ int defining_line;
+} FLOAT_ELT;
+
+extern void add_new_float (char *id, char *title, char *shorttitle,
+ char *type, char *position);
+extern void current_float_set_title_used (void);
+
+/* Information retrieval about the current float env. */
+extern char *current_float_id (void);
+extern char *current_float_title (void);
+extern char *current_float_shorttitle (void);
+extern char *current_float_type (void);
+extern char *current_float_position (void);
+extern char *current_float_number (void);
+extern char *get_float_ref (char *id);
+
+extern int count_floats_of_type_in_chapter (char *type, char *chapter);
+extern int current_float_used_title (void);
+
+#endif /* not FLOAT_H */
diff --git a/makeinfo/footnote.c b/makeinfo/footnote.c
new file mode 100644
index 0000000..381ce58
--- /dev/null
+++ b/makeinfo/footnote.c
@@ -0,0 +1,390 @@
+/* footnote.c -- footnotes for Texinfo.
+ $Id: footnote.c,v 1.12 2007/12/03 01:38:43 karl Exp $
+
+ Copyright (C) 1998, 1999, 2002, 2007 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "footnote.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "node.h"
+#include "xml.h"
+#include "xref.h"
+
+/* Nonzero means that the footnote style for this document was set on
+ the command line, which overrides any other settings. */
+int footnote_style_preset = 0;
+
+/* The current footnote number in this node. Each time a new node is
+ started this is reset to 1. */
+int current_footnote_number = 1;
+
+/* Nonzero means we automatically number footnotes with no specified marker. */
+int number_footnotes = 1;
+
+/* Nonzero means we are currently outputting footnotes. */
+int already_outputting_pending_notes = 0;
+
+
+/* Footnotes can be handled in one of two ways:
+
+ separate_node:
+ Make them look like followed references, with the reference
+ destinations in a makeinfo manufactured node or,
+ end_node:
+ Make them appear at the bottom of the node that they originally
+ appeared in. */
+
+#define separate_node 0
+#define end_node 1
+
+int footnote_style = end_node;
+int first_footnote_this_node = 1;
+int footnote_count = 0;
+
+/* Set the footnote style based on the style identifier in STRING. */
+int
+set_footnote_style (char *string)
+{
+ if (mbscasecmp (string, "separate") == 0)
+ footnote_style = separate_node;
+ else if (mbscasecmp (string, "end") == 0)
+ footnote_style = end_node;
+ else
+ return -1;
+
+ return 0;
+}
+
+void
+cm_footnotestyle (void)
+{
+ char *arg;
+
+ get_rest_of_line (1, &arg);
+
+ /* If set on command line, do not change the footnote style. */
+ if (!footnote_style_preset && set_footnote_style (arg) != 0)
+ line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
+
+ free (arg);
+}
+
+typedef struct fn
+{
+ struct fn *next;
+ char *marker;
+ char *note;
+ int number;
+} FN;
+
+FN *pending_notes = NULL;
+
+/* A method for remembering footnotes. Note that this list gets output
+ at the end of the current node. */
+static void
+remember_note (char *marker, char *note)
+{
+ FN *temp = xmalloc (sizeof (FN));
+
+ temp->marker = xstrdup (marker);
+ temp->note = xstrdup (note);
+ temp->next = pending_notes;
+ temp->number = current_footnote_number;
+ pending_notes = temp;
+ footnote_count++;
+}
+
+/* How to get rid of existing footnotes. */
+static void
+free_pending_notes (void)
+{
+ FN *temp;
+
+ while ((temp = pending_notes))
+ {
+ free (temp->marker);
+ free (temp->note);
+ pending_notes = pending_notes->next;
+ free (temp);
+ }
+ first_footnote_this_node = 1;
+ footnote_count = 0;
+ current_footnote_number = 1; /* for html */
+}
+
+/* What to do when you see a @footnote construct. */
+
+ /* Handle a "footnote".
+ footnote *{this is a footnote}
+ where "*" is the (optional) marker character for this note. */
+void
+cm_footnote (void)
+{
+ char *marker;
+ char *note;
+
+ get_until ("{", &marker);
+ canon_white (marker);
+
+ if (macro_expansion_output_stream && !executing_string)
+ append_to_expansion_output (input_text_offset + 1); /* include the { */
+
+ /* Read the argument in braces. */
+ if (curchar () != '{')
+ {
+ line_error (_("`%c%s' needs an argument `{...}', not just `%s'"),
+ COMMAND_PREFIX, command, marker);
+ free (marker);
+ return;
+ }
+ else
+ {
+ int len;
+ int braces = 1;
+ int loc = ++input_text_offset;
+
+ while (braces)
+ {
+ if (loc == input_text_length)
+ {
+ line_error (_("No closing brace for footnote `%s'"), marker);
+ return;
+ }
+
+ if (input_text[loc] == '{')
+ braces++;
+ else if (input_text[loc] == '}')
+ braces--;
+ else if (input_text[loc] == '\n')
+ line_number++;
+
+ loc++;
+ }
+
+ len = (loc - input_text_offset) - 1;
+ note = xmalloc (len + 1);
+ memcpy (note, &input_text[input_text_offset], len);
+ note[len] = 0;
+ input_text_offset = loc;
+ }
+
+ /* Must write the macro-expanded argument to the macro expansion
+ output stream. This is like the case in index_add_arg. */
+ if (macro_expansion_output_stream && !executing_string)
+ {
+ /* Calling me_execute_string on a lone } provokes an error, since
+ as far as the reader knows there is no matching {. We wrote
+ the { above in the call to append_to_expansion_output. */
+ me_execute_string_keep_state (note, "}");
+ }
+
+ if (!current_node || !*current_node)
+ {
+ line_error (_("Footnote defined without parent node"));
+ free (marker);
+ free (note);
+ return;
+ }
+
+ /* output_pending_notes is non-reentrant (it uses a global data
+ structure pending_notes, which it frees before it returns), and
+ TeX doesn't grok footnotes inside footnotes anyway. Disallow
+ that. */
+ if (already_outputting_pending_notes)
+ {
+ line_error (_("Footnotes inside footnotes are not allowed"));
+ free (marker);
+ free (note);
+ return;
+ }
+
+ if (!*marker)
+ {
+ free (marker);
+
+ if (number_footnotes)
+ {
+ marker = xmalloc (10);
+ sprintf (marker, "%d", current_footnote_number);
+ }
+ else
+ marker = xstrdup ("*");
+ }
+
+ if (xml)
+ xml_insert_footnote (note);
+ else
+ {
+ remember_note (marker, note);
+
+ /* fixme: html: footnote processing needs work; we currently ignore
+ the style requested; we could clash with a node name of the form
+ `fn-<n>', though that's unlikely. */
+ if (html)
+ {
+ /* Hyperlink also serves as an anchor (mnemonic: fnd is footnote
+ definition.) */
+ add_html_elt ("<a rel=\"footnote\" href=");
+ add_word_args ("\"#fn-%d\" name=\"fnd-%d\"><sup>%s</sup></a>",
+ current_footnote_number, current_footnote_number,
+ marker);
+ }
+ else
+ /* Your method should at least insert MARKER. */
+ switch (footnote_style)
+ {
+ case separate_node:
+ add_word_args ("(%s)", marker);
+ execute_string (" (*note %s-Footnote-%d::)",
+ current_node, current_footnote_number);
+ if (first_footnote_this_node)
+ {
+ char *temp_string, *expanded_ref;
+
+ temp_string = xmalloc (strlen (current_node)
+ + strlen ("-Footnotes") + 1);
+
+ strcpy (temp_string, current_node);
+ strcat (temp_string, "-Footnotes");
+ expanded_ref = expansion (temp_string, 0);
+ remember_node_reference (expanded_ref, line_number,
+ followed_reference);
+ free (temp_string);
+ free (expanded_ref);
+ first_footnote_this_node = 0;
+ }
+ break;
+
+ case end_node:
+ add_word_args ("(%s)", marker);
+ break;
+
+ default:
+ break;
+ }
+ current_footnote_number++;
+ }
+ free (marker);
+ free (note);
+}
+
+/* Output the footnotes. We are at the end of the current node. */
+void
+output_pending_notes (void)
+{
+ FN *footnote = pending_notes;
+
+ if (!pending_notes)
+ return;
+
+ if (html)
+ {
+ add_html_block_elt ("<div class=\"footnote\">\n<hr>\n");
+ /* We add an anchor here so @printindex can refer to this point
+ (as the node name) for entries defined in footnotes. */
+ if (!splitting)
+ add_word ("<a name=\"texinfo-footnotes-in-document\"></a>");
+ add_word_args ("<h4>%s</h4>", (char *) _("Footnotes"));
+ }
+ else
+ switch (footnote_style)
+ {
+ case separate_node:
+ {
+ char *old_current_node = current_node;
+ char *old_command = xstrdup (command);
+
+ already_outputting_pending_notes++;
+ execute_string ("%cnode %s-Footnotes,,,%s\n",
+ COMMAND_PREFIX, current_node, current_node);
+ already_outputting_pending_notes--;
+ current_node = old_current_node;
+ free (command);
+ command = old_command;
+ }
+ break;
+
+ case end_node:
+ close_paragraph ();
+ in_fixed_width_font++;
+ /* This string should be translated according to the
+ @documentlanguage, not the current LANG. We can't do that
+ yet, so leave it in English. */
+ execute_string ("---------- Footnotes ----------\n\n");
+ in_fixed_width_font--;
+ break;
+ }
+
+ /* Handle the footnotes in reverse order. */
+ {
+ int save_in_fixed_width_font = in_fixed_width_font;
+ FN **array = xmalloc ((footnote_count + 1) * sizeof (FN *));
+ array[footnote_count] = NULL;
+
+ while (--footnote_count > -1)
+ {
+ array[footnote_count] = footnote;
+ footnote = footnote->next;
+ }
+
+ filling_enabled = 1;
+ indented_fill = 1;
+ in_fixed_width_font = 0;
+
+ while ((footnote = array[++footnote_count]))
+ {
+ if (html)
+ {
+ /* Make the text of every footnote begin a separate paragraph. */
+ add_html_block_elt ("<p class=\"footnote\"><small>");
+ /* Make footnote number a link to its definition. */
+ add_word_args ("[<a name=\"fn-%d\" href=\"#fnd-%d\">%d</a>]",
+ footnote->number, footnote->number, footnote->number);
+ add_word ("</small> ");
+ already_outputting_pending_notes++;
+ execute_string ("%s", footnote->note);
+ already_outputting_pending_notes--;
+ add_word ("</p>\n");
+ }
+ else
+ {
+ char *old_current_node = current_node;
+ char *old_command = xstrdup (command);
+
+ already_outputting_pending_notes++;
+ execute_string ("%canchor{%s-Footnote-%d}(%s) %s",
+ COMMAND_PREFIX, current_node, footnote->number,
+ footnote->marker, footnote->note);
+ already_outputting_pending_notes--;
+ current_node = old_current_node;
+ free (command);
+ command = old_command;
+ }
+
+ close_paragraph ();
+ }
+
+ if (html)
+ add_html_block_elt ("<hr></div>");
+ close_paragraph ();
+ free (array);
+
+ in_fixed_width_font = save_in_fixed_width_font;
+ }
+
+ free_pending_notes ();
+}
diff --git a/makeinfo/footnote.h b/makeinfo/footnote.h
new file mode 100644
index 0000000..8a812a6
--- /dev/null
+++ b/makeinfo/footnote.h
@@ -0,0 +1,36 @@
+/* footnote.h -- declarations for footnote.c.
+ $Id: footnote.h,v 1.5 2007/07/01 21:20:32 karl Exp $
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef FOOTNOTE_H
+#define FOOTNOTE_H
+
+extern int footnote_style_preset;
+extern int current_footnote_number;
+extern int number_footnotes;
+extern int already_outputting_pending_notes;
+
+/* The Texinfo @commands. */
+extern void cm_footnote (void);
+extern void cm_footnotestyle (void);
+
+extern int set_footnote_style (char *string); /* called for -s option */
+
+extern void output_pending_notes (void); /* called for output */
+
+#endif /* !FOOTNOTE_H */
diff --git a/makeinfo/html.c b/makeinfo/html.c
new file mode 100644
index 0000000..2d25d3d
--- /dev/null
+++ b/makeinfo/html.c
@@ -0,0 +1,870 @@
+/* html.c -- html-related utilities.
+ $Id: html.c,v 1.42 2008/05/19 18:26:47 karl Exp $
+
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "cmds.h"
+#include "files.h"
+#include "html.h"
+#include "lang.h"
+#include "makeinfo.h"
+#include "node.h"
+#include "sectioning.h"
+
+
+
+/* Filename to which to write list of index entries */
+char *internal_links_filename = NULL;
+FILE *internal_links_stream = NULL;
+
+/* Append CHAR to BUFFER, (re)allocating as necessary. We don't handle
+ null characters. */
+
+typedef struct
+{
+ unsigned size; /* allocated */
+ unsigned length; /* used */
+ char *buffer;
+} buffer_type;
+
+static buffer_type *
+init_buffer (void)
+{
+ buffer_type *buf = xmalloc (sizeof (buffer_type));
+ buf->length = 0;
+ buf->size = 0;
+ buf->buffer = NULL;
+
+ return buf;
+}
+
+static void
+append_char (buffer_type *buf, int c)
+{
+ buf->length++;
+ if (buf->length >= buf->size)
+ {
+ buf->size += 100;
+ buf->buffer = xrealloc (buf->buffer, buf->size);
+ }
+ buf->buffer[buf->length - 1] = c;
+ buf->buffer[buf->length] = 0;
+}
+
+/* Read the cascading style-sheet file FILENAME. Write out any @import
+ commands, which must come first, by the definition of css. If the
+ file contains any actual css code following the @imports, return it;
+ else return NULL. */
+static char *
+process_css_file (char *filename)
+{
+ int c;
+ int lastchar = 0;
+ FILE *f;
+ buffer_type *import_text = init_buffer ();
+ buffer_type *inline_text = init_buffer ();
+ unsigned lineno = 1;
+ enum { null_state, comment_state, import_state, inline_state } state
+ = null_state, prev_state;
+
+ prev_state = null_state;
+
+ /* read from stdin if `-' is the filename. */
+ f = STREQ (filename, "-") ? stdin : fopen (filename, "r");
+ if (!f)
+ {
+ error (_("%s: could not open --css-file: %s"), progname, filename);
+ return NULL;
+ }
+
+ /* Read the file. The @import statements must come at the beginning,
+ with only whitespace and comments allowed before any inline css code. */
+ while ((c = getc (f)) >= 0)
+ {
+ if (c == '\n')
+ lineno++;
+
+ switch (state)
+ {
+ case null_state: /* between things */
+ if (c == '@')
+ { /* Only @import and @charset should switch into
+ import_state, other @-commands, such as @media, should
+ put us into inline_state. I don't think any other css
+ @-commands start with `i' or `c', although of course
+ this will break when such a command is defined. */
+ int nextchar = getc (f);
+ if (nextchar == 'i' || nextchar == 'c')
+ {
+ append_char (import_text, c);
+ append_char (import_text, nextchar);
+ state = import_state;
+ }
+ else
+ {
+ ungetc (nextchar, f); /* wasn't an @import */
+ state = inline_state;
+ }
+ }
+ else if (c == '/')
+ { /* possible start of a comment */
+ int nextchar = getc (f);
+ if (nextchar == '*')
+ state = comment_state;
+ else
+ {
+ ungetc (nextchar, f); /* wasn't a comment */
+ state = inline_state;
+ }
+ }
+ else if (isspace (c))
+ ; /* skip whitespace; maybe should use c_isspace? */
+
+ else
+ /* not an @import, not a comment, not whitespace: we must
+ have started the inline text. */
+ state = inline_state;
+
+ if (state == inline_state)
+ append_char (inline_text, c);
+
+ if (state != null_state)
+ prev_state = null_state;
+ break;
+
+ case comment_state:
+ if (c == '/' && lastchar == '*')
+ state = prev_state; /* end of comment */
+ break; /* else ignore this comment char */
+
+ case import_state:
+ append_char (import_text, c); /* include this import char */
+ if (c == ';')
+ { /* done with @import */
+ append_char (import_text, '\n'); /* make the output nice */
+ state = null_state;
+ prev_state = import_state;
+ }
+ break;
+
+ case inline_state:
+ /* No harm in writing out comments, so don't bother parsing
+ them out, just append everything. */
+ append_char (inline_text, c);
+ break;
+ }
+
+ lastchar = c;
+ }
+
+ fclose (f); /* Even closing stdin should be ok, can't read it more
+ than once? */
+
+ /* Reached the end of the file. We should not be still in a comment. */
+ if (state == comment_state)
+ warning (_("%s:%d: --css-file ended in comment"), filename, lineno);
+
+ /* Write the @import text, if any. */
+ if (import_text->buffer)
+ {
+ add_word (import_text->buffer);
+ free (import_text->buffer);
+ free (import_text);
+ }
+
+ /* We're wasting the buffer struct memory, but so what. */
+ return inline_text->buffer;
+}
+
+HSTACK *htmlstack = NULL;
+
+/* See html.h. */
+int html_title_written = 0;
+
+void
+html_output_head (void)
+{
+ static const char *html_title = NULL;
+ char *encoding = current_document_encoding ();
+
+ /* The <title> should not have markup, so use text_expansion. */
+ if (!html_title)
+ html_title = escape_string (title ?
+ text_expansion (title) : (char *) gdt("Untitled"));
+
+ /* Make sure this is the very first string of the output document. */
+ output_paragraph_offset = 0;
+
+ add_html_block_elt_args ("<html lang=\"%s\">\n<head>\n",
+ language_table[language_code].abbrev);
+
+ /* When splitting, add current node's name to title if it's available
+ and not Top. */
+ if (splitting && current_node && !STREQ (current_node, "Top"))
+ add_word_args ("<title>%s - %s</title>\n",
+ escape_string (xstrdup (current_node)), html_title);
+ else
+ add_word_args ("<title>%s</title>\n", html_title);
+
+ add_word ("<meta http-equiv=\"Content-Type\" content=\"text/html");
+ if (encoding && *encoding)
+ add_word_args ("; charset=%s", encoding);
+
+ add_word ("\">\n");
+
+ if (!document_description)
+ document_description = html_title;
+
+ add_word_args ("<meta name=\"description\" content=\"%s\">\n",
+ document_description);
+ add_word_args ("<meta name=\"generator\" content=\"makeinfo %s\">\n",
+ VERSION);
+
+ /* Navigation bar links. */
+ if (!splitting)
+ add_word ("<link title=\"Top\" rel=\"top\" href=\"#Top\">\n");
+ else if (tag_table)
+ {
+ /* Always put a top link. */
+ add_word ("<link title=\"Top\" rel=\"start\" href=\"index.html#Top\">\n");
+
+ /* We already have a top link, avoid duplication. */
+ if (tag_table->up && !STREQ (tag_table->up, "Top"))
+ add_link (tag_table->up, "rel=\"up\"");
+
+ if (tag_table->prev)
+ add_link (tag_table->prev, "rel=\"prev\"");
+
+ if (tag_table->next)
+ add_link (tag_table->next, "rel=\"next\"");
+
+ /* fixxme: Look for a way to put links to various indices in the
+ document. Also possible candidates to be added here are First and
+ Last links. */
+ }
+ else
+ {
+ /* We are splitting, but we neither have a tag_table. So this must be
+ index.html. So put a link to Top. */
+ add_word ("<link title=\"Top\" rel=\"start\" href=\"#Top\">\n");
+ }
+
+ add_word ("<link href=\"http://www.gnu.org/software/texinfo/\" \
+rel=\"generator-home\" title=\"Texinfo Homepage\">\n");
+
+ if (copying_text)
+ { /* It is not ideal that we include the html markup here within
+ <head>, so we use text_expansion. */
+ insert_string ("<!--\n");
+ insert_string (text_expansion (copying_text));
+ insert_string ("-->\n");
+ }
+
+ /* Put the style definitions in a comment for the sake of browsers
+ that don't support <style>. */
+ add_word ("<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n");
+ add_word ("<style type=\"text/css\"><!--\n");
+
+ {
+ char *css_inline = NULL;
+
+ if (css_include)
+ /* This writes out any @import commands from the --css-file,
+ and returns any actual css code following the imports. */
+ css_inline = process_css_file (css_include);
+
+ /* This seems cleaner than adding <br>'s at the end of each line for
+ these "roman" displays. It's hardly the end of the world if the
+ browser doesn't do <style>s, in any case; they'll just come out in
+ typewriter. */
+#define CSS_FONT_INHERIT "font-family:inherit"
+ add_word_args (" pre.display { %s }\n", CSS_FONT_INHERIT);
+ add_word_args (" pre.format { %s }\n", CSS_FONT_INHERIT);
+
+ /* Alternatively, we could do <font size=-1> in insertion.c, but this
+ way makes it easier to override. */
+#define CSS_FONT_SMALLER "font-size:smaller"
+ add_word_args (" pre.smalldisplay { %s; %s }\n", CSS_FONT_INHERIT,
+ CSS_FONT_SMALLER);
+ add_word_args (" pre.smallformat { %s; %s }\n", CSS_FONT_INHERIT,
+ CSS_FONT_SMALLER);
+ add_word_args (" pre.smallexample { %s }\n", CSS_FONT_SMALLER);
+ add_word_args (" pre.smalllisp { %s }\n", CSS_FONT_SMALLER);
+
+ /* Since HTML doesn't have a sc element, we use span with a bit of
+ CSS spice instead. */
+#define CSS_FONT_SMALL_CAPS "font-variant:small-caps"
+ add_word_args (" span.sc { %s }\n", CSS_FONT_SMALL_CAPS);
+
+ /* Roman (default) font class, closest we can come. */
+#define CSS_FONT_ROMAN "font-family:serif; font-weight:normal;"
+ add_word_args (" span.roman { %s } \n", CSS_FONT_ROMAN);
+
+ /* Sans serif font class. */
+#define CSS_FONT_SANSSERIF "font-family:sans-serif; font-weight:normal;"
+ add_word_args (" span.sansserif { %s } \n", CSS_FONT_SANSSERIF);
+
+ /* Write out any css code from the user's --css-file. */
+ if (css_inline)
+ insert_string (css_inline);
+
+ add_word ("--></style>\n");
+ }
+ if (css_ref)
+ add_word_args ("<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">\n",
+ css_ref);
+
+ add_word ("</head>\n<body>\n");
+
+ if (title && !html_title_written && titlepage_cmd_present)
+ {
+ add_word_args ("<h1 class=\"settitle\">%s</h1>\n", html_title);
+ html_title_written = 1;
+ }
+
+ free (encoding);
+}
+
+/* Escape HTML special characters in the string if necessary,
+ returning a pointer to a possibly newly-allocated one. */
+char *
+escape_string (char *string)
+{
+ char *newstring;
+ int i = 0, newlen = 0;
+
+ do
+ {
+ /* Find how much to allocate. */
+ switch (string[i])
+ {
+ case '"':
+ newlen += 6; /* `&quot;' */
+ break;
+ case '&':
+ newlen += 5; /* `&amp;' */
+ break;
+ case '<':
+ case '>':
+ newlen += 4; /* `&lt;', `&gt;' */
+ break;
+ default:
+ newlen++;
+ }
+ }
+ while (string[i++]);
+
+ if (newlen == i) return string; /* Already OK. */
+
+ newstring = xmalloc (newlen);
+ i = 0;
+ do
+ {
+ switch (string[i])
+ {
+ case '"':
+ strcpy (newstring, "&quot;");
+ newstring += 6;
+ break;
+ case '&':
+ strcpy (newstring, "&amp;");
+ newstring += 5;
+ break;
+ case '<':
+ strcpy (newstring, "&lt;");
+ newstring += 4;
+ break;
+ case '>':
+ strcpy (newstring, "&gt;");
+ newstring += 4;
+ break;
+ default:
+ newstring[0] = string[i];
+ newstring++;
+ }
+ }
+ while (string[i++]);
+
+ return newstring - newlen;
+}
+
+/* Save current tag. */
+static void
+push_tag (char *tag, char *attribs)
+{
+ HSTACK *newstack = xmalloc (sizeof (HSTACK));
+
+ newstack->tag = tag;
+ newstack->attribs = xstrdup (attribs);
+ newstack->next = htmlstack;
+ htmlstack = newstack;
+}
+
+/* Get last tag. */
+static void
+pop_tag (void)
+{
+ HSTACK *tos = htmlstack;
+
+ if (!tos)
+ {
+ line_error (_("[unexpected] no html tag to pop"));
+ return;
+ }
+
+ free (htmlstack->attribs);
+
+ htmlstack = htmlstack->next;
+ free (tos);
+}
+
+/* Check if tag is an empty or a whitespace only element.
+ If so, remove it, keeping whitespace intact. */
+int
+rollback_empty_tag (char *tag)
+{
+ int check_position = output_paragraph_offset;
+ int taglen = strlen (tag);
+ int rollback_happened = 0;
+ char *contents = ""; /* FIXME (ptr to constant, later
+ assigned to malloc'd address).
+ */
+ char *contents_canon_white = "";
+
+ /* If output_paragraph is empty, we cannot rollback :-\ */
+ if (output_paragraph_offset <= 0)
+ return 0;
+
+ /* Find the end of the previous tag. */
+ while (check_position > 0 && output_paragraph[check_position-1] != '>')
+ check_position--;
+
+ /* Save stuff between tag's end to output_paragraph's end. */
+ if (check_position != output_paragraph_offset)
+ {
+ contents = xmalloc (output_paragraph_offset - check_position + 1);
+ memcpy (contents, output_paragraph + check_position,
+ output_paragraph_offset - check_position);
+
+ contents[output_paragraph_offset - check_position] = '\0';
+
+ contents_canon_white = xstrdup (contents);
+ canon_white (contents_canon_white);
+ }
+
+ /* Find the start of the previous tag. */
+ while (check_position > 0 && output_paragraph[check_position-1] != '<')
+ check_position--;
+
+ /* Check to see if this is the tag. */
+ if (strncmp ((char *) output_paragraph + check_position, tag, taglen) == 0
+ && (whitespace (output_paragraph[check_position + taglen])
+ || output_paragraph[check_position + taglen] == '>'))
+ {
+ if (!contents_canon_white || !*contents_canon_white)
+ {
+ /* Empty content after whitespace removal, so roll it back. */
+ output_paragraph_offset = check_position - 1;
+ rollback_happened = 1;
+
+ /* Original contents may not be empty (whitespace.) */
+ if (contents && *contents)
+ {
+ insert_string (contents);
+ free (contents);
+ }
+ }
+ }
+
+ return rollback_happened;
+}
+
+/* Open or close TAG according to START_OR_END. */
+void
+#if defined (VA_FPRINTF) && __STDC__
+insert_html_tag_with_attribute (int start_or_end, char *tag, char *format, ...)
+#else
+insert_html_tag_with_attribute (start_or_end, tag, format, va_alist)
+ int start_or_end;
+ char *tag;
+ char *format;
+ va_dcl
+#endif
+{
+ char *old_tag = NULL;
+ char *old_attribs = NULL;
+ char formatted_attribs[2000]; /* xx no fixed limits */
+ int do_return = 0;
+ extern int in_html_elt;
+
+ if (start_or_end != START)
+ pop_tag ();
+
+ if (htmlstack)
+ {
+ old_tag = htmlstack->tag;
+ old_attribs = htmlstack->attribs;
+ }
+
+ if (format)
+ {
+#ifdef VA_SPRINTF
+ va_list ap;
+#endif
+
+ VA_START (ap, format);
+#ifdef VA_SPRINTF
+ VA_SPRINTF (formatted_attribs, format, ap);
+#else
+ sprintf (formatted_attribs, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+ va_end (ap);
+ }
+ else
+ formatted_attribs[0] = '\0';
+
+ /* Exception: can nest multiple spans. */
+ if (htmlstack
+ && STREQ (htmlstack->tag, tag)
+ && !(STREQ (tag, "span") && STREQ (old_attribs, formatted_attribs)))
+ do_return = 1;
+
+ if (start_or_end == START)
+ push_tag (tag, formatted_attribs);
+
+ if (do_return)
+ return;
+
+ in_html_elt++;
+
+ /* texinfo.tex doesn't support more than one font attribute
+ at the same time. */
+ if ((start_or_end == START) && old_tag && *old_tag
+ && !STREQ (old_tag, "samp")
+ && !rollback_empty_tag (old_tag))
+ add_word_args ("</%s>", old_tag);
+
+ if (*tag)
+ {
+ if (start_or_end == START)
+ add_word_args (format ? "<%s %s>" : "<%s>", tag, formatted_attribs);
+ else if (STREQ (tag, "samp") || !rollback_empty_tag (tag))
+ /* Insert close tag only if we didn't rollback,
+ in which case the opening tag is removed. */
+ add_word_args ("</%s>", tag);
+ }
+
+ if ((start_or_end != START) && old_tag && *old_tag && !STREQ (old_tag, "samp"))
+ add_word_args (strlen (old_attribs) > 0 ? "<%s %s>" : "<%s>",
+ old_tag, old_attribs);
+
+ in_html_elt--;
+}
+
+void
+insert_html_tag (int start_or_end, char *tag)
+{
+ insert_html_tag_with_attribute (start_or_end, tag, NULL);
+}
+
+/* Output an HTML <link> to the filename for NODE, including the
+ other string as extra attributes. */
+
+void
+add_link (char *nodename, char *attributes)
+{
+ if (nodename)
+ {
+ char *escaped_nodename;
+ add_html_elt ("<link ");
+ add_word_args ("%s", attributes);
+ add_word_args (" href=\"");
+ add_anchor_name (nodename, 1);
+ escaped_nodename = escape_string (nodename);
+ add_word_args ("\" title=\"%s\">\n", escaped_nodename);
+ if (escaped_nodename != nodename)
+ free (escaped_nodename);
+ }
+}
+
+/* Copy a name with characters escaped as appropriate for an anchor
+ name, i.e., escape URL special characters with our _00hh convention.
+ (See the manual for details on the new scheme.) */
+
+char *
+escaped_anchor_name (const char *name)
+{
+ /* The factor 5 in the next allocation allows all chars to be expanded. */
+ char *res = xmalloc (5 * strlen (name) + 1);
+ char *d = res;
+
+ for (; *name; name++)
+ {
+ if (cr_or_whitespace (*name))
+ *d++ = '-';
+ else if (! URL_SAFE_CHAR (*name))
+ {
+ sprintf (d, "_00%x", (unsigned char) *name);
+ /* do this manually since sprintf returns char * on
+ SunOS 4 and other old systems. */
+ while (*d)
+ d++;
+ }
+ else
+ *d++ = *name;
+ }
+ *d = 0;
+ return res;
+}
+
+/* Output NAME with characters escaped as appropriate for an anchor
+ name, i.e., escape URL special characters with our _00hh convention
+ if OLD is zero. (See the manual for details on the new scheme.)
+
+ If OLD is nonzero, generate the node name with the 4.6-and-earlier
+ convention of %hh (and more special characters output as-is, notably
+ - and *). This is only so that external references to old names can
+ still work with HTML generated by the new makeinfo; the gcc folks
+ needed this. Our own HTML does not refer to these names. */
+
+void
+add_escaped_anchor_name (char *name, int old)
+{
+ canon_white (name);
+
+ if (!old && !strchr ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ *name))
+ { /* XHTML does not allow anything but an ASCII letter to start an
+ identifier. Therefore kludge in this constant string if we
+ have a nonletter. */
+ add_word ("g_t");
+ }
+ if (!old)
+ {
+ char *expanded = escaped_anchor_name (name);
+ add_word (expanded);
+ free (expanded);
+ }
+ else for (; *name; name++)
+ {
+ if (cr_or_whitespace (*name))
+ add_char ('-');
+
+ else if (!URL_SAFE_CHAR (*name) && !OLD_URL_SAFE_CHAR (*name))
+ /* Cast so characters with the high bit set are treated as >128,
+ for example o-umlaut should be 246, not -10. */
+ add_word_args ("%%%x", (unsigned char) *name);
+ else
+ add_char (*name);
+ }
+}
+
+/* Insert the text for the name of a reference in an HTML anchor
+ appropriate for NODENAME.
+
+ If HREF is zero, generate text for name= in the new node name
+ conversion convention.
+ If HREF is negative, generate text for name= in the old convention.
+ If HREF is positive, generate the name for an href= attribute, i.e.,
+ including the `#' if it's an internal reference. */
+void
+add_anchor_name (char *nodename, int href)
+{
+ if (href > 0)
+ {
+ if (splitting)
+ add_url_name (nodename, href);
+ add_char ('#');
+ }
+ /* Always add NODENAME, so that the reference would pinpoint the
+ exact node on its file. This is so several nodes could share the
+ same file, in case of file-name clashes, but also for more
+ accurate browser positioning. */
+ if (mbscasecmp (nodename, "(dir)") == 0)
+ /* Strip the parens, but keep the original letter-case. */
+ add_word_args ("%.3s", nodename + 1);
+ else if (mbscasecmp (nodename, "top") == 0)
+ add_word ("Top");
+ else
+ add_escaped_anchor_name (nodename, href < 0);
+}
+
+/* Insert the text for the name of a reference in an HTML url, aprropriate
+ for NODENAME */
+void
+add_url_name (char *nodename, int href)
+{
+ add_nodename_to_filename (nodename, href);
+}
+
+/* Convert non [A-Za-z0-9] characters depending on the command line options given.
+ If --transliterate-file-names is specified, these are replaced with their ASCII
+ phonetic transliteration. Otherwise, _00xx notation is used, where xx means the
+ hexadecimal representation of the ASCII character. Also convert spaces and
+ newlines to dashes. */
+static void
+fix_filename (char *filename)
+{
+ int i;
+ int len = strlen (filename);
+ char *oldname = xstrdup (filename);
+
+ *filename = '\0';
+
+ for (i = 0; i < len; i++)
+ {
+ const char *p = lang_transliterate_char (oldname[i]);
+
+ if (p)
+ strcat (filename, p);
+ else if (cr_or_whitespace (oldname[i]))
+ strcat (filename, "-");
+ else if (URL_SAFE_CHAR (oldname[i]))
+ strncat (filename, (char *) oldname + i, 1);
+ else
+ {
+ char *hexchar = xmalloc (6 * sizeof (char));
+ sprintf (hexchar, "_00%x", (unsigned char) oldname[i]);
+ strcat (filename, hexchar);
+ free (hexchar);
+ }
+
+ /* Check if we are nearing boundaries. */
+ if (strlen (filename) >= PATH_MAX - 20)
+ break;
+ }
+
+ free (oldname);
+}
+
+/* As we can't look-up a (forward-referenced) nodes' html filename
+ from the tentry, we take the easy way out. We assume that
+ nodenames are unique, and generate the html filename from the
+ nodename, that's always known. */
+static char *
+nodename_to_filename_1 (char *nodename, int href)
+{
+ char *p;
+ char *filename;
+ char dirname[PATH_MAX];
+
+ if (mbscasecmp (nodename, "Top") == 0)
+ {
+ /* We want to convert references to the Top node into
+ "index.html#Top". */
+ if (href)
+ filename = xstrdup ("index.html"); /* "#Top" is added by our callers */
+ else
+ filename = xstrdup ("Top");
+ }
+ else if (mbscasecmp (nodename, "(dir)") == 0)
+ /* We want to convert references to the (dir) node into
+ "../index.html". */
+ filename = xstrdup ("../index.html");
+ else
+ {
+ filename = xmalloc (PATH_MAX);
+ dirname[0] = '\0';
+ *filename = '\0';
+
+ /* Check for external reference: ``(info-document)node-name''
+ Assume this node lives at: ``../info-document/node-name.html''
+
+ We need to handle the special case (sigh): ``(info-document)'',
+ ie, an external top-node, which should translate to:
+ ``../info-document/info-document.html'' */
+
+ p = nodename;
+ if (*nodename == '(')
+ {
+ int length;
+
+ p = strchr (nodename, ')');
+ if (p == NULL)
+ {
+ line_error (_("[unexpected] invalid node name: `%s'"), nodename);
+ xexit (1);
+ }
+
+ length = p - nodename - 1;
+ if (length > 5 &&
+ FILENAME_CMPN (p - 5, ".info", 5) == 0)
+ length -= 5;
+ /* This is for DOS, and also for Windows and GNU/Linux
+ systems that might have Info files copied from a DOS 8+3
+ filesystem. */
+ if (length > 4 &&
+ FILENAME_CMPN (p - 4, ".inf", 4) == 0)
+ length -= 4;
+ strcpy (filename, "../");
+ strncpy (dirname, nodename + 1, length);
+ *(dirname + length) = '\0';
+ fix_filename (dirname);
+ strcat (filename, dirname);
+ strcat (filename, "/");
+ p++;
+ }
+
+ /* In the case of just (info-document), there will be nothing
+ remaining, and we will refer to ../info-document/, which will
+ work fine. */
+ strcat (filename, p);
+ if (*p)
+ {
+ /* Hmm */
+ fix_filename (filename + strlen (filename) - strlen (p));
+ strcat (filename, ".html");
+ }
+ }
+
+ /* Produce a file name suitable for the underlying filesystem. */
+ normalize_filename (filename);
+
+#if 0
+ /* We add ``#Nodified-filename'' anchor to external references to be
+ prepared for non-split HTML support. Maybe drop this. */
+ if (href && *dirname)
+ {
+ strcat (filename, "#");
+ strcat (filename, p);
+ /* Hmm, again */
+ fix_filename (filename + strlen (filename) - strlen (p));
+ }
+#endif
+
+ return filename;
+}
+
+/* If necessary, ie, if current filename != filename of node, output
+ the node name. */
+void
+add_nodename_to_filename (char *nodename, int href)
+{
+ /* for now, don't check: always output filename */
+ char *filename = nodename_to_filename_1 (nodename, href);
+ add_word (filename);
+ free (filename);
+}
+
+char *
+nodename_to_filename (char *nodename)
+{
+ /* The callers of nodename_to_filename use the result to produce
+ <a href=, so call nodename_to_filename_1 with last arg non-zero. */
+ return nodename_to_filename_1 (nodename, 1);
+}
diff --git a/makeinfo/html.h b/makeinfo/html.h
new file mode 100644
index 0000000..814be9e
--- /dev/null
+++ b/makeinfo/html.h
@@ -0,0 +1,67 @@
+/* html.h -- declarations for html-related utilities.
+ $Id: html.h,v 1.11 2008/05/19 18:26:48 karl Exp $
+
+ Copyright (C) 1999, 2000, 2002, 2004, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef HTML_H
+#define HTML_H
+
+/* A stack of font tags. */
+typedef struct hstack
+{
+ struct hstack *next;
+ char *tag;
+ char *attribs;
+} HSTACK;
+
+/* Nonzero if we have output a title, from @titlefont or @settitle. */
+extern int html_title_written;
+
+/* Filename to which to write list of index entries, and stream for them */
+extern char *internal_links_filename;
+extern FILE *internal_links_stream;
+
+/* Perform the <head> output. */
+extern void html_output_head (void);
+
+/* Escape &<>. */
+extern char *escape_string (char *);
+
+/* Open or close TAG according to START_OR_END. */
+extern void insert_html_tag (int start_or_end, char *tag);
+
+/* Output HTML <link> to NODE, plus extra ATTRIBUTES. */
+extern void add_link (char *nodename, char *attributes);
+
+/* Escape URL-special characters. */
+extern char *escaped_anchor_name (const char *name);
+extern void add_escaped_anchor_name (char *name, int old);
+
+/* See html.c. */
+extern void add_anchor_name (char *nodename, int href);
+extern void add_url_name (char *nodename, int href);
+extern void add_nodename_to_filename (char *nodename, int href);
+extern char *nodename_to_filename (char *nodename);
+extern int rollback_empty_tag (char *tag);
+
+#if defined (VA_FPRINTF) && __STDC__
+extern void insert_html_tag_with_attribute (int start_or_end, char *tag, char *format, ...);
+#else
+extern void insert_html_tag_with_attribute ();
+#endif
+
+#endif /* !HTML_H */
diff --git a/makeinfo/index.c b/makeinfo/index.c
new file mode 100644
index 0000000..65d7484
--- /dev/null
+++ b/makeinfo/index.c
@@ -0,0 +1,1004 @@
+/* index.c -- indexing for Texinfo.
+ $Id: index.c,v 1.27 2008/05/19 18:26:48 karl Exp $
+
+ Copyright (C) 1998, 1999, 2002, 2003, 2004, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "mbswidth.h"
+#include "files.h"
+#include "footnote.h"
+#include "html.h"
+#include "index.h"
+#include "lang.h"
+#include "macro.h"
+#include "sectioning.h"
+#include "toc.h"
+#include "xml.h"
+
+/* Nonzero means that we are in the middle of printing an index. */
+int printing_index = 0;
+
+/* The number of defined indices. */
+int defined_indices = 0;
+
+/* This is the order of the index. */
+int index_counter = 0;
+
+/* Stuff for defining commands on the fly. */
+COMMAND **user_command_array = NULL;
+int user_command_array_len = 0;
+
+INDEX_ALIST **name_index_alist = NULL;
+
+/* An array of pointers. Each one is for a different index. The @synindex
+ command changes which array slot is pointed to by a given index. */
+static INDEX_ELT **the_indices = NULL;
+
+/* How to compare index entries for sorting. May be set to strcoll. */
+static int (*index_compare_fn) (const char *a, const char *b) = mbscasecmp;
+
+/* Find which element in the known list of indices has this name.
+ Returns -1 if NAME isn't found. */
+static int
+find_index_offset (char *name)
+{
+ int i;
+ for (i = 0; i < defined_indices; i++)
+ if (name_index_alist[i] && STREQ (name, name_index_alist[i]->name))
+ return i;
+ return -1;
+}
+
+/* Return a pointer to the entry of (name . index) for this name.
+ Return NULL if the index doesn't exist. */
+static INDEX_ALIST *
+find_index (char *name)
+{
+ int offset = find_index_offset (name);
+ if (offset > -1)
+ return name_index_alist[offset];
+ else
+ return NULL;
+}
+
+/* User-defined commands, which happens only from user-defined indexes.
+ Used to initialize the builtin indices, too. */
+static void
+define_user_command (char *name, COMMAND_FUNCTION (*proc), int needs_braces_p)
+{
+ int slot = user_command_array_len;
+ user_command_array_len++;
+
+ if (!user_command_array)
+ user_command_array = xmalloc (1 * sizeof (COMMAND *));
+
+ user_command_array = xrealloc (user_command_array,
+ (1 + user_command_array_len) * sizeof (COMMAND *));
+
+ user_command_array[slot] = xmalloc (sizeof (COMMAND));
+ user_command_array[slot]->name = xstrdup (name);
+ user_command_array[slot]->proc = proc;
+ user_command_array[slot]->argument_in_braces = needs_braces_p;
+}
+
+/* Please release me, let me go... */
+static void
+free_index (INDEX_ELT *index)
+{
+ INDEX_ELT *temp;
+
+ while ((temp = index))
+ {
+ free (temp->entry);
+ free (temp->entry_text);
+ /* Do not free the node, because we already freed the tag table,
+ which freed all the node names. */
+ /* free (temp->node); */
+ index = index->next;
+ free (temp);
+ }
+}
+
+/* Flush an index by name. This will delete the list of entries that
+ would be written by a @printindex command for this index. */
+static void
+undefindex (char *name)
+{
+ int i;
+ int which = find_index_offset (name);
+
+ /* The index might have already been freed if this was the target of
+ an @synindex. */
+ if (which < 0 || !name_index_alist[which])
+ return;
+
+ i = name_index_alist[which]->read_index;
+
+ free_index (the_indices[i]);
+ the_indices[i] = NULL;
+
+ free (name_index_alist[which]->name);
+ free (name_index_alist[which]);
+ name_index_alist[which] = NULL;
+}
+
+/* Add the arguments to the current index command to the index NAME. */
+static void
+index_add_arg (char *name)
+{
+ int which;
+ char *index_entry;
+ INDEX_ALIST *tem;
+
+ tem = find_index (name);
+
+ which = tem ? tem->write_index : -1;
+
+ if (macro_expansion_output_stream && !executing_string)
+ append_to_expansion_output (input_text_offset + 1);
+
+ get_rest_of_line (0, &index_entry);
+ ignore_blank_line ();
+
+ if (macro_expansion_output_stream && !executing_string)
+ {
+ char *index_line = xmalloc (strlen (index_entry) + 2);
+ sprintf (index_line, "%s\n", index_entry);
+ me_execute_string_keep_state (index_line, NULL);
+ free (index_line);
+ }
+
+ if (which < 0)
+ {
+ line_error (_("Unknown index `%s'"), name);
+ free (index_entry);
+ }
+ else
+ {
+ INDEX_ELT *new = xmalloc (sizeof (INDEX_ELT));
+
+ index_counter++;
+
+ /* Get output line number updated before doing anything. */
+ if (!html && !xml)
+ flush_output ();
+
+ new->next = the_indices[which];
+ new->entry = NULL;
+ new->entry_text = index_entry;
+ /* Since footnotes are handled at the very end of the document,
+ node name in the non-split HTML outputs always show the last
+ node. We artificially make it ``Footnotes''. */
+ if (html && !splitting && already_outputting_pending_notes)
+ new->node = xstrdup (_("Footnotes"));
+ else
+ new->node = current_node ? current_node : xstrdup ("");
+ if (!html && !xml && no_headers)
+ {
+ new->section = current_sectioning_number ();
+ if (strlen (new->section) == 0)
+ new->section_name = current_sectioning_name ();
+ else
+ new->section_name = "";
+ }
+ else
+ {
+ new->section = NULL;
+ new->section_name = NULL;
+ }
+ new->code = tem->code;
+ new->defining_line = line_number - 1;
+ new->output_line = no_headers ? output_line_number : node_line_number;
+ /* We need to make a copy since input_filename may point to
+ something that goes away, for example, inside a macro.
+ (see the findexerr test). */
+ new->defining_file = xstrdup (input_filename);
+
+ if (html && splitting)
+ {
+ if (current_output_filename && *current_output_filename)
+ new->output_file = filename_part (current_output_filename);
+ else
+ new->output_file = xstrdup ("");
+ }
+ else
+ new->output_file = NULL;
+
+ new->entry_number = index_counter;
+ the_indices[which] = new;
+
+#if 0
+ /* The index breaks if there are colons in the entry.
+ -- This is true, but it's too painful to force changing index
+ entries to use `colon', and too confusing for users. The real
+ fix is to change Info support to support arbitrary characters
+ in node names, and we're not ready to do that. --karl,
+ 19mar02. */
+ if (strchr (new->entry_text, ':'))
+ warning (_("Info cannot handle `:' in index entry `%s'"),
+ new->entry_text);
+#endif
+
+ if (html)
+ {
+ /* Anchor. */
+ int removed_empty_elt = 0;
+
+ /* We must put the anchor outside the <dl> and <ul> blocks. */
+ if (rollback_empty_tag ("dl"))
+ removed_empty_elt = 1;
+ else if (rollback_empty_tag ("ul"))
+ removed_empty_elt = 2;
+
+ add_word ("<a name=\"index-");
+ add_escaped_anchor_name (index_entry, 0);
+ add_word_args ("-%d\"></a>", index_counter);
+
+ if (removed_empty_elt == 1)
+ add_html_block_elt_args ("\n<dl>");
+ else if (removed_empty_elt == 2)
+ add_html_block_elt_args ("\n<ul>");
+ }
+ }
+
+ if (xml)
+ xml_insert_indexterm (index_entry, name);
+}
+
+/* The function which user defined index commands call. */
+static void
+gen_index (void)
+{
+ char *name = xstrdup (command);
+ if (strlen (name) >= strlen ("index"))
+ name[strlen (name) - strlen ("index")] = 0;
+ index_add_arg (name);
+ free (name);
+}
+
+/* Define an index known as NAME. We assign the slot number.
+ If CODE is nonzero, make this a code index. */
+static void
+defindex (char *name, int code)
+{
+ int i, slot;
+
+ /* If it already exists, flush it. */
+ undefindex (name);
+
+ /* Try to find an empty slot. */
+ slot = -1;
+ for (i = 0; i < defined_indices; i++)
+ if (!name_index_alist[i])
+ {
+ slot = i;
+ break;
+ }
+
+ if (slot < 0)
+ { /* No such luck. Make space for another index. */
+ slot = defined_indices;
+ defined_indices++;
+
+ name_index_alist = (INDEX_ALIST **)
+ xrealloc (name_index_alist, (1 + defined_indices)
+ * sizeof (INDEX_ALIST *));
+ the_indices = (INDEX_ELT **)
+ xrealloc (the_indices, (1 + defined_indices) * sizeof (INDEX_ELT *));
+ }
+
+ /* We have a slot. Start assigning. */
+ name_index_alist[slot] = xmalloc (sizeof (INDEX_ALIST));
+ name_index_alist[slot]->name = xstrdup (name);
+ name_index_alist[slot]->read_index = slot;
+ name_index_alist[slot]->write_index = slot;
+ name_index_alist[slot]->code = code;
+
+ the_indices[slot] = NULL;
+}
+
+/* Define an index NAME, implicitly @code if CODE is nonzero. */
+static void
+top_defindex (char *name, int code)
+{
+ char *temp;
+
+ temp = xmalloc (1 + strlen (name) + strlen ("index"));
+ sprintf (temp, "%sindex", name);
+ define_user_command (temp, gen_index, 0);
+ defindex (name, code);
+ free (temp);
+}
+
+/* Set up predefined indices. */
+void
+init_indices (void)
+{
+ int i;
+
+ /* Create the default data structures. */
+
+ /* Initialize data space. */
+ if (!the_indices)
+ {
+ the_indices = xmalloc ((1 + defined_indices) * sizeof (INDEX_ELT *));
+ the_indices[defined_indices] = NULL;
+
+ name_index_alist = xmalloc ((1 + defined_indices)
+ * sizeof (INDEX_ALIST *));
+ name_index_alist[defined_indices] = NULL;
+ }
+
+ /* If there were existing indices, get rid of them now. */
+ for (i = 0; i < defined_indices; i++)
+ {
+ if (name_index_alist[i])
+ { /* Suppose we're called with two input files, and the first
+ does a @synindex pg cp. Then, when we get here to start
+ the second file, the "pg" element won't get freed by
+ undefindex (because it's pointing to "cp"). So free it
+ here; otherwise, when we try to define the pg index again
+ just below, it will still point to cp. */
+ undefindex (name_index_alist[i]->name);
+
+ /* undefindex sets all this to null in some cases. */
+ if (name_index_alist[i])
+ {
+ free (name_index_alist[i]->name);
+ free (name_index_alist[i]);
+ name_index_alist[i] = NULL;
+ }
+ }
+ }
+
+ /* Add the default indices. */
+ top_defindex ("cp", 0); /* cp is the only non-code index. */
+ top_defindex ("fn", 1);
+ top_defindex ("ky", 1);
+ top_defindex ("pg", 1);
+ top_defindex ("tp", 1);
+ top_defindex ("vr", 1);
+}
+
+/* Given an index name, return the offset in the_indices of this index,
+ or -1 if there is no such index. */
+static int
+translate_index (char *name)
+{
+ INDEX_ALIST *which = find_index (name);
+
+ if (which)
+ return which->read_index;
+ else
+ return -1;
+}
+
+/* Return the index list which belongs to NAME. */
+INDEX_ELT *
+index_list (char *name)
+{
+ int which = translate_index (name);
+ if (which < 0)
+ return (INDEX_ELT *) -1;
+ else
+ return the_indices[which];
+}
+
+/* Define a new index command. Arg is name of index. */
+static void
+gen_defindex (int code)
+{
+ char *name;
+ get_rest_of_line (0, &name);
+
+ if (find_index (name))
+ {
+ line_error (_("Index `%s' already exists"), name);
+ }
+ else
+ {
+ char *temp = xmalloc (strlen (name) + sizeof ("index"));
+ sprintf (temp, "%sindex", name);
+ define_user_command (temp, gen_index, 0);
+ defindex (name, code);
+ free (temp);
+ }
+
+ free (name);
+}
+
+void
+cm_defindex (void)
+{
+ gen_defindex (0);
+}
+
+void
+cm_defcodeindex (void)
+{
+ gen_defindex (1);
+}
+
+/* Expects 2 args, on the same line. Both are index abbreviations.
+ Make the first one be a synonym for the second one, i.e. make the
+ first one have the same index as the second one. */
+void
+cm_synindex (void)
+{
+ int source, target;
+ char *abbrev1, *abbrev2;
+
+ skip_whitespace ();
+ get_until_in_line (0, " ", &abbrev1);
+ target = find_index_offset (abbrev1);
+ skip_whitespace ();
+ get_until_in_line (0, " ", &abbrev2);
+ source = find_index_offset (abbrev2);
+ if (source < 0 || target < 0)
+ {
+ line_error (_("Unknown index `%s' and/or `%s' in @synindex"),
+ abbrev1, abbrev2);
+ }
+ else
+ {
+ if (xml && !docbook)
+ xml_synindex (abbrev1, abbrev2);
+ else
+ name_index_alist[target]->write_index
+ = name_index_alist[source]->write_index;
+ }
+
+ free (abbrev1);
+ free (abbrev2);
+}
+
+void
+cm_pindex (void) /* Pinhead index. */
+{
+ index_add_arg ("pg");
+}
+
+void
+cm_vindex (void) /* Variable index. */
+{
+ index_add_arg ("vr");
+}
+
+void
+cm_kindex (void) /* Key index. */
+{
+ index_add_arg ("ky");
+}
+
+void
+cm_cindex (void) /* Concept index. */
+{
+ index_add_arg ("cp");
+}
+
+void
+cm_findex (void) /* Function index. */
+{
+ index_add_arg ("fn");
+}
+
+void
+cm_tindex (void) /* Data Type index. */
+{
+ index_add_arg ("tp");
+}
+
+static int
+index_element_compare (const void *element1, const void *element2)
+{
+ INDEX_ELT **elt1 = (INDEX_ELT **) element1;
+ INDEX_ELT **elt2 = (INDEX_ELT **) element2;
+
+ return index_compare_fn ((*elt1)->entry, (*elt2)->entry);
+}
+
+/* Force all index entries to be unique. */
+static void
+make_index_entries_unique (INDEX_ELT **array, int count)
+{
+ int i, j;
+ INDEX_ELT **copy;
+ int counter = 1;
+
+ copy = xmalloc ((1 + count) * sizeof (INDEX_ELT *));
+
+ for (i = 0, j = 0; i < count; i++)
+ {
+ if (i == (count - 1)
+ || array[i]->node != array[i + 1]->node
+ || !STREQ (array[i]->entry, array[i + 1]->entry))
+ copy[j++] = array[i];
+ else
+ {
+ free (array[i]->entry);
+ free (array[i]->entry_text);
+ free (array[i]);
+ }
+ }
+ copy[j] = NULL;
+
+ /* Now COPY contains only unique entries. Duplicated entries in the
+ original array have been freed. Replace the current array with
+ the copy, fixing the NEXT pointers. */
+ for (i = 0; copy[i]; i++)
+ {
+ copy[i]->next = copy[i + 1];
+
+ /* Fix entry names which are the same. They point to different nodes,
+ so we make the entry name unique. */
+ if (copy[i+1]
+ && STREQ (copy[i]->entry, copy[i + 1]->entry)
+ && !html)
+ {
+ char *new_entry_name;
+
+ new_entry_name = xmalloc (10 + strlen (copy[i]->entry));
+ sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
+ free (copy[i]->entry);
+ copy[i]->entry = new_entry_name;
+ counter++;
+ }
+ else
+ counter = 1;
+
+ array[i] = copy[i];
+ }
+ array[i] = NULL;
+
+ /* Free the storage used only by COPY. */
+ free (copy);
+}
+
+
+/* Sort the index passed in INDEX, returning an array of pointers to
+ elements. The array is terminated with a NULL pointer. */
+
+static INDEX_ELT **
+sort_index (INDEX_ELT *index)
+{
+ INDEX_ELT **array;
+ INDEX_ELT *temp;
+ int count = 0;
+ int save_line_number = line_number;
+ char *save_input_filename = input_filename;
+ int save_html = html;
+
+ /* Pretend we are in non-HTML mode, for the purpose of getting the
+ expanded index entry that lacks any markup and other HTML escape
+ characters which could produce a wrong sort order. */
+ /* fixme: html: this still causes some markup, such as non-ASCII
+ characters @AE{} etc., to sort incorrectly. */
+ html = 0;
+
+ for (temp = index, count = 0; temp; temp = temp->next, count++)
+ ;
+ /* We have the length, now we can allocate an array. */
+ array = xmalloc ((count + 1) * sizeof (INDEX_ELT *));
+
+ for (temp = index, count = 0; temp; temp = temp->next, count++)
+ {
+ /* Allocate new memory for the return array, since parts of the
+ original INDEX get freed. Otherwise, if the document calls
+ @printindex twice on the same index, with duplicate entries,
+ we'll have garbage the second time. There are cleaner ways to
+ deal, but this will suffice for now. */
+ array[count] = xmalloc (sizeof (INDEX_ELT));
+ *(array[count]) = *(temp); /* struct assignment, hope it's ok */
+
+ /* Adjust next pointers to use the new memory. */
+ if (count > 0)
+ array[count-1]->next = array[count];
+
+ /* Set line number and input filename to the source line for this
+ index entry, as this expansion finds any errors. */
+ line_number = array[count]->defining_line;
+ input_filename = array[count]->defining_file;
+
+ /* If this particular entry should be printed as a "code" index,
+ then expand it as @code{entry}, i.e., as in fixed-width font. */
+ array[count]->entry = expansion (temp->entry_text, array[count]->code);
+ }
+ array[count] = NULL; /* terminate the array. */
+
+ line_number = save_line_number;
+ input_filename = save_input_filename;
+ html = save_html;
+
+#ifdef HAVE_STRCOLL
+ /* This is not perfect. We should set (then restore) the locale to the
+ documentlanguage, so strcoll operates according to the document's
+ locale, not the user's. For now, I'm just going to assume that
+ those few new documents which use @documentlanguage will be
+ processed in the appropriate locale. In any case, don't use
+ strcoll in the C (aka POSIX) locale, that is the ASCII ordering. */
+ if (language_code != en)
+ {
+ char *lang_env = getenv ("LANG");
+ if (lang_env && !STREQ (lang_env, "C") && !STREQ (lang_env, "POSIX"))
+ index_compare_fn = strcoll;
+ }
+#endif /* HAVE_STRCOLL */
+
+ /* Sort the array. */
+ qsort (array, count, sizeof (INDEX_ELT *), index_element_compare);
+
+ /* Remove duplicate entries. */
+ make_index_entries_unique (array, count);
+
+ /* Replace the original index with the sorted one, in case the
+ document wants to print it again. If the index wasn't empty. */
+ if (index)
+ *index = **array;
+
+ return array;
+}
+
+/* Return the number of times that the byte CH occurs in the LEN bytes
+ starting at STR. Multibyte strings are not taken into account, which
+ is incorrect, but we need this for @tie; see more comments below. */
+
+static int
+count_strn_chars (const char *str, int len, int ch)
+{
+ int count = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (str[i] == ch)
+ count++;
+
+ return count;
+}
+
+static void
+insert_index_output_line_no (int line_number, int output_line_number_len)
+{
+ int last_column, out_line_no_width;
+ int str_size = output_line_number_len + strlen (_("(line )"))
+ + sizeof (NULL);
+ char *out_line_no_str = (char *) xmalloc (str_size + 1);
+
+ /* Do not translate ``(line NNN)'' below for !no_headers case (Info output),
+ because it's something like the ``* Menu'' strings. For plaintext output
+ it should be translated though. */
+ sprintf (out_line_no_str,
+ no_headers ? _("(line %*d)") : "(line %*d)",
+ output_line_number_len, line_number);
+
+ {
+ int i = output_paragraph_offset;
+ while (0 < i && output_paragraph[i-1] != '\n')
+ i--;
+ last_column = mbsnwidth ((char *)(output_paragraph + i),
+ output_paragraph_offset - i, 0);
+ last_column += count_strn_chars (output_paragraph + i,
+ output_paragraph_offset - i, NON_BREAKING_SPACE);
+ }
+
+ out_line_no_width = mbswidth (out_line_no_str, 0);
+ if (last_column + out_line_no_width > fill_column)
+ {
+ insert ('\n');
+ last_column = 0;
+ }
+
+ while (last_column + out_line_no_width < fill_column)
+ {
+ insert (' ');
+ last_column++;
+ }
+
+ insert_string (out_line_no_str);
+ insert ('\n');
+
+ free (out_line_no_str);
+}
+
+/* Takes one arg, a short name of an index to print.
+ Outputs a menu of the sorted elements of the index. */
+void
+cm_printindex (void)
+{
+ char *index_name;
+ get_rest_of_line (0, &index_name);
+
+ /* get_rest_of_line increments the line number by one,
+ so to make warnings/errors point to the correct line,
+ we decrement the line_number again. */
+ if (!handling_delayed_writes)
+ line_number--;
+
+ if (xml)
+ {
+ xml_insert_element (PRINTINDEX, START);
+ if (! docbook)
+ insert_string (index_name);
+ xml_insert_element (PRINTINDEX, END);
+ }
+ else if (!handling_delayed_writes)
+ {
+ int command_len = sizeof ("@ ") + strlen (command) + strlen (index_name);
+ char *index_command = xmalloc (command_len + 1);
+
+ close_paragraph ();
+ sprintf (index_command, "@%s %s", command, index_name);
+ register_delayed_write (index_command);
+ free (index_command);
+ }
+ else
+ {
+ int item;
+ INDEX_ELT *index;
+ INDEX_ELT *last_index = 0;
+ INDEX_ELT **array;
+ unsigned line_length;
+ char *line;
+ int saved_inhibit_paragraph_indentation = inhibit_paragraph_indentation;
+ int saved_filling_enabled = filling_enabled;
+ int saved_line_number = line_number;
+ char *saved_input_filename = input_filename;
+ unsigned output_line_number_len;
+ FILE *saved_output_stream = output_stream;
+
+ index = index_list (index_name);
+ if (index == (INDEX_ELT *)-1)
+ {
+ line_error (_("Unknown index `%s' in @printindex"), index_name);
+ free (index_name);
+ return;
+ }
+
+ /* Do this before sorting, so execute_string is in the good environment */
+ if (xml && docbook)
+ xml_begin_index ();
+
+ /* Do this before sorting, so execute_string in index_element_compare
+ will give the same results as when we actually print. */
+ printing_index = 1;
+ filling_enabled = 0;
+ inhibit_paragraph_indentation = 1;
+ xml_sort_index = 1;
+ array = sort_index (index);
+ xml_sort_index = 0;
+ close_paragraph ();
+ if (html)
+ add_html_block_elt_args ("<ul class=\"index-%s\" compact>",
+ index_name);
+ else if (!no_headers && !docbook)
+ { /* Info. Add magic cookie for info readers (to treat this
+ menu differently), and the usual start-of-menu. */
+ add_char ('\0');
+ add_word ("\010[index");
+ add_char ('\0');
+ add_word ("\010]\n");
+ add_word ("* Menu:\n\n");
+ }
+
+ me_inhibit_expansion++;
+
+ /* This will probably be enough. */
+ line_length = 100;
+ line = xmalloc (line_length);
+
+ {
+ char *max_output_line_number = (char *) xmalloc (25 * sizeof (char));
+
+ if (no_headers)
+ sprintf (max_output_line_number, "%d", output_line_number);
+ else
+ {
+ INDEX_ELT *tmp_entry = index;
+ unsigned tmp = 0;
+ for (tmp_entry = index; tmp_entry; tmp_entry = tmp_entry->next)
+ tmp = tmp_entry->output_line > tmp ? tmp_entry->output_line : tmp;
+ sprintf (max_output_line_number, "%d", tmp);
+ }
+
+ output_line_number_len = strlen (max_output_line_number);
+ free (max_output_line_number);
+ }
+
+ for (item = 0; (index = array[item]); item++)
+ {
+ /* A pathological document might have an index entry outside of any
+ node. Don't crash; try using the section name instead. */
+ char *index_node = index->node;
+
+ line_number = index->defining_line;
+ input_filename = index->defining_file;
+
+ if ((!index_node || !*index_node) && html)
+ index_node = toc_find_section_of_node (index_node);
+
+ if (!index_node || !*index_node)
+ {
+ line_error (_("Entry for index `%s' outside of any node"),
+ index_name);
+ if (html || !no_headers)
+ index_node = (char *) _("(outside of any node)");
+ }
+
+ if (html)
+ {
+ /* For HTML, we need to expand and HTML-escape the
+ original entry text, at the same time. Consider
+ @cindex J@"urgen. We want J&uuml;urgen. We can't
+ expand and then escape since we'll end up with
+ J&amp;uuml;rgen. We can't escape and then expand
+ because then `expansion' will see J@&quot;urgen, and
+ @&quot;urgen is not a command. */
+ char *html_entry =
+ maybe_escaped_expansion (index->entry_text, index->code, 1);
+
+ add_html_block_elt_args ("\n<li><a href=\"%s#index-",
+ (splitting && index->output_file) ? index->output_file : "");
+ add_escaped_anchor_name (index->entry_text, 0);
+ add_word_args ("-%d\">%s</a>: ", index->entry_number,
+ html_entry);
+ free (html_entry);
+
+ add_word ("<a href=\"");
+ if (index->node && *index->node)
+ {
+ /* Ensure any non-macros in the node name are expanded. */
+ char *expanded_index;
+
+ in_fixed_width_font++;
+ expanded_index = expansion (index_node, 0);
+ in_fixed_width_font--;
+ add_anchor_name (expanded_index, 1);
+ expanded_index = escape_string (expanded_index);
+ add_word_args ("\">%s</a>", expanded_index);
+ free (expanded_index);
+ }
+ else if (STREQ (index_node, _("(outside of any node)")))
+ {
+ add_anchor_name (index_node, 1);
+ add_word_args ("\">%s</a>", index_node);
+ }
+ else
+ /* If we use the section instead of the (missing) node, then
+ index_node already includes all we need except the #. */
+ add_word_args ("#%s</a>", index_node);
+
+ add_html_block_elt ("</li>");
+
+ if (internal_links_stream)
+ {
+ char *escaped = escaped_anchor_name (index->entry_text);
+ fprintf (internal_links_stream, "%s#index-%s-%d\t%s\t%s\n",
+ (splitting && index->output_file) ? index->output_file : "",
+ escaped, index->entry_number, index_name,
+ index->entry_text);
+ free (escaped);
+ }
+ }
+ else if (xml && docbook)
+ {
+ /* Let DocBook processor generate the index. */
+ }
+ else
+ {
+#define MIN_ENTRY_COLUMNS 37
+ /* Make sure there is enough space even if index->entry has zero
+ width. */
+ unsigned new_length = strlen (index->entry) + MIN_ENTRY_COLUMNS;
+
+ if (new_length < 50) /* minimum length used below */
+ new_length = 50;
+ new_length += strlen (index_node) + 7; /* * : .\n\0 */
+
+ if (new_length > line_length)
+ {
+ line_length = new_length;
+ line = xrealloc (line, line_length);
+ }
+ /* Print the entry, nicely formatted. We've already
+ expanded any commands in index->entry, including any
+ implicit @code. Thus, can't call execute_string, since
+ @@ has turned into @. */
+ if (!no_headers)
+ {
+ int nspaces;
+ int width = mbswidth (index->entry, 0);
+
+ /* Unfortunately, our @tie{} / @w{ } magic is an
+ unprintable character, and so mbswidth doesn't
+ count it. If that byte value occurs in a multibyte
+ string, we'd lose, but at least it's only a
+ question of minor formatting, not functionality. */
+ width += count_strn_chars (index->entry,
+ strlen (index->entry), NON_BREAKING_SPACE);
+
+ nspaces = -(strlen (index->entry)
+ + (MIN_ENTRY_COLUMNS - width));
+ sprintf (line, "* %*s ",
+ width < MIN_ENTRY_COLUMNS ? nspaces : 0,
+ index->entry);
+ line[2 + strlen (index->entry)] = ':';
+ insert_string (line);
+ /* Expand any non-macros in the node name. */
+ in_fixed_width_font++;
+ execute_string ("%s. ", index_node);
+ insert_index_output_line_no (index->output_line,
+ output_line_number_len);
+ in_fixed_width_font--;
+ }
+ else
+ {
+ /* With --no-headers, the @node lines are gone, so
+ there's little sense in referring to them in the
+ index. Instead, output the number or name of the
+ section that corresponds to that node. */
+ sprintf (line, "%-*s ", number_sections ? 46 : 1,
+ index->entry);
+ line[strlen (index->entry)] = ':';
+ insert_string (line);
+
+ if (strlen (index->section) > 0)
+ { /* We got your number. */
+ insert_string ((char *) _("See "));
+ insert_string (index->section);
+ }
+ else
+ { /* Sigh, index in an @unnumbered. :-\ */
+ insert_string ("\n ");
+ insert_string ((char *) _("See "));
+ insert_string ("``");
+ insert_string (expansion (index->section_name, 0));
+ insert_string ("''");
+ }
+
+ insert_string (". ");
+ insert_index_output_line_no (index->output_line,
+ output_line_number_len);
+ }
+ }
+
+ /* Prevent `output_paragraph' from growing to the size of the
+ whole index. */
+ flush_output ();
+ last_index = index;
+ }
+
+ free (line);
+
+ me_inhibit_expansion--;
+ printing_index = 0;
+
+ close_single_paragraph ();
+ filling_enabled = saved_filling_enabled;
+ inhibit_paragraph_indentation = saved_inhibit_paragraph_indentation;
+ input_filename = saved_input_filename;
+ line_number = saved_line_number;
+
+ if (html)
+ add_html_block_elt ("</ul>");
+ else if (xml && docbook)
+ xml_end_index ();
+ }
+
+ free (index_name);
+ /* Re-increment the line number, because get_rest_of_line
+ left us looking at the next line after the command. */
+ line_number++;
+}
diff --git a/makeinfo/index.h b/makeinfo/index.h
new file mode 100644
index 0000000..1e78107
--- /dev/null
+++ b/makeinfo/index.h
@@ -0,0 +1,92 @@
+/* index.h -- declarations for index.c.
+ $Id: index.h,v 1.6 2007/09/26 20:53:40 karl Exp $
+
+ Copyright (C) 1998, 1999, 2007 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef INDEX_H
+#define INDEX_H
+
+#include "makeinfo.h"
+#include "cmds.h"
+
+extern int printing_index;
+extern int defined_indices;
+extern int index_counter;
+
+/* User commands are only new indices. (Macros are handled separately.) */
+extern COMMAND **user_command_array;
+extern int user_command_array_len;
+
+/* An index element... */
+typedef struct index_elt
+{
+ struct index_elt *next;
+ char *entry; /* The index entry itself, after expansion. */
+ char *entry_text; /* The original, non-expanded entry text. */
+ char *node; /* The node from whence it came. */
+ char *section; /* Current section number we are in, */
+ char *section_name; /* ... and its title. */
+ int code; /* Nonzero means add `@code{...}' when
+ printing this element. */
+ int defining_line; /* Line number where this entry was written. */
+ int output_line; /* And line number where it is in the output. */
+ char *defining_file; /* Source file for defining_line. */
+ char *output_file; /* Output file for output_line. */
+ int entry_number; /* Entry number. */
+} INDEX_ELT;
+
+
+/* A list of short-names for each index.
+ There are two indices into the the_indices array.
+ * read_index is the index that points to the list of index
+ entries that we will find if we ask for the list of entries for
+ this name.
+ * write_index is the index that points to the list of index entries
+ that we will add new entries to.
+
+ Initially, read_index and write_index are the same, but the
+ @syncodeindex and @synindex commands can change the list we add
+ entries to.
+
+ For example, after the commands
+ @cindex foo
+ @defindex ii
+ @synindex cp ii
+ @cindex bar
+
+ the cp index will contain the entry `foo', and the new ii
+ index will contain the entry `bar'. This is consistent with the
+ way texinfo.tex handles the same situation.
+
+ In addition, for each index, it is remembered whether that index is
+ a code index or not. Code indices have @code{} inserted around the
+ first word when they are printed with printindex. */
+typedef struct
+{
+ char *name;
+ int read_index; /* index entries for `name' */
+ int write_index; /* store index entries here, @synindex can change it */
+ int code;
+} INDEX_ALIST;
+
+extern INDEX_ALIST **name_index_alist;
+
+/* Initialize all indices. */
+extern void init_indices (void);
+
+INDEX_ELT *index_list (char *name);
+
+#endif /* !INDEX_H */
diff --git a/makeinfo/insertion.c b/makeinfo/insertion.c
new file mode 100644
index 0000000..9928115
--- /dev/null
+++ b/makeinfo/insertion.c
@@ -0,0 +1,2403 @@
+/* insertion.c -- insertions for Texinfo.
+ $Id: insertion.c,v 1.71 2008/04/09 17:31:10 karl Exp $
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ 2007, 2008 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "cmds.h"
+#include "defun.h"
+#include "float.h"
+#include "html.h"
+#include "insertion.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "multi.h"
+#include "xml.h"
+
+/* Must match list in insertion.h. */
+static char *insertion_type_names[] =
+{
+ "cartouche", "copying", "defcv", "deffn", "defivar", "defmac",
+ "defmethod", "defop", "defopt", "defspec", "deftp", "deftypecv",
+ "deftypefn", "deftypefun", "deftypeivar", "deftypemethod",
+ "deftypeop", "deftypevar", "deftypevr", "defun", "defvar", "defvr",
+ "detailmenu", "direntry", "display", "documentdescription",
+ "enumerate", "example", "float", "flushleft", "flushright", "format",
+ "ftable", "group", "ifclear", "ifdocbook", "ifhtml", "ifinfo",
+ "ifnotdocbook", "ifnothtml", "ifnotinfo", "ifnotplaintext", "ifnottex",
+ "ifnotxml", "ifplaintext", "ifset", "iftex", "ifxml", "itemize", "lisp",
+ "menu", "multitable", "quotation", "rawdocbook", "rawhtml", "rawtex",
+ "rawxml", "smalldisplay", "smallexample", "smallformat", "smalllisp",
+ "verbatim", "table", "tex", "vtable", "titlepage", "bad_type"
+};
+
+/* All nested environments. */
+INSERTION_ELT *insertion_stack = NULL;
+
+/* How deeply we're nested. */
+int insertion_level = 0;
+
+/* Set to 1 if we've processed (commentary) text in a @menu that
+ wasn't part of a menu item. */
+int had_menu_commentary;
+
+/* How to examine menu lines. */
+int in_detailmenu = 0;
+
+/* Whether to examine menu lines. */
+int in_menu = 0;
+
+/* Set to 1 if <p> is written in normal context.
+ Used for menu and itemize. */
+int in_paragraph = 0;
+
+/* Since an insertion is already in the stack before we reach the switch
+ statement, we cannot use is_in_insertion_of_type (always returns true.) Also
+ making it return the level found, and comparing it with the current level is
+ no use, due to the order of stack. */
+static int float_active = 0;
+
+/* Unsetting escape_html blindly causes text inside @html/etc. to be escaped if
+ used within a rmacro. */
+static int raw_output_block = 0;
+
+/* Non-zero if a <dl> element has a <dt> element in it. We use this when
+ deciding whether to insert a <br> or not. */
+static int html_deflist_has_term = 0;
+
+const char default_item_function[] = { "@bullet" };
+
+void
+init_insertion_stack (void)
+{
+ insertion_stack = NULL;
+}
+
+/* Return the type of the current insertion. */
+static enum insertion_type
+current_insertion_type (void)
+{
+ return insertion_level ? insertion_stack->insertion : bad_type;
+}
+
+/* Return the string which is the function to wrap around items, or NULL
+ if we're not in an environment where @item is ok. */
+static char *
+current_item_function (void)
+{
+ int done = 0;
+ INSERTION_ELT *elt = insertion_stack;
+
+ /* Skip down through the stack until we find an insertion with an
+ itemize function defined, i.e., skip conditionals, @cartouche, etc. */
+ while (!done && elt)
+ {
+ switch (elt->insertion)
+ {
+ /* This list should match the one in cm_item. */
+ case ifclear:
+ case ifhtml:
+ case ifinfo:
+ case ifnothtml:
+ case ifnotinfo:
+ case ifnotplaintext:
+ case ifnottex:
+ case ifnotxml:
+ case ifplaintext:
+ case ifset:
+ case iftex:
+ case ifxml:
+ case rawdocbook:
+ case rawhtml:
+ case rawxml:
+ case rawtex:
+ case tex:
+ case cartouche:
+ elt = elt->next;
+ break;
+
+ default:
+ done = 1;
+ }
+ }
+
+ /* item_function usually gets assigned the empty string. */
+ return done && (*elt->item_function) ? elt->item_function : NULL;
+}
+
+/* Parse the item marker function off the input. If result is just "@",
+ change it to "@ ", since "@" by itself is not a command. This makes
+ "@ ", "@\t", and "@\n" all the same, but their default meanings are
+ the same anyway, and let's not worry about supporting redefining them. */
+char *
+get_item_function (void)
+{
+ char *item_function;
+ char *item_loc;
+
+ get_rest_of_line (0, &item_function);
+
+ /* If the document erroneously says
+ @itemize @bullet @item foobar
+ it's nicer to give an error up front than repeat `@bullet expected
+ braces' until we get a segmentation fault. */
+ item_loc = strstr (item_function, "@item");
+ if (item_loc)
+ {
+ line_error (_("@item not allowed in argument to @itemize"));
+ *item_loc = 0;
+ }
+
+ /* If we hit the end of text in get_rest_of_line, backing up
+ input pointer will cause the last character of the last line
+ be pushed back onto the input, which is wrong. */
+ if (input_text_offset < input_text_length)
+ backup_input_pointer ();
+
+ if (STREQ (item_function, "@"))
+ {
+ free (item_function);
+ item_function = xstrdup ("@ ");
+ }
+
+ return item_function;
+}
+
+ /* Push the state of the current insertion on the stack. */
+static void
+push_insertion (enum insertion_type type, char *item_function)
+{
+ INSERTION_ELT *new = xmalloc (sizeof (INSERTION_ELT));
+
+ new->item_function = item_function;
+ new->filling_enabled = filling_enabled;
+ new->indented_fill = indented_fill;
+ new->insertion = type;
+ new->line_number = line_number;
+ new->filename = xstrdup (input_filename);
+ new->inhibited = inhibit_paragraph_indentation;
+ new->in_fixed_width_font = in_fixed_width_font;
+ new->next = insertion_stack;
+ insertion_stack = new;
+ insertion_level++;
+}
+
+ /* Pop the value on top of the insertion stack into the
+ global variables. */
+void
+pop_insertion (void)
+{
+ INSERTION_ELT *temp = insertion_stack;
+
+ if (temp == NULL)
+ return;
+
+ in_fixed_width_font = temp->in_fixed_width_font;
+ inhibit_paragraph_indentation = temp->inhibited;
+ filling_enabled = temp->filling_enabled;
+ indented_fill = temp->indented_fill;
+ if (temp->item_function == default_item_function)
+ temp->item_function = NULL;
+ else
+ free_and_clear (&(temp->item_function));
+ free_and_clear (&(temp->filename));
+ insertion_stack = insertion_stack->next;
+ free (temp);
+ insertion_level--;
+}
+
+ /* Return a pointer to the print name of this
+ enumerated type. */
+static const char *
+insertion_type_pname (enum insertion_type type)
+{
+ if ((int) type < (int) bad_type)
+ {
+ if (type == rawdocbook)
+ return "docbook";
+ else if (type == rawhtml)
+ return "html";
+ else if (type == rawxml)
+ return "xml";
+ else if (type == rawtex)
+ return "tex";
+ else
+ return insertion_type_names[(int) type];
+ }
+ else
+ return _("Broken-Type in insertion_type_pname");
+}
+
+/* Return the insertion_type associated with NAME.
+ If the type is not one of the known ones, return BAD_TYPE. */
+enum insertion_type
+find_type_from_name (char *name)
+{
+ int index = 0;
+ while (index < (int) bad_type)
+ {
+ if (STREQ (name, insertion_type_names[index]))
+ return (enum insertion_type) index;
+ if (index == rawdocbook && STREQ (name, "docbook"))
+ return rawdocbook;
+ if (index == rawhtml && STREQ (name, "html"))
+ return rawhtml;
+ if (index == rawxml && STREQ (name, "xml"))
+ return rawxml;
+ if (index == rawtex && STREQ (name, "tex"))
+ return rawtex;
+ index++;
+ }
+ return bad_type;
+}
+
+/* Simple function to query insertion_stack to see if we are inside a given
+ insertion type. */
+int
+is_in_insertion_of_type (int type)
+{
+ INSERTION_ELT *temp = insertion_stack;
+
+ if (!insertion_level)
+ return 0;
+
+ while (temp)
+ {
+ if (temp->insertion == type)
+ return 1;
+ temp = temp->next;
+ }
+
+ return 0;
+}
+
+
+static int
+defun_insertion (enum insertion_type type)
+{
+ return 0
+ || (type == defcv)
+ || (type == deffn)
+ || (type == defivar)
+ || (type == defmac)
+ || (type == defmethod)
+ || (type == defop)
+ || (type == defopt)
+ || (type == defspec)
+ || (type == deftp)
+ || (type == deftypecv)
+ || (type == deftypefn)
+ || (type == deftypefun)
+ || (type == deftypeivar)
+ || (type == deftypemethod)
+ || (type == deftypeop)
+ || (type == deftypevar)
+ || (type == deftypevr)
+ || (type == defun)
+ || (type == defvar)
+ || (type == defvr)
+ ;
+}
+
+/* MAX_NS is the maximum nesting level for enumerations. I picked 100
+ which seemed reasonable. This doesn't control the number of items,
+ just the number of nested lists. */
+#define max_stack_depth 100
+#define ENUM_DIGITS 1
+#define ENUM_ALPHA 2
+typedef struct {
+ int enumtype;
+ int enumval;
+} DIGIT_ALPHA;
+
+DIGIT_ALPHA enumstack[max_stack_depth];
+int enumstack_offset = 0;
+int current_enumval = 1;
+int current_enumtype = ENUM_DIGITS;
+char *enumeration_arg = NULL;
+
+static void
+start_enumerating (int at, int type)
+{
+ if ((enumstack_offset + 1) == max_stack_depth)
+ {
+ line_error (_("Enumeration stack overflow"));
+ return;
+ }
+ enumstack[enumstack_offset].enumtype = current_enumtype;
+ enumstack[enumstack_offset].enumval = current_enumval;
+ enumstack_offset++;
+ current_enumval = at;
+ current_enumtype = type;
+}
+
+static void
+stop_enumerating (void)
+{
+ --enumstack_offset;
+ if (enumstack_offset < 0)
+ enumstack_offset = 0;
+
+ current_enumval = enumstack[enumstack_offset].enumval;
+ current_enumtype = enumstack[enumstack_offset].enumtype;
+}
+
+/* Place a letter or digits into the output stream. */
+static void
+enumerate_item (void)
+{
+ char temp[10];
+
+ if (current_enumtype == ENUM_ALPHA)
+ {
+ if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
+ {
+ current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
+ warning (_("lettering overflow, restarting at %c"), current_enumval);
+ }
+ sprintf (temp, "%c. ", current_enumval);
+ }
+ else
+ sprintf (temp, "%d. ", current_enumval);
+
+ indent (current_output_column () + (current_indent - strlen (temp)));
+ add_word (temp);
+ current_enumval++;
+}
+
+static void
+enum_html (void)
+{
+ char type;
+ int start;
+
+ if (isdigit (*enumeration_arg))
+ {
+ type = '1';
+ start = atoi (enumeration_arg);
+ }
+ else if (isupper (*enumeration_arg))
+ {
+ type = 'A';
+ start = *enumeration_arg - 'A' + 1;
+ }
+ else
+ {
+ type = 'a';
+ start = *enumeration_arg - 'a' + 1;
+ }
+
+ add_html_block_elt_args ("<ol type=%c start=%d>\n", type, start);
+}
+
+/* Conditionally parse based on the current command name. */
+void
+command_name_condition (void)
+{
+ char *discarder = xmalloc (8 + strlen (command));
+
+ sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command);
+ discard_until (discarder);
+ discard_until ("\n");
+
+ free (discarder);
+}
+
+/* This is where the work for all the "insertion" style
+ commands is done. A huge switch statement handles the
+ various setups, and generic code is on both sides. */
+void
+begin_insertion (enum insertion_type type)
+{
+ int no_discard = 0;
+
+ if (defun_insertion (type))
+ {
+ push_insertion (type, xstrdup (""));
+ no_discard++;
+ }
+ else
+ {
+ push_insertion (type, get_item_function ());
+ }
+
+ switch (type)
+ {
+ case menu:
+ if (!no_headers)
+ close_paragraph ();
+
+ filling_enabled = no_indent = 0;
+ inhibit_paragraph_indentation = 1;
+
+ if (html)
+ {
+ had_menu_commentary = 1;
+ }
+ else if (!no_headers && !xml)
+ add_word ("* Menu:\n");
+
+ if (xml)
+ xml_insert_element (MENU, START);
+ else
+ in_fixed_width_font++;
+
+ next_menu_item_number = 1;
+ in_menu++;
+ no_discard++;
+ break;
+
+ case detailmenu:
+ if (!in_menu)
+ {
+ if (!no_headers)
+ close_paragraph ();
+
+ filling_enabled = no_indent = 0;
+ inhibit_paragraph_indentation = 1;
+
+ no_discard++;
+ }
+
+ if (xml)
+ {
+ xml_insert_element (DETAILMENU, START);
+ skip_whitespace_and_newlines ();
+ }
+ else
+ in_fixed_width_font++;
+
+ in_detailmenu++;
+ break;
+
+ case direntry:
+ close_single_paragraph ();
+ filling_enabled = no_indent = 0;
+ inhibit_paragraph_indentation = 1;
+ add_word ("START-INFO-DIR-ENTRY\n");
+
+ /* The zsh manual, maybe others, wrongly indents the * line of the
+ direntry in the source. Ignore that whitespace. */
+ skip_whitespace_and_newlines ();
+ no_discard++;
+ break;
+
+ case documentdescription:
+ {
+ char *desc;
+ int start_of_end;
+ int save_fixed_width;
+
+ discard_until ("\n"); /* ignore the @documentdescription line */
+ start_of_end = get_until ("\n@end documentdescription", &desc);
+ save_fixed_width = in_fixed_width_font;
+
+ in_fixed_width_font = 0;
+ document_description = expansion (desc, 0);
+ free (desc);
+
+ in_fixed_width_font = save_fixed_width;
+ input_text_offset = start_of_end; /* go back to the @end to match */
+ }
+ break;
+
+ case copying:
+ /* Save the copying text away for @insertcopying,
+ typically used on the back of the @titlepage (for TeX) and
+ the Top node (for info/html). */
+ if (input_text[input_text_offset] != '\n')
+ discard_until ("\n"); /* ignore remainder of @copying line */
+
+ input_text_offset = get_until ("\n@end copying", &copying_text);
+ canon_white (copying_text);
+
+ /* For info, output the copying text right away, so it will end up
+ in the header of the Info file, before the first node, and thus
+ get copied automatically to all the split files. For xml, also
+ output it right away since xml output is never split.
+ For html, we output it specifically in html_output_head.
+ For plain text, there's no way to hide it, so the author must
+ use @insertcopying in the desired location. */
+ if (docbook)
+ {
+ if (!xml_in_bookinfo)
+ {
+ xml_insert_element (BOOKINFO, START);
+ xml_in_bookinfo = 1;
+ }
+ xml_insert_element (LEGALNOTICE, START);
+ }
+
+ if (!html && !no_headers)
+ cm_insert_copying ();
+
+ if (docbook)
+ xml_insert_element (LEGALNOTICE, END);
+ break;
+
+ case quotation:
+ /* @quotation does filling (@display doesn't). */
+ if (html)
+ add_html_block_elt ("<blockquote>\n");
+ else
+ {
+ /* with close_single_paragraph, we get no blank line above
+ within @copying. */
+ close_paragraph ();
+ last_char_was_newline = no_indent = 0;
+ indented_fill = filling_enabled = 1;
+ inhibit_paragraph_indentation = 1;
+ current_indent += default_indentation_increment;
+ }
+ if (xml)
+ xml_insert_quotation (insertion_stack->item_function, START);
+ else if (strlen(insertion_stack->item_function))
+ execute_string ("@b{%s:} ", insertion_stack->item_function);
+ break;
+
+ case example:
+ case smallexample:
+ case lisp:
+ case smalllisp:
+ in_fixed_width_font++;
+ /* fall through */
+
+ /* Like @example but no fixed width font. */
+ case display:
+ case smalldisplay:
+ /* Like @display but without indentation. */
+ case smallformat:
+ case format:
+ close_single_paragraph ();
+ inhibit_paragraph_indentation = 1;
+ filling_enabled = 0;
+ last_char_was_newline = 0;
+
+ if (html)
+ /* Kludge alert: if <pre> is followed by a newline, IE3,
+ mozilla, maybe others render an extra blank line before the
+ pre-formatted block. So don't output a newline. */
+ add_html_block_elt_args ("<pre class=\"%s\">", command);
+
+ if (type != format && type != smallformat)
+ {
+ current_indent += example_indentation_increment;
+ if (html)
+ {
+ /* Since we didn't put \n after <pre>, we need to insert
+ the indentation by hand. */
+ int i;
+ for (i = current_indent; i > 0; i--)
+ add_char (' ');
+ }
+ }
+ break;
+
+ case multitable:
+ do_multitable ();
+ break;
+
+ case table:
+ case ftable:
+ case vtable:
+ case itemize:
+ close_single_paragraph ();
+ current_indent += default_indentation_increment;
+ filling_enabled = indented_fill = 1;
+#if defined (INDENT_PARAGRAPHS_IN_TABLE)
+ inhibit_paragraph_indentation = 0;
+#else
+ inhibit_paragraph_indentation = 1;
+#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
+
+ /* Make things work for losers who forget the itemize syntax. */
+ if (type == itemize)
+ {
+ if (!(*insertion_stack->item_function))
+ {
+ free (insertion_stack->item_function);
+ insertion_stack->item_function = (char *) default_item_function;
+ }
+ }
+
+ if (!*insertion_stack->item_function)
+ {
+ line_error (_("%s requires an argument: the formatter for %citem"),
+ insertion_type_pname (type), COMMAND_PREFIX);
+ }
+
+ if (html)
+ {
+ if (type == itemize)
+ {
+ add_html_block_elt ("<ul>\n");
+ in_paragraph = 0;
+ }
+ else
+ { /* We are just starting, so this <dl>
+ has no <dt> children yet. */
+ html_deflist_has_term = 0;
+ add_html_block_elt ("<dl>\n");
+ }
+ }
+ if (xml)
+ xml_begin_table (type, insertion_stack->item_function);
+
+ while (input_text[input_text_offset] == '\n'
+ && input_text[input_text_offset+1] == '\n')
+ {
+ line_number++;
+ input_text_offset++;
+ }
+
+ break;
+
+ case enumerate:
+ close_single_paragraph ();
+ no_indent = 0;
+#if defined (INDENT_PARAGRAPHS_IN_TABLE)
+ inhibit_paragraph_indentation = 0;
+#else
+ inhibit_paragraph_indentation = 1;
+#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
+
+ current_indent += default_indentation_increment;
+ filling_enabled = indented_fill = 1;
+
+ if (html)
+ {
+ enum_html ();
+ in_paragraph = 0;
+ }
+
+ if (xml)
+ xml_begin_enumerate (enumeration_arg);
+
+ if (isdigit (*enumeration_arg))
+ start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
+ else
+ start_enumerating (*enumeration_arg, ENUM_ALPHA);
+ break;
+
+ /* @group produces no output in info. */
+ case group:
+ /* Only close the paragraph if we are not inside of an
+ @example-like environment. */
+ if (xml)
+ xml_insert_element (GROUP, START);
+ else if (!insertion_stack->next
+ || (insertion_stack->next->insertion != display
+ && insertion_stack->next->insertion != smalldisplay
+ && insertion_stack->next->insertion != example
+ && insertion_stack->next->insertion != smallexample
+ && insertion_stack->next->insertion != lisp
+ && insertion_stack->next->insertion != smalllisp
+ && insertion_stack->next->insertion != format
+ && insertion_stack->next->insertion != smallformat
+ && insertion_stack->next->insertion != flushleft
+ && insertion_stack->next->insertion != flushright))
+ close_single_paragraph ();
+ break;
+
+ case cartouche:
+ if (html)
+ add_html_block_elt ("<p><table class=\"cartouche\" summary=\"cartouche\" border=\"1\"><tr><td>\n");
+ if (in_menu)
+ no_discard++;
+ break;
+
+ case floatenv:
+ /* Cannot nest floats, so complain. */
+ if (float_active)
+ {
+ line_error (_("%cfloat environments cannot be nested"), COMMAND_PREFIX);
+ pop_insertion ();
+ break;
+ }
+
+ float_active++;
+
+ { /* Collect data about this float. */
+ /* Example: @float [FLOATTYPE][,XREFLABEL][,POSITION] */
+ char floattype[200] = "";
+ char xreflabel[200] = "";
+ char position[200] = "";
+ char *text;
+ char *caption;
+ char *shortcaption;
+ int start_of_end;
+ int save_line_number = line_number;
+ int save_input_text_offset = input_text_offset;
+ int i;
+
+ if (strlen (insertion_stack->item_function) > 0)
+ {
+ int i = 0, t = 0, c = 0;
+ while (insertion_stack->item_function[i])
+ {
+ if (insertion_stack->item_function[i] == ',')
+ {
+ switch (t)
+ {
+ case 0:
+ floattype[c] = '\0';
+ break;
+ case 1:
+ xreflabel[c] = '\0';
+ break;
+ case 2:
+ position[c] = '\0';
+ break;
+ }
+ c = 0;
+ t++;
+ i++;
+ continue;
+ }
+
+ switch (t)
+ {
+ case 0:
+ floattype[c] = insertion_stack->item_function[i];
+ break;
+ case 1:
+ xreflabel[c] = insertion_stack->item_function[i];
+ break;
+ case 2:
+ position[c] = insertion_stack->item_function[i];
+ break;
+ }
+ c++;
+ i++;
+ }
+ }
+
+ skip_whitespace_and_newlines ();
+
+ start_of_end = get_until ("\n@end float", &text);
+
+ /* Get also the @caption. */
+ i = search_forward_until_pos ("\n@caption{",
+ save_input_text_offset, start_of_end);
+ if (i > -1)
+ {
+ input_text_offset = i + sizeof ("\n@caption{") - 1;
+ get_until_in_braces ("\n@end float", &caption);
+ input_text_offset = save_input_text_offset;
+ }
+ else
+ caption = "";
+
+ /* ... and the @shortcaption. */
+ i = search_forward_until_pos ("\n@shortcaption{",
+ save_input_text_offset, start_of_end);
+ if (i > -1)
+ {
+ input_text_offset = i + sizeof ("\n@shortcaption{") - 1;
+ get_until_in_braces ("\n@end float", &shortcaption);
+ input_text_offset = save_input_text_offset;
+ }
+ else
+ shortcaption = "";
+
+ canon_white (xreflabel);
+ canon_white (floattype);
+ canon_white (position);
+ canon_white (caption);
+ canon_white (shortcaption);
+
+ add_new_float (xstrdup (xreflabel),
+ xstrdup (caption), xstrdup (shortcaption),
+ xstrdup (floattype), xstrdup (position));
+
+ /* Move to the start of the @float so the contents get processed as
+ usual. */
+ input_text_offset = save_input_text_offset;
+ line_number = save_line_number;
+ }
+
+ if (html)
+ add_html_block_elt ("<div class=\"float\">\n");
+ else if (docbook)
+ xml_insert_element (FLOAT, START);
+ else if (xml)
+ {
+ xml_insert_element_with_attribute (FLOAT, START,
+ "name=\"%s\"", current_float_id ());
+
+ xml_insert_element (FLOATTYPE, START);
+ execute_string ("%s", current_float_type ());
+ xml_insert_element (FLOATTYPE, END);
+
+ xml_insert_element (FLOATPOS, START);
+ execute_string ("%s", current_float_position ());
+ xml_insert_element (FLOATPOS, END);
+ }
+ else
+ { /* Info */
+ close_single_paragraph ();
+ inhibit_paragraph_indentation = 1;
+ }
+
+ /* Anchor now. Note that XML documents get their
+ anchors with <float name="anchor"> tag. */
+ if ((!xml || docbook) && strlen (current_float_id ()) > 0)
+ execute_string ("@anchor{%s}", current_float_id ());
+
+ break;
+
+ /* Insertions that are no-ops in info, but do something in TeX. */
+ case ifclear:
+ case ifdocbook:
+ case ifhtml:
+ case ifinfo:
+ case ifnotdocbook:
+ case ifnothtml:
+ case ifnotinfo:
+ case ifnotplaintext:
+ case ifnottex:
+ case ifnotxml:
+ case ifplaintext:
+ case ifset:
+ case iftex:
+ case ifxml:
+ case rawtex:
+ if (in_menu)
+ no_discard++;
+ break;
+
+ case rawdocbook:
+ case rawhtml:
+ case rawxml:
+ raw_output_block++;
+
+ if (raw_output_block > 0)
+ {
+ xml_no_para = 1;
+ escape_html = 0;
+ xml_keep_space++;
+ }
+
+ {
+ /* Some deuglification for improved readability. */
+ extern int xml_in_para;
+ if (xml && !xml_in_para && xml_indentation_increment > 0)
+ add_char ('\n');
+ }
+
+ break;
+
+ case defcv:
+ case deffn:
+ case defivar:
+ case defmac:
+ case defmethod:
+ case defop:
+ case defopt:
+ case defspec:
+ case deftp:
+ case deftypecv:
+ case deftypefn:
+ case deftypefun:
+ case deftypeivar:
+ case deftypemethod:
+ case deftypeop:
+ case deftypevar:
+ case deftypevr:
+ case defun:
+ case defvar:
+ case defvr:
+ inhibit_paragraph_indentation = 1;
+ filling_enabled = indented_fill = 1;
+ current_indent += default_indentation_increment;
+ no_indent = 0;
+ if (xml)
+ xml_begin_definition ();
+ break;
+
+ case flushleft:
+ close_single_paragraph ();
+ inhibit_paragraph_indentation = 1;
+ filling_enabled = indented_fill = no_indent = 0;
+ if (html)
+ add_html_block_elt ("<div align=\"left\">");
+ break;
+
+ case flushright:
+ close_single_paragraph ();
+ filling_enabled = indented_fill = no_indent = 0;
+ inhibit_paragraph_indentation = 1;
+ force_flush_right++;
+ if (html)
+ add_html_block_elt ("<div align=\"right\">");
+ break;
+
+ case titlepage:
+ xml_insert_element (TITLEPAGE, START);
+ break;
+
+ default:
+ line_error ("begin_insertion internal error: type=%d", type);
+ }
+
+ if (!no_discard)
+ discard_until ("\n");
+}
+
+/* Try to end the insertion with the specified TYPE. With a value of
+ `bad_type', TYPE gets translated to match the value currently on top
+ of the stack. Otherwise, if TYPE doesn't match the top of the
+ insertion stack, give error. */
+static void
+end_insertion (enum insertion_type type)
+{
+ enum insertion_type temp_type;
+
+ if (!insertion_level)
+ return;
+
+ temp_type = current_insertion_type ();
+
+ if (type == bad_type)
+ type = temp_type;
+
+ if (type != temp_type)
+ {
+ line_error
+ (_("`@end' expected `%s', but saw `%s'"),
+ insertion_type_pname (temp_type), insertion_type_pname (type));
+ return;
+ }
+
+ pop_insertion ();
+
+ if (xml)
+ {
+ switch (type)
+ {
+ case ifinfo:
+ case documentdescription:
+ break;
+ case quotation:
+ xml_insert_quotation ("", END);
+ break;
+ case example:
+ xml_insert_element (EXAMPLE, END);
+ if (docbook && current_insertion_type () == floatenv)
+ xml_insert_element (FLOATEXAMPLE, END);
+ break;
+ case smallexample:
+ xml_insert_element (SMALLEXAMPLE, END);
+ if (docbook && current_insertion_type () == floatenv)
+ xml_insert_element (FLOATEXAMPLE, END);
+ break;
+ case lisp:
+ xml_insert_element (LISP, END);
+ if (docbook && current_insertion_type () == floatenv)
+ xml_insert_element (FLOATEXAMPLE, END);
+ break;
+ case smalllisp:
+ xml_insert_element (SMALLLISP, END);
+ if (docbook && current_insertion_type () == floatenv)
+ xml_insert_element (FLOATEXAMPLE, END);
+ break;
+ case cartouche:
+ xml_insert_element (CARTOUCHE, END);
+ break;
+ case format:
+ if (docbook && xml_in_bookinfo && xml_in_abstract)
+ {
+ xml_insert_element (ABSTRACT, END);
+ xml_in_abstract = 0;
+ }
+ else
+ xml_insert_element (FORMAT, END);
+ break;
+ case smallformat:
+ xml_insert_element (SMALLFORMAT, END);
+ break;
+ case display:
+ xml_insert_element (DISPLAY, END);
+ break;
+ case smalldisplay:
+ xml_insert_element (SMALLDISPLAY, END);
+ break;
+ case table:
+ case ftable:
+ case vtable:
+ case itemize:
+ xml_end_table (type);
+ break;
+ case enumerate:
+ xml_end_enumerate ();
+ break;
+ case group:
+ xml_insert_element (GROUP, END);
+ break;
+ case titlepage:
+ xml_insert_element (TITLEPAGE, END);
+ break;
+ }
+ }
+ switch (type)
+ {
+ /* Insertions which have no effect on paragraph formatting. */
+ case copying:
+ line_number--;
+ break;
+
+ case ifclear:
+ case ifdocbook:
+ case ifinfo:
+ case ifhtml:
+ case ifnotdocbook:
+ case ifnothtml:
+ case ifnotinfo:
+ case ifnotplaintext:
+ case ifnottex:
+ case ifnotxml:
+ case ifplaintext:
+ case ifset:
+ case iftex:
+ case ifxml:
+ case rawtex:
+ case titlepage:
+ break;
+
+ case rawdocbook:
+ case rawhtml:
+ case rawxml:
+ raw_output_block--;
+
+ if (raw_output_block <= 0)
+ {
+ xml_no_para = 0;
+ escape_html = 1;
+ xml_keep_space--;
+ }
+
+ if ((xml || html)
+ && output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ break;
+
+ case detailmenu:
+ if (xml)
+ xml_insert_element (DETAILMENU, END);
+
+ in_detailmenu--; /* No longer hacking menus. */
+ if (!in_menu)
+ {
+ if (!no_headers)
+ close_insertion_paragraph ();
+ }
+ break;
+
+ case direntry: /* Eaten if html. */
+ insert_string ("END-INFO-DIR-ENTRY\n\n");
+ close_insertion_paragraph ();
+ break;
+
+ case documentdescription:
+ if (xml)
+ insert_string (document_description);
+ xml_insert_element (DOCUMENTDESCRIPTION, END);
+ break;
+
+ case menu:
+ in_menu--; /* No longer hacking menus. */
+ if (html && !no_headers)
+ add_html_block_elt ("</ul>\n");
+ else if (!no_headers && !xml)
+ close_insertion_paragraph ();
+ break;
+
+ case multitable:
+ end_multitable ();
+ break;
+
+ case enumerate:
+ stop_enumerating ();
+ close_insertion_paragraph ();
+ current_indent -= default_indentation_increment;
+ if (html)
+ add_html_block_elt ("</ol>\n");
+ break;
+
+ case flushleft:
+ if (html)
+ add_html_block_elt ("</div>\n");
+ close_insertion_paragraph ();
+ break;
+
+ case cartouche:
+ if (html)
+ add_html_block_elt ("</td></tr></table>\n");
+ close_insertion_paragraph ();
+ break;
+
+ case group:
+ if (!xml || docbook)
+ close_insertion_paragraph ();
+ break;
+
+ case floatenv:
+ if (xml)
+ xml_insert_element (FLOAT, END);
+ else
+ {
+ if (html)
+ add_html_block_elt ("<p><strong class=\"float-caption\">");
+ else
+ close_paragraph ();
+
+ no_indent = 1;
+
+ /* Legend:
+ 1) @float Foo,lbl & no caption: Foo 1.1
+ 2) @float Foo & no caption: Foo
+ 3) @float ,lbl & no caption: 1.1
+ 4) @float & no caption: */
+
+ if (!xml && !html)
+ indent (current_indent);
+
+ if (strlen (current_float_type ()))
+ execute_string ("%s", current_float_type ());
+
+ if (strlen (current_float_id ()) > 0)
+ {
+ if (strlen (current_float_type ()) > 0)
+ add_char (' ');
+
+ add_word (current_float_number ());
+ }
+
+ if (strlen (current_float_title ()) > 0)
+ {
+ if (strlen (current_float_type ()) > 0
+ || strlen (current_float_id ()) > 0)
+ insert_string (": ");
+
+ execute_string ("%s", current_float_title ());
+ }
+
+ /* Indent the following paragraph. */
+ inhibit_paragraph_indentation = 0;
+
+ if (html)
+ add_word ("</strong></p></div>\n");
+ else
+ close_paragraph ();
+ }
+ float_active--;
+ break;
+
+ case format:
+ case smallformat:
+ case display:
+ case smalldisplay:
+ case example:
+ case smallexample:
+ case lisp:
+ case smalllisp:
+ case quotation:
+ /* @format and @smallformat are the only fixed_width insertion
+ without a change in indentation. */
+ if (type != format && type != smallformat && type != quotation)
+ current_indent -= example_indentation_increment;
+ else if (type == quotation && !html)
+ current_indent -= default_indentation_increment;
+
+ if (html)
+ { /* The complex code in close_paragraph that kills whitespace
+ does not function here, since we've inserted non-whitespace
+ (the </whatever>) before it. The indentation already got
+ inserted at the end of the last example line, so we have to
+ delete it, or browsers wind up showing an extra blank line.
+ Furthermore, if we're inside indented environments, we
+ might have arbitrarily much indentation, so remove it all. */
+ kill_self_indent (-1);
+ add_html_block_elt (type == quotation
+ ? "</blockquote>\n" : "</pre>\n");
+ }
+
+ /* The ending of one of these insertions always marks the
+ start of a new paragraph, except for the XML output. */
+ if (!xml || docbook)
+ close_insertion_paragraph ();
+
+ /* </pre> closes paragraph without messing with </p>. */
+ if (html && type != quotation)
+ paragraph_is_open = 0;
+ break;
+
+ case table:
+ case ftable:
+ case vtable:
+ current_indent -= default_indentation_increment;
+ if (html)
+ add_html_block_elt ("</dl>\n");
+ close_insertion_paragraph ();
+ break;
+
+ case itemize:
+ current_indent -= default_indentation_increment;
+ if (html)
+ add_html_block_elt ("</ul>\n");
+ close_insertion_paragraph ();
+ break;
+
+ case flushright:
+ force_flush_right--;
+ if (html)
+ add_html_block_elt ("</div>\n");
+ close_insertion_paragraph ();
+ break;
+
+ /* Handle the @defun insertions with this default clause. */
+ default:
+ {
+ int base_type;
+
+ if (type < defcv || type > defvr)
+ line_error ("end_insertion internal error: type=%d", type);
+
+ base_type = get_base_type (type);
+ switch (base_type)
+ {
+ case deffn:
+ case defvr:
+ case deftp:
+ case deftypecv:
+ case deftypefn:
+ case deftypevr:
+ case defcv:
+ case defop:
+ case deftypemethod:
+ case deftypeop:
+ case deftypeivar:
+ if (html)
+ {
+ if (paragraph_is_open)
+ add_html_block_elt ("</p>");
+ /* close the div and blockquote which has been opened in defun.c */
+ if (!rollback_empty_tag ("blockquote"))
+ add_html_block_elt ("</blockquote>");
+ add_html_block_elt ("</div>\n");
+ }
+ if (xml)
+ xml_end_definition ();
+ break;
+ } /* switch (base_type)... */
+
+ current_indent -= default_indentation_increment;
+ close_insertion_paragraph ();
+ }
+ break;
+
+ }
+
+ if (current_indent < 0)
+ line_error ("end_insertion internal error: current indent=%d",
+ current_indent);
+}
+
+/* Insertions cannot cross certain boundaries, such as node beginnings. In
+ code that creates such boundaries, you should call `discard_insertions'
+ before doing anything else. It prints the errors for you, and cleans up
+ the insertion stack.
+
+ With nonzero SPECIALS_OK argument, allows unmatched
+ @if... conditionals, otherwise not. This is because conditionals can
+ cross node boundaries. Always happens with the @top node, for example. */
+void
+discard_insertions (int specials_ok)
+{
+ int real_line_number = line_number;
+ while (insertion_stack)
+ {
+ if (specials_ok
+ && ((ifclear <= insertion_stack->insertion
+ && insertion_stack->insertion <= iftex)
+ || insertion_stack->insertion == rawdocbook
+ || insertion_stack->insertion == rawhtml
+ || insertion_stack->insertion == rawxml
+ || insertion_stack->insertion == rawtex))
+ break;
+ else
+ {
+ const char *offender = insertion_type_pname (insertion_stack->insertion);
+
+ file_line_error (insertion_stack->filename,
+ insertion_stack->line_number,
+ _("No matching `%cend %s'"), COMMAND_PREFIX,
+ offender);
+ pop_insertion ();
+ }
+ }
+ line_number = real_line_number;
+}
+
+/* Insertion (environment) commands. */
+
+void
+cm_quotation (void)
+{
+ /* We start the blockquote element in the insertion. */
+ begin_insertion (quotation);
+}
+
+void
+cm_example (void)
+{
+ if (docbook && current_insertion_type () == floatenv)
+ xml_begin_docbook_float (FLOATEXAMPLE);
+
+ if (xml)
+ {
+ /* Rollback previous newlines. These occur between
+ </para> and <example>. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+
+ xml_insert_element (EXAMPLE, START);
+
+ /* Make sure example text is starting on a new line
+ for improved readability. */
+ if (docbook)
+ add_char ('\n');
+ }
+
+ begin_insertion (example);
+}
+
+void
+cm_smallexample (void)
+{
+ if (docbook && current_insertion_type () == floatenv)
+ xml_begin_docbook_float (FLOATEXAMPLE);
+
+ if (xml)
+ {
+ /* See cm_example comments about newlines. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ xml_insert_element (SMALLEXAMPLE, START);
+ if (docbook)
+ add_char ('\n');
+ }
+
+ begin_insertion (smallexample);
+}
+
+void
+cm_lisp (void)
+{
+ if (docbook && current_insertion_type () == floatenv)
+ xml_begin_docbook_float (FLOATEXAMPLE);
+
+ if (xml)
+ {
+ /* See cm_example comments about newlines. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ xml_insert_element (LISP, START);
+ if (docbook)
+ add_char ('\n');
+ }
+
+ begin_insertion (lisp);
+}
+
+void
+cm_smalllisp (void)
+{
+ if (docbook && current_insertion_type () == floatenv)
+ xml_begin_docbook_float (FLOATEXAMPLE);
+
+ if (xml)
+ {
+ /* See cm_example comments about newlines. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ xml_insert_element (SMALLLISP, START);
+ if (docbook)
+ add_char ('\n');
+ }
+
+ begin_insertion (smalllisp);
+}
+
+void
+cm_cartouche (void)
+{
+ if (docbook && current_insertion_type () == floatenv)
+ xml_begin_docbook_float (CARTOUCHE);
+
+ if (xml)
+ xml_insert_element (CARTOUCHE, START);
+ begin_insertion (cartouche);
+}
+
+void
+cm_copying (void)
+{
+ begin_insertion (copying);
+}
+
+/* Not an insertion, despite the name, but it goes with cm_copying. */
+void
+cm_insert_copying (void)
+{
+ if (!copying_text)
+ {
+ warning ("@copying not used before %s", command);
+ return;
+ }
+
+ /* It is desirable that @copying is set early in the input file. For
+ Info output, we write the copying text out right away, and thus it
+ may well be the first thing in the output, and we want the file
+ header first. The special case in add_char has to check for
+ executing_string, so it won't be effective. Thus, do it explicitly. */
+ output_head ();
+ execute_string ("%s", copying_text);
+
+ if (!xml && !html)
+ {
+ add_word ("\n\n");
+ /* Update output_position so that the node positions in the tag
+ tables will take account of the copying text. */
+ flush_output ();
+ }
+}
+
+void
+cm_format (void)
+{
+ if (xml)
+ {
+ if (docbook && xml_in_bookinfo)
+ {
+ xml_insert_element (ABSTRACT, START);
+ xml_in_abstract = 1;
+ }
+ else
+ {
+ /* See cm_example comments about newlines. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ xml_insert_element (FORMAT, START);
+ if (docbook)
+ add_char ('\n');
+ }
+ }
+ begin_insertion (format);
+}
+
+void
+cm_smallformat (void)
+{
+ if (xml)
+ {
+ /* See cm_example comments about newlines. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ xml_insert_element (SMALLFORMAT, START);
+ if (docbook)
+ add_char ('\n');
+ }
+
+ begin_insertion (smallformat);
+}
+
+void
+cm_display (void)
+{
+ if (xml)
+ {
+ /* See cm_example comments about newlines. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ xml_insert_element (DISPLAY, START);
+ if (docbook)
+ add_char ('\n');
+ }
+
+ begin_insertion (display);
+}
+
+void
+cm_smalldisplay (void)
+{
+ if (xml)
+ {
+ /* See cm_example comments about newlines. */
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ xml_insert_element (SMALLDISPLAY, START);
+ if (docbook)
+ add_char ('\n');
+ }
+
+ begin_insertion (smalldisplay);
+}
+
+void
+cm_direntry (void)
+{
+ if (html || xml || no_headers)
+ command_name_condition ();
+ else
+ begin_insertion (direntry);
+}
+
+void
+cm_documentdescription (void)
+{
+ if (html)
+ begin_insertion (documentdescription);
+
+ else if (xml)
+ {
+ xml_insert_element (DOCUMENTDESCRIPTION, START);
+ begin_insertion (documentdescription);
+ }
+
+ else
+ command_name_condition ();
+}
+
+
+void
+cm_itemize (void)
+{
+ begin_insertion (itemize);
+}
+
+/* Start an enumeration insertion of type TYPE. If the user supplied
+ no argument on the line, then use DEFAULT_STRING as the initial string. */
+static void
+do_enumeration (enum insertion_type type, char *default_string)
+{
+ get_until_in_line (0, ".", &enumeration_arg);
+ canon_white (enumeration_arg);
+
+ if (!*enumeration_arg)
+ {
+ free (enumeration_arg);
+ enumeration_arg = xstrdup (default_string);
+ }
+
+ if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
+ {
+ warning (_("%s requires letter or digit"), insertion_type_pname (type));
+
+ switch (type)
+ {
+ case enumerate:
+ default_string = "1";
+ break;
+ }
+ enumeration_arg = xstrdup (default_string);
+ }
+ begin_insertion (type);
+}
+
+void
+cm_enumerate (void)
+{
+ do_enumeration (enumerate, "1");
+}
+
+
+
+/* Handle verbatim environment:
+ find_end_verbatim == 0: process until end of file
+ find_end_verbatim != 0: process until 'COMMAND_PREFIXend verbatim'
+ or end of file
+
+ No indentation is inserted: this is verbatim after all.
+ If you want indentation, enclose @verbatim in @example.
+
+ Thus, we cannot simply copy the input to the output, since the
+ verbatim environment may be encapsulated in an @example environment,
+ for example. */
+
+void
+handle_verbatim_environment (int find_end_verbatim)
+{
+ int character;
+ int seen_end = 0;
+ int save_filling_enabled = filling_enabled;
+ int save_inhibit_paragraph_indentation = inhibit_paragraph_indentation;
+
+ if (!insertion_stack)
+ close_single_paragraph (); /* no blank lines if not at outer level */
+ inhibit_paragraph_indentation = 1;
+ filling_enabled = 0;
+ in_fixed_width_font++;
+ last_char_was_newline = 0;
+
+ if (html)
+ { /* If inside @example, we'll be preceded by the indentation
+ already. Browsers will ignore those spaces because we're about
+ to start another <pre> (don't ask me). So, wipe them out for
+ cleanliness, and re-insert. */
+ int i;
+ kill_self_indent (default_indentation_increment);
+ add_html_block_elt ("<pre class=\"verbatim\">");
+ for (i = current_indent; i > 0; i--)
+ add_char (' ');
+ }
+ else if (xml)
+ {
+ xml_insert_element (VERBATIM, START);
+ }
+
+ if (find_end_verbatim)
+ { /* Ignore the remainder of the @verbatim line. */
+ char *junk;
+ get_rest_of_line (0, &junk);
+ free (junk);
+ }
+
+ while (input_text_offset < input_text_length)
+ {
+ character = curchar ();
+
+ if (character == '\n')
+ line_number++;
+
+ /* Assume no newlines in END_VERBATIM. */
+ else if (find_end_verbatim && (character == COMMAND_PREFIX) /* @ */
+ && (input_text_length - input_text_offset > sizeof (END_VERBATIM))
+ && !strncmp (&input_text[input_text_offset+1], END_VERBATIM,
+ sizeof (END_VERBATIM)-1))
+ {
+ input_text_offset += sizeof (END_VERBATIM);
+ seen_end = 1;
+ break;
+ }
+
+ if (html && character == '&' && escape_html)
+ add_word ("&amp;");
+ else if (html && character == '<' && escape_html)
+ add_word ("&lt;");
+ else
+ add_char (character);
+
+ input_text_offset++;
+ }
+
+ if (find_end_verbatim && !seen_end)
+ warning (_("end of file inside verbatim block"));
+
+ if (html)
+ { /* See comments in example case above. */
+ kill_self_indent (default_indentation_increment);
+ add_word ("</pre>");
+ }
+ else if (xml)
+ {
+ xml_insert_element (VERBATIM, END);
+ }
+
+ in_fixed_width_font--;
+ filling_enabled = save_filling_enabled;
+ inhibit_paragraph_indentation = save_inhibit_paragraph_indentation;
+}
+
+void
+cm_verbatim (void)
+{
+ handle_verbatim_environment (1);
+}
+
+void
+cm_table (void)
+{
+ begin_insertion (table);
+}
+
+void
+cm_multitable (void)
+{
+ begin_insertion (multitable); /* @@ */
+}
+
+void
+cm_ftable (void)
+{
+ begin_insertion (ftable);
+}
+
+void
+cm_vtable (void)
+{
+ begin_insertion (vtable);
+}
+
+void
+cm_group (void)
+{
+ begin_insertion (group);
+}
+
+/* Insert raw HTML (no escaping of `<' etc.). */
+void
+cm_html (int arg)
+{
+ if (process_html)
+ begin_insertion (rawhtml);
+ else
+ command_name_condition ();
+}
+
+void
+cm_xml (int arg)
+{
+ if (process_xml)
+ begin_insertion (rawxml);
+ else
+ command_name_condition ();
+}
+
+void
+cm_docbook (int arg)
+{
+ if (process_docbook)
+ begin_insertion (rawdocbook);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifdocbook (void)
+{
+ if (process_docbook)
+ begin_insertion (ifdocbook);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnotdocbook (void)
+{
+ if (!process_docbook)
+ begin_insertion (ifnotdocbook);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifhtml (void)
+{
+ if (process_html)
+ begin_insertion (ifhtml);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnothtml (void)
+{
+ if (!process_html)
+ begin_insertion (ifnothtml);
+ else
+ command_name_condition ();
+}
+
+
+void
+cm_ifinfo (void)
+{
+ if (process_info)
+ begin_insertion (ifinfo);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnotinfo (void)
+{
+ if (!process_info)
+ begin_insertion (ifnotinfo);
+ else
+ command_name_condition ();
+}
+
+
+void
+cm_ifplaintext (void)
+{
+ if (process_plaintext)
+ begin_insertion (ifplaintext);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnotplaintext (void)
+{
+ if (!process_plaintext)
+ begin_insertion (ifnotplaintext);
+ else
+ command_name_condition ();
+}
+
+
+void
+cm_tex (void)
+{
+ if (process_tex)
+ begin_insertion (rawtex);
+ else
+ command_name_condition ();
+}
+
+void
+cm_iftex (void)
+{
+ if (process_tex)
+ begin_insertion (iftex);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnottex (void)
+{
+ if (!process_tex)
+ begin_insertion (ifnottex);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifxml (void)
+{
+ if (process_xml)
+ begin_insertion (ifxml);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnotxml (void)
+{
+ if (!process_xml)
+ begin_insertion (ifnotxml);
+ else
+ command_name_condition ();
+}
+
+
+/* Generic xrefable block with a caption. */
+void
+cm_float (void)
+{
+ begin_insertion (floatenv);
+}
+
+void
+cm_caption (int arg)
+{
+ char *temp;
+
+ /* This is a no_op command for most formats, as we handle it during @float
+ insertion. For XML though, we handle it here to keep document structure
+ as close as possible, to the Texinfo source. */
+
+ /* Everything is already handled at START. */
+ if (arg == END)
+ return;
+
+ /* Check if it's mislocated. */
+ if (current_insertion_type () != floatenv)
+ line_error (_("@%s not meaningful outside `@float' environment"), command);
+
+ get_until_in_braces ("\n@end float", &temp);
+
+ if (xml)
+ {
+ int elt = STREQ (command, "shortcaption") ? SHORTCAPTION : CAPTION;
+ xml_insert_element (elt, START);
+ if (!docbook)
+ execute_string ("%s", temp);
+ xml_insert_element (elt, END);
+ }
+
+ free (temp);
+}
+
+/* Begin an insertion where the lines are not filled or indented. */
+void
+cm_flushleft (void)
+{
+ begin_insertion (flushleft);
+}
+
+/* Begin an insertion where the lines are not filled, and each line is
+ forced to the right-hand side of the page. */
+void
+cm_flushright (void)
+{
+ begin_insertion (flushright);
+}
+
+void
+cm_menu (void)
+{
+ if (current_node == NULL && !macro_expansion_output_stream)
+ {
+ warning (_("@menu seen before first @node, creating `Top' node"));
+ warning (_("perhaps your @top node should be wrapped in @ifnottex rather than @ifinfo?"));
+ /* Include @top command so we can construct the implicit node tree. */
+ execute_string ("@node top\n@top Top\n");
+ }
+ begin_insertion (menu);
+}
+
+void
+cm_detailmenu (void)
+{
+ if (current_node == NULL && !macro_expansion_output_stream)
+ { /* Problems anyway, @detailmenu should always be inside @menu. */
+ warning (_("@detailmenu seen before first node, creating `Top' node"));
+ execute_string ("@node top\n@top Top\n");
+ }
+ begin_insertion (detailmenu);
+}
+
+/* Title page commands. */
+
+void
+cm_titlepage (void)
+{
+ titlepage_cmd_present = 1;
+ if (xml && !docbook)
+ begin_insertion (titlepage);
+ else
+ command_name_condition ();
+}
+
+void
+cm_author (void)
+{
+ char *rest;
+ get_rest_of_line (1, &rest);
+
+ if (is_in_insertion_of_type (quotation))
+ {
+ if (html)
+ add_word_args ("&mdash; %s", rest);
+ else if (docbook)
+ {
+ /* FIXME Ideally, we should use an attribution element,
+ but they are supposed to be at the start of quotation
+ blocks. So to avoid looking ahead mess, let's just
+ use mdash like HTML for now. */
+ xml_insert_entity ("mdash");
+ add_word (rest);
+ }
+ else if (xml)
+ {
+ xml_insert_element (AUTHOR, START);
+ add_word (rest);
+ xml_insert_element (AUTHOR, END);
+ }
+ else
+ add_word_args ("-- %s", rest);
+ }
+ else if (is_in_insertion_of_type (titlepage))
+ {
+ if (xml && !docbook)
+ {
+ xml_insert_element (AUTHOR, START);
+ add_word (rest);
+ xml_insert_element (AUTHOR, END);
+ }
+ }
+ else
+ line_error (_("@%s not meaningful outside `@titlepage' and `@quotation' environments"),
+ command);
+
+ free (rest);
+}
+
+void
+cm_titlepage_cmds (void)
+{
+ char *rest;
+
+ get_rest_of_line (1, &rest);
+
+ if (!is_in_insertion_of_type (titlepage))
+ line_error (_("@%s not meaningful outside `@titlepage' environment"),
+ command);
+
+ if (xml && !docbook)
+ {
+ int elt = 0;
+
+ if (STREQ (command, "title"))
+ elt = BOOKTITLE;
+ else if (STREQ (command, "subtitle"))
+ elt = BOOKSUBTITLE;
+
+ xml_insert_element (elt, START);
+ add_word (rest);
+ xml_insert_element (elt, END);
+ }
+
+ free (rest);
+}
+
+/* End existing insertion block. */
+void
+cm_end (void)
+{
+ char *temp;
+ enum insertion_type type;
+
+ get_rest_of_line (0, &temp);
+
+ if (!insertion_level)
+ {
+ line_error (_("Unmatched `%c%s'"), COMMAND_PREFIX, command);
+ return;
+ }
+
+ if (temp[0] == 0)
+ line_error (_("`%c%s' needs something after it"), COMMAND_PREFIX, command);
+
+ type = find_type_from_name (temp);
+
+ if (type == bad_type)
+ {
+ line_error (_("Bad argument `%s' to `@%s', using `%s'"),
+ temp, command, insertion_type_pname (current_insertion_type ()));
+ }
+ if (xml && type == menu) /* fixme */
+ {
+ xml_end_menu ();
+ }
+ end_insertion (type);
+ free (temp);
+}
+
+/* @itemx, @item. */
+
+static int itemx_flag = 0;
+
+/* Return whether CMD takes a brace-delimited {arg}. */
+int
+command_needs_braces (char *cmd)
+{
+ int i;
+ for (i = 0; command_table[i].name; i++)
+ {
+ if (STREQ (command_table[i].name, cmd))
+ return command_table[i].argument_in_braces == BRACE_ARGS;
+ }
+
+ return 0; /* macro or alias */
+}
+
+
+void
+cm_item (void)
+{
+ char *rest_of_line, *item_func;
+
+ /* Can only hack "@item" while inside of an insertion. */
+ if (insertion_level)
+ {
+ INSERTION_ELT *stack = insertion_stack;
+ int original_input_text_offset;
+
+ skip_whitespace ();
+ original_input_text_offset = input_text_offset;
+
+ get_rest_of_line (0, &rest_of_line);
+ item_func = current_item_function ();
+
+ /* Do the right thing depending on which insertion function is active. */
+ switch_top:
+ switch (stack->insertion)
+ {
+ case multitable:
+ multitable_item ();
+ /* Support text directly after the @item. */
+ if (*rest_of_line)
+ {
+ line_number--;
+ input_text_offset = original_input_text_offset;
+ }
+ break;
+
+ case ifclear:
+ case ifhtml:
+ case ifinfo:
+ case ifnothtml:
+ case ifnotinfo:
+ case ifnotplaintext:
+ case ifnottex:
+ case ifnotxml:
+ case ifplaintext:
+ case ifset:
+ case iftex:
+ case ifxml:
+ case rawdocbook:
+ case rawhtml:
+ case rawxml:
+ case rawtex:
+ case tex:
+ case cartouche:
+ stack = stack->next;
+ if (!stack)
+ goto no_insertion;
+ else
+ goto switch_top;
+ break;
+
+ case menu:
+ case quotation:
+ case example:
+ case smallexample:
+ case lisp:
+ case smalllisp:
+ case format:
+ case smallformat:
+ case display:
+ case smalldisplay:
+ case group:
+ line_error (_("@%s not meaningful inside `@%s' block"),
+ command,
+ insertion_type_pname (current_insertion_type ()));
+ break;
+
+ case itemize:
+ case enumerate:
+ if (itemx_flag)
+ {
+ line_error (_("@itemx not meaningful inside `%s' block"),
+ insertion_type_pname (current_insertion_type ()));
+ }
+ else
+ {
+ if (html)
+ add_html_block_elt ("<li>");
+ else if (xml)
+ xml_begin_item ();
+ else
+ {
+ start_paragraph ();
+ kill_self_indent (-1);
+ filling_enabled = indented_fill = 1;
+
+ if (current_item_function ())
+ {
+ indent (current_indent - 2);
+
+ /* The item marker can be given with or without
+ braces -- @bullet and @bullet{} are both ok.
+ Or it might be something that doesn't take
+ braces at all, such as "o" or "#" or "@ ".
+ Thus, only supply braces if the item marker is
+ a command, they haven't supplied braces
+ themselves, and we know it needs them. */
+ if (item_func && *item_func)
+ {
+ if (*item_func == COMMAND_PREFIX
+ && item_func[strlen (item_func) - 1] != '}'
+ && command_needs_braces (item_func + 1))
+ execute_string ("%s{}", item_func);
+ else
+ execute_string ("%s", item_func);
+ }
+ insert (' ');
+ }
+ else
+ enumerate_item ();
+
+ /* Special hack. This makes `close_paragraph' a no-op until
+ `start_paragraph' has been called. */
+ must_start_paragraph = 1;
+ }
+
+ /* Handle text directly after the @item. */
+ if (*rest_of_line)
+ {
+ line_number--;
+ input_text_offset = original_input_text_offset;
+ }
+ }
+ break;
+
+ case table:
+ case ftable:
+ case vtable:
+ if (html)
+ { /* If nothing has been output since the last <dd>,
+ remove the empty <dd> element. Some browsers render
+ an extra empty line for <dd><dt>, which makes @itemx
+ conversion look ugly. */
+ rollback_empty_tag ("dd");
+
+ /* Force the browser to render one blank line before
+ each new @item in a table. But don't do that if
+ this is the first <dt> after the <dl>, or if we are
+ converting @itemx.
+
+ Note that there are some browsers which ignore <br>
+ in this context, but I cannot find any way to force
+ them all render exactly one blank line. */
+ if (!itemx_flag && html_deflist_has_term)
+ add_html_block_elt ("<br>");
+
+ /* We are about to insert a <dt>, so this <dl> has a term.
+ Feel free to insert a <br> next time. :) */
+ html_deflist_has_term = 1;
+
+ add_html_block_elt ("<dt>");
+ if (item_func && *item_func)
+ execute_string ("%s{%s}", item_func, rest_of_line);
+ else
+ execute_string ("%s", rest_of_line);
+
+ if (current_insertion_type () == ftable)
+ execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
+
+ if (current_insertion_type () == vtable)
+ execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
+
+ add_html_block_elt ("<dd>");
+ }
+ else if (xml) /* && docbook)*/ /* 05-08 */
+ {
+ xml_begin_table_item ();
+
+ if (!docbook && current_insertion_type () == ftable)
+ execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
+
+ if (!docbook && current_insertion_type () == vtable)
+ execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
+
+ if (item_func && *item_func)
+ execute_string ("%s{%s}", item_func, rest_of_line);
+ else
+ execute_string ("%s", rest_of_line);
+ xml_continue_table_item ();
+ }
+ else
+ {
+ /* We need this to determine if we have two @item's in a row
+ (see test just below). */
+ static int last_item_output_position = 0;
+
+ /* Get rid of extra characters. */
+ kill_self_indent (-1);
+
+ /* If we have one @item followed directly by another @item,
+ we need to insert a blank line. This is not true for
+ @itemx, though. */
+ if (!itemx_flag && last_item_output_position == output_position)
+ insert ('\n');
+
+ /* `close_paragraph' almost does what we want. The problem
+ is when paragraph_is_open, and last_char_was_newline, and
+ the last newline has been turned into a space, because
+ filling_enabled. I handle it here. */
+ if (last_char_was_newline && filling_enabled &&
+ paragraph_is_open)
+ insert ('\n');
+ close_paragraph ();
+
+#if defined (INDENT_PARAGRAPHS_IN_TABLE)
+ /* Indent on a new line, but back up one indentation level. */
+ {
+ int save = inhibit_paragraph_indentation;
+ inhibit_paragraph_indentation = 1;
+ /* At this point, inserting any non-whitespace character will
+ force the existing indentation to be output. */
+ add_char ('i');
+ inhibit_paragraph_indentation = save;
+ }
+#else /* !INDENT_PARAGRAPHS_IN_TABLE */
+ add_char ('i');
+#endif /* !INDENT_PARAGRAPHS_IN_TABLE */
+
+ output_paragraph_offset--;
+ kill_self_indent (default_indentation_increment + 1);
+
+ /* Add item's argument to the line. */
+ filling_enabled = 0;
+ if (item_func && *item_func)
+ execute_string ("%s{%s}", item_func, rest_of_line);
+ else
+ execute_string ("%s", rest_of_line);
+
+ if (current_insertion_type () == ftable)
+ execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line);
+ else if (current_insertion_type () == vtable)
+ execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line);
+
+ /* Start a new line, and let start_paragraph ()
+ do the indenting of it for you. */
+ close_single_paragraph ();
+ indented_fill = filling_enabled = 1;
+ last_item_output_position = output_position;
+ }
+ }
+ free (rest_of_line);
+ }
+ else
+ {
+ no_insertion:
+ line_error (_("%c%s found outside of an insertion block"),
+ COMMAND_PREFIX, command);
+ }
+}
+
+void
+cm_itemx (void)
+{
+ itemx_flag++;
+ cm_item ();
+ itemx_flag--;
+}
+
+int headitem_flag = 0;
+
+void
+cm_headitem (void)
+{
+ headitem_flag = 1;
+ cm_item ();
+}
diff --git a/makeinfo/insertion.h b/makeinfo/insertion.h
new file mode 100644
index 0000000..75e5468
--- /dev/null
+++ b/makeinfo/insertion.h
@@ -0,0 +1,82 @@
+/* insertion.h -- declarations for insertion.c.
+ $Id: insertion.h,v 1.15 2008/01/31 18:33:27 karl Exp $
+
+ Copyright (C) 1998, 1999, 2001, 2002, 2003, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef INSERTION_H
+#define INSERTION_H
+
+/* Must match list in insertion.c. */
+enum insertion_type
+{
+ cartouche, copying, defcv, deffn, defivar, defmac, defmethod, defop,
+ defopt, defspec, deftp, deftypecv, deftypefn, deftypefun, deftypeivar,
+ deftypemethod, deftypeop, deftypevar, deftypevr, defun, defvar, defvr,
+ detailmenu, direntry, display, documentdescription, enumerate,
+ example, floatenv, flushleft, flushright, format, ftable, group,
+ ifclear, ifdocbook, ifhtml, ifinfo, ifnotdocbook, ifnothtml, ifnotinfo,
+ ifnotplaintext, ifnottex, ifnotxml, ifplaintext, ifset, iftex, ifxml,
+ itemize, lisp, menu, multitable, quotation, rawdocbook, rawhtml, rawtex,
+ rawxml, smalldisplay, smallexample, smallformat, smalllisp, verbatim,
+ table, tex, vtable, titlepage, bad_type
+};
+
+extern const char default_item_function[];
+
+typedef struct istack_elt
+{
+ struct istack_elt *next;
+ char *item_function;
+ char *filename;
+ int line_number;
+ int filling_enabled;
+ int indented_fill;
+ int insertion;
+ int inhibited;
+ int in_fixed_width_font;
+} INSERTION_ELT;
+
+extern int insertion_level;
+extern INSERTION_ELT *insertion_stack;
+extern int in_menu;
+extern int in_detailmenu;
+extern int had_menu_commentary;
+extern int in_paragraph;
+
+extern int headitem_flag;
+extern int after_headitem;
+
+extern void init_insertion_stack (void);
+extern void command_name_condition (void);
+extern void cm_ifdocbook (void), cm_ifnotdocbook(void), cm_docbook (int arg);
+extern void cm_ifhtml (void), cm_ifnothtml(void), cm_html (int arg);
+extern void cm_ifinfo (void), cm_ifnotinfo (void);
+extern void cm_ifplaintext (void), cm_ifnotplaintext(void);
+extern void cm_iftex (void), cm_ifnottex (void), cm_tex (void);
+extern void cm_ifxml (void), cm_ifnotxml (void), cm_xml (int arg);
+extern void handle_verbatim_environment (int find_end_verbatim);
+extern void begin_insertion (enum insertion_type type);
+extern void pop_insertion (void);
+extern void discard_insertions (int specials_ok);
+
+extern int is_in_insertion_of_type (int type);
+extern int command_needs_braces (char *cmd);
+
+extern enum insertion_type find_type_from_name (char *name);
+
+extern char *get_item_function (void);
+#endif /* !INSERTION_H */
diff --git a/makeinfo/lang.c b/makeinfo/lang.c
new file mode 100644
index 0000000..a56cb61
--- /dev/null
+++ b/makeinfo/lang.c
@@ -0,0 +1,1811 @@
+/* lang.c -- language-dependent support.
+ $Id: lang.c,v 1.34 2007/12/03 01:38:43 karl Exp $
+
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#include "system.h"
+#include "cmds.h"
+#include "files.h"
+#include "lang.h"
+#include "makeinfo.h"
+#include "xml.h"
+
+#include <assert.h>
+
+/* Current document encoding. */
+encoding_code_type document_encoding_code = no_encoding;
+
+/* Current language code; default is English. */
+language_code_type language_code = en;
+
+/* Language to use for translations that end up in the output. */
+char *document_language = "C";
+
+/* By default, unsupported encoding is an empty string. */
+char *unknown_encoding = NULL;
+
+static iso_map_type asis_map [] = {{NULL, 0, 0}}; /* ASCII, etc. */
+
+/* Translation table between HTML and ISO Codes. The last item is
+ hopefully the Unicode. It might be possible that those Unicodes are
+ not correct, cause I didn't check them. kama */
+static iso_map_type iso8859_1_map [] = {
+ { "nbsp", 0xA0, 0x00A0 },
+ { "iexcl", 0xA1, 0x00A1 },
+ { "cent", 0xA2, 0x00A2 },
+ { "pound", 0xA3, 0x00A3 },
+ { "curren", 0xA4, 0x00A4 },
+ { "yen", 0xA5, 0x00A5 },
+ { "brkbar", 0xA6, 0x00A6 },
+ { "sect", 0xA7, 0x00A7 },
+ { "uml", 0xA8, 0x00A8 },
+ { "copy", 0xA9, 0x00A9 },
+ { "ordf", 0xAA, 0x00AA },
+ { "laquo", 0xAB, 0x00AB },
+ { "not", 0xAC, 0x00AC },
+ { "shy", 0xAD, 0x00AD },
+ { "reg", 0xAE, 0x00AE },
+ { "hibar", 0xAF, 0x00AF },
+ { "deg", 0xB0, 0x00B0 },
+ { "plusmn", 0xB1, 0x00B1 },
+ { "sup2", 0xB2, 0x00B2 },
+ { "sup3", 0xB3, 0x00B3 },
+ { "acute", 0xB4, 0x00B4 },
+ { "micro", 0xB5, 0x00B5 },
+ { "para", 0xB6, 0x00B6 },
+ { "middot", 0xB7, 0x00B7 },
+ { "cedil", 0xB8, 0x00B8 },
+ { "sup1", 0xB9, 0x00B9 },
+ { "ordm", 0xBA, 0x00BA },
+ { "raquo", 0xBB, 0x00BB },
+ { "frac14", 0xBC, 0x00BC },
+ { "frac12", 0xBD, 0x00BD },
+ { "frac34", 0xBE, 0x00BE },
+ { "iquest", 0xBF, 0x00BF },
+ { "Agrave", 0xC0, 0x00C0, "A" },
+ { "Aacute", 0xC1, 0x00C1, "A" },
+ { "Acirc", 0xC2, 0x00C2, "A" },
+ { "Atilde", 0xC3, 0x00C3, "A" },
+ { "Auml", 0xC4, 0x00C4, "A" },
+ { "Aring", 0xC5, 0x00C5, "AA" },
+ { "AElig", 0xC6, 0x00C6, "AE" },
+ { "Ccedil", 0xC7, 0x00C7, "C" },
+ { "Ccedil", 0xC7, 0x00C7, "C" },
+ { "Egrave", 0xC8, 0x00C8, "E" },
+ { "Eacute", 0xC9, 0x00C9, "E" },
+ { "Ecirc", 0xCA, 0x00CA, "E" },
+ { "Euml", 0xCB, 0x00CB, "E" },
+ { "Igrave", 0xCC, 0x00CC, "I" },
+ { "Iacute", 0xCD, 0x00CD, "I" },
+ { "Icirc", 0xCE, 0x00CE, "I" },
+ { "Iuml", 0xCF, 0x00CF, "I" },
+ { "ETH", 0xD0, 0x00D0, "DH" },
+ { "Ntilde", 0xD1, 0x00D1, "N" },
+ { "Ograve", 0xD2, 0x00D2, "O" },
+ { "Oacute", 0xD3, 0x00D3, "O" },
+ { "Ocirc", 0xD4, 0x00D4, "O" },
+ { "Otilde", 0xD5, 0x00D5, "O" },
+ { "Ouml", 0xD6, 0x00D6, "O" },
+ { "times", 0xD7, 0x00D7 },
+ { "Oslash", 0xD8, 0x00D8, "OE" },
+ { "Ugrave", 0xD9, 0x00D9, "U" },
+ { "Uacute", 0xDA, 0x00DA, "U" },
+ { "Ucirc", 0xDB, 0x00DB, "U" },
+ { "Uuml", 0xDC, 0x00DC, "U" },
+ { "Yacute", 0xDD, 0x00DD, "Y" },
+ { "THORN", 0xDE, 0x00DE, "TH" },
+ { "szlig", 0xDF, 0x00DF, "s" },
+ { "agrave", 0xE0, 0x00E0, "a" },
+ { "aacute", 0xE1, 0x00E1, "a" },
+ { "acirc", 0xE2, 0x00E2, "a" },
+ { "atilde", 0xE3, 0x00E3, "a" },
+ { "auml", 0xE4, 0x00E4, "a" },
+ { "aring", 0xE5, 0x00E5, "aa" },
+ { "aelig", 0xE6, 0x00E6, "ae" },
+ { "ccedil", 0xE7, 0x00E7, "c" },
+ { "egrave", 0xE8, 0x00E8, "e" },
+ { "eacute", 0xE9, 0x00E9, "e" },
+ { "ecirc", 0xEA, 0x00EA, "e" },
+ { "euml", 0xEB, 0x00EB, "e" },
+ { "igrave", 0xEC, 0x00EC, "i" },
+ { "iacute", 0xED, 0x00ED, "i" },
+ { "icirc", 0xEE, 0x00EE, "i" },
+ { "iuml", 0xEF, 0x00EF, "i" },
+ { "eth", 0xF0, 0x00F0, "dh" },
+ { "ntilde", 0xF1, 0x00F1, "n" },
+ { "ograve", 0xF2, 0x00F2, "o"},
+ { "oacute", 0xF3, 0x00F3, "o" },
+ { "ocirc", 0xF4, 0x00F4, "o" },
+ { "otilde", 0xF5, 0x00F5, "o" },
+ { "ouml", 0xF6, 0x00F6, "o" },
+ { "divide", 0xF7, 0x00F7 },
+ { "oslash", 0xF8, 0x00F8, "oe" },
+ { "ugrave", 0xF9, 0x00F9, "u" },
+ { "uacute", 0xFA, 0x00FA, "u" },
+ { "ucirc", 0xFB, 0x00FB, "u" },
+ { "uuml", 0xFC, 0x00FC, "u" },
+ { "yacute", 0xFD, 0x00FD, "y" },
+ { "thorn", 0xFE, 0x00FE, "th" },
+ { "yuml", 0xFF, 0x00FF, "y" },
+ { NULL, 0, 0 }
+};
+
+
+/* ISO 8859-15, also known as Latin 9, differs from Latin 1 in only a
+ few positions. http://www.cs.tut.fi/~jkorpela/latin9.html has a good
+ explanation and listing, summarized here. The names here are
+ abbreviated to fit in a decent line length.
+
+ code position
+ dec oct hex latin1 latin1 name latin9 latin9 name
+
+ 164 0244 0xA4 U+00A4 currency symbol U+20AC euro sign
+ 166 0246 0xA6 U+00A6 broken bar U+0160 S with caron
+ 168 0250 0xA8 U+00A8 diaeresis U+0161 s with caron
+ 180 0264 0xB4 U+00B4 acute accent U+017D Z with caron
+ 184 0270 0xB8 U+00B8 cedilla U+017E z with caron
+ 188 0274 0xBC U+00BC fraction 1/4 U+0152 ligature OE
+ 189 0275 0xBD U+00BD fraction 1/2 U+0153 ligature oe
+ 190 0276 0xBE U+00BE fraction 3/4 U+0178 Y with diaeresis
+*/
+
+static iso_map_type iso8859_15_map [] = {
+ { "nbsp", 0xA0, 0x00A0 },
+ { "iexcl", 0xA1, 0x00A1 },
+ { "cent", 0xA2, 0x00A2 },
+ { "pound", 0xA3, 0x00A3 },
+ { "euro", 0xA4, 0x20AC },
+ { "yen", 0xA5, 0x00A5 },
+ { "Scaron", 0xA6, 0x0160, "S" },
+ { "sect", 0xA7, 0x00A7 },
+ { "scaron", 0xA8, 0x0161, "s" },
+ { "copy", 0xA9, 0x00A9 },
+ { "ordf", 0xAA, 0x00AA },
+ { "laquo", 0xAB, 0x00AB },
+ { "not", 0xAC, 0x00AC },
+ { "shy", 0xAD, 0x00AD },
+ { "reg", 0xAE, 0x00AE },
+ { "hibar", 0xAF, 0x00AF },
+ { "deg", 0xB0, 0x00B0 },
+ { "plusmn", 0xB1, 0x00B1 },
+ { "sup2", 0xB2, 0x00B2 },
+ { "sup3", 0xB3, 0x00B3 },
+ { "Zcaron", 0xB4, 0x017D, "Z" },
+ { "micro", 0xB5, 0x00B5 },
+ { "para", 0xB6, 0x00B6 },
+ { "middot", 0xB7, 0x00B7 },
+ { "zcaron", 0xB8, 0x017E, "z" },
+ { "sup1", 0xB9, 0x00B9 },
+ { "ordm", 0xBA, 0x00BA },
+ { "raquo", 0xBB, 0x00BB },
+ { "OElig", 0xBC, 0x0152, "OE" },
+ { "oelig", 0xBD, 0x0153, "oe" },
+ { "Yuml", 0xBE, 0x0178, "y" },
+ { "iquest", 0xBF, 0x00BF },
+ { "Agrave", 0xC0, 0x00C0, "A" },
+ { "Aacute", 0xC1, 0x00C1, "A" },
+ { "Acirc", 0xC2, 0x00C2, "A" },
+ { "Atilde", 0xC3, 0x00C3, "A" },
+ { "Auml", 0xC4, 0x00C4, "A" },
+ { "Aring", 0xC5, 0x00C5, "AA" },
+ { "AElig", 0xC6, 0x00C6, "AE" },
+ { "Ccedil", 0xC7, 0x00C7, "C" },
+ { "Egrave", 0xC8, 0x00C8, "E" },
+ { "Eacute", 0xC9, 0x00C9, "E" },
+ { "Ecirc", 0xCA, 0x00CA, "E" },
+ { "Euml", 0xCB, 0x00CB, "E" },
+ { "Igrave", 0xCC, 0x00CC, "I" },
+ { "Iacute", 0xCD, 0x00CD, "I" },
+ { "Icirc", 0xCE, 0x00CE, "I" },
+ { "Iuml", 0xCF, 0x00CF, "I" },
+ { "ETH", 0xD0, 0x00D0, "DH" },
+ { "Ntilde", 0xD1, 0x00D1, "N" },
+ { "Ograve", 0xD2, 0x00D2, "O" },
+ { "Oacute", 0xD3, 0x00D3, "O" },
+ { "Ocirc", 0xD4, 0x00D4, "O" },
+ { "Otilde", 0xD5, 0x00D5, "O" },
+ { "Ouml", 0xD6, 0x00D6, "O" },
+ { "times", 0xD7, 0x00D7 },
+ { "Oslash", 0xD8, 0x00D8, "OE" },
+ { "Ugrave", 0xD9, 0x00D9, "U" },
+ { "Uacute", 0xDA, 0x00DA, "U" },
+ { "Ucirc", 0xDB, 0x00DB, "U" },
+ { "Uuml", 0xDC, 0x00DC, "U" },
+ { "Yacute", 0xDD, 0x00DD, "Y" },
+ { "THORN", 0xDE, 0x00DE, "TH" },
+ { "szlig", 0xDF, 0x00DF, "s" },
+ { "agrave", 0xE0, 0x00E0, "a" },
+ { "aacute", 0xE1, 0x00E1, "a" },
+ { "acirc", 0xE2, 0x00E2, "a" },
+ { "atilde", 0xE3, 0x00E3, "a" },
+ { "auml", 0xE4, 0x00E4, "a" },
+ { "aring", 0xE5, 0x00E5, "aa" },
+ { "aelig", 0xE6, 0x00E6, "ae" },
+ { "ccedil", 0xE7, 0x00E7, "c" },
+ { "egrave", 0xE8, 0x00E8, "e" },
+ { "eacute", 0xE9, 0x00E9, "e" },
+ { "ecirc", 0xEA, 0x00EA, "e" },
+ { "euml", 0xEB, 0x00EB, "e" },
+ { "igrave", 0xEC, 0x00EC, "i" },
+ { "iacute", 0xED, 0x00ED, "i" },
+ { "icirc", 0xEE, 0x00EE, "i" },
+ { "iuml", 0xEF, 0x00EF, "i" },
+ { "eth", 0xF0, 0x00F0, "d" },
+ { "ntilde", 0xF1, 0x00F1, "n" },
+ { "ograve", 0xF2, 0x00F2, "o" },
+ { "oacute", 0xF3, 0x00F3, "o" },
+ { "ocirc", 0xF4, 0x00F4, "o" },
+ { "otilde", 0xF5, 0x00F5, "o" },
+ { "ouml", 0xF6, 0x00F6, "o" },
+ { "divide", 0xF7, 0x00F7 },
+ { "oslash", 0xF8, 0x00F8, "oe" },
+ { "ugrave", 0xF9, 0x00F9, "u" },
+ { "uacute", 0xFA, 0x00FA, "u" },
+ { "ucirc", 0xFB, 0x00FB, "u" },
+ { "uuml", 0xFC, 0x00FC, "u" },
+ { "yacute", 0xFD, 0x00FD, "y" },
+ { "thorn", 0xFE, 0x00FE, "th" },
+ { "yuml", 0xFF, 0x00FF, "y" },
+ { NULL, 0, 0 }
+};
+
+
+
+/* Date: Mon, 31 Mar 2003 00:19:28 +0200
+ From: Wojciech Polak <polak@gnu.org>
+...
+ * Primary Polish site for ogonki is http://www.agh.edu.pl/ogonki/,
+ but it's only in Polish language (it has some interesting links).
+
+ * A general site about ISO 8859-2 at http://nl.ijs.si/gnusl/cee/iso8859-2.html
+
+ * ISO 8859-2 Character Set at http://nl.ijs.si/gnusl/cee/charset.html
+ This site provides almost all information about iso-8859-2,
+ including the character table!!! (must see!)
+
+ * ISO 8859-2 and even HTML entities !!! (must see!)
+ 88592.txt in the GNU enscript distribution
+
+ * (minor) http://www.agh.edu.pl/ogonki/plchars.html
+ One more table, this time it includes even information about Polish
+ characters in Unicode.
+*/
+
+static iso_map_type iso8859_2_map [] = {
+ { "nbsp", 0xA0, 0x00A0 }, /* NO-BREAK SPACE */
+ { "", 0xA1, 0x0104, "A" }, /* LATIN CAPITAL LETTER A WITH OGONEK */
+ { "", 0xA2, 0x02D8 }, /* BREVE */
+ { "", 0xA3, 0x0141, "L" }, /* LATIN CAPITAL LETTER L WITH STROKE */
+ { "curren", 0xA4, 0x00A4 }, /* CURRENCY SIGN */
+ { "", 0xA5, 0x013D, "L" }, /* LATIN CAPITAL LETTER L WITH CARON */
+ { "", 0xA6, 0x015A, "S" }, /* LATIN CAPITAL LETTER S WITH ACUTE */
+ { "sect", 0xA7, 0x00A7 }, /* SECTION SIGN */
+ { "uml", 0xA8, 0x00A8 }, /* DIAERESIS */
+ { "#xa9", 0xA9, 0x0160, "S" }, /* LATIN CAPITAL LETTER S WITH CARON */
+ { "", 0xAA, 0x015E, "S" }, /* LATIN CAPITAL LETTER S WITH CEDILLA */
+ { "", 0xAB, 0x0164, "T" }, /* LATIN CAPITAL LETTER T WITH CARON */
+ { "", 0xAC, 0x0179, "Z" }, /* LATIN CAPITAL LETTER Z WITH ACUTE */
+ { "shy", 0xAD, 0x00AD }, /* SOFT HYPHEN */
+ { "", 0xAE, 0x017D, "Z" }, /* LATIN CAPITAL LETTER Z WITH CARON */
+ { "", 0xAF, 0x017B, "Z" }, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */
+ { "deg", 0xB0, 0x00B0 }, /* DEGREE SIGN */
+ { "", 0xB1, 0x0105, "a" }, /* LATIN SMALL LETTER A WITH OGONEK */
+ { "", 0xB2, 0x02DB }, /* OGONEK */
+ { "", 0xB3, 0x0142, "l" }, /* LATIN SMALL LETTER L WITH STROKE */
+ { "acute", 0xB4, 0x00B4 }, /* ACUTE ACCENT */
+ { "", 0xB5, 0x013E, "l" }, /* LATIN SMALL LETTER L WITH CARON */
+ { "", 0xB6, 0x015B, "s" }, /* LATIN SMALL LETTER S WITH ACUTE */
+ { "", 0xB7, 0x02C7 }, /* CARON (Mandarin Chinese third tone) */
+ { "cedil", 0xB8, 0x00B8 }, /* CEDILLA */
+ { "", 0xB9, 0x0161, "s" }, /* LATIN SMALL LETTER S WITH CARON */
+ { "", 0xBA, 0x015F, "s" }, /* LATIN SMALL LETTER S WITH CEDILLA */
+ { "", 0xBB, 0x0165, "t" }, /* LATIN SMALL LETTER T WITH CARON */
+ { "", 0xBC, 0x017A, "z" }, /* LATIN SMALL LETTER Z WITH ACUTE */
+ { "", 0xBD, 0x02DD }, /* DOUBLE ACUTE ACCENT */
+ { "", 0xBE, 0x017E, "z" }, /* LATIN SMALL LETTER Z WITH CARON */
+ { "", 0xBF, 0x017C, "z" }, /* LATIN SMALL LETTER Z WITH DOT ABOVE */
+ { "", 0xC0, 0x0154, "R" }, /* LATIN CAPITAL LETTER R WITH ACUTE */
+ { "Aacute", 0xC1, 0x00C1, "A" }, /* LATIN CAPITAL LETTER A WITH ACUTE */
+ { "Acirc", 0xC2, 0x00C2, "A" }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
+ { "", 0xC3, 0x0102, "A" }, /* LATIN CAPITAL LETTER A WITH BREVE */
+ { "Auml", 0xC4, 0x00C4, "A" }, /* LATIN CAPITAL LETTER A WITH DIAERESIS */
+ { "", 0xC5, 0x0139, "L" }, /* LATIN CAPITAL LETTER L WITH ACUTE */
+ { "", 0xC6, 0x0106, "C" }, /* LATIN CAPITAL LETTER C WITH ACUTE */
+ { "Ccedil", 0xC7, 0x00C7, "C" }, /* LATIN CAPITAL LETTER C WITH CEDILLA */
+ { "", 0xC8, 0x010C, "C" }, /* LATIN CAPITAL LETTER C WITH CARON */
+ { "Eacute", 0xC9, 0x00C9, "E" }, /* LATIN CAPITAL LETTER E WITH ACUTE */
+ { "", 0xCA, 0x0118, "E" }, /* LATIN CAPITAL LETTER E WITH OGONEK */
+ { "Euml", 0xCB, 0x00CB, "E" }, /* LATIN CAPITAL LETTER E WITH DIAERESIS */
+ { "", 0xCC, 0x011A, "E" }, /* LATIN CAPITAL LETTER E WITH CARON */
+ { "Iacute", 0xCD, 0x00CD, "I" }, /* LATIN CAPITAL LETTER I WITH ACUTE */
+ { "Icirc", 0xCE, 0x00CE, "I" }, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
+ { "", 0xCF, 0x010E, "D" }, /* LATIN CAPITAL LETTER D WITH CARON */
+ { "ETH", 0xD0, 0x0110, "D" }, /* LATIN CAPITAL LETTER D WITH STROKE */
+ { "", 0xD1, 0x0143, "N" }, /* LATIN CAPITAL LETTER N WITH ACUTE */
+ { "", 0xD2, 0x0147, "N" }, /* LATIN CAPITAL LETTER N WITH CARON */
+ { "Oacute", 0xD3, 0x00D3, "O" }, /* LATIN CAPITAL LETTER O WITH ACUTE */
+ { "Ocirc", 0xD4, 0x00D4, "O" }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
+ { "", 0xD5, 0x0150, "O" }, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
+ { "Ouml", 0xD6, 0x00D6, "O" }, /* LATIN CAPITAL LETTER O WITH DIAERESIS */
+ { "times", 0xD7, 0x00D7 }, /* MULTIPLICATION SIGN */
+ { "", 0xD8, 0x0158, "R" }, /* LATIN CAPITAL LETTER R WITH CARON */
+ { "", 0xD9, 0x016E, "U" }, /* LATIN CAPITAL LETTER U WITH RING ABOVE */
+ { "Uacute", 0xDA, 0x00DA, "U" }, /* LATIN CAPITAL LETTER U WITH ACUTE */
+ { "", 0xDB, 0x0170, "U" }, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
+ { "Uuml", 0xDC, 0x00DC, "U" }, /* LATIN CAPITAL LETTER U WITH DIAERESIS */
+ { "Yacute", 0xDD, 0x00DD, "Y" }, /* LATIN CAPITAL LETTER Y WITH ACUTE */
+ { "", 0xDE, 0x0162, "T" }, /* LATIN CAPITAL LETTER T WITH CEDILLA */
+ { "szlig", 0xDF, 0x00DF, "ss" }, /* LATIN SMALL LETTER SHARP S (German) */
+ { "", 0xE0, 0x0155, "s" }, /* LATIN SMALL LETTER R WITH ACUTE */
+ { "aacute", 0xE1, 0x00E1, "a" }, /* LATIN SMALL LETTER A WITH ACUTE */
+ { "acirc", 0xE2, 0x00E2, "a" }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */
+ { "", 0xE3, 0x0103, "a" }, /* LATIN SMALL LETTER A WITH BREVE */
+ { "auml", 0xE4, 0x00E4, "a" }, /* LATIN SMALL LETTER A WITH DIAERESIS */
+ { "", 0xE5, 0x013A, "l" }, /* LATIN SMALL LETTER L WITH ACUTE */
+ { "", 0xE6, 0x0107, "c" }, /* LATIN SMALL LETTER C WITH ACUTE */
+ { "ccedil", 0xE7, 0x00E7, "c" }, /* LATIN SMALL LETTER C WITH CEDILLA */
+ { "", 0xE8, 0x010D, "c" }, /* LATIN SMALL LETTER C WITH CARON */
+ { "eacute", 0xE9, 0x00E9, "e" }, /* LATIN SMALL LETTER E WITH ACUTE */
+ { "", 0xEA, 0x0119, "e" }, /* LATIN SMALL LETTER E WITH OGONEK */
+ { "euml", 0xEB, 0x00EB, "e" }, /* LATIN SMALL LETTER E WITH DIAERESIS */
+ { "", 0xEC, 0x011B, "e" }, /* LATIN SMALL LETTER E WITH CARON */
+ { "iacute", 0xED, 0x00ED, "i" }, /* LATIN SMALL LETTER I WITH ACUTE */
+ { "icirc", 0xEE, 0x00EE, "i" }, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */
+ { "", 0xEF, 0x010F, "d" }, /* LATIN SMALL LETTER D WITH CARON */
+ { "", 0xF0, 0x0111, "d" }, /* LATIN SMALL LETTER D WITH STROKE */
+ { "", 0xF1, 0x0144, "n" }, /* LATIN SMALL LETTER N WITH ACUTE */
+ { "", 0xF2, 0x0148, "n" }, /* LATIN SMALL LETTER N WITH CARON */
+ { "oacute", 0xF3, 0x00F3, "o" }, /* LATIN SMALL LETTER O WITH ACUTE */
+ { "ocirc", 0xF4, 0x00F4, "o" }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */
+ { "", 0xF5, 0x0151, "o" }, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */
+ { "ouml", 0xF6, 0x00F6, "o" }, /* LATIN SMALL LETTER O WITH DIAERESIS */
+ { "divide", 0xF7, 0x00F7 }, /* DIVISION SIGN */
+ { "", 0xF8, 0x0159, "r" }, /* LATIN SMALL LETTER R WITH CARON */
+ { "", 0xF9, 0x016F, "u" }, /* LATIN SMALL LETTER U WITH RING ABOVE */
+ { "uacute", 0xFA, 0x00FA, "u" }, /* LATIN SMALL LETTER U WITH ACUTE */
+ { "", 0xFB, 0x0171, "u" }, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */
+ { "uuml", 0xFC, 0x00FC, "u" }, /* LATIN SMALL LETTER U WITH DIAERESIS */
+ { "yacute", 0xFD, 0x00FD, "y" }, /* LATIN SMALL LETTER Y WITH ACUTE */
+ { "", 0xFE, 0x0163, "t" }, /* LATIN SMALL LETTER T WITH CEDILLA */
+ { "", 0xFF, 0x02D9 }, /* DOT ABOVE (Mandarin Chinese light tone) */
+ { NULL, 0, 0 }
+};
+
+/* Common map for koi8-u, koi8-r */
+static iso_map_type koi8_map [] = {
+ { "", 0xa3, 0x0415, "io"}, /* CYRILLIC SMALL LETTER IO */
+ { "", 0xa4, 0x0454, "ie"}, /* CYRILLIC SMALL LETTER UKRAINIAN IE */
+ { "", 0xa6, 0x0456, "i"}, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
+ { "", 0xa7, 0x0457, "yi"}, /* CYRILLIC SMALL LETTER YI */
+
+ { "", 0xb3, 0x04d7, "IO"}, /* CYRILLIC CAPITAL LETTER IO */
+ { "", 0xb4, 0x0404, "IE"}, /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */
+ { "", 0xb6, 0x0406, "I"}, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
+ { "", 0xb7, 0x0407, "YI"}, /* CYRILLIC CAPITAL LETTER YI */
+/* { "", 0xbf, 0x}, / * CYRILLIC COPYRIGHT SIGN */
+ { "", 0xc0, 0x042e, "yu"}, /* CYRILLIC SMALL LETTER YU */
+ { "", 0xc1, 0x0430, "a"}, /* CYRILLIC SMALL LETTER A */
+ { "", 0xc2, 0x0431, "b"}, /* CYRILLIC SMALL LETTER BE */
+ { "", 0xc3, 0x0446, "c"}, /* CYRILLIC SMALL LETTER TSE */
+ { "", 0xc4, 0x0434, "d"}, /* CYRILLIC SMALL LETTER DE */
+ { "", 0xc5, 0x0435, "e"}, /* CYRILLIC SMALL LETTER IE */
+ { "", 0xc6, 0x0444, "f"}, /* CYRILLIC SMALL LETTER EF */
+ { "", 0xc7, 0x0433, "g"}, /* CYRILLIC SMALL LETTER GHE */
+ { "", 0xc8, 0x0445, "h"}, /* CYRILLIC SMALL LETTER HA */
+ { "", 0xc9, 0x0438, "i"}, /* CYRILLIC SMALL LETTER I */
+ { "", 0xca, 0x0439, "i"}, /* CYRILLIC SMALL LETTER SHORT I */
+ { "", 0xcb, 0x043a, "k"}, /* CYRILLIC SMALL LETTER KA */
+ { "", 0xcc, 0x043b, "l"}, /* CYRILLIC SMALL LETTER EL */
+ { "", 0xcd, 0x043c, "m"}, /* CYRILLIC SMALL LETTER EM */
+ { "", 0xce, 0x043d, "n"}, /* CYRILLIC SMALL LETTER EN */
+ { "", 0xcf, 0x043e, "o"}, /* CYRILLIC SMALL LETTER O */
+ { "", 0xd0, 0x043f, "p"}, /* CYRILLIC SMALL LETTER PE */
+ { "", 0xd1, 0x044f, "ya"}, /* CYRILLIC SMALL LETTER YA */
+ { "", 0xd2, 0x0440, "r"}, /* CYRILLIC SMALL LETTER ER */
+ { "", 0xd3, 0x0441, "s"}, /* CYRILLIC SMALL LETTER ES */
+ { "", 0xd4, 0x0442, "t"}, /* CYRILLIC SMALL LETTER TE */
+ { "", 0xd5, 0x0443, "u"}, /* CYRILLIC SMALL LETTER U */
+ { "", 0xd6, 0x0436, "zh"}, /* CYRILLIC SMALL LETTER ZHE */
+ { "", 0xd7, 0x0432, "v"}, /* CYRILLIC SMALL LETTER VE */
+ { "", 0xd8, 0x044c, "x"}, /* CYRILLIC SMALL LETTER SOFT SIGN */
+ { "", 0xd9, 0x044b, "y"}, /* CYRILLIC SMALL LETTER YERU */
+ { "", 0xda, 0x0437, "z"}, /* CYRILLIC SMALL LETTER ZE */
+ { "", 0xdb, 0x0448, "sh"}, /* CYRILLIC SMALL LETTER SHA */
+ { "", 0xdc, 0x044d, "e"}, /* CYRILLIC SMALL LETTER E */
+ { "", 0xdd, 0x0449, "shch"}, /* CYRILLIC SMALL LETTER SHCHA */
+ { "", 0xde, 0x0447, "ch"}, /* CYRILLIC SMALL LETTER CHA */
+ { "", 0xdf, 0x044a, "w"}, /* CYRILLIC SMALL LETTER HARD SIGN */
+ { "", 0xe0, 0x042d, "YU"}, /* CYRILLIC CAPITAL LETTER YU */
+ { "", 0xe1, 0x0410, "A"}, /* CYRILLIC CAPITAL LETTER A */
+ { "", 0xe2, 0x0411, "B"}, /* CYRILLIC CAPITAL LETTER BE */
+ { "", 0xe3, 0x0426, "C"}, /* CYRILLIC CAPITAL LETTER TSE */
+ { "", 0xe4, 0x0414, "D"}, /* CYRILLIC CAPITAL LETTER DE */
+ { "", 0xe5, 0x0415, "E"}, /* CYRILLIC CAPITAL LETTER IE */
+ { "", 0xe6, 0x0424, "F"}, /* CYRILLIC CAPITAL LETTER EF */
+ { "", 0xe7, 0x0413, "G"}, /* CYRILLIC CAPITAL LETTER GHE */
+ { "", 0xe8, 0x0425, "H"}, /* CYRILLIC CAPITAL LETTER HA */
+ { "", 0xe9, 0x0418, "I"}, /* CYRILLIC CAPITAL LETTER I */
+ { "", 0xea, 0x0419, "I"}, /* CYRILLIC CAPITAL LETTER SHORT I */
+ { "", 0xeb, 0x041a, "K"}, /* CYRILLIC CAPITAL LETTER KA */
+ { "", 0xec, 0x041b, "L"}, /* CYRILLIC CAPITAL LETTER EL */
+ { "", 0xed, 0x041c, "M"}, /* CYRILLIC CAPITAL LETTER EM */
+ { "", 0xee, 0x041d, "N"}, /* CYRILLIC CAPITAL LETTER EN */
+ { "", 0xef, 0x041e, "O"}, /* CYRILLIC CAPITAL LETTER O */
+ { "", 0xf0, 0x041f, "P"}, /* CYRILLIC CAPITAL LETTER PE */
+ { "", 0xf1, 0x042f, "YA"}, /* CYRILLIC CAPITAL LETTER YA */
+ { "", 0xf2, 0x0420, "R"}, /* CYRILLIC CAPITAL LETTER ER */
+ { "", 0xf3, 0x0421, "S"}, /* CYRILLIC CAPITAL LETTER ES */
+ { "", 0xf4, 0x0422, "T"}, /* CYRILLIC CAPITAL LETTER TE */
+ { "", 0xf5, 0x0423, "U"}, /* CYRILLIC CAPITAL LETTER U */
+ { "", 0xf6, 0x0416, "ZH"}, /* CYRILLIC CAPITAL LETTER ZHE */
+ { "", 0xf7, 0x0412, "V"}, /* CYRILLIC CAPITAL LETTER VE */
+ { "", 0xf8, 0x042c, "X"}, /* CYRILLIC CAPITAL LETTER SOFT SIGN */
+ { "", 0xf9, 0x042b, "Y"}, /* CYRILLIC CAPITAL LETTER YERU */
+ { "", 0xfa, 0x0417, "Z"}, /* CYRILLIC CAPITAL LETTER ZE */
+ { "", 0xfb, 0x0428, "SH"}, /* CYRILLIC CAPITAL LETTER SHA */
+ { "", 0xfc, 0x042d, "E"}, /* CYRILLIC CAPITAL LETTER E */
+ { "", 0xfd, 0x0429, "SHCH"}, /* CYRILLIC CAPITAL LETTER SHCHA */
+ { "", 0xfe, 0x0427, "CH"}, /* CYRILLIC CAPITAL LETTER CHE */
+ { "", 0xff, 0x042a, "W"}, /* CYRILLIC CAPITAL LETTER HARD SIGN */
+ { NULL, 0, 0 }
+};
+
+encoding_type encoding_table[] = {
+ { no_encoding, "(no encoding)", NULL },
+ { US_ASCII, "US-ASCII", asis_map },
+ { ISO_8859_1, "iso-8859-1", (iso_map_type *) iso8859_1_map },
+ { ISO_8859_2, "iso-8859-2", (iso_map_type *) iso8859_2_map },
+ { ISO_8859_3, "iso-8859-3", NULL },
+ { ISO_8859_4, "iso-8859-4", NULL },
+ { ISO_8859_5, "iso-8859-5", NULL },
+ { ISO_8859_6, "iso-8859-6", NULL },
+ { ISO_8859_7, "iso-8859-7", NULL },
+ { ISO_8859_8, "iso-8859-8", NULL },
+ { ISO_8859_9, "iso-8859-9", NULL },
+ { ISO_8859_10, "iso-8859-10", NULL },
+ { ISO_8859_11, "iso-8859-11", NULL },
+ { ISO_8859_12, "iso-8859-12", NULL },
+ { ISO_8859_13, "iso-8859-13", NULL },
+ { ISO_8859_14, "iso-8859-14", NULL },
+ { ISO_8859_15, "iso-8859-15", (iso_map_type *) iso8859_15_map },
+ { KOI8_R, "koi8-r", (iso_map_type *) koi8_map },
+ { KOI8_U, "koi8-u", (iso_map_type *) koi8_map },
+ { UTF_8, "utf-8", asis_map }, /* specific code for this below */
+ { last_encoding_code, NULL, NULL }
+};
+
+
+/* List of HTML entities. */
+static struct { const char *html; unsigned int unicode; } unicode_map[] = {
+/* Extracted from http://www.w3.org/TR/html401/sgml/entities.html through
+ sed -n -e 's|<!ENTITY \([^ ][^ ]*\) *CDATA "[&]#\([0-9][0-9]*\);".*| { "\1", \2 },|p'
+ | LC_ALL=C sort -k2 */
+ { "AElig", 198 },
+ { "Aacute", 193 },
+ { "Acirc", 194 },
+ { "Agrave", 192 },
+ { "Alpha", 913 },
+ { "Aring", 197 },
+ { "Atilde", 195 },
+ { "Auml", 196 },
+ { "Beta", 914 },
+ { "Ccedil", 199 },
+ { "Chi", 935 },
+ { "Dagger", 8225 },
+ { "Delta", 916 },
+ { "ETH", 208 },
+ { "Eacute", 201 },
+ { "Ecirc", 202 },
+ { "Egrave", 200 },
+ { "Epsilon", 917 },
+ { "Eta", 919 },
+ { "Euml", 203 },
+ { "Gamma", 915 },
+ { "Iacute", 205 },
+ { "Icirc", 206 },
+ { "Igrave", 204 },
+ { "Iota", 921 },
+ { "Iuml", 207 },
+ { "Kappa", 922 },
+ { "Lambda", 923 },
+ { "Mu", 924 },
+ { "Ntilde", 209 },
+ { "Nu", 925 },
+ { "OElig", 338 },
+ { "Oacute", 211 },
+ { "Ocirc", 212 },
+ { "Ograve", 210 },
+ { "Omega", 937 },
+ { "Omicron", 927 },
+ { "Oslash", 216 },
+ { "Otilde", 213 },
+ { "Ouml", 214 },
+ { "Phi", 934 },
+ { "Pi", 928 },
+ { "Prime", 8243 },
+ { "Psi", 936 },
+ { "Rho", 929 },
+ { "Scaron", 352 },
+ { "Sigma", 931 },
+ { "THORN", 222 },
+ { "Tau", 932 },
+ { "Theta", 920 },
+ { "Uacute", 218 },
+ { "Ucirc", 219 },
+ { "Ugrave", 217 },
+ { "Upsilon", 933 },
+ { "Uuml", 220 },
+ { "Xi", 926 },
+ { "Yacute", 221 },
+ { "Yuml", 376 },
+ { "Zeta", 918 },
+ { "aacute", 225 },
+ { "acirc", 226 },
+ { "acute", 180 },
+ { "aelig", 230 },
+ { "agrave", 224 },
+ { "alefsym", 8501 },
+ { "alpha", 945 },
+ { "amp", 38 },
+ { "and", 8743 },
+ { "ang", 8736 },
+ { "aring", 229 },
+ { "asymp", 8776 },
+ { "atilde", 227 },
+ { "auml", 228 },
+ { "bdquo", 8222 },
+ { "beta", 946 },
+ { "brvbar", 166 },
+ { "bull", 8226 },
+ { "cap", 8745 },
+ { "ccedil", 231 },
+ { "cedil", 184 },
+ { "cent", 162 },
+ { "chi", 967 },
+ { "circ", 710 },
+ { "clubs", 9827 },
+ { "cong", 8773 },
+ { "copy", 169 },
+ { "crarr", 8629 },
+ { "cup", 8746 },
+ { "curren", 164 },
+ { "dArr", 8659 },
+ { "dagger", 8224 },
+ { "darr", 8595 },
+ { "deg", 176 },
+ { "delta", 948 },
+ { "diams", 9830 },
+ { "divide", 247 },
+ { "eacute", 233 },
+ { "ecirc", 234 },
+ { "egrave", 232 },
+ { "empty", 8709 },
+ { "emsp", 8195 },
+ { "ensp", 8194 },
+ { "epsilon", 949 },
+ { "equiv", 8801 },
+ { "eta", 951 },
+ { "eth", 240 },
+ { "euml", 235 },
+ { "euro", 8364 },
+ { "exist", 8707 },
+ { "fnof", 402 },
+ { "forall", 8704 },
+ { "frac12", 189 },
+ { "frac14", 188 },
+ { "frac34", 190 },
+ { "frasl", 8260 },
+ { "gamma", 947 },
+ { "ge", 8805 },
+ { "gt", 62 },
+ { "hArr", 8660 },
+ { "harr", 8596 },
+ { "hearts", 9829 },
+ { "hellip", 8230 },
+ { "iacute", 237 },
+ { "icirc", 238 },
+ { "iexcl", 161 },
+ { "igrave", 236 },
+ { "image", 8465 },
+ { "infin", 8734 },
+ { "int", 8747 },
+ { "iota", 953 },
+ { "iquest", 191 },
+ { "isin", 8712 },
+ { "iuml", 239 },
+ { "kappa", 954 },
+ { "lArr", 8656 },
+ { "lambda", 955 },
+ { "lang", 9001 },
+ { "laquo", 171 },
+ { "larr", 8592 },
+ { "lceil", 8968 },
+ { "ldquo", 8220 },
+ { "le", 8804 },
+ { "lfloor", 8970 },
+ { "lowast", 8727 },
+ { "loz", 9674 },
+ { "lrm", 8206 },
+ { "lsaquo", 8249 },
+ { "lsquo", 8216 },
+ { "lt", 60 },
+ { "macr", 175 },
+ { "mdash", 8212 },
+ { "micro", 181 },
+ { "middot", 183 },
+ { "minus", 8722 },
+ { "mu", 956 },
+ { "nabla", 8711 },
+ { "nbsp", 160 },
+ { "ndash", 8211 },
+ { "ne", 8800 },
+ { "ni", 8715 },
+ { "not", 172 },
+ { "notin", 8713 },
+ { "nsub", 8836 },
+ { "ntilde", 241 },
+ { "nu", 957 },
+ { "oacute", 243 },
+ { "ocirc", 244 },
+ { "oelig", 339 },
+ { "ograve", 242 },
+ { "oline", 8254 },
+ { "omega", 969 },
+ { "omicron", 959 },
+ { "oplus", 8853 },
+ { "or", 8744 },
+ { "ordf", 170 },
+ { "ordm", 186 },
+ { "oslash", 248 },
+ { "otilde", 245 },
+ { "otimes", 8855 },
+ { "ouml", 246 },
+ { "para", 182 },
+ { "part", 8706 },
+ { "permil", 8240 },
+ { "perp", 8869 },
+ { "phi", 966 },
+ { "pi", 960 },
+ { "piv", 982 },
+ { "plusmn", 177 },
+ { "pound", 163 },
+ { "prime", 8242 },
+ { "prod", 8719 },
+ { "prop", 8733 },
+ { "psi", 968 },
+ { "quot", 34 },
+ { "rArr", 8658 },
+ { "radic", 8730 },
+ { "rang", 9002 },
+ { "raquo", 187 },
+ { "rarr", 8594 },
+ { "rceil", 8969 },
+ { "rdquo", 8221 },
+ { "real", 8476 },
+ { "reg", 174 },
+ { "rfloor", 8971 },
+ { "rho", 961 },
+ { "rlm", 8207 },
+ { "rsaquo", 8250 },
+ { "rsquo", 8217 },
+ { "sbquo", 8218 },
+ { "scaron", 353 },
+ { "sdot", 8901 },
+ { "sect", 167 },
+ { "shy", 173 },
+ { "sigma", 963 },
+ { "sigmaf", 962 },
+ { "sim", 8764 },
+ { "spades", 9824 },
+ { "sub", 8834 },
+ { "sube", 8838 },
+ { "sum", 8721 },
+ { "sup", 8835 },
+ { "sup1", 185 },
+ { "sup2", 178 },
+ { "sup3", 179 },
+ { "supe", 8839 },
+ { "szlig", 223 },
+ { "tau", 964 },
+ { "there4", 8756 },
+ { "theta", 952 },
+ { "thetasym", 977 },
+ { "thinsp", 8201 },
+ { "thorn", 254 },
+ { "tilde", 732 },
+ { "times", 215 },
+ { "trade", 8482 },
+ { "uArr", 8657 },
+ { "uacute", 250 },
+ { "uarr", 8593 },
+ { "ucirc", 251 },
+ { "ugrave", 249 },
+ { "uml", 168 },
+ { "upsih", 978 },
+ { "upsilon", 965 },
+ { "uuml", 252 },
+ { "weierp", 8472 },
+ { "xi", 958 },
+ { "yacute", 253 },
+ { "yen", 165 },
+ { "yuml", 255 },
+ { "zeta", 950 },
+ { "zwj", 8205 },
+ { "zwnj", 8204 }
+};
+
+
+
+/* To update this list of language codes, download the current data from
+ http://www.loc.gov/standards/iso639-2; specifically,
+ http://www.loc.gov/standards/iso639-2/ISO-639-2_values_8bits-8559-1.txt.
+ Run cut -d\| -f 3,4 <ISO-639-2_values_8bits-8559-1.txt | sort -u >/tmp/639.2
+
+ Extract the C table below to a file, say /tmp/lc, and run:
+ cut -c 10- /tmp/lc| sed -e 's/", "/|/' -e 's,".*,,'>/tmp/lang.2
+
+ Then: comm -3 /tmp/639.2 /tmp/lang.c.
+
+ Also update the enum in lang.h. */
+
+language_type language_table[] = {
+ { aa, "aa", "Afar" },
+ { ab, "ab", "Abkhazian" },
+ { ae, "ae", "Avestan" },
+ { af, "af", "Afrikaans" },
+ { ak, "ak", "Akan" },
+ { am, "am", "Amharic" },
+ { an, "an", "Aragonese" },
+ { ar, "ar", "Arabic" },
+ { as, "as", "Assamese" },
+ { av, "av", "Avaric" },
+ { ay, "ay", "Aymara" },
+ { az, "az", "Azerbaijani" },
+ { ba, "ba", "Bashkir" },
+ { be, "be", "Belarusian" },
+ { bg, "bg", "Bulgarian" },
+ { bh, "bh", "Bihari" },
+ { bi, "bi", "Bislama" },
+ { bm, "bm", "Bambara" },
+ { bn, "bn", "Bengali" },
+ { bo, "bo", "Tibetan" },
+ { br, "br", "Breton" },
+ { bs, "bs", "Bosnian" },
+ { ca, "ca", "Catalan; Valencian" },
+ { ce, "ce", "Chechen" },
+ { ch, "ch", "Chamorro" },
+ { co, "co", "Corsican" },
+ { cr, "cr", "Cree" },
+ { cs, "cs", "Czech" },
+ { cu, "cu", "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" },
+ { cv, "cv", "Chuvash" },
+ { cy, "cy", "Welsh" },
+ { da, "da", "Danish" },
+ { de, "de", "German" },
+ { dv, "dv", "Divehi; Dhivehi; Maldivian" },
+ { dz, "dz", "Dzongkha" },
+ { ee, "ee", "Ewe" },
+ { el, "el", "Greek, Modern (1453-)" },
+ { en, "en", "English" },
+ { eo, "eo", "Esperanto" },
+ { es, "es", "Spanish; Castilian" },
+ { et, "et", "Estonian" },
+ { eu, "eu", "Basque" },
+ { fa, "fa", "Persian" },
+ { ff, "ff", "Fulah" },
+ { fi, "fi", "Finnish" },
+ { fj, "fj", "Fijian" },
+ { fo, "fo", "Faroese" },
+ { fr, "fr", "French" },
+ { fy, "fy", "Western Frisian" },
+ { ga, "ga", "Irish" },
+ { gd, "gd", "Gaelic; Scottish Gaelic" },
+ { gl, "gl", "Galician" },
+ { gn, "gn", "Guarani" },
+ { gu, "gu", "Gujarati" },
+ { gv, "gv", "Manx" },
+ { ha, "ha", "Hausa" },
+ { he, "he", "Hebrew" } /* (formerly iw) */,
+ { hi, "hi", "Hindi" },
+ { ho, "ho", "Hiri Motu" },
+ { hr, "hr", "Croatian" },
+ { ht, "ht", "Haitian; Haitian Creole" },
+ { hu, "hu", "Hungarian" },
+ { hy, "hy", "Armenian" },
+ { hz, "hz", "Herero" },
+ { ia, "ia", "Interlingua (International Auxiliary Language Association)" },
+ { id, "id", "Indonesian" } /* (formerly in) */,
+ { ie, "ie", "Interlingue" },
+ { ig, "ig", "Igbo" },
+ { ii, "ii", "Sichuan Yi" },
+ { ik, "ik", "Inupiaq" },
+ { io, "io", "Ido" },
+ { is, "is", "Icelandic" },
+ { it, "it", "Italian" },
+ { iu, "iu", "Inuktitut" },
+ { ja, "ja", "Japanese" },
+ { jv, "jv", "Javanese" },
+ { ka, "ka", "Georgian" },
+ { kg, "kg", "Kongo" },
+ { ki, "ki", "Kikuyu; Gikuyu" },
+ { kj, "kj", "Kuanyama; Kwanyama" },
+ { kk, "kk", "Kazakh" },
+ { kl, "kl", "Kalaallisut; Greenlandic" },
+ { km, "km", "Central Khmer" },
+ { kn, "kn", "Kannada" },
+ { ko, "ko", "Korean" },
+ { kr, "kr", "Kanuri" },
+ { ks, "ks", "Kashmiri" },
+ { ku, "ku", "Kurdish" },
+ { kv, "kv", "Komi" },
+ { kw, "kw", "Cornish" },
+ { ky, "ky", "Kirghiz; Kyrgyz" },
+ { la, "la", "Latin" },
+ { lb, "lb", "Luxembourgish; Letzeburgesch" },
+ { lg, "lg", "Ganda" },
+ { li, "li", "Limburgan; Limburger; Limburgish" },
+ { ln, "ln", "Lingala" },
+ { lo, "lo", "Lao" },
+ { lt, "lt", "Lithuanian" },
+ { lu, "lu", "Luba-Katanga" },
+ { lv, "lv", "Latvian" },
+ { mg, "mg", "Malagasy" },
+ { mh, "mh", "Marshallese" },
+ { mi, "mi", "Maori" },
+ { mk, "mk", "Macedonian" },
+ { ml, "ml", "Malayalam" },
+ { mn, "mn", "Mongolian" },
+ { mo, "mo", "Moldavian" },
+ { mr, "mr", "Marathi" },
+ { ms, "ms", "Malay" },
+ { mt, "mt", "Maltese" },
+ { my, "my", "Burmese" },
+ { na, "na", "Nauru" },
+ { nb, "nb", "Bokmål, Norwegian; Norwegian Bokmål" },
+ { nd, "nd", "Ndebele, North; North Ndebele" },
+ { ne, "ne", "Nepali" },
+ { ng, "ng", "Ndonga" },
+ { nl, "nl", "Dutch; Flemish" },
+ { nn, "nn", "Norwegian Nynorsk; Nynorsk, Norwegian" },
+ { no, "no", "Norwegian" },
+ { nr, "nr", "Ndebele, South; South Ndebele" },
+ { nv, "nv", "Navajo; Navaho" },
+ { ny, "ny", "Chichewa; Chewa; Nyanja" },
+ { oc, "oc", "Occitan (post 1500); Provençal" },
+ { oj, "oj", "Ojibwa" },
+ { om, "om", "Oromo" },
+ { or, "or", "Oriya" },
+ { os, "os", "Ossetian; Ossetic" },
+ { pa, "pa", "Panjabi; Punjabi" },
+ { pi, "pi", "Pali" },
+ { pl, "pl", "Polish" },
+ { ps, "ps", "Pushto" },
+ { pt, "pt", "Portuguese" },
+ { qu, "qu", "Quechua" },
+ { rm, "rm", "Romansh" },
+ { rn, "rn", "Rundi" },
+ { ro, "ro", "Romanian" },
+ { ru, "ru", "Russian" },
+ { rw, "rw", "Kinyarwanda" },
+ { sa, "sa", "Sanskrit" },
+ { sc, "sc", "Sardinian" },
+ { sd, "sd", "Sindhi" },
+ { se, "se", "Northern Sami" },
+ { sg, "sg", "Sango" },
+ { si, "si", "Sinhala; Sinhalese" },
+ { sk, "sk", "Slovak" },
+ { sl, "sl", "Slovenian" },
+ { sm, "sm", "Samoan" },
+ { sn, "sn", "Shona" },
+ { so, "so", "Somali" },
+ { sq, "sq", "Albanian" },
+ { sr, "sr", "Serbian" },
+ { ss, "ss", "Swati" },
+ { st, "st", "Sotho, Souther" },
+ { su, "su", "Sundanese" },
+ { sv, "sv", "Swedish" },
+ { sw, "sw", "Swahili" },
+ { ta, "ta", "Tamil" },
+ { te, "te", "Telugu" },
+ { tg, "tg", "Tajik" },
+ { th, "th", "Thai" },
+ { ti, "ti", "Tigrinya" },
+ { tk, "tk", "Turkmen" },
+ { tl, "tl", "Tagalog" },
+ { tn, "tn", "Tswana" },
+ { to, "to", "Tonga (Tonga Islands)" },
+ { tr, "tr", "Turkish" },
+ { ts, "ts", "Tsonga" },
+ { tt, "tt", "Tatar" },
+ { tw, "tw", "Twi" },
+ { ty, "ty", "Tahitian" },
+ { ug, "ug", "Uighur; Uyghur" },
+ { uk, "uk", "Ukrainian" },
+ { ur, "ur", "Urdu" },
+ { uz, "uz", "Uzbek" },
+ { ve, "ve", "Venda" },
+ { vi, "vi", "Vietnamese" },
+ { vo, "vo", "Volapük" },
+ { wa, "wa", "Walloon" },
+ { wo, "wo", "Wolof" },
+ { xh, "xh", "Xhosa" },
+ { yi, "yi", "Yiddish" } /* (formerly ji) */,
+ { yo, "yo", "Yoruba" },
+ { za, "za", "Zhuang; Chuang" },
+ { zh, "zh", "Chinese" },
+ { zu, "zu", "Zulu" },
+ { last_language_code, NULL, NULL }
+};
+
+static const char *
+cm_search_iso_map_char (byte_t ch)
+{
+ int i;
+ iso_map_type *iso = encoding_table[document_encoding_code].isotab;
+
+ /* If no conversion table for this encoding, quit. */
+ if (!iso)
+ return NULL;
+
+ for (i = 0; iso[i].html; i++)
+ if (iso[i].bytecode == ch)
+ return iso[i].translit;
+
+ return NULL;
+}
+
+const char *
+lang_transliterate_char (byte_t ch)
+{
+ if (transliterate_file_names
+ && document_encoding_code != no_encoding)
+ return cm_search_iso_map_char (ch);
+ return NULL;
+}
+
+
+
+/* Given a language code LL_CODE, return a "default" country code (in
+ new memory). We use the same table as gettext, and return LL_CODE
+ uppercased in the absence of any better possibility, with a warning.
+ (gettext silently defaults to the C locale, but we want to give users
+ a shot at fixing ambiguities.) We also return en_US for en, while
+ gettext does not. */
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+static char *
+default_country_for_lang (const char *ll_code)
+{
+ /* This table comes from msginit.c in gettext (0.16.1). It is
+ intentionally copied verbatim even though the format is not the
+ most convenient for our purposes, to make updates simple. */
+ static const char *locales_with_principal_territory[] = {
+ /* Language Main territory */
+ "ace_ID", /* Achinese Indonesia */
+ "af_ZA", /* Afrikaans South Africa */
+ "ak_GH", /* Akan Ghana */
+ "am_ET", /* Amharic Ethiopia */
+ "an_ES", /* Aragonese Spain */
+ "ang_GB", /* Old English Britain */
+ "as_IN", /* Assamese India */
+ "av_RU", /* Avaric Russia */
+ "awa_IN", /* Awadhi India */
+ "az_AZ", /* Azerbaijani Azerbaijan */
+ "bad_CF", /* Banda Central African Republic */
+ "ban_ID", /* Balinese Indonesia */
+ "be_BY", /* Belarusian Belarus */
+ "bem_ZM", /* Bemba Zambia */
+ "bg_BG", /* Bulgarian Bulgaria */
+ "bho_IN", /* Bhojpuri India */
+ "bik_PH", /* Bikol Philippines */
+ "bin_NG", /* Bini Nigeria */
+ "bm_ML", /* Bambara Mali */
+ "bn_IN", /* Bengali India */
+ "bo_CN", /* Tibetan China */
+ "br_FR", /* Breton France */
+ "bs_BA", /* Bosnian Bosnia */
+ "btk_ID", /* Batak Indonesia */
+ "bug_ID", /* Buginese Indonesia */
+ "ca_ES", /* Catalan Spain */
+ "ce_RU", /* Chechen Russia */
+ "ceb_PH", /* Cebuano Philippines */
+ "co_FR", /* Corsican France */
+ "cr_CA", /* Cree Canada */
+ "cs_CZ", /* Czech Czech Republic */
+ "csb_PL", /* Kashubian Poland */
+ "cy_GB", /* Welsh Britain */
+ "da_DK", /* Danish Denmark */
+ "de_DE", /* German Germany */
+ "din_SD", /* Dinka Sudan */
+ "doi_IN", /* Dogri India */
+ "dv_MV", /* Divehi Maldives */
+ "dz_BT", /* Dzongkha Bhutan */
+ "ee_GH", /* Éwé Ghana */
+ "el_GR", /* Greek Greece */
+ /* Don't put "en_GB" or "en_US" here. That would be asking for fruitless
+ political discussion. */
+ "es_ES", /* Spanish Spain */
+ "et_EE", /* Estonian Estonia */
+ "fa_IR", /* Persian Iran */
+ "fi_FI", /* Finnish Finland */
+ "fil_PH", /* Filipino Philippines */
+ "fj_FJ", /* Fijian Fiji */
+ "fo_FO", /* Faroese Faeroe Islands */
+ "fon_BJ", /* Fon Benin */
+ "fr_FR", /* French France */
+ "fy_NL", /* Western Frisian Netherlands */
+ "ga_IE", /* Irish Ireland */
+ "gd_GB", /* Scots Britain */
+ "gon_IN", /* Gondi India */
+ "gsw_CH", /* Swiss German Switzerland */
+ "gu_IN", /* Gujarati India */
+ "he_IL", /* Hebrew Israel */
+ "hi_IN", /* Hindi India */
+ "hil_PH", /* Hiligaynon Philippines */
+ "hr_HR", /* Croatian Croatia */
+ "ht_HT", /* Haitian Haiti */
+ "hu_HU", /* Hungarian Hungary */
+ "hy_AM", /* Armenian Armenia */
+ "id_ID", /* Indonesian Indonesia */
+ "ig_NG", /* Igbo Nigeria */
+ "ii_CN", /* Sichuan Yi China */
+ "ilo_PH", /* Iloko Philippines */
+ "is_IS", /* Icelandic Iceland */
+ "it_IT", /* Italian Italy */
+ "ja_JP", /* Japanese Japan */
+ "jab_NG", /* Hyam Nigeria */
+ "jv_ID", /* Javanese Indonesia */
+ "ka_GE", /* Georgian Georgia */
+ "kab_DZ", /* Kabyle Algeria */
+ "kaj_NG", /* Jju Nigeria */
+ "kam_KE", /* Kamba Kenya */
+ "kmb_AO", /* Kimbundu Angola */
+ "kcg_NG", /* Tyap Nigeria */
+ "kdm_NG", /* Kagoma Nigeria */
+ "kg_CD", /* Kongo Democratic Republic of Congo */
+ "kk_KZ", /* Kazakh Kazakhstan */
+ "kl_GL", /* Kalaallisut Greenland */
+ "km_KH", /* Khmer Cambodia */
+ "kn_IN", /* Kannada India */
+ "ko_KR", /* Korean Korea (South) */
+ "kok_IN", /* Konkani India */
+ "kr_NG", /* Kanuri Nigeria */
+ "kru_IN", /* Kurukh India */
+ "lg_UG", /* Ganda Uganda */
+ "li_BE", /* Limburgish Belgium */
+ "lo_LA", /* Laotian Laos */
+ "lt_LT", /* Lithuanian Lithuania */
+ "lu_CD", /* Luba-Katanga Democratic Republic of Congo */
+ "lua_CD", /* Luba-Lulua Democratic Republic of Congo */
+ "luo_KE", /* Luo Kenya */
+ "lv_LV", /* Latvian Latvia */
+ "mad_ID", /* Madurese Indonesia */
+ "mag_IN", /* Magahi India */
+ "mai_IN", /* Maithili India */
+ "mak_ID", /* Makasar Indonesia */
+ "man_ML", /* Mandingo Mali */
+ "men_SL", /* Mende Sierra Leone */
+ "mg_MG", /* Malagasy Madagascar */
+ "min_ID", /* Minangkabau Indonesia */
+ "mk_MK", /* Macedonian Macedonia */
+ "ml_IN", /* Malayalam India */
+ "mn_MN", /* Mongolian Mongolia */
+ "mni_IN", /* Manipuri India */
+ "mos_BF", /* Mossi Burkina Faso */
+ "mr_IN", /* Marathi India */
+ "ms_MY", /* Malay Malaysia */
+ "mt_MT", /* Maltese Malta */
+ "mwr_IN", /* Marwari India */
+ "my_MM", /* Burmese Myanmar */
+ "na_NR", /* Nauru Nauru */
+ "nah_MX", /* Nahuatl Mexico */
+ "nap_IT", /* Neapolitan Italy */
+ "nb_NO", /* Norwegian Bokmål Norway */
+ "nds_DE", /* Low Saxon Germany */
+ "ne_NP", /* Nepali Nepal */
+ "nl_NL", /* Dutch Netherlands */
+ "nn_NO", /* Norwegian Nynorsk Norway */
+ "no_NO", /* Norwegian Norway */
+ "nr_ZA", /* South Ndebele South Africa */
+ "nso_ZA", /* Northern Sotho South Africa */
+ "nym_TZ", /* Nyamwezi Tanzania */
+ "nyn_UG", /* Nyankole Uganda */
+ "oc_FR", /* Occitan France */
+ "oj_CA", /* Ojibwa Canada */
+ "or_IN", /* Oriya India */
+ "pa_IN", /* Punjabi India */
+ "pag_PH", /* Pangasinan Philippines */
+ "pam_PH", /* Pampanga Philippines */
+ "pbb_CO", /* Páez Colombia */
+ "pl_PL", /* Polish Poland */
+ "ps_AF", /* Pashto Afghanistan */
+ "pt_PT", /* Portuguese Portugal */
+ "raj_IN", /* Rajasthani India */
+ "rm_CH", /* Rhaeto-Roman Switzerland */
+ "rn_BI", /* Kirundi Burundi */
+ "ro_RO", /* Romanian Romania */
+ "ru_RU", /* Russian Russia */
+ "sa_IN", /* Sanskrit India */
+ "sas_ID", /* Sasak Indonesia */
+ "sat_IN", /* Santali India */
+ "sc_IT", /* Sardinian Italy */
+ "scn_IT", /* Sicilian Italy */
+ "sg_CF", /* Sango Central African Republic */
+ "shn_MM", /* Shan Myanmar */
+ "si_LK", /* Sinhala Sri Lanka */
+ "sid_ET", /* Sidamo Ethiopia */
+ "sk_SK", /* Slovak Slovakia */
+ "sl_SI", /* Slovenian Slovenia */
+ "so_SO", /* Somali Somalia */
+ "sq_AL", /* Albanian Albania */
+ "sr_RS", /* Serbian Serbia */
+ "sr_YU", /* Serbian Yugoslavia */
+ "srr_SN", /* Serer Senegal */
+ "suk_TZ", /* Sukuma Tanzania */
+ "sus_GN", /* Susu Guinea */
+ "sv_SE", /* Swedish Sweden */
+ "te_IN", /* Telugu India */
+ "tem_SL", /* Timne Sierra Leone */
+ "tet_ID", /* Tetum Indonesia */
+ "tg_TJ", /* Tajik Tajikistan */
+ "th_TH", /* Thai Thailand */
+ "tiv_NG", /* Tiv Nigeria */
+ "tk_TM", /* Turkmen Turkmenistan */
+ "tl_PH", /* Tagalog Philippines */
+ "to_TO", /* Tonga Tonga */
+ "tr_TR", /* Turkish Turkey */
+ "tum_MW", /* Tumbuka Malawi */
+ "uk_UA", /* Ukrainian Ukraine */
+ "umb_AO", /* Umbundu Angola */
+ "ur_PK", /* Urdu Pakistan */
+ "uz_UZ", /* Uzbek Uzbekistan */
+ "ve_ZA", /* Venda South Africa */
+ "vi_VN", /* Vietnamese Vietnam */
+ "wa_BE", /* Walloon Belgium */
+ "wal_ET", /* Walamo Ethiopia */
+ "war_PH", /* Waray Philippines */
+ "wen_DE", /* Sorbian Germany */
+ "yao_MW", /* Yao Malawi */
+ "zap_MX" /* Zapotec Mexico */
+ };
+ int c;
+ int cc_len;
+ int ll_len = strlen (ll_code);
+ char *cc_code = xmalloc (ll_len + 1 + 1);
+ int principal_len = SIZEOF (locales_with_principal_territory);
+
+ strcpy (cc_code, ll_code);
+ strcat (cc_code, "_");
+ cc_len = ll_len + 1;
+
+ for (c = 0; c < principal_len; c++)
+ {
+ const char *principal_locale = locales_with_principal_territory[c];
+ if (strncmp (principal_locale, cc_code, cc_len) == 0)
+ {
+ const char *underscore = strchr (principal_locale, '_');
+ /* should always be there, but in case ... */
+ free (cc_code);
+ cc_code = xstrdup (underscore ? underscore + 1 : principal_locale);
+ break;
+ }
+ }
+
+ /* If we didn't find one to copy, warn and duplicate. */
+ if (c == principal_len)
+ {
+ if (mbscasecmp (ll_code, "en") == 0)
+ cc_code = xstrdup ("en_US");
+ else
+ {
+ warning (_("no default territory known for language `%s'"), ll_code);
+ for (c = 0; c < strlen (ll_code); c++)
+ cc_code[c] = toupper (ll_code[c]);
+ cc_code[c] = 0;
+ /* We're probably wasting a byte, oops. */
+ }
+ }
+
+ return cc_code;
+}
+
+
+
+/* @documentlanguage ARG. We set the global `document_language' (which
+ `getdocumenttext' uses) to the gettextish string, and
+ `language_code' to the corresponding enum value. */
+
+void
+cm_documentlanguage (void)
+{
+ language_code_type c;
+ char *lang_arg, *ll_part, *cc_part, *locale_string;
+ char *underscore;
+
+ /* Read the line with the language code on it. */
+ get_rest_of_line (0, &lang_arg);
+
+ /* What we're passed might be either just a language code LL (we'll
+ also need it with the usual _CC appended, if there is one), or a
+ locale name as used by gettext, LL_CC (we'll also need its
+ constituents separately). */
+ underscore = strchr (lang_arg, '_');
+ if (underscore)
+ {
+ ll_part = substring (lang_arg, underscore);
+ cc_part = xstrdup (underscore + 1);
+ locale_string = xstrdup (lang_arg);
+ }
+ else
+ {
+ ll_part = xstrdup (lang_arg);
+ cc_part = default_country_for_lang (ll_part);
+ locale_string = xmalloc (strlen (ll_part) + 1 + strlen (cc_part) + 1);
+ strcpy (locale_string, ll_part);
+ strcat (locale_string, "_");
+ strcat (locale_string, cc_part);
+ }
+
+ /* Done with analysis of user-supplied string. */
+ free (lang_arg);
+
+ /* Linear search is fine these days. */
+ for (c = aa; c != last_language_code; c++)
+ {
+ if (strcmp (ll_part, language_table[c].abbrev) == 0)
+ { /* Set current language code. */
+ language_code = c;
+ break;
+ }
+ }
+
+ /* If we didn't find this code, complain. */
+ if (c == last_language_code)
+ warning (_("%s is not a valid ISO 639 language code"), ll_part);
+
+ /* Set the language our `getdocumenttext' function uses for
+ translating document strings. */
+ document_language = xstrdup (locale_string);
+ free (locale_string);
+
+ if (xml && !docbook)
+ { /* According to http://www.opentag.com/xfaq_lang.htm, xml:lang
+ takes an ISO 639 language code, optionally followed by a dash
+ (not underscore) and an ISO 3166 country code. So we have
+ to make another version with - instead of _. */
+ char *xml_locale = xmalloc (strlen (ll_part) + 1 + strlen (cc_part) + 1);
+ strcpy (xml_locale, ll_part);
+ strcat (xml_locale, "-");
+ strcat (xml_locale, cc_part);
+ xml_insert_element_with_attribute (DOCUMENTLANGUAGE, START,
+ "xml:lang=\"%s\"", xml_locale);
+ xml_insert_element (DOCUMENTLANGUAGE, END);
+ free (xml_locale);
+ }
+
+ free (ll_part);
+ free (cc_part);
+}
+
+
+
+/* Search through the encoding table for the given character, returning
+ its equivalent. */
+
+static int
+cm_search_iso_map (char *html)
+{
+ if (document_encoding_code == UTF_8)
+ {
+ /* Binary search in unicode_map. */
+ size_t low = 0;
+ size_t high = sizeof (unicode_map) / sizeof (unicode_map[0]);
+
+ /* At each loop iteration, low < high; for indices < low the values are
+ smaller than HTML; for indices >= high the values are greater than HTML.
+ So, if HTML occurs in the list, it is at low <= position < high. */
+ do
+ {
+ size_t mid = low + (high - low) / 2; /* low <= mid < high */
+ int cmp = strcmp (unicode_map[mid].html, html);
+
+ if (cmp < 0)
+ low = mid + 1;
+ else if (cmp > 0)
+ high = mid;
+ else /* cmp == 0 */
+ return unicode_map[mid].unicode;
+ }
+ while (low < high);
+
+ return -1;
+ }
+ else
+ {
+ int i;
+ iso_map_type *iso = encoding_table[document_encoding_code].isotab;
+
+ /* If no conversion table for this encoding, quit. */
+ if (!iso)
+ return -1;
+
+ for (i = 0; iso[i].html; i++)
+ {
+ if (strcmp (html, iso[i].html) == 0)
+ return i;
+ }
+
+ return -1;
+ }
+}
+
+
+/* @documentencoding. Set the translation table. */
+
+void
+cm_documentencoding (void)
+{
+ if (!handling_delayed_writes)
+ {
+ encoding_code_type enc;
+ char *enc_arg;
+
+ /* This is ugly and probably needs to apply to other commands'
+ argument parsing as well. When we're doing @documentencoding,
+ we're generally in the frontmatter of the document, and so the.
+ expansion in html/xml/docbook would generally be the empty string.
+ (Because those modes wait until the first normal text of the
+ document to start outputting.) The result would thus be a warning
+ "unrecognized encoding name `'". Sigh. */
+ int save_html = html;
+ int save_xml = xml;
+
+ html = 0;
+ xml = 0;
+ get_rest_of_line (1, &enc_arg);
+ html = save_html;
+ xml = save_xml;
+
+ /* See if we have this encoding. */
+ for (enc = no_encoding+1; enc != last_encoding_code; enc++)
+ {
+ if (mbscasecmp (enc_arg, encoding_table[enc].encname) == 0)
+ {
+ document_encoding_code = enc;
+ break;
+ }
+ }
+
+ /* If we didn't find this code, complain. */
+ if (enc == last_encoding_code)
+ {
+ warning (_("unrecognized encoding name `%s'"), enc_arg);
+ /* Let the previous one go. */
+ if (unknown_encoding && *unknown_encoding)
+ free (unknown_encoding);
+ unknown_encoding = xstrdup (enc_arg);
+ }
+
+ else if (encoding_table[document_encoding_code].isotab == NULL)
+ warning (_("sorry, encoding `%s' not supported"), enc_arg);
+
+ free (enc_arg);
+ }
+ else if (xml)
+ {
+ char *encoding = current_document_encoding ();
+
+ if (encoding && *encoding)
+ {
+ insert_string (" encoding=\"");
+ insert_string (encoding);
+ insert_string ("\"");
+ }
+
+ free (encoding);
+ }
+}
+
+char *
+current_document_encoding (void)
+{
+ if (document_encoding_code != no_encoding)
+ return xstrdup (encoding_table[document_encoding_code].encname);
+ else if (unknown_encoding && *unknown_encoding)
+ return xstrdup (unknown_encoding);
+ else
+ return xstrdup ("");
+}
+
+
+/* Add RC per the current encoding. */
+
+static void
+add_encoded_char_from_code (int rc)
+{
+ if (document_encoding_code == UTF_8)
+ {
+ if (rc < 0x80)
+ add_char (rc);
+ else if (rc < 0x800)
+ {
+ add_char (0xc0 | (rc >> 6));
+ add_char (0x80 | (rc & 0x3f));
+ }
+ else if (rc < 0x10000)
+ {
+ add_char (0xe0 | (rc >> 12));
+ add_char (0x80 | ((rc >> 6) & 0x3f));
+ add_char (0x80 | (rc & 0x3f));
+ }
+ else
+ {
+ add_char (0xf0 | (rc >> 18));
+ add_char (0x80 | ((rc >> 12) & 0x3f));
+ add_char (0x80 | ((rc >> 6) & 0x3f));
+ add_char (0x80 | (rc & 0x3f));
+ }
+ }
+ else
+ add_char (encoding_table[document_encoding_code].isotab[rc].bytecode);
+}
+
+
+/* If html or xml output, add &HTML_STR; to the output. If not html and
+ the user requested encoded output, add the real 8-bit character
+ corresponding to HTML_STR from the translation tables. Otherwise,
+ add INFO_STR. */
+
+static void
+add_encoded_char (char *html_str, char *info_str)
+{
+ if (html)
+ add_word_args ("&%s;", html_str);
+ else if (xml)
+ xml_insert_entity (html_str);
+ else if (enable_encoding && document_encoding_code != no_encoding)
+ {
+ /* Look for HTML_STR in the current translation table. */
+ int rc = cm_search_iso_map (html_str);
+ if (rc >= 0)
+ /* We found it, add the real character. */
+ add_encoded_char_from_code (rc);
+ else
+ { /* We didn't find it, that seems bad. */
+ warning (_("invalid encoded character `%s'"), html_str);
+ add_word (info_str);
+ }
+ }
+ else
+ add_word (info_str);
+}
+
+
+
+/* Output an accent for HTML or XML. */
+
+static void
+cm_accent_generic_html (int arg, int start, int end, char *html_supported,
+ int single, int html_solo_standalone, char *html_solo)
+{
+ static int valid_html_accent; /* yikes */
+
+ if (arg == START)
+ { /* If HTML has good support for this character, use it. */
+ if (strchr (html_supported, curchar ()))
+ { /* Yes; start with an ampersand. The character itself
+ will be added later in read_command (makeinfo.c). */
+ int saved_escape_html = escape_html;
+ escape_html = 0;
+ valid_html_accent = 1;
+ add_char ('&');
+ escape_html = saved_escape_html;
+ }
+ else
+ { /* @dotless{i} is not listed in html_supported but HTML entities
+ starting with `i' can be used, such as &icirc;. */
+ int save_input_text_offset = input_text_offset;
+ char *accent_contents;
+
+ get_until_in_braces ("\n", &accent_contents);
+ canon_white (accent_contents);
+
+ if (strstr (accent_contents, "@dotless{i"))
+ {
+ add_word_args ("&%c", accent_contents[9]);
+ valid_html_accent = 1;
+ }
+ else
+ {
+ /* Search for @dotless{} wasn't successful, so rewind. */
+ input_text_offset = save_input_text_offset;
+ valid_html_accent = 0;
+ if (html_solo_standalone)
+ { /* No special HTML support, so produce standalone char. */
+ if (xml)
+ xml_insert_entity (html_solo);
+ else
+ add_word_args ("&%s;", html_solo);
+ }
+ else
+ /* If the html_solo does not exist as standalone character
+ (namely &circ; &grave; &tilde;), then we use
+ the single character version instead. */
+ add_char (single);
+ }
+
+ free (accent_contents);
+ }
+ }
+ else if (arg == END)
+ { /* Only if we saw a valid_html_accent can we use the full
+ HTML accent (umlaut, grave ...). */
+ if (valid_html_accent)
+ {
+ add_word (html_solo);
+ add_char (';');
+ }
+ }
+}
+
+
+/* If END is zero, there is nothing in the paragraph to accent. This
+ can happen when we're in a menu with an accent command and
+ --no-headers is given, so the base character is not added. In this
+ case we're not producing any output anyway, so just forget it.
+ Otherwise, produce the ASCII version of the accented char. */
+static void
+cm_accent_generic_no_headers (int arg, int start, int end, int single,
+ char *html_solo)
+{
+ if (arg == END && end > 0)
+ {
+ if (no_encoding)
+ add_char (single);
+ else
+ {
+ int rc;
+ char *buffer = xmalloc (1 + strlen (html_solo) + 1);
+ assert (end > 0);
+ buffer[0] = output_paragraph[end - 1];
+ buffer[1] = 0;
+ strcat (buffer, html_solo);
+
+ rc = cm_search_iso_map (buffer);
+ if (rc >= 0)
+ {
+ /* Here we replace the character which has
+ been inserted in read_command with
+ the value we have found in the conversion table. */
+ output_paragraph_offset--;
+ add_encoded_char_from_code (rc);
+ }
+ else
+ { /* If we didn't find a translation for this character,
+ put the single instead. E.g., &Xuml; does not exist so X&uml;
+ should be produced. */
+ /* When the below warning is issued, an author has nothing
+ wrong in their document, let alone anything ``fixable''
+ on their side. So it is commented out for now. */
+ /* warning (_("%s is an invalid ISO code, using %c"),
+ buffer, single); */
+ add_char (single);
+ }
+
+ free (buffer);
+ }
+ }
+}
+
+
+
+/* Accent commands that take explicit arguments and don't have any
+ special HTML support. */
+
+void
+cm_accent (int arg)
+{
+ int old_escape_html = escape_html;
+ escape_html = 0;
+ if (arg == START)
+ {
+ /* Must come first to avoid ambiguity with overdot. */
+ if (strcmp (command, "udotaccent") == 0) /* underdot */
+ add_char ('.');
+ }
+ else if (arg == END)
+ {
+ if (strcmp (command, "=") == 0) /* macron */
+ add_word ((html || xml) ? "&macr;" : "=");
+ else if (strcmp (command, "H") == 0) /* Hungarian umlaut */
+ add_word ("''");
+ else if (strcmp (command, "dotaccent") == 0) /* overdot */
+ add_meta_char ('.');
+ else if (strcmp (command, "ringaccent") == 0) /* ring */
+ add_char ('*');
+ else if (strcmp (command, "tieaccent") == 0) /* long tie */
+ add_char ('[');
+ else if (strcmp (command, "u") == 0) /* breve */
+ add_char ('(');
+ else if (strcmp (command, "ubaraccent") == 0) /* underbar */
+ add_char ('_');
+ else if (strcmp (command, "v") == 0) /* hacek/check */
+ add_word ((html || xml) ? "&lt;" : "<");
+ }
+ escape_html = old_escape_html;
+}
+
+/* Common routine for the accent characters that have support in HTML.
+ If the character being accented is in the HTML_SUPPORTED set, then
+ produce &CHTML_SOLO;, for example, &Auml; for an A-umlaut. If not in
+ HTML_SUPPORTED, just produce &HTML_SOLO;X for the best we can do with
+ at an X-umlaut. If not producing HTML, just use SINGLE, a
+ character such as " which is the best plain text representation we
+ can manage. If HTML_SOLO_STANDALONE is nonzero the given HTML_SOLO
+ exists as valid standalone character in HTML, e.g., &uml;. */
+
+static void
+cm_accent_generic (int arg, int start, int end, char *html_supported,
+ int single, int html_solo_standalone, char *html_solo)
+{
+ /* Accentuating space characters makes no sense, so issue a warning. */
+ if (arg == START && isspace (input_text[input_text_offset]))
+ warning ("Accent command `@%s' must not be followed by whitespace",
+ command);
+
+ if (html || xml)
+ cm_accent_generic_html (arg, start, end, html_supported,
+ single, html_solo_standalone, html_solo);
+ else if (no_headers)
+ cm_accent_generic_no_headers (arg, start, end, single, html_solo);
+ else if (arg == END)
+ {
+ if (enable_encoding)
+ /* use 8-bit if available */
+ cm_accent_generic_no_headers (arg, start, end, single, html_solo);
+ else
+ /* use regular character */
+ add_char (single);
+ }
+}
+
+void
+cm_accent_umlaut (int arg, int start, int end)
+{
+ cm_accent_generic (arg, start, end, "aouAOUEeIiy", '"', 1, "uml");
+}
+
+void
+cm_accent_acute (int arg, int start, int end)
+{
+ cm_accent_generic (arg, start, end, "AEIOUYaeiouy", '\'', 1, "acute");
+}
+
+void
+cm_accent_cedilla (int arg, int start, int end)
+{
+ cm_accent_generic (arg, start, end, "Cc", ',', 1, "cedil");
+}
+
+void
+cm_accent_hat (int arg, int start, int end)
+{
+ cm_accent_generic (arg, start, end, "AEIOUaeiou", '^', 0, "circ");
+}
+
+void
+cm_accent_grave (int arg, int start, int end)
+{
+ cm_accent_generic (arg, start, end, "AEIOUaeiou", '`', 0, "grave");
+}
+
+void
+cm_accent_tilde (int arg, int start, int end)
+{
+ cm_accent_generic (arg, start, end, "ANOano", '~', 0, "tilde");
+}
+
+
+
+/* Non-English letters/characters that don't insert themselves. */
+void
+cm_special_char (int arg)
+{
+ int old_escape_html = escape_html;
+ escape_html = 0;
+
+ if (arg == START)
+ {
+ if ((*command == 'L' || *command == 'l'
+ || *command == 'O' || *command == 'o')
+ && command[1] == 0)
+ { /* Lslash lslash Oslash oslash.
+ Lslash and lslash aren't supported in HTML. */
+ if (command[0] == 'O')
+ add_encoded_char ("Oslash", "/O");
+ else if (command[0] == 'o')
+ add_encoded_char ("oslash", "/o");
+ else
+ add_word_args ("/%c", command[0]);
+ }
+ else if (strcmp (command, "exclamdown") == 0)
+ add_encoded_char ("iexcl", "!");
+ else if (strcmp (command, "questiondown") == 0)
+ add_encoded_char ("iquest", "?");
+ else if (strcmp (command, "euro") == 0)
+ /* http://www.cs.tut.fi/~jkorpela/html/euro.html suggests that
+ &euro; degrades best in old browsers. */
+ add_encoded_char ("euro", "Euro ");
+ else if (strcmp (command, "pounds") == 0)
+ add_encoded_char ("pound" , "#");
+ else if (strcmp (command, "ordf") == 0)
+ add_encoded_char ("ordf" , "a");
+ else if (strcmp (command, "ordm") == 0)
+ add_encoded_char ("ordm" , "o");
+ else if (strcmp (command, "textdegree") == 0)
+ add_encoded_char ("deg" , "o");
+ else if (strcmp (command, "AE") == 0)
+ add_encoded_char ("AElig", command);
+ else if (strcmp (command, "ae") == 0)
+ add_encoded_char ("aelig", command);
+ else if (strcmp (command, "OE") == 0)
+ add_encoded_char ("OElig", command);
+ else if (strcmp (command, "oe") == 0)
+ add_encoded_char ("oelig", command);
+ else if (strcmp (command, "AA") == 0)
+ add_encoded_char ("Aring", command);
+ else if (strcmp (command, "aa") == 0)
+ add_encoded_char ("aring", command);
+ else if (strcmp (command, "ss") == 0)
+ add_encoded_char ("szlig", command);
+ else if (strcmp (command, "guillemetleft") == 0
+ || strcmp (command, "guillemotleft") == 0)
+ add_encoded_char ("laquo", "<<");
+ else if (strcmp (command, "guillemetright") == 0
+ || strcmp (command, "guillemotright") == 0)
+ add_encoded_char ("raquo", ">>");
+ else
+ line_error ("cm_special_char internal error: command=@%s", command);
+ }
+ escape_html = old_escape_html;
+}
+
+/* Dotless i or j. */
+void
+cm_dotless (int arg, int start, int end)
+{
+ if (arg == END)
+ {
+ xml_no_para --;
+ if (output_paragraph[start] != 'i' && output_paragraph[start] != 'j')
+ /* This error message isn't perfect if the argument is multiple
+ characters, but it doesn't seem worth getting right. */
+ line_error (_("%c%s expects `i' or `j' as argument, not `%c'"),
+ COMMAND_PREFIX, command, output_paragraph[start]);
+
+ else if (end - start != 1)
+ line_error (_("%c%s expects a single character `i' or `j' as argument"),
+ COMMAND_PREFIX, command);
+
+ /* We've already inserted the `i' or `j', so nothing to do. */
+ }
+ else
+ xml_no_para ++;
+}
diff --git a/makeinfo/lang.h b/makeinfo/lang.h
new file mode 100644
index 0000000..b902f19
--- /dev/null
+++ b/makeinfo/lang.h
@@ -0,0 +1,141 @@
+/* lang.h -- declarations for language codes etc.
+ $Id: lang.h,v 1.14 2007/11/21 23:02:22 karl Exp $
+
+ Copyright (C) 1999, 2001, 2002, 2003, 2006, 2007
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#ifndef LANG_H
+#define LANG_H
+
+/* The language code which can be changed through @documentlanguage
+ These code are the ISO-639 two letter codes. */
+
+#undef hz /* AIX 4.3.3 */
+typedef enum
+{
+ aa, ab, ae, af, ak, am, an, ar, as, av, ay, az, ba, be, bg, bh, bi,
+ bm, bn, bo, br, bs, ca, ce, ch, co, cr, cs, cu, cv, cy, da, de, dv,
+ dz, ee, el, en, eo, es, et, eu, fa, ff, fi, fj, fo, fr, fy, ga, gd,
+ gl, gn, gu, gv, ha, he, hi, ho, hr, ht, hu, hy, hz, ia, id, ie, ig,
+ ii, ik, io, is, it, iu, ja, jv, ka, kg, ki, kj, kk, kl, km, kn, ko,
+ kr, ks, ku, kv, kw, ky, la, lb, lg, li, ln, lo, lt, lu, lv, mg, mh,
+ mi, mk, ml, mn, mo, mr, ms, mt, my, na, nb, nd, ne, ng, nl, nn, no,
+ nr, nv, ny, oc, oj, om, or, os, pa, pi, pl, ps, pt, qu, rm, rn, ro,
+ ru, rw, sa, sc, sd, se, sg, si, sk, sl, sm, sn, so, sq, sr, ss, st,
+ su, sv, sw, ta, te, tg, th, ti, tk, tl, tn, to, tr, ts, tt, tw, ty,
+ ug, uk, ur, uz, ve, vi, vo, wa, wo, xh, yi, yo, za, zh, zu,
+ last_language_code
+} language_code_type;
+
+/* The current language code. */
+extern language_code_type language_code;
+
+
+/* Information for each language. */
+typedef struct
+{
+ language_code_type lc; /* language code as enum type */
+ char *abbrev; /* two letter language code */
+ char *desc; /* full name for language code */
+} language_type;
+
+extern language_type language_table[];
+
+
+
+/* The document encoding. This is useful to produce true 8-bit
+ characters according to the @documentencoding. */
+
+typedef enum {
+ no_encoding,
+ US_ASCII,
+ ISO_8859_1,
+ ISO_8859_2,
+ ISO_8859_3, /* this and none of the rest are supported. */
+ ISO_8859_4,
+ ISO_8859_5,
+ ISO_8859_6,
+ ISO_8859_7,
+ ISO_8859_8,
+ ISO_8859_9,
+ ISO_8859_10,
+ ISO_8859_11,
+ ISO_8859_12,
+ ISO_8859_13,
+ ISO_8859_14,
+ ISO_8859_15,
+ KOI8_R,
+ KOI8_U,
+ UTF_8,
+ last_encoding_code
+} encoding_code_type;
+
+/* The current document encoding, or null if not set. */
+extern encoding_code_type document_encoding_code;
+
+/* If an encoding is not supported, just keep it as a string. */
+extern char *unknown_encoding;
+
+/* Maps an HTML abbreviation to ISO and Unicode codes for a given code. */
+
+typedef unsigned short int unicode_t; /* should be 16 bits */
+typedef unsigned char byte_t;
+
+typedef struct
+{
+ char *html; /* HTML equivalent like umlaut auml => &auml; */
+ byte_t bytecode; /* 8-Bit Code (ISO 8859-1,...) */
+ unicode_t unicode; /* Unicode in U+ convention */
+ char *translit; /* 7-bit transliteration */
+} iso_map_type;
+
+/* Information about the document encoding. */
+typedef struct
+{
+ encoding_code_type ec; /* document encoding type (see above enum) */
+ char *encname; /* encoding name like "iso-8859-1", valid in
+ HTML and Emacs */
+ iso_map_type *isotab; /* address of ISO translation table */
+} encoding_type;
+
+/* Table with all the encoding codes that we recognize. */
+extern encoding_type encoding_table[];
+
+
+/* The commands. */
+extern void cm_documentlanguage (void),
+ cm_documentencoding (void);
+
+/* Accents, other non-English characters. */
+void cm_accent (int arg), cm_special_char (int arg),
+ cm_dotless (int arg, int start, int end);
+
+extern void cm_accent_umlaut (int arg, int start, int end),
+ cm_accent_acute (int arg, int start, int end),
+ cm_accent_cedilla (int arg, int start, int end),
+ cm_accent_hat (int arg, int start, int end),
+ cm_accent_grave (int arg, int start, int end),
+ cm_accent_tilde (int arg, int start, int end);
+
+extern char *current_document_encoding (void);
+
+extern const char *lang_transliterate_char (byte_t ch);
+
+extern char *document_language;
+
+#endif /* not LANG_H */
diff --git a/makeinfo/macro.c b/makeinfo/macro.c
new file mode 100644
index 0000000..10dabb2
--- /dev/null
+++ b/makeinfo/macro.c
@@ -0,0 +1,1095 @@
+/* macro.c -- user-defined macros for Texinfo.
+ $Id: macro.c,v 1.12 2007/07/01 21:20:32 karl Exp $
+
+ Copyright (C) 1998, 1999, 2002, 2003, 2005, 2007
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "cmds.h"
+#include "files.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "insertion.h"
+
+/* If non-NULL, this is an output stream to write the full macro expansion
+ of the input text to. The result is another texinfo file, but
+ missing @include, @infoinclude, @macro, and macro invocations. Instead,
+ all of the text is placed within the file. */
+FILE *macro_expansion_output_stream = NULL;
+
+/* Output file for -E. */
+char *macro_expansion_filename;
+
+/* Nonzero means a macro string is in execution, as opposed to a file. */
+int me_executing_string = 0;
+
+/* Nonzero means we want only to expand macros and
+ leave everything else intact. */
+int only_macro_expansion = 0;
+
+static ITEXT **itext_info = NULL;
+static int itext_size = 0;
+
+/* Return the arglist on the current line. This can behave in two different
+ ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
+int braces_required_for_macro_args = 0;
+
+/* Array of macros and definitions. */
+MACRO_DEF **macro_list = NULL;
+
+int macro_list_len = 0; /* Number of elements. */
+int macro_list_size = 0; /* Number of slots in total. */
+
+/* Return the length of the array in ARRAY. */
+int
+array_len (char **array)
+{
+ int i = 0;
+
+ if (array)
+ for (i = 0; array[i]; i++);
+
+ return i;
+}
+
+void
+free_array (char **array)
+{
+ if (array)
+ {
+ int i;
+ for (i = 0; array[i]; i++)
+ free (array[i]);
+
+ free (array);
+ }
+}
+
+/* Return the macro definition of NAME or NULL if NAME is not defined. */
+MACRO_DEF *
+find_macro (char *name)
+{
+ int i;
+ MACRO_DEF *def;
+
+ def = NULL;
+ for (i = 0; macro_list && (def = macro_list[i]); i++)
+ {
+ if ((!def->inhibited) && (strcmp (def->name, name) == 0))
+ break;
+ }
+ return def;
+}
+
+/* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
+ SOURCE_FILE is the name of the file where this definition can be found,
+ and SOURCE_LINENO is the line number within that file. If a macro already
+ exists with NAME, then a warning is produced, and that previous
+ definition is overwritten. */
+static void
+add_macro (char *name, char **arglist, char *body, char *source_file,
+ int source_lineno, int flags)
+{
+ MACRO_DEF *def;
+
+ def = find_macro (name);
+
+ if (!def)
+ {
+ if (macro_list_len + 2 >= macro_list_size)
+ macro_list = xrealloc
+ (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
+
+ macro_list[macro_list_len] = xmalloc (sizeof (MACRO_DEF));
+ macro_list[macro_list_len + 1] = NULL;
+
+ def = macro_list[macro_list_len];
+ macro_list_len += 1;
+ def->name = name;
+ }
+ else
+ {
+ char *temp_filename = input_filename;
+ int temp_line = line_number;
+
+ warning (_("macro `%s' previously defined"), name);
+
+ input_filename = def->source_file;
+ line_number = def->source_lineno;
+ warning (_("here is the previous definition of `%s'"), name);
+
+ input_filename = temp_filename;
+ line_number = temp_line;
+
+ if (def->arglist)
+ {
+ int i;
+
+ for (i = 0; def->arglist[i]; i++)
+ free (def->arglist[i]);
+
+ free (def->arglist);
+ }
+ free (def->source_file);
+ free (def->body);
+ }
+
+ def->source_file = xstrdup (source_file);
+ def->source_lineno = source_lineno;
+ def->body = body;
+ def->arglist = arglist;
+ def->argcount = array_len (arglist);
+ def->inhibited = 0;
+ def->flags = flags;
+}
+
+
+char **
+get_brace_args (enum quote_type quote)
+{
+ char **arglist, *word;
+ int arglist_index, arglist_size;
+ int character, escape_seen, start;
+ int depth = 1;
+
+ /* There is an arglist in braces here, so gather the args inside of it. */
+ skip_whitespace_and_newlines ();
+ input_text_offset++;
+ arglist = NULL;
+ arglist_index = arglist_size = 0;
+
+ get_arg:
+ skip_whitespace_and_newlines ();
+ start = input_text_offset;
+ escape_seen = 0;
+
+ while ((character = curchar ()))
+ {
+ if (character == '\\')
+ {
+ input_text_offset += 2;
+ escape_seen = 1;
+ }
+ else if (character == '{')
+ {
+ depth++;
+ input_text_offset++;
+ }
+ else if ((character == ','
+ && !(quote == quote_single
+ || (quote == quote_many && depth > 1)))
+ || ((character == '}') && depth == 1))
+ {
+ int len = input_text_offset - start;
+
+ if (len || (character != '}'))
+ {
+ word = xmalloc (1 + len);
+ memcpy (word, input_text + start, len);
+ word[len] = 0;
+
+ /* Clean up escaped characters. */
+ if (escape_seen)
+ {
+ int i;
+ for (i = 0; word[i]; i++)
+ if (word[i] == '\\')
+ memmove (word + i, word + i + 1,
+ 1 + strlen (word + i + 1));
+ }
+
+ if (arglist_index + 2 >= arglist_size)
+ arglist = xrealloc
+ (arglist, (arglist_size += 10) * sizeof (char *));
+
+ arglist[arglist_index++] = word;
+ arglist[arglist_index] = NULL;
+ }
+
+ input_text_offset++;
+ if (character == '}')
+ break;
+ else
+ goto get_arg;
+ }
+ else if (character == '}')
+ {
+ depth--;
+ input_text_offset++;
+ }
+ else
+ {
+ input_text_offset++;
+ if (character == '\n') line_number++;
+ }
+ }
+ return arglist;
+}
+
+static char **
+get_macro_args (MACRO_DEF *def)
+{
+ int i;
+ char *word;
+
+ /* Quickly check to see if this macro has been invoked with any arguments.
+ If not, then don't skip any of the following whitespace. */
+ for (i = input_text_offset; i < input_text_length; i++)
+ if (!cr_or_whitespace (input_text[i]))
+ break;
+
+ if (input_text[i] != '{')
+ {
+ if (braces_required_for_macro_args)
+ {
+ return NULL;
+ }
+ else
+ {
+ /* Braces are not required to fill out the macro arguments. If
+ this macro takes one argument, it is considered to be the
+ remainder of the line, sans whitespace. */
+ if (def->arglist && def->arglist[0] && !def->arglist[1])
+ {
+ char **arglist;
+
+ get_rest_of_line (0, &word);
+ if (input_text_offset > 0
+ && input_text[input_text_offset - 1] == '\n')
+ {
+ input_text_offset--;
+ line_number--;
+ }
+ /* canon_white (word); */
+ arglist = xmalloc (2 * sizeof (char *));
+ arglist[0] = word;
+ arglist[1] = NULL;
+ return arglist;
+ }
+ else
+ {
+ /* The macro either took no arguments, or took more than
+ one argument. In that case, it must be invoked with
+ arguments surrounded by braces. */
+ return NULL;
+ }
+ }
+ }
+ return get_brace_args (def->argcount == 1 ? quote_single : quote_many);
+}
+
+/* Substitute actual parameters for named parameters in body.
+ The named parameters which appear in BODY must by surrounded
+ reverse slashes, as in \foo\. */
+static char *
+apply (char **named, char **actuals, char *body)
+{
+ int i;
+ int new_body_index, new_body_size;
+ char *new_body, *text;
+ int length_of_actuals;
+
+ length_of_actuals = array_len (actuals);
+ new_body_size = strlen (body);
+ new_body = xmalloc (1 + new_body_size);
+
+ /* Copy chars from BODY into NEW_BODY. */
+ i = 0;
+ new_body_index = 0;
+
+ while (body[i])
+ { /* Anything but a \ is easy. */
+ if (body[i] != '\\')
+ new_body[new_body_index++] = body[i++];
+ else
+ { /* Snarf parameter name, check against named parameters. */
+ char *param;
+ int param_start, len;
+
+ param_start = ++i;
+ while (body[i] && body[i] != '\\')
+ i++;
+
+ len = i - param_start;
+ param = xmalloc (1 + len);
+ memcpy (param, body + param_start, len);
+ param[len] = 0;
+
+ if (body[i]) /* move past \ */
+ i++;
+
+ if (len == 0)
+ { /* \\ always means \, even if macro has no args. */
+ len++;
+ text = xmalloc (1 + len);
+ sprintf (text, "\\%s", param);
+ }
+ else
+ {
+ int which;
+
+ /* Check against named parameters. */
+ for (which = 0; named && named[which]; which++)
+ if (STREQ (named[which], param))
+ break;
+
+ if (named && named[which])
+ {
+ text = which < length_of_actuals ? actuals[which] : NULL;
+ if (!text)
+ text = "";
+ len = strlen (text);
+ text = xstrdup (text); /* so we can free it */
+ }
+ else
+ { /* not a parameter, so it's an error. */
+ warning (_("\\ in macro expansion followed by `%s' instead of parameter name"),
+ param);
+ len++;
+ text = xmalloc (1 + len);
+ sprintf (text, "\\%s", param);
+ }
+ }
+
+ if (strlen (param) + 2 < len)
+ {
+ new_body_size += len + 1;
+ new_body = xrealloc (new_body, new_body_size);
+ }
+
+ free (param);
+
+ strcpy (new_body + new_body_index, text);
+ new_body_index += len;
+
+ free (text);
+ }
+ }
+
+ new_body[new_body_index] = 0;
+ return new_body;
+}
+
+/* Expand macro passed in DEF, a pointer to a MACRO_DEF, and
+ return its expansion as a string. */
+char *
+expand_macro (MACRO_DEF *def)
+{
+ char **arglist;
+ char *execution_string = NULL;
+ int start_line = line_number;
+
+ /* Gather the arguments present on the line if there are any. */
+ arglist = get_macro_args (def);
+
+ if (def->argcount < array_len (arglist))
+ {
+ free_array (arglist);
+ line_error (_("Macro `%s' called on line %d with too many args"),
+ def->name, start_line);
+ return execution_string;
+ }
+
+ if (def->body)
+ execution_string = apply (def->arglist, arglist, def->body);
+
+ free_array (arglist);
+ return execution_string;
+}
+
+/* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */
+void
+execute_macro (MACRO_DEF *def)
+{
+ char *execution_string;
+ int start_line = line_number, end_line;
+
+ if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
+ me_append_before_this_command ();
+
+ execution_string = expand_macro (def);
+ if (!execution_string)
+ return;
+
+ if (def->body)
+ {
+ /* Reset the line number to where the macro arguments began.
+ This makes line numbers reported in error messages correct in
+ case the macro arguments span several lines and the expanded
+ arguments invoke other commands. */
+ end_line = line_number;
+ line_number = start_line;
+
+ if (macro_expansion_output_stream
+ && !executing_string && !me_inhibit_expansion)
+ {
+ remember_itext (input_text, input_text_offset);
+ me_execute_string (execution_string);
+ }
+ else
+ execute_string ("%s", execution_string);
+
+ free (execution_string);
+ line_number = end_line;
+ }
+}
+
+
+/* Read and remember the definition of a macro. If RECURSIVE is set,
+ set the ME_RECURSE flag. MACTYPE is either "macro" or "rmacro", and
+ tells us what the matching @end should be. */
+static void
+define_macro (char *mactype, int recursive)
+{
+ int i, start;
+ char *name, *line;
+ char *last_end = NULL;
+ char *body = NULL;
+ char **arglist = NULL;
+ int body_size = 0, body_index = 0;
+ int depth = 1;
+ int flags = 0;
+ int defining_line = line_number;
+
+ if (macro_expansion_output_stream && !executing_string)
+ me_append_before_this_command ();
+
+ skip_whitespace ();
+
+ /* Get the name of the macro. This is the set of characters which are
+ not whitespace and are not `{' immediately following the @macro. */
+ start = input_text_offset;
+ {
+ int len;
+
+ for (i = start; i < input_text_length && input_text[i] != '{'
+ && !cr_or_whitespace (input_text[i]);
+ i++) ;
+
+ len = i - start;
+ name = xmalloc (1 + len);
+ memcpy (name, input_text + start, len);
+ name[len] = 0;
+ input_text_offset = i;
+ }
+
+ skip_whitespace ();
+
+ /* It is not required that the definition of a macro includes an arglist.
+ If not, don't try to get the named parameters, just use a null list. */
+ if (curchar () == '{')
+ {
+ int character;
+ int arglist_index = 0, arglist_size = 0;
+ int gathering_words = 1;
+ char *word = NULL;
+
+ /* Read the words inside of the braces which determine the arglist.
+ These words will be replaced within the body of the macro at
+ execution time. */
+
+ input_text_offset++;
+ skip_whitespace_and_newlines ();
+
+ while (gathering_words)
+ {
+ int len;
+
+ for (i = input_text_offset;
+ (character = input_text[i]);
+ i++)
+ {
+ switch (character)
+ {
+ case '\n':
+ line_number++;
+ case ' ':
+ case '\t':
+ case ',':
+ case '}':
+ /* Found the end of the current arglist word. Save it. */
+ len = i - input_text_offset;
+ word = xmalloc (1 + len);
+ memcpy (word, input_text + input_text_offset, len);
+ word[len] = 0;
+ input_text_offset = i;
+
+ /* Advance to the comma or close-brace that signified
+ the end of the argument. */
+ while ((character = curchar ())
+ && character != ','
+ && character != '}')
+ {
+ input_text_offset++;
+ if (character == '\n')
+ line_number++;
+ }
+
+ /* Add the word to our list of words. */
+ if (arglist_index + 2 >= arglist_size)
+ {
+ arglist_size += 10;
+ arglist = xrealloc (arglist,
+ arglist_size * sizeof (char *));
+ }
+
+ arglist[arglist_index++] = word;
+ arglist[arglist_index] = NULL;
+ break;
+ }
+
+ if (character == '}')
+ {
+ input_text_offset++;
+ gathering_words = 0;
+ break;
+ }
+
+ if (character == ',')
+ {
+ input_text_offset++;
+ skip_whitespace_and_newlines ();
+ i = input_text_offset - 1;
+ }
+ }
+ }
+ }
+
+ /* Read the text carefully until we find an "@end macro" which
+ matches this one. The text in between is the body of the macro. */
+ skip_whitespace_and_newlines ();
+
+ while (depth)
+ {
+ if ((input_text_offset + 9) > input_text_length)
+ {
+ file_line_error (input_filename, defining_line,
+ _("%cend macro not found"), COMMAND_PREFIX);
+ return;
+ }
+
+ get_rest_of_line (0, &line);
+
+ /* Handle commands only meaningful within a macro. */
+ if ((*line == COMMAND_PREFIX) && (depth == 1) &&
+ (strncmp (line + 1, "allow-recursion", 15) == 0) &&
+ (line[16] == 0 || whitespace (line[16])))
+ {
+ warning (_("@allow-recursion is deprecated; please use @rmacro instead"));
+ for (i = 16; whitespace (line[i]); i++);
+ strcpy (line, line + i);
+ flags |= ME_RECURSE;
+ if (!*line)
+ {
+ free (line);
+ continue;
+ }
+ }
+
+ if ((*line == COMMAND_PREFIX) && (depth == 1) &&
+ (strncmp (line + 1, "quote-arg", 9) == 0) &&
+ (line[10] == 0 || whitespace (line[10])))
+ {
+ warning (_("@quote-arg is deprecated; arguments are quoted by default"));
+ for (i = 10; whitespace (line[i]); i++);
+ strcpy (line, line + i);
+ if (!*line)
+ {
+ free (line);
+ continue;
+ }
+ }
+
+ if (*line == COMMAND_PREFIX
+ && (strncmp (line + 1, "macro ", 6) == 0
+ || strncmp (line + 1, "rmacro ", 7) == 0))
+ depth++;
+
+ /* Incorrect implementation of nesting -- just check that the last
+ @end matches what we started with. Since nested macros don't
+ work in TeX anyway, this isn't worth the trouble to get right. */
+ if (*line == COMMAND_PREFIX && strncmp (line + 1, "end macro", 9) == 0)
+ {
+ depth--;
+ last_end = "macro";
+ }
+ if (*line == COMMAND_PREFIX && strncmp (line + 1, "end rmacro", 10) == 0)
+ {
+ depth--;
+ last_end = "rmacro";
+ }
+
+ if (depth)
+ {
+ if ((body_index + strlen (line) + 3) >= body_size)
+ body = xrealloc (body, body_size += 3 + strlen (line));
+ strcpy (body + body_index, line);
+ body_index += strlen (line);
+ body[body_index++] = '\n';
+ body[body_index] = 0;
+ }
+ free (line);
+ }
+
+ /* Check that @end matched the macro command. */
+ if (!STREQ (last_end, mactype))
+ warning (_("mismatched @end %s with @%s"), last_end, mactype);
+
+ /* If it was an empty macro like
+ @macro foo
+ @end macro
+ create an empty body. (Otherwise, the macro is not expanded.) */
+ if (!body)
+ {
+ body = (char *)malloc(1);
+ *body = 0;
+ }
+
+ /* We now have the name, the arglist, and the body. However, BODY
+ includes the final newline which preceded the `@end macro' text.
+ Delete it. */
+ if (body && strlen (body))
+ body[strlen (body) - 1] = 0;
+
+ if (recursive)
+ flags |= ME_RECURSE;
+
+ add_macro (name, arglist, body, input_filename, defining_line, flags);
+
+ if (macro_expansion_output_stream && !executing_string)
+ {
+ /* Remember text for future expansions. */
+ remember_itext (input_text, input_text_offset);
+
+ /* Bizarrely, output the @macro itself. This is so texinfo.tex
+ will have a chance to read it when texi2dvi calls makeinfo -E.
+ The problem is that we don't really expand macros in all
+ contexts; a @table's @item is one. And a fix is not obvious to
+ me, since it appears virtually identical to any other internal
+ expansion. Just setting a variable in cm_item caused other
+ strange expansion problems. */
+ write_region_to_macro_output ("@", 0, 1);
+ write_region_to_macro_output (mactype, 0, strlen (mactype));
+ write_region_to_macro_output (" ", 0, 1);
+ write_region_to_macro_output (input_text, start, input_text_offset);
+ }
+}
+
+void
+cm_macro (void)
+{
+ define_macro ("macro", 0);
+}
+
+void
+cm_rmacro (void)
+{
+ define_macro ("rmacro", 1);
+}
+
+/* Delete the macro with name NAME. The macro is deleted from the list,
+ but it is also returned. If there was no macro defined, NULL is
+ returned. */
+
+static MACRO_DEF *
+delete_macro (char *name)
+{
+ int i;
+ MACRO_DEF *def;
+
+ def = NULL;
+
+ for (i = 0; macro_list && (def = macro_list[i]); i++)
+ if (strcmp (def->name, name) == 0)
+ {
+ memmove (macro_list + i, macro_list + i + 1,
+ ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
+ macro_list_len--;
+ break;
+ }
+ return def;
+}
+
+void
+cm_unmacro (void)
+{
+ int i;
+ char *line, *name;
+ MACRO_DEF *def;
+
+ if (macro_expansion_output_stream && !executing_string)
+ me_append_before_this_command ();
+
+ get_rest_of_line (0, &line);
+
+ for (i = 0; line[i] && !whitespace (line[i]); i++);
+ name = xmalloc (i + 1);
+ memcpy (name, line, i);
+ name[i] = 0;
+
+ def = delete_macro (name);
+
+ if (def)
+ {
+ free (def->source_file);
+ free (def->name);
+ free (def->body);
+
+ if (def->arglist)
+ {
+ int i;
+
+ for (i = 0; def->arglist[i]; i++)
+ free (def->arglist[i]);
+
+ free (def->arglist);
+ }
+
+ free (def);
+ }
+
+ free (line);
+ free (name);
+
+ if (macro_expansion_output_stream && !executing_string)
+ remember_itext (input_text, input_text_offset);
+}
+
+/* How to output sections of the input file verbatim. */
+
+/* Set the value of POINTER's offset to OFFSET. */
+ITEXT *
+remember_itext (char *pointer, int offset)
+{
+ int i;
+ ITEXT *itext = NULL;
+
+ /* If we have no info, initialize a blank list. */
+ if (!itext_info)
+ {
+ itext_info = xmalloc ((itext_size = 10) * sizeof (ITEXT *));
+ for (i = 0; i < itext_size; i++)
+ itext_info[i] = NULL;
+ }
+
+ /* If the pointer is already present in the list, then set the offset. */
+ for (i = 0; i < itext_size; i++)
+ if ((itext_info[i]) &&
+ (itext_info[i]->pointer == pointer))
+ {
+ itext = itext_info[i];
+ itext_info[i]->offset = offset;
+ break;
+ }
+
+ if (i == itext_size)
+ {
+ /* Find a blank slot (or create a new one), and remember the
+ pointer and offset. */
+ for (i = 0; i < itext_size; i++)
+ if (itext_info[i] == NULL)
+ break;
+
+ /* If not found, then add some slots. */
+ if (i == itext_size)
+ {
+ int j;
+
+ itext_info = xrealloc
+ (itext_info, (itext_size += 10) * sizeof (ITEXT *));
+
+ for (j = i; j < itext_size; j++)
+ itext_info[j] = NULL;
+ }
+
+ /* Now add the pointer and the offset. */
+ itext_info[i] = xmalloc (sizeof (ITEXT));
+ itext_info[i]->pointer = pointer;
+ itext_info[i]->offset = offset;
+ itext = itext_info[i];
+ }
+ return itext;
+}
+
+/* Forget the input text associated with POINTER. */
+void
+forget_itext (char *pointer)
+{
+ int i;
+
+ for (i = 0; i < itext_size; i++)
+ if (itext_info[i] && (itext_info[i]->pointer == pointer))
+ {
+ free (itext_info[i]);
+ itext_info[i] = NULL;
+ break;
+ }
+}
+
+/* Append the text which appeared in input_text from the last offset to
+ the character just before the command that we are currently executing. */
+void
+me_append_before_this_command (void)
+{
+ int i;
+
+ for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--)
+ ;
+ maybe_write_itext (input_text, i);
+}
+
+/* Similar to execute_string, but only takes a single string argument,
+ and remembers the input text location, etc. */
+void
+me_execute_string (char *execution_string)
+{
+ int saved_escape_html = escape_html;
+ int saved_in_paragraph = in_paragraph;
+ escape_html = me_executing_string == 0;
+ in_paragraph = 0;
+
+ pushfile ();
+ input_text_offset = 0;
+ /* The following xstrdup is so we can relocate input_text at will. */
+ input_text = xstrdup (execution_string);
+ input_filename = xstrdup (input_filename);
+ input_text_length = strlen (execution_string);
+
+ remember_itext (input_text, 0);
+
+ me_executing_string++;
+ reader_loop ();
+ free (input_text);
+ free (input_filename);
+ popfile ();
+ me_executing_string--;
+
+ in_paragraph = saved_in_paragraph;
+ escape_html = saved_escape_html;
+}
+
+/* A wrapper around me_execute_string which saves and restores
+ variables important for output generation. This is called
+ when we need to produce macro-expanded output for input which
+ leaves no traces in the Info output. */
+void
+me_execute_string_keep_state (char *execution_string, char *append_string)
+{
+ int op_orig, popen_orig;
+ int fill_orig, newline_orig, indent_orig, meta_pos_orig;
+
+ remember_itext (input_text, input_text_offset);
+ op_orig = output_paragraph_offset;
+ meta_pos_orig = meta_char_pos;
+ popen_orig = paragraph_is_open;
+ fill_orig = filling_enabled;
+ newline_orig = last_char_was_newline;
+ filling_enabled = 0;
+ indent_orig = no_indent;
+ no_indent = 1;
+ me_execute_string (execution_string);
+ if (append_string)
+ write_region_to_macro_output (append_string, 0, strlen (append_string));
+ output_paragraph_offset = op_orig;
+ meta_char_pos = meta_pos_orig;
+ paragraph_is_open = popen_orig;
+ filling_enabled = fill_orig;
+ last_char_was_newline = newline_orig;
+ no_indent = indent_orig;
+}
+
+/* Append the text which appears in input_text from the last offset to
+ the current OFFSET. */
+void
+append_to_expansion_output (int offset)
+{
+ int i;
+ ITEXT *itext = NULL;
+
+ for (i = 0; i < itext_size; i++)
+ if (itext_info[i] && itext_info[i]->pointer == input_text)
+ {
+ itext = itext_info[i];
+ break;
+ }
+
+ if (!itext)
+ return;
+
+ if (offset > itext->offset)
+ {
+ write_region_to_macro_output (input_text, itext->offset, offset);
+ remember_itext (input_text, offset);
+ }
+}
+
+/* Only write this input text iff it appears in our itext list. */
+void
+maybe_write_itext (char *pointer, int offset)
+{
+ int i;
+ ITEXT *itext = NULL;
+
+ for (i = 0; i < itext_size; i++)
+ if (itext_info[i] && (itext_info[i]->pointer == pointer))
+ {
+ itext = itext_info[i];
+ break;
+ }
+
+ if (itext && (itext->offset < offset))
+ {
+ write_region_to_macro_output (itext->pointer, itext->offset, offset);
+ remember_itext (pointer, offset);
+ }
+}
+
+void
+write_region_to_macro_output (char *string, int start, int end)
+{
+ if (macro_expansion_output_stream)
+ fwrite (string + start, 1, end - start, macro_expansion_output_stream);
+}
+
+/* Aliases. */
+
+typedef struct alias_struct
+{
+ char *alias;
+ char *mapto;
+ struct alias_struct *next;
+} alias_type;
+
+static alias_type *aliases;
+
+/* @alias aname = cmdname */
+
+void
+cm_alias (void)
+{
+ alias_type *a = xmalloc (sizeof (alias_type));
+
+ skip_whitespace ();
+ get_until_in_line (0, "=", &(a->alias));
+ canon_white (a->alias);
+
+ discard_until ("=");
+ skip_whitespace ();
+ get_until_in_line (0, " ", &(a->mapto));
+
+ a->next = aliases;
+ aliases = a;
+}
+
+/* Perform an alias expansion. Called from read_command. */
+char *
+alias_expand (char *tok)
+{
+ alias_type *findit = aliases;
+
+ while (findit)
+ if (strcmp (findit->alias, tok) == 0)
+ {
+ free (tok);
+ return alias_expand (xstrdup (findit->mapto));
+ }
+ else
+ findit = findit->next;
+
+ return tok;
+}
+
+/* definfoenclose implementation. */
+
+/* This structure is used to track enclosure macros. When an enclosure
+ macro is recognized, a pointer to the enclosure block corresponding
+ to its name is saved in the brace element for its argument. */
+typedef struct enclose_struct
+{
+ char *enclose;
+ char *before;
+ char *after;
+ struct enclose_struct *next;
+} enclosure_type;
+
+static enclosure_type *enclosures;
+
+typedef struct enclosure_stack_struct
+{
+ enclosure_type *current;
+ struct enclosure_stack_struct *next;
+} enclosure_stack_type;
+
+static enclosure_stack_type *enclosure_stack;
+
+/* @definfoenclose */
+void
+cm_definfoenclose (void)
+{
+ enclosure_type *e = xmalloc (sizeof (enclosure_type));
+
+ skip_whitespace ();
+ get_until_in_line (1, ",", &(e->enclose));
+ discard_until (",");
+ get_until_in_line (0, ",", &(e->before));
+ discard_until (",");
+ get_until_in_line (0, "\n", &(e->after));
+
+ e->next = enclosures;
+ enclosures = e;
+}
+
+/* If TOK is an enclosure command, push it on the enclosure stack and
+ return 1. Else return 0. */
+
+int
+enclosure_command (char *tok)
+{
+ enclosure_type *findit = enclosures;
+
+ while (findit)
+ if (strcmp (findit->enclose, tok) == 0)
+ {
+ enclosure_stack_type *new = xmalloc (sizeof (enclosure_stack_type));
+ new->current = findit;
+ new->next = enclosure_stack;
+ enclosure_stack = new;
+
+ return 1;
+ }
+ else
+ findit = findit->next;
+
+ return 0;
+}
+
+/* actually perform the enclosure expansion */
+void
+enclosure_expand (int arg, int start, int end)
+{
+ if (arg == START)
+ add_word (enclosure_stack->current->before);
+ else
+ {
+ enclosure_stack_type *temp;
+
+ add_word (enclosure_stack->current->after);
+
+ temp = enclosure_stack;
+ enclosure_stack = enclosure_stack->next;
+ free (temp);
+ }
+}
diff --git a/makeinfo/macro.h b/makeinfo/macro.h
new file mode 100644
index 0000000..de9c65a
--- /dev/null
+++ b/makeinfo/macro.h
@@ -0,0 +1,77 @@
+/* macro.h -- declarations for macro.c.
+ $Id: macro.h,v 1.6 2007/07/01 21:20:32 karl Exp $
+
+ Copyright (C) 1998, 1999, 2007 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef MACRO_H
+#define MACRO_H
+
+extern FILE *macro_expansion_output_stream;
+extern char *macro_expansion_filename;
+extern int me_executing_string;
+extern int only_macro_expansion;
+
+/* Here is a structure used to remember input text strings and offsets
+ within them. */
+typedef struct {
+ char *pointer; /* Pointer to the input text. */
+ int offset; /* Offset of the last character output. */
+} ITEXT;
+
+/* Macro definitions for user-defined commands. */
+typedef struct {
+ char *name; /* Name of the macro. */
+ char **arglist; /* Args to replace when executing. */
+ int argcount; /* Number of args in arglist */
+ char *body; /* Macro body. */
+ char *source_file; /* File where this macro is defined. */
+ int source_lineno; /* Line number within FILENAME. */
+ int inhibited; /* Nonzero means make find_macro () fail. */
+ int flags; /* ME_RECURSE, etc. */
+} MACRO_DEF;
+
+/* flags for MACRO_DEF */
+#define ME_RECURSE 0x01
+
+extern void execute_macro (MACRO_DEF *def);
+extern MACRO_DEF *find_macro (char *name);
+extern char *expand_macro (MACRO_DEF *def);
+
+extern ITEXT *remember_itext (char *pointer, int offset);
+extern void forget_itext (char *pointer);
+extern void maybe_write_itext (char *pointer, int offset);
+extern void write_region_to_macro_output (char *string, int start, int end);
+extern void append_to_expansion_output (int offset);
+extern void me_append_before_this_command (void);
+extern void me_execute_string (char *execution_string);
+extern void me_execute_string_keep_state (char *execution_string,
+ char *append_string);
+
+extern char *alias_expand (char *tok);
+extern int enclosure_command (char *tok);
+extern void enclosure_expand (int arg, int start, int end);
+
+/* The @commands. */
+extern void cm_macro (void), cm_rmacro (void), cm_unmacro (void);
+extern void cm_alias (void), cm_definfoenclose (void);
+
+extern int array_len (char **array);
+extern void free_array (char **array);
+
+enum quote_type { quote_none, quote_single, quote_many };
+extern char **get_brace_args (enum quote_type type);
+
+#endif /* not MACRO_H */
diff --git a/makeinfo/makeinfo.c b/makeinfo/makeinfo.c
new file mode 100644
index 0000000..362687b
--- /dev/null
+++ b/makeinfo/makeinfo.c
@@ -0,0 +1,4397 @@
+/* makeinfo -- convert Texinfo source into other formats.
+ $Id: makeinfo.c,v 1.123 2008/08/28 22:53:30 karl Exp $
+
+ Copyright (C) 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Original author of makeinfo: Brian Fox (bfox@ai.mit.edu). */
+
+#include "system.h"
+#include "getopt.h"
+#include "mbswidth.h"
+
+#define COMPILING_MAKEINFO
+#include "makeinfo.h"
+#include "cmds.h"
+#include "files.h"
+#include "float.h"
+#include "footnote.h"
+#include "html.h"
+#include "index.h"
+#include "insertion.h"
+#include "lang.h"
+#include "macro.h"
+#include "node.h"
+#include "sectioning.h"
+#include "toc.h"
+#include "xml.h"
+
+/* You can change some of the behavior of Makeinfo by changing the
+ following defines: */
+
+/* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
+ appear within an @table, @ftable, or @itemize environment to have
+ standard paragraph indentation. Without this, such paragraphs have
+ no starting indentation. */
+/* #define INDENT_PARAGRAPHS_IN_TABLE */
+
+/* Define PARAGRAPH_START_INDENT to be the amount of indentation that
+ the first lines of paragraphs receive by default, where no other
+ value has been specified. Users can change this value on the command
+ line, with the --paragraph-indent option, or within the texinfo file,
+ with the @paragraphindent command. */
+#define PARAGRAPH_START_INDENT 3
+
+/* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
+ wish to appear between paragraphs. A value of 1 creates a single blank
+ line between paragraphs. Paragraphs are defined by 2 or more consecutive
+ newlines in the input file (i.e., one or more blank lines). */
+#define DEFAULT_PARAGRAPH_SPACING 1
+
+/* Global variables. */
+
+/* The output file name. */
+char *output_filename = NULL;
+
+/* Name of the output file that the user elected to pass on the command line.
+ Such a name overrides any name found with the @setfilename command. */
+char *command_output_filename = NULL;
+static char *save_command_output_filename = NULL;
+
+/* The amount of indentation to add at the starts of paragraphs.
+ 0 means don't change existing indentation at paragraph starts.
+ > 0 is amount to indent new paragraphs by.
+ < 0 means indent to column zero by removing indentation if necessary.
+
+ This is normally zero, but some people prefer paragraph starts to be
+ somewhat more indented than paragraph bodies. A pretty value for
+ this is 3. */
+int paragraph_start_indent = PARAGRAPH_START_INDENT;
+
+/* Indentation that is pending insertion. We have this for hacking lines
+ which look blank, but contain whitespace. We want to treat those as
+ blank lines. */
+int pending_indent = 0;
+
+/* The index in our internal command table of the currently
+ executing command. */
+int command_index;
+
+/* A search string which is used to find the first @setfilename. */
+char setfilename_search[] =
+ { COMMAND_PREFIX,
+ 's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', 0 };
+
+/* Values for calling handle_variable_internal (). */
+#define SET 1
+#define CLEAR 2
+#define IFSET 3
+#define IFCLEAR 4
+
+/* Flags controlling the operation of the program. */
+
+/* Default is to remove output if there were errors. */
+int force = 0;
+
+/* Default is to notify users of bad choices. */
+int print_warnings = 1;
+
+/* Number of errors that we tolerate on a given fileset. */
+int max_error_level = 100;
+
+/* The actual last inserted character. Note that this may be something
+ other than NEWLINE even if last_char_was_newline is 1. */
+int last_inserted_character = 0;
+
+/* Nonzero means that a newline character has already been
+ inserted, so close_paragraph () should insert one less. */
+int line_already_broken = 0;
+
+/* When nonzero we have finished an insertion (see end_insertion ()) and we
+ want to ignore false continued paragraph closings. */
+int insertion_paragraph_closed = 0;
+
+/* Nonzero means attempt to make all of the lines have fill_column width. */
+int do_justification = 0;
+
+/* Nonzero means don't replace whitespace with &nbsp; in HTML mode. */
+int in_html_elt = 0;
+
+/* Nonzero means we are inserting a block level HTML element that must not be
+ enclosed in a <p>, such as <ul>, <ol> and <h?>. */
+int in_html_block_level_elt = 0;
+
+/* True when expanding a macro definition. */
+static int executing_macro = 0;
+
+/* True when we are inside a <li> block of a menu. */
+static int in_menu_item = 0;
+
+/* The column position of output_paragraph[0]. This is not saved and restored
+ in the multitable code because flush_output () is only called in environment
+ 0. */
+static int output_paragraph_start_column = 0;
+
+typedef struct brace_element
+{
+ struct brace_element *next;
+ COMMAND_FUNCTION *proc;
+ char *command;
+ int pos, line;
+ int in_fixed_width_font;
+} BRACE_ELEMENT;
+
+BRACE_ELEMENT *brace_stack = NULL;
+
+static void convert_from_file (char *name);
+static void convert_from_loaded_file (char *name);
+static void convert_from_stream (FILE *stream, char *name);
+static void do_flush_right_indentation (void);
+static void handle_variable (int action);
+static void handle_variable_internal (int action, char *name);
+static void init_brace_stack (void);
+static void init_internals (void);
+static void pop_and_call_brace (void);
+static void remember_brace (COMMAND_FUNCTION (*proc));
+static void write_trailer (char *filename, char *trailer);
+static int end_of_sentence_p (void);
+
+void maybe_update_execution_strings (char **text, unsigned int new_len);
+
+/* Error handling. */
+
+/* Number of errors encountered. */
+int errors_printed = 0;
+
+/* Remember that an error has been printed. If more than
+ max_error_level have been printed, then exit the program. */
+static void
+remember_error (void)
+{
+ errors_printed++;
+ if (max_error_level && (errors_printed > max_error_level))
+ {
+ fprintf (stderr, _("Too many errors! Gave up.\n"));
+ flush_file_stack ();
+ if (errors_printed - max_error_level < 2)
+ cm_bye ();
+ xexit (1);
+ }
+}
+
+/* Print the last error gotten from the file system. */
+int
+fs_error (char *filename)
+{
+ remember_error ();
+ perror (filename);
+ return 0;
+}
+
+/* Print an error message, and return false. */
+void
+#if defined (VA_FPRINTF) && __STDC__
+error (const char *format, ...)
+#else
+error (format, va_alist)
+ const char *format;
+ va_dcl
+#endif
+{
+#ifdef VA_FPRINTF
+ va_list ap;
+#endif
+
+ remember_error ();
+
+ VA_START (ap, format);
+#ifdef VA_FPRINTF
+ VA_FPRINTF (stderr, format, ap);
+#else
+ fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* not VA_FPRINTF */
+ va_end (ap);
+
+ putc ('\n', stderr);
+}
+
+/* Just like error (), but print the input file and line number as well. */
+void
+#if defined (VA_FPRINTF) && __STDC__
+file_line_error (char *infile, int lno, const char *format, ...)
+#else
+file_line_error (infile, lno, format, va_alist)
+ char *infile;
+ int lno;
+ const char *format;
+ va_dcl
+#endif
+{
+#ifdef VA_FPRINTF
+ va_list ap;
+#endif
+
+ remember_error ();
+ fprintf (stderr, "%s:%d: ", infile, lno);
+
+ VA_START (ap, format);
+#ifdef VA_FPRINTF
+ VA_FPRINTF (stderr, format, ap);
+#else
+ fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* not VA_FPRINTF */
+ va_end (ap);
+
+ fprintf (stderr, ".\n");
+}
+
+/* Just like file_line_error (), but take the input file and the line
+ number from global variables. */
+void
+#if defined (VA_FPRINTF) && __STDC__
+line_error (const char *format, ...)
+#else
+line_error (format, va_alist)
+ const char *format;
+ va_dcl
+#endif
+{
+#ifdef VA_FPRINTF
+ va_list ap;
+#endif
+
+ remember_error ();
+ fprintf (stderr, "%s:%d: ", input_filename, line_number);
+
+ VA_START (ap, format);
+#ifdef VA_FPRINTF
+ VA_FPRINTF (stderr, format, ap);
+#else
+ fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* not VA_FPRINTF */
+ va_end (ap);
+
+ fprintf (stderr, ".\n");
+}
+
+void
+#if defined (VA_FPRINTF) && __STDC__
+warning (const char *format, ...)
+#else
+warning (format, va_alist)
+ const char *format;
+ va_dcl
+#endif
+{
+#ifdef VA_FPRINTF
+ va_list ap;
+#endif
+
+ if (print_warnings)
+ {
+ fprintf (stderr, _("%s:%d: warning: "), input_filename, line_number);
+
+ VA_START (ap, format);
+#ifdef VA_FPRINTF
+ VA_FPRINTF (stderr, format, ap);
+#else
+ fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* not VA_FPRINTF */
+ va_end (ap);
+
+ fprintf (stderr, ".\n");
+ }
+}
+
+
+/* The other side of a malformed expression. */
+static void
+misplaced_brace (void)
+{
+ line_error (_("Misplaced %c"), '}');
+}
+
+/* Main. */
+
+/* Display the version info of this invocation of Makeinfo. */
+static void
+print_version_info (void)
+{
+ printf ("makeinfo (GNU %s) %s\n", PACKAGE, VERSION);
+}
+
+/* If EXIT_VALUE is zero, print the full usage message to stdout.
+ Otherwise, just say to use --help for more info.
+ Then exit with EXIT_VALUE. */
+static void
+usage (int exit_value)
+{
+ if (exit_value != 0)
+ fprintf (stderr, _("Try `%s --help' for more information.\n"), progname);
+ else
+ {
+ printf (_("Usage: %s [OPTION]... TEXINFO-FILE...\n"), progname);
+ puts ("");
+
+ puts (_("\
+Translate Texinfo source documentation to various other formats, by default\n\
+Info files suitable for reading online with Emacs or standalone GNU Info.\n"));
+
+ printf (_("\
+General options:\n\
+ --error-limit=NUM quit after NUM errors (default %d).\n\
+ --document-language=STR locale to use in translating Texinfo keywords\n\
+ for the output document (default C).\n\
+ --force preserve output even if errors.\n\
+ --help display this help and exit.\n\
+ --no-validate suppress node cross-reference validation.\n\
+ --no-warn suppress warnings (but not errors).\n\
+ -v, --verbose explain what is being done.\n\
+ --version display version information and exit.\n"),
+ max_error_level);
+ puts ("");
+
+ /* xgettext: no-wrap */
+ puts (_("\
+Output format selection (default is to produce Info):\n\
+ --docbook output Docbook XML rather than Info.\n\
+ --html output HTML rather than Info.\n\
+ --xml output Texinfo XML rather than Info.\n\
+ --plaintext output plain text rather than Info.\n\
+"));
+
+ puts (_("\
+General output options:\n\
+ -E, --macro-expand=FILE output macro-expanded source to FILE,\n\
+ ignoring any @setfilename.\n\
+ --no-headers suppress node separators, Node: lines, and menus\n\
+ from Info output (thus producing plain text)\n\
+ or from HTML (thus producing shorter output);\n\
+ also, write to standard output by default.\n\
+ --no-split suppress the splitting of Info or HTML output,\n\
+ generate only one output file.\n\
+ --number-sections output chapter and sectioning numbers.\n\
+ -o, --output=FILE output to FILE (or directory if split HTML).\n\
+"));
+
+ printf (_("\
+Options for Info and plain text:\n\
+ --disable-encoding do not output accented and special characters\n\
+ in Info output based on @documentencoding.\n\
+ --enable-encoding override --disable-encoding (default).\n\
+ --fill-column=NUM break Info lines at NUM characters (default %d).\n\
+ --footnote-style=STYLE output footnotes in Info according to STYLE:\n\
+ `separate' to put them in their own node;\n\
+ `end' to put them at the end of the node, in\n\
+ which they are defined (this is the default).\n\
+ --paragraph-indent=VAL indent Info paragraphs by VAL spaces (default %d).\n\
+ If VAL is `none', do not indent; if VAL is\n\
+ `asis', preserve existing indentation.\n\
+ --split-size=NUM split Info files at size NUM (default %d).\n"),
+ fill_column, paragraph_start_indent,
+ DEFAULT_SPLIT_SIZE);
+ puts ("");
+
+ puts (_("\
+Options for HTML:\n\
+ --css-include=FILE include FILE in HTML <style> output;\n\
+ read stdin if FILE is -.\n\
+ --css-ref=URL generate reference to a CSS file.\n\
+ --internal-links=FILE produce list of internal links in FILE.\n\
+ --transliterate-file-names\n\
+ produce file names in ASCII transliteration.\n\
+"));
+
+ printf (_("\
+Options for XML and Docbook:\n\
+ --output-indent=VAL indent XML elements by VAL spaces (default %d).\n\
+ If VAL is 0, ignorable whitespace is dropped.\n\
+"), xml_indentation_increment);
+ puts ("");
+
+ puts (_("\
+Input file options:\n\
+ --commands-in-node-names allow @ commands in node names.\n\
+ -D VAR define the variable VAR, as with @set.\n\
+ -I DIR append DIR to the @include search path.\n\
+ -P DIR prepend DIR to the @include search path.\n\
+ -U VAR undefine the variable VAR, as with @clear.\n\
+"));
+
+ puts (_("\
+Conditional processing in input:\n\
+ --ifdocbook process @ifdocbook and @docbook even if\n\
+ not generating Docbook.\n\
+ --ifhtml process @ifhtml and @html even if not generating HTML.\n\
+ --ifinfo process @ifinfo even if not generating Info.\n\
+ --ifplaintext process @ifplaintext even if not generating plain text.\n\
+ --iftex process @iftex and @tex; implies --no-split.\n\
+ --ifxml process @ifxml and @xml.\n\
+ --no-ifdocbook do not process @ifdocbook and @docbook text.\n\
+ --no-ifhtml do not process @ifhtml and @html text.\n\
+ --no-ifinfo do not process @ifinfo text.\n\
+ --no-ifplaintext do not process @ifplaintext text.\n\
+ --no-iftex do not process @iftex and @tex text.\n\
+ --no-ifxml do not process @ifxml and @xml text.\n\
+\n\
+ Also, for the --no-ifFORMAT options, do process @ifnotFORMAT text.\n\
+"));
+
+ puts (_("\
+ The defaults for the @if... conditionals depend on the output format:\n\
+ if generating HTML, --ifhtml is on and the others are off;\n\
+ if generating Info, --ifinfo is on and the others are off;\n\
+ if generating plain text, --ifplaintext is on and the others are off;\n\
+ if generating XML, --ifxml is on and the others are off.\n\
+"));
+
+ fputs (_("\
+Examples:\n\
+ makeinfo foo.texi write Info to foo's @setfilename\n\
+ makeinfo --html foo.texi write HTML to @setfilename\n\
+ makeinfo --xml foo.texi write Texinfo XML to @setfilename\n\
+ makeinfo --docbook foo.texi write DocBook XML to @setfilename\n\
+ makeinfo --no-headers foo.texi write plain text to standard output\n\
+\n\
+ makeinfo --html --no-headers foo.texi write html without node lines, menus\n\
+ makeinfo --number-sections foo.texi write Info with numbered sections\n\
+ makeinfo --no-split foo.texi write one Info file however big\n\
+"), stdout);
+
+ puts (_("\n\
+Email bug reports to bug-texinfo@gnu.org,\n\
+general questions and discussion to help-texinfo@gnu.org.\n\
+Texinfo home page: http://www.gnu.org/software/texinfo/"));
+
+ } /* end of full help */
+
+ xexit (exit_value);
+}
+
+#define OPT_CSSREF 256
+#define OPT_TRANSLITERATE_FILE_NAMES 257
+#define OPT_INTERNAL_LINKS 258
+
+struct option long_options[] =
+{
+ { "commands-in-node-names", 0, &expensive_validation, 1 },
+ { "css-include", 1, 0, 'C' },
+ { "css-ref", 1, 0, OPT_CSSREF },
+ { "docbook", 0, 0, 'd' },
+ { "disable-encoding", 0, &enable_encoding, 0 },
+ { "enable-encoding", 0, &enable_encoding, 1 },
+ { "document-language", 1, 0, 'l' },
+ { "error-limit", 1, 0, 'e' },
+ { "fill-column", 1, 0, 'f' },
+ { "footnote-style", 1, 0, 's' },
+ { "force", 0, &force, 1 },
+ { "help", 0, 0, 'h' },
+ { "html", 0, 0, 'w' },
+ { "ifdocbook", 0, &process_docbook, 1 },
+ { "ifhtml", 0, &process_html, 1 },
+ { "ifinfo", 0, &process_info, 1 },
+ { "ifplaintext", 0, &process_plaintext, 1 },
+ { "iftex", 0, &process_tex, 1 },
+ { "ifxml", 0, &process_xml, 1 },
+ { "internal-links", 1, 0, OPT_INTERNAL_LINKS },
+ { "macro-expand", 1, 0, 'E' },
+ { "no-headers", 0, &no_headers, 1 },
+ { "no-ifdocbook", 0, &process_docbook, 0 },
+ { "no-ifhtml", 0, &process_html, 0 },
+ { "no-ifinfo", 0, &process_info, 0 },
+ { "no-ifplaintext", 0, &process_plaintext, 0 },
+ { "no-iftex", 0, &process_tex, 0 },
+ { "no-ifxml", 0, &process_xml, 0 },
+ { "no-number-footnotes", 0, &number_footnotes, 0 },
+ { "no-number-sections", 0, &number_sections, 0 },
+ { "no-pointer-validate", 0, &validating, 0 },
+ { "no-split", 0, &splitting, 0 },
+ { "no-validate", 0, &validating, 0 },
+ { "no-warn", 0, &print_warnings, 0 },
+ { "number-footnotes", 0, &number_footnotes, 1 },
+ { "number-sections", 0, &number_sections, 1 },
+ { "output", 1, 0, 'o' },
+ { "output-indent", 1, 0, 'i' },
+ { "paragraph-indent", 1, 0, 'p' },
+ { "plaintext", 0, 0, 't' },
+ { "reference-limit", 1, 0, 'r' },
+ { "split-size", 1, 0, 'S'},
+ { "transliterate-file-names", 0, &transliterate_file_names, 1 },
+ { "verbose", 0, &verbose_mode, 1 },
+ { "version", 0, 0, 'V' },
+ { "xml", 0, 0, 'x' },
+ {NULL, 0, NULL, 0}
+};
+
+/* We use handle_variable_internal for -D and -U, and it depends on
+ execute_string, which depends on input_filename, which is not defined
+ while we are handling options. :-\ So we save these defines in this
+ struct, and handle them later. */
+typedef struct command_line_define
+{
+ struct command_line_define *next;
+ int action;
+ char *define;
+} COMMAND_LINE_DEFINE;
+
+static COMMAND_LINE_DEFINE *command_line_defines = NULL;
+
+/* For each file mentioned in the command line, process it, turning
+ Texinfo commands into wonderfully formatted output text. */
+int
+main (int argc, char *argv[])
+{
+ int c, ind;
+ int reading_from_stdin = 0;
+
+#ifdef HAVE_SETLOCALE
+ /* Do not use LC_ALL, because LC_NUMERIC screws up the scanf parsing
+ of the argument to @multicolumn. */
+ setlocale (LC_TIME, "");
+#ifdef LC_MESSAGES /* ultrix, djgpp 2.04 */
+ setlocale (LC_MESSAGES, "");
+#endif
+ setlocale (LC_CTYPE, "");
+ setlocale (LC_COLLATE, "");
+#endif
+
+#ifdef ENABLE_NLS
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+#endif
+
+ /* If TEXINFO_OUTPUT_FORMAT envvar is set, use it to set default output.
+ Can be overridden with one of the output options. */
+ if (getenv ("TEXINFO_OUTPUT_FORMAT") != NULL)
+ {
+ if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "docbook"))
+ {
+ splitting = 0;
+ html = 0;
+ docbook = 1;
+ xml = 1;
+ process_docbook = 1;
+ }
+ else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "html"))
+ {
+ html = 1;
+ docbook = 0;
+ xml = 0;
+ process_html = 1;
+ }
+ else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "info"))
+ {
+ html = 0;
+ docbook = 0;
+ xml = 0;
+ }
+ else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "plaintext"))
+ {
+ splitting = 0;
+ no_headers = 1;
+ html = 0;
+ docbook = 0;
+ xml = 0;
+ process_plaintext = 1;
+ }
+ else if (STREQ (getenv ("TEXINFO_OUTPUT_FORMAT"), "xml"))
+ {
+ splitting = 0;
+ html = 0;
+ docbook = 0;
+ xml = 1;
+ process_xml = 1;
+ }
+ else
+ fprintf (stderr,
+ _("%s: Ignoring unrecognized TEXINFO_OUTPUT_FORMAT value `%s'.\n"),
+ progname, getenv ("TEXINFO_OUTPUT_FORMAT"));
+ }
+
+ /* Parse argument flags from the input line. */
+ while ((c = getopt_long (argc, argv, "D:de:E:f:hI:i:o:p:P:r:s:t:U:vV:wx",
+ long_options, &ind)) != EOF)
+ {
+ if (c == 0 && long_options[ind].flag == 0)
+ c = long_options[ind].val;
+
+ switch (c)
+ {
+ case 'C': /* --css-include */
+ css_include = xstrdup (optarg);
+ break;
+
+ case OPT_CSSREF:
+ css_ref = xstrdup (optarg);
+ break;
+
+ case 'D':
+ case 'U':
+ /* User specified variable to set or clear. */
+ if (xml && !docbook)
+ {
+ COMMAND_LINE_DEFINE *new = xmalloc (sizeof (COMMAND_LINE_DEFINE));
+ new->action = (c == 'D') ? SET : CLEAR;
+ new->define = xstrdup (optarg);
+ new->next = command_line_defines;
+ command_line_defines = new;
+ }
+ else
+ handle_variable_internal ((c == 'D' ? SET : CLEAR), optarg);
+ break;
+
+ case 'd': /* --docbook */
+ splitting = 0;
+ xml = 1;
+ docbook = 1;
+ html = 0;
+ process_docbook = 1;
+ break;
+
+ case 'e': /* --error-limit */
+ if (sscanf (optarg, "%d", &max_error_level) != 1)
+ {
+ fprintf (stderr,
+ _("%s: %s arg must be numeric, not `%s'.\n"),
+ progname, "--error-limit", optarg);
+ usage (1);
+ }
+ break;
+
+ case 'E': /* --macro-expand */
+ if (!macro_expansion_output_stream)
+ {
+ macro_expansion_filename = optarg;
+ macro_expansion_output_stream
+ = strcmp (optarg, "-") == 0 ? stdout : fopen (optarg, "w");
+ if (!macro_expansion_output_stream)
+ error (_("%s: could not open macro expansion output `%s'"),
+ progname, optarg);
+ }
+ else
+ fprintf (stderr,
+ _("%s: ignoring second macro expansion output `%s'.\n"),
+ progname, optarg);
+ break;
+
+ case 'f': /* --fill-column */
+ if (sscanf (optarg, "%d", &fill_column) != 1)
+ {
+ fprintf (stderr,
+ _("%s: %s arg must be numeric, not `%s'.\n"),
+ progname, "--fill-column", optarg);
+ usage (1);
+ }
+ break;
+
+ case 'h': /* --help */
+ usage (0);
+ break;
+
+ case 'I':
+ /* Append user-specified dir to include file path. */
+ append_to_include_path (optarg);
+ break;
+
+ case 'l':
+ /* save the override language code */
+ document_language = xstrdup (optarg);
+ break;
+
+ case 'i':
+ if (sscanf (optarg, "%d", &xml_indentation_increment) != 1)
+ {
+ fprintf (stderr,
+ _("%s: %s arg must be numeric, not `%s'.\n"),
+ progname, "--output-indent", optarg);
+ usage (1);
+ }
+ break;
+
+ case OPT_INTERNAL_LINKS:
+ if (!internal_links_stream)
+ {
+ internal_links_filename = xstrdup (optarg);
+ internal_links_stream = strcmp (optarg, "-") == 0 ? stdout :
+ fopen (optarg, "w");
+ if (!internal_links_stream)
+ error (_("%s: could not open internal links output `%s'"),
+ progname, optarg);
+ }
+ else
+ fprintf (stderr,
+ _("%s: ignoring second internal links output `%s'.\n"),
+ progname, optarg);
+ break;
+
+ case 'o': /* --output */
+ command_output_filename = xstrdup (optarg);
+ save_command_output_filename = command_output_filename;
+ break;
+
+ case 'p': /* --paragraph-indent */
+ if (set_paragraph_indent (optarg) < 0)
+ {
+ fprintf (stderr,
+ _("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"),
+ progname, optarg);
+ usage (1);
+ }
+ break;
+
+ case 'P':
+ /* Prepend user-specified include dir to include path. */
+ prepend_to_include_path (optarg);
+ break;
+
+ case 'r': /* was --reference-limit; obsolete, ignore */
+ break;
+
+ case 's': /* --footnote-style */
+ if (set_footnote_style (optarg) < 0)
+ {
+ fprintf (stderr,
+ _("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"),
+ progname, optarg);
+ usage (1);
+ }
+ footnote_style_preset = 1;
+ break;
+
+ case 'S': /* --split-size */
+ if (sscanf (optarg, "%d", &split_size) != 1)
+ {
+ fprintf (stderr,
+ _("%s: %s arg must be numeric, not `%s'.\n"),
+ progname, "--split-size", optarg);
+ usage (1);
+ }
+ break;
+
+ case 't': /* --plaintext */
+ splitting = 0;
+ no_headers = 1;
+ html = 0;
+ docbook = 0;
+ xml = 0;
+ process_plaintext = 1;
+ break;
+
+ case 'v':
+ verbose_mode++;
+ break;
+
+ case 'V': /* --version */
+ print_version_info ();
+ puts ("");
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n"),
+ "2008");
+ xexit (0);
+ break;
+
+ case 'w': /* --html */
+ xml = 0;
+ docbook = 0;
+ html = 1;
+ process_html = 1;
+ break;
+
+ case 'x': /* --xml */
+ splitting = 0;
+ html = 0;
+ docbook = 0;
+ xml = 1;
+ process_xml = 1;
+ break;
+
+ case '?':
+ usage (1);
+ break;
+ }
+ }
+
+ if (macro_expansion_output_stream)
+ validating = 0;
+
+ if (!validating)
+ expensive_validation = 0;
+
+ if (optind == argc)
+ {
+ /* Check to see if input is a file. If so, process that. */
+ if (!isatty (fileno (stdin)))
+ reading_from_stdin = 1;
+ else
+ {
+ fprintf (stderr, _("%s: missing file argument.\n"), progname);
+ usage (1);
+ }
+ }
+
+ if (no_headers)
+ {
+ /* If the user did not specify an output file, use stdout. */
+ if (!command_output_filename)
+ command_output_filename = xstrdup ("-");
+
+ if (html && splitting && !STREQ (command_output_filename, "-"))
+ { /* --no-headers --no-split --html indicates confusion. */
+ fprintf (stderr,
+ "%s: can't split --html output to `%s' with --no-headers.\n",
+ progname, command_output_filename);
+ usage (1);
+ }
+
+ /* --no-headers implies --no-split. */
+ splitting = 0;
+ }
+
+ if (process_info == -1)
+ { /* no explicit --[no-]ifinfo option, so we'll do @ifinfo
+ if we're generating info or (for compatibility) plain text. */
+ process_info = !html && !xml;
+ }
+
+ if (process_plaintext == -1)
+ { /* no explicit --[no-]ifplaintext option, so we'll do @ifplaintext
+ if we're generating plain text. */
+ process_plaintext = no_headers && !html && !xml;
+ }
+
+ if (verbose_mode)
+ print_version_info ();
+
+ /* Remaining arguments are file names of texinfo files.
+ Convert them, one by one. */
+ if (!reading_from_stdin)
+ {
+ while (optind != argc)
+ {
+ if (STREQ (argv[optind], "-"))
+ convert_from_stream (stdin, "stdin");
+ else
+ convert_from_file (argv[optind]);
+ optind++;
+ }
+ }
+ else
+ convert_from_stream (stdin, "stdin");
+
+ xexit (errors_printed ? 2 : 0);
+ return 0; /* Avoid bogus warnings. */
+}
+
+/* Hacking tokens and strings. */
+
+/* Return the next token as a string pointer. We cons the string. This
+ `token' means simply a command name. */
+
+/* = is so @alias works. ^ and _ are so macros can be used in math mode
+ without a space following. Possibly we should simply allow alpha, to
+ be compatible with TeX. */
+#define COMMAND_CHAR(c) (!cr_or_whitespace(c) \
+ && (c) != '{' \
+ && (c) != '}' \
+ && (c) != '=' \
+ && (c) != '_' \
+ && (c) != '^' \
+ )
+
+static char *
+read_token (void)
+{
+ int i, character;
+ char *result;
+
+ /* If the first character to be read is self-delimiting, then that
+ is the command itself. */
+ character = curchar ();
+ if (self_delimiting (character))
+ {
+ input_text_offset++;
+
+ if (character == '\n')
+ line_number++;
+
+ result = xstrdup (" ");
+ *result = character;
+ return result;
+ }
+
+ for (i = 0; ((input_text_offset != input_text_length)
+ && (character = curchar ())
+ && COMMAND_CHAR (character));
+ i++, input_text_offset++);
+ result = xmalloc (i + 1);
+ memcpy (result, &input_text[input_text_offset - i], i);
+ result[i] = 0;
+ return result;
+}
+
+/* Return nonzero if CHARACTER is self-delimiting. */
+int
+self_delimiting (int character)
+{
+ /* @; and @\ are not Texinfo commands, but they are listed here
+ anyway. I don't know why. --karl, 10aug96. */
+ return strchr ("~{|}`^\\@?=;:./-,*\'\" !\n\t", character) != NULL;
+}
+
+/* Clear whitespace from the front and end of string. */
+void
+canon_white (char *string)
+{
+ char *p = string;
+ unsigned len;
+
+ if (!*p)
+ return;
+
+ do
+ {
+ if (!cr_or_whitespace (*p))
+ break;
+ ++p;
+ }
+ while (*p);
+
+ len = strlen (p);
+ while (len && cr_or_whitespace (p[len-1]))
+ --len;
+
+ if (p != string)
+ memmove (string, p, len);
+
+ string[len] = 0;
+}
+
+/* Bash STRING, replacing all whitespace with just one space. */
+void
+fix_whitespace (char *string)
+{
+ char *temp = xmalloc (strlen (string) + 1);
+ int string_index = 0;
+ int temp_index = 0;
+ int c;
+
+ canon_white (string);
+
+ while (string[string_index])
+ {
+ c = temp[temp_index++] = string[string_index++];
+
+ if (c == ' ' || c == '\n' || c == '\t')
+ {
+ temp[temp_index - 1] = ' ';
+ while ((c = string[string_index]) && (c == ' ' ||
+ c == '\t' ||
+ c == '\n'))
+ string_index++;
+ }
+ }
+ temp[temp_index] = 0;
+ strcpy (string, temp);
+ free (temp);
+}
+
+/* Discard text until the desired string is found. The string is
+ included in the discarded text. */
+void
+discard_until (char *string)
+{
+ int temp = search_forward (string, input_text_offset);
+
+ int tt = (temp < 0) ? input_text_length : temp + strlen (string);
+ int from = input_text_offset;
+
+ /* Find out what line we are on. */
+ while (from != tt)
+ if (input_text[from++] == '\n')
+ line_number++;
+
+ if (temp < 0)
+ {
+ /* not found, move current position to end of string */
+ input_text_offset = input_text_length;
+ if (strcmp (string, "\n") != 0)
+ { /* Give a more descriptive feedback, if we are looking for ``@end ''
+ during macro execution. That means someone used a multiline
+ command as an argument to, say, @section ... style commands. */
+ char *end_block = xmalloc (8);
+ sprintf (end_block, "\n%cend ", COMMAND_PREFIX);
+ if (executing_string && strstr (string, end_block))
+ line_error (_("Multiline command %c%s used improperly"),
+ COMMAND_PREFIX, command);
+ else
+ line_error (_("Expected `%s'"), string);
+ free (end_block);
+ return;
+ }
+ }
+ else
+ /* found, move current position to after the found string */
+ input_text_offset = temp + strlen (string);
+}
+
+/* Read characters from the file until we are at MATCH.
+ Place the characters read into STRING.
+ On exit input_text_offset is after the match string.
+ Return the offset where the string starts. */
+int
+get_until (char *match, char **string)
+{
+ int len, current_point, x, new_point, tem;
+
+ current_point = x = input_text_offset;
+ new_point = search_forward (match, input_text_offset);
+
+ if (new_point < 0)
+ new_point = input_text_length;
+ len = new_point - current_point;
+
+ /* Keep track of which line number we are at. */
+ tem = new_point + (strlen (match) - 1);
+ while (x != tem)
+ if (input_text[x++] == '\n')
+ line_number++;
+
+ *string = xmalloc (len + 1);
+
+ memcpy (*string, &input_text[current_point], len);
+ (*string)[len] = 0;
+
+ /* Now leave input_text_offset in a consistent state. */
+ input_text_offset = tem;
+
+ if (input_text_offset > input_text_length)
+ input_text_offset = input_text_length;
+
+ return new_point;
+}
+
+/* Replace input_text[FROM .. TO] with its expansion. */
+void
+replace_with_expansion (int from, int *to)
+{
+ char *xp;
+ unsigned xp_len, new_len;
+ char *old_input = input_text;
+ unsigned raw_len = *to - from;
+ char *str;
+
+ /* The rest of the code here moves large buffers, so let's
+ not waste time if the input cannot possibly expand
+ into anything. Unfortunately, we cannot avoid expansion
+ when we see things like @code etc., even if they only
+ asked for expansion of macros, since any Texinfo command
+ can be potentially redefined with a macro. */
+ if (only_macro_expansion &&
+ memchr (input_text + from, COMMAND_PREFIX, raw_len) == 0)
+ return;
+
+ /* Get original string from input. */
+ str = xmalloc (raw_len + 1);
+ memcpy (str, input_text + from, raw_len);
+ str[raw_len] = 0;
+
+ /* We are going to relocate input_text, so we had better output
+ pending portion of input_text now, before the pointer changes. */
+ if (macro_expansion_output_stream && !executing_string
+ && !me_inhibit_expansion)
+ append_to_expansion_output (from);
+
+ /* Expand it. */
+ xp = expansion (str, 0);
+ xp_len = strlen (xp);
+ free (str);
+
+ /* Plunk the expansion into the middle of `input_text' --
+ which is terminated by a newline, not a null. Avoid
+ expensive move of the rest of the input if the expansion
+ has the same length as the original string. */
+ if (xp_len != raw_len)
+ {
+ new_len = from + xp_len + input_text_length - *to + 1;
+ if (executing_string)
+ { /* If we are in execute_string, we might need to update
+ the relevant element in the execution_strings[] array,
+ since it could have to be relocated from under our
+ feet. (input_text is reallocated here as well, if needed.) */
+ maybe_update_execution_strings (&input_text, new_len);
+ }
+ else if (new_len > input_text_length + 1)
+ /* Don't bother to realloc if we have enough space. */
+ input_text = xrealloc (input_text, new_len);
+
+ memmove (input_text + from + xp_len,
+ input_text + *to, input_text_length - *to + 1);
+
+ *to += xp_len - raw_len;
+ /* Since we change input_text_length here, the comparison above
+ isn't really valid, but it seems the worst that might happen is
+ an extra xrealloc or two, so let's not worry. */
+ input_text_length += xp_len - raw_len;
+ }
+ memcpy (input_text + from, xp, xp_len);
+ free (xp);
+
+ /* Synchronize the macro-expansion pointers with our new input_text. */
+ if (input_text != old_input)
+ forget_itext (old_input);
+ if (macro_expansion_output_stream && !executing_string)
+ remember_itext (input_text, from);
+}
+
+/* Read characters from the file until we are at MATCH or end of line.
+ Place the characters read into STRING. If EXPAND is nonzero,
+ expand the text before looking for MATCH for those cases where
+ MATCH might be produced by some macro. */
+void
+get_until_in_line (int expand, char *match, char **string)
+{
+ int real_bottom = input_text_length;
+ int limit = search_forward ("\n", input_text_offset);
+ if (limit < 0)
+ limit = input_text_length;
+
+ /* Replace input_text[input_text_offset .. limit-1] with its expansion.
+ This allows the node names and menu entries themselves to be
+ constructed via a macro, as in:
+ @macro foo{p, q}
+ Together: \p\ & \q\.
+ @end macro
+
+ @node @foo{A,B}, next, prev, top
+
+ Otherwise, the `,' separating the macro args A and B is taken as
+ the node argument separator, so the node name is `@foo{A'. This
+ expansion is only necessary on the first call, since we expand the
+ whole line then. */
+ if (expand)
+ {
+ replace_with_expansion (input_text_offset, &limit);
+ }
+
+ real_bottom = input_text_length;
+ input_text_length = limit;
+ get_until (match, string);
+ input_text_length = real_bottom;
+}
+
+void
+get_rest_of_line (int expand, char **string)
+{
+ xml_no_para ++;
+ if (expand)
+ {
+ char *tem;
+
+ /* Don't expand non-macros in input, since we want them
+ intact in the macro-expanded output. */
+ only_macro_expansion++;
+ get_until_in_line (1, "\n", &tem);
+ only_macro_expansion--;
+ *string = expansion (tem, 0);
+ free (tem);
+ }
+ else
+ get_until_in_line (0, "\n", string);
+
+ canon_white (*string);
+
+ if (curchar () == '\n') /* as opposed to the end of the file... */
+ {
+ line_number++;
+ input_text_offset++;
+ }
+ xml_no_para --;
+}
+
+/* Backup the input pointer to the previous character, keeping track
+ of the current line number. */
+void
+backup_input_pointer (void)
+{
+ if (input_text_offset)
+ {
+ input_text_offset--;
+ if (curchar () == '\n')
+ line_number--;
+ }
+}
+
+/* Read characters from the file until we are at MATCH or closing brace.
+ Place the characters read into STRING. */
+void
+get_until_in_braces (char *match, char **string)
+{
+ char *temp;
+ int i, brace = 0;
+ int match_len = strlen (match);
+
+ for (i = input_text_offset; i < input_text_length; i++)
+ {
+ if (i < input_text_length - 1 && input_text[i] == '@')
+ {
+ i++; /* skip commands like @, and @{ */
+ continue;
+ }
+ else if (input_text[i] == '{')
+ brace++;
+ else if (input_text[i] == '}')
+ {
+ brace--;
+ /* If looking for a brace, don't stop at the interior brace,
+ like after "baz" in "@foo{something @bar{baz} more}". */
+ if (brace == 0)
+ continue;
+ }
+ else if (input_text[i] == '\n')
+ line_number++;
+
+ if (brace < 0 ||
+ (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
+ break;
+ }
+
+ match_len = i - input_text_offset;
+ temp = xmalloc (2 + match_len);
+ memcpy (temp, input_text + input_text_offset, match_len);
+ temp[match_len] = 0;
+ input_text_offset = i;
+ *string = temp;
+}
+
+
+
+/* Converting a file. */
+
+/* Convert the file named by NAME. The output is saved on the file
+ named as the argument to the @setfilename command. */
+static char *suffixes[] = {
+ /* ".txi" is checked first so that on 8+3 DOS filesystems, if they
+ have "texinfo.txi" and "texinfo.tex" in the same directory, the
+ former is used rather than the latter, due to file name truncation. */
+ ".txi",
+ ".texinfo",
+ ".texi",
+ ".txinfo",
+ "",
+ NULL
+};
+
+static void
+initialize_conversion (void)
+{
+ init_tag_table ();
+ init_indices ();
+ init_internals ();
+ init_paragraph ();
+
+ /* This is used for splitting the output file and for doing section
+ headings. It was previously initialized in `init_paragraph', but its
+ use there loses with the `init_paragraph' calls done by the
+ multitable code; the tag indices get reset to zero. */
+ output_position = 0;
+}
+
+/* Reverse the chain of structures in LIST. Output the new head
+ of the chain. You should always assign the output value of this
+ function to something, or you will lose the chain. */
+GENERIC_LIST *
+reverse_list (GENERIC_LIST *list)
+{
+ GENERIC_LIST *next;
+ GENERIC_LIST *prev = NULL;
+
+ while (list)
+ {
+ next = list->next;
+ list->next = prev;
+ prev = list;
+ list = next;
+ }
+ return prev;
+}
+
+/* We read in multiples of 4k, simply because it is a typical pipe size
+ on unix systems. */
+#define READ_BUFFER_GROWTH (4 * 4096)
+
+/* Convert the Texinfo file coming from the open stream STREAM. Assume the
+ source of the stream is named NAME. */
+static void
+convert_from_stream (FILE *stream, char *name)
+{
+ char *buffer = NULL;
+ int buffer_offset = 0, buffer_size = 0;
+
+ initialize_conversion ();
+
+ /* Read until the end of the stream. This isn't strictly correct, since
+ the texinfo input may end before the stream ends, but it is a quick
+ working heuristic. */
+ while (!feof (stream))
+ {
+ int count;
+
+ if (buffer_offset + (READ_BUFFER_GROWTH + 1) >= buffer_size)
+ buffer = (char *)
+ xrealloc (buffer, (buffer_size += READ_BUFFER_GROWTH));
+
+ count = fread (buffer + buffer_offset, 1, READ_BUFFER_GROWTH, stream);
+
+ if (count < 0)
+ {
+ perror (name);
+ xexit (1);
+ }
+
+ buffer_offset += count;
+ if (count == 0)
+ break;
+ }
+
+ /* Set the globals to the new file. */
+ input_text = buffer;
+ input_text_length = buffer_offset;
+ input_filename = xstrdup (name);
+ node_filename = xstrdup (name);
+ input_text_offset = 0;
+ line_number = 1;
+
+ /* Not strictly necessary. This magic prevents read_token () from doing
+ extra unnecessary work each time it is called (that is a lot of times).
+ The INPUT_TEXT_LENGTH is one past the actual end of the text. */
+ input_text[input_text_length] = '\n';
+
+ convert_from_loaded_file (name);
+}
+
+static void
+convert_from_file (char *name)
+{
+ int i;
+ char *filename = xmalloc (strlen (name) + 50);
+
+ /* Prepend NAME's directory to the search path, so relative links work. */
+ prepend_to_include_path (pathname_part (name));
+
+ initialize_conversion ();
+
+ /* Try to load the file specified by NAME, concatenated with our
+ various suffixes. Prefer files like `makeinfo.texi' to `makeinfo'. */
+ for (i = 0; suffixes[i]; i++)
+ {
+ strcpy (filename, name);
+ strcat (filename, suffixes[i]);
+
+ if (find_and_load (filename, 1))
+ break;
+ }
+
+ if (!suffixes[i])
+ {
+ fs_error (name);
+ free (filename);
+ return;
+ }
+
+ /* `find_and_load' (when successful) clobbers this global with new
+ memory. We're about to reset it, so may as well free first. */
+ free (input_filename);
+
+ /* Set the current filename. */
+ input_filename = filename;
+
+ /* Do the main conversion. */
+ convert_from_loaded_file (name);
+
+ /* Pop the prepended path, so multiple filenames in the
+ command line do not screw each others include paths. */
+ pop_path_from_include_path ();
+}
+
+static int
+create_html_directory (char *dir, int can_remove_file)
+{
+ struct stat st;
+
+ /* Already exists. */
+ if (stat (dir, &st) == 0)
+ {
+ /* And it's a directory, so silently reuse it. */
+ if (S_ISDIR (st.st_mode))
+ return 1;
+ /* Not a directory, so move it out of the way if we are allowed. */
+ else if (can_remove_file)
+ {
+ if (unlink (dir) != 0)
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ if (mkdir (dir, 0777) == 0)
+ /* Success! */
+ return 1;
+ else
+ return 0;
+}
+
+/* Given OUTPUT_FILENAME == ``/foo/bar/baz.html'', return
+ "/foo/bar/baz/baz.html". This routine is called only if html && splitting.
+
+ Split html output goes into the subdirectory of the toplevel
+ filename, without extension. For example:
+ @setfilename foo.info
+ produces output in files foo/index.html, foo/second-node.html, ...
+
+ But if the user said -o foo.whatever on the cmd line, then use
+ foo.whatever unchanged. */
+
+static char *
+insert_toplevel_subdirectory (char *output_filename)
+{
+ static const char index_name[] = "index.html";
+ char *dir, *subdir, *base, *basename, *p;
+ char buf[PATH_MAX];
+ const int index_len = sizeof (index_name) - 1;
+
+ strcpy (buf, output_filename);
+ dir = pathname_part (buf); /* directory of output_filename */
+ base = filename_part (buf); /* strips suffix, too */
+ basename = xstrdup (base); /* remember real @setfilename name */
+ p = dir + strlen (dir) - 1;
+ if (p > dir && IS_SLASH (*p))
+ *p = 0;
+ p = strrchr (base, '.');
+ if (p)
+ *p = 0;
+
+ /* Split html output goes into subdirectory of toplevel name. */
+ if (save_command_output_filename
+ && STREQ (output_filename, save_command_output_filename))
+ subdir = basename; /* from user, use unchanged */
+ else
+ subdir = base; /* implicit, omit suffix */
+
+ free (output_filename);
+ output_filename = xmalloc (strlen (dir) + 1
+ + strlen (basename) + 1
+ + index_len
+ + 1);
+ strcpy (output_filename, dir);
+ if (strlen (dir))
+ strcat (output_filename, "/");
+ strcat (output_filename, subdir);
+
+ /* First try, do not remove existing file. */
+ if (!create_html_directory (output_filename, 0))
+ {
+ /* That failed, try subdir name with .html.
+ Remove it if it exists. */
+ strcpy (output_filename, dir);
+ if (strlen (dir))
+ strcat (output_filename, "/");
+ strcat (output_filename, basename);
+
+ if (!create_html_directory (output_filename, 1))
+ {
+ /* Last try failed too :-\ */
+ line_error (_("Can't create directory `%s': %s"),
+ output_filename, strerror (errno));
+ xexit (1);
+ }
+ }
+
+ strcat (output_filename, "/");
+ strcat (output_filename, index_name);
+ return output_filename;
+}
+
+/* FIXME: this is way too hairy */
+static void
+convert_from_loaded_file (char *name)
+{
+ char *real_output_filename = NULL;
+
+ remember_itext (input_text, 0);
+
+ input_text_offset = 0;
+
+ /* Avoid the `\input texinfo' line in HTML output (assuming it starts
+ the file). */
+ if (looking_at ("\\input"))
+ discard_until ("\n");
+
+ /* Search this file looking for the special string which starts conversion.
+ Once found, we may truly begin. */
+ while (input_text_offset >= 0)
+ {
+ input_text_offset =
+ search_forward (setfilename_search, input_text_offset);
+
+ if (input_text_offset == 0
+ || (input_text_offset > 0
+ && input_text[input_text_offset -1] == '\n'))
+ break;
+ else if (input_text_offset > 0)
+ input_text_offset++;
+ }
+
+ if (input_text_offset < 0)
+ {
+ if (!command_output_filename)
+ {
+#if defined (REQUIRE_SETFILENAME)
+ error (_("No `%s' found in `%s'"), setfilename_search, name);
+ goto finished;
+#else
+ command_output_filename = output_name_from_input_name (name);
+#endif /* !REQUIRE_SETFILENAME */
+ }
+
+ {
+ int i, end_of_first_line;
+
+ /* Find the end of the first line in the file. */
+ for (i = 0; i < input_text_length - 1; i++)
+ if (input_text[i] == '\n')
+ break;
+
+ end_of_first_line = i + 1;
+
+ for (i = 0; i < end_of_first_line; i++)
+ {
+ if ((input_text[i] == '\\') &&
+ (strncmp (input_text + i + 1, "input", 5) == 0))
+ {
+ input_text_offset = i;
+ break;
+ }
+ }
+ }
+ }
+ else
+ input_text_offset += strlen (setfilename_search);
+
+ if (!command_output_filename)
+ {
+ get_until ("\n", &output_filename); /* read rest of line */
+ if (html || xml)
+ { /* Change any extension to .html or .xml. */
+ char *html_name, *directory_part, *basename_part, *temp;
+
+ canon_white (output_filename);
+ directory_part = pathname_part (output_filename);
+
+ basename_part = filename_part (output_filename);
+
+ /* Zap any existing extension. */
+ temp = strrchr (basename_part, '.');
+ if (temp)
+ *temp = 0;
+
+ /* Construct new filename. */
+ html_name = xmalloc (strlen (directory_part)
+ + strlen (basename_part) + 6);
+ strcpy (html_name, directory_part);
+ strcat (html_name, basename_part);
+ strcat (html_name, html ? ".html" : ".xml");
+
+ /* Replace name from @setfilename with the html name. */
+ free (output_filename);
+ output_filename = html_name;
+ }
+ }
+ else
+ {
+ if (input_text_offset != -1)
+ discard_until ("\n");
+ else
+ input_text_offset = 0;
+
+ real_output_filename = output_filename = command_output_filename;
+ command_output_filename = NULL; /* for included files or whatever */
+ }
+
+ canon_white (output_filename);
+ toplevel_output_filename = xstrdup (output_filename);
+
+ if (real_output_filename && strcmp (real_output_filename, "-") == 0)
+ {
+ if (macro_expansion_filename
+ && strcmp (macro_expansion_filename, "-") == 0)
+ {
+ fprintf (stderr,
+ _("%s: Skipping macro expansion to stdout as Info output is going there.\n"),
+ progname);
+ macro_expansion_output_stream = NULL;
+ }
+ real_output_filename = xstrdup (real_output_filename);
+ output_stream = stdout;
+ splitting = 0; /* Cannot split when writing to stdout. */
+ }
+ else
+ {
+ if (html && splitting)
+ {
+ if (FILENAME_CMP (output_filename, NULL_DEVICE) == 0
+ || FILENAME_CMP (output_filename, ALSO_NULL_DEVICE) == 0)
+ splitting = 0;
+ else
+ output_filename = insert_toplevel_subdirectory (output_filename);
+ real_output_filename = xstrdup (output_filename);
+ }
+ else if (!real_output_filename)
+ real_output_filename = expand_filename (output_filename, name);
+ else
+ real_output_filename = xstrdup (real_output_filename);
+
+ output_stream = fopen (real_output_filename, "w");
+ }
+
+ set_current_output_filename (real_output_filename);
+
+ if (verbose_mode)
+ printf (_("Making %s file `%s' from `%s'.\n"),
+ no_headers ? "text"
+ : html ? "HTML"
+ : xml ? "XML"
+ : "info",
+ output_filename, input_filename);
+
+ if (output_stream == NULL)
+ {
+ fs_error (real_output_filename);
+ goto finished;
+ }
+
+ if (xml)
+ xml_begin_document (filename_part (output_filename));
+
+ /* Make the displayable filename from output_filename. Only the base
+ portion of the filename need be displayed. */
+ flush_output (); /* in case there was no @bye */
+ if (output_stream != stdout)
+ pretty_output_filename = filename_part (output_filename);
+ else
+ pretty_output_filename = xstrdup ("stdout");
+
+ /* For this file only, count the number of newlines from the top of
+ the file to here. This way, we keep track of line numbers for
+ error reporting. Line_number starts at 1, since the user isn't
+ zero-based. */
+ {
+ int temp = 0;
+ line_number = 1;
+ while (temp != input_text_offset)
+ if (input_text[temp++] == '\n')
+ line_number++;
+ }
+
+ if (xml && !docbook)
+ {
+ /* Just before the real main loop, let's handle the defines. */
+ COMMAND_LINE_DEFINE *temp;
+
+ for (temp = command_line_defines; temp; temp = temp->next)
+ {
+ handle_variable_internal (temp->action, temp->define);
+ free(temp->define);
+ }
+ }
+
+ reader_loop ();
+ if (xml)
+ xml_end_document ();
+
+
+finished:
+ discard_insertions (0);
+ close_paragraph ();
+ flush_file_stack ();
+
+ if (macro_expansion_output_stream)
+ {
+ fclose (macro_expansion_output_stream);
+ if (errors_printed && !force
+ && strcmp (macro_expansion_filename, "-") != 0
+ && FILENAME_CMP (macro_expansion_filename, NULL_DEVICE) != 0
+ && FILENAME_CMP (macro_expansion_filename, ALSO_NULL_DEVICE) != 0)
+ {
+ fprintf (stderr,
+_("%s: Removing macro output file `%s' due to errors; use --force to preserve.\n"),
+ progname, macro_expansion_filename);
+ if (unlink (macro_expansion_filename) < 0)
+ perror (macro_expansion_filename);
+ }
+ }
+
+ if (output_stream)
+ {
+ output_pending_notes ();
+
+ if (html)
+ {
+ no_indent = 1;
+ start_paragraph ();
+ add_word ("</body></html>\n");
+ close_paragraph ();
+ }
+
+ /* Write stuff makeinfo generates after @bye, ie. info_trailer. */
+ flush_output ();
+
+ if (output_stream != stdout)
+ if (fclose (output_stream) != 0)
+ fs_error (real_output_filename);
+
+ /* If validating, then validate the entire file right now. */
+ if (validating)
+ validate_file (tag_table);
+
+ handle_delayed_writes ();
+
+ if (tag_table)
+ {
+ tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *) tag_table);
+ if (!no_headers && !html && !STREQ (current_output_filename, "-"))
+ write_tag_table (real_output_filename);
+ }
+
+ /* Maybe we want local variables in info output. Must be after
+ tag table, since otherwise usually Emacs will not see it. */
+ write_trailer (real_output_filename, info_trailer ());
+
+ if (splitting && !html && (!errors_printed || force))
+ {
+ clean_old_split_files (real_output_filename);
+ split_file (real_output_filename, split_size);
+ }
+ else if (errors_printed
+ && !force
+ && strcmp (real_output_filename, "-") != 0
+ && FILENAME_CMP (real_output_filename, NULL_DEVICE) != 0
+ && FILENAME_CMP (real_output_filename, ALSO_NULL_DEVICE) != 0)
+ { /* If there were errors, and no --force, remove the output. */
+ fprintf (stderr,
+ _("%s: Removing output file `%s' due to errors; use --force to preserve.\n"),
+ progname, real_output_filename);
+ if (unlink (real_output_filename) < 0)
+ perror (real_output_filename);
+ }
+ }
+
+ if (internal_links_stream)
+ {
+ if (internal_links_stream != stdout
+ && fclose (internal_links_stream) != 0)
+ fs_error(internal_links_filename);
+ internal_links_stream = NULL;
+ if (errors_printed && !force
+ && strcmp (internal_links_filename, "-") != 0
+ && FILENAME_CMP (internal_links_filename, NULL_DEVICE) != 0
+ && FILENAME_CMP (internal_links_filename, ALSO_NULL_DEVICE) != 0)
+ {
+ fprintf (stderr,
+_("%s: Removing internal links output file `%s' due to errors; use --force to preserve.\n"),
+ progname, internal_links_filename);
+ if (unlink (internal_links_filename) < 0)
+ perror (internal_links_filename);
+ }
+ }
+
+ free (real_output_filename);
+}
+
+/* If enable_encoding is set and @documentencoding is used, return a
+ Local Variables section (as a malloc-ed string) so that Emacs'
+ locale features can work. Else return NULL. */
+char *
+info_trailer (void)
+{
+ char *encoding;
+
+ if (!enable_encoding)
+ return NULL;
+
+ encoding = current_document_encoding ();
+
+ if (encoding && *encoding)
+ {
+#define LV_FMT "\n\037\nLocal Variables:\ncoding: %s\nEnd:\n"
+ char *lv = xmalloc (sizeof (LV_FMT) + strlen (encoding));
+ sprintf (lv, LV_FMT, encoding);
+ free (encoding);
+ return lv;
+ }
+
+ free (encoding);
+ return NULL;
+}
+
+/* Append TRAILER to FILENAME for Info and HTML output. Include HTML
+ comments if needed. */
+static void
+write_trailer (char *filename, char *trailer)
+{
+ if (!trailer || xml || docbook)
+ return;
+
+ if (output_stream != stdout)
+ output_stream = fopen (filename, "a");
+ if (!output_stream)
+ {
+ fs_error (filename);
+ return;
+ }
+
+ if (html)
+ fwrite ("<!--", 1, 4, output_stream);
+
+ fwrite (trailer, 1, strlen (trailer), output_stream);
+ free (trailer);
+
+ if (html)
+ fwrite ("\n-->\n", 1, 5, output_stream);
+
+ if (output_stream != stdout)
+ if (fclose (output_stream) != 0)
+ fs_error (filename);
+}
+
+
+void
+free_and_clear (char **pointer)
+{
+ if (*pointer)
+ {
+ free (*pointer);
+ *pointer = NULL;
+ }
+}
+
+ /* Initialize some state. */
+static void
+init_internals (void)
+{
+ free_and_clear (&output_filename);
+ free_and_clear (&command);
+ free_and_clear (&input_filename);
+ free_node_references ();
+ free_node_node_references ();
+ toc_free ();
+ init_insertion_stack ();
+ init_brace_stack ();
+ current_node = NULL; /* sometimes already freed */
+ command_index = 0;
+ in_menu = 0;
+ in_detailmenu = 0;
+ top_node_seen = 0;
+ non_top_node_seen = 0;
+ node_number = -1;
+}
+
+void
+init_paragraph (void)
+{
+ if (output_paragraph)
+ free (output_paragraph);
+ output_paragraph = xmalloc (paragraph_buffer_len);
+ output_paragraph[0] = 0;
+ output_paragraph_offset = 0;
+ output_paragraph_start_column = 0;
+ paragraph_is_open = 0;
+ current_indent = 0;
+ meta_char_pos = 0;
+}
+
+/* This is called from `reader_loop' when we are at the * beginning a
+ menu line. */
+
+static void
+handle_menu_entry (void)
+{
+ char *tem;
+
+ /* Ugh, glean_node_from_menu wants to read the * itself. */
+ input_text_offset--;
+
+ /* Find node name in menu entry and save it in references list for
+ later validation. Use followed_reference type for detailmenu
+ references since we don't want to use them for default node pointers. */
+ tem = glean_node_from_menu (1, in_detailmenu
+ ? followed_reference : menu_reference);
+
+ if (html && tem)
+ { /* Start a menu item with the cleaned-up line. Put an anchor
+ around the start text (before `:' or the node name). */
+ char *string;
+
+ discard_until ("* ");
+
+ /* The line number was already incremented in reader_loop when we
+ saw the newline, and discard_until has now incremented again. */
+ line_number--;
+
+ if (had_menu_commentary)
+ {
+ add_html_block_elt ("<ul class=\"menu\">\n");
+ had_menu_commentary = 0;
+ in_paragraph = 0;
+ }
+
+ if (in_paragraph)
+ {
+ add_html_block_elt ("</p>\n");
+ add_html_block_elt ("<ul class=\"menu\">\n");
+ in_paragraph = 0;
+ }
+
+ in_menu_item = 1;
+
+ add_html_block_elt ("<li><a");
+ if (next_menu_item_number <= 9)
+ {
+ add_word(" accesskey=");
+ add_word_args("\"%d\"", next_menu_item_number);
+ next_menu_item_number++;
+ }
+ add_word (" href=\"");
+ string = expansion (tem, 0);
+ add_anchor_name (string, 1);
+ add_word ("\">");
+ free (string);
+
+ /* The menu item may use macros, so expand them now. */
+ only_macro_expansion++;
+ get_until_in_line (1, ":", &string);
+ only_macro_expansion--;
+ execute_string ("%s", string); /* get escaping done */
+ free (string);
+
+ add_word ("</a>");
+
+ if (looking_at ("::"))
+ discard_until (":");
+ else
+ { /* discard the node name */
+ get_until_in_line (0, ".", &string);
+ free (string);
+ }
+ input_text_offset++; /* discard the second colon or the period */
+
+ /* Insert a colon only if there is a description of this menu item. */
+ {
+ int save_input_text_offset = input_text_offset;
+ int save_line_number = line_number;
+ char *test_string;
+ get_rest_of_line (0, &test_string);
+ if (strlen (test_string) > 0)
+ add_word (": ");
+ input_text_offset = save_input_text_offset;
+ line_number = save_line_number;
+ }
+ }
+ else if (xml && tem)
+ {
+ xml_start_menu_entry (tem);
+ }
+ else if (tem)
+ { /* For Info output, we can just use the input and the main case in
+ reader_loop where we output what comes in. Just move off the *
+ so the next time through reader_loop we don't end up back here. */
+ add_char ('*');
+ input_text_offset += 2; /* undo the pointer back-up above. */
+ }
+
+ if (tem)
+ free (tem);
+}
+
+/* Find the command corresponding to STRING. If the command is found,
+ return a pointer to the data structure. Otherwise return -1. */
+static COMMAND *
+get_command_entry (char *string)
+{
+ int i;
+
+ for (i = 0; command_table[i].name; i++)
+ if (strcmp (command_table[i].name, string) == 0)
+ return &command_table[i];
+
+ /* This command is not in our predefined command table. Perhaps
+ it is a user defined command. */
+ for (i = 0; i < user_command_array_len; i++)
+ if (user_command_array[i] &&
+ (strcmp (user_command_array[i]->name, string) == 0))
+ return user_command_array[i];
+
+ /* We never heard of this command. */
+ return (COMMAND *) -1;
+}
+
+
+/* input_text_offset is right at the command prefix character.
+ Read the next token to determine what to do. Return zero
+ if there's no known command or macro after the prefix character. */
+static int
+read_command (void)
+{
+ COMMAND *entry;
+ int old_text_offset = input_text_offset++;
+
+ free_and_clear (&command);
+ command = read_token ();
+
+ /* Check to see if this command is a macro. If so, execute it here. */
+ {
+ MACRO_DEF *def;
+
+ def = find_macro (command);
+
+ if (def)
+ {
+ /* We disallow recursive use of a macro call. Inhibit the expansion
+ of this macro during the life of its execution. */
+ if (!(def->flags & ME_RECURSE))
+ def->inhibited = 1;
+
+ executing_macro++;
+ execute_macro (def);
+ executing_macro--;
+
+ if (!(def->flags & ME_RECURSE))
+ def->inhibited = 0;
+
+ return 1;
+ }
+ }
+
+ if (only_macro_expansion)
+ {
+ /* Back up to the place where we were called, so the
+ caller will have a chance to process this non-macro. */
+ input_text_offset = old_text_offset;
+ return 0;
+ }
+
+ /* Perform alias expansion */
+ command = alias_expand (command);
+
+ if (enclosure_command (command))
+ {
+ remember_brace (enclosure_expand);
+ enclosure_expand (START, output_paragraph_offset, 0);
+ return 0;
+ }
+
+ entry = get_command_entry (command);
+ if (entry == (COMMAND *)-1)
+ {
+ line_error (_("Unknown command `%s'"), command);
+ return 0;
+ }
+
+ if (entry->argument_in_braces == BRACE_ARGS)
+ remember_brace (entry->proc);
+ else if (entry->argument_in_braces == MAYBE_BRACE_ARGS)
+ {
+ if (curchar () == '{')
+ remember_brace (entry->proc);
+ else
+ { /* No braces, so arg is next char. */
+ int ch;
+ int saved_offset = output_paragraph_offset;
+ (*(entry->proc)) (START, output_paragraph_offset, 0);
+
+ /* Possibilities left for the next character: @ (error), }
+ (error), whitespace (skip) anything else (normal char). */
+ skip_whitespace ();
+ ch = curchar ();
+ if (ch == '@')
+ {
+ line_error (_("Use braces to give a command as an argument to @%s"),
+ entry->name);
+ return 0;
+ }
+ else if (ch == '}')
+ {
+ /* Our caller will give the error message, because this }
+ won't match anything. */
+ return 0;
+ }
+
+ add_char (ch);
+ input_text_offset++;
+ (*(entry->proc)) (END, saved_offset, output_paragraph_offset);
+ return 1;
+ }
+ }
+
+ /* Get here if we have BRACE_ARGS, NO_BRACE_ARGS, or MAYBE_BRACE_ARGS
+ with braces. */
+ (*(entry->proc)) (START, output_paragraph_offset, 0);
+ return 1;
+}
+
+/* Okay, we are ready to start the conversion. Call the reader on
+ some text, and fill the text as it is output. Handle commands by
+ remembering things like open braces and the current file position on a
+ stack, and when the corresponding close brace is found, you can call
+ the function with the proper arguments. Although the filling isn't
+ necessary for HTML, it should do no harm. */
+void
+reader_loop (void)
+{
+ int character;
+ int done = 0;
+
+ while (!done)
+ {
+ if (input_text_offset >= input_text_length)
+ break;
+
+ character = curchar ();
+
+ /* If only_macro_expansion, only handle macros and leave
+ everything else intact. */
+ if (!only_macro_expansion && !in_fixed_width_font
+ && ((!html && !xml) || escape_html)
+ && (character == '\'' || character == '`')
+ && input_text[input_text_offset + 1] == character)
+ {
+ if (html)
+ {
+ input_text_offset += 2;
+ add_word (character == '`' ? "&ldquo;" : "&rdquo;");
+ continue;
+ }
+ else if (xml)
+ {
+ input_text_offset += 2;
+ xml_insert_entity (character == '`' ? "ldquo" : "rdquo");
+ continue;
+ }
+ else
+ {
+ input_text_offset++;
+ character = '"';
+ }
+ }
+
+ /* Convert --- to --. */
+ if (!only_macro_expansion && character == '-' && !in_fixed_width_font
+ && ((!html && !xml) || escape_html))
+ {
+ int dash_count = 0;
+
+ /* Get the number of consequtive dashes. */
+ while (input_text[input_text_offset] == '-')
+ {
+ dash_count++;
+ input_text_offset++;
+ }
+
+ /* Eat one dash. */
+ dash_count--;
+
+ if (html || xml)
+ {
+ if (dash_count == 0)
+ add_char ('-');
+ else
+ while (dash_count > 0)
+ {
+ if (dash_count >= 2)
+ {
+ if (html)
+ add_word ("&mdash;");
+ else
+ xml_insert_entity ("mdash");
+ dash_count -= 2;
+ }
+ else if (dash_count >= 1)
+ {
+ if (html)
+ add_word ("&ndash;");
+ else
+ xml_insert_entity ("ndash");
+ dash_count--;
+ }
+ }
+ }
+ else
+ {
+ add_char ('-');
+ while (--dash_count > 0)
+ add_char ('-');
+ }
+
+ continue;
+ }
+
+ /* If this is a whitespace character, then check to see if the line
+ is blank. If so, advance to the carriage return. */
+ if (!only_macro_expansion && whitespace (character))
+ {
+ int i = input_text_offset + 1;
+
+ while (i < input_text_length && whitespace (input_text[i]))
+ i++;
+
+ if (i == input_text_length || input_text[i] == '\n')
+ {
+ if (i == input_text_length)
+ i--;
+
+ input_text_offset = i;
+ character = curchar ();
+ }
+ }
+
+ if (character == '\n')
+ line_number++;
+
+ switch (character)
+ {
+ case '*': /* perhaps we are at a menu */
+ /* We used to check for this in the \n case but an @c in a
+ menu swallows its newline, so check here instead. */
+ if (!only_macro_expansion && in_menu
+ && input_text_offset + 1 < input_text_length
+ && input_text_offset > 0
+ && input_text[input_text_offset-1] == '\n')
+ handle_menu_entry ();
+ else
+ { /* Duplicate code from below, but not worth twisting the
+ fallthroughs to get down there. */
+ add_char (character);
+ input_text_offset++;
+ }
+ break;
+
+ /* Escapes for HTML unless we're outputting raw HTML. Do
+ this always, even if SGML rules don't require it since
+ that's easier and safer for non-conforming browsers. */
+ case '&':
+ if (html && escape_html)
+ add_word ("&amp;");
+ else
+ add_char (character);
+ input_text_offset++;
+ break;
+
+ case '<':
+ if (html && escape_html)
+ add_word ("&lt;");
+ else if (xml && escape_html)
+ xml_insert_entity ("lt");
+ else
+ add_char (character);
+ input_text_offset++;
+ break;
+
+ case '>':
+ if (html && escape_html)
+ add_word ("&gt;");
+ else if (xml && escape_html)
+ xml_insert_entity ("gt");
+ else
+ add_char (character);
+ input_text_offset++;
+ break;
+
+ case COMMAND_PREFIX: /* @ */
+ if (read_command () || !only_macro_expansion)
+ break;
+
+ /* FALLTHROUGH (usually) */
+ case '{':
+ /* Special case. We're not supposed to see this character by itself.
+ If we do, it means there is a syntax error in the input text.
+ Report the error here, but remember this brace on the stack so
+ we can ignore its partner. */
+ if (!only_macro_expansion)
+ {
+ if (command && !STREQ (command, "math"))
+ {
+ line_error (_("Misplaced %c"), '{');
+ remember_brace (misplaced_brace);
+ }
+ else
+ /* We don't mind `extra' braces inside @math. */
+ remember_brace (cm_no_op);
+ /* remember_brace advances input_text_offset. */
+ break;
+ }
+
+ /* FALLTHROUGH (usually) */
+ case '}':
+ if (!only_macro_expansion)
+ {
+ pop_and_call_brace ();
+ input_text_offset++;
+ break;
+ }
+
+ /* FALLTHROUGH (usually) */
+ default:
+ add_char (character);
+ input_text_offset++;
+ }
+ }
+ if (macro_expansion_output_stream && !only_macro_expansion)
+ maybe_write_itext (input_text, input_text_offset);
+}
+
+static void
+init_brace_stack (void)
+{
+ brace_stack = NULL;
+}
+
+/* Remember the current output position here. Save PROC
+ along with it so you can call it later. */
+static void
+remember_brace_1 (COMMAND_FUNCTION (*proc), int position)
+{
+ BRACE_ELEMENT *new = xmalloc (sizeof (BRACE_ELEMENT));
+ new->next = brace_stack;
+ new->proc = proc;
+ new->command = xstrdup (command ? command : "");
+ new->pos = position;
+ new->line = line_number;
+ new->in_fixed_width_font = in_fixed_width_font;
+ brace_stack = new;
+}
+
+static void
+remember_brace (COMMAND_FUNCTION (*proc))
+{
+ if (curchar () != '{')
+ line_error (_("%c%s expected braces"), COMMAND_PREFIX, command);
+ else
+ input_text_offset++;
+ remember_brace_1 (proc, output_paragraph_offset);
+}
+
+/* Pop the top of the brace stack, and call the associated function
+ with the args END and POS. */
+static void
+pop_and_call_brace (void)
+{
+ if (brace_stack == NULL)
+ {
+ line_error (_("Unmatched }"));
+ return;
+ }
+
+ {
+ BRACE_ELEMENT *temp;
+
+ int pos = brace_stack->pos;
+ COMMAND_FUNCTION *proc = brace_stack->proc;
+ in_fixed_width_font = brace_stack->in_fixed_width_font;
+
+ /* Reset current command, so the proc can know who it is. This is
+ used in cm_accent. */
+ command = brace_stack->command;
+
+ temp = brace_stack->next;
+ free (brace_stack);
+ brace_stack = temp;
+
+ (*proc) (END, pos, output_paragraph_offset);
+ }
+}
+
+/* Shift all of the markers in `brace_stack' by AMOUNT. */
+static void
+adjust_braces_following (int here, int amount)
+{
+ BRACE_ELEMENT *stack = brace_stack;
+
+ while (stack)
+ {
+ if (stack->pos >= here)
+ stack->pos += amount;
+ stack = stack->next;
+ }
+}
+
+/* Return the string which invokes PROC; a pointer to a function.
+ Always returns the first function in the command table if more than
+ one matches PROC. */
+static const char *
+find_proc_name (COMMAND_FUNCTION (*proc))
+{
+ int i;
+
+ for (i = 0; command_table[i].name; i++)
+ if (proc == command_table[i].proc)
+ return command_table[i].name;
+ return _("NO_NAME!");
+}
+
+/* You call discard_braces () when you shouldn't have any braces on the stack.
+ I used to think that this happens for commands that don't take arguments
+ in braces, but that was wrong because of things like @code{foo @@}. So now
+ I only detect it at the beginning of nodes. */
+void
+discard_braces (void)
+{
+ if (!brace_stack)
+ return;
+
+ while (brace_stack)
+ {
+ if (brace_stack->proc != misplaced_brace)
+ {
+ const char *proc_name;
+
+ proc_name = find_proc_name (brace_stack->proc);
+ file_line_error (input_filename, brace_stack->line,
+ _("%c%s missing close brace"), COMMAND_PREFIX,
+ proc_name);
+ pop_and_call_brace ();
+ }
+ else
+ {
+ BRACE_ELEMENT *temp;
+ temp = brace_stack->next;
+ free (brace_stack);
+ brace_stack = temp;
+ }
+ }
+}
+
+/* Return the 0-based number of the current output column */
+int
+current_output_column (void)
+{
+ int i, column;
+
+ for (i = output_paragraph_offset; i > 0 && output_paragraph[i - 1] != '\n';
+ i--)
+ ;
+ if (i == 0)
+ column = output_paragraph_start_column;
+ else
+ column = 0;
+ while (i < output_paragraph_offset)
+ {
+ int j;
+
+ /* Find a span of non-control characters */
+ for (j = i; j < output_paragraph_offset; j++)
+ {
+ char c;
+
+ c = output_paragraph[j];
+ if ((0 <= c && c < ' ') || c == '\t' || c == NON_BREAKING_SPACE)
+ break;
+ }
+ if (i < j)
+ {
+ column += mbsnwidth ((char *)(output_paragraph + i), j - i, 0);
+ i = j;
+ }
+ if (i < output_paragraph_offset)
+ {
+ char c;
+
+ /* Handle a control character */
+ c = output_paragraph[i];
+ if (c == '\t')
+ {
+ column = (column + 8) & ~0x7;
+ if (column > fill_column)
+ column = fill_column;
+ }
+ else if (c == NON_BREAKING_SPACE)
+ column++;
+ else
+ {
+ /* ASCII control characters appear as two characters in the
+ output (e.g., ^A). */
+ if (!(0 <= c && c < ' '))
+ abort ();
+ column += 2;
+ }
+ i++;
+ }
+ }
+ return column;
+}
+
+void
+#if defined (VA_FPRINTF) && __STDC__
+add_word_args (const char *format, ...)
+#else
+add_word_args (format, va_alist)
+ const char *format;
+ va_dcl
+#endif
+{
+ char buffer[2000]; /* xx no fixed limits */
+#ifdef VA_FPRINTF
+ va_list ap;
+#endif
+
+ VA_START (ap, format);
+#ifdef VA_SPRINTF
+ VA_SPRINTF (buffer, format, ap);
+#else
+ sprintf (buffer, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* not VA_SPRINTF */
+ va_end (ap);
+ add_word (buffer);
+}
+
+/* Add STRING to output_paragraph. */
+void
+add_word (char *string)
+{
+ while (*string)
+ add_char (*string++);
+}
+
+/* Like add_word, but inhibits conversion of whitespace into &nbsp;.
+ Use this to output HTML directives with embedded blanks, to make
+ them @w-safe. */
+void
+add_html_elt (char *string)
+{
+ in_html_elt++;
+ add_word (string);
+ in_html_elt--;
+}
+
+/* These two functions below, add_html_block_elt and add_html_block_elt_args,
+ are mixtures of add_html_elt and add_word_args. They inform makeinfo that
+ the current HTML element being inserted should not be enclosed in a <p>
+ element. */
+void
+add_html_block_elt (char *string)
+{
+ in_html_block_level_elt++;
+ add_word (string);
+ in_html_block_level_elt--;
+}
+
+void
+#if defined (VA_FPRINTF) && __STDC__
+add_html_block_elt_args (const char *format, ...)
+#else
+add_html_block_elt_args (format, va_alist)
+ const char *format;
+ va_dcl
+#endif
+{
+ char buffer[2000]; /* xx no fixed limits */
+#ifdef VA_FPRINTF
+ va_list ap;
+#endif
+
+ VA_START (ap, format);
+#ifdef VA_SPRINTF
+ VA_SPRINTF (buffer, format, ap);
+#else
+ sprintf (buffer, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* not VA_SPRINTF */
+ va_end (ap);
+ add_html_block_elt (buffer);
+}
+
+/* Here is another awful kludge, used in add_char. Ordinarily, macro
+ expansions take place in the body of the document, and therefore we
+ should html_output_head when we see one. But there's an exception: a
+ macro call might take place within @copying, and that does not start
+ the real output, even though we fully expand the copying text.
+
+ So we need to be able to check if we are defining the @copying text.
+ We do this by looking back through the insertion stack. */
+static int
+defining_copying (void)
+{
+ INSERTION_ELT *i;
+ for (i = insertion_stack; i; i = i->next)
+ {
+ if (i->insertion == copying)
+ return 1;
+ }
+ return 0;
+}
+
+/* Output the header for Info.
+ html fixxme: should output this as trailer on first page (at least). */
+
+static void
+info_output_head (void)
+{
+ add_word_args (gdt("This is %s, produced by makeinfo version %s from %s.\n"),
+ output_filename, VERSION, input_filename);
+
+ /* Start afresh with whatever real text we have. */
+ close_paragraph ();
+
+ /* We do not want indentation in what follows, which is usually going
+ to be a node marker (CTRL-_). */
+ if (inhibit_paragraph_indentation == 0)
+ inhibit_paragraph_indentation = -1;
+}
+
+void
+output_head (void)
+{
+ if (output_head_p) /* no-op if we're mistakenly called twice */
+ return;
+ output_head_p = 1;
+
+ if (html)
+ html_output_head ();
+ else if (xml)
+ ; /* handled differently, via xml_begin_document */
+ else if (no_headers)
+ ; /* no header for plain text */
+ else
+ info_output_head ();
+}
+
+
+/* Add the character to the current paragraph. If filling_enabled is
+ nonzero, then do filling as well. */
+void
+add_char (int character)
+{
+ if (xml)
+ {
+ xml_add_char (character);
+ return;
+ }
+
+ /* If we are avoiding outputting headers, and we are currently
+ in a menu, then simply return. But if we're only expanding macros,
+ then we're being called from glean_node_from_menu to try to
+ remember a menu reference, and we need that so we can do defaulting. */
+ if (no_headers && !only_macro_expansion && (in_menu || in_detailmenu))
+ return;
+
+ /* If we are adding a character now, then we don't have to
+ ignore close_paragraph () calls any more. */
+ if (must_start_paragraph && character != '\n')
+ {
+ int column;
+
+ must_start_paragraph = 0;
+ line_already_broken = 0; /* The line is no longer broken. */
+ column = current_output_column ();
+ if (current_indent > column)
+ indent (current_indent - column);
+ }
+
+ if (non_splitting_words
+ && !(html && in_html_elt)
+ && strchr (" \t\n", character))
+ {
+ if (html || docbook)
+ { /* Seems cleaner to use &nbsp; than an 8-bit char. */
+ int saved_escape_html = escape_html;
+ escape_html = 0;
+ add_word ("&nbsp");
+ escape_html = saved_escape_html;
+ character = ';';
+ }
+ else
+ character = NON_BREAKING_SPACE; /* restored in flush_output */
+ }
+
+ insertion_paragraph_closed = 0;
+
+ switch (character)
+ {
+ case '\n':
+ if (!filling_enabled && !(html && (in_menu || in_detailmenu)))
+ {
+ insert ('\n');
+
+ if (force_flush_right)
+ {
+ close_paragraph ();
+ /* Hack to force single blank lines out in this mode. */
+ flush_output ();
+ }
+
+ if (!no_indent && paragraph_is_open)
+ indent (current_indent);
+ break;
+ }
+ else if (end_of_sentence_p () && !french_spacing)
+ /* CHARACTER is newline, filling is enabled, and we're at the
+ end of a sentence. Insert an extra space, unless
+ @frenchspacing is in effect. */
+ {
+ insert (' ');
+ last_inserted_character = character;
+ }
+
+ if (last_char_was_newline)
+ {
+ if (html)
+ last_char_was_newline++;
+ close_paragraph ();
+ pending_indent = 0;
+ }
+ else
+ {
+ last_char_was_newline = 1;
+ if (html)
+ insert ('\n');
+ else
+ insert (' ');
+ }
+ break;
+
+ default: /* not at newline */
+ {
+ int column;
+ int suppress_insert = 0;
+
+ if ((character == ' ') && (last_char_was_newline))
+ {
+ if (!paragraph_is_open)
+ {
+ pending_indent++;
+ return;
+ }
+ }
+
+ /* This is a sad place to do this, but it seems highly desirable
+ to not force any particular order on the front matter
+ commands. This way, the document can do @settitle,
+ @documentlanguage, etc, in any order and with any omissions,
+ and we'll still output the header ("produced by makeinfo",
+ HTML <head>, etc.) `just in time'. */
+ if ((executing_macro || !executing_string)
+ && !only_macro_expansion
+ && !defining_copying ()
+ && !output_head_p)
+ {
+ output_head ();
+ }
+
+ if (!paragraph_is_open)
+ {
+ start_paragraph ();
+ /* If the paragraph is supposed to be indented a certain
+ way, then discard all of the pending whitespace.
+ Otherwise, we let the whitespace stay. */
+ if (!paragraph_start_indent)
+ indent (pending_indent);
+ pending_indent = 0;
+
+ /* This check for in_html_block_level_elt prevents <p> from being
+ inserted when we already have html markup starting a paragraph,
+ as with <ul> and <h1> and the like. */
+ if (html && !in_html_block_level_elt)
+ {
+ if ((in_menu || in_detailmenu) && in_menu_item)
+ {
+ insert_string ("</li></ul>\n");
+ in_menu_item = 0;
+ }
+ insert_string ("<p>");
+ in_paragraph = 1;
+ adjust_braces_following (0, 3); /* adjust for <p> */
+ }
+ }
+
+ output_paragraph[output_paragraph_offset] = character;
+ output_paragraph_offset++;
+ column = current_output_column ();
+ output_paragraph_offset--;
+ if (column > fill_column)
+ {
+ if (filling_enabled && !html)
+ {
+ int temp = output_paragraph_offset;
+ while (--temp > 0 && output_paragraph[temp] != '\n')
+ {
+ /* If we have found a space, we have the place to break
+ the line. */
+ if (output_paragraph[temp] == ' ')
+ {
+ /* Remove trailing whitespace from output. */
+ while (temp && whitespace (output_paragraph[temp - 1]))
+ temp--;
+
+ /* If we went back all the way to the newline of the
+ preceding line, it probably means that the word we
+ are adding is itself wider than the space that the
+ indentation and the fill_column let us use. In
+ that case, do NOT insert another newline, since it
+ won't help. Just indent to current_indent and
+ leave it alone, since that's the most we can do. */
+ if (temp && output_paragraph[temp - 1] != '\n')
+ output_paragraph[temp++] = '\n';
+
+ /* We have correctly broken the line where we want
+ to. What we don't want is spaces following where
+ we have decided to break the line. We get rid of
+ them. */
+ {
+ int t1 = temp;
+
+ for (;; t1++)
+ {
+ if (t1 == output_paragraph_offset)
+ {
+ if (whitespace (character))
+ suppress_insert = 1;
+ break;
+ }
+ if (!whitespace (output_paragraph[t1]))
+ break;
+ }
+
+ if (t1 != temp)
+ {
+ adjust_braces_following (temp, (- (t1 - temp)));
+ memmove (&output_paragraph[temp],
+ &output_paragraph[t1],
+ output_paragraph_offset - t1);
+ output_paragraph_offset -= (t1 - temp);
+ }
+ }
+
+ /* Filled, but now indent if that is right. */
+ if (indented_fill && current_indent > 0)
+ {
+ int buffer_len = ((output_paragraph_offset - temp)
+ + current_indent);
+ char *temp_buffer = xmalloc (buffer_len);
+ int indentation = 0;
+
+ /* We have to shift any markers that are in
+ front of the wrap point. */
+ adjust_braces_following (temp, current_indent);
+
+ while (current_indent > 0 &&
+ indentation != current_indent)
+ temp_buffer[indentation++] = ' ';
+
+ memcpy ((char *) &temp_buffer[current_indent],
+ (char *) &output_paragraph[temp],
+ buffer_len - current_indent);
+
+ if (output_paragraph_offset + buffer_len
+ >= paragraph_buffer_len)
+ {
+ unsigned char *tt = xrealloc
+ (output_paragraph,
+ (paragraph_buffer_len += buffer_len));
+ output_paragraph = tt;
+ }
+ memcpy ((char *) &output_paragraph[temp],
+ temp_buffer, buffer_len);
+ output_paragraph_offset += current_indent;
+ free (temp_buffer);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (!suppress_insert)
+ {
+ insert (character);
+ last_inserted_character = character;
+ }
+ last_char_was_newline = 0;
+ line_already_broken = 0;
+ }
+ }
+}
+
+/* Add a character and store its position in meta_char_pos. */
+void
+add_meta_char (int character)
+{
+ meta_char_pos = output_paragraph_offset;
+ add_char (character);
+}
+
+/* Insert CHARACTER into `output_paragraph'. */
+void
+insert (int character)
+{
+ /* We don't want to strip trailing whitespace in multitables. Otherwise
+ horizontal separators confuse the font locking in Info mode in Emacs,
+ because it looks like a @subsection. Adding a trailing space to those
+ lines fixes it. */
+ if (character == '\n' && !html && !xml && !multitable_active)
+ {
+ while (output_paragraph_offset
+ && whitespace (output_paragraph[output_paragraph_offset-1]))
+ output_paragraph_offset--;
+ }
+
+ output_paragraph[output_paragraph_offset++] = character;
+ if (output_paragraph_offset == paragraph_buffer_len)
+ {
+ output_paragraph =
+ xrealloc (output_paragraph, (paragraph_buffer_len += 100));
+ }
+}
+
+/* Insert the null-terminated string STRING into `output_paragraph'. */
+void
+insert_string (const char *string)
+{
+ while (*string)
+ insert (*string++);
+}
+
+
+/* Sentences might have these characters after the period (or whatever). */
+#define POST_SENTENCE(c) ((c) == ')' || (c) == '\'' || (c) == '"' \
+ || (c) == ']')
+
+/* Return true if at an end-of-sentence character, possibly followed by
+ post-sentence punctuation to ignore. */
+static int
+end_of_sentence_p (void)
+{
+ int loc = output_paragraph_offset - 1;
+
+ /* If nothing has been output, don't check output_paragraph[-1]. */
+ if (loc < 0)
+ return 0;
+
+ /* A post-sentence character that is at meta_char_pos is not really
+ a post-sentence character; it was produced by a markup such as
+ @samp. We don't want the period inside @samp to be treated as a
+ sentence ender. */
+ while (loc > 0
+ && loc != meta_char_pos && POST_SENTENCE (output_paragraph[loc]))
+ loc--;
+ return loc != meta_char_pos && sentence_ender (output_paragraph[loc]);
+}
+
+
+/* Remove upto COUNT characters of whitespace from the
+ the current output line. If COUNT is less than zero,
+ then remove until none left. */
+void
+kill_self_indent (int count)
+{
+ /* Handle infinite case first. */
+ if (count < 0)
+ {
+ while (output_paragraph_offset)
+ {
+ if (whitespace (output_paragraph[output_paragraph_offset - 1]))
+ output_paragraph_offset--;
+ else
+ break;
+ }
+ }
+ else
+ {
+ while (output_paragraph_offset && count--)
+ if (whitespace (output_paragraph[output_paragraph_offset - 1]))
+ output_paragraph_offset--;
+ else
+ break;
+ }
+}
+
+/* Nonzero means do not honor calls to flush_output (). */
+static int flushing_ignored = 0;
+
+/* Prevent calls to flush_output () from having any effect. */
+void
+inhibit_output_flushing (void)
+{
+ flushing_ignored++;
+}
+
+/* Allow calls to flush_output () to write the paragraph data. */
+void
+uninhibit_output_flushing (void)
+{
+ flushing_ignored--;
+}
+
+void
+flush_output (void)
+{
+ int i;
+
+ if (!output_paragraph_offset || flushing_ignored)
+ return;
+
+ for (i = 0; i < output_paragraph_offset; i++)
+ {
+ if (output_paragraph[i] == '\n')
+ {
+ output_line_number++;
+ node_line_number++;
+ }
+
+ /* If we turned on the 8th bit for a space inside @w, turn it back off
+ for output. Don't do this for HTML, since the nbsp character is valid
+ input and must be passed along to the browser. */
+ if (!html && output_paragraph[i] == NON_BREAKING_SPACE)
+ output_paragraph[i] = ' ';
+ }
+
+ fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
+
+ output_position += output_paragraph_offset;
+ output_paragraph_start_column = current_output_column ();
+ output_paragraph_offset = 0;
+ meta_char_pos = 0;
+}
+
+/* How to close a paragraph controlling the number of lines between
+ this one and the last one. */
+
+/* Paragraph spacing is controlled by this variable. It is the number of
+ blank lines that you wish to appear between paragraphs. A value of
+ 1 creates a single blank line between paragraphs. */
+int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
+
+static void
+close_paragraph_with_lines (int lines)
+{
+ int old_spacing = paragraph_spacing;
+ paragraph_spacing = lines;
+ close_paragraph ();
+ paragraph_spacing = old_spacing;
+}
+
+/* Close the current paragraph, leaving no blank lines between them. */
+void
+close_single_paragraph (void)
+{
+ close_paragraph_with_lines (0);
+}
+
+/* Close a paragraph after an insertion has ended. */
+void
+close_insertion_paragraph (void)
+{
+ if (!insertion_paragraph_closed)
+ {
+ /* Close the current paragraph, breaking the line. */
+ close_single_paragraph ();
+
+ /* Start a new paragraph, with the correct indentation for the now
+ current insertion level (one above the one that we are ending). */
+ start_paragraph ();
+
+ /* Tell `close_paragraph' that the previous line has already been
+ broken, so it should insert one less newline. */
+ line_already_broken = 1;
+
+ /* Tell functions such as `add_char' we've already found a newline. */
+ ignore_blank_line ();
+ }
+ else
+ {
+ int column;
+
+ /* If the insertion paragraph is closed already, then we are seeing
+ two `@end' commands in a row. Note that the first one we saw was
+ handled in the first part of this if-then-else clause, and at that
+ time `start_paragraph' was called, partially to handle the proper
+ indentation of the current line. However, the indentation level
+ may have just changed again, so we may have to outdent the current
+ line to the new indentation level. */
+ column = current_output_column ();
+ if (current_indent < column)
+ kill_self_indent (column - current_indent);
+ }
+
+ insertion_paragraph_closed = 1;
+}
+
+/* Close the currently open paragraph. */
+void
+close_paragraph (void)
+{
+ int i;
+
+ /* We don't need these newlines in XML and Docbook outputs for
+ paragraph separation. We have the <para> element for that. */
+ if (xml)
+ return;
+
+ /* The insertion paragraph is no longer closed. */
+ insertion_paragraph_closed = 0;
+
+ if (paragraph_is_open && !must_start_paragraph)
+ {
+ int tindex = output_paragraph_offset;
+
+ /* Back up to last non-newline/space character, forcing all such
+ subsequent characters to be newlines. This isn't strictly
+ necessary, but a couple of functions use the presence of a newline
+ to make decisions. */
+ for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
+ {
+ int c = output_paragraph[tindex];
+
+ if (c == ' '|| c == '\n')
+ output_paragraph[tindex] = '\n';
+ else
+ break;
+ }
+
+ /* All trailing whitespace is ignored. */
+ output_paragraph_offset = ++tindex;
+
+ /* Break the line if that is appropriate. */
+ if (paragraph_spacing >= 0)
+ insert ('\n');
+
+ /* Add as many blank lines as is specified in `paragraph_spacing'. */
+ if (!force_flush_right)
+ {
+ for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
+ {
+ insert ('\n');
+ /* Don't need anything extra for HTML in usual case of no
+ extra paragraph spacing. */
+ if (html && i > 0)
+ insert_string ("<br>");
+ }
+ }
+
+ /* If we are doing flush right indentation, then do it now
+ on the paragraph (really a single line). */
+ if (force_flush_right)
+ do_flush_right_indentation ();
+
+ flush_output ();
+ paragraph_is_open = 0;
+ no_indent = 0;
+ }
+
+ ignore_blank_line ();
+}
+
+/* Make the last line just read look as if it were only a newline. */
+void
+ignore_blank_line (void)
+{
+ last_inserted_character = '\n';
+ last_char_was_newline = 1;
+}
+
+/* Align the end of the text in output_paragraph with fill_column. The last
+ character in output_paragraph is '\n'. */
+static void
+do_flush_right_indentation (void)
+{
+ char *temp;
+
+ kill_self_indent (-1);
+
+ if (output_paragraph[0] != '\n')
+ {
+ int width;
+
+ width = mbsnwidth ((char *)output_paragraph, output_paragraph_offset - 1,
+ 0) + 1;
+ if (width < fill_column)
+ {
+ int i;
+
+ if (output_paragraph_offset + (fill_column - width)
+ >= paragraph_buffer_len)
+ output_paragraph =
+ xrealloc (output_paragraph,
+ (paragraph_buffer_len += fill_column));
+
+ temp = xmalloc (output_paragraph_offset);
+ memcpy (temp, (char *)output_paragraph, output_paragraph_offset);
+
+ for (i = 0; i < fill_column - width; i++)
+ output_paragraph[i] = ' ';
+
+ memcpy ((char *)output_paragraph + i, temp, output_paragraph_offset);
+ free (temp);
+ output_paragraph_offset += i;
+ adjust_braces_following (0, i);
+ }
+ }
+}
+
+/* Begin a new paragraph. */
+void
+start_paragraph (void)
+{
+ /* First close existing one. */
+ if (paragraph_is_open)
+ close_paragraph ();
+
+ /* In either case, the insertion paragraph is no longer closed. */
+ insertion_paragraph_closed = 0;
+
+ /* However, the paragraph is open! */
+ paragraph_is_open = 1;
+
+ /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
+ had to be called before we would allow any other paragraph operations
+ to have an effect. */
+ if (!must_start_paragraph)
+ {
+ int amount_to_indent = 0;
+
+ /* If doing indentation, then insert the appropriate amount. */
+ if (!no_indent)
+ {
+ int column;
+
+ if (inhibit_paragraph_indentation)
+ {
+ amount_to_indent = current_indent;
+ if (inhibit_paragraph_indentation < 0)
+ inhibit_paragraph_indentation++;
+ }
+ else if (paragraph_start_indent < 0)
+ amount_to_indent = current_indent;
+ else
+ amount_to_indent = current_indent + paragraph_start_indent;
+
+ column = current_output_column ();
+ if (amount_to_indent >= column)
+ indent (amount_to_indent - column);
+ }
+ }
+ else
+ must_start_paragraph = 0;
+}
+
+/* Insert the indentation specified by AMOUNT. */
+void
+indent (int amount)
+{
+ /* For every START_POS saved within the brace stack which will be affected
+ by this indentation, bump that start pos forward. */
+ adjust_braces_following (output_paragraph_offset, amount);
+
+ while (--amount >= 0)
+ insert (' ');
+}
+
+/* Search forward for STRING in input_text.
+ FROM says where to start. */
+int
+search_forward (const char *string, int from)
+{
+ int len = strlen (string);
+
+ while (from < input_text_length)
+ {
+ if (strncmp (input_text + from, string, len) == 0)
+ return from;
+ from++;
+ }
+ return -1;
+}
+
+/* search_forward until n characters. */
+int
+search_forward_until_pos (char *string, int from, int end_pos)
+{
+ int save_input_text_length = input_text_length;
+ input_text_length = end_pos;
+
+ from = search_forward (string, from);
+
+ input_text_length = save_input_text_length;
+
+ return from;
+}
+
+/* Return next non-whitespace and non-cr character. */
+int
+next_nonwhitespace_character (void)
+{
+ /* First check the current input_text. Start from the next char because
+ we already have input_text[input_text_offset] in ``current''. */
+ int pos = input_text_offset + 1;
+
+ while (pos < input_text_length)
+ {
+ if (!cr_or_whitespace(input_text[pos]))
+ return input_text[pos];
+ pos++;
+ }
+
+ { /* Can't find a valid character, so go through filestack
+ in case we are doing @include or expanding a macro. */
+ FSTACK *tos = filestack;
+
+ while (tos)
+ {
+ int tmp_input_text_length = filestack->size;
+ int tmp_input_text_offset = filestack->offset;
+ char *tmp_input_text = filestack->text;
+
+ while (tmp_input_text_offset < tmp_input_text_length)
+ {
+ if (!cr_or_whitespace(tmp_input_text[tmp_input_text_offset]))
+ return tmp_input_text[tmp_input_text_offset];
+ tmp_input_text_offset++;
+ }
+
+ tos = tos->next;
+ }
+ }
+
+ return -1;
+}
+
+
+
+/* Replace " with \" and \ with \\. Used for alt tag in Info output.
+ Return a newly-malloced string in all cases. */
+
+static char *
+bs_escape_quote (const char *src)
+{
+ int c;
+ char *dest = xmalloc (2 * strlen (src) + 1); /* can't need more. */
+ char *p = dest;
+
+ for (; c = *src; src++)
+ {
+ if (c == '"' || c == '\\')
+ *p++ = '\\';
+
+ *p++ = c;
+ }
+ *p = 0;
+
+ return dest;
+}
+
+
+
+/* An external image is a reference, kind of. The parsing is (not
+ coincidentally) similar, anyway. */
+
+void
+cm_image (int arg)
+{
+ char *name_arg, *w_arg, *h_arg, *alt_arg, *ext_arg;
+
+ if (arg == END)
+ return;
+
+ name_arg = get_xref_token (1); /* expands all macros in image */
+ w_arg = get_xref_token (0);
+ h_arg = get_xref_token (0);
+ alt_arg = get_xref_token (1); /* expands all macros in alt text */
+ ext_arg = get_xref_token (0);
+
+ if (*name_arg)
+ {
+ struct stat file_info;
+ char *pathname = NULL;
+ unsigned ext_len = (ext_arg && *ext_arg) ? strlen (ext_arg) : 0;
+ /* One byte for the . period separator, one byte for the null.
+ The 3 is for the max length of the hardwired extensions we try. */
+ char *fullname = xmalloc (strlen (name_arg) + 1 + MAX (ext_len, 3) + 1);
+
+ if (ext_arg && *ext_arg)
+ {
+ sprintf (fullname, "%s%s", name_arg, ext_arg);
+ if (access (fullname, R_OK) != 0)
+ pathname = get_file_info_in_path (fullname, include_files_path,
+ &file_info);
+
+ if (pathname == NULL)
+ {
+ /* Backwards compatibility (4.6 <= version < 4.7):
+ try prefixing @image's EXTENSION parameter with a period. */
+ sprintf (fullname, "%s.%s", name_arg, ext_arg);
+ if (access (fullname, R_OK) != 0)
+ pathname = get_file_info_in_path (fullname, include_files_path,
+ &file_info);
+ }
+ }
+ else
+ {
+ sprintf (fullname, "%s.png", name_arg);
+ if (access (fullname, R_OK) != 0) {
+ pathname = get_file_info_in_path (fullname,
+ include_files_path, &file_info);
+ if (pathname == NULL) {
+ sprintf (fullname, "%s.jpg", name_arg);
+ if (access (fullname, R_OK) != 0) {
+ pathname = get_file_info_in_path (fullname,
+ include_files_path,
+ &file_info);
+ if (pathname == NULL) {
+ sprintf (fullname, "%s.gif", name_arg);
+ if (access (fullname, R_OK) != 0) {
+ pathname = get_file_info_in_path (fullname,
+ include_files_path,
+ &file_info);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (html)
+ {
+ int image_in_div = 0;
+
+ if (pathname == NULL && access (fullname, R_OK) != 0)
+ {
+ line_error(_("@image file `%s' (for HTML) not readable: %s"),
+ fullname, strerror (errno));
+ return;
+ }
+ if (pathname != NULL && access (pathname, R_OK) != 0)
+ {
+ line_error (_("No such file `%s'"),
+ fullname);
+ return;
+ }
+
+ if (!paragraph_is_open)
+ {
+ add_html_block_elt ("<div class=\"block-image\">");
+ image_in_div = 1;
+ }
+
+ add_html_elt ("<img src=");
+ add_word_args ("\"%s\"", fullname);
+ add_html_elt (" alt=");
+ add_word_args ("\"%s\">",
+ escape_string (*alt_arg ? text_expansion (alt_arg) : fullname));
+
+ if (image_in_div)
+ add_html_block_elt ("</div>");
+ }
+ else if (xml && docbook)
+ xml_insert_docbook_image (name_arg);
+ else if (xml)
+ {
+ extern int xml_in_para;
+ extern int xml_no_para;
+ int elt = xml_in_para ? INLINEIMAGE : IMAGE;
+
+ if (!xml_in_para)
+ xml_no_para++;
+
+ xml_insert_element_with_attribute (elt,
+ START, "width=\"%s\" height=\"%s\" name=\"%s\" extension=\"%s\"",
+ w_arg, h_arg, name_arg, ext_arg);
+ xml_insert_element (IMAGEALTTEXT, START);
+ execute_string ("%s", alt_arg);
+ xml_insert_element (IMAGEALTTEXT, END);
+ xml_insert_element (elt, END);
+
+ if (!xml_in_para)
+ xml_no_para--;
+ }
+ else
+ { /* Prefer foo.txt for Info/ASCII. Seems wrong nowadays. */
+ FILE *image_file;
+ char *txtpath = NULL;
+ char *txtname = xmalloc (strlen (name_arg) + 4 + 1);
+ strcpy (txtname, name_arg);
+ strcat (txtname, ".txt");
+ image_file = fopen (txtname, "r");
+ if (image_file == NULL)
+ {
+ txtpath = get_file_info_in_path (txtname,
+ include_files_path, &file_info);
+ if (txtpath != NULL)
+ image_file = fopen (txtpath, "r");
+ }
+
+ if (image_file != NULL
+ || access (fullname, R_OK) == 0
+ || (pathname != NULL && access (pathname, R_OK) == 0))
+ {
+ int ch;
+ int save_inhibit_indentation = inhibit_paragraph_indentation;
+ int save_filling_enabled = filling_enabled;
+ int image_in_brackets = paragraph_is_open;
+
+ /* Write magic ^@^H[image ...^@^H] cookie in the info file, if
+ there's an accompanying bitmap. Otherwise just include the
+ text image. In the plaintext output, always include the text
+ image without the magic cookie. */
+ int use_magic_cookie = !no_headers
+ && access (fullname, R_OK) == 0 && !STREQ (fullname, txtname);
+
+ inhibit_paragraph_indentation = 1;
+ filling_enabled = 0;
+ last_char_was_newline = 0;
+
+ if (use_magic_cookie)
+ {
+ add_char ('\0');
+ add_word ("\010[image");
+
+ if (access (fullname, R_OK) == 0
+ || (pathname != NULL && access (pathname, R_OK) == 0))
+ add_word_args (" src=\"%s\"", fullname);
+
+ if (*alt_arg)
+ {
+ char *expanded = text_expansion (alt_arg);
+ char *escaped = bs_escape_quote (expanded);
+ add_word_args (" alt=\"%s\"", escaped);
+ free (expanded);
+ free (escaped);
+ }
+ }
+
+ if (image_file != NULL)
+ {
+ if (use_magic_cookie)
+ add_word (" text=\"");
+
+ if (image_in_brackets)
+ add_char ('[');
+
+ /* Maybe we need to remove the final newline if the image
+ file is only one line to allow in-line images. On the
+ other hand, they could just make the file without a
+ final newline. */
+ while ((ch = getc (image_file)) != EOF)
+ {
+ if (use_magic_cookie && (ch == '"' || ch == '\\'))
+ add_char ('\\');
+ add_char (ch);
+ }
+
+ if (image_in_brackets)
+ add_char (']');
+
+ if (use_magic_cookie)
+ add_char ('"');
+
+ if (fclose (image_file) != 0)
+ perror (txtname);
+ }
+
+ if (use_magic_cookie)
+ {
+ add_char ('\0');
+ add_word ("\010]");
+ }
+
+ inhibit_paragraph_indentation = save_inhibit_indentation;
+ filling_enabled = save_filling_enabled;
+ }
+ else
+ warning (_("@image file `%s' (for text) unreadable: %s"),
+ txtname, strerror (errno));
+ }
+
+ free (fullname);
+ if (pathname)
+ free (pathname);
+ }
+ else
+ line_error (_("@image missing filename argument"));
+
+ if (name_arg)
+ free (name_arg);
+ if (w_arg)
+ free (w_arg);
+ if (h_arg)
+ free (h_arg);
+ if (alt_arg)
+ free (alt_arg);
+ if (ext_arg)
+ free (ext_arg);
+}
+
+/* Conditionals. */
+
+/* A structure which contains `defined' variables. */
+typedef struct defines {
+ struct defines *next;
+ char *name;
+ char *value;
+} DEFINE;
+
+/* The linked list of `set' defines. */
+DEFINE *defines = NULL;
+
+/* Add NAME to the list of `set' defines. */
+static void
+set (char *name, char *value)
+{
+ DEFINE *temp;
+
+ for (temp = defines; temp; temp = temp->next)
+ if (strcmp (name, temp->name) == 0)
+ {
+ free (temp->value);
+ temp->value = xstrdup (value);
+ return;
+ }
+
+ temp = xmalloc (sizeof (DEFINE));
+ temp->next = defines;
+ temp->name = xstrdup (name);
+ temp->value = xstrdup (value);
+ defines = temp;
+
+ if (xml && !docbook)
+ {
+ xml_insert_element_with_attribute (SETVALUE, START, "name=\"%s\"", name);
+ execute_string ("%s", value);
+ xml_insert_element (SETVALUE, END);
+ }
+}
+
+/* Remove NAME from the list of `set' defines. */
+static void
+clear (char *name)
+{
+ DEFINE *temp, *last;
+
+ last = NULL;
+ temp = defines;
+
+ while (temp)
+ {
+ if (strcmp (temp->name, name) == 0)
+ {
+ if (last)
+ last->next = temp->next;
+ else
+ defines = temp->next;
+
+ free (temp->name);
+ free (temp->value);
+ free (temp);
+ break;
+ }
+ last = temp;
+ temp = temp->next;
+ }
+
+ if (xml && !docbook)
+ {
+ xml_insert_element_with_attribute (CLEARVALUE, START, "name=\"%s\"", name);
+ xml_insert_element (CLEARVALUE, END);
+ }
+}
+
+/* Return the value of NAME. The return value is NULL if NAME is unset. */
+static char *
+set_p (char *name)
+{
+ DEFINE *temp;
+
+ for (temp = defines; temp; temp = temp->next)
+ if (strcmp (temp->name, name) == 0)
+ return temp->value;
+
+ return NULL;
+}
+
+/* Create a variable whose name appears as the first word on this line. */
+void
+cm_set (void)
+{
+ handle_variable (SET);
+}
+
+/* Remove a variable whose name appears as the first word on this line. */
+void
+cm_clear (void)
+{
+ handle_variable (CLEAR);
+}
+
+void
+cm_ifset (void)
+{
+ handle_variable (IFSET);
+}
+
+void
+cm_ifclear (void)
+{
+ handle_variable (IFCLEAR);
+}
+
+/* This command takes braces, but we parse the contents specially, so we
+ don't use the standard brace popping code.
+
+ The syntax @ifeq{arg1, arg2, texinfo-commands} performs texinfo-commands
+ if ARG1 and ARG2 caselessly string compare to the same string, otherwise,
+ it produces no output. */
+void
+cm_ifeq (void)
+{
+ char **arglist;
+
+ arglist = get_brace_args (quote_none);
+
+ if (arglist)
+ {
+ if (array_len (arglist) > 1)
+ {
+ if ((mbscasecmp (arglist[0], arglist[1]) == 0) &&
+ (arglist[2]))
+ execute_string ("%s\n", arglist[2]);
+ }
+
+ free_array (arglist);
+ }
+}
+
+void
+cm_value (int arg, int start_pos, int end_pos)
+{
+ static int value_level = 0, saved_meta_pos = -1;
+
+ /* xml_add_char() skips any content inside menus when output format is
+ Docbook, so @value{} is no use there. Also start_pos and end_pos does not
+ get updated, causing name to be empty string. So just return. */
+ if (docbook && in_menu)
+ return;
+
+ /* All the text after @value{ upto the matching } will eventually
+ disappear from output_paragraph, when this function is called
+ with ARG == END. If the text produced until then sets
+ meta_char_pos, we will need to restore it to the value it had
+ before @value was seen. So we need to save the previous value
+ of meta_char_pos here. */
+ if (arg == START)
+ {
+ /* If we are already inside some outer @value, don't overwrite
+ the value saved in saved_meta_pos. */
+ if (!value_level)
+ saved_meta_pos = meta_char_pos;
+ value_level++;
+ /* While the argument of @value is processed, we need to inhibit
+ textual transformations like "--" into "-", since @set didn't
+ do that when it grabbed the name of the variable. */
+ in_fixed_width_font++;
+ }
+ else
+ {
+ char *name = (char *) &output_paragraph[start_pos];
+ char *value;
+ output_paragraph[end_pos] = 0;
+ name = xstrdup (name);
+ value = set_p (name);
+ output_paragraph_offset = start_pos;
+
+ /* Restore the previous value of meta_char_pos if the stuff
+ inside this @value{} moved it. */
+ if (saved_meta_pos == -1) /* can't happen inside @value{} */
+ abort ();
+ if (value_level == 1
+ && meta_char_pos >= start_pos && meta_char_pos < end_pos)
+ {
+ meta_char_pos = saved_meta_pos;
+ saved_meta_pos = -1;
+ }
+ value_level--;
+ /* No need to decrement in_fixed_width_font, since before
+ we are called with arg == END, the reader loop already
+ popped the brace stack, which restored in_fixed_width_font,
+ among other things. */
+
+ if (value)
+ {
+ /* We need to get past the closing brace since the value may
+ expand to a context-sensitive macro (e.g. @xref) and produce
+ spurious warnings */
+ input_text_offset++;
+ execute_string ("%s", value);
+ input_text_offset--;
+ }
+ else
+ {
+ warning (_("undefined flag: %s"), name);
+ add_word_args (_("{No value for `%s'}"), name);
+ }
+
+ free (name);
+ }
+}
+
+/* Set, clear, or conditionalize based on ACTION. */
+static void
+handle_variable (int action)
+{
+ char *name;
+
+ get_rest_of_line (0, &name);
+ /* If we hit the end of text in get_rest_of_line, backing up
+ input pointer will cause the last character of the last line
+ be pushed back onto the input, which is wrong. */
+ if (input_text_offset < input_text_length)
+ backup_input_pointer ();
+ handle_variable_internal (action, name);
+ free (name);
+}
+
+static void
+handle_variable_internal (int action, char *name)
+{
+ char *temp;
+ int delimiter, additional_text_present = 0;
+
+ /* Only the first word of NAME is a valid tag. */
+ temp = name;
+ delimiter = 0;
+ while (*temp && (delimiter || !whitespace (*temp)))
+ {
+/* #if defined (SET_WITH_EQUAL) */
+ if (*temp == '"' || *temp == '\'')
+ {
+ if (*temp == delimiter)
+ delimiter = 0;
+ else
+ delimiter = *temp;
+ }
+/* #endif SET_WITH_EQUAL */
+ temp++;
+ }
+
+ if (*temp)
+ additional_text_present++;
+
+ *temp = 0;
+
+ if (!*name)
+ line_error (_("%c%s requires a name"), COMMAND_PREFIX, command);
+ else
+ {
+ switch (action)
+ {
+ case SET:
+ {
+ char *value;
+
+#if defined (SET_WITH_EQUAL)
+ /* Allow a value to be saved along with a variable. The value is
+ the text following an `=' sign in NAME, if any is present. */
+
+ for (value = name; *value && *value != '='; value++);
+
+ if (*value)
+ *value++ = 0;
+
+ if (*value == '"' || *value == '\'')
+ {
+ value++;
+ value[strlen (value) - 1] = 0;
+ }
+
+#else /* !SET_WITH_EQUAL */
+ /* The VALUE of NAME is the remainder of the line sans
+ whitespace. */
+ if (additional_text_present)
+ {
+ value = temp + 1;
+ canon_white (value);
+ }
+ else
+ value = "";
+#endif /* !SET_WITH_VALUE */
+
+ set (name, value);
+ }
+ break;
+
+ case CLEAR:
+ clear (name);
+ break;
+
+ case IFSET:
+ case IFCLEAR:
+ /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
+ read lines from the the file until we reach a matching
+ "@end CONDITION". This means that we only take note of
+ "@ifset/clear" and "@end" commands. */
+ {
+ char condition[8];
+ int condition_len;
+ int orig_line_number = line_number;
+
+ if (action == IFSET)
+ strcpy (condition, "ifset");
+ else
+ strcpy (condition, "ifclear");
+
+ condition_len = strlen (condition);
+
+ if ((action == IFSET && !set_p (name))
+ || (action == IFCLEAR && set_p (name)))
+ {
+ int level = 0, done = 0;
+
+ while (!done && input_text_offset < input_text_length)
+ {
+ char *freeable_line, *line;
+
+ get_rest_of_line (0, &freeable_line);
+
+ for (line = freeable_line; whitespace (*line); line++);
+
+ if (*line == COMMAND_PREFIX &&
+ (strncmp (line + 1, condition, condition_len) == 0))
+ level++;
+ else if (strncmp (line, "@end", 4) == 0)
+ {
+ char *cname = line + 4;
+ char *temp;
+
+ while (*cname && whitespace (*cname))
+ cname++;
+ temp = cname;
+
+ while (*temp && !whitespace (*temp))
+ temp++;
+ *temp = 0;
+
+ if (strcmp (cname, condition) == 0)
+ {
+ if (!level)
+ {
+ done = 1;
+ }
+ else
+ level--;
+ }
+ }
+ free (freeable_line);
+ }
+
+ if (!done)
+ file_line_error (input_filename, orig_line_number,
+ _("Reached eof before matching @end %s"),
+ condition);
+
+ /* We found the end of a false @ifset/ifclear. If we are
+ in a menu, back up over the newline that ends the ifset,
+ since that newline may also begin the next menu entry. */
+ break;
+ }
+ else
+ {
+ if (action == IFSET)
+ begin_insertion (ifset);
+ else
+ begin_insertion (ifclear);
+ }
+ }
+ break;
+ }
+ }
+}
+
+/* Execution of random text not in file. */
+typedef struct {
+ char *string; /* The string buffer. */
+ int size; /* The size of the buffer. */
+ int in_use; /* Nonzero means string currently in use. */
+} EXECUTION_STRING;
+
+static EXECUTION_STRING **execution_strings = NULL;
+static int execution_strings_index = 0;
+static int execution_strings_slots = 0;
+
+static EXECUTION_STRING *
+get_execution_string (int initial_size)
+{
+ int i = 0;
+ EXECUTION_STRING *es = NULL;
+
+ if (execution_strings)
+ {
+ for (i = 0; i < execution_strings_index; i++)
+ if (execution_strings[i] && (execution_strings[i]->in_use == 0))
+ {
+ es = execution_strings[i];
+ break;
+ }
+ }
+
+ if (!es)
+ {
+ if (execution_strings_index + 1 >= execution_strings_slots)
+ {
+ execution_strings = xrealloc
+ (execution_strings,
+ (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *));
+ for (; i < execution_strings_slots; i++)
+ execution_strings[i] = NULL;
+ }
+
+ execution_strings[execution_strings_index] =
+ xmalloc (sizeof (EXECUTION_STRING));
+ es = execution_strings[execution_strings_index];
+ execution_strings_index++;
+
+ es->size = 0;
+ es->string = NULL;
+ es->in_use = 0;
+ }
+
+ if (initial_size > es->size)
+ {
+ es->string = xrealloc (es->string, initial_size);
+ es->size = initial_size;
+ }
+ return es;
+}
+
+/* Given a pointer to TEXT and its desired length NEW_LEN, find TEXT's
+ entry in the execution_strings[] array and change the .STRING and
+ .SIZE members of that entry as appropriate. */
+void
+maybe_update_execution_strings (char **text, unsigned int new_len)
+{
+ int i = 0;
+
+ if (execution_strings)
+ {
+ for (i = 0; i < execution_strings_index; i++)
+ if (execution_strings[i] && (execution_strings[i]->in_use == 1) &&
+ execution_strings[i]->string == *text)
+ {
+ /* Don't ever shrink the string storage in execution_strings[]!
+ execute_string assumes that it is always big enough to store
+ every possible execution_string, and will break if that's
+ not true. So we only enlarge the string storage if the
+ current size isn't big enough. */
+ if (execution_strings[i]->size < new_len)
+ {
+ execution_strings[i]->string =
+ *text = xrealloc (*text, new_len + 1);
+ execution_strings[i]->size = new_len + 1;
+ }
+ return;
+ }
+ }
+ /* We should *never* end up here, since if we are inside
+ execute_string, TEXT is always in execution_strings[]. */
+ abort ();
+}
+
+#define EXECUTE_STRING_MAX (32*1024) /* FIXXME: this is an arbitrary limit. */
+
+/* Execute the string produced by formatting the ARGs with FORMAT. This
+ is like submitting a new file with @include. */
+void
+#if defined (VA_FPRINTF) && __STDC__
+execute_string (char *format, ...)
+#else
+execute_string (format, va_alist)
+ char *format;
+ va_dcl
+#endif
+{
+ EXECUTION_STRING *es;
+ char *temp_string, *temp_input_filename;
+#ifdef VA_FPRINTF
+ va_list ap;
+#endif
+ int insertion_level_at_start = insertion_level;
+
+ es = get_execution_string (EXECUTE_STRING_MAX);
+ temp_string = es->string;
+ es->in_use = 1;
+
+ VA_START (ap, format);
+#ifdef VA_SPRINTF
+ VA_SPRINTF (temp_string, format, ap);
+#else
+ sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* not VA_SPRINTF */
+ va_end (ap);
+
+ pushfile ();
+ input_text_offset = 0;
+ input_text = temp_string;
+ input_text_length = strlen (temp_string);
+ input_filename = xstrdup (input_filename);
+ temp_input_filename = input_filename;
+
+ executing_string++;
+ reader_loop ();
+
+ /* If insertion stack level changes during execution, that means a multiline
+ command is used inside braces or @section ... kind of commands. */
+ if (insertion_level_at_start != insertion_level && !executing_macro)
+ {
+ line_error (_("Multiline command %c%s used improperly"),
+ COMMAND_PREFIX,
+ command);
+ /* We also need to keep insertion_level intact to make sure warnings are
+ issued for @end ... command. */
+ while (insertion_level > insertion_level_at_start)
+ pop_insertion ();
+ }
+
+ popfile ();
+ executing_string--;
+ es->in_use = 0;
+ free (temp_input_filename);
+}
+
+
+/* Return what would be output for STR (in newly-malloced memory), i.e.,
+ expand Texinfo commands according to the current output format. If
+ IMPLICIT_CODE is set, expand @code{STR}. This is generally used for
+ short texts; filling, indentation, and html escapes are disabled. */
+
+char *
+expansion (char *str, int implicit_code)
+{
+ return maybe_escaped_expansion (str, implicit_code, 0);
+}
+
+
+/* Do HTML escapes according to DO_HTML_ESCAPE. Needed in
+ cm_printindex, q.v. */
+
+char *
+maybe_escaped_expansion (char *str, int implicit_code, int do_html_escape)
+{
+ char *result;
+
+ /* Inhibit indentation and filling, so that extra newlines
+ are not added to the expansion. (This is undesirable if
+ we write the expanded text to macro_expansion_output_stream.) */
+ int saved_filling_enabled = filling_enabled;
+ int saved_indented_fill = indented_fill;
+ int saved_no_indent = no_indent;
+ int saved_escape_html = escape_html;
+
+ filling_enabled = 0;
+ indented_fill = 0;
+ no_indent = 1;
+ escape_html = do_html_escape;
+ xml_no_para++;
+
+ result = full_expansion (str, implicit_code);
+
+ filling_enabled = saved_filling_enabled;
+ indented_fill = saved_indented_fill;
+ no_indent = saved_no_indent;
+ escape_html = saved_escape_html;
+ xml_no_para--;
+
+ return result;
+}
+
+
+/* Expand STR (or @code{STR} if IMPLICIT_CODE is nonzero). No change to
+ any formatting parameters -- filling, indentation, html escapes,
+ etc., are not reset. Always returned in new memory. */
+
+char *
+full_expansion (char *str, int implicit_code)
+{
+ int length;
+ char *result;
+
+ /* Inhibit any real output. */
+ int start = output_paragraph_offset;
+ int saved_paragraph_is_open = paragraph_is_open;
+
+ /* More output state to save. */
+ int saved_meta_pos = meta_char_pos;
+ int saved_last_char = last_inserted_character;
+ int saved_last_nl = last_char_was_newline;
+
+ /* If we are called in the middle of processing a command, we need
+ to dup and save the global variable `command' (which holds the
+ name of this command), since the recursive reader loop will free
+ it from under our feet if it finds any macros in STR. */
+ char *saved_command = command ? xstrdup (command) : NULL;
+
+ inhibit_output_flushing ();
+ paragraph_is_open = 1;
+ if (strlen (str) > (implicit_code
+ ? EXECUTE_STRING_MAX - 1 - sizeof("@code{}")
+ : EXECUTE_STRING_MAX - 1))
+ line_error (_("`%.40s...' is too long for expansion; not expanded"), str);
+ else
+ execute_string (implicit_code ? "@code{%s}" : "%s", str);
+ uninhibit_output_flushing ();
+
+ /* Copy the expansion from the buffer. */
+ length = output_paragraph_offset - start;
+ result = xmalloc (1 + length);
+ memcpy (result, (char *) (output_paragraph + start), length);
+ result[length] = 0;
+
+ /* Pretend it never happened. */
+ free_and_clear (&command);
+ command = saved_command;
+
+ output_paragraph_offset = start;
+ paragraph_is_open = saved_paragraph_is_open;
+
+ meta_char_pos = saved_meta_pos;
+ last_inserted_character = saved_last_char;
+ last_char_was_newline = saved_last_nl;
+
+ return result;
+}
+
+
+/* Return text (info) expansion of STR no matter what the current output
+ format is. */
+
+char *
+text_expansion (char *str)
+{
+ char *ret;
+ int save_html = html;
+ int save_xml = xml;
+ int save_docbook = docbook;
+
+ html = 0;
+ xml = 0;
+ docbook = 0;
+ ret = expansion (str, 0);
+ html = save_html;
+ xml = save_xml;
+ docbook = save_docbook;
+
+ return ret;
+}
+
+
+/* Set the paragraph indentation variable to the value specified in STRING.
+ Values can be:
+ `asis': Don't change existing indentation.
+ `none': Remove existing indentation.
+ NUM: Indent NUM spaces at the starts of paragraphs.
+ If NUM is zero, we assume `none'.
+ Returns 0 if successful, or nonzero if STRING isn't one of the above. */
+int
+set_paragraph_indent (char *string)
+{
+ if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
+ paragraph_start_indent = 0;
+ else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
+ paragraph_start_indent = -1;
+ else
+ {
+ if (sscanf (string, "%d", &paragraph_start_indent) != 1)
+ return -1;
+ else
+ {
+ if (paragraph_start_indent == 0)
+ paragraph_start_indent = -1;
+ }
+ }
+ return 0;
+}
+
+
+/* Translate MSGID according to the document language (@documentlanguage
+ value or --document-language), rather than the current locale
+ (LANGUAGE/LC_ALL/LANG). This code is from the get_title function in
+ gettext. (xsetenv and unsetenv come from the gnulib xsetenv module.) */
+
+char *
+getdocumenttext (const char *msgid)
+{
+ /* The original get_title also saves, sets, and restores
+ OUTPUT_CHARSET, so that the translation will be given in
+ the proper encoding (via canonical_locale_charset). But defining
+ that function ends up pulling a whole lot of subsidiary functions.
+ Not sure how to handle it; skip the whole thing for now. */
+ const char *tmp;
+ char *old_LC_ALL;
+ char *old_LANGUAGE;
+ const char *result;
+#ifdef HAVE_SETLOCALE
+ char *old_locale;
+#endif
+
+ /* Save LC_ALL, LANGUAGE environment variables. */
+
+ tmp = getenv ("LC_ALL");
+ old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+ tmp = getenv ("LANGUAGE");
+ old_LANGUAGE = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+ xsetenv ("LC_ALL", document_language, 1);
+ unsetenv ("LANGUAGE");
+
+#ifdef HAVE_SETLOCALE
+ old_locale = xstrdup (setlocale (LC_ALL, NULL));
+ if (setlocale (LC_ALL, "") == NULL)
+ /* Nonexistent locale. Use the original. */
+ result = msgid;
+ else
+#endif
+ {
+ /* Fetch the translation. */
+ result = gettext ((char *) msgid);
+ }
+
+ /* Restore LC_ALL, LANGUAGE environment variables. */
+
+ if (old_LC_ALL != NULL)
+ xsetenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
+ else
+ unsetenv ("LC_ALL");
+
+ if (old_LANGUAGE != NULL)
+ xsetenv ("LANGUAGE", old_LANGUAGE, 1), free (old_LANGUAGE);
+ else
+ unsetenv ("LANGUAGE");
+
+#ifdef HAVE_SETLOCALE
+ setlocale (LC_ALL, old_locale);
+ free (old_locale);
+#endif
+
+ return (char *) result;
+}
diff --git a/makeinfo/makeinfo.h b/makeinfo/makeinfo.h
new file mode 100644
index 0000000..ec3b26b
--- /dev/null
+++ b/makeinfo/makeinfo.h
@@ -0,0 +1,395 @@
+/* makeinfo.h -- declarations for Makeinfo.
+ $Id: makeinfo.h,v 1.31 2008/03/26 23:57:12 karl Exp $
+
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007, 2008 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 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 <http://www.gnu.org/licenses/>.
+
+ Written by Brian Fox (bfox@ai.mit.edu). */
+
+#ifndef MAKEINFO_H
+#define MAKEINFO_H
+
+#ifdef COMPILING_MAKEINFO
+# define DECLARE(type,var,init) type var = init
+#else
+# define DECLARE(type,var,init) extern type var
+#endif
+
+/* Hardcoded per GNU standards, not dependent on argv[0]. */
+DECLARE (char *, progname, "makeinfo");
+
+/* Nonzero means a string is in execution, as opposed to a file. */
+DECLARE (int, executing_string, 0);
+
+/* Nonzero means to inhibit writing macro expansions to the output
+ stream, because it has already been written. */
+DECLARE (int, me_inhibit_expansion, 0);
+
+/* Current output stream. */
+DECLARE (FILE *, output_stream, NULL);
+
+DECLARE (char *, pretty_output_filename, NULL);
+
+/* Current output file name. */
+DECLARE (char *, current_output_filename, NULL);
+
+/* Output paragraph buffer and its length. */
+#define INITIAL_PARAGRAPH_BUFFER_LEN 5000
+DECLARE (unsigned char *, output_paragraph, NULL);
+DECLARE (int, paragraph_buffer_len, INITIAL_PARAGRAPH_BUFFER_LEN);
+
+/* Offset into OUTPUT_PARAGRAPH. */
+DECLARE (int, output_paragraph_offset, 0);
+
+/* Position in the output file. */
+DECLARE (int, output_position, 0);
+
+/* Number of lines in the output. */
+DECLARE (int, output_line_number, 1);
+DECLARE (int, node_line_number, 0);
+
+/* The offset into OUTPUT_PARAGRAPH where we have a meta character
+ produced by a markup such as @code or @dfn. */
+DECLARE (int, meta_char_pos, -1);
+
+/* Nonzero means output_paragraph contains text. */
+DECLARE (int, paragraph_is_open, 0);
+
+/* Nonzero means that `start_paragraph' MUST be called before we pay
+ any attention to `close_paragraph' calls. */
+DECLARE (int, must_start_paragraph, 0);
+
+/* Nonzero if we have output the topmatter of the output file. */
+DECLARE (int, output_head_p, 0);
+
+/* Nonzero means that we have seen "@top" once already. */
+DECLARE (int, top_node_seen, 0);
+
+/* Nonzero means that we have seen a non-"@top" node already. */
+DECLARE (int, non_top_node_seen, 0);
+
+/* Nonzero indicates that indentation is temporarily turned off. */
+DECLARE (int, no_indent, 1);
+
+/* The amount of indentation to apply at the start of each line. */
+DECLARE (int, current_indent, 0);
+
+/* Nonzero means that we suppress the indentation of the first paragraph
+ following any section heading. */
+DECLARE (int, do_first_par_indent, 0);
+
+/* Amount by which @example indentation increases/decreases. */
+DECLARE (int, example_indentation_increment, 5);
+
+/* Amount by which @table, @defun, etc. indentation increases/decreases. */
+DECLARE (int, default_indentation_increment, 5);
+
+/* Amount by which xml indentation increases/decreases.
+ Zero means unnecessary whitespace is compressed. */
+DECLARE (int, xml_indentation_increment, 2);
+
+/* Nonzero indicates that filling a line also indents the new line. */
+DECLARE (int, indented_fill, 0);
+
+/* Nonzero means forcing output text to be flushright. */
+DECLARE (int, force_flush_right, 0);
+
+/* The column at which long lines are broken. */
+DECLARE (int, fill_column, 72);
+
+/* Nonzero means we're doing one space after sentences (@frenchspacing). */
+DECLARE (int, french_spacing, 0);
+
+/* Nonzero means that words are not to be split, even in long lines. This
+ gets changed for cm_w (). */
+DECLARE (int, non_splitting_words, 0);
+
+/* Nonzero means that we are currently hacking the insides of an
+ insertion which would use a fixed width font. */
+DECLARE (int, in_fixed_width_font, 0);
+
+/* Nonzero if we are currently processing a multitable command */
+DECLARE (int, multitable_active, 0);
+
+/* Nonzero means that we're generating HTML. (--html) */
+DECLARE (int, html, 0);
+
+/* Nonzero means that we're generating XML. (--xml) */
+DECLARE (int, xml, 0);
+
+/* Nonzero means that we're generating DocBook. (--docbook) */
+DECLARE (int, docbook, 0);
+
+/* Nonzero means 8-bit output for Info and plain text, according to
+ @documentencoding. (--enable-encoding) */
+DECLARE (int, enable_encoding, 1);
+
+/* Nonzero means escape characters in HTML output. */
+DECLARE (int, escape_html, 1);
+
+/* Access key number for next menu entry to be generated (1 to 9, or 10 to
+ mean no access key) */
+DECLARE (int, next_menu_item_number, 1);
+
+/* Nonzero means that the use of paragraph_start_indent is inhibited.
+ @example uses this to line up the left columns of the example text.
+ A negative value for this variable is incremented each time it is used.
+ @noindent uses this to inhibit indentation for a single paragraph. */
+DECLARE (int, inhibit_paragraph_indentation, 0);
+
+/* Nonzero indicates that filling will take place on long lines. */
+DECLARE (int, filling_enabled, 1);
+
+/* The current node's node name. */
+DECLARE (char *, current_node, NULL);
+
+/* Command name in the process of being hacked. */
+DECLARE (char *, command, NULL);
+
+/* Nonzero if we have seen an @titlepage command. */
+DECLARE (int, titlepage_cmd_present, 0);
+
+/* @copying ... @end copying. */
+DECLARE (char *, copying_text, NULL);
+
+/* @documentdescription ... @end documentdescription. */
+DECLARE (const char *, document_description, NULL);
+
+/* Nonzero if the last character inserted has the syntax class of NEWLINE. */
+DECLARE (int, last_char_was_newline, 1);
+
+/* The current input file state. */
+DECLARE (char *, input_filename, (char *)NULL);
+DECLARE (char *, input_text, (char *)NULL);
+DECLARE (int, input_text_length, 0);
+DECLARE (int, input_text_offset, 0);
+DECLARE (int, line_number, 0);
+DECLARE (char *, toplevel_output_filename, NULL);
+#define curchar() input_text[input_text_offset]
+
+/* A colon separated list of directories to search for files included
+ with @include. This can be controlled with the `-I' option to makeinfo. */
+DECLARE (char *, include_files_path, NULL);
+
+/* The filename of the current input file. This is never freed. */
+DECLARE (char *, node_filename, NULL);
+
+/* Name of CSS file to include, if any. (--css-include). */
+DECLARE (char *, css_include, NULL);
+
+/* Name of CSS to reference, if any. (--css-ref). */
+DECLARE (char *, css_ref, NULL);
+
+/* Transliterate file names into ASCII */
+DECLARE (int, transliterate_file_names, 0);
+
+/* Nonzero means do not output "Node: Foo" for node separations, that
+ is, generate plain text. (--no-headers) */
+DECLARE (int, no_headers, 0);
+
+/* Nonzero means that we process @docbook and @ifdocbook. (--ifdocbook) */
+DECLARE (int, process_docbook, 0);
+
+/* Nonzero means that we process @html and @rawhtml even when not
+ generating HTML. (--ifhtml) */
+DECLARE (int, process_html, 0);
+
+/* Positive means process @ifinfo (even if not generating Info);
+ zero means don't process @ifinfo (even if we are);
+ -1 means we don't know yet. (--ifinfo) */
+DECLARE (int, process_info, -1);
+
+/* Positive means process @ifplaintext (even if not generating plain text);
+ zero means we don't process @ifplaintext (even if we are);
+ -1 means we don't know yet. (--ifplaintext) */
+DECLARE (int, process_plaintext, -1);
+
+/* Nonzero means that we process @tex and @iftex. (--iftex) */
+DECLARE (int, process_tex, 0);
+
+/* Nonzero means that we process @xml and @ifxml. (--ifxml) */
+DECLARE (int, process_xml, 0);
+
+/* Default is to check node references. (--no-validate) */
+DECLARE (int, validating, 1);
+
+/* Nonzero means print information about what is going on. (--verbose) */
+DECLARE (int, verbose_mode, 0);
+
+/* Nonzero means prefix each @chapter, ... with a number like
+ 1, 1.1, etc. (--number-sections) */
+DECLARE (int, number_sections, 1);
+
+/* Nonzero means split size. When zero, DEFAULT_SPLIT_SIZE is used. */
+DECLARE (int, split_size, 0);
+
+/* Nonzero means expand node names and references while validating.
+ This will avoid errors when the Texinfo document uses features
+ like @@ and @value inconsistently in node names, but will slow
+ the program by about 80%. You HAVE been warned. */
+DECLARE (int, expensive_validation, 0);
+
+/* C's standard macros don't check to make sure that the characters being
+ changed are within range. So I have to check explicitly. */
+
+#define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
+#define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
+
+#define whitespace(c) ((c) == '\t' || (c) == ' ')
+#define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
+#define cr_or_whitespace(c) (whitespace(c) || (c) == '\r' || (c) == '\n')
+
+#ifndef isletter
+#define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
+#endif
+
+#ifndef isupper
+#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
+#endif
+
+#ifndef isdigit
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+#endif
+
+#ifndef digit_value
+#define digit_value(c) ((c) - '0')
+#endif
+
+/* These characters are not really HTML-safe (with strict XHTML),
+ and also there are possible collisions. That's the whole reason we
+ designed a new conversion scheme in the first place. But we
+ nevertheless need to generate the old names. See
+ `add_escaped_anchor_name' in html.c. */
+#define OLD_HTML_SAFE "$-_.+!*'()"
+#define OLD_URL_SAFE_CHAR(ch) (strchr (OLD_HTML_SAFE, ch))
+
+/* For the current/stable scheme. */
+#define URL_SAFE_CHAR(ch) (((unsigned char)ch)<128 && isalnum (ch))
+
+#define COMMAND_PREFIX '@'
+
+/* A byte value to represent a non-breaking space until flush_output (). */
+#define NON_BREAKING_SPACE 036
+
+#define END_VERBATIM "end verbatim"
+
+/* Stuff for splitting large files. The numbers for Emacs
+ texinfo-format-buffer are much smaller, but memory capacities have
+ increased so much, 50k info files seem a bit tiny these days. */
+#define DEFAULT_SPLIT_SIZE 300000
+DECLARE (int, splitting, 1); /* Defaults to true for now. */
+
+#define skip_whitespace() \
+ while ((input_text_offset != input_text_length) && \
+ whitespace (curchar())) \
+ input_text_offset++
+
+#define skip_whitespace_and_newlines() \
+ do { \
+ while (input_text_offset != input_text_length \
+ && cr_or_whitespace (curchar ())) \
+ { \
+ if (curchar () == '\n') \
+ line_number++; \
+ input_text_offset++; \
+ } \
+ } while (0)
+
+/* Return nonzero if STRING is the text at input_text + input_text_offset,
+ else zero. */
+#define looking_at(string) \
+ (strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
+
+/* Any list with a member named `next'. */
+typedef struct generic_list {
+ struct generic_list *next;
+} GENERIC_LIST;
+
+/* Use `gdt' instead of `_' to translate strings that end up in the
+ output document. */
+extern char *getdocumenttext (const char *msgid);
+#define gdt(s) getdocumenttext(s)
+
+/* Reverse the order of a list. */
+extern GENERIC_LIST * reverse_list (GENERIC_LIST *list);
+
+/* Possibly return Local Variables trailer for Info output. */
+extern char *info_trailer (void),
+ *expansion (char *str, int implicit_code),
+ *text_expansion (char *str),
+ *maybe_escaped_expansion (char *str, int implicit_code, int do_escape_html),
+ *full_expansion (char *str, int implicit_code);
+
+extern void free_and_clear (char **pointer),
+ add_word (char *string),
+ add_char (int character),
+ add_meta_char (int character),
+ close_single_paragraph (void),
+ insert_string (const char *),
+ insert (int character),
+ get_rest_of_line (int expand, char **string),
+ add_html_block_elt (char *string),
+ get_until_in_braces (char *match, char **string),
+ get_until_in_line (int expand, char *match, char **string),
+ canon_white (char *string),
+ discard_until (char *string),
+ indent (int amount),
+ kill_self_indent (int count),
+ backup_input_pointer (void),
+ inhibit_output_flushing (void),
+ uninhibit_output_flushing (void),
+ flush_output (void),
+ start_paragraph (void),
+ close_paragraph (void),
+ close_insertion_paragraph (void),
+ init_paragraph (void),
+ ignore_blank_line (void),
+ reader_loop (void),
+ discard_braces (void),
+ replace_with_expansion (int from, int *to),
+ fix_whitespace (char *string),
+ output_head (void),
+ add_html_elt (char *string);
+
+extern int get_until (char *match, char **string),
+ set_paragraph_indent (char *string),
+ self_delimiting (int character),
+ search_forward (const char *string, int from),
+ search_forward_until_pos (char *string, int from, int end_pos),
+ next_nonwhitespace_character (void),
+ current_output_column (void),
+ fs_error (char *filename);
+
+#if defined (VA_FPRINTF) && __STDC__
+/* Unfortunately we must use prototypes if we are to use <stdarg.h>. */
+extern void add_word_args (const char *, ...),
+ add_html_block_elt_args (const char *, ...),
+ execute_string (char *, ...),
+ warning (const char *format, ...),
+ error (const char *format, ...),
+ line_error (const char *format, ...),
+ file_line_error (char *infile, int lno, const char *format, ...);
+#else
+extern void add_word_args (),
+ add_html_block_elt_args (),
+ execute_string (),
+ warning (),
+ error (),
+ line_error (),
+ file_line_error ();
+#endif /* no prototypes */
+
+#endif /* not MAKEINFO_H */
diff --git a/makeinfo/multi.c b/makeinfo/multi.c
new file mode 100644
index 0000000..43033a2
--- /dev/null
+++ b/makeinfo/multi.c
@@ -0,0 +1,646 @@
+/* multi.c -- multiple-column tables (@multitable) for makeinfo.
+ $Id: multi.c,v 1.18 2007/07/01 21:20:33 karl Exp $
+
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2007
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Originally written by phr@gnu.org (Paul Rubin). */
+
+#include "system.h"
+#include "mbswidth.h"
+#include "cmds.h"
+#include "insertion.h"
+#include "makeinfo.h"
+#include "multi.h"
+#include "xml.h"
+
+#define MAXCOLS 100 /* remove this limit later @@ */
+
+
+/*
+ * Output environments. This is a hack grafted onto existing
+ * structure. The "output environment" used to consist of the
+ * global variables `output_paragraph', `fill_column', etc.
+ * Routines like add_char would manipulate these variables.
+ *
+ * Now, when formatting a multitable, we maintain separate environments
+ * for each column. That way we can build up the columns separately
+ * and write them all out at once. The "current" output environment"
+ * is still kept in those global variables, so that the old output
+ * routines don't have to change. But we provide routines to save
+ * and restore these variables in an "environment table". The
+ * `select_output_environment' function switches from one output
+ * environment to another.
+ *
+ * Environment #0 (i.e., element #0 of the table) is the regular
+ * environment that is used when we're not formatting a multitable.
+ *
+ * Environment #N (where N = 1,2,3,...) is the env. for column #N of
+ * the table, when a multitable is active.
+ */
+
+/* contents of an output environment */
+/* some more vars may end up being needed here later @@ */
+static struct env
+{
+ unsigned char *output_paragraph;
+ int output_paragraph_offset;
+ int paragraph_buffer_len;
+ int paragraph_is_open;
+ int meta_char_pos;
+ int current_indent;
+ int fill_column;
+} envs[MAXCOLS]; /* the environment table */
+
+/* index in environment table of currently selected environment */
+static int current_env_no;
+
+/* current column number */
+static int current_column_no;
+
+/* We need to make a difference between template based widths and
+ @columnfractions for HTML tables' sake. Sigh. */
+static int seen_column_fractions;
+
+/* column number of last column in current multitable */
+static int last_column;
+
+/* flags indicating whether horizontal and vertical separators need
+ to be drawn, separating rows and columns in the current multitable. */
+static int hsep, vsep;
+
+/* whether this is the first row. */
+static int first_row;
+
+/* Called to handle a {...} template on the @multitable line.
+ We're at the { and our first job is to find the matching }; as a side
+ effect, we change *PARAMS to point to after it. Our other job is to
+ expand the template text and return the width of that string. */
+static unsigned
+find_template_width (char **params)
+{
+ char *template, *xtemplate;
+ unsigned len;
+ char *start = *params;
+ int brace_level = 0;
+
+ /* The first character should be a {. */
+ if (!params || !*params || **params != '{')
+ {
+ line_error ("find_template width internal error: passed %s",
+ params ? *params : "null");
+ return 0;
+ }
+
+ do
+ {
+ if (**params == '{' && (*params == start || (*params)[-1] != '@'))
+ brace_level++;
+ else if (**params == '}' && (*params)[-1] != '@')
+ brace_level--;
+ else if (**params == 0)
+ {
+ line_error (_("Missing } in @multitable template"));
+ return 0;
+ }
+ (*params)++;
+ }
+ while (brace_level > 0);
+
+ template = substring (start + 1, *params - 1); /* omit braces */
+ xtemplate = expansion (template, 0);
+ len = strlen (xtemplate);
+
+ free (template);
+ free (xtemplate);
+
+ return len;
+}
+
+/* Direct current output to environment number N. Used when
+ switching work from one column of a multitable to the next.
+ Returns previous environment number. */
+static int
+select_output_environment (int n)
+{
+ struct env *e = &envs[current_env_no];
+ int old_env_no = current_env_no;
+
+ /* stash current env info from global vars into the old environment */
+ e->output_paragraph = output_paragraph;
+ e->output_paragraph_offset = output_paragraph_offset;
+ e->paragraph_buffer_len = paragraph_buffer_len;
+ e->paragraph_is_open = paragraph_is_open;
+ e->meta_char_pos = meta_char_pos;
+ e->current_indent = current_indent;
+ e->fill_column = fill_column;
+
+ /* now copy new environment into global vars */
+ current_env_no = n;
+ e = &envs[current_env_no];
+ output_paragraph = e->output_paragraph;
+ output_paragraph_offset = e->output_paragraph_offset;
+ /* All the elements in the output environment structures start out as
+ zeros from the static declaration. However, we don't want to try
+ to malloc zero bytes when init_paragraph is called just below,
+ after we return. */
+ paragraph_buffer_len = e->paragraph_buffer_len ? e->paragraph_buffer_len
+ : INITIAL_PARAGRAPH_BUFFER_LEN;
+ paragraph_is_open = e->paragraph_is_open;
+ meta_char_pos = e->meta_char_pos;
+ current_indent = e->current_indent;
+ fill_column = e->fill_column;
+
+ return old_env_no;
+}
+
+/* Initialize environment number ENV_NO, of width WIDTH.
+ The idea is that we're going to use one environment for each column of
+ a multitable, so we can build them up separately and print them
+ all out at the end. */
+static int
+setup_output_environment (int env_no, int width)
+{
+ int old_env = select_output_environment (env_no);
+
+ /* clobber old environment and set width of new one */
+ init_paragraph ();
+
+ /* make our change */
+ fill_column = width;
+
+ /* Save new environment and restore previous one. */
+ select_output_environment (old_env);
+
+ return env_no;
+}
+
+/* Read the parameters for a multitable from the current command
+ line, save the parameters away, and return the
+ number of columns. */
+static int
+setup_multitable_parameters (void)
+{
+ char *params = insertion_stack->item_function;
+ int nchars;
+ float columnfrac;
+ char command[200]; /* xx no fixed limits */
+ int i = 1;
+
+ /* We implement @hsep and @vsep even though TeX doesn't.
+ We don't get mixing of @columnfractions and templates right,
+ but TeX doesn't either. */
+ hsep = vsep = 0;
+
+ /* Assume no @columnfractions per default. */
+ seen_column_fractions = 0;
+
+ while (*params) {
+ while (whitespace (*params))
+ params++;
+
+ if (*params == '@') {
+ sscanf (params, "%200s", command);
+ nchars = strlen (command);
+ params += nchars;
+ if (strcmp (command, "@hsep") == 0)
+ hsep++;
+ else if (strcmp (command, "@vsep") == 0)
+ vsep++;
+ else if (strcmp (command, "@columnfractions") == 0) {
+ seen_column_fractions = 1;
+ /* Clobber old environments and create new ones, starting at #1.
+ Environment #0 is the normal output, so don't mess with it. */
+ for ( ; i <= MAXCOLS; i++) {
+ if (sscanf (params, "%f", &columnfrac) < 1)
+ goto done;
+ /* Unfortunately, can't use %n since m68k-hp-bsd libc (at least)
+ doesn't support it. So skip whitespace (preceding the
+ number) and then non-whitespace (the number). */
+ while (*params && (*params == ' ' || *params == '\t'))
+ params++;
+ /* Hmm, but what about @columnfractions 3foo. Oh well,
+ it's invalid input anyway. */
+ while (*params && *params != ' ' && *params != '\t'
+ && *params != '\n' && *params != '@')
+ params++;
+
+ {
+ /* For html/xml/docbook, translate fractions into integer
+ percentages, adding .005 to avoid rounding problems. For
+ info, we want the character width. */
+ int width = xml || html ? (columnfrac + .005) * 100
+ : (columnfrac * (fill_column - current_indent) + .5);
+ setup_output_environment (i, width);
+ }
+ }
+ }
+
+ } else if (*params == '{') {
+ unsigned template_width = find_template_width (&params);
+
+ /* This gives us two spaces between columns. Seems reasonable.
+ How to take into account current_indent here? */
+ setup_output_environment (i++, template_width + 2);
+
+ } else {
+ warning (_("ignoring stray text `%s' after @multitable"), params);
+ break;
+ }
+ }
+
+done:
+ flush_output ();
+ inhibit_output_flushing ();
+
+ last_column = i - 1;
+ return last_column;
+}
+
+/* Output a row. Calls insert, but also flushes the buffered output
+ when we see a newline, since in multitable every line is a separate
+ paragraph. */
+static void
+out_char (int ch)
+{
+ if (html || xml)
+ add_char (ch);
+ else
+ {
+ int env = select_output_environment (0);
+ insert (ch);
+ if (ch == '\n')
+ {
+ uninhibit_output_flushing ();
+ flush_output ();
+ inhibit_output_flushing ();
+ }
+ select_output_environment (env);
+ }
+}
+
+
+static void
+draw_horizontal_separator (void)
+{
+ int i, j, s;
+
+ if (html)
+ {
+ add_word ("<hr>");
+ return;
+ }
+ if (xml)
+ return;
+
+ for (s = 0; s < envs[0].current_indent; s++)
+ out_char (' ');
+ if (vsep)
+ out_char ('+');
+ for (i = 1; i <= last_column; i++) {
+ for (j = 0; j <= envs[i].fill_column; j++)
+ out_char ('-');
+ if (vsep)
+ out_char ('+');
+ }
+ out_char (' ');
+ out_char ('\n');
+}
+
+
+/* multitable strategy:
+ for each item {
+ for each column in an item {
+ initialize a new paragraph
+ do ordinary formatting into the new paragraph
+ save the paragraph away
+ repeat if there are more paragraphs in the column
+ }
+ dump out the saved paragraphs and free the storage
+ }
+
+ For HTML we construct a simple HTML 3.2 table with <br>s inserted
+ to help non-tables browsers. `@item' inserts a <tr> and `@tab'
+ inserts <td>; we also try to close <tr>. The only real
+ alternative is to rely on the info formatting engine and present
+ preformatted text. */
+
+void
+do_multitable (void)
+{
+ int ncolumns;
+
+ if (multitable_active)
+ {
+ line_error ("Multitables cannot be nested");
+ return;
+ }
+
+ close_single_paragraph ();
+
+ if (xml)
+ {
+ xml_no_para = 1;
+ if (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ }
+
+ /* scan the current item function to get the field widths
+ and number of columns, and set up the output environment list
+ accordingly. */
+ ncolumns = setup_multitable_parameters ();
+ first_row = 1;
+
+ /* <p> for non-tables browsers. @multitable implicitly ends the
+ current paragraph, so this is ok. */
+ if (html)
+ add_html_block_elt ("<p><table summary=\"\">");
+ /* else if (docbook)*/ /* 05-08 */
+ else if (xml)
+ {
+ int *widths = xmalloc (ncolumns * sizeof (int));
+ int i;
+ for (i=0; i<ncolumns; i++)
+ widths[i] = envs[i+1].fill_column;
+ xml_begin_multitable (ncolumns, widths);
+ free (widths);
+ }
+
+ if (hsep)
+ draw_horizontal_separator ();
+
+ /* The next @item command will direct stdout into the first column
+ and start processing. @tab will then switch to the next column,
+ and @item will flush out the saved output and return to the first
+ column. Environment #1 is the first column. (Environment #0 is
+ the normal output) */
+
+ ++multitable_active;
+}
+
+/* advance to the next environment number */
+static void
+nselect_next_environment (void)
+{
+ if (current_env_no >= last_column) {
+ line_error (_("Too many columns in multitable item (max %d)"), last_column);
+ return;
+ }
+ select_output_environment (current_env_no + 1);
+}
+
+
+/* do anything needed at the beginning of processing a
+ multitable column. */
+static void
+init_column (void)
+{
+ /* don't indent 1st paragraph in the item */
+ cm_noindent ();
+
+ /* throw away possible whitespace after @item or @tab command */
+ skip_whitespace ();
+}
+
+static void
+output_multitable_row (void)
+{
+ /* offset in the output paragraph of the next char needing
+ to be output for that column. */
+ int offset[MAXCOLS];
+ int i, j, s, remaining;
+ int had_newline = 0;
+
+ for (i = 0; i <= last_column; i++)
+ offset[i] = 0;
+
+ /* select the current environment, to make sure the env variables
+ get updated */
+ select_output_environment (current_env_no);
+
+#define CHAR_ADDR(n) (offset[i] + (n))
+#define CHAR_AT(n) (envs[i].output_paragraph[CHAR_ADDR(n)])
+
+ /* remove trailing whitespace from each column */
+ for (i = 1; i <= last_column; i++) {
+ while (envs[i].output_paragraph_offset
+ && cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1)))
+ envs[i].output_paragraph_offset--;
+
+ if (i == current_env_no)
+ output_paragraph_offset = envs[i].output_paragraph_offset;
+ }
+
+ /* read the current line from each column, outputting them all
+ pasted together. Do this til all lines are output from all
+ columns. */
+ for (;;) {
+ remaining = 0;
+ /* first, see if there is any work to do */
+ for (i = 1; i <= last_column; i++) {
+ if (CHAR_ADDR (0) < envs[i].output_paragraph_offset) {
+ remaining = 1;
+ break;
+ }
+ }
+ if (!remaining)
+ break;
+
+ for (s = 0; s < envs[0].current_indent; s++)
+ out_char (' ');
+
+ if (vsep)
+ out_char ('|');
+
+ for (i = 1; i <= last_column; i++) {
+ for (s = 0; s < envs[i].current_indent; s++)
+ out_char (' ');
+ for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) {
+ if (CHAR_AT (j) == '\n')
+ break;
+ out_char (CHAR_AT (j));
+ }
+ /* Do not output trailing blanks if we're in the last column and
+ there will be no trailing |. */
+ if (i < last_column && !vsep)
+ for (s = mbsnwidth ((char *)&CHAR_AT (0), j, 0);
+ s <= envs[i].fill_column; s++)
+ out_char (' ');
+ if (vsep)
+ out_char ('|'); /* draw column separator */
+
+ offset[i] += j + 1; /* skip last text plus skip the newline */
+ }
+ out_char ('\n'); /* end of line */
+ had_newline = 1;
+ }
+
+ /* If completely blank item, get blank line despite no other output. */
+ if (!had_newline)
+ out_char ('\n'); /* end of line */
+
+ if (hsep)
+ draw_horizontal_separator ();
+
+ /* Now dispose of the buffered output. */
+ for (i = 1; i <= last_column; i++) {
+ select_output_environment (i);
+ init_paragraph ();
+ }
+}
+
+int after_headitem = 0;
+int headitem_row = 0;
+
+/* start a new item (row) of a multitable */
+int
+multitable_item (void)
+{
+ if (!multitable_active) {
+ line_error ("multitable_item internal error: no active multitable");
+ xexit (1);
+ }
+
+ current_column_no = 1;
+
+ if (html)
+ {
+ if (!first_row)
+ /* <br> for non-tables browsers. */
+ add_word_args ("<br></%s></tr>", after_headitem ? "th" : "td");
+
+ if (seen_column_fractions)
+ add_word_args ("<tr align=\"left\"><%s valign=\"top\" width=\"%d%%\">",
+ headitem_flag ? "th" : "td",
+ envs[current_column_no].fill_column);
+ else
+ add_word_args ("<tr align=\"left\"><%s valign=\"top\">",
+ headitem_flag ? "th" : "td");
+
+ if (headitem_flag)
+ after_headitem = 1;
+ else
+ after_headitem = 0;
+ first_row = 0;
+ headitem_row = headitem_flag;
+ headitem_flag = 0;
+ return 0;
+ }
+ /* else if (docbook)*/ /* 05-08 */
+ else if (xml)
+ {
+ xml_end_multitable_row (first_row);
+ if (headitem_flag)
+ after_headitem = 1;
+ else
+ after_headitem = 0;
+ first_row = 0;
+ headitem_flag = 0;
+ return 0;
+ }
+ first_row = 0;
+
+ if (current_env_no > 0) {
+ output_multitable_row ();
+ }
+ /* start at column 1 */
+ select_output_environment (1);
+ if (!output_paragraph) {
+ line_error (_("[unexpected] cannot select column #%d in multitable"),
+ current_env_no);
+ xexit (1);
+ }
+
+ init_column ();
+
+ if (headitem_flag)
+ hsep = 1;
+ else
+ hsep = 0;
+
+ if (headitem_flag)
+ after_headitem = 1;
+ else
+ after_headitem = 0;
+ headitem_flag = 0;
+
+ return 0;
+}
+
+#undef CHAR_AT
+#undef CHAR_ADDR
+
+/* select a new column in current row of multitable */
+void
+cm_tab (void)
+{
+ if (!multitable_active)
+ error (_("ignoring @tab outside of multitable"));
+
+ current_column_no++;
+
+ if (html)
+ {
+ if (seen_column_fractions)
+ add_word_args ("</%s><%s valign=\"top\" width=\"%d%%\">",
+ headitem_row ? "th" : "td",
+ headitem_row ? "th" : "td",
+ envs[current_column_no].fill_column);
+ else
+ add_word_args ("</%s><%s valign=\"top\">",
+ headitem_row ? "th" : "td",
+ headitem_row ? "th" : "td");
+ }
+ /* else if (docbook)*/ /* 05-08 */
+ else if (xml)
+ xml_end_multitable_column ();
+ else
+ nselect_next_environment ();
+
+ init_column ();
+}
+
+/* close a multitable, flushing its output and resetting
+ whatever needs resetting */
+void
+end_multitable (void)
+{
+ if (!html && !docbook)
+ output_multitable_row ();
+
+ /* Multitables cannot be nested. Otherwise, we'd have to save the
+ previous output environment number on a stack somewhere, and then
+ restore to that environment. */
+ select_output_environment (0);
+ multitable_active = 0;
+ uninhibit_output_flushing ();
+ close_insertion_paragraph ();
+
+ if (html)
+ add_word_args ("<br></%s></tr></table>\n", headitem_row ? "th" : "td");
+ /* else if (docbook)*/ /* 05-08 */
+ else if (xml)
+ xml_end_multitable ();
+
+#if 0
+ printf (_("** Multicolumn output from last row:\n"));
+ for (i = 1; i <= last_column; i++) {
+ select_output_environment (i);
+ printf (_("* column #%d: output = %s\n"), i, output_paragraph);
+ }
+#endif
+}
diff --git a/makeinfo/multi.h b/makeinfo/multi.h
new file mode 100644
index 0000000..391c05d
--- /dev/null
+++ b/makeinfo/multi.h
@@ -0,0 +1,26 @@
+/* multi.h -- declarations for multi.c.
+ $Id: multi.h,v 1.4 2007/07/01 21:20:33 karl Exp $
+
+ Copyright (C) 2004, 2007 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef MULTI_H
+#define MULTI_H
+
+extern void do_multitable (void);
+extern void end_multitable (void);
+extern int multitable_item (void);
+
+#endif /* !MULTI_H */
diff --git a/makeinfo/node.c b/makeinfo/node.c
new file mode 100644
index 0000000..4a5a598
--- /dev/null
+++ b/makeinfo/node.c
@@ -0,0 +1,1947 @@
+/* node.c -- nodes for Texinfo.
+ $Id: node.c,v 1.41 2008/07/05 23:57:29 karl Exp $
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "cmds.h"
+#include "files.h"
+#include "float.h"
+#include "footnote.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "node.h"
+#include "html.h"
+#include "sectioning.h"
+#include "insertion.h"
+#include "xml.h"
+
+/* See comments in node.h. */
+NODE_REF *node_references = NULL;
+NODE_REF *node_node_references = NULL;
+TAG_ENTRY *tag_table = NULL;
+int node_number = -1;
+int node_order = 0;
+int current_section = 0;
+int outstanding_node = 0;
+
+/* Adding nodes, and making tags. */
+
+/* Start a new tag table. */
+void
+init_tag_table (void)
+{
+ while (tag_table)
+ {
+ TAG_ENTRY *temp = tag_table;
+ free (temp->node);
+ free (temp->prev);
+ free (temp->next);
+ free (temp->up);
+ tag_table = tag_table->next_ent;
+ free (temp);
+ }
+}
+
+/* Write out the contents of the existing tag table.
+ INDIRECT_P says how to format the output (it depends on whether the
+ table is direct or indirect). */
+static void
+write_tag_table_internal (int indirect_p)
+{
+ TAG_ENTRY *node;
+ int old_indent = no_indent;
+
+ if (xml)
+ {
+ flush_output ();
+ return;
+ }
+
+ no_indent = 1;
+ filling_enabled = 0;
+ must_start_paragraph = 0;
+ close_paragraph ();
+
+ if (!indirect_p)
+ {
+ no_indent = 1;
+ insert ('\n');
+ }
+
+ add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
+
+ /* Do not collapse -- to -, etc., in node names. */
+ in_fixed_width_font++;
+
+ for (node = tag_table; node; node = node->next_ent)
+ {
+ if (node->flags & TAG_FLAG_ANCHOR)
+ { /* This reference is to an anchor. */
+ execute_string ("Ref: %s", node->node);
+ }
+ else
+ { /* This reference is to a node. */
+ execute_string ("Node: %s", node->node);
+ }
+ add_word_args ("\177%d\n", node->position);
+ }
+
+ add_word ("\037\nEnd Tag Table\n");
+
+ /* Do not collapse -- to -, etc., in node names. */
+ in_fixed_width_font--;
+
+ flush_output ();
+ no_indent = old_indent;
+}
+
+void
+write_tag_table (char *filename)
+{
+ output_stream = fopen (filename, "a");
+ if (!output_stream)
+ {
+ fs_error (filename);
+ return;
+ }
+
+ write_tag_table_internal (0); /* Not indirect. */
+
+ if (fclose (output_stream) != 0)
+ fs_error (filename);
+}
+
+static void
+write_tag_table_indirect (void)
+{
+ write_tag_table_internal (1);
+}
+
+/* Convert "top" and friends into "Top". */
+static void
+normalize_node_name (char *string)
+{
+ if (mbscasecmp (string, "Top") == 0)
+ strcpy (string, "Top");
+}
+
+static char *
+get_node_token (int expand)
+{
+ char *string;
+
+ get_until_in_line (expand, ",", &string);
+
+ if (curchar () == ',')
+ input_text_offset++;
+
+ fix_whitespace (string);
+
+ /* Force all versions of "top" to be "Top". */
+ normalize_node_name (string);
+
+ return string;
+}
+
+/* Expand any macros and other directives in a node name, and
+ return the expanded name as an malloc'ed string. */
+char *
+expand_node_name (char *node)
+{
+ char *result = node;
+
+ if (node)
+ {
+ /* Don't expand --, `` etc., in case somebody will want
+ to print the result. */
+ in_fixed_width_font++;
+ result = expansion (node, 0);
+ in_fixed_width_font--;
+ fix_whitespace (result);
+ normalize_node_name (result);
+ }
+ return result;
+}
+
+/* Look up NAME in the tag table, and return the associated
+ tag_entry. If the node is not in the table return NULL. */
+TAG_ENTRY *
+find_node (char *name)
+{
+ TAG_ENTRY *tag = tag_table;
+ char *expanded_name;
+ char n1 = name[0];
+
+ while (tag)
+ {
+ if (tag->node[0] == n1 && strcmp (tag->node, name) == 0)
+ return tag;
+ tag = tag->next_ent;
+ }
+
+ if (!expensive_validation)
+ return NULL;
+
+ /* Try harder. Maybe TAG_TABLE has the expanded NAME, or maybe NAME
+ is expanded while TAG_TABLE has its unexpanded form. This may
+ slow down the search, but if they want this feature, let them
+ pay! If they want it fast, they should write every node name
+ consistently (either always expanded or always unexpaned). */
+ expanded_name = expand_node_name (name);
+ for (tag = tag_table; tag; tag = tag->next_ent)
+ {
+ if (STREQ (tag->node, expanded_name))
+ break;
+ /* If the tag name doesn't have the command prefix, there's no
+ chance it could expand into anything but itself. */
+ if (strchr (tag->node, COMMAND_PREFIX))
+ {
+ char *expanded_node = expand_node_name (tag->node);
+
+ if (STREQ (expanded_node, expanded_name))
+ {
+ free (expanded_node);
+ break;
+ }
+ free (expanded_node);
+ }
+ }
+ free (expanded_name);
+ return tag;
+}
+
+/* Look in the tag table for a node whose file name is FNAME, and
+ return the associated tag_entry. If there's no such node in the
+ table, return NULL. */
+static TAG_ENTRY *
+find_node_by_fname (char *fname)
+{
+ TAG_ENTRY *tag = tag_table;
+ while (tag)
+ {
+ if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0)
+ return tag;
+ tag = tag->next_ent;
+ }
+
+ return tag;
+}
+
+/* Remember next, prev, etc. references in a @node command, where we
+ don't care about most of the entries. */
+static void
+remember_node_node_reference (char *node)
+{
+ NODE_REF *temp = xmalloc (sizeof (NODE_REF));
+ int number;
+
+ if (!node) return;
+ temp->next = node_node_references;
+ temp->node = xstrdup (node);
+ temp->type = followed_reference;
+ number = number_of_node (node);
+ if (number)
+ temp->number = number; /* Already assigned. */
+ else
+ {
+ node_number++;
+ temp->number = node_number;
+ }
+ node_node_references = temp;
+}
+
+/* Remember NODE and associates. */
+static void
+remember_node (char *node, char *prev, char *next, char *up,
+ int position, int line_no, char *fname, int flags)
+{
+ /* Check for existence of this tag already. */
+ if (validating)
+ {
+ TAG_ENTRY *tag = find_node (node);
+ if (tag)
+ {
+ line_error (_("Node `%s' previously defined at line %d"),
+ node, tag->line_no);
+ return;
+ }
+ }
+
+ if (!(flags & TAG_FLAG_ANCHOR))
+ {
+ /* Make this the current node. */
+ current_node = node;
+ }
+
+ /* Add it to the list. */
+ {
+ int number = number_of_node (node);
+
+ TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY));
+ new->node = node;
+ new->prev = prev;
+ new->next = next;
+ new->up = up;
+ new->position = position;
+ new->line_no = line_no;
+ new->filename = node_filename;
+ new->touched = 0;
+ new->flags = flags;
+ if (number)
+ new->number = number; /* Already assigned. */
+ else
+ {
+ node_number++;
+ new->number = node_number;
+ }
+ if (fname)
+ new->html_fname = fname;
+ else
+ /* This happens for Top node under split-HTML, for example. */
+ new->html_fname
+ = normalize_filename (filename_part (current_output_filename));
+ new->next_ent = tag_table;
+
+ /* Increment the order counter, and save it. */
+ node_order++;
+ new->order = node_order;
+
+ tag_table = new;
+ }
+
+ if (html)
+ { /* Note the references to the next etc. nodes too. */
+ remember_node_node_reference (next);
+ remember_node_node_reference (prev);
+ remember_node_node_reference (up);
+ }
+}
+
+/* Remember this node name for later validation use. This is used to
+ remember menu references while reading the input file. After the
+ output file has been written, if validation is on, then we use the
+ contents of `node_references' as a list of nodes to validate. */
+void
+remember_node_reference (char *node, int line, enum reftype type)
+{
+ NODE_REF *temp = xmalloc (sizeof (NODE_REF));
+ int number = number_of_node (node);
+
+ temp->next = node_references;
+ temp->node = xstrdup (node);
+ temp->line_no = line;
+ temp->section = current_section;
+ temp->type = type;
+ temp->containing_node = xstrdup (current_node ? current_node : "");
+ temp->filename = node_filename;
+ if (number)
+ temp->number = number; /* Already assigned. */
+ else
+ {
+ node_number++;
+ temp->number = node_number;
+ }
+
+ node_references = temp;
+}
+
+static void
+isolate_nodename (char *nodename)
+{
+ int i, c;
+ int paren_seen, paren;
+
+ if (!nodename)
+ return;
+
+ canon_white (nodename);
+ paren_seen = paren = i = 0;
+
+ if (*nodename == '.' || !*nodename)
+ {
+ *nodename = 0;
+ return;
+ }
+
+ if (*nodename == '(')
+ {
+ paren++;
+ paren_seen++;
+ i++;
+ }
+
+ for (; (c = nodename[i]); i++)
+ {
+ if (paren)
+ {
+ if (c == '(')
+ paren++;
+ else if (c == ')')
+ paren--;
+
+ continue;
+ }
+
+ /* If the character following the close paren is a space, then this
+ node has no more characters associated with it. */
+ if (c == '\t' ||
+ c == '\n' ||
+ c == ',' ||
+ ((paren_seen && i > 0 && nodename[i - 1] == ')') &&
+ (c == ' ' || c == '.')) ||
+ (c == '.' &&
+ ((!nodename[i + 1] ||
+ (cr_or_whitespace (nodename[i + 1])) ||
+ (nodename[i + 1] == ')')))))
+ break;
+ }
+ nodename[i] = 0;
+}
+
+/* This function gets called at the start of every line while inside a
+ menu. It checks to see if the line starts with "* ", and if so and
+ REMEMBER_REF is nonzero, remembers the node reference as type
+ REF_TYPE that this menu refers to. input_text_offset is at the \n
+ just before the menu line. If REMEMBER_REF is zero, REF_TYPE is unused. */
+#define MENU_STARTER "* "
+char *
+glean_node_from_menu (int remember_ref, enum reftype ref_type)
+{
+ int i, orig_offset = input_text_offset;
+ char *nodename;
+ char *line, *expanded_line;
+ char *old_input = input_text;
+ int old_size = input_text_length;
+
+ if (strncmp (&input_text[input_text_offset + 1],
+ MENU_STARTER,
+ strlen (MENU_STARTER)) != 0)
+ return NULL;
+ else
+ input_text_offset += strlen (MENU_STARTER) + 1;
+
+ /* The menu entry might include macro calls, so we need to expand them. */
+ get_until ("\n", &line);
+ only_macro_expansion++; /* only expand macros in menu entries */
+ expanded_line = expansion (line, 0);
+ only_macro_expansion--;
+ free (line);
+ input_text = expanded_line;
+ input_text_offset = 0;
+ input_text_length = strlen (expanded_line);
+
+ get_until_in_line (0, ":", &nodename);
+ if (curchar () == ':')
+ input_text_offset++;
+
+ if (curchar () != ':')
+ {
+ free (nodename);
+ get_until_in_line (0, "\n", &nodename);
+ isolate_nodename (nodename);
+ }
+
+ input_text = old_input;
+ input_text_offset = orig_offset;
+ input_text_length = old_size;
+ free (expanded_line);
+ fix_whitespace (nodename);
+ normalize_node_name (nodename);
+ i = strlen (nodename);
+ if (i && nodename[i - 1] == ':')
+ nodename[i - 1] = 0;
+
+ if (remember_ref)
+ remember_node_reference (nodename, line_number, ref_type);
+
+ return nodename;
+}
+
+/* Set the name of the current output file. */
+void
+set_current_output_filename (const char *fname)
+{
+ if (current_output_filename)
+ free (current_output_filename);
+ current_output_filename = xstrdup (fname);
+}
+
+
+/* Output the <a name="..."></a> constructs for NODE. We output both
+ the new-style conversion and the old-style, if they are different.
+ See comments at `add_escaped_anchor_name' in html.c. */
+
+static void
+add_html_names (char *node)
+{
+ char *tem = expand_node_name (node);
+ char *otem = xstrdup (tem);
+
+ /* Determine if the old and new schemes come up with different names;
+ only output the old scheme if that is so. We don't want to output
+ the same name twice. */
+ canon_white (otem);
+ {
+ char *optr = otem;
+ int need_old = 0;
+
+ for (; *optr; optr++)
+ {
+ if (!cr_or_whitespace (*optr) && !URL_SAFE_CHAR (*optr))
+ {
+ need_old = 1;
+ break;
+ }
+ }
+
+ if (need_old)
+ {
+ add_word ("<a name=\"");
+ add_anchor_name (otem, -1); /* old anchor name conversion */
+ add_word ("\"></a>\n");
+ }
+ free (otem);
+ }
+
+ /* Always output the new scheme. */
+ canon_white (tem);
+ add_word ("<a name=\"");
+ add_anchor_name (tem, 0);
+ add_word ("\"></a>\n");
+
+ free (tem);
+}
+
+
+/* The order is: nodename, nextnode, prevnode, upnode.
+ If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
+ You must follow a node command which has those fields defaulted
+ with a sectioning command (e.g., @chapter) giving the "level" of that node.
+ It is an error not to do so.
+ The defaults come from the menu in this node's parent. */
+void
+cm_node (void)
+{
+ static long epilogue_len = 0L;
+ char *node, *prev, *next, *up;
+ int new_node_pos, defaulting, this_section;
+ int no_warn = 0;
+ char *fname_for_this_node = NULL;
+ char *tem;
+ TAG_ENTRY *tag = NULL;
+
+ if (strcmp (command, "nwnode") == 0)
+ no_warn = TAG_FLAG_NO_WARN;
+
+ /* Get rid of unmatched brace arguments from previous commands. */
+ discard_braces ();
+
+ /* There also might be insertions left lying around that haven't been
+ ended yet. Do that also. */
+ discard_insertions (1);
+
+ if (!html && !already_outputting_pending_notes)
+ {
+ close_paragraph ();
+ output_pending_notes ();
+ }
+
+ new_node_pos = output_position;
+
+ if (macro_expansion_output_stream && !executing_string)
+ append_to_expansion_output (input_text_offset + 1);
+
+ /* Do not collapse -- to -, etc., in node names. */
+ in_fixed_width_font++;
+
+ /* While expanding the @node line, leave any non-macros
+ intact, so that the macro-expanded output includes them. */
+ only_macro_expansion++;
+ node = get_node_token (1);
+ only_macro_expansion--;
+ next = get_node_token (0);
+ prev = get_node_token (0);
+ up = get_node_token (0);
+
+ if (html && splitting
+ /* If there is a Top node, it always goes into index.html. So
+ don't start a new HTML file for Top. */
+ && (top_node_seen || mbscasecmp (node, "Top") != 0))
+ {
+ /* We test *node here so that @node without a valid name won't
+ start a new file name with a bogus name such as ".html".
+ This could happen if we run under "--force", where we cannot
+ simply bail out. Continuing to use the same file sounds like
+ the best we can do in such cases. */
+ if (current_output_filename && output_stream && *node)
+ {
+ char *fname_for_prev_node;
+
+ if (current_node)
+ {
+ /* NOTE: current_node at this point still holds the name
+ of the previous node. */
+ tem = expand_node_name (current_node);
+ fname_for_prev_node = nodename_to_filename (tem);
+ free (tem);
+ }
+ else /* could happen if their top node isn't named "Top" */
+ fname_for_prev_node = filename_part (current_output_filename);
+ tem = expand_node_name (node);
+ fname_for_this_node = nodename_to_filename (tem);
+ free (tem);
+ /* Don't close current output file, if next output file is
+ to have the same name. This may happen at top level, or
+ if two nodes produce the same file name under --split. */
+ if (FILENAME_CMP (fname_for_this_node, fname_for_prev_node) != 0)
+ {
+ long pos1 = 0;
+
+ /* End the current split output file. */
+ close_paragraph ();
+ output_pending_notes ();
+ start_paragraph ();
+ /* Compute the length of the HTML file's epilogue. We
+ cannot know the value until run time, due to the
+ text/binary nuisance on DOS/Windows platforms, where
+ 2 `\r' characters could be added to the epilogue when
+ it is written in text mode. */
+ if (epilogue_len == 0)
+ {
+ flush_output ();
+ pos1 = ftell (output_stream);
+ }
+ add_word ("</body></html>\n");
+ close_paragraph ();
+ if (epilogue_len == 0)
+ epilogue_len = ftell (output_stream) - pos1;
+ fclose (output_stream);
+ output_stream = NULL;
+ output_position = 0;
+ tag = find_node_by_fname (fname_for_this_node);
+ }
+ free (fname_for_prev_node);
+ }
+ }
+
+ filling_enabled = indented_fill = 0;
+ if (!html || (html && splitting))
+ current_footnote_number = 1;
+
+ if (verbose_mode)
+ printf (_("Formatting node %s...\n"), node);
+
+ if (macro_expansion_output_stream && !executing_string)
+ remember_itext (input_text, input_text_offset);
+
+ /* Reset the line number in each node for Info output, so that
+ index entries will save the line numbers of parent node. */
+ node_line_number = 0;
+
+ no_indent = 1;
+ if (xml)
+ {
+ xml_begin_node ();
+ if (!docbook)
+ {
+ xml_insert_element (NODENAME, START);
+ if (macro_expansion_output_stream && !executing_string)
+ me_execute_string (node);
+ else
+ execute_string ("%s", node);
+ xml_insert_element (NODENAME, END);
+ }
+ else
+ xml_node_id = xml_id (node);
+ }
+ else if (!no_headers && !html)
+ {
+ /* Emacs Info reader cannot grok indented escape sequence. */
+ kill_self_indent (-1);
+
+ add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename);
+
+ if (macro_expansion_output_stream && !executing_string)
+ me_execute_string (node);
+ else
+ execute_string ("%s", node);
+ filling_enabled = indented_fill = 0;
+ }
+
+ /* Check for defaulting of this node's next, prev, and up fields. */
+ defaulting = (*next == 0 && *prev == 0 && *up == 0);
+
+ this_section = what_section (input_text + input_text_offset, NULL);
+
+ /* If we are defaulting, then look at the immediately following
+ sectioning command (error if none) to determine the node's
+ level. Find the node that contains the menu mentioning this node
+ that is one level up (error if not found). That node is the "Up"
+ of this node. Default the "Next" and "Prev" from the menu. */
+ if (defaulting)
+ {
+ NODE_REF *last_ref = NULL;
+ NODE_REF *ref = node_references;
+
+ if (this_section < 0 && !STREQ (node, "Top"))
+ {
+ char *polite_section_name = "top";
+ int i;
+
+ for (i = 0; section_alist[i].name; i++)
+ if (section_alist[i].level == current_section + 1)
+ {
+ polite_section_name = section_alist[i].name;
+ break;
+ }
+
+ line_error
+ (_("Node `%s' requires a sectioning command (e.g., %c%s)"),
+ node, COMMAND_PREFIX, polite_section_name);
+ }
+ else
+ {
+ if (strcmp (node, "Top") == 0)
+ {
+ /* Default the NEXT pointer to be the first menu item in
+ this node, if there is a menu in this node. We have to
+ try very hard to find the menu, as it may be obscured
+ by execution_strings which are on the filestack. For
+ every member of the filestack which has a FILENAME
+ member which is identical to the current INPUT_FILENAME,
+ search forward from that offset. */
+ int saved_input_text_offset = input_text_offset;
+ int saved_input_text_length = input_text_length;
+ char *saved_input_text = input_text;
+ FSTACK *next_file = filestack;
+
+ int orig_offset, orig_size;
+
+ int bye_offset = search_forward ("\n@bye", input_text_offset);
+
+ /* No matter what, make this file point back at `(dir)'. */
+ free (up);
+ up = xstrdup ("(dir)"); /* html fixxme */
+
+ while (1)
+ {
+ orig_offset = input_text_offset;
+ orig_size =
+ search_forward (node_search_string, orig_offset);
+
+ if (orig_size < 0)
+ orig_size = input_text_length;
+
+ input_text_offset = search_forward ("\n@menu", orig_offset);
+ if (input_text_offset > -1
+ && (bye_offset > -1 && input_text_offset < bye_offset)
+ && cr_or_whitespace (input_text[input_text_offset + 6]))
+ {
+ char *nodename_from_menu = NULL;
+
+ input_text_offset =
+ search_forward ("\n* ", input_text_offset);
+
+ if (input_text_offset != -1)
+ nodename_from_menu = glean_node_from_menu (0,
+ (enum reftype) 0);
+
+ if (nodename_from_menu)
+ {
+ free (next);
+ next = nodename_from_menu;
+ break;
+ }
+ }
+
+ /* We got here, so it hasn't been found yet. Try
+ the next file on the filestack if there is one. */
+ if (next_file
+ && FILENAME_CMP (next_file->filename, input_filename)
+ == 0)
+ {
+ input_text = next_file->text;
+ input_text_offset = next_file->offset;
+ input_text_length = next_file->size;
+ next_file = next_file->next;
+ }
+ else
+ { /* No more input files to check. */
+ break;
+ }
+ }
+
+ input_text = saved_input_text;
+ input_text_offset = saved_input_text_offset;
+ input_text_length = saved_input_text_length;
+ }
+ }
+
+ /* Fix the level of the menu references in the Top node, iff it
+ was declared with @top, and no subsequent reference was found. */
+ if (top_node_seen && !non_top_node_seen)
+ {
+ /* Then this is the first non-@top node seen. */
+ int level;
+
+ level = set_top_section_level (this_section - 1);
+ non_top_node_seen = 1;
+
+ while (ref)
+ {
+ if (ref->section == level)
+ ref->section = this_section - 1;
+ ref = ref->next;
+ }
+
+ ref = node_references;
+ }
+
+ while (ref)
+ {
+ if (ref->section == (this_section - 1)
+ && ref->type == menu_reference
+ && strcmp (ref->node, node) == 0)
+ {
+ char *containing_node = ref->containing_node;
+
+ free (up);
+ up = xstrdup (containing_node);
+
+ if (last_ref
+ && last_ref->type == menu_reference
+ && strcmp (last_ref->containing_node, containing_node) == 0)
+ {
+ free (next);
+ next = xstrdup (last_ref->node);
+ }
+
+ while (ref->section == this_section - 1
+ && ref->next
+ && ref->next->type != menu_reference)
+ ref = ref->next;
+
+ if (ref->next && ref->type == menu_reference
+ && strcmp (ref->next->containing_node, containing_node) == 0)
+ {
+ free (prev);
+ prev = xstrdup (ref->next->node);
+ }
+ else if (!ref->next
+ && mbscasecmp (ref->containing_node, "Top") == 0)
+ {
+ free (prev);
+ prev = xstrdup (ref->containing_node);
+ }
+ break;
+ }
+ last_ref = ref;
+ ref = ref->next;
+ }
+ }
+
+ /* Insert the correct args if we are expanding macros, and the node's
+ pointers weren't defaulted. */
+ if (macro_expansion_output_stream && !executing_string && !defaulting)
+ {
+ char *temp;
+ int op_orig = output_paragraph_offset;
+ int meta_pos_orig = meta_char_pos;
+ int extra = html ? strlen (node) : 0;
+
+ temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up));
+ sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up);
+ me_execute_string (temp);
+ free (temp);
+
+ output_paragraph_offset = op_orig;
+ meta_char_pos = meta_pos_orig;
+ }
+
+ if (!*node)
+ {
+ line_error (_("No node name specified for `%c%s' command"),
+ COMMAND_PREFIX, command);
+ free (node);
+ free (next); next = NULL;
+ free (prev); prev= NULL;
+ free (up); up = NULL;
+ node_number++; /* else it doesn't get bumped */
+ }
+ else
+ {
+ if (!*next) { free (next); next = NULL; }
+ if (!*prev) { free (prev); prev = NULL; }
+ if (!*up) { free (up); up = NULL; }
+ remember_node (node, prev, next, up, new_node_pos, line_number,
+ fname_for_this_node, no_warn);
+ outstanding_node = 1;
+ }
+
+ if (html)
+ {
+ if (splitting && *node && output_stream == NULL)
+ {
+ char *dirname;
+ char filename[PATH_MAX];
+
+ dirname = pathname_part (current_output_filename);
+ strcpy (filename, dirname);
+ strcat (filename, fname_for_this_node);
+ free (dirname);
+
+ /* See if the node name converted to a file name clashes
+ with other nodes or anchors. If it clashes with an
+ anchor, we complain and nuke that anchor's file. */
+ if (!tag)
+ {
+ output_stream = fopen (filename, "w");
+ output_head_p = 0; /* so that we generate HTML preamble */
+ output_head ();
+ }
+ else if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
+ {
+ line_error (_("Anchor `%s' and node `%s' map to the same file name"),
+ tag->node, node);
+ file_line_error (tag->filename, tag->line_no,
+ _("This @anchor command ignored; references to it will not work"));
+ file_line_error (tag->filename, tag->line_no,
+ _("Rename this anchor or use the `--no-split' option"));
+ /* Nuke the file name recorded in anchor's tag.
+ Since we are about to nuke the file itself, we
+ don't want find_node_by_fname to consider this
+ anchor anymore. */
+ free (tag->html_fname);
+ tag->html_fname = NULL;
+ output_stream = fopen (filename, "w");
+ output_head_p = 0; /* so that we generate HTML preamble */
+ output_head ();
+ }
+ else
+ {
+ /* This node's file name clashes with another node.
+ We put them both on the same file. */
+ output_stream = fopen (filename, "r+");
+ if (output_stream)
+ {
+ static char html_end[] = "</body></html>\n";
+ char end_line[sizeof(html_end)];
+ int fpos = fseek (output_stream, -epilogue_len,
+ SEEK_END);
+
+ if (fpos < 0
+ || fgets (end_line, sizeof (html_end),
+ output_stream) == NULL
+ /* Paranoia: did someone change the way HTML
+ files are finished up? */
+ || mbscasecmp (end_line, html_end) != 0)
+ {
+ line_error (_("Unexpected string at end of split-HTML file `%s'"),
+ fname_for_this_node);
+ fclose (output_stream);
+ xexit (1);
+ }
+ fseek (output_stream, -epilogue_len, SEEK_END);
+ }
+ }
+ if (output_stream == NULL)
+ {
+ fs_error (filename);
+ xexit (1);
+ }
+ set_current_output_filename (filename);
+ }
+
+ if (!splitting && no_headers)
+ { /* cross refs need a name="#anchor" even if not writing headers */
+ add_html_names (node);
+ }
+
+ if (splitting || !no_headers)
+ { /* Navigation bar. */
+ add_html_block_elt ("<div class=\"node\">\n");
+
+ /* In the split HTML case, the filename is wrong for the
+ old-style converted names, but we'll add them anyway, for
+ consistency. (And we need them in the normal (not
+ no_headers) nonsplit case.) */
+ add_html_names (node);
+
+ /* Do this after adding the anchors, so the browser rendering
+ can be better. The <p> avoids the links area running on
+ with old Lynxen. */
+ add_word_args ("<p>%s\n", splitting ? "" : "<hr>");
+
+ if (next)
+ {
+ tem = expansion (next, 0);
+ add_word ((char *) gdt("Next:"));
+ add_word ("&nbsp;");
+
+ add_word ("<a rel=\"next\" accesskey=\"n\" href=\"");
+ add_anchor_name (tem, 1);
+ tem = escape_string (tem);
+ add_word_args ("\">%s</a>", tem);
+
+ free (tem);
+
+ if (prev || up)
+ add_word (",\n");
+ }
+ if (prev)
+ {
+ tem = expansion (prev, 0);
+ add_word ((char *) gdt("Previous:"));
+ add_word ("&nbsp;");
+ add_word ("<a rel=\"previous\" accesskey=\"p\" href=\"");
+ add_anchor_name (tem, 1);
+ tem = escape_string (tem);
+ add_word_args ("\">%s</a>", tem);
+ free (tem);
+
+ if (up)
+ add_word (",\n");
+ }
+ if (up)
+ {
+ tem = expansion (up, 0);
+ add_word ((char *) gdt("Up:"));
+ add_word ("&nbsp;");
+ add_word ("<a rel=\"up\" accesskey=\"u\" href=\"");
+ add_anchor_name (tem, 1);
+ tem = escape_string (tem);
+ add_word_args ("\">%s</a>", tem);
+ free (tem);
+ }
+ /* html fixxme: we want a `top' or `contents' link here. */
+
+ add_word_args ("\n%s\n", splitting ? "<hr>" : "");
+ add_word ("</div>\n");
+ }
+ }
+ else if (docbook)
+ ;
+ else if (xml)
+ {
+ if (next)
+ {
+ xml_insert_element (NODENEXT, START);
+ execute_string ("%s", next);
+ xml_insert_element (NODENEXT, END);
+ }
+ if (prev)
+ {
+ xml_insert_element (NODEPREV, START);
+ execute_string ("%s", prev);
+ xml_insert_element (NODEPREV, END);
+ }
+ if (up)
+ {
+ xml_insert_element (NODEUP, START);
+ execute_string ("%s", up);
+ xml_insert_element (NODEUP, END);
+ }
+ }
+ else if (!no_headers)
+ {
+ if (macro_expansion_output_stream)
+ me_inhibit_expansion++;
+
+ /* These strings are not translatable. */
+ if (next)
+ {
+ execute_string (", Next: %s", next);
+ filling_enabled = indented_fill = 0;
+ }
+ if (prev)
+ {
+ execute_string (", Prev: %s", prev);
+ filling_enabled = indented_fill = 0;
+ }
+ if (up)
+ {
+ execute_string (", Up: %s", up);
+ filling_enabled = indented_fill = 0;
+ }
+ if (macro_expansion_output_stream)
+ me_inhibit_expansion--;
+ }
+
+ close_paragraph ();
+ no_indent = 0;
+
+ /* Change the section only if there was a sectioning command. */
+ if (this_section >= 0)
+ current_section = this_section;
+
+ if (current_node && STREQ (current_node, "Top"))
+ top_node_seen = 1;
+
+ filling_enabled = 1;
+ in_fixed_width_font--;
+}
+
+/* Cross-reference target at an arbitrary spot. */
+void
+cm_anchor (int arg)
+{
+ char *anchor;
+ char *fname_for_anchor = NULL;
+
+ if (arg == END)
+ {
+ /* We want to ignore whitespace following @anchor a la
+ texinfo.tex, but we're sitting at the }. So advance past it,
+ ignore the whitespace, and then go back one character. When we
+ return, reader_loop will increment input_text_offset again (see
+ the '}' case). Sorry. */
+ input_text_offset++;
+ skip_whitespace_and_newlines ();
+ input_text_offset--;
+ return;
+ }
+
+ /* Parse the anchor text. */
+ anchor = get_xref_token (1);
+
+ /* Force all versions of "top" to be "Top". */
+ normalize_node_name (anchor);
+
+ /* In HTML mode, need to actually produce some output. */
+ if (html)
+ {
+ /* If this anchor is at the beginning of a new paragraph, make
+ sure a new paragraph is indeed started. */
+ if (!paragraph_is_open)
+ {
+ if (!executing_string && html)
+ output_head ();
+ start_paragraph ();
+ if (!in_fixed_width_font || in_menu || in_detailmenu)
+ {
+ insert_string ("<p>");
+ in_paragraph = 1;
+ }
+ }
+ add_word ("<a name=\"");
+ add_anchor_name (anchor, 0);
+ add_word ("\"></a>");
+ if (splitting)
+ {
+ /* If we are splitting, cm_xref will produce a reference to
+ a file whose name is derived from the anchor name. So we
+ must create a file when we see an @anchor, otherwise
+ xref's to anchors won't work. The file we create simply
+ redirects to the file of this anchor's node. */
+ TAG_ENTRY *tag;
+
+ fname_for_anchor = nodename_to_filename (anchor);
+ /* See if the anchor name converted to a file name clashes
+ with other anchors or nodes. */
+ tag = find_node_by_fname (fname_for_anchor);
+ if (tag)
+ {
+ if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
+ line_error (_("Anchors `%s' and `%s' map to the same file name"),
+ anchor, tag->node);
+ else
+ line_error (_("Anchor `%s' and node `%s' map to the same file name"),
+ anchor, tag->node);
+ line_error (_("@anchor command ignored; references to it will not work"));
+ line_error (_("Rename this anchor or use the `--no-split' option"));
+ free (fname_for_anchor);
+ /* We will not be creating a file for this anchor, so
+ set its name to NULL, so that remember_node stores a
+ NULL and find_node_by_fname won't consider this
+ anchor for clashes. */
+ fname_for_anchor = NULL;
+ }
+ else
+ {
+ char *dirname, *p;
+ char filename[PATH_MAX];
+ FILE *anchor_stream;
+
+ dirname = pathname_part (current_output_filename);
+ strcpy (filename, dirname);
+ strcat (filename, fname_for_anchor);
+ free (dirname);
+
+ anchor_stream = fopen (filename, "w");
+ if (anchor_stream == NULL)
+ {
+ fs_error (filename);
+ xexit (1);
+ }
+ /* The HTML magic below will cause the browser to
+ immediately go to the anchor's node's file. Lynx
+ seems not to support this redirection, but it looks
+ like a bug in Lynx, and they can work around it by
+ clicking on the link once more. */
+ fputs ("<meta http-equiv=\"refresh\" content=\"0; url=",
+ anchor_stream);
+ /* Make the indirect link point to the current node's
+ file and anchor's "<a name" label. If we don't have
+ a valid node name, refer to the current output file
+ instead. */
+ if (current_node && *current_node)
+ {
+ char *fn, *tem;
+
+ tem = expand_node_name (current_node);
+ fn = nodename_to_filename (tem);
+ free (tem);
+ fputs (fn, anchor_stream);
+ free (fn);
+ }
+ else
+ {
+ char *base = filename_part (current_output_filename);
+
+ fputs (base, anchor_stream);
+ free (base);
+ }
+ fputs ("#", anchor_stream);
+ for (p = anchor; *p; p++)
+ {
+ if (*p == '&')
+ fputs ("&amp;", anchor_stream);
+ else if (!URL_SAFE_CHAR (*p))
+ fprintf (anchor_stream, "%%%x", (unsigned char) *p);
+ else
+ fputc (*p, anchor_stream);
+ }
+ fputs ("\">\n", anchor_stream);
+ fclose (anchor_stream);
+ }
+ }
+ }
+ else if (xml || docbook)
+ {
+ xml_insert_element_with_attribute (ANCHOR, START,
+ docbook ? "id=\"%s\"" : "name=\"%s\"",
+ anchor);
+ xml_insert_element (ANCHOR, END);
+ }
+
+ /* Save it in the tag table. */
+ remember_node (anchor, NULL, NULL, NULL,
+ output_position + output_paragraph_offset,
+ line_number, fname_for_anchor, TAG_FLAG_ANCHOR);
+}
+
+/* Find NODE in REF_LIST. */
+static NODE_REF *
+find_node_reference (char *node, NODE_REF *ref_list)
+{
+ NODE_REF *orig_ref_list = ref_list;
+ char *expanded_node;
+
+ while (ref_list)
+ {
+ if (strcmp (node, ref_list->node) == 0)
+ break;
+ ref_list = ref_list->next;
+ }
+
+ if (ref_list || !expensive_validation)
+ return ref_list;
+
+ /* Maybe NODE is not expanded yet. This may be SLOW. */
+ expanded_node = expand_node_name (node);
+ for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next)
+ {
+ if (STREQ (expanded_node, ref_list->node))
+ break;
+ if (strchr (ref_list->node, COMMAND_PREFIX))
+ {
+ char *expanded_ref = expand_node_name (ref_list->node);
+
+ if (STREQ (expanded_node, expanded_ref))
+ {
+ free (expanded_ref);
+ break;
+ }
+ free (expanded_ref);
+ }
+ }
+ free (expanded_node);
+ return ref_list;
+}
+
+void
+free_node_references (void)
+{
+ NODE_REF *list, *temp;
+
+ list = node_references;
+
+ while (list)
+ {
+ temp = list;
+ free (list->node);
+ free (list->containing_node);
+ list = list->next;
+ free (temp);
+ }
+ node_references = NULL;
+}
+
+void
+free_node_node_references (void)
+{
+ NODE_REF *list, *temp;
+
+ list = node_references;
+
+ while (list)
+ {
+ temp = list;
+ free (list->node);
+ list = list->next;
+ free (temp);
+ }
+ node_node_references = NULL;
+}
+
+/* Return the number assigned to a named node in either the tag_table
+ or node_references list or zero if no number has been assigned. */
+int
+number_of_node (char *node)
+{
+ NODE_REF *temp_ref;
+ TAG_ENTRY *temp_node = find_node (node);
+
+ if (temp_node)
+ return temp_node->number;
+ else if ((temp_ref = find_node_reference (node, node_references)))
+ return temp_ref->number;
+ else if ((temp_ref = find_node_reference (node, node_node_references)))
+ return temp_ref->number;
+ else
+ return 0;
+}
+
+/* validation */
+
+/* Return 1 if TAG (at LINE) correctly validated, or 0 if not.
+ LABEL is the (translated) description of the type of reference --
+ Menu, Cross, Next, etc. */
+
+static int
+validate (char *tag, int line, const char *label)
+{
+ TAG_ENTRY *result;
+
+ /* If there isn't a tag to verify, or if the tag is in another file,
+ then it must be okay. */
+ if (!tag || !*tag || *tag == '(')
+ return 1;
+
+ /* Otherwise, the tag must exist. */
+ result = find_node (tag);
+
+ if (!result)
+ {
+ line_number = line;
+ line_error (_("%s reference to nonexistent node `%s' (perhaps incorrect sectioning?)"), label, tag);
+ return 0;
+ }
+ result->touched++;
+ return 1;
+}
+
+/* The strings here are followed in the message by `reference to...' in
+ the `validate' routine. They are only used in messages, thus are
+ translated. */
+static const char *
+reftype_type_string (enum reftype type)
+{
+ switch (type)
+ {
+ case menu_reference:
+ return gdt("Menu");
+ case followed_reference:
+ return gdt("Cross");
+ default:
+ return "Internal-bad-reference-type";
+ }
+}
+
+static void
+validate_other_references (NODE_REF *ref_list)
+{
+ char *old_input_filename = input_filename;
+
+ while (ref_list)
+ {
+ input_filename = ref_list->filename;
+ validate (ref_list->node, ref_list->line_no,
+ reftype_type_string (ref_list->type));
+ ref_list = ref_list->next;
+ }
+ input_filename = old_input_filename;
+}
+
+/* Validation of an info file.
+ Scan through the list of tag entries touching the Prev, Next, and Up
+ elements of each. It is an error not to be able to touch one of them,
+ except in the case of external node references, such as "(DIR)".
+
+ If the Prev is different from the Up,
+ then the Prev node must have a Next pointing at this node.
+
+ Every node except Top must have an Up.
+ The Up node must contain some sort of reference, other than a Next,
+ to this node.
+
+ If the Next is different from the Next of the Up,
+ then the Next node must have a Prev pointing at this node. */
+void
+validate_file (TAG_ENTRY *tag_table)
+{
+ char *old_input_filename = input_filename;
+ TAG_ENTRY *tags = tag_table;
+
+ while (tags)
+ {
+ TAG_ENTRY *temp_tag;
+ char *tem1, *tem2;
+
+ input_filename = tags->filename;
+ line_number = tags->line_no;
+
+ /* If this is a "no warn" node, don't validate it in any way. */
+ if (tags->flags & TAG_FLAG_NO_WARN)
+ {
+ tags = tags->next_ent;
+ continue;
+ }
+
+ /* If this node has a Next, then make sure that the Next exists. */
+ if (tags->next)
+ {
+ validate (tags->next, tags->line_no, gdt("Next"));
+
+ /* If the Next node exists, and there is no Up, then make sure
+ that the Prev of the Next points back. But do nothing if
+ we aren't supposed to issue warnings about this node. */
+ temp_tag = find_node (tags->next);
+ if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN))
+ {
+ char *prev = temp_tag->prev;
+ int you_lose = !prev || !STREQ (prev, tags->node);
+
+ if (you_lose && expensive_validation)
+ {
+ tem1 = expand_node_name (prev);
+ tem2 = expand_node_name (tags->node);
+
+ if (tem1 && tem2 && STREQ (tem1, tem2))
+ you_lose = 0;
+ free (tem1);
+ free (tem2);
+ }
+ if (you_lose)
+ {
+ line_error (_("Next field of node `%s' not pointed to (perhaps incorrect sectioning?)"),
+ tags->node);
+ file_line_error (temp_tag->filename, temp_tag->line_no,
+ _("This node (%s) has the bad Prev"),
+ temp_tag->node);
+ temp_tag->flags |= TAG_FLAG_PREV_ERROR;
+ }
+ }
+ }
+
+ /* Validate the Prev field if there is one, and we haven't already
+ complained about it in some way. You don't have to have a Prev
+ field at this stage. */
+ if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev)
+ {
+ int valid_p = validate (tags->prev, tags->line_no, gdt("Prev"));
+
+ if (!valid_p)
+ tags->flags |= TAG_FLAG_PREV_ERROR;
+ else
+ { /* If the Prev field is not the same as the Up field,
+ then the node pointed to by the Prev field must have
+ a Next field which points to this node. */
+ int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up);
+
+ if (!prev_equals_up && expensive_validation)
+ {
+ tem1 = expand_node_name (tags->prev);
+ tem2 = expand_node_name (tags->up);
+ prev_equals_up = STREQ (tem1, tem2);
+ free (tem1);
+ free (tem2);
+ }
+ if (!prev_equals_up)
+ {
+ temp_tag = find_node (tags->prev);
+
+ /* If we aren't supposed to issue warnings about the
+ target node, do nothing. */
+ if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN))
+ /* Do nothing. */ ;
+ else
+ {
+ int you_lose = !temp_tag->next
+ || !STREQ (temp_tag->next, tags->node);
+
+ if (temp_tag->next && you_lose && expensive_validation)
+ {
+ tem1 = expand_node_name (temp_tag->next);
+ tem2 = expand_node_name (tags->node);
+ if (STREQ (tem1, tem2))
+ you_lose = 0;
+ free (tem1);
+ free (tem2);
+ }
+ if (you_lose)
+ {
+ line_error
+ (_("Prev field of node `%s' not pointed to"),
+ tags->node);
+ file_line_error (temp_tag->filename,
+ temp_tag->line_no,
+ _("This node (%s) has the bad Next"),
+ temp_tag->node);
+ temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
+ }
+ }
+ }
+ }
+ }
+
+ if (!tags->up
+ && !(tags->flags & TAG_FLAG_ANCHOR)
+ && mbscasecmp (tags->node, "Top") != 0)
+ line_error (_("`%s' has no Up field (perhaps incorrect sectioning?)"), tags->node);
+ else if (tags->up)
+ {
+ int valid_p = validate (tags->up, tags->line_no, gdt("Up"));
+
+ /* If node X has Up: Y, then warn if Y fails to have a menu item
+ or note pointing at X, if Y isn't of the form "(Y)". */
+ if (valid_p && *tags->up != '(')
+ {
+ NODE_REF *nref;
+ NODE_REF *tref = NULL;
+ NODE_REF *list = node_references;
+
+ for (;;)
+ {
+ nref = find_node_reference (tags->node, list);
+ if (!nref)
+ break;
+
+ if (strcmp (nref->containing_node, tags->up) == 0)
+ {
+ if (nref->type != menu_reference)
+ {
+ tref = nref;
+ list = nref->next;
+ }
+ else
+ break;
+ }
+ list = nref->next;
+ }
+
+ if (!nref)
+ {
+ if (!tref && expensive_validation)
+ {
+ /* Sigh... This might be AWFULLY slow, but if
+ they want this feature, they'll have to pay!
+ We do all the loop again expanding each
+ containing_node reference as we go. */
+ char *tags_up = expand_node_name (tags->up);
+ char *tem;
+
+ list = node_references;
+
+ for (;;)
+ {
+ nref = find_node_reference (tags->node, list);
+ if (!nref)
+ break;
+ tem = expand_node_name (nref->containing_node);
+ if (STREQ (tem, tags_up))
+ {
+ if (nref->type != menu_reference)
+ tref = nref;
+ else
+ {
+ free (tem);
+ break;
+ }
+ }
+ free (tem);
+ list = nref->next;
+ }
+ }
+ if (!nref && !tref)
+ {
+ temp_tag = find_node (tags->up);
+ file_line_error (temp_tag->filename, temp_tag->line_no,
+ _("Node `%s' lacks menu item for `%s' despite being its Up target"),
+ tags->up, tags->node);
+ }
+ }
+ }
+ }
+ tags = tags->next_ent;
+ }
+
+ validate_other_references (node_references);
+ /* We have told the user about the references which didn't exist.
+ Now tell him about the nodes which aren't referenced. */
+
+ for (tags = tag_table; tags; tags = tags->next_ent)
+ {
+ /* If this node is a "no warn" node, do nothing. */
+ if (tags->flags & TAG_FLAG_NO_WARN)
+ {
+ tags = tags->next_ent;
+ continue;
+ }
+
+ if (tags->touched == 0)
+ {
+ input_filename = tags->filename;
+ line_number = tags->line_no;
+
+ /* Notice that the node "Top" is special, and doesn't have to
+ be referenced. Anchors don't have to be referenced
+ either, you might define them for another document. */
+ if (mbscasecmp (tags->node, "Top") != 0
+ && !(tags->flags & TAG_FLAG_ANCHOR))
+ warning (_("unreferenced node `%s'"), tags->node);
+ }
+ }
+ input_filename = old_input_filename;
+}
+
+
+/* Splitting */
+
+/* Return true if the tag entry pointed to by TAGS is the last node.
+ This means only anchors follow. */
+
+static int
+last_node_p (TAG_ENTRY *tags)
+{
+ int last = 1;
+ while (tags->next_ent) {
+ tags = tags->next_ent;
+ if (tags->flags & TAG_FLAG_ANCHOR)
+ ;
+ else
+ {
+ last = 0;
+ break;
+ }
+ }
+
+ return last;
+}
+
+
+static char *
+enumerate_filename (char *pathname, char *basename, int number)
+{
+ /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */
+ const int dos_file_names = !HAVE_LONG_FILENAMES (pathname ? pathname : ".");
+ unsigned name_len = strlen (basename);
+ char *filename = xmalloc (10 + strlen (pathname) + name_len);
+ char *base_filename = xmalloc (10 + name_len);
+
+ sprintf (base_filename, "%s-%d", basename, number);
+
+ if (dos_file_names)
+ {
+ char *dot = strchr (base_filename, '.');
+ unsigned base_len = strlen (base_filename);
+
+ if (dot)
+ { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
+ dot[1] = 'i';
+ memmove (number <= 99 ? dot + 2 : dot + 1,
+ base_filename + name_len + 1,
+ strlen (base_filename + name_len + 1) + 1);
+ }
+ else if (base_len > 8)
+ {
+ /* Make foobar-1, .., fooba-10, .., foob-100, ... */
+ unsigned numlen = base_len - name_len;
+
+ memmove (base_filename + 8 - numlen, base_filename + name_len, numlen + 1);
+ }
+ }
+
+ sprintf (filename, "%s%s", pathname, base_filename);
+
+ return filename;
+}
+
+/* Remove previously split files, to avoid
+ lingering parts of shrinked documents. */
+void
+clean_old_split_files (char *filename)
+{
+ char *root_filename = filename_part (filename);
+ char *root_pathname = pathname_part (filename);
+ int i;
+
+ /* We break as soon as we hit an inexistent file,
+ so looping until large numbers is harmless. */
+ for (i = 1; i < 1000; i++)
+ {
+ struct stat st;
+ char *check_file = enumerate_filename (root_pathname, root_filename, i);
+
+ if (stat (check_file, &st) != 0)
+ break;
+ else if (!S_ISDIR (st.st_mode))
+ {
+ /* Give feedback if requested, removing a file is important. */
+ if (verbose_mode)
+ printf (_("Removing %s\n"), check_file);
+
+ /* Warn user that we cannot remove the file. */
+ if (unlink (check_file) != 0)
+ warning (_("Can't remove file `%s': %s"), check_file, strerror (errno));
+ }
+
+ free (check_file);
+ }
+}
+
+
+/* Split large output files into a series of smaller files. Each file
+ is pointed to in the tag table, which then gets written out as the
+ original file. The new files have the same name as the original file
+ with a "-num" attached. SIZE is the largest number of bytes to allow
+ in any single split file. */
+void
+split_file (char *filename, int size)
+{
+ char *root_filename, *root_pathname;
+ char *the_file;
+ struct stat fileinfo;
+ long file_size;
+ char *the_header;
+ int header_size;
+
+ /* Can only do this to files with tag tables. */
+ if (!tag_table)
+ return;
+
+ if (size == 0)
+ size = DEFAULT_SPLIT_SIZE;
+
+ if ((stat (filename, &fileinfo) != 0)
+ || (((long) fileinfo.st_size) < size))
+ return;
+ file_size = (long) fileinfo.st_size;
+
+ the_file = find_and_load (filename, 0);
+ if (!the_file)
+ return;
+
+ root_filename = filename_part (filename);
+ root_pathname = pathname_part (filename);
+
+ if (!root_pathname)
+ root_pathname = xstrdup ("");
+
+ /* Start splitting the file. Walk along the tag table
+ outputting sections of the file. When we have written
+ all of the nodes in the tag table, make the top-level
+ pointer file, which contains indirect pointers and
+ tags for the nodes. */
+ {
+ int which_file = 1;
+ TAG_ENTRY *tags = tag_table;
+ char *indirect_info = NULL;
+
+ /* Maybe we want a Local Variables section. */
+ char *trailer = info_trailer ();
+ int trailer_len = trailer ? strlen (trailer) : 0;
+
+ /* Remember the `header' of this file. The first tag in the file is
+ the bottom of the header; the top of the file is the start. */
+ the_header = xmalloc (1 + (header_size = tags->position));
+ memcpy (the_header, the_file, header_size);
+
+ while (tags)
+ {
+ int file_top, file_bot, limit;
+
+ /* Have to include the Control-_. */
+ file_top = file_bot = tags->position;
+ limit = file_top + size;
+
+ /* If the rest of this file is only one node, then
+ that is the entire subfile. */
+ if (last_node_p (tags))
+ {
+ int i = tags->position + 1;
+ char last_char = the_file[i];
+
+ while (i < file_size)
+ {
+ if ((the_file[i] == '\037') &&
+ ((last_char == '\n') ||
+ (last_char == '\014')))
+ break;
+ else
+ last_char = the_file[i];
+ i++;
+ }
+ file_bot = i;
+ tags = tags->next_ent;
+ goto write_region;
+ }
+
+ /* Otherwise, find the largest number of nodes that can fit in
+ this subfile. */
+ for (; tags; tags = tags->next_ent)
+ {
+ if (last_node_p (tags))
+ {
+ /* This entry is the last node. Search forward for the end
+ of this node, and that is the end of this file. */
+ int i = tags->position + 1;
+ char last_char = the_file[i];
+
+ while (i < file_size)
+ {
+ if ((the_file[i] == '\037') &&
+ ((last_char == '\n') ||
+ (last_char == '\014')))
+ break;
+ else
+ last_char = the_file[i];
+ i++;
+ }
+ file_bot = i;
+
+ if (file_bot < limit)
+ {
+ tags = tags->next_ent;
+ goto write_region;
+ }
+ else
+ {
+ /* Here we want to write out everything before the last
+ node, and then write the last node out in a file
+ by itself. */
+ file_bot = tags->position;
+ goto write_region;
+ }
+ }
+
+ /* Write region only if this was a node, not an anchor. */
+ if (tags->next_ent->position > limit
+ && !(tags->flags & TAG_FLAG_ANCHOR))
+ {
+ if (tags->position == file_top)
+ tags = tags->next_ent;
+
+ file_bot = tags->position;
+
+ write_region:
+ {
+ int fd;
+ char *split_filename = enumerate_filename (root_pathname,
+ root_filename, which_file);
+ char *split_basename = filename_part (split_filename);
+
+ fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
+ if (fd < 0
+ || write (fd, the_header, header_size) != header_size
+ || write (fd, the_file + file_top, file_bot - file_top)
+ != (file_bot - file_top)
+ || (trailer_len
+ && write (fd, trailer, trailer_len) != trailer_len)
+ || close (fd) < 0)
+ {
+ perror (split_filename);
+ if (fd != -1)
+ close (fd);
+ xexit (1);
+ }
+
+ if (!indirect_info)
+ {
+ indirect_info = the_file + file_top;
+ sprintf (indirect_info, "\037\nIndirect:\n");
+ indirect_info += strlen (indirect_info);
+ }
+
+ sprintf (indirect_info, "%s: %d\n",
+ split_basename, file_top);
+
+ free (split_basename);
+ free (split_filename);
+ indirect_info += strlen (indirect_info);
+ which_file++;
+ break;
+ }
+ }
+ }
+ }
+
+ /* We have sucessfully created the subfiles. Now write out the
+ original again. We must use `output_stream', or
+ write_tag_table_indirect () won't know where to place the output. */
+ output_stream = fopen (filename, "w");
+ if (!output_stream)
+ {
+ perror (filename);
+ xexit (1);
+ }
+
+ {
+ int distance = indirect_info - the_file;
+ fwrite (the_file, 1, distance, output_stream);
+
+ /* Inhibit newlines. */
+ paragraph_is_open = 0;
+
+ /* Write the indirect tag table. */
+ write_tag_table_indirect ();
+
+ /* preserve local variables in info output. */
+ if (trailer)
+ {
+ fwrite (trailer, 1, trailer_len, output_stream);
+ free (trailer);
+ }
+
+ fclose (output_stream);
+ free (the_header);
+ free (the_file);
+ return;
+ }
+ }
+}
diff --git a/makeinfo/node.h b/makeinfo/node.h
new file mode 100644
index 0000000..6f7fc39
--- /dev/null
+++ b/makeinfo/node.h
@@ -0,0 +1,129 @@
+/* node.h -- declarations for Node.
+ $Id: node.h,v 1.5 2007/07/01 21:20:33 karl Exp $
+
+ Copyright (C) 1996, 1997, 1998, 1999, 2002, 2007
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Written by Brian Fox (bfox@ai.mit.edu). */
+
+#ifndef NODE_H
+#define NODE_H
+
+#include "xref.h"
+
+/* The various references that we know about. */
+/* What we remember for each node. */
+typedef struct tentry
+{
+ struct tentry *next_ent;
+ char *node; /* Name of this node. */
+ char *prev; /* Name of "Prev:" for this node. */
+ char *next; /* Name of "Next:" for this node. */
+ char *up; /* Name of "Up:" for this node. */
+ int position; /* Output file position of this node. */
+ int line_no; /* Defining line in source file. */
+ char *filename; /* The file that this node was found in. */
+ int touched; /* Nonzero means this node has been referenced. */
+ int flags;
+ int number; /* Number for this node, relevant for HTML
+ splitting -- from use+define order, not just
+ define. */
+ int order; /* The order of the tag, starting from zero. */
+ char *html_fname; /* The HTML file to which this node is written
+ (non-NULL only for HTML splitting). */
+} TAG_ENTRY;
+
+/* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
+ we turn on this flag bit in node-b's tag entry. This means that when
+ it is time to validate node-b, we don't report an additional error
+ if there was no "Prev" field. */
+#define TAG_FLAG_PREV_ERROR 1
+#define TAG_FLAG_NEXT_ERROR 2
+#define TAG_FLAG_UP_ERROR 4
+#define TAG_FLAG_NO_WARN 8
+#define TAG_FLAG_IS_TOP 16
+#define TAG_FLAG_ANCHOR 32
+
+/* Menu reference, *note reference, and validation hacking. */
+
+/* A structure to remember references with. A reference to a node is
+ either an entry in a menu, or a cross-reference made with [px]ref. */
+typedef struct node_ref
+{
+ struct node_ref *next;
+ char *node; /* Name of node referred to. */
+ char *containing_node; /* Name of node containing this reference. */
+ int line_no; /* Line number where the reference occurs. */
+ int section; /* Section level where the reference occurs. */
+ char *filename; /* Name of file where the reference occurs. */
+ enum reftype type; /* Type of reference, either menu or note. */
+ int number; /* Number for this node, relevant for
+ HTML splitting -- from use+define
+ order, not just define. */
+} NODE_REF;
+
+/* The linked list of such structures. */
+extern NODE_REF *node_references;
+
+/* A similar list for references occuring in @node next
+ and similar references, needed for HTML. */
+extern NODE_REF *node_node_references;
+
+/* List of all nodes. */
+extern TAG_ENTRY *tag_table;
+
+/* Counter for setting node_ref.number; zero is Top. */
+extern int node_number;
+
+/* Node order counter. */
+extern int node_order;
+
+/* The current node's section level. */
+extern int current_section;
+
+/* Nonzero when the next sectioning command should generate an anchor
+ corresponding to the current node in HTML mode. */
+extern int outstanding_node;
+
+extern TAG_ENTRY *find_node (char *name);
+
+/* A search string which is used to find a line defining a node. */
+DECLARE (char *, node_search_string, "\n@node ");
+
+/* Extract node name from a menu item. */
+extern char *glean_node_from_menu (int remember_ref, enum reftype ref_type);
+
+/* Remember a node for later validation. */
+extern void remember_node_reference (char *node, int line, enum reftype type);
+
+/* Remember the name of the current output file. */
+extern void set_current_output_filename (const char *fname);
+
+/* Expand macros and commands in the node name and canonicalize
+ whitespace in the resulting expansion. */
+extern char *expand_node_name (char *node);
+
+extern int number_of_node (char *node);
+
+extern void init_tag_table (void);
+extern void write_tag_table (char *filename);
+extern void free_node_references (void);
+extern void free_node_node_references (void);
+extern void validate_file (TAG_ENTRY *tag_table);
+extern void split_file (char *filename, int size);
+extern void clean_old_split_files (char *filename);
+
+#endif /* NODE_H */
diff --git a/makeinfo/sectioning.c b/makeinfo/sectioning.c
new file mode 100644
index 0000000..da26ebb
--- /dev/null
+++ b/makeinfo/sectioning.c
@@ -0,0 +1,832 @@
+/* sectioning.c -- for @chapter, @section, ..., @contents ...
+ $Id: sectioning.c,v 1.29 2007/07/01 21:20:33 karl Exp $
+
+ Copyright (C) 1999, 2001, 2002, 2003, 2004, 2007
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#include "system.h"
+#include "cmds.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "node.h"
+#include "toc.h"
+#include "sectioning.h"
+#include "xml.h"
+
+/* See comment in sectioning.h. */
+section_alist_type section_alist[] = {
+ { "unnumberedsubsubsec", 5, ENUM_SECT_NO, TOC_YES },
+ { "unnumberedsubsec", 4, ENUM_SECT_NO, TOC_YES },
+ { "unnumberedsec", 3, ENUM_SECT_NO, TOC_YES },
+ { "unnumbered", 2, ENUM_SECT_NO, TOC_YES },
+ { "centerchap", 2, ENUM_SECT_NO, TOC_YES },
+
+ { "appendixsubsubsec", 5, ENUM_SECT_APP, TOC_YES }, /* numbered like A.X.X.X */
+ { "appendixsubsec", 4, ENUM_SECT_APP, TOC_YES },
+ { "appendixsec", 3, ENUM_SECT_APP, TOC_YES },
+ { "appendixsection", 3, ENUM_SECT_APP, TOC_YES },
+ { "appendix", 2, ENUM_SECT_APP, TOC_YES },
+
+ { "subsubsec", 5, ENUM_SECT_YES, TOC_YES },
+ { "subsubsection", 5, ENUM_SECT_YES, TOC_YES },
+ { "subsection", 4, ENUM_SECT_YES, TOC_YES },
+ { "section", 3, ENUM_SECT_YES, TOC_YES },
+ { "chapter", 2, ENUM_SECT_YES, TOC_YES },
+
+ { "subsubheading", 5, ENUM_SECT_NO, TOC_NO },
+ { "subheading", 4, ENUM_SECT_NO, TOC_NO },
+ { "heading", 3, ENUM_SECT_NO, TOC_NO },
+ { "chapheading", 2, ENUM_SECT_NO, TOC_NO },
+ { "majorheading", 2, ENUM_SECT_NO, TOC_NO },
+
+ { "top", 1, ENUM_SECT_NO, TOC_YES },
+ { NULL, 0, 0, 0 }
+};
+
+/* The argument of @settitle, used for HTML. */
+char *title = NULL;
+
+
+#define APPENDIX_MAGIC 1024
+#define UNNUMBERED_MAGIC 2048
+
+/* Number memory for every level @chapter, @section,
+ @subsection, @subsubsection. */
+static int numbers [] = { 0, 0, 0, 0 };
+
+/* enum_marker == APPENDIX_MAGIC then we are counting appendencies
+ enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area.
+ Handling situations like this:
+ @unnumbered ..
+ @section ... */
+static int enum_marker = 0;
+
+/* Organized by level commands. That is, "*" == chapter, "=" == section. */
+static char *scoring_characters = "*=-.";
+
+/* Amount to offset the name of sectioning commands to levels by. */
+static int section_alist_offset = 0;
+
+/* These two variables are for @float, @cindex like commands that need to know
+ in which section they are used. */
+/* Last value returned by get_sectioning_number. */
+static char *last_sectioning_number = "";
+/* Last title used by sectioning_underscore, etc. */
+static char *last_sectioning_title = "";
+
+/* num == ENUM_SECT_NO means unnumbered (should never call this)
+ num == ENUM_SECT_YES means numbered
+ num == ENUM_SECT_APP means numbered like A.1 and so on */
+static char *
+get_sectioning_number (int level, int num)
+{
+ static char s[100]; /* should ever be enough for 99.99.99.99
+ Appendix A.1 */
+
+ char *p;
+ int i;
+
+ s[0] = 0;
+
+ /* create enumeration in front of chapter, section, subsection and so on. */
+ for (i = 0; i < level; i++)
+ {
+ p = s + strlen (s);
+ if ((i == 0) && (enum_marker == APPENDIX_MAGIC))
+ sprintf (p, "%c.", numbers[i] + 64); /* Should be changed to
+ be more portable */
+ else
+ sprintf (p, "%d.", numbers[i]);
+ }
+
+ /* the last number is never followed by a dot */
+ p = s + strlen (s);
+ if ((num == ENUM_SECT_APP)
+ && (i == 0)
+ && (enum_marker == APPENDIX_MAGIC))
+ sprintf (p, _("Appendix %c"), numbers[i] + 64);
+ else
+ sprintf (p, "%d", numbers[i]);
+
+ /* Poor man's cache :-) */
+ if (strlen (last_sectioning_number))
+ free (last_sectioning_number);
+ last_sectioning_number = xstrdup (s);
+
+ return s;
+}
+
+
+/* Set the level of @top to LEVEL. Return the old level of @top. */
+int
+set_top_section_level (int level)
+{
+ int i, result = -1;
+
+ for (i = 0; section_alist[i].name; i++)
+ if (strcmp (section_alist[i].name, "top") == 0)
+ {
+ result = section_alist[i].level;
+ section_alist[i].level = level;
+ break;
+ }
+ return result;
+}
+
+
+/* return the index of the given sectioning command in section_alist */
+static int
+search_sectioning (char *text)
+{
+ int i;
+ char *t;
+
+ /* ignore the optional command prefix */
+ if (text[0] == COMMAND_PREFIX)
+ text++;
+
+ for (i = 0; (t = section_alist[i].name); i++)
+ {
+ if (strcmp (t, text) == 0)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* Return an integer which identifies the type of section present in
+ TEXT -- 1 for @top, 2 for chapters, ..., 5 for subsubsections (as
+ specified in section_alist). We take into account any @lowersections
+ and @raisesections. If SECNAME is non-NULL, also return the
+ corresponding section name. */
+int
+what_section (char *text, char **secname)
+{
+ int index, j;
+ char *temp;
+ int return_val;
+
+ find_section_command:
+ for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
+ if (text[j] != COMMAND_PREFIX)
+ return -1;
+
+ text = text + j + 1;
+
+ /* We skip @c, @comment, and @?index commands. */
+ if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
+ (text[0] == 'c' && cr_or_whitespace (text[1])) ||
+ (strcmp (text + 1, "index") == 0))
+ {
+ while (*text++ != '\n');
+ goto find_section_command;
+ }
+
+ /* Handle italicized sectioning commands. */
+ if (*text == 'i')
+ text++;
+
+ for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
+
+ temp = xmalloc (1 + j);
+ strncpy (temp, text, j);
+ temp[j] = 0;
+
+ index = search_sectioning (temp);
+ free (temp);
+ if (index >= 0)
+ {
+ return_val = section_alist[index].level + section_alist_offset;
+ if (return_val < 0)
+ return_val = 0;
+ else if (return_val > 5)
+ return_val = 5;
+
+ if (secname)
+ {
+ int i;
+ int alist_size = sizeof (section_alist) / sizeof(section_alist_type);
+ /* Find location of offset sectioning entry, but don't go off
+ either end of the array. */
+ int index_offset = MAX (index - section_alist_offset, 0);
+ index_offset = MIN (index_offset, alist_size - 1);
+
+ /* Also make sure we don't go into the next "group" of
+ sectioning changes, e.g., change from an @appendix to an
+ @heading or some such. */
+#define SIGN(expr) ((expr) < 0 ? -1 : 1)
+ for (i = index; i != index_offset; i -= SIGN (section_alist_offset))
+ {
+ /* As it happens, each group has unique .num/.toc values. */
+ if (section_alist[i].num != section_alist[index_offset].num
+ || section_alist[i].toc != section_alist[index_offset].toc)
+ break;
+ }
+ *secname = section_alist[i].name;
+ }
+ return return_val;
+ }
+ return -1;
+}
+
+/* Returns current top level division (ie. chapter, unnumbered) number.
+ - For chapters, returns the number.
+ - For unnumbered sections, returns empty string.
+ - For appendices, returns A, B, etc. */
+char *
+current_chapter_number (void)
+{
+ if (enum_marker == UNNUMBERED_MAGIC)
+ return xstrdup ("");
+ else if (enum_marker == APPENDIX_MAGIC)
+ {
+ char s[1];
+ sprintf (s, "%c", numbers[0] + 64);
+ return xstrdup (s);
+ }
+ else
+ {
+ char s[5];
+ sprintf (s, "%d", numbers[0]);
+ return xstrdup (s);
+ }
+}
+
+/* Returns number of the last sectioning command used. */
+char *
+current_sectioning_number (void)
+{
+ if (enum_marker == UNNUMBERED_MAGIC || !number_sections)
+ return xstrdup ("");
+ else
+ return xstrdup (last_sectioning_number);
+}
+
+/* Returns arguments of the last sectioning command used. */
+char *
+current_sectioning_name (void)
+{
+ return xstrdup (last_sectioning_title);
+}
+
+/* insert_and_underscore, sectioning_underscore and sectioning_html call this. */
+
+static char *
+handle_enum_increment (int level, int index)
+{
+ /* Here is how TeX handles enumeration:
+ - Anything starting with @unnumbered is not enumerated.
+ - @majorheading and the like are not enumberated. */
+ int i;
+
+ /* First constraint above. */
+ if (enum_marker == UNNUMBERED_MAGIC && level == 0)
+ return xstrdup ("");
+
+ /* Second constraint. */
+ if (section_alist[index].num == ENUM_SECT_NO)
+ return xstrdup ("");
+
+ /* reset all counters which are one level deeper */
+ for (i = level; i < 3; i++)
+ numbers [i + 1] = 0;
+
+ numbers[level]++;
+ if (section_alist[index].num == ENUM_SECT_NO || enum_marker == UNNUMBERED_MAGIC
+ || !number_sections)
+ return xstrdup ("");
+ else
+ return xstrdup (get_sectioning_number (level, section_alist[index].num));
+}
+
+
+void
+sectioning_underscore (char *cmd)
+{
+ char *temp, *secname;
+ int level;
+
+ /* If we're not indenting the first paragraph, we shall make it behave
+ like @noindent is called directly after the section heading. */
+ if (! do_first_par_indent)
+ cm_noindent ();
+
+ temp = xmalloc (2 + strlen (cmd));
+ temp[0] = COMMAND_PREFIX;
+ strcpy (&temp[1], cmd);
+ level = what_section (temp, &secname);
+ level -= 2;
+ if (level < 0)
+ level = 0;
+ free (temp);
+
+ /* If the argument to @top is empty, we try using the one from @settitle.
+ Warn if both are unusable. */
+ if (STREQ (command, "top"))
+ {
+ int save_input_text_offset = input_text_offset;
+
+ get_rest_of_line (0, &temp);
+
+ /* Due to get_rest_of_line ... */
+ line_number--;
+
+ if (strlen (temp) == 0 && (!title || strlen (title) == 0))
+ warning ("Must specify a title with least one of @settitle or @top");
+
+ input_text_offset = save_input_text_offset;
+ }
+
+ if (xml)
+ {
+ /* If the section appears in the toc, it means it's a real section
+ unlike majorheading, chapheading etc. */
+ if (section_alist[search_sectioning (cmd)].toc == TOC_YES)
+ {
+ xml_close_sections (level);
+ /* Mark the beginning of the section
+ If the next command is printindex, we will remove
+ the section and put an Index instead */
+ flush_output ();
+ xml_last_section_output_position = output_paragraph_offset;
+
+ get_rest_of_line (0, &temp);
+
+ /* Use @settitle value if @top parameter is empty. */
+ if (STREQ (command, "top") && strlen(temp) == 0)
+ temp = xstrdup (title ? title : "");
+
+ /* Docbook does not support @unnumbered at all. So we provide numbers
+ that other formats use. @appendix seems to be fine though, so we let
+ Docbook handle that as usual. */
+ if (docbook && enum_marker != APPENDIX_MAGIC)
+ {
+ if (section_alist[search_sectioning (cmd)].num == ENUM_SECT_NO
+ && section_alist[search_sectioning (cmd)].toc == TOC_YES)
+ xml_insert_element_with_attribute (xml_element (secname),
+ START, "label=\"%s\" xreflabel=\"%s\"",
+ handle_enum_increment (level, search_sectioning (cmd)),
+ text_expansion (temp));
+ else
+ xml_insert_element_with_attribute (xml_element (secname),
+ START, "label=\"%s\"",
+ handle_enum_increment (level, search_sectioning (cmd)));
+ }
+ else
+ xml_insert_element (xml_element (secname), START);
+
+ xml_insert_element (TITLE, START);
+ xml_open_section (level, secname);
+ execute_string ("%s", temp);
+ xml_insert_element (TITLE, END);
+
+ free (temp);
+ }
+ else
+ {
+ if (docbook)
+ {
+ if (level > 0)
+ xml_insert_element_with_attribute (xml_element (secname), START,
+ "renderas=\"sect%d\"", level);
+ else
+ xml_insert_element_with_attribute (xml_element (secname), START,
+ "renderas=\"other\"");
+ }
+ else
+ xml_insert_element (xml_element (secname), START);
+
+ get_rest_of_line (0, &temp);
+ execute_string ("%s", temp);
+ free (temp);
+
+ xml_insert_element (xml_element (secname), END);
+ }
+ }
+ else if (html)
+ sectioning_html (level, secname);
+ else
+ insert_and_underscore (level, secname);
+}
+
+
+/* Insert the text following input_text_offset up to the end of the line
+ in a new, separate paragraph. Directly underneath it, insert a
+ line of WITH_CHAR, the same length of the inserted text. */
+void
+insert_and_underscore (int level, char *cmd)
+{
+ int i, len;
+ int index;
+ int old_no_indent;
+ unsigned char *starting_pos, *ending_pos;
+ char *temp;
+ char with_char = scoring_characters[level];
+
+ close_paragraph ();
+ filling_enabled = indented_fill = 0;
+ old_no_indent = no_indent;
+ no_indent = 1;
+
+ if (macro_expansion_output_stream && !executing_string)
+ append_to_expansion_output (input_text_offset + 1);
+
+ get_rest_of_line (0, &temp);
+
+ /* Use @settitle value if @top parameter is empty. */
+ if (STREQ (command, "top") && strlen(temp) == 0)
+ temp = xstrdup (title ? title : "");
+
+ starting_pos = output_paragraph + output_paragraph_offset;
+
+ /* Poor man's cache for section title. */
+ if (strlen (last_sectioning_title))
+ free (last_sectioning_title);
+ last_sectioning_title = xstrdup (temp);
+
+ index = search_sectioning (cmd);
+ if (index < 0)
+ {
+ /* should never happen, but a poor guy, named Murphy ... */
+ warning (_("Internal error (search_sectioning) `%s'!"), cmd);
+ return;
+ }
+
+ /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the
+ Info output and in TOC, but only SECTION-NAME in the macro-expanded
+ output. */
+
+ /* Step 1: produce "X.Y" and add it to Info output. */
+ add_word_args ("%s ", handle_enum_increment (level, index));
+
+ /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output. */
+ if (macro_expansion_output_stream && !executing_string)
+ {
+ char *temp1 = xmalloc (2 + strlen (temp));
+ sprintf (temp1, "%s\n", temp);
+ remember_itext (input_text, input_text_offset);
+ me_execute_string (temp1);
+ free (temp1);
+ }
+ else
+ execute_string ("%s\n", temp);
+
+ /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and
+ insert it into the TOC. */
+ ending_pos = output_paragraph + output_paragraph_offset;
+ if (section_alist[index].toc == TOC_YES)
+ toc_add_entry (substring ((char *)starting_pos, (char *)ending_pos - 1),
+ level, current_node, NULL);
+
+ free (temp);
+
+ len = (ending_pos - starting_pos) - 1;
+ for (i = 0; i < len; i++)
+ add_char (with_char);
+ insert ('\n');
+ close_paragraph ();
+ filling_enabled = 1;
+ no_indent = old_no_indent;
+}
+
+/* Insert the text following input_text_offset up to the end of the
+ line as an HTML heading element of the appropriate `level' and
+ tagged as an anchor for the current node.. */
+
+void
+sectioning_html (int level, char *cmd)
+{
+ static int toc_ref_count = 0;
+ int index;
+ int old_no_indent;
+ unsigned char *starting_pos, *ending_pos;
+ char *temp, *toc_anchor = NULL;
+
+ close_paragraph ();
+ filling_enabled = indented_fill = 0;
+ old_no_indent = no_indent;
+ no_indent = 1;
+
+ /* level 0 (chapter) is <h2>, and we go down from there. */
+ add_html_block_elt_args ("<h%d class=\"%s\">", level + 2, cmd);
+
+ /* If we are outside of any node, produce an anchor that
+ the TOC could refer to. */
+ if (!current_node || !*current_node)
+ {
+ static const char a_name[] = "<a name=\"";
+
+ starting_pos = output_paragraph + output_paragraph_offset;
+ add_word_args ("%sTOC%d\">", a_name, toc_ref_count++);
+ toc_anchor = substring ((char *)starting_pos + sizeof (a_name) - 1,
+ (char *)output_paragraph
+ + output_paragraph_offset);
+ /* This must be added after toc_anchor is extracted, since
+ toc_anchor cannot include the closing </a>. For details,
+ see toc.c:toc_add_entry and toc.c:contents_update_html.
+
+ Also, the anchor close must be output before the section name
+ in case the name itself contains an anchor. */
+ add_word ("</a>");
+ }
+ starting_pos = output_paragraph + output_paragraph_offset;
+
+ if (macro_expansion_output_stream && !executing_string)
+ append_to_expansion_output (input_text_offset + 1);
+
+ get_rest_of_line (0, &temp);
+
+ /* Use @settitle value if @top parameter is empty. */
+ if (STREQ (command, "top") && strlen(temp) == 0)
+ temp = xstrdup (title ? title : "");
+
+ index = search_sectioning (cmd);
+ if (index < 0)
+ {
+ /* should never happen, but a poor guy, named Murphy ... */
+ warning (_("Internal error (search_sectioning) \"%s\"!"), cmd);
+ return;
+ }
+
+ /* Produce "X.Y" and add it to HTML output. */
+ {
+ char *title_number = handle_enum_increment (level, index);
+ if (strlen (title_number) > 0)
+ add_word_args ("%s ", title_number);
+ }
+
+ /* add the section name to both HTML and macro-expanded output. */
+ if (macro_expansion_output_stream && !executing_string)
+ {
+ remember_itext (input_text, input_text_offset);
+ me_execute_string (temp);
+ write_region_to_macro_output ("\n", 0, 1);
+ }
+ else
+ execute_string ("%s", temp);
+
+ ending_pos = output_paragraph + output_paragraph_offset;
+
+ /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it
+ into the TOC. */
+ if (section_alist[index].toc == TOC_YES)
+ toc_add_entry (substring ((char *)starting_pos, (char *)ending_pos),
+ level, current_node, toc_anchor);
+
+ free (temp);
+
+ if (outstanding_node)
+ outstanding_node = 0;
+
+ add_word_args ("</h%d>", level + 2);
+ close_paragraph();
+ filling_enabled = 1;
+ no_indent = old_no_indent;
+}
+
+
+/* Shift the meaning of @section to @chapter. */
+void
+cm_raisesections (void)
+{
+ discard_until ("\n");
+ section_alist_offset--;
+}
+
+/* Shift the meaning of @chapter to @section. */
+void
+cm_lowersections (void)
+{
+ discard_until ("\n");
+ section_alist_offset++;
+}
+
+/* The command still works, but prints a warning message in addition. */
+void
+cm_ideprecated (int arg, int start, int end)
+{
+ warning (_("%c%s is obsolete; use %c%s instead"),
+ COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1);
+ sectioning_underscore (command + 1);
+}
+
+
+/* Treat this just like @unnumbered. The only difference is
+ in node defaulting. */
+void
+cm_top (void)
+{
+ /* It is an error to have more than one @top. */
+ if (top_node_seen && strcmp (current_node, "Top") != 0)
+ {
+ TAG_ENTRY *tag = tag_table;
+
+ line_error (_("Node with %ctop as a section already exists"),
+ COMMAND_PREFIX);
+
+ while (tag)
+ {
+ if (tag->flags & TAG_FLAG_IS_TOP)
+ {
+ file_line_error (tag->filename, tag->line_no,
+ _("Here is the %ctop node"), COMMAND_PREFIX);
+ return;
+ }
+ tag = tag->next_ent;
+ }
+ }
+ else
+ {
+ top_node_seen = 1;
+
+ /* It is an error to use @top before using @node. */
+ if (!tag_table)
+ {
+ char *top_name;
+
+ get_rest_of_line (0, &top_name);
+ line_error (_("%ctop used before %cnode, defaulting to %s"),
+ COMMAND_PREFIX, COMMAND_PREFIX, top_name);
+ execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
+ free (top_name);
+ return;
+ }
+
+ cm_unnumbered ();
+
+ /* The most recently defined node is the top node. */
+ tag_table->flags |= TAG_FLAG_IS_TOP;
+
+ /* Now set the logical hierarchical level of the Top node. */
+ {
+ int orig_offset = input_text_offset;
+
+ input_text_offset = search_forward (node_search_string, orig_offset);
+
+ if (input_text_offset > 0)
+ {
+ int this_section;
+
+ /* We have encountered a non-top node, so mark that one exists. */
+ non_top_node_seen = 1;
+
+ /* Move to the end of this line, and find out what the
+ sectioning command is here. */
+ while (input_text[input_text_offset] != '\n')
+ input_text_offset++;
+
+ if (input_text_offset < input_text_length)
+ input_text_offset++;
+
+ this_section = what_section (input_text + input_text_offset,
+ NULL);
+
+ /* If we found a sectioning command, then give the top section
+ a level of this section - 1. */
+ if (this_section != -1)
+ set_top_section_level (this_section - 1);
+ }
+ input_text_offset = orig_offset;
+ }
+ }
+}
+
+/* The remainder of the text on this line is a chapter heading. */
+void
+cm_chapter (void)
+{
+ enum_marker = 0;
+ sectioning_underscore ("chapter");
+}
+
+/* The remainder of the text on this line is a section heading. */
+void
+cm_section (void)
+{
+ sectioning_underscore ("section");
+}
+
+/* The remainder of the text on this line is a subsection heading. */
+void
+cm_subsection (void)
+{
+ sectioning_underscore ("subsection");
+}
+
+/* The remainder of the text on this line is a subsubsection heading. */
+void
+cm_subsubsection (void)
+{
+ sectioning_underscore ("subsubsection");
+}
+
+/* The remainder of the text on this line is an unnumbered heading. */
+void
+cm_unnumbered (void)
+{
+ enum_marker = UNNUMBERED_MAGIC;
+ sectioning_underscore ("unnumbered");
+}
+
+/* The remainder of the text on this line is an unnumbered section heading. */
+void
+cm_unnumberedsec (void)
+{
+ sectioning_underscore ("unnumberedsec");
+}
+
+/* The remainder of the text on this line is an unnumbered
+ subsection heading. */
+void
+cm_unnumberedsubsec (void)
+{
+ sectioning_underscore ("unnumberedsubsec");
+}
+
+/* The remainder of the text on this line is an unnumbered
+ subsubsection heading. */
+void
+cm_unnumberedsubsubsec (void)
+{
+ sectioning_underscore ("unnumberedsubsubsec");
+}
+
+/* The remainder of the text on this line is an appendix heading. */
+void
+cm_appendix (void)
+{
+ /* Reset top level number so we start from Appendix A */
+ if (enum_marker != APPENDIX_MAGIC)
+ numbers [0] = 0;
+ enum_marker = APPENDIX_MAGIC;
+ sectioning_underscore ("appendix");
+}
+
+/* The remainder of the text on this line is an appendix section heading. */
+void
+cm_appendixsec (void)
+{
+ sectioning_underscore ("appendixsec");
+}
+
+/* The remainder of the text on this line is an appendix subsection heading. */
+void
+cm_appendixsubsec (void)
+{
+ sectioning_underscore ("appendixsubsec");
+}
+
+/* The remainder of the text on this line is an appendix
+ subsubsection heading. */
+void
+cm_appendixsubsubsec (void)
+{
+ sectioning_underscore ("appendixsubsubsec");
+}
+
+/* Compatibility functions substitute for chapter, section, etc. */
+void
+cm_majorheading (void)
+{
+ sectioning_underscore ("majorheading");
+}
+
+void
+cm_chapheading (void)
+{
+ sectioning_underscore ("chapheading");
+}
+
+void
+cm_heading (void)
+{
+ sectioning_underscore ("heading");
+}
+
+void
+cm_subheading (void)
+{
+ sectioning_underscore ("subheading");
+}
+
+void
+cm_subsubheading (void)
+{
+ sectioning_underscore ("subsubheading");
+}
diff --git a/makeinfo/sectioning.h b/makeinfo/sectioning.h
new file mode 100644
index 0000000..30b4451
--- /dev/null
+++ b/makeinfo/sectioning.h
@@ -0,0 +1,102 @@
+/* sectioning.h -- all related stuff @chapter, @section... @contents
+ $Id: sectioning.h,v 1.8 2007/07/01 21:20:33 karl Exp $
+
+ Copyright (C) 1999, 2003, 2007 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 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 <http://www.gnu.org/licenses/>.
+
+ Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#ifndef SECTIONING_H
+#define SECTIONING_H
+
+/* Sectioning. */
+
+/* Level 4. */
+extern void cm_chapter (void),
+ cm_unnumbered (void),
+ cm_appendix (void),
+ cm_top (void);
+
+/* Level 3. */
+extern void cm_section (void),
+ cm_unnumberedsec (void),
+ cm_appendixsec (void);
+
+/* Level 2. */
+extern void cm_subsection (void),
+ cm_unnumberedsubsec (void),
+ cm_appendixsubsec (void);
+
+/* Level 1. */
+extern void cm_subsubsection (void),
+ cm_unnumberedsubsubsec (void),
+ cm_appendixsubsubsec (void);
+
+/* Headings. */
+extern void cm_heading (void),
+ cm_chapheading (void),
+ cm_subheading (void),
+ cm_subsubheading (void),
+ cm_majorheading (void);
+
+extern void cm_raisesections (void),
+ cm_lowersections (void),
+ cm_ideprecated (int arg, int start, int end);
+
+extern void
+ sectioning_underscore (char *cmd),
+ insert_and_underscore (int level, char *cmd);
+
+/* needed in node.c */
+extern int set_top_section_level (int level);
+
+extern void sectioning_html (int level, char *cmd);
+extern int what_section (char *text, char **secname);
+extern char *current_chapter_number (void),
+ *current_sectioning_number (void),
+ *current_sectioning_name (void);
+
+/* The argument of @settitle, used for HTML. */
+extern char *title;
+
+
+/* Here is a structure which associates sectioning commands with
+ an integer that reflects the depth of the current section. */
+typedef struct
+{
+ char *name;
+ int level; /* I can't replace the levels with defines
+ because it is changed during run */
+ int num; /* ENUM_SECT_NO means no enumeration...
+ ENUM_SECT_YES means enumerated version
+ ENUM_SECT_APP appendix (Character enumerated
+ at first position */
+ int toc; /* TOC_NO means do not enter in toc;
+ TOC_YES means enter it in toc */
+} section_alist_type;
+
+extern section_alist_type section_alist[];
+
+/* enumerate sections */
+#define ENUM_SECT_NO 0
+#define ENUM_SECT_YES 1
+#define ENUM_SECT_APP 2
+
+/* make entries into toc no/yes */
+#define TOC_NO 0
+#define TOC_YES 1
+
+
+#endif /* not SECTIONING_H */
diff --git a/makeinfo/tests/Makefile.am b/makeinfo/tests/Makefile.am
new file mode 100644
index 0000000..353a822
--- /dev/null
+++ b/makeinfo/tests/Makefile.am
@@ -0,0 +1,40 @@
+# $Id: Makefile.am,v 1.7 2006/07/10 23:07:12 karl Exp $
+# Makefile.am for texinfo/tests/makeinfo.
+#
+# This file is free software; as a special exception the author 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.
+
+TESTS = accent accentenc \
+ cond \
+ copying \
+ defxcond \
+ emph-option \
+ html-docdesc html-extrali html-min html-manuals html-para html-title \
+ html-top \
+ include-value \
+ macro-at menu-whitespace \
+ no-headers \
+ node-expand node-value node-whitespace \
+ quote-args \
+ top \
+ twofiles
+
+noinst_SCRIPTS = $(TESTS)
+
+EXTRA_DIST = $(noinst_SCRIPTS) \
+ accent.txi accentenc.txi accent-text.txi \
+ cond.txi copying.txi \
+ defxcond.txi \
+ emph-option.txi \
+ html-docdesc.txi html-extrali.txi html-min.txi html-para.txi html-title.txi \
+ html-top.txi \
+ include-value.txi incl-incl.txi \
+ macro-at.txi menu-whitespace.txi \
+ node-expand.txi node-value.txi node-whitespace.txi \
+ quote-args.txi \
+ top.txi # top2.txi
diff --git a/makeinfo/tests/Makefile.in b/makeinfo/tests/Makefile.in
new file mode 100644
index 0000000..0467b8f
--- /dev/null
+++ b/makeinfo/tests/Makefile.in
@@ -0,0 +1,672 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# $Id: Makefile.am,v 1.7 2006/07/10 23:07:12 karl Exp $
+# Makefile.am for texinfo/tests/makeinfo.
+#
+# This file is free software; as a special exception the author 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.
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = makeinfo/tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/gnulib/m4/alloca.m4 \
+ $(top_srcdir)/gnulib/m4/argz.m4 \
+ $(top_srcdir)/gnulib/m4/codeset.m4 \
+ $(top_srcdir)/gnulib/m4/eealloc.m4 \
+ $(top_srcdir)/gnulib/m4/environ.m4 \
+ $(top_srcdir)/gnulib/m4/error.m4 \
+ $(top_srcdir)/gnulib/m4/exitfail.m4 \
+ $(top_srcdir)/gnulib/m4/extensions.m4 \
+ $(top_srcdir)/gnulib/m4/getopt.m4 \
+ $(top_srcdir)/gnulib/m4/gettext.m4 \
+ $(top_srcdir)/gnulib/m4/gettimeofday.m4 \
+ $(top_srcdir)/gnulib/m4/glibc21.m4 \
+ $(top_srcdir)/gnulib/m4/gnulib-common.m4 \
+ $(top_srcdir)/gnulib/m4/gnulib-comp.m4 \
+ $(top_srcdir)/gnulib/m4/iconv.m4 \
+ $(top_srcdir)/gnulib/m4/include_next.m4 \
+ $(top_srcdir)/gnulib/m4/inline.m4 \
+ $(top_srcdir)/gnulib/m4/intlmacosx.m4 \
+ $(top_srcdir)/gnulib/m4/lib-ld.m4 \
+ $(top_srcdir)/gnulib/m4/lib-link.m4 \
+ $(top_srcdir)/gnulib/m4/lib-prefix.m4 \
+ $(top_srcdir)/gnulib/m4/localcharset.m4 \
+ $(top_srcdir)/gnulib/m4/longlong.m4 \
+ $(top_srcdir)/gnulib/m4/malloc.m4 \
+ $(top_srcdir)/gnulib/m4/malloca.m4 \
+ $(top_srcdir)/gnulib/m4/mbchar.m4 \
+ $(top_srcdir)/gnulib/m4/mbiter.m4 \
+ $(top_srcdir)/gnulib/m4/mbrtowc.m4 \
+ $(top_srcdir)/gnulib/m4/mbscasecmp.m4 \
+ $(top_srcdir)/gnulib/m4/mbschr.m4 \
+ $(top_srcdir)/gnulib/m4/mbslen.m4 \
+ $(top_srcdir)/gnulib/m4/mbsncasecmp.m4 \
+ $(top_srcdir)/gnulib/m4/mbsstr.m4 \
+ $(top_srcdir)/gnulib/m4/mbstate_t.m4 \
+ $(top_srcdir)/gnulib/m4/mbswidth.m4 \
+ $(top_srcdir)/gnulib/m4/memchr.m4 \
+ $(top_srcdir)/gnulib/m4/memcmp.m4 \
+ $(top_srcdir)/gnulib/m4/memcpy.m4 \
+ $(top_srcdir)/gnulib/m4/memmem.m4 \
+ $(top_srcdir)/gnulib/m4/memmove.m4 \
+ $(top_srcdir)/gnulib/m4/mempcpy.m4 \
+ $(top_srcdir)/gnulib/m4/mkstemp.m4 \
+ $(top_srcdir)/gnulib/m4/nls.m4 \
+ $(top_srcdir)/gnulib/m4/onceonly.m4 \
+ $(top_srcdir)/gnulib/m4/po.m4 \
+ $(top_srcdir)/gnulib/m4/progtest.m4 \
+ $(top_srcdir)/gnulib/m4/setenv.m4 \
+ $(top_srcdir)/gnulib/m4/stdbool.m4 \
+ $(top_srcdir)/gnulib/m4/stdint.m4 \
+ $(top_srcdir)/gnulib/m4/stdlib_h.m4 \
+ $(top_srcdir)/gnulib/m4/stpcpy.m4 \
+ $(top_srcdir)/gnulib/m4/strdup.m4 \
+ $(top_srcdir)/gnulib/m4/strerror.m4 \
+ $(top_srcdir)/gnulib/m4/string_h.m4 \
+ $(top_srcdir)/gnulib/m4/strndup.m4 \
+ $(top_srcdir)/gnulib/m4/strnlen.m4 \
+ $(top_srcdir)/gnulib/m4/sys_stat_h.m4 \
+ $(top_srcdir)/gnulib/m4/sys_time_h.m4 \
+ $(top_srcdir)/gnulib/m4/tempname.m4 \
+ $(top_srcdir)/gnulib/m4/unistd_h.m4 \
+ $(top_srcdir)/gnulib/m4/wchar.m4 \
+ $(top_srcdir)/gnulib/m4/wchar_t.m4 \
+ $(top_srcdir)/gnulib/m4/wctype.m4 \
+ $(top_srcdir)/gnulib/m4/wcwidth.m4 \
+ $(top_srcdir)/gnulib/m4/wint_t.m4 \
+ $(top_srcdir)/gnulib/m4/xalloc.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+SCRIPTS = $(noinst_SCRIPTS)
+SOURCES =
+DIST_SOURCES =
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALLOCA_H = @ALLOCA_H@
+AMTAR = @AMTAR@
+ARGZ_H = @ARGZ_H@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
+BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
+BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
+BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
+BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GETOPT_H = @GETOPT_H@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GLIBC21 = @GLIBC21@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@
+GNULIB_CHOWN = @GNULIB_CHOWN@
+GNULIB_DUP2 = @GNULIB_DUP2@
+GNULIB_ENVIRON = @GNULIB_ENVIRON@
+GNULIB_FCHDIR = @GNULIB_FCHDIR@
+GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@
+GNULIB_GETCWD = @GNULIB_GETCWD@
+GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@
+GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@
+GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@
+GNULIB_LCHOWN = @GNULIB_LCHOWN@
+GNULIB_LSEEK = @GNULIB_LSEEK@
+GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@
+GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@
+GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@
+GNULIB_MBSCHR = @GNULIB_MBSCHR@
+GNULIB_MBSCSPN = @GNULIB_MBSCSPN@
+GNULIB_MBSLEN = @GNULIB_MBSLEN@
+GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@
+GNULIB_MBSNLEN = @GNULIB_MBSNLEN@
+GNULIB_MBSPBRK = @GNULIB_MBSPBRK@
+GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@
+GNULIB_MBSRCHR = @GNULIB_MBSRCHR@
+GNULIB_MBSSEP = @GNULIB_MBSSEP@
+GNULIB_MBSSPN = @GNULIB_MBSSPN@
+GNULIB_MBSSTR = @GNULIB_MBSSTR@
+GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@
+GNULIB_MEMMEM = @GNULIB_MEMMEM@
+GNULIB_MEMPCPY = @GNULIB_MEMPCPY@
+GNULIB_MEMRCHR = @GNULIB_MEMRCHR@
+GNULIB_MKDTEMP = @GNULIB_MKDTEMP@
+GNULIB_MKSTEMP = @GNULIB_MKSTEMP@
+GNULIB_PUTENV = @GNULIB_PUTENV@
+GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@
+GNULIB_READLINK = @GNULIB_READLINK@
+GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@
+GNULIB_RPMATCH = @GNULIB_RPMATCH@
+GNULIB_SETENV = @GNULIB_SETENV@
+GNULIB_SLEEP = @GNULIB_SLEEP@
+GNULIB_STPCPY = @GNULIB_STPCPY@
+GNULIB_STPNCPY = @GNULIB_STPNCPY@
+GNULIB_STRCASESTR = @GNULIB_STRCASESTR@
+GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@
+GNULIB_STRDUP = @GNULIB_STRDUP@
+GNULIB_STRERROR = @GNULIB_STRERROR@
+GNULIB_STRNDUP = @GNULIB_STRNDUP@
+GNULIB_STRNLEN = @GNULIB_STRNLEN@
+GNULIB_STRPBRK = @GNULIB_STRPBRK@
+GNULIB_STRSEP = @GNULIB_STRSEP@
+GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@
+GNULIB_STRSTR = @GNULIB_STRSTR@
+GNULIB_STRTOD = @GNULIB_STRTOD@
+GNULIB_STRTOK_R = @GNULIB_STRTOK_R@
+GNULIB_UNSETENV = @GNULIB_UNSETENV@
+GNULIB_WCWIDTH = @GNULIB_WCWIDTH@
+GREP = @GREP@
+HAVE_CALLOC_POSIX = @HAVE_CALLOC_POSIX@
+HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@
+HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@
+HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@
+HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@
+HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@
+HAVE_DECL_STRERROR = @HAVE_DECL_STRERROR@
+HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@
+HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@
+HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@
+HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@
+HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@
+HAVE_DUP2 = @HAVE_DUP2@
+HAVE_FTRUNCATE = @HAVE_FTRUNCATE@
+HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@
+HAVE_GETSUBOPT = @HAVE_GETSUBOPT@
+HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
+HAVE_ISWCNTRL = @HAVE_ISWCNTRL@
+HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@
+HAVE_LSTAT = @HAVE_LSTAT@
+HAVE_MALLOC_POSIX = @HAVE_MALLOC_POSIX@
+HAVE_MEMPCPY = @HAVE_MEMPCPY@
+HAVE_MKDTEMP = @HAVE_MKDTEMP@
+HAVE_OS_H = @HAVE_OS_H@
+HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@
+HAVE_READLINK = @HAVE_READLINK@
+HAVE_REALLOC_POSIX = @HAVE_REALLOC_POSIX@
+HAVE_RPMATCH = @HAVE_RPMATCH@
+HAVE_SETENV = @HAVE_SETENV@
+HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
+HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
+HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
+HAVE_SLEEP = @HAVE_SLEEP@
+HAVE_STDINT_H = @HAVE_STDINT_H@
+HAVE_STPCPY = @HAVE_STPCPY@
+HAVE_STPNCPY = @HAVE_STPNCPY@
+HAVE_STRCASESTR = @HAVE_STRCASESTR@
+HAVE_STRCHRNUL = @HAVE_STRCHRNUL@
+HAVE_STRNDUP = @HAVE_STRNDUP@
+HAVE_STRPBRK = @HAVE_STRPBRK@
+HAVE_STRSEP = @HAVE_STRSEP@
+HAVE_STRTOD = @HAVE_STRTOD@
+HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@
+HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@
+HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@
+HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@
+HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@
+HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
+HAVE_UNISTD_H = @HAVE_UNISTD_H@
+HAVE_UNSETENV = @HAVE_UNSETENV@
+HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@
+HAVE_WCHAR_H = @HAVE_WCHAR_H@
+HAVE_WCTYPE_H = @HAVE_WCTYPE_H@
+HAVE_WINT_T = @HAVE_WINT_T@
+HAVE__BOOL = @HAVE__BOOL@
+HELP2MAN = @HELP2MAN@
+HEVEA = @HEVEA@
+INCLUDE_NEXT = @INCLUDE_NEXT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBGNU_LIBDEPS = @LIBGNU_LIBDEPS@
+LIBGNU_LTLIBDEPS = @LIBGNU_LTLIBDEPS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEXT_STDINT_H = @NEXT_STDINT_H@
+NEXT_STDLIB_H = @NEXT_STDLIB_H@
+NEXT_STRING_H = @NEXT_STRING_H@
+NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@
+NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@
+NEXT_UNISTD_H = @NEXT_UNISTD_H@
+NEXT_WCHAR_H = @NEXT_WCHAR_H@
+NEXT_WCTYPE_H = @NEXT_WCTYPE_H@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
+RANLIB = @RANLIB@
+REPLACE_CHOWN = @REPLACE_CHOWN@
+REPLACE_FCHDIR = @REPLACE_FCHDIR@
+REPLACE_GETCWD = @REPLACE_GETCWD@
+REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@
+REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@
+REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@
+REPLACE_LCHOWN = @REPLACE_LCHOWN@
+REPLACE_LSEEK = @REPLACE_LSEEK@
+REPLACE_MEMMEM = @REPLACE_MEMMEM@
+REPLACE_MKDIR = @REPLACE_MKDIR@
+REPLACE_MKSTEMP = @REPLACE_MKSTEMP@
+REPLACE_PUTENV = @REPLACE_PUTENV@
+REPLACE_STRCASESTR = @REPLACE_STRCASESTR@
+REPLACE_STRERROR = @REPLACE_STRERROR@
+REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@
+REPLACE_STRSTR = @REPLACE_STRSTR@
+REPLACE_STRTOD = @REPLACE_STRTOD@
+REPLACE_WCWIDTH = @REPLACE_WCWIDTH@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
+SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
+STDBOOL_H = @STDBOOL_H@
+STDINT_H = @STDINT_H@
+STRIP = @STRIP@
+SYS_STAT_H = @SYS_STAT_H@
+SYS_TIME_H = @SYS_TIME_H@
+TERMLIBS = @TERMLIBS@
+TEX = @TEX@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VOID_UNSETENV = @VOID_UNSETENV@
+WCHAR_H = @WCHAR_H@
+WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
+WCTYPE_H = @WCTYPE_H@
+WINT_T_SUFFIX = @WINT_T_SUFFIX@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+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@
+gl_LIBOBJS = @gl_LIBOBJS@
+gl_LTLIBOBJS = @gl_LTLIBOBJS@
+gltests_LIBOBJS = @gltests_LIBOBJS@
+gltests_LTLIBOBJS = @gltests_LTLIBOBJS@
+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@
+native_tools = @native_tools@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+TESTS = accent accentenc \
+ cond \
+ copying \
+ defxcond \
+ emph-option \
+ html-docdesc html-extrali html-min html-manuals html-para html-title \
+ html-top \
+ include-value \
+ macro-at menu-whitespace \
+ no-headers \
+ node-expand node-value node-whitespace \
+ quote-args \
+ top \
+ twofiles
+
+noinst_SCRIPTS = $(TESTS)
+EXTRA_DIST = $(noinst_SCRIPTS) \
+ accent.txi accentenc.txi accent-text.txi \
+ cond.txi copying.txi \
+ defxcond.txi \
+ emph-option.txi \
+ html-docdesc.txi html-extrali.txi html-min.txi html-para.txi html-title.txi \
+ html-top.txi \
+ include-value.txi incl-incl.txi \
+ macro-at.txi menu-whitespace.txi \
+ node-expand.txi node-value.txi node-whitespace.txi \
+ quote-args.txi \
+ top.txi # top2.txi
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu makeinfo/tests/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu makeinfo/tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; ws='[ ]'; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *$$ws$$tst$$ws*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ echo "XPASS: $$tst"; \
+ ;; \
+ *) \
+ echo "PASS: $$tst"; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *$$ws$$tst$$ws*) \
+ xfail=`expr $$xfail + 1`; \
+ echo "XFAIL: $$tst"; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ echo "FAIL: $$tst"; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ echo "SKIP: $$tst"; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="All $$all tests passed"; \
+ else \
+ banner="All $$all tests behaved as expected ($$xfail expected failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all tests failed"; \
+ else \
+ banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ skipped="($$skip tests were not run)"; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(SCRIPTS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-TESTS check-am clean clean-generic \
+ distclean distclean-generic distdir dvi dvi-am html html-am \
+ info info-am install install-am install-data install-data-am \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-html install-html-am install-info install-info-am \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \
+ uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/makeinfo/tests/accent b/makeinfo/tests/accent
new file mode 100755
index 0000000..16c69e8
--- /dev/null
+++ b/makeinfo/tests/accent
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Test accent output.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+input=`basename $0`.txi
+
+# html.
+houtput=`basename $0`.html
+../makeinfo --html --no-split -I$srcdir $srcdir/$input
+hexit_status=$?
+if test $hexit_status = 0; then
+ grep '&igrave;' $houtput >/dev/null \
+ && grep '&acute;j' $houtput >/dev/null \
+ && grep '&Oslash;' $houtput >/dev/null \
+ && grep '/L' $houtput >/dev/null
+ hexit_status=$?
+fi
+
+# info
+ioutput=`basename $0`.info
+../makeinfo --no-split -I$srcdir $srcdir/$input
+iexit_status=$?
+if test $iexit_status = 0; then
+ grep 'i`' $ioutput >/dev/null \
+ && grep 'i"' $ioutput >/dev/null \
+ && grep '/L' $ioutput >/dev/null
+ iexit_status=$?
+fi
+
+rm -f $houtput $ioutput
+
+exit_status=`expr $hexit_status + $iexit_status`
+exit $exit_status
diff --git a/makeinfo/tests/accent-text.txi b/makeinfo/tests/accent-text.txi
new file mode 100644
index 0000000..4d9ef3c
--- /dev/null
+++ b/makeinfo/tests/accent-text.txi
@@ -0,0 +1,73 @@
+@c args with braces, without braces/following whitespace
+
+should be e`: @`{e} @`e
+
+should be e': @'{e} @'e
+
+should be e^: @^{e} @^e
+
+should be u": @"{u} @"u
+
+should be i`: @`{i} @`i
+
+should be i': @'{i} @'i
+
+should be i^: @^{i} @^i
+
+should be u": @"{u} @"u
+
+should be c,: @,{c} @,c
+
+should be n~: @~{n} @~n
+
+should be e=: @={e} @=e
+
+should be e@w{'}': @H{e} @H e
+
+should be e.: @dotaccent{e} @dotaccent e
+
+should be e*: @ringaccent{e} @ringaccent e
+
+should be ee[: @tieaccent{ee}
+
+should be e(: @u{e} @u e
+
+should be e_: @ubaraccent{e} @ubaraccent e
+
+should be .e: @udotaccent{e} @udotaccent e
+
+should be e<: @v{e} @v e
+
+upside down: @questiondown{} @exclamdown{}
+
+A-with-circle: @aa{},@AA{}
+
+AE, OE ligatures: @ae{} @AE{} @oe{} @OE{}
+
+dotless i, j: @dotless{i} @dotless{j}
+
+Polish suppressed-L: @l{} @L{}
+
+O-with-slash: @o{} @O{}
+
+es-zet or sharp S: @ss{}
+
+pounds sterling: @pounds{}
+
+@c arg is command -- @dotless{i} is special-cased for HTML
+should be dotless i`: @`{@dotless{i}}
+
+should be dotless i': @'{@dotless{i}}
+
+should be dotless i^: @^{@dotless{i}}
+
+should be dotless i": @"{@dotless{i}}
+
+@c arg is command -- @dotless{j}
+should be dotless j`: @`{@dotless{j}}
+
+should be dotless j': @'{@dotless{j}}
+
+should be dotless j^: @^{@dotless{j}}
+
+should be dotless j": @"{@dotless{j}}
diff --git a/makeinfo/tests/accent.txi b/makeinfo/tests/accent.txi
new file mode 100644
index 0000000..3c52a90
--- /dev/null
+++ b/makeinfo/tests/accent.txi
@@ -0,0 +1,10 @@
+\input texinfo
+@setfilename accent.info
+@settitle Accent test
+
+@node Top
+@top Accent test top
+
+@include accent-text.txi
+
+@bye
diff --git a/makeinfo/tests/accentenc b/makeinfo/tests/accentenc
new file mode 100755
index 0000000..be842c3
--- /dev/null
+++ b/makeinfo/tests/accentenc
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Test encoded accent info output.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+input=`basename $0`.txi
+output=`basename $0`.info
+
+# we expect two invalid encoded characters:
+# accent-text.txi:45: warning: invalid encoded character `#156'.
+# accent-text.txi:45: warning: invalid encoded character `#140'.
+# These are oe and OE, and they are not supported in Latin 1.
+../makeinfo --enable-encoding --no-split -I$srcdir $srcdir/$input 2>/dev/null
+exit_status=$?
+if test $exit_status = 0; then
+ grep 'ì' $output >/dev/null \
+ && grep 'ï' $output >/dev/null \
+ && grep '/L' $output >/dev/null
+ exit_status=$?
+fi
+
+rm -f $output
+
+exit $exit_status
diff --git a/makeinfo/tests/accentenc.txi b/makeinfo/tests/accentenc.txi
new file mode 100644
index 0000000..b6e209c
--- /dev/null
+++ b/makeinfo/tests/accentenc.txi
@@ -0,0 +1,11 @@
+\input texinfo
+@setfilename accentenc.info
+@settitle Accent encoding test
+@documentencoding ISO-8859-1
+
+@node Top
+@top Accent encoding test top
+
+@include accent-text.txi
+
+@bye
diff --git a/makeinfo/tests/cond b/makeinfo/tests/cond
new file mode 100755
index 0000000..41f5920
--- /dev/null
+++ b/makeinfo/tests/cond
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Test conditional text.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+
+# Default Info output.
+../makeinfo -o cond.out $srcdir/cond.txi || exit 1
+egrep 'This is (ifnothtml|ifinfo|ifnottex) text' cond.out >/dev/null \
+ || exit 2
+test `fgrep -c ' text.' cond.out` -eq 3 || exit 3
+
+# Default HTML output.
+../makeinfo --no-split --html -o cond.out $srcdir/cond.txi || exit 1
+egrep 'This is (html|ifhtml|ifnotinfo|ifnottex) text' cond.out >/dev/null \
+ || exit 2
+test `fgrep -c ' text.' cond.out` -eq 4 || exit 3
+
+# --ifhtml off, --ifinfo off, --iftex off.
+../makeinfo --no-ifhtml --no-ifinfo --no-iftex -o cond.out $srcdir/cond.txi || exit 1
+egrep 'This is ifnot(html|info|tex) text' cond.out >/dev/null \
+ || exit 2
+test `fgrep -c ' text.' cond.out` -eq 3 || exit 3
+
+# Do we really need to test all the other permutations?
+
+# --ifhtml on, --ifinfo on, --iftex on.
+../makeinfo --ifhtml --ifinfo --iftex -o cond.out $srcdir/cond.txi || exit 1
+egrep 'This is (html|ifhtml|ifinfo|tex|iftex) text' cond.out >/dev/null \
+ || exit 2
+test `fgrep -c ' text.' cond.out` -eq 5 || exit 3
+
+rm -f cond.out cond.info
diff --git a/makeinfo/tests/cond.txi b/makeinfo/tests/cond.txi
new file mode 100644
index 0000000..b602423
--- /dev/null
+++ b/makeinfo/tests/cond.txi
@@ -0,0 +1,40 @@
+\input texinfo
+@setfilename cond.info
+
+@node Top
+
+@html
+This is html text.
+@end html
+
+@ifhtml
+This is ifhtml text.
+@end ifhtml
+
+@ifnothtml
+This is ifnothtml text.
+@end ifnothtml
+
+
+@ifinfo
+This is ifinfo text.
+@end ifinfo
+
+@ifnotinfo
+This is ifnotinfo text.
+@end ifnotinfo
+
+
+@tex
+This is tex text.
+@end tex
+
+@iftex
+This is iftex text.
+@end iftex
+
+@ifnottex
+This is ifnottex text.
+@end ifnottex
+
+@bye
diff --git a/makeinfo/tests/copying b/makeinfo/tests/copying
new file mode 100755
index 0000000..4a033f0
--- /dev/null
+++ b/makeinfo/tests/copying
@@ -0,0 +1,13 @@
+#!/bin/sh
+# $Id: copying,v 1.4 2007/07/01 18:38:52 karl Exp $
+# Test @copying. The configure script for tramp uses this to make sure
+# the makeinfo that is present supports @copying.
+
+unset TEXINFO_OUTPUT LANG LANGUAGE
+LC_ALL=POSIX; export LC_ALL
+: ${srcdir=.}
+
+../makeinfo -o copying.out $srcdir/copying.txi || exit 1
+fgrep 'produced by' copying.out >/dev/null || exit 2
+
+rm -f copying.out
diff --git a/makeinfo/tests/copying.txi b/makeinfo/tests/copying.txi
new file mode 100644
index 0000000..4981170
--- /dev/null
+++ b/makeinfo/tests/copying.txi
@@ -0,0 +1,8 @@
+\input texinfo @c -*-texinfo-*-
+@copying
+Public domain.
+@end copying
+
+@insertcopying
+
+@bye
diff --git a/makeinfo/tests/defxcond b/makeinfo/tests/defxcond
new file mode 100755
index 0000000..e489867
--- /dev/null
+++ b/makeinfo/tests/defxcond
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Test conditionalized @deffnx.
+# Report from: Akim Demaille <akim@epita.fr>, 14 Aug 2003 12:10:37 +0200.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+
+test=defxcond
+
+../makeinfo -Dbar -o $test.out $srcdir/$test.txi || exit 1
+grep ' -- bar:' $test.out >/dev/null || exit 2
+
+../makeinfo -Ubar -o $test.out $srcdir/$test.txi || exit 3
+grep ' -- bar:' $test.out >/dev/null && exit 4
+
+rm -f $test.out
diff --git a/makeinfo/tests/defxcond.txi b/makeinfo/tests/defxcond.txi
new file mode 100644
index 0000000..a7af9e2
--- /dev/null
+++ b/makeinfo/tests/defxcond.txi
@@ -0,0 +1,18 @@
+\input texinfo
+@setfilename defxcond.info
+
+@c set this from the command line.
+@c set bar
+
+@c deffnx inside conditional.
+@deffn foo
+
+@ifset bar
+@deffnx bar
+@end ifset
+
+Documentation.
+
+@end deffn
+
+@bye
diff --git a/makeinfo/tests/emph-option b/makeinfo/tests/emph-option
new file mode 100755
index 0000000..2dfb150
--- /dev/null
+++ b/makeinfo/tests/emph-option
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Test @option inside @emph.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+
+../makeinfo --no-split --html -o emph-option.out $srcdir/emph-option.txi || exit 1
+fgrep '<em>emphasized and referring to <samp></em>' emph-option.out && exit 3
+
+rm -f emph-option.out
diff --git a/makeinfo/tests/emph-option.txi b/makeinfo/tests/emph-option.txi
new file mode 100644
index 0000000..601c9f3
--- /dev/null
+++ b/makeinfo/tests/emph-option.txi
@@ -0,0 +1,8 @@
+\input texinfo
+@setfilename cond.info
+
+@node Top
+
+Foo @emph{emphasized and referring to @option{--some} option}.
+
+@bye
diff --git a/makeinfo/tests/html-docdesc b/makeinfo/tests/html-docdesc
new file mode 100755
index 0000000..59df74e
--- /dev/null
+++ b/makeinfo/tests/html-docdesc
@@ -0,0 +1,12 @@
+#!/bin/sh
+# Test that @documentdescription works.
+
+if ../makeinfo --html --no-split ${srcdir-.}/html-docdesc.txi; then
+ grep 'explicit document description' html-docdesc.html >/dev/null
+ exit_status=$?
+else
+ exit_status=1
+fi
+
+rm -f html-docdesc.html
+exit $exit_status
diff --git a/makeinfo/tests/html-docdesc.txi b/makeinfo/tests/html-docdesc.txi
new file mode 100644
index 0000000..0b328da
--- /dev/null
+++ b/makeinfo/tests/html-docdesc.txi
@@ -0,0 +1,14 @@
+\input texinfo
+@setfilename html-docdesc.info
+@settitle HTML docdesc test
+
+@documentdescription
+This is the explicit document description.
+@end documentdescription
+
+@node Top
+@top Top of HTML docdesc test
+
+This is the top.
+
+@bye
diff --git a/makeinfo/tests/html-extrali b/makeinfo/tests/html-extrali
new file mode 100755
index 0000000..7d0e5f9
--- /dev/null
+++ b/makeinfo/tests/html-extrali
@@ -0,0 +1,14 @@
+#!/bin/sh
+# Test no extra <li> from @menu.
+
+: ${srcdir=.}
+
+li_count=`../makeinfo --no-split --html -o - $srcdir/html-extrali.txi 2>/dev/null \
+| grep -c '<li>'`
+
+if test "$li_count" -ne 1; then
+ echo "$li_count <li>s instead of one." >&2
+ exit 1
+else
+ exit 0
+fi
diff --git a/makeinfo/tests/html-extrali.txi b/makeinfo/tests/html-extrali.txi
new file mode 100644
index 0000000..d0c17e9
--- /dev/null
+++ b/makeinfo/tests/html-extrali.txi
@@ -0,0 +1,11 @@
+\input texinfo
+@setfilename menuli.info
+
+@c extra li generated after <menu>
+@c From: Marius Groeger <mag@sysgo.de>, 13nov98.
+
+@menu
+* entry1::
+@end menu
+
+@bye
diff --git a/makeinfo/tests/html-manuals b/makeinfo/tests/html-manuals
new file mode 100755
index 0000000..2ba24f3
--- /dev/null
+++ b/makeinfo/tests/html-manuals
@@ -0,0 +1,13 @@
+#!/bin/sh
+# $Id: html-manuals,v 1.3 2004/04/11 17:56:47 karl Exp $
+# Test that all the distribution manuals can be converted to HTML.
+
+: ${srcdir=.}
+
+for manual in info.texi info-stnd.texi texinfo.txi; do
+ base=`echo $manual | sed 's/\.te*xi$//'`
+ ../makeinfo --html -I$srcdir/../../doc -I../../doc --no-split \
+ $srcdir/../../doc/$manual -o $base.html \
+ || exit 1
+ rm -f $base.html
+done
diff --git a/makeinfo/tests/html-min b/makeinfo/tests/html-min
new file mode 100755
index 0000000..948071a
--- /dev/null
+++ b/makeinfo/tests/html-min
@@ -0,0 +1,8 @@
+#!/bin/sh
+# Test that a minimal Texinfo file can be converted to HTML.
+
+../makeinfo --html --no-split ${srcdir-.}/html-min.txi
+exit_status=$?
+
+rm -f html-min.html
+exit $exit_status
diff --git a/makeinfo/tests/html-min.txi b/makeinfo/tests/html-min.txi
new file mode 100644
index 0000000..116519e
--- /dev/null
+++ b/makeinfo/tests/html-min.txi
@@ -0,0 +1,12 @@
+\input texinfo
+@setfilename html-min.info
+@settitle HTML min test
+
+@node Top
+@top Top of HTML min test
+
+Top.
+
+Second paragraph.
+
+@bye
diff --git a/makeinfo/tests/html-para b/makeinfo/tests/html-para
new file mode 100755
index 0000000..a9ea2b5
--- /dev/null
+++ b/makeinfo/tests/html-para
@@ -0,0 +1,8 @@
+#!/bin/sh
+# Test that paragraph beginnings in HTML work ok.
+
+../makeinfo --no-split --html ${srcdir-.}/html-para.txi
+exit_status=$?
+
+rm -f html-para.html
+exit $exit_status
diff --git a/makeinfo/tests/html-para.txi b/makeinfo/tests/html-para.txi
new file mode 100644
index 0000000..c6e34fd
--- /dev/null
+++ b/makeinfo/tests/html-para.txi
@@ -0,0 +1,24 @@
+\input texinfo
+@setfilename html-para.info
+@settitle HTML paragraph beginning test
+
+@c <p> is emitted at paragraph beginning, which makes the text
+@c between START and END not exactly what some cm_xxx functions
+@c expect, when they are called by pop_and_call_brace.
+
+@set val @@value@{@}
+@definfoenclose foo,\\,//
+
+@node Top
+@top Top of HTML paragraph test
+
+@value{val} should work at the beginning of a new paragraph.
+
+@dotless{i} dotless should not trigger error messages at the beginning
+of a new paragraph.
+
+@sc{small-caps} should work at the beginning of a new paragraph.
+
+@foo{@@definfoenclose} should work at the beginning of a new paragraph.
+
+@bye
diff --git a/makeinfo/tests/html-title b/makeinfo/tests/html-title
new file mode 100755
index 0000000..54badd5
--- /dev/null
+++ b/makeinfo/tests/html-title
@@ -0,0 +1,13 @@
+#!/bin/sh
+# Test that titles with @ commands don't produce markup in the <title>.
+# (And that the @ commands get expanded.)
+
+if ../makeinfo --no-split --html ${srcdir-.}/html-title.txi; then
+ grep '^<title>@[^<>]*</title>$' html-title.html >/dev/null
+ exit_status=$?
+else
+ exit_status=1
+fi
+
+rm -f html-title.html
+exit $exit_status
diff --git a/makeinfo/tests/html-title.txi b/makeinfo/tests/html-title.txi
new file mode 100644
index 0000000..2e2f25c
--- /dev/null
+++ b/makeinfo/tests/html-title.txi
@@ -0,0 +1,12 @@
+\input texinfo
+@setfilename html-title.info
+@settitle @@title @sc{html} @code{test}
+
+@node Top
+@top Top of @@title @sc{html} @code{test}
+
+Top.
+
+Second paragraph.
+
+@bye
diff --git a/makeinfo/tests/html-top b/makeinfo/tests/html-top
new file mode 100755
index 0000000..038404a
--- /dev/null
+++ b/makeinfo/tests/html-top
@@ -0,0 +1,13 @@
+#!/bin/sh
+# Test that a bare top node does not crash with --html.
+
+: ${srcdir=.}
+
+# But this input file is erroneous, so throw away errors.
+../makeinfo --no-split --force -o html-top.html --html $srcdir/html-top.txi \
+2>/dev/null
+test -s html-top.html
+exit_status=$?
+
+rm -f html-top.html
+exit $exit_status
diff --git a/makeinfo/tests/html-top.txi b/makeinfo/tests/html-top.txi
new file mode 100644
index 0000000..ade7214
--- /dev/null
+++ b/makeinfo/tests/html-top.txi
@@ -0,0 +1,7 @@
+\input texinfo
+@setfilename top.info
+
+@node start
+@top
+
+@bye
diff --git a/makeinfo/tests/incl-incl.txi b/makeinfo/tests/incl-incl.txi
new file mode 100644
index 0000000..4efea48
--- /dev/null
+++ b/makeinfo/tests/incl-incl.txi
@@ -0,0 +1 @@
+This is the @emph{included} file (include-value2.txi).
diff --git a/makeinfo/tests/include-value b/makeinfo/tests/include-value
new file mode 100755
index 0000000..3cef577
--- /dev/null
+++ b/makeinfo/tests/include-value
@@ -0,0 +1,22 @@
+#!/bin/sh
+# $Id: include-value,v 1.4 2005/04/05 21:04:16 karl Exp $
+# Test @value expansion in @include and @verbatiminclude names.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+
+../makeinfo -I $srcdir $srcdir/include-value.txi
+exit_status=$?
+
+if test $exit_status = 0; then
+ # should have three instances of _included_,
+ # and three of {included}.
+ count=`grep -c _included_ include-value.info`
+ test $count = 3 || exit_status=`expr $exit_status + 1`
+
+ count=`grep -c '{included}' include-value.info`
+ test $count = 3 || exit_status=`expr $exit_status + 1`
+fi
+
+rm -f include-value.info
+exit $exit_status
diff --git a/makeinfo/tests/include-value.txi b/makeinfo/tests/include-value.txi
new file mode 100644
index 0000000..1c7f9b9
--- /dev/null
+++ b/makeinfo/tests/include-value.txi
@@ -0,0 +1,32 @@
+\input texinfo
+@setfilename include-value.info
+@c $Id: include-value.txi,v 1.2 2004/04/11 17:56:47 karl Exp $
+
+@set testvar incl-incl.txi
+
+@c test - in the variable name, and concatenation of text after.
+@set test-var incl-incl.tx
+
+@c test - in the variable name, and concatenation of text before and after.
+@set test_var ncl-incl.tx
+
+
+@node Top
+
+testvar include: @include @value{testvar}
+
+testvar verbatiminclude: @verbatiminclude @value{testvar}
+
+
+test-var include: @include @value{test-var}i
+
+test-var verbatiminclude: @verbatiminclude @value{test-var}i
+
+
+test_var include: @include i@value{test_var}i
+
+test_var verbatiminclude: @verbatiminclude i@value{test_var}i
+
+
+@bye
+
diff --git a/makeinfo/tests/macro-at b/makeinfo/tests/macro-at
new file mode 100755
index 0000000..a9dfdab
--- /dev/null
+++ b/makeinfo/tests/macro-at
@@ -0,0 +1,8 @@
+#!/bin/sh
+# Test @@ in macro expansions, etc. --eliz, 14nov99.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+../makeinfo $srcdir/macro-at.txi || exit 1
+
+rm -f macro-at.info
diff --git a/makeinfo/tests/macro-at.txi b/makeinfo/tests/macro-at.txi
new file mode 100644
index 0000000..24b9101
--- /dev/null
+++ b/makeinfo/tests/macro-at.txi
@@ -0,0 +1,38 @@
+\input texinfo @c -*- texinfo -*-
+@setfilename macro-at.info
+@settitle AUTHORS -- who did what on GNU LilyPond
+
+@macro foo
+foo-expansion
+@end macro
+
+@macro bar
+bar-expansion
+@end macro
+
+@node Top, , AUTHORS -- who did what on GNU LilyPond, (dir)
+@top
+@menu
+* AUTHORS -- who did what on GNU LilyPond:: AUTHORS -- who did what.
+@end menu
+
+@node AUTHORS -- who did what on GNU LilyPond, Top, , Top
+@chapter AUTHORS -- who did what on GNU LilyPond?
+
+This file lists authors of GNU LilyPond, and what they wrote.
+It also uses foobar@{.
+
+@itemize @bullet
+@item @email{pinard@@iro.montreal.ca, Fran@,{c}ois Pinard},
+ parts of Documentation.
+@item @email{foobar@@baz@@, The Foobar},
+ the usual foobarical thing.
+@item @email{another@@foobar@{, Buzzer},
+ buzzed all the way.
+@item @email{@foo{}@@@bar{}},
+ also helped.
+@item @email{tomcato@@xoommail.com, Tom Cato Amundsen},
+ cembalo-partita in mundela.
+@end itemize
+
+@bye
diff --git a/makeinfo/tests/menu-whitespace b/makeinfo/tests/menu-whitespace
new file mode 100755
index 0000000..a01a38d
--- /dev/null
+++ b/makeinfo/tests/menu-whitespace
@@ -0,0 +1,72 @@
+#!/bin/sh
+# Bug where whitespace after @menu caused confusion.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+input=`basename $0`.txi
+
+../makeinfo -o /dev/null $srcdir/$input
+exit $?
+
+Date: 07 Dec 1998 11:23:44 +0100
+From: Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+To: bug-texinfo@gnu.org
+Subject: Makeinfo mishandles defaulted node links
+
+The following example demonstrates a bug in makeinfo:
+
+$ cat top.texi
+@setfilename top.info
+
+@node Top
+@top Top
+
+@menu
+* first::
+@end menu
+
+@node first
+@chapter first
+
+@menu @c
+* second::
+@end menu
+
+@node second
+@section second
+$ makeinfo top.texi
+Making info file `top.info' from `top.texi'.
+./top.texi:3: Next field of node `Top' not pointed to.
+./top.texi:17: This node (second) has the bad Prev.
+makeinfo: Removing output file `/home/as/test/top.info' due to errors; use --force to preserve.
+
+Makeinfo is being confused by the whitespace after @menu, or rather by its
+absence.
+
+
+1998-12-06 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
+
+ * makeinfo/node.c (cm_node): When searching for @menu don't
+ require a space after it.
+
+--- texinfo-3.12b/makeinfo/node.c.~1~ Mon Oct 26 23:14:59 1998
++++ texinfo-3.12b/makeinfo/node.c Sun Dec 6 00:23:59 1998
+@@ -523,9 +523,10 @@
+ orig_size = size_of_input_text;
+
+ input_text_offset =
+- search_forward ("\n@menu ", orig_offset);
++ search_forward ("\n@menu", orig_offset);
+
+- if (input_text_offset > -1)
++ if (input_text_offset > -1
++ && cr_or_whitespace (input_text[input_text_offset + 6]))
+ {
+ char *nodename_from_menu = NULL;
+
+
+--
+Andreas Schwab "And now for something
+schwab@issan.cs.uni-dortmund.de completely different"
+schwab@gnu.org
+
diff --git a/makeinfo/tests/menu-whitespace.txi b/makeinfo/tests/menu-whitespace.txi
new file mode 100644
index 0000000..fd1c39f
--- /dev/null
+++ b/makeinfo/tests/menu-whitespace.txi
@@ -0,0 +1,18 @@
+@setfilename top.info
+
+@node Top
+@top Top
+
+@menu
+* first::
+@end menu
+
+@node first
+@chapter first
+
+@menu @c
+* second::
+@end menu
+
+@node second
+@section second
diff --git a/makeinfo/tests/no-headers b/makeinfo/tests/no-headers
new file mode 100755
index 0000000..6b96184
--- /dev/null
+++ b/makeinfo/tests/no-headers
@@ -0,0 +1,12 @@
+#!/bin/sh
+# Test that info.texi works with --no-headers (this includes node
+# pointer defaulting).
+
+# maybe need "nul" sometimes? Not clear.
+: ${nulldev=/dev/null}
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+docdir=$srcdir/../../doc
+
+../makeinfo --no-headers -o $nulldev -I$docdir info.texi
diff --git a/makeinfo/tests/node-expand b/makeinfo/tests/node-expand
new file mode 100755
index 0000000..26dee24
--- /dev/null
+++ b/makeinfo/tests/node-expand
@@ -0,0 +1,12 @@
+#!/bin/sh
+# Test command expansion in node names.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+
+../makeinfo --commands-in-node-names $srcdir/node-expand.txi
+test -s node-expand.info
+exit_status=$?
+
+rm -f node-expand.info
+exit $exit_status
diff --git a/makeinfo/tests/node-expand.txi b/makeinfo/tests/node-expand.txi
new file mode 100644
index 0000000..c31be5a
--- /dev/null
+++ b/makeinfo/tests/node-expand.txi
@@ -0,0 +1,64 @@
+\input texinfo.tex @c -*-texinfo-*-
+
+@setfilename node-expand.info
+
+@c Makeinfo should expand non-macros such as @@ and @value
+@c in node names and node references, including menus.
+@c This file deliberately references "Node 1" both via
+@c @value and directly; this should not trigger any errors,
+@c as long as --commands-in-node-names is used.
+@c Index entries should all be expanded as well.
+
+@set node1 Node 1
+
+@ifnottex
+
+@node Top, (dir), (dir), (dir)
+@top Expansion in Node Names
+
+@end ifnottex
+
+@menu
+* @value{node1} ::
+* @@node `2'::
+* ``node'' with---tricks::
+@end menu
+
+@xref{@@node `2'}.
+
+@node @value{node1}, @@node `2', Top, Top
+@chapter Chapter 1
+
+@cindex entry for chapter 1
+This is chapter 1.
+@xref{@@node `2'}.
+
+@set sec1 Section 1.1
+
+@menu
+* @value{sec1}::
+@end menu
+
+@node Section 1.1, , Node 1, Node 1
+@comment node-name, next, previous, up
+
+@cindex entry for section 1.1
+This is section 1.1.
+
+@node @@node `2', ``node'' with---tricks, @value{node1}, Top
+@comment node-name, next, previous, up
+@chapter Node 2
+
+@cindex entry for chapter 2
+This is chapter 2.
+@xref{@value{node1}}. @xref{Node 1}.
+@xref{``node'' with---tricks, Node with some tricks}.
+
+@node ``node'' with---tricks, , @@node `2', Top
+
+@cindex tricks
+Another node.
+
+@printindex cp
+
+@bye
diff --git a/makeinfo/tests/node-value b/makeinfo/tests/node-value
new file mode 100755
index 0000000..aff0be2
--- /dev/null
+++ b/makeinfo/tests/node-value
@@ -0,0 +1,12 @@
+#!/bin/sh
+# Test @value expansion in node names.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+
+../makeinfo $srcdir/node-value.txi
+grep -v "No Value" node-value.info >/dev/null
+exit_status=$?
+
+rm -f node-value.info
+exit $exit_status
diff --git a/makeinfo/tests/node-value.txi b/makeinfo/tests/node-value.txi
new file mode 100644
index 0000000..ba1173f
--- /dev/null
+++ b/makeinfo/tests/node-value.txi
@@ -0,0 +1,15 @@
+\input texinfo
+@setfilename node-value.info
+@set a--foo bar
+
+@node Top, @value{a--foo}, (dir), (dir)
+@top Var @value{a--foo}
+@value{a--foo}
+
+@node @value{a--foo}, BarFoo, Top, (dir)
+@chapter BarFoo
+
+@node BarFoo, , @value{a--foo}, (dir)
+@chapter bar
+
+@bye
diff --git a/makeinfo/tests/node-whitespace b/makeinfo/tests/node-whitespace
new file mode 100755
index 0000000..e480108
--- /dev/null
+++ b/makeinfo/tests/node-whitespace
@@ -0,0 +1,12 @@
+#!/bin/sh
+# Test whitespace collapse in node names.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+
+../makeinfo $srcdir/node-whitespace.txi
+test -s node-whitespace.info
+exit_status=$?
+
+rm -f node-whitespace.info
+exit $exit_status
diff --git a/makeinfo/tests/node-whitespace.txi b/makeinfo/tests/node-whitespace.txi
new file mode 100644
index 0000000..04f0339
--- /dev/null
+++ b/makeinfo/tests/node-whitespace.txi
@@ -0,0 +1,30 @@
+\input texinfo.tex @c -*-texinfo-*-
+
+@setfilename node-whitespace.info
+
+@c Makeinfo should collapse whitespace in node names.
+
+@ifnottex
+
+@node Top
+@top Whitespace in Node Names
+
+@end ifnottex
+
+@menu
+* Chap 1.3 ::
+* Chap 1.4 :Chap 1.4.
+@end menu
+
+@node Chap 1.3
+@chapter Chap 1.3
+
+Can I reach here?
+
+@node Chap 1.4
+@chapter Another space test
+
+How about here?
+@xref{Chap 1.3}.
+
+@bye
diff --git a/makeinfo/tests/quote-args b/makeinfo/tests/quote-args
new file mode 100755
index 0000000..6940c0c
--- /dev/null
+++ b/makeinfo/tests/quote-args
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Test @quote-args facility.
+
+: ${srcdir=.}
+
+unset TEXINFO_OUTPUT
+TMP=quote-args.out
+
+cat > quote-args.samp <<EOT
+*FIXME: Many arguments, separated by commas, are processed here*
+natopocotuototam
+
+EOT
+
+../makeinfo --plaintex $srcdir/quote-args.txi | diff - quote-args.samp
+exit_code=$?
+
+rm quote-args.samp
+exit $exit_code
diff --git a/makeinfo/tests/quote-args.txi b/makeinfo/tests/quote-args.txi
new file mode 100644
index 0000000..4f78d1d
--- /dev/null
+++ b/makeinfo/tests/quote-args.txi
@@ -0,0 +1,20 @@
+\input texinfo
+@smallbook
+@setfilename quote-args.info
+@settitle Quote-args facility in macros
+
+@rmacro cat{a,b}
+\a\\b\
+@end rmacro
+
+@macro FIXME{a}
+@strong{FIXME: \a\}
+@end macro
+
+@node Top, , (dir), (dir)
+
+@noindent
+@FIXME{Many arguments, separated by commas, are processed here}
+@cat{@cat{@cat{@cat{@cat{@cat{na, to}, po}, co}, tu}, oto},tam}
+@bye
+
diff --git a/makeinfo/tests/top b/makeinfo/tests/top
new file mode 100755
index 0000000..c1288f3
--- /dev/null
+++ b/makeinfo/tests/top
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Test that a top node can be ignored.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+../makeinfo $srcdir/top.txi || exit 1
+
+# Expected warnings due to use of @ifinfo instead of @ifnottex.
+../makeinfo --no-split --no-warn --html $srcdir/top.txi -o top.html || exit 1
+
+rm -f top*.html top.info
diff --git a/makeinfo/tests/top.txi b/makeinfo/tests/top.txi
new file mode 100644
index 0000000..e8c3cbb
--- /dev/null
+++ b/makeinfo/tests/top.txi
@@ -0,0 +1,25 @@
+\input texinfo
+@setfilename top.info
+@settitle top test
+
+@c This traditional top node uses @ifinfo for testing.
+@c Therefore there will be warnings when processing with --html.
+@c The solution is to use @ifnottex instead.
+
+@ifinfo
+@node Top
+@top Top test
+
+Typical top node.
+@end ifinfo
+
+@menu
+* Subnode::
+@end menu
+
+@node Subnode
+@chapter Subnode
+
+Subnode.
+
+@bye
diff --git a/makeinfo/tests/twofiles b/makeinfo/tests/twofiles
new file mode 100755
index 0000000..22c477c
--- /dev/null
+++ b/makeinfo/tests/twofiles
@@ -0,0 +1,23 @@
+#!/bin/sh
+# $Id: twofiles,v 1.3 2004/04/11 17:56:47 karl Exp $
+# Test that an existing and nonexisting file doesn't cause a
+# segmentation fault.
+# From: Arkadiusz Miskiewicz <misiek@pld.ORG.PL>, 15 Feb 2003 13:22:49 +0100.
+
+unset TEXINFO_OUTPUT
+: ${srcdir=.}
+
+outfile=outfile
+errfile=errfile
+trap 'status=$?; rm -f $outfile $errfile && exit $status' 0
+
+../makeinfo -o /dev/null $srcdir/html-min.txi /nonexistent.texinfo \
+>$outfile 2>$errfile
+exit_status=$?
+
+# we expect one error message about /nonexistent.texinfo and bad exit status.
+test $exit_status -ne 0 \
+&& grep /nonexistent $errfile >/dev/null \
+&& exit_status=0
+
+exit $exit_status
diff --git a/makeinfo/toc.c b/makeinfo/toc.c
new file mode 100644
index 0000000..3f0fd87
--- /dev/null
+++ b/makeinfo/toc.c
@@ -0,0 +1,378 @@
+/* toc.c -- table of contents handling.
+ $Id: toc.c,v 1.12 2008/05/19 18:26:48 karl Exp $
+
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#include "system.h"
+#include "makeinfo.h"
+#include "cmds.h"
+#include "files.h"
+#include "macro.h"
+#include "node.h"
+#include "html.h"
+#include "lang.h"
+#include "makeinfo.h"
+#include "sectioning.h"
+#include "toc.h"
+#include "xml.h"
+
+/* array of toc entries */
+static TOC_ENTRY_ELT **toc_entry_alist = NULL;
+
+/* toc_counter start from 0 ... n for every @chapter, @section ... */
+static int toc_counter = 0;
+
+/* Routine to add an entry to the table of contents */
+int
+toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
+{
+ char *expanded_node, *d;
+ char *s = NULL;
+ char *filename = NULL;
+
+ if (!node_name)
+ node_name = "";
+
+ /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
+ NULL */
+ toc_entry_alist = xrealloc (toc_entry_alist,
+ (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
+
+ toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
+
+ if (html)
+ {
+ /* We need to insert the expanded node name into the toc, so
+ that when we eventually output the toc, its <a ref= link will
+ point to the <a name= tag created by cm_node in the navigation
+ bar. We cannot expand the containing_node member, for the
+ reasons explained in the WARNING below. We also cannot wait
+ with the node name expansion until the toc is actually output,
+ since by that time the macro definitions may have been changed.
+ So instead we store in the tocname member the expanded node
+ name and the toc name concatenated together (with the necessary
+ html markup), since that's how they are output. */
+ if (!anchor)
+ s = expanded_node = expand_node_name (node_name);
+ else
+ expanded_node = anchor;
+ if (splitting)
+ {
+ if (!anchor)
+ filename = nodename_to_filename (expanded_node);
+ else
+ filename = filename_part (current_output_filename);
+ }
+ if (!anchor)
+ /* Need to HTML-escape the expanded node name like
+ add_anchor_name does... */
+ d = escaped_anchor_name (expanded_node);
+ else
+ /* Section outside any node, they provided explicit anchor. */
+ d = xstrdup(anchor);
+
+ /* Add space for the "> which may be needed, and the tocname */
+ d = xrealloc (d, strlen (d) + strlen (tocname) + 3);
+ if (!anchor)
+ strcat (d, "\">");
+ strcat (d, tocname);
+ free (tocname); /* it was malloc'ed by substring() */
+ free (expanded_node);
+ toc_entry_alist[toc_counter]->name = d;
+ }
+ else
+ toc_entry_alist[toc_counter]->name = tocname;
+ /* WARNING! The node name saved in containing_node member must
+ be the node name with _only_ macros expanded (the macros in
+ the node name are expanded by cm_node when it grabs the name
+ from the @node directive). Non-macros, like @value, @@ and
+ other @-commands must NOT be expanded in containing_node,
+ because toc_find_section_of_node looks up the node name where
+ they are also unexpanded. You *have* been warned! */
+ toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
+ toc_entry_alist[toc_counter]->level = level;
+ toc_entry_alist[toc_counter]->number = toc_counter;
+ toc_entry_alist[toc_counter]->html_file = filename;
+
+ /* have to be done at least */
+ return toc_counter++;
+}
+
+/* Return the name of a chapter/section/subsection etc. that
+ corresponds to the node NODE. If the node isn't found,
+ return NULL.
+
+ WARNING! This function relies on NODE being unexpanded
+ except for macros (i.e., @value, @@, and other non-macros
+ should NOT be expanded), because the containing_node member
+ stores unexpanded node names.
+
+ Note that this function returns the first section whose
+ containing node is NODE. Thus, they will lose if they use
+ more than a single chapter structioning command in a node,
+ or if they have a node without any structuring commands. */
+char *
+toc_find_section_of_node (char *node)
+{
+ int i;
+
+ if (!node)
+ node = "";
+ for (i = 0; i < toc_counter; i++)
+ if (STREQ (node, toc_entry_alist[i]->containing_node))
+ return toc_entry_alist[i]->name;
+
+ return NULL;
+}
+
+/* free up memory used by toc entries */
+void
+toc_free (void)
+{
+ int i;
+
+ if (toc_counter)
+ {
+ for (i = 0; i < toc_counter; i++)
+ {
+ free (toc_entry_alist[i]->name);
+ free (toc_entry_alist[i]->containing_node);
+ free (toc_entry_alist[i]);
+ }
+
+ free (toc_entry_alist);
+ toc_entry_alist = NULL; /* to be sure ;-) */
+ toc_counter = 0; /* to be absolutley sure ;-) */
+ }
+}
+
+/* Print table of contents in HTML. */
+
+static void
+contents_update_html (void)
+{
+ int i;
+ int k;
+ int last_level;
+
+ /* does exist any toc? */
+ if (!toc_counter)
+ /* no, so return to sender ;-) */
+ return;
+
+ add_html_block_elt_args ("\n<div class=\"contents\">\n<h2>%s</h2>\n<ul>\n", gdt("Table of Contents"));
+
+ last_level = toc_entry_alist[0]->level;
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if (toc_entry_alist[i]->level > last_level)
+ {
+ /* unusual, but it is possible
+ @chapter ...
+ @subsubsection ... ? */
+ for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
+ add_html_block_elt ("<ul>\n");
+ }
+ else if (toc_entry_alist[i]->level < last_level)
+ {
+ /* @subsubsection ...
+ @chapter ... this IS usual.*/
+ for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
+ add_word ("</li></ul>\n");
+ }
+
+ /* No double entries in TOC. */
+ if (!(i && strcmp (toc_entry_alist[i]->name,
+ toc_entry_alist[i-1]->name) == 0))
+ {
+ /* each toc entry is a list item. */
+ add_word ("<li>");
+
+ /* Insert link -- to an external file if splitting, or
+ within the current document if not splitting. */
+ add_word ("<a ");
+ /* For chapters (only), insert an anchor that the short contents
+ will link to. */
+ if (toc_entry_alist[i]->level == 0)
+ {
+ char *p = toc_entry_alist[i]->name;
+
+ /* toc_entry_alist[i]->name has the form `foo">bar',
+ that is, it includes both the node name and anchor
+ text. We need to find where `foo', the node name,
+ ends, and use that in toc_FOO. */
+ while (*p && *p != '"')
+ p++;
+ add_word_args ("name=\"toc_%.*s\" ",
+ p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
+ /* save the link if necessary */
+ if (internal_links_stream)
+ {
+ fprintf (internal_links_stream, "%s#toc_%.*s\ttoc\t%s\n",
+ splitting ? toc_entry_alist[i]->html_file : "",
+ p - toc_entry_alist[i]->name, toc_entry_alist[i]->name,
+ p + 2);
+ }
+ }
+ add_word_args ("href=\"%s#%s</a>\n",
+ splitting ? toc_entry_alist[i]->html_file : "",
+ toc_entry_alist[i]->name);
+ }
+
+ last_level = toc_entry_alist[i]->level;
+ }
+
+ /* Go back to start level. */
+ if (toc_entry_alist[0]->level < last_level)
+ for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
+ add_word ("</li></ul>\n");
+
+ add_word ("</li></ul>\n</div>\n\n");
+}
+
+/* print table of contents in ASCII (--no-headers)
+ May be we should create a new command line switch --ascii ? */
+static void
+contents_update_info (void)
+{
+ int i;
+ int k;
+
+ if (!toc_counter)
+ return;
+
+ insert_string ((char *) gdt("Table of Contents"));
+ insert ('\n');
+ for (i = 0; i < strlen (gdt("Table of Contents")); i++)
+ insert ('*');
+ insert_string ("\n\n");
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if (toc_entry_alist[i]->level == 0)
+ add_char ('\n');
+
+ /* indention with two spaces per level, should this
+ changed? */
+ for (k = 0; k < toc_entry_alist[i]->level; k++)
+ insert_string (" ");
+
+ insert_string (toc_entry_alist[i]->name);
+ insert ('\n');
+ }
+ insert_string ("\n\n");
+}
+
+/* shortcontents in HTML; Should this produce a standalone file? */
+static void
+shortcontents_update_html (char *contents_filename)
+{
+ int i;
+ char *toc_file = NULL;
+
+ /* does exist any toc? */
+ if (!toc_counter)
+ return;
+
+ add_html_block_elt_args ("\n<div class=\"shortcontents\">\n<h2>%s</h2>\n<ul>\n", gdt("Short Contents"));
+
+ if (contents_filename)
+ toc_file = filename_part (contents_filename);
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ char *name = toc_entry_alist[i]->name;
+
+ if (toc_entry_alist[i]->level == 0)
+ {
+ if (contents_filename)
+ add_word_args ("<li><a href=\"%s#toc_%s</a></li>\n",
+ splitting ? toc_file : "", name);
+ else
+ add_word_args ("<a href=\"%s#%s</a>\n",
+ splitting ? toc_entry_alist[i]->html_file : "", name);
+ }
+ }
+ add_word ("</ul>\n</div>\n\n");
+ if (contents_filename)
+ free (toc_file);
+}
+
+/* short contents in ASCII (--no-headers). */
+static void
+shortcontents_update_info (void)
+{
+ int i;
+
+ if (!toc_counter)
+ return;
+
+ insert_string ((char *) gdt("Short Contents"));
+ insert ('\n');
+ for (i = 0; i < strlen (gdt("Short Contents")); i++)
+ insert ('*');
+ insert_string ("\n\n");
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if (toc_entry_alist[i]->level == 0)
+ {
+ insert_string (toc_entry_alist[i]->name);
+ insert ('\n');
+ }
+ }
+ insert_string ("\n\n");
+}
+
+void
+cm_contents (int arg)
+{
+ /* the file where we found the @contents directive */
+ static char *contents_filename;
+
+ /* No need to mess with delayed stuff for XML and Docbook. */
+ if (xml)
+ {
+ if (arg == START)
+ {
+ int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
+ xml_insert_element (elt, START);
+ xml_insert_element (elt, END);
+ }
+ }
+ else if (!handling_delayed_writes)
+ {
+ register_delayed_write (STREQ (command, "contents")
+ ? "@contents" : "@shortcontents");
+
+ if (html && STREQ (command, "contents"))
+ {
+ if (contents_filename)
+ free (contents_filename);
+ contents_filename = xstrdup (current_output_filename);
+ }
+ }
+ else if (html)
+ STREQ (command, "contents")
+ ? contents_update_html () : shortcontents_update_html (contents_filename);
+ else if (no_headers)
+ STREQ (command, "contents")
+ ? contents_update_info () : shortcontents_update_info ();
+}
diff --git a/makeinfo/toc.h b/makeinfo/toc.h
new file mode 100644
index 0000000..788420c
--- /dev/null
+++ b/makeinfo/toc.h
@@ -0,0 +1,44 @@
+/* toc.h -- table of contents handling.
+ $Id: toc.h,v 1.5 2007/07/01 21:20:33 karl Exp $
+
+ Copyright (C) 1999, 2007 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 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 <http://www.gnu.org/licenses/>.
+
+ Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#ifndef TOC_H
+#define TOC_H
+
+/* Structure to hold one entry for the toc. */
+typedef struct toc_entry_elt {
+ char *name;
+ char *containing_node; /* Name of node containing this section. */
+ char *html_file; /* Name of HTML node-file in split-HTML mode */
+ int number; /* counting number from 0...n independent from
+ chapter/section can be used for anchors or
+ references to it. */
+ int level; /* level: chapter, section, subsection... */
+} TOC_ENTRY_ELT;
+
+/* all routines which have relationship with TOC should start with
+ toc_ (this is a kind of name-space) */
+extern int toc_add_entry (char *tocname, int level,
+ char *node_name, char *anchor); /* return the number for the toc-entry */
+extern void toc_free (void);
+extern char *toc_find_section_of_node (char *node);
+
+extern void cm_contents (int arg);
+
+#endif /* not TOC_H */
diff --git a/makeinfo/xml.c b/makeinfo/xml.c
new file mode 100644
index 0000000..88c452a
--- /dev/null
+++ b/makeinfo/xml.c
@@ -0,0 +1,2252 @@
+/* xml.c -- xml output, both TexinfoML and Docbook.
+ $Id: xml.c,v 1.75 2008/01/31 18:33:27 karl Exp $
+
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Originally written by Philippe Martin <feloy@free.fr>. */
+
+#include "system.h"
+#include "makeinfo.h"
+#include "insertion.h"
+#include "files.h"
+#include "float.h"
+#include "macro.h"
+#include "cmds.h"
+#include "lang.h"
+
+#include "xml.h"
+
+#include <assert.h>
+
+#if !__OPTIMIZE__
+/* To make enum names available to debugger. */
+static enum xml_element xml_element_dummy;
+#endif
+
+typedef struct _element
+{
+ char name[32];
+ int contains_para;
+ int contained_in_para;
+ int keep_space;
+} element;
+
+element texinfoml_element_list [] = {
+ { "texinfo", 1, 0, 0 },
+ { "setfilename", 0, 0, 0 },
+ { "titlefont", 0, 0, 0 },
+ { "settitle", 0, 0, 0 },
+ { "documentdescription", 1, 0, 0 },
+
+ { "node", 1, 0, 0 },
+ { "nodenext", 0, 0, 0 },
+ { "nodeprev", 0, 0, 0 },
+ { "nodeup", 0, 0, 0 },
+
+ { "chapter", 1, 0, 0 },
+ { "section", 1, 0, 0 },
+ { "subsection", 1, 0, 0 },
+ { "subsubsection", 1, 0, 0 },
+
+ { "top", 1, 0, 0 },
+ { "unnumbered", 1, 0, 0 },
+ { "unnumberedsec", 1, 0, 0 },
+ { "unnumberedsubsec", 1, 0, 0 },
+ { "unnumberedsubsubsec", 1, 0, 0 },
+
+ { "appendix", 1, 0, 0 },
+ { "appendixsec", 1, 0, 0 },
+ { "appendixsubsec", 1, 0, 0 },
+ { "appendixsubsubsec", 1, 0, 0 },
+
+ { "majorheading", 0, 0, 0 },
+ { "chapheading", 0, 0, 0 },
+ { "heading", 0, 0, 0 },
+ { "subheading", 0, 0, 0 },
+ { "subsubheading", 0, 0, 0 },
+
+ { "titlepage", 1, 0, 0 },
+ { "author", 0, 0, 0 },
+ { "booktitle", 0, 0, 0 },
+ { "booksubtitle", 0, 0, 0 },
+
+ { "menu", 1, 0, 0 },
+ { "detailmenu", 1, 0, 0 },
+ { "menuentry", 0, 0, 0 },
+ { "menutitle", 0, 0, 0 },
+ { "menucomment", 0, 0, 0 },
+ { "menunode", 0, 0, 0 },
+ { "nodename", 0, 0, 0 },
+
+ { "acronym", 0, 1, 0 },
+ { "acronymword", 0, 1, 0 },
+ { "acronymdesc", 0, 1, 0 },
+
+ { "abbrev", 0, 1, 0 },
+ { "abbrevword", 0, 1, 0 },
+ { "abbrevdesc", 0, 1, 0 },
+
+ { "tt", 0, 1, 0 },
+ { "code", 0, 1, 0 },
+ { "command", 0, 1, 0 },
+ { "env", 0, 1, 0 },
+ { "file", 0, 1, 0 },
+ { "option", 0, 1, 0 },
+ { "samp", 0, 1, 0 },
+ { "kbd", 0, 1, 0 },
+ { "url", 0, 1, 0 },
+ { "key", 0, 1, 0 },
+ { "var", 0, 1, 0 },
+ { "sc", 0, 1, 0 },
+ { "dfn", 0, 1, 0 },
+ { "emph", 0, 1, 0 },
+ { "strong", 0, 1, 0 },
+ { "cite", 0, 1, 0 },
+ { "notfixedwidth", 0, 1, 0 },
+ { "i", 0, 1, 0 },
+ { "b", 0, 1, 0 },
+ { "r", 0, 1, 0 },
+ { "slanted", 0, 1, 0 },
+ { "sansserif", 0, 1, 0 },
+
+ { "exdent", 0, 0, 0 },
+
+ { "title", 0, 0, 0 },
+ { "ifinfo", 1, 0, 0 },
+ { "sp", 0, 0, 0 },
+ { "center", 1, 0, 0 },
+ { "dircategory", 0, 0, 0 },
+ { "quotation", 1, 0, 0 },
+ { "example", 0, 0, 1 },
+ { "smallexample", 0, 0, 1 },
+ { "lisp", 0, 0, 1 },
+ { "smalllisp", 0, 0, 1 },
+ { "cartouche", 1, 0, 0 },
+ { "copying", 1, 0, 0 },
+ { "format", 0, 0, 1 },
+ { "smallformat", 0, 0, 1 },
+ { "display", 0, 0, 1 },
+ { "smalldisplay", 0, 0, 1 },
+ { "verbatim", 0, 0, 1 },
+ { "footnote", 0, 1, 0 },
+ { "", 0, 1, 0 }, /* LINEANNOTATION (docbook) */
+
+ { "", 1, 0, 0 }, /* TIP (docbook) */
+ { "", 1, 0, 0 }, /* NOTE (docbook) */
+ { "", 1, 0, 0 }, /* IMPORTANT (docbook) */
+ { "", 1, 0, 0 }, /* WARNING (docbook) */
+ { "", 1, 0, 0 }, /* CAUTION (docbook) */
+
+ { "itemize", 0, 0, 0 },
+ { "itemfunction", 0, 0, 0 },
+ { "item", 1, 0, 0 },
+ { "enumerate", 0, 0, 0 },
+ { "table", 0, 0, 0 },
+ { "tableitem", 0, 0, 0 },
+ { "tableterm", 0, 0, 0 },
+
+ { "indexterm", 0, 1, 0 },
+
+ { "math", 0, 1, 0 },
+
+ { "dmn", 0, 1, 0 },
+
+ { "clicksequence", 0, 1, 0 },
+ { "click", 0, 1, 0 },
+
+ { "xref", 0, 1, 0 },
+ { "xrefnodename", 0, 1, 0 },
+ { "xrefinfoname", 0, 1, 0 },
+ { "xrefprinteddesc", 0, 1, 0 },
+ { "xrefinfofile", 0, 1, 0 },
+ { "xrefprintedname", 0, 1, 0 },
+
+ { "inforef", 0, 1, 0 },
+ { "inforefnodename", 0, 1, 0 },
+ { "inforefrefname", 0, 1, 0 },
+ { "inforefinfoname", 0, 1, 0 },
+
+ { "uref", 0, 1, 0 },
+ { "urefurl", 0, 1, 0 },
+ { "urefdesc", 0, 1, 0 },
+ { "urefreplacement", 0, 1, 0 },
+
+ { "email", 0, 1, 0 },
+ { "emailaddress", 0, 1, 0 },
+ { "emailname", 0, 1, 0 },
+
+ { "group", 0, 0, 0 },
+ { "float", 1, 0, 0 },
+ { "floattype", 0, 0, 0 },
+ { "floatpos", 0, 0, 0 },
+ { "caption", 0, 0, 0 },
+ { "shortcaption", 0, 0, 0 },
+
+ { "", 0, 0, 0 }, /* TABLE (docbook) */
+ { "", 0, 0, 0 }, /* FIGURE (docbook) */
+ { "", 0, 0, 0 }, /* EXAMPLE (docbook) */
+ { "", 1, 0, 0 }, /* SIDEBAR (docbook) */
+
+ { "printindex", 0, 0, 0 },
+ { "listoffloats", 0, 0, 0 },
+ { "anchor", 0, 1, 0 },
+
+ { "image", 0, 0, 0 },
+ { "inlineimage", 0, 1, 0 },
+ { "alttext", 0, 1, 0 },
+
+ { "", 0, 1, 0 }, /* PRIMARY (docbook) */
+ { "", 0, 1, 0 }, /* SECONDARY (docbook) */
+ { "", 0, 0, 0 }, /* INFORMALFIGURE (docbook) */
+ { "", 0, 0, 0 }, /* MEDIAOBJECT (docbook) */
+ { "", 0, 0, 0 }, /* IMAGEOBJECT (docbook) */
+ { "", 0, 0, 0 }, /* IMAGEDATA (docbook) */
+ { "", 0, 0, 0 }, /* TEXTOBJECT (docbook) */
+ { "", 0, 0, 0 }, /* INDEXENTRY (docbook) */
+ { "", 0, 0, 0 }, /* PRIMARYIE (docbook) */
+ { "", 0, 0, 0 }, /* SECONDARYIE (docbook) */
+ { "", 0, 0, 0 }, /* INDEXDIV (docbook) */
+ { "multitable", 0, 0, 0 },
+ { "", 0, 0, 0 }, /* TGROUP (docbook) */
+ { "columnfraction", 0, 0, 0 },
+ { "thead", 0, 0, 0 },
+ { "tbody", 0, 0, 0 },
+ { "entry", 0, 0, 0 },
+ { "row", 0, 0, 0 },
+ { "", 0, 0, 0 }, /* BOOKINFO (docbook) */
+ { "", 0, 0, 0 }, /* ABSTRACT (docbook) */
+ { "", 0, 0, 0 }, /* REPLACEABLE (docbook) */
+ { "", 0, 0, 0 }, /* ENVAR (docbook) */
+ { "", 0, 0, 0 }, /* COMMENT (docbook) */
+ { "", 0, 0, 0 }, /* FUNCTION (docbook) */
+ { "", 0, 0, 0 }, /* LEGALNOTICE (docbook) */
+
+ { "contents", 0, 0, 0 },
+ { "shortcontents", 0, 0, 0 },
+ { "documentlanguage", 0, 0, 0 },
+
+ { "setvalue", 0, 0, 0 },
+ { "clearvalue", 0, 0, 0 },
+
+ { "definition", 0, 0, 0 },
+ { "definitionterm", 0, 0, 0 },
+ { "definitionitem", 1, 0, 0 },
+ { "defcategory", 0, 0, 0 },
+ { "deffunction", 0, 0, 0 },
+ { "defvariable", 0, 0, 0 },
+ { "defparam", 0, 0, 0 },
+ { "defdelimiter", 0, 0, 0 },
+ { "deftype", 0, 0, 0 },
+ { "defparamtype", 0, 0, 0 },
+ { "defdatatype", 0, 0, 0 },
+ { "defclass", 0, 0, 0 },
+ { "defclassvar", 0, 0, 0 },
+ { "defoperation", 0, 0, 0 },
+ { "frenchspacing", 0, 0, 0 },
+
+ { "para", 0, 0, 0 } /* Must be last */
+ /* name / contains para / contained in para / preserve space */
+};
+
+element docbook_element_list [] = {
+ { "book", 0, 0, 0 }, /* TEXINFO */
+ { "", 0, 0, 0 }, /* SETFILENAME */
+ { "", 0, 0, 0 }, /* TITLEINFO */
+ { "title", 0, 0, 0 }, /* SETTITLE */
+ { "", 1, 0, 0 }, /* DOCUMENTDESCRIPTION (?) */
+
+ { "", 1, 0, 0 }, /* NODE */
+ { "", 0, 0, 0 }, /* NODENEXT */
+ { "", 0, 0, 0 }, /* NODEPREV */
+ { "", 0, 0, 0 }, /* NODEUP */
+
+ { "chapter", 1, 0, 0 },
+ { "sect1", 1, 0, 0 }, /* SECTION */
+ { "sect2", 1, 0, 0 }, /* SUBSECTION */
+ { "sect3", 1, 0, 0 }, /* SUBSUBSECTION */
+
+ { "chapter", 1, 0, 0 }, /* TOP */
+ { "chapter", 1, 0, 0 }, /* UNNUMBERED */
+ { "sect1", 1, 0, 0 }, /* UNNUMBEREDSEC */
+ { "sect2", 1, 0, 0 }, /* UNNUMBEREDSUBSEC */
+ { "sect3", 1, 0, 0 }, /* UNNUMBEREDSUBSUBSEC */
+
+ { "appendix", 1, 0, 0 },
+ { "sect1", 1, 0, 0 }, /* APPENDIXSEC */
+ { "sect2", 1, 0, 0 }, /* APPENDIXSUBSEC */
+ { "sect3", 1, 0, 0 }, /* APPENDIXSUBSUBSEC */
+
+ { "bridgehead", 0, 0, 0 }, /* MAJORHEADING */
+ { "bridgehead", 0, 0, 0 }, /* CHAPHEADING */
+ { "bridgehead", 0, 0, 0 }, /* HEADING */
+ { "bridgehead", 0, 0, 0 }, /* SUBHEADING */
+ { "bridgehead", 0, 0, 0 }, /* SUBSUBHEADING */
+
+ { "", 0, 0, 0 }, /* TITLEPAGE */
+ { "", 0, 0, 0 }, /* AUTHOR */
+ { "", 0, 0, 0 }, /* BOOKTITLE */
+ { "", 0, 0, 0 }, /* BOOKSUBTITLE */
+
+ { "", 1, 0, 0 }, /* MENU */
+ { "", 1, 0, 0 }, /* DETAILMENU */
+ { "", 1, 0, 0 }, /* MENUENTRY */
+ { "", 0, 0, 0 }, /* MENUTITLE */
+ { "", 1, 0, 0 }, /* MENUCOMMENT */
+ { "", 0, 0, 0 }, /* MENUNODE */
+ { "anchor", 0, 0, 0 }, /* NODENAME */
+
+ { "acronym", 0, 1, 0 },
+ { "", 0, 1, 0 }, /* ACRONYMWORD */
+ { "", 0, 1, 0 }, /* ACRONYMDESC */
+
+ { "abbrev", 0, 1, 0 },
+ { "", 0, 1, 0 }, /* ABBREVWORD */
+ { "", 0, 1, 0 }, /* ABBREVDESC */
+
+ { "literal", 0, 1, 0 }, /* TT */
+ { "literal", 0, 1, 0 }, /* CODE */
+ { "command", 0, 1, 0 }, /* COMMAND */
+ { "envar", 0, 1, 0 }, /* ENV */
+ { "filename", 0, 1, 0 }, /* FILE */
+ { "option", 0, 1, 0 }, /* OPTION */
+ { "literal", 0, 1, 0 }, /* SAMP */
+ { "userinput", 0, 1, 0 }, /* KBD */
+ { "wordasword", 0, 1, 0 }, /* URL */
+ { "keycap", 0, 1, 0 }, /* KEY */
+ { "replaceable", 0, 1, 0 }, /* VAR */
+ { "", 0, 1, 0 }, /* SC */
+ { "firstterm", 0, 1, 0 }, /* DFN */
+ { "emphasis", 0, 1, 0 }, /* EMPH */
+ { "emphasis", 0, 1, 0 }, /* STRONG */
+ { "citetitle", 0, 1, 0 }, /* CITE */
+ { "", 0, 1, 0 }, /* NOTFIXEDWIDTH */
+ { "wordasword", 0, 1, 0 }, /* I */
+ { "emphasis", 0, 1, 0 }, /* B */
+ { "", 0, 1, 0 }, /* R */
+ { "", 0, 1, 0 }, /* SLANTED */
+ { "", 0, 1, 0 }, /* SANSSERIF */
+
+ { "", 0, 0, 0 }, /* EXDENT */
+
+ { "title", 0, 0, 0 },
+ { "", 1, 0, 0 }, /* IFINFO */
+ { "", 0, 0, 0 }, /* SP */
+ { "", 1, 0, 0 }, /* CENTER */
+ { "", 0, 0, 0 }, /* DIRCATEGORY */
+ { "blockquote", 1, 0, 0 }, /* QUOTATION */
+ { "screen", 0, 0, 1 }, /* EXAMPLE */
+ { "screen", 0, 0, 1 }, /* SMALLEXAMPLE */
+ { "programlisting", 0, 0, 1 }, /* LISP */
+ { "programlisting", 0, 0, 1 }, /* SMALLLISP */
+ { "", 1, 0, 0 }, /* CARTOUCHE */
+ { "", 1, 0, 0 }, /* COPYING */
+ { "screen", 0, 1, 1 }, /* FORMAT */
+ { "screen", 0, 1, 1 }, /* SMALLFORMAT */
+ { "literallayout", 0, 1, 1 }, /* DISPLAY */
+ { "literallayout", 0, 1, 1 }, /* SMALLDISPLAY */
+ { "screen", 0, 0, 1 }, /* VERBATIM */
+ { "footnote", 0, 1, 0 },
+ { "lineannotation", 0, 1, 0 },
+
+ { "tip", 1, 0, 0 },
+ { "note", 1, 0, 0 },
+ { "important", 1, 0, 0 },
+ { "warning", 1, 0, 0 },
+ { "caution", 1, 0, 0 },
+
+ { "itemizedlist", 0, 0, 0 }, /* ITEMIZE */
+ { "", 0, 0, 0 }, /* ITEMFUNCTION */
+ { "listitem", 1, 0, 0 }, /* ITEM */
+ { "orderedlist", 0, 0, 0 }, /* ENUMERATE */
+ { "variablelist", 0, 0, 0 }, /* TABLE */
+ { "varlistentry", 0, 0, 0 }, /* TABLEITEM */
+ { "term", 0, 0, 0 }, /* TABLETERM */
+
+ { "indexterm", 0, 1, 0 }, /* INDEXTERM */
+
+ { "", 0, 1, 0 }, /* MATH */
+
+ { "", 0, 1, 0 }, /* DIMENSION */
+
+ { "", 0, 1, 0 }, /* CLICKSEQUENCE */
+ { "", 0, 1, 0 }, /* CLICK */
+
+ { "xref", 0, 1, 0 }, /* XREF */
+ { "link", 0, 1, 0 }, /* XREFNODENAME */
+ { "", 0, 1, 0 }, /* XREFINFONAME */
+ { "", 0, 1, 0 }, /* XREFPRINTEDDESC */
+ { "", 0, 1, 0 }, /* XREFINFOFILE */
+ { "", 0, 1, 0 }, /* XREFPRINTEDNAME */
+
+ { "", 0, 1, 0 }, /* INFOREF */
+ { "", 0, 1, 0 }, /* INFOREFNODENAME */
+ { "", 0, 1, 0 }, /* INFOREFREFNAME */
+ { "", 0, 1, 0 }, /* INFOREFINFONAME */
+
+ { "ulink", 0, 1, 0 }, /* UREF */
+ { "", 0, 1, 0 }, /* UREFURL */
+ { "", 0, 1, 0 }, /* UREFDESC */
+ { "", 0, 1, 0 }, /* UREFREPLACEMENT */
+
+ { "ulink", 0, 1, 0 }, /* EMAIL */
+ { "email", 0, 1, 0 }, /* EMAILADDRESS */
+ { "", 0, 1, 0 }, /* EMAILNAME */
+
+ { "", 0, 0, 0 }, /* GROUP */
+ { "", 1, 0, 0 }, /* FLOAT */
+ { "", 0, 0, 0 }, /* FLOATTYPE */
+ { "", 0, 0, 0 }, /* FLOATPOS */
+ { "", 0, 0, 0 }, /* CAPTION */
+ { "", 0, 0, 0 }, /* SHORTCAPTION */
+
+ { "table", 0, 1, 0 },
+ { "figure", 0, 1, 0 },
+ { "example", 1, 1, 0 },
+ { "sidebar", 1, 0, 0 },
+
+ { "index", 0, 0, 0 }, /* PRINTINDEX */
+ { "", 0, 1, 0 }, /* LISTOFFLOATS */
+ { "anchor", 0, 1, 0 }, /* ANCHOR */
+
+ { "", 0, 0, 0 }, /* IMAGE */
+ { "inlinemediaobject", 0, 1, 0 }, /* INLINEIMAGE */
+ { "", 0, 0, 0 }, /* IMAGEALTTEXT */
+
+ { "primary", 0, 1, 0 }, /* PRIMARY */
+ { "secondary", 0, 1, 0 },
+ { "informalfigure", 0, 0, 0 },
+ { "mediaobject", 0, 0, 0 },
+ { "imageobject", 0, 1, 0 },
+ { "imagedata", 0, 1, 0 },
+ { "textobject", 0, 1, 0 },
+ { "indexentry", 0, 0, 0 },
+ { "primaryie", 0, 0, 0 },
+ { "secondaryie", 0, 0, 0 },
+ { "indexdiv", 0, 0, 0 },
+ { "informaltable", 0, 0, 0 },
+ { "tgroup", 0, 0, 0 },
+ { "colspec", 0, 0, 0 },
+ { "thead", 0, 0, 0 },
+ { "tbody", 0, 0, 0 },
+ { "entry", 0, 0, 0 },
+ { "row", 0, 0, 0 },
+ { "bookinfo", 0, 0, 0 },
+ { "abstract", 1, 0, 0 },
+ { "replaceable", 0, 0, 0 },
+ { "envar", 0, 1, 0 },
+ { "comment", 0, 0, 0 },
+ { "function", 0, 1, 0 },
+ { "legalnotice", 1, 0, 0 },
+
+ { "", 0, 0, 0 }, /* CONTENTS (xml) */
+ { "", 0, 0, 0 }, /* SHORTCONTENTS (xml) */
+ { "", 0, 0, 0 }, /* DOCUMENT LANGUAGE (xml) */
+
+ { "", 0, 0, 0 }, /* SETVALUE (xml) */
+ { "", 0, 0, 0 }, /* CLEARVALUE (xml) */
+
+ { "informalfigure", 1, 0, 0 }, /* DEFINITION */
+ { "synopsis", 0, 0, 1 }, /* DEFINITIONTERM */
+ { "blockquote", 1, 0, 0 }, /* DEFINITIONITEM (xml) */
+ { "", 0, 0, 0 }, /* DEFCATEGORY (xml) */
+ { "function", 0, 0, 0 }, /* DEFFUNCTION */
+ { "varname", 0, 0, 0 }, /* DEFVARIABLE */
+ { "replaceable", 0, 0, 0 }, /* DEFPARAM */
+ { "", 0, 0, 0 }, /* DEFDELIMITER (xml) */
+ { "returnvalue", 0, 0, 0 }, /* DEFTYPE */
+ { "type", 0, 0, 0 }, /* DEFPARAMTYPE */
+ { "structname", 0, 0, 0 }, /* DEFDATATYPE */
+ { "classname", 0, 0, 0 }, /* DEFCLASS */
+ { "property", 0, 0, 0 }, /* DEFCLASSVAR */
+ { "methodname", 0, 0, 0 }, /* DEFOPERATION */
+ { "", 0, 0, 0 }, /* FRENCHSPACING */
+
+ { "para", 0, 0, 0 } /* Must be last */
+ /* name / contains para / contained in para / preserve space */
+};
+
+element *xml_element_list = NULL;
+
+
+typedef struct _replace_element
+{
+ int element_to_replace;
+ int element_containing;
+ int element_replacing;
+} replace_element;
+
+/* Elements to replace - Docbook only
+ -------------------
+ if `element_to_replace' have to be inserted
+ as a child of `element_containing,'
+ use `element_replacing' instead.
+
+ A value of `-1' for element_replacing means `do not use any element.'
+*/
+
+replace_element replace_elements [] = {
+ { I, TABLETERM, EMPH },
+ { B, TABLETERM, EMPH },
+ { TT, CODE, -1 },
+ { EXAMPLE, DISPLAY, -1 },
+ { CODE, DFN, -1 },
+ { CODE, VAR, -1 },
+ { EMPH, CODE, REPLACEABLE },
+ { VAR, VAR, -1},
+ { VAR, B, EMPH},
+ { B, CODE, ENVAR},
+ { CODE, I, EMPH},
+ { SAMP, VAR, -1 },
+ { FORMAT, BOOKINFO, ABSTRACT },
+ { QUOTATION, ABSTRACT, -1},
+ { LINEANNOTATION, LINEANNOTATION, -1 },
+ { LEGALNOTICE, ABSTRACT, -1 },
+ { QUOTATION, QUOTATION, -1 },
+ /* Formal versions of table and image elements. */
+ { MULTITABLE, FLOAT, FLOATTABLE },
+ { INFORMALFIGURE, FLOAT, FLOATFIGURE },
+ { CARTOUCHE, FLOAT, FLOATCARTOUCHE },
+ /* Unnecessary markup in @defun blocks. */
+ { VAR, DEFPARAM, -1 },
+ { CODE, DEFTYPE, -1 },
+ /* Add your elements to replace here */
+ {-1, 0, 0}
+};
+
+int xml_in_menu_entry = 0;
+int xml_in_menu_entry_comment = 0;
+int xml_node_open = 0;
+int xml_node_level = -1;
+int xml_in_para = 0;
+int xml_just_after_element = 0;
+int xml_keep_space = 0;
+
+int xml_no_indent = 0;
+
+int xml_no_para = 0;
+char *xml_node_id = NULL;
+
+/* Currently sorting the index. */
+int xml_sort_index = 0;
+
+int xml_in_xref_token = 0;
+int xml_in_bookinfo = 0;
+int xml_in_book_title = 0;
+int xml_in_abstract = 0;
+
+/* Non-zero if we are handling an element that can appear between
+ @item and @itemx, @deffn and @deffnx. */
+int xml_dont_touch_items_defs = 0;
+
+/* We need to keep footnote state, because elements inside footnote may try
+ to close the previous parent para. */
+static int xml_in_footnote = 0;
+
+static int xml_after_table_term = 0;
+static int book_started = 0;
+static int first_section_opened = 0;
+
+static int xml_in_tableitem[256];
+static int xml_in_item[256];
+static int xml_table_level = 0;
+
+static int xml_in_def_item[256];
+static int xml_definition_level = 0;
+int xml_after_def_term = 0;
+
+static int in_table_title = 0;
+
+static int in_indexentry = 0;
+static int in_secondary = 0;
+static int in_indexterm = 0;
+
+/* Technically there are certain Unicode characters which cannot
+ appear. Trying to implement the real rules would lead to deep
+ waters. So essentially just pass along what we are given. Some
+ references:
+ http://www.w3.org/TR/xml-id/
+ -> http://www.w3.org/TR/xml-names11/#NT-NCName
+ -> http://www.w3.org/TR/xml11/#NT-NameChar
+*/
+
+char *
+xml_id (char *id)
+{
+ char *tem = xmalloc (strlen (id) + 1);
+ char *p = tem;
+ strcpy (tem, id);
+ while (*p)
+ {
+ if (strchr (":\" \t\f\r\n", *p))
+ *p = '-';
+ p++;
+ }
+ p = tem;
+
+ /* First character cannot be a number. Clearly we should make this
+ dependent on the actual numeral found. */
+ if (strchr ("0123456789", *p))
+ *p = 'i';
+
+ return tem;
+}
+
+int
+xml_element (char *name)
+{
+ int i;
+ for (i = 0; i<=PARA; i++)
+ {
+ if (mbscasecmp (name, texinfoml_element_list[i].name) == 0)
+ return i;
+ }
+ printf ("Error xml_element\n");
+ return -1;
+}
+
+void
+xml_begin_document (const char *output_basename)
+{
+ if (book_started)
+ return;
+
+ book_started = 1;
+
+ /* Make sure this is the very first string of the output document. */
+ output_paragraph_offset = 0;
+
+ insert_string ("<?xml version=\"1.0\"");
+
+ /* At this point, we register a delayed writing for document encoding,
+ so in the end, proper encoding attribute will be inserted here.
+ Since the user is unaware that we are implicitly executing this
+ command, we should disable warnings temporarily, in order to avoid
+ possible confusion. (ie. if the output is not seekable,
+ register_delayed_write issues a warning.) */
+ {
+ extern int print_warnings;
+ int save_print_warnings = print_warnings;
+ print_warnings = 0;
+ register_delayed_write ("@documentencoding");
+ print_warnings = save_print_warnings;
+ }
+
+ insert_string ("?>\n");
+
+ if (docbook)
+ {
+ insert_string ("<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\" \"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\" [\n <!ENTITY tex \"TeX\">\n <!ENTITY latex \"LaTeX\">\n]>");
+ xml_element_list = docbook_element_list;
+ }
+ else
+ {
+ insert_string ("<!DOCTYPE texinfo PUBLIC \"-//GNU//DTD TexinfoML V");
+ insert_string (VERSION);
+ insert_string ("//EN\" \"http://www.gnu.org/software/texinfo/dtd/");
+ insert_string (VERSION);
+ insert_string ("/texinfo.dtd\">");
+ xml_element_list = texinfoml_element_list;
+ }
+
+ if (strcmp (xml_element_list[PARA].name, "para"))
+ {
+ warning ("internal error: xml_element_list table inconsistent");
+ xexit (-1);
+ }
+
+ if (language_code != last_language_code)
+ {
+ if (docbook)
+ /* The toplevel <book> element needs an id attribute if you want
+ to use the chunk.xml feature of the Docbook-XSL stylesheets.
+ Might as well use the output filename? */
+ xml_insert_element_with_attribute (TEXINFO, START,
+ "id=\"%s\" lang=\"%s\"",
+ output_basename,
+ language_table[language_code].abbrev);
+ else
+ xml_insert_element_with_attribute (TEXINFO, START,
+ "xml:lang=\"%s\"",
+ language_table[language_code].abbrev);
+ }
+ if (!docbook)
+ {
+ xml_insert_element (SETFILENAME, START);
+ insert_string (output_basename);
+ xml_insert_element (SETFILENAME, END);
+ }
+}
+
+/* */
+static int element_stack[256];
+static int element_stack_index = 0;
+
+static int
+xml_current_element (void)
+{
+ assert (element_stack_index > 0);
+ return element_stack[element_stack_index-1];
+}
+
+static void
+xml_push_current_element (int elt)
+{
+ element_stack[element_stack_index++] = elt;
+ if (element_stack_index > 200) /* fixxme, no hard limits */
+ warning ("internal error: xml stack overflow (%d - %s)",
+ element_stack_index, xml_element_list[elt].name);
+}
+
+static void
+xml_pop_current_element (void)
+{
+ element_stack_index--;
+ if (element_stack_index < 0)
+ {
+ warning ("internal error: xml stack underflow (index %d)",
+ element_stack_index);
+ element_stack_index = 0;
+ }
+}
+
+int
+xml_current_stack_index (void)
+{
+ return element_stack_index;
+}
+
+void
+xml_end_current_element (void)
+{
+ xml_insert_element (xml_current_element (), END);
+}
+
+static void
+xml_indent (void)
+{
+ if (xml_indentation_increment > 0)
+ {
+ int i;
+ if (output_paragraph[output_paragraph_offset-1] != '\n')
+ insert ('\n');
+ for (i = 0; i < element_stack_index * xml_indentation_increment; i++)
+ insert (' ');
+ }
+}
+
+void
+xml_start_para (void)
+{
+ if (xml_in_para || xml_in_footnote
+ || !xml_element_list[xml_current_element()].contains_para)
+ return;
+
+ while (output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+ xml_indent ();
+
+ insert_string ("<para");
+ if (xml_no_indent)
+ insert_string (" role=\"continues\"");
+ insert_string (">");
+ xml_no_indent = 0;
+ xml_in_para = 1;
+}
+
+void
+xml_end_para (void)
+{
+ if (!xml_in_para || xml_in_footnote)
+ return;
+
+ while (output_paragraph_offset > 0
+ && cr_or_whitespace(output_paragraph[output_paragraph_offset-1]))
+ output_paragraph_offset--;
+
+ insert_string ("</para>");
+ if (xml_indentation_increment > 0)
+ insert ('\n');
+ xml_in_para = 0;
+}
+
+void
+xml_end_document (void)
+{
+ if (xml_node_open)
+ {
+ if (xml_node_level != -1)
+ {
+ xml_close_sections (xml_node_level);
+ xml_node_level = -1;
+ }
+ xml_insert_element (NODE, END);
+ }
+ else
+ xml_close_sections (xml_node_level);
+
+ xml_insert_element (TEXINFO, END);
+ if (xml_indentation_increment == 0)
+ insert ('\n');
+ insert_string ("<!-- Keep this comment at the end of the file\n\
+Local variables:\n\
+mode: sgml\n\
+sgml-indent-step:1\n\
+sgml-indent-data:nil\n\
+End:\n\
+-->\n");
+ if (element_stack_index != 0)
+ error ("Element stack index : %d\n", element_stack_index);
+}
+
+/* MUST be 0 or 1, not true or false values */
+static int start_element_inserted = 1;
+
+/* NOTE: We use `elt' rather than `element' in the argument list of
+ the next function, since otherwise the Solaris SUNWspro compiler
+ barfs because `element' is a typedef declared near the beginning of
+ this file. */
+void
+#if defined (VA_FPRINTF) && __STDC__
+xml_insert_element_with_attribute (int elt, int arg, char *format, ...)
+#else
+xml_insert_element_with_attribute (elt, arg, format, va_alist)
+ int elt;
+ int arg;
+ char *format;
+ va_dcl
+#endif
+{
+ /* Look at the replace_elements table to see if we have to change the element */
+ if (xml_sort_index)
+ return;
+ if (docbook && element_stack_index > 0)
+ {
+ replace_element *element_list = replace_elements;
+ while (element_list->element_to_replace >= 0)
+ {
+ if ( ( (arg == START) &&
+ (element_list->element_containing == xml_current_element ()) &&
+ (element_list->element_to_replace == elt) ) ||
+ ( (arg == END) &&
+ (element_list->element_containing == element_stack[element_stack_index-1-start_element_inserted]) &&
+ (element_list->element_to_replace == elt) ) )
+ {
+ elt = element_list->element_replacing;
+ break;
+ }
+ element_list ++;
+ }
+
+ /* Forget the element */
+ if (elt < 0)
+ {
+ if (arg == START)
+ start_element_inserted = 0;
+ else
+ /* Replace the default value, for the next time */
+ start_element_inserted = 1;
+ return;
+ }
+ }
+
+ if (!book_started)
+ return;
+
+ if (!xml_dont_touch_items_defs && arg == START)
+ {
+ if (xml_after_table_term && elt != TABLETERM && xml_table_level
+ && !xml_in_item[xml_table_level])
+ {
+ xml_after_table_term = 0;
+ xml_insert_element (ITEM, START);
+ xml_in_item[xml_table_level] = 1;
+ }
+ else if (xml_after_def_term && elt != DEFINITIONTERM)
+ {
+ xml_after_def_term = 0;
+ xml_insert_element (DEFINITIONITEM, START);
+ xml_in_def_item[xml_definition_level] = 1;
+ }
+ }
+
+ if (docbook && !only_macro_expansion && (in_menu || in_detailmenu))
+ return;
+
+ if (executing_string && arg == END)
+ switch (elt)
+ {
+ case TABLEITEM:
+ xml_in_tableitem[xml_table_level] = 0;
+ break;
+ case ITEM:
+ xml_in_item[xml_table_level] = 0;
+ break;
+ case DEFINITIONTERM:
+ xml_in_def_item[xml_definition_level] = 0;
+ break;
+ }
+
+ /* We are special-casing FIGURE element for docbook. It does appear in
+ the tag stack, but not in the output. This is to make element replacement
+ work beautifully. */
+ if (docbook && elt == FLOAT)
+ {
+ if (arg == START)
+ xml_push_current_element (elt);
+ else
+ xml_pop_current_element ();
+ return;
+ }
+
+ if (!xml_element_list[elt].name || !strlen (xml_element_list[elt].name))
+ {
+ /*printf ("Warning: Inserting empty element %d\n", elt);*/
+ return;
+ }
+
+ if (arg == START && !xml_in_para && !xml_no_para
+ && xml_element_list[elt].contained_in_para)
+ xml_start_para ();
+
+ if (arg == START && xml_in_para && !xml_element_list[elt].contained_in_para)
+ xml_end_para ();
+
+ if (arg == END && xml_in_para && !xml_element_list[elt].contained_in_para)
+ xml_end_para ();
+
+ /* Oddly, we want to do the same thing whether we starting or ending
+ the detailmenu -- close any existing menu elements before inserting
+ the detailmenu element. */
+ if (elt == DETAILMENU && xml_in_menu_entry)
+ {
+ if (xml_in_menu_entry_comment)
+ {
+ xml_insert_element (MENUCOMMENT, END);
+ xml_in_menu_entry_comment = 0;
+ }
+ xml_insert_element (MENUENTRY, END);
+ xml_in_menu_entry = 0;
+ }
+
+ if (docbook && xml_table_level && !in_table_title
+ && !xml_in_tableitem[xml_table_level] && !xml_in_item[xml_table_level]
+ && arg == START && elt != TABLEITEM && elt != TABLETERM
+ && !in_indexterm && xml_current_element() == TABLE)
+ {
+ in_table_title = 1;
+ xml_insert_element (TITLE, START);
+ }
+
+ if (arg == START && !xml_in_para && !xml_keep_space
+ && !xml_element_list[elt].contained_in_para)
+ xml_indent ();
+
+ if (arg == START)
+ xml_push_current_element (elt);
+ else
+ xml_pop_current_element ();
+
+ /* Eat one newline before </example> and the like. */
+ if (!docbook && arg == END
+ && (xml_element_list[elt].keep_space || elt == GROUP)
+ && output_paragraph_offset > 0
+ && output_paragraph[output_paragraph_offset-1] == '\n')
+ output_paragraph_offset--;
+
+ /* And eat whitespace before </entry> in @multitables. */
+ if (arg == END && elt == ENTRY)
+ while (output_paragraph_offset > 0 &&
+ cr_or_whitespace(output_paragraph[output_paragraph_offset-1]))
+ output_paragraph_offset--;
+
+ /* Indent elements that can contain <para>. */
+ if (arg == END && !xml_in_para && !xml_keep_space
+ && xml_element_list[elt].contains_para)
+ xml_indent ();
+
+ /* Here are the elements we want indented. These do not contain <para>
+ directly. */
+ if (arg == END && (elt == MENUENTRY || elt == ITEMIZE || elt == ENUMERATE
+ || elt == TABLEITEM || elt == TABLE
+ || elt == MULTITABLE || elt == TGROUP || elt == THEAD || elt == TBODY
+ || elt == ROW || elt == INFORMALFIGURE
+ || (!docbook && (elt == DEFINITION || elt == DEFINITIONTERM))))
+ xml_indent ();
+
+ insert ('<');
+ if (arg == END)
+ insert ('/');
+ insert_string (xml_element_list[elt].name);
+
+ /* printf ("%s ", xml_element_list[elt].name);*/
+
+ if (format)
+ {
+ char temp_string[2000]; /* xx no fixed limits */
+#ifdef VA_SPRINTF
+ va_list ap;
+#endif
+
+ VA_START (ap, format);
+#ifdef VA_SPRINTF
+ VA_SPRINTF (temp_string, format, ap);
+#else
+ sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+ insert (' ');
+ insert_string (temp_string);
+ va_end (ap);
+ }
+
+ if (arg == START && xml_node_id && elt != NODENAME)
+ {
+ insert_string (" id=\"");
+ insert_string (xml_node_id);
+ insert ('"');
+ free (xml_node_id);
+ xml_node_id = NULL;
+ }
+
+ if (xml_element_list[elt].keep_space)
+ {
+ if (arg == START)
+ {
+ if (!docbook)
+ insert_string (" xml:space=\"preserve\"");
+ xml_keep_space++;
+ }
+ else
+ xml_keep_space--;
+ }
+
+ insert ('>');
+
+ if (!xml_in_para && !xml_element_list[elt].contained_in_para
+ && xml_element_list[elt].contains_para && xml_indentation_increment > 0)
+ insert ('\n');
+
+ xml_just_after_element = 1;
+}
+
+/* See the NOTE before xml_insert_element_with_attribute, for why we
+ use `elt' rather than `element' here. */
+void
+xml_insert_element (int elt, int arg)
+{
+ xml_insert_element_with_attribute (elt, arg, NULL);
+}
+
+void
+xml_insert_entity (char *entity_name)
+{
+ int saved_escape_html = escape_html;
+
+ if (xml)
+ {
+ if (!book_started)
+ return;
+ if (docbook && !only_macro_expansion && (in_menu || in_detailmenu))
+ return;
+
+ if (!xml_in_para && !xml_no_para && !only_macro_expansion
+ && xml_element_list[xml_current_element ()].contains_para
+ && !in_fixed_width_font)
+ xml_start_para ();
+ }
+
+ escape_html = 0;
+ add_char ('&');
+ escape_html = saved_escape_html;
+ insert_string (entity_name);
+ add_char (';');
+}
+
+typedef struct _xml_section xml_section;
+struct _xml_section {
+ int level;
+ char *name;
+ xml_section *prev;
+};
+
+xml_section *last_section = NULL;
+
+void
+xml_begin_node (void)
+{
+ first_section_opened = 1;
+ if (xml_in_abstract)
+ {
+ xml_insert_element (ABSTRACT, END);
+ xml_in_abstract = 0;
+ }
+ if (xml_in_bookinfo)
+ {
+ xml_insert_element (BOOKINFO, END);
+ xml_in_bookinfo = 0;
+ }
+ if (xml_node_open && !docbook)
+ {
+ if (xml_node_level != -1)
+ {
+ xml_close_sections (xml_node_level);
+ xml_node_level = -1;
+ }
+ xml_insert_element (NODE, END);
+ }
+ xml_insert_element (NODE, START);
+ xml_node_open = 1;
+}
+
+void
+xml_close_sections (int level)
+{
+ if (!first_section_opened)
+ {
+ if (xml_in_abstract)
+ {
+ xml_insert_element (ABSTRACT, END);
+ xml_in_abstract = 0;
+ }
+ if (xml_in_bookinfo)
+ {
+ xml_insert_element (BOOKINFO, END);
+ xml_in_bookinfo = 0;
+ }
+ first_section_opened = 1;
+ }
+
+ while (last_section && last_section->level >= level)
+ {
+ xml_section *temp = last_section;
+ xml_insert_element (xml_element(last_section->name), END);
+ temp = last_section;
+ last_section = last_section->prev;
+ free (temp->name);
+ free (temp);
+ }
+}
+
+void
+xml_open_section (int level, char *name)
+{
+ xml_section *sect = (xml_section *) xmalloc (sizeof (xml_section));
+
+ sect->level = level;
+ sect->name = xmalloc (1 + strlen (name));
+ strcpy (sect->name, name);
+ sect->prev = last_section;
+ last_section = sect;
+
+ if (xml_node_open && xml_node_level == -1)
+ xml_node_level = level;
+}
+
+void
+xml_start_menu_entry (char *tem)
+{
+ char *string;
+ discard_until ("* ");
+
+ /* The line number was already incremented in reader_loop when we
+ saw the newline, and discard_until has now incremented again. */
+ line_number--;
+
+ if (xml_in_menu_entry)
+ {
+ if (xml_in_menu_entry_comment)
+ {
+ xml_insert_element (MENUCOMMENT, END);
+ xml_in_menu_entry_comment = 0;
+ }
+ xml_insert_element (MENUENTRY, END);
+ xml_in_menu_entry = 0;
+ }
+ xml_insert_element (MENUENTRY, START);
+ xml_in_menu_entry = 1;
+
+ xml_insert_element (MENUNODE, START);
+ string = expansion (tem, 0);
+ add_word (string);
+ xml_insert_element (MENUNODE, END);
+ free (string);
+
+ /* The menu item may use macros, so expand them now. */
+ xml_insert_element (MENUTITLE, START);
+ only_macro_expansion++;
+ get_until_in_line (1, ":", &string);
+ only_macro_expansion--;
+ execute_string ("%s", string); /* get escaping done */
+ xml_insert_element (MENUTITLE, END);
+ free (string);
+
+ if (looking_at ("::"))
+ discard_until (":");
+ else
+ { /* discard the node name */
+ get_until_in_line (0, ".", &string);
+ free (string);
+ }
+ input_text_offset++; /* discard the second colon or the period */
+ skip_whitespace_and_newlines();
+ xml_insert_element (MENUCOMMENT, START);
+ xml_in_menu_entry_comment ++;
+}
+
+void
+xml_end_menu (void)
+{
+ if (xml_in_menu_entry)
+ {
+ if (xml_in_menu_entry_comment)
+ {
+ xml_insert_element (MENUCOMMENT, END);
+ xml_in_menu_entry_comment --;
+ }
+ xml_insert_element (MENUENTRY, END);
+ xml_in_menu_entry--;
+ }
+ xml_insert_element (MENU, END);
+}
+
+static int xml_last_character;
+
+void
+xml_add_char (int character)
+{
+ if (!book_started)
+ return;
+ if (docbook && !only_macro_expansion && (in_menu || in_detailmenu))
+ return;
+
+ if (docbook && xml_table_level && !in_table_title
+ && !xml_in_item[xml_table_level] && !xml_in_tableitem[xml_table_level]
+ && !cr_or_whitespace (character) && !in_indexterm)
+ {
+ in_table_title = 1;
+ xml_insert_element (TITLE, START);
+ }
+
+ if (!first_section_opened && !xml_in_abstract && !xml_in_book_title
+ && !xml_no_para && character != '\r' && character != '\n'
+ && character != ' ' && !is_in_insertion_of_type (copying))
+ {
+ if (!xml_in_bookinfo)
+ {
+ xml_insert_element (BOOKINFO, START);
+ xml_in_bookinfo = 1;
+ }
+ xml_insert_element (ABSTRACT, START);
+ xml_in_abstract = 1;
+ }
+
+ if (!xml_sort_index && !xml_in_xref_token && !xml_dont_touch_items_defs)
+ {
+ if (xml_after_table_term && xml_table_level
+ && !xml_in_item[xml_table_level])
+ {
+ xml_after_table_term = 0;
+ xml_insert_element (ITEM, START);
+ xml_in_item[xml_table_level] = 1;
+ }
+ else if (xml_after_def_term)
+ {
+ xml_after_def_term = 0;
+ xml_insert_element (DEFINITIONITEM, START);
+ xml_in_def_item[xml_definition_level] = 1;
+ }
+ }
+
+ if (xml_just_after_element && !xml_in_para && !inhibit_paragraph_indentation)
+ {
+ if (character == '\r' || character == '\n' || character == '\t' || character == ' ')
+ return;
+ xml_just_after_element = 0;
+ }
+
+ if (element_stack_index > 0
+ && xml_element_list[xml_current_element()].contains_para
+ && !xml_in_para && !only_macro_expansion && !xml_no_para
+ && !cr_or_whitespace (character) && !in_fixed_width_font)
+ xml_start_para ();
+
+ if (xml_in_para && character == '\n' && xml_last_character == '\n'
+ && !only_macro_expansion && !xml_no_para
+ && xml_element_list[xml_current_element()].contains_para )
+ {
+ xml_end_para ();
+ xml_just_after_element = 1;
+ return;
+ }
+
+ if (xml_in_menu_entry_comment && character == '\n' && xml_last_character == '\n')
+ {
+ xml_insert_element (MENUCOMMENT, END);
+ xml_in_menu_entry_comment = 0;
+ xml_insert_element (MENUENTRY, END);
+ xml_in_menu_entry = 0;
+ }
+
+ if (xml_in_menu_entry_comment && whitespace(character)
+ && cr_or_whitespace(xml_last_character))
+ return;
+
+ if (character == '\n' && !xml_in_para && !inhibit_paragraph_indentation)
+ return;
+
+ xml_last_character = character;
+
+ if (character == '&' && escape_html)
+ insert_string ("&amp;");
+ else if (character == '<' && escape_html)
+ insert_string ("&lt;");
+ else if (character == '>' && escape_html)
+ insert_string ("&gt;");
+ else if (character == '\n' && !xml_keep_space)
+ {
+ if (!xml_in_para && xml_just_after_element && !multitable_active)
+ return;
+ else
+ insert (docbook ? '\n' : ' ');
+ }
+ else
+ insert (character);
+
+ return;
+}
+
+void
+xml_insert_footnote (char *note)
+{
+ if (!xml_in_para)
+ xml_start_para ();
+
+ xml_in_footnote = 1;
+ xml_insert_element (FOOTNOTE, START);
+ insert_string ("<para>");
+ execute_string ("%s", note);
+ insert_string ("</para>");
+ xml_insert_element (FOOTNOTE, END);
+ xml_in_footnote = 0;
+}
+
+/* We need to keep the quotation stack ourself, because insertion_stack
+ loses item_function when we are closing the block, so we don't know
+ what to close then. */
+typedef struct quotation_elt
+{
+ struct quotation_elt *next;
+ char *type;
+} QUOTATION_ELT;
+
+static QUOTATION_ELT *quotation_stack = NULL;
+
+void
+xml_insert_quotation (char *type, int arg)
+{
+ int quotation_started = 0;
+
+ if (arg == START)
+ {
+ QUOTATION_ELT *new = xmalloc (sizeof (QUOTATION_ELT));
+ new->type = xstrdup (type);
+ new->next = quotation_stack;
+ quotation_stack = new;
+ }
+ else
+ type = quotation_stack->type;
+
+ /* Make use of special quotation styles of Docbook if we can. */
+ if (docbook && strlen (type))
+ {
+ /* Let's assume it started. */
+ quotation_started = 1;
+
+ if (mbscasecmp (type, "tip") == 0)
+ xml_insert_element (TIP, arg);
+ else if (mbscasecmp (type, "note") == 0)
+ xml_insert_element (NOTE, arg);
+ else if (mbscasecmp (type, "important") == 0)
+ xml_insert_element (IMPORTANT, arg);
+ else if (mbscasecmp (type, "warning") == 0)
+ xml_insert_element (WARNING, arg);
+ else if (mbscasecmp (type, "caution") == 0)
+ xml_insert_element (CAUTION, arg);
+ else
+ /* Didn't find a known quotation type :\ */
+ quotation_started = 0;
+ }
+
+ if (!quotation_started)
+ {
+ xml_insert_element (QUOTATION, arg);
+ if (strlen(type) && arg == START)
+ execute_string ("@b{%s:} ", type);
+ }
+
+ if (arg == END)
+ {
+ QUOTATION_ELT *temp = quotation_stack;
+ if (temp == NULL)
+ return;
+ quotation_stack = quotation_stack->next;
+ free(temp->type);
+ free(temp);
+ }
+}
+
+/* Starting generic docbook floats. Just starts elt with correct label
+ and id attributes, and inserts title. */
+void
+xml_begin_docbook_float (int elt)
+{
+ if (current_float_used_title ()) /* in a nested float */
+ {
+ xml_insert_element (elt, START); /* just insert the tag */
+ return;
+ }
+
+
+ /* OK, need the title, tag, etc. */
+ if (elt == CARTOUCHE) /* no labels on <sidebar> */
+ {
+ if (strlen (current_float_id ()) == 0)
+ xml_insert_element (elt, START);
+ else
+ xml_insert_element_with_attribute (elt, START,
+ "id=\"%s\"", xml_id (current_float_id ()));
+ }
+ else if (strlen (current_float_id ()) == 0)
+ xml_insert_element_with_attribute (elt, START, "label=\"\"");
+ else
+ xml_insert_element_with_attribute (elt, START,
+ "id=\"%s\" label=\"%s\"", xml_id (current_float_id ()),
+ current_float_number ());
+
+ xml_insert_element (TITLE, START);
+ execute_string ("%s", current_float_title ());
+ xml_insert_element (TITLE, END);
+
+ current_float_set_title_used (); /* mark this title, tag, etc used */
+}
+
+/*
+ * Lists and Tables
+ */
+void
+xml_begin_table (int type, char *item_function)
+{
+ switch (type)
+ {
+ case ftable:
+ case vtable:
+ case table:
+ /*if (docbook)*/ /* 05-08 */
+ {
+ xml_insert_element (TABLE, START);
+ xml_table_level ++;
+ xml_in_tableitem[xml_table_level] = 0;
+ xml_in_item[xml_table_level] = 0;
+ xml_after_table_term = 0;
+ }
+ break;
+ case itemize:
+ if (!docbook || item_function==default_item_function)
+ xml_insert_element (ITEMIZE, START);
+ else
+ xml_insert_element_with_attribute (ITEMIZE, START,
+ "mark=\"%s\"",
+ (*item_function == COMMAND_PREFIX) ?
+ &item_function[1] : item_function);
+ xml_table_level ++;
+ xml_in_item[xml_table_level] = 0;
+ if (!docbook)
+ {
+ xml_insert_element (ITEMFUNCTION, START);
+ if (*item_function == COMMAND_PREFIX
+ && item_function[strlen (item_function) - 1] != '}'
+ && command_needs_braces (item_function + 1))
+ execute_string ("%s{}", item_function);
+ else
+ execute_string ("%s", item_function);
+ xml_insert_element (ITEMFUNCTION, END);
+ }
+ break;
+ }
+}
+
+void
+xml_end_table (int type)
+{
+ switch (type)
+ {
+ case ftable:
+ case vtable:
+ case table:
+ if (xml_in_item[xml_table_level])
+ {
+ xml_insert_element (ITEM, END);
+ xml_in_item[xml_table_level] = 0;
+ }
+ if (xml_in_tableitem[xml_table_level])
+ {
+ xml_insert_element (TABLEITEM, END);
+ xml_in_tableitem[xml_table_level] = 0;
+ }
+ xml_insert_element (TABLE, END);
+ xml_after_table_term = 0;
+ xml_table_level --;
+
+ break;
+ case itemize:
+ if (xml_in_item[xml_table_level])
+ {
+ xml_insert_element (ITEM, END);
+ xml_in_item[xml_table_level] = 0;
+ }
+ /* gnat-style manual contains an itemized list without items! */
+ if (in_table_title)
+ {
+ xml_insert_element (TITLE, END);
+ in_table_title = 0;
+ }
+ xml_insert_element (ITEMIZE, END);
+ xml_table_level --;
+ break;
+ }
+}
+
+void
+xml_begin_item (void)
+{
+ if (xml_in_item[xml_table_level])
+ xml_insert_element (ITEM, END);
+
+ xml_insert_element (ITEM, START);
+ xml_in_item[xml_table_level] = 1;
+}
+
+void
+xml_begin_table_item (void)
+{
+ if (!xml_after_table_term)
+ {
+ if (xml_in_item[xml_table_level])
+ xml_insert_element (ITEM, END);
+ if (xml_in_tableitem[xml_table_level])
+ xml_insert_element (TABLEITEM, END);
+
+ if (in_table_title)
+ {
+ in_table_title = 0;
+ xml_insert_element (TITLE, END);
+ }
+ xml_insert_element (TABLEITEM, START);
+ }
+ xml_insert_element (TABLETERM, START);
+ xml_in_tableitem[xml_table_level] = 1;
+ xml_in_item[xml_table_level] = 0;
+ xml_after_table_term = 0;
+}
+
+void
+xml_continue_table_item (void)
+{
+ xml_insert_element (TABLETERM, END);
+ xml_after_table_term = 1;
+ xml_in_item[xml_table_level] = 0;
+}
+
+void
+xml_begin_enumerate (char *enum_arg)
+{
+ if (!docbook)
+ xml_insert_element_with_attribute (ENUMERATE, START, "first=\"%s\"", enum_arg);
+ else
+ {
+ if (isdigit (*enum_arg))
+ {
+ int enum_val = atoi (enum_arg);
+
+ /* Have to check the value, not just the first digit. */
+ if (enum_val == 0)
+ xml_insert_element_with_attribute (ENUMERATE, START,
+ "numeration=\"arabic\" role=\"0\"", NULL);
+ else if (enum_val == 1)
+ xml_insert_element_with_attribute (ENUMERATE, START,
+ "numeration=\"arabic\"", NULL);
+ else
+ xml_insert_element_with_attribute (ENUMERATE, START,
+ "continuation=\"continues\" numeration=\"arabic\"", NULL);
+ }
+ else if (isupper (*enum_arg))
+ {
+ if (enum_arg[0] == 'A')
+ xml_insert_element_with_attribute (ENUMERATE, START,
+ "numeration=\"upperalpha\"", NULL);
+ else
+ xml_insert_element_with_attribute (ENUMERATE, START,
+ "continuation=\"continues\" numeration=\"upperalpha\"", NULL);
+ }
+ else
+ {
+ if (enum_arg[0] == 'a')
+ xml_insert_element_with_attribute (ENUMERATE, START,
+ "numeration=\"loweralpha\"", NULL);
+ else
+ xml_insert_element_with_attribute (ENUMERATE, START,
+ "continuation=\"continues\" numeration=\"loweralpha\"", NULL);
+ }
+ }
+ xml_table_level ++;
+ xml_in_item[xml_table_level] = 0;
+}
+
+void
+xml_end_enumerate (void)
+{
+ if (xml_in_item[xml_table_level])
+ {
+ xml_insert_element (ITEM, END);
+ xml_in_item[xml_table_level] = 0;
+ }
+ xml_insert_element (ENUMERATE, END);
+ xml_table_level --;
+}
+
+static void
+xml_insert_text_file (char *name_arg)
+{
+ char *fullname = xmalloc (strlen (name_arg) + 4 + 1);
+ FILE *image_file;
+ strcpy (fullname, name_arg);
+ strcat (fullname, ".txt");
+ image_file = fopen (fullname, "r");
+ if (image_file)
+ {
+ int ch;
+ int save_inhibit_indentation = inhibit_paragraph_indentation;
+ int save_filling_enabled = filling_enabled;
+
+ xml_insert_element (TEXTOBJECT, START);
+ xml_insert_element (DISPLAY, START);
+
+ inhibit_paragraph_indentation = 1;
+ filling_enabled = 0;
+ last_char_was_newline = 0;
+
+ /* Maybe we need to remove the final newline if the image
+ file is only one line to allow in-line images. On the
+ other hand, they could just make the file without a
+ final newline. */
+ while ((ch = getc (image_file)) != EOF)
+ add_char (ch);
+
+ inhibit_paragraph_indentation = save_inhibit_indentation;
+ filling_enabled = save_filling_enabled;
+
+ xml_insert_element (DISPLAY, END);
+ xml_insert_element (TEXTOBJECT, END);
+
+ if (fclose (image_file) != 0)
+ perror (fullname);
+ }
+ else
+ warning (_("@image file `%s' unreadable: %s"), fullname,
+ strerror (errno));
+
+ free (fullname);
+}
+
+/* If NAME.EXT is accessible or FORCE is nonzero, insert a docbook
+ imagedata element for FMT. Return 1 if inserted something, 0 else. */
+
+static int
+try_docbook_image (const char *name, const char *ext, const char *fmt,
+ int force)
+{
+ int used = 0;
+ char *fullname = xmalloc (strlen (name) + 1 + strlen (ext) + 1);
+ sprintf (fullname, "%s.%s", name, ext);
+
+ if (force || access (fullname, R_OK) == 0)
+ {
+ xml_insert_element (IMAGEOBJECT, START);
+ xml_insert_element_with_attribute (IMAGEDATA, START,
+ "fileref=\"%s\" format=\"%s\"", fullname, fmt);
+ xml_insert_element (IMAGEDATA, END);
+ xml_insert_element (IMAGEOBJECT, END);
+ used = 1;
+ }
+
+ free (fullname);
+ return used;
+}
+
+
+void
+xml_insert_docbook_image (char *name_arg)
+{
+ int found = 0;
+ int elt = xml_in_para ? INLINEIMAGE : MEDIAOBJECT;
+
+ if (is_in_insertion_of_type (floatenv))
+ xml_begin_docbook_float (INFORMALFIGURE);
+ else if (!xml_in_para)
+ xml_insert_element (INFORMALFIGURE, START);
+
+ xml_no_para++;
+
+ xml_insert_element (elt, START);
+
+ /* A selected few from http://docbook.org/tdg/en/html/imagedata.html. */
+ if (try_docbook_image (name_arg, "eps", "EPS", 0))
+ found++;
+ if (try_docbook_image (name_arg, "gif", "GIF", 0))
+ found++;
+ if (try_docbook_image (name_arg, "jpg", "JPG", 0))
+ found++;
+ if (try_docbook_image (name_arg, "jpeg", "JPEG", 0))
+ found++;
+ if (try_docbook_image (name_arg, "pdf", "PDF", 0))
+ found++;
+ if (try_docbook_image (name_arg, "png", "PNG", 0))
+ found++;
+ if (try_docbook_image (name_arg, "svg", "SVG", 0))
+ found++;
+
+ /* If no luck so far, just assume we'll eventually have a jpg. */
+ if (!found)
+ try_docbook_image (name_arg, "jpg", "JPG", 1);
+
+ xml_insert_text_file (name_arg);
+ xml_insert_element (elt, END);
+
+ xml_no_para--;
+
+ if (elt == MEDIAOBJECT)
+ xml_insert_element (INFORMALFIGURE, END);
+}
+
+void
+xml_asterisk (void)
+{
+}
+
+
+/*
+ * INDEX
+ */
+/* Used to separate primary and secondary entries in an index -- we need
+ to have real multilivel indexing support, not just string analysis. */
+#define INDEX_SEP "@this string will never appear@" /* was , */
+
+typedef struct
+{
+ char *from;
+ char *to;
+} XML_SYNONYM;
+
+static XML_SYNONYM **xml_synonyms = NULL;
+static int xml_synonyms_count = 0;
+
+void
+xml_insert_indexterm (char *indexterm, char *index)
+{
+ /* @index commands can appear between @item and @itemx, @deffn and @deffnx. */
+ if (!docbook)
+ {
+ /* Check to see if we need to do index redirection per @synindex. */
+ int i;
+ for (i = 0; i < xml_synonyms_count; i++)
+ {
+ if (STREQ (xml_synonyms[i]->from, index))
+ index = xstrdup (xml_synonyms[i]->to);
+ }
+
+ xml_dont_touch_items_defs++;
+ xml_insert_element_with_attribute (INDEXTERM, START, "index=\"%s\"", index);
+ in_indexterm = 1;
+ execute_string ("%s", indexterm);
+ xml_insert_element (INDEXTERM, END);
+ in_indexterm = 0;
+ xml_dont_touch_items_defs--;
+ }
+ else
+ {
+ char *primary = NULL, *secondary = NULL;
+ if (strstr (indexterm+1, INDEX_SEP))
+ {
+ primary = xmalloc (strlen (indexterm) + 1);
+ strcpy (primary, indexterm);
+ secondary = strstr (primary+1, INDEX_SEP);
+ *secondary = '\0';
+ secondary += strlen (INDEX_SEP);
+ }
+ xml_insert_element_with_attribute (INDEXTERM, START, "role=\"%s\"", index);
+ in_indexterm = 1;
+ xml_insert_element (PRIMARY, START);
+ if (primary)
+ execute_string ("%s", primary);
+ else
+ execute_string ("%s", indexterm);
+ xml_insert_element (PRIMARY, END);
+ if (primary)
+ {
+ xml_insert_element (SECONDARY, START);
+ execute_string ("%s", secondary);
+ xml_insert_element (SECONDARY, END);
+ }
+ xml_insert_element (INDEXTERM, END);
+ in_indexterm = 0;
+ }
+}
+
+
+int xml_last_section_output_position = 0;
+static char last_division_letter = ' ';
+static char index_primary[2000]; /** xx no fixed limit */
+static int indexdivempty = 0;
+
+static void
+xml_close_indexentry (void)
+{
+ if (!in_indexentry)
+ return;
+ if (in_secondary)
+ xml_insert_element (SECONDARYIE, END);
+ xml_insert_element (INDEXENTRY, END);
+ in_secondary = 0;
+ in_indexentry = 0;
+}
+
+void
+xml_begin_index (void)
+{
+ if (handling_delayed_writes)
+ {
+ /* We put <INDEX> */
+ xml_insert_element (PRINTINDEX, START);
+ }
+}
+
+void
+xml_end_index (void)
+{
+ xml_insert_element (PRINTINDEX, END);
+}
+
+static void
+xml_index_divide (char *entry)
+{
+ char c;
+ if (strlen (entry) > (strlen (xml_element_list[CODE].name) + 2) &&
+ strncmp (entry+1, xml_element_list[CODE].name, strlen (xml_element_list[CODE].name)) == 0)
+ c = entry[strlen (xml_element_list[CODE].name)+2];
+ else
+ c = entry[0];
+ if (tolower (c) != last_division_letter && isalpha (c))
+ {
+ last_division_letter = tolower (c);
+ xml_close_indexentry ();
+ if (!indexdivempty)
+ {
+ xml_insert_element (INDEXDIV, END);
+ xml_insert_element (INDEXDIV, START);
+ }
+ xml_insert_element (TITLE, START);
+ insert (toupper (c));
+ xml_insert_element (TITLE, END);
+ }
+}
+
+void
+xml_synindex (char *from, char *to)
+{
+ int i, slot;
+
+ slot = -1;
+ for (i = 0; i < xml_synonyms_count; i++)
+ if (!xml_synonyms[i])
+ {
+ slot = i;
+ break;
+ }
+
+ if (slot < 0)
+ {
+ slot = xml_synonyms_count;
+ xml_synonyms_count++;
+
+ xml_synonyms = (XML_SYNONYM **) xrealloc (xml_synonyms,
+ (xml_synonyms_count + 1) * sizeof (XML_SYNONYM *));
+ }
+
+ xml_synonyms[slot] = xmalloc (sizeof (XML_SYNONYM));
+ xml_synonyms[slot]->from = xstrdup (from);
+ xml_synonyms[slot]->to = xstrdup (to);
+}
+
+/*
+ * MULTITABLE
+ */
+
+static int multitable_columns_count;
+static int *multitable_column_widths;
+
+void
+xml_begin_multitable (int ncolumns, int *column_widths)
+{
+ int i;
+ if (docbook)
+ {
+ if (is_in_insertion_of_type (floatenv))
+ xml_begin_docbook_float (MULTITABLE);
+ else
+ xml_insert_element (MULTITABLE, START);
+
+ multitable_columns_count = ncolumns;
+ multitable_column_widths = xmalloc (sizeof (int) * ncolumns);
+ memcpy (multitable_column_widths, column_widths,
+ sizeof (int) * ncolumns);
+
+ xml_no_para = 1;
+ }
+ else
+ {
+ xml_insert_element (MULTITABLE, START);
+ for (i = 0; i < ncolumns; i++)
+ {
+ xml_insert_element (COLSPEC, START);
+ add_word_args ("%d", column_widths[i]);
+ xml_insert_element (COLSPEC, END);
+ }
+ xml_no_para = 1;
+ }
+}
+
+static void
+xml_begin_multitable_group (void)
+{
+ int i;
+
+ xml_insert_element_with_attribute (TGROUP, START, "cols=\"%d\"",
+ multitable_columns_count);
+
+ for (i = 0; i < multitable_columns_count; i++)
+ {
+ xml_insert_element_with_attribute (COLSPEC, START,
+ "colwidth=\"%d*\"", multitable_column_widths[i]);
+ xml_insert_element (COLSPEC, END);
+ }
+}
+
+void
+xml_end_multitable_row (int first_row)
+{
+ if (!first_row)
+ {
+ xml_insert_element (ENTRY, END);
+ xml_insert_element (ROW, END);
+ }
+
+ if (headitem_flag)
+ {
+ if (!first_row)
+ {
+ if (after_headitem)
+ xml_insert_element (THEAD, END);
+ else
+ xml_insert_element (TBODY, END);
+ xml_insert_element (TGROUP, END);
+ }
+
+ xml_begin_multitable_group ();
+ xml_insert_element (THEAD, START);
+ }
+ else if (first_row)
+ {
+ xml_begin_multitable_group ();
+ xml_insert_element (TBODY, START);
+ }
+ else if (after_headitem)
+ {
+ xml_insert_element (THEAD, END);
+ xml_insert_element (TBODY, START);
+ }
+ else if (first_row)
+ xml_insert_element (TBODY, START);
+
+ xml_insert_element (ROW, START);
+ xml_insert_element (ENTRY, START);
+}
+
+void
+xml_end_multitable_column (void)
+{
+ xml_insert_element (ENTRY, END);
+ xml_insert_element (ENTRY, START);
+}
+
+void
+xml_end_multitable (void)
+{
+ xml_insert_element (ENTRY, END);
+ xml_insert_element (ROW, END);
+
+ if (after_headitem)
+ {
+ if (docbook)
+ warning (_("@headitem as the last item of @multitable produces invalid Docbook documents"));
+ xml_insert_element (THEAD, END);
+ }
+ else
+ xml_insert_element (TBODY, END);
+
+ if (docbook)
+ xml_insert_element (TGROUP, END);
+
+ xml_insert_element (MULTITABLE, END);
+ xml_no_para = 0;
+}
+
+/*
+ * Parameters in @def definitions
+ */
+
+#define DEFUN_SELF_DELIMITING(c) \
+ ((c) == '(' || (c) == ')' || (c) == '[' || (c) == ']')
+
+void
+xml_process_defun_args (char **defun_args, int auto_var_p)
+{
+ int pending_space = 0;
+ int just_after_paramtype = 0;
+
+ for (;;)
+ {
+ char *defun_arg = *defun_args++;
+
+ if (defun_arg == NULL)
+ break;
+
+ if (defun_arg[0] == ' ')
+ {
+ pending_space = 1;
+ continue;
+ }
+
+ if (pending_space)
+ {
+ add_char (' ');
+ pending_space = 0;
+ }
+
+ if (DEFUN_SELF_DELIMITING (defun_arg[0]))
+ {
+ xml_insert_element (DEFDELIMITER, START);
+ add_char (defun_arg[0]);
+ xml_insert_element (DEFDELIMITER, END);
+ just_after_paramtype = 0;
+ }
+ else if (defun_arg[0] == '&')
+ {
+ xml_insert_element (DEFPARAM, START);
+ add_word (defun_arg);
+ xml_insert_element (DEFPARAM, END);
+ just_after_paramtype = 0;
+ }
+ else if (defun_arg[0] == COMMAND_PREFIX || just_after_paramtype)
+ {
+ xml_insert_element (DEFPARAM, START);
+ execute_string ("%s", defun_arg);
+ xml_insert_element (DEFPARAM, END);
+ just_after_paramtype = 0;
+ }
+ else if (defun_arg[0] == ',' || defun_arg[0] == ';')
+ {
+ xml_insert_element (DEFDELIMITER, START);
+ add_word (defun_arg);
+ xml_insert_element (DEFDELIMITER, END);
+ just_after_paramtype = 0;
+ }
+ else if (auto_var_p)
+ {
+ xml_insert_element (DEFPARAM, START);
+ add_word (defun_arg);
+ xml_insert_element (DEFPARAM, END);
+ just_after_paramtype = 0;
+ }
+ else
+ {
+ xml_insert_element (DEFPARAMTYPE, START);
+ add_word (defun_arg);
+ xml_insert_element (DEFPARAMTYPE, END);
+ just_after_paramtype = 1;
+ }
+ }
+}
+
+void
+xml_begin_definition (void)
+{
+ xml_insert_element (DEFINITION, START);
+ xml_definition_level ++;
+ xml_in_def_item[xml_definition_level] = 0;
+}
+
+void
+xml_end_definition (void)
+{
+ if (xml_in_def_item[xml_definition_level])
+ {
+ xml_insert_element (DEFINITIONITEM, END);
+ xml_in_def_item[xml_definition_level] = 0;
+ }
+ xml_after_def_term = 0;
+ xml_insert_element (DEFINITION, END);
+ xml_definition_level --;
+}
+
+void
+xml_begin_def_term (int base_type, const char *category,
+ char *defined_name, char *type_name, char *type_name2)
+{
+ xml_after_def_term = 0;
+ if (docbook)
+ xml_insert_element_with_attribute (DEFINITIONTERM, START,
+ "role=\"%s\"", category);
+ else
+ xml_insert_element (DEFINITIONTERM, START);
+
+ /* Index entry */
+ switch (base_type)
+ {
+ case deffn:
+ case deftypefn:
+ execute_string ("@findex %s\n", defined_name);
+ break;
+ case defvr:
+ case deftypevr:
+ case defcv:
+ execute_string ("@vindex %s\n", defined_name);
+ break;
+ case deftypecv:
+ case deftypeivar:
+ execute_string ("@vindex %s %s %s\n", defined_name, _("of"), type_name);
+ break;
+ case deftypemethod:
+ case defop:
+ case deftypeop:
+ execute_string ("@findex %s %s %s\n", defined_name, _("on"), type_name);
+ break;
+ case deftp:
+ execute_string ("@tindex %s\n", defined_name);
+ break;
+ }
+
+ /* Start with category. */
+ if (! docbook)
+ {
+ xml_insert_element (DEFCATEGORY, START);
+ execute_string (docbook ? "[%s]" : "%s", category);
+ xml_insert_element (DEFCATEGORY, END);
+ add_char(' ');
+ }
+
+ /* Output type name first for typed definitions. */
+ switch (base_type)
+ {
+ case deffn:
+ case defvr:
+ case deftp:
+ break;
+
+ case deftypefn:
+ case deftypevr:
+ xml_insert_element (DEFTYPE, START);
+ execute_string ("%s", type_name);
+ xml_insert_element (DEFTYPE, END);
+ add_char (' ');
+ break;
+
+ case deftypecv:
+ case deftypeivar:
+ case deftypemethod:
+ case deftypeop:
+ xml_insert_element (DEFTYPE, START);
+ execute_string ("%s", type_name2);
+ xml_insert_element (DEFTYPE, END);
+ add_char (' ');
+ break;
+
+ default:
+ xml_insert_element (DEFCLASS, START);
+ execute_string ("%s", type_name);
+ xml_insert_element (DEFCLASS, END);
+ add_char (' ');
+ break;
+ }
+
+ /* Categorize rest of the definitions. */
+ switch (base_type)
+ {
+ case deffn:
+ case deftypefn:
+ xml_insert_element (DEFFUNCTION, START);
+ execute_string ("%s", defined_name);
+ xml_insert_element (DEFFUNCTION, END);
+ break;
+
+ case defvr:
+ case deftypevr:
+ xml_insert_element (DEFVARIABLE, START);
+ execute_string ("%s", defined_name);
+ xml_insert_element (DEFVARIABLE, END);
+ break;
+
+ case deftp:
+ xml_insert_element (DEFDATATYPE, START);
+ execute_string ("%s", defined_name);
+ xml_insert_element (DEFDATATYPE, END);
+ break;
+
+ case defcv:
+ case deftypecv:
+ case deftypeivar:
+ xml_insert_element (DEFCLASSVAR, START);
+ execute_string ("%s", defined_name);
+ xml_insert_element (DEFCLASSVAR, END);
+ break;
+
+ case defop:
+ case deftypeop:
+ case deftypemethod:
+ /* Operation / Method */
+ xml_insert_element (DEFOPERATION, START);
+ execute_string ("%s", defined_name);
+ xml_insert_element (DEFOPERATION, END);
+ break;
+ }
+}
+
+void
+xml_end_def_term (void)
+{
+ xml_insert_element (DEFINITIONTERM, END);
+ xml_after_def_term = 1;
+}
diff --git a/makeinfo/xml.h b/makeinfo/xml.h
new file mode 100644
index 0000000..5d18ff5
--- /dev/null
+++ b/makeinfo/xml.h
@@ -0,0 +1,159 @@
+/* xml.h -- xml (including Docbook) output declarations.
+ $Id: xml.h,v 1.32 2008/01/31 18:33:28 karl Exp $
+
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008
+ 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 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 <http://www.gnu.org/licenses/>.
+
+ Originally written by Philippe Martin <feloy@free.fr>. */
+
+#ifndef XML_H
+#define XML_H
+
+/* Currently sorting the index. */
+extern int xml_sort_index;
+
+/* Options. */
+
+extern int xml_no_indent;
+
+extern int xml_node_open;
+extern int xml_no_para;
+extern char *xml_node_id;
+extern int xml_last_section_output_position;
+
+extern int xml_in_xref_token;
+extern int xml_in_bookinfo;
+extern int xml_in_book_title;
+extern int xml_in_abstract;
+
+/* Non-zero if we are handling an element that can appear between
+ @item and @itemx, @deffn and @deffnx. */
+extern int xml_dont_touch_items_defs;
+
+/* Non-zero if whitespace in the source document should be kept as-is. */
+extern int xml_keep_space;
+
+enum xml_element
+{
+ TEXINFO=0, SETFILENAME, TITLEFONT, SETTITLE, DOCUMENTDESCRIPTION,
+ /* Node */
+ NODE, NODENEXT, NODEPREV, NODEUP,
+ /* Structuring */
+ CHAPTER, SECTION, SUBSECTION, SUBSUBSECTION,
+ TOP, UNNUMBERED, UNNUMBEREDSEC, UNNUMBEREDSUBSEC,
+ UNNUMBEREDSUBSUBSEC,
+ APPENDIX, APPENDIXSEC, APPENDIXSUBSEC, APPENDIXSUBSUBSEC,
+ MAJORHEADING, CHAPHEADING, HEADING, SUBHEADING, SUBSUBHEADING,
+ /* Titlepage */
+ TITLEPAGE, AUTHOR, BOOKTITLE, BOOKSUBTITLE,
+ /* Menu */
+ MENU, DETAILMENU, MENUENTRY, MENUTITLE, MENUCOMMENT, MENUNODE,
+ NODENAME,
+ /* -- */
+ ACRONYM, ACRONYMWORD, ACRONYMDESC,
+ ABBREV, ABBREVWORD, ABBREVDESC,
+ TT, CODE, COMMAND_TAG, ENV, FILE_TAG, OPTION, SAMP, KBD, URL, KEY,
+ VAR, SC, DFN, EMPH, STRONG, CITE, NOTFIXEDWIDTH, I, B, R, SLANTED, SANSSERIF,
+ EXDENT,
+ TITLE,
+ IFINFO,
+ SP, CENTER,
+ DIRCATEGORY,
+ QUOTATION, EXAMPLE, SMALLEXAMPLE, LISP, SMALLLISP, CARTOUCHE,
+ COPYING, FORMAT, SMALLFORMAT, DISPLAY, SMALLDISPLAY, VERBATIM,
+ FOOTNOTE, LINEANNOTATION,
+ TIP, NOTE, IMPORTANT, WARNING, CAUTION,
+ ITEMIZE, ITEMFUNCTION, ITEM, ENUMERATE, TABLE, TABLEITEM, TABLETERM,
+ INDEXTERM,
+ MATH, DIMENSION,
+ CLICKSEQUENCE, CLICK,
+ XREF, XREFNODENAME, XREFINFONAME, XREFPRINTEDDESC, XREFINFOFILE,
+ XREFPRINTEDNAME,
+ INFOREF, INFOREFNODENAME, INFOREFREFNAME, INFOREFINFONAME,
+ UREF, UREFURL, UREFDESC, UREFREPLACEMENT,
+ EMAIL, EMAILADDRESS, EMAILNAME,
+ GROUP, FLOAT, FLOATTYPE, FLOATPOS, CAPTION, SHORTCAPTION,
+ FLOATTABLE, FLOATFIGURE, FLOATEXAMPLE, FLOATCARTOUCHE,
+ PRINTINDEX, LISTOFFLOATS,
+ ANCHOR,
+ IMAGE, INLINEIMAGE, IMAGEALTTEXT,
+ PRIMARY, SECONDARY, INFORMALFIGURE, MEDIAOBJECT, IMAGEOBJECT,
+ IMAGEDATA, TEXTOBJECT,
+ INDEXENTRY, PRIMARYIE, SECONDARYIE, INDEXDIV,
+ MULTITABLE, TGROUP, COLSPEC, THEAD, TBODY, ENTRY, ROW,
+ BOOKINFO, ABSTRACT, REPLACEABLE, ENVAR, COMMENT, FUNCTION, LEGALNOTICE,
+ CONTENTS, SHORTCONTENTS, DOCUMENTLANGUAGE,
+ SETVALUE, CLEARVALUE,
+ DEFINITION, DEFINITIONTERM, DEFINITIONITEM,
+ DEFCATEGORY, DEFFUNCTION, DEFVARIABLE, DEFPARAM, DEFDELIMITER, DEFTYPE,
+ DEFPARAMTYPE, DEFDATATYPE, DEFCLASS, DEFCLASSVAR, DEFOPERATION,
+ FRENCHSPACING,
+ PARA
+};
+
+extern void xml_add_char (int character),
+ xml_asterisk (void),
+ xml_insert_element (int elt, int arg),
+ xml_insert_entity (char *entity_name),
+ xml_insert_footnote (char *note),
+ xml_insert_quotation (char *type, int arg),
+ xml_insert_indexterm (char *indexterm, char *index),
+ xml_insert_docbook_image (char *name_arg),
+ xml_synindex (char *from, char *to),
+ xml_start_para (void),
+ xml_end_para (void),
+ xml_begin_document (const char *output_basename),
+ xml_end_document (void),
+ xml_start_menu_entry (char *tem),
+ xml_end_menu (void),
+ xml_end_current_element (void),
+ xml_open_section (int level, char *name),
+ xml_close_sections (int level),
+ xml_begin_node (void),
+ xml_begin_index (void),
+ xml_end_index (void),
+ xml_begin_multitable (int ncolumns, int *column_widths),
+ xml_end_multitable (void),
+ xml_end_multitable_row (int first_row),
+ xml_end_multitable_column (void),
+ xml_begin_table (int type, char *item_function),
+ xml_end_table (int type),
+ xml_begin_item (void),
+ xml_begin_table_item (void),
+ xml_continue_table_item (void),
+ xml_begin_enumerate (char *enum_arg),
+ xml_end_enumerate (void),
+ xml_begin_docbook_float (int elt);
+
+extern char *xml_id (char *id);
+
+extern void xml_begin_definition (void),
+ xml_end_definition (void),
+ xml_process_defun_args (char **defun_args, int auto_var_p),
+ xml_begin_def_term (int base_type, const char *category,
+ char *defined_name, char *type_name, char *type_name2),
+ xml_end_def_term (void);
+
+extern int xml_current_stack_index (void),
+ xml_element (char *name);
+
+#if defined (VA_FPRINTF) && __STDC__
+void xml_insert_element_with_attribute (int elt, int arg, char *format, ...);
+#else
+void xml_insert_element_with_attribute ();
+#endif
+
+#endif /* XML_H */
diff --git a/makeinfo/xref.c b/makeinfo/xref.c
new file mode 100644
index 0000000..43e9ae9
--- /dev/null
+++ b/makeinfo/xref.c
@@ -0,0 +1,632 @@
+/* xref.c -- cross references for Texinfo.
+ $Id: xref.c,v 1.14 2007/09/26 20:53:40 karl Exp $
+
+ Copyright (C) 2004, 2005, 2007 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 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 <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "cmds.h"
+#include "float.h"
+#include "html.h"
+#include "index.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "node.h"
+#include "xml.h"
+#include "xref.h"
+
+/* Flags which control initial output string for xrefs. */
+int px_ref_flag = 0;
+int ref_flag = 0;
+
+/* Called in the multiple-argument case to make sure we generate a valid
+ Info reference. In the single-argument case, the :: we output
+ suffices for the Info readers to find the end of the reference. */
+static void
+add_xref_punctuation (void)
+{
+ if (px_ref_flag || ref_flag) /* user inserts punct after @xref */
+ {
+ /* Check if there's already punctuation. */
+ int next_char = next_nonwhitespace_character ();
+
+ if (next_char == -1)
+ /* EOF while looking for punctuation, let's
+ insert a period instead of crying. */
+ add_char ('.');
+ else if (next_char != ',' && next_char != '.')
+ /* period and comma terminate xrefs, and nothing else. Instead
+ of generating an Info reference that can't be followed,
+ though, just insert a period. Not pretty, but functional. */
+ add_char ('.');
+ }
+}
+
+/* Return next comma-delimited argument, but do not cross a close-brace
+ boundary. Clean up whitespace, too. If EXPAND is nonzero, replace
+ the entire brace-delimited argument list with its expansion before
+ looking for the next comma. */
+char *
+get_xref_token (int expand)
+{
+ char *string = 0;
+
+ if (docbook)
+ xml_in_xref_token = 1;
+
+ if (expand)
+ {
+ int old_offset = input_text_offset;
+ int old_lineno = line_number;
+
+ get_until_in_braces ("}", &string);
+ if (curchar () == '}') /* as opposed to end of text */
+ input_text_offset++;
+ if (input_text_offset > old_offset)
+ {
+ int limit = input_text_offset;
+
+ input_text_offset = old_offset;
+ line_number = old_lineno;
+ only_macro_expansion++;
+ replace_with_expansion (input_text_offset, &limit);
+ only_macro_expansion--;
+ }
+ free (string);
+ }
+
+ get_until_in_braces (",", &string);
+ if (curchar () == ',')
+ input_text_offset++;
+ fix_whitespace (string);
+
+ if (docbook)
+ xml_in_xref_token = 0;
+
+ return string;
+}
+
+
+/* NOTE: If you wonder why the HTML output is produced with such a
+ peculiar mix of calls to add_word and execute_string, here's the
+ reason. get_xref_token (1) expands all macros in a reference, but
+ any other commands, like @value, @@, etc., are left intact. To
+ expand them, we need to run the arguments through execute_string.
+ However, characters like <, &, > and others cannot be let into
+ execute_string, because they will be escaped. See the mess? */
+
+/* Make a cross reference. */
+void
+cm_xref (int arg)
+{
+ if (arg == START)
+ {
+ char *arg1 = get_xref_token (1); /* expands all macros in xref */
+ char *arg2 = get_xref_token (0);
+ char *arg3 = get_xref_token (0);
+ char *arg4 = get_xref_token (0);
+ char *arg5 = get_xref_token (0);
+ char *tem;
+
+ /* "@xref{,Foo,, Bar, Baz} is not valid usage of @xref. The
+ first argument must never be blank." --rms.
+ We hereby comply by disallowing such constructs. */
+ if (!*arg1)
+ line_error (_("First argument to cross-reference may not be empty"));
+
+ if (docbook)
+ {
+ if (!ref_flag)
+ add_word (px_ref_flag || printing_index
+ ? (char *) gdt("see ") : (char *) gdt("See "));
+
+ if (!*arg4 && !*arg5)
+ {
+ char *arg1_id = xml_id (arg1);
+
+ if (*arg2 || *arg3)
+ {
+ xml_insert_element_with_attribute (XREFNODENAME, START,
+ "linkend=\"%s\"", arg1_id);
+ free (arg1_id);
+ execute_string ("%s", *arg3 ? arg3 : arg2);
+ xml_insert_element (XREFNODENAME, END);
+ }
+ else
+ {
+ xml_insert_element_with_attribute (XREF, START,
+ "linkend=\"%s\"", arg1_id);
+ xml_insert_element (XREF, END);
+ free (arg1_id);
+ }
+ }
+ else if (*arg5)
+ {
+ add_word_args (gdt("See section ``%s'' in "), *arg3 ? arg3 : arg1);
+ xml_insert_element (CITE, START);
+ add_word (arg5);
+ xml_insert_element (CITE, END);
+ }
+ else if (*arg4)
+ {
+ /* Very sad, we are losing xrefs made to ``info only'' books. */
+ }
+ }
+ else if (xml)
+ {
+ if (!ref_flag)
+ add_word_args ("%s", px_ref_flag ? gdt("see ") : gdt("See "));
+
+ xml_insert_element (XREF, START);
+ xml_insert_element (XREFNODENAME, START);
+ execute_string ("%s", arg1);
+ xml_insert_element (XREFNODENAME, END);
+ if (*arg2)
+ {
+ xml_insert_element (XREFINFONAME, START);
+ execute_string ("%s", arg2);
+ xml_insert_element (XREFINFONAME, END);
+ }
+ if (*arg3)
+ {
+ xml_insert_element (XREFPRINTEDDESC, START);
+ execute_string ("%s", arg3);
+ xml_insert_element (XREFPRINTEDDESC, END);
+ }
+ if (*arg4)
+ {
+ xml_insert_element (XREFINFOFILE, START);
+ execute_string ("%s", arg4);
+ xml_insert_element (XREFINFOFILE, END);
+ }
+ if (*arg5)
+ {
+ xml_insert_element (XREFPRINTEDNAME, START);
+ execute_string ("%s", arg5);
+ xml_insert_element (XREFPRINTEDNAME, END);
+ }
+ xml_insert_element (XREF, END);
+ }
+ else if (html)
+ {
+ if (!ref_flag)
+ add_word_args ("%s", px_ref_flag ? gdt("see ") : gdt("See "));
+ }
+ else
+ add_word_args ("%s", px_ref_flag || ref_flag ? "*note " : "*Note ");
+
+ if (!xml)
+ {
+ if (*arg5 || *arg4)
+ {
+ /* arg1 - node name
+ arg2 - reference name
+ arg3 - title or topic (and reference name if arg2 is NULL)
+ arg4 - info file name
+ arg5 - printed manual title */
+ char *ref_name;
+
+ if (!*arg2)
+ {
+ if (*arg3)
+ ref_name = arg3;
+ else
+ ref_name = arg1;
+ }
+ else
+ ref_name = arg2;
+
+ if (html)
+ { /* More to do eventually, down to Unicode
+ Normalization Form C. See the HTML Xref nodes in
+ the manual. */
+ char *file_arg = arg4;
+ add_html_elt ("<a href=");
+
+ {
+ /* If there's a directory part, ignore it. */
+ char *p = strrchr (file_arg, '/');
+ if (p)
+ file_arg = p + 1;
+
+ /* If there's a dot, make it a NULL terminator, so the
+ extension does not get into the way. */
+ p = strrchr (file_arg , '.');
+ if (p != NULL)
+ *p = 0;
+ }
+
+ if (! *file_arg)
+ warning (_("Empty file name for HTML cross reference in `%s'"),
+ arg4);
+
+ /* Note that if we are splitting, and the referenced
+ tag is an anchor rather than a node, we will
+ produce a reference to a file whose name is
+ derived from the anchor name. However, only
+ nodes create files, so we are referencing a
+ non-existent file. cm_anchor, which see, deals
+ with that problem. */
+ if (splitting)
+ execute_string ("\"../%s/", file_arg);
+ else
+ execute_string ("\"%s.html", file_arg);
+ /* Do not collapse -- to -, etc., in references. */
+ in_fixed_width_font++;
+ tem = expansion (arg1, 0); /* expand @-commands in node */
+ in_fixed_width_font--;
+ add_anchor_name (tem, 1);
+ free (tem);
+ add_word ("\">");
+ execute_string ("%s",ref_name);
+ add_word ("</a>");
+ }
+ else
+ {
+ execute_string ("%s:", ref_name);
+ in_fixed_width_font++;
+ execute_string (" (%s)%s", arg4, arg1);
+ add_xref_punctuation ();
+ in_fixed_width_font--;
+ }
+
+ /* Free all of the arguments found. */
+ if (arg1) free (arg1);
+ if (arg2) free (arg2);
+ if (arg3) free (arg3);
+ if (arg4) free (arg4);
+ if (arg5) free (arg5);
+ return;
+ }
+ else
+ remember_node_reference (arg1, line_number, followed_reference);
+
+ if (*arg3)
+ {
+ if (html)
+ {
+ add_html_elt ("<a href=\"");
+ in_fixed_width_font++;
+ tem = expansion (arg1, 0);
+ in_fixed_width_font--;
+ add_anchor_name (tem, 1);
+ free (tem);
+ add_word ("\">");
+ execute_string ("%s", *arg2 ? arg2 : arg3);
+ add_word ("</a>");
+ }
+ else
+ {
+ execute_string ("%s:", *arg2 ? arg2 : arg3);
+ in_fixed_width_font++;
+ execute_string (" %s", arg1);
+ add_xref_punctuation ();
+ in_fixed_width_font--;
+ }
+ }
+ else
+ {
+ if (html)
+ {
+ add_html_elt ("<a href=\"");
+ in_fixed_width_font++;
+ tem = expansion (arg1, 0);
+ in_fixed_width_font--;
+ add_anchor_name (tem, 1);
+ free (tem);
+ add_word ("\">");
+ if (*arg2)
+ execute_string ("%s", arg2);
+ else
+ {
+ char *fref = get_float_ref (arg1);
+ execute_string ("%s", fref ? fref : arg1);
+ free (fref);
+ }
+ add_word ("</a>");
+ }
+ else
+ {
+ if (*arg2)
+ {
+ execute_string ("%s:", arg2);
+ in_fixed_width_font++;
+ execute_string (" %s", arg1);
+ add_xref_punctuation ();
+ in_fixed_width_font--;
+ }
+ else
+ {
+ char *fref = get_float_ref (arg1);
+ if (fref)
+ { /* Reference is being made to a float. */
+ execute_string ("%s:", fref);
+ in_fixed_width_font++;
+ execute_string (" %s", arg1);
+ add_xref_punctuation ();
+ in_fixed_width_font--;
+ }
+ else
+ {
+ in_fixed_width_font++;
+ execute_string ("%s::", arg1);
+ in_fixed_width_font--;
+ }
+ }
+ }
+ }
+ }
+ /* Free all of the arguments found. */
+ if (arg1) free (arg1);
+ if (arg2) free (arg2);
+ if (arg3) free (arg3);
+ if (arg4) free (arg4);
+ if (arg5) free (arg5);
+ }
+ else
+ { /* Check that the next non-whitespace character is valid to follow
+ an xref (so Info readers can find the node names).
+ `input_text_offset' is pointing at the "}" which ended the xref
+ command. This is not used for @pxref or @ref, since we insert
+ the necessary punctuation above, if needed. */
+ int temp = next_nonwhitespace_character ();
+
+ if (temp == -1)
+ warning (_("End of file reached while looking for `.' or `,'"));
+ else if (temp != '.' && temp != ',')
+ {
+ warning (_("`.' or `,' must follow @%s, not `%c'"), command, temp);
+ if (temp == ')')
+ warning (_("for cross-references in parentheses, use @pxref"));
+ }
+ }
+}
+
+void
+cm_pxref (int arg)
+{
+ if (arg == START)
+ {
+ px_ref_flag++;
+ cm_xref (arg);
+ px_ref_flag--;
+ }
+ /* cm_xref isn't called with arg == END, which disables the code near
+ the end of cm_xref that checks for `.' or `,' after the
+ cross-reference. This is because cm_xref generates the required
+ character itself (when needed) if px_ref_flag is set. */
+}
+
+void
+cm_ref (int arg)
+{
+ /* See the comments in cm_pxref about the checks for punctuation. */
+ if (arg == START)
+ {
+ ref_flag++;
+ cm_xref (arg);
+ ref_flag--;
+ }
+}
+
+void
+cm_inforef (int arg)
+{
+ if (arg == START)
+ {
+ char *node = get_xref_token (1); /* expands all macros in inforef */
+ char *pname = get_xref_token (0);
+ char *file = get_xref_token (0);
+
+ /* (see comments at cm_xref). */
+ if (!*node)
+ line_error (_("First argument to @inforef may not be empty"));
+
+ if (xml && !docbook)
+ {
+ xml_insert_element (INFOREF, START);
+ xml_insert_element (INFOREFNODENAME, START);
+ execute_string ("%s", node);
+ xml_insert_element (INFOREFNODENAME, END);
+ if (*pname)
+ {
+ xml_insert_element (INFOREFREFNAME, START);
+ execute_string ("%s", pname);
+ xml_insert_element (INFOREFREFNAME, END);
+ }
+ xml_insert_element (INFOREFINFONAME, START);
+ execute_string ("%s", file);
+ xml_insert_element (INFOREFINFONAME, END);
+
+ xml_insert_element (INFOREF, END);
+ }
+ else if (html)
+ {
+ char *tem;
+
+ add_word ((char *) gdt("see "));
+ /* html fixxme: revisit this */
+ add_html_elt ("<a href=");
+ if (splitting)
+ execute_string ("\"../%s/", file);
+ else
+ execute_string ("\"%s.html", file);
+ tem = expansion (node, 0);
+ add_anchor_name (tem, 1);
+ add_word ("\">");
+ execute_string ("%s", *pname ? pname : tem);
+ add_word ("</a>");
+ free (tem);
+ }
+ else
+ {
+ if (*pname)
+ execute_string ("*note %s: (%s)%s", pname, file, node);
+ else
+ execute_string ("*note (%s)%s::", file, node);
+ }
+
+ free (node);
+ free (pname);
+ free (file);
+ }
+}
+
+/* A URL reference. */
+void
+cm_uref (int arg)
+{
+ if (arg == START)
+ {
+ char *url = get_xref_token (1); /* expands all macros in uref */
+ char *desc = get_xref_token (0);
+ char *replacement = get_xref_token (0);
+
+ if (docbook)
+ {
+ xml_insert_element_with_attribute (UREF, START, "url=\"%s\"",
+ maybe_escaped_expansion (url, 0, 1));
+ if (*replacement)
+ execute_string ("%s", replacement);
+ else if (*desc)
+ execute_string ("%s", desc);
+ else
+ execute_string ("%s", url);
+ xml_insert_element (UREF, END);
+ }
+ else if (xml)
+ {
+ xml_insert_element (UREF, START);
+ xml_insert_element (UREFURL, START);
+ execute_string ("%s", url);
+ xml_insert_element (UREFURL, END);
+ if (*desc)
+ {
+ xml_insert_element (UREFDESC, START);
+ execute_string ("%s", desc);
+ xml_insert_element (UREFDESC, END);
+ }
+ if (*replacement)
+ {
+ xml_insert_element (UREFREPLACEMENT, START);
+ execute_string ("%s", replacement);
+ xml_insert_element (UREFREPLACEMENT, END);
+ }
+ xml_insert_element (UREF, END);
+ }
+ else if (html)
+ { /* never need to show the url */
+ add_html_elt ("<a href=");
+ /* don't collapse `--' etc. in the url */
+ in_fixed_width_font++;
+ execute_string ("\"%s\"", url);
+ in_fixed_width_font--;
+ add_word (">");
+ execute_string ("%s", *replacement ? replacement
+ : (*desc ? desc : url));
+ add_word ("</a>");
+ }
+ else if (*replacement) /* do not show the url */
+ execute_string ("%s", replacement);
+ else if (*desc) /* show both text and url */
+ {
+ execute_string ("%s ", desc);
+ in_fixed_width_font++;
+ execute_string ("(%s)", url);
+ in_fixed_width_font--;
+ }
+ else /* no text at all, so have the url to show */
+ {
+ in_fixed_width_font++;
+ execute_string ("%s%s%s",
+ printing_index ? "" : "`",
+ url,
+ printing_index ? "" : "'");
+ in_fixed_width_font--;
+ }
+ if (url)
+ free (url);
+ if (desc)
+ free (desc);
+ if (replacement)
+ free (replacement);
+ }
+}
+
+/* An email reference. */
+void
+cm_email (int arg)
+{
+ if (arg == START)
+ {
+ char *addr = get_xref_token (1); /* expands all macros in email */
+ char *name = get_xref_token (0);
+
+ if (xml && docbook)
+ {
+ if (*name)
+ {
+ xml_insert_element_with_attribute (EMAIL, START,
+ "url=\"mailto:%s\"",
+ maybe_escaped_expansion (addr, 0, 1));
+ execute_string ("%s", name);
+ xml_insert_element (EMAIL, END);
+ }
+ else
+ {
+ xml_insert_element (EMAILADDRESS, START);
+ execute_string ("%s", addr);
+ xml_insert_element (EMAILADDRESS, END);
+ }
+ }
+ else if (xml)
+ {
+ xml_insert_element (EMAIL, START);
+ xml_insert_element (EMAILADDRESS, START);
+ execute_string ("%s", addr);
+ xml_insert_element (EMAILADDRESS, END);
+ if (*name)
+ {
+ xml_insert_element (EMAILNAME, START);
+ execute_string ("%s", name);
+ xml_insert_element (EMAILNAME, END);
+ }
+ xml_insert_element (EMAIL, END);
+ }
+ else if (html)
+ {
+ add_html_elt ("<a href=");
+ /* don't collapse `--' etc. in the address */
+ in_fixed_width_font++;
+ execute_string ("\"mailto:%s\"", addr);
+ in_fixed_width_font--;
+ add_word (">");
+ execute_string ("%s", *name ? name : addr);
+ add_word ("</a>");
+ }
+ else
+ {
+ execute_string ("%s%s", name, *name ? " " : "");
+ in_fixed_width_font++;
+ execute_string ("<%s>", addr);
+ in_fixed_width_font--;
+ }
+
+ if (addr)
+ free (addr);
+ if (name)
+ free (name);
+ }
+}
diff --git a/makeinfo/xref.h b/makeinfo/xref.h
new file mode 100644
index 0000000..a6fc874
--- /dev/null
+++ b/makeinfo/xref.h
@@ -0,0 +1,29 @@
+/* xref.h -- declarations for the cross references.
+ $Id: xref.h,v 1.4 2007/07/01 21:20:33 karl Exp $
+
+ Copyright (C) 2004, 2007 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 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef XREF_H
+#define XREF_H
+
+enum reftype
+{
+ menu_reference, followed_reference
+};
+
+extern char *get_xref_token (int expand);
+
+#endif /* not XREF_H */