* Sync comment with code's reality.
[dragonfly.git] / contrib / texinfo / makeinfo / html.c
1 /* html.c -- html-related utilities.
2    $Id: html.c,v 1.26 2002/03/23 20:39:49 karl Exp $
3
4    Copyright (C) 1999, 2000, 01, 02 Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include "system.h"
21 #include "cmds.h"
22 #include "html.h"
23 #include "lang.h"
24 #include "makeinfo.h"
25 #include "sectioning.h"
26
27 /* See html.h.  */
28 int html_output_head_p = 0;
29
30 void
31 html_output_head ()
32 {
33   static char *html_title = NULL;
34   static int html_title_written = 0;
35
36   if (html_output_head_p)
37     return;
38   html_output_head_p = 1;
39
40   /* The <title> should not have markup, so use text_expansion.  */
41   if (!html_title)
42     html_title = title ? text_expansion (title) : _("Untitled");
43
44   add_word_args ("<html lang=\"%s\">\n<head>\n<title>%s</title>\n",
45                  language_table[language_code].abbrev, html_title);
46
47   add_word ("<meta http-equiv=\"Content-Type\" content=\"text/html");
48   if (document_encoding_code != no_encoding)
49     add_word_args ("; charset=%s",
50                    encoding_table[document_encoding_code].ecname);
51   add_word ("\">\n");
52
53   if (!document_description)
54     document_description = html_title;
55
56   add_word_args ("<meta name=description content=\"%s\">\n",
57                  document_description);
58   add_word_args ("<meta name=generator content=\"makeinfo %s\">\n", VERSION);
59   add_word ("<link href=\"http://www.gnu.org/software/texinfo/\" rel=generator-home>\n");
60
61   if (copying_text)
62     { /* copying_text has already been fully expanded in
63          begin_insertion (by full_expansion), so use insert_ rather than
64          add_.  It is not ideal that we include the html markup here within
65          <head>, but the alternative is to have yet more and different
66          expansions of the copying text.  Yuck.  */
67       insert_string ("<!--\n");
68       insert_string (copying_text);
69       insert_string ("-->\n");
70     }
71
72   add_word ("</head>\n<body>\n");
73
74   if (title && !html_title_written)
75     {
76       add_word_args ("<h1>%s</h1>\n", html_title);
77       html_title_written = 1;
78     }
79 }
80
81 \f
82 /* Escape HTML special characters in the string if necessary,
83    returning a pointer to a possibly newly-allocated one. */
84 char *
85 escape_string (string)
86      char * string;
87 {
88   int i=0, newlen=0;
89   char * newstring;
90
91   do
92     {
93       /* Find how much to allocate. */
94       switch (string[i])
95         {
96         case '&':
97           newlen += 5;          /* `&amp;' */
98           break;
99         case '<':
100         case '>':
101           newlen += 4;          /* `&lt;', `&gt;' */
102           break;
103         default:
104           newlen++;
105         }
106     }
107   while (string[i++]);
108
109   if (newlen == i) return string; /* Already OK. */
110
111   newstring = xmalloc (newlen);
112   i = 0;
113   do
114     {
115       switch (string[i])
116         {
117         case '&':
118           strcpy (newstring, "&amp;");
119           newstring += 5;
120           break;
121         case '<':
122           strcpy (newstring, "&lt;");
123           newstring += 4;
124           break;
125         case '>':
126           strcpy (newstring, "&gt;");
127           newstring += 4;
128           break;
129         default:
130           newstring[0] = string[i];
131           newstring++;
132         }
133     }
134   while (string[i++]);
135   free (string);
136   return newstring - newlen;
137 }
138
139 /* Open or close TAG according to START_OR_END. */
140 void
141 insert_html_tag (start_or_end, tag)
142      int start_or_end;
143      char *tag;
144 {
145   if (!paragraph_is_open && (start_or_end == START))
146     {
147       /* Need to compensate for the <p> we are about to insert, or
148          else cm_xxx functions that call us will get wrong text
149          between START and END.  */
150       adjust_braces_following (output_paragraph_offset, 3);
151       add_word ("<p>");
152     }
153   add_char ('<');
154   if (start_or_end != START)
155     add_char ('/');
156   add_word (tag);
157   add_char ('>');
158 }
159
160 /* Output an HTML <link> to the filename for NODE, including the
161    other string as extra attributes. */
162 void
163 add_link (nodename, attributes)
164      char *nodename, *attributes;
165 {
166   if (nodename)
167     {
168       add_html_elt ("<link ");
169       add_word_args ("%s", attributes);
170       add_word_args (" href=\"");
171       add_anchor_name (nodename, 1);
172       add_word ("\"></a>\n");
173     }
174 }
175
176 /* Output NAME with characters escaped as appropriate for an anchor
177    name, i.e., escape URL special characters as %<n>.  */
178 void
179 add_escaped_anchor_name (name)
180      char *name;
181 {
182   for (; *name; name++)
183     {
184       if (*name == '&')
185         add_word ("&amp;");
186       else if (! URL_SAFE_CHAR (*name))
187         /* Cast so characters with the high bit set are treated as >128,
188            for example o-umlaut should be 246, not -10.  */
189         add_word_args ("%%%x", (unsigned char) *name);
190       else
191         add_char (*name);
192     }
193 }
194
195 /* Insert the text for the name of a reference in an HTML anchor
196    appropriate for NODENAME.  If HREF is nonzero, it will be
197    appropriate for a href= attribute, rather than name= i.e., including
198    the `#' if it's an internal reference. */
199 void
200 add_anchor_name (nodename, href)
201      char *nodename;
202      int href;
203 {
204   if (href)
205     {
206       if (splitting)
207         add_url_name (nodename, href);
208       add_char ('#');
209     }
210   /* Always add NODENAME, so that the reference would pinpoint the
211      exact node on its file.  This is so several nodes could share the
212      same file, in case of file-name clashes, but also for more
213      accurate browser positioning.  */
214   if (strcasecmp (nodename, "(dir)") == 0)
215     /* Strip the parens, but keep the original letter-case.  */
216     add_word_args ("%.3s", nodename + 1);
217   else
218     add_escaped_anchor_name (nodename);
219 }
220
221 /* Insert the text for the name of a reference in an HTML url, aprropriate
222    for NODENAME */
223 void
224 add_url_name (nodename, href)
225      char *nodename;
226      int href;
227 {
228     add_nodename_to_filename (nodename, href);
229 }
230
231 /* Only allow [-0-9a-zA-Z_.] when nodifying filenames.  This may
232    result in filename clashes; e.g.,
233
234    @node Foo ],,,
235    @node Foo [,,,
236
237    both map to Foo--.html.  If that happens, cm_node will put all
238    the nodes whose file names clash on the same file.  */
239 void
240 fix_filename (filename)
241      char *filename;
242 {
243   char *p;
244   for (p = filename; *p; p++)
245     {
246       if (!(isalnum (*p) || strchr ("-._", *p)))
247         *p = '-';
248     }
249 }
250
251 /* As we can't look-up a (forward-referenced) nodes' html filename
252    from the tentry, we take the easy way out.  We assume that
253    nodenames are unique, and generate the html filename from the
254    nodename, that's always known.  */
255 static char *
256 nodename_to_filename_1 (nodename, href)
257      char *nodename;
258      int href;
259 {
260   char *p;
261   char *filename;
262   char dirname[PATH_MAX];
263
264   if (strcasecmp (nodename, "Top") == 0)
265     {
266       /* We want to convert references to the Top node into
267          "index.html#Top".  */
268       if (href)
269         filename = xstrdup ("index.html"); /* "#Top" is added by our callers */
270       else
271         filename = xstrdup ("Top");
272     }
273   else if (strcasecmp (nodename, "(dir)") == 0)
274     /* We want to convert references to the (dir) node into
275        "../index.html".  */
276     filename = xstrdup ("../index.html");
277   else
278     {
279       filename = xmalloc (PATH_MAX);
280       dirname[0] = '\0';
281       *filename = '\0';
282
283       /* Check for external reference: ``(info-document)node-name''
284          Assume this node lives at: ``../info-document/node-name.html''
285
286          We need to handle the special case (sigh): ``(info-document)'',
287          ie, an external top-node, which should translate to:
288          ``../info-document/info-document.html'' */
289
290       p = nodename;
291       if (*nodename == '(')
292         {
293           int length;
294
295           p = strchr (nodename, ')');
296           if (p == NULL)
297             {
298               line_error (_("Invalid node name: `%s'"), nodename);
299               exit (1);
300             }
301
302           length = p - nodename - 1;
303           if (length > 5 &&
304               FILENAME_CMPN (p - 5, ".info", 5) == 0)
305             length -= 5;
306           /* This is for DOS, and also for Windows and GNU/Linux
307              systems that might have Info files copied from a DOS 8+3
308              filesystem.  */
309           if (length > 4 &&
310               FILENAME_CMPN (p - 4, ".inf", 4) == 0)
311             length -= 4;
312           strcpy (filename, "../");
313           strncpy (dirname, nodename + 1, length);
314           *(dirname + length) = '\0';
315           fix_filename (dirname);
316           strcat (filename, dirname);
317           strcat (filename, "/");
318           p++;
319         }
320
321       /* In the case of just (info-document), there will be nothing
322          remaining, and we will refer to ../info-document/, which will
323          work fine.  */
324       strcat (filename, p);
325       if (*p)
326         {
327           /* Hmm */
328           fix_filename (filename + strlen (filename) - strlen (p));
329           strcat (filename, ".html");
330         }
331     }
332
333   /* Produce a file name suitable for the underlying filesystem.  */
334   normalize_filename (filename);
335
336 #if 0
337   /* We add ``#Nodified-filename'' anchor to external references to be
338      prepared for non-split HTML support.  Maybe drop this. */
339   if (href && *dirname)
340     {
341       strcat (filename, "#");
342       strcat (filename, p);
343       /* Hmm, again */
344       fix_filename (filename + strlen (filename) - strlen (p));
345     }
346 #endif
347
348   return filename;
349 }
350
351 /* If necessary, ie, if current filename != filename of node, output
352    the node name.  */
353 void
354 add_nodename_to_filename (nodename, href)
355      char *nodename;
356      int href;
357 {
358   /* for now, don't check: always output filename */
359   char *filename = nodename_to_filename_1 (nodename, href);
360   add_word (filename);
361   free (filename);
362 }
363
364 char *
365 nodename_to_filename (nodename)
366      char *nodename;
367 {
368   /* The callers of nodename_to_filename use the result to produce
369      <a href=, so call nodename_to_filename_1 with last arg non-zero.  */
370   return nodename_to_filename_1 (nodename, 1);
371 }