source: trunk/essentials/sys-apps/texinfo/makeinfo/html.c

Last change on this file was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

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