1 /* html.c -- html-related utilities.
2 $Id: html.c,v 1.28 2004/12/06 01:13:06 karl Exp $
3
4 Copyright (C) 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 "files.h"
24 #include "html.h"
25 #include "lang.h"
26 #include "makeinfo.h"
27 #include "node.h"
28 #include "sectioning.h"
29
30 __RCSID("$MirOS: src/gnu/usr.bin/texinfo/makeinfo/html.c,v 1.6 2012/05/21 21:30:48 tg Exp $");
31
32 /* Append CHAR to BUFFER, (re)allocating as necessary. We don't handle
33 null characters. */
34
35 typedef struct
36 {
37 unsigned size; /* allocated */
38 unsigned length; /* used */
39 char *buffer;
40 } buffer_type;
41
42 static buffer_type *
init_buffer(void)43 init_buffer (void)
44 {
45 buffer_type *buf = xmalloc (sizeof (buffer_type));
46 buf->length = 0;
47 buf->size = 0;
48 buf->buffer = NULL;
49
50 return buf;
51 }
52
53 static void
append_char(buffer_type * buf,int c)54 append_char (buffer_type *buf, int c)
55 {
56 buf->length++;
57 if (buf->length >= buf->size)
58 {
59 buf->size += 100;
60 buf->buffer = xrealloc (buf->buffer, buf->size);
61 }
62 buf->buffer[buf->length - 1] = c;
63 buf->buffer[buf->length] = 0;
64 }
65
66 /* Read the cascading style-sheet file FILENAME. Write out any @import
67 commands, which must come first, by the definition of css. If the
68 file contains any actual css code following the @imports, return it;
69 else return NULL. */
70 static char *
process_css_file(char * filename)71 process_css_file (char *filename)
72 {
73 int c;
74 int lastchar = 0;
75 FILE *f;
76 buffer_type *import_text = init_buffer ();
77 buffer_type *inline_text = init_buffer ();
78 unsigned lineno = 1;
79 enum { null_state, comment_state, import_state, inline_state } state
80 = null_state, prev_state;
81
82 prev_state = null_state;
83
84 /* read from stdin if `-' is the filename. */
85 f = STREQ (filename, "-") ? stdin : fopen (filename, "r");
86 if (!f)
87 {
88 error (_("%s: could not open --css-file: %s"), progname, filename);
89 return NULL;
90 }
91
92 /* Read the file. The @import statements must come at the beginning,
93 with only whitespace and comments allowed before any inline css code. */
94 while ((c = getc (f)) >= 0)
95 {
96 if (c == '\n')
97 lineno++;
98
99 switch (state)
100 {
101 case null_state: /* between things */
102 if (c == '@')
103 { /* Only @import and @charset should switch into
104 import_state, other @-commands, such as @media, should
105 put us into inline_state. I don't think any other css
106 @-commands start with `i' or `c', although of course
107 this will break when such a command is defined. */
108 int nextchar = getc (f);
109 if (nextchar == 'i' || nextchar == 'c')
110 {
111 append_char (import_text, c);
112 state = import_state;
113 }
114 else
115 {
116 ungetc (nextchar, f); /* wasn't an @import */
117 state = inline_state;
118 }
119 }
120 else if (c == '/')
121 { /* possible start of a comment */
122 int nextchar = getc (f);
123 if (nextchar == '*')
124 state = comment_state;
125 else
126 {
127 ungetc (nextchar, f); /* wasn't a comment */
128 state = inline_state;
129 }
130 }
131 else if (isspace (c))
132 ; /* skip whitespace; maybe should use c_isspace? */
133
134 else
135 /* not an @import, not a comment, not whitespace: we must
136 have started the inline text. */
137 state = inline_state;
138
139 if (state == inline_state)
140 append_char (inline_text, c);
141
142 if (state != null_state)
143 prev_state = null_state;
144 break;
145
146 case comment_state:
147 if (c == '/' && lastchar == '*')
148 state = prev_state; /* end of comment */
149 break; /* else ignore this comment char */
150
151 case import_state:
152 append_char (import_text, c); /* include this import char */
153 if (c == ';')
154 { /* done with @import */
155 append_char (import_text, '\n'); /* make the output nice */
156 state = null_state;
157 prev_state = import_state;
158 }
159 break;
160
161 case inline_state:
162 /* No harm in writing out comments, so don't bother parsing
163 them out, just append everything. */
164 append_char (inline_text, c);
165 break;
166 }
167
168 lastchar = c;
169 }
170
171 fclose(f);
172
173 /* Reached the end of the file. We should not be still in a comment. */
174 if (state == comment_state)
175 warning (_("%s:%d: --css-file ended in comment"), filename, lineno);
176
177 /* Write the @import text, if any. */
178 if (import_text->buffer)
179 {
180 add_word (import_text->buffer);
181 free (import_text->buffer);
182 free (import_text);
183 }
184
185 /* We're wasting the buffer struct memory, but so what. */
186 return inline_text->buffer;
187 }
188
189 HSTACK *htmlstack = NULL;
190
191 /* See html.h. */
192 int html_output_head_p = 0;
193 int html_title_written = 0;
194
195 static char utf8_encoding[] = "utf-8";
196
197 void
html_output_head(void)198 html_output_head (void)
199 {
200 static const char *html_title = NULL;
201 char *encoding;
202
203 if (html_output_head_p)
204 return;
205 html_output_head_p = 1;
206
207 encoding = current_document_encoding ();
208 if (!encoding || !*encoding)
209 encoding = utf8_encoding;
210 if (strcasecmp(encoding, "utf-8") && strcasecmp(encoding, "utf8"))
211 warning("encoding \"%s\" not UTF-8, this WILL lead to problems", encoding);
212
213 /* The <title> should not have markup, so use text_expansion. */
214 if (!html_title)
215 html_title = escape_string (title ?
216 text_expansion (title) : (char *) _("Untitled"));
217
218 /* Make sure this is the very first string of the output document. */
219 output_paragraph_offset = 0;
220
221 add_html_block_elt_args ("%s\n<html lang=\"%s\">\n<head>\n",
222 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\""
223 " \"http://www.w3.org/TR/html4/loose.dtd\">",
224 language_table[language_code].abbrev);
225
226 /* When splitting, add current node's name to title if it's available and not
227 Top. */
228 if (splitting && current_node && !STREQ (current_node, "Top"))
229 add_word_args ("<title>%s - %s</title>\n",
230 escape_string (xstrdup (current_node)), html_title);
231 else
232 add_word_args ("<title>%s</title>\n", html_title);
233
234 add_word_args ("<meta http-equiv=\"Content-Type\" content=\"text/html;"
235 " charset=%s\">\n",
236 encoding);
237
238 if (!document_description)
239 document_description = html_title;
240
241 add_word_args ("<meta name=\"description\" content=\"%s\">\n",
242 document_description);
243 add_word_args ("<meta name=\"generator\" content=\"makeinfo %s\">\n",
244 VERSION "-MirOS");
245
246 /* Navigation bar links. */
247 if (!splitting)
248 add_word ("<link title=\"Top\" rel=\"top\" href=\"#Top\">\n");
249 else if (tag_table)
250 {
251 /* Always put a top link. */
252 add_word ("<link title=\"Top\" rel=\"start\" href=\"index.html#Top\">\n");
253
254 /* We already have a top link, avoid duplication. */
255 if (tag_table->up && !STREQ (tag_table->up, "Top"))
256 add_link (tag_table->up, "rel=\"up\"");
257
258 if (tag_table->prev)
259 add_link (tag_table->prev, "rel=\"prev\"");
260
261 if (tag_table->next)
262 add_link (tag_table->next, "rel=\"next\"");
263
264 /* fixxme: Look for a way to put links to various indices in the
265 document. Also possible candidates to be added here are First and
266 Last links. */
267 }
268 else
269 {
270 /* We are splitting, but we neither have a tag_table. So this must be
271 index.html. So put a link to Top. */
272 add_word ("<link title=\"Top\" rel=\"start\" href=\"#Top\">\n");
273 }
274
275 add_word ("<link href=\"http://www.gnu.org/software/texinfo/\" \
276 rel=\"generator-home\" title=\"Texinfo Homepage\">\n");
277
278 if (copying_text)
279 { /* It is not ideal that we include the html markup here within
280 <head>, so we use text_expansion. */
281 insert_string ("<!--\n");
282 insert_string (text_expansion (copying_text));
283 insert_string ("-->\n");
284 }
285
286 /* Put the style definitions in a comment for the sake of browsers
287 that don't support <style>. */
288 add_word ("<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n");
289 add_word ("<style type=\"text/css\"><!--\n");
290
291 {
292 char *css_inline = NULL;
293
294 if (css_include)
295 /* This writes out any @import commands from the --css-file,
296 and returns any actual css code following the imports. */
297 css_inline = process_css_file (css_include);
298
299 /* This seems cleaner than adding <br>'s at the end of each line for
300 these "roman" displays. It's hardly the end of the world if the
301 browser doesn't do <style>s, in any case; they'll just come out in
302 typewriter. */
303 #define CSS_FONT_INHERIT "font-family:inherit"
304 add_word_args (" pre.display { %s }\n", CSS_FONT_INHERIT);
305 add_word_args (" pre.format { %s }\n", CSS_FONT_INHERIT);
306
307 /* Alternatively, we could do <font size=-1> in insertion.c, but this
308 way makes it easier to override. */
309 #define CSS_FONT_SMALLER "font-size:smaller"
310 add_word_args (" pre.smalldisplay { %s; %s }\n", CSS_FONT_INHERIT,
311 CSS_FONT_SMALLER);
312 add_word_args (" pre.smallformat { %s; %s }\n", CSS_FONT_INHERIT,
313 CSS_FONT_SMALLER);
314 add_word_args (" pre.smallexample { %s }\n", CSS_FONT_SMALLER);
315 add_word_args (" pre.smalllisp { %s }\n", CSS_FONT_SMALLER);
316
317 /* Since HTML doesn't have a sc element, we use span with a bit of
318 CSS spice instead. */
319 #define CSS_FONT_SMALL_CAPS "font-variant:small-caps"
320 add_word_args (" span.sc { %s }\n", CSS_FONT_SMALL_CAPS);
321
322 /* Roman (default) font class, closest we can come. */
323 #define CSS_FONT_ROMAN "font-family:serif; font-weight:normal;"
324 add_word_args (" span.roman { %s } \n", CSS_FONT_ROMAN);
325
326 /* Sans serif font class. */
327 #define CSS_FONT_SANSSERIF "font-family:sans-serif; font-weight:normal;"
328 add_word_args (" span.sansserif { %s } \n", CSS_FONT_SANSSERIF);
329
330 /* Write out any css code from the user's --css-file. */
331 if (css_inline)
332 insert_string (css_inline);
333
334 add_word ("--></style>\n");
335 }
336
337 add_word ("</head>\n<body>\n");
338
339 if (title && !html_title_written && titlepage_cmd_present)
340 {
341 add_word_args ("<h1 class=\"settitle\">%s</h1>\n", html_title);
342 html_title_written = 1;
343 }
344
345 if (encoding != utf8_encoding)
346 free (encoding);
347 }
348
349 /* Escape HTML special characters in the string if necessary,
350 returning a pointer to a possibly newly-allocated one. */
351 char *
escape_string(char * string)352 escape_string (char *string)
353 {
354 char *newstring;
355 int i = 0, newlen = 0;
356
357 do
358 {
359 /* Find how much to allocate. */
360 switch (string[i])
361 {
362 case '"':
363 newlen += 6; /* `"' */
364 break;
365 case '&':
366 newlen += 5; /* `&' */
367 break;
368 case '<':
369 case '>':
370 newlen += 4; /* `<', `>' */
371 break;
372 default:
373 newlen++;
374 }
375 }
376 while (string[i++]);
377
378 if (newlen == i) return string; /* Already OK. */
379
380 newstring = xmalloc (newlen);
381 i = 0;
382 do
383 {
384 switch (string[i])
385 {
386 case '"':
387 strcpy (newstring, """);
388 newstring += 6;
389 break;
390 case '&':
391 strcpy (newstring, "&");
392 newstring += 5;
393 break;
394 case '<':
395 strcpy (newstring, "<");
396 newstring += 4;
397 break;
398 case '>':
399 strcpy (newstring, ">");
400 newstring += 4;
401 break;
402 default:
403 newstring[0] = string[i];
404 newstring++;
405 }
406 }
407 while (string[i++]);
408 free (string);
409 return newstring - newlen;
410 }
411
412 /* Save current tag. */
413 static void
push_tag(char * tag,char * attribs)414 push_tag (char *tag, char *attribs)
415 {
416 HSTACK *newstack = xmalloc (sizeof (HSTACK));
417
418 newstack->tag = tag;
419 newstack->attribs = xstrdup (attribs);
420 newstack->next = htmlstack;
421 htmlstack = newstack;
422 }
423
424 /* Get last tag. */
425 static void
pop_tag(void)426 pop_tag (void)
427 {
428 HSTACK *tos = htmlstack;
429
430 if (!tos)
431 {
432 line_error (_("[unexpected] no html tag to pop"));
433 return;
434 }
435
436 free (htmlstack->attribs);
437
438 htmlstack = htmlstack->next;
439 free (tos);
440 }
441
442 /* Check if tag is an empty or a whitespace only element.
443 If so, remove it, keeping whitespace intact. */
444 int
rollback_empty_tag(char * tag)445 rollback_empty_tag (char *tag)
446 {
447 int check_position = output_paragraph_offset;
448 int taglen = strlen (tag);
449 int rollback_happened = 0;
450 char *contents = ""; /* FIXME (ptr to constant, later
451 assigned to malloc'd address).
452 */
453 char *contents_canon_white = "";
454
455 /* If output_paragraph is empty, we cannot rollback :-\ */
456 if (output_paragraph_offset <= 0)
457 return 0;
458
459 /* Find the end of the previous tag. */
460 while (check_position > 0 && output_paragraph[check_position-1] != '>')
461 check_position--;
462
463 /* Save stuff between tag's end to output_paragraph's end. */
464 if (check_position != output_paragraph_offset)
465 {
466 contents = xmalloc (output_paragraph_offset - check_position + 1);
467 memcpy (contents, output_paragraph + check_position,
468 output_paragraph_offset - check_position);
469
470 contents[output_paragraph_offset - check_position] = '\0';
471
472 contents_canon_white = xstrdup (contents);
473 canon_white (contents_canon_white);
474 }
475
476 /* Find the start of the previous tag. */
477 while (check_position > 0 && output_paragraph[check_position-1] != '<')
478 check_position--;
479
480 /* Check to see if this is the tag. */
481 if (strncmp ((char *) output_paragraph + check_position, tag, taglen) == 0
482 && (whitespace (output_paragraph[check_position + taglen])
483 || output_paragraph[check_position + taglen] == '>'))
484 {
485 if (!contents_canon_white || !*contents_canon_white)
486 {
487 /* Empty content after whitespace removal, so roll it back. */
488 output_paragraph_offset = check_position - 1;
489 rollback_happened = 1;
490
491 /* Original contents may not be empty (whitespace.) */
492 if (contents && *contents)
493 {
494 insert_string (contents);
495 free (contents);
496 }
497 }
498 }
499
500 return rollback_happened;
501 }
502
503 /* Open or close TAG according to START_OR_END. */
504 void
505 #if defined (VA_FPRINTF) && __STDC__
insert_html_tag_with_attribute(int start_or_end,char * tag,char * format,...)506 insert_html_tag_with_attribute (int start_or_end, char *tag, char *format, ...)
507 #else
508 insert_html_tag_with_attribute (start_or_end, tag, format, va_alist)
509 int start_or_end;
510 char *tag;
511 char *format;
512 va_dcl
513 #endif
514 {
515 char *old_tag = NULL;
516 char *old_attribs = NULL;
517 char formatted_attribs[2000]; /* xx no fixed limits */
518 int do_return = 0;
519 extern int in_html_elt;
520
521 if (start_or_end != START)
522 pop_tag ();
523
524 if (htmlstack)
525 {
526 old_tag = htmlstack->tag;
527 old_attribs = htmlstack->attribs;
528 }
529
530 if (format)
531 {
532 #ifdef VA_SPRINTF
533 va_list ap;
534 #endif
535
536 VA_START (ap, format);
537 #ifdef VA_SPRINTF
538 VA_SPRINTF (formatted_attribs, format, ap);
539 #else
540 sprintf (formatted_attribs, format, a1, a2, a3, a4, a5, a6, a7, a8);
541 #endif
542 va_end (ap);
543 }
544 else
545 formatted_attribs[0] = '\0';
546
547 /* Exception: can nest multiple spans. */
548 if (htmlstack
549 && STREQ (htmlstack->tag, tag)
550 && !(STREQ (tag, "span") && STREQ (old_attribs, formatted_attribs)))
551 do_return = 1;
552
553 if (start_or_end == START)
554 push_tag (tag, formatted_attribs);
555
556 if (do_return)
557 return;
558
559 in_html_elt++;
560
561 /* texinfo.tex doesn't support more than one font attribute
562 at the same time. */
563 if ((start_or_end == START) && old_tag && *old_tag
564 && !rollback_empty_tag (old_tag))
565 add_word_args ("</%s>", old_tag);
566
567 if (*tag)
568 {
569 if (start_or_end == START)
570 add_word_args (format ? "<%s %s>" : "<%s>", tag, formatted_attribs);
571 else if (!rollback_empty_tag (tag))
572 /* Insert close tag only if we didn't rollback,
573 in which case the opening tag is removed. */
574 add_word_args ("</%s>", tag);
575 }
576
577 if ((start_or_end != START) && old_tag && *old_tag)
578 add_word_args (strlen (old_attribs) > 0 ? "<%s %s>" : "<%s>",
579 old_tag, old_attribs);
580
581 in_html_elt--;
582 }
583
584 void
insert_html_tag(int start_or_end,char * tag)585 insert_html_tag (int start_or_end, char *tag)
586 {
587 insert_html_tag_with_attribute (start_or_end, tag, NULL);
588 }
589
590 /* Output an HTML <link> to the filename for NODE, including the
591 other string as extra attributes. */
592 void
add_link(char * nodename,char * attributes)593 add_link (char *nodename, char *attributes)
594 {
595 if (nodename)
596 {
597 add_html_elt ("<link ");
598 add_word_args ("%s", attributes);
599 add_word_args (" href=\"");
600 add_anchor_name (nodename, 1);
601 add_word_args ("\" title=\"%s\">\n", nodename);
602 }
603 }
604
605 /* Output NAME with characters escaped as appropriate for an anchor
606 name, i.e., escape URL special characters with our _00hh convention
607 if OLD is zero. (See the manual for details on the new scheme.)
608
609 If OLD is nonzero, generate the node name with the 4.6-and-earlier
610 convention of %hh (and more special characters output as-is, notably
611 - and *). This is only so that external references to old names can
612 still work with HTML generated by the new makeinfo; the gcc folks
613 needed this. Our own HTML does not refer to these names. */
614
615 void
add_escaped_anchor_name(char * name,int old)616 add_escaped_anchor_name (char *name, int old)
617 {
618 canon_white (name);
619
620 if (!old && !strchr ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
621 *name))
622 { /* XHTML does not allow anything but an ASCII letter to start an
623 identifier. Therefore kludge in this constant string if we
624 have a nonletter. */
625 add_word ("g_t");
626 }
627
628 for (; *name; name++)
629 {
630 if (cr_or_whitespace (*name))
631 add_char ('-');
632
633 else if (!old && !URL_SAFE_CHAR (*name))
634 /* Cast so characters with the high bit set are treated as >128,
635 for example o-umlaut should be 246, not -10. */
636 add_word_args ("_00%x", (unsigned char) *name);
637
638 else if (old && !URL_SAFE_CHAR (*name) && !OLD_URL_SAFE_CHAR (*name))
639 /* Different output convention, but still cast as above. */
640 add_word_args ("%%%x", (unsigned char) *name);
641
642 else
643 add_char (*name);
644 }
645 }
646
647 /* Insert the text for the name of a reference in an HTML anchor
648 appropriate for NODENAME.
649
650 If HREF is zero, generate text for name= in the new node name
651 conversion convention.
652 If HREF is negative, generate text for name= in the old convention.
653 If HREF is positive, generate the name for an href= attribute, i.e.,
654 including the `#' if it's an internal reference. */
655 void
add_anchor_name(char * nodename,int href)656 add_anchor_name (char *nodename, int href)
657 {
658 if (href > 0)
659 {
660 if (splitting)
661 add_url_name (nodename, href);
662 add_char ('#');
663 }
664 /* Always add NODENAME, so that the reference would pinpoint the
665 exact node on its file. This is so several nodes could share the
666 same file, in case of file-name clashes, but also for more
667 accurate browser positioning. */
668 if (strcasecmp (nodename, "(dir)") == 0)
669 /* Strip the parens, but keep the original letter-case. */
670 add_word_args ("%.3s", nodename + 1);
671 else if (strcasecmp (nodename, "top") == 0)
672 add_word ("Top");
673 else
674 add_escaped_anchor_name (nodename, href < 0);
675 }
676
677 /* Insert the text for the name of a reference in an HTML url, aprropriate
678 for NODENAME */
679 void
add_url_name(char * nodename,int href)680 add_url_name (char *nodename, int href)
681 {
682 add_nodename_to_filename (nodename, href);
683 }
684
685 /* Convert non [A-Za-z0-9] to _00xx, where xx means the hexadecimal
686 representation of the ASCII character. Also convert spaces and
687 newlines to dashes. */
688 static void
fix_filename(char * filename)689 fix_filename (char *filename)
690 {
691 int i;
692 int len = strlen (filename);
693 char *oldname = xstrdup (filename);
694
695 *filename = '\0';
696
697 for (i = 0; i < len; i++)
698 {
699 if (cr_or_whitespace (oldname[i]))
700 strcat (filename, "-");
701 else if (URL_SAFE_CHAR (oldname[i]))
702 strncat (filename, (char *) oldname + i, 1);
703 else
704 {
705 char *hexchar = xmalloc (6 * sizeof (char));
706 sprintf (hexchar, "_00%x", (unsigned char) oldname[i]);
707 strcat (filename, hexchar);
708 free (hexchar);
709 }
710
711 /* Check if we are nearing boundaries. */
712 if (strlen (filename) >= PATH_MAX - 20)
713 break;
714 }
715
716 free (oldname);
717 }
718
719 /* As we can't look-up a (forward-referenced) nodes' html filename
720 from the tentry, we take the easy way out. We assume that
721 nodenames are unique, and generate the html filename from the
722 nodename, that's always known. */
723 static char *
nodename_to_filename_1(char * nodename,int href)724 nodename_to_filename_1 (char *nodename, int href)
725 {
726 char *p;
727 char *filename;
728 char dirname[PATH_MAX];
729
730 if (strcasecmp (nodename, "Top") == 0)
731 {
732 /* We want to convert references to the Top node into
733 "index.html#Top". */
734 if (href)
735 filename = xstrdup ("index.html"); /* "#Top" is added by our callers */
736 else
737 filename = xstrdup ("Top");
738 }
739 else if (strcasecmp (nodename, "(dir)") == 0)
740 /* We want to convert references to the (dir) node into
741 "../index.html". */
742 filename = xstrdup ("../index.html");
743 else
744 {
745 filename = xmalloc (PATH_MAX);
746 dirname[0] = '\0';
747 *filename = '\0';
748
749 /* Check for external reference: ``(info-document)node-name''
750 Assume this node lives at: ``../info-document/node-name.html''
751
752 We need to handle the special case (sigh): ``(info-document)'',
753 ie, an external top-node, which should translate to:
754 ``../info-document/info-document.html'' */
755
756 p = nodename;
757 if (*nodename == '(')
758 {
759 int length;
760
761 p = strchr (nodename, ')');
762 if (p == NULL)
763 {
764 line_error (_("[unexpected] invalid node name: `%s'"), nodename);
765 xexit (1);
766 }
767
768 length = p - nodename - 1;
769 if (length > 5 &&
770 FILENAME_CMPN (p - 5, ".info", 5) == 0)
771 length -= 5;
772 /* This is for DOS, and also for Windows and GNU/Linux
773 systems that might have Info files copied from a DOS 8+3
774 filesystem. */
775 if (length > 4 &&
776 FILENAME_CMPN (p - 4, ".inf", 4) == 0)
777 length -= 4;
778 strcpy (filename, "../");
779 strncpy (dirname, nodename + 1, length);
780 *(dirname + length) = '\0';
781 fix_filename (dirname);
782 strcat (filename, dirname);
783 strcat (filename, "/");
784 p++;
785 }
786
787 /* In the case of just (info-document), there will be nothing
788 remaining, and we will refer to ../info-document/, which will
789 work fine. */
790 strcat (filename, p);
791 if (*p)
792 {
793 /* Hmm */
794 fix_filename (filename + strlen (filename) - strlen (p));
795 strcat (filename, ".html");
796 }
797 }
798
799 /* Produce a file name suitable for the underlying filesystem. */
800 normalize_filename (filename);
801
802 #if 0
803 /* We add ``#Nodified-filename'' anchor to external references to be
804 prepared for non-split HTML support. Maybe drop this. */
805 if (href && *dirname)
806 {
807 strcat (filename, "#");
808 strcat (filename, p);
809 /* Hmm, again */
810 fix_filename (filename + strlen (filename) - strlen (p));
811 }
812 #endif
813
814 return filename;
815 }
816
817 /* If necessary, ie, if current filename != filename of node, output
818 the node name. */
819 void
add_nodename_to_filename(char * nodename,int href)820 add_nodename_to_filename (char *nodename, int href)
821 {
822 /* for now, don't check: always output filename */
823 char *filename = nodename_to_filename_1 (nodename, href);
824 add_word (filename);
825 free (filename);
826 }
827
828 char *
nodename_to_filename(char * nodename)829 nodename_to_filename (char *nodename)
830 {
831 /* The callers of nodename_to_filename use the result to produce
832 <a href=, so call nodename_to_filename_1 with last arg non-zero. */
833 return nodename_to_filename_1 (nodename, 1);
834 }
835