node.c revision 56160
1/* node.c -- nodes for Texinfo. 2 $Id: node.c,v 1.23 1999/09/20 12:31:21 karl Exp $ 3 4 Copyright (C) 1998, 99 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 "files.h" 23#include "footnote.h" 24#include "macro.h" 25#include "makeinfo.h" 26#include "node.h" 27#include "sectioning.h" 28#include "insertion.h" 29 30 31/* See comments in node.h. */ 32NODE_REF *node_references = NULL; 33NODE_REF *node_node_references = NULL; 34TAG_ENTRY *tag_table = NULL; 35int node_number = -1; 36int current_section = 0; 37int outstanding_node = 0; 38 39/* Adding nodes, and making tags. */ 40 41/* Start a new tag table. */ 42void 43init_tag_table () 44{ 45 while (tag_table) 46 { 47 TAG_ENTRY *temp = tag_table; 48 free (temp->node); 49 free (temp->prev); 50 free (temp->next); 51 free (temp->up); 52 tag_table = tag_table->next_ent; 53 free (temp); 54 } 55} 56 57/* Write out the contents of the existing tag table. 58 INDIRECT_P says how to format the output (it depends on whether the 59 table is direct or indirect). */ 60static void 61write_tag_table_internal (indirect_p) 62 int indirect_p; 63{ 64 TAG_ENTRY *node; 65 int old_indent = no_indent; 66 67 no_indent = 1; 68 filling_enabled = 0; 69 must_start_paragraph = 0; 70 close_paragraph (); 71 72 if (!indirect_p) 73 { 74 no_indent = 1; 75 insert ('\n'); 76 } 77 78 add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : ""); 79 80 /* Do not collapse -- to -, etc., in node names. */ 81 in_fixed_width_font++; 82 83 for (node = tag_table; node; node = node->next_ent) 84 { 85 if (node->flags & TAG_FLAG_ANCHOR) 86 { /* This reference is to an anchor. */ 87 execute_string ("Ref: %s", node->node); 88 } 89 else 90 { /* This reference is to a node. */ 91 execute_string ("Node: %s", node->node); 92 } 93 add_word_args ("\177%d\n", node->position); 94 } 95 96 add_word ("\037\nEnd Tag Table\n"); 97 98 /* Do not collapse -- to -, etc., in node names. */ 99 in_fixed_width_font--; 100 101 flush_output (); 102 no_indent = old_indent; 103} 104 105void 106write_tag_table () 107{ 108 write_tag_table_internal (0); /* Not indirect. */ 109} 110 111void 112write_tag_table_indirect () 113{ 114 write_tag_table_internal (1); 115} 116 117/* Convert "top" and friends into "Top". */ 118static void 119normalize_node_name (string) 120 char *string; 121{ 122 if (strcasecmp (string, "Top") == 0) 123 strcpy (string, "Top"); 124} 125 126char * 127get_node_token (expand) 128 int expand; 129{ 130 char *string; 131 132 get_until_in_line (expand, ",", &string); 133 134 if (curchar () == ',') 135 input_text_offset++; 136 137 fix_whitespace (string); 138 139 /* Force all versions of "top" to be "Top". */ 140 normalize_node_name (string); 141 142 return string; 143} 144 145/* Expand any macros and other directives in a node name, and 146 return the expanded name as an malloc'ed string. */ 147char * 148expand_node_name (node) 149 char *node; 150{ 151 char *result = node; 152 153 if (node) 154 { 155 /* Don't expand --, `` etc., in case somebody will want 156 to print the result. */ 157 in_fixed_width_font++; 158 result = expansion (node, 0); 159 in_fixed_width_font--; 160 fix_whitespace (result); 161 normalize_node_name (result); 162 } 163 return result; 164} 165 166/* Look up NAME in the tag table, and return the associated 167 tag_entry. If the node is not in the table return NULL. */ 168TAG_ENTRY * 169find_node (name) 170 char *name; 171{ 172 TAG_ENTRY *tag = tag_table; 173 char *expanded_name; 174 char n1 = name[0]; 175 176 while (tag) 177 { 178 if (tag->node[0] == n1 && strcmp (tag->node, name) == 0) 179 return tag; 180 tag = tag->next_ent; 181 } 182 183 if (!expensive_validation) 184 return NULL; 185 186 /* Try harder. Maybe TAG_TABLE has the expanded NAME, or maybe NAME 187 is expanded while TAG_TABLE has its unexpanded form. This may 188 slow down the search, but if they want this feature, let them 189 pay! If they want it fast, they should write every node name 190 consistently (either always expanded or always unexpaned). */ 191 expanded_name = expand_node_name (name); 192 for (tag = tag_table; tag; tag = tag->next_ent) 193 { 194 if (STREQ (tag->node, expanded_name)) 195 break; 196 /* If the tag name doesn't have the command prefix, there's no 197 chance it could expand into anything but itself. */ 198 if (strchr (tag->node, COMMAND_PREFIX)) 199 { 200 char *expanded_node = expand_node_name (tag->node); 201 202 if (STREQ (expanded_node, expanded_name)) 203 { 204 free (expanded_node); 205 break; 206 } 207 free (expanded_node); 208 } 209 } 210 free (expanded_name); 211 return tag; 212} 213 214/* Similarly for next etc. references in a @node command, where we 215 don't care about most of the entries. */ 216static void 217remember_node_node_reference (node) 218 char *node; 219{ 220 NODE_REF *temp = xmalloc (sizeof (NODE_REF)); 221 int number; 222 223 if (!node) return; 224 temp->next = node_node_references; 225 temp->node = xstrdup (node); 226 temp->type = followed_reference; 227 number = number_of_node (node); 228 if (number) 229 temp->number = number; /* Already assigned. */ 230 else 231 { 232 node_number++; 233 temp->number = node_number; 234 } 235 node_node_references = temp; 236} 237 238/* Remember NODE and associates. */ 239void 240remember_node (node, prev, next, up, position, line_no, flags) 241 char *node, *prev, *next, *up; 242 int position, line_no, flags; 243{ 244 /* Check for existence of this tag already. */ 245 if (validating) 246 { 247 TAG_ENTRY *tag = find_node (node); 248 if (tag) 249 { 250 line_error (_("Node `%s' previously defined at line %d"), 251 node, tag->line_no); 252 return; 253 } 254 } 255 256 if (!(flags & TAG_FLAG_ANCHOR)) 257 { 258 /* Make this the current node. */ 259 current_node = node; 260 } 261 262 /* Add it to the list. */ 263 { 264 int number = number_of_node (node); 265 266 TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY)); 267 new->node = node; 268 new->prev = prev; 269 new->next = next; 270 new->up = up; 271 new->position = position; 272 new->line_no = line_no; 273 new->filename = node_filename; 274 new->touched = 0; 275 new->flags = flags; 276 if (number) 277 new->number = number; /* Already assigned. */ 278 else 279 { 280 node_number++; 281 new->number = node_number; 282 } 283 new->next_ent = tag_table; 284 tag_table = new; 285 } 286 287 if (html) 288 { /* Note the references to the next etc. nodes too. */ 289 remember_node_node_reference (next); 290 remember_node_node_reference (prev); 291 remember_node_node_reference (up); 292 } 293} 294 295/* Remember this node name for later validation use. This is used to 296 remember menu references while reading the input file. After the 297 output file has been written, if validation is on, then we use the 298 contents of `node_references' as a list of nodes to validate. */ 299void 300remember_node_reference (node, line, type) 301 char *node; 302 int line; 303 enum reftype type; 304{ 305 NODE_REF *temp = xmalloc (sizeof (NODE_REF)); 306 int number = number_of_node (node); 307 308 temp->next = node_references; 309 temp->node = xstrdup (node); 310 temp->line_no = line; 311 temp->section = current_section; 312 temp->type = type; 313 temp->containing_node = xstrdup (current_node ? current_node : ""); 314 temp->filename = node_filename; 315 if (number) 316 temp->number = number; /* Already assigned. */ 317 else 318 { 319 node_number++; 320 temp->number = node_number; 321 } 322 323 node_references = temp; 324} 325 326static void 327isolate_nodename (nodename) 328 char *nodename; 329{ 330 int i, c; 331 int paren_seen, paren; 332 333 if (!nodename) 334 return; 335 336 canon_white (nodename); 337 paren_seen = paren = i = 0; 338 339 if (*nodename == '.' || !*nodename) 340 { 341 *nodename = 0; 342 return; 343 } 344 345 if (*nodename == '(') 346 { 347 paren++; 348 paren_seen++; 349 i++; 350 } 351 352 for (; (c = nodename[i]); i++) 353 { 354 if (paren) 355 { 356 if (c == '(') 357 paren++; 358 else if (c == ')') 359 paren--; 360 361 continue; 362 } 363 364 /* If the character following the close paren is a space, then this 365 node has no more characters associated with it. */ 366 if (c == '\t' || 367 c == '\n' || 368 c == ',' || 369 ((paren_seen && nodename[i - 1] == ')') && 370 (c == ' ' || c == '.')) || 371 (c == '.' && 372 ((!nodename[i + 1] || 373 (cr_or_whitespace (nodename[i + 1])) || 374 (nodename[i + 1] == ')'))))) 375 break; 376 } 377 nodename[i] = 0; 378} 379 380/* This function gets called at the start of every line while inside a 381 menu. It checks to see if the line starts with "* ", and if so and 382 REMEMBER_REF is nonzero, remembers the node reference as type 383 REF_TYPE that this menu refers to. input_text_offset is at the \n 384 just before the menu line. If REMEMBER_REF is zero, REF_TYPE is unused. */ 385#define MENU_STARTER "* " 386char * 387glean_node_from_menu (remember_ref, ref_type) 388 int remember_ref; 389 enum reftype ref_type; 390{ 391 int i, orig_offset = input_text_offset; 392 char *nodename; 393 char *line, *expanded_line; 394 char *old_input = input_text; 395 size_t old_size = input_text_length; 396 397 if (strncmp (&input_text[input_text_offset + 1], 398 MENU_STARTER, 399 strlen (MENU_STARTER)) != 0) 400 return NULL; 401 else 402 input_text_offset += strlen (MENU_STARTER) + 1; 403 404 /* The menu entry might include macro calls, so we need to expand them. */ 405 get_until ("\n", &line); 406 only_macro_expansion++; /* only expand macros in menu entries */ 407 expanded_line = expansion (line, 0); 408 only_macro_expansion--; 409 free (line); 410 input_text = expanded_line; 411 input_text_offset = 0; 412 input_text_length = strlen (expanded_line); 413 414 get_until_in_line (0, ":", &nodename); 415 if (curchar () == ':') 416 input_text_offset++; 417 418 if (curchar () != ':') 419 { 420 free (nodename); 421 get_until_in_line (0, "\n", &nodename); 422 isolate_nodename (nodename); 423 } 424 425 input_text = old_input; 426 input_text_offset = orig_offset; 427 input_text_length = old_size; 428 free (expanded_line); 429 fix_whitespace (nodename); 430 normalize_node_name (nodename); 431 i = strlen (nodename); 432 if (i && nodename[i - 1] == ':') 433 nodename[i - 1] = 0; 434 435 if (remember_ref) 436 remember_node_reference (nodename, line_number, ref_type); 437 438 return nodename; 439} 440 441/* Set the name of the current output file. */ 442void 443set_current_output_filename (fname) 444 const char *fname; 445{ 446 if (current_output_filename) 447 free (current_output_filename); 448 current_output_filename = xstrdup (fname); 449} 450 451/* The order is: nodename, nextnode, prevnode, upnode. 452 If all of the NEXT, PREV, and UP fields are empty, they are defaulted. 453 You must follow a node command which has those fields defaulted 454 with a sectioning command (e.g. @chapter) giving the "level" of that node. 455 It is an error not to do so. 456 The defaults come from the menu in this node's parent. */ 457void 458cm_node () 459{ 460 char *node, *prev, *next, *up; 461 int new_node_pos, defaulting, this_section; 462 int no_warn = 0; 463 464 if (strcmp (command, "nwnode") == 0) 465 no_warn = TAG_FLAG_NO_WARN; 466 467 /* Get rid of unmatched brace arguments from previous commands. */ 468 discard_braces (); 469 470 /* There also might be insertions left lying around that haven't been 471 ended yet. Do that also. */ 472 discard_insertions (1); 473 474 if (!html && !already_outputting_pending_notes) 475 { 476 close_paragraph (); 477 output_pending_notes (); 478 } 479 480 if (html && splitting && top_node_seen) 481 { 482 /* End the current split output file. */ 483 close_paragraph (); 484 output_pending_notes (); 485 start_paragraph (); 486 /* Fixme: html: need a navigation bar here. */ 487 add_word ("</body></html>\n"); 488 close_paragraph (); 489 fclose (output_stream); 490 output_stream = NULL; 491 } 492 493 filling_enabled = indented_fill = 0; 494 new_node_pos = output_position; 495 if (!html || (html && splitting)) 496 current_footnote_number = 1; 497 498 if (macro_expansion_output_stream && !executing_string) 499 append_to_expansion_output (input_text_offset + 1); 500 501 /* Do not collapse -- to -, etc., in node names. */ 502 in_fixed_width_font++; 503 504 /* While expanding the @node line, leave any non-macros 505 intact, so that the macro-expanded output includes them. */ 506 only_macro_expansion++; 507 node = get_node_token (1); 508 only_macro_expansion--; 509 next = get_node_token (0); 510 prev = get_node_token (0); 511 up = get_node_token (0); 512 513 if (verbose_mode) 514 printf (_("Formatting node %s...\n"), node); 515 516 if (macro_expansion_output_stream && !executing_string) 517 remember_itext (input_text, input_text_offset); 518 519 no_indent = 1; 520 if (!no_headers && !html) 521 { 522 add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename); 523 524 if (macro_expansion_output_stream && !executing_string) 525 me_execute_string (node); 526 else 527 execute_string ("%s", node); 528 filling_enabled = indented_fill = 0; 529 } 530 531 /* Check for defaulting of this node's next, prev, and up fields. */ 532 defaulting = (*next == 0 && *prev == 0 && *up == 0); 533 534 this_section = what_section (input_text + input_text_offset); 535 536 /* If we are defaulting, then look at the immediately following 537 sectioning command (error if none) to determine the node's 538 level. Find the node that contains the menu mentioning this node 539 that is one level up (error if not found). That node is the "Up" 540 of this node. Default the "Next" and "Prev" from the menu. */ 541 if (defaulting) 542 { 543 NODE_REF *last_ref = NULL; 544 NODE_REF *ref = node_references; 545 546 if (this_section < 0 && !STREQ (node, "Top")) 547 { 548 char *polite_section_name = "top"; 549 int i; 550 551 for (i = 0; section_alist[i].name; i++) 552 if (section_alist[i].level == current_section + 1) 553 { 554 polite_section_name = section_alist[i].name; 555 break; 556 } 557 558 line_error 559 (_("Node `%s' requires a sectioning command (e.g. %c%s)"), 560 node, COMMAND_PREFIX, polite_section_name); 561 } 562 else 563 { 564 if (strcmp (node, "Top") == 0) 565 { 566 /* Default the NEXT pointer to be the first menu item in 567 this node, if there is a menu in this node. We have to 568 try very hard to find the menu, as it may be obscured 569 by execution_strings which are on the filestack. For 570 every member of the filestack which has a FILENAME 571 member which is identical to the current INPUT_FILENAME, 572 search forward from that offset. */ 573 int saved_input_text_offset = input_text_offset; 574 int saved_input_text_length = input_text_length; 575 char *saved_input_text = input_text; 576 FSTACK *next_file = filestack; 577 578 int orig_offset, orig_size; 579 580 /* No matter what, make this file point back at `(dir)'. */ 581 free (up); 582 up = xstrdup ("(dir)"); /* html fixxme */ 583 584 while (1) 585 { 586 orig_offset = input_text_offset; 587 orig_size = 588 search_forward (node_search_string, orig_offset); 589 590 if (orig_size < 0) 591 orig_size = input_text_length; 592 593 input_text_offset = search_forward ("\n@menu", orig_offset); 594 if (input_text_offset > -1 595 && cr_or_whitespace (input_text[input_text_offset + 6])) 596 { 597 char *nodename_from_menu = NULL; 598 599 input_text_offset = 600 search_forward ("\n* ", input_text_offset); 601 602 if (input_text_offset != -1) 603 nodename_from_menu = glean_node_from_menu (0, 0); 604 605 if (nodename_from_menu) 606 { 607 free (next); 608 next = nodename_from_menu; 609 break; 610 } 611 } 612 613 /* We got here, so it hasn't been found yet. Try 614 the next file on the filestack if there is one. */ 615 if (next_file 616 && FILENAME_CMP (next_file->filename, input_filename) 617 == 0) 618 { 619 input_text = next_file->text; 620 input_text_offset = next_file->offset; 621 input_text_length = next_file->size; 622 next_file = next_file->next; 623 } 624 else 625 { /* No more input files to check. */ 626 break; 627 } 628 } 629 630 input_text = saved_input_text; 631 input_text_offset = saved_input_text_offset; 632 input_text_length = saved_input_text_length; 633 } 634 } 635 636 /* Fix the level of the menu references in the Top node, iff it 637 was declared with @top, and no subsequent reference was found. */ 638 if (top_node_seen && !non_top_node_seen) 639 { 640 /* Then this is the first non-@top node seen. */ 641 int level; 642 643 level = set_top_section_level (this_section - 1); 644 non_top_node_seen = 1; 645 646 while (ref) 647 { 648 if (ref->section == level) 649 ref->section = this_section - 1; 650 ref = ref->next; 651 } 652 653 ref = node_references; 654 } 655 656 while (ref) 657 { 658 if (ref->section == (this_section - 1) 659 && ref->type == menu_reference 660 && strcmp (ref->node, node) == 0) 661 { 662 char *containing_node = ref->containing_node; 663 664 free (up); 665 up = xstrdup (containing_node); 666 667 if (last_ref 668 && last_ref->type == menu_reference 669 && strcmp (last_ref->containing_node, containing_node) == 0) 670 { 671 free (next); 672 next = xstrdup (last_ref->node); 673 } 674 675 while (ref->section == this_section - 1 676 && ref->next 677 && ref->next->type != menu_reference) 678 ref = ref->next; 679 680 if (ref->next && ref->type == menu_reference 681 && strcmp (ref->next->containing_node, containing_node) == 0) 682 { 683 free (prev); 684 prev = xstrdup (ref->next->node); 685 } 686 else if (!ref->next 687 && strcasecmp (ref->containing_node, "Top") == 0) 688 { 689 free (prev); 690 prev = xstrdup (ref->containing_node); 691 } 692 break; 693 } 694 last_ref = ref; 695 ref = ref->next; 696 } 697 } 698 699 /* Insert the correct args if we are expanding macros, and the node's 700 pointers weren't defaulted. */ 701 if (macro_expansion_output_stream && !executing_string && !defaulting) 702 { 703 char *temp; 704 int op_orig = output_paragraph_offset; 705 int meta_pos_orig = meta_char_pos; 706 int extra = html ? strlen (node) : 0; 707 708 temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up)); 709 sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up); 710 me_execute_string (temp); 711 free (temp); 712 713 output_paragraph_offset = op_orig; 714 meta_char_pos = meta_pos_orig; 715 } 716 717 if (!*node) 718 { 719 line_error (_("No node name specified for `%c%s' command"), 720 COMMAND_PREFIX, command); 721 free (node); 722 free (next); next = NULL; 723 free (prev); prev= NULL; 724 free (up); up = NULL; 725 node_number++; /* else it doesn't get bumped */ 726 } 727 else 728 { 729 if (!*next) { free (next); next = NULL; } 730 if (!*prev) { free (prev); prev = NULL; } 731 if (!*up) { free (up); up = NULL; } 732 remember_node (node, prev, next, up, new_node_pos, line_number, no_warn); 733 outstanding_node = 1; 734 } 735 736 if (html) 737 { 738 char *tem; 739 740 if (splitting) 741 { /* this code not operational, we do not currently split html */ 742 char filename[20]; 743 744 sprintf (filename, "node%d.html", number_of_node (node)); 745 output_stream = fopen (filename, "w"); 746 if (output_stream == NULL) 747 { 748 fs_error (filename); 749 xexit (1); 750 } 751 set_current_output_filename (filename); 752 /* FIXME: when this code is operational, we will need to 753 expand node, next, prev, and up before output. */ 754 add_word_args ("<html><head><title>%s</title>", node); 755 if (next) add_link (next, "rel=next"); 756 if (prev) add_link (prev, "rel=previous"); 757 if (up) add_link (up, "rel=up"); 758 add_word ("</head>\n<body>\n"); 759 } 760 761 if (!splitting && no_headers) 762 { /* cross refs need a name="#anchor" even if we're not writing headers*/ 763 add_word ("<a name=\""); 764 tem = expand_node_name (node); 765 add_anchor_name (tem, 0); 766 add_word ("\"></a>"); 767 free (tem); 768 } 769 770 if (splitting || !no_headers) 771 { /* Navigation bar. The <p> avoids the links area running 772 on with old Lynxen. */ 773 add_word_args ("<p>%s\n", splitting ? "" : "<hr>"); 774 add_word_args ("%s<a name=\"", _("Node:")); 775 tem = expand_node_name (node); 776 add_anchor_name (tem, 0); 777 add_word_args ("\">%s</a>", tem); 778 free (tem); 779 780 if (next) 781 { 782 add_word (",\n"); 783 add_word (_("Next:")); 784 add_word ("<a rel=next href=\""); 785 tem = expansion (next, 0); 786 add_anchor_name (tem, 1); 787 add_word_args ("\">%s</a>", tem); 788 free (tem); 789 } 790 if (prev) 791 { 792 add_word (",\n"); 793 add_word (_("Previous:")); 794 add_word ("<a rel=previous href=\""); 795 tem = expansion (prev, 0); 796 add_anchor_name (tem, 1); 797 add_word_args ("\">%s</a>", tem); 798 free (tem); 799 } 800 if (up) 801 { 802 add_word (",\n"); 803 add_word (_("Up:")); 804 add_word ("<a rel=up href=\""); 805 tem = expansion (up, 0); 806 add_anchor_name (tem, 1); 807 add_word_args ("\">%s</a>", tem); 808 free (tem); 809 } 810 /* html fixxme: we want a `top' or `contents' link here. */ 811 812 add_word_args ("\n%s<br>\n", splitting ? "<hr>" : ""); 813 } 814 } 815 816 else if (!no_headers) 817 { 818 if (macro_expansion_output_stream) 819 me_inhibit_expansion++; 820 821 /* These strings are not translatable. */ 822 if (next) 823 { 824 execute_string (", Next: %s", next); 825 filling_enabled = indented_fill = 0; 826 } 827 if (prev) 828 { 829 execute_string (", Prev: %s", prev); 830 filling_enabled = indented_fill = 0; 831 } 832 if (up) 833 { 834 execute_string (", Up: %s", up); 835 filling_enabled = indented_fill = 0; 836 } 837 if (macro_expansion_output_stream) 838 me_inhibit_expansion--; 839 } 840 841 close_paragraph (); 842 no_indent = 0; 843 844 /* Change the section only if there was a sectioning command. */ 845 if (this_section >= 0) 846 current_section = this_section; 847 848 if (current_node && STREQ (current_node, "Top")) 849 top_node_seen = 1; 850 851 filling_enabled = 1; 852 in_fixed_width_font--; 853} 854 855/* Cross-reference target at an arbitrary spot. */ 856void 857cm_anchor (arg) 858 int arg; 859{ 860 char *anchor; 861 862 if (arg == END) 863 return; 864 865 /* Parse the anchor text. */ 866 anchor = get_xref_token (1); 867 868 /* In HTML mode, need to actually produce some output. */ 869 if (html) 870 { 871 /* If this anchor is at the beginning of a new paragraph, make 872 sure a new paragraph is indeed started. */ 873 if (!paragraph_is_open) 874 { 875 start_paragraph (); 876 if (!in_fixed_width_font || in_menu || in_detailmenu) 877 { 878 insert_string ("<p>"); 879 in_paragraph = 1; 880 } 881 } 882 add_word ("<a name=\""); 883 add_anchor_name (anchor, 0); 884 add_word ("\"></a>"); 885 } 886 887 /* Save it in the tag table. */ 888 remember_node (anchor, NULL, NULL, NULL, output_position + output_column, 889 line_number, TAG_FLAG_ANCHOR); 890} 891 892/* Find NODE in REF_LIST. */ 893static NODE_REF * 894find_node_reference (node, ref_list) 895 char *node; 896 NODE_REF *ref_list; 897{ 898 NODE_REF *orig_ref_list = ref_list; 899 char *expanded_node; 900 901 while (ref_list) 902 { 903 if (strcmp (node, ref_list->node) == 0) 904 break; 905 ref_list = ref_list->next; 906 } 907 908 if (ref_list || !expensive_validation) 909 return ref_list; 910 911 /* Maybe NODE is not expanded yet. This may be SLOW. */ 912 expanded_node = expand_node_name (node); 913 for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next) 914 { 915 if (STREQ (expanded_node, ref_list->node)) 916 break; 917 if (strchr (ref_list->node, COMMAND_PREFIX)) 918 { 919 char *expanded_ref = expand_node_name (ref_list->node); 920 921 if (STREQ (expanded_node, expanded_ref)) 922 { 923 free (expanded_ref); 924 break; 925 } 926 free (expanded_ref); 927 } 928 } 929 free (expanded_node); 930 return ref_list; 931} 932 933void 934free_node_references () 935{ 936 NODE_REF *list, *temp; 937 938 list = node_references; 939 940 while (list) 941 { 942 temp = list; 943 free (list->node); 944 free (list->containing_node); 945 list = list->next; 946 free (temp); 947 } 948 node_references = NULL; 949} 950 951void 952free_node_node_references () 953{ 954 NODE_REF *list, *temp; 955 956 list = node_references; 957 958 while (list) 959 { 960 temp = list; 961 free (list->node); 962 list = list->next; 963 free (temp); 964 } 965 node_node_references = NULL; 966} 967 968/* Return the number assigned to a named node in either the tag_table 969 or node_references list or zero if no number has been assigned. */ 970int 971number_of_node (node) 972 char *node; 973{ 974 NODE_REF *temp_ref; 975 TAG_ENTRY *temp_node = find_node (node); 976 977 if (temp_node) 978 return temp_node->number; 979 else if ((temp_ref = find_node_reference (node, node_references))) 980 return temp_ref->number; 981 else if ((temp_ref = find_node_reference (node, node_node_references))) 982 return temp_ref->number; 983 else 984 return 0; 985} 986 987/* validation */ 988 989/* Return 1 if TAG (at LINE) correctly validated, or 0 if not. 990 LABEL is the (translated) description of the type of reference -- 991 Menu, Cross, Next, etc. */ 992 993static int 994validate (tag, line, label) 995 char *tag; 996 int line; 997 char *label; 998{ 999 TAG_ENTRY *result; 1000 1001 /* If there isn't a tag to verify, or if the tag is in another file, 1002 then it must be okay. */ 1003 if (!tag || !*tag || *tag == '(') 1004 return 1; 1005 1006 /* Otherwise, the tag must exist. */ 1007 result = find_node (tag); 1008 1009 if (!result) 1010 { 1011 line_number = line; 1012 line_error (_("%s reference to nonexistent node `%s'"), label, tag); 1013 return 0; 1014 } 1015 result->touched++; 1016 return 1; 1017} 1018 1019/* The strings here are followed in the message by `reference to...' in 1020 the `validate' routine. They are only used in messages, thus are 1021 translated. */ 1022static char * 1023reftype_type_string (type) 1024 enum reftype type; 1025{ 1026 switch (type) 1027 { 1028 case menu_reference: 1029 return _("Menu"); 1030 case followed_reference: 1031 return _("Cross"); 1032 default: 1033 return "Internal-bad-reference-type"; 1034 } 1035} 1036 1037static void 1038validate_other_references (ref_list) 1039 NODE_REF *ref_list; 1040{ 1041 char *old_input_filename = input_filename; 1042 1043 while (ref_list) 1044 { 1045 input_filename = ref_list->filename; 1046 validate (ref_list->node, ref_list->line_no, 1047 reftype_type_string (ref_list->type)); 1048 ref_list = ref_list->next; 1049 } 1050 input_filename = old_input_filename; 1051} 1052 1053/* Validation of an info file. 1054 Scan through the list of tag entries touching the Prev, Next, and Up 1055 elements of each. It is an error not to be able to touch one of them, 1056 except in the case of external node references, such as "(DIR)". 1057 1058 If the Prev is different from the Up, 1059 then the Prev node must have a Next pointing at this node. 1060 1061 Every node except Top must have an Up. 1062 The Up node must contain some sort of reference, other than a Next, 1063 to this node. 1064 1065 If the Next is different from the Next of the Up, 1066 then the Next node must have a Prev pointing at this node. */ 1067void 1068validate_file (tag_table) 1069 TAG_ENTRY *tag_table; 1070{ 1071 char *old_input_filename = input_filename; 1072 TAG_ENTRY *tags = tag_table; 1073 1074 while (tags) 1075 { 1076 TAG_ENTRY *temp_tag; 1077 char *tem1, *tem2; 1078 1079 input_filename = tags->filename; 1080 line_number = tags->line_no; 1081 1082 /* If this is a "no warn" node, don't validate it in any way. */ 1083 if (tags->flags & TAG_FLAG_NO_WARN) 1084 { 1085 tags = tags->next_ent; 1086 continue; 1087 } 1088 1089 /* If this node has a Next, then make sure that the Next exists. */ 1090 if (tags->next) 1091 { 1092 validate (tags->next, tags->line_no, _("Next")); 1093 1094 /* If the Next node exists, and there is no Up, then make sure 1095 that the Prev of the Next points back. But do nothing if 1096 we aren't supposed to issue warnings about this node. */ 1097 temp_tag = find_node (tags->next); 1098 if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN)) 1099 { 1100 char *prev = temp_tag->prev; 1101 int you_lose = !prev || !STREQ (prev, tags->node); 1102 1103 if (you_lose && expensive_validation) 1104 { 1105 tem1 = expand_node_name (prev); 1106 tem2 = expand_node_name (tags->node); 1107 1108 if (STREQ (tem1, tem2)) 1109 you_lose = 0; 1110 free (tem1); 1111 free (tem2); 1112 } 1113 if (you_lose) 1114 { 1115 line_error (_("Next field of node `%s' not pointed to"), 1116 tags->node); 1117 line_number = temp_tag->line_no; 1118 input_filename = temp_tag->filename; 1119 line_error (_("This node (%s) has the bad Prev"), 1120 temp_tag->node); 1121 input_filename = tags->filename; 1122 line_number = tags->line_no; 1123 temp_tag->flags |= TAG_FLAG_PREV_ERROR; 1124 } 1125 } 1126 } 1127 1128 /* Validate the Prev field if there is one, and we haven't already 1129 complained about it in some way. You don't have to have a Prev 1130 field at this stage. */ 1131 if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev) 1132 { 1133 int valid_p = validate (tags->prev, tags->line_no, _("Prev")); 1134 1135 if (!valid_p) 1136 tags->flags |= TAG_FLAG_PREV_ERROR; 1137 else 1138 { /* If the Prev field is not the same as the Up field, 1139 then the node pointed to by the Prev field must have 1140 a Next field which points to this node. */ 1141 int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up); 1142 1143 if (!prev_equals_up && expensive_validation) 1144 { 1145 tem1 = expand_node_name (tags->prev); 1146 tem2 = expand_node_name (tags->up); 1147 prev_equals_up = STREQ (tem1, tem2); 1148 free (tem1); 1149 free (tem2); 1150 } 1151 if (!prev_equals_up) 1152 { 1153 temp_tag = find_node (tags->prev); 1154 1155 /* If we aren't supposed to issue warnings about the 1156 target node, do nothing. */ 1157 if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN)) 1158 /* Do nothing. */ ; 1159 else 1160 { 1161 int you_lose = !temp_tag->next 1162 || !STREQ (temp_tag->next, tags->node); 1163 1164 if (temp_tag->next && you_lose && expensive_validation) 1165 { 1166 tem1 = expand_node_name (temp_tag->next); 1167 tem2 = expand_node_name (tags->node); 1168 if (STREQ (tem1, tem2)) 1169 you_lose = 0; 1170 free (tem1); 1171 free (tem2); 1172 } 1173 if (you_lose) 1174 { 1175 line_error 1176 (_("Prev field of node `%s' not pointed to"), 1177 tags->node); 1178 line_number = temp_tag->line_no; 1179 input_filename = temp_tag->filename; 1180 line_error (_("This node (%s) has the bad Next"), 1181 temp_tag->node); 1182 input_filename = tags->filename; 1183 line_number = tags->line_no; 1184 temp_tag->flags |= TAG_FLAG_NEXT_ERROR; 1185 } 1186 } 1187 } 1188 } 1189 } 1190 1191 if (!tags->up 1192 && !(tags->flags & TAG_FLAG_ANCHOR) 1193 && strcasecmp (tags->node, "Top") != 0) 1194 line_error (_("`%s' has no Up field"), tags->node); 1195 else if (tags->up) 1196 { 1197 int valid_p = validate (tags->up, tags->line_no, _("Up")); 1198 1199 /* If node X has Up: Y, then warn if Y fails to have a menu item 1200 or note pointing at X, if Y isn't of the form "(Y)". */ 1201 if (valid_p && *tags->up != '(') 1202 { 1203 NODE_REF *nref; 1204 NODE_REF *tref = NULL; 1205 NODE_REF *list = node_references; 1206 1207 for (;;) 1208 { 1209 nref = find_node_reference (tags->node, list); 1210 if (!nref) 1211 break; 1212 1213 if (strcmp (nref->containing_node, tags->up) == 0) 1214 { 1215 if (nref->type != menu_reference) 1216 { 1217 tref = nref; 1218 list = nref->next; 1219 } 1220 else 1221 break; 1222 } 1223 list = nref->next; 1224 } 1225 1226 if (!nref) 1227 { 1228 if (!tref && expensive_validation) 1229 { 1230 /* Sigh... This might be AWFULLY slow, but if 1231 they want this feature, they'll have to pay! 1232 We do all the loop again expanding each 1233 containing_node reference as we go. */ 1234 char *tags_up = expand_node_name (tags->up); 1235 char *tem; 1236 1237 list = node_references; 1238 1239 for (;;) 1240 { 1241 nref = find_node_reference (tags->node, list); 1242 if (!nref) 1243 break; 1244 tem = expand_node_name (nref->containing_node); 1245 if (STREQ (tem, tags_up)) 1246 { 1247 if (nref->type != menu_reference) 1248 tref = nref; 1249 else 1250 { 1251 free (tem); 1252 break; 1253 } 1254 } 1255 free (tem); 1256 list = nref->next; 1257 } 1258 } 1259 if (!nref && !tref) 1260 { 1261 temp_tag = find_node (tags->up); 1262 line_number = temp_tag->line_no; 1263 input_filename = temp_tag->filename; 1264 line_error ( 1265 _("Node `%s' lacks menu item for `%s' despite being its Up target"), 1266 tags->up, tags->node); 1267 line_number = tags->line_no; 1268 input_filename = tags->filename; 1269 } 1270 } 1271 } 1272 } 1273 tags = tags->next_ent; 1274 } 1275 1276 validate_other_references (node_references); 1277 /* We have told the user about the references which didn't exist. 1278 Now tell him about the nodes which aren't referenced. */ 1279 1280 for (tags = tag_table; tags; tags = tags->next_ent) 1281 { 1282 /* If this node is a "no warn" node, do nothing. */ 1283 if (tags->flags & TAG_FLAG_NO_WARN) 1284 { 1285 tags = tags->next_ent; 1286 continue; 1287 } 1288 1289 /* Special hack. If the node in question appears to have 1290 been referenced more than REFERENCE_WARNING_LIMIT times, 1291 give a warning. */ 1292 if (tags->touched > reference_warning_limit) 1293 { 1294 input_filename = tags->filename; 1295 line_number = tags->line_no; 1296 warning (_("node `%s' has been referenced %d times"), 1297 tags->node, tags->touched); 1298 } 1299 1300 if (tags->touched == 0) 1301 { 1302 input_filename = tags->filename; 1303 line_number = tags->line_no; 1304 1305 /* Notice that the node "Top" is special, and doesn't have to 1306 be referenced. Anchors don't have to be referenced 1307 either, you might define them for another document. */ 1308 if (strcasecmp (tags->node, "Top") != 0 1309 && !(tags->flags & TAG_FLAG_ANCHOR)) 1310 warning (_("unreferenced node `%s'"), tags->node); 1311 } 1312 } 1313 input_filename = old_input_filename; 1314} 1315 1316 1317/* Splitting */ 1318 1319/* Return true if the tag entry pointed to by TAGS is the last node. 1320 This means only anchors follow. */ 1321 1322static int 1323last_node_p (tags) 1324 TAG_ENTRY *tags; 1325{ 1326 int last = 1; 1327 while (tags->next_ent) { 1328 tags = tags->next_ent; 1329 if (tags->flags & TAG_FLAG_ANCHOR) 1330 ; 1331 else 1332 { 1333 last = 0; 1334 break; 1335 } 1336 } 1337 1338 return last; 1339} 1340 1341 1342/* Split large output files into a series of smaller files. Each file 1343 is pointed to in the tag table, which then gets written out as the 1344 original file. The new files have the same name as the original file 1345 with a "-num" attached. SIZE is the largest number of bytes to allow 1346 in any single split file. */ 1347void 1348split_file (filename, size) 1349 char *filename; 1350 int size; 1351{ 1352 char *root_filename, *root_pathname; 1353 char *the_file, *filename_part (); 1354 struct stat fileinfo; 1355 long file_size; 1356 char *the_header; 1357 int header_size; 1358 int dos_file_names = 0; /* if nonzero, don't exceed 8+3 limits */ 1359 1360 /* Can only do this to files with tag tables. */ 1361 if (!tag_table) 1362 return; 1363 1364 if (size == 0) 1365 size = DEFAULT_SPLIT_SIZE; 1366 1367 if ((stat (filename, &fileinfo) != 0) || 1368 (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD)) 1369 return; 1370 file_size = (long) fileinfo.st_size; 1371 1372 the_file = find_and_load (filename); 1373 if (!the_file) 1374 return; 1375 1376 root_filename = filename_part (filename); 1377 root_pathname = pathname_part (filename); 1378 1379 /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */ 1380 dos_file_names = !HAVE_LONG_FILENAMES (root_pathname ? root_pathname : "."); 1381 1382 if (!root_pathname) 1383 root_pathname = xstrdup (""); 1384 1385 /* Start splitting the file. Walk along the tag table 1386 outputting sections of the file. When we have written 1387 all of the nodes in the tag table, make the top-level 1388 pointer file, which contains indirect pointers and 1389 tags for the nodes. */ 1390 { 1391 int which_file = 1; 1392 TAG_ENTRY *tags = tag_table; 1393 char *indirect_info = NULL; 1394 1395 /* Remember the `header' of this file. The first tag in the file is 1396 the bottom of the header; the top of the file is the start. */ 1397 the_header = xmalloc (1 + (header_size = tags->position)); 1398 memcpy (the_header, the_file, header_size); 1399 1400 while (tags) 1401 { 1402 int file_top, file_bot, limit; 1403 1404 /* Have to include the Control-_. */ 1405 file_top = file_bot = tags->position; 1406 limit = file_top + size; 1407 1408 /* If the rest of this file is only one node, then 1409 that is the entire subfile. */ 1410 if (last_node_p (tags)) 1411 { 1412 int i = tags->position + 1; 1413 char last_char = the_file[i]; 1414 1415 while (i < file_size) 1416 { 1417 if ((the_file[i] == '\037') && 1418 ((last_char == '\n') || 1419 (last_char == '\014'))) 1420 break; 1421 else 1422 last_char = the_file[i]; 1423 i++; 1424 } 1425 file_bot = i; 1426 tags = tags->next_ent; 1427 goto write_region; 1428 } 1429 1430 /* Otherwise, find the largest number of nodes that can fit in 1431 this subfile. */ 1432 for (; tags; tags = tags->next_ent) 1433 { 1434 if (last_node_p (tags)) 1435 { 1436 /* This entry is the last node. Search forward for the end 1437 of this node, and that is the end of this file. */ 1438 int i = tags->position + 1; 1439 char last_char = the_file[i]; 1440 1441 while (i < file_size) 1442 { 1443 if ((the_file[i] == '\037') && 1444 ((last_char == '\n') || 1445 (last_char == '\014'))) 1446 break; 1447 else 1448 last_char = the_file[i]; 1449 i++; 1450 } 1451 file_bot = i; 1452 1453 if (file_bot < limit) 1454 { 1455 tags = tags->next_ent; 1456 goto write_region; 1457 } 1458 else 1459 { 1460 /* Here we want to write out everything before the last 1461 node, and then write the last node out in a file 1462 by itself. */ 1463 file_bot = tags->position; 1464 goto write_region; 1465 } 1466 } 1467 1468 /* Write region only if this was a node, not an anchor. */ 1469 if (tags->next_ent->position > limit 1470 && !(tags->flags & TAG_FLAG_ANCHOR)) 1471 { 1472 if (tags->position == file_top) 1473 tags = tags->next_ent; 1474 1475 file_bot = tags->position; 1476 1477 write_region: 1478 { 1479 int fd; 1480 char *split_filename, *split_basename; 1481 unsigned root_len = strlen (root_filename); 1482 1483 split_filename = xmalloc (10 + strlen (root_pathname) 1484 + root_len); 1485 split_basename = xmalloc (10 + root_len); 1486 sprintf (split_basename, "%s-%d", root_filename, which_file); 1487 if (dos_file_names) 1488 { 1489 char *dot = strchr (split_basename, '.'); 1490 unsigned base_len = strlen (split_basename); 1491 1492 if (dot) 1493 { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */ 1494 dot[1] = 'i'; 1495 memmove (which_file <= 99 ? dot + 2 : dot + 1, 1496 split_basename + root_len + 1, 1497 strlen (split_basename + root_len + 1) + 1); 1498 } 1499 else if (base_len > 8) 1500 { 1501 /* Make foobar-1, .., fooba-10, .., foob-100, ... */ 1502 unsigned numlen = base_len - root_len; 1503 1504 memmove (split_basename + 8 - numlen, 1505 split_basename + root_len, numlen + 1); 1506 } 1507 } 1508 sprintf (split_filename, "%s%s", root_pathname, 1509 split_basename); 1510 1511 fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666); 1512 if (fd < 0 1513 || write (fd, the_header, header_size) != header_size 1514 || write (fd, the_file + file_top, file_bot - file_top) 1515 != (file_bot - file_top) 1516 || (close (fd)) < 0) 1517 { 1518 perror (split_filename); 1519 if (fd != -1) 1520 close (fd); 1521 xexit (1); 1522 } 1523 1524 if (!indirect_info) 1525 { 1526 indirect_info = the_file + file_top; 1527 sprintf (indirect_info, "\037\nIndirect:\n"); 1528 indirect_info += strlen (indirect_info); 1529 } 1530 1531 sprintf (indirect_info, "%s: %d\n", 1532 split_basename, file_top); 1533 1534 free (split_basename); 1535 free (split_filename); 1536 indirect_info += strlen (indirect_info); 1537 which_file++; 1538 break; 1539 } 1540 } 1541 } 1542 } 1543 1544 /* We have sucessfully created the subfiles. Now write out the 1545 original again. We must use `output_stream', or 1546 write_tag_table_indirect () won't know where to place the output. */ 1547 output_stream = fopen (filename, "w"); 1548 if (!output_stream) 1549 { 1550 perror (filename); 1551 xexit (1); 1552 } 1553 1554 { 1555 int distance = indirect_info - the_file; 1556 fwrite (the_file, 1, distance, output_stream); 1557 1558 /* Inhibit newlines. */ 1559 paragraph_is_open = 0; 1560 1561 write_tag_table_indirect (); 1562 fclose (output_stream); 1563 free (the_header); 1564 free (the_file); 1565 return; 1566 } 1567 } 1568} 1569