1/* $NetBSD: html.c,v 1.2 2020/06/05 12:47:28 rin Exp $ */ 2 3/* html.c -- html-related utilities. 4 Id: html.c,v 1.28 2004/12/06 01:13:06 karl Exp 5 6 Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software 7 Foundation, Inc. 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2, or (at your option) 12 any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software Foundation, 21 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 22 23#include "system.h" 24#include "cmds.h" 25#include "files.h" 26#include "html.h" 27#include "lang.h" 28#include "makeinfo.h" 29#include "node.h" 30#include "sectioning.h" 31 32 33/* Append CHAR to BUFFER, (re)allocating as necessary. We don't handle 34 null characters. */ 35 36typedef struct 37{ 38 unsigned size; /* allocated */ 39 unsigned length; /* used */ 40 char *buffer; 41} buffer_type; 42 43static buffer_type * 44init_buffer (void) 45{ 46 buffer_type *buf = xmalloc (sizeof (buffer_type)); 47 buf->length = 0; 48 buf->size = 0; 49 buf->buffer = NULL; 50 51 return buf; 52} 53 54static void 55append_char (buffer_type *buf, int c) 56{ 57 buf->length++; 58 if (buf->length >= buf->size) 59 { 60 buf->size += 100; 61 buf->buffer = xrealloc (buf->buffer, buf->size); 62 } 63 buf->buffer[buf->length - 1] = c; 64 buf->buffer[buf->length] = 0; 65} 66 67/* Read the cascading style-sheet file FILENAME. Write out any @import 68 commands, which must come first, by the definition of css. If the 69 file contains any actual css code following the @imports, return it; 70 else return NULL. */ 71static char * 72process_css_file (char *filename) 73{ 74 int c; 75 int lastchar = 0; 76 FILE *f; 77 buffer_type *import_text = init_buffer (); 78 buffer_type *inline_text = init_buffer (); 79 unsigned lineno = 1; 80 enum { null_state, comment_state, import_state, inline_state } state 81 = null_state, prev_state; 82 83 prev_state = null_state; 84 85 /* read from stdin if `-' is the filename. */ 86 f = STREQ (filename, "-") ? stdin : fopen (filename, "r"); 87 if (!f) 88 { 89 error (_("%s: could not open --css-file: %s"), progname, filename); 90 return NULL; 91 } 92 93 /* Read the file. The @import statements must come at the beginning, 94 with only whitespace and comments allowed before any inline css code. */ 95 while ((c = getc (f)) >= 0) 96 { 97 if (c == '\n') 98 lineno++; 99 100 switch (state) 101 { 102 case null_state: /* between things */ 103 if (c == '@') 104 { /* Only @import and @charset should switch into 105 import_state, other @-commands, such as @media, should 106 put us into inline_state. I don't think any other css 107 @-commands start with `i' or `c', although of course 108 this will break when such a command is defined. */ 109 int nextchar = getc (f); 110 if (nextchar == 'i' || nextchar == 'c') 111 { 112 append_char (import_text, c); 113 state = import_state; 114 } 115 else 116 { 117 ungetc (nextchar, f); /* wasn't an @import */ 118 state = inline_state; 119 } 120 } 121 else if (c == '/') 122 { /* possible start of a comment */ 123 int nextchar = getc (f); 124 if (nextchar == '*') 125 state = comment_state; 126 else 127 { 128 ungetc (nextchar, f); /* wasn't a comment */ 129 state = inline_state; 130 } 131 } 132 else if (isspace (c)) 133 ; /* skip whitespace; maybe should use c_isspace? */ 134 135 else 136 /* not an @import, not a comment, not whitespace: we must 137 have started the inline text. */ 138 state = inline_state; 139 140 if (state == inline_state) 141 append_char (inline_text, c); 142 143 if (state != null_state) 144 prev_state = null_state; 145 break; 146 147 case comment_state: 148 if (c == '/' && lastchar == '*') 149 state = prev_state; /* end of comment */ 150 break; /* else ignore this comment char */ 151 152 case import_state: 153 append_char (import_text, c); /* include this import char */ 154 if (c == ';') 155 { /* done with @import */ 156 append_char (import_text, '\n'); /* make the output nice */ 157 state = null_state; 158 prev_state = import_state; 159 } 160 break; 161 162 case inline_state: 163 /* No harm in writing out comments, so don't bother parsing 164 them out, just append everything. */ 165 append_char (inline_text, c); 166 break; 167 } 168 169 lastchar = c; 170 } 171 172 /* Reached the end of the file. We should not be still in a comment. */ 173 if (state == comment_state) 174 warning (_("%s:%d: --css-file ended in comment"), filename, lineno); 175 176 /* Write the @import text, if any. */ 177 if (import_text->buffer) 178 { 179 add_word (import_text->buffer); 180 free (import_text->buffer); 181 free (import_text); 182 } 183 184 /* We're wasting the buffer struct memory, but so what. */ 185 return inline_text->buffer; 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/* Escape HTML special characters in the string if necessary, 342 returning a pointer to a possibly newly-allocated one. */ 343char * 344escape_string (char *string) 345{ 346 char *newstring; 347 int i = 0, newlen = 0; 348 349 do 350 { 351 /* Find how much to allocate. */ 352 switch (string[i]) 353 { 354 case '"': 355 newlen += 6; /* `"' */ 356 break; 357 case '&': 358 newlen += 5; /* `&' */ 359 break; 360 case '<': 361 case '>': 362 newlen += 4; /* `<', `>' */ 363 break; 364 default: 365 newlen++; 366 } 367 } 368 while (string[i++]); 369 370 if (newlen == i) return string; /* Already OK. */ 371 372 newstring = xmalloc (newlen); 373 i = 0; 374 do 375 { 376 switch (string[i]) 377 { 378 case '"': 379 strcpy (newstring, """); 380 newstring += 6; 381 break; 382 case '&': 383 strcpy (newstring, "&"); 384 newstring += 5; 385 break; 386 case '<': 387 strcpy (newstring, "<"); 388 newstring += 4; 389 break; 390 case '>': 391 strcpy (newstring, ">"); 392 newstring += 4; 393 break; 394 default: 395 newstring[0] = string[i]; 396 newstring++; 397 } 398 } 399 while (string[i++]); 400 free (string); 401 return newstring - newlen; 402} 403 404/* Save current tag. */ 405static void 406push_tag (char *tag, char *attribs) 407{ 408 HSTACK *newstack = xmalloc (sizeof (HSTACK)); 409 410 newstack->tag = tag; 411 newstack->attribs = xstrdup (attribs); 412 newstack->next = htmlstack; 413 htmlstack = newstack; 414} 415 416/* Get last tag. */ 417static void 418pop_tag (void) 419{ 420 HSTACK *tos = htmlstack; 421 422 if (!tos) 423 { 424 line_error (_("[unexpected] no html tag to pop")); 425 return; 426 } 427 428 free (htmlstack->attribs); 429 430 htmlstack = htmlstack->next; 431 free (tos); 432} 433 434/* Check if tag is an empty or a whitespace only element. 435 If so, remove it, keeping whitespace intact. */ 436int 437rollback_empty_tag (char *tag) 438{ 439 int check_position = output_paragraph_offset; 440 int taglen = strlen (tag); 441 int rollback_happened = 0; 442 char *contents = ""; 443 char *contents_canon_white = ""; 444 445 /* If output_paragraph is empty, we cannot rollback :-\ */ 446 if (output_paragraph_offset <= 0) 447 return 0; 448 449 /* Find the end of the previous tag. */ 450 while (check_position > 0 && output_paragraph[check_position-1] != '>') 451 check_position--; 452 453 /* Save stuff between tag's end to output_paragraph's end. */ 454 if (check_position != output_paragraph_offset) 455 { 456 contents = xmalloc (output_paragraph_offset - check_position + 1); 457 memcpy (contents, output_paragraph + check_position, 458 output_paragraph_offset - check_position); 459 460 contents[output_paragraph_offset - check_position] = '\0'; 461 462 contents_canon_white = xstrdup (contents); 463 canon_white (contents_canon_white); 464 } 465 466 /* Find the start of the previous tag. */ 467 while (check_position > 0 && output_paragraph[check_position-1] != '<') 468 check_position--; 469 470 /* Check to see if this is the tag. */ 471 if (strncmp ((char *) output_paragraph + check_position, tag, taglen) == 0 472 && (whitespace (output_paragraph[check_position + taglen]) 473 || output_paragraph[check_position + taglen] == '>')) 474 { 475 if (!contents_canon_white || !*contents_canon_white) 476 { 477 /* Empty content after whitespace removal, so roll it back. */ 478 output_paragraph_offset = check_position - 1; 479 rollback_happened = 1; 480 481 /* Original contents may not be empty (whitespace.) */ 482 if (contents && *contents) 483 { 484 insert_string (contents); 485 free (contents); 486 } 487 } 488 } 489 490 return rollback_happened; 491} 492 493/* Open or close TAG according to START_OR_END. */ 494void 495#if defined (VA_FPRINTF) && __STDC__ 496insert_html_tag_with_attribute (int start_or_end, char *tag, char *format, ...) 497#else 498insert_html_tag_with_attribute (start_or_end, tag, format, va_alist) 499 int start_or_end; 500 char *tag; 501 char *format; 502 va_dcl 503#endif 504{ 505 char *old_tag = NULL; 506 char *old_attribs = NULL; 507 char formatted_attribs[2000]; /* xx no fixed limits */ 508 int do_return = 0; 509 extern int in_html_elt; 510 511 if (start_or_end != START) 512 pop_tag (); 513 514 if (htmlstack) 515 { 516 old_tag = htmlstack->tag; 517 old_attribs = htmlstack->attribs; 518 } 519 520 if (format) 521 { 522#ifdef VA_SPRINTF 523 va_list ap; 524#endif 525 526 VA_START (ap, format); 527#ifdef VA_SPRINTF 528 VA_SPRINTF (formatted_attribs, format, ap); 529#else 530 sprintf (formatted_attribs, format, a1, a2, a3, a4, a5, a6, a7, a8); 531#endif 532 va_end (ap); 533 } 534 else 535 formatted_attribs[0] = '\0'; 536 537 /* Exception: can nest multiple spans. */ 538 if (htmlstack 539 && STREQ (htmlstack->tag, tag) 540 && !(STREQ (tag, "span") && STREQ (old_attribs, formatted_attribs))) 541 do_return = 1; 542 543 if (start_or_end == START) 544 push_tag (tag, formatted_attribs); 545 546 if (do_return) 547 return; 548 549 in_html_elt++; 550 551 /* texinfo.tex doesn't support more than one font attribute 552 at the same time. */ 553 if ((start_or_end == START) && old_tag && *old_tag 554 && !rollback_empty_tag (old_tag)) 555 add_word_args ("</%s>", old_tag); 556 557 if (*tag) 558 { 559 if (start_or_end == START) 560 add_word_args (format ? "<%s %s>" : "<%s>", tag, formatted_attribs); 561 else if (!rollback_empty_tag (tag)) 562 /* Insert close tag only if we didn't rollback, 563 in which case the opening tag is removed. */ 564 add_word_args ("</%s>", tag); 565 } 566 567 if ((start_or_end != START) && old_tag && *old_tag) 568 add_word_args (strlen (old_attribs) > 0 ? "<%s %s>" : "<%s>", 569 old_tag, old_attribs); 570 571 in_html_elt--; 572} 573 574void 575insert_html_tag (int start_or_end, char *tag) 576{ 577 insert_html_tag_with_attribute (start_or_end, tag, NULL); 578} 579 580/* Output an HTML <link> to the filename for NODE, including the 581 other string as extra attributes. */ 582void 583add_link (char *nodename, char *attributes) 584{ 585 if (nodename) 586 { 587 add_html_elt ("<link "); 588 add_word_args ("%s", attributes); 589 add_word_args (" href=\""); 590 add_anchor_name (nodename, 1); 591 add_word_args ("\" title=\"%s\">\n", nodename); 592 } 593} 594 595/* Output NAME with characters escaped as appropriate for an anchor 596 name, i.e., escape URL special characters with our _00hh convention 597 if OLD is zero. (See the manual for details on the new scheme.) 598 599 If OLD is nonzero, generate the node name with the 4.6-and-earlier 600 convention of %hh (and more special characters output as-is, notably 601 - and *). This is only so that external references to old names can 602 still work with HTML generated by the new makeinfo; the gcc folks 603 needed this. Our own HTML does not refer to these names. */ 604 605void 606add_escaped_anchor_name (char *name, int old) 607{ 608 canon_white (name); 609 610 if (!old && !strchr ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 611 *name)) 612 { /* XHTML does not allow anything but an ASCII letter to start an 613 identifier. Therefore kludge in this constant string if we 614 have a nonletter. */ 615 add_word ("g_t"); 616 } 617 618 for (; *name; name++) 619 { 620 if (cr_or_whitespace (*name)) 621 add_char ('-'); 622 623 else if (!old && !URL_SAFE_CHAR (*name)) 624 /* Cast so characters with the high bit set are treated as >128, 625 for example o-umlaut should be 246, not -10. */ 626 add_word_args ("_00%x", (unsigned char) *name); 627 628 else if (old && !URL_SAFE_CHAR (*name) && !OLD_URL_SAFE_CHAR (*name)) 629 /* Different output convention, but still cast as above. */ 630 add_word_args ("%%%x", (unsigned char) *name); 631 632 else 633 add_char (*name); 634 } 635} 636 637/* Insert the text for the name of a reference in an HTML anchor 638 appropriate for NODENAME. 639 640 If HREF is zero, generate text for name= in the new node name 641 conversion convention. 642 If HREF is negative, generate text for name= in the old convention. 643 If HREF is positive, generate the name for an href= attribute, i.e., 644 including the `#' if it's an internal reference. */ 645void 646add_anchor_name (char *nodename, int href) 647{ 648 if (href > 0) 649 { 650 if (splitting) 651 add_url_name (nodename, href); 652 add_char ('#'); 653 } 654 /* Always add NODENAME, so that the reference would pinpoint the 655 exact node on its file. This is so several nodes could share the 656 same file, in case of file-name clashes, but also for more 657 accurate browser positioning. */ 658 if (strcasecmp (nodename, "(dir)") == 0) 659 /* Strip the parens, but keep the original letter-case. */ 660 add_word_args ("%.3s", nodename + 1); 661 else if (strcasecmp (nodename, "top") == 0) 662 add_word ("Top"); 663 else 664 add_escaped_anchor_name (nodename, href < 0); 665} 666 667/* Insert the text for the name of a reference in an HTML url, aprropriate 668 for NODENAME */ 669void 670add_url_name (char *nodename, int href) 671{ 672 add_nodename_to_filename (nodename, href); 673} 674 675/* Convert non [A-Za-z0-9] to _00xx, where xx means the hexadecimal 676 representation of the ASCII character. Also convert spaces and 677 newlines to dashes. */ 678static void 679fix_filename (char *filename) 680{ 681 int i; 682 int len = strlen (filename); 683 char *oldname = xstrdup (filename); 684 685 *filename = '\0'; 686 687 for (i = 0; i < len; i++) 688 { 689 if (cr_or_whitespace (oldname[i])) 690 strcat (filename, "-"); 691 else if (URL_SAFE_CHAR (oldname[i])) 692 strncat (filename, (char *) oldname + i, 1); 693 else 694 { 695 char *hexchar = xmalloc (6 * sizeof (char)); 696 sprintf (hexchar, "_00%x", (unsigned char) oldname[i]); 697 strcat (filename, hexchar); 698 free (hexchar); 699 } 700 701 /* Check if we are nearing boundaries. */ 702 if (strlen (filename) >= PATH_MAX - 20) 703 break; 704 } 705 706 free (oldname); 707} 708 709/* As we can't look-up a (forward-referenced) nodes' html filename 710 from the tentry, we take the easy way out. We assume that 711 nodenames are unique, and generate the html filename from the 712 nodename, that's always known. */ 713static char * 714nodename_to_filename_1 (char *nodename, int href) 715{ 716 char *p; 717 char *filename; 718 char dirname[PATH_MAX]; 719 720 if (strcasecmp (nodename, "Top") == 0) 721 { 722 /* We want to convert references to the Top node into 723 "index.html#Top". */ 724 if (href) 725 filename = xstrdup ("index.html"); /* "#Top" is added by our callers */ 726 else 727 filename = xstrdup ("Top"); 728 } 729 else if (strcasecmp (nodename, "(dir)") == 0) 730 /* We want to convert references to the (dir) node into 731 "../index.html". */ 732 filename = xstrdup ("../index.html"); 733 else 734 { 735 filename = xmalloc (PATH_MAX); 736 dirname[0] = '\0'; 737 *filename = '\0'; 738 739 /* Check for external reference: ``(info-document)node-name'' 740 Assume this node lives at: ``../info-document/node-name.html'' 741 742 We need to handle the special case (sigh): ``(info-document)'', 743 ie, an external top-node, which should translate to: 744 ``../info-document/info-document.html'' */ 745 746 p = nodename; 747 if (*nodename == '(') 748 { 749 int length; 750 751 p = strchr (nodename, ')'); 752 if (p == NULL) 753 { 754 line_error (_("[unexpected] invalid node name: `%s'"), nodename); 755 xexit (1); 756 } 757 758 length = p - nodename - 1; 759 if (length > 5 && 760 FILENAME_CMPN (p - 5, ".info", 5) == 0) 761 length -= 5; 762 /* This is for DOS, and also for Windows and GNU/Linux 763 systems that might have Info files copied from a DOS 8+3 764 filesystem. */ 765 if (length > 4 && 766 FILENAME_CMPN (p - 4, ".inf", 4) == 0) 767 length -= 4; 768 strcpy (filename, "../"); 769 strncpy (dirname, nodename + 1, length); 770 *(dirname + length) = '\0'; 771 fix_filename (dirname); 772 strcat (filename, dirname); 773 strcat (filename, "/"); 774 p++; 775 } 776 777 /* In the case of just (info-document), there will be nothing 778 remaining, and we will refer to ../info-document/, which will 779 work fine. */ 780 strcat (filename, p); 781 if (*p) 782 { 783 /* Hmm */ 784 fix_filename (filename + strlen (filename) - strlen (p)); 785 strcat (filename, ".html"); 786 } 787 } 788 789 /* Produce a file name suitable for the underlying filesystem. */ 790 normalize_filename (filename); 791 792#if 0 793 /* We add ``#Nodified-filename'' anchor to external references to be 794 prepared for non-split HTML support. Maybe drop this. */ 795 if (href && *dirname) 796 { 797 strcat (filename, "#"); 798 strcat (filename, p); 799 /* Hmm, again */ 800 fix_filename (filename + strlen (filename) - strlen (p)); 801 } 802#endif 803 804 return filename; 805} 806 807/* If necessary, ie, if current filename != filename of node, output 808 the node name. */ 809void 810add_nodename_to_filename (char *nodename, int href) 811{ 812 /* for now, don't check: always output filename */ 813 char *filename = nodename_to_filename_1 (nodename, href); 814 add_word (filename); 815 free (filename); 816} 817 818char * 819nodename_to_filename (char *nodename) 820{ 821 /* The callers of nodename_to_filename use the result to produce 822 <a href=, so call nodename_to_filename_1 with last arg non-zero. */ 823 return nodename_to_filename_1 (nodename, 1); 824} 825