summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/texinfo/makeinfo
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/texinfo/makeinfo')
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/README4
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/cmds.c1121
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/cmds.h50
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/defun.c663
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/defun.h31
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/files.c529
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/files.h45
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/footnote.c359
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/footnote.h37
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/html.c182
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/html.h44
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/index.c823
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/index.h36
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/insertion.c1368
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/insertion.h61
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/lang.c415
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/lang.h86
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macro.c1114
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/macro.h71
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/node.c1568
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/node.h111
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/sectioning.c691
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/sectioning.h80
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/Makefile.am18
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/Makefile.in223
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/cond32
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/cond.txi40
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-extrali13
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-extrali.txi11
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-manuals12
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-min8
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-min.txi12
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-para8
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-para.txi24
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-title13
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/html-title.txi12
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace71
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace.txi18
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/no-headers8
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/node-expand11
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/node-expand.txi64
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/node-value11
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/node-value.txi15
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace11
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace.txi30
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/top10
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/top.txi25
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/top211
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/tests/top2.txi7
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/toc.c476
-rw-r--r--gnu/usr.bin/texinfo/makeinfo/toc.h49
51 files changed, 10730 insertions, 2 deletions
diff --git a/gnu/usr.bin/texinfo/makeinfo/README b/gnu/usr.bin/texinfo/makeinfo/README
index 2bfe6e1cbeb..a6f97ebed4c 100644
--- a/gnu/usr.bin/texinfo/makeinfo/README
+++ b/gnu/usr.bin/texinfo/makeinfo/README
@@ -1,8 +1,8 @@
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 plain ASCII. Work to support HTML and Troff
-output is almost complete.
+makeinfo can also output plain ASCII (with --no-headers)
+or HTML (with --html).
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/gnu/usr.bin/texinfo/makeinfo/cmds.c b/gnu/usr.bin/texinfo/makeinfo/cmds.c
new file mode 100644
index 00000000000..740007bc8cf
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/cmds.c
@@ -0,0 +1,1121 @@
+/* cmds.c -- Texinfo commands.
+ $Id: cmds.c,v 1.1 2000/02/09 01:25:09 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "cmds.h"
+#include "defun.h"
+#include "files.h"
+#include "footnote.h"
+#include "insertion.h"
+#include "lang.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "node.h"
+#include "sectioning.h"
+#include "toc.h"
+
+#ifdef TM_IN_SYS_TIME
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+
+void insert_self (), insert_space (), cm_ignore_line (), cm_ignore_arg ();
+
+void
+ cm_TeX (), cm_acronym (), cm_asterisk (), cm_b (), cm_bullet (), cm_cite (),
+ cm_code (), cm_copyright (), cm_ctrl (), cm_dfn (), cm_dircategory (),
+ cm_direntry (), cm_dmn (), cm_dots (), cm_emph (), cm_enddots (), cm_i (),
+ cm_image (), cm_kbd (), cm_key (), cm_no_op (),
+ cm_novalidate (), cm_not_fixed_width (), cm_r (),
+ cm_strong (), cm_var (), cm_sc (), cm_w (), cm_email (), cm_url ();
+
+void
+ cm_anchor (), cm_node (), cm_menu (), cm_xref (), cm_ftable (),
+ cm_vtable (), cm_pxref (), cm_inforef (), cm_uref (), cm_email (),
+ cm_quotation (), cm_display (), cm_smalldisplay (), cm_itemize (),
+ cm_enumerate (), cm_tab (), cm_table (), cm_itemx (), cm_noindent (),
+ cm_setfilename (), cm_br (), cm_sp (), cm_page (), cm_group (),
+ cm_center (), cm_ref (), cm_include (), cm_bye (), cm_item (), cm_end (),
+ cm_kindex (), cm_cindex (), cm_findex (), cm_pindex (), cm_vindex (),
+ cm_tindex (), cm_synindex (), cm_printindex (), cm_minus (),
+ cm_example (), cm_smallexample (), cm_smalllisp (), cm_lisp (),
+ cm_format (), cm_smallformat (), cm_exdent (), cm_defindex (),
+ cm_defcodeindex (), cm_result (), cm_expansion (), cm_equiv (),
+ cm_print (), cm_error (), cm_point (), cm_today (), cm_flushleft (),
+ cm_flushright (), cm_finalout (), cm_cartouche (), cm_detailmenu (),
+ cm_multitable (), cm_settitle (), cm_titlefont (), cm_tt ();
+
+/* Conditionals. */
+void cm_set (), cm_clear (), cm_ifset (), cm_ifclear ();
+void cm_value (), cm_ifeq ();
+
+/* Options. */
+static void cm_paragraphindent (), cm_exampleindent ();
+
+/* Internals. */
+static void cm_obsolete ();
+
+/* A random string. */
+static const char small_tag[] = "small";
+
+/* The dispatch table. */
+COMMAND command_table[] = {
+ { "\t", insert_space, NO_BRACE_ARGS },
+ { "\n", insert_space, NO_BRACE_ARGS },
+ { " ", insert_self, NO_BRACE_ARGS },
+ { "!", insert_self, 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 },
+ { ".", insert_self, NO_BRACE_ARGS },
+ { ":", cm_no_op, NO_BRACE_ARGS },
+ { "=", cm_accent, MAYBE_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 },
+ { "O", cm_special_char, BRACE_ARGS },
+ { "OE", cm_special_char, BRACE_ARGS },
+ { "TeX", cm_TeX, BRACE_ARGS },
+ { "aa", cm_special_char, BRACE_ARGS },
+ { "acronym", cm_acronym, BRACE_ARGS },
+ { "ae", cm_special_char, BRACE_ARGS },
+ { "afourpaper", cm_ignore_line, NO_BRACE_ARGS },
+ { "alias", cm_alias, 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 },
+ { "asis", cm_no_op, BRACE_ARGS },
+ { "b", cm_b, BRACE_ARGS },
+ { "bullet", cm_bullet, BRACE_ARGS },
+ { "bye", cm_bye, NO_BRACE_ARGS },
+ { "c", cm_ignore_line, NO_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 },
+ { "code", cm_code, BRACE_ARGS },
+ { "command", cm_code, BRACE_ARGS },
+ { "comment", cm_ignore_line, NO_BRACE_ARGS },
+ { "contents", cm_contents, 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 },
+ { "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_no_op, 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 },
+ { "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 },
+ { "flushleft", cm_flushleft, NO_BRACE_ARGS },
+ { "flushright", cm_flushright, NO_BRACE_ARGS },
+ { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
+ { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
+ { "format", cm_format, NO_BRACE_ARGS },
+ { "ftable", cm_ftable, NO_BRACE_ARGS },
+ { "group", cm_group, NO_BRACE_ARGS },
+ { "heading", cm_heading, NO_BRACE_ARGS },
+ { "headings", cm_ignore_line, 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 },
+ { "ifhtml", cm_ifhtml, NO_BRACE_ARGS },
+ { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
+ { "ifnothtml", cm_ifnothtml, NO_BRACE_ARGS },
+ { "ifnotinfo", cm_ifnotinfo, NO_BRACE_ARGS },
+ { "ifnottex", cm_ifnottex, NO_BRACE_ARGS },
+ { "ifset", cm_ifset, NO_BRACE_ARGS },
+ { "iftex", cm_iftex, NO_BRACE_ARGS },
+ { "ignore", command_name_condition, NO_BRACE_ARGS },
+ { "image", cm_image, BRACE_ARGS },
+ { "include", cm_include, NO_BRACE_ARGS },
+ { "inforef", cm_inforef, 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 },
+ { "lisp", cm_lisp, NO_BRACE_ARGS },
+ { "lowersections", cm_lowersections, NO_BRACE_ARGS },
+ { "macro", cm_macro, NO_BRACE_ARGS },
+ { "majorheading", cm_majorheading, NO_BRACE_ARGS },
+ { "math", cm_no_op, 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, NO_BRACE_ARGS },
+ { "noindent", cm_novalidate, NO_BRACE_ARGS },
+ { "nwnode", cm_node, NO_BRACE_ARGS },
+ { "o", cm_special_char, BRACE_ARGS },
+ { "oe", cm_special_char, BRACE_ARGS },
+ { "option", cm_code, 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 },
+ { "r", cm_r, BRACE_ARGS },
+ { "raisesections", cm_raisesections, NO_BRACE_ARGS },
+ { "ref", cm_ref, BRACE_ARGS },
+ { "refill", cm_no_op, NO_BRACE_ARGS },
+ { "result", cm_result, BRACE_ARGS },
+ { "ringaccent", cm_accent, MAYBE_BRACE_ARGS },
+ { "rmacro", cm_rmacro, NO_BRACE_ARGS },
+ { "samp", cm_code, 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 },
+ { "shortcontents", cm_shortcontents, NO_BRACE_ARGS },
+ { "shorttitlepage", cm_ignore_line, NO_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 },
+ { "summarycontents", cm_no_op, 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 },
+ { "tieaccent", cm_accent, MAYBE_BRACE_ARGS },
+ { "tindex", cm_tindex, NO_BRACE_ARGS },
+ { "titlefont", cm_titlefont, BRACE_ARGS },
+ { "titlepage", command_name_condition, 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_url, BRACE_ARGS },
+ { "v", cm_accent, MAYBE_BRACE_ARGS },
+ { "value", cm_value, BRACE_ARGS },
+ { "var", cm_var, BRACE_ARGS },
+ { "vindex", cm_vindex, NO_BRACE_ARGS },
+ { "vtable", cm_vtable, NO_BRACE_ARGS },
+ { "w", cm_w, 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 (arg)
+ int arg;
+{
+ if (arg == START)
+ add_word (command);
+}
+
+void
+insert_space (arg)
+ int arg;
+{
+ if (arg == START)
+ add_char (' ');
+}
+
+/* Force a line break in the output. */
+void
+cm_asterisk ()
+{
+ if (html)
+ add_word ("<br>");
+ else
+ {
+ close_single_paragraph ();
+ cm_noindent ();
+ }
+}
+
+/* Insert ellipsis. */
+void
+cm_dots (arg)
+ int arg;
+{
+ if (arg == START)
+ add_word (html ? "<small>...</small>" : "...");
+}
+
+/* Insert ellipsis for sentence end. */
+void
+cm_enddots (arg)
+ int arg;
+{
+ if (arg == START)
+ add_word (html ? "<small>...</small>." : "....");
+}
+
+void
+cm_bullet (arg)
+ int arg;
+{
+ if (arg == START)
+ {
+ if (html)
+ add_word ("&#149;");
+ else
+ add_char ('*');
+ }
+}
+
+void
+cm_minus (arg)
+ int arg;
+{
+ if (arg == START)
+ add_char ('-');
+}
+
+/* Insert "TeX". */
+void
+cm_TeX (arg)
+ int arg;
+{
+ if (arg == START)
+ add_word ("TeX");
+}
+
+/* Copyright symbol. */
+void
+cm_copyright (arg)
+ int arg;
+{
+ if (arg == START)
+ if (html)
+ add_word ("&copy;");
+ else
+ add_word ("(C)");
+}
+
+void
+cm_today (arg)
+ 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_acronym (arg)
+ int arg;
+{
+ if (html)
+ insert_html_tag (arg, small_tag);
+}
+
+void
+cm_tt (arg)
+ int arg;
+{
+ /* @t{} is a no-op in Info. */
+ if (html)
+ insert_html_tag (arg, "tt");
+}
+
+void
+cm_code (arg)
+ int arg;
+{
+ extern int printing_index;
+
+ if (arg == START)
+ {
+ in_fixed_width_font++;
+
+ if (html)
+ insert_html_tag (arg, "code");
+ else if (!printing_index)
+ add_char ('`');
+ }
+ else if (html)
+ insert_html_tag (arg, "code");
+ else
+ {
+ if (!printing_index)
+ add_meta_char ('\'');
+ }
+}
+
+void
+cm_kbd (arg)
+ int arg;
+{
+ 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);
+ }
+}
+
+void
+cm_url (arg, start, end)
+{
+ if (html)
+ {
+ if (arg == START)
+ add_word ("&lt;<code>");
+ else
+ add_word ("</code>&gt;");
+ }
+ else
+ if (arg == START)
+ add_word ("<");
+ else
+ add_word (">");
+}
+
+void
+cm_key (arg)
+ int arg;
+{
+ if (html)
+ add_word (arg == START ? "&lt;" : "&gt;");
+ else
+ add_char (arg == START ? '<' : '>');
+}
+
+/* Handle a command that switches to a non-fixed-width font. */
+void
+not_fixed_width (arg)
+ int arg;
+{
+ if (arg == START)
+ in_fixed_width_font = 0;
+}
+
+/* @var in makeinfo just uppercases the text. */
+void
+cm_var (arg, start_pos, end_pos)
+ int arg, start_pos, end_pos;
+{
+ 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 (arg, start_pos, end_pos)
+ int arg, start_pos, end_pos;
+{
+ not_fixed_width (arg);
+
+ if (arg == START)
+ {
+ if (html)
+ insert_html_tag (arg, small_tag);
+ }
+ else
+ {
+ int all_upper = 1;
+
+ if (html)
+ start_pos += sizeof (small_tag) + 2 - 1; /* skip <small> */
+
+ while (start_pos < end_pos)
+ {
+ unsigned char c = output_paragraph[start_pos];
+ if (!isupper (c))
+ all_upper = 0;
+ 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, small_tag);
+ }
+}
+
+void
+cm_dfn (arg, position)
+ int arg, position;
+{
+ if (html)
+ insert_html_tag (arg, "dfn");
+ else if (arg == START)
+ add_char ('"');
+ else
+ add_meta_char ('"');
+}
+
+void
+cm_emph (arg)
+ int arg;
+{
+ if (html)
+ insert_html_tag (arg, "em");
+ else
+ add_char ('_');
+}
+
+void
+cm_strong (arg, position)
+ int arg, position;
+{
+ if (html)
+ insert_html_tag (arg, "strong");
+ else
+ add_char ('*');
+}
+
+void
+cm_cite (arg, position)
+ int arg, position;
+{
+ if (html)
+ insert_html_tag (arg, "cite");
+ else
+ {
+ if (arg == START)
+ add_char ('`');
+ else
+ add_char ('\'');
+ }
+}
+
+/* No highlighting, but argument switches fonts. */
+void
+cm_not_fixed_width (arg, start, end)
+ int arg, start, end;
+{
+ not_fixed_width (arg);
+}
+
+void
+cm_i (arg)
+ int arg;
+{
+ if (html)
+ insert_html_tag (arg, "i");
+ else
+ not_fixed_width (arg);
+}
+
+void
+cm_b (arg)
+ int arg;
+{
+ if (html)
+ insert_html_tag (arg, "b");
+ else
+ not_fixed_width (arg);
+}
+
+void
+cm_r (arg)
+ int arg;
+{
+ extern int printing_index;
+
+ /* People use @r{} in index entries like this:
+
+ @findex foo@r{, some text}
+
+ This is supposed to produce output as if the entry were saying
+ "@code{foo}, some text", since the "fn" index is typeset as
+ @code. The following attempts to do the same in HTML. Note that
+ this relies on the fact that only @code bumps up the variable
+ in_fixed_width_font while processing index entries in HTML mode. */
+ if (html && printing_index)
+ {
+ int level = in_fixed_width_font;
+
+ while (level--)
+ insert_html_tag (arg == START ? END : START, "code");
+ }
+
+ not_fixed_width (arg);
+}
+
+void
+cm_titlefont (arg)
+ int arg;
+{
+ not_fixed_width (arg);
+}
+
+/* Various commands are no-op's. */
+void
+cm_no_op ()
+{
+}
+
+
+/* For proofing single chapters, etc. */
+void
+cm_novalidate ()
+{
+ validating = 0;
+}
+
+
+/* Prevent the argument from being split across two lines. */
+void
+cm_w (arg, start, end)
+ int arg, start, end;
+{
+ if (arg == START)
+ non_splitting_words++;
+ else
+ non_splitting_words--;
+}
+
+
+/* Explain that this command is obsolete, thus the user shouldn't
+ do anything with it. */
+static void
+cm_obsolete (arg, start, end)
+ int arg, start, end;
+{
+ if (arg == START)
+ warning (_("%c%s is obsolete"), COMMAND_PREFIX, command);
+}
+
+
+/* This says to inhibit the indentation of the next paragraph, but
+ not of following paragraphs. */
+void
+cm_noindent ()
+{
+ if (!inhibit_paragraph_indentation)
+ inhibit_paragraph_indentation = -1;
+}
+
+/* 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 ()
+{
+ char *filename;
+ get_rest_of_line (1, &filename);
+ /* warning ("`@%s %s' encountered and ignored", command, filename); */
+ free (filename);
+}
+
+void
+cm_settitle ()
+{
+ get_rest_of_line (0, &title);
+}
+
+/* Ignore argument in braces. */
+void
+cm_ignore_arg (arg, start_pos, end_pos)
+ int arg, start_pos, end_pos;
+{
+ if (arg == END)
+ output_paragraph_offset = start_pos;
+}
+
+/* Ignore argument on rest of line. */
+void
+cm_ignore_line ()
+{
+ discard_until ("\n");
+}
+
+/* Insert the number of blank lines passed as argument. */
+void
+cm_sp ()
+{
+ int lines;
+ char *line;
+
+ get_rest_of_line (1, &line);
+
+ if (sscanf (line, "%d", &lines) != 1 || lines <= 0)
+ line_error (_("@sp requires a positive numeric argument, not `%s'"), line);
+ 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 ();
+
+ while (lines--)
+ {
+ if (html)
+ insert_string ("<br><p>\n");
+ else
+ add_char ('\n');
+ }
+
+ filling_enabled = save_filling_enabled;
+ }
+ free (line);
+}
+
+/* @dircategory LINE outputs INFO-DIR-SECTION LINE, unless --no-headers. */
+void
+cm_dircategory ()
+{
+ char *line;
+
+ if (html)
+ cm_ignore_line ();
+ else
+ {
+ get_rest_of_line (1, &line);
+
+ if (!no_headers && !html)
+ {
+ kill_self_indent (-1); /* make sure there's no indentation */
+ insert_string ("INFO-DIR-SECTION ");
+ insert_string (line);
+ insert ('\n');
+ }
+
+ free (line);
+ }
+}
+
+/* Start a new line with just this text on it.
+ Then center the line of text.
+ This always ends the current paragraph. */
+void
+cm_center ()
+{
+ int i, start, length;
+ unsigned char *line;
+ int save_indented_fill = indented_fill;
+ int save_filling_enabled = filling_enabled;
+ int fudge_factor = 1;
+
+ close_paragraph ();
+ filling_enabled = indented_fill = 0;
+ cm_noindent ();
+ start = output_paragraph_offset;
+
+ if (html)
+ add_word ("<p align=\"center\">");
+
+ inhibit_output_flushing ();
+ get_rest_of_line (0, (char **)&line);
+ execute_string ("%s", (char *)line);
+ free (line);
+ uninhibit_output_flushing ();
+ if (html)
+ add_word ("</p>");
+
+ else
+ {
+ i = output_paragraph_offset - 1;
+ while (i > (start - 1) && output_paragraph[i] == '\n')
+ i--;
+
+ output_paragraph_offset = ++i;
+ length = output_paragraph_offset - start;
+
+ if (length < (fill_column - fudge_factor))
+ {
+ line = xmalloc (1 + length);
+ memcpy (line, (char *)(output_paragraph + start), length);
+
+ i = (fill_column - fudge_factor - length) / 2;
+ output_paragraph_offset = start;
+
+ while (i--)
+ insert (' ');
+
+ for (i = 0; i < length; i++)
+ insert (line[i]);
+
+ free (line);
+ }
+ }
+
+ insert ('\n');
+ close_paragraph ();
+ filling_enabled = save_filling_enabled;
+ indented_fill = save_indented_fill;
+}
+
+/* Show what an expression returns. */
+void
+cm_result (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word (html ? "=&gt;" : "=>");
+}
+
+/* What an expression expands to. */
+void
+cm_expansion (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word (html ? "==&gt;" : "==>");
+}
+
+/* Indicates two expressions are equivalent. */
+void
+cm_equiv (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word ("==");
+}
+
+/* What an expression may print. */
+void
+cm_print (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word ("-|");
+}
+
+/* An error signaled. */
+void
+cm_error (arg)
+ int arg;
+{
+ if (arg == END)
+ add_word (html ? "error--&gt;" : "error-->");
+}
+
+/* The location of point in an example of a buffer. */
+void
+cm_point (arg)
+ 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 ()
+{
+ 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>");
+
+ /* Can't close_single_paragraph, then we lose preceding blank lines. */
+ flush_output ();
+ execute_string ("%s", line);
+ free (line);
+
+ if (html)
+ add_word ("<br>");
+ close_single_paragraph ();
+
+ current_indent = save_indent;
+ in_fixed_width_font = save_in_fixed_width_font;
+}
+
+
+/* Remember this file, and move onto the next. */
+void
+cm_include ()
+{
+ char *filename;
+
+ if (macro_expansion_output_stream && !executing_string)
+ me_append_before_this_command ();
+
+ close_paragraph ();
+ get_rest_of_line (0, &filename);
+
+ 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))
+ {
+ extern int errno;
+
+ popfile ();
+ line_number--;
+
+ /* Cannot "@include foo", in line 5 of "/wh/bar". */
+ 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);
+ reader_loop ();
+ }
+ free (filename);
+ popfile ();
+}
+
+
+/* @bye: Signals end of processing. Easy to make this happen. */
+
+void
+cm_bye ()
+{
+ discard_braces (); /* should not have any unclosed braces left */
+ flush_output ();
+ input_text_offset = input_text_length;
+}
+
+/* @paragraphindent */
+
+static void
+cm_paragraphindent ()
+{
+ 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_default_indentation_increment (string)
+ char *string;
+{
+ if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
+ ;
+ else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
+ default_indentation_increment = 0;
+ else if (sscanf (string, "%d", &default_indentation_increment) != 1)
+ return -1;
+ return 0;
+}
+
+static void
+cm_exampleindent ()
+{
+ char *arg;
+
+ get_rest_of_line (1, &arg);
+ if (set_default_indentation_increment (arg) != 0)
+ line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
+
+ free (arg);
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/cmds.h b/gnu/usr.bin/texinfo/makeinfo/cmds.h
new file mode 100644
index 00000000000..528b08512ec
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/cmds.h
@@ -0,0 +1,50 @@
+/* cmds.h -- declarations for cmds.c.
+ $Id: cmds.h,v 1.1.1.1 2000/02/09 01:25:09 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#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[];
+
+#endif /* !CMDS_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/defun.c b/gnu/usr.bin/texinfo/makeinfo/defun.c
new file mode 100644
index 00000000000..e9078c655f7
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/defun.c
@@ -0,0 +1,663 @@
+/* defun.c -- @defun and friends.
+ $Id: defun.c,v 1.1.1.1 2000/02/09 01:25:10 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "defun.h"
+#include "insertion.h"
+#include "makeinfo.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 (accumulator)
+ struct token_accumulator *accumulator;
+{
+ accumulator->length = 0;
+ accumulator->index = 0;
+ accumulator->tokens = NULL;
+}
+
+static void
+accumulate_token (accumulator, 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 (string_pointer)
+ char **string_pointer;
+{
+ char *scan_string = (*string_pointer) + 1;
+ unsigned int level = 1;
+
+ 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 -= 1;
+ line_error (_("Missing `}' in @def arg"));
+ line_number += 1;
+ *string_pointer = scan_string - 1;
+ return 0;
+ }
+
+ if (c == '{')
+ level += 1;
+ if (c == '}')
+ level -= 1;
+ }
+}
+
+/* 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 (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;
+ }
+
+ /* 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;
+ }
+
+ /* 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 (defun_args, auto_var_p)
+ char **defun_args;
+ int auto_var_p;
+{
+ int pending_space = 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]))
+ add_char (defun_arg[0]);
+ else if (defun_arg[0] == '&')
+ if (html)
+ {
+ defun_arg = escape_string (xstrdup (defun_arg));
+ add_word (defun_arg);
+ free (defun_arg);
+ }
+ else
+ add_word (defun_arg);
+ else if (defun_arg[0] == COMMAND_PREFIX)
+ execute_string ("%s", defun_arg);
+ else if (auto_var_p)
+ if (html)
+ {
+ defun_arg = escape_string (xstrdup (defun_arg));
+ add_word (defun_arg);
+ free (defun_arg);
+ }
+ else
+ add_word (defun_arg);
+ else
+ add_word (defun_arg);
+ }
+}
+
+static char *
+next_nonwhite_defun_arg (arg_pointer)
+ 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 (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 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 (type, x_p)
+ enum insertion_type type;
+ int x_p;
+{
+ enum insertion_type base_type;
+ char **defun_args, **scan_args;
+ char *category, *defined_name, *type_name, *type_name2;
+
+ {
+ 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);
+ 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 = _("Function");
+ break;
+ case defmac:
+ category = _("Macro");
+ break;
+ case defspec:
+ category = _("Special Form");
+ break;
+ case defvar:
+ case deftypevar:
+ category = _("Variable");
+ break;
+ case defopt:
+ category = _("User Option");
+ break;
+ case defivar:
+ case deftypeivar:
+ category = _("Instance Variable");
+ break;
+ case defmethod:
+ case deftypemethod:
+ category = _("Method");
+ break;
+ default:
+ category = next_nonwhite_defun_arg (&scan_args);
+ break;
+ }
+
+ /* The class name. */
+ if ((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 == deftypemethod
+ || base_type == deftypeivar
+ || 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;
+ }
+
+ 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 && !x_p)
+ /* Start the definition on new paragraph. */
+ add_word ("<p>\n");
+
+ if (!html)
+ 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, _("of"), type_name,
+ defined_name);
+ break;
+ case deftypeivar:
+ execute_string (" -- %s %s %s: %s %s", category, _("of"), type_name,
+ type_name2, defined_name);
+ break;
+ case defop:
+ execute_string (" -- %s %s %s: %s", category, _("on"), type_name,
+ defined_name);
+ break;
+ case deftypeop:
+ execute_string (" -- %s %s %s: %s %s", category, _("on"), type_name,
+ type_name2, defined_name);
+ break;
+ case deftypemethod:
+ execute_string (" -- %s %s %s: %s %s", category, _("on"), type_name,
+ type_name2, defined_name);
+ break;
+ }
+
+ 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)
+ add_word ("<table width=\"100%\">\n");
+
+ /* If this is an @def...x there has to be an other @def... before
+ it, so this is only a new row within an existing table. With
+ two complete standalone tables the gap between them is too big. */
+ add_word ("<tr>\n");
+ add_word ("<td align=\"left\">");
+
+ switch (base_type)
+ {
+ case deffn:
+ case defvr:
+ case deftp:
+ /* <i> is for the following function arguments. */
+ add_word_args ("<b>%s</b><i>", defined_name);
+ break;
+ case deftypefn:
+ case deftypevr:
+ add_word_args ("%s <b>%s</b><i>", type_name, defined_name);
+ break;
+ case defcv:
+ case defop:
+ add_word_args ("<b>%s</b><i>", defined_name);
+ break;
+ case deftypemethod:
+ case deftypeop:
+ case deftypeivar:
+ add_word_args ("%s <b>%s</b><i>", type_name2, defined_name);
+ break;
+ }
+ } /* if (html)... */
+
+ 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;
+ close_single_paragraph ();
+
+ if (html)
+ {
+ /* 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:
+ add_word ("</i>"); /* close italic area for arguments */
+ /* put the rest into the second column */
+ add_word_args ("</td>\n<td align=\"right\">%s", category);
+ break;
+
+ case defcv:
+ add_word ("</td>\n<td align=\"right\">");
+ add_word_args ("%s %s %s", category, _("of"), type_name);
+ break;
+
+ case defop:
+ case deftypemethod:
+ case deftypeop:
+ add_word ("</i>");
+ add_word ("</td>\n<td align=\"right\">");
+ add_word_args ("%s %s %s", category, _("on"), type_name);
+ break;
+
+ case deftypeivar:
+ add_word ("</i>");
+ add_word ("</td>\n<td align=\"right\">");
+ add_word_args ("%s %s %s", category, _("of"), type_name);
+ break;
+ } /* switch (base_type)... */
+
+ add_word ("</td>\n"); /* close second column */
+ add_word ("</tr>\n"); /* close row */
+
+ /* This is needed because I have to know if the next line is
+ normal text or another @def..x. If text follows, create a new
+ table to get the indentation for the following text.
+
+ This construction would fail if someone uses:
+ @deffn
+ @sp 2
+ @deffnx
+ .
+ @end deffn
+ But we don't care. */
+ if (!looking_at ("@def"))
+ {
+ add_word ("</table>\n");
+ add_word ("<table width=\"95%\" align=\"center\">\n");
+ add_word ("<tr><td>\n");
+ }
+
+ } /* if (html)... */
+
+ /* Make an entry in the appropriate index. */
+ 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 deftypeivar:
+ execute_string ("@vindex %s %s %s\n", defined_name, _("of"), type_name);
+ break;
+ case defop:
+ case deftypeop:
+ case deftypemethod:
+ execute_string ("@findex %s %s %s\n", defined_name, _("on"), type_name);
+ break;
+ case deftp:
+ execute_string ("@tindex %s\n", defined_name);
+ break;
+ }
+
+ /* 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 ()
+{
+ int x_p;
+ enum insertion_type type;
+ char *temp = xstrdup (command);
+
+ x_p = (command[strlen (command) - 1] == 'x');
+
+ if (x_p)
+ temp[strlen (temp) - 1] = 0;
+
+ type = find_type_from_name (temp);
+ free (temp);
+
+ /* 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_level || insertion_stack->insertion != type))
+ {
+ line_error (_("Must be in `%s' insertion to use `%sx'"),
+ command, command);
+ discard_until ("\n");
+ return;
+ }
+
+ defun_internal (type, x_p);
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/defun.h b/gnu/usr.bin/texinfo/makeinfo/defun.h
new file mode 100644
index 00000000000..9ab64483a7e
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/defun.h
@@ -0,0 +1,31 @@
+/* defun.h -- declaration for defuns.
+ $Id: defun.h,v 1.1.1.1 2000/02/09 01:25:10 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ 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 ();
+extern void cm_defun ();
+
+#endif /* !DEFUN_H */
+
diff --git a/gnu/usr.bin/texinfo/makeinfo/files.c b/gnu/usr.bin/texinfo/makeinfo/files.c
new file mode 100644
index 00000000000..6f2dcb2b8d6
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/files.c
@@ -0,0 +1,529 @@
+/* files.c -- file-related functions for Texinfo.
+ $Id: files.c,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "files.h"
+#include "macro.h"
+#include "makeinfo.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 (string, index)
+ 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. */
+static char *
+get_file_info_in_path (filename, path, finfo)
+ char *filename, *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;
+}
+
+/* Find and load the file named FILENAME. Return a pointer to
+ the loaded file, or NULL if it can't be loaded. */
+char *
+find_and_load (filename)
+ char *filename;
+{
+ struct stat fileinfo;
+ long file_size;
+ int file = -1, count = 0;
+ char *fullpath, *result;
+#if O_BINARY || defined (VMS)
+ int n;
+#endif
+
+ result = fullpath = NULL;
+
+ fullpath = get_file_info_in_path (filename, include_files_path, &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. */
+#if O_BINARY || defined (VMS)
+#ifdef VMS
+ while ((n = read (file, result + count, file_size)) > 0)
+#else /* !VMS */
+#ifndef WIN32
+ while ((n = read (file, result + count, file_size)) > 0)
+#else /* WIN32 */
+ /* Does WIN32 really need reading 1 character at a time?? */
+ while ((n = read (file, result + count, 1)) > 0)
+#endif /* WIN32 */
+#endif /* !VMS */
+ count += n;
+ if (0 < count && count < file_size)
+ result = xrealloc (result, count + 2); /* why waste the slack? */
+ else if (n == -1)
+#else /* !VMS && !O_BINARY */
+ count = file_size;
+ if (read (file, result, file_size) != file_size)
+#endif /* !VMS && !WIN32 */
+
+ 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. */
+void
+push_node_filename ()
+{
+ 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++;
+}
+
+void
+pop_node_filename ()
+{
+ node_filename = node_filename_stack[--node_filename_stack_index];
+}
+
+/* Save the state of the current input file. */
+void
+pushfile ()
+{
+ 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 ()
+{
+ 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 ()
+{
+ 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 (filename)
+ 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;
+}
+
+char *
+filename_non_directory (name)
+ 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 (filename)
+ 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;
+
+ 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 (filename)
+ char *filename;
+{
+ char *expand_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 expansion of FILENAME. */
+char *
+expand_filename (filename, input_name)
+ char *filename, *input_name;
+{
+ int i;
+ char *full_pathname ();
+
+ 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;
+}
+
+/* Return the full path to FILENAME. */
+char *
+full_pathname (filename)
+ 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;
+}
+
+char *
+output_name_from_input_name (name)
+ char *name;
+{
+ return expand_filename (NULL, name);
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/files.h b/gnu/usr.bin/texinfo/makeinfo/files.h
new file mode 100644
index 00000000000..6cae9e543e4
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/files.h
@@ -0,0 +1,45 @@
+/* files.h -- declarations for files.c.
+ $Id: files.h,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $
+
+ Copyright (C) 1998 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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 (), popfile ();
+extern void flush_file_stack ();
+extern char *find_and_load ();
+extern char *output_name_from_input_name ();
+extern char *expand_filename ();
+extern char *filename_part ();
+extern char *pathname_part ();
+
+#endif /* !FILES_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/footnote.c b/gnu/usr.bin/texinfo/makeinfo/footnote.c
new file mode 100644
index 00000000000..79a52e6996b
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/footnote.c
@@ -0,0 +1,359 @@
+/* footnote.c -- footnotes for Texinfo.
+ $Id: footnote.c,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "footnote.h"
+#include "macro.h"
+#include "makeinfo.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 (string)
+ char *string;
+{
+ if (strcasecmp (string, "separate") == 0)
+ footnote_style = separate_node;
+ else if (strcasecmp (string, "end") == 0)
+ footnote_style = end_node;
+ else
+ return -1;
+
+ return 0;
+}
+
+void
+cm_footnotestyle ()
+{
+ 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. */
+void
+remember_note (marker, note)
+ char *marker, *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 ()
+{
+ 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 ()
+{
+ 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;
+ }
+
+ if (!*marker)
+ {
+ free (marker);
+
+ if (number_footnotes)
+ {
+ marker = xmalloc (10);
+ sprintf (marker, "%d", current_footnote_number);
+ }
+ else
+ marker = xstrdup ("*");
+ }
+
+ 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)
+ add_word_args ("<a rel=footnote href=\"#fn-%d\"><sup>%s</sup></a>",
+ 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 ()
+{
+ FN *footnote = pending_notes;
+
+ if (!pending_notes)
+ return;
+
+ if (html)
+ { /* The type= attribute is used just in case some weirdo browser
+ out there doesn't use numbers by default. Since we rely on the
+ browser to produce the footnote numbers, we need to make sure
+ they ARE indeed numbers. Pre-HTML4 browsers seem to not care. */
+ add_word ("<hr><h4>");
+ add_word (_("Footnotes"));
+ add_word ("</h4>\n<ol type=\"1\">\n");
+ }
+ 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. */
+ {
+ 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;
+
+ while ((footnote = array[++footnote_count]))
+ {
+ if (html)
+ {
+ /* Make the text of every footnote begin a separate paragraph. */
+ add_word_args ("<li><a name=\"fn-%d\"></a>\n<p>",
+ footnote->number);
+ execute_string ("%s", footnote->note);
+ 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_word ("</ol><hr>");
+ close_paragraph ();
+ free (array);
+ }
+
+ free_pending_notes ();
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/footnote.h b/gnu/usr.bin/texinfo/makeinfo/footnote.h
new file mode 100644
index 00000000000..b5edb509ade
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/footnote.h
@@ -0,0 +1,37 @@
+/* footnote.h -- declarations for footnote.c.
+ $Id: footnote.h,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $
+
+ Copyright (C) 1998 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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 ();
+extern void cm_footnotestyle ();
+
+extern int set_footnote_style (); /* called for -s option */
+
+extern void output_pending_notes (); /* called for output */
+
+#endif /* !FOOTNOTE_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/html.c b/gnu/usr.bin/texinfo/makeinfo/html.c
new file mode 100644
index 00000000000..931327a7ed7
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/html.c
@@ -0,0 +1,182 @@
+/* html.c -- html-related utilities.
+ $Id: html.c,v 1.1 2000/02/09 01:25:11 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "cmds.h"
+#include "html.h"
+#include "lang.h"
+#include "makeinfo.h"
+#include "sectioning.h"
+
+/* See html.h. */
+int html_output_head_p = 0;
+
+void
+html_output_head ()
+{
+ char *html_title;
+
+ if (html_output_head_p)
+ return;
+ html_output_head_p = 1;
+
+ /* The <title> should not have markup. */
+ html_title = title ? text_expansion (title) : _("Untitled");
+
+ add_word_args ("<html lang=\"%s\"><head>\n<title>%s</title>\n",
+ language_table[language_code].abbrev, html_title);
+
+ add_word ("<meta http-equiv=\"Content-Type\" content=\"text/html");
+ if (document_encoding)
+ add_word_args ("; charset=%s", document_encoding);
+ add_word ("\">\n");
+
+ add_word_args ("<meta name=description content=\"%s\">\n", html_title);
+ add_word_args ("<meta name=generator content=\"makeinfo %s\">\n", VERSION);
+ add_word ("<link href=\"http://texinfo.org/\" rel=generator-home>\n");
+ add_word ("</head><body>\n\n");
+}
+
+
+/* Escape HTML special characters in the string if necessary,
+ returning a pointer to a possibly newly-allocated one. */
+char *
+escape_string (string)
+ char * string;
+{
+ int i=0, newlen=0;
+ char * newstring;
+
+ do
+ {
+ /* Find how much to allocate. */
+ switch (string[i])
+ {
+ case '&':
+ newlen += 5; /* `&amp;' */
+ break;
+ case '<':
+ case '>':
+ newlen += 4; /* `&lt;', `&gt;' */
+ break;
+ default:
+ newlen++;
+ }
+ i++;
+ }
+ while (string[i]);
+
+ if (newlen == i) return string; /* Already OK. */
+
+ newstring = xmalloc (newlen + 2);
+ i = 0;
+ do
+ {
+ switch (string[i])
+ {
+ case '&':
+ strcpy (newstring, "&amp;");
+ newstring += 5;
+ break;
+ case '<':
+ strcpy (newstring, "&lt;");
+ newstring += 4;
+ break;
+ case '>':
+ strcpy (newstring, "&gt;");
+ newstring += 4;
+ break;
+ default:
+ newstring[0] = string[i];
+ newstring++;
+ }
+ }
+ while (string[i++]);
+ free (string);
+ return newstring - newlen -1;
+}
+
+/* Open or close TAG according to START_OR_END. */
+void
+insert_html_tag (start_or_end, tag)
+ int start_or_end;
+ char *tag;
+{
+ if (!paragraph_is_open && (start_or_end == START))
+ {
+ /* Need to compensate for the <p> we are about to insert, or
+ else cm_xxx functions that call us will get wrong text
+ between START and END. */
+ adjust_braces_following (output_paragraph_offset, 3);
+ add_word ("<p>");
+ }
+ add_char ('<');
+ if (start_or_end != START)
+ add_char ('/');
+ add_word (tag);
+ add_char ('>');
+}
+
+/* Output an HTML <link> to the filename for NODE, including the
+ other string as extra attributes. */
+void
+add_link (node, attributes)
+ char *node, *attributes;
+{
+ if (node)
+ {
+ add_word_args ("<link %s href=\"", attributes);
+ add_anchor_name (node, 1);
+ add_word ("\">\n");
+ }
+}
+
+/* Output NAME with characters escaped as appropriate for an anchor
+ name, i.e., escape URL special characters as %<n>. */
+void
+add_escaped_anchor_name (name)
+ char *name;
+{
+ for (; *name; name++)
+ {
+ if (*name == '&')
+ add_word ("&amp;");
+ else if (! 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 nonzero, it will be
+ appropriate for a href= attribute, rather than name= i.e., including
+ the `#' if it's an internal reference. */
+void
+add_anchor_name (nodename, href)
+ char *nodename;
+ int href;
+{
+ if (href)
+ add_char ('#');
+
+ add_escaped_anchor_name (nodename);
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/html.h b/gnu/usr.bin/texinfo/makeinfo/html.h
new file mode 100644
index 00000000000..54305b82082
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/html.h
@@ -0,0 +1,44 @@
+/* html.h -- declarations for html-related utilities.
+ $Id: html.h,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef HTML_H
+#define HTML_H
+
+/* Nonzero if we have output the <head>. */
+extern int html_output_head_p;
+
+/* Perform the <head> output. */
+extern void html_output_head ();
+
+/* 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 *node, char *attributes */);
+
+/* Escape URL-special characters as %xy. */
+extern void add_escaped_anchor_name (/* char *name */);
+
+/* See html.c. */
+extern void add_anchor_name (/* nodename, href */);
+
+#endif /* !HTML_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/index.c b/gnu/usr.bin/texinfo/makeinfo/index.c
new file mode 100644
index 00000000000..72442efa1ef
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/index.c
@@ -0,0 +1,823 @@
+/* index.c -- indexing for Texinfo.
+ $Id: index.c,v 1.1.1.1 2000/02/09 01:25:16 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "index.h"
+#include "lang.h"
+#include "macro.h"
+#include "toc.h"
+
+/* 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. */
+ int code; /* Nonzero means add `@code{...}' when
+ printing this element. */
+ int defining_line; /* Line number where this entry was written. */
+ char *defining_file; /* Source file for defining_line. */
+} 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;
+
+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". */
+INDEX_ELT **the_indices = NULL;
+
+/* The number of defined indices. */
+int defined_indices = 0;
+
+/* Stuff for defining commands on the fly. */
+COMMAND **user_command_array = NULL;
+int user_command_array_len = 0;
+
+/* How to compare index entries for sorting. May be set to strcoll. */
+int (*index_compare_fn) () = strcasecmp;
+
+/* Find which element in the known list of indices has this name.
+ Returns -1 if NAME isn't found. */
+static int
+find_index_offset (name)
+ 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. */
+INDEX_ALIST *
+find_index (name)
+ 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. */
+void
+define_user_command (name, proc, needs_braces_p)
+ 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)
+ 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 (name)
+ 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.
+ html fixxme generate specific html anchor */
+static void
+index_add_arg (name)
+ 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));
+ new->next = the_indices[which];
+ new->entry_text = index_entry;
+ new->entry = NULL;
+ new->node = current_node ? current_node : xstrdup ("");
+ new->code = tem->code;
+ new->defining_line = line_number - 1;
+ /* 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);
+ the_indices[which] = new;
+ }
+}
+
+/* The function which user defined index commands call. */
+static void
+gen_index ()
+{
+ 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 (name, code)
+ 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 (name, code)
+ 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 ()
+{
+ 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++)
+ {
+ undefindex (name_index_alist[i]->name);
+ 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. */
+ 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. */
+int
+translate_index (name)
+ 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 (name)
+ 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 (code)
+ 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 ()
+{
+ gen_defindex (0);
+}
+
+void
+cm_defcodeindex ()
+{
+ 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 ()
+{
+ 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
+ {
+ name_index_alist[target]->write_index
+ = name_index_alist[source]->write_index;
+ }
+
+ free (abbrev1);
+ free (abbrev2);
+}
+
+void
+cm_pindex () /* Pinhead index. */
+{
+ index_add_arg ("pg");
+}
+
+void
+cm_vindex () /* Variable index. */
+{
+ index_add_arg ("vr");
+}
+
+void
+cm_kindex () /* Key index. */
+{
+ index_add_arg ("ky");
+}
+
+void
+cm_cindex () /* Concept index. */
+{
+ index_add_arg ("cp");
+}
+
+void
+cm_findex () /* Function index. */
+{
+ index_add_arg ("fn");
+}
+
+void
+cm_tindex () /* Data Type index. */
+{
+ index_add_arg ("tp");
+}
+
+int
+index_element_compare (element1, element2)
+ INDEX_ELT **element1, **element2;
+{
+ return index_compare_fn ((*element1)->entry, (*element2)->entry);
+}
+
+/* Force all index entries to be unique. */
+void
+make_index_entries_unique (array, count)
+ 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. We call qsort because it's supposed to be fast.
+ I think this looks bad. */
+INDEX_ELT **
+sort_index (index)
+ INDEX_ELT *index;
+{
+ INDEX_ELT **array;
+ INDEX_ELT *temp = index;
+ 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;
+
+ while (temp)
+ {
+ count++;
+ temp = temp->next;
+ }
+
+ /* We have the length. Make an array. */
+
+ array = xmalloc ((count + 1) * sizeof (INDEX_ELT *));
+ count = 0;
+ temp = index;
+
+ while (temp)
+ {
+ array[count++] = temp;
+
+ /* Set line number and input filename to the source line for this
+ index entry, as this expansion finds any errors. */
+ line_number = array[count - 1]->defining_line;
+ input_filename = array[count - 1]->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-1]->entry = expansion (temp->entry_text,
+ array[count-1]->code);
+
+ temp = temp->next;
+ }
+ 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);
+ make_index_entries_unique (array, count);
+ return array;
+}
+
+/* Nonzero means that we are in the middle of printing an index. */
+int printing_index = 0;
+
+/* Takes one arg, a short name of an index to print.
+ Outputs a menu of the sorted elements of the index. */
+void
+cm_printindex ()
+{
+ int item;
+ INDEX_ELT *index;
+ INDEX_ELT *last_index = 0;
+ INDEX_ELT **array;
+ char *index_name;
+ 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;
+
+ close_paragraph ();
+ get_rest_of_line (0, &index_name);
+
+ 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 in index_element_compare
+ will give the same results as when we actually print. */
+ printing_index = 1;
+ filling_enabled = 0;
+ inhibit_paragraph_indentation = 1;
+ array = sort_index (index);
+
+ close_paragraph ();
+ if (html)
+ add_word ("<ul compact>");
+ else if (!no_headers)
+ add_word ("* Menu:\n\n");
+
+ me_inhibit_expansion++;
+
+ /* This will probably be enough. */
+ line_length = 100;
+ line = xmalloc (line_length);
+
+ 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 = _("(outside of any node)");
+ }
+
+ if (html)
+ /* fixme: html: we should use specific index anchors pointing
+ to the actual location of the indexed position (but then we
+ have to find something to wrap the anchor around). */
+ {
+ if (last_index
+ && STREQ (last_index->entry_text, index->entry_text))
+ add_word (", "); /* Don't repeat the previous entry. */
+ else
+ {
+ /* In the HTML case, the expanded index entry is not
+ good for us, since it was expanded for non-HTML mode
+ inside sort_index. So we need to HTML-escape and
+ expand the original entry text here. */
+ char *escaped_entry = xstrdup (index->entry_text);
+ char *expanded_entry;
+
+ /* expansion() doesn't HTML-escape the argument, so need
+ to do it separately. */
+ escaped_entry = escape_string (escaped_entry);
+ expanded_entry = expansion (escaped_entry, index->code);
+ add_word_args ("\n<li>%s: ", expanded_entry);
+ free (escaped_entry);
+ free (expanded_entry);
+ }
+ add_word ("<a href=\"");
+ if (index->node && *index->node)
+ {
+ /* Make sure any non-macros in the node name are expanded. */
+ in_fixed_width_font++;
+ index_node = expansion (index_node, 0);
+ in_fixed_width_font--;
+ add_anchor_name (index_node, 1);
+ add_word_args ("\">%s</a>", index_node);
+ free (index_node);
+ }
+ 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);
+ }
+ else
+ {
+ unsigned new_length = strlen (index->entry);
+
+ 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)
+ {
+ sprintf (line, "* %-37s ", index->entry);
+ line[2 + strlen (index->entry)] = ':';
+ insert_string (line);
+ /* Make sure any non-macros in the node name are expanded. */
+ in_fixed_width_font++;
+ execute_string ("%s.\n", index_node);
+ 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. */
+ char *section_name = toc_find_section_of_node (index_node);
+
+ sprintf (line, "%-*s ", number_sections ? 50 : 1, index->entry);
+ line[strlen (index->entry)] = ':';
+ insert_string (line);
+ if (section_name)
+ {
+ int idx = 0;
+ unsigned ref_len = strlen (section_name) + 30;
+
+ if (ref_len > line_length)
+ {
+ line_length = ref_len;
+ line = xrealloc (line, line_length);
+ }
+
+ if (number_sections)
+ {
+ while (section_name[idx]
+ && (isdigit (section_name[idx])
+ || (idx && section_name[idx] == '.')))
+ idx++;
+ }
+ if (idx)
+ sprintf (line, " See %.*s.\n", idx, section_name);
+ else
+ sprintf (line, "\n See ``%s''.\n", section_name);
+ insert_string (line);
+ }
+ else
+ {
+ insert_string (" "); /* force a blank */
+ execute_string ("See node %s.\n", index_node);
+ }
+ }
+ }
+
+ /* Prevent `output_paragraph' from growing to the size of the
+ whole index. */
+ flush_output ();
+ last_index = index;
+ }
+
+ free (line);
+ free (index_name);
+
+ me_inhibit_expansion--;
+
+ printing_index = 0;
+ free (array);
+ 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_word ("</ul>");
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/index.h b/gnu/usr.bin/texinfo/makeinfo/index.h
new file mode 100644
index 00000000000..0d155125310
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/index.h
@@ -0,0 +1,36 @@
+/* index.h -- declarations for index.c.
+ $Id: index.h,v 1.1.1.1 2000/02/09 01:25:16 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INDEX_H
+#define INDEX_H
+
+#include "makeinfo.h"
+#include "cmds.h"
+
+/* User commands are only new indices. (Macros are handled separately.) */
+extern COMMAND **user_command_array;
+extern int user_command_array_len;
+
+/* Initialize all indices. */
+extern void init_indices ();
+
+/* Function to compare index entries for sorting. */
+extern int (*index_compare_fn) ();
+
+#endif /* !INDEX_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/insertion.c b/gnu/usr.bin/texinfo/makeinfo/insertion.c
new file mode 100644
index 00000000000..218b892ffa7
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/insertion.c
@@ -0,0 +1,1368 @@
+/* insertion.c -- insertions for Texinfo.
+ $Id: insertion.c,v 1.1 2000/02/09 01:25:19 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "cmds.h"
+#include "defun.h"
+#include "insertion.h"
+#include "macro.h"
+#include "makeinfo.h"
+
+/* Must match list in insertion.h. */
+static char *insertion_type_names[] =
+{
+ "cartouche", "defcv", "deffn", "defivar", "defmac", "defmethod",
+ "defop", "defopt", "defspec", "deftp", "deftypefn", "deftypefun",
+ "deftypeivar", "deftypemethod", "deftypeop", "deftypevar",
+ "deftypevr", "defun", "defvar", "defvr", "detailmenu", "direntry",
+ "display", "enumerate", "example", "flushleft", "flushright",
+ "format", "ftable", "group", "ifclear", "ifhtml", "ifinfo",
+ "ifnothtml", "ifnotinfo", "ifnottex", "ifset", "iftex", "itemize",
+ "lisp", "menu", "multitable", "quotation", "rawhtml", "rawtex",
+ "smalldisplay", "smallexample", "smallformat", "smalllisp", "table",
+ "tex", "vtable", "bad_type"
+};
+
+/* All nested environments. */
+INSERTION_ELT *insertion_stack = NULL;
+
+/* How deeply we're nested. */
+int insertion_level = 0;
+
+/* Whether to examine menu lines. */
+int in_menu = 0;
+
+/* How to examine menu lines. */
+int in_detailmenu = 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;
+
+/* Set to 1 if <p> is written in normal context.
+ Used for menu and itemize. */
+int in_paragraph = 0;
+
+static const char dl_tag[] = "<dl>\n";
+
+void
+init_insertion_stack ()
+{
+ insertion_stack = NULL;
+}
+
+/* Return the type of the current insertion. */
+static enum insertion_type
+current_insertion_type ()
+{
+ 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 ()
+{
+ 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 ifnottex:
+ case ifset:
+ case iftex:
+ case rawhtml:
+ 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 ()
+{
+ char *item_function;
+ get_rest_of_line (0, &item_function);
+
+ /* 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. */
+void
+push_insertion (type, item_function)
+ 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 ()
+{
+ 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;
+ 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. */
+char *
+insertion_type_pname (type)
+ enum insertion_type type;
+{
+ if ((int) type < (int) bad_type)
+ 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 (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 == rawhtml && STREQ (name, "html"))
+ return rawhtml;
+ if (index == rawtex && STREQ (name, "tex"))
+ return rawtex;
+ index++;
+ }
+ return bad_type;
+}
+
+int
+defun_insertion (type)
+ 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 == 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;
+
+void
+start_enumerating (at, type)
+ int at, 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;
+}
+
+void
+stop_enumerating ()
+{
+ --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. */
+void
+enumerate_item ()
+{
+ 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 (output_column += (current_indent - strlen (temp)));
+ add_word (temp);
+ current_enumval++;
+}
+
+static void
+enum_html ()
+{
+ 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_word_args ("<ol type=%c start=%d>\n", type, start);
+}
+
+/* Conditionally parse based on the current command name. */
+void
+command_name_condition ()
+{
+ 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 (type)
+ 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)
+ add_word ("* Menu:\n");
+
+ in_menu++;
+ in_fixed_width_font++;
+ no_discard++;
+ break;
+
+ case detailmenu:
+ if (!in_menu)
+ {
+ if (!no_headers)
+ close_paragraph ();
+
+ filling_enabled = no_indent = 0;
+ inhibit_paragraph_indentation = 1;
+
+ no_discard++;
+ }
+
+ in_fixed_width_font++;
+ in_detailmenu++;
+ break;
+
+ case direntry:
+ if (html)
+ command_name_condition ();
+ else
+ {
+ close_single_paragraph ();
+ filling_enabled = no_indent = 0;
+ inhibit_paragraph_indentation = 1;
+ insert_string ("START-INFO-DIR-ENTRY\n");
+ }
+ break;
+
+ case quotation:
+ /* @quotation does filling (@display doesn't). */
+ if (html)
+ add_word ("<blockquote>\n");
+ else
+ {
+ close_single_paragraph ();
+ last_char_was_newline = no_indent = 0;
+ indented_fill = filling_enabled = 1;
+ inhibit_paragraph_indentation = 1;
+ }
+ current_indent += default_indentation_increment;
+ break;
+
+ case display:
+ case smalldisplay:
+ case example:
+ case smallexample:
+ case lisp:
+ case smalllisp:
+ /* Like @display but without indentation. */
+ case smallformat:
+ case format:
+ close_single_paragraph ();
+ inhibit_paragraph_indentation = 1;
+ in_fixed_width_font++;
+ filling_enabled = 0;
+ last_char_was_newline = 0;
+
+ if (html)
+ /* Kludge alert: if <pre> is followed by a newline, IE3
+ renders an extra blank line before the pre-formatted block.
+ Other browsers seem to not mind one way or the other. */
+ add_word ("<pre>");
+
+ if (type != format && type != smallformat)
+ current_indent += default_indentation_increment;
+ 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 = xstrdup ("@bullet");
+ }
+ }
+
+ 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_word ("<ul>\n");
+ in_paragraph = 0;
+ }
+ else
+ add_word (dl_tag);
+ }
+ 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 ();
+
+ if (isdigit (*enumeration_arg))
+ start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
+ else
+ start_enumerating (*enumeration_arg, ENUM_ALPHA);
+ break;
+
+ /* @group does nothing special in makeinfo. */
+ case group:
+ /* Only close the paragraph if we are not inside of an
+ @example-like environment. */
+ 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;
+
+ /* Insertions that are no-ops in info, but do something in TeX. */
+ case cartouche:
+ case ifclear:
+ case ifhtml:
+ case ifinfo:
+ case ifnothtml:
+ case ifnotinfo:
+ case ifnottex:
+ case ifset:
+ case iftex:
+ case rawtex:
+ if (in_menu)
+ no_discard++;
+ break;
+
+ case rawhtml:
+ escape_html = 0;
+ break;
+
+ case defcv:
+ case deffn:
+ case defivar:
+ case defmac:
+ case defmethod:
+ case defop:
+ case defopt:
+ case defspec:
+ case deftp:
+ 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;
+ break;
+
+ case flushleft:
+ close_single_paragraph ();
+ inhibit_paragraph_indentation = 1;
+ filling_enabled = indented_fill = no_indent = 0;
+ break;
+
+ case flushright:
+ close_single_paragraph ();
+ filling_enabled = indented_fill = no_indent = 0;
+ inhibit_paragraph_indentation = 1;
+ force_flush_right++;
+ 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. */
+void
+end_insertion (type)
+ 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 ();
+
+ switch (type)
+ {
+ /* Insertions which have no effect on paragraph formatting. */
+ case ifclear:
+ case ifhtml:
+ case ifinfo:
+ case ifnothtml:
+ case ifnotinfo:
+ case ifnottex:
+ case ifset:
+ case iftex:
+ case rawtex:
+ break;
+
+ case rawhtml:
+ escape_html = 1;
+ break;
+
+ case direntry: /* Eaten if html. */
+ insert_string ("END-INFO-DIR-ENTRY\n\n");
+ close_insertion_paragraph ();
+ break;
+
+ case detailmenu:
+ in_detailmenu--; /* No longer hacking menus. */
+ if (!in_menu)
+ {
+ if (!no_headers)
+ close_insertion_paragraph ();
+ }
+ break;
+
+ case menu:
+ in_menu--; /* No longer hacking menus. */
+ if (html)
+ add_word ("</ul>\n");
+ else if (!no_headers)
+ close_insertion_paragraph ();
+ break;
+
+ case multitable:
+ end_multitable ();
+ break;
+
+ case enumerate:
+ stop_enumerating ();
+ close_insertion_paragraph ();
+ current_indent -= default_indentation_increment;
+ if (html)
+ add_word ("</ol>\n");
+ break;
+
+ case flushleft:
+ case group:
+ case cartouche:
+ close_insertion_paragraph ();
+ 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)
+ current_indent -= default_indentation_increment;
+
+ if (html)
+ add_word (type == quotation ? "</blockquote>\n" : "</pre>\n");
+
+ /* The ending of one of these insertions always marks the
+ start of a new paragraph. */
+ close_insertion_paragraph ();
+ break;
+
+ case table:
+ case ftable:
+ case vtable:
+ current_indent -= default_indentation_increment;
+ if (html)
+ add_word ("</dl>\n");
+ break;
+
+ case itemize:
+ current_indent -= default_indentation_increment;
+ if (html)
+ add_word ("</ul>\n");
+ close_insertion_paragraph ();
+ break;
+
+ case flushright:
+ force_flush_right--;
+ close_insertion_paragraph ();
+ break;
+
+ /* Handle the @defun insertions with this default clause. */
+ default:
+ {
+ enum insertion_type 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 deftypefn:
+ case deftypevr:
+ case defcv:
+ case defop:
+ case deftypemethod:
+ case deftypeop:
+ case deftypeivar:
+ if (html)
+ /* close the tables which has been opened in defun.c */
+ add_word ("</TD></TR>\n</TABLE>\n");
+ 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 (specials_ok)
+ 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 == rawhtml
+ || insertion_stack->insertion == rawtex))
+ break;
+ else
+ {
+ char *offender = insertion_type_pname (insertion_stack->insertion);
+ char *current_filename = input_filename;
+
+ input_filename = insertion_stack->filename;
+ line_number = insertion_stack->line_number;
+ line_error (_("No matching `%cend %s'"), COMMAND_PREFIX, offender);
+ input_filename = current_filename;
+ pop_insertion ();
+ }
+ }
+ line_number = real_line_number;
+}
+
+/* Insertion (environment) commands. */
+
+void
+cm_quotation ()
+{
+ begin_insertion (quotation);
+}
+
+void
+cm_example ()
+{
+ begin_insertion (example);
+}
+
+void
+cm_smallexample ()
+{
+ begin_insertion (smallexample);
+}
+
+void
+cm_lisp ()
+{
+ begin_insertion (lisp);
+}
+
+void
+cm_smalllisp ()
+{
+ begin_insertion (smalllisp);
+}
+
+/* @cartouche/@end cartouche draws box with rounded corners in
+ TeX output. Right now, just a no-op insertion. */
+void
+cm_cartouche ()
+{
+ begin_insertion (cartouche);
+}
+
+void
+cm_format ()
+{
+ begin_insertion (format);
+}
+
+void
+cm_smallformat ()
+{
+ begin_insertion (smallformat);
+}
+
+void
+cm_display ()
+{
+ begin_insertion (display);
+}
+
+void
+cm_smalldisplay ()
+{
+ begin_insertion (smalldisplay);
+}
+
+void
+cm_direntry ()
+{
+ if (no_headers || html)
+ command_name_condition ();
+ else
+ begin_insertion (direntry);
+}
+
+void
+cm_itemize ()
+{
+ 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 (type, default_string)
+ int 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 ()
+{
+ do_enumeration (enumerate, "1");
+}
+
+void
+cm_table ()
+{
+ begin_insertion (table);
+}
+
+void
+cm_multitable ()
+{
+ begin_insertion (multitable); /* @@ */
+}
+
+void
+cm_ftable ()
+{
+ begin_insertion (ftable);
+}
+
+void
+cm_vtable ()
+{
+ begin_insertion (vtable);
+}
+
+void
+cm_group ()
+{
+ begin_insertion (group);
+}
+
+void
+cm_ifinfo ()
+{
+ if (process_info)
+ begin_insertion (ifinfo);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnotinfo ()
+{
+ if (!process_info)
+ begin_insertion (ifnotinfo);
+ else
+ command_name_condition ();
+}
+
+
+/* Insert raw HTML (no escaping of `<' etc.). */
+void
+cm_html ()
+{
+ if (process_html)
+ begin_insertion (rawhtml);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifhtml ()
+{
+ if (process_html)
+ begin_insertion (ifhtml);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnothtml ()
+{
+ if (!process_html)
+ begin_insertion (ifnothtml);
+ else
+ command_name_condition ();
+}
+
+
+void
+cm_tex ()
+{
+ if (process_tex)
+ begin_insertion (rawtex);
+ else
+ command_name_condition ();
+}
+
+void
+cm_iftex ()
+{
+ if (process_tex)
+ begin_insertion (iftex);
+ else
+ command_name_condition ();
+}
+
+void
+cm_ifnottex ()
+{
+ if (!process_tex)
+ begin_insertion (ifnottex);
+ else
+ command_name_condition ();
+}
+
+/* Begin an insertion where the lines are not filled or indented. */
+void
+cm_flushleft ()
+{
+ 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 ()
+{
+ begin_insertion (flushright);
+}
+
+void
+cm_menu ()
+{
+ if (current_node == NULL)
+ {
+ 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 ()
+{
+ if (current_node == NULL)
+ { /* 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);
+}
+
+/* End existing insertion block. */
+void
+cm_end ()
+{
+ char *temp;
+ enum insertion_type type;
+
+ if (!insertion_level)
+ {
+ line_error (_("Unmatched `%c%s'"), COMMAND_PREFIX, command);
+ return;
+ }
+
+ get_rest_of_line (0, &temp);
+
+ 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 to `%s', `%s', using `%s'"),
+ command, temp, insertion_type_pname (current_insertion_type ()));
+ }
+ end_insertion (type);
+ free (temp);
+}
+
+/* @itemx, @item. */
+
+static int itemx_flag = 0;
+
+/* Return whether CMD takes a brace-delimited {arg}. */
+static int
+command_needs_braces (cmd)
+ 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 ()
+{
+ 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 ifnottex:
+ case ifset:
+ case iftex:
+ case rawhtml:
+ 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)
+ {
+ if (in_paragraph)
+ {
+ add_word ("</p>");
+ in_paragraph = 0;
+ }
+ add_word ("<li>");
+ }
+ else
+ {
+ start_paragraph ();
+ kill_self_indent (-1);
+ filling_enabled = indented_fill = 1;
+
+ if (current_item_function ())
+ {
+ output_column = current_indent - 2;
+ indent (output_column);
+
+ /* 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 (' ');
+ output_column++;
+ }
+ 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)
+ {
+ static int last_html_output_position = 0;
+
+ /* 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. */
+ if (last_html_output_position == output_position
+ && strncmp ((char *) output_paragraph, "<dd>",
+ output_paragraph_offset) == 0)
+ output_paragraph_offset = 0;
+
+ /* Force the browser to render one blank line before
+ each new @item in a table. But don't do that unless
+ 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
+ && strncmp ((char *) output_paragraph
+ + output_paragraph_offset - sizeof (dl_tag) + 1,
+ dl_tag, sizeof (dl_tag) - 1) != 0)
+ add_word ("<br>");
+
+ add_word ("<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);
+ /* Make sure output_position is updated, so we could
+ remember it. */
+ close_single_paragraph ();
+ last_html_output_position = output_position;
+ add_word ("<dd>");
+ }
+ 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 ()
+{
+ itemx_flag++;
+ cm_item ();
+ itemx_flag--;
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/insertion.h b/gnu/usr.bin/texinfo/makeinfo/insertion.h
new file mode 100644
index 00000000000..a926e71c839
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/insertion.h
@@ -0,0 +1,61 @@
+/* insertion.h -- declarations for insertion.c.
+ $Id: insertion.h,v 1.1.1.1 2000/02/09 01:25:20 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INSERTION_H
+#define INSERTION_H
+
+/* Must match list in insertion.c. */
+enum insertion_type
+{
+ cartouche, defcv, deffn, defivar, defmac, defmethod, defop, defopt,
+ defspec, deftp, deftypefn, deftypefun, deftypeivar, deftypemethod,
+ deftypeop, deftypevar, deftypevr, defun, defvar, defvr, detailmenu,
+ direntry, display, enumerate, example, flushleft, flushright, format,
+ ftable, group, ifclear, ifhtml, ifinfo, ifnothtml, ifnotinfo,
+ ifnottex, ifset, iftex, itemize, lisp, menu, multitable, quotation,
+ rawhtml, rawtex, smalldisplay, smallexample, smallformat, smalllisp,
+ table, tex, vtable, bad_type
+};
+
+typedef struct istack_elt
+{
+ struct istack_elt *next;
+ char *item_function;
+ char *filename;
+ int line_number;
+ int filling_enabled;
+ int indented_fill;
+ enum insertion_type 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 void command_name_condition ();
+extern void cm_ifnothtml (), cm_ifhtml(), cm_html ();
+extern void cm_ifinfo (), cm_ifnotinfo ();
+extern void cm_ifnottex (), cm_iftex (), cm_tex ();
+#endif /* !INSERTION_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/lang.c b/gnu/usr.bin/texinfo/makeinfo/lang.c
new file mode 100644
index 00000000000..44c84262669
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/lang.c
@@ -0,0 +1,415 @@
+/* lang.c -- language depend behaviour (startpoint)
+ $Id: lang.c,v 1.1.1.1 2000/02/09 01:25:20 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#include "system.h"
+#include "cmds.h"
+#include "lang.h"
+#include "makeinfo.h"
+
+/* Current document encoding. */
+char *document_encoding = NULL;
+
+/* Current language code; default is English. */
+language_code_type language_code = en;
+
+language_struct language_table[] = {
+ { aa, "aa", "Afar" },
+ { ab, "ab", "Abkhazian" },
+ { af, "af", "Afrikaans" },
+ { am, "am", "Amharic" },
+ { ar, "ar", "Arabic" },
+ { as, "as", "Assamese" },
+ { ay, "ay", "Aymara" },
+ { az, "az", "Azerbaijani" },
+ { ba, "ba", "Bashkir" },
+ { be, "be", "Byelorussian" },
+ { bg, "bg", "Bulgarian" },
+ { bh, "bh", "Bihari" },
+ { bi, "bi", "Bislama" },
+ { bn, "bn", "Bengali; Bangla" },
+ { bo, "bo", "Tibetan" },
+ { br, "br", "Breton" },
+ { ca, "ca", "Catalan" },
+ { co, "co", "Corsican" },
+ { cs, "cs", "Czech" },
+ { cy, "cy", "Welsh" },
+ { da, "da", "Danish" },
+ { de, "de", "German" },
+ { dz, "dz", "Bhutani" },
+ { el, "el", "Greek" },
+ { en, "en", "English" },
+ { eo, "eo", "Esperanto" },
+ { es, "es", "Spanish" },
+ { et, "et", "Estonian" },
+ { eu, "eu", "Basque" },
+ { fa, "fa", "Persian" },
+ { fi, "fi", "Finnish" },
+ { fj, "fj", "Fiji" },
+ { fo, "fo", "Faroese" },
+ { fr, "fr", "French" },
+ { fy, "fy", "Frisian" },
+ { ga, "ga", "Irish" },
+ { gd, "gd", "Scots Gaelic" },
+ { gl, "gl", "Galician" },
+ { gn, "gn", "Guarani" },
+ { gu, "gu", "Gujarati" },
+ { ha, "ha", "Hausa" },
+ { he, "he", "Hebrew" } /* (formerly iw) */,
+ { hi, "hi", "Hindi" },
+ { hr, "hr", "Croatian" },
+ { hu, "hu", "Hungarian" },
+ { hy, "hy", "Armenian" },
+ { ia, "ia", "Interlingua" },
+ { id, "id", "Indonesian" } /* (formerly in) */,
+ { ie, "ie", "Interlingue" },
+ { ik, "ik", "Inupiak" },
+ { is, "is", "Icelandic" },
+ { it, "it", "Italian" },
+ { iu, "iu", "Inuktitut" },
+ { ja, "ja", "Japanese" },
+ { jw, "jw", "Javanese" },
+ { ka, "ka", "Georgian" },
+ { kk, "kk", "Kazakh" },
+ { kl, "kl", "Greenlandic" },
+ { km, "km", "Cambodian" },
+ { kn, "kn", "Kannada" },
+ { ko, "ko", "Korean" },
+ { ks, "ks", "Kashmiri" },
+ { ku, "ku", "Kurdish" },
+ { ky, "ky", "Kirghiz" },
+ { la, "la", "Latin" },
+ { ln, "ln", "Lingala" },
+ { lo, "lo", "Laothian" },
+ { lt, "lt", "Lithuanian" },
+ { lv, "lv", "Latvian, Lettish" },
+ { mg, "mg", "Malagasy" },
+ { 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" },
+ { ne, "ne", "Nepali" },
+ { nl, "nl", "Dutch" },
+ { no, "no", "Norwegian" },
+ { oc, "oc", "Occitan" },
+ { om, "om", "(Afan) Oromo" },
+ { or, "or", "Oriya" },
+ { pa, "pa", "Punjabi" },
+ { pl, "pl", "Polish" },
+ { ps, "ps", "Pashto, Pushto" },
+ { pt, "pt", "Portuguese" },
+ { qu, "qu", "Quechua" },
+ { rm, "rm", "Rhaeto-Romance" },
+ { rn, "rn", "Kirundi" },
+ { ro, "ro", "Romanian" },
+ { ru, "ru", "Russian" },
+ { rw, "rw", "Kinyarwanda" },
+ { sa, "sa", "Sanskrit" },
+ { sd, "sd", "Sindhi" },
+ { sg, "sg", "Sangro" },
+ { sh, "sh", "Serbo-Croatian" },
+ { si, "si", "Sinhalese" },
+ { sk, "sk", "Slovak" },
+ { sl, "sl", "Slovenian" },
+ { sm, "sm", "Samoan" },
+ { sn, "sn", "Shona" },
+ { so, "so", "Somali" },
+ { sq, "sq", "Albanian" },
+ { sr, "sr", "Serbian" },
+ { ss, "ss", "Siswati" },
+ { st, "st", "Sesotho" },
+ { 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", "Setswana" },
+ { to, "to", "Tonga" },
+ { tr, "tr", "Turkish" },
+ { ts, "ts", "Tsonga" },
+ { tt, "tt", "Tatar" },
+ { tw, "tw", "Twi" },
+ { ug, "ug", "Uighur" },
+ { uk, "uk", "Ukrainian" },
+ { ur, "ur", "Urdu" },
+ { uz, "uz", "Uzbek" },
+ { vi, "vi", "Vietnamese" },
+ { vo, "vo", "Volapuk" },
+ { wo, "wo", "Wolof" },
+ { xh, "xh", "Xhosa" },
+ { yi, "yi", "Yiddish" } /* (formerly ji) */,
+ { yo, "yo", "Yoruba" },
+ { za, "za", "Zhuang" },
+ { zh, "zh", "Chinese" },
+ { zu, "zu", "Zulu" },
+ { last_language_code, NULL, NULL }
+};
+
+/* @documentlanguage. Maybe we'll do something useful with this in the
+ future. For now, we just recognize it. */
+void
+cm_documentlanguage ()
+{
+ language_code_type c;
+ char *lang_arg;
+
+ /* Read the line with the language code on it. */
+ get_rest_of_line (1, &lang_arg);
+
+ /* Linear search is fine these days. */
+ for (c = aa; c != last_language_code; c++)
+ {
+ if (strcmp (lang_arg, 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"), lang_arg);
+
+ free (lang_arg);
+}
+
+
+
+/* @documentencoding. Set global. */
+void
+cm_documentencoding ()
+{
+ get_rest_of_line (1, &document_encoding);
+}
+
+
+
+/* Accent commands that take explicit arguments and don't have any
+ special HTML support. */
+
+void
+cm_accent (arg)
+ int arg;
+{
+ 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 ? "&macr;" : "=");
+ else if (strcmp (command, "H") == 0) /* Hungarian umlaut */
+ add_word ("''");
+ else if (strcmp (command, "dotaccent") == 0) /* overdot */
+ add_meta_char ('.');
+ else if (strcmp (command, "ringaccent") == 0) /* ring */
+ add_char ('*');
+ else if (strcmp (command, "tieaccent") == 0) /* long tie */
+ add_char ('[');
+ else if (strcmp (command, "u") == 0) /* breve */
+ add_char ('(');
+ else if (strcmp (command, "ubaraccent") == 0) /* underbar */
+ add_char ('_');
+ else if (strcmp (command, "v") == 0) /* hacek/check */
+ add_word (html ? "&lt;" : "<");
+ }
+}
+
+/* Common routine for the accent characters that have support in HTML.
+ If the character being accented is in the HTML_SUPPORTED set, then
+ produce &CHTML_SOLO;, for example, &Auml; for an A-umlaut. If not in
+ HTML_SUPPORTED, just produce &HTML_SOLO;X for the best we can do with
+ at an X-umlaut. Finally, 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 zero the given HTML_SOLO
+ does not exist as valid standalone character in HTML. */
+
+static void
+cm_accent_generic (arg, start, end, html_supported, single,
+ html_solo_standalone, html_solo)
+ int arg, start, end;
+ char *html_supported;
+ int single;
+ int html_solo_standalone;
+ char *html_solo;
+{
+ if (html)
+ {
+ static int valid_html_accent;
+
+ 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). */
+ add_char ('&');
+ valid_html_accent = 1;
+ }
+ else
+ { /* No special HTML support, so produce standalone char. */
+ valid_html_accent = 0;
+ if (html_solo_standalone)
+ {
+ add_char ('&');
+ add_word (html_solo);
+ add_char (';');
+ }
+ else
+ /* If the html_solo does not exist as standalone character
+ (namely &circ; &grave; &tilde;), then we use
+ the single character version instead. */
+ add_char (single);
+ }
+ }
+ 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 (';');
+ }
+ }
+ }
+ else if (arg == END)
+ { /* Not producing HTML, so just use the normal character. */
+ add_char (single);
+ }
+}
+
+void
+cm_accent_umlaut (arg, start, end)
+ int arg, start, end;
+{
+ cm_accent_generic (arg, start, end, "aouAOUEeIiy", '"', 1, "uml");
+}
+
+void
+cm_accent_acute (arg, start, end)
+ int arg, start, end;
+{
+ cm_accent_generic (arg, start, end, "AEIOUYaeiouy", '\'', 1, "acute");
+}
+
+void
+cm_accent_cedilla (arg, start, end)
+ int arg, start, end;
+{
+ cm_accent_generic (arg, start, end, "Cc", ',', 1, "cedil");
+}
+
+void
+cm_accent_hat (arg, start, end)
+ int arg, start, end;
+{
+ cm_accent_generic (arg, start, end, "AEIOUaeiou", '^', 0, "circ");
+}
+
+void
+cm_accent_grave (arg, start, end)
+ int arg, start, end;
+{
+ cm_accent_generic (arg, start, end, "AEIOUaeiou", '`', 0, "grave");
+}
+
+void
+cm_accent_tilde (arg, start, end)
+ int arg, start, end;
+{
+ cm_accent_generic (arg, start, end, "AOano", '~', 0, "tilde");
+}
+
+
+
+/* Non-English letters/characters that don't insert themselves. */
+void
+cm_special_char (arg)
+{
+ 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 (html && (command[0] == 'O' || command[0] == 'o'))
+ add_word_args ("&%cslash;", command[0]);
+ else
+ add_word_args ("/%c", command[0]);
+ }
+ else if (strcmp (command, "exclamdown") == 0)
+ add_word (html ? "&iexcl;" : "!");
+ else if (strcmp (command, "pounds") == 0)
+ add_word (html ? "&pound;" : "#");
+ else if (strcmp (command, "questiondown") == 0)
+ add_word (html ? "&iquest;" : "?");
+ else if (strcmp (command, "AE") == 0)
+ add_word (html ? "&AElig;" : command);
+ else if (strcmp (command, "ae") == 0)
+ add_word (html ? "&aelig;" : command);
+ else if (strcmp (command, "OE") == 0)
+ add_word (html ? "&#140;" : command);
+ else if (strcmp (command, "oe") == 0)
+ add_word (html ? "&#156;" : command);
+ else if (strcmp (command, "AA") == 0)
+ add_word (html ? "&Aring;" : command);
+ else if (strcmp (command, "aa") == 0)
+ add_word (html ? "&aring;" : command);
+ else if (strcmp (command, "ss") == 0)
+ add_word (html ? "&szlig;" : command);
+ else
+ line_error ("cm_special_char internal error: command=@%s", command);
+ }
+}
+
+/* Dotless i or j. */
+void
+cm_dotless (arg, start, end)
+ int arg, start, end;
+{
+ if (arg == END)
+ {
+ 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. */
+ }
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/lang.h b/gnu/usr.bin/texinfo/makeinfo/lang.h
new file mode 100644
index 00000000000..40c48aca98d
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/lang.h
@@ -0,0 +1,86 @@
+/* lang.h -- declarations for language codes etc.
+ $Id: lang.h,v 1.1.1.1 2000/02/09 01:25:20 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#ifndef LANG_H
+#define LANG_H
+
+/* The langauge code which can be changed through @documentlanguage
+ * Actualy Info does not support this (may be in the future) ;-)
+ * Default for language code is en (english!) kama
+ * These code should ISO 639 two letter codes.
+ */
+typedef enum
+{
+ aa, ab, af, am, ar, as, ay, az,
+ ba, be, bg, bh, bi, bn, bo, br,
+ ca, co, cs, cy,
+ da, de, dz,
+ el, en, eo, es, et, eu,
+ fa, fi, fj, fo, fr, fy,
+ ga, gd, gl, gn, gu,
+ ha, he, hi, hr, hu, hy,
+ ia, id, ie, ik, is, it, iu,
+ ja, jw,
+ ka, kk, kl, km, kn, ko, ks, ku, ky,
+ la, ln, lo, lt, lv,
+ mg, mi, mk, ml, mn, mo, mr, ms, mt, my,
+ na, ne, nl, no,
+ oc, om, or,
+ pa, pl, ps, pt,
+ qu,
+ rm, rn, ro, ru, rw,
+ sa, sd, sg, sh, 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,
+ ug, uk, ur, uz,
+ vi, vo,
+ wo,
+ xh,
+ yi, yo,
+ za, zh, zu,
+ last_language_code
+} language_code_type;
+
+/* The current language code. */
+extern language_code_type language_code;
+
+/* Information about all valid languages. */
+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_struct;
+extern language_struct language_table[];
+
+/* The encoding, or null if not set. */
+extern char *document_encoding;
+
+
+/* The commands. */
+extern void cm_documentlanguage (), cm_documentencoding ();
+
+/* Accents, other non-English characters. */
+void cm_accent (), cm_special_char (), cm_dotless ();
+
+extern void cm_accent_umlaut (), cm_accent_acute (), cm_accent_cedilla (),
+ cm_accent_hat (), cm_accent_grave (), cm_accent_tilde ();
+
+#endif /* not LANG_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/macro.c b/gnu/usr.bin/texinfo/makeinfo/macro.c
new file mode 100644
index 00000000000..82821b9fe86
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macro.c
@@ -0,0 +1,1114 @@
+/* macro.c -- user-defined macros for Texinfo.
+ $Id: macro.c,v 1.1.1.1 2000/02/09 01:25:23 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "cmds.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 (array)
+ char **array;
+{
+ int i = 0;
+
+ if (array)
+ for (i = 0; array[i]; i++);
+
+ return i;
+}
+
+void
+free_array (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 (name)
+ 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. */
+void
+add_macro (name, arglist, body, source_file, source_lineno, flags)
+ char *name;
+ char **arglist;
+ char *body;
+ char *source_file;
+ int source_lineno, 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->inhibited = 0;
+ def->flags = flags;
+}
+
+
+char **
+get_brace_args (quote_single)
+ int quote_single;
+{
+ 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_single) ||
+ ((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;
+}
+
+char **
+get_macro_args (def)
+ 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[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->flags & ME_QUOTE_ARG);
+}
+
+/* Substitute actual parameters for named parameters in body.
+ The named parameters which appear in BODY must by surrounded
+ reverse slashes, as in \foo\. */
+char *
+apply (named, actuals, body)
+ char **named, **actuals, *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, which, 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++;
+
+ /* Now 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);
+ }
+ else
+ { /* not a parameter, either it's \\ (if len==0) or an
+ error. In either case, restore one \ at least. */
+ if (len) {
+ warning (_("\\ in macro expansion followed by `%s' instead of \\ or 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;
+
+ if (!named || !named[which])
+ 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 (def)
+ MACRO_DEF *def;
+{
+ char **arglist;
+ int num_args;
+ char *execution_string = NULL;
+ int start_line = line_number;
+
+ /* Find out how many arguments this macro definition takes. */
+ num_args = array_len (def->arglist);
+
+ /* Gather the arguments present on the line if there are any. */
+ arglist = get_macro_args (def);
+
+ if (num_args < 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 (def)
+ 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 (mactype, recursive)
+ char *mactype;
+ int recursive;
+{
+ int i;
+ char *name, **arglist, *body, *line, *last_end;
+ int body_size, body_index;
+ int depth = 1;
+ int defining_line = line_number;
+ int flags = 0;
+
+ arglist = NULL;
+ body = NULL;
+ body_size = 0;
+ body_index = 0;
+
+ 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. */
+ {
+ int 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;
+ }
+ }
+ }
+
+ /* If we have exactly one argument, do @quote-arg implicitly. Not
+ only does this match TeX's behavior (which can't feasibly be
+ changed), but it's a good idea. */
+ if (arglist_index == 1)
+ flags |= ME_QUOTE_ARG;
+ }
+
+ /* 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)
+ {
+ int temp_line = line_number;
+ line_number = defining_line;
+ line_error (_("%cend macro not found"), COMMAND_PREFIX);
+ line_number = temp_line;
+ 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])))
+ {
+ 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])))
+ {
+ for (i = 10; whitespace (line[i]); i++);
+ strcpy (line, line + i);
+
+ if (arglist && arglist[0] && !arglist[1])
+ {
+ flags |= ME_QUOTE_ARG;
+ if (!*line)
+ {
+ free (line);
+ continue;
+ }
+ }
+ else
+ line_error (_("@quote-arg only useful for single-argument macros"));
+ }
+
+ 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", 9) == 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_itext (input_text, input_text_offset);
+}
+
+void
+cm_macro ()
+{
+ define_macro ("macro", 0);
+}
+
+void
+cm_rmacro ()
+{
+ 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 (name)
+ 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 ()
+{
+ 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 (pointer, offset)
+ 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 (pointer)
+ 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 ()
+{
+ 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 (execution_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 (execution_string, append_string)
+ char *execution_string, *append_string;
+{
+ int op_orig, opcol_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;
+ opcol_orig = output_column;
+ 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;
+ output_column = opcol_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 (offset)
+ 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 (pointer, offset)
+ 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 (string, start, end)
+ char *string;
+ int start, 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 */
+void
+cm_alias ()
+{
+ alias_type *a = xmalloc (sizeof (alias_type));
+
+ skip_whitespace ();
+ get_until_in_line (1, "=", &(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 (tok)
+ 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 ()
+{
+ 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 (tok)
+ 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 (arg, start, end)
+ int arg, start, 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/gnu/usr.bin/texinfo/makeinfo/macro.h b/gnu/usr.bin/texinfo/makeinfo/macro.h
new file mode 100644
index 00000000000..4fac46b268d
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/macro.h
@@ -0,0 +1,71 @@
+/* macro.h -- declarations for macro.c.
+ $Id: macro.h,v 1.1.1.1 2000/02/09 01:25:23 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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. */
+ 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, ME_QUOTE_ARG, etc. */
+} MACRO_DEF;
+
+/* flags for MACRO_DEF */
+#define ME_RECURSE 0x01
+#define ME_QUOTE_ARG 0x02
+
+extern void execute_macro ();
+extern MACRO_DEF *find_macro ();
+extern char *expand_macro ();
+
+extern ITEXT *remember_itext ();
+extern void forget_itext ();
+extern void maybe_write_itext ();
+extern void write_region_to_macro_output ();
+extern void append_to_expansion_output ();
+extern void me_append_before_this_command ();
+extern void me_execute_string ();
+
+extern char *alias_expand ();
+extern int enclosure_command ();
+extern void enclosure_expand ();
+
+/* The @commands. */
+extern void cm_macro (), cm_rmacro (), cm_unmacro ();
+extern void cm_alias (), cm_definfoenclose ();
+
+#endif /* not MACRO_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/node.c b/gnu/usr.bin/texinfo/makeinfo/node.c
new file mode 100644
index 00000000000..bb649d3f098
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/node.c
@@ -0,0 +1,1568 @@
+/* node.c -- nodes for Texinfo.
+ $Id: node.c,v 1.1 2000/02/09 01:25:30 espie Exp $
+
+ Copyright (C) 1998, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "system.h"
+#include "cmds.h"
+#include "files.h"
+#include "footnote.h"
+#include "macro.h"
+#include "makeinfo.h"
+#include "node.h"
+#include "sectioning.h"
+#include "insertion.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 current_section = 0;
+int outstanding_node = 0;
+
+/* Adding nodes, and making tags. */
+
+/* Start a new tag table. */
+void
+init_tag_table ()
+{
+ 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 (indirect_p)
+ int indirect_p;
+{
+ TAG_ENTRY *node;
+ int old_indent = no_indent;
+
+ 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 ()
+{
+ write_tag_table_internal (0); /* Not indirect. */
+}
+
+void
+write_tag_table_indirect ()
+{
+ write_tag_table_internal (1);
+}
+
+/* Convert "top" and friends into "Top". */
+static void
+normalize_node_name (string)
+ char *string;
+{
+ if (strcasecmp (string, "Top") == 0)
+ strcpy (string, "Top");
+}
+
+char *
+get_node_token (expand)
+ 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 (node)
+ 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 (name)
+ 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;
+}
+
+/* Similarly for next etc. references in a @node command, where we
+ don't care about most of the entries. */
+static void
+remember_node_node_reference (node)
+ 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. */
+void
+remember_node (node, prev, next, up, position, line_no, flags)
+ char *node, *prev, *next, *up;
+ int position, line_no, 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;
+ }
+ new->next_ent = tag_table;
+ 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 (node, line, type)
+ 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 (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 && 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 (remember_ref, ref_type)
+ 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;
+ size_t 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 (fname)
+ const char *fname;
+{
+ if (current_output_filename)
+ free (current_output_filename);
+ current_output_filename = xstrdup (fname);
+}
+
+/* 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 ()
+{
+ char *node, *prev, *next, *up;
+ int new_node_pos, defaulting, this_section;
+ int no_warn = 0;
+
+ 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 ();
+ }
+
+ if (html && splitting && top_node_seen)
+ {
+ /* End the current split output file. */
+ close_paragraph ();
+ output_pending_notes ();
+ start_paragraph ();
+ /* Fixme: html: need a navigation bar here. */
+ add_word ("</body></html>\n");
+ close_paragraph ();
+ fclose (output_stream);
+ output_stream = NULL;
+ }
+
+ filling_enabled = indented_fill = 0;
+ new_node_pos = output_position;
+ if (!html || (html && splitting))
+ current_footnote_number = 1;
+
+ 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 (verbose_mode)
+ printf (_("Formatting node %s...\n"), node);
+
+ if (macro_expansion_output_stream && !executing_string)
+ remember_itext (input_text, input_text_offset);
+
+ no_indent = 1;
+ if (!no_headers && !html)
+ {
+ 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);
+
+ /* 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;
+
+ /* 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
+ && 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, 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
+ && strcasecmp (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, no_warn);
+ outstanding_node = 1;
+ }
+
+ if (html)
+ {
+ char *tem;
+
+ if (splitting)
+ { /* this code not operational, we do not currently split html */
+ char filename[20];
+
+ sprintf (filename, "node%d.html", number_of_node (node));
+ output_stream = fopen (filename, "w");
+ if (output_stream == NULL)
+ {
+ fs_error (filename);
+ xexit (1);
+ }
+ set_current_output_filename (filename);
+ /* FIXME: when this code is operational, we will need to
+ expand node, next, prev, and up before output. */
+ add_word_args ("<html><head><title>%s</title>", node);
+ if (next) add_link (next, "rel=next");
+ if (prev) add_link (prev, "rel=previous");
+ if (up) add_link (up, "rel=up");
+ add_word ("</head>\n<body>\n");
+ }
+
+ if (!splitting && no_headers)
+ { /* cross refs need a name="#anchor" even if we're not writing headers*/
+ add_word ("<a name=\"");
+ tem = expand_node_name (node);
+ add_anchor_name (tem, 0);
+ add_word ("\"></a>");
+ free (tem);
+ }
+
+ if (splitting || !no_headers)
+ { /* Navigation bar. The <p> avoids the links area running
+ on with old Lynxen. */
+ add_word_args ("<p>%s\n", splitting ? "" : "<hr>");
+ add_word_args ("%s<a name=\"", _("Node:"));
+ tem = expand_node_name (node);
+ add_anchor_name (tem, 0);
+ add_word_args ("\">%s</a>", tem);
+ free (tem);
+
+ if (next)
+ {
+ add_word (",\n");
+ add_word (_("Next:"));
+ add_word ("<a rel=next href=\"");
+ tem = expansion (next, 0);
+ add_anchor_name (tem, 1);
+ add_word_args ("\">%s</a>", tem);
+ free (tem);
+ }
+ if (prev)
+ {
+ add_word (",\n");
+ add_word (_("Previous:"));
+ add_word ("<a rel=previous href=\"");
+ tem = expansion (prev, 0);
+ add_anchor_name (tem, 1);
+ add_word_args ("\">%s</a>", tem);
+ free (tem);
+ }
+ if (up)
+ {
+ add_word (",\n");
+ add_word (_("Up:"));
+ add_word ("<a rel=up href=\"");
+ tem = expansion (up, 0);
+ add_anchor_name (tem, 1);
+ add_word_args ("\">%s</a>", tem);
+ free (tem);
+ }
+ /* html fixxme: we want a `top' or `contents' link here. */
+
+ add_word_args ("\n%s<br>\n", splitting ? "<hr>" : "");
+ }
+ }
+
+ 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 (arg)
+ int arg;
+{
+ char *anchor;
+
+ if (arg == END)
+ return;
+
+ /* Parse the anchor text. */
+ anchor = get_xref_token (1);
+
+ /* 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)
+ {
+ 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>");
+ }
+
+ /* Save it in the tag table. */
+ remember_node (anchor, NULL, NULL, NULL, output_position + output_column,
+ line_number, TAG_FLAG_ANCHOR);
+}
+
+/* Find NODE in REF_LIST. */
+static NODE_REF *
+find_node_reference (node, ref_list)
+ 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 ()
+{
+ 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 ()
+{
+ 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 (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 (tag, line, label)
+ char *tag;
+ int line;
+ 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'"), 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 char *
+reftype_type_string (type)
+ enum reftype type;
+{
+ switch (type)
+ {
+ case menu_reference:
+ return _("Menu");
+ case followed_reference:
+ return _("Cross");
+ default:
+ return "Internal-bad-reference-type";
+ }
+}
+
+static void
+validate_other_references (ref_list)
+ 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_table)
+ 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, _("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 (STREQ (tem1, tem2))
+ you_lose = 0;
+ free (tem1);
+ free (tem2);
+ }
+ if (you_lose)
+ {
+ line_error (_("Next field of node `%s' not pointed to"),
+ tags->node);
+ line_number = temp_tag->line_no;
+ input_filename = temp_tag->filename;
+ line_error (_("This node (%s) has the bad Prev"),
+ temp_tag->node);
+ input_filename = tags->filename;
+ line_number = tags->line_no;
+ 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, _("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);
+ line_number = temp_tag->line_no;
+ input_filename = temp_tag->filename;
+ line_error (_("This node (%s) has the bad Next"),
+ temp_tag->node);
+ input_filename = tags->filename;
+ line_number = tags->line_no;
+ temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
+ }
+ }
+ }
+ }
+ }
+
+ if (!tags->up
+ && !(tags->flags & TAG_FLAG_ANCHOR)
+ && strcasecmp (tags->node, "Top") != 0)
+ line_error (_("`%s' has no Up field"), tags->node);
+ else if (tags->up)
+ {
+ int valid_p = validate (tags->up, tags->line_no, _("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);
+ line_number = temp_tag->line_no;
+ input_filename = temp_tag->filename;
+ line_error (
+ _("Node `%s' lacks menu item for `%s' despite being its Up target"),
+ tags->up, tags->node);
+ line_number = tags->line_no;
+ input_filename = tags->filename;
+ }
+ }
+ }
+ }
+ 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;
+ }
+
+ /* Special hack. If the node in question appears to have
+ been referenced more than REFERENCE_WARNING_LIMIT times,
+ give a warning. */
+ if (tags->touched > reference_warning_limit)
+ {
+ input_filename = tags->filename;
+ line_number = tags->line_no;
+ warning (_("node `%s' has been referenced %d times"),
+ tags->node, tags->touched);
+ }
+
+ 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 (strcasecmp (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 (tags)
+ 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;
+}
+
+
+/* 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 (filename, size)
+ char *filename;
+ int size;
+{
+ char *root_filename, *root_pathname;
+ char *the_file, *filename_part ();
+ struct stat fileinfo;
+ long file_size;
+ char *the_header;
+ int header_size;
+ int dos_file_names = 0; /* if nonzero, don't exceed 8+3 limits */
+
+ /* 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) < SPLIT_SIZE_THRESHOLD))
+ return;
+ file_size = (long) fileinfo.st_size;
+
+ the_file = find_and_load (filename);
+ if (!the_file)
+ return;
+
+ root_filename = filename_part (filename);
+ root_pathname = pathname_part (filename);
+
+ /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */
+ dos_file_names = !HAVE_LONG_FILENAMES (root_pathname ? root_pathname : ".");
+
+ 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;
+
+ /* 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, *split_basename;
+ unsigned root_len = strlen (root_filename);
+
+ split_filename = xmalloc (10 + strlen (root_pathname)
+ + root_len);
+ split_basename = xmalloc (10 + root_len);
+ sprintf (split_basename, "%s-%d", root_filename, which_file);
+ if (dos_file_names)
+ {
+ char *dot = strchr (split_basename, '.');
+ unsigned base_len = strlen (split_basename);
+
+ if (dot)
+ { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
+ dot[1] = 'i';
+ memmove (which_file <= 99 ? dot + 2 : dot + 1,
+ split_basename + root_len + 1,
+ strlen (split_basename + root_len + 1) + 1);
+ }
+ else if (base_len > 8)
+ {
+ /* Make foobar-1, .., fooba-10, .., foob-100, ... */
+ unsigned numlen = base_len - root_len;
+
+ memmove (split_basename + 8 - numlen,
+ split_basename + root_len, numlen + 1);
+ }
+ }
+ sprintf (split_filename, "%s%s", root_pathname,
+ split_basename);
+
+ 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)
+ || (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_tag_table_indirect ();
+ fclose (output_stream);
+ free (the_header);
+ free (the_file);
+ return;
+ }
+ }
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/node.h b/gnu/usr.bin/texinfo/makeinfo/node.h
new file mode 100644
index 00000000000..3539f88f480
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/node.h
@@ -0,0 +1,111 @@
+/* node.h -- declarations for Node.
+ $Id: node.h,v 1.1.1.1 2000/02/09 01:25:30 espie Exp $
+
+ Copyright (C) 1996, 97, 98, 99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Written by Brian Fox (bfox@ai.mit.edu). */
+
+#ifndef NODE_H
+#define NODE_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. */
+} 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;
+
+/* 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 ();
+
+/* 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 ();
+
+/* Remember a node for later validation. */
+extern void remember_node_reference ();
+
+/* Remember the name of the current output file. */
+extern void set_current_output_filename ();
+
+/* Expand macros and commands in the node name and canonicalize
+ whitespace in the resulting expansion. */
+extern char *expand_node_name ();
+
+#endif /* NODE_H */
diff --git a/gnu/usr.bin/texinfo/makeinfo/sectioning.c b/gnu/usr.bin/texinfo/makeinfo/sectioning.c
new file mode 100644
index 00000000000..5ec18dfd5b0
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/sectioning.c
@@ -0,0 +1,691 @@
+/* sectioning.c -- all related stuff @chapter, @section... @contents
+ $Id: sectioning.c,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ 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"
+
+/* 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 },
+
+ { "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;
+
+
+/* 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 */
+char *
+get_sectioning_number (level, num)
+ 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]);
+
+ return s;
+}
+
+
+/* Set the level of @top to LEVEL. Return the old level of @top. */
+int
+set_top_section_level (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 */
+int
+search_sectioning (text)
+ 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 section present in TEXT. */
+int
+what_section (text)
+ char *text;
+{
+ 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;
+ return return_val;
+ }
+ return -1;
+}
+
+
+void
+sectioning_underscore (cmd)
+ char *cmd;
+{
+ char character;
+ char *temp;
+ int level;
+
+ temp = xmalloc (2 + strlen (cmd));
+ temp[0] = COMMAND_PREFIX;
+ strcpy (&temp[1], cmd);
+ level = what_section (temp);
+ free (temp);
+ level -= 2;
+
+ if (level < 0)
+ level = 0;
+
+ if (html)
+ sectioning_html (level, cmd);
+ else
+ {
+ character = scoring_characters[level];
+ insert_and_underscore (level, character, cmd);
+ }
+}
+
+/* insert_and_underscore and sectioning_html are the
+ only functions which call this.
+ I have created this, because it was exactly the same
+ code in both functions. */
+static char *
+handle_enum_increment (level, index)
+ int level;
+ int index;
+{
+ /* special for unnumbered */
+ if (number_sections && section_alist[index].num == ENUM_SECT_NO)
+ {
+ if (level == 0
+ && enum_marker != UNNUMBERED_MAGIC)
+ enum_marker = UNNUMBERED_MAGIC;
+ }
+ /* enumerate only things which are allowed */
+ if (number_sections && section_alist[index].num)
+ {
+ /* reset the marker if we get into enumerated areas */
+ if (section_alist[index].num == ENUM_SECT_YES
+ && level == 0
+ && enum_marker == UNNUMBERED_MAGIC)
+ enum_marker = 0;
+ /* This is special for appendix; if we got the first
+ time an appendix command then we are entering appendix.
+ Thats the point we have to start countint with A, B and so on. */
+ if (section_alist[index].num == ENUM_SECT_APP
+ && level == 0
+ && enum_marker != APPENDIX_MAGIC)
+ {
+ enum_marker = APPENDIX_MAGIC;
+ numbers [0] = 0; /* this means we start with Appendix A */
+ }
+
+ /* only increment counters if we are not in unnumbered
+ area. This handles situations like this:
+ @unnumbered .... This sets enum_marker to UNNUMBERED_MAGIC
+ @section .... */
+ if (enum_marker != UNNUMBERED_MAGIC)
+ {
+ int i;
+
+ /* reset all counters which are one level deeper */
+ for (i = level; i < 3; i++)
+ numbers [i + 1] = 0;
+
+ numbers[level]++;
+ return xstrdup
+ (get_sectioning_number (level, section_alist[index].num));
+ }
+ } /* if (number_sections)... */
+
+ return xstrdup ("");
+}
+
+
+/* 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 (level, with_char, cmd)
+ int level;
+ int with_char;
+ char *cmd;
+{
+ int i, len;
+ int index;
+ int old_no_indent;
+ unsigned char *starting_pos, *ending_pos;
+ char *temp;
+
+ 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);
+ starting_pos = output_paragraph + output_paragraph_offset;
+
+ 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 (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 (starting_pos, 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 (level, cmd)
+ 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;
+
+ add_word_args ("<h%d>", level + 1); /* level 0 is <h1> */
+
+ /* If we are outside of any node, produce an anchor that
+ the TOC could refer to. */
+ if (!current_node || !*current_node)
+ {
+ starting_pos = output_paragraph + output_paragraph_offset;
+ add_word_args ("<a name=\"TOC%d\">", toc_ref_count++);
+ toc_anchor = substring (starting_pos + 9,
+ output_paragraph + output_paragraph_offset);
+ }
+ 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);
+
+ 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. */
+ add_word (handle_enum_increment (level, index));
+
+ /* 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 (starting_pos, ending_pos),
+ level, current_node, toc_anchor);
+
+ free (temp);
+
+ if (outstanding_node)
+ outstanding_node = 0;
+
+ add_word_args ("</h%d>", level+1);
+ close_paragraph();
+ filling_enabled = 1;
+ no_indent = old_no_indent;
+}
+
+
+/* Shift the meaning of @section to @chapter. */
+void
+cm_raisesections ()
+{
+ discard_until ("\n");
+ section_alist_offset--;
+}
+
+/* Shift the meaning of @chapter to @section. */
+void
+cm_lowersections ()
+{
+ discard_until ("\n");
+ section_alist_offset++;
+}
+
+/* The command still works, but prints a warning message in addition. */
+void
+cm_ideprecated (arg, start, end)
+ int arg, start, 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 ()
+{
+ /* 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)
+ {
+ int old_line_number = line_number;
+ char *old_input_filename = input_filename;
+
+ line_number = tag->line_no;
+ input_filename = tag->filename;
+ line_error (_("Here is the %ctop node"), COMMAND_PREFIX);
+ input_filename = old_input_filename;
+ line_number = old_line_number;
+ return;
+ }
+ tag = tag->next_ent;
+ }
+ }
+ else
+ {
+ TAG_ENTRY *top_node = find_node ("Top");
+ top_node_seen = 1;
+
+ /* It is an error to use @top before you have used @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;
+ }
+ else if (html && splitting)
+ {
+ char *next = top_node ? top_node->next : NULL;
+
+ add_word ("<p>");
+ if (next)
+ {
+ add_word (_("Next:"));
+ add_word ("<a rel=next href=\"");
+ add_anchor_name (next, 1);
+ add_word ("\">");
+ execute_string (next);
+ add_word ("</a>\n");
+ }
+ }
+
+ 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);
+
+ /* 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 ()
+{
+ sectioning_underscore ("chapter");
+}
+
+/* The remainder of the text on this line is a section heading. */
+void
+cm_section ()
+{
+ sectioning_underscore ("section");
+}
+
+/* The remainder of the text on this line is a subsection heading. */
+void
+cm_subsection ()
+{
+ sectioning_underscore ("subsection");
+}
+
+/* The remainder of the text on this line is a subsubsection heading. */
+void
+cm_subsubsection ()
+{
+ sectioning_underscore ("subsubsection");
+}
+
+/* The remainder of the text on this line is an unnumbered heading. */
+void
+cm_unnumbered ()
+{
+ sectioning_underscore ("unnumbered");
+}
+
+/* The remainder of the text on this line is an unnumbered section heading. */
+void
+cm_unnumberedsec ()
+{
+ sectioning_underscore ("unnumberedsec");
+}
+
+/* The remainder of the text on this line is an unnumbered
+ subsection heading. */
+void
+cm_unnumberedsubsec ()
+{
+ sectioning_underscore ("unnumberedsubsec");
+}
+
+/* The remainder of the text on this line is an unnumbered
+ subsubsection heading. */
+void
+cm_unnumberedsubsubsec ()
+{
+ sectioning_underscore ("unnumberedsubsubsec");
+}
+
+/* The remainder of the text on this line is an appendix heading. */
+void
+cm_appendix ()
+{
+ sectioning_underscore ("appendix");
+}
+
+/* The remainder of the text on this line is an appendix section heading. */
+void
+cm_appendixsec ()
+{
+ sectioning_underscore ("appendixsec");
+}
+
+/* The remainder of the text on this line is an appendix subsection heading. */
+void
+cm_appendixsubsec ()
+{
+ sectioning_underscore ("appendixsubsec");
+}
+
+/* The remainder of the text on this line is an appendix
+ subsubsection heading. */
+void
+cm_appendixsubsubsec ()
+{
+ sectioning_underscore ("appendixsubsubsec");
+}
+
+/* Compatibility functions substitute for chapter, section, etc. */
+void
+cm_majorheading ()
+{
+ sectioning_underscore ("majorheading");
+}
+
+void
+cm_chapheading ()
+{
+ sectioning_underscore ("chapheading");
+}
+
+void
+cm_heading ()
+{
+ sectioning_underscore ("heading");
+}
+
+void
+cm_subheading ()
+{
+ sectioning_underscore ("subheading");
+}
+
+void
+cm_subsubheading ()
+{
+ sectioning_underscore ("subsubheading");
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/sectioning.h b/gnu/usr.bin/texinfo/makeinfo/sectioning.h
new file mode 100644
index 00000000000..2941088f1f3
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/sectioning.h
@@ -0,0 +1,80 @@
+/* sectioning.h -- all related stuff @chapter, @section... @contents
+ $Id: sectioning.h,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#ifndef SECTIONING_H
+#define SECTIONING_H
+
+/* Sectioning. */
+extern void
+ cm_chapter (), cm_unnumbered (), cm_appendix (), cm_top (),
+ cm_section (), cm_unnumberedsec (), cm_appendixsec (),
+ cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (),
+ cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (),
+ cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (),
+ cm_majorheading (), cm_raisesections (), cm_lowersections (),
+
+ cm_ideprecated ();
+
+extern void
+ sectioning_underscore (), insert_and_underscore ();
+
+extern int what_section ();
+
+
+
+/* is needed in node.c */
+extern int set_top_section_level ();
+
+extern void sectioning_html ();
+extern int what_section ();
+
+/* 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/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.am b/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.am
new file mode 100644
index 00000000000..3e502ed4f32
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.am
@@ -0,0 +1,18 @@
+## Makefile.am for texinfo/tests/makeinfo.
+## Run automake in ../.. to produce Makefile.in from this.
+
+TESTS = cond \
+ html-extrali html-min html-manuals html-para html-title \
+ menu-whitespace \
+ no-headers \
+ node-expand node-value node-whitespace \
+ top top2
+
+noinst_SCRIPTS = $(TESTS)
+
+EXTRA_DIST = $(noinst_SCRIPTS) \
+ cond.txi \
+ html-extrali.txi html-min.txi html-para.txi html-title.txi \
+ menu-whitespace.txi \
+ node-expand.txi node-value.txi node-whitespace.txi \
+ top.txi top2.txi
diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.in b/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.in
new file mode 100644
index 00000000000..50dedc8403c
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.in
@@ -0,0 +1,223 @@
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 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.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ../..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+DATADIRNAME = @DATADIRNAME@
+GENCAT = @GENCAT@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GT_NO = @GT_NO@
+GT_YES = @GT_YES@
+INCLUDE_LOCALE_H = @INCLUDE_LOCALE_H@
+INSTOBJEXT = @INSTOBJEXT@
+INTLDEPS = @INTLDEPS@
+INTLLIBS = @INTLLIBS@
+INTLOBJS = @INTLOBJS@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+PACKAGE = @PACKAGE@
+POFILES = @POFILES@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+TERMLIBS = @TERMLIBS@
+USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+l = @l@
+
+TESTS = cond html-extrali html-min html-manuals html-para html-title menu-whitespace no-headers node-expand node-value node-whitespace top top2
+
+
+noinst_SCRIPTS = $(TESTS)
+
+EXTRA_DIST = $(noinst_SCRIPTS) cond.txi html-extrali.txi html-min.txi html-para.txi html-title.txi menu-whitespace.txi node-expand.txi node-value.txi node-whitespace.txi top.txi top2.txi
+
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../../config.h
+CONFIG_CLEAN_FILES =
+SCRIPTS = $(noinst_SCRIPTS)
+
+DIST_COMMON = Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+all: all-redirect
+.SUFFIXES:
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps makeinfo/tests/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+tags: TAGS
+TAGS:
+
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = makeinfo/tests
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+check-TESTS: $(TESTS)
+ @failed=0; all=0; \
+ srcdir=$(srcdir); export srcdir; \
+ for tst in $(TESTS); do \
+ if test -f $$tst; then dir=.; \
+ else dir="$(srcdir)"; fi; \
+ if $(TESTS_ENVIRONMENT) $$dir/$$tst; then \
+ all=`expr $$all + 1`; \
+ echo "PASS: $$tst"; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ failed=`expr $$failed + 1`; \
+ echo "FAIL: $$tst"; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ banner="All $$all tests passed"; \
+ else \
+ banner="$$failed of $$all tests failed"; \
+ fi; \
+ dashes=`echo "$$banner" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am:
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am:
+uninstall: uninstall-am
+all-am: Makefile $(SCRIPTS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: tags distdir check-TESTS info-am info dvi-am dvi check check-am \
+installcheck-am installcheck install-exec-am install-exec \
+install-data-am install-data install-am install uninstall-am uninstall \
+all-redirect all-am all installdirs mostlyclean-generic \
+distclean-generic clean-generic maintainer-clean-generic clean \
+mostlyclean distclean maintainer-clean
+
+
+# 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/gnu/usr.bin/texinfo/makeinfo/tests/cond b/gnu/usr.bin/texinfo/makeinfo/tests/cond
new file mode 100644
index 00000000000..30c28bdd25a
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/cond
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Test conditional text.
+
+: ${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 ' text.' cond.out | wc -l` -eq 3 || exit 3
+
+# Default HTML output.
+../makeinfo --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 ' text.' cond.out | wc -l` -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 ' text.' cond.out | wc -l` -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 ' text.' cond.out | wc -l` -eq 5 || exit 3
+
+rm -f cond.out cond.info
diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/cond.txi b/gnu/usr.bin/texinfo/makeinfo/tests/cond.txi
new file mode 100644
index 00000000000..b60242322dc
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali b/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali
new file mode 100644
index 00000000000..22ecd896ad7
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali
@@ -0,0 +1,13 @@
+#!/bin/sh
+# Test no extra <li> from @menu.
+
+li_count=`../makeinfo --html -o - ${srcdir-.}/html-extrali.txi 2>/dev/null \
+| grep '<li>' \
+| wc -l`
+
+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/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali.txi b/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali.txi
new file mode 100644
index 00000000000..281aa7b3193
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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/gnu/usr.bin/texinfo/makeinfo/tests/html-manuals b/gnu/usr.bin/texinfo/makeinfo/tests/html-manuals
new file mode 100644
index 00000000000..5e9523c820e
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-manuals
@@ -0,0 +1,12 @@
+#!/bin/sh
+# 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 --no-split \
+ $srcdir/../../doc/$manual -o $base.html \
+ || exit 1
+ rm -f $base.html
+done
diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-min b/gnu/usr.bin/texinfo/makeinfo/tests/html-min
new file mode 100644
index 00000000000..e5971530afc
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-min
@@ -0,0 +1,8 @@
+#!/bin/sh
+# Test that a minimal Texinfo file can be converted to HTML.
+
+../makeinfo --html ${srcdir-.}/html-min.txi
+exit_status=$?
+
+rm -f html-min.html
+exit $exit_status
diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-min.txi b/gnu/usr.bin/texinfo/makeinfo/tests/html-min.txi
new file mode 100644
index 00000000000..116519e0611
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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/gnu/usr.bin/texinfo/makeinfo/tests/html-para b/gnu/usr.bin/texinfo/makeinfo/tests/html-para
new file mode 100644
index 00000000000..ba3191bf18f
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-para
@@ -0,0 +1,8 @@
+#!/bin/sh
+# Test that paragraph beginnings in HTML work ok.
+
+../makeinfo --html ${srcdir-.}/html-para.txi
+exit_status=$?
+
+rm -f html-para.html
+exit $exit_status
diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-para.txi b/gnu/usr.bin/texinfo/makeinfo/tests/html-para.txi
new file mode 100644
index 00000000000..c6e34fd2513
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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/gnu/usr.bin/texinfo/makeinfo/tests/html-title b/gnu/usr.bin/texinfo/makeinfo/tests/html-title
new file mode 100644
index 00000000000..425226155dd
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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 --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/gnu/usr.bin/texinfo/makeinfo/tests/html-title.txi b/gnu/usr.bin/texinfo/makeinfo/tests/html-title.txi
new file mode 100644
index 00000000000..2e2f25c579f
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace b/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace
new file mode 100644
index 00000000000..d512fc1bcb9
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace
@@ -0,0 +1,71 @@
+#!/bin/sh
+# Test that a minimal Texinfo file can be converted to HTML.
+
+: ${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/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace.txi b/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace.txi
new file mode 100644
index 00000000000..fd1c39f7ebf
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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/gnu/usr.bin/texinfo/makeinfo/tests/no-headers b/gnu/usr.bin/texinfo/makeinfo/tests/no-headers
new file mode 100644
index 00000000000..17570bb6f48
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/no-headers
@@ -0,0 +1,8 @@
+#!/bin/sh
+# Test that info.texi works with --no-headers (this includes node
+# pointer defaulting).
+
+: ${srcdir=.}
+docdir=$srcdir/../../doc
+
+../makeinfo --no-headers -o /dev/null -I$docdir info.texi
diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/node-expand b/gnu/usr.bin/texinfo/makeinfo/tests/node-expand
new file mode 100644
index 00000000000..12a67a43f82
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-expand
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Test command expansion in node names.
+
+: ${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/gnu/usr.bin/texinfo/makeinfo/tests/node-expand.txi b/gnu/usr.bin/texinfo/makeinfo/tests/node-expand.txi
new file mode 100644
index 00000000000..42cb614d308
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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
+
+@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/gnu/usr.bin/texinfo/makeinfo/tests/node-value b/gnu/usr.bin/texinfo/makeinfo/tests/node-value
new file mode 100644
index 00000000000..90c250bc17b
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-value
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Test @value expansion in node names.
+
+: ${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/gnu/usr.bin/texinfo/makeinfo/tests/node-value.txi b/gnu/usr.bin/texinfo/makeinfo/tests/node-value.txi
new file mode 100644
index 00000000000..ba1173f9a89
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace b/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace
new file mode 100644
index 00000000000..7cb568554dc
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Test whitespace collapse in node names.
+
+: ${srcdir=.}
+
+../makeinfo $srcdir/node-whitespace.txi
+test -s node-whitespace.info
+exit_status=$?
+
+rm -f node-whitespace.info
+exit $exit_status
diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace.txi b/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace.txi
new file mode 100644
index 00000000000..df1253fc507
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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
+
+@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/gnu/usr.bin/texinfo/makeinfo/tests/top b/gnu/usr.bin/texinfo/makeinfo/tests/top
new file mode 100644
index 00000000000..852194a71cd
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/top
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Test that a top node can be ignored.
+
+: ${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/gnu/usr.bin/texinfo/makeinfo/tests/top.txi b/gnu/usr.bin/texinfo/makeinfo/tests/top.txi
new file mode 100644
index 00000000000..e8c3cbbc078
--- /dev/null
+++ b/gnu/usr.bin/texinfo/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/gnu/usr.bin/texinfo/makeinfo/tests/top2 b/gnu/usr.bin/texinfo/makeinfo/tests/top2
new file mode 100644
index 00000000000..d38984654d5
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/top2
@@ -0,0 +1,11 @@
+#!/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 --force -o top2.html --html $srcdir/top2.txi 2>/dev/null
+test -s top2.html
+exit_status=$?
+rm -f top2.html
+exit $exit_status
diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/top2.txi b/gnu/usr.bin/texinfo/makeinfo/tests/top2.txi
new file mode 100644
index 00000000000..ade7214b0c2
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/tests/top2.txi
@@ -0,0 +1,7 @@
+\input texinfo
+@setfilename top.info
+
+@node start
+@top
+
+@bye
diff --git a/gnu/usr.bin/texinfo/makeinfo/toc.c b/gnu/usr.bin/texinfo/makeinfo/toc.c
new file mode 100644
index 00000000000..447ad5d0388
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/toc.c
@@ -0,0 +1,476 @@
+/* toc.c -- table of contents handling.
+ $Id: toc.c,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ 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 "lang.h"
+#include "makeinfo.h"
+#include "sectioning.h"
+#include "toc.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;
+
+/* the file where we found the @contents directive */
+char *contents_filename;
+
+/* the file where we found the @shortcontents directive */
+char *shortcontents_filename;
+
+static const char contents_placebo[] = "\n...Table of Contents...\n";
+static const char shortcontents_placebo[] = "\n...Short Contents...\n";
+static const char lots_of_stars[] =
+"***************************************************************************";
+
+
+/* Routine to add an entry to the table of contents */
+int
+toc_add_entry (tocname, level, node_name, anchor)
+ char *tocname;
+ int level;
+ char *node_name;
+ char *anchor;
+{
+ char *tocname_and_node, *expanded_node, *s, *d;
+
+ 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;
+ /* Sigh... Need to HTML-escape the expanded node name like
+ add_anchor_name does, except that we are not writing this to
+ the output, so can't use add_anchor_name... */
+ /* The factor 5 in the next allocation is because the maximum
+ expansion of HTML-escaping is for the & character, which is
+ output as "&amp;". 2 is for "> that separates node from tocname. */
+ d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node)
+ + strlen (tocname) + 1);
+ if (!anchor)
+ {
+ for (; *s; s++)
+ {
+ if (*s == '&')
+ {
+ strcpy (d, "&amp;");
+ d += 5;
+ }
+ else if (! URL_SAFE_CHAR (*s))
+ {
+ sprintf (d, "%%%x", (unsigned char) *s);
+ /* do this manually since sprintf returns char * on
+ SunOS 4 and other old systems. */
+ while (*d)
+ d++;
+ }
+ else
+ *d++ = *s;
+ }
+ strcpy (d, "\">");
+ }
+ else
+ /* Section outside any node, they provided explicit anchor. */
+ strcpy (d, anchor);
+ strcat (d, tocname);
+ free (tocname); /* it was malloc'ed by substring() */
+ free (expanded_node);
+ toc_entry_alist[toc_counter]->name = tocname_and_node;
+ }
+ 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;
+
+ /* 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 (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 ()
+{
+ 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, may be we can produce a standalone
+ HTML file? */
+static void
+contents_update_html (fp)
+ FILE *fp;
+{
+ int i;
+ int k;
+ int last_level;
+
+ /* does exist any toc? */
+ if (!toc_counter)
+ /* no, so return to sender ;-) */
+ return;
+
+ flush_output (); /* in case we are writing stdout */
+
+ fprintf (fp, "\n<h1>%s</h1>\n<ul>\n", _("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++)
+ fputs ("<ul>\n", fp);
+ }
+ 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++)
+ fputs ("</ul>\n", fp);
+ }
+
+ fprintf (fp, "<li><a href=\"#%s</a>\n", 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++)
+ fputs ("</ul>\n", fp);
+
+ fputs ("</ul>\n\n", fp);
+}
+
+/* print table of contents in ASCII (--no-headers)
+ May be we should create a new command line switch --ascii ? */
+static void
+contents_update_info (fp)
+ FILE *fp;
+{
+ int i;
+ int k;
+
+ if (!toc_counter)
+ return;
+
+ flush_output (); /* in case we are writing stdout */
+
+ fprintf (fp, "%s\n%.*s\n\n", _("Table of Contents"),
+ (int) strlen (_("Table of Contents")), lots_of_stars);
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if (toc_entry_alist[i]->level == 0)
+ fputs ("\n", fp);
+
+ /* indention with two spaces per level, should this
+ changed? */
+ for (k = 0; k < toc_entry_alist[i]->level; k++)
+ fputs (" ", fp);
+
+ fprintf (fp, "%s\n", toc_entry_alist[i]->name);
+ }
+ fputs ("\n\n", fp);
+}
+
+/* shortcontents in HTML; Should this produce a standalone file? */
+static void
+shortcontents_update_html (fp)
+ FILE *fp;
+{
+ int i;
+
+ /* does exist any toc? */
+ if (!toc_counter)
+ return;
+
+ flush_output (); /* in case we are writing stdout */
+
+ fprintf (fp, "\n<h1>%s</h1>\n<ul>\n", _("Short Contents"));
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if ((toc_entry_alist[i])->level == 0)
+ {
+ fputs ("<li>", fp);
+ fprintf (fp, "<a href=\"#%s\n", toc_entry_alist[i]->name);
+ }
+ }
+
+ fputs ("</ul>\n\n", fp);
+}
+
+/* short contents in ASCII (--no-headers).
+ May be we should create a new command line switch --ascii ? */
+static void
+shortcontents_update_info (fp)
+ FILE *fp;
+{
+ int i;
+
+ if (!toc_counter)
+ return;
+
+ flush_output (); /* in case we are writing stdout */
+
+ fprintf (fp, "%s\n%.*s\n\n", _("Short Contents"),
+ (int) strlen (_("Short Contents")), lots_of_stars);
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if ((toc_entry_alist[i])->level == 0)
+ fprintf (fp, "%s\n", toc_entry_alist[i]->name);
+ }
+ fputs ("\n\n", fp);
+}
+
+
+static FILE *toc_fp;
+static char *toc_buf;
+
+static int
+rewrite_top (fname, placebo)
+ const char *fname, *placebo;
+{
+ int idx;
+
+ toc_buf = find_and_load (fname);
+
+ if (!toc_buf)
+ {
+ /* Can't rewrite standard output. No point in complaining. */
+ if (!STREQ (fname, "-"))
+ fs_error (fname);
+ return -1;
+ }
+
+ idx = search_forward (placebo, 0);
+
+ if (idx < 0)
+ {
+ error (_("%s: TOC should be here, but it was not found"), fname);
+ return -1;
+ }
+
+ toc_fp = fopen (fname, "w");
+ if (!toc_fp)
+ {
+ fs_error (fname);
+ return -1;
+ }
+
+ if (fwrite (toc_buf, 1, idx, toc_fp) != idx)
+ {
+ fs_error (fname);
+ return -1;
+ }
+
+ return idx + strlen (placebo);
+}
+
+static void
+contents_update ()
+{
+ int cont_idx = rewrite_top (contents_filename, contents_placebo);
+
+ if (cont_idx < 0)
+ return;
+
+ if (html)
+ contents_update_html (toc_fp);
+ else
+ contents_update_info (toc_fp);
+
+ if (fwrite (toc_buf + cont_idx, 1, input_text_length - cont_idx, toc_fp)
+ != input_text_length - cont_idx
+ || fclose (toc_fp) != 0)
+ fs_error (contents_filename);
+}
+
+static void
+shortcontents_update ()
+{
+ int cont_idx = rewrite_top (shortcontents_filename, shortcontents_placebo);
+
+ if (cont_idx < 0)
+ return;
+
+ if (html)
+ shortcontents_update_html (toc_fp);
+ else
+ shortcontents_update_info (toc_fp);
+
+ if (fwrite (toc_buf + cont_idx, 1, input_text_length - cont_idx - 1, toc_fp)
+ != input_text_length - cont_idx - 1
+ || fclose (toc_fp) != 0)
+ fs_error (shortcontents_filename);
+}
+
+void
+toc_update ()
+{
+ if (!html && !no_headers)
+ return;
+
+ if (contents_filename)
+ contents_update ();
+ if (shortcontents_filename)
+ shortcontents_update ();
+}
+
+void
+cm_contents (arg)
+ int arg;
+{
+ if ((html || no_headers) && arg == START)
+ {
+ if (contents_filename)
+ {
+ free (contents_filename);
+ contents_filename = NULL;
+ }
+
+ if (contents_filename && STREQ (contents_filename, "-"))
+ {
+ if (html)
+ contents_update_html (stdout);
+ else
+ contents_update_info (stdout);
+ }
+ else
+ {
+ contents_filename = xstrdup (current_output_filename);
+ insert_string (contents_placebo); /* just mark it, for now */
+ }
+ }
+}
+
+void
+cm_shortcontents (arg)
+ int arg;
+{
+ if ((html || no_headers) && arg == START)
+ {
+ if (shortcontents_filename)
+ {
+ free (shortcontents_filename);
+ shortcontents_filename = NULL;
+ }
+
+ if (shortcontents_filename && STREQ (shortcontents_filename, "-"))
+ {
+ if (html)
+ shortcontents_update_html (stdout);
+ else
+ shortcontents_update_info (stdout);
+ }
+ else
+ {
+ shortcontents_filename = xstrdup (current_output_filename);
+ insert_string (shortcontents_placebo); /* just mark it, for now */
+ }
+ }
+}
diff --git a/gnu/usr.bin/texinfo/makeinfo/toc.h b/gnu/usr.bin/texinfo/makeinfo/toc.h
new file mode 100644
index 00000000000..ec99eea335c
--- /dev/null
+++ b/gnu/usr.bin/texinfo/makeinfo/toc.h
@@ -0,0 +1,49 @@
+/* toc.h -- table of contents handling.
+ $Id: toc.h,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#ifndef TOC_H
+#define TOC_H
+
+/* the file where we found the @contents directive */
+extern char *contents_filename;
+
+/* the file where we found the @shortcontents directive */
+extern char *shortcontents_filename;
+
+/* Structure to hold one entry for the toc. */
+typedef struct toc_entry_elt {
+ char *name;
+ char *containing_node; /* Name of node containing this section. */
+ 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 (); /* return the number for the toc-entry */
+extern void toc_free ();
+extern char *toc_find_section_of_node ();
+
+extern void cm_contents (), cm_shortcontents ();
+
+#endif /* not TOC_H */