1/* cmds.c -- Texinfo commands.
2   $Id: cmds.c,v 1.55 2004/12/14 00:15:36 karl Exp $
3
4   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
5   Foundation, Inc.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software Foundation,
19   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21#include "system.h"
22#include "cmds.h"
23#include "defun.h"
24#include "files.h"
25#include "footnote.h"
26#include "html.h"
27#include "insertion.h"
28#include "lang.h"
29#include "macro.h"
30#include "makeinfo.h"
31#include "node.h"
32#include "sectioning.h"
33#include "toc.h"
34#include "xml.h"
35
36#ifdef TM_IN_SYS_TIME
37#include <sys/time.h>
38#else
39#include <time.h>
40#endif
41
42/* Options. */
43static void cm_exampleindent (void),
44     cm_firstparagraphindent (void),
45     cm_paragraphindent (void),
46     cm_novalidate (void);
47
48/* Internals. */
49static void cm_obsolete (int arg, int start, int end),
50     not_fixed_width (int arg);
51
52/* The dispatch table.  */
53COMMAND command_table[] = {
54  { "\t", insert_space, NO_BRACE_ARGS },
55  { "\n", insert_space, NO_BRACE_ARGS },
56  { " ", insert_space, NO_BRACE_ARGS },
57  { "!", cm_punct, NO_BRACE_ARGS },
58  { "\"", cm_accent_umlaut, MAYBE_BRACE_ARGS },
59  { "'", cm_accent_acute, MAYBE_BRACE_ARGS },
60  { "*", cm_asterisk, NO_BRACE_ARGS },
61  { ",", cm_accent_cedilla, MAYBE_BRACE_ARGS },
62  { "-", cm_no_op, NO_BRACE_ARGS },
63  { ".", cm_punct, NO_BRACE_ARGS },
64  { "/", cm_no_op, NO_BRACE_ARGS },
65  { ":", cm_colon, NO_BRACE_ARGS },
66  { "=", cm_accent, MAYBE_BRACE_ARGS },
67  { "?", cm_punct, NO_BRACE_ARGS },
68  { "@", insert_self, NO_BRACE_ARGS },
69  { "\\", insert_self, NO_BRACE_ARGS },
70  { "^", cm_accent_hat, MAYBE_BRACE_ARGS },
71  { "`", cm_accent_grave, MAYBE_BRACE_ARGS },
72  { "{", insert_self, NO_BRACE_ARGS },
73  { "|", cm_no_op, NO_BRACE_ARGS },
74  { "}", insert_self, NO_BRACE_ARGS },
75  { "~", cm_accent_tilde, MAYBE_BRACE_ARGS },
76  { "AA", cm_special_char, BRACE_ARGS },
77  { "AE", cm_special_char, BRACE_ARGS },
78  { "H", cm_accent, MAYBE_BRACE_ARGS },
79  { "L", cm_special_char, BRACE_ARGS },
80  { "LaTeX", cm_LaTeX, BRACE_ARGS },
81  { "O", cm_special_char, BRACE_ARGS },
82  { "OE", cm_special_char, BRACE_ARGS },
83  { "TeX", cm_TeX, BRACE_ARGS },
84  { "aa", cm_special_char, BRACE_ARGS },
85  { "abbr", cm_abbr, BRACE_ARGS },
86  { "acronym", cm_acronym, BRACE_ARGS },
87  { "ae", cm_special_char, BRACE_ARGS },
88  { "afivepaper", cm_ignore_line, NO_BRACE_ARGS },
89  { "afourlatex", cm_ignore_line, NO_BRACE_ARGS },
90  { "afourpaper", cm_ignore_line, NO_BRACE_ARGS },
91  { "afourwide", cm_ignore_line, NO_BRACE_ARGS },
92  { "alias", cm_alias, NO_BRACE_ARGS },
93  { "anchor", cm_anchor, BRACE_ARGS },
94  { "appendix", cm_appendix, NO_BRACE_ARGS },
95  { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
96  { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
97  { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
98  { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
99  { "asis", cm_no_op, BRACE_ARGS },
100  { "author", cm_author, NO_BRACE_ARGS },
101  { "b", cm_b, BRACE_ARGS },
102  { "bullet", cm_bullet, BRACE_ARGS },
103  { "bye", cm_bye, NO_BRACE_ARGS },
104  { "c", cm_comment, NO_BRACE_ARGS },
105  { "caption", cm_caption, BRACE_ARGS },
106  { "cartouche", cm_cartouche, NO_BRACE_ARGS },
107  { "center", cm_center, NO_BRACE_ARGS },
108  { "centerchap", cm_unnumbered, NO_BRACE_ARGS },
109  { "chapheading", cm_chapheading, NO_BRACE_ARGS },
110  { "chapter", cm_chapter, NO_BRACE_ARGS },
111  { "cindex", cm_cindex, NO_BRACE_ARGS },
112  { "cite", cm_cite, BRACE_ARGS },
113  { "clear", cm_clear, NO_BRACE_ARGS },
114  { "code", cm_code, BRACE_ARGS },
115  { "comma", cm_comma, BRACE_ARGS },
116  { "command", cm_code, BRACE_ARGS },
117  { "comment", cm_comment, NO_BRACE_ARGS },
118  { "contents", cm_contents, NO_BRACE_ARGS },
119  { "copying", cm_copying, NO_BRACE_ARGS },
120  { "copyright", cm_copyright, BRACE_ARGS },
121  { "ctrl", cm_obsolete, BRACE_ARGS },
122  { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
123  { "defcv", cm_defun, NO_BRACE_ARGS },
124  { "defcvx", cm_defun, NO_BRACE_ARGS },
125  { "deffn", cm_defun, NO_BRACE_ARGS },
126  { "deffnx", cm_defun, NO_BRACE_ARGS },
127  { "defindex", cm_defindex, NO_BRACE_ARGS },
128  { "definfoenclose", cm_definfoenclose, NO_BRACE_ARGS },
129  { "defivar", cm_defun, NO_BRACE_ARGS },
130  { "defivarx", cm_defun, NO_BRACE_ARGS },
131  { "defmac", cm_defun, NO_BRACE_ARGS },
132  { "defmacx", cm_defun, NO_BRACE_ARGS },
133  { "defmethod", cm_defun, NO_BRACE_ARGS },
134  { "defmethodx", cm_defun, NO_BRACE_ARGS },
135  { "defop", cm_defun, NO_BRACE_ARGS },
136  { "defopt", cm_defun, NO_BRACE_ARGS },
137  { "defoptx", cm_defun, NO_BRACE_ARGS },
138  { "defopx", cm_defun, NO_BRACE_ARGS },
139  { "defspec", cm_defun, NO_BRACE_ARGS },
140  { "defspecx", cm_defun, NO_BRACE_ARGS },
141  { "deftp", cm_defun, NO_BRACE_ARGS },
142  { "deftpx", cm_defun, NO_BRACE_ARGS },
143  { "deftypecv", cm_defun, NO_BRACE_ARGS },
144  { "deftypecvx", cm_defun, NO_BRACE_ARGS },
145  { "deftypefn", cm_defun, NO_BRACE_ARGS },
146  { "deftypefnx", cm_defun, NO_BRACE_ARGS },
147  { "deftypefun", cm_defun, NO_BRACE_ARGS },
148  { "deftypefunx", cm_defun, NO_BRACE_ARGS },
149  { "deftypeivar", cm_defun, NO_BRACE_ARGS },
150  { "deftypeivarx", cm_defun, NO_BRACE_ARGS },
151  { "deftypemethod", cm_defun, NO_BRACE_ARGS },
152  { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
153  { "deftypeop", cm_defun, NO_BRACE_ARGS },
154  { "deftypeopx", cm_defun, NO_BRACE_ARGS },
155  { "deftypevar", cm_defun, NO_BRACE_ARGS },
156  { "deftypevarx", cm_defun, NO_BRACE_ARGS },
157  { "deftypevr", cm_defun, NO_BRACE_ARGS },
158  { "deftypevrx", cm_defun, NO_BRACE_ARGS },
159  { "defun", cm_defun, NO_BRACE_ARGS },
160  { "defunx", cm_defun, NO_BRACE_ARGS },
161  { "defvar", cm_defun, NO_BRACE_ARGS },
162  { "defvarx", cm_defun, NO_BRACE_ARGS },
163  { "defvr", cm_defun, NO_BRACE_ARGS },
164  { "defvrx", cm_defun, NO_BRACE_ARGS },
165  { "detailmenu", cm_detailmenu, NO_BRACE_ARGS },
166  { "dfn", cm_dfn, BRACE_ARGS },
167  { "dircategory", cm_dircategory, NO_BRACE_ARGS },
168  { "direntry", cm_direntry, NO_BRACE_ARGS },
169  { "display", cm_display, NO_BRACE_ARGS },
170  { "dmn", cm_dmn, BRACE_ARGS },
171  { "docbook", cm_docbook, NO_BRACE_ARGS },
172  { "documentdescription", cm_documentdescription, NO_BRACE_ARGS },
173  { "documentencoding", cm_documentencoding, NO_BRACE_ARGS },
174  { "documentlanguage", cm_documentlanguage, NO_BRACE_ARGS },
175  { "dotaccent", cm_accent, MAYBE_BRACE_ARGS },
176  { "dotless", cm_dotless, BRACE_ARGS },
177  { "dots", cm_dots, BRACE_ARGS },
178  { "email", cm_email, BRACE_ARGS },
179  { "emph", cm_emph, BRACE_ARGS },
180  { "end", cm_end, NO_BRACE_ARGS },
181  { "enddots", cm_enddots, BRACE_ARGS },
182  { "enumerate", cm_enumerate, NO_BRACE_ARGS },
183  { "env", cm_code, BRACE_ARGS },
184  { "equiv", cm_equiv, BRACE_ARGS },
185  { "error", cm_error, BRACE_ARGS },
186  { "euro", cm_special_char, BRACE_ARGS },
187  { "evenfooting", cm_ignore_line, NO_BRACE_ARGS },
188  { "evenheading", cm_ignore_line, NO_BRACE_ARGS },
189  { "everyfooting", cm_ignore_line, NO_BRACE_ARGS },
190  { "everyheading", cm_ignore_line, NO_BRACE_ARGS },
191  { "example", cm_example, NO_BRACE_ARGS },
192  { "exampleindent", cm_exampleindent, NO_BRACE_ARGS },
193  { "exclamdown", cm_special_char, BRACE_ARGS },
194  { "exdent", cm_exdent, NO_BRACE_ARGS },
195  { "expansion", cm_expansion, BRACE_ARGS },
196  { "file", cm_code, BRACE_ARGS },
197  { "finalout", cm_no_op, NO_BRACE_ARGS },
198  { "findex", cm_findex, NO_BRACE_ARGS },
199  { "firstparagraphindent", cm_firstparagraphindent, NO_BRACE_ARGS },
200  { "float", cm_float, NO_BRACE_ARGS },
201  { "flushleft", cm_flushleft, NO_BRACE_ARGS },
202  { "flushright", cm_flushright, NO_BRACE_ARGS },
203  { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
204  { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
205  { "format", cm_format, NO_BRACE_ARGS },
206  { "ftable", cm_ftable, NO_BRACE_ARGS },
207  { "group", cm_group, NO_BRACE_ARGS },
208  { "heading", cm_heading, NO_BRACE_ARGS },
209  { "headings", cm_ignore_line, NO_BRACE_ARGS },
210  { "headitem", cm_headitem, NO_BRACE_ARGS },
211  { "html", cm_html, NO_BRACE_ARGS },
212  { "hyphenation", cm_ignore_arg, BRACE_ARGS },
213  { "i", cm_i, BRACE_ARGS },
214  { "ifclear", cm_ifclear, NO_BRACE_ARGS },
215  { "ifeq", cm_ifeq, NO_BRACE_ARGS },
216  { "ifdocbook", cm_ifdocbook, NO_BRACE_ARGS },
217  { "ifhtml", cm_ifhtml, NO_BRACE_ARGS },
218  { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
219  { "ifnotdocbook", cm_ifnotdocbook, NO_BRACE_ARGS },
220  { "ifnothtml", cm_ifnothtml, NO_BRACE_ARGS },
221  { "ifnotinfo", cm_ifnotinfo, NO_BRACE_ARGS },
222  { "ifnotplaintext", cm_ifnotplaintext, NO_BRACE_ARGS },
223  { "ifnottex", cm_ifnottex, NO_BRACE_ARGS },
224  { "ifnotxml", cm_ifnotxml, NO_BRACE_ARGS },
225  { "ifplaintext", cm_ifplaintext, NO_BRACE_ARGS },
226  { "ifset", cm_ifset, NO_BRACE_ARGS },
227  { "iftex", cm_iftex, NO_BRACE_ARGS },
228  { "ifxml", cm_ifxml, NO_BRACE_ARGS },
229  { "ignore", command_name_condition, NO_BRACE_ARGS },
230  { "image", cm_image, BRACE_ARGS },
231  { "include", cm_include, NO_BRACE_ARGS },
232  { "indent", cm_indent, NO_BRACE_ARGS },
233  { "indicateurl", cm_indicate_url, BRACE_ARGS },
234  { "inforef", cm_inforef, BRACE_ARGS },
235  { "insertcopying", cm_insert_copying, NO_BRACE_ARGS },
236  { "item", cm_item, NO_BRACE_ARGS },
237  { "itemize", cm_itemize, NO_BRACE_ARGS },
238  { "itemx", cm_itemx, NO_BRACE_ARGS },
239  { "kbd", cm_kbd, BRACE_ARGS },
240  { "kbdinputstyle", cm_ignore_line, NO_BRACE_ARGS },
241  { "key", cm_key, BRACE_ARGS },
242  { "kindex", cm_kindex, NO_BRACE_ARGS },
243  { "l", cm_special_char, BRACE_ARGS },
244  { "lisp", cm_lisp, NO_BRACE_ARGS },
245  { "listoffloats", cm_listoffloats, NO_BRACE_ARGS },
246  { "lowersections", cm_lowersections, NO_BRACE_ARGS },
247  { "macro", cm_macro, NO_BRACE_ARGS },
248  { "majorheading", cm_majorheading, NO_BRACE_ARGS },
249  { "math", cm_math, BRACE_ARGS },
250  { "menu", cm_menu, NO_BRACE_ARGS },
251  { "minus", cm_minus, BRACE_ARGS },
252  { "multitable", cm_multitable, NO_BRACE_ARGS },
253  { "need", cm_ignore_line, NO_BRACE_ARGS },
254  { "node", cm_node, NO_BRACE_ARGS },
255  { "noindent", cm_noindent_cmd, NO_BRACE_ARGS },
256  { "novalidate", cm_novalidate, NO_BRACE_ARGS },
257  { "nwnode", cm_node, NO_BRACE_ARGS },
258  { "o", cm_special_char, BRACE_ARGS },
259  { "oddfooting", cm_ignore_line, NO_BRACE_ARGS },
260  { "oddheading", cm_ignore_line, NO_BRACE_ARGS },
261  { "oe", cm_special_char, BRACE_ARGS },
262  { "option", cm_code, BRACE_ARGS },
263  { "ordf", cm_special_char, BRACE_ARGS },
264  { "ordm", cm_special_char, BRACE_ARGS },
265  { "page", cm_no_op, NO_BRACE_ARGS },
266  { "pagesizes", cm_ignore_line, NO_BRACE_ARGS },
267  { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
268  { "pindex", cm_pindex, NO_BRACE_ARGS },
269  { "point", cm_point, BRACE_ARGS },
270  { "pounds", cm_special_char, BRACE_ARGS },
271  { "print", cm_print, BRACE_ARGS },
272  { "printindex", cm_printindex, NO_BRACE_ARGS },
273  { "pxref", cm_pxref, BRACE_ARGS },
274  { "questiondown", cm_special_char, BRACE_ARGS },
275  { "quotation", cm_quotation, NO_BRACE_ARGS },
276  { "r", cm_r, BRACE_ARGS },
277  { "raisesections", cm_raisesections, NO_BRACE_ARGS },
278  { "ref", cm_ref, BRACE_ARGS },
279  { "refill", cm_no_op, NO_BRACE_ARGS },
280  { "registeredsymbol", cm_registeredsymbol, BRACE_ARGS },
281  { "result", cm_result, BRACE_ARGS },
282  { "ringaccent", cm_accent, MAYBE_BRACE_ARGS },
283  { "rmacro", cm_rmacro, NO_BRACE_ARGS },
284  { "samp", cm_code, BRACE_ARGS },
285  { "sansserif", cm_sansserif, BRACE_ARGS },
286  { "sc", cm_sc, BRACE_ARGS },
287  { "section", cm_section, NO_BRACE_ARGS },
288  { "set", cm_set, NO_BRACE_ARGS },
289  { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
290  { "setchapterstyle", cm_obsolete, NO_BRACE_ARGS },
291  { "setcontentsaftertitlepage", cm_no_op, NO_BRACE_ARGS },
292  { "setfilename", cm_setfilename, NO_BRACE_ARGS },
293  { "setshortcontentsaftertitlepage", cm_no_op, NO_BRACE_ARGS },
294  { "settitle", cm_settitle, NO_BRACE_ARGS },
295  { "shortcaption", cm_caption, BRACE_ARGS },
296  { "shortcontents", cm_contents, NO_BRACE_ARGS },
297  { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS },
298  { "slanted", cm_slanted, BRACE_ARGS },
299  { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
300  { "smalldisplay", cm_smalldisplay, NO_BRACE_ARGS },
301  { "smallexample", cm_smallexample, NO_BRACE_ARGS },
302  { "smallformat", cm_smallformat, NO_BRACE_ARGS },
303  { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
304  { "sp", cm_sp, NO_BRACE_ARGS },
305  { "ss", cm_special_char, BRACE_ARGS },
306  { "strong", cm_strong, BRACE_ARGS },
307  { "subheading", cm_subheading, NO_BRACE_ARGS },
308  { "subsection", cm_subsection, NO_BRACE_ARGS },
309  { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
310  { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
311  { "subtitle", cm_titlepage_cmds, NO_BRACE_ARGS },
312  { "summarycontents", cm_contents, NO_BRACE_ARGS },
313  { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
314  { "synindex", cm_synindex, NO_BRACE_ARGS },
315  { "t", cm_tt, BRACE_ARGS },
316  { "tab", cm_tab, NO_BRACE_ARGS },
317  { "table", cm_table, NO_BRACE_ARGS },
318  { "tex", cm_tex, NO_BRACE_ARGS },
319  { "tie", cm_tie, BRACE_ARGS },
320  { "tieaccent", cm_accent, MAYBE_BRACE_ARGS },
321  { "tindex", cm_tindex, NO_BRACE_ARGS },
322  { "title", cm_titlepage_cmds, NO_BRACE_ARGS },
323  { "titlefont", cm_titlefont, BRACE_ARGS },
324  { "titlepage", cm_titlepage, NO_BRACE_ARGS },
325  { "today", cm_today, BRACE_ARGS },
326  { "top", cm_top, NO_BRACE_ARGS  },
327  { "u", cm_accent, MAYBE_BRACE_ARGS },
328  { "ubaraccent", cm_accent, MAYBE_BRACE_ARGS },
329  { "udotaccent", cm_accent, MAYBE_BRACE_ARGS },
330  { "unmacro", cm_unmacro, NO_BRACE_ARGS },
331  { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
332  { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
333  { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
334  { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
335  { "uref", cm_uref, BRACE_ARGS },
336  { "url", cm_uref, BRACE_ARGS },
337  { "v", cm_accent, MAYBE_BRACE_ARGS },
338  { "value", cm_value, BRACE_ARGS },
339  { "var", cm_var, BRACE_ARGS },
340  { "verb", cm_verb, NO_BRACE_ARGS },
341  { "verbatim", cm_verbatim, NO_BRACE_ARGS },
342  { "verbatiminclude", cm_verbatiminclude, NO_BRACE_ARGS },
343  { "vindex", cm_vindex, NO_BRACE_ARGS },
344  { "vtable", cm_vtable, NO_BRACE_ARGS },
345  { "vskip", cm_ignore_line, NO_BRACE_ARGS },
346  { "w", cm_w, BRACE_ARGS },
347  { "xml", cm_xml, NO_BRACE_ARGS },
348  { "xref", cm_xref, BRACE_ARGS },
349
350  /* Deprecated commands.  These used to be for italics.  */
351  { "iappendix", cm_ideprecated, NO_BRACE_ARGS },
352  { "iappendixsec", cm_ideprecated, NO_BRACE_ARGS },
353  { "iappendixsection", cm_ideprecated, NO_BRACE_ARGS },
354  { "iappendixsubsec", cm_ideprecated, NO_BRACE_ARGS },
355  { "iappendixsubsubsec", cm_ideprecated, NO_BRACE_ARGS },
356  { "ichapter", cm_ideprecated, NO_BRACE_ARGS },
357  { "isection", cm_ideprecated, NO_BRACE_ARGS },
358  { "isubsection", cm_ideprecated, NO_BRACE_ARGS },
359  { "isubsubsection", cm_ideprecated, NO_BRACE_ARGS },
360  { "iunnumbered", cm_ideprecated, NO_BRACE_ARGS },
361  { "iunnumberedsec", cm_ideprecated, NO_BRACE_ARGS },
362  { "iunnumberedsubsec", cm_ideprecated, NO_BRACE_ARGS },
363  { "iunnumberedsubsubsec", cm_ideprecated, NO_BRACE_ARGS },
364
365  /* Now @include does what this was used to. */
366  { "infoinclude", cm_obsolete, NO_BRACE_ARGS },
367  { "titlespec", cm_obsolete, NO_BRACE_ARGS },
368
369  { NULL, NULL, NO_BRACE_ARGS }
370};
371
372/* The bulk of the Texinfo commands. */
373
374/* Commands which insert their own names. */
375void
376insert_self (int arg)
377{
378  if (arg == START)
379    add_word (command);
380}
381
382void
383insert_space (int arg)
384{
385  if (arg == START)
386    {
387      if (xml && !docbook)
388        xml_insert_entity ("space");
389      else
390        add_char (' ');
391    }
392}
393
394/* Insert a comma.  Useful when a literal , would break our parsing of
395   multiple arguments.  */
396void
397cm_comma (int arg)
398{
399  if (arg == START)
400    add_char (',');
401}
402
403
404/* Force a line break in the output. */
405void
406cm_asterisk (void)
407{
408  if (html)
409    add_word ("<br>");
410  else if (xml && !docbook)
411    xml_insert_entity ("linebreak");
412  else if (docbook)
413    xml_asterisk ();
414  else
415    {
416      close_single_paragraph ();
417      cm_noindent ();
418    }
419}
420
421/* Insert ellipsis. */
422void
423cm_dots (int arg)
424{
425  if (arg == START)
426    {
427      if (xml && !docbook)
428        xml_insert_entity ("dots");
429      else if (docbook)
430        xml_insert_entity ("hellip");
431      else
432	if (html && !in_fixed_width_font)
433	  insert_string ("<small class=\"dots\">...</small>");
434	else
435	  add_word ("...");
436    }
437}
438
439/* Insert ellipsis for sentence end. */
440void
441cm_enddots (int arg)
442{
443  if (arg == START)
444    {
445      if (xml && !docbook)
446	xml_insert_entity ("enddots");
447      else if (docbook)
448	{
449	  xml_insert_entity ("hellip");
450	  add_char ('.');
451	}
452      else
453	if (html && !in_fixed_width_font)
454	  insert_string ("<small class=\"enddots\">....</small>");
455	else
456	  add_word ("....");
457    }
458}
459
460void
461cm_bullet (int arg)
462{
463  if (arg == START)
464    {
465      if (html)
466        add_word ("&bull;");
467      else if (xml && !docbook)
468	xml_insert_entity ("bullet");
469      else if (docbook)
470	xml_insert_entity ("bull");
471      else
472        add_char ('*');
473    }
474}
475
476void
477cm_minus (int arg)
478{
479  if (arg == START)
480    {
481      if (xml)
482	xml_insert_entity ("minus");
483      else if (html)
484        add_word ("&minus;");
485      else
486	add_char ('-');
487    }
488}
489
490/* Formatting a dimension unit.  */
491void
492cm_dmn (int arg)
493{
494  if (html)
495    insert_html_tag_with_attribute (arg, "span", "class=\"dmn\"");
496  else if (docbook)
497    /* No units in docbook yet.  */
498    ;
499  else if (xml)
500    xml_insert_element (DIMENSION, arg);
501}
502
503/* Insert "TeX". */
504void
505cm_TeX (int arg)
506{
507  static int last_position;
508
509  if (arg == START)
510    {
511      if (xml)
512	xml_insert_entity ("tex");
513      else
514	add_word ("TeX");
515
516      last_position = output_paragraph_offset;
517    }
518  else if (last_position != output_paragraph_offset)
519    {
520      warning (_("arguments to @%s ignored"), command);
521      output_paragraph_offset = last_position;
522    }
523}
524
525/* Insert "LaTeX".  */
526void
527cm_LaTeX (int arg)
528{
529  static int last_position;
530
531  if (arg == START)
532    {
533      if (xml)
534        xml_insert_entity ("latex");
535      else
536        add_word ("LaTeX");
537
538      last_position = output_paragraph_offset;
539    }
540  else if (last_position != output_paragraph_offset)
541    {
542      warning (_("arguments to @%s ignored"), command);
543      output_paragraph_offset = last_position;
544    }
545}
546
547/* Copyright symbol.  */
548void
549cm_copyright (int arg)
550{
551  if (arg == START)
552    {
553    if (html)
554      add_word ("&copy;");
555    else if (xml && !docbook)
556      xml_insert_entity ("copyright");
557    else if (docbook)
558      xml_insert_entity ("copy");
559    else
560      add_word ("(C)");
561    }
562}
563
564/* Registered symbol.  */
565void
566cm_registeredsymbol (int arg)
567{
568  if (arg == START)
569    {
570      if (html)
571        add_word ("&reg;");
572      else if (docbook)
573        xml_insert_entity ("reg");
574      else if (xml && !docbook)
575        xml_insert_entity ("registered");
576      else
577        add_word ("(R)");
578    }
579}
580
581void
582cm_today (int arg)
583{
584  static char *months[12] =
585    { N_("January"), N_("February"), N_("March"), N_("April"), N_("May"),
586      N_("June"), N_("July"), N_("August"), N_("September"), N_("October"),
587      N_("November"), N_("December") };
588  if (arg == START)
589    {
590      time_t timer = time (0);
591      struct tm *ts = localtime (&timer);
592      add_word_args ("%d %s %d", ts->tm_mday, _(months[ts->tm_mon]),
593                     ts->tm_year + 1900);
594    }
595}
596
597void
598cm_comment (void)
599{
600  /* For HTML, do not output comments before HTML header is written,
601     otherwise comments before @settitle cause an empty <title> in the
602     header.  */
603  if ((html && html_output_head_p) || xml)
604    {
605      char *line;
606      get_rest_of_line (0, &line);
607
608      if (strlen (line) > 0)
609        {
610          int save_inhibit_indentation = inhibit_paragraph_indentation;
611          int save_paragraph_is_open = paragraph_is_open;
612          int save_escape_html = escape_html;
613          int save_xml_no_para = xml_no_para;
614          int i;
615
616          inhibit_paragraph_indentation = 1;
617          escape_html = 0;
618          xml_no_para = 1;
619
620          /* @c and @comment can appear between @item and @itemx,
621             @deffn and @deffnx.  */
622          xml_dont_touch_items_defs++;
623
624          /* Use insert for HTML, and XML when indentation is enabled.
625             For Docbook, use add_char.  */
626          if (xml && xml_indentation_increment > 0
627              && output_paragraph[output_paragraph_offset-1] != '\n')
628            insert ('\n');
629
630          /* Crunch double hyphens in comments.  */
631          add_html_block_elt ("<!-- ");
632          for (i = 0; i < strlen (line); i++)
633            if (line[i] != '-' || (i && line[i-1] != '-'))
634              add_char (line[i]);
635          add_word (" -->");
636
637          if (html)
638            add_char ('\n');
639
640          inhibit_paragraph_indentation = save_inhibit_indentation;
641          paragraph_is_open = save_paragraph_is_open;
642          escape_html = save_escape_html;
643          xml_no_para = save_xml_no_para;
644          xml_dont_touch_items_defs--;
645        }
646
647      free (line);
648    }
649  else
650    cm_ignore_line ();
651}
652
653
654
655/* We keep acronyms with two arguments around, to be able to refer to them
656   later with only one argument.  */
657static ACRONYM_DESC *acronyms_stack = NULL;
658
659static void
660cm_acronym_or_abbr (int arg, int is_abbr)
661{
662  char *aa, *description;
663  unsigned len;
664
665  /* We do everything at START.  */
666  if (arg == END)
667    return;
668
669  get_until_in_braces (",", &aa);
670  if (input_text[input_text_offset] == ',')
671    input_text_offset++;
672  get_until_in_braces ("}", &description);
673
674  canon_white (aa);
675  canon_white (description);
676
677  /* If not enclosed in braces, strip after comma to be compatible
678     with texinfo.tex.  */
679  if (description[0] != '{' && strchr (description, ',') != NULL)
680    {
681      int i = 0;
682      while (description[i] != ',')
683        i++;
684      /* For now, just terminate the string at comma.  */
685      description[i] = 0;
686    }
687
688  /* Get description out of braces.  */
689  if (description[0] == '{')
690    description++;
691
692  len = strlen (description);
693  if (len && description[len-1] == '}')
694    description[len-1] = 0;
695
696  /* Save new description.  */
697  if (strlen (description) > 0)
698    {
699      ACRONYM_DESC *new = xmalloc (sizeof (ACRONYM_DESC));
700
701      new->acronym = xstrdup (aa);
702      new->description = xstrdup (description);
703      new->next = acronyms_stack;
704      acronyms_stack = new;
705    }
706
707  if (html)
708    {
709      add_word (is_abbr ? "<abbr" : "<acronym");
710
711      if (strlen (description) > 0)
712        add_word_args (" title=\"%s\"", text_expansion (description));
713      else if (acronyms_stack)
714        {
715          /* No second argument, get from previous.  Search order is from
716             last to first defined, so we get the most recent version of
717             the description.  */
718          ACRONYM_DESC *temp = acronyms_stack;
719
720          while (temp)
721            {
722              if (STREQ (aa, temp->acronym)
723                  && strlen (temp->description) > 0)
724                {
725                  add_word_args (" title=\"%s\"",
726                                 text_expansion (temp->description));
727                  break;
728                }
729              temp = temp->next;
730            }
731        }
732
733      add_char ('>');
734      execute_string ("%s", aa);
735      add_word (is_abbr ? "</abbr>" : "</acronym>");
736    }
737  else if (docbook)
738    {
739      xml_insert_element (is_abbr ? ABBREV : ACRONYM, START);
740      execute_string ("%s", aa);
741      xml_insert_element (is_abbr ? ABBREV : ACRONYM, END);
742    }
743  else if (xml)
744    {
745      xml_insert_element (is_abbr ? ABBREV : ACRONYM, START);
746
747      xml_insert_element (is_abbr ? ABBREVWORD : ACRONYMWORD, START);
748      execute_string ("%s", aa);
749      xml_insert_element (is_abbr ? ABBREVWORD : ACRONYMWORD, END);
750
751      if (strlen (description) > 0)
752        {
753          xml_insert_element (is_abbr ? ABBREVDESC : ACRONYMDESC, START);
754          execute_string ("%s", description);
755          xml_insert_element (is_abbr ? ABBREVDESC : ACRONYMDESC, END);
756        }
757
758      xml_insert_element (is_abbr ? ABBREV : ACRONYM, END);
759    }
760  else
761    execute_string ("%s", aa);
762
763  /* Put description into parenthesis after the acronym for all outputs
764     except XML.  */
765  if (strlen (description) > 0 && (!xml || docbook))
766    add_word_args (" (%s)", description);
767}
768
769void
770cm_acronym (int arg)
771{
772  cm_acronym_or_abbr (arg, 0);
773}
774
775void
776cm_abbr (int arg)
777{
778  cm_acronym_or_abbr (arg, 1);
779}
780
781void
782cm_tt (int arg)
783{
784  /* @t{} is a no-op in Info.  */
785  if (html)
786    insert_html_tag (arg, "tt");
787  else if (xml)
788    xml_insert_element (TT, arg);
789}
790
791void
792cm_code (int arg)
793{
794  if (arg == START)
795    in_fixed_width_font++;
796
797  if (xml)
798    {
799      if (STREQ (command, "command"))
800	xml_insert_element (COMMAND_TAG, arg);
801      else if (STREQ (command, "env"))
802	xml_insert_element (ENV, arg);
803      else if (STREQ (command, "file"))
804	xml_insert_element (FILE_TAG, arg);
805      else if (STREQ (command, "option"))
806	xml_insert_element (OPTION, arg);
807      else if (STREQ (command, "samp"))
808        {
809          if (docbook && arg == START)
810            {
811              /* Even though @samp is in_fixed_width_font, it
812                 should always start a paragraph.  Unfortunately,
813                 in_fixed_width_font inhibits that.  */
814              xml_start_para ();
815              xml_insert_entity ("lsquo");
816            }
817          xml_insert_element (SAMP, arg);
818          if (docbook && arg == END)
819            xml_insert_entity ("rsquo");
820        }
821      else
822	xml_insert_element (CODE, arg);
823    }
824  else if (html)
825    {
826      if (STREQ (command, "code"))
827        insert_html_tag (arg, "code");
828      else
829        { /* Use <samp> tag in general to get typewriter.  */
830          if (arg == START)
831            { /* If @samp specifically, add quotes a la TeX output.  */
832              if (STREQ (command, "samp")) add_char ('`');
833              add_word ("<samp>");
834            }
835          insert_html_tag_with_attribute (arg, "span", "class=\"%s\"",command);
836          if (arg == END)
837            {
838              add_word ("</samp>");
839              if (STREQ (command, "samp")) add_char ('\'');
840            }
841        }
842    }
843  else
844    {
845      extern int printing_index;
846
847      if (!printing_index)
848        {
849          if (arg == START)
850            add_char ('`');
851          else
852            add_meta_char ('\'');
853        }
854    }
855}
856
857void
858cm_kbd (int arg)
859{
860  if (xml)
861    xml_insert_element (KBD, arg);
862  else if (html)
863    { /* Seems like we should increment in_fixed_width_font for Info
864         format too, but then the quote-omitting special case gets
865         confused.  Punt.  */
866      if (arg == START)
867        in_fixed_width_font++;
868      insert_html_tag (arg, "kbd");
869    }
870  else
871    { /* People use @kbd in an example to get the "user input" font.
872         We don't want quotes in that case.  */
873      if (!in_fixed_width_font)
874        cm_code (arg);
875    }
876}
877
878/* Just show a url (http://example.org/..., for example), don't link to it.  */
879void
880cm_indicate_url (int arg, int start, int end)
881{
882  if (xml)
883    xml_insert_element (URL, arg);
884  else if (html)
885    {
886      if (arg == START)
887        add_word ("&lt;");
888      insert_html_tag (arg, "code");
889      if (arg != START)
890        add_word ("&gt;");
891    }
892  else
893    if (arg == START)
894      add_word ("<");
895    else
896      add_word (">");
897}
898
899void
900cm_key (int arg)
901{
902  if (xml)
903    xml_insert_element (KEY, arg);
904  else if (html)
905    add_word (arg == START ? "&lt;" : "&gt;");
906  else
907    add_char (arg == START ? '<' : '>');
908}
909
910/* Handle a command that switches to a non-fixed-width font.  */
911void
912not_fixed_width (int arg)
913{
914  if (arg == START)
915    in_fixed_width_font = 0;
916}
917
918/* @var in makeinfo just uppercases the text. */
919void
920cm_var (int arg, int start_pos, int end_pos)
921{
922  if (xml)
923    xml_insert_element (VAR, arg);
924  else
925    {
926  not_fixed_width (arg);
927
928  if (html)
929    insert_html_tag (arg, "var");
930  else if (arg == END)
931    {
932      while (start_pos < end_pos)
933        {
934          unsigned char c = output_paragraph[start_pos];
935          if (strchr ("[](),", c))
936            warning (_("unlikely character %c in @var"), c);
937          output_paragraph[start_pos] = coerce_to_upper (c);
938          start_pos++;
939        }
940    }
941    }
942}
943
944void
945cm_sc (int arg, int start_pos, int end_pos)
946{
947  if (xml)
948    xml_insert_element (SC, arg);
949  else
950    {
951      not_fixed_width (arg);
952
953      if (arg == START)
954        {
955          if (html)
956            insert_html_tag_with_attribute (arg, "span", "class=\"sc\"");
957        }
958      else
959        {
960          int all_upper;
961
962          if (html)
963            start_pos += sizeof ("<span class=\"sc\">") - 1; /* skip <span> */
964
965          /* Avoid the warning below if there's no text inside @sc{}, or
966             when processing menus under --no-headers.  */
967          all_upper = start_pos < end_pos;
968
969          while (start_pos < end_pos)
970            {
971              unsigned char c = output_paragraph[start_pos];
972              if (!isupper (c))
973                all_upper = 0;
974              if (!html)
975                output_paragraph[start_pos] = coerce_to_upper (c);
976              start_pos++;
977            }
978          if (all_upper)
979            warning (_("@sc argument all uppercase, thus no effect"));
980
981          if (html)
982            insert_html_tag (arg, "span");
983        }
984    }
985}
986
987void
988cm_dfn (int arg, int position)
989{
990  if (xml)
991    xml_insert_element (DFN, arg);
992  else
993    {
994  if (html)
995    insert_html_tag (arg, "dfn");
996  else if (arg == START)
997    add_char ('"');
998  else
999    add_meta_char ('"');
1000    }
1001}
1002
1003void
1004cm_emph (int arg)
1005{
1006  if (xml)
1007    xml_insert_element (EMPH, arg);
1008  else if (html)
1009    insert_html_tag (arg, "em");
1010  else
1011    add_char ('_');
1012}
1013
1014void
1015cm_verb (int arg)
1016{
1017  int character;
1018  int delimiter = 0; /* avoid warning */
1019  int seen_end = 0;
1020
1021  in_fixed_width_font++;
1022  /* are these necessary ? */
1023  last_char_was_newline = 0;
1024
1025  if (html)
1026    add_word ("<tt>");
1027
1028  if (input_text_offset < input_text_length)
1029    {
1030      character = curchar ();
1031      if (character == '{')
1032	input_text_offset++;
1033      else
1034	line_error (_("`{' expected, but saw `%c'"), character);
1035    }
1036
1037  if (input_text_offset < input_text_length)
1038    {
1039      delimiter = curchar ();
1040      input_text_offset++;
1041    }
1042
1043  while (input_text_offset < input_text_length)
1044    {
1045      character = curchar ();
1046
1047      if (character == '\n')
1048        {
1049          line_number++;
1050          if (html)
1051            add_word ("<br>\n");
1052        }
1053
1054      else if (html && character == '<')
1055        add_word ("&lt;");
1056
1057      else if (html && character == '&')
1058        add_word ("&amp;");
1059
1060      else if (character == delimiter && input_text[input_text_offset+1] == '}')
1061	{ /* Assume no newlines in END_VERBATIM. */
1062	  seen_end = 1;
1063	  input_text_offset++;
1064	  break;
1065	}
1066
1067      else
1068        add_char (character);
1069
1070      input_text_offset++;
1071    }
1072
1073  if (!seen_end)
1074    warning (_("end of file inside verb block"));
1075
1076  if (input_text_offset < input_text_length)
1077    {
1078      character = curchar ();
1079      if (character == '}')
1080	input_text_offset++;
1081      else
1082	line_error (_("`}' expected, but saw `%c'"), character);
1083    }
1084
1085  if (html)
1086    add_word ("</tt>");
1087
1088  in_fixed_width_font--;
1089}
1090
1091
1092void
1093cm_strong (int arg, int start_pos, int end_pos)
1094{
1095  if (docbook && arg == START)
1096    xml_insert_element_with_attribute (B, arg, "role=\"bold\"");
1097  else if (xml)
1098    xml_insert_element (STRONG, arg);
1099  else if (html)
1100    insert_html_tag (arg, "strong");
1101  else
1102    add_char ('*');
1103
1104  if (!xml && !html && !docbook && !no_headers
1105      && arg == END
1106      && end_pos - start_pos >= 6
1107      && (STRNCASEEQ ((char *) output_paragraph + start_pos, "*Note:", 6)
1108          || STRNCASEEQ ((char *) output_paragraph + start_pos, "*Note ", 6)))
1109    {
1110      /* Translators: "Note:" is literal here and should not be
1111         translated.  @strong{Nota}, say, does not cause the problem.  */
1112      warning (_("@strong{Note...} produces a spurious cross-reference in Info; reword to avoid that"));
1113      /* Adjust the output to avoid writing the bad xref.  */
1114      output_paragraph[start_pos + 5] = '_';
1115    }
1116}
1117
1118void
1119cm_cite (int arg, int position)
1120{
1121  if (xml)
1122    xml_insert_element (CITE, arg);
1123  else if (html)
1124    insert_html_tag (arg, "cite");
1125  else
1126    {
1127      if (arg == START)
1128        add_char ('`');
1129      else
1130        add_char ('\'');
1131    }
1132}
1133
1134/* No highlighting, but argument switches fonts.  */
1135void
1136cm_not_fixed_width (int arg, int start, int end)
1137{
1138  if (xml)
1139    xml_insert_element (NOTFIXEDWIDTH, arg);
1140  not_fixed_width (arg);
1141}
1142
1143void
1144cm_i (int arg)
1145{
1146  /* Make use of <lineannotation> of Docbook, if we are
1147     inside an @example or similar.  */
1148  extern int printing_index;
1149  if (docbook && !filling_enabled && !printing_index)
1150    xml_insert_element (LINEANNOTATION, arg);
1151  else if (xml)
1152    xml_insert_element (I, arg);
1153  else if (html)
1154    insert_html_tag (arg, "i");
1155  else
1156    not_fixed_width (arg);
1157}
1158
1159void
1160cm_slanted (int arg)
1161{
1162  /* Make use of <lineannotation> of Docbook, if we are
1163     inside an @example or similar.  */
1164  extern int printing_index;
1165  if (docbook && !filling_enabled && !printing_index)
1166    xml_insert_element (LINEANNOTATION, arg);
1167  else if (xml)
1168    xml_insert_element (SLANTED, arg);
1169  else if (html)
1170    insert_html_tag (arg, "i");
1171  else
1172    not_fixed_width (arg);
1173}
1174
1175void
1176cm_b (int arg)
1177{
1178  /* See cm_i comments.  */
1179  extern int printing_index;
1180  if (docbook && !filling_enabled && !printing_index)
1181    xml_insert_element (LINEANNOTATION, arg);
1182  else if (docbook && arg == START)
1183    xml_insert_element_with_attribute (B, arg, "role=\"bold\"");
1184  else if (xml)
1185    xml_insert_element (B, arg);
1186  else if (html)
1187    insert_html_tag (arg, "b");
1188  else
1189    not_fixed_width (arg);
1190}
1191
1192void
1193cm_r (int arg)
1194{
1195  /* See cm_i comments.  */
1196  extern int printing_index;
1197  if (docbook && !filling_enabled && !printing_index)
1198    xml_insert_element (LINEANNOTATION, arg);
1199  else if (xml)
1200    xml_insert_element (R, arg);
1201  else if (html)
1202    insert_html_tag_with_attribute (arg, "span", "class=\"roman\"");
1203  else
1204    not_fixed_width (arg);
1205}
1206
1207void
1208cm_sansserif (int arg)
1209{
1210  /* See cm_i comments.  */
1211  extern int printing_index;
1212  if (docbook && !filling_enabled && !printing_index)
1213    xml_insert_element (LINEANNOTATION, arg);
1214  else if (xml)
1215    xml_insert_element (SANSSERIF, arg);
1216  else if (html)
1217    insert_html_tag_with_attribute (arg, "span", "class=\"sansserif\"");
1218  else
1219    not_fixed_width (arg);
1220}
1221
1222void
1223cm_titlefont (int arg)
1224{
1225  if (xml)
1226    xml_insert_element (TITLEFONT, arg);
1227  else
1228   {
1229     not_fixed_width (arg);
1230     if (html)
1231	{
1232	  html_title_written = 1; /* suppress title from @settitle */
1233	  if (arg == START)
1234	    add_word ("<h1 class=\"titlefont\">");
1235	  else
1236	    add_word ("</h1>\n");
1237	}
1238   }
1239}
1240
1241
1242/* Unfortunately, we cannot interpret @math{} contents like TeX does.  We just
1243   pass them through.  */
1244void
1245cm_math (int arg)
1246{
1247  if (xml && !docbook)
1248    xml_insert_element (MATH, arg);
1249}
1250
1251/* Various commands are no-op's. */
1252void
1253cm_no_op (void)
1254{
1255}
1256
1257
1258/* For proofing single chapters, etc.  */
1259void
1260cm_novalidate (void)
1261{
1262  validating = 0;
1263}
1264
1265
1266/* Prevent the argument from being split across two lines. */
1267void
1268cm_w (int arg)
1269{
1270  if (arg == START)
1271    non_splitting_words++;
1272  else
1273    {
1274      if (docbook || html || xml)
1275        /* This is so @w{$}Log$ doesn't end up as <dollar>Log<dollar>
1276           in the output.  */
1277        insert_string ("<!-- /@w -->");
1278
1279      non_splitting_words--;
1280    }
1281}
1282
1283
1284/* An unbreakable word space.  Same as @w{ } for makeinfo, but different
1285   for TeX (the space stretches and stretches, and does not inhibit
1286   hyphenation).  */
1287void
1288cm_tie (int arg)
1289{
1290  if (arg == START)
1291    {
1292      cm_w (START);
1293      add_char (' ');
1294    }
1295  else
1296    cm_w (END);
1297}
1298
1299/* Explain that this command is obsolete, thus the user shouldn't
1300   do anything with it. */
1301static void
1302cm_obsolete (int arg, int start, int end)
1303{
1304  if (arg == START)
1305    warning (_("%c%s is obsolete"), COMMAND_PREFIX, command);
1306}
1307
1308
1309/* Inhibit the indentation of the next paragraph, but not of following
1310   paragraphs.  */
1311void
1312cm_noindent (void)
1313{
1314  if (!inhibit_paragraph_indentation)
1315    inhibit_paragraph_indentation = -1;
1316}
1317
1318void
1319cm_noindent_cmd (void)
1320{
1321  cm_noindent ();
1322  xml_no_indent = 1;
1323  skip_whitespace_and_newlines();
1324
1325  if (xml)
1326    xml_start_para ();
1327  else if (html && !paragraph_is_open)
1328    add_html_block_elt ("<p class=\"noindent\">");
1329  else
1330    {
1331      paragraph_is_open = 0;
1332      start_paragraph ();
1333    }
1334}
1335
1336/* Force indentation of the next paragraph. */
1337void
1338cm_indent (void)
1339{
1340  inhibit_paragraph_indentation = 0;
1341  xml_no_indent = 0;
1342  skip_whitespace_and_newlines();
1343
1344  if (xml)
1345    xml_start_para ();
1346  else if (html && !paragraph_is_open)
1347    add_html_block_elt ("<p class=\"indent\">");
1348  else
1349    start_paragraph ();
1350}
1351
1352/* I don't know exactly what to do with this.  Should I allow
1353   someone to switch filenames in the middle of output?  Since the
1354   file could be partially written, this doesn't seem to make sense.
1355   Another option: ignore it, since they don't really want to
1356   switch files.  Finally, complain, or at least warn.  It doesn't
1357   really matter, anyway, since this doesn't get executed.  */
1358void
1359cm_setfilename (void)
1360{
1361  char *filename;
1362  get_rest_of_line (1, &filename);
1363  /* warning ("`@%s %s' encountered and ignored", command, filename); */
1364  if (xml)
1365    add_word_args ("<setfilename>%s</setfilename>", filename);
1366  free (filename);
1367}
1368
1369void
1370cm_settitle (void)
1371{
1372  if (xml)
1373    {
1374      xml_begin_document (current_output_filename);
1375      xml_insert_element (SETTITLE, START);
1376      xml_in_book_title = 1;
1377      get_rest_of_line (0, &title);
1378      execute_string ("%s", title);
1379      xml_in_book_title = 0;
1380      xml_insert_element (SETTITLE, END);
1381    }
1382  else
1383    get_rest_of_line (0, &title);
1384}
1385
1386
1387/* Ignore argument in braces.  */
1388void
1389cm_ignore_arg (int arg, int start_pos, int end_pos)
1390{
1391  if (arg == END)
1392    output_paragraph_offset = start_pos;
1393}
1394
1395/* Ignore argument on rest of line.  */
1396void
1397cm_ignore_line (void)
1398{
1399  discard_until ("\n");
1400}
1401
1402/* Insert the number of blank lines passed as argument. */
1403void
1404cm_sp (void)
1405{
1406  int lines;
1407  char *line;
1408
1409  /* Due to tricky stuff in execute_string(), @value{} can't be expanded.
1410     So there is really no reason to enable expansion for @sp parameters.  */
1411  get_rest_of_line (0, &line);
1412
1413  if (sscanf (line, "%d", &lines) != 1 || lines <= 0)
1414    line_error (_("@sp requires a positive numeric argument, not `%s'"), line);
1415  else
1416    {
1417      if (xml)
1418	{
1419          /* @sp can appear between @item and @itemx, @deffn and @deffnx.  */
1420          xml_dont_touch_items_defs++;
1421	  xml_insert_element_with_attribute (SP, START, "lines=\"%s\"", line);
1422	  /*	  insert_string (line);*/
1423	  xml_insert_element (SP, END);
1424          xml_dont_touch_items_defs--;
1425	}
1426      else
1427        {
1428          /* Must disable filling since otherwise multiple newlines is like
1429             multiple spaces.  Must close paragraph since that's what the
1430             manual says and that's what TeX does.  */
1431          int save_filling_enabled = filling_enabled;
1432          filling_enabled = 0;
1433
1434          /* close_paragraph generates an extra blank line.  */
1435          close_single_paragraph ();
1436
1437          if (lines && html && !executing_string)
1438            html_output_head ();
1439
1440          if (html)
1441            add_html_block_elt ("<pre class=\"sp\">\n");
1442
1443          while (lines--)
1444            add_char ('\n');
1445
1446          if (html)
1447            add_html_block_elt ("</pre>\n");
1448
1449          filling_enabled = save_filling_enabled;
1450        }
1451    }
1452  free (line);
1453}
1454
1455/* @dircategory LINE outputs INFO-DIR-SECTION LINE, unless --no-headers.  */
1456void
1457cm_dircategory (void)
1458{
1459  char *line;
1460
1461  if (html || docbook)
1462    cm_ignore_line ();
1463  else if (xml)
1464    {
1465      xml_insert_element (DIRCATEGORY, START);
1466      get_rest_of_line (1, &line);
1467      insert_string (line);
1468      free (line);
1469      xml_insert_element (DIRCATEGORY, END);
1470    }
1471  else
1472    {
1473      get_rest_of_line (1, &line);
1474
1475      if (!no_headers && !html)
1476        {
1477          kill_self_indent (-1); /* make sure there's no indentation */
1478          insert_string ("INFO-DIR-SECTION ");
1479          insert_string (line);
1480          insert ('\n');
1481        }
1482
1483      free (line);
1484    }
1485}
1486
1487/* Start a new line with just this text on it.
1488   Then center the line of text.
1489   */
1490void
1491cm_center (void)
1492{
1493  if (xml)
1494    {
1495      char *line;
1496      xml_insert_element (CENTER, START);
1497      get_rest_of_line (0, &line);
1498      execute_string ("%s", line);
1499      free (line);
1500      xml_insert_element (CENTER, END);
1501    }
1502  else
1503    {
1504      int i, start, length;
1505      char *line;
1506      int save_indented_fill = indented_fill;
1507      int save_filling_enabled = filling_enabled;
1508      int fudge_factor = 1;
1509
1510      filling_enabled = indented_fill = 0;
1511      cm_noindent ();
1512      start = output_paragraph_offset;
1513
1514      if (html)
1515        add_html_block_elt ("<div align=\"center\">");
1516
1517      inhibit_output_flushing ();
1518      get_rest_of_line (0, &line);
1519      execute_string ("%s", line);
1520      free (line);
1521      uninhibit_output_flushing ();
1522      if (html)
1523        add_html_block_elt ("</div>");
1524
1525       else
1526         {
1527           i = output_paragraph_offset - 1;
1528           while (i > (start - 1) && output_paragraph[i] == '\n')
1529             i--;
1530
1531           output_paragraph_offset = ++i;
1532           length = output_paragraph_offset - start;
1533
1534           if (length < (fill_column - fudge_factor))
1535             {
1536               line = xmalloc (1 + length);
1537               memcpy (line, (char *)(output_paragraph + start), length);
1538
1539               i = (fill_column - fudge_factor - length) / 2;
1540               output_paragraph_offset = start;
1541
1542               while (i--)
1543                 insert (' ');
1544
1545               for (i = 0; i < length; i++)
1546                 insert (line[i]);
1547
1548               free (line);
1549             }
1550         }
1551
1552      insert ('\n');
1553      filling_enabled = save_filling_enabled;
1554      indented_fill = save_indented_fill;
1555      close_single_paragraph ();
1556      if (looking_at("\n"))
1557        insert ('\n');
1558    }
1559}
1560
1561/* Show what an expression returns. */
1562void
1563cm_result (int arg)
1564{
1565  if (arg == END)
1566    add_word (html ? "=&gt;" : "=>");
1567}
1568
1569/* What an expression expands to. */
1570void
1571cm_expansion (int arg)
1572{
1573  if (arg == END)
1574    add_word (html ? "==&gt;" : "==>");
1575}
1576
1577/* Indicates two expressions are equivalent. */
1578void
1579cm_equiv (int arg)
1580{
1581  if (arg == END)
1582    add_word ("==");
1583}
1584
1585/* What an expression may print. */
1586void
1587cm_print (int arg)
1588{
1589  if (arg == END)
1590    add_word ("-|");
1591}
1592
1593/* An error signaled. */
1594void
1595cm_error (int arg)
1596{
1597  if (arg == END)
1598    add_word (html ? "error--&gt;" : "error-->");
1599}
1600
1601/* The location of point in an example of a buffer. */
1602void
1603cm_point (int arg)
1604{
1605  if (arg == END)
1606    add_word ("-!-");
1607}
1608
1609/* @exdent: Start a new line with just this text on it.
1610   The text is outdented one level if possible. */
1611void
1612cm_exdent (void)
1613{
1614  char *line;
1615  int save_indent = current_indent;
1616  int save_in_fixed_width_font = in_fixed_width_font;
1617
1618  /* Read argument.  */
1619  get_rest_of_line (0, &line);
1620
1621  /* Exdent the output.  Actually this may be a no-op.   */
1622  if (current_indent)
1623    current_indent -= default_indentation_increment;
1624
1625  /* @exdent arg is supposed to be in roman.  */
1626  in_fixed_width_font = 0;
1627
1628  /* The preceding newline already inserted the `current_indent'.
1629     Remove one level's worth.  */
1630  kill_self_indent (default_indentation_increment);
1631
1632  if (html)
1633    add_word ("<br>");
1634  else if (docbook)
1635    xml_insert_element (LINEANNOTATION, START);
1636  else if (xml)
1637    xml_insert_element (EXDENT, START);
1638
1639  /* Can't close_single_paragraph, then we lose preceding blank lines.  */
1640  flush_output ();
1641  execute_string ("%s", line);
1642  free (line);
1643
1644  if (html)
1645    add_word ("<br>");
1646  else if (xml)
1647    {
1648      xml_insert_element (docbook ? LINEANNOTATION : EXDENT, END);
1649      insert ('\n');
1650    }
1651
1652  close_single_paragraph ();
1653
1654  current_indent = save_indent;
1655  in_fixed_width_font = save_in_fixed_width_font;
1656  if (!xml)
1657    start_paragraph ();
1658}
1659
1660/*
1661  Read include-filename, process the include-file:
1662    verbatim_include == 0: process through reader_loop
1663    verbatim_include != 0: process through handle_verbatim_environment
1664 */
1665static void
1666handle_include (int verbatim_include)
1667{
1668  char *arg, *filename;
1669
1670  if (macro_expansion_output_stream && !executing_string)
1671    me_append_before_this_command ();
1672
1673  if (!insertion_stack)
1674    close_paragraph ();  /* No blank lines etc. if not at outer level.  */
1675
1676  get_rest_of_line (0, &arg);
1677  /* We really only want to expand @value, but it's easier to just do
1678     everything.  TeX will only work with @value.  */
1679  filename = text_expansion (arg);
1680  free (arg);
1681
1682  if (macro_expansion_output_stream && !executing_string)
1683    remember_itext (input_text, input_text_offset);
1684
1685  pushfile ();
1686
1687  /* In verbose mode we print info about including another file. */
1688  if (verbose_mode)
1689    {
1690      int i = 0;
1691      FSTACK *stack = filestack;
1692
1693      for (i = 0, stack = filestack; stack; stack = stack->next, i++);
1694
1695      i *= 2;
1696
1697      printf ("%*s", i, "");
1698      printf ("%c%s `%s'\n", COMMAND_PREFIX, command, filename);
1699      fflush (stdout);
1700    }
1701
1702  if (!find_and_load (filename, 1))
1703    {
1704      popfile ();
1705      line_number--;
1706
1707      /* /wh/bar:5: @include/@verbatiminclude `foo': No such file or dir */
1708      line_error ("%c%s `%s': %s", COMMAND_PREFIX, command, filename,
1709                  strerror (errno));
1710
1711      free (filename);
1712      return;
1713    }
1714  else
1715    {
1716      if (macro_expansion_output_stream && !executing_string)
1717	remember_itext (input_text, input_text_offset);
1718
1719      if (!verbatim_include)
1720	reader_loop ();
1721      else
1722	handle_verbatim_environment (0);
1723    }
1724  free (filename);
1725  popfile ();
1726}
1727
1728
1729/* Include file as if put in @verbatim environment */
1730void
1731cm_verbatiminclude (void)
1732{
1733  handle_include (1);
1734}
1735
1736
1737/* Remember this file, and move onto the next. */
1738void
1739cm_include (void)
1740{
1741  handle_include (0);
1742}
1743
1744
1745/* @bye: Signals end of processing.  Easy to make this happen. */
1746
1747void
1748cm_bye (void)
1749{
1750  discard_braces (); /* should not have any unclosed braces left */
1751  input_text_offset = input_text_length;
1752}
1753
1754/* @paragraphindent */
1755
1756static void
1757cm_paragraphindent (void)
1758{
1759  char *arg;
1760
1761  get_rest_of_line (1, &arg);
1762  if (set_paragraph_indent (arg) != 0)
1763    line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
1764
1765  free (arg);
1766}
1767
1768
1769/* @exampleindent: change indentation of example-like environments.   */
1770static int
1771set_example_indentation_increment (char *string)
1772{
1773  if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0)
1774    ;
1775  else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0)
1776    example_indentation_increment = 0;
1777  else if (sscanf (string, "%d", &example_indentation_increment) != 1)
1778    return -1;
1779  return 0;
1780}
1781
1782static void
1783cm_exampleindent (void)
1784{
1785  char *arg;
1786
1787  get_rest_of_line (1, &arg);
1788  if (set_example_indentation_increment (arg) != 0)
1789    line_error (_("Bad argument to @%s"), command);
1790
1791  if (input_text[input_text_offset] == '\n')
1792    close_single_paragraph ();
1793
1794  free (arg);
1795}
1796
1797
1798/* @firstparagraphindent: suppress indentation in first paragraphs after
1799   headings. */
1800static int
1801set_firstparagraphindent (char *string)
1802{
1803  if (STREQ (string, "insert") || STREQ (string, _("insert")))
1804    do_first_par_indent = 1;
1805  else if (STREQ (string, "none") || STREQ (string, _("none")))
1806    do_first_par_indent = 0;
1807  else
1808    return -1;
1809  return 0;
1810}
1811
1812static void
1813cm_firstparagraphindent (void)
1814{
1815  char *arg;
1816
1817  get_rest_of_line (1, &arg);
1818  if (set_firstparagraphindent (arg) != 0)
1819    line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
1820
1821  free (arg);
1822}
1823
1824/* For DocBook and XML, produce &period; for `.@:'. This gives the processing
1825   software a fighting chance to treat it specially by not adding extra space.
1826
1827   Do this also for ?, !, and :.  */
1828void
1829cm_colon (void)
1830{
1831  if (xml)
1832    {
1833      if (strchr (".?!:", input_text[input_text_offset-3]) != NULL)
1834        {
1835          /* Erase literal character that's there, except `>', which is
1836             part of the XML tag.  */
1837          if (output_paragraph[output_paragraph_offset-1] != '>')
1838            output_paragraph_offset--;
1839
1840          switch (input_text[input_text_offset-3])
1841            {
1842            case '.':
1843              xml_insert_entity ("period");
1844              break;
1845            case '?':
1846              xml_insert_entity ("quest");
1847              break;
1848            case '!':
1849              xml_insert_entity ("excl");
1850              break;
1851            case ':':
1852              xml_insert_entity ("colon");
1853              break;
1854            }
1855        }
1856    }
1857}
1858
1859/* Ending sentences explicitly.  Currently, only outputs entities for XML
1860   output, for other formats it calls insert_self.  */
1861void
1862cm_punct (int arg)
1863{
1864  if (xml && !docbook)
1865    {
1866      switch (input_text[input_text_offset-1])
1867        {
1868        case '.':
1869          xml_insert_entity ("eosperiod");
1870          break;
1871        case '?':
1872          xml_insert_entity ("eosquest");
1873          break;
1874        case '!':
1875          xml_insert_entity ("eosexcl");
1876          break;
1877        }
1878    }
1879  else
1880    {
1881      insert_self (arg);
1882    }
1883}
1884