diff options
Diffstat (limited to 'gnu/usr.bin/texinfo/makeinfo')
51 files changed, 10730 insertions, 2 deletions
diff --git a/gnu/usr.bin/texinfo/makeinfo/README b/gnu/usr.bin/texinfo/makeinfo/README index 2bfe6e1cbeb..a6f97ebed4c 100644 --- a/gnu/usr.bin/texinfo/makeinfo/README +++ b/gnu/usr.bin/texinfo/makeinfo/README @@ -1,8 +1,8 @@ makeinfo is a standalone program to convert Texinfo source into Info files readable with standalone info or M-x info in Emacs. -makeinfo can also output plain ASCII. Work to support HTML and Troff -output is almost complete. +makeinfo can also output plain ASCII (with --no-headers) +or HTML (with --html). The Emacs function M-x texinfo-format-buffer does more or less the same job, but makeinfo is faster and gives better error messages. diff --git a/gnu/usr.bin/texinfo/makeinfo/cmds.c b/gnu/usr.bin/texinfo/makeinfo/cmds.c new file mode 100644 index 00000000000..740007bc8cf --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/cmds.c @@ -0,0 +1,1121 @@ +/* cmds.c -- Texinfo commands. + $Id: cmds.c,v 1.1 2000/02/09 01:25:09 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "cmds.h" +#include "defun.h" +#include "files.h" +#include "footnote.h" +#include "insertion.h" +#include "lang.h" +#include "macro.h" +#include "makeinfo.h" +#include "node.h" +#include "sectioning.h" +#include "toc.h" + +#ifdef TM_IN_SYS_TIME +#include <sys/time.h> +#else +#include <time.h> +#endif + + +void insert_self (), insert_space (), cm_ignore_line (), cm_ignore_arg (); + +void + cm_TeX (), cm_acronym (), cm_asterisk (), cm_b (), cm_bullet (), cm_cite (), + cm_code (), cm_copyright (), cm_ctrl (), cm_dfn (), cm_dircategory (), + cm_direntry (), cm_dmn (), cm_dots (), cm_emph (), cm_enddots (), cm_i (), + cm_image (), cm_kbd (), cm_key (), cm_no_op (), + cm_novalidate (), cm_not_fixed_width (), cm_r (), + cm_strong (), cm_var (), cm_sc (), cm_w (), cm_email (), cm_url (); + +void + cm_anchor (), cm_node (), cm_menu (), cm_xref (), cm_ftable (), + cm_vtable (), cm_pxref (), cm_inforef (), cm_uref (), cm_email (), + cm_quotation (), cm_display (), cm_smalldisplay (), cm_itemize (), + cm_enumerate (), cm_tab (), cm_table (), cm_itemx (), cm_noindent (), + cm_setfilename (), cm_br (), cm_sp (), cm_page (), cm_group (), + cm_center (), cm_ref (), cm_include (), cm_bye (), cm_item (), cm_end (), + cm_kindex (), cm_cindex (), cm_findex (), cm_pindex (), cm_vindex (), + cm_tindex (), cm_synindex (), cm_printindex (), cm_minus (), + cm_example (), cm_smallexample (), cm_smalllisp (), cm_lisp (), + cm_format (), cm_smallformat (), cm_exdent (), cm_defindex (), + cm_defcodeindex (), cm_result (), cm_expansion (), cm_equiv (), + cm_print (), cm_error (), cm_point (), cm_today (), cm_flushleft (), + cm_flushright (), cm_finalout (), cm_cartouche (), cm_detailmenu (), + cm_multitable (), cm_settitle (), cm_titlefont (), cm_tt (); + +/* Conditionals. */ +void cm_set (), cm_clear (), cm_ifset (), cm_ifclear (); +void cm_value (), cm_ifeq (); + +/* Options. */ +static void cm_paragraphindent (), cm_exampleindent (); + +/* Internals. */ +static void cm_obsolete (); + +/* A random string. */ +static const char small_tag[] = "small"; + +/* The dispatch table. */ +COMMAND command_table[] = { + { "\t", insert_space, NO_BRACE_ARGS }, + { "\n", insert_space, NO_BRACE_ARGS }, + { " ", insert_self, NO_BRACE_ARGS }, + { "!", insert_self, NO_BRACE_ARGS }, + { "\"", cm_accent_umlaut, MAYBE_BRACE_ARGS }, + { "'", cm_accent_acute, MAYBE_BRACE_ARGS }, + { "*", cm_asterisk, NO_BRACE_ARGS }, + { ",", cm_accent_cedilla, MAYBE_BRACE_ARGS }, + { "-", cm_no_op, NO_BRACE_ARGS }, + { ".", insert_self, NO_BRACE_ARGS }, + { ":", cm_no_op, NO_BRACE_ARGS }, + { "=", cm_accent, MAYBE_BRACE_ARGS }, + { "?", insert_self, NO_BRACE_ARGS }, + { "@", insert_self, NO_BRACE_ARGS }, + { "^", cm_accent_hat, MAYBE_BRACE_ARGS }, + { "`", cm_accent_grave, MAYBE_BRACE_ARGS }, + { "{", insert_self, NO_BRACE_ARGS }, + { "|", cm_no_op, NO_BRACE_ARGS }, + { "}", insert_self, NO_BRACE_ARGS }, + { "~", cm_accent_tilde, MAYBE_BRACE_ARGS }, + { "AA", cm_special_char, BRACE_ARGS }, + { "AE", cm_special_char, BRACE_ARGS }, + { "H", cm_accent, MAYBE_BRACE_ARGS }, + { "L", cm_special_char, BRACE_ARGS }, + { "O", cm_special_char, BRACE_ARGS }, + { "OE", cm_special_char, BRACE_ARGS }, + { "TeX", cm_TeX, BRACE_ARGS }, + { "aa", cm_special_char, BRACE_ARGS }, + { "acronym", cm_acronym, BRACE_ARGS }, + { "ae", cm_special_char, BRACE_ARGS }, + { "afourpaper", cm_ignore_line, NO_BRACE_ARGS }, + { "alias", cm_alias, NO_BRACE_ARGS }, + { "anchor", cm_anchor, BRACE_ARGS }, + { "appendix", cm_appendix, NO_BRACE_ARGS }, + { "appendixsection", cm_appendixsec, NO_BRACE_ARGS }, + { "appendixsec", cm_appendixsec, NO_BRACE_ARGS }, + { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS }, + { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS }, + { "asis", cm_no_op, BRACE_ARGS }, + { "b", cm_b, BRACE_ARGS }, + { "bullet", cm_bullet, BRACE_ARGS }, + { "bye", cm_bye, NO_BRACE_ARGS }, + { "c", cm_ignore_line, NO_BRACE_ARGS }, + { "cartouche", cm_cartouche, NO_BRACE_ARGS }, + { "center", cm_center, NO_BRACE_ARGS }, + { "centerchap", cm_unnumbered, NO_BRACE_ARGS }, + { "chapheading", cm_chapheading, NO_BRACE_ARGS }, + { "chapter", cm_chapter, NO_BRACE_ARGS }, + { "cindex", cm_cindex, NO_BRACE_ARGS }, + { "cite", cm_cite, BRACE_ARGS }, + { "clear", cm_clear, NO_BRACE_ARGS }, + { "code", cm_code, BRACE_ARGS }, + { "command", cm_code, BRACE_ARGS }, + { "comment", cm_ignore_line, NO_BRACE_ARGS }, + { "contents", cm_contents, NO_BRACE_ARGS }, + { "copyright", cm_copyright, BRACE_ARGS }, + { "ctrl", cm_obsolete, BRACE_ARGS }, + { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS }, + { "defcv", cm_defun, NO_BRACE_ARGS }, + { "defcvx", cm_defun, NO_BRACE_ARGS }, + { "deffn", cm_defun, NO_BRACE_ARGS }, + { "deffnx", cm_defun, NO_BRACE_ARGS }, + { "defindex", cm_defindex, NO_BRACE_ARGS }, + { "definfoenclose", cm_definfoenclose, NO_BRACE_ARGS }, + { "defivar", cm_defun, NO_BRACE_ARGS }, + { "defivarx", cm_defun, NO_BRACE_ARGS }, + { "defmac", cm_defun, NO_BRACE_ARGS }, + { "defmacx", cm_defun, NO_BRACE_ARGS }, + { "defmethod", cm_defun, NO_BRACE_ARGS }, + { "defmethodx", cm_defun, NO_BRACE_ARGS }, + { "defop", cm_defun, NO_BRACE_ARGS }, + { "defopt", cm_defun, NO_BRACE_ARGS }, + { "defoptx", cm_defun, NO_BRACE_ARGS }, + { "defopx", cm_defun, NO_BRACE_ARGS }, + { "defspec", cm_defun, NO_BRACE_ARGS }, + { "defspecx", cm_defun, NO_BRACE_ARGS }, + { "deftp", cm_defun, NO_BRACE_ARGS }, + { "deftpx", cm_defun, NO_BRACE_ARGS }, + { "deftypefn", cm_defun, NO_BRACE_ARGS }, + { "deftypefnx", cm_defun, NO_BRACE_ARGS }, + { "deftypefun", cm_defun, NO_BRACE_ARGS }, + { "deftypefunx", cm_defun, NO_BRACE_ARGS }, + { "deftypeivar", cm_defun, NO_BRACE_ARGS }, + { "deftypeivarx", cm_defun, NO_BRACE_ARGS }, + { "deftypemethod", cm_defun, NO_BRACE_ARGS }, + { "deftypemethodx", cm_defun, NO_BRACE_ARGS }, + { "deftypeop", cm_defun, NO_BRACE_ARGS }, + { "deftypeopx", cm_defun, NO_BRACE_ARGS }, + { "deftypevar", cm_defun, NO_BRACE_ARGS }, + { "deftypevarx", cm_defun, NO_BRACE_ARGS }, + { "deftypevr", cm_defun, NO_BRACE_ARGS }, + { "deftypevrx", cm_defun, NO_BRACE_ARGS }, + { "defun", cm_defun, NO_BRACE_ARGS }, + { "defunx", cm_defun, NO_BRACE_ARGS }, + { "defvar", cm_defun, NO_BRACE_ARGS }, + { "defvarx", cm_defun, NO_BRACE_ARGS }, + { "defvr", cm_defun, NO_BRACE_ARGS }, + { "defvrx", cm_defun, NO_BRACE_ARGS }, + { "detailmenu", cm_detailmenu, NO_BRACE_ARGS }, + { "dfn", cm_dfn, BRACE_ARGS }, + { "dircategory", cm_dircategory, NO_BRACE_ARGS }, + { "direntry", cm_direntry, NO_BRACE_ARGS }, + { "display", cm_display, NO_BRACE_ARGS }, + { "dmn", cm_no_op, BRACE_ARGS }, + { "documentencoding", cm_documentencoding, NO_BRACE_ARGS }, + { "documentlanguage", cm_documentlanguage, NO_BRACE_ARGS }, + { "dotaccent", cm_accent, MAYBE_BRACE_ARGS }, + { "dotless", cm_dotless, BRACE_ARGS }, + { "dots", cm_dots, BRACE_ARGS }, + { "email", cm_email, BRACE_ARGS }, + { "emph", cm_emph, BRACE_ARGS }, + { "end", cm_end, NO_BRACE_ARGS }, + { "enddots", cm_enddots, BRACE_ARGS }, + { "enumerate", cm_enumerate, NO_BRACE_ARGS }, + { "env", cm_code, BRACE_ARGS }, + { "equiv", cm_equiv, BRACE_ARGS }, + { "error", cm_error, BRACE_ARGS }, + { "example", cm_example, NO_BRACE_ARGS }, + { "exampleindent", cm_exampleindent, NO_BRACE_ARGS }, + { "exclamdown", cm_special_char, BRACE_ARGS }, + { "exdent", cm_exdent, NO_BRACE_ARGS }, + { "expansion", cm_expansion, BRACE_ARGS }, + { "file", cm_code, BRACE_ARGS }, + { "finalout", cm_no_op, NO_BRACE_ARGS }, + { "findex", cm_findex, NO_BRACE_ARGS }, + { "flushleft", cm_flushleft, NO_BRACE_ARGS }, + { "flushright", cm_flushright, NO_BRACE_ARGS }, + { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */ + { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS }, + { "format", cm_format, NO_BRACE_ARGS }, + { "ftable", cm_ftable, NO_BRACE_ARGS }, + { "group", cm_group, NO_BRACE_ARGS }, + { "heading", cm_heading, NO_BRACE_ARGS }, + { "headings", cm_ignore_line, NO_BRACE_ARGS }, + { "html", cm_html, NO_BRACE_ARGS }, + { "hyphenation", cm_ignore_arg, BRACE_ARGS }, + { "i", cm_i, BRACE_ARGS }, + { "ifclear", cm_ifclear, NO_BRACE_ARGS }, + { "ifeq", cm_ifeq, NO_BRACE_ARGS }, + { "ifhtml", cm_ifhtml, NO_BRACE_ARGS }, + { "ifinfo", cm_ifinfo, NO_BRACE_ARGS }, + { "ifnothtml", cm_ifnothtml, NO_BRACE_ARGS }, + { "ifnotinfo", cm_ifnotinfo, NO_BRACE_ARGS }, + { "ifnottex", cm_ifnottex, NO_BRACE_ARGS }, + { "ifset", cm_ifset, NO_BRACE_ARGS }, + { "iftex", cm_iftex, NO_BRACE_ARGS }, + { "ignore", command_name_condition, NO_BRACE_ARGS }, + { "image", cm_image, BRACE_ARGS }, + { "include", cm_include, NO_BRACE_ARGS }, + { "inforef", cm_inforef, BRACE_ARGS }, + { "item", cm_item, NO_BRACE_ARGS }, + { "itemize", cm_itemize, NO_BRACE_ARGS }, + { "itemx", cm_itemx, NO_BRACE_ARGS }, + { "kbd", cm_kbd, BRACE_ARGS }, + { "kbdinputstyle", cm_ignore_line, NO_BRACE_ARGS }, + { "key", cm_key, BRACE_ARGS }, + { "kindex", cm_kindex, NO_BRACE_ARGS }, + { "l", cm_special_char, BRACE_ARGS }, + { "lisp", cm_lisp, NO_BRACE_ARGS }, + { "lowersections", cm_lowersections, NO_BRACE_ARGS }, + { "macro", cm_macro, NO_BRACE_ARGS }, + { "majorheading", cm_majorheading, NO_BRACE_ARGS }, + { "math", cm_no_op, BRACE_ARGS }, + { "menu", cm_menu, NO_BRACE_ARGS }, + { "minus", cm_minus, BRACE_ARGS }, + { "multitable", cm_multitable, NO_BRACE_ARGS }, + { "need", cm_ignore_line, NO_BRACE_ARGS }, + { "node", cm_node, NO_BRACE_ARGS }, + { "noindent", cm_noindent, NO_BRACE_ARGS }, + { "noindent", cm_novalidate, NO_BRACE_ARGS }, + { "nwnode", cm_node, NO_BRACE_ARGS }, + { "o", cm_special_char, BRACE_ARGS }, + { "oe", cm_special_char, BRACE_ARGS }, + { "option", cm_code, BRACE_ARGS }, + { "page", cm_no_op, NO_BRACE_ARGS }, + { "pagesizes", cm_ignore_line, NO_BRACE_ARGS }, + { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS }, + { "pindex", cm_pindex, NO_BRACE_ARGS }, + { "point", cm_point, BRACE_ARGS }, + { "pounds", cm_special_char, BRACE_ARGS }, + { "print", cm_print, BRACE_ARGS }, + { "printindex", cm_printindex, NO_BRACE_ARGS }, + { "pxref", cm_pxref, BRACE_ARGS }, + { "questiondown", cm_special_char, BRACE_ARGS }, + { "quotation", cm_quotation, NO_BRACE_ARGS }, + { "r", cm_r, BRACE_ARGS }, + { "raisesections", cm_raisesections, NO_BRACE_ARGS }, + { "ref", cm_ref, BRACE_ARGS }, + { "refill", cm_no_op, NO_BRACE_ARGS }, + { "result", cm_result, BRACE_ARGS }, + { "ringaccent", cm_accent, MAYBE_BRACE_ARGS }, + { "rmacro", cm_rmacro, NO_BRACE_ARGS }, + { "samp", cm_code, BRACE_ARGS }, + { "sc", cm_sc, BRACE_ARGS }, + { "section", cm_section, NO_BRACE_ARGS }, + { "set", cm_set, NO_BRACE_ARGS }, + { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS }, + { "setchapterstyle", cm_obsolete, NO_BRACE_ARGS }, + { "setcontentsaftertitlepage", cm_no_op, NO_BRACE_ARGS }, + { "setfilename", cm_setfilename, NO_BRACE_ARGS }, + { "setshortcontentsaftertitlepage", cm_no_op, NO_BRACE_ARGS }, + { "settitle", cm_settitle, NO_BRACE_ARGS }, + { "shortcontents", cm_shortcontents, NO_BRACE_ARGS }, + { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS }, + { "smallbook", cm_ignore_line, NO_BRACE_ARGS }, + { "smalldisplay", cm_smalldisplay, NO_BRACE_ARGS }, + { "smallexample", cm_smallexample, NO_BRACE_ARGS }, + { "smallformat", cm_smallformat, NO_BRACE_ARGS }, + { "smalllisp", cm_smalllisp, NO_BRACE_ARGS }, + { "sp", cm_sp, NO_BRACE_ARGS }, + { "ss", cm_special_char, BRACE_ARGS }, + { "strong", cm_strong, BRACE_ARGS }, + { "subheading", cm_subheading, NO_BRACE_ARGS }, + { "subsection", cm_subsection, NO_BRACE_ARGS }, + { "subsubheading", cm_subsubheading, NO_BRACE_ARGS }, + { "subsubsection", cm_subsubsection, NO_BRACE_ARGS }, + { "summarycontents", cm_no_op, NO_BRACE_ARGS }, + { "syncodeindex", cm_synindex, NO_BRACE_ARGS }, + { "synindex", cm_synindex, NO_BRACE_ARGS }, + { "t", cm_tt, BRACE_ARGS }, + { "tab", cm_tab, NO_BRACE_ARGS }, + { "table", cm_table, NO_BRACE_ARGS }, + { "tex", cm_tex, NO_BRACE_ARGS }, + { "tieaccent", cm_accent, MAYBE_BRACE_ARGS }, + { "tindex", cm_tindex, NO_BRACE_ARGS }, + { "titlefont", cm_titlefont, BRACE_ARGS }, + { "titlepage", command_name_condition, NO_BRACE_ARGS }, + { "today", cm_today, BRACE_ARGS }, + { "top", cm_top, NO_BRACE_ARGS }, + { "u", cm_accent, MAYBE_BRACE_ARGS }, + { "ubaraccent", cm_accent, MAYBE_BRACE_ARGS }, + { "udotaccent", cm_accent, MAYBE_BRACE_ARGS }, + { "unmacro", cm_unmacro, NO_BRACE_ARGS }, + { "unnumbered", cm_unnumbered, NO_BRACE_ARGS }, + { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS }, + { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS }, + { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS }, + { "uref", cm_uref, BRACE_ARGS }, + { "url", cm_url, BRACE_ARGS }, + { "v", cm_accent, MAYBE_BRACE_ARGS }, + { "value", cm_value, BRACE_ARGS }, + { "var", cm_var, BRACE_ARGS }, + { "vindex", cm_vindex, NO_BRACE_ARGS }, + { "vtable", cm_vtable, NO_BRACE_ARGS }, + { "w", cm_w, BRACE_ARGS }, + { "xref", cm_xref, BRACE_ARGS }, + + /* Deprecated commands. These used to be for italics. */ + { "iappendix", cm_ideprecated, NO_BRACE_ARGS }, + { "iappendixsec", cm_ideprecated, NO_BRACE_ARGS }, + { "iappendixsection", cm_ideprecated, NO_BRACE_ARGS }, + { "iappendixsubsec", cm_ideprecated, NO_BRACE_ARGS }, + { "iappendixsubsubsec", cm_ideprecated, NO_BRACE_ARGS }, + { "ichapter", cm_ideprecated, NO_BRACE_ARGS }, + { "isection", cm_ideprecated, NO_BRACE_ARGS }, + { "isubsection", cm_ideprecated, NO_BRACE_ARGS }, + { "isubsubsection", cm_ideprecated, NO_BRACE_ARGS }, + { "iunnumbered", cm_ideprecated, NO_BRACE_ARGS }, + { "iunnumberedsec", cm_ideprecated, NO_BRACE_ARGS }, + { "iunnumberedsubsec", cm_ideprecated, NO_BRACE_ARGS }, + { "iunnumberedsubsubsec", cm_ideprecated, NO_BRACE_ARGS }, + + /* Now @include does what this was used to. */ + { "infoinclude", cm_obsolete, NO_BRACE_ARGS }, + { "titlespec", cm_obsolete, NO_BRACE_ARGS }, + + { NULL, NULL, NO_BRACE_ARGS } +}; + +/* The bulk of the Texinfo commands. */ + +/* Commands which insert their own names. */ +void +insert_self (arg) + int arg; +{ + if (arg == START) + add_word (command); +} + +void +insert_space (arg) + int arg; +{ + if (arg == START) + add_char (' '); +} + +/* Force a line break in the output. */ +void +cm_asterisk () +{ + if (html) + add_word ("<br>"); + else + { + close_single_paragraph (); + cm_noindent (); + } +} + +/* Insert ellipsis. */ +void +cm_dots (arg) + int arg; +{ + if (arg == START) + add_word (html ? "<small>...</small>" : "..."); +} + +/* Insert ellipsis for sentence end. */ +void +cm_enddots (arg) + int arg; +{ + if (arg == START) + add_word (html ? "<small>...</small>." : "...."); +} + +void +cm_bullet (arg) + int arg; +{ + if (arg == START) + { + if (html) + add_word ("•"); + 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/gnu/usr.bin/texinfo/makeinfo/cmds.h b/gnu/usr.bin/texinfo/makeinfo/cmds.h new file mode 100644 index 00000000000..528b08512ec --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/cmds.h @@ -0,0 +1,50 @@ +/* cmds.h -- declarations for cmds.c. + $Id: cmds.h,v 1.1.1.1 2000/02/09 01:25:09 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef CMDS_H +#define CMDS_H + +/* The three arguments a command can get are a flag saying whether it is + before argument parsing (START) or after (END), the starting position + of the arguments, and the ending position. */ +typedef void COMMAND_FUNCTION (); /* So we can say COMMAND_FUNCTION *foo; */ + +/* Each command has an associated function. When the command is + encountered in the text, the associated function is called with START + as the argument. If the function expects arguments in braces, it + remembers itself on the stack. When the corresponding close brace is + encountered, the function is called with END as the argument. */ +#define START 0 +#define END 1 + +/* Does the command expect braces? */ +#define NO_BRACE_ARGS 0 +#define BRACE_ARGS 1 +#define MAYBE_BRACE_ARGS 2 + +typedef struct +{ + char *name; + COMMAND_FUNCTION *proc; + int argument_in_braces; +} COMMAND; + +extern COMMAND command_table[]; + +#endif /* !CMDS_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/defun.c b/gnu/usr.bin/texinfo/makeinfo/defun.c new file mode 100644 index 00000000000..e9078c655f7 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/defun.c @@ -0,0 +1,663 @@ +/* defun.c -- @defun and friends. + $Id: defun.c,v 1.1.1.1 2000/02/09 01:25:10 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "defun.h" +#include "insertion.h" +#include "makeinfo.h" + + +#define DEFUN_SELF_DELIMITING(c) \ + ((c) == '(' || (c) == ')' || (c) == '[' || (c) == ']') + +struct token_accumulator +{ + unsigned int length; + unsigned int index; + char **tokens; +}; + +static void +initialize_token_accumulator (accumulator) + struct token_accumulator *accumulator; +{ + accumulator->length = 0; + accumulator->index = 0; + accumulator->tokens = NULL; +} + +static void +accumulate_token (accumulator, token) + struct token_accumulator *accumulator; + char *token; +{ + if (accumulator->index >= accumulator->length) + { + accumulator->length += 10; + accumulator->tokens = xrealloc (accumulator->tokens, + (accumulator->length * sizeof (char *))); + } + accumulator->tokens[accumulator->index] = token; + accumulator->index += 1; +} + +/* Given STRING_POINTER pointing at an open brace, skip forward and return a + pointer to just past the matching close brace. */ +static int +scan_group_in_string (string_pointer) + char **string_pointer; +{ + char *scan_string = (*string_pointer) + 1; + unsigned int level = 1; + + for (;;) + { + int c; + if (level == 0) + { + *string_pointer = scan_string; + return 1; + } + c = *scan_string++; + if (c == 0) + { + /* Tweak line_number to compensate for fact that + we gobbled the whole line before coming here. */ + line_number -= 1; + line_error (_("Missing `}' in @def arg")); + line_number += 1; + *string_pointer = scan_string - 1; + return 0; + } + + if (c == '{') + level += 1; + if (c == '}') + level -= 1; + } +} + +/* Return a list of tokens from the contents of STRING. + Commands and brace-delimited groups count as single tokens. + Contiguous whitespace characters are converted to a token + consisting of a single space. */ +static char ** +args_from_string (string) + char *string; +{ + struct token_accumulator accumulator; + char *token_start, *token_end; + char *scan_string = string; + + initialize_token_accumulator (&accumulator); + + while (*scan_string) + { /* Replace arbitrary whitespace by a single space. */ + if (whitespace (*scan_string)) + { + scan_string += 1; + while (whitespace (*scan_string)) + scan_string += 1; + accumulate_token ((&accumulator), (xstrdup (" "))); + continue; + } + + /* Commands count as single tokens. */ + if (*scan_string == COMMAND_PREFIX) + { + token_start = scan_string; + scan_string += 1; + if (self_delimiting (*scan_string)) + scan_string += 1; + else + { + int c; + while (1) + { + c = *scan_string++; + + if ((c == 0) || (c == '{') || (whitespace (c))) + { + scan_string -= 1; + break; + } + } + + if (*scan_string == '{') + { + char *s = scan_string; + (void) scan_group_in_string (&s); + scan_string = s; + } + } + token_end = scan_string; + } + + /* Parentheses and brackets are self-delimiting. */ + else if (DEFUN_SELF_DELIMITING (*scan_string)) + { + token_start = scan_string; + scan_string += 1; + token_end = scan_string; + } + + /* Open brace introduces a group that is a single token. */ + else if (*scan_string == '{') + { + char *s = scan_string; + int balanced = scan_group_in_string (&s); + + token_start = scan_string + 1; + scan_string = s; + token_end = balanced ? (scan_string - 1) : scan_string; + } + + /* Otherwise a token is delimited by whitespace, parentheses, + brackets, or braces. A token is also ended by a command. */ + else + { + token_start = scan_string; + + for (;;) + { + int c; + + c = *scan_string++; + + /* Do not back up if we're looking at a }; since the only + valid }'s are those matched with {'s, we want to give + an error. If we back up, we go into an infinite loop. */ + if (!c || whitespace (c) || DEFUN_SELF_DELIMITING (c) + || c == '{') + { + scan_string--; + break; + } + + /* If we encounter a command embedded within a token, + then end the token. */ + if (c == COMMAND_PREFIX) + { + scan_string--; + break; + } + } + token_end = scan_string; + } + + accumulate_token (&accumulator, substring (token_start, token_end)); + } + accumulate_token (&accumulator, NULL); + return accumulator.tokens; +} + +static void +process_defun_args (defun_args, auto_var_p) + char **defun_args; + int auto_var_p; +{ + int pending_space = 0; + + for (;;) + { + char *defun_arg = *defun_args++; + + if (defun_arg == NULL) + break; + + if (defun_arg[0] == ' ') + { + pending_space = 1; + continue; + } + + if (pending_space) + { + add_char (' '); + pending_space = 0; + } + + if (DEFUN_SELF_DELIMITING (defun_arg[0])) + add_char (defun_arg[0]); + else if (defun_arg[0] == '&') + if (html) + { + defun_arg = escape_string (xstrdup (defun_arg)); + add_word (defun_arg); + free (defun_arg); + } + else + add_word (defun_arg); + else if (defun_arg[0] == COMMAND_PREFIX) + execute_string ("%s", defun_arg); + else if (auto_var_p) + if (html) + { + defun_arg = escape_string (xstrdup (defun_arg)); + add_word (defun_arg); + free (defun_arg); + } + else + add_word (defun_arg); + else + add_word (defun_arg); + } +} + +static char * +next_nonwhite_defun_arg (arg_pointer) + char ***arg_pointer; +{ + char **scan = (*arg_pointer); + char *arg = (*scan++); + + if ((arg != 0) && (*arg == ' ')) + arg = *scan++; + + if (arg == 0) + scan -= 1; + + *arg_pointer = scan; + + return (arg == 0) ? "" : arg; +} + + +/* This is needed also in insertion.c. */ + +enum insertion_type +get_base_type (type) + enum insertion_type type; +{ + enum insertion_type base_type; + switch (type) + { + case defivar: base_type = defcv; break; + case defmac: base_type = deffn; break; + case defmethod: base_type = defop; break; + case defopt: base_type = defvr; break; + case defspec: base_type = deffn; break; + case deftypefun: base_type = deftypefn; break; + case deftypeivar: base_type = deftypeivar; break; + case deftypemethod: base_type = deftypemethod; break; + case deftypeop: base_type = deftypeop; break; + case deftypevar: base_type = deftypevr; break; + case defun: base_type = deffn; break; + case defvar: base_type = defvr; break; + default: + base_type = type; + break; + } + + return base_type; +} + +/* Make the defun type insertion. + TYPE says which insertion this is. + X_P, if nonzero, says not to start a new insertion. */ +static void +defun_internal (type, x_p) + enum insertion_type type; + int x_p; +{ + enum insertion_type base_type; + char **defun_args, **scan_args; + char *category, *defined_name, *type_name, *type_name2; + + { + char *line; + + /* The @def.. line is the only place in Texinfo where you are + allowed to use unquoted braces that don't delimit arguments of + a command or a macro; in any other place it will trigger an + error message from the reader loop. The special handling of + this case inside `args_from_string' is an extra special hack + which allows this. The side effect is that if we try to expand + the rest of the line below, the recursive reader loop will + signal an error if there are brace-delimited arguments on that line. + + The best solution to this would be to change the syntax of + @def.. commands so that it doesn't violate Texinfo's own rules. + But it's probably too late for this now, as it will break a lot + of existing manuals. + + Unfortunately, this means that you can't call macros, use @value, etc. + inside @def.. commands, sigh. */ + get_rest_of_line (0, &line); + defun_args = (args_from_string (line)); + free (line); + } + + scan_args = defun_args; + + /* Get base type and category string. */ + base_type = get_base_type (type); + + /* xx all these const strings should be determined upon + documentlanguage argument and NOT via gettext (kama). */ + switch (type) + { + case defun: + case deftypefun: + category = _("Function"); + break; + case defmac: + category = _("Macro"); + break; + case defspec: + category = _("Special Form"); + break; + case defvar: + case deftypevar: + category = _("Variable"); + break; + case defopt: + category = _("User Option"); + break; + case defivar: + case deftypeivar: + category = _("Instance Variable"); + break; + case defmethod: + case deftypemethod: + category = _("Method"); + break; + default: + category = next_nonwhite_defun_arg (&scan_args); + break; + } + + /* The class name. */ + if ((base_type == deftypefn) + || (base_type == deftypevr) + || (base_type == defcv) + || (base_type == defop) + || (base_type == deftypeivar) + || (base_type == deftypemethod) + || (base_type == deftypeop) + ) + type_name = next_nonwhite_defun_arg (&scan_args); + + /* The type name for typed languages. */ + if (base_type == deftypemethod + || base_type == deftypeivar + || base_type == deftypeop + ) + type_name2 = next_nonwhite_defun_arg (&scan_args); + + /* The function or whatever that's actually being defined. */ + defined_name = next_nonwhite_defun_arg (&scan_args); + + /* This hack exists solely for the purposes of formatting the Texinfo + manual. I couldn't think of a better way. The token might be a + simple @@ followed immediately by more text. If this is the case, + then the next defun arg is part of this one, and we should + concatenate them. */ + if (*scan_args && **scan_args && !whitespace (**scan_args) + && STREQ (defined_name, "@@")) + { + char *tem = xmalloc (3 + strlen (scan_args[0])); + + sprintf (tem, "@@%s", scan_args[0]); + + free (scan_args[0]); + scan_args[0] = tem; + scan_args++; + defined_name = tem; + } + + if (!x_p) + begin_insertion (type); + + /* Write the definition header line. + This should start at the normal indentation. */ + current_indent -= default_indentation_increment; + start_paragraph (); + + if (html && !x_p) + /* Start the definition on new paragraph. */ + add_word ("<p>\n"); + + if (!html) + switch (base_type) + { + case deffn: + case defvr: + case deftp: + execute_string (" -- %s: %s", category, defined_name); + break; + case deftypefn: + case deftypevr: + execute_string (" -- %s: %s %s", category, type_name, defined_name); + break; + case defcv: + execute_string (" -- %s %s %s: %s", category, _("of"), type_name, + defined_name); + break; + case deftypeivar: + execute_string (" -- %s %s %s: %s %s", category, _("of"), type_name, + type_name2, defined_name); + break; + case defop: + execute_string (" -- %s %s %s: %s", category, _("on"), type_name, + defined_name); + break; + case deftypeop: + execute_string (" -- %s %s %s: %s %s", category, _("on"), type_name, + type_name2, defined_name); + break; + case deftypemethod: + execute_string (" -- %s %s %s: %s %s", category, _("on"), type_name, + type_name2, defined_name); + break; + } + + if (html) + { + /* If this is not a @def...x version, it could only + be a normal version @def.... So start the table here. */ + if (!x_p) + add_word ("<table width=\"100%\">\n"); + + /* If this is an @def...x there has to be an other @def... before + it, so this is only a new row within an existing table. With + two complete standalone tables the gap between them is too big. */ + add_word ("<tr>\n"); + add_word ("<td align=\"left\">"); + + switch (base_type) + { + case deffn: + case defvr: + case deftp: + /* <i> is for the following function arguments. */ + add_word_args ("<b>%s</b><i>", defined_name); + break; + case deftypefn: + case deftypevr: + add_word_args ("%s <b>%s</b><i>", type_name, defined_name); + break; + case defcv: + case defop: + add_word_args ("<b>%s</b><i>", defined_name); + break; + case deftypemethod: + case deftypeop: + case deftypeivar: + add_word_args ("%s <b>%s</b><i>", type_name2, defined_name); + break; + } + } /* if (html)... */ + + current_indent += default_indentation_increment; + + /* Now process the function arguments, if any. If these carry onto + the next line, they should be indented by two increments to + distinguish them from the body of the definition, which is indented + by one increment. */ + current_indent += default_indentation_increment; + + switch (base_type) + { + case deffn: + case defop: + process_defun_args (scan_args, 1); + break; + + /* Through Makeinfo 1.67 we processed remaining args only for deftp, + deftypefn, and deftypemethod. But the libc manual, for example, + needs to say: + @deftypevar {char *} tzname[2] + And simply allowing the extra text seems far simpler than trying + to invent yet more defn commands. In any case, we should either + output it or give an error, not silently ignore it. */ + default: + process_defun_args (scan_args, 0); + break; + } + + current_indent -= default_indentation_increment; + close_single_paragraph (); + + if (html) + { + /* xx The single words (on, off) used here, should depend on + documentlanguage and NOT on gettext --kama. */ + switch (base_type) + { + case deffn: + case defvr: + case deftp: + case deftypefn: + case deftypevr: + add_word ("</i>"); /* close italic area for arguments */ + /* put the rest into the second column */ + add_word_args ("</td>\n<td align=\"right\">%s", category); + break; + + case defcv: + add_word ("</td>\n<td align=\"right\">"); + add_word_args ("%s %s %s", category, _("of"), type_name); + break; + + case defop: + case deftypemethod: + case deftypeop: + add_word ("</i>"); + add_word ("</td>\n<td align=\"right\">"); + add_word_args ("%s %s %s", category, _("on"), type_name); + break; + + case deftypeivar: + add_word ("</i>"); + add_word ("</td>\n<td align=\"right\">"); + add_word_args ("%s %s %s", category, _("of"), type_name); + break; + } /* switch (base_type)... */ + + add_word ("</td>\n"); /* close second column */ + add_word ("</tr>\n"); /* close row */ + + /* This is needed because I have to know if the next line is + normal text or another @def..x. If text follows, create a new + table to get the indentation for the following text. + + This construction would fail if someone uses: + @deffn + @sp 2 + @deffnx + . + @end deffn + But we don't care. */ + if (!looking_at ("@def")) + { + add_word ("</table>\n"); + add_word ("<table width=\"95%\" align=\"center\">\n"); + add_word ("<tr><td>\n"); + } + + } /* if (html)... */ + + /* Make an entry in the appropriate index. */ + switch (base_type) + { + case deffn: + case deftypefn: + execute_string ("@findex %s\n", defined_name); + break; + case defvr: + case deftypevr: + case defcv: + execute_string ("@vindex %s\n", defined_name); + break; + case deftypeivar: + execute_string ("@vindex %s %s %s\n", defined_name, _("of"), type_name); + break; + case defop: + case deftypeop: + case deftypemethod: + execute_string ("@findex %s %s %s\n", defined_name, _("on"), type_name); + break; + case deftp: + execute_string ("@tindex %s\n", defined_name); + break; + } + + /* Deallocate the token list. */ + scan_args = defun_args; + while (1) + { + char * arg = (*scan_args++); + if (arg == NULL) + break; + free (arg); + } + free (defun_args); +} + +/* Add an entry for a function, macro, special form, variable, or option. + If the name of the calling command ends in `x', then this is an extra + entry included in the body of an insertion of the same type. */ +void +cm_defun () +{ + int x_p; + enum insertion_type type; + char *temp = xstrdup (command); + + x_p = (command[strlen (command) - 1] == 'x'); + + if (x_p) + temp[strlen (temp) - 1] = 0; + + type = find_type_from_name (temp); + free (temp); + + /* If we are adding to an already existing insertion, then make sure + that we are already in an insertion of type TYPE. */ + if (x_p && (!insertion_level || insertion_stack->insertion != type)) + { + line_error (_("Must be in `%s' insertion to use `%sx'"), + command, command); + discard_until ("\n"); + return; + } + + defun_internal (type, x_p); +} diff --git a/gnu/usr.bin/texinfo/makeinfo/defun.h b/gnu/usr.bin/texinfo/makeinfo/defun.h new file mode 100644 index 00000000000..9ab64483a7e --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/defun.h @@ -0,0 +1,31 @@ +/* defun.h -- declaration for defuns. + $Id: defun.h,v 1.1.1.1 2000/02/09 01:25:10 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */ + +#ifndef DEFUN_H +#define DEFUN_H + +#include "insertion.h" + +extern enum insertion_type get_base_type (); +extern void cm_defun (); + +#endif /* !DEFUN_H */ + diff --git a/gnu/usr.bin/texinfo/makeinfo/files.c b/gnu/usr.bin/texinfo/makeinfo/files.c new file mode 100644 index 00000000000..6f2dcb2b8d6 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/files.c @@ -0,0 +1,529 @@ +/* files.c -- file-related functions for Texinfo. + $Id: files.c,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "files.h" +#include "macro.h" +#include "makeinfo.h" + +FSTACK *filestack = NULL; + +static int node_filename_stack_index = 0; +static int node_filename_stack_size = 0; +static char **node_filename_stack = NULL; + + +/* Looking for include files. */ + +/* Given a string containing units of information separated by colons, + return the next one pointed to by INDEX, or NULL if there are no more. + Advance INDEX to the character after the colon. */ +static char * +extract_colon_unit (string, index) + char *string; + int *index; +{ + int start; + int path_sep_char = PATH_SEP[0]; + int i = *index; + + if (!string || (i >= strlen (string))) + return NULL; + + /* Each call to this routine leaves the index pointing at a colon if + there is more to the path. If i > 0, then increment past the + `:'. If i == 0, then the path has a leading colon. Trailing colons + are handled OK by the `else' part of the if statement; an empty + string is returned in that case. */ + if (i && string[i] == path_sep_char) + i++; + + start = i; + while (string[i] && string[i] != path_sep_char) i++; + *index = i; + + if (i == start) + { + if (string[i]) + (*index)++; + + /* Return "" in the case of a trailing `:'. */ + return xstrdup (""); + } + else + { + char *value; + + value = xmalloc (1 + (i - start)); + memcpy (value, &string[start], (i - start)); + value [i - start] = 0; + + return value; + } +} + +/* Return the full pathname for FILENAME by searching along PATH. + When found, return the stat () info for FILENAME in FINFO. + If PATH is NULL, only the current directory is searched. + If the file could not be found, return a NULL pointer. */ +static char * +get_file_info_in_path (filename, path, finfo) + char *filename, *path; + struct stat *finfo; +{ + char *dir; + int result, index = 0; + + if (path == NULL) + path = "."; + + /* Handle absolute pathnames. */ + if (IS_ABSOLUTE (filename) + || (*filename == '.' + && (IS_SLASH (filename[1]) + || (filename[1] == '.' && IS_SLASH (filename[2]))))) + { + if (stat (filename, finfo) == 0) + return xstrdup (filename); + else + return NULL; + } + + while ((dir = extract_colon_unit (path, &index))) + { + char *fullpath; + + if (!*dir) + { + free (dir); + dir = xstrdup ("."); + } + + fullpath = xmalloc (2 + strlen (dir) + strlen (filename)); + sprintf (fullpath, "%s/%s", dir, filename); + free (dir); + + result = stat (fullpath, finfo); + + if (result == 0) + return fullpath; + else + free (fullpath); + } + return NULL; +} + +/* Find and load the file named FILENAME. Return a pointer to + the loaded file, or NULL if it can't be loaded. */ +char * +find_and_load (filename) + char *filename; +{ + struct stat fileinfo; + long file_size; + int file = -1, count = 0; + char *fullpath, *result; +#if O_BINARY || defined (VMS) + int n; +#endif + + result = fullpath = NULL; + + fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo); + + if (!fullpath) + goto error_exit; + + filename = fullpath; + file_size = (long) fileinfo.st_size; + + file = open (filename, O_RDONLY); + if (file < 0) + goto error_exit; + + /* Load the file, with enough room for a newline and a null. */ + result = xmalloc (file_size + 2); + + /* VMS stat lies about the st_size value. The actual number of + readable bytes is always less than this value. The arcane + mysteries of VMS/RMS are too much to probe, so this hack + suffices to make things work. */ +#if O_BINARY || defined (VMS) +#ifdef VMS + while ((n = read (file, result + count, file_size)) > 0) +#else /* !VMS */ +#ifndef WIN32 + while ((n = read (file, result + count, file_size)) > 0) +#else /* WIN32 */ + /* Does WIN32 really need reading 1 character at a time?? */ + while ((n = read (file, result + count, 1)) > 0) +#endif /* WIN32 */ +#endif /* !VMS */ + count += n; + if (0 < count && count < file_size) + result = xrealloc (result, count + 2); /* why waste the slack? */ + else if (n == -1) +#else /* !VMS && !O_BINARY */ + count = file_size; + if (read (file, result, file_size) != file_size) +#endif /* !VMS && !WIN32 */ + + error_exit: + { + if (result) + free (result); + + if (fullpath) + free (fullpath); + + if (file != -1) + close (file); + + return NULL; + } + close (file); + + /* Set the globals to the new file. */ + input_text = result; + input_text_length = count; + input_filename = fullpath; + node_filename = xstrdup (fullpath); + input_text_offset = 0; + line_number = 1; + /* Not strictly necessary. This magic prevents read_token () from doing + extra unnecessary work each time it is called (that is a lot of times). + INPUT_TEXT_LENGTH is one past the actual end of the text. */ + input_text[input_text_length] = '\n'; + /* This, on the other hand, is always necessary. */ + input_text[input_text_length+1] = 0; + return result; +} + +/* Pushing and popping files. */ +void +push_node_filename () +{ + if (node_filename_stack_index + 1 > node_filename_stack_size) + node_filename_stack = xrealloc + (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *)); + + node_filename_stack[node_filename_stack_index] = node_filename; + node_filename_stack_index++; +} + +void +pop_node_filename () +{ + node_filename = node_filename_stack[--node_filename_stack_index]; +} + +/* Save the state of the current input file. */ +void +pushfile () +{ + FSTACK *newstack = xmalloc (sizeof (FSTACK)); + newstack->filename = input_filename; + newstack->text = input_text; + newstack->size = input_text_length; + newstack->offset = input_text_offset; + newstack->line_number = line_number; + newstack->next = filestack; + + filestack = newstack; + push_node_filename (); +} + +/* Make the current file globals be what is on top of the file stack. */ +void +popfile () +{ + FSTACK *tos = filestack; + + if (!tos) + abort (); /* My fault. I wonder what I did? */ + + if (macro_expansion_output_stream) + { + maybe_write_itext (input_text, input_text_offset); + forget_itext (input_text); + } + + /* Pop the stack. */ + filestack = filestack->next; + + /* Make sure that commands with braces have been satisfied. */ + if (!executing_string && !me_executing_string) + discard_braces (); + + /* Get the top of the stack into the globals. */ + input_filename = tos->filename; + input_text = tos->text; + input_text_length = tos->size; + input_text_offset = tos->offset; + line_number = tos->line_number; + free (tos); + + /* Go back to the (now) current node. */ + pop_node_filename (); +} + +/* Flush all open files on the file stack. */ +void +flush_file_stack () +{ + while (filestack) + { + char *fname = input_filename; + char *text = input_text; + popfile (); + free (fname); + free (text); + } +} + +/* Return the index of the first character in the filename + which is past all the leading directory characters. */ +static int +skip_directory_part (filename) + char *filename; +{ + int i = strlen (filename) - 1; + + while (i && !IS_SLASH (filename[i])) + i--; + if (IS_SLASH (filename[i])) + i++; + else if (filename[i] && HAVE_DRIVE (filename)) + i = 2; + + return i; +} + +char * +filename_non_directory (name) + char *name; +{ + return xstrdup (name + skip_directory_part (name)); +} + +/* Return just the simple part of the filename; i.e. the + filename without the path information, or extensions. + This conses up a new string. */ +char * +filename_part (filename) + char *filename; +{ + char *basename = filename_non_directory (filename); + +#ifdef REMOVE_OUTPUT_EXTENSIONS + /* See if there is an extension to remove. If so, remove it. */ + { + char *temp; + + temp = strrchr (basename, '.'); + if (temp) + *temp = 0; + } +#endif /* REMOVE_OUTPUT_EXTENSIONS */ + return basename; +} + +/* Return the pathname part of filename. This can be NULL. */ +char * +pathname_part (filename) + char *filename; +{ + char *expand_filename (); + char *result = NULL; + int i; + + filename = expand_filename (filename, ""); + + i = skip_directory_part (filename); + if (i) + { + result = xmalloc (1 + i); + strncpy (result, filename, i); + result[i] = 0; + } + free (filename); + return result; +} + +/* Return the expansion of FILENAME. */ +char * +expand_filename (filename, input_name) + char *filename, *input_name; +{ + int i; + char *full_pathname (); + + if (filename) + { + filename = full_pathname (filename); + if (IS_ABSOLUTE (filename) + || (*filename == '.' && + (IS_SLASH (filename[1]) || + (filename[1] == '.' && IS_SLASH (filename[2]))))) + return filename; + } + else + { + filename = filename_non_directory (input_name); + + if (!*filename) + { + free (filename); + filename = xstrdup ("noname.texi"); + } + + for (i = strlen (filename) - 1; i; i--) + if (filename[i] == '.') + break; + + if (!i) + i = strlen (filename); + + if (i + 6 > (strlen (filename))) + filename = xrealloc (filename, i + 6); + strcpy (filename + i, html ? ".html" : ".info"); + return filename; + } + + if (IS_ABSOLUTE (input_name)) + { + /* Make it so that relative names work. */ + char *result; + + i = strlen (input_name) - 1; + + result = xmalloc (1 + strlen (input_name) + strlen (filename)); + strcpy (result, input_name); + + while (!IS_SLASH (result[i]) && i) + i--; + if (IS_SLASH (result[i])) + i++; + + strcpy (&result[i], filename); + free (filename); + return result; + } + return filename; +} + +/* Return the full path to FILENAME. */ +char * +full_pathname (filename) + char *filename; +{ + int initial_character; + char *result; + + /* No filename given? */ + if (!filename || !*filename) + return xstrdup (""); + + /* Already absolute? */ + if (IS_ABSOLUTE (filename) || + (*filename == '.' && + (IS_SLASH (filename[1]) || + (filename[1] == '.' && IS_SLASH (filename[2]))))) + return xstrdup (filename); + + initial_character = *filename; + if (initial_character != '~') + { + char *localdir = xmalloc (1025); +#ifdef HAVE_GETCWD + if (!getcwd (localdir, 1024)) +#else + if (!getwd (localdir)) +#endif + { + fprintf (stderr, _("%s: getwd: %s, %s\n"), + progname, filename, localdir); + xexit (1); + } + + strcat (localdir, "/"); + strcat (localdir, filename); + result = xstrdup (localdir); + free (localdir); + } + else + { /* Does anybody know why WIN32 doesn't want to support $HOME? + If the reason is they don't have getpwnam, they should + only disable the else clause below. */ +#ifndef WIN32 + if (IS_SLASH (filename[1])) + { + /* Return the concatenation of the environment variable HOME + and the rest of the string. */ + char *temp_home; + + temp_home = (char *) getenv ("HOME"); + result = xmalloc (strlen (&filename[1]) + + 1 + + temp_home ? strlen (temp_home) + : 0); + *result = 0; + + if (temp_home) + strcpy (result, temp_home); + + strcat (result, &filename[1]); + } + else + { + struct passwd *user_entry; + int i, c; + char *username = xmalloc (257); + + for (i = 1; (c = filename[i]); i++) + { + if (IS_SLASH (c)) + break; + else + username[i - 1] = c; + } + if (c) + username[i - 1] = 0; + + user_entry = getpwnam (username); + + if (!user_entry) + return xstrdup (filename); + + result = xmalloc (1 + strlen (user_entry->pw_dir) + + strlen (&filename[i])); + strcpy (result, user_entry->pw_dir); + strcat (result, &filename[i]); + } +#endif /* not WIN32 */ + } + return result; +} + +char * +output_name_from_input_name (name) + char *name; +{ + return expand_filename (NULL, name); +} diff --git a/gnu/usr.bin/texinfo/makeinfo/files.h b/gnu/usr.bin/texinfo/makeinfo/files.h new file mode 100644 index 00000000000..6cae9e543e4 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/files.h @@ -0,0 +1,45 @@ +/* files.h -- declarations for files.c. + $Id: files.h,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $ + + Copyright (C) 1998 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FILES_H +#define FILES_H + +/* A stack of file information records. If a new file is read in with + "@input", we remember the old input file state on this stack. */ +typedef struct fstack +{ + struct fstack *next; + char *filename; + char *text; + int size; + int offset; + int line_number; +} FSTACK; +extern FSTACK *filestack; + +extern void pushfile (), popfile (); +extern void flush_file_stack (); +extern char *find_and_load (); +extern char *output_name_from_input_name (); +extern char *expand_filename (); +extern char *filename_part (); +extern char *pathname_part (); + +#endif /* !FILES_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/footnote.c b/gnu/usr.bin/texinfo/makeinfo/footnote.c new file mode 100644 index 00000000000..79a52e6996b --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/footnote.c @@ -0,0 +1,359 @@ +/* footnote.c -- footnotes for Texinfo. + $Id: footnote.c,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "footnote.h" +#include "macro.h" +#include "makeinfo.h" + +/* Nonzero means that the footnote style for this document was set on + the command line, which overrides any other settings. */ +int footnote_style_preset = 0; + +/* The current footnote number in this node. Each time a new node is + started this is reset to 1. */ +int current_footnote_number = 1; + +/* Nonzero means we automatically number footnotes with no specified marker. */ +int number_footnotes = 1; + +/* Nonzero means we are currently outputting footnotes. */ +int already_outputting_pending_notes = 0; + + +/* Footnotes can be handled in one of two ways: + + separate_node: + Make them look like followed references, with the reference + destinations in a makeinfo manufactured node or, + end_node: + Make them appear at the bottom of the node that they originally + appeared in. */ + +#define separate_node 0 +#define end_node 1 + +int footnote_style = end_node; +int first_footnote_this_node = 1; +int footnote_count = 0; + +/* Set the footnote style based on the style identifier in STRING. */ +int +set_footnote_style (string) + char *string; +{ + if (strcasecmp (string, "separate") == 0) + footnote_style = separate_node; + else if (strcasecmp (string, "end") == 0) + footnote_style = end_node; + else + return -1; + + return 0; +} + +void +cm_footnotestyle () +{ + char *arg; + + get_rest_of_line (1, &arg); + + /* If set on command line, do not change the footnote style. */ + if (!footnote_style_preset && set_footnote_style (arg) != 0) + line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command); + + free (arg); +} + +typedef struct fn +{ + struct fn *next; + char *marker; + char *note; + int number; +} FN; + +FN *pending_notes = NULL; + +/* A method for remembering footnotes. Note that this list gets output + at the end of the current node. */ +void +remember_note (marker, note) + char *marker, *note; +{ + FN *temp = xmalloc (sizeof (FN)); + + temp->marker = xstrdup (marker); + temp->note = xstrdup (note); + temp->next = pending_notes; + temp->number = current_footnote_number; + pending_notes = temp; + footnote_count++; +} + +/* How to get rid of existing footnotes. */ +static void +free_pending_notes () +{ + FN *temp; + + while ((temp = pending_notes)) + { + free (temp->marker); + free (temp->note); + pending_notes = pending_notes->next; + free (temp); + } + first_footnote_this_node = 1; + footnote_count = 0; + current_footnote_number = 1; /* for html */ +} + +/* What to do when you see a @footnote construct. */ + + /* Handle a "footnote". + footnote *{this is a footnote} + where "*" is the (optional) marker character for this note. */ +void +cm_footnote () +{ + char *marker; + char *note; + + get_until ("{", &marker); + canon_white (marker); + + if (macro_expansion_output_stream && !executing_string) + append_to_expansion_output (input_text_offset + 1); /* include the { */ + + /* Read the argument in braces. */ + if (curchar () != '{') + { + line_error (_("`%c%s' needs an argument `{...}', not just `%s'"), + COMMAND_PREFIX, command, marker); + free (marker); + return; + } + else + { + int len; + int braces = 1; + int loc = ++input_text_offset; + + while (braces) + { + if (loc == input_text_length) + { + line_error (_("No closing brace for footnote `%s'"), marker); + return; + } + + if (input_text[loc] == '{') + braces++; + else if (input_text[loc] == '}') + braces--; + else if (input_text[loc] == '\n') + line_number++; + + loc++; + } + + len = (loc - input_text_offset) - 1; + note = xmalloc (len + 1); + memcpy (note, &input_text[input_text_offset], len); + note[len] = 0; + input_text_offset = loc; + } + + /* Must write the macro-expanded argument to the macro expansion + output stream. This is like the case in index_add_arg. */ + if (macro_expansion_output_stream && !executing_string) + { + /* Calling me_execute_string on a lone } provokes an error, since + as far as the reader knows there is no matching {. We wrote + the { above in the call to append_to_expansion_output. */ + me_execute_string_keep_state (note, "}"); + } + + if (!current_node || !*current_node) + { + line_error (_("Footnote defined without parent node")); + free (marker); + free (note); + return; + } + + if (!*marker) + { + free (marker); + + if (number_footnotes) + { + marker = xmalloc (10); + sprintf (marker, "%d", current_footnote_number); + } + else + marker = xstrdup ("*"); + } + + remember_note (marker, note); + + /* fixme: html: footnote processing needs work; we currently ignore + the style requested; we could clash with a node name of the form + `fn-<n>', though that's unlikely. */ + if (html) + add_word_args ("<a rel=footnote href=\"#fn-%d\"><sup>%s</sup></a>", + current_footnote_number, marker); + else + /* Your method should at least insert MARKER. */ + switch (footnote_style) + { + case separate_node: + add_word_args ("(%s)", marker); + execute_string (" (*note %s-Footnote-%d::)", + current_node, current_footnote_number); + if (first_footnote_this_node) + { + char *temp_string, *expanded_ref; + + temp_string = xmalloc (strlen (current_node) + + strlen ("-Footnotes") + 1); + + strcpy (temp_string, current_node); + strcat (temp_string, "-Footnotes"); + expanded_ref = expansion (temp_string, 0); + remember_node_reference (expanded_ref, line_number, + followed_reference); + free (temp_string); + free (expanded_ref); + first_footnote_this_node = 0; + } + break; + + case end_node: + add_word_args ("(%s)", marker); + break; + + default: + break; + } + current_footnote_number++; + + free (marker); + free (note); +} + +/* Output the footnotes. We are at the end of the current node. */ +void +output_pending_notes () +{ + FN *footnote = pending_notes; + + if (!pending_notes) + return; + + if (html) + { /* The type= attribute is used just in case some weirdo browser + out there doesn't use numbers by default. Since we rely on the + browser to produce the footnote numbers, we need to make sure + they ARE indeed numbers. Pre-HTML4 browsers seem to not care. */ + add_word ("<hr><h4>"); + add_word (_("Footnotes")); + add_word ("</h4>\n<ol type=\"1\">\n"); + } + else + switch (footnote_style) + { + case separate_node: + { + char *old_current_node = current_node; + char *old_command = xstrdup (command); + + already_outputting_pending_notes++; + execute_string ("%cnode %s-Footnotes,,,%s\n", + COMMAND_PREFIX, current_node, current_node); + already_outputting_pending_notes--; + current_node = old_current_node; + free (command); + command = old_command; + } + break; + + case end_node: + close_paragraph (); + in_fixed_width_font++; + /* This string should be translated according to the + @documentlanguage, not the current LANG. We can't do that + yet, so leave it in English. */ + execute_string ("---------- Footnotes ----------\n\n"); + in_fixed_width_font--; + break; + } + + /* Handle the footnotes in reverse order. */ + { + FN **array = xmalloc ((footnote_count + 1) * sizeof (FN *)); + array[footnote_count] = NULL; + + while (--footnote_count > -1) + { + array[footnote_count] = footnote; + footnote = footnote->next; + } + + filling_enabled = 1; + indented_fill = 1; + + while ((footnote = array[++footnote_count])) + { + if (html) + { + /* Make the text of every footnote begin a separate paragraph. */ + add_word_args ("<li><a name=\"fn-%d\"></a>\n<p>", + footnote->number); + execute_string ("%s", footnote->note); + add_word ("</p>\n"); + } + else + { + char *old_current_node = current_node; + char *old_command = xstrdup (command); + + already_outputting_pending_notes++; + execute_string ("%canchor{%s-Footnote-%d}(%s) %s", + COMMAND_PREFIX, current_node, footnote->number, + footnote->marker, footnote->note); + already_outputting_pending_notes--; + current_node = old_current_node; + free (command); + command = old_command; + } + + close_paragraph (); + } + + if (html) + add_word ("</ol><hr>"); + close_paragraph (); + free (array); + } + + free_pending_notes (); +} diff --git a/gnu/usr.bin/texinfo/makeinfo/footnote.h b/gnu/usr.bin/texinfo/makeinfo/footnote.h new file mode 100644 index 00000000000..b5edb509ade --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/footnote.h @@ -0,0 +1,37 @@ +/* footnote.h -- declarations for footnote.c. + $Id: footnote.h,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $ + + Copyright (C) 1998 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FOOTNOTE_H +#define FOOTNOTE_H + +extern int footnote_style_preset; +extern int current_footnote_number; +extern int number_footnotes; +extern int already_outputting_pending_notes; + +/* The Texinfo @commands. */ +extern void cm_footnote (); +extern void cm_footnotestyle (); + +extern int set_footnote_style (); /* called for -s option */ + +extern void output_pending_notes (); /* called for output */ + +#endif /* !FOOTNOTE_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/html.c b/gnu/usr.bin/texinfo/makeinfo/html.c new file mode 100644 index 00000000000..931327a7ed7 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/html.c @@ -0,0 +1,182 @@ +/* html.c -- html-related utilities. + $Id: html.c,v 1.1 2000/02/09 01:25:11 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "cmds.h" +#include "html.h" +#include "lang.h" +#include "makeinfo.h" +#include "sectioning.h" + +/* See html.h. */ +int html_output_head_p = 0; + +void +html_output_head () +{ + char *html_title; + + if (html_output_head_p) + return; + html_output_head_p = 1; + + /* The <title> should not have markup. */ + html_title = title ? text_expansion (title) : _("Untitled"); + + add_word_args ("<html lang=\"%s\"><head>\n<title>%s</title>\n", + language_table[language_code].abbrev, html_title); + + add_word ("<meta http-equiv=\"Content-Type\" content=\"text/html"); + if (document_encoding) + add_word_args ("; charset=%s", document_encoding); + add_word ("\">\n"); + + add_word_args ("<meta name=description content=\"%s\">\n", html_title); + add_word_args ("<meta name=generator content=\"makeinfo %s\">\n", VERSION); + add_word ("<link href=\"http://texinfo.org/\" rel=generator-home>\n"); + add_word ("</head><body>\n\n"); +} + + +/* Escape HTML special characters in the string if necessary, + returning a pointer to a possibly newly-allocated one. */ +char * +escape_string (string) + char * string; +{ + int i=0, newlen=0; + char * newstring; + + do + { + /* Find how much to allocate. */ + switch (string[i]) + { + case '&': + newlen += 5; /* `&' */ + 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/gnu/usr.bin/texinfo/makeinfo/html.h b/gnu/usr.bin/texinfo/makeinfo/html.h new file mode 100644 index 00000000000..54305b82082 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/html.h @@ -0,0 +1,44 @@ +/* html.h -- declarations for html-related utilities. + $Id: html.h,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef HTML_H +#define HTML_H + +/* Nonzero if we have output the <head>. */ +extern int html_output_head_p; + +/* Perform the <head> output. */ +extern void html_output_head (); + +/* Escape &<>. */ +extern char *escape_string (/* char * */); + +/* Open or close TAG according to START_OR_END. */ +extern void insert_html_tag (/* int start_or_end, char *tag */); + +/* Output HTML <link> to NODE, plus extra ATTRIBUTES. */ +extern void add_link (/* char *node, char *attributes */); + +/* Escape URL-special characters as %xy. */ +extern void add_escaped_anchor_name (/* char *name */); + +/* See html.c. */ +extern void add_anchor_name (/* nodename, href */); + +#endif /* !HTML_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/index.c b/gnu/usr.bin/texinfo/makeinfo/index.c new file mode 100644 index 00000000000..72442efa1ef --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/index.c @@ -0,0 +1,823 @@ +/* index.c -- indexing for Texinfo. + $Id: index.c,v 1.1.1.1 2000/02/09 01:25:16 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "index.h" +#include "lang.h" +#include "macro.h" +#include "toc.h" + +/* An index element... */ +typedef struct index_elt +{ + struct index_elt *next; + char *entry; /* The index entry itself, after expansion. */ + char *entry_text; /* The original, non-expanded entry text. */ + char *node; /* The node from whence it came. */ + int code; /* Nonzero means add `@code{...}' when + printing this element. */ + int defining_line; /* Line number where this entry was written. */ + char *defining_file; /* Source file for defining_line. */ +} INDEX_ELT; + + +/* A list of short-names for each index. + There are two indices into the the_indices array. + * read_index is the index that points to the list of index + entries that we will find if we ask for the list of entries for + this name. + * write_index is the index that points to the list of index entries + that we will add new entries to. + + Initially, read_index and write_index are the same, but the + @syncodeindex and @synindex commands can change the list we add + entries to. + + For example, after the commands + @cindex foo + @defindex ii + @synindex cp ii + @cindex bar + + the cp index will contain the entry `foo', and the new ii + index will contain the entry `bar'. This is consistent with the + way texinfo.tex handles the same situation. + + In addition, for each index, it is remembered whether that index is + a code index or not. Code indices have @code{} inserted around the + first word when they are printed with printindex. */ +typedef struct +{ + char *name; + int read_index; /* index entries for `name' */ + int write_index; /* store index entries here, @synindex can change it */ + int code; +} INDEX_ALIST; + +INDEX_ALIST **name_index_alist = NULL; + +/* An array of pointers. Each one is for a different index. The + "synindex" command changes which array slot is pointed to by a + given "index". */ +INDEX_ELT **the_indices = NULL; + +/* The number of defined indices. */ +int defined_indices = 0; + +/* Stuff for defining commands on the fly. */ +COMMAND **user_command_array = NULL; +int user_command_array_len = 0; + +/* How to compare index entries for sorting. May be set to strcoll. */ +int (*index_compare_fn) () = strcasecmp; + +/* Find which element in the known list of indices has this name. + Returns -1 if NAME isn't found. */ +static int +find_index_offset (name) + char *name; +{ + int i; + for (i = 0; i < defined_indices; i++) + if (name_index_alist[i] && STREQ (name, name_index_alist[i]->name)) + return i; + return -1; +} + +/* Return a pointer to the entry of (name . index) for this name. + Return NULL if the index doesn't exist. */ +INDEX_ALIST * +find_index (name) + char *name; +{ + int offset = find_index_offset (name); + if (offset > -1) + return name_index_alist[offset]; + else + return NULL; +} + +/* User-defined commands, which happens only from user-defined indexes. + Used to initialize the builtin indices, too. */ +void +define_user_command (name, proc, needs_braces_p) + char *name; + COMMAND_FUNCTION *proc; + int needs_braces_p; +{ + int slot = user_command_array_len; + user_command_array_len++; + + if (!user_command_array) + user_command_array = xmalloc (1 * sizeof (COMMAND *)); + + user_command_array = xrealloc (user_command_array, + (1 + user_command_array_len) * sizeof (COMMAND *)); + + user_command_array[slot] = xmalloc (sizeof (COMMAND)); + user_command_array[slot]->name = xstrdup (name); + user_command_array[slot]->proc = proc; + user_command_array[slot]->argument_in_braces = needs_braces_p; +} + +/* Please release me, let me go... */ +static void +free_index (index) + INDEX_ELT *index; +{ + INDEX_ELT *temp; + + while ((temp = index)) + { + free (temp->entry); + free (temp->entry_text); + /* Do not free the node, because we already freed the tag table, + which freed all the node names. */ + /* free (temp->node); */ + index = index->next; + free (temp); + } +} + +/* Flush an index by name. This will delete the list of entries that + would be written by a @printindex command for this index. */ +static void +undefindex (name) + char *name; +{ + int i; + int which = find_index_offset (name); + + /* The index might have already been freed if this was the target of + an @synindex. */ + if (which < 0 || !name_index_alist[which]) + return; + + i = name_index_alist[which]->read_index; + + free_index (the_indices[i]); + the_indices[i] = NULL; + + free (name_index_alist[which]->name); + free (name_index_alist[which]); + name_index_alist[which] = NULL; +} + +/* Add the arguments to the current index command to the index NAME. + html fixxme generate specific html anchor */ +static void +index_add_arg (name) + char *name; +{ + int which; + char *index_entry; + INDEX_ALIST *tem; + + tem = find_index (name); + + which = tem ? tem->write_index : -1; + + if (macro_expansion_output_stream && !executing_string) + append_to_expansion_output (input_text_offset + 1); + + get_rest_of_line (0, &index_entry); + ignore_blank_line (); + + if (macro_expansion_output_stream && !executing_string) + { + char *index_line = xmalloc (strlen (index_entry) + 2); + sprintf (index_line, "%s\n", index_entry); + me_execute_string_keep_state (index_line, NULL); + free (index_line); + } + + if (which < 0) + { + line_error (_("Unknown index `%s'"), name); + free (index_entry); + } + else + { + INDEX_ELT *new = xmalloc (sizeof (INDEX_ELT)); + new->next = the_indices[which]; + new->entry_text = index_entry; + new->entry = NULL; + new->node = current_node ? current_node : xstrdup (""); + new->code = tem->code; + new->defining_line = line_number - 1; + /* We need to make a copy since input_filename may point to + something that goes away, for example, inside a macro. + (see the findexerr test). */ + new->defining_file = xstrdup (input_filename); + the_indices[which] = new; + } +} + +/* The function which user defined index commands call. */ +static void +gen_index () +{ + char *name = xstrdup (command); + if (strlen (name) >= strlen ("index")) + name[strlen (name) - strlen ("index")] = 0; + index_add_arg (name); + free (name); +} + +/* Define an index known as NAME. We assign the slot number. + If CODE is nonzero, make this a code index. */ +static void +defindex (name, code) + char *name; + int code; +{ + int i, slot; + + /* If it already exists, flush it. */ + undefindex (name); + + /* Try to find an empty slot. */ + slot = -1; + for (i = 0; i < defined_indices; i++) + if (!name_index_alist[i]) + { + slot = i; + break; + } + + if (slot < 0) + { /* No such luck. Make space for another index. */ + slot = defined_indices; + defined_indices++; + + name_index_alist = (INDEX_ALIST **) + xrealloc (name_index_alist, (1 + defined_indices) + * sizeof (INDEX_ALIST *)); + the_indices = (INDEX_ELT **) + xrealloc (the_indices, (1 + defined_indices) * sizeof (INDEX_ELT *)); + } + + /* We have a slot. Start assigning. */ + name_index_alist[slot] = xmalloc (sizeof (INDEX_ALIST)); + name_index_alist[slot]->name = xstrdup (name); + name_index_alist[slot]->read_index = slot; + name_index_alist[slot]->write_index = slot; + name_index_alist[slot]->code = code; + + the_indices[slot] = NULL; +} + +/* Define an index NAME, implicitly @code if CODE is nonzero. */ +static void +top_defindex (name, code) + char *name; + int code; +{ + char *temp; + + temp = xmalloc (1 + strlen (name) + strlen ("index")); + sprintf (temp, "%sindex", name); + define_user_command (temp, gen_index, 0); + defindex (name, code); + free (temp); +} + +/* Set up predefined indices. */ +void +init_indices () +{ + int i; + + /* Create the default data structures. */ + + /* Initialize data space. */ + if (!the_indices) + { + the_indices = xmalloc ((1 + defined_indices) * sizeof (INDEX_ELT *)); + the_indices[defined_indices] = NULL; + + name_index_alist = xmalloc ((1 + defined_indices) + * sizeof (INDEX_ALIST *)); + name_index_alist[defined_indices] = NULL; + } + + /* If there were existing indices, get rid of them now. */ + for (i = 0; i < defined_indices; i++) + { + undefindex (name_index_alist[i]->name); + if (name_index_alist[i]) + { /* Suppose we're called with two input files, and the first + does a @synindex pg cp. Then, when we get here to start + the second file, the "pg" element won't get freed by + undefindex (because it's pointing to "cp"). So free it + here; otherwise, when we try to define the pg index again + just below, it will still point to cp. */ + free (name_index_alist[i]->name); + free (name_index_alist[i]); + name_index_alist[i] = NULL; + } + } + + /* Add the default indices. */ + top_defindex ("cp", 0); /* cp is the only non-code index. */ + top_defindex ("fn", 1); + top_defindex ("ky", 1); + top_defindex ("pg", 1); + top_defindex ("tp", 1); + top_defindex ("vr", 1); +} + +/* Given an index name, return the offset in the_indices of this index, + or -1 if there is no such index. */ +int +translate_index (name) + char *name; +{ + INDEX_ALIST *which = find_index (name); + + if (which) + return which->read_index; + else + return -1; +} + +/* Return the index list which belongs to NAME. */ +INDEX_ELT * +index_list (name) + char *name; +{ + int which = translate_index (name); + if (which < 0) + return (INDEX_ELT *) -1; + else + return the_indices[which]; +} + +/* Define a new index command. Arg is name of index. */ +static void +gen_defindex (code) + int code; +{ + char *name; + get_rest_of_line (0, &name); + + if (find_index (name)) + { + line_error (_("Index `%s' already exists"), name); + } + else + { + char *temp = xmalloc (strlen (name) + sizeof ("index")); + sprintf (temp, "%sindex", name); + define_user_command (temp, gen_index, 0); + defindex (name, code); + free (temp); + } + + free (name); +} + +void +cm_defindex () +{ + gen_defindex (0); +} + +void +cm_defcodeindex () +{ + gen_defindex (1); +} + +/* Expects 2 args, on the same line. Both are index abbreviations. + Make the first one be a synonym for the second one, i.e. make the + first one have the same index as the second one. */ +void +cm_synindex () +{ + int source, target; + char *abbrev1, *abbrev2; + + skip_whitespace (); + get_until_in_line (0, " ", &abbrev1); + target = find_index_offset (abbrev1); + skip_whitespace (); + get_until_in_line (0, " ", &abbrev2); + source = find_index_offset (abbrev2); + if (source < 0 || target < 0) + { + line_error (_("Unknown index `%s' and/or `%s' in @synindex"), + abbrev1, abbrev2); + } + else + { + name_index_alist[target]->write_index + = name_index_alist[source]->write_index; + } + + free (abbrev1); + free (abbrev2); +} + +void +cm_pindex () /* Pinhead index. */ +{ + index_add_arg ("pg"); +} + +void +cm_vindex () /* Variable index. */ +{ + index_add_arg ("vr"); +} + +void +cm_kindex () /* Key index. */ +{ + index_add_arg ("ky"); +} + +void +cm_cindex () /* Concept index. */ +{ + index_add_arg ("cp"); +} + +void +cm_findex () /* Function index. */ +{ + index_add_arg ("fn"); +} + +void +cm_tindex () /* Data Type index. */ +{ + index_add_arg ("tp"); +} + +int +index_element_compare (element1, element2) + INDEX_ELT **element1, **element2; +{ + return index_compare_fn ((*element1)->entry, (*element2)->entry); +} + +/* Force all index entries to be unique. */ +void +make_index_entries_unique (array, count) + INDEX_ELT **array; + int count; +{ + int i, j; + INDEX_ELT **copy; + int counter = 1; + + copy = xmalloc ((1 + count) * sizeof (INDEX_ELT *)); + + for (i = 0, j = 0; i < count; i++) + { + if (i == (count - 1) + || array[i]->node != array[i + 1]->node + || !STREQ (array[i]->entry, array[i + 1]->entry)) + copy[j++] = array[i]; + else + { + free (array[i]->entry); + free (array[i]->entry_text); + free (array[i]); + } + } + copy[j] = NULL; + + /* Now COPY contains only unique entries. Duplicated entries in the + original array have been freed. Replace the current array with + the copy, fixing the NEXT pointers. */ + for (i = 0; copy[i]; i++) + { + copy[i]->next = copy[i + 1]; + + /* Fix entry names which are the same. They point to different nodes, + so we make the entry name unique. */ + if (copy[i+1] + && STREQ (copy[i]->entry, copy[i + 1]->entry) + && !html) + { + char *new_entry_name; + + new_entry_name = xmalloc (10 + strlen (copy[i]->entry)); + sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter); + free (copy[i]->entry); + copy[i]->entry = new_entry_name; + counter++; + } + else + counter = 1; + + array[i] = copy[i]; + } + array[i] = NULL; + + /* Free the storage used only by COPY. */ + free (copy); +} + +/* Sort the index passed in INDEX, returning an array of + pointers to elements. The array is terminated with a NULL + pointer. We call qsort because it's supposed to be fast. + I think this looks bad. */ +INDEX_ELT ** +sort_index (index) + INDEX_ELT *index; +{ + INDEX_ELT **array; + INDEX_ELT *temp = index; + int count = 0; + int save_line_number = line_number; + char *save_input_filename = input_filename; + int save_html = html; + + /* Pretend we are in non-HTML mode, for the purpose of getting the + expanded index entry that lacks any markup and other HTML escape + characters which could produce a wrong sort order. */ + /* fixme: html: this still causes some markup, such as non-ASCII + characters @AE{} etc., to sort incorrectly. */ + html = 0; + + while (temp) + { + count++; + temp = temp->next; + } + + /* We have the length. Make an array. */ + + array = xmalloc ((count + 1) * sizeof (INDEX_ELT *)); + count = 0; + temp = index; + + while (temp) + { + array[count++] = temp; + + /* Set line number and input filename to the source line for this + index entry, as this expansion finds any errors. */ + line_number = array[count - 1]->defining_line; + input_filename = array[count - 1]->defining_file; + + /* If this particular entry should be printed as a "code" index, + then expand it as @code{entry}, i.e. as in fixed-width font. */ + array[count-1]->entry = expansion (temp->entry_text, + array[count-1]->code); + + temp = temp->next; + } + array[count] = NULL; /* terminate the array. */ + line_number = save_line_number; + input_filename = save_input_filename; + html = save_html; + +#ifdef HAVE_STRCOLL + /* This is not perfect. We should set (then restore) the locale to the + documentlanguage, so strcoll operates according to the document's + locale, not the user's. For now, I'm just going to assume that + those few new documents which use @documentlanguage will be + processed in the appropriate locale. In any case, don't use + strcoll in the C (aka POSIX) locale, that is the ASCII ordering. */ + if (language_code != en) + { + char *lang_env = getenv ("LANG"); + if (lang_env && !STREQ (lang_env, "C") && !STREQ (lang_env, "POSIX")) + index_compare_fn = strcoll; + } +#endif /* HAVE_STRCOLL */ + + /* Sort the array. */ + qsort (array, count, sizeof (INDEX_ELT *), index_element_compare); + make_index_entries_unique (array, count); + return array; +} + +/* Nonzero means that we are in the middle of printing an index. */ +int printing_index = 0; + +/* Takes one arg, a short name of an index to print. + Outputs a menu of the sorted elements of the index. */ +void +cm_printindex () +{ + int item; + INDEX_ELT *index; + INDEX_ELT *last_index = 0; + INDEX_ELT **array; + char *index_name; + unsigned line_length; + char *line; + int saved_inhibit_paragraph_indentation = inhibit_paragraph_indentation; + int saved_filling_enabled = filling_enabled; + int saved_line_number = line_number; + char *saved_input_filename = input_filename; + + close_paragraph (); + get_rest_of_line (0, &index_name); + + index = index_list (index_name); + if (index == (INDEX_ELT *)-1) + { + line_error (_("Unknown index `%s' in @printindex"), index_name); + free (index_name); + return; + } + + /* Do this before sorting, so execute_string in index_element_compare + will give the same results as when we actually print. */ + printing_index = 1; + filling_enabled = 0; + inhibit_paragraph_indentation = 1; + array = sort_index (index); + + close_paragraph (); + if (html) + add_word ("<ul compact>"); + else if (!no_headers) + add_word ("* Menu:\n\n"); + + me_inhibit_expansion++; + + /* This will probably be enough. */ + line_length = 100; + line = xmalloc (line_length); + + for (item = 0; (index = array[item]); item++) + { + /* A pathological document might have an index entry outside of any + node. Don't crash; try using the section name instead. */ + char *index_node = index->node; + + line_number = index->defining_line; + input_filename = index->defining_file; + + if ((!index_node || !*index_node) && html) + index_node = toc_find_section_of_node (index_node); + + if (!index_node || !*index_node) + { + line_error (_("Entry for index `%s' outside of any node"), + index_name); + if (html || !no_headers) + index_node = _("(outside of any node)"); + } + + if (html) + /* fixme: html: we should use specific index anchors pointing + to the actual location of the indexed position (but then we + have to find something to wrap the anchor around). */ + { + if (last_index + && STREQ (last_index->entry_text, index->entry_text)) + add_word (", "); /* Don't repeat the previous entry. */ + else + { + /* In the HTML case, the expanded index entry is not + good for us, since it was expanded for non-HTML mode + inside sort_index. So we need to HTML-escape and + expand the original entry text here. */ + char *escaped_entry = xstrdup (index->entry_text); + char *expanded_entry; + + /* expansion() doesn't HTML-escape the argument, so need + to do it separately. */ + escaped_entry = escape_string (escaped_entry); + expanded_entry = expansion (escaped_entry, index->code); + add_word_args ("\n<li>%s: ", expanded_entry); + free (escaped_entry); + free (expanded_entry); + } + add_word ("<a href=\""); + if (index->node && *index->node) + { + /* Make sure any non-macros in the node name are expanded. */ + in_fixed_width_font++; + index_node = expansion (index_node, 0); + in_fixed_width_font--; + add_anchor_name (index_node, 1); + add_word_args ("\">%s</a>", index_node); + free (index_node); + } + else if (STREQ (index_node, _("(outside of any node)"))) + { + add_anchor_name (index_node, 1); + add_word_args ("\">%s</a>", index_node); + } + else + /* If we use the section instead of the (missing) node, then + index_node already includes all we need except the #. */ + add_word_args ("#%s</a>", index_node); + } + else + { + unsigned new_length = strlen (index->entry); + + if (new_length < 50) /* minimum length used below */ + new_length = 50; + new_length += strlen (index_node) + 7; /* * : .\n\0 */ + + if (new_length > line_length) + { + line_length = new_length; + line = xrealloc (line, line_length); + } + /* Print the entry, nicely formatted. We've already + expanded any commands in index->entry, including any + implicit @code. Thus, can't call execute_string, since + @@ has turned into @. */ + if (!no_headers) + { + sprintf (line, "* %-37s ", index->entry); + line[2 + strlen (index->entry)] = ':'; + insert_string (line); + /* Make sure any non-macros in the node name are expanded. */ + in_fixed_width_font++; + execute_string ("%s.\n", index_node); + in_fixed_width_font--; + } + else + { + /* With --no-headers, the @node lines are gone, so + there's little sense in referring to them in the + index. Instead, output the number or name of the + section that corresponds to that node. */ + char *section_name = toc_find_section_of_node (index_node); + + sprintf (line, "%-*s ", number_sections ? 50 : 1, index->entry); + line[strlen (index->entry)] = ':'; + insert_string (line); + if (section_name) + { + int idx = 0; + unsigned ref_len = strlen (section_name) + 30; + + if (ref_len > line_length) + { + line_length = ref_len; + line = xrealloc (line, line_length); + } + + if (number_sections) + { + while (section_name[idx] + && (isdigit (section_name[idx]) + || (idx && section_name[idx] == '.'))) + idx++; + } + if (idx) + sprintf (line, " See %.*s.\n", idx, section_name); + else + sprintf (line, "\n See ``%s''.\n", section_name); + insert_string (line); + } + else + { + insert_string (" "); /* force a blank */ + execute_string ("See node %s.\n", index_node); + } + } + } + + /* Prevent `output_paragraph' from growing to the size of the + whole index. */ + flush_output (); + last_index = index; + } + + free (line); + free (index_name); + + me_inhibit_expansion--; + + printing_index = 0; + free (array); + close_single_paragraph (); + filling_enabled = saved_filling_enabled; + inhibit_paragraph_indentation = saved_inhibit_paragraph_indentation; + input_filename = saved_input_filename; + line_number = saved_line_number; + + if (html) + add_word ("</ul>"); +} diff --git a/gnu/usr.bin/texinfo/makeinfo/index.h b/gnu/usr.bin/texinfo/makeinfo/index.h new file mode 100644 index 00000000000..0d155125310 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/index.h @@ -0,0 +1,36 @@ +/* index.h -- declarations for index.c. + $Id: index.h,v 1.1.1.1 2000/02/09 01:25:16 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef INDEX_H +#define INDEX_H + +#include "makeinfo.h" +#include "cmds.h" + +/* User commands are only new indices. (Macros are handled separately.) */ +extern COMMAND **user_command_array; +extern int user_command_array_len; + +/* Initialize all indices. */ +extern void init_indices (); + +/* Function to compare index entries for sorting. */ +extern int (*index_compare_fn) (); + +#endif /* !INDEX_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/insertion.c b/gnu/usr.bin/texinfo/makeinfo/insertion.c new file mode 100644 index 00000000000..218b892ffa7 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/insertion.c @@ -0,0 +1,1368 @@ +/* insertion.c -- insertions for Texinfo. + $Id: insertion.c,v 1.1 2000/02/09 01:25:19 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "cmds.h" +#include "defun.h" +#include "insertion.h" +#include "macro.h" +#include "makeinfo.h" + +/* Must match list in insertion.h. */ +static char *insertion_type_names[] = +{ + "cartouche", "defcv", "deffn", "defivar", "defmac", "defmethod", + "defop", "defopt", "defspec", "deftp", "deftypefn", "deftypefun", + "deftypeivar", "deftypemethod", "deftypeop", "deftypevar", + "deftypevr", "defun", "defvar", "defvr", "detailmenu", "direntry", + "display", "enumerate", "example", "flushleft", "flushright", + "format", "ftable", "group", "ifclear", "ifhtml", "ifinfo", + "ifnothtml", "ifnotinfo", "ifnottex", "ifset", "iftex", "itemize", + "lisp", "menu", "multitable", "quotation", "rawhtml", "rawtex", + "smalldisplay", "smallexample", "smallformat", "smalllisp", "table", + "tex", "vtable", "bad_type" +}; + +/* All nested environments. */ +INSERTION_ELT *insertion_stack = NULL; + +/* How deeply we're nested. */ +int insertion_level = 0; + +/* Whether to examine menu lines. */ +int in_menu = 0; + +/* How to examine menu lines. */ +int in_detailmenu = 0; + +/* Set to 1 if we've processed (commentary) text in a @menu that + wasn't part of a menu item. */ +int had_menu_commentary; + +/* Set to 1 if <p> is written in normal context. + Used for menu and itemize. */ +int in_paragraph = 0; + +static const char dl_tag[] = "<dl>\n"; + +void +init_insertion_stack () +{ + insertion_stack = NULL; +} + +/* Return the type of the current insertion. */ +static enum insertion_type +current_insertion_type () +{ + return insertion_level ? insertion_stack->insertion : bad_type; +} + +/* Return the string which is the function to wrap around items, or NULL + if we're not in an environment where @item is ok. */ +static char * +current_item_function () +{ + int done = 0; + INSERTION_ELT *elt = insertion_stack; + + /* Skip down through the stack until we find an insertion with an + itemize function defined, i.e., skip conditionals, @cartouche, etc. */ + while (!done && elt) + { + switch (elt->insertion) + { + /* This list should match the one in cm_item. */ + case ifclear: + case ifhtml: + case ifinfo: + case ifnothtml: + case ifnotinfo: + case ifnottex: + case ifset: + case iftex: + case rawhtml: + case rawtex: + case tex: + case cartouche: + elt = elt->next; + break; + + default: + done = 1; + } + } + + /* item_function usually gets assigned the empty string. */ + return done && (*elt->item_function) ? elt->item_function : NULL; +} + +/* Parse the item marker function off the input. If result is just "@", + change it to "@ ", since "@" by itself is not a command. This makes + "@ ", "@\t", and "@\n" all the same, but their default meanings are + the same anyway, and let's not worry about supporting redefining them. */ +char * +get_item_function () +{ + char *item_function; + get_rest_of_line (0, &item_function); + + /* If we hit the end of text in get_rest_of_line, backing up + input pointer will cause the last character of the last line + be pushed back onto the input, which is wrong. */ + if (input_text_offset < input_text_length) + backup_input_pointer (); + + if (STREQ (item_function, "@")) + { + free (item_function); + item_function = xstrdup ("@ "); + } + + return item_function; +} + + /* Push the state of the current insertion on the stack. */ +void +push_insertion (type, item_function) + enum insertion_type type; + char *item_function; +{ + INSERTION_ELT *new = xmalloc (sizeof (INSERTION_ELT)); + + new->item_function = item_function; + new->filling_enabled = filling_enabled; + new->indented_fill = indented_fill; + new->insertion = type; + new->line_number = line_number; + new->filename = xstrdup (input_filename); + new->inhibited = inhibit_paragraph_indentation; + new->in_fixed_width_font = in_fixed_width_font; + new->next = insertion_stack; + insertion_stack = new; + insertion_level++; +} + + /* Pop the value on top of the insertion stack into the + global variables. */ +void +pop_insertion () +{ + INSERTION_ELT *temp = insertion_stack; + + if (temp == NULL) + return; + + in_fixed_width_font = temp->in_fixed_width_font; + inhibit_paragraph_indentation = temp->inhibited; + filling_enabled = temp->filling_enabled; + indented_fill = temp->indented_fill; + free_and_clear (&(temp->item_function)); + free_and_clear (&(temp->filename)); + insertion_stack = insertion_stack->next; + free (temp); + insertion_level--; +} + + /* Return a pointer to the print name of this + enumerated type. */ +char * +insertion_type_pname (type) + enum insertion_type type; +{ + if ((int) type < (int) bad_type) + return insertion_type_names[(int) type]; + else + return _("Broken-Type in insertion_type_pname"); +} + +/* Return the insertion_type associated with NAME. + If the type is not one of the known ones, return BAD_TYPE. */ +enum insertion_type +find_type_from_name (name) + char *name; +{ + int index = 0; + while (index < (int) bad_type) + { + if (STREQ (name, insertion_type_names[index])) + return (enum insertion_type) index; + if (index == rawhtml && STREQ (name, "html")) + return rawhtml; + if (index == rawtex && STREQ (name, "tex")) + return rawtex; + index++; + } + return bad_type; +} + +int +defun_insertion (type) + enum insertion_type type; +{ + return 0 + || (type == defcv) + || (type == deffn) + || (type == defivar) + || (type == defmac) + || (type == defmethod) + || (type == defop) + || (type == defopt) + || (type == defspec) + || (type == deftp) + || (type == deftypefn) + || (type == deftypefun) + || (type == deftypeivar) + || (type == deftypemethod) + || (type == deftypeop) + || (type == deftypevar) + || (type == deftypevr) + || (type == defun) + || (type == defvar) + || (type == defvr) + ; +} + +/* MAX_NS is the maximum nesting level for enumerations. I picked 100 + which seemed reasonable. This doesn't control the number of items, + just the number of nested lists. */ +#define max_stack_depth 100 +#define ENUM_DIGITS 1 +#define ENUM_ALPHA 2 +typedef struct { + int enumtype; + int enumval; +} DIGIT_ALPHA; + +DIGIT_ALPHA enumstack[max_stack_depth]; +int enumstack_offset = 0; +int current_enumval = 1; +int current_enumtype = ENUM_DIGITS; +char *enumeration_arg = NULL; + +void +start_enumerating (at, type) + int at, type; +{ + if ((enumstack_offset + 1) == max_stack_depth) + { + line_error (_("Enumeration stack overflow")); + return; + } + enumstack[enumstack_offset].enumtype = current_enumtype; + enumstack[enumstack_offset].enumval = current_enumval; + enumstack_offset++; + current_enumval = at; + current_enumtype = type; +} + +void +stop_enumerating () +{ + --enumstack_offset; + if (enumstack_offset < 0) + enumstack_offset = 0; + + current_enumval = enumstack[enumstack_offset].enumval; + current_enumtype = enumstack[enumstack_offset].enumtype; +} + +/* Place a letter or digits into the output stream. */ +void +enumerate_item () +{ + char temp[10]; + + if (current_enumtype == ENUM_ALPHA) + { + if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1)) + { + current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A'); + warning (_("lettering overflow, restarting at %c"), current_enumval); + } + sprintf (temp, "%c. ", current_enumval); + } + else + sprintf (temp, "%d. ", current_enumval); + + indent (output_column += (current_indent - strlen (temp))); + add_word (temp); + current_enumval++; +} + +static void +enum_html () +{ + char type; + int start; + + if (isdigit (*enumeration_arg)) + { + type = '1'; + start = atoi (enumeration_arg); + } + else if (isupper (*enumeration_arg)) + { + type = 'A'; + start = *enumeration_arg - 'A' + 1; + } + else + { + type = 'a'; + start = *enumeration_arg - 'a' + 1; + } + + add_word_args ("<ol type=%c start=%d>\n", type, start); +} + +/* Conditionally parse based on the current command name. */ +void +command_name_condition () +{ + char *discarder = xmalloc (8 + strlen (command)); + + sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command); + discard_until (discarder); + discard_until ("\n"); + + free (discarder); +} + +/* This is where the work for all the "insertion" style + commands is done. A huge switch statement handles the + various setups, and generic code is on both sides. */ +void +begin_insertion (type) + enum insertion_type type; +{ + int no_discard = 0; + + if (defun_insertion (type)) + { + push_insertion (type, xstrdup ("")); + no_discard++; + } + else + push_insertion (type, get_item_function ()); + + switch (type) + { + case menu: + if (!no_headers) + close_paragraph (); + + filling_enabled = no_indent = 0; + inhibit_paragraph_indentation = 1; + + if (html) + { + had_menu_commentary = 1; + } + else if (!no_headers) + add_word ("* Menu:\n"); + + in_menu++; + in_fixed_width_font++; + no_discard++; + break; + + case detailmenu: + if (!in_menu) + { + if (!no_headers) + close_paragraph (); + + filling_enabled = no_indent = 0; + inhibit_paragraph_indentation = 1; + + no_discard++; + } + + in_fixed_width_font++; + in_detailmenu++; + break; + + case direntry: + if (html) + command_name_condition (); + else + { + close_single_paragraph (); + filling_enabled = no_indent = 0; + inhibit_paragraph_indentation = 1; + insert_string ("START-INFO-DIR-ENTRY\n"); + } + break; + + case quotation: + /* @quotation does filling (@display doesn't). */ + if (html) + add_word ("<blockquote>\n"); + else + { + close_single_paragraph (); + last_char_was_newline = no_indent = 0; + indented_fill = filling_enabled = 1; + inhibit_paragraph_indentation = 1; + } + current_indent += default_indentation_increment; + break; + + case display: + case smalldisplay: + case example: + case smallexample: + case lisp: + case smalllisp: + /* Like @display but without indentation. */ + case smallformat: + case format: + close_single_paragraph (); + inhibit_paragraph_indentation = 1; + in_fixed_width_font++; + filling_enabled = 0; + last_char_was_newline = 0; + + if (html) + /* Kludge alert: if <pre> is followed by a newline, IE3 + renders an extra blank line before the pre-formatted block. + Other browsers seem to not mind one way or the other. */ + add_word ("<pre>"); + + if (type != format && type != smallformat) + current_indent += default_indentation_increment; + break; + + case multitable: + do_multitable (); + break; + + case table: + case ftable: + case vtable: + case itemize: + close_single_paragraph (); + current_indent += default_indentation_increment; + filling_enabled = indented_fill = 1; +#if defined (INDENT_PARAGRAPHS_IN_TABLE) + inhibit_paragraph_indentation = 0; +#else + inhibit_paragraph_indentation = 1; +#endif /* !INDENT_PARAGRAPHS_IN_TABLE */ + + /* Make things work for losers who forget the itemize syntax. */ + if (type == itemize) + { + if (!(*insertion_stack->item_function)) + { + free (insertion_stack->item_function); + insertion_stack->item_function = xstrdup ("@bullet"); + } + } + + if (!*insertion_stack->item_function) + { + line_error (_("%s requires an argument: the formatter for %citem"), + insertion_type_pname (type), COMMAND_PREFIX); + } + + if (html) + { + if (type == itemize) + { + add_word ("<ul>\n"); + in_paragraph = 0; + } + else + add_word (dl_tag); + } + break; + + case enumerate: + close_single_paragraph (); + no_indent = 0; +#if defined (INDENT_PARAGRAPHS_IN_TABLE) + inhibit_paragraph_indentation = 0; +#else + inhibit_paragraph_indentation = 1; +#endif /* !INDENT_PARAGRAPHS_IN_TABLE */ + + current_indent += default_indentation_increment; + filling_enabled = indented_fill = 1; + + if (html) + enum_html (); + + if (isdigit (*enumeration_arg)) + start_enumerating (atoi (enumeration_arg), ENUM_DIGITS); + else + start_enumerating (*enumeration_arg, ENUM_ALPHA); + break; + + /* @group does nothing special in makeinfo. */ + case group: + /* Only close the paragraph if we are not inside of an + @example-like environment. */ + if (!insertion_stack->next + || (insertion_stack->next->insertion != display + && insertion_stack->next->insertion != smalldisplay + && insertion_stack->next->insertion != example + && insertion_stack->next->insertion != smallexample + && insertion_stack->next->insertion != lisp + && insertion_stack->next->insertion != smalllisp + && insertion_stack->next->insertion != format + && insertion_stack->next->insertion != smallformat + && insertion_stack->next->insertion != flushleft + && insertion_stack->next->insertion != flushright)) + close_single_paragraph (); + break; + + /* Insertions that are no-ops in info, but do something in TeX. */ + case cartouche: + case ifclear: + case ifhtml: + case ifinfo: + case ifnothtml: + case ifnotinfo: + case ifnottex: + case ifset: + case iftex: + case rawtex: + if (in_menu) + no_discard++; + break; + + case rawhtml: + escape_html = 0; + break; + + case defcv: + case deffn: + case defivar: + case defmac: + case defmethod: + case defop: + case defopt: + case defspec: + case deftp: + case deftypefn: + case deftypefun: + case deftypeivar: + case deftypemethod: + case deftypeop: + case deftypevar: + case deftypevr: + case defun: + case defvar: + case defvr: + inhibit_paragraph_indentation = 1; + filling_enabled = indented_fill = 1; + current_indent += default_indentation_increment; + no_indent = 0; + break; + + case flushleft: + close_single_paragraph (); + inhibit_paragraph_indentation = 1; + filling_enabled = indented_fill = no_indent = 0; + break; + + case flushright: + close_single_paragraph (); + filling_enabled = indented_fill = no_indent = 0; + inhibit_paragraph_indentation = 1; + force_flush_right++; + break; + + default: + line_error ("begin_insertion internal error: type=%d", type); + + } + + if (!no_discard) + discard_until ("\n"); +} + +/* Try to end the insertion with the specified TYPE. With a value of + `bad_type', TYPE gets translated to match the value currently on top + of the stack. Otherwise, if TYPE doesn't match the top of the + insertion stack, give error. */ +void +end_insertion (type) + enum insertion_type type; +{ + enum insertion_type temp_type; + + if (!insertion_level) + return; + + temp_type = current_insertion_type (); + + if (type == bad_type) + type = temp_type; + + if (type != temp_type) + { + line_error + (_("`@end' expected `%s', but saw `%s'"), + insertion_type_pname (temp_type), insertion_type_pname (type)); + return; + } + + pop_insertion (); + + switch (type) + { + /* Insertions which have no effect on paragraph formatting. */ + case ifclear: + case ifhtml: + case ifinfo: + case ifnothtml: + case ifnotinfo: + case ifnottex: + case ifset: + case iftex: + case rawtex: + break; + + case rawhtml: + escape_html = 1; + break; + + case direntry: /* Eaten if html. */ + insert_string ("END-INFO-DIR-ENTRY\n\n"); + close_insertion_paragraph (); + break; + + case detailmenu: + in_detailmenu--; /* No longer hacking menus. */ + if (!in_menu) + { + if (!no_headers) + close_insertion_paragraph (); + } + break; + + case menu: + in_menu--; /* No longer hacking menus. */ + if (html) + add_word ("</ul>\n"); + else if (!no_headers) + close_insertion_paragraph (); + break; + + case multitable: + end_multitable (); + break; + + case enumerate: + stop_enumerating (); + close_insertion_paragraph (); + current_indent -= default_indentation_increment; + if (html) + add_word ("</ol>\n"); + break; + + case flushleft: + case group: + case cartouche: + close_insertion_paragraph (); + break; + + case format: + case smallformat: + case display: + case smalldisplay: + case example: + case smallexample: + case lisp: + case smalllisp: + case quotation: + /* @format and @smallformat are the only fixed_width insertion + without a change in indentation. */ + if (type != format && type != smallformat) + current_indent -= default_indentation_increment; + + if (html) + add_word (type == quotation ? "</blockquote>\n" : "</pre>\n"); + + /* The ending of one of these insertions always marks the + start of a new paragraph. */ + close_insertion_paragraph (); + break; + + case table: + case ftable: + case vtable: + current_indent -= default_indentation_increment; + if (html) + add_word ("</dl>\n"); + break; + + case itemize: + current_indent -= default_indentation_increment; + if (html) + add_word ("</ul>\n"); + close_insertion_paragraph (); + break; + + case flushright: + force_flush_right--; + close_insertion_paragraph (); + break; + + /* Handle the @defun insertions with this default clause. */ + default: + { + enum insertion_type base_type; + + if (type < defcv || type > defvr) + line_error ("end_insertion internal error: type=%d", type); + + base_type = get_base_type (type); + switch (base_type) + { + case deffn: + case defvr: + case deftp: + case deftypefn: + case deftypevr: + case defcv: + case defop: + case deftypemethod: + case deftypeop: + case deftypeivar: + if (html) + /* close the tables which has been opened in defun.c */ + add_word ("</TD></TR>\n</TABLE>\n"); + break; + } /* switch (base_type)... */ + + current_indent -= default_indentation_increment; + close_insertion_paragraph (); + } + break; + + } + + if (current_indent < 0) + line_error ("end_insertion internal error: current indent=%d", + current_indent); +} + +/* Insertions cannot cross certain boundaries, such as node beginnings. In + code that creates such boundaries, you should call `discard_insertions' + before doing anything else. It prints the errors for you, and cleans up + the insertion stack. + + With nonzero SPECIALS_OK argument, allows unmatched + @if... conditionals, otherwise not. This is because conditionals can + cross node boundaries. Always happens with the @top node, for example. */ +void +discard_insertions (specials_ok) + int specials_ok; +{ + int real_line_number = line_number; + while (insertion_stack) + { + if (specials_ok + && ((ifclear <= insertion_stack->insertion + && insertion_stack->insertion <= iftex) + || insertion_stack->insertion == rawhtml + || insertion_stack->insertion == rawtex)) + break; + else + { + char *offender = insertion_type_pname (insertion_stack->insertion); + char *current_filename = input_filename; + + input_filename = insertion_stack->filename; + line_number = insertion_stack->line_number; + line_error (_("No matching `%cend %s'"), COMMAND_PREFIX, offender); + input_filename = current_filename; + pop_insertion (); + } + } + line_number = real_line_number; +} + +/* Insertion (environment) commands. */ + +void +cm_quotation () +{ + begin_insertion (quotation); +} + +void +cm_example () +{ + begin_insertion (example); +} + +void +cm_smallexample () +{ + begin_insertion (smallexample); +} + +void +cm_lisp () +{ + begin_insertion (lisp); +} + +void +cm_smalllisp () +{ + begin_insertion (smalllisp); +} + +/* @cartouche/@end cartouche draws box with rounded corners in + TeX output. Right now, just a no-op insertion. */ +void +cm_cartouche () +{ + begin_insertion (cartouche); +} + +void +cm_format () +{ + begin_insertion (format); +} + +void +cm_smallformat () +{ + begin_insertion (smallformat); +} + +void +cm_display () +{ + begin_insertion (display); +} + +void +cm_smalldisplay () +{ + begin_insertion (smalldisplay); +} + +void +cm_direntry () +{ + if (no_headers || html) + command_name_condition (); + else + begin_insertion (direntry); +} + +void +cm_itemize () +{ + begin_insertion (itemize); +} + +/* Start an enumeration insertion of type TYPE. If the user supplied + no argument on the line, then use DEFAULT_STRING as the initial string. */ +static void +do_enumeration (type, default_string) + int type; + char *default_string; +{ + get_until_in_line (0, ".", &enumeration_arg); + canon_white (enumeration_arg); + + if (!*enumeration_arg) + { + free (enumeration_arg); + enumeration_arg = xstrdup (default_string); + } + + if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg)) + { + warning (_("%s requires letter or digit"), insertion_type_pname (type)); + + switch (type) + { + case enumerate: + default_string = "1"; + break; + } + enumeration_arg = xstrdup (default_string); + } + begin_insertion (type); +} + +void +cm_enumerate () +{ + do_enumeration (enumerate, "1"); +} + +void +cm_table () +{ + begin_insertion (table); +} + +void +cm_multitable () +{ + begin_insertion (multitable); /* @@ */ +} + +void +cm_ftable () +{ + begin_insertion (ftable); +} + +void +cm_vtable () +{ + begin_insertion (vtable); +} + +void +cm_group () +{ + begin_insertion (group); +} + +void +cm_ifinfo () +{ + if (process_info) + begin_insertion (ifinfo); + else + command_name_condition (); +} + +void +cm_ifnotinfo () +{ + if (!process_info) + begin_insertion (ifnotinfo); + else + command_name_condition (); +} + + +/* Insert raw HTML (no escaping of `<' etc.). */ +void +cm_html () +{ + if (process_html) + begin_insertion (rawhtml); + else + command_name_condition (); +} + +void +cm_ifhtml () +{ + if (process_html) + begin_insertion (ifhtml); + else + command_name_condition (); +} + +void +cm_ifnothtml () +{ + if (!process_html) + begin_insertion (ifnothtml); + else + command_name_condition (); +} + + +void +cm_tex () +{ + if (process_tex) + begin_insertion (rawtex); + else + command_name_condition (); +} + +void +cm_iftex () +{ + if (process_tex) + begin_insertion (iftex); + else + command_name_condition (); +} + +void +cm_ifnottex () +{ + if (!process_tex) + begin_insertion (ifnottex); + else + command_name_condition (); +} + +/* Begin an insertion where the lines are not filled or indented. */ +void +cm_flushleft () +{ + begin_insertion (flushleft); +} + +/* Begin an insertion where the lines are not filled, and each line is + forced to the right-hand side of the page. */ +void +cm_flushright () +{ + begin_insertion (flushright); +} + +void +cm_menu () +{ + if (current_node == NULL) + { + warning (_("@menu seen before first @node, creating `Top' node")); + warning (_("perhaps your @top node should be wrapped in @ifnottex rather than @ifinfo?")); + /* Include @top command so we can construct the implicit node tree. */ + execute_string ("@node top\n@top Top\n"); + } + begin_insertion (menu); +} + +void +cm_detailmenu () +{ + if (current_node == NULL) + { /* Problems anyway, @detailmenu should always be inside @menu. */ + warning (_("@detailmenu seen before first node, creating `Top' node")); + execute_string ("@node top\n@top Top\n"); + } + begin_insertion (detailmenu); +} + +/* End existing insertion block. */ +void +cm_end () +{ + char *temp; + enum insertion_type type; + + if (!insertion_level) + { + line_error (_("Unmatched `%c%s'"), COMMAND_PREFIX, command); + return; + } + + get_rest_of_line (0, &temp); + + if (temp[0] == 0) + line_error (_("`%c%s' needs something after it"), COMMAND_PREFIX, command); + + type = find_type_from_name (temp); + + if (type == bad_type) + { + line_error (_("Bad argument to `%s', `%s', using `%s'"), + command, temp, insertion_type_pname (current_insertion_type ())); + } + end_insertion (type); + free (temp); +} + +/* @itemx, @item. */ + +static int itemx_flag = 0; + +/* Return whether CMD takes a brace-delimited {arg}. */ +static int +command_needs_braces (cmd) + char *cmd; +{ + int i; + for (i = 0; command_table[i].name; i++) + { + if (STREQ (command_table[i].name, cmd)) + return command_table[i].argument_in_braces == BRACE_ARGS; + } + + return 0; /* macro or alias */ +} + + +void +cm_item () +{ + char *rest_of_line, *item_func; + + /* Can only hack "@item" while inside of an insertion. */ + if (insertion_level) + { + INSERTION_ELT *stack = insertion_stack; + int original_input_text_offset; + + skip_whitespace (); + original_input_text_offset = input_text_offset; + + get_rest_of_line (0, &rest_of_line); + item_func = current_item_function (); + + /* Do the right thing depending on which insertion function is active. */ + switch_top: + switch (stack->insertion) + { + case multitable: + multitable_item (); + /* Support text directly after the @item. */ + if (*rest_of_line) + { + line_number--; + input_text_offset = original_input_text_offset; + } + break; + + case ifclear: + case ifhtml: + case ifinfo: + case ifnothtml: + case ifnotinfo: + case ifnottex: + case ifset: + case iftex: + case rawhtml: + case rawtex: + case tex: + case cartouche: + stack = stack->next; + if (!stack) + goto no_insertion; + else + goto switch_top; + break; + + case menu: + case quotation: + case example: + case smallexample: + case lisp: + case smalllisp: + case format: + case smallformat: + case display: + case smalldisplay: + case group: + line_error (_("@%s not meaningful inside `@%s' block"), + command, + insertion_type_pname (current_insertion_type ())); + break; + + case itemize: + case enumerate: + if (itemx_flag) + { + line_error (_("@itemx not meaningful inside `%s' block"), + insertion_type_pname (current_insertion_type ())); + } + else + { + if (html) + { + if (in_paragraph) + { + add_word ("</p>"); + in_paragraph = 0; + } + add_word ("<li>"); + } + else + { + start_paragraph (); + kill_self_indent (-1); + filling_enabled = indented_fill = 1; + + if (current_item_function ()) + { + output_column = current_indent - 2; + indent (output_column); + + /* The item marker can be given with or without + braces -- @bullet and @bullet{} are both ok. + Or it might be something that doesn't take + braces at all, such as "o" or "#" or "@ ". + Thus, only supply braces if the item marker is + a command, they haven't supplied braces + themselves, and we know it needs them. */ + if (item_func && *item_func) + { + if (*item_func == COMMAND_PREFIX + && item_func[strlen (item_func) - 1] != '}' + && command_needs_braces (item_func + 1)) + execute_string ("%s{}", item_func); + else + execute_string ("%s", item_func); + } + insert (' '); + output_column++; + } + else + enumerate_item (); + + /* Special hack. This makes `close_paragraph' a no-op until + `start_paragraph' has been called. */ + must_start_paragraph = 1; + } + + /* Handle text directly after the @item. */ + if (*rest_of_line) + { + line_number--; + input_text_offset = original_input_text_offset; + } + } + break; + + case table: + case ftable: + case vtable: + if (html) + { + static int last_html_output_position = 0; + + /* If nothing has been output since the last <dd>, + remove the empty <dd> element. Some browsers render + an extra empty line for <dd><dt>, which makes @itemx + conversion look ugly. */ + if (last_html_output_position == output_position + && strncmp ((char *) output_paragraph, "<dd>", + output_paragraph_offset) == 0) + output_paragraph_offset = 0; + + /* Force the browser to render one blank line before + each new @item in a table. But don't do that unless + this is the first <dt> after the <dl>, or if we are + converting @itemx. + + Note that there are some browsers which ignore <br> + in this context, but I cannot find any way to force + them all render exactly one blank line. */ + if (!itemx_flag + && strncmp ((char *) output_paragraph + + output_paragraph_offset - sizeof (dl_tag) + 1, + dl_tag, sizeof (dl_tag) - 1) != 0) + add_word ("<br>"); + + add_word ("<dt>"); + if (item_func && *item_func) + execute_string ("%s{%s}", item_func, rest_of_line); + else + execute_string ("%s", rest_of_line); + + if (current_insertion_type () == ftable) + execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line); + + if (current_insertion_type () == vtable) + execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line); + /* Make sure output_position is updated, so we could + remember it. */ + close_single_paragraph (); + last_html_output_position = output_position; + add_word ("<dd>"); + } + else + { + /* We need this to determine if we have two @item's in a row + (see test just below). */ + static int last_item_output_position = 0; + + /* Get rid of extra characters. */ + kill_self_indent (-1); + + /* If we have one @item followed directly by another @item, + we need to insert a blank line. This is not true for + @itemx, though. */ + if (!itemx_flag && last_item_output_position == output_position) + insert ('\n'); + + /* `close_paragraph' almost does what we want. The problem + is when paragraph_is_open, and last_char_was_newline, and + the last newline has been turned into a space, because + filling_enabled. I handle it here. */ + if (last_char_was_newline && filling_enabled && + paragraph_is_open) + insert ('\n'); + close_paragraph (); + +#if defined (INDENT_PARAGRAPHS_IN_TABLE) + /* Indent on a new line, but back up one indentation level. */ + { + int save = inhibit_paragraph_indentation; + inhibit_paragraph_indentation = 1; + /* At this point, inserting any non-whitespace character will + force the existing indentation to be output. */ + add_char ('i'); + inhibit_paragraph_indentation = save; + } +#else /* !INDENT_PARAGRAPHS_IN_TABLE */ + add_char ('i'); +#endif /* !INDENT_PARAGRAPHS_IN_TABLE */ + + output_paragraph_offset--; + kill_self_indent (default_indentation_increment + 1); + + /* Add item's argument to the line. */ + filling_enabled = 0; + if (item_func && *item_func) + execute_string ("%s{%s}", item_func, rest_of_line); + else + execute_string ("%s", rest_of_line); + + if (current_insertion_type () == ftable) + execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line); + else if (current_insertion_type () == vtable) + execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line); + + /* Start a new line, and let start_paragraph () + do the indenting of it for you. */ + close_single_paragraph (); + indented_fill = filling_enabled = 1; + last_item_output_position = output_position; + } + } + free (rest_of_line); + } + else + { + no_insertion: + line_error (_("%c%s found outside of an insertion block"), + COMMAND_PREFIX, command); + } +} + +void +cm_itemx () +{ + itemx_flag++; + cm_item (); + itemx_flag--; +} diff --git a/gnu/usr.bin/texinfo/makeinfo/insertion.h b/gnu/usr.bin/texinfo/makeinfo/insertion.h new file mode 100644 index 00000000000..a926e71c839 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/insertion.h @@ -0,0 +1,61 @@ +/* insertion.h -- declarations for insertion.c. + $Id: insertion.h,v 1.1.1.1 2000/02/09 01:25:20 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef INSERTION_H +#define INSERTION_H + +/* Must match list in insertion.c. */ +enum insertion_type +{ + cartouche, defcv, deffn, defivar, defmac, defmethod, defop, defopt, + defspec, deftp, deftypefn, deftypefun, deftypeivar, deftypemethod, + deftypeop, deftypevar, deftypevr, defun, defvar, defvr, detailmenu, + direntry, display, enumerate, example, flushleft, flushright, format, + ftable, group, ifclear, ifhtml, ifinfo, ifnothtml, ifnotinfo, + ifnottex, ifset, iftex, itemize, lisp, menu, multitable, quotation, + rawhtml, rawtex, smalldisplay, smallexample, smallformat, smalllisp, + table, tex, vtable, bad_type +}; + +typedef struct istack_elt +{ + struct istack_elt *next; + char *item_function; + char *filename; + int line_number; + int filling_enabled; + int indented_fill; + enum insertion_type insertion; + int inhibited; + int in_fixed_width_font; +} INSERTION_ELT; + + +extern int insertion_level; +extern INSERTION_ELT *insertion_stack; +extern int in_menu; +extern int in_detailmenu; +extern int had_menu_commentary; +extern int in_paragraph; + +extern void command_name_condition (); +extern void cm_ifnothtml (), cm_ifhtml(), cm_html (); +extern void cm_ifinfo (), cm_ifnotinfo (); +extern void cm_ifnottex (), cm_iftex (), cm_tex (); +#endif /* !INSERTION_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/lang.c b/gnu/usr.bin/texinfo/makeinfo/lang.c new file mode 100644 index 00000000000..44c84262669 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/lang.c @@ -0,0 +1,415 @@ +/* lang.c -- language depend behaviour (startpoint) + $Id: lang.c,v 1.1.1.1 2000/02/09 01:25:20 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */ + +#include "system.h" +#include "cmds.h" +#include "lang.h" +#include "makeinfo.h" + +/* Current document encoding. */ +char *document_encoding = NULL; + +/* Current language code; default is English. */ +language_code_type language_code = en; + +language_struct language_table[] = { + { aa, "aa", "Afar" }, + { ab, "ab", "Abkhazian" }, + { af, "af", "Afrikaans" }, + { am, "am", "Amharic" }, + { ar, "ar", "Arabic" }, + { as, "as", "Assamese" }, + { ay, "ay", "Aymara" }, + { az, "az", "Azerbaijani" }, + { ba, "ba", "Bashkir" }, + { be, "be", "Byelorussian" }, + { bg, "bg", "Bulgarian" }, + { bh, "bh", "Bihari" }, + { bi, "bi", "Bislama" }, + { bn, "bn", "Bengali; Bangla" }, + { bo, "bo", "Tibetan" }, + { br, "br", "Breton" }, + { ca, "ca", "Catalan" }, + { co, "co", "Corsican" }, + { cs, "cs", "Czech" }, + { cy, "cy", "Welsh" }, + { da, "da", "Danish" }, + { de, "de", "German" }, + { dz, "dz", "Bhutani" }, + { el, "el", "Greek" }, + { en, "en", "English" }, + { eo, "eo", "Esperanto" }, + { es, "es", "Spanish" }, + { et, "et", "Estonian" }, + { eu, "eu", "Basque" }, + { fa, "fa", "Persian" }, + { fi, "fi", "Finnish" }, + { fj, "fj", "Fiji" }, + { fo, "fo", "Faroese" }, + { fr, "fr", "French" }, + { fy, "fy", "Frisian" }, + { ga, "ga", "Irish" }, + { gd, "gd", "Scots Gaelic" }, + { gl, "gl", "Galician" }, + { gn, "gn", "Guarani" }, + { gu, "gu", "Gujarati" }, + { ha, "ha", "Hausa" }, + { he, "he", "Hebrew" } /* (formerly iw) */, + { hi, "hi", "Hindi" }, + { hr, "hr", "Croatian" }, + { hu, "hu", "Hungarian" }, + { hy, "hy", "Armenian" }, + { ia, "ia", "Interlingua" }, + { id, "id", "Indonesian" } /* (formerly in) */, + { ie, "ie", "Interlingue" }, + { ik, "ik", "Inupiak" }, + { is, "is", "Icelandic" }, + { it, "it", "Italian" }, + { iu, "iu", "Inuktitut" }, + { ja, "ja", "Japanese" }, + { jw, "jw", "Javanese" }, + { ka, "ka", "Georgian" }, + { kk, "kk", "Kazakh" }, + { kl, "kl", "Greenlandic" }, + { km, "km", "Cambodian" }, + { kn, "kn", "Kannada" }, + { ko, "ko", "Korean" }, + { ks, "ks", "Kashmiri" }, + { ku, "ku", "Kurdish" }, + { ky, "ky", "Kirghiz" }, + { la, "la", "Latin" }, + { ln, "ln", "Lingala" }, + { lo, "lo", "Laothian" }, + { lt, "lt", "Lithuanian" }, + { lv, "lv", "Latvian, Lettish" }, + { mg, "mg", "Malagasy" }, + { mi, "mi", "Maori" }, + { mk, "mk", "Macedonian" }, + { ml, "ml", "Malayalam" }, + { mn, "mn", "Mongolian" }, + { mo, "mo", "Moldavian" }, + { mr, "mr", "Marathi" }, + { ms, "ms", "Malay" }, + { mt, "mt", "Maltese" }, + { my, "my", "Burmese" }, + { na, "na", "Nauru" }, + { ne, "ne", "Nepali" }, + { nl, "nl", "Dutch" }, + { no, "no", "Norwegian" }, + { oc, "oc", "Occitan" }, + { om, "om", "(Afan) Oromo" }, + { or, "or", "Oriya" }, + { pa, "pa", "Punjabi" }, + { pl, "pl", "Polish" }, + { ps, "ps", "Pashto, Pushto" }, + { pt, "pt", "Portuguese" }, + { qu, "qu", "Quechua" }, + { rm, "rm", "Rhaeto-Romance" }, + { rn, "rn", "Kirundi" }, + { ro, "ro", "Romanian" }, + { ru, "ru", "Russian" }, + { rw, "rw", "Kinyarwanda" }, + { sa, "sa", "Sanskrit" }, + { sd, "sd", "Sindhi" }, + { sg, "sg", "Sangro" }, + { sh, "sh", "Serbo-Croatian" }, + { si, "si", "Sinhalese" }, + { sk, "sk", "Slovak" }, + { sl, "sl", "Slovenian" }, + { sm, "sm", "Samoan" }, + { sn, "sn", "Shona" }, + { so, "so", "Somali" }, + { sq, "sq", "Albanian" }, + { sr, "sr", "Serbian" }, + { ss, "ss", "Siswati" }, + { st, "st", "Sesotho" }, + { su, "su", "Sundanese" }, + { sv, "sv", "Swedish" }, + { sw, "sw", "Swahili" }, + { ta, "ta", "Tamil" }, + { te, "te", "Telugu" }, + { tg, "tg", "Tajik" }, + { th, "th", "Thai" }, + { ti, "ti", "Tigrinya" }, + { tk, "tk", "Turkmen" }, + { tl, "tl", "Tagalog" }, + { tn, "tn", "Setswana" }, + { to, "to", "Tonga" }, + { tr, "tr", "Turkish" }, + { ts, "ts", "Tsonga" }, + { tt, "tt", "Tatar" }, + { tw, "tw", "Twi" }, + { ug, "ug", "Uighur" }, + { uk, "uk", "Ukrainian" }, + { ur, "ur", "Urdu" }, + { uz, "uz", "Uzbek" }, + { vi, "vi", "Vietnamese" }, + { vo, "vo", "Volapuk" }, + { wo, "wo", "Wolof" }, + { xh, "xh", "Xhosa" }, + { yi, "yi", "Yiddish" } /* (formerly ji) */, + { yo, "yo", "Yoruba" }, + { za, "za", "Zhuang" }, + { zh, "zh", "Chinese" }, + { zu, "zu", "Zulu" }, + { last_language_code, NULL, NULL } +}; + +/* @documentlanguage. Maybe we'll do something useful with this in the + future. For now, we just recognize it. */ +void +cm_documentlanguage () +{ + language_code_type c; + char *lang_arg; + + /* Read the line with the language code on it. */ + get_rest_of_line (1, &lang_arg); + + /* Linear search is fine these days. */ + for (c = aa; c != last_language_code; c++) + { + if (strcmp (lang_arg, language_table[c].abbrev) == 0) + { /* Set current language code. */ + language_code = c; + break; + } + } + + /* If we didn't find this code, complain. */ + if (c == last_language_code) + warning (_("%s is not a valid ISO 639 language code"), lang_arg); + + free (lang_arg); +} + + + +/* @documentencoding. Set global. */ +void +cm_documentencoding () +{ + get_rest_of_line (1, &document_encoding); +} + + + +/* Accent commands that take explicit arguments and don't have any + special HTML support. */ + +void +cm_accent (arg) + int arg; +{ + if (arg == START) + { + /* Must come first to avoid ambiguity with overdot. */ + if (strcmp (command, "udotaccent") == 0) /* underdot */ + add_char ('.'); + } + else if (arg == END) + { + if (strcmp (command, "=") == 0) /* macron */ + add_word (html ? "¯" : "="); + 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/gnu/usr.bin/texinfo/makeinfo/lang.h b/gnu/usr.bin/texinfo/makeinfo/lang.h new file mode 100644 index 00000000000..40c48aca98d --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/lang.h @@ -0,0 +1,86 @@ +/* lang.h -- declarations for language codes etc. + $Id: lang.h,v 1.1.1.1 2000/02/09 01:25:20 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */ + +#ifndef LANG_H +#define LANG_H + +/* The langauge code which can be changed through @documentlanguage + * Actualy Info does not support this (may be in the future) ;-) + * Default for language code is en (english!) kama + * These code should ISO 639 two letter codes. + */ +typedef enum +{ + aa, ab, af, am, ar, as, ay, az, + ba, be, bg, bh, bi, bn, bo, br, + ca, co, cs, cy, + da, de, dz, + el, en, eo, es, et, eu, + fa, fi, fj, fo, fr, fy, + ga, gd, gl, gn, gu, + ha, he, hi, hr, hu, hy, + ia, id, ie, ik, is, it, iu, + ja, jw, + ka, kk, kl, km, kn, ko, ks, ku, ky, + la, ln, lo, lt, lv, + mg, mi, mk, ml, mn, mo, mr, ms, mt, my, + na, ne, nl, no, + oc, om, or, + pa, pl, ps, pt, + qu, + rm, rn, ro, ru, rw, + sa, sd, sg, sh, si, sk, sl, sm, sn, so, sq, sr, ss, st, su, sv, sw, + ta, te, tg, th, ti, tk, tl, tn, to, tr, ts, tt, tw, + ug, uk, ur, uz, + vi, vo, + wo, + xh, + yi, yo, + za, zh, zu, + last_language_code +} language_code_type; + +/* The current language code. */ +extern language_code_type language_code; + +/* Information about all valid languages. */ +typedef struct +{ + language_code_type lc; /* language code as enum type */ + char *abbrev; /* two letter language code */ + char *desc; /* full name for language code */ +} language_struct; +extern language_struct language_table[]; + +/* The encoding, or null if not set. */ +extern char *document_encoding; + + +/* The commands. */ +extern void cm_documentlanguage (), cm_documentencoding (); + +/* Accents, other non-English characters. */ +void cm_accent (), cm_special_char (), cm_dotless (); + +extern void cm_accent_umlaut (), cm_accent_acute (), cm_accent_cedilla (), + cm_accent_hat (), cm_accent_grave (), cm_accent_tilde (); + +#endif /* not LANG_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/macro.c b/gnu/usr.bin/texinfo/makeinfo/macro.c new file mode 100644 index 00000000000..82821b9fe86 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/macro.c @@ -0,0 +1,1114 @@ +/* macro.c -- user-defined macros for Texinfo. + $Id: macro.c,v 1.1.1.1 2000/02/09 01:25:23 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "cmds.h" +#include "macro.h" +#include "makeinfo.h" +#include "insertion.h" + +/* If non-NULL, this is an output stream to write the full macro expansion + of the input text to. The result is another texinfo file, but + missing @include, @infoinclude, @macro, and macro invocations. Instead, + all of the text is placed within the file. */ +FILE *macro_expansion_output_stream = NULL; + +/* Output file for -E. */ +char *macro_expansion_filename; + +/* Nonzero means a macro string is in execution, as opposed to a file. */ +int me_executing_string = 0; + +/* Nonzero means we want only to expand macros and + leave everything else intact. */ +int only_macro_expansion = 0; + +static ITEXT **itext_info = NULL; +static int itext_size = 0; + +/* Return the arglist on the current line. This can behave in two different + ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */ +int braces_required_for_macro_args = 0; + +/* Array of macros and definitions. */ +MACRO_DEF **macro_list = NULL; + +int macro_list_len = 0; /* Number of elements. */ +int macro_list_size = 0; /* Number of slots in total. */ + +/* Return the length of the array in ARRAY. */ +int +array_len (array) + char **array; +{ + int i = 0; + + if (array) + for (i = 0; array[i]; i++); + + return i; +} + +void +free_array (array) + char **array; +{ + if (array) + { + int i; + for (i = 0; array[i]; i++) + free (array[i]); + + free (array); + } +} + +/* Return the macro definition of NAME or NULL if NAME is not defined. */ +MACRO_DEF * +find_macro (name) + char *name; +{ + int i; + MACRO_DEF *def; + + def = NULL; + for (i = 0; macro_list && (def = macro_list[i]); i++) + { + if ((!def->inhibited) && (strcmp (def->name, name) == 0)) + break; + } + return def; +} + +/* Add the macro NAME with ARGLIST and BODY to the list of defined macros. + SOURCE_FILE is the name of the file where this definition can be found, + and SOURCE_LINENO is the line number within that file. If a macro already + exists with NAME, then a warning is produced, and that previous + definition is overwritten. */ +void +add_macro (name, arglist, body, source_file, source_lineno, flags) + char *name; + char **arglist; + char *body; + char *source_file; + int source_lineno, flags; +{ + MACRO_DEF *def; + + def = find_macro (name); + + if (!def) + { + if (macro_list_len + 2 >= macro_list_size) + macro_list = xrealloc + (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *))); + + macro_list[macro_list_len] = xmalloc (sizeof (MACRO_DEF)); + macro_list[macro_list_len + 1] = NULL; + + def = macro_list[macro_list_len]; + macro_list_len += 1; + def->name = name; + } + else + { + char *temp_filename = input_filename; + int temp_line = line_number; + + warning (_("macro `%s' previously defined"), name); + + input_filename = def->source_file; + line_number = def->source_lineno; + warning (_("here is the previous definition of `%s'"), name); + + input_filename = temp_filename; + line_number = temp_line; + + if (def->arglist) + { + int i; + + for (i = 0; def->arglist[i]; i++) + free (def->arglist[i]); + + free (def->arglist); + } + free (def->source_file); + free (def->body); + } + + def->source_file = xstrdup (source_file); + def->source_lineno = source_lineno; + def->body = body; + def->arglist = arglist; + def->inhibited = 0; + def->flags = flags; +} + + +char ** +get_brace_args (quote_single) + int quote_single; +{ + char **arglist, *word; + int arglist_index, arglist_size; + int character, escape_seen, start; + int depth = 1; + + /* There is an arglist in braces here, so gather the args inside of it. */ + skip_whitespace_and_newlines (); + input_text_offset++; + arglist = NULL; + arglist_index = arglist_size = 0; + + get_arg: + skip_whitespace_and_newlines (); + start = input_text_offset; + escape_seen = 0; + + while ((character = curchar ())) + { + if (character == '\\') + { + input_text_offset += 2; + escape_seen = 1; + } + else if (character == '{') + { + depth++; + input_text_offset++; + } + else if ((character == ',' && !quote_single) || + ((character == '}') && depth == 1)) + { + int len = input_text_offset - start; + + if (len || (character != '}')) + { + word = xmalloc (1 + len); + memcpy (word, input_text + start, len); + word[len] = 0; + + /* Clean up escaped characters. */ + if (escape_seen) + { + int i; + for (i = 0; word[i]; i++) + if (word[i] == '\\') + memmove (word + i, word + i + 1, + 1 + strlen (word + i + 1)); + } + + if (arglist_index + 2 >= arglist_size) + arglist = xrealloc + (arglist, (arglist_size += 10) * sizeof (char *)); + + arglist[arglist_index++] = word; + arglist[arglist_index] = NULL; + } + + input_text_offset++; + if (character == '}') + break; + else + goto get_arg; + } + else if (character == '}') + { + depth--; + input_text_offset++; + } + else + { + input_text_offset++; + if (character == '\n') line_number++; + } + } + return arglist; +} + +char ** +get_macro_args (def) + MACRO_DEF *def; +{ + int i; + char *word; + + /* Quickly check to see if this macro has been invoked with any arguments. + If not, then don't skip any of the following whitespace. */ + for (i = input_text_offset; i < input_text_length; i++) + if (!cr_or_whitespace (input_text[i])) + break; + + if (input_text[i] != '{') + { + if (braces_required_for_macro_args) + { + return NULL; + } + else + { + /* Braces are not required to fill out the macro arguments. If + this macro takes one argument, it is considered to be the + remainder of the line, sans whitespace. */ + if (def->arglist && def->arglist[0] && !def->arglist[1]) + { + char **arglist; + + get_rest_of_line (0, &word); + if (input_text[input_text_offset - 1] == '\n') + { + input_text_offset--; + line_number--; + } + /* canon_white (word); */ + arglist = xmalloc (2 * sizeof (char *)); + arglist[0] = word; + arglist[1] = NULL; + return arglist; + } + else + { + /* The macro either took no arguments, or took more than + one argument. In that case, it must be invoked with + arguments surrounded by braces. */ + return NULL; + } + } + } + return get_brace_args (def->flags & ME_QUOTE_ARG); +} + +/* Substitute actual parameters for named parameters in body. + The named parameters which appear in BODY must by surrounded + reverse slashes, as in \foo\. */ +char * +apply (named, actuals, body) + char **named, **actuals, *body; +{ + int i; + int new_body_index, new_body_size; + char *new_body, *text; + int length_of_actuals; + + length_of_actuals = array_len (actuals); + new_body_size = strlen (body); + new_body = xmalloc (1 + new_body_size); + + /* Copy chars from BODY into NEW_BODY. */ + i = 0; + new_body_index = 0; + + while (body[i]) + { /* Anything but a \ is easy. */ + if (body[i] != '\\') + new_body[new_body_index++] = body[i++]; + else + { /* Snarf parameter name, check against named parameters. */ + char *param; + int param_start, which, len; + + param_start = ++i; + while (body[i] && body[i] != '\\') + i++; + + len = i - param_start; + param = xmalloc (1 + len); + memcpy (param, body + param_start, len); + param[len] = 0; + + if (body[i]) /* move past \ */ + i++; + + /* Now check against named parameters. */ + for (which = 0; named && named[which]; which++) + if (STREQ (named[which], param)) + break; + + if (named && named[which]) + { + text = which < length_of_actuals ? actuals[which] : NULL; + if (!text) + text = ""; + len = strlen (text); + } + else + { /* not a parameter, either it's \\ (if len==0) or an + error. In either case, restore one \ at least. */ + if (len) { + warning (_("\\ in macro expansion followed by `%s' instead of \\ or parameter name"), + param); + } + len++; + text = xmalloc (1 + len); + sprintf (text, "\\%s", param); + } + + if (strlen (param) + 2 < len) + { + new_body_size += len + 1; + new_body = xrealloc (new_body, new_body_size); + } + + free (param); + + strcpy (new_body + new_body_index, text); + new_body_index += len; + + if (!named || !named[which]) + free (text); + } + } + + new_body[new_body_index] = 0; + return new_body; +} + +/* Expand macro passed in DEF, a pointer to a MACRO_DEF, and + return its expansion as a string. */ +char * +expand_macro (def) + MACRO_DEF *def; +{ + char **arglist; + int num_args; + char *execution_string = NULL; + int start_line = line_number; + + /* Find out how many arguments this macro definition takes. */ + num_args = array_len (def->arglist); + + /* Gather the arguments present on the line if there are any. */ + arglist = get_macro_args (def); + + if (num_args < array_len (arglist)) + { + free_array (arglist); + line_error (_("Macro `%s' called on line %d with too many args"), + def->name, start_line); + return execution_string; + } + + if (def->body) + execution_string = apply (def->arglist, arglist, def->body); + + free_array (arglist); + return execution_string; +} + +/* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */ +void +execute_macro (def) + MACRO_DEF *def; +{ + char *execution_string; + int start_line = line_number, end_line; + + if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion) + me_append_before_this_command (); + + execution_string = expand_macro (def); + if (!execution_string) + return; + + if (def->body) + { + /* Reset the line number to where the macro arguments began. + This makes line numbers reported in error messages correct in + case the macro arguments span several lines and the expanded + arguments invoke other commands. */ + end_line = line_number; + line_number = start_line; + + if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion) + { + remember_itext (input_text, input_text_offset); + me_execute_string (execution_string); + } + else + execute_string ("%s", execution_string); + + free (execution_string); + line_number = end_line; + } +} + + +/* Read and remember the definition of a macro. If RECURSIVE is set, + set the ME_RECURSE flag. MACTYPE is either "macro" or "rmacro", and + tells us what the matching @end should be. */ +static void +define_macro (mactype, recursive) + char *mactype; + int recursive; +{ + int i; + char *name, **arglist, *body, *line, *last_end; + int body_size, body_index; + int depth = 1; + int defining_line = line_number; + int flags = 0; + + arglist = NULL; + body = NULL; + body_size = 0; + body_index = 0; + + if (macro_expansion_output_stream && !executing_string) + me_append_before_this_command (); + + skip_whitespace (); + + /* Get the name of the macro. This is the set of characters which are + not whitespace and are not `{' immediately following the @macro. */ + { + int start = input_text_offset; + int len; + + for (i = start; + (i < input_text_length) && + (input_text[i] != '{') && + (!cr_or_whitespace (input_text[i])); + i++); + + len = i - start; + name = xmalloc (1 + len); + memcpy (name, input_text + start, len); + name[len] = 0; + input_text_offset = i; + } + + skip_whitespace (); + + /* It is not required that the definition of a macro includes an arglist. + If not, don't try to get the named parameters, just use a null list. */ + if (curchar () == '{') + { + int character; + int arglist_index = 0, arglist_size = 0; + int gathering_words = 1; + char *word = NULL; + + /* Read the words inside of the braces which determine the arglist. + These words will be replaced within the body of the macro at + execution time. */ + + input_text_offset++; + skip_whitespace_and_newlines (); + + while (gathering_words) + { + int len; + + for (i = input_text_offset; + (character = input_text[i]); + i++) + { + switch (character) + { + case '\n': + line_number++; + case ' ': + case '\t': + case ',': + case '}': + /* Found the end of the current arglist word. Save it. */ + len = i - input_text_offset; + word = xmalloc (1 + len); + memcpy (word, input_text + input_text_offset, len); + word[len] = 0; + input_text_offset = i; + + /* Advance to the comma or close-brace that signified + the end of the argument. */ + while ((character = curchar ()) + && character != ',' + && character != '}') + { + input_text_offset++; + if (character == '\n') + line_number++; + } + + /* Add the word to our list of words. */ + if (arglist_index + 2 >= arglist_size) + { + arglist_size += 10; + arglist = xrealloc (arglist, + arglist_size * sizeof (char *)); + } + + arglist[arglist_index++] = word; + arglist[arglist_index] = NULL; + break; + } + + if (character == '}') + { + input_text_offset++; + gathering_words = 0; + break; + } + + if (character == ',') + { + input_text_offset++; + skip_whitespace_and_newlines (); + i = input_text_offset - 1; + } + } + } + + /* If we have exactly one argument, do @quote-arg implicitly. Not + only does this match TeX's behavior (which can't feasibly be + changed), but it's a good idea. */ + if (arglist_index == 1) + flags |= ME_QUOTE_ARG; + } + + /* Read the text carefully until we find an "@end macro" which + matches this one. The text in between is the body of the macro. */ + skip_whitespace_and_newlines (); + + while (depth) + { + if ((input_text_offset + 9) > input_text_length) + { + int temp_line = line_number; + line_number = defining_line; + line_error (_("%cend macro not found"), COMMAND_PREFIX); + line_number = temp_line; + return; + } + + get_rest_of_line (0, &line); + + /* Handle commands only meaningful within a macro. */ + if ((*line == COMMAND_PREFIX) && (depth == 1) && + (strncmp (line + 1, "allow-recursion", 15) == 0) && + (line[16] == 0 || whitespace (line[16]))) + { + for (i = 16; whitespace (line[i]); i++); + strcpy (line, line + i); + flags |= ME_RECURSE; + if (!*line) + { + free (line); + continue; + } + } + + if ((*line == COMMAND_PREFIX) && (depth == 1) && + (strncmp (line + 1, "quote-arg", 9) == 0) && + (line[10] == 0 || whitespace (line[10]))) + { + for (i = 10; whitespace (line[i]); i++); + strcpy (line, line + i); + + if (arglist && arglist[0] && !arglist[1]) + { + flags |= ME_QUOTE_ARG; + if (!*line) + { + free (line); + continue; + } + } + else + line_error (_("@quote-arg only useful for single-argument macros")); + } + + if (*line == COMMAND_PREFIX + && (strncmp (line + 1, "macro ", 6) == 0 + || strncmp (line + 1, "rmacro ", 7) == 0)) + depth++; + + /* Incorrect implementation of nesting -- just check that the last + @end matches what we started with. Since nested macros don't + work in TeX anyway, this isn't worth the trouble to get right. */ + if (*line == COMMAND_PREFIX && strncmp (line + 1, "end macro", 9) == 0) + { + depth--; + last_end = "macro"; + } + if (*line == COMMAND_PREFIX && strncmp (line + 1, "end rmacro", 9) == 0) + { + depth--; + last_end = "rmacro"; + } + + if (depth) + { + if ((body_index + strlen (line) + 3) >= body_size) + body = xrealloc (body, body_size += 3 + strlen (line)); + strcpy (body + body_index, line); + body_index += strlen (line); + body[body_index++] = '\n'; + body[body_index] = 0; + } + free (line); + } + + /* Check that @end matched the macro command. */ + if (!STREQ (last_end, mactype)) + warning (_("mismatched @end %s with @%s"), last_end, mactype); + + /* If it was an empty macro like + @macro foo + @end macro + create an empty body. (Otherwise, the macro is not expanded.) */ + if (!body) + { + body = (char *)malloc(1); + *body = 0; + } + + /* We now have the name, the arglist, and the body. However, BODY + includes the final newline which preceded the `@end macro' text. + Delete it. */ + if (body && strlen (body)) + body[strlen (body) - 1] = 0; + + if (recursive) + flags |= ME_RECURSE; + + add_macro (name, arglist, body, input_filename, defining_line, flags); + + if (macro_expansion_output_stream && !executing_string) + remember_itext (input_text, input_text_offset); +} + +void +cm_macro () +{ + define_macro ("macro", 0); +} + +void +cm_rmacro () +{ + define_macro ("rmacro", 1); +} + +/* Delete the macro with name NAME. The macro is deleted from the list, + but it is also returned. If there was no macro defined, NULL is + returned. */ + +static MACRO_DEF * +delete_macro (name) + char *name; +{ + int i; + MACRO_DEF *def; + + def = NULL; + + for (i = 0; macro_list && (def = macro_list[i]); i++) + if (strcmp (def->name, name) == 0) + { + memmove (macro_list + i, macro_list + i + 1, + ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *)); + macro_list_len--; + break; + } + return def; +} + +void +cm_unmacro () +{ + int i; + char *line, *name; + MACRO_DEF *def; + + if (macro_expansion_output_stream && !executing_string) + me_append_before_this_command (); + + get_rest_of_line (0, &line); + + for (i = 0; line[i] && !whitespace (line[i]); i++); + name = xmalloc (i + 1); + memcpy (name, line, i); + name[i] = 0; + + def = delete_macro (name); + + if (def) + { + free (def->source_file); + free (def->name); + free (def->body); + + if (def->arglist) + { + int i; + + for (i = 0; def->arglist[i]; i++) + free (def->arglist[i]); + + free (def->arglist); + } + + free (def); + } + + free (line); + free (name); + + if (macro_expansion_output_stream && !executing_string) + remember_itext (input_text, input_text_offset); +} + +/* How to output sections of the input file verbatim. */ + +/* Set the value of POINTER's offset to OFFSET. */ +ITEXT * +remember_itext (pointer, offset) + char *pointer; + int offset; +{ + int i; + ITEXT *itext = NULL; + + /* If we have no info, initialize a blank list. */ + if (!itext_info) + { + itext_info = xmalloc ((itext_size = 10) * sizeof (ITEXT *)); + for (i = 0; i < itext_size; i++) + itext_info[i] = NULL; + } + + /* If the pointer is already present in the list, then set the offset. */ + for (i = 0; i < itext_size; i++) + if ((itext_info[i]) && + (itext_info[i]->pointer == pointer)) + { + itext = itext_info[i]; + itext_info[i]->offset = offset; + break; + } + + if (i == itext_size) + { + /* Find a blank slot (or create a new one), and remember the + pointer and offset. */ + for (i = 0; i < itext_size; i++) + if (itext_info[i] == NULL) + break; + + /* If not found, then add some slots. */ + if (i == itext_size) + { + int j; + + itext_info = xrealloc + (itext_info, (itext_size += 10) * sizeof (ITEXT *)); + + for (j = i; j < itext_size; j++) + itext_info[j] = NULL; + } + + /* Now add the pointer and the offset. */ + itext_info[i] = xmalloc (sizeof (ITEXT)); + itext_info[i]->pointer = pointer; + itext_info[i]->offset = offset; + itext = itext_info[i]; + } + return itext; +} + +/* Forget the input text associated with POINTER. */ +void +forget_itext (pointer) + char *pointer; +{ + int i; + + for (i = 0; i < itext_size; i++) + if (itext_info[i] && (itext_info[i]->pointer == pointer)) + { + free (itext_info[i]); + itext_info[i] = NULL; + break; + } +} + +/* Append the text which appeared in input_text from the last offset to + the character just before the command that we are currently executing. */ +void +me_append_before_this_command () +{ + int i; + + for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--) + ; + maybe_write_itext (input_text, i); +} + +/* Similar to execute_string, but only takes a single string argument, + and remembers the input text location, etc. */ +void +me_execute_string (execution_string) + char *execution_string; +{ + int saved_escape_html = escape_html; + int saved_in_paragraph = in_paragraph; + escape_html = me_executing_string == 0; + in_paragraph = 0; + + pushfile (); + input_text_offset = 0; + /* The following xstrdup is so we can relocate input_text at will. */ + input_text = xstrdup (execution_string); + input_filename = xstrdup (input_filename); + input_text_length = strlen (execution_string); + + remember_itext (input_text, 0); + + me_executing_string++; + reader_loop (); + free (input_text); + free (input_filename); + popfile (); + me_executing_string--; + + in_paragraph = saved_in_paragraph; + escape_html = saved_escape_html; +} + +/* A wrapper around me_execute_string which saves and restores + variables important for output generation. This is called + when we need to produce macro-expanded output for input which + leaves no traces in the Info output. */ +void +me_execute_string_keep_state (execution_string, append_string) + char *execution_string, *append_string; +{ + int op_orig, opcol_orig, popen_orig; + int fill_orig, newline_orig, indent_orig, meta_pos_orig; + + remember_itext (input_text, input_text_offset); + op_orig = output_paragraph_offset; + meta_pos_orig = meta_char_pos; + opcol_orig = output_column; + popen_orig = paragraph_is_open; + fill_orig = filling_enabled; + newline_orig = last_char_was_newline; + filling_enabled = 0; + indent_orig = no_indent; + no_indent = 1; + me_execute_string (execution_string); + if (append_string) + write_region_to_macro_output (append_string, 0, strlen (append_string)); + output_paragraph_offset = op_orig; + meta_char_pos = meta_pos_orig; + output_column = opcol_orig; + paragraph_is_open = popen_orig; + filling_enabled = fill_orig; + last_char_was_newline = newline_orig; + no_indent = indent_orig; +} + +/* Append the text which appears in input_text from the last offset to + the current OFFSET. */ +void +append_to_expansion_output (offset) + int offset; +{ + int i; + ITEXT *itext = NULL; + + for (i = 0; i < itext_size; i++) + if (itext_info[i] && itext_info[i]->pointer == input_text) + { + itext = itext_info[i]; + break; + } + + if (!itext) + return; + + if (offset > itext->offset) + { + write_region_to_macro_output (input_text, itext->offset, offset); + remember_itext (input_text, offset); + } +} + +/* Only write this input text iff it appears in our itext list. */ +void +maybe_write_itext (pointer, offset) + char *pointer; + int offset; +{ + int i; + ITEXT *itext = NULL; + + for (i = 0; i < itext_size; i++) + if (itext_info[i] && (itext_info[i]->pointer == pointer)) + { + itext = itext_info[i]; + break; + } + + if (itext && (itext->offset < offset)) + { + write_region_to_macro_output (itext->pointer, itext->offset, offset); + remember_itext (pointer, offset); + } +} + +void +write_region_to_macro_output (string, start, end) + char *string; + int start, end; +{ + if (macro_expansion_output_stream) + fwrite (string + start, 1, end - start, macro_expansion_output_stream); +} + +/* Aliases. */ + +typedef struct alias_struct +{ + char *alias; + char *mapto; + struct alias_struct *next; +} alias_type; + +static alias_type *aliases; + +/* @alias */ +void +cm_alias () +{ + alias_type *a = xmalloc (sizeof (alias_type)); + + skip_whitespace (); + get_until_in_line (1, "=", &(a->alias)); + discard_until ("="); + skip_whitespace (); + get_until_in_line (0, " ", &(a->mapto)); + + a->next = aliases; + aliases = a; +} + +/* Perform an alias expansion. Called from read_command. */ +char * +alias_expand (tok) + char *tok; +{ + alias_type *findit = aliases; + + while (findit) + if (strcmp (findit->alias, tok) == 0) + { + free (tok); + return alias_expand (xstrdup (findit->mapto)); + } + else + findit = findit->next; + + return tok; +} + +/* definfoenclose implementation. */ + +/* This structure is used to track enclosure macros. When an enclosure + macro is recognized, a pointer to the enclosure block corresponding + to its name is saved in the brace element for its argument. */ +typedef struct enclose_struct +{ + char *enclose; + char *before; + char *after; + struct enclose_struct *next; +} enclosure_type; + +static enclosure_type *enclosures; + +typedef struct enclosure_stack_struct +{ + enclosure_type *current; + struct enclosure_stack_struct *next; +} enclosure_stack_type; + +static enclosure_stack_type *enclosure_stack; + +/* @definfoenclose */ +void +cm_definfoenclose () +{ + enclosure_type *e = xmalloc (sizeof (enclosure_type)); + + skip_whitespace (); + get_until_in_line (1, ",", &(e->enclose)); + discard_until (","); + get_until_in_line (0, ",", &(e->before)); + discard_until (","); + get_until_in_line (0, "\n", &(e->after)); + + e->next = enclosures; + enclosures = e; +} + +/* If TOK is an enclosure command, push it on the enclosure stack and + return 1. Else return 0. */ + +int +enclosure_command (tok) + char *tok; +{ + enclosure_type *findit = enclosures; + + while (findit) + if (strcmp (findit->enclose, tok) == 0) + { + enclosure_stack_type *new = xmalloc (sizeof (enclosure_stack_type)); + new->current = findit; + new->next = enclosure_stack; + enclosure_stack = new; + + return 1; + } + else + findit = findit->next; + + return 0; +} + +/* actually perform the enclosure expansion */ +void +enclosure_expand (arg, start, end) + int arg, start, end; +{ + if (arg == START) + add_word (enclosure_stack->current->before); + else + { + enclosure_stack_type *temp; + + add_word (enclosure_stack->current->after); + + temp = enclosure_stack; + enclosure_stack = enclosure_stack->next; + free (temp); + } +} diff --git a/gnu/usr.bin/texinfo/makeinfo/macro.h b/gnu/usr.bin/texinfo/makeinfo/macro.h new file mode 100644 index 00000000000..4fac46b268d --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/macro.h @@ -0,0 +1,71 @@ +/* macro.h -- declarations for macro.c. + $Id: macro.h,v 1.1.1.1 2000/02/09 01:25:23 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MACRO_H +#define MACRO_H + +extern FILE *macro_expansion_output_stream; +extern char *macro_expansion_filename; +extern int me_executing_string; +extern int only_macro_expansion; + +/* Here is a structure used to remember input text strings and offsets + within them. */ +typedef struct { + char *pointer; /* Pointer to the input text. */ + int offset; /* Offset of the last character output. */ +} ITEXT; + +/* Macro definitions for user-defined commands. */ +typedef struct { + char *name; /* Name of the macro. */ + char **arglist; /* Args to replace when executing. */ + char *body; /* Macro body. */ + char *source_file; /* File where this macro is defined. */ + int source_lineno; /* Line number within FILENAME. */ + int inhibited; /* Nonzero means make find_macro () fail. */ + int flags; /* ME_RECURSE, ME_QUOTE_ARG, etc. */ +} MACRO_DEF; + +/* flags for MACRO_DEF */ +#define ME_RECURSE 0x01 +#define ME_QUOTE_ARG 0x02 + +extern void execute_macro (); +extern MACRO_DEF *find_macro (); +extern char *expand_macro (); + +extern ITEXT *remember_itext (); +extern void forget_itext (); +extern void maybe_write_itext (); +extern void write_region_to_macro_output (); +extern void append_to_expansion_output (); +extern void me_append_before_this_command (); +extern void me_execute_string (); + +extern char *alias_expand (); +extern int enclosure_command (); +extern void enclosure_expand (); + +/* The @commands. */ +extern void cm_macro (), cm_rmacro (), cm_unmacro (); +extern void cm_alias (), cm_definfoenclose (); + +#endif /* not MACRO_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/node.c b/gnu/usr.bin/texinfo/makeinfo/node.c new file mode 100644 index 00000000000..bb649d3f098 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/node.c @@ -0,0 +1,1568 @@ +/* node.c -- nodes for Texinfo. + $Id: node.c,v 1.1 2000/02/09 01:25:30 espie Exp $ + + Copyright (C) 1998, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include "cmds.h" +#include "files.h" +#include "footnote.h" +#include "macro.h" +#include "makeinfo.h" +#include "node.h" +#include "sectioning.h" +#include "insertion.h" + + +/* See comments in node.h. */ +NODE_REF *node_references = NULL; +NODE_REF *node_node_references = NULL; +TAG_ENTRY *tag_table = NULL; +int node_number = -1; +int current_section = 0; +int outstanding_node = 0; + +/* Adding nodes, and making tags. */ + +/* Start a new tag table. */ +void +init_tag_table () +{ + while (tag_table) + { + TAG_ENTRY *temp = tag_table; + free (temp->node); + free (temp->prev); + free (temp->next); + free (temp->up); + tag_table = tag_table->next_ent; + free (temp); + } +} + +/* Write out the contents of the existing tag table. + INDIRECT_P says how to format the output (it depends on whether the + table is direct or indirect). */ +static void +write_tag_table_internal (indirect_p) + int indirect_p; +{ + TAG_ENTRY *node; + int old_indent = no_indent; + + no_indent = 1; + filling_enabled = 0; + must_start_paragraph = 0; + close_paragraph (); + + if (!indirect_p) + { + no_indent = 1; + insert ('\n'); + } + + add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : ""); + + /* Do not collapse -- to -, etc., in node names. */ + in_fixed_width_font++; + + for (node = tag_table; node; node = node->next_ent) + { + if (node->flags & TAG_FLAG_ANCHOR) + { /* This reference is to an anchor. */ + execute_string ("Ref: %s", node->node); + } + else + { /* This reference is to a node. */ + execute_string ("Node: %s", node->node); + } + add_word_args ("\177%d\n", node->position); + } + + add_word ("\037\nEnd Tag Table\n"); + + /* Do not collapse -- to -, etc., in node names. */ + in_fixed_width_font--; + + flush_output (); + no_indent = old_indent; +} + +void +write_tag_table () +{ + write_tag_table_internal (0); /* Not indirect. */ +} + +void +write_tag_table_indirect () +{ + write_tag_table_internal (1); +} + +/* Convert "top" and friends into "Top". */ +static void +normalize_node_name (string) + char *string; +{ + if (strcasecmp (string, "Top") == 0) + strcpy (string, "Top"); +} + +char * +get_node_token (expand) + int expand; +{ + char *string; + + get_until_in_line (expand, ",", &string); + + if (curchar () == ',') + input_text_offset++; + + fix_whitespace (string); + + /* Force all versions of "top" to be "Top". */ + normalize_node_name (string); + + return string; +} + +/* Expand any macros and other directives in a node name, and + return the expanded name as an malloc'ed string. */ +char * +expand_node_name (node) + char *node; +{ + char *result = node; + + if (node) + { + /* Don't expand --, `` etc., in case somebody will want + to print the result. */ + in_fixed_width_font++; + result = expansion (node, 0); + in_fixed_width_font--; + fix_whitespace (result); + normalize_node_name (result); + } + return result; +} + +/* Look up NAME in the tag table, and return the associated + tag_entry. If the node is not in the table return NULL. */ +TAG_ENTRY * +find_node (name) + char *name; +{ + TAG_ENTRY *tag = tag_table; + char *expanded_name; + char n1 = name[0]; + + while (tag) + { + if (tag->node[0] == n1 && strcmp (tag->node, name) == 0) + return tag; + tag = tag->next_ent; + } + + if (!expensive_validation) + return NULL; + + /* Try harder. Maybe TAG_TABLE has the expanded NAME, or maybe NAME + is expanded while TAG_TABLE has its unexpanded form. This may + slow down the search, but if they want this feature, let them + pay! If they want it fast, they should write every node name + consistently (either always expanded or always unexpaned). */ + expanded_name = expand_node_name (name); + for (tag = tag_table; tag; tag = tag->next_ent) + { + if (STREQ (tag->node, expanded_name)) + break; + /* If the tag name doesn't have the command prefix, there's no + chance it could expand into anything but itself. */ + if (strchr (tag->node, COMMAND_PREFIX)) + { + char *expanded_node = expand_node_name (tag->node); + + if (STREQ (expanded_node, expanded_name)) + { + free (expanded_node); + break; + } + free (expanded_node); + } + } + free (expanded_name); + return tag; +} + +/* Similarly for next etc. references in a @node command, where we + don't care about most of the entries. */ +static void +remember_node_node_reference (node) + char *node; +{ + NODE_REF *temp = xmalloc (sizeof (NODE_REF)); + int number; + + if (!node) return; + temp->next = node_node_references; + temp->node = xstrdup (node); + temp->type = followed_reference; + number = number_of_node (node); + if (number) + temp->number = number; /* Already assigned. */ + else + { + node_number++; + temp->number = node_number; + } + node_node_references = temp; +} + +/* Remember NODE and associates. */ +void +remember_node (node, prev, next, up, position, line_no, flags) + char *node, *prev, *next, *up; + int position, line_no, flags; +{ + /* Check for existence of this tag already. */ + if (validating) + { + TAG_ENTRY *tag = find_node (node); + if (tag) + { + line_error (_("Node `%s' previously defined at line %d"), + node, tag->line_no); + return; + } + } + + if (!(flags & TAG_FLAG_ANCHOR)) + { + /* Make this the current node. */ + current_node = node; + } + + /* Add it to the list. */ + { + int number = number_of_node (node); + + TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY)); + new->node = node; + new->prev = prev; + new->next = next; + new->up = up; + new->position = position; + new->line_no = line_no; + new->filename = node_filename; + new->touched = 0; + new->flags = flags; + if (number) + new->number = number; /* Already assigned. */ + else + { + node_number++; + new->number = node_number; + } + new->next_ent = tag_table; + tag_table = new; + } + + if (html) + { /* Note the references to the next etc. nodes too. */ + remember_node_node_reference (next); + remember_node_node_reference (prev); + remember_node_node_reference (up); + } +} + +/* Remember this node name for later validation use. This is used to + remember menu references while reading the input file. After the + output file has been written, if validation is on, then we use the + contents of `node_references' as a list of nodes to validate. */ +void +remember_node_reference (node, line, type) + char *node; + int line; + enum reftype type; +{ + NODE_REF *temp = xmalloc (sizeof (NODE_REF)); + int number = number_of_node (node); + + temp->next = node_references; + temp->node = xstrdup (node); + temp->line_no = line; + temp->section = current_section; + temp->type = type; + temp->containing_node = xstrdup (current_node ? current_node : ""); + temp->filename = node_filename; + if (number) + temp->number = number; /* Already assigned. */ + else + { + node_number++; + temp->number = node_number; + } + + node_references = temp; +} + +static void +isolate_nodename (nodename) + char *nodename; +{ + int i, c; + int paren_seen, paren; + + if (!nodename) + return; + + canon_white (nodename); + paren_seen = paren = i = 0; + + if (*nodename == '.' || !*nodename) + { + *nodename = 0; + return; + } + + if (*nodename == '(') + { + paren++; + paren_seen++; + i++; + } + + for (; (c = nodename[i]); i++) + { + if (paren) + { + if (c == '(') + paren++; + else if (c == ')') + paren--; + + continue; + } + + /* If the character following the close paren is a space, then this + node has no more characters associated with it. */ + if (c == '\t' || + c == '\n' || + c == ',' || + ((paren_seen && nodename[i - 1] == ')') && + (c == ' ' || c == '.')) || + (c == '.' && + ((!nodename[i + 1] || + (cr_or_whitespace (nodename[i + 1])) || + (nodename[i + 1] == ')'))))) + break; + } + nodename[i] = 0; +} + +/* This function gets called at the start of every line while inside a + menu. It checks to see if the line starts with "* ", and if so and + REMEMBER_REF is nonzero, remembers the node reference as type + REF_TYPE that this menu refers to. input_text_offset is at the \n + just before the menu line. If REMEMBER_REF is zero, REF_TYPE is unused. */ +#define MENU_STARTER "* " +char * +glean_node_from_menu (remember_ref, ref_type) + int remember_ref; + enum reftype ref_type; +{ + int i, orig_offset = input_text_offset; + char *nodename; + char *line, *expanded_line; + char *old_input = input_text; + size_t old_size = input_text_length; + + if (strncmp (&input_text[input_text_offset + 1], + MENU_STARTER, + strlen (MENU_STARTER)) != 0) + return NULL; + else + input_text_offset += strlen (MENU_STARTER) + 1; + + /* The menu entry might include macro calls, so we need to expand them. */ + get_until ("\n", &line); + only_macro_expansion++; /* only expand macros in menu entries */ + expanded_line = expansion (line, 0); + only_macro_expansion--; + free (line); + input_text = expanded_line; + input_text_offset = 0; + input_text_length = strlen (expanded_line); + + get_until_in_line (0, ":", &nodename); + if (curchar () == ':') + input_text_offset++; + + if (curchar () != ':') + { + free (nodename); + get_until_in_line (0, "\n", &nodename); + isolate_nodename (nodename); + } + + input_text = old_input; + input_text_offset = orig_offset; + input_text_length = old_size; + free (expanded_line); + fix_whitespace (nodename); + normalize_node_name (nodename); + i = strlen (nodename); + if (i && nodename[i - 1] == ':') + nodename[i - 1] = 0; + + if (remember_ref) + remember_node_reference (nodename, line_number, ref_type); + + return nodename; +} + +/* Set the name of the current output file. */ +void +set_current_output_filename (fname) + const char *fname; +{ + if (current_output_filename) + free (current_output_filename); + current_output_filename = xstrdup (fname); +} + +/* The order is: nodename, nextnode, prevnode, upnode. + If all of the NEXT, PREV, and UP fields are empty, they are defaulted. + You must follow a node command which has those fields defaulted + with a sectioning command (e.g. @chapter) giving the "level" of that node. + It is an error not to do so. + The defaults come from the menu in this node's parent. */ +void +cm_node () +{ + char *node, *prev, *next, *up; + int new_node_pos, defaulting, this_section; + int no_warn = 0; + + if (strcmp (command, "nwnode") == 0) + no_warn = TAG_FLAG_NO_WARN; + + /* Get rid of unmatched brace arguments from previous commands. */ + discard_braces (); + + /* There also might be insertions left lying around that haven't been + ended yet. Do that also. */ + discard_insertions (1); + + if (!html && !already_outputting_pending_notes) + { + close_paragraph (); + output_pending_notes (); + } + + if (html && splitting && top_node_seen) + { + /* End the current split output file. */ + close_paragraph (); + output_pending_notes (); + start_paragraph (); + /* Fixme: html: need a navigation bar here. */ + add_word ("</body></html>\n"); + close_paragraph (); + fclose (output_stream); + output_stream = NULL; + } + + filling_enabled = indented_fill = 0; + new_node_pos = output_position; + if (!html || (html && splitting)) + current_footnote_number = 1; + + if (macro_expansion_output_stream && !executing_string) + append_to_expansion_output (input_text_offset + 1); + + /* Do not collapse -- to -, etc., in node names. */ + in_fixed_width_font++; + + /* While expanding the @node line, leave any non-macros + intact, so that the macro-expanded output includes them. */ + only_macro_expansion++; + node = get_node_token (1); + only_macro_expansion--; + next = get_node_token (0); + prev = get_node_token (0); + up = get_node_token (0); + + if (verbose_mode) + printf (_("Formatting node %s...\n"), node); + + if (macro_expansion_output_stream && !executing_string) + remember_itext (input_text, input_text_offset); + + no_indent = 1; + if (!no_headers && !html) + { + add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename); + + if (macro_expansion_output_stream && !executing_string) + me_execute_string (node); + else + execute_string ("%s", node); + filling_enabled = indented_fill = 0; + } + + /* Check for defaulting of this node's next, prev, and up fields. */ + defaulting = (*next == 0 && *prev == 0 && *up == 0); + + this_section = what_section (input_text + input_text_offset); + + /* If we are defaulting, then look at the immediately following + sectioning command (error if none) to determine the node's + level. Find the node that contains the menu mentioning this node + that is one level up (error if not found). That node is the "Up" + of this node. Default the "Next" and "Prev" from the menu. */ + if (defaulting) + { + NODE_REF *last_ref = NULL; + NODE_REF *ref = node_references; + + if (this_section < 0 && !STREQ (node, "Top")) + { + char *polite_section_name = "top"; + int i; + + for (i = 0; section_alist[i].name; i++) + if (section_alist[i].level == current_section + 1) + { + polite_section_name = section_alist[i].name; + break; + } + + line_error + (_("Node `%s' requires a sectioning command (e.g. %c%s)"), + node, COMMAND_PREFIX, polite_section_name); + } + else + { + if (strcmp (node, "Top") == 0) + { + /* Default the NEXT pointer to be the first menu item in + this node, if there is a menu in this node. We have to + try very hard to find the menu, as it may be obscured + by execution_strings which are on the filestack. For + every member of the filestack which has a FILENAME + member which is identical to the current INPUT_FILENAME, + search forward from that offset. */ + int saved_input_text_offset = input_text_offset; + int saved_input_text_length = input_text_length; + char *saved_input_text = input_text; + FSTACK *next_file = filestack; + + int orig_offset, orig_size; + + /* No matter what, make this file point back at `(dir)'. */ + free (up); + up = xstrdup ("(dir)"); /* html fixxme */ + + while (1) + { + orig_offset = input_text_offset; + orig_size = + search_forward (node_search_string, orig_offset); + + if (orig_size < 0) + orig_size = input_text_length; + + input_text_offset = search_forward ("\n@menu", orig_offset); + if (input_text_offset > -1 + && cr_or_whitespace (input_text[input_text_offset + 6])) + { + char *nodename_from_menu = NULL; + + input_text_offset = + search_forward ("\n* ", input_text_offset); + + if (input_text_offset != -1) + nodename_from_menu = glean_node_from_menu (0, 0); + + if (nodename_from_menu) + { + free (next); + next = nodename_from_menu; + break; + } + } + + /* We got here, so it hasn't been found yet. Try + the next file on the filestack if there is one. */ + if (next_file + && FILENAME_CMP (next_file->filename, input_filename) + == 0) + { + input_text = next_file->text; + input_text_offset = next_file->offset; + input_text_length = next_file->size; + next_file = next_file->next; + } + else + { /* No more input files to check. */ + break; + } + } + + input_text = saved_input_text; + input_text_offset = saved_input_text_offset; + input_text_length = saved_input_text_length; + } + } + + /* Fix the level of the menu references in the Top node, iff it + was declared with @top, and no subsequent reference was found. */ + if (top_node_seen && !non_top_node_seen) + { + /* Then this is the first non-@top node seen. */ + int level; + + level = set_top_section_level (this_section - 1); + non_top_node_seen = 1; + + while (ref) + { + if (ref->section == level) + ref->section = this_section - 1; + ref = ref->next; + } + + ref = node_references; + } + + while (ref) + { + if (ref->section == (this_section - 1) + && ref->type == menu_reference + && strcmp (ref->node, node) == 0) + { + char *containing_node = ref->containing_node; + + free (up); + up = xstrdup (containing_node); + + if (last_ref + && last_ref->type == menu_reference + && strcmp (last_ref->containing_node, containing_node) == 0) + { + free (next); + next = xstrdup (last_ref->node); + } + + while (ref->section == this_section - 1 + && ref->next + && ref->next->type != menu_reference) + ref = ref->next; + + if (ref->next && ref->type == menu_reference + && strcmp (ref->next->containing_node, containing_node) == 0) + { + free (prev); + prev = xstrdup (ref->next->node); + } + else if (!ref->next + && strcasecmp (ref->containing_node, "Top") == 0) + { + free (prev); + prev = xstrdup (ref->containing_node); + } + break; + } + last_ref = ref; + ref = ref->next; + } + } + + /* Insert the correct args if we are expanding macros, and the node's + pointers weren't defaulted. */ + if (macro_expansion_output_stream && !executing_string && !defaulting) + { + char *temp; + int op_orig = output_paragraph_offset; + int meta_pos_orig = meta_char_pos; + int extra = html ? strlen (node) : 0; + + temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up)); + sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up); + me_execute_string (temp); + free (temp); + + output_paragraph_offset = op_orig; + meta_char_pos = meta_pos_orig; + } + + if (!*node) + { + line_error (_("No node name specified for `%c%s' command"), + COMMAND_PREFIX, command); + free (node); + free (next); next = NULL; + free (prev); prev= NULL; + free (up); up = NULL; + node_number++; /* else it doesn't get bumped */ + } + else + { + if (!*next) { free (next); next = NULL; } + if (!*prev) { free (prev); prev = NULL; } + if (!*up) { free (up); up = NULL; } + remember_node (node, prev, next, up, new_node_pos, line_number, no_warn); + outstanding_node = 1; + } + + if (html) + { + char *tem; + + if (splitting) + { /* this code not operational, we do not currently split html */ + char filename[20]; + + sprintf (filename, "node%d.html", number_of_node (node)); + output_stream = fopen (filename, "w"); + if (output_stream == NULL) + { + fs_error (filename); + xexit (1); + } + set_current_output_filename (filename); + /* FIXME: when this code is operational, we will need to + expand node, next, prev, and up before output. */ + add_word_args ("<html><head><title>%s</title>", node); + if (next) add_link (next, "rel=next"); + if (prev) add_link (prev, "rel=previous"); + if (up) add_link (up, "rel=up"); + add_word ("</head>\n<body>\n"); + } + + if (!splitting && no_headers) + { /* cross refs need a name="#anchor" even if we're not writing headers*/ + add_word ("<a name=\""); + tem = expand_node_name (node); + add_anchor_name (tem, 0); + add_word ("\"></a>"); + free (tem); + } + + if (splitting || !no_headers) + { /* Navigation bar. The <p> avoids the links area running + on with old Lynxen. */ + add_word_args ("<p>%s\n", splitting ? "" : "<hr>"); + add_word_args ("%s<a name=\"", _("Node:")); + tem = expand_node_name (node); + add_anchor_name (tem, 0); + add_word_args ("\">%s</a>", tem); + free (tem); + + if (next) + { + add_word (",\n"); + add_word (_("Next:")); + add_word ("<a rel=next href=\""); + tem = expansion (next, 0); + add_anchor_name (tem, 1); + add_word_args ("\">%s</a>", tem); + free (tem); + } + if (prev) + { + add_word (",\n"); + add_word (_("Previous:")); + add_word ("<a rel=previous href=\""); + tem = expansion (prev, 0); + add_anchor_name (tem, 1); + add_word_args ("\">%s</a>", tem); + free (tem); + } + if (up) + { + add_word (",\n"); + add_word (_("Up:")); + add_word ("<a rel=up href=\""); + tem = expansion (up, 0); + add_anchor_name (tem, 1); + add_word_args ("\">%s</a>", tem); + free (tem); + } + /* html fixxme: we want a `top' or `contents' link here. */ + + add_word_args ("\n%s<br>\n", splitting ? "<hr>" : ""); + } + } + + else if (!no_headers) + { + if (macro_expansion_output_stream) + me_inhibit_expansion++; + + /* These strings are not translatable. */ + if (next) + { + execute_string (", Next: %s", next); + filling_enabled = indented_fill = 0; + } + if (prev) + { + execute_string (", Prev: %s", prev); + filling_enabled = indented_fill = 0; + } + if (up) + { + execute_string (", Up: %s", up); + filling_enabled = indented_fill = 0; + } + if (macro_expansion_output_stream) + me_inhibit_expansion--; + } + + close_paragraph (); + no_indent = 0; + + /* Change the section only if there was a sectioning command. */ + if (this_section >= 0) + current_section = this_section; + + if (current_node && STREQ (current_node, "Top")) + top_node_seen = 1; + + filling_enabled = 1; + in_fixed_width_font--; +} + +/* Cross-reference target at an arbitrary spot. */ +void +cm_anchor (arg) + int arg; +{ + char *anchor; + + if (arg == END) + return; + + /* Parse the anchor text. */ + anchor = get_xref_token (1); + + /* In HTML mode, need to actually produce some output. */ + if (html) + { + /* If this anchor is at the beginning of a new paragraph, make + sure a new paragraph is indeed started. */ + if (!paragraph_is_open) + { + start_paragraph (); + if (!in_fixed_width_font || in_menu || in_detailmenu) + { + insert_string ("<p>"); + in_paragraph = 1; + } + } + add_word ("<a name=\""); + add_anchor_name (anchor, 0); + add_word ("\"></a>"); + } + + /* Save it in the tag table. */ + remember_node (anchor, NULL, NULL, NULL, output_position + output_column, + line_number, TAG_FLAG_ANCHOR); +} + +/* Find NODE in REF_LIST. */ +static NODE_REF * +find_node_reference (node, ref_list) + char *node; + NODE_REF *ref_list; +{ + NODE_REF *orig_ref_list = ref_list; + char *expanded_node; + + while (ref_list) + { + if (strcmp (node, ref_list->node) == 0) + break; + ref_list = ref_list->next; + } + + if (ref_list || !expensive_validation) + return ref_list; + + /* Maybe NODE is not expanded yet. This may be SLOW. */ + expanded_node = expand_node_name (node); + for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next) + { + if (STREQ (expanded_node, ref_list->node)) + break; + if (strchr (ref_list->node, COMMAND_PREFIX)) + { + char *expanded_ref = expand_node_name (ref_list->node); + + if (STREQ (expanded_node, expanded_ref)) + { + free (expanded_ref); + break; + } + free (expanded_ref); + } + } + free (expanded_node); + return ref_list; +} + +void +free_node_references () +{ + NODE_REF *list, *temp; + + list = node_references; + + while (list) + { + temp = list; + free (list->node); + free (list->containing_node); + list = list->next; + free (temp); + } + node_references = NULL; +} + +void +free_node_node_references () +{ + NODE_REF *list, *temp; + + list = node_references; + + while (list) + { + temp = list; + free (list->node); + list = list->next; + free (temp); + } + node_node_references = NULL; +} + +/* Return the number assigned to a named node in either the tag_table + or node_references list or zero if no number has been assigned. */ +int +number_of_node (node) + char *node; +{ + NODE_REF *temp_ref; + TAG_ENTRY *temp_node = find_node (node); + + if (temp_node) + return temp_node->number; + else if ((temp_ref = find_node_reference (node, node_references))) + return temp_ref->number; + else if ((temp_ref = find_node_reference (node, node_node_references))) + return temp_ref->number; + else + return 0; +} + +/* validation */ + +/* Return 1 if TAG (at LINE) correctly validated, or 0 if not. + LABEL is the (translated) description of the type of reference -- + Menu, Cross, Next, etc. */ + +static int +validate (tag, line, label) + char *tag; + int line; + char *label; +{ + TAG_ENTRY *result; + + /* If there isn't a tag to verify, or if the tag is in another file, + then it must be okay. */ + if (!tag || !*tag || *tag == '(') + return 1; + + /* Otherwise, the tag must exist. */ + result = find_node (tag); + + if (!result) + { + line_number = line; + line_error (_("%s reference to nonexistent node `%s'"), label, tag); + return 0; + } + result->touched++; + return 1; +} + +/* The strings here are followed in the message by `reference to...' in + the `validate' routine. They are only used in messages, thus are + translated. */ +static char * +reftype_type_string (type) + enum reftype type; +{ + switch (type) + { + case menu_reference: + return _("Menu"); + case followed_reference: + return _("Cross"); + default: + return "Internal-bad-reference-type"; + } +} + +static void +validate_other_references (ref_list) + NODE_REF *ref_list; +{ + char *old_input_filename = input_filename; + + while (ref_list) + { + input_filename = ref_list->filename; + validate (ref_list->node, ref_list->line_no, + reftype_type_string (ref_list->type)); + ref_list = ref_list->next; + } + input_filename = old_input_filename; +} + +/* Validation of an info file. + Scan through the list of tag entries touching the Prev, Next, and Up + elements of each. It is an error not to be able to touch one of them, + except in the case of external node references, such as "(DIR)". + + If the Prev is different from the Up, + then the Prev node must have a Next pointing at this node. + + Every node except Top must have an Up. + The Up node must contain some sort of reference, other than a Next, + to this node. + + If the Next is different from the Next of the Up, + then the Next node must have a Prev pointing at this node. */ +void +validate_file (tag_table) + TAG_ENTRY *tag_table; +{ + char *old_input_filename = input_filename; + TAG_ENTRY *tags = tag_table; + + while (tags) + { + TAG_ENTRY *temp_tag; + char *tem1, *tem2; + + input_filename = tags->filename; + line_number = tags->line_no; + + /* If this is a "no warn" node, don't validate it in any way. */ + if (tags->flags & TAG_FLAG_NO_WARN) + { + tags = tags->next_ent; + continue; + } + + /* If this node has a Next, then make sure that the Next exists. */ + if (tags->next) + { + validate (tags->next, tags->line_no, _("Next")); + + /* If the Next node exists, and there is no Up, then make sure + that the Prev of the Next points back. But do nothing if + we aren't supposed to issue warnings about this node. */ + temp_tag = find_node (tags->next); + if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN)) + { + char *prev = temp_tag->prev; + int you_lose = !prev || !STREQ (prev, tags->node); + + if (you_lose && expensive_validation) + { + tem1 = expand_node_name (prev); + tem2 = expand_node_name (tags->node); + + if (STREQ (tem1, tem2)) + you_lose = 0; + free (tem1); + free (tem2); + } + if (you_lose) + { + line_error (_("Next field of node `%s' not pointed to"), + tags->node); + line_number = temp_tag->line_no; + input_filename = temp_tag->filename; + line_error (_("This node (%s) has the bad Prev"), + temp_tag->node); + input_filename = tags->filename; + line_number = tags->line_no; + temp_tag->flags |= TAG_FLAG_PREV_ERROR; + } + } + } + + /* Validate the Prev field if there is one, and we haven't already + complained about it in some way. You don't have to have a Prev + field at this stage. */ + if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev) + { + int valid_p = validate (tags->prev, tags->line_no, _("Prev")); + + if (!valid_p) + tags->flags |= TAG_FLAG_PREV_ERROR; + else + { /* If the Prev field is not the same as the Up field, + then the node pointed to by the Prev field must have + a Next field which points to this node. */ + int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up); + + if (!prev_equals_up && expensive_validation) + { + tem1 = expand_node_name (tags->prev); + tem2 = expand_node_name (tags->up); + prev_equals_up = STREQ (tem1, tem2); + free (tem1); + free (tem2); + } + if (!prev_equals_up) + { + temp_tag = find_node (tags->prev); + + /* If we aren't supposed to issue warnings about the + target node, do nothing. */ + if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN)) + /* Do nothing. */ ; + else + { + int you_lose = !temp_tag->next + || !STREQ (temp_tag->next, tags->node); + + if (temp_tag->next && you_lose && expensive_validation) + { + tem1 = expand_node_name (temp_tag->next); + tem2 = expand_node_name (tags->node); + if (STREQ (tem1, tem2)) + you_lose = 0; + free (tem1); + free (tem2); + } + if (you_lose) + { + line_error + (_("Prev field of node `%s' not pointed to"), + tags->node); + line_number = temp_tag->line_no; + input_filename = temp_tag->filename; + line_error (_("This node (%s) has the bad Next"), + temp_tag->node); + input_filename = tags->filename; + line_number = tags->line_no; + temp_tag->flags |= TAG_FLAG_NEXT_ERROR; + } + } + } + } + } + + if (!tags->up + && !(tags->flags & TAG_FLAG_ANCHOR) + && strcasecmp (tags->node, "Top") != 0) + line_error (_("`%s' has no Up field"), tags->node); + else if (tags->up) + { + int valid_p = validate (tags->up, tags->line_no, _("Up")); + + /* If node X has Up: Y, then warn if Y fails to have a menu item + or note pointing at X, if Y isn't of the form "(Y)". */ + if (valid_p && *tags->up != '(') + { + NODE_REF *nref; + NODE_REF *tref = NULL; + NODE_REF *list = node_references; + + for (;;) + { + nref = find_node_reference (tags->node, list); + if (!nref) + break; + + if (strcmp (nref->containing_node, tags->up) == 0) + { + if (nref->type != menu_reference) + { + tref = nref; + list = nref->next; + } + else + break; + } + list = nref->next; + } + + if (!nref) + { + if (!tref && expensive_validation) + { + /* Sigh... This might be AWFULLY slow, but if + they want this feature, they'll have to pay! + We do all the loop again expanding each + containing_node reference as we go. */ + char *tags_up = expand_node_name (tags->up); + char *tem; + + list = node_references; + + for (;;) + { + nref = find_node_reference (tags->node, list); + if (!nref) + break; + tem = expand_node_name (nref->containing_node); + if (STREQ (tem, tags_up)) + { + if (nref->type != menu_reference) + tref = nref; + else + { + free (tem); + break; + } + } + free (tem); + list = nref->next; + } + } + if (!nref && !tref) + { + temp_tag = find_node (tags->up); + line_number = temp_tag->line_no; + input_filename = temp_tag->filename; + line_error ( + _("Node `%s' lacks menu item for `%s' despite being its Up target"), + tags->up, tags->node); + line_number = tags->line_no; + input_filename = tags->filename; + } + } + } + } + tags = tags->next_ent; + } + + validate_other_references (node_references); + /* We have told the user about the references which didn't exist. + Now tell him about the nodes which aren't referenced. */ + + for (tags = tag_table; tags; tags = tags->next_ent) + { + /* If this node is a "no warn" node, do nothing. */ + if (tags->flags & TAG_FLAG_NO_WARN) + { + tags = tags->next_ent; + continue; + } + + /* Special hack. If the node in question appears to have + been referenced more than REFERENCE_WARNING_LIMIT times, + give a warning. */ + if (tags->touched > reference_warning_limit) + { + input_filename = tags->filename; + line_number = tags->line_no; + warning (_("node `%s' has been referenced %d times"), + tags->node, tags->touched); + } + + if (tags->touched == 0) + { + input_filename = tags->filename; + line_number = tags->line_no; + + /* Notice that the node "Top" is special, and doesn't have to + be referenced. Anchors don't have to be referenced + either, you might define them for another document. */ + if (strcasecmp (tags->node, "Top") != 0 + && !(tags->flags & TAG_FLAG_ANCHOR)) + warning (_("unreferenced node `%s'"), tags->node); + } + } + input_filename = old_input_filename; +} + + +/* Splitting */ + +/* Return true if the tag entry pointed to by TAGS is the last node. + This means only anchors follow. */ + +static int +last_node_p (tags) + TAG_ENTRY *tags; +{ + int last = 1; + while (tags->next_ent) { + tags = tags->next_ent; + if (tags->flags & TAG_FLAG_ANCHOR) + ; + else + { + last = 0; + break; + } + } + + return last; +} + + +/* Split large output files into a series of smaller files. Each file + is pointed to in the tag table, which then gets written out as the + original file. The new files have the same name as the original file + with a "-num" attached. SIZE is the largest number of bytes to allow + in any single split file. */ +void +split_file (filename, size) + char *filename; + int size; +{ + char *root_filename, *root_pathname; + char *the_file, *filename_part (); + struct stat fileinfo; + long file_size; + char *the_header; + int header_size; + int dos_file_names = 0; /* if nonzero, don't exceed 8+3 limits */ + + /* Can only do this to files with tag tables. */ + if (!tag_table) + return; + + if (size == 0) + size = DEFAULT_SPLIT_SIZE; + + if ((stat (filename, &fileinfo) != 0) || + (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD)) + return; + file_size = (long) fileinfo.st_size; + + the_file = find_and_load (filename); + if (!the_file) + return; + + root_filename = filename_part (filename); + root_pathname = pathname_part (filename); + + /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */ + dos_file_names = !HAVE_LONG_FILENAMES (root_pathname ? root_pathname : "."); + + if (!root_pathname) + root_pathname = xstrdup (""); + + /* Start splitting the file. Walk along the tag table + outputting sections of the file. When we have written + all of the nodes in the tag table, make the top-level + pointer file, which contains indirect pointers and + tags for the nodes. */ + { + int which_file = 1; + TAG_ENTRY *tags = tag_table; + char *indirect_info = NULL; + + /* Remember the `header' of this file. The first tag in the file is + the bottom of the header; the top of the file is the start. */ + the_header = xmalloc (1 + (header_size = tags->position)); + memcpy (the_header, the_file, header_size); + + while (tags) + { + int file_top, file_bot, limit; + + /* Have to include the Control-_. */ + file_top = file_bot = tags->position; + limit = file_top + size; + + /* If the rest of this file is only one node, then + that is the entire subfile. */ + if (last_node_p (tags)) + { + int i = tags->position + 1; + char last_char = the_file[i]; + + while (i < file_size) + { + if ((the_file[i] == '\037') && + ((last_char == '\n') || + (last_char == '\014'))) + break; + else + last_char = the_file[i]; + i++; + } + file_bot = i; + tags = tags->next_ent; + goto write_region; + } + + /* Otherwise, find the largest number of nodes that can fit in + this subfile. */ + for (; tags; tags = tags->next_ent) + { + if (last_node_p (tags)) + { + /* This entry is the last node. Search forward for the end + of this node, and that is the end of this file. */ + int i = tags->position + 1; + char last_char = the_file[i]; + + while (i < file_size) + { + if ((the_file[i] == '\037') && + ((last_char == '\n') || + (last_char == '\014'))) + break; + else + last_char = the_file[i]; + i++; + } + file_bot = i; + + if (file_bot < limit) + { + tags = tags->next_ent; + goto write_region; + } + else + { + /* Here we want to write out everything before the last + node, and then write the last node out in a file + by itself. */ + file_bot = tags->position; + goto write_region; + } + } + + /* Write region only if this was a node, not an anchor. */ + if (tags->next_ent->position > limit + && !(tags->flags & TAG_FLAG_ANCHOR)) + { + if (tags->position == file_top) + tags = tags->next_ent; + + file_bot = tags->position; + + write_region: + { + int fd; + char *split_filename, *split_basename; + unsigned root_len = strlen (root_filename); + + split_filename = xmalloc (10 + strlen (root_pathname) + + root_len); + split_basename = xmalloc (10 + root_len); + sprintf (split_basename, "%s-%d", root_filename, which_file); + if (dos_file_names) + { + char *dot = strchr (split_basename, '.'); + unsigned base_len = strlen (split_basename); + + if (dot) + { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */ + dot[1] = 'i'; + memmove (which_file <= 99 ? dot + 2 : dot + 1, + split_basename + root_len + 1, + strlen (split_basename + root_len + 1) + 1); + } + else if (base_len > 8) + { + /* Make foobar-1, .., fooba-10, .., foob-100, ... */ + unsigned numlen = base_len - root_len; + + memmove (split_basename + 8 - numlen, + split_basename + root_len, numlen + 1); + } + } + sprintf (split_filename, "%s%s", root_pathname, + split_basename); + + fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666); + if (fd < 0 + || write (fd, the_header, header_size) != header_size + || write (fd, the_file + file_top, file_bot - file_top) + != (file_bot - file_top) + || (close (fd)) < 0) + { + perror (split_filename); + if (fd != -1) + close (fd); + xexit (1); + } + + if (!indirect_info) + { + indirect_info = the_file + file_top; + sprintf (indirect_info, "\037\nIndirect:\n"); + indirect_info += strlen (indirect_info); + } + + sprintf (indirect_info, "%s: %d\n", + split_basename, file_top); + + free (split_basename); + free (split_filename); + indirect_info += strlen (indirect_info); + which_file++; + break; + } + } + } + } + + /* We have sucessfully created the subfiles. Now write out the + original again. We must use `output_stream', or + write_tag_table_indirect () won't know where to place the output. */ + output_stream = fopen (filename, "w"); + if (!output_stream) + { + perror (filename); + xexit (1); + } + + { + int distance = indirect_info - the_file; + fwrite (the_file, 1, distance, output_stream); + + /* Inhibit newlines. */ + paragraph_is_open = 0; + + write_tag_table_indirect (); + fclose (output_stream); + free (the_header); + free (the_file); + return; + } + } +} diff --git a/gnu/usr.bin/texinfo/makeinfo/node.h b/gnu/usr.bin/texinfo/makeinfo/node.h new file mode 100644 index 00000000000..3539f88f480 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/node.h @@ -0,0 +1,111 @@ +/* node.h -- declarations for Node. + $Id: node.h,v 1.1.1.1 2000/02/09 01:25:30 espie Exp $ + + Copyright (C) 1996, 97, 98, 99 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef NODE_H +#define NODE_H + +/* The various references that we know about. */ +/* What we remember for each node. */ +typedef struct tentry +{ + struct tentry *next_ent; + char *node; /* Name of this node. */ + char *prev; /* Name of "Prev:" for this node. */ + char *next; /* Name of "Next:" for this node. */ + char *up; /* Name of "Up:" for this node. */ + int position; /* Output file position of this node. */ + int line_no; /* Defining line in source file. */ + char *filename; /* The file that this node was found in. */ + int touched; /* Nonzero means this node has been referenced. */ + int flags; + int number; /* Number for this node, relevant for HTML + splitting -- from use+define order, not just + define. */ +} TAG_ENTRY; + +/* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a, + we turn on this flag bit in node-b's tag entry. This means that when + it is time to validate node-b, we don't report an additional error + if there was no "Prev" field. */ +#define TAG_FLAG_PREV_ERROR 1 +#define TAG_FLAG_NEXT_ERROR 2 +#define TAG_FLAG_UP_ERROR 4 +#define TAG_FLAG_NO_WARN 8 +#define TAG_FLAG_IS_TOP 16 +#define TAG_FLAG_ANCHOR 32 + +/* Menu reference, *note reference, and validation hacking. */ + +/* A structure to remember references with. A reference to a node is + either an entry in a menu, or a cross-reference made with [px]ref. */ +typedef struct node_ref +{ + struct node_ref *next; + char *node; /* Name of node referred to. */ + char *containing_node; /* Name of node containing this reference. */ + int line_no; /* Line number where the reference occurs. */ + int section; /* Section level where the reference occurs. */ + char *filename; /* Name of file where the reference occurs. */ + enum reftype type; /* Type of reference, either menu or note. */ + int number; /* Number for this node, relevant for + HTML splitting -- from use+define + order, not just define. */ +} NODE_REF; + +/* The linked list of such structures. */ +extern NODE_REF *node_references; + +/* A similar list for references occuring in @node next + and similar references, needed for HTML. */ +extern NODE_REF *node_node_references; + +/* List of all nodes. */ +extern TAG_ENTRY *tag_table; + +/* Counter for setting node_ref.number; zero is Top. */ +extern int node_number; + +/* The current node's section level. */ +extern int current_section; + +/* Nonzero when the next sectioning command should generate an anchor + corresponding to the current node in HTML mode. */ +extern int outstanding_node; + +extern TAG_ENTRY *find_node (); + +/* A search string which is used to find a line defining a node. */ +DECLARE (char *, node_search_string, "\n@node "); + +/* Extract node name from a menu item. */ +extern char *glean_node_from_menu (); + +/* Remember a node for later validation. */ +extern void remember_node_reference (); + +/* Remember the name of the current output file. */ +extern void set_current_output_filename (); + +/* Expand macros and commands in the node name and canonicalize + whitespace in the resulting expansion. */ +extern char *expand_node_name (); + +#endif /* NODE_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/sectioning.c b/gnu/usr.bin/texinfo/makeinfo/sectioning.c new file mode 100644 index 00000000000..5ec18dfd5b0 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/sectioning.c @@ -0,0 +1,691 @@ +/* sectioning.c -- all related stuff @chapter, @section... @contents + $Id: sectioning.c,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */ + +#include "system.h" +#include "cmds.h" +#include "macro.h" +#include "makeinfo.h" +#include "node.h" +#include "toc.h" +#include "sectioning.h" + +/* See comment in sectioning.h. */ +section_alist_type section_alist[] = { + { "unnumberedsubsubsec", 5, ENUM_SECT_NO, TOC_YES }, + { "unnumberedsubsec", 4, ENUM_SECT_NO, TOC_YES }, + { "unnumberedsec", 3, ENUM_SECT_NO, TOC_YES }, + { "unnumbered", 2, ENUM_SECT_NO, TOC_YES }, + + { "appendixsubsubsec", 5, ENUM_SECT_APP, TOC_YES }, /* numbered like A.X.X.X */ + { "appendixsubsec", 4, ENUM_SECT_APP, TOC_YES }, + { "appendixsec", 3, ENUM_SECT_APP, TOC_YES }, + { "appendixsection", 3, ENUM_SECT_APP, TOC_YES }, + { "appendix", 2, ENUM_SECT_APP, TOC_YES }, + + { "subsubsec", 5, ENUM_SECT_YES, TOC_YES }, + { "subsubsection", 5, ENUM_SECT_YES, TOC_YES }, + { "subsection", 4, ENUM_SECT_YES, TOC_YES }, + { "section", 3, ENUM_SECT_YES, TOC_YES }, + { "chapter", 2, ENUM_SECT_YES, TOC_YES }, + + { "subsubheading", 5, ENUM_SECT_NO, TOC_NO }, + { "subheading", 4, ENUM_SECT_NO, TOC_NO }, + { "heading", 3, ENUM_SECT_NO, TOC_NO }, + { "chapheading", 2, ENUM_SECT_NO, TOC_NO }, + { "majorheading", 2, ENUM_SECT_NO, TOC_NO }, + + { "top", 1, ENUM_SECT_NO, TOC_YES }, + { NULL, 0, 0, 0 } +}; + +/* The argument of @settitle, used for HTML. */ +char *title = NULL; + + +#define APPENDIX_MAGIC 1024 +#define UNNUMBERED_MAGIC 2048 + +/* Number memory for every level @chapter, @section, + @subsection, @subsubsection. */ +static int numbers [] = { 0, 0, 0, 0 }; + +/* enum_marker == APPENDIX_MAGIC then we are counting appendencies + enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area. + Handling situations like this: + @unnumbered .. + @section ... */ +static int enum_marker = 0; + +/* Organized by level commands. That is, "*" == chapter, "=" == section. */ +static char *scoring_characters = "*=-."; + +/* Amount to offset the name of sectioning commands to levels by. */ +static int section_alist_offset = 0; + + +/* num == ENUM_SECT_NO means unnumbered (should never call this) + num == ENUM_SECT_YES means numbered + num == ENUM_SECT_APP means numbered like A.1 and so on */ +char * +get_sectioning_number (level, num) + int level; + int num; +{ + static char s[100]; /* should ever be enough for 99.99.99.99 + Appendix A.1 */ + + char *p; + int i; + + s[0] = 0; + + /* create enumeration in front of chapter, section, subsection and so on. */ + for (i = 0; i < level; i++) + { + p = s + strlen (s); + if ((i == 0) && (enum_marker == APPENDIX_MAGIC)) + sprintf (p, "%c.", numbers[i] + 64); /* Should be changed to + be more portable */ + else + sprintf (p, "%d.", numbers[i]); + } + + /* the last number is never followed by a dot */ + p = s + strlen (s); + if ((num == ENUM_SECT_APP) + && (i == 0) + && (enum_marker == APPENDIX_MAGIC)) + sprintf (p, _("Appendix %c "), numbers[i] + 64); + else + sprintf (p, "%d ", numbers[i]); + + return s; +} + + +/* Set the level of @top to LEVEL. Return the old level of @top. */ +int +set_top_section_level (level) + int level; +{ + int i, result = -1; + + for (i = 0; section_alist[i].name; i++) + if (strcmp (section_alist[i].name, "top") == 0) + { + result = section_alist[i].level; + section_alist[i].level = level; + break; + } + return result; +} + + +/* return the index of the given sectioning command in section_alist */ +int +search_sectioning (text) + char *text; +{ + int i; + char *t; + + /* ignore the optional command prefix */ + if (text[0] == COMMAND_PREFIX) + text++; + + for (i = 0; (t = section_alist[i].name); i++) + { + if (strcmp (t, text) == 0) + { + return i; + } + } + return -1; +} + +/* Return an integer which identifies the type section present in TEXT. */ +int +what_section (text) + char *text; +{ + int index, j; + char *temp; + int return_val; + + find_section_command: + for (j = 0; text[j] && cr_or_whitespace (text[j]); j++); + if (text[j] != COMMAND_PREFIX) + return -1; + + text = text + j + 1; + + /* We skip @c, @comment, and @?index commands. */ + if ((strncmp (text, "comment", strlen ("comment")) == 0) || + (text[0] == 'c' && cr_or_whitespace (text[1])) || + (strcmp (text + 1, "index") == 0)) + { + while (*text++ != '\n'); + goto find_section_command; + } + + /* Handle italicized sectioning commands. */ + if (*text == 'i') + text++; + + for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++); + + temp = xmalloc (1 + j); + strncpy (temp, text, j); + temp[j] = 0; + + index = search_sectioning (temp); + free (temp); + if (index >= 0) + { + return_val = section_alist[index].level + section_alist_offset; + if (return_val < 0) + return_val = 0; + else if (return_val > 5) + return_val = 5; + return return_val; + } + return -1; +} + + +void +sectioning_underscore (cmd) + char *cmd; +{ + char character; + char *temp; + int level; + + temp = xmalloc (2 + strlen (cmd)); + temp[0] = COMMAND_PREFIX; + strcpy (&temp[1], cmd); + level = what_section (temp); + free (temp); + level -= 2; + + if (level < 0) + level = 0; + + if (html) + sectioning_html (level, cmd); + else + { + character = scoring_characters[level]; + insert_and_underscore (level, character, cmd); + } +} + +/* insert_and_underscore and sectioning_html are the + only functions which call this. + I have created this, because it was exactly the same + code in both functions. */ +static char * +handle_enum_increment (level, index) + int level; + int index; +{ + /* special for unnumbered */ + if (number_sections && section_alist[index].num == ENUM_SECT_NO) + { + if (level == 0 + && enum_marker != UNNUMBERED_MAGIC) + enum_marker = UNNUMBERED_MAGIC; + } + /* enumerate only things which are allowed */ + if (number_sections && section_alist[index].num) + { + /* reset the marker if we get into enumerated areas */ + if (section_alist[index].num == ENUM_SECT_YES + && level == 0 + && enum_marker == UNNUMBERED_MAGIC) + enum_marker = 0; + /* This is special for appendix; if we got the first + time an appendix command then we are entering appendix. + Thats the point we have to start countint with A, B and so on. */ + if (section_alist[index].num == ENUM_SECT_APP + && level == 0 + && enum_marker != APPENDIX_MAGIC) + { + enum_marker = APPENDIX_MAGIC; + numbers [0] = 0; /* this means we start with Appendix A */ + } + + /* only increment counters if we are not in unnumbered + area. This handles situations like this: + @unnumbered .... This sets enum_marker to UNNUMBERED_MAGIC + @section .... */ + if (enum_marker != UNNUMBERED_MAGIC) + { + int i; + + /* reset all counters which are one level deeper */ + for (i = level; i < 3; i++) + numbers [i + 1] = 0; + + numbers[level]++; + return xstrdup + (get_sectioning_number (level, section_alist[index].num)); + } + } /* if (number_sections)... */ + + return xstrdup (""); +} + + +/* Insert the text following input_text_offset up to the end of the line + in a new, separate paragraph. Directly underneath it, insert a + line of WITH_CHAR, the same length of the inserted text. */ +void +insert_and_underscore (level, with_char, cmd) + int level; + int with_char; + char *cmd; +{ + int i, len; + int index; + int old_no_indent; + unsigned char *starting_pos, *ending_pos; + char *temp; + + close_paragraph (); + filling_enabled = indented_fill = 0; + old_no_indent = no_indent; + no_indent = 1; + + if (macro_expansion_output_stream && !executing_string) + append_to_expansion_output (input_text_offset + 1); + + get_rest_of_line (0, &temp); + starting_pos = output_paragraph + output_paragraph_offset; + + index = search_sectioning (cmd); + if (index < 0) + { + /* should never happen, but a poor guy, named Murphy ... */ + warning (_("Internal error (search_sectioning) \"%s\"!"), cmd); + return; + } + + /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the + Info output and in TOC, but only SECTION-NAME in the macro-expanded + output. */ + + /* Step 1: produce "X.Y" and add it to Info output. */ + add_word (handle_enum_increment (level, index)); + + /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output. */ + if (macro_expansion_output_stream && !executing_string) + { + char *temp1 = xmalloc (2 + strlen (temp)); + sprintf (temp1, "%s\n", temp); + remember_itext (input_text, input_text_offset); + me_execute_string (temp1); + free (temp1); + } + else + execute_string ("%s\n", temp); + + /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and + insert it into the TOC. */ + ending_pos = output_paragraph + output_paragraph_offset; + if (section_alist[index].toc == TOC_YES) + toc_add_entry (substring (starting_pos, ending_pos - 1), + level, current_node, NULL); + + free (temp); + + len = (ending_pos - starting_pos) - 1; + for (i = 0; i < len; i++) + add_char (with_char); + insert ('\n'); + close_paragraph (); + filling_enabled = 1; + no_indent = old_no_indent; +} + +/* Insert the text following input_text_offset up to the end of the + line as an HTML heading element of the appropriate `level' and + tagged as an anchor for the current node.. */ +void +sectioning_html (level, cmd) + int level; + char *cmd; +{ + static int toc_ref_count = 0; + int index; + int old_no_indent; + unsigned char *starting_pos, *ending_pos; + char *temp, *toc_anchor = NULL; + + close_paragraph (); + filling_enabled = indented_fill = 0; + old_no_indent = no_indent; + no_indent = 1; + + add_word_args ("<h%d>", level + 1); /* level 0 is <h1> */ + + /* If we are outside of any node, produce an anchor that + the TOC could refer to. */ + if (!current_node || !*current_node) + { + starting_pos = output_paragraph + output_paragraph_offset; + add_word_args ("<a name=\"TOC%d\">", toc_ref_count++); + toc_anchor = substring (starting_pos + 9, + output_paragraph + output_paragraph_offset); + } + starting_pos = output_paragraph + output_paragraph_offset; + + if (macro_expansion_output_stream && !executing_string) + append_to_expansion_output (input_text_offset + 1); + + get_rest_of_line (0, &temp); + + index = search_sectioning (cmd); + if (index < 0) + { + /* should never happen, but a poor guy, named Murphy ... */ + warning (_("Internal error (search_sectioning) \"%s\"!"), cmd); + return; + } + + /* Produce "X.Y" and add it to HTML output. */ + add_word (handle_enum_increment (level, index)); + + /* add the section name to both HTML and macro-expanded output. */ + if (macro_expansion_output_stream && !executing_string) + { + remember_itext (input_text, input_text_offset); + me_execute_string (temp); + write_region_to_macro_output ("\n", 0, 1); + } + else + execute_string ("%s", temp); + + ending_pos = output_paragraph + output_paragraph_offset; + + /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it + into the TOC. */ + if (section_alist[index].toc == TOC_YES) + toc_add_entry (substring (starting_pos, ending_pos), + level, current_node, toc_anchor); + + free (temp); + + if (outstanding_node) + outstanding_node = 0; + + add_word_args ("</h%d>", level+1); + close_paragraph(); + filling_enabled = 1; + no_indent = old_no_indent; +} + + +/* Shift the meaning of @section to @chapter. */ +void +cm_raisesections () +{ + discard_until ("\n"); + section_alist_offset--; +} + +/* Shift the meaning of @chapter to @section. */ +void +cm_lowersections () +{ + discard_until ("\n"); + section_alist_offset++; +} + +/* The command still works, but prints a warning message in addition. */ +void +cm_ideprecated (arg, start, end) + int arg, start, end; +{ + warning (_("%c%s is obsolete; use %c%s instead"), + COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1); + sectioning_underscore (command + 1); +} + + +/* Treat this just like @unnumbered. The only difference is + in node defaulting. */ +void +cm_top () +{ + /* It is an error to have more than one @top. */ + if (top_node_seen && strcmp (current_node, "Top") != 0) + { + TAG_ENTRY *tag = tag_table; + + line_error (_("Node with %ctop as a section already exists"), + COMMAND_PREFIX); + + while (tag) + { + if (tag->flags & TAG_FLAG_IS_TOP) + { + int old_line_number = line_number; + char *old_input_filename = input_filename; + + line_number = tag->line_no; + input_filename = tag->filename; + line_error (_("Here is the %ctop node"), COMMAND_PREFIX); + input_filename = old_input_filename; + line_number = old_line_number; + return; + } + tag = tag->next_ent; + } + } + else + { + TAG_ENTRY *top_node = find_node ("Top"); + top_node_seen = 1; + + /* It is an error to use @top before you have used @node. */ + if (!tag_table) + { + char *top_name; + + get_rest_of_line (0, &top_name); + line_error (_("%ctop used before %cnode, defaulting to %s"), + COMMAND_PREFIX, COMMAND_PREFIX, top_name); + execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name); + free (top_name); + return; + } + else if (html && splitting) + { + char *next = top_node ? top_node->next : NULL; + + add_word ("<p>"); + if (next) + { + add_word (_("Next:")); + add_word ("<a rel=next href=\""); + add_anchor_name (next, 1); + add_word ("\">"); + execute_string (next); + add_word ("</a>\n"); + } + } + + cm_unnumbered (); + + /* The most recently defined node is the top node. */ + tag_table->flags |= TAG_FLAG_IS_TOP; + + /* Now set the logical hierarchical level of the Top node. */ + { + int orig_offset = input_text_offset; + + input_text_offset = search_forward (node_search_string, orig_offset); + + if (input_text_offset > 0) + { + int this_section; + + /* We have encountered a non-top node, so mark that one exists. */ + non_top_node_seen = 1; + + /* Move to the end of this line, and find out what the + sectioning command is here. */ + while (input_text[input_text_offset] != '\n') + input_text_offset++; + + if (input_text_offset < input_text_length) + input_text_offset++; + + this_section = what_section (input_text + input_text_offset); + + /* If we found a sectioning command, then give the top section + a level of this section - 1. */ + if (this_section != -1) + set_top_section_level (this_section - 1); + } + input_text_offset = orig_offset; + } + } +} + +/* The remainder of the text on this line is a chapter heading. */ +void +cm_chapter () +{ + sectioning_underscore ("chapter"); +} + +/* The remainder of the text on this line is a section heading. */ +void +cm_section () +{ + sectioning_underscore ("section"); +} + +/* The remainder of the text on this line is a subsection heading. */ +void +cm_subsection () +{ + sectioning_underscore ("subsection"); +} + +/* The remainder of the text on this line is a subsubsection heading. */ +void +cm_subsubsection () +{ + sectioning_underscore ("subsubsection"); +} + +/* The remainder of the text on this line is an unnumbered heading. */ +void +cm_unnumbered () +{ + sectioning_underscore ("unnumbered"); +} + +/* The remainder of the text on this line is an unnumbered section heading. */ +void +cm_unnumberedsec () +{ + sectioning_underscore ("unnumberedsec"); +} + +/* The remainder of the text on this line is an unnumbered + subsection heading. */ +void +cm_unnumberedsubsec () +{ + sectioning_underscore ("unnumberedsubsec"); +} + +/* The remainder of the text on this line is an unnumbered + subsubsection heading. */ +void +cm_unnumberedsubsubsec () +{ + sectioning_underscore ("unnumberedsubsubsec"); +} + +/* The remainder of the text on this line is an appendix heading. */ +void +cm_appendix () +{ + sectioning_underscore ("appendix"); +} + +/* The remainder of the text on this line is an appendix section heading. */ +void +cm_appendixsec () +{ + sectioning_underscore ("appendixsec"); +} + +/* The remainder of the text on this line is an appendix subsection heading. */ +void +cm_appendixsubsec () +{ + sectioning_underscore ("appendixsubsec"); +} + +/* The remainder of the text on this line is an appendix + subsubsection heading. */ +void +cm_appendixsubsubsec () +{ + sectioning_underscore ("appendixsubsubsec"); +} + +/* Compatibility functions substitute for chapter, section, etc. */ +void +cm_majorheading () +{ + sectioning_underscore ("majorheading"); +} + +void +cm_chapheading () +{ + sectioning_underscore ("chapheading"); +} + +void +cm_heading () +{ + sectioning_underscore ("heading"); +} + +void +cm_subheading () +{ + sectioning_underscore ("subheading"); +} + +void +cm_subsubheading () +{ + sectioning_underscore ("subsubheading"); +} diff --git a/gnu/usr.bin/texinfo/makeinfo/sectioning.h b/gnu/usr.bin/texinfo/makeinfo/sectioning.h new file mode 100644 index 00000000000..2941088f1f3 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/sectioning.h @@ -0,0 +1,80 @@ +/* sectioning.h -- all related stuff @chapter, @section... @contents + $Id: sectioning.h,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */ + +#ifndef SECTIONING_H +#define SECTIONING_H + +/* Sectioning. */ +extern void + cm_chapter (), cm_unnumbered (), cm_appendix (), cm_top (), + cm_section (), cm_unnumberedsec (), cm_appendixsec (), + cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (), + cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (), + cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (), + cm_majorheading (), cm_raisesections (), cm_lowersections (), + + cm_ideprecated (); + +extern void + sectioning_underscore (), insert_and_underscore (); + +extern int what_section (); + + + +/* is needed in node.c */ +extern int set_top_section_level (); + +extern void sectioning_html (); +extern int what_section (); + +/* The argument of @settitle, used for HTML. */ +extern char *title; + + +/* Here is a structure which associates sectioning commands with + an integer that reflects the depth of the current section. */ +typedef struct +{ + char *name; + int level; /* I can't replace the levels with defines + because it is changed during run */ + int num; /* ENUM_SECT_NO means no enumeration... + ENUM_SECT_YES means enumerated version + ENUM_SECT_APP appendix (Character enumerated + at first position */ + int toc; /* TOC_NO means do not enter in toc; + TOC_YES means enter it in toc */ +} section_alist_type; + +extern section_alist_type section_alist[]; + +/* enumerate sections */ +#define ENUM_SECT_NO 0 +#define ENUM_SECT_YES 1 +#define ENUM_SECT_APP 2 + +/* make entries into toc no/yes */ +#define TOC_NO 0 +#define TOC_YES 1 + + +#endif /* not SECTIONING_H */ diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.am b/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.am new file mode 100644 index 00000000000..3e502ed4f32 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.am @@ -0,0 +1,18 @@ +## Makefile.am for texinfo/tests/makeinfo. +## Run automake in ../.. to produce Makefile.in from this. + +TESTS = cond \ + html-extrali html-min html-manuals html-para html-title \ + menu-whitespace \ + no-headers \ + node-expand node-value node-whitespace \ + top top2 + +noinst_SCRIPTS = $(TESTS) + +EXTRA_DIST = $(noinst_SCRIPTS) \ + cond.txi \ + html-extrali.txi html-min.txi html-para.txi html-title.txi \ + menu-whitespace.txi \ + node-expand.txi node-value.txi node-whitespace.txi \ + top.txi top2.txi diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.in b/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.in new file mode 100644 index 00000000000..50dedc8403c --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/Makefile.in @@ -0,0 +1,223 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +DATADIRNAME = @DATADIRNAME@ +GENCAT = @GENCAT@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GT_NO = @GT_NO@ +GT_YES = @GT_YES@ +INCLUDE_LOCALE_H = @INCLUDE_LOCALE_H@ +INSTOBJEXT = @INSTOBJEXT@ +INTLDEPS = @INTLDEPS@ +INTLLIBS = @INTLLIBS@ +INTLOBJS = @INTLOBJS@ +MAKEINFO = @MAKEINFO@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +TERMLIBS = @TERMLIBS@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +l = @l@ + +TESTS = cond html-extrali html-min html-manuals html-para html-title menu-whitespace no-headers node-expand node-value node-whitespace top top2 + + +noinst_SCRIPTS = $(TESTS) + +EXTRA_DIST = $(noinst_SCRIPTS) cond.txi html-extrali.txi html-min.txi html-para.txi html-title.txi menu-whitespace.txi node-expand.txi node-value.txi node-whitespace.txi top.txi top2.txi + +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +SCRIPTS = $(noinst_SCRIPTS) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = gtar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps makeinfo/tests/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = makeinfo/tests + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +check-TESTS: $(TESTS) + @failed=0; all=0; \ + srcdir=$(srcdir); export srcdir; \ + for tst in $(TESTS); do \ + if test -f $$tst; then dir=.; \ + else dir="$(srcdir)"; fi; \ + if $(TESTS_ENVIRONMENT) $$dir/$$tst; then \ + all=`expr $$all + 1`; \ + echo "PASS: $$tst"; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + failed=`expr $$failed + 1`; \ + echo "FAIL: $$tst"; \ + fi; \ + done; \ + if test "$$failed" -eq 0; then \ + banner="All $$all tests passed"; \ + else \ + banner="$$failed of $$all tests failed"; \ + fi; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + echo "$$dashes"; \ + test "$$failed" -eq 0 +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile $(SCRIPTS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: tags distdir check-TESTS info-am info dvi-am dvi check check-am \ +installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/cond b/gnu/usr.bin/texinfo/makeinfo/tests/cond new file mode 100644 index 00000000000..30c28bdd25a --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/cond @@ -0,0 +1,32 @@ +#!/bin/sh +# Test conditional text. + +: ${srcdir=.} + +# Default Info output. +../makeinfo -o cond.out $srcdir/cond.txi || exit 1 +egrep 'This is (ifnothtml|ifinfo|ifnottex) text' cond.out >/dev/null \ + || exit 2 +test `fgrep ' text.' cond.out | wc -l` -eq 3 || exit 3 + +# Default HTML output. +../makeinfo --html -o cond.out $srcdir/cond.txi || exit 1 +egrep 'This is (html|ifhtml|ifnotinfo|ifnottex) text' cond.out >/dev/null \ + || exit 2 +test `fgrep ' text.' cond.out | wc -l` -eq 4 || exit 3 + +# --ifhtml off, --ifinfo off, --iftex off. +../makeinfo --no-ifhtml --no-ifinfo --no-iftex -o cond.out $srcdir/cond.txi || exit 1 +egrep 'This is ifnot(html|info|tex) text' cond.out >/dev/null \ + || exit 2 +test `fgrep ' text.' cond.out | wc -l` -eq 3 || exit 3 + +# Do we really need to test all the other permutations? + +# --ifhtml on, --ifinfo on, --iftex on. +../makeinfo --ifhtml --ifinfo --iftex -o cond.out $srcdir/cond.txi || exit 1 +egrep 'This is (html|ifhtml|ifinfo|tex|iftex) text' cond.out >/dev/null \ + || exit 2 +test `fgrep ' text.' cond.out | wc -l` -eq 5 || exit 3 + +rm -f cond.out cond.info diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/cond.txi b/gnu/usr.bin/texinfo/makeinfo/tests/cond.txi new file mode 100644 index 00000000000..b60242322dc --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/cond.txi @@ -0,0 +1,40 @@ +\input texinfo +@setfilename cond.info + +@node Top + +@html +This is html text. +@end html + +@ifhtml +This is ifhtml text. +@end ifhtml + +@ifnothtml +This is ifnothtml text. +@end ifnothtml + + +@ifinfo +This is ifinfo text. +@end ifinfo + +@ifnotinfo +This is ifnotinfo text. +@end ifnotinfo + + +@tex +This is tex text. +@end tex + +@iftex +This is iftex text. +@end iftex + +@ifnottex +This is ifnottex text. +@end ifnottex + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali b/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali new file mode 100644 index 00000000000..22ecd896ad7 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali @@ -0,0 +1,13 @@ +#!/bin/sh +# Test no extra <li> from @menu. + +li_count=`../makeinfo --html -o - ${srcdir-.}/html-extrali.txi 2>/dev/null \ +| grep '<li>' \ +| wc -l` + +if test "$li_count" -ne 1; then + echo "$li_count <li>s instead of one." >&2 + exit 1 +else + exit 0 +fi diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali.txi b/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali.txi new file mode 100644 index 00000000000..281aa7b3193 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-extrali.txi @@ -0,0 +1,11 @@ +\input texinfo +@setfilename menuli.info + +@c extra <li> generated after <menu> +@c From: Marius Groeger <mag@sysgo.de>, 13nov98. + +@menu +* entry1:: +@end menu + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-manuals b/gnu/usr.bin/texinfo/makeinfo/tests/html-manuals new file mode 100644 index 00000000000..5e9523c820e --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-manuals @@ -0,0 +1,12 @@ +#!/bin/sh +# Test that all the distribution manuals can be converted to HTML. + +: ${srcdir=.} + +for manual in info.texi info-stnd.texi texinfo.txi; do + base=`echo $manual | sed 's/\.te*xi$//'` + ../makeinfo --html -I$srcdir/../../doc --no-split \ + $srcdir/../../doc/$manual -o $base.html \ + || exit 1 + rm -f $base.html +done diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-min b/gnu/usr.bin/texinfo/makeinfo/tests/html-min new file mode 100644 index 00000000000..e5971530afc --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-min @@ -0,0 +1,8 @@ +#!/bin/sh +# Test that a minimal Texinfo file can be converted to HTML. + +../makeinfo --html ${srcdir-.}/html-min.txi +exit_status=$? + +rm -f html-min.html +exit $exit_status diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-min.txi b/gnu/usr.bin/texinfo/makeinfo/tests/html-min.txi new file mode 100644 index 00000000000..116519e0611 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-min.txi @@ -0,0 +1,12 @@ +\input texinfo +@setfilename html-min.info +@settitle HTML min test + +@node Top +@top Top of HTML min test + +Top. + +Second paragraph. + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-para b/gnu/usr.bin/texinfo/makeinfo/tests/html-para new file mode 100644 index 00000000000..ba3191bf18f --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-para @@ -0,0 +1,8 @@ +#!/bin/sh +# Test that paragraph beginnings in HTML work ok. + +../makeinfo --html ${srcdir-.}/html-para.txi +exit_status=$? + +rm -f html-para.html +exit $exit_status diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-para.txi b/gnu/usr.bin/texinfo/makeinfo/tests/html-para.txi new file mode 100644 index 00000000000..c6e34fd2513 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-para.txi @@ -0,0 +1,24 @@ +\input texinfo +@setfilename html-para.info +@settitle HTML paragraph beginning test + +@c <p> is emitted at paragraph beginning, which makes the text +@c between START and END not exactly what some cm_xxx functions +@c expect, when they are called by pop_and_call_brace. + +@set val @@value@{@} +@definfoenclose foo,\\,// + +@node Top +@top Top of HTML paragraph test + +@value{val} should work at the beginning of a new paragraph. + +@dotless{i} dotless should not trigger error messages at the beginning +of a new paragraph. + +@sc{small-caps} should work at the beginning of a new paragraph. + +@foo{@@definfoenclose} should work at the beginning of a new paragraph. + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-title b/gnu/usr.bin/texinfo/makeinfo/tests/html-title new file mode 100644 index 00000000000..425226155dd --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-title @@ -0,0 +1,13 @@ +#!/bin/sh +# Test that titles with @ commands don't produce markup in the <title>. +# (And that the @ commands get expanded.) + +if ../makeinfo --html ${srcdir-.}/html-title.txi; then + grep '^<title>@[^<>]*</title>$' html-title.html >/dev/null + exit_status=$? +else + exit_status=1 +fi + +#rm -f html-title.html +exit $exit_status diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/html-title.txi b/gnu/usr.bin/texinfo/makeinfo/tests/html-title.txi new file mode 100644 index 00000000000..2e2f25c579f --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/html-title.txi @@ -0,0 +1,12 @@ +\input texinfo +@setfilename html-title.info +@settitle @@title @sc{html} @code{test} + +@node Top +@top Top of @@title @sc{html} @code{test} + +Top. + +Second paragraph. + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace b/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace new file mode 100644 index 00000000000..d512fc1bcb9 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace @@ -0,0 +1,71 @@ +#!/bin/sh +# Test that a minimal Texinfo file can be converted to HTML. + +: ${srcdir=.} +input=`basename $0`.txi + +../makeinfo -o /dev/null $srcdir/$input +exit $? + +Date: 07 Dec 1998 11:23:44 +0100 +From: Andreas Schwab <schwab@issan.informatik.uni-dortmund.de> +To: bug-texinfo@gnu.org +Subject: Makeinfo mishandles defaulted node links + +The following example demonstrates a bug in makeinfo: + +$ cat top.texi +@setfilename top.info + +@node Top +@top Top + +@menu +* first:: +@end menu + +@node first +@chapter first + +@menu @c +* second:: +@end menu + +@node second +@section second +$ makeinfo top.texi +Making info file `top.info' from `top.texi'. +./top.texi:3: Next field of node `Top' not pointed to. +./top.texi:17: This node (second) has the bad Prev. +makeinfo: Removing output file `/home/as/test/top.info' due to errors; use --force to preserve. + +Makeinfo is being confused by the whitespace after @menu, or rather by its +absence. + + +1998-12-06 Andreas Schwab <schwab@issan.cs.uni-dortmund.de> + + * makeinfo/node.c (cm_node): When searching for @menu don't + require a space after it. + +--- texinfo-3.12b/makeinfo/node.c.~1~ Mon Oct 26 23:14:59 1998 ++++ texinfo-3.12b/makeinfo/node.c Sun Dec 6 00:23:59 1998 +@@ -523,9 +523,10 @@ + orig_size = size_of_input_text; + + input_text_offset = +- search_forward ("\n@menu ", orig_offset); ++ search_forward ("\n@menu", orig_offset); + +- if (input_text_offset > -1) ++ if (input_text_offset > -1 ++ && cr_or_whitespace (input_text[input_text_offset + 6])) + { + char *nodename_from_menu = NULL; + + +-- +Andreas Schwab "And now for something +schwab@issan.cs.uni-dortmund.de completely different" +schwab@gnu.org + diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace.txi b/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace.txi new file mode 100644 index 00000000000..fd1c39f7ebf --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/menu-whitespace.txi @@ -0,0 +1,18 @@ +@setfilename top.info + +@node Top +@top Top + +@menu +* first:: +@end menu + +@node first +@chapter first + +@menu @c +* second:: +@end menu + +@node second +@section second diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/no-headers b/gnu/usr.bin/texinfo/makeinfo/tests/no-headers new file mode 100644 index 00000000000..17570bb6f48 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/no-headers @@ -0,0 +1,8 @@ +#!/bin/sh +# Test that info.texi works with --no-headers (this includes node +# pointer defaulting). + +: ${srcdir=.} +docdir=$srcdir/../../doc + +../makeinfo --no-headers -o /dev/null -I$docdir info.texi diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/node-expand b/gnu/usr.bin/texinfo/makeinfo/tests/node-expand new file mode 100644 index 00000000000..12a67a43f82 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-expand @@ -0,0 +1,11 @@ +#!/bin/sh +# Test command expansion in node names. + +: ${srcdir=.} + +../makeinfo --commands-in-node-names $srcdir/node-expand.txi +test -s node-expand.info +exit_status=$? + +rm -f node-expand.info +exit $exit_status diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/node-expand.txi b/gnu/usr.bin/texinfo/makeinfo/tests/node-expand.txi new file mode 100644 index 00000000000..42cb614d308 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-expand.txi @@ -0,0 +1,64 @@ +\input texinfo.tex @c -*-texinfo-*- + +@setfilename node-expand.info + +@c Makeinfo should expand non-macros such as @@ and @value +@c in node names and node references, including menus. +@c This file deliberately references "Node 1" both via +@c @value and directly; this should not trigger any errors, +@c as long as --commands-in-node-names is used. +@c Index entries should all be expanded as well. + +@set node1 Node 1 + +@ifnottex + +@node Top, (dir), (dir), (dir) +@top + +@end ifnottex + +@menu +* @value{node1} :: +* @@node `2':: +* ``node'' with---tricks:: +@end menu + +@xref{@@node `2'}. + +@node @value{node1}, @@node `2', Top, Top +@chapter Chapter 1 + +@cindex entry for chapter 1 +This is chapter 1. +@xref{@@node `2'}. + +@set sec1 Section 1.1 + +@menu +* @value{sec1}:: +@end menu + +@node Section 1.1, , Node 1, Node 1 +@comment node-name, next, previous, up + +@cindex entry for section 1.1 +This is section 1.1. + +@node @@node `2', ``node'' with---tricks, @value{node1}, Top +@comment node-name, next, previous, up +@chapter Node 2 + +@cindex entry for chapter 2 +This is chapter 2. +@xref{@value{node1}}. @xref{Node 1}. +@xref{``node'' with---tricks, Node with some tricks}. + +@node ``node'' with---tricks, , @@node `2', Top + +@cindex tricks +Another node. + +@printindex cp + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/node-value b/gnu/usr.bin/texinfo/makeinfo/tests/node-value new file mode 100644 index 00000000000..90c250bc17b --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-value @@ -0,0 +1,11 @@ +#!/bin/sh +# Test @value expansion in node names. + +: ${srcdir=.} + +../makeinfo $srcdir/node-value.txi +grep -v "No Value" node-value.info >/dev/null +exit_status=$? + +rm -f node-value.info +exit $exit_status diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/node-value.txi b/gnu/usr.bin/texinfo/makeinfo/tests/node-value.txi new file mode 100644 index 00000000000..ba1173f9a89 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-value.txi @@ -0,0 +1,15 @@ +\input texinfo +@setfilename node-value.info +@set a--foo bar + +@node Top, @value{a--foo}, (dir), (dir) +@top Var @value{a--foo} +@value{a--foo} + +@node @value{a--foo}, BarFoo, Top, (dir) +@chapter BarFoo + +@node BarFoo, , @value{a--foo}, (dir) +@chapter bar + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace b/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace new file mode 100644 index 00000000000..7cb568554dc --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace @@ -0,0 +1,11 @@ +#!/bin/sh +# Test whitespace collapse in node names. + +: ${srcdir=.} + +../makeinfo $srcdir/node-whitespace.txi +test -s node-whitespace.info +exit_status=$? + +rm -f node-whitespace.info +exit $exit_status diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace.txi b/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace.txi new file mode 100644 index 00000000000..df1253fc507 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/node-whitespace.txi @@ -0,0 +1,30 @@ +\input texinfo.tex @c -*-texinfo-*- + +@setfilename node-whitespace.info + +@c Makeinfo should collapse whitespace in node names. + +@ifnottex + +@node Top +@top + +@end ifnottex + +@menu +* Chap 1.3 :: +* Chap 1.4 :Chap 1.4. +@end menu + +@node Chap 1.3 +@chapter Chap 1.3 + +Can I reach here? + +@node Chap 1.4 +@chapter Another space test + +How about here? +@xref{Chap 1.3}. + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/top b/gnu/usr.bin/texinfo/makeinfo/tests/top new file mode 100644 index 00000000000..852194a71cd --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/top @@ -0,0 +1,10 @@ +#!/bin/sh +# Test that a top node can be ignored. + +: ${srcdir=.} +../makeinfo $srcdir/top.txi || exit 1 + +# Expected warnings due to use of @ifinfo instead of @ifnottex. +../makeinfo --no-split --no-warn --html $srcdir/top.txi -o top.html || exit 1 + +rm -f top*.html top.info diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/top.txi b/gnu/usr.bin/texinfo/makeinfo/tests/top.txi new file mode 100644 index 00000000000..e8c3cbbc078 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/top.txi @@ -0,0 +1,25 @@ +\input texinfo +@setfilename top.info +@settitle top test + +@c This traditional top node uses @ifinfo for testing. +@c Therefore there will be warnings when processing with --html. +@c The solution is to use @ifnottex instead. + +@ifinfo +@node Top +@top Top test + +Typical top node. +@end ifinfo + +@menu +* Subnode:: +@end menu + +@node Subnode +@chapter Subnode + +Subnode. + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/top2 b/gnu/usr.bin/texinfo/makeinfo/tests/top2 new file mode 100644 index 00000000000..d38984654d5 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/top2 @@ -0,0 +1,11 @@ +#!/bin/sh +# Test that a bare top node does not crash with --html. + +: ${srcdir=.} + +# But this input file is erroneous, so throw away errors. +../makeinfo --force -o top2.html --html $srcdir/top2.txi 2>/dev/null +test -s top2.html +exit_status=$? +rm -f top2.html +exit $exit_status diff --git a/gnu/usr.bin/texinfo/makeinfo/tests/top2.txi b/gnu/usr.bin/texinfo/makeinfo/tests/top2.txi new file mode 100644 index 00000000000..ade7214b0c2 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/tests/top2.txi @@ -0,0 +1,7 @@ +\input texinfo +@setfilename top.info + +@node start +@top + +@bye diff --git a/gnu/usr.bin/texinfo/makeinfo/toc.c b/gnu/usr.bin/texinfo/makeinfo/toc.c new file mode 100644 index 00000000000..447ad5d0388 --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/toc.c @@ -0,0 +1,476 @@ +/* toc.c -- table of contents handling. + $Id: toc.c,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */ + +#include "system.h" +#include "makeinfo.h" +#include "cmds.h" +#include "files.h" +#include "macro.h" +#include "node.h" +#include "lang.h" +#include "makeinfo.h" +#include "sectioning.h" +#include "toc.h" + + + + +/* array of toc entries */ +static TOC_ENTRY_ELT **toc_entry_alist = NULL; + +/* toc_counter start from 0 ... n for every @chapter, @section ... */ +static int toc_counter = 0; + +/* the file where we found the @contents directive */ +char *contents_filename; + +/* the file where we found the @shortcontents directive */ +char *shortcontents_filename; + +static const char contents_placebo[] = "\n...Table of Contents...\n"; +static const char shortcontents_placebo[] = "\n...Short Contents...\n"; +static const char lots_of_stars[] = +"***************************************************************************"; + + +/* Routine to add an entry to the table of contents */ +int +toc_add_entry (tocname, level, node_name, anchor) + char *tocname; + int level; + char *node_name; + char *anchor; +{ + char *tocname_and_node, *expanded_node, *s, *d; + + if (!node_name) + node_name = ""; + + /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is + NULL */ + toc_entry_alist = xrealloc (toc_entry_alist, + (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *)); + + toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT)); + + if (html) + { + /* We need to insert the expanded node name into the TOC, so + that when we eventually output the TOC, its <A REF= link will + point to the <A NAME= tag created by cm_node in the navigation + bar. We cannot expand the containing_node member, for the + reasons explained in the WARNING below. We also cannot wait + with the node name expansion until the TOC is actually output, + since by that time the macro definitions may have been changed. + So instead we store in the tocname member the expanded node + name and the TOC name concatenated together (with the necessary + HTML markup), since that's how they are output. */ + if (!anchor) + s = expanded_node = expand_node_name (node_name); + else + expanded_node = anchor; + /* Sigh... Need to HTML-escape the expanded node name like + add_anchor_name does, except that we are not writing this to + the output, so can't use add_anchor_name... */ + /* The factor 5 in the next allocation is because the maximum + expansion of HTML-escaping is for the & character, which is + output as "&". 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/gnu/usr.bin/texinfo/makeinfo/toc.h b/gnu/usr.bin/texinfo/makeinfo/toc.h new file mode 100644 index 00000000000..ec99eea335c --- /dev/null +++ b/gnu/usr.bin/texinfo/makeinfo/toc.h @@ -0,0 +1,49 @@ +/* toc.h -- table of contents handling. + $Id: toc.h,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $ + + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */ + +#ifndef TOC_H +#define TOC_H + +/* the file where we found the @contents directive */ +extern char *contents_filename; + +/* the file where we found the @shortcontents directive */ +extern char *shortcontents_filename; + +/* Structure to hold one entry for the toc. */ +typedef struct toc_entry_elt { + char *name; + char *containing_node; /* Name of node containing this section. */ + int number; /* counting number from 0...n independent from + chapter/section can be used for anchors or + references to it. */ + int level; /* level: chapter, section, subsection... */ +} TOC_ENTRY_ELT; + +/* all routines which have relationship with TOC should start with + toc_ (this is a kind of name-space) */ +extern int toc_add_entry (); /* return the number for the toc-entry */ +extern void toc_free (); +extern char *toc_find_section_of_node (); + +extern void cm_contents (), cm_shortcontents (); + +#endif /* not TOC_H */ |