diff options
Diffstat (limited to 'makeinfo')
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 ("•"); + 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 ("≥"); + else + insert_string (">="); + } +} + +static void +cm_leq (int arg) +{ + if (arg == START) + { + if (xml) + xml_insert_entity ("le"); + else if (html) + add_word ("≤"); + else + insert_string ("<="); + } +} + +static void +cm_minus (int arg) +{ + if (arg == START) + { + if (xml) + xml_insert_entity ("minus"); + else if (html) + add_word ("−"); + 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 ("©"); + 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 ("®"); + 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 ("‹"); + 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 ("›"); + 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 ("„"); + 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 ("“"); + 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 ("”"); + 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 ("‘"); + 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 ("’"); + 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 ("‚"); + 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 ("‘"); + 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 ("’"); + } + } + } + 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 ("<"); + insert_html_tag (arg, "code"); + if (arg != START) + add_word (">"); + } + 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 ? "<" : ">"); + 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 ("<"); + + else if (html && character == '&') + add_word ("&"); + + 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 (" "); + else if (xml) + insert_string (" "); + 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 ? "==>" : "==>"); +} + +/* 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-->" : "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 . 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; /* `"' */ + break; + case '&': + newlen += 5; /* `&' */ + break; + case '<': + case '>': + newlen += 4; /* `<', `>' */ + 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, """); + newstring += 6; + break; + case '&': + strcpy (newstring, "&"); + newstring += 5; + break; + case '<': + strcpy (newstring, "<"); + newstring += 4; + break; + case '>': + strcpy (newstring, ">"); + 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üurgen. We can't + expand and then escape since we'll end up with + J&uuml;rgen. We can't escape and then expand + because then `expansion' will see J@"urgen, and + @"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", ©ing_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 ("&"); + else if (html && character == '<' && escape_html) + add_word ("<"); + 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 ("— %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 î. */ + 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 ˆ ` ˜), 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¨ + 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) ? "¯" : "="); + 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) ? "<" : "<"); + } + 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, Ä 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., ¨. */ + +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 + € 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 => ä */ + 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 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 == '`' ? "“" : "”"); + 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 ("—"); + else + xml_insert_entity ("mdash"); + dash_count -= 2; + } + else if (dash_count >= 1) + { + if (html) + add_word ("–"); + 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 ("&"); + else + add_char (character); + input_text_offset++; + break; + + case '<': + if (html && escape_html) + add_word ("<"); + else if (xml && escape_html) + xml_insert_entity ("lt"); + else + add_char (character); + input_text_offset++; + break; + + case '>': + if (html && escape_html) + add_word (">"); + 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 . + 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 than an 8-bit char. */ + int saved_escape_html = escape_html; + escape_html = 0; + add_word (" "); + 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", ¶graph_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 (¶ms); + + /* 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 (" "); + + 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 (" "); + 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 (" "); + 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 ("&", 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 'ì' $houtput >/dev/null \ + && grep '´j' $houtput >/dev/null \ + && grep 'Ø' $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 ("&"); + else if (character == '<' && escape_html) + insert_string ("<"); + else if (character == '>' && escape_html) + insert_string (">"); + 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 */ |