diff options
author | Ruslan Ermilov <ru@FreeBSD.org> | 2000-01-17 10:39:58 +0000 |
---|---|---|
committer | Ruslan Ermilov <ru@FreeBSD.org> | 2000-01-17 10:39:58 +0000 |
commit | 1f9d6869becd194ae3b329d00b62411c76606015 (patch) | |
tree | 0489e7639d2e043fd10d286e926db228f104e98e /contrib/texinfo/makeinfo | |
parent | 1f8c32bdd26c3e7d6d4e94e7414890589bf385d8 (diff) | |
parent | cab79d664647c337ec2def083dd99de2ce69c174 (diff) |
This commit was generated by cvs2svn to compensate for changes in r56160,
which included commits to RCS files with non-trunk default branches.
Notes
Notes:
svn path=/head/; revision=56161
Diffstat (limited to 'contrib/texinfo/makeinfo')
27 files changed, 10328 insertions, 147 deletions
diff --git a/contrib/texinfo/makeinfo/README b/contrib/texinfo/makeinfo/README index 2bfe6e1cbebb..a6f97ebed4c5 100644 --- a/contrib/texinfo/makeinfo/README +++ b/contrib/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/contrib/texinfo/makeinfo/cmds.c b/contrib/texinfo/makeinfo/cmds.c new file mode 100644 index 000000000000..65d382e8d4ba --- /dev/null +++ b/contrib/texinfo/makeinfo/cmds.c @@ -0,0 +1,1121 @@ +/* cmds.c -- Texinfo commands. + $Id: cmds.c,v 1.57 1999/09/19 16:39:35 karl 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 ("•"); + 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 ("©"); + 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 ("<<code>"); + else + add_word ("</code>>"); + } + else + if (arg == START) + add_word ("<"); + else + add_word (">"); +} + +void +cm_key (arg) + int arg; +{ + if (html) + add_word (arg == START ? "<" : ">"); + else + add_char (arg == START ? '<' : '>'); +} + +/* Handle a command that switches to a non-fixed-width font. */ +void +not_fixed_width (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 ? "=>" : "=>"); +} + +/* What an expression expands to. */ +void +cm_expansion (arg) + int arg; +{ + if (arg == END) + add_word (html ? "==>" : "==>"); +} + +/* 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-->" : "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/contrib/texinfo/makeinfo/cmds.h b/contrib/texinfo/makeinfo/cmds.h new file mode 100644 index 000000000000..7edc8107df7b --- /dev/null +++ b/contrib/texinfo/makeinfo/cmds.h @@ -0,0 +1,50 @@ +/* cmds.h -- declarations for cmds.c. + $Id: cmds.h,v 1.4 1999/04/25 20:43:51 karl 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/contrib/texinfo/makeinfo/defun.c b/contrib/texinfo/makeinfo/defun.c new file mode 100644 index 000000000000..c62aba791d37 --- /dev/null +++ b/contrib/texinfo/makeinfo/defun.c @@ -0,0 +1,663 @@ +/* defun.c -- @defun and friends. + $Id: defun.c,v 1.11 1999/07/11 16:50:19 karl 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/contrib/texinfo/makeinfo/defun.h b/contrib/texinfo/makeinfo/defun.h new file mode 100644 index 000000000000..ebff9d67b6f0 --- /dev/null +++ b/contrib/texinfo/makeinfo/defun.h @@ -0,0 +1,31 @@ +/* defun.h -- declaration for defuns. + $Id: defun.h,v 1.2 1999/03/25 22:49:10 karl 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/contrib/texinfo/makeinfo/files.c b/contrib/texinfo/makeinfo/files.c new file mode 100644 index 000000000000..ce8ace014de3 --- /dev/null +++ b/contrib/texinfo/makeinfo/files.c @@ -0,0 +1,529 @@ +/* files.c -- file-related functions for Texinfo. + $Id: files.c,v 1.5 1999/03/23 21:42:44 karl 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/contrib/texinfo/makeinfo/files.h b/contrib/texinfo/makeinfo/files.h new file mode 100644 index 000000000000..d96c444f2a85 --- /dev/null +++ b/contrib/texinfo/makeinfo/files.h @@ -0,0 +1,45 @@ +/* files.h -- declarations for files.c. + $Id: files.h,v 1.1 1998/10/24 21:37:25 karl 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/contrib/texinfo/makeinfo/footnote.c b/contrib/texinfo/makeinfo/footnote.c new file mode 100644 index 000000000000..c1a056d6e32c --- /dev/null +++ b/contrib/texinfo/makeinfo/footnote.c @@ -0,0 +1,359 @@ +/* footnote.c -- footnotes for Texinfo. + $Id: footnote.c,v 1.10 1999/09/20 12:20:52 karl 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/contrib/texinfo/makeinfo/footnote.h b/contrib/texinfo/makeinfo/footnote.h new file mode 100644 index 000000000000..c87a0aa23ed1 --- /dev/null +++ b/contrib/texinfo/makeinfo/footnote.h @@ -0,0 +1,37 @@ +/* footnote.h -- declarations for footnote.c. + $Id: footnote.h,v 1.2 1998/10/26 22:16:15 karl 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/contrib/texinfo/makeinfo/html.c b/contrib/texinfo/makeinfo/html.c new file mode 100644 index 000000000000..f2e53e510db6 --- /dev/null +++ b/contrib/texinfo/makeinfo/html.c @@ -0,0 +1,182 @@ +/* html.c -- html-related utilities. + $Id: html.c,v 1.5 1999/09/18 19:27:41 karl 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; /* `&' */ + break; + case '<': + case '>': + newlen += 4; /* `<', `>' */ + 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, "&"); + newstring += 5; + break; + case '<': + strcpy (newstring, "<"); + newstring += 4; + break; + case '>': + strcpy (newstring, ">"); + 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 ("&"); + 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/contrib/texinfo/makeinfo/html.h b/contrib/texinfo/makeinfo/html.h new file mode 100644 index 000000000000..2d68c77308dd --- /dev/null +++ b/contrib/texinfo/makeinfo/html.h @@ -0,0 +1,44 @@ +/* html.h -- declarations for html-related utilities. + $Id: html.h,v 1.1 1999/04/25 20:53:33 karl 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/contrib/texinfo/makeinfo/index.c b/contrib/texinfo/makeinfo/index.c new file mode 100644 index 000000000000..05466ce9a4f0 --- /dev/null +++ b/contrib/texinfo/makeinfo/index.c @@ -0,0 +1,823 @@ +/* index.c -- indexing for Texinfo. + $Id: index.c,v 1.21 1999/07/18 18:50:02 karl 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/contrib/texinfo/makeinfo/index.h b/contrib/texinfo/makeinfo/index.h new file mode 100644 index 000000000000..ed8583a80499 --- /dev/null +++ b/contrib/texinfo/makeinfo/index.h @@ -0,0 +1,36 @@ +/* index.h -- declarations for index.c. + $Id: index.h,v 1.4 1999/04/19 18:12:17 karl 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/contrib/texinfo/makeinfo/insertion.c b/contrib/texinfo/makeinfo/insertion.c new file mode 100644 index 000000000000..11b908901db5 --- /dev/null +++ b/contrib/texinfo/makeinfo/insertion.c @@ -0,0 +1,1368 @@ +/* insertion.c -- insertions for Texinfo. + $Id: insertion.c,v 1.27 1999/07/06 23:12:53 karl 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/contrib/texinfo/makeinfo/insertion.h b/contrib/texinfo/makeinfo/insertion.h new file mode 100644 index 000000000000..6f4a24b79267 --- /dev/null +++ b/contrib/texinfo/makeinfo/insertion.h @@ -0,0 +1,61 @@ +/* insertion.h -- declarations for insertion.c. + $Id: insertion.h,v 1.6 1999/07/06 23:12:58 karl 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/contrib/texinfo/makeinfo/lang.c b/contrib/texinfo/makeinfo/lang.c new file mode 100644 index 000000000000..eeb9ef582a4f --- /dev/null +++ b/contrib/texinfo/makeinfo/lang.c @@ -0,0 +1,415 @@ +/* lang.c -- language depend behaviour (startpoint) + $Id: lang.c,v 1.11 1999/07/13 21:16:29 karl 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 ? "¯" : "="); + 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 ? "<" : "<"); + } +} + +/* Common routine for the accent characters that have support in HTML. + If the character being accented is in the HTML_SUPPORTED set, then + produce &CHTML_SOLO;, for example, Ä for an A-umlaut. If not in + HTML_SUPPORTED, just produce &HTML_SOLO;X for the best we can do with + at an X-umlaut. 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 ˆ ` ˜), 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 ? "¡" : "!"); + else if (strcmp (command, "pounds") == 0) + add_word (html ? "£" : "#"); + else if (strcmp (command, "questiondown") == 0) + add_word (html ? "¿" : "?"); + else if (strcmp (command, "AE") == 0) + add_word (html ? "Æ" : command); + else if (strcmp (command, "ae") == 0) + add_word (html ? "æ" : command); + else if (strcmp (command, "OE") == 0) + add_word (html ? "Œ" : command); + else if (strcmp (command, "oe") == 0) + add_word (html ? "œ" : command); + else if (strcmp (command, "AA") == 0) + add_word (html ? "Å" : command); + else if (strcmp (command, "aa") == 0) + add_word (html ? "å" : command); + else if (strcmp (command, "ss") == 0) + add_word (html ? "ß" : 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/contrib/texinfo/makeinfo/lang.h b/contrib/texinfo/makeinfo/lang.h new file mode 100644 index 000000000000..25bf0bd9c4bd --- /dev/null +++ b/contrib/texinfo/makeinfo/lang.h @@ -0,0 +1,86 @@ +/* lang.h -- declarations for language codes etc. + $Id: lang.h,v 1.6 1999/03/22 20:07:34 karl 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/contrib/texinfo/makeinfo/macro.c b/contrib/texinfo/makeinfo/macro.c new file mode 100644 index 000000000000..8c89da257106 --- /dev/null +++ b/contrib/texinfo/makeinfo/macro.c @@ -0,0 +1,1114 @@ +/* macro.c -- user-defined macros for Texinfo. + $Id: macro.c,v 1.10 1999/08/17 21:06:35 karl 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/contrib/texinfo/makeinfo/macro.h b/contrib/texinfo/makeinfo/macro.h new file mode 100644 index 000000000000..a64c61333e16 --- /dev/null +++ b/contrib/texinfo/makeinfo/macro.h @@ -0,0 +1,71 @@ +/* macro.h -- declarations for macro.c. + $Id: macro.h,v 1.5 1999/07/15 00:00:46 karl 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/contrib/texinfo/makeinfo/makeinfo.h b/contrib/texinfo/makeinfo/makeinfo.h index 205a3bdf0415..caff188ebe42 100644 --- a/contrib/texinfo/makeinfo/makeinfo.h +++ b/contrib/texinfo/makeinfo/makeinfo.h @@ -1,7 +1,7 @@ -/* makeinfo.h -- Declarations for Makeinfo. - $Id: makeinfo.h,v 1.3 1997/07/15 18:28:38 karl Exp $ +/* makeinfo.h -- declarations for Makeinfo. + $Id: makeinfo.h,v 1.25 1999/09/18 18:09:22 karl Exp $ - Copyright (C) 1996, 97 Free Software Foundation, Inc. + 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 @@ -19,68 +19,44 @@ Written by Brian Fox (bfox@ai.mit.edu). */ -/* Why, oh why, did I ever listen to rms when he said: - "Don't make lots of small files, just make one big one!" I've - regretted it ever since with this program, and with readline. - bfox@ai.mit.edu Thu Jul 11 07:54:32 1996 */ - -#if !defined (MAKEINFO_H) +#ifndef MAKEINFO_H #define MAKEINFO_H -#if defined (COMPILING_MAKEINFO) -# define DECLARE(type, var, init) type var = init +#ifdef COMPILING_MAKEINFO +# define DECLARE(type,var,init) type var = init #else -# define DECLARE(type, var, init) extern type var +# define DECLARE(type,var,init) extern type var #endif -enum insertion_type -{ - cartouche, defcv, deffn, defivar, defmac, defmethod, - defop, defopt, defspec, deftp, deftypefn, deftypefun, - deftypemethod, deftypevar, deftypevr, defun, defvar, - defvr, detailmenu, direntry, display, enumerate, example, - flushleft, flushright, format, ftable, group, ifclear, - ifinfo, ifnothtml, ifnottex, ifset, itemize, lisp, menu, - multitable, quotation, smallexample, smalllisp, table, vtable, - bad_type -}; +/* Hardcoded per GNU standards, not dependent on argv[0]. */ +DECLARE (char *, progname, "makeinfo"); -DECLARE (int, insertion_level, 0); - -#if defined (COMPILING_MAKEINFO) -char *insertion_type_names[] = +enum reftype { - "cartouche", "defcv", "deffn", "defivar", "defmac", "defmethod", - "defop", "defopt", "defspec", "deftp", "deftypefn", "deftypefun", - "deftypemethod", "deftypevar", "deftypevr", "defun", "defvar", - "defvr", "detailmenu", "direntry", "display", "enumerate", "example", - "flushleft", "flushright", "format", "ftable", "group", "ifclear", - "ifinfo", "ifnothtml", "ifnottex", "ifset", "itemize", "lisp", "menu", - "multitable", "quotation", "smallexample", "smalllisp", "table", "vtable", - "bad_type" + menu_reference, followed_reference }; -#endif -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; - -DECLARE (INSERTION_ELT *, insertion_stack, (INSERTION_ELT *)NULL); +extern char *get_xref_token (); + +/* Nonzero means a string is in execution, as opposed to a file. */ +DECLARE (int, executing_string, 0); + +/* Nonzero means to inhibit writing macro expansions to the output + stream, because it has already been written. */ +DECLARE (int, me_inhibit_expansion, 0); +extern char *expansion (), *text_expansion (); + /* Current output stream. */ -DECLARE (FILE *, output_stream, (FILE *)NULL); +DECLARE (FILE *, output_stream, NULL); + +DECLARE (char *, pretty_output_filename, NULL); + +/* Current output file name. */ +DECLARE (char *, current_output_filename, NULL); /* Output paragraph buffer. */ -DECLARE (unsigned char *, output_paragraph, (unsigned char *)NULL); +DECLARE (unsigned char *, output_paragraph, NULL); /* Offset into OUTPUT_PARAGRAPH. */ DECLARE (int, output_paragraph_offset, 0); @@ -88,46 +64,132 @@ DECLARE (int, output_paragraph_offset, 0); /* The output paragraph "cursor" horizontal position. */ DECLARE (int, output_column, 0); -/* Non-zero means output_paragraph contains text. */ +/* Position in the output file. */ +DECLARE (int, output_position, 0); + +/* The offset into OUTPUT_PARAGRAPH where we have a meta character + produced by a markup such as @code or @dfn. */ +DECLARE (int, meta_char_pos, -1); + +/* Nonzero means output_paragraph contains text. */ DECLARE (int, paragraph_is_open, 0); +/* Nonzero means that `start_paragraph' MUST be called before we pay + any attention to `close_paragraph' calls. */ +DECLARE (int, must_start_paragraph, 0); + +/* Nonzero means that we have seen "@top" once already. */ +DECLARE (int, top_node_seen, 0); + +/* Nonzero means that we have seen a non-"@top" node already. */ +DECLARE (int, non_top_node_seen, 0); + +/* Nonzero indicates that indentation is temporarily turned off. */ +DECLARE (int, no_indent, 1); + +/* Nonzero indicates that filling a line also indents the new line. */ +DECLARE (int, indented_fill, 0); + +/* Nonzero means forcing output text to be flushright. */ +DECLARE (int, force_flush_right, 0); + /* The amount of indentation to apply at the start of each line. */ DECLARE (int, current_indent, 0); -/* nonzero if we are currently processing a multitable command */ -DECLARE (int, multitable_active, 0); - /* The column at which long lines are broken. */ DECLARE (int, fill_column, 72); +/* Nonzero means that words are not to be split, even in long lines. This + gets changed for cm_w (). */ +DECLARE (int, non_splitting_words, 0); + +/* Amount by which @example indentation increases/decreases. */ +DECLARE (int, default_indentation_increment, 5); + +/* Nonzero means that we are currently hacking the insides of an + insertion which would use a fixed width font. */ +DECLARE (int, in_fixed_width_font, 0); + +/* Nonzero if we are currently processing a multitable command */ +DECLARE (int, multitable_active, 0); + +/* Nonzero means that we're generating HTML. */ +DECLARE (int, html, 0); + +/* Nonzero means escape characters in HTML output. */ +DECLARE (int, escape_html, 1); +extern char *escape_string (); /* do HTML escapes */ + +/* Nonzero means that the use of paragraph_start_indent is inhibited. + @example uses this to line up the left columns of the example text. + A negative value for this variable is incremented each time it is used. + @noindent uses this to inhibit indentation for a single paragraph. */ +DECLARE (int, inhibit_paragraph_indentation, 0); + +/* Nonzero indicates that filling will take place on long lines. */ +DECLARE (int, filling_enabled, 1); + +/* The current node's node name. */ +DECLARE (char *, current_node, NULL); + +/* Command name in the process of being hacked. */ +DECLARE (char *, command, NULL); + +/* Nonzero if the last character inserted has the syntax class of NEWLINE. */ +DECLARE (int, last_char_was_newline, 1); + /* The current input file state. */ DECLARE (char *, input_filename, (char *)NULL); DECLARE (char *, input_text, (char *)NULL); -DECLARE (int, size_of_input_text, 0); +DECLARE (int, input_text_length, 0); DECLARE (int, input_text_offset, 0); DECLARE (int, line_number, 0); - #define curchar() input_text[input_text_offset] -/* **************************************************************** */ -/* */ -/* Global Defines */ -/* */ -/* **************************************************************** */ - -/* Error levels */ -#define NO_ERROR 0 -#define SYNTAX 2 -#define FATAL 4 - + +/* A colon separated list of directories to search for files included + with @include. This can be controlled with the `-I' option to makeinfo. */ +DECLARE (char *, include_files_path, NULL); + +/* The filename of the current input file. This is never freed. */ +DECLARE (char *, node_filename, NULL); + +/* Nonzero means do not output "Node: Foo" for node separations, that + is, generate plain text. (--no-headers) */ +DECLARE (int, no_headers, 0); + +/* Nonzero means that we process @html and @rawhtml even when not + generating HTML. (--ifhtml) */ +DECLARE (int, process_html, 0); + +/* Nonzero means that we process @ifinfo even when generating HTML. + (--ifinfo) */ +DECLARE (int, process_info, 1); + +/* Nonzero means that we process @tex and @iftex. (--iftex) */ +DECLARE (int, process_tex, 0); + +/* Maximum number of references to a single node before complaining. + (--reference-limit) */ +DECLARE (int, reference_warning_limit, 1000); + +/* Default is to check node references. (--no-validate) */ +DECLARE (int, validating, 1); + +/* Nonzero means print information about what is going on. (--verbose) */ +DECLARE (int, verbose_mode, 0); + +/* Nonzero means prefix each @chapter, ... with a number like 1. (--number-sections) */ +DECLARE (int, number_sections, 0); + +/* Nonzero means expand node names and references while validating. + This will avoid errors when the Texinfo document uses features + like @@ and @value inconsistently in node names, but will slow + the program by about 80%. You HAVE been warned. */ +DECLARE (int, expensive_validation, 0); + /* C's standard macros don't check to make sure that the characters being changed are within range. So I have to check explicitly. */ -/* GNU Library doesn't have toupper(). Until GNU gets this fixed, I will - have to do it. */ -#ifndef toupper -#define toupper(c) ((c) - 32) -#endif - #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c))) #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c))) @@ -138,9 +200,9 @@ DECLARE (int, line_number, 0); #define META(c) ((c) | (meta_character_bit)) #define UNMETA(c) ((c) & (~meta_character_bit)) -#define whitespace(c) (((c) == '\t') || ((c) == ' ')) -#define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!') -#define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n')) +#define whitespace(c) ((c) == '\t' || (c) == ' ') +#define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!') +#define cr_or_whitespace(c) (whitespace(c) || (c) == '\r' || (c) == '\n') #ifndef isletter #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) @@ -158,33 +220,30 @@ DECLARE (int, line_number, 0); #define digit_value(c) ((c) - '0') #endif -#define member(c, s) (strchr (s, c) != NULL) +#define HTML_SAFE "$-_.+!*'()" +#define URL_SAFE_CHAR(ch) (isalnum (ch) || strchr (HTML_SAFE, ch)) #define COMMAND_PREFIX '@' /* Stuff for splitting large files. */ #define SPLIT_SIZE_THRESHOLD 70000 /* What's good enough for Stallman... */ #define DEFAULT_SPLIT_SIZE 50000 /* Is probably good enough for me. */ - DECLARE (int, splitting, 1); /* Defaults to true for now. */ -typedef void COMMAND_FUNCTION (); /* So I can say COMMAND_FUNCTION *foo; */ - -#define command_char(c) ((!whitespace(c)) && \ - ((c) != '\n') && \ - ((c) != '{') && \ - ((c) != '}') && \ - ((c) != '=')) +#define command_char(c) (!cr_or_whitespace(c) \ + && (c) != '{' \ + && (c) != '}' \ + && (c) != '=') #define skip_whitespace() \ - while ((input_text_offset != size_of_input_text) && \ + while ((input_text_offset != input_text_length) && \ whitespace (curchar())) \ input_text_offset++ #define skip_whitespace_and_newlines() \ do { \ - while ((input_text_offset != size_of_input_text) && \ - (whitespace (curchar ()) || (curchar () == '\n'))) \ + while (input_text_offset != input_text_length \ + && cr_or_whitespace (curchar ())) \ { \ if (curchar () == '\n') \ line_number++; \ @@ -192,4 +251,10 @@ typedef void COMMAND_FUNCTION (); /* So I can say COMMAND_FUNCTION *foo; */ } \ } while (0) -#endif /* !MAKEINFO_H */ +/* Return nonzero if STRING is the text at input_text + input_text_offset, + else zero. */ +#define looking_at(string) \ + (strncmp (input_text + input_text_offset, string, strlen (string)) == 0) + + +#endif /* not MAKEINFO_H */ diff --git a/contrib/texinfo/makeinfo/multi.c b/contrib/texinfo/makeinfo/multi.c index 99c380d3dd95..b41bb4709977 100644 --- a/contrib/texinfo/makeinfo/multi.c +++ b/contrib/texinfo/makeinfo/multi.c @@ -1,7 +1,7 @@ /* multi.c -- multitable stuff for makeinfo. - $Id: multi.c,v 1.9 1997/07/24 22:01:00 karl Exp $ + $Id: multi.c,v 1.18 1999/08/17 21:06:56 karl Exp $ - Copyright (C) 1996, 97 Free Software Foundation, Inc. + 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 @@ -18,6 +18,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "system.h" +#include "insertion.h" #include "makeinfo.h" #define MAXCOLS 100 /* remove this limit later @@ */ @@ -51,6 +52,7 @@ struct env { unsigned char *output_paragraph; int output_paragraph_offset; + int meta_char_pos; int output_column; int paragraph_is_open; int current_indent; @@ -66,20 +68,33 @@ static int last_column; /* flags indicating whether horizontal and vertical separators need to be drawn, separating rows and columns in the current multitable. */ static int hsep, vsep; + +/* whether this is the first row. */ +static int first_row; + +static void output_multitable_row (); -/* Output a row. Have to keep `output_position' up-to-date for each - character we output, or the tags table will be off, leading to - chopped-off output files and undefined nodes (because they're in the - wrong file, etc.). Perhaps it would be better to accumulate this - value somewhere and add it once at the end of the table, or return it - as the value, but this seems simplest. */ +/* Output a row. Calls insert, but also flushes the buffered output + when we see a newline, since in multitable every line is a separate + paragraph. */ static void out_char (ch) int ch; { - extern int output_position; - putc (ch, output_stream); - output_position++; + if (html) + add_char (ch); + else + { + int env = select_output_environment (0); + insert (ch); + if (ch == '\n') + { + uninhibit_output_flushing (); + flush_output (); + inhibit_output_flushing (); + } + select_output_environment (env); + } } @@ -88,6 +103,12 @@ draw_horizontal_separator () { int i, j, s; + if (html) + { + add_word ("<hr>"); + return; + } + for (s = 0; s < envs[0].current_indent; s++) out_char (' '); if (vsep) @@ -101,34 +122,48 @@ draw_horizontal_separator () out_char ('\n'); } + +/* multitable strategy: + for each item { + for each column in an item { + initialize a new paragraph + do ordinary formatting into the new paragraph + save the paragraph away + repeat if there are more paragraphs in the column + } + dump out the saved paragraphs and free the storage + } + + For HTML we construct a simple HTML 3.2 table with <br>s inserted + to help non-tables browsers. `@item' inserts a <tr> and `@tab' + inserts <td>; we also try to close <tr>. The only real + alternative is to rely on the info formatting engine and present + preformatted text. */ + void do_multitable () { int ncolumns; - /* - * multitable strategy: - * for each item { - * for each column in an item { - * initialize a new paragraph - * do ordinary formatting into the new paragraph - * save the paragraph away - * repeat if there are more paragraphs in the column - * } - * dump out the saved paragraphs and free the storage - * } - */ - if (multitable_active) { line_error ("Multitables cannot be nested"); return; } + close_single_paragraph (); + /* scan the current item function to get the field widths and number of columns, and set up the output environment list accordingly. */ ncolumns = setup_multitable_parameters (); + first_row = 1; + + /* <p> for non-tables browsers. @multitable implicitly ends the + current paragraph, so this is ok. */ + if (html) + add_word ("<p><table>"); + if (hsep) draw_horizontal_separator (); @@ -141,6 +176,53 @@ do_multitable () ++multitable_active; } +/* Called to handle a {...} template on the @multitable line. + We're at the { and our first job is to find the matching }; as a side + effect, we change *PARAMS to point to after it. Our other job is to + expand the template text and return the width of that string. */ +static unsigned +find_template_width (params) + char **params; +{ + char *template, *xtemplate; + unsigned len; + char *start = *params; + int brace_level = 0; + + /* The first character should be a {. */ + if (!params || !*params || **params != '{') + { + line_error ("find_template width internal error: passed %s", + params ? *params : "null"); + return 0; + } + + do + { + if (**params == '{' && (*params)[-1] != '@') + brace_level++; + else if (**params == '}' && (*params)[-1] != '@') + brace_level--; + else if (**params == 0) + { + line_error (_("Missing } in @multitable template")); + return 0; + } + (*params)++; + } + while (brace_level > 0); + + template = substring (start + 1, *params - 1); /* omit braces */ + xtemplate = expansion (template, 0); + len = strlen (xtemplate); + + free (template); + free (xtemplate); + + return len; +} + + /* Read the parameters for a multitable from the current command line, save the parameters away, and return the number of columns. */ @@ -150,7 +232,7 @@ setup_multitable_parameters () char *params = insertion_stack->item_function; int nchars; float columnfrac; - char command[200]; /* naughty, should be no fixed limits */ + char command[200]; /* xx no fixed limits */ int i = 1; /* We implement @hsep and @vsep even though TeX doesn't. @@ -176,12 +258,12 @@ setup_multitable_parameters () for ( ; i <= MAXCOLS; i++) { if (sscanf (params, "%f", &columnfrac) < 1) goto done; - /* Unfortunately, can't use %n since some m68k-hp-bsd libc + /* Unfortunately, can't use %n since m68k-hp-bsd libc (at least) doesn't support it. So skip whitespace (preceding the number) and then non-whitespace (the number). */ while (*params && (*params == ' ' || *params == '\t')) params++; - /* Hmm, but what what @columnfractions 3foo. Well, I suppose + /* Hmm, but what about @columnfractions 3foo. Well, I suppose it's invalid input anyway. */ while (*params && *params != ' ' && *params != '\t' && *params != '\n' && *params != '@') @@ -192,15 +274,11 @@ setup_multitable_parameters () } } else if (*params == '{') { - char *start = params; - while ((*params != '}' || params[-1] == '@') && *params) { - params++; - } + unsigned template_width = find_template_width (¶ms); + /* This gives us two spaces between columns. Seems reasonable. - Really should expand the text, though, so a template of - `@code{foo}' has a width of five, not ten. Also have to match - braces, then. How to take into account current_indent here? */ - setup_output_environment (i++, params++ - start); + How to take into account current_indent here? */ + setup_output_environment (i++, template_width + 2); } else { warning (_("ignoring stray text `%s' after @multitable"), params); @@ -252,6 +330,7 @@ select_output_environment (n) /* stash current env info from global vars into the old environment */ e->output_paragraph = output_paragraph; e->output_paragraph_offset = output_paragraph_offset; + e->meta_char_pos = meta_char_pos; e->output_column = output_column; e->paragraph_is_open = paragraph_is_open; e->current_indent = current_indent; @@ -262,6 +341,7 @@ select_output_environment (n) e = &envs[current_env_no]; output_paragraph = e->output_paragraph; output_paragraph_offset = e->output_paragraph_offset; + meta_char_pos = e->meta_char_pos; output_column = e->output_column; paragraph_is_open = e->paragraph_is_open; current_indent = e->current_indent; @@ -281,8 +361,6 @@ nselect_next_environment () } -static void output_multitable_row (); - /* do anything needed at the beginning of processing a multitable column. */ void @@ -300,10 +378,20 @@ int multitable_item () { if (!multitable_active) { - /* impossible, I think. */ - error (_("multitable item not in active multitable")); - exit (1); + line_error ("multitable_item internal error: no active multitable"); + xexit (1); } + + if (html) + { + if (!first_row) + add_word ("<br></tr>"); /* <br> for non-tables browsers. */ + add_word ("<tr align=\"left\"><td>"); + first_row = 0; + return; + } + first_row = 0; + if (current_env_no > 0) { output_multitable_row (); } @@ -311,7 +399,7 @@ multitable_item () select_output_environment (1); if (!output_paragraph) { line_error (_("Cannot select column #%d in multitable"), current_env_no); - exit (FATAL); + exit (1); } init_column (); @@ -322,11 +410,11 @@ multitable_item () static void output_multitable_row () { - int i, j, s, remaining; - /* offset in the output paragraph of the next char needing to be output for that column. */ int offset[MAXCOLS]; + int i, j, s, remaining; + int had_newline = 0; for (i = 0; i <= last_column; i++) offset[i] = 0; @@ -340,9 +428,12 @@ output_multitable_row () /* remove trailing whitespace from each column */ for (i = 1; i <= last_column; i++) { - while (cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1))) { - envs[i].output_paragraph_offset--; - } + if (envs[i].output_paragraph_offset) + while (cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1))) + envs[i].output_paragraph_offset--; + + if (i == current_env_no) + output_paragraph_offset = envs[i].output_paragraph_offset; } /* read the current line from each column, outputting them all @@ -367,7 +458,7 @@ output_multitable_row () out_char ('|'); for (i = 1; i <= last_column; i++) { - for (s = 0; i < envs[i].current_indent; s++) + for (s = 0; s < envs[i].current_indent; s++) out_char (' '); for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) { if (CHAR_AT (j) == '\n') @@ -375,13 +466,22 @@ output_multitable_row () out_char (CHAR_AT (j)); } offset[i] += j + 1; /* skip last text plus skip the newline */ - for (; j <= envs[i].fill_column; j++) - out_char (' '); + + /* Do not output trailing blanks if we're in the last column and + there will be no trailing |. */ + if (i < last_column && !vsep) + for (; j <= envs[i].fill_column; j++) + out_char (' '); if (vsep) out_char ('|'); /* draw column separator */ } out_char ('\n'); /* end of line */ + had_newline = 1; } + + /* If completely blank item, get blank line despite no other output. */ + if (!had_newline) + out_char ('\n'); /* end of line */ if (hsep) draw_horizontal_separator (); @@ -403,7 +503,11 @@ cm_tab () if (!multitable_active) error (_("ignoring @tab outside of multitable")); - nselect_next_environment (); + if (html) + add_word ("<td>"); + else + nselect_next_environment (); + init_column (); } @@ -412,17 +516,19 @@ cm_tab () void end_multitable () { - output_multitable_row (); + if (!html) + output_multitable_row (); /* Multitables cannot be nested. Otherwise, we'd have to save the previous output environment number on a stack somewhere, and then restore to that environment. */ select_output_environment (0); - close_paragraph (); - insert ('\n'); /* we swallow newlines, so insert one of our own */ - multitable_active = 0; uninhibit_output_flushing (); + close_insertion_paragraph (); + + if (html) + add_word ("<br></tr></table>\n"); #if 0 printf (_("** Multicolumn output from last row:\n")); diff --git a/contrib/texinfo/makeinfo/node.c b/contrib/texinfo/makeinfo/node.c new file mode 100644 index 000000000000..8dbbd422846b --- /dev/null +++ b/contrib/texinfo/makeinfo/node.c @@ -0,0 +1,1568 @@ +/* node.c -- nodes for Texinfo. + $Id: node.c,v 1.23 1999/09/20 12:31:21 karl 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/contrib/texinfo/makeinfo/node.h b/contrib/texinfo/makeinfo/node.h new file mode 100644 index 000000000000..e2fc883d73aa --- /dev/null +++ b/contrib/texinfo/makeinfo/node.h @@ -0,0 +1,111 @@ +/* node.h -- declarations for Node. + $Id: node.h,v 1.5 1999/07/11 16:50:19 karl 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/contrib/texinfo/makeinfo/sectioning.c b/contrib/texinfo/makeinfo/sectioning.c new file mode 100644 index 000000000000..b06785b94b62 --- /dev/null +++ b/contrib/texinfo/makeinfo/sectioning.c @@ -0,0 +1,691 @@ +/* sectioning.c -- all related stuff @chapter, @section... @contents + $Id: sectioning.c,v 1.12 1999/08/17 21:06:50 karl 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/contrib/texinfo/makeinfo/sectioning.h b/contrib/texinfo/makeinfo/sectioning.h new file mode 100644 index 000000000000..75243f0125ec --- /dev/null +++ b/contrib/texinfo/makeinfo/sectioning.h @@ -0,0 +1,80 @@ +/* sectioning.h -- all related stuff @chapter, @section... @contents + $Id: sectioning.h,v 1.2 1999/03/09 22:48:15 karl 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/contrib/texinfo/makeinfo/toc.c b/contrib/texinfo/makeinfo/toc.c new file mode 100644 index 000000000000..41c5ffb8edcf --- /dev/null +++ b/contrib/texinfo/makeinfo/toc.c @@ -0,0 +1,476 @@ +/* toc.c -- table of contents handling. + $Id: toc.c,v 1.14 1999/08/09 20:28:18 karl 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 "&". 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, "&"); + 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/contrib/texinfo/makeinfo/toc.h b/contrib/texinfo/makeinfo/toc.h new file mode 100644 index 000000000000..a37faf78730d --- /dev/null +++ b/contrib/texinfo/makeinfo/toc.h @@ -0,0 +1,49 @@ +/* toc.h -- table of contents handling. + $Id: toc.h,v 1.4 1999/04/25 19:49:22 karl 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 */ |