1 /* $NetBSD: node.c,v 1.5 2025/12/31 22:18:50 oster Exp $ */ 2 3 /* node.c -- nodes for Texinfo. 4 Id: node.c,v 1.27 2004/12/20 23:56:07 karl Exp 5 6 Copyright (C) 1998, 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 "float.h" 27 #include "footnote.h" 28 #include "macro.h" 29 #include "makeinfo.h" 30 #include "node.h" 31 #include "html.h" 32 #include "sectioning.h" 33 #include "insertion.h" 34 #include "xml.h" 35 36 /* See comments in node.h. */ 37 NODE_REF *node_references = NULL; 38 NODE_REF *node_node_references = NULL; 39 TAG_ENTRY *tag_table = NULL; 40 int node_number = -1; 41 int node_order = 0; 42 int current_section = 0; 43 int outstanding_node = 0; 44 45 /* Adding nodes, and making tags. */ 47 48 /* Start a new tag table. */ 49 void 50 init_tag_table (void) 51 { 52 while (tag_table) 53 { 54 TAG_ENTRY *temp = tag_table; 55 free (temp->node); 56 free (temp->prev); 57 free (temp->next); 58 free (temp->up); 59 tag_table = tag_table->next_ent; 60 free (temp); 61 } 62 } 63 64 /* Write out the contents of the existing tag table. 66 INDIRECT_P says how to format the output (it depends on whether the 67 table is direct or indirect). */ 68 static void 69 write_tag_table_internal (int indirect_p) 70 { 71 TAG_ENTRY *node; 72 int old_indent = no_indent; 73 74 if (xml) 75 { 76 flush_output (); 77 return; 78 } 79 80 no_indent = 1; 81 filling_enabled = 0; 82 must_start_paragraph = 0; 83 close_paragraph (); 84 85 if (!indirect_p) 86 { 87 no_indent = 1; 88 insert ('\n'); 89 } 90 91 add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : ""); 92 93 /* Do not collapse -- to -, etc., in node names. */ 94 in_fixed_width_font++; 95 96 for (node = tag_table; node; node = node->next_ent) 97 { 98 if (node->flags & TAG_FLAG_ANCHOR) 99 { /* This reference is to an anchor. */ 100 execute_string ("Ref: %s", node->node); 101 } 102 else 103 { /* This reference is to a node. */ 104 execute_string ("Node: %s", node->node); 105 } 106 add_word_args ("\177%d\n", node->position); 107 } 108 109 add_word ("\037\nEnd Tag Table\n"); 110 111 /* Do not collapse -- to -, etc., in node names. */ 112 in_fixed_width_font--; 113 114 flush_output (); 115 no_indent = old_indent; 116 } 117 118 void 119 write_tag_table (char *filename) 120 { 121 output_stream = fopen (filename, "a"); 122 if (!output_stream) 123 { 124 fs_error (filename); 125 return; 126 } 127 128 write_tag_table_internal (0); /* Not indirect. */ 129 130 if (fclose (output_stream) != 0) 131 fs_error (filename); 132 } 133 134 static void 135 write_tag_table_indirect (void) 136 { 137 write_tag_table_internal (1); 138 } 139 140 /* Convert "top" and friends into "Top". */ 142 static void 143 normalize_node_name (char *string) 144 { 145 if (strcasecmp (string, "Top") == 0) 146 strcpy (string, "Top"); 147 } 148 149 static char * 150 get_node_token (int expand) 151 { 152 char *string; 153 154 get_until_in_line (expand, ",", &string); 155 156 if (curchar () == ',') 157 input_text_offset++; 158 159 fix_whitespace (string); 160 161 /* Force all versions of "top" to be "Top". */ 162 normalize_node_name (string); 163 164 return string; 165 } 166 167 /* Expand any macros and other directives in a node name, and 168 return the expanded name as an malloc'ed string. */ 169 char * 170 expand_node_name (char *node) 171 { 172 char *result = node; 173 174 if (node) 175 { 176 /* Don't expand --, `` etc., in case somebody will want 177 to print the result. */ 178 in_fixed_width_font++; 179 result = expansion (node, 0); 180 in_fixed_width_font--; 181 fix_whitespace (result); 182 normalize_node_name (result); 183 } 184 return result; 185 } 186 187 /* Look up NAME in the tag table, and return the associated 188 tag_entry. If the node is not in the table return NULL. */ 189 TAG_ENTRY * 190 find_node (char *name) 191 { 192 TAG_ENTRY *tag = tag_table; 193 char *expanded_name; 194 char n1 = name[0]; 195 196 while (tag) 197 { 198 if (tag->node[0] == n1 && strcmp (tag->node, name) == 0) 199 return tag; 200 tag = tag->next_ent; 201 } 202 203 if (!expensive_validation) 204 return NULL; 205 206 /* Try harder. Maybe TAG_TABLE has the expanded NAME, or maybe NAME 207 is expanded while TAG_TABLE has its unexpanded form. This may 208 slow down the search, but if they want this feature, let them 209 pay! If they want it fast, they should write every node name 210 consistently (either always expanded or always unexpaned). */ 211 expanded_name = expand_node_name (name); 212 for (tag = tag_table; tag; tag = tag->next_ent) 213 { 214 if (STREQ (tag->node, expanded_name)) 215 break; 216 /* If the tag name doesn't have the command prefix, there's no 217 chance it could expand into anything but itself. */ 218 if (strchr (tag->node, COMMAND_PREFIX)) 219 { 220 char *expanded_node = expand_node_name (tag->node); 221 222 if (STREQ (expanded_node, expanded_name)) 223 { 224 free (expanded_node); 225 break; 226 } 227 free (expanded_node); 228 } 229 } 230 free (expanded_name); 231 return tag; 232 } 233 234 /* Look in the tag table for a node whose file name is FNAME, and 235 return the associated tag_entry. If there's no such node in the 236 table, return NULL. */ 237 static TAG_ENTRY * 238 find_node_by_fname (char *fname) 239 { 240 TAG_ENTRY *tag = tag_table; 241 while (tag) 242 { 243 if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0) 244 return tag; 245 tag = tag->next_ent; 246 } 247 248 return tag; 249 } 250 251 /* Remember next, prev, etc. references in a @node command, where we 252 don't care about most of the entries. */ 253 static void 254 remember_node_node_reference (char *node) 255 { 256 NODE_REF *temp = xmalloc (sizeof (NODE_REF)); 257 int number; 258 259 if (!node) return; 260 temp->next = node_node_references; 261 temp->node = xstrdup (node); 262 temp->type = followed_reference; 263 number = number_of_node (node); 264 if (number) 265 temp->number = number; /* Already assigned. */ 266 else 267 { 268 node_number++; 269 temp->number = node_number; 270 } 271 node_node_references = temp; 272 } 273 274 /* Remember NODE and associates. */ 275 static void 276 remember_node (char *node, char *prev, char *next, char *up, 277 int position, int line_no, char *fname, int flags) 278 { 279 /* Check for existence of this tag already. */ 280 if (validating) 281 { 282 TAG_ENTRY *tag = find_node (node); 283 if (tag) 284 { 285 line_error (_("Node `%s' previously defined at line %d"), 286 node, tag->line_no); 287 return; 288 } 289 } 290 291 if (!(flags & TAG_FLAG_ANCHOR)) 292 { 293 /* Make this the current node. */ 294 current_node = node; 295 } 296 297 /* Add it to the list. */ 298 { 299 int number = number_of_node (node); 300 301 TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY)); 302 new->node = node; 303 new->prev = prev; 304 new->next = next; 305 new->up = up; 306 new->position = position; 307 new->line_no = line_no; 308 new->filename = node_filename; 309 new->touched = 0; 310 new->flags = flags; 311 if (number) 312 new->number = number; /* Already assigned. */ 313 else 314 { 315 node_number++; 316 new->number = node_number; 317 } 318 if (fname) 319 new->html_fname = fname; 320 else 321 /* This happens for Top node under split-HTML, for example. */ 322 new->html_fname 323 = normalize_filename (filename_part (current_output_filename)); 324 new->next_ent = tag_table; 325 326 /* Increment the order counter, and save it. */ 327 node_order++; 328 new->order = node_order; 329 330 tag_table = new; 331 } 332 333 if (html) 334 { /* Note the references to the next etc. nodes too. */ 335 remember_node_node_reference (next); 336 remember_node_node_reference (prev); 337 remember_node_node_reference (up); 338 } 339 } 340 341 /* Remember this node name for later validation use. This is used to 342 remember menu references while reading the input file. After the 343 output file has been written, if validation is on, then we use the 344 contents of `node_references' as a list of nodes to validate. */ 345 void 346 remember_node_reference (char *node, int line, enum reftype type) 347 { 348 NODE_REF *temp = xmalloc (sizeof (NODE_REF)); 349 int number = number_of_node (node); 350 351 temp->next = node_references; 352 temp->node = xstrdup (node); 353 temp->line_no = line; 354 temp->section = current_section; 355 temp->type = type; 356 temp->containing_node = xstrdup (current_node ? current_node : ""); 357 temp->filename = node_filename; 358 if (number) 359 temp->number = number; /* Already assigned. */ 360 else 361 { 362 node_number++; 363 temp->number = node_number; 364 } 365 366 node_references = temp; 367 } 368 369 static void 370 isolate_nodename (char *nodename) 371 { 372 int i, c; 373 int paren_seen, paren; 374 375 if (!nodename) 376 return; 377 378 canon_white (nodename); 379 paren_seen = paren = i = 0; 380 381 if (*nodename == '.' || !*nodename) 382 { 383 *nodename = 0; 384 return; 385 } 386 387 if (*nodename == '(') 388 { 389 paren++; 390 paren_seen++; 391 i++; 392 } 393 394 for (; (c = nodename[i]); i++) 395 { 396 if (paren) 397 { 398 if (c == '(') 399 paren++; 400 else if (c == ')') 401 paren--; 402 403 continue; 404 } 405 406 /* If the character following the close paren is a space, then this 407 node has no more characters associated with it. */ 408 if (c == '\t' || 409 c == '\n' || 410 c == ',' || 411 ((paren_seen && nodename[i - 1] == ')') && 412 (c == ' ' || c == '.')) || 413 (c == '.' && 414 ((!nodename[i + 1] || 415 (cr_or_whitespace (nodename[i + 1])) || 416 (nodename[i + 1] == ')'))))) 417 break; 418 } 419 nodename[i] = 0; 420 } 421 422 /* This function gets called at the start of every line while inside a 423 menu. It checks to see if the line starts with "* ", and if so and 424 REMEMBER_REF is nonzero, remembers the node reference as type 425 REF_TYPE that this menu refers to. input_text_offset is at the \n 426 just before the menu line. If REMEMBER_REF is zero, REF_TYPE is unused. */ 427 #define MENU_STARTER "* " 428 char * 429 glean_node_from_menu (int remember_ref, enum reftype ref_type) 430 { 431 int i, orig_offset = input_text_offset; 432 char *nodename; 433 char *line, *expanded_line; 434 char *old_input = input_text; 435 int old_size = input_text_length; 436 437 if (strncmp (&input_text[input_text_offset + 1], 438 MENU_STARTER, 439 strlen (MENU_STARTER)) != 0) 440 return NULL; 441 else 442 input_text_offset += strlen (MENU_STARTER) + 1; 443 444 /* The menu entry might include macro calls, so we need to expand them. */ 445 get_until ("\n", &line); 446 only_macro_expansion++; /* only expand macros in menu entries */ 447 expanded_line = expansion (line, 0); 448 only_macro_expansion--; 449 free (line); 450 input_text = expanded_line; 451 input_text_offset = 0; 452 input_text_length = strlen (expanded_line); 453 454 get_until_in_line (0, ":", &nodename); 455 if (curchar () == ':') 456 input_text_offset++; 457 458 if (curchar () != ':') 459 { 460 free (nodename); 461 get_until_in_line (0, "\n", &nodename); 462 isolate_nodename (nodename); 463 } 464 465 input_text = old_input; 466 input_text_offset = orig_offset; 467 input_text_length = old_size; 468 free (expanded_line); 469 fix_whitespace (nodename); 470 normalize_node_name (nodename); 471 i = strlen (nodename); 472 if (i && nodename[i - 1] == ':') 473 nodename[i - 1] = 0; 474 475 if (remember_ref) 476 remember_node_reference (nodename, line_number, ref_type); 477 478 return nodename; 479 } 480 481 /* Set the name of the current output file. */ 482 void 483 set_current_output_filename (const char *fname) 484 { 485 if (current_output_filename) 486 free (current_output_filename); 487 current_output_filename = xstrdup (fname); 488 } 489 490 491 /* Output the <a name="..."></a> constructs for NODE. We output both 492 the new-style conversion and the old-style, if they are different. 493 See comments at `add_escaped_anchor_name' in html.c. */ 494 495 static void 496 add_html_names (char *node) 497 { 498 char *tem = expand_node_name (node); 499 char *otem = xstrdup (tem); 500 501 /* Determine if the old and new schemes come up with different names; 502 only output the old scheme if that is so. We don't want to output 503 the same name twice. */ 504 canon_white (otem); 505 { 506 char *optr = otem; 507 int need_old = 0; 508 509 for (; *optr; optr++) 510 { 511 if (!cr_or_whitespace (*optr) && !URL_SAFE_CHAR (*optr)) 512 { 513 need_old = 1; 514 break; 515 } 516 } 517 518 if (need_old) 519 { 520 add_word ("<a name=\""); 521 add_anchor_name (otem, -1); /* old anchor name conversion */ 522 add_word ("\"></a>\n"); 523 } 524 free (otem); 525 } 526 527 /* Always output the new scheme. */ 528 canon_white (tem); 529 add_word ("<a name=\""); 530 add_anchor_name (tem, 0); 531 add_word ("\"></a>\n"); 532 533 free (tem); 534 } 535 536 537 /* The order is: nodename, nextnode, prevnode, upnode. 539 If all of the NEXT, PREV, and UP fields are empty, they are defaulted. 540 You must follow a node command which has those fields defaulted 541 with a sectioning command (e.g., @chapter) giving the "level" of that node. 542 It is an error not to do so. 543 The defaults come from the menu in this node's parent. */ 544 void 545 cm_node (int arg, int arg2, int arg3) 546 { 547 static long epilogue_len = 0L; 548 char *node, *prev, *next, *up; 549 int new_node_pos, defaulting, this_section; 550 int no_warn = 0; 551 char *fname_for_this_node = NULL; 552 char *tem; 553 TAG_ENTRY *tag = NULL; 554 555 if (strcmp (command, "nwnode") == 0) 556 no_warn = TAG_FLAG_NO_WARN; 557 558 /* Get rid of unmatched brace arguments from previous commands. */ 559 discard_braces (); 560 561 /* There also might be insertions left lying around that haven't been 562 ended yet. Do that also. */ 563 discard_insertions (1); 564 565 if (!html && !already_outputting_pending_notes) 566 { 567 close_paragraph (); 568 output_pending_notes (); 569 } 570 571 new_node_pos = output_position; 572 573 if (macro_expansion_output_stream && !executing_string) 574 append_to_expansion_output (input_text_offset + 1); 575 576 /* Do not collapse -- to -, etc., in node names. */ 577 in_fixed_width_font++; 578 579 /* While expanding the @node line, leave any non-macros 580 intact, so that the macro-expanded output includes them. */ 581 only_macro_expansion++; 582 node = get_node_token (1); 583 only_macro_expansion--; 584 next = get_node_token (0); 585 prev = get_node_token (0); 586 up = get_node_token (0); 587 588 if (html && splitting 589 /* If there is a Top node, it always goes into index.html. So 590 don't start a new HTML file for Top. */ 591 && (top_node_seen || strcasecmp (node, "Top") != 0)) 592 { 593 /* We test *node here so that @node without a valid name won't 594 start a new file name with a bogus name such as ".html". 595 This could happen if we run under "--force", where we cannot 596 simply bail out. Continuing to use the same file sounds like 597 the best we can do in such cases. */ 598 if (current_output_filename && output_stream && *node) 599 { 600 char *fname_for_prev_node; 601 602 if (current_node) 603 { 604 /* NOTE: current_node at this point still holds the name 605 of the previous node. */ 606 tem = expand_node_name (current_node); 607 fname_for_prev_node = nodename_to_filename (tem); 608 free (tem); 609 } 610 else /* could happen if their top node isn't named "Top" */ 611 fname_for_prev_node = filename_part (current_output_filename); 612 tem = expand_node_name (node); 613 fname_for_this_node = nodename_to_filename (tem); 614 free (tem); 615 /* Don't close current output file, if next output file is 616 to have the same name. This may happen at top level, or 617 if two nodes produce the same file name under --split. */ 618 if (FILENAME_CMP (fname_for_this_node, fname_for_prev_node) != 0) 619 { 620 long pos1 = 0; 621 622 /* End the current split output file. */ 623 close_paragraph (); 624 output_pending_notes (); 625 start_paragraph (); 626 /* Compute the length of the HTML file's epilogue. We 627 cannot know the value until run time, due to the 628 text/binary nuisance on DOS/Windows platforms, where 629 2 `\r' characters could be added to the epilogue when 630 it is written in text mode. */ 631 if (epilogue_len == 0) 632 { 633 flush_output (); 634 pos1 = ftell (output_stream); 635 } 636 add_word ("</body></html>\n"); 637 close_paragraph (); 638 if (epilogue_len == 0) 639 epilogue_len = ftell (output_stream) - pos1; 640 fclose (output_stream); 641 output_stream = NULL; 642 output_position = 0; 643 tag = find_node_by_fname (fname_for_this_node); 644 } 645 free (fname_for_prev_node); 646 } 647 } 648 649 filling_enabled = indented_fill = 0; 650 if (!html || (html && splitting)) 651 current_footnote_number = 1; 652 653 if (verbose_mode) 654 printf (_("Formatting node %s...\n"), node); 655 656 if (macro_expansion_output_stream && !executing_string) 657 remember_itext (input_text, input_text_offset); 658 659 /* Reset the line number in each node for Info output, so that 660 index entries will save the line numbers of parent node. */ 661 node_line_number = 0; 662 663 no_indent = 1; 664 if (xml) 665 { 666 xml_begin_document (current_output_filename); 667 xml_begin_node (); 668 if (!docbook) 669 { 670 xml_insert_element (NODENAME, START); 671 if (macro_expansion_output_stream && !executing_string) 672 me_execute_string (node); 673 else 674 execute_string ("%s", node); 675 xml_insert_element (NODENAME, END); 676 } 677 else 678 xml_node_id = xml_id (node); 679 } 680 else if (!no_headers && !html) 681 { 682 /* Emacs Info reader cannot grok indented escape sequence. */ 683 kill_self_indent (-1); 684 685 add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename); 686 687 if (macro_expansion_output_stream && !executing_string) 688 me_execute_string (node); 689 else 690 execute_string ("%s", node); 691 filling_enabled = indented_fill = 0; 692 } 693 694 /* Check for defaulting of this node's next, prev, and up fields. */ 695 defaulting = (*next == 0 && *prev == 0 && *up == 0); 696 697 this_section = what_section (input_text + input_text_offset, NULL); 698 699 /* If we are defaulting, then look at the immediately following 700 sectioning command (error if none) to determine the node's 701 level. Find the node that contains the menu mentioning this node 702 that is one level up (error if not found). That node is the "Up" 703 of this node. Default the "Next" and "Prev" from the menu. */ 704 if (defaulting) 705 { 706 NODE_REF *last_ref = NULL; 707 NODE_REF *ref = node_references; 708 709 if (this_section < 0 && !STREQ (node, "Top")) 710 { 711 char *polite_section_name = "top"; 712 int i; 713 714 for (i = 0; section_alist[i].name; i++) 715 if (section_alist[i].level == current_section + 1) 716 { 717 polite_section_name = section_alist[i].name; 718 break; 719 } 720 721 line_error 722 (_("Node `%s' requires a sectioning command (e.g., %c%s)"), 723 node, COMMAND_PREFIX, polite_section_name); 724 } 725 else 726 { 727 if (strcmp (node, "Top") == 0) 728 { 729 /* Default the NEXT pointer to be the first menu item in 730 this node, if there is a menu in this node. We have to 731 try very hard to find the menu, as it may be obscured 732 by execution_strings which are on the filestack. For 733 every member of the filestack which has a FILENAME 734 member which is identical to the current INPUT_FILENAME, 735 search forward from that offset. */ 736 int saved_input_text_offset = input_text_offset; 737 int saved_input_text_length = input_text_length; 738 char *saved_input_text = input_text; 739 FSTACK *next_file = filestack; 740 741 int orig_offset, orig_size; 742 743 int bye_offset = search_forward ("\n@bye", input_text_offset); 744 745 /* No matter what, make this file point back at `(dir)'. */ 746 free (up); 747 up = xstrdup ("(dir)"); /* html fixxme */ 748 749 while (1) 750 { 751 orig_offset = input_text_offset; 752 orig_size = 753 search_forward (node_search_string, orig_offset); 754 755 if (orig_size < 0) 756 orig_size = input_text_length; 757 758 input_text_offset = search_forward ("\n@menu", orig_offset); 759 if (input_text_offset > -1 760 && (bye_offset > -1 && input_text_offset < bye_offset) 761 && cr_or_whitespace (input_text[input_text_offset + 6])) 762 { 763 char *nodename_from_menu = NULL; 764 765 input_text_offset = 766 search_forward ("\n* ", input_text_offset); 767 768 if (input_text_offset != -1) 769 nodename_from_menu = glean_node_from_menu (0, 0); 770 771 if (nodename_from_menu) 772 { 773 free (next); 774 next = nodename_from_menu; 775 break; 776 } 777 } 778 779 /* We got here, so it hasn't been found yet. Try 780 the next file on the filestack if there is one. */ 781 if (next_file 782 && FILENAME_CMP (next_file->filename, input_filename) 783 == 0) 784 { 785 input_text = next_file->text; 786 input_text_offset = next_file->offset; 787 input_text_length = next_file->size; 788 next_file = next_file->next; 789 } 790 else 791 { /* No more input files to check. */ 792 break; 793 } 794 } 795 796 input_text = saved_input_text; 797 input_text_offset = saved_input_text_offset; 798 input_text_length = saved_input_text_length; 799 } 800 } 801 802 /* Fix the level of the menu references in the Top node, iff it 803 was declared with @top, and no subsequent reference was found. */ 804 if (top_node_seen && !non_top_node_seen) 805 { 806 /* Then this is the first non-@top node seen. */ 807 int level; 808 809 level = set_top_section_level (this_section - 1); 810 non_top_node_seen = 1; 811 812 while (ref) 813 { 814 if (ref->section == level) 815 ref->section = this_section - 1; 816 ref = ref->next; 817 } 818 819 ref = node_references; 820 } 821 822 while (ref) 823 { 824 if (ref->section == (this_section - 1) 825 && ref->type == menu_reference 826 && strcmp (ref->node, node) == 0) 827 { 828 char *containing_node = ref->containing_node; 829 830 free (up); 831 up = xstrdup (containing_node); 832 833 if (last_ref 834 && last_ref->type == menu_reference 835 && strcmp (last_ref->containing_node, containing_node) == 0) 836 { 837 free (next); 838 next = xstrdup (last_ref->node); 839 } 840 841 while (ref->section == this_section - 1 842 && ref->next 843 && ref->next->type != menu_reference) 844 ref = ref->next; 845 846 if (ref->next && ref->type == menu_reference 847 && strcmp (ref->next->containing_node, containing_node) == 0) 848 { 849 free (prev); 850 prev = xstrdup (ref->next->node); 851 } 852 else if (!ref->next 853 && strcasecmp (ref->containing_node, "Top") == 0) 854 { 855 free (prev); 856 prev = xstrdup (ref->containing_node); 857 } 858 break; 859 } 860 last_ref = ref; 861 ref = ref->next; 862 } 863 } 864 865 /* Insert the correct args if we are expanding macros, and the node's 866 pointers weren't defaulted. */ 867 if (macro_expansion_output_stream && !executing_string && !defaulting) 868 { 869 char *temp; 870 int op_orig = output_paragraph_offset; 871 int meta_pos_orig = meta_char_pos; 872 int extra = html ? strlen (node) : 0; 873 874 temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up)); 875 sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up); 876 me_execute_string (temp); 877 free (temp); 878 879 output_paragraph_offset = op_orig; 880 meta_char_pos = meta_pos_orig; 881 } 882 883 if (!*node) 884 { 885 line_error (_("No node name specified for `%c%s' command"), 886 COMMAND_PREFIX, command); 887 free (node); 888 free (next); next = NULL; 889 free (prev); prev= NULL; 890 free (up); up = NULL; 891 node_number++; /* else it doesn't get bumped */ 892 } 893 else 894 { 895 if (!*next) { free (next); next = NULL; } 896 if (!*prev) { free (prev); prev = NULL; } 897 if (!*up) { free (up); up = NULL; } 898 remember_node (node, prev, next, up, new_node_pos, line_number, 899 fname_for_this_node, no_warn); 900 outstanding_node = 1; 901 } 902 903 if (html) 904 { 905 if (splitting && *node && output_stream == NULL) 906 { 907 char *dirname; 908 char filename[PATH_MAX]; 909 910 dirname = pathname_part (current_output_filename); 911 strcpy (filename, dirname); 912 strcat (filename, fname_for_this_node); 913 free (dirname); 914 915 /* See if the node name converted to a file name clashes 916 with other nodes or anchors. If it clashes with an 917 anchor, we complain and nuke that anchor's file. */ 918 if (!tag) 919 { 920 output_stream = fopen (filename, "w"); 921 html_output_head_p = 0; /* so that we generate HTML preamble */ 922 html_output_head (); 923 } 924 else if ((tag->flags & TAG_FLAG_ANCHOR) != 0) 925 { 926 line_error (_("Anchor `%s' and node `%s' map to the same file name"), 927 tag->node, node); 928 file_line_error (tag->filename, tag->line_no, 929 _("This @anchor command ignored; references to it will not work")); 930 file_line_error (tag->filename, tag->line_no, 931 _("Rename this anchor or use the `--no-split' option")); 932 /* Nuke the file name recorded in anchor's tag. 933 Since we are about to nuke the file itself, we 934 don't want find_node_by_fname to consider this 935 anchor anymore. */ 936 free (tag->html_fname); 937 tag->html_fname = NULL; 938 output_stream = fopen (filename, "w"); 939 html_output_head_p = 0; /* so that we generate HTML preamble */ 940 html_output_head (); 941 } 942 else 943 { 944 /* This node's file name clashes with another node. 945 We put them both on the same file. */ 946 output_stream = fopen (filename, "r+"); 947 if (output_stream) 948 { 949 static char html_end[] = "</body></html>\n"; 950 char end_line[sizeof(html_end)]; 951 int fpos = fseek (output_stream, -epilogue_len, 952 SEEK_END); 953 954 if (fpos < 0 955 || fgets (end_line, sizeof (html_end), 956 output_stream) == NULL 957 /* Paranoia: did someone change the way HTML 958 files are finished up? */ 959 || strcasecmp (end_line, html_end) != 0) 960 { 961 line_error (_("Unexpected string at end of split-HTML file `%s'"), 962 fname_for_this_node); 963 fclose (output_stream); 964 xexit (1); 965 } 966 fseek (output_stream, -epilogue_len, SEEK_END); 967 } 968 } 969 if (output_stream == NULL) 970 { 971 fs_error (filename); 972 xexit (1); 973 } 974 set_current_output_filename (filename); 975 } 976 977 if (!splitting && no_headers) 978 { /* cross refs need a name="#anchor" even if not writing headers */ 979 add_html_names (node); 980 } 981 982 if (splitting || !no_headers) 983 { /* Navigation bar. */ 984 add_html_block_elt ("<div class=\"node\">\n"); 985 /* The <p> avoids the links area running on with old Lynxen. */ 986 add_word_args ("<p>%s\n", splitting ? "" : "<hr>"); 987 988 /* In the split HTML case, the filename is wrong for the 989 old-style converted names, but we'll add them anyway, for 990 consistency. (And we need them in the normal (not 991 no_headers) nonsplit case.) */ 992 add_html_names (node); 993 994 if (next) 995 { 996 tem = expansion (next, 0); 997 add_word ((char *) _("Next:")); 998 add_word (" "); 999 1000 add_word ("<a rel=\"next\" accesskey=\"n\" href=\""); 1001 add_anchor_name (tem, 1); 1002 tem = escape_string (tem); 1003 add_word_args ("\">%s</a>", tem); 1004 1005 free (tem); 1006 1007 if (prev || up) 1008 add_word (",\n"); 1009 } 1010 if (prev) 1011 { 1012 tem = expansion (prev, 0); 1013 add_word ((char *) _("Previous:")); 1014 add_word (" "); 1015 add_word ("<a rel=\"previous\" accesskey=\"p\" href=\""); 1016 add_anchor_name (tem, 1); 1017 tem = escape_string (tem); 1018 add_word_args ("\">%s</a>", tem); 1019 free (tem); 1020 1021 if (up) 1022 add_word (",\n"); 1023 } 1024 if (up) 1025 { 1026 tem = expansion (up, 0); 1027 add_word ((char *) _("Up:")); 1028 add_word (" "); 1029 add_word ("<a rel=\"up\" accesskey=\"u\" href=\""); 1030 add_anchor_name (tem, 1); 1031 tem = escape_string (tem); 1032 add_word_args ("\">%s</a>", tem); 1033 free (tem); 1034 } 1035 /* html fixxme: we want a `top' or `contents' link here. */ 1036 1037 add_word_args ("\n%s\n", splitting ? "<hr>" : ""); 1038 add_word ("</div>\n"); 1039 } 1040 } 1041 else if (docbook) 1042 ; 1043 else if (xml) 1044 { 1045 if (next) 1046 { 1047 xml_insert_element (NODENEXT, START); 1048 execute_string ("%s", next); 1049 xml_insert_element (NODENEXT, END); 1050 } 1051 if (prev) 1052 { 1053 xml_insert_element (NODEPREV, START); 1054 execute_string ("%s", prev); 1055 xml_insert_element (NODEPREV, END); 1056 } 1057 if (up) 1058 { 1059 xml_insert_element (NODEUP, START); 1060 execute_string ("%s", up); 1061 xml_insert_element (NODEUP, END); 1062 } 1063 } 1064 else if (!no_headers) 1065 { 1066 if (macro_expansion_output_stream) 1067 me_inhibit_expansion++; 1068 1069 /* These strings are not translatable. */ 1070 if (next) 1071 { 1072 execute_string (", Next: %s", next); 1073 filling_enabled = indented_fill = 0; 1074 } 1075 if (prev) 1076 { 1077 execute_string (", Prev: %s", prev); 1078 filling_enabled = indented_fill = 0; 1079 } 1080 if (up) 1081 { 1082 execute_string (", Up: %s", up); 1083 filling_enabled = indented_fill = 0; 1084 } 1085 if (macro_expansion_output_stream) 1086 me_inhibit_expansion--; 1087 } 1088 1089 close_paragraph (); 1090 no_indent = 0; 1091 1092 /* Change the section only if there was a sectioning command. */ 1093 if (this_section >= 0) 1094 current_section = this_section; 1095 1096 if (current_node && STREQ (current_node, "Top")) 1097 top_node_seen = 1; 1098 1099 filling_enabled = 1; 1100 in_fixed_width_font--; 1101 } 1102 1103 /* Cross-reference target at an arbitrary spot. */ 1104 void 1105 cm_anchor (int arg, int arg2, int arg3) 1106 { 1107 char *anchor; 1108 char *fname_for_anchor = NULL; 1109 1110 if (arg == END) 1111 return; 1112 1113 /* Parse the anchor text. */ 1114 anchor = get_xref_token (1); 1115 1116 /* Force all versions of "top" to be "Top". */ 1117 normalize_node_name (anchor); 1118 1119 /* In HTML mode, need to actually produce some output. */ 1120 if (html) 1121 { 1122 /* If this anchor is at the beginning of a new paragraph, make 1123 sure a new paragraph is indeed started. */ 1124 if (!paragraph_is_open) 1125 { 1126 if (!executing_string && html) 1127 html_output_head (); 1128 start_paragraph (); 1129 if (!in_fixed_width_font || in_menu || in_detailmenu) 1130 { 1131 insert_string ("<p>"); 1132 in_paragraph = 1; 1133 } 1134 } 1135 add_word ("<a name=\""); 1136 add_anchor_name (anchor, 0); 1137 add_word ("\"></a>"); 1138 if (splitting) 1139 { 1140 /* If we are splitting, cm_xref will produce a reference to 1141 a file whose name is derived from the anchor name. So we 1142 must create a file when we see an @anchor, otherwise 1143 xref's to anchors won't work. The file we create simply 1144 redirects to the file of this anchor's node. */ 1145 TAG_ENTRY *tag; 1146 1147 fname_for_anchor = nodename_to_filename (anchor); 1148 /* See if the anchor name converted to a file name clashes 1149 with other anchors or nodes. */ 1150 tag = find_node_by_fname (fname_for_anchor); 1151 if (tag) 1152 { 1153 if ((tag->flags & TAG_FLAG_ANCHOR) != 0) 1154 line_error (_("Anchors `%s' and `%s' map to the same file name"), 1155 anchor, tag->node); 1156 else 1157 line_error (_("Anchor `%s' and node `%s' map to the same file name"), 1158 anchor, tag->node); 1159 line_error (_("@anchor command ignored; references to it will not work")); 1160 line_error (_("Rename this anchor or use the `--no-split' option")); 1161 free (fname_for_anchor); 1162 /* We will not be creating a file for this anchor, so 1163 set its name to NULL, so that remember_node stores a 1164 NULL and find_node_by_fname won't consider this 1165 anchor for clashes. */ 1166 fname_for_anchor = NULL; 1167 } 1168 else 1169 { 1170 char *dirname, *p; 1171 char filename[PATH_MAX]; 1172 FILE *anchor_stream; 1173 1174 dirname = pathname_part (current_output_filename); 1175 strcpy (filename, dirname); 1176 strcat (filename, fname_for_anchor); 1177 free (dirname); 1178 1179 anchor_stream = fopen (filename, "w"); 1180 if (anchor_stream == NULL) 1181 { 1182 fs_error (filename); 1183 xexit (1); 1184 } 1185 /* The HTML magic below will cause the browser to 1186 immediately go to the anchor's node's file. Lynx 1187 seems not to support this redirection, but it looks 1188 like a bug in Lynx, and they can work around it by 1189 clicking on the link once more. */ 1190 fputs ("<meta http-equiv=\"refresh\" content=\"0; url=", 1191 anchor_stream); 1192 /* Make the indirect link point to the current node's 1193 file and anchor's "<a name" label. If we don't have 1194 a valid node name, refer to the current output file 1195 instead. */ 1196 if (current_node && *current_node) 1197 { 1198 char *fn, *tem; 1199 1200 tem = expand_node_name (current_node); 1201 fn = nodename_to_filename (tem); 1202 free (tem); 1203 fputs (fn, anchor_stream); 1204 free (fn); 1205 } 1206 else 1207 { 1208 char *base = filename_part (current_output_filename); 1209 1210 fputs (base, anchor_stream); 1211 free (base); 1212 } 1213 fputs ("#", anchor_stream); 1214 for (p = anchor; *p; p++) 1215 { 1216 if (*p == '&') 1217 fputs ("&", anchor_stream); 1218 else if (!URL_SAFE_CHAR (*p)) 1219 fprintf (anchor_stream, "%%%x", (unsigned char) *p); 1220 else 1221 fputc (*p, anchor_stream); 1222 } 1223 fputs ("\">\n", anchor_stream); 1224 fclose (anchor_stream); 1225 } 1226 } 1227 } 1228 else if (xml) 1229 { 1230 xml_insert_element_with_attribute (ANCHOR, START, "name=\"%s\"", anchor); 1231 xml_insert_element (ANCHOR, END); 1232 } 1233 /* Save it in the tag table. */ 1234 remember_node (anchor, NULL, NULL, NULL, 1235 output_position + output_paragraph_offset, 1236 line_number, fname_for_anchor, TAG_FLAG_ANCHOR); 1237 } 1238 1239 /* Find NODE in REF_LIST. */ 1241 static NODE_REF * 1242 find_node_reference (char *node, NODE_REF *ref_list) 1243 { 1244 NODE_REF *orig_ref_list = ref_list; 1245 char *expanded_node; 1246 1247 while (ref_list) 1248 { 1249 if (strcmp (node, ref_list->node) == 0) 1250 break; 1251 ref_list = ref_list->next; 1252 } 1253 1254 if (ref_list || !expensive_validation) 1255 return ref_list; 1256 1257 /* Maybe NODE is not expanded yet. This may be SLOW. */ 1258 expanded_node = expand_node_name (node); 1259 for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next) 1260 { 1261 if (STREQ (expanded_node, ref_list->node)) 1262 break; 1263 if (strchr (ref_list->node, COMMAND_PREFIX)) 1264 { 1265 char *expanded_ref = expand_node_name (ref_list->node); 1266 1267 if (STREQ (expanded_node, expanded_ref)) 1268 { 1269 free (expanded_ref); 1270 break; 1271 } 1272 free (expanded_ref); 1273 } 1274 } 1275 free (expanded_node); 1276 return ref_list; 1277 } 1278 1279 void 1280 free_node_references (void) 1281 { 1282 NODE_REF *list, *temp; 1283 1284 list = node_references; 1285 1286 while (list) 1287 { 1288 temp = list; 1289 free (list->node); 1290 free (list->containing_node); 1291 list = list->next; 1292 free (temp); 1293 } 1294 node_references = NULL; 1295 } 1296 1297 void 1298 free_node_node_references (void) 1299 { 1300 NODE_REF *list, *temp; 1301 1302 list = node_references; 1303 1304 while (list) 1305 { 1306 temp = list; 1307 free (list->node); 1308 list = list->next; 1309 free (temp); 1310 } 1311 node_node_references = NULL; 1312 } 1313 1314 /* Return the number assigned to a named node in either the tag_table 1315 or node_references list or zero if no number has been assigned. */ 1316 int 1317 number_of_node (char *node) 1318 { 1319 NODE_REF *temp_ref; 1320 TAG_ENTRY *temp_node = find_node (node); 1321 1322 if (temp_node) 1323 return temp_node->number; 1324 else if ((temp_ref = find_node_reference (node, node_references))) 1325 return temp_ref->number; 1326 else if ((temp_ref = find_node_reference (node, node_node_references))) 1327 return temp_ref->number; 1328 else 1329 return 0; 1330 } 1331 1332 /* validation */ 1334 1335 /* Return 1 if TAG (at LINE) correctly validated, or 0 if not. 1336 LABEL is the (translated) description of the type of reference -- 1337 Menu, Cross, Next, etc. */ 1338 1339 static int 1340 validate (char *tag, int line, const char *label) 1341 { 1342 TAG_ENTRY *result; 1343 1344 /* If there isn't a tag to verify, or if the tag is in another file, 1345 then it must be okay. */ 1346 if (!tag || !*tag || *tag == '(') 1347 return 1; 1348 1349 /* Otherwise, the tag must exist. */ 1350 result = find_node (tag); 1351 1352 if (!result) 1353 { 1354 line_number = line; 1355 line_error (_("%s reference to nonexistent node `%s' (perhaps incorrect sectioning?)"), label, tag); 1356 return 0; 1357 } 1358 result->touched++; 1359 return 1; 1360 } 1361 1362 /* The strings here are followed in the message by `reference to...' in 1363 the `validate' routine. They are only used in messages, thus are 1364 translated. */ 1365 static const char * 1366 reftype_type_string (enum reftype type) 1367 { 1368 switch (type) 1369 { 1370 case menu_reference: 1371 return _("Menu"); 1372 case followed_reference: 1373 return _("Cross"); 1374 default: 1375 return "Internal-bad-reference-type"; 1376 } 1377 } 1378 1379 static void 1380 validate_other_references (NODE_REF *ref_list) 1381 { 1382 char *old_input_filename = input_filename; 1383 1384 while (ref_list) 1385 { 1386 input_filename = ref_list->filename; 1387 validate (ref_list->node, ref_list->line_no, 1388 reftype_type_string (ref_list->type)); 1389 ref_list = ref_list->next; 1390 } 1391 input_filename = old_input_filename; 1392 } 1393 1394 /* Validation of an info file. 1395 Scan through the list of tag entries touching the Prev, Next, and Up 1396 elements of each. It is an error not to be able to touch one of them, 1397 except in the case of external node references, such as "(DIR)". 1398 1399 If the Prev is different from the Up, 1400 then the Prev node must have a Next pointing at this node. 1401 1402 Every node except Top must have an Up. 1403 The Up node must contain some sort of reference, other than a Next, 1404 to this node. 1405 1406 If the Next is different from the Next of the Up, 1407 then the Next node must have a Prev pointing at this node. */ 1408 void 1409 validate_file (TAG_ENTRY *tag_table) 1410 { 1411 char *old_input_filename = input_filename; 1412 TAG_ENTRY *tags = tag_table; 1413 1414 while (tags) 1415 { 1416 TAG_ENTRY *temp_tag; 1417 char *tem1, *tem2; 1418 1419 input_filename = tags->filename; 1420 line_number = tags->line_no; 1421 1422 /* If this is a "no warn" node, don't validate it in any way. */ 1423 if (tags->flags & TAG_FLAG_NO_WARN) 1424 { 1425 tags = tags->next_ent; 1426 continue; 1427 } 1428 1429 /* If this node has a Next, then make sure that the Next exists. */ 1430 if (tags->next) 1431 { 1432 validate (tags->next, tags->line_no, _("Next")); 1433 1434 /* If the Next node exists, and there is no Up, then make sure 1435 that the Prev of the Next points back. But do nothing if 1436 we aren't supposed to issue warnings about this node. */ 1437 temp_tag = find_node (tags->next); 1438 if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN)) 1439 { 1440 char *prev = temp_tag->prev; 1441 int you_lose = !prev || !STREQ (prev, tags->node); 1442 1443 if (you_lose && expensive_validation) 1444 { 1445 tem1 = expand_node_name (prev); 1446 tem2 = expand_node_name (tags->node); 1447 1448 if (tem1 && tem2 && STREQ (tem1, tem2)) 1449 you_lose = 0; 1450 free (tem1); 1451 free (tem2); 1452 } 1453 if (you_lose) 1454 { 1455 line_error (_("Next field of node `%s' not pointed to (perhaps incorrect sectioning?)"), 1456 tags->node); 1457 file_line_error (temp_tag->filename, temp_tag->line_no, 1458 _("This node (%s) has the bad Prev"), 1459 temp_tag->node); 1460 temp_tag->flags |= TAG_FLAG_PREV_ERROR; 1461 } 1462 } 1463 } 1464 1465 /* Validate the Prev field if there is one, and we haven't already 1466 complained about it in some way. You don't have to have a Prev 1467 field at this stage. */ 1468 if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev) 1469 { 1470 int valid_p = validate (tags->prev, tags->line_no, _("Prev")); 1471 1472 if (!valid_p) 1473 tags->flags |= TAG_FLAG_PREV_ERROR; 1474 else 1475 { /* If the Prev field is not the same as the Up field, 1476 then the node pointed to by the Prev field must have 1477 a Next field which points to this node. */ 1478 int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up); 1479 1480 if (!prev_equals_up && expensive_validation) 1481 { 1482 tem1 = expand_node_name (tags->prev); 1483 tem2 = expand_node_name (tags->up); 1484 prev_equals_up = STREQ (tem1, tem2); 1485 free (tem1); 1486 free (tem2); 1487 } 1488 if (!prev_equals_up) 1489 { 1490 temp_tag = find_node (tags->prev); 1491 1492 /* If we aren't supposed to issue warnings about the 1493 target node, do nothing. */ 1494 if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN)) 1495 /* Do nothing. */ ; 1496 else 1497 { 1498 int you_lose = !temp_tag->next 1499 || !STREQ (temp_tag->next, tags->node); 1500 1501 if (temp_tag->next && you_lose && expensive_validation) 1502 { 1503 tem1 = expand_node_name (temp_tag->next); 1504 tem2 = expand_node_name (tags->node); 1505 if (STREQ (tem1, tem2)) 1506 you_lose = 0; 1507 free (tem1); 1508 free (tem2); 1509 } 1510 if (you_lose) 1511 { 1512 line_error 1513 (_("Prev field of node `%s' not pointed to"), 1514 tags->node); 1515 file_line_error (temp_tag->filename, 1516 temp_tag->line_no, 1517 _("This node (%s) has the bad Next"), 1518 temp_tag->node); 1519 temp_tag->flags |= TAG_FLAG_NEXT_ERROR; 1520 } 1521 } 1522 } 1523 } 1524 } 1525 1526 if (!tags->up 1527 && !(tags->flags & TAG_FLAG_ANCHOR) 1528 && strcasecmp (tags->node, "Top") != 0) 1529 line_error (_("`%s' has no Up field (perhaps incorrect sectioning?)"), tags->node); 1530 else if (tags->up) 1531 { 1532 int valid_p = validate (tags->up, tags->line_no, _("Up")); 1533 1534 /* If node X has Up: Y, then warn if Y fails to have a menu item 1535 or note pointing at X, if Y isn't of the form "(Y)". */ 1536 if (valid_p && *tags->up != '(') 1537 { 1538 NODE_REF *nref; 1539 NODE_REF *tref = NULL; 1540 NODE_REF *list = node_references; 1541 1542 for (;;) 1543 { 1544 nref = find_node_reference (tags->node, list); 1545 if (!nref) 1546 break; 1547 1548 if (strcmp (nref->containing_node, tags->up) == 0) 1549 { 1550 if (nref->type != menu_reference) 1551 { 1552 tref = nref; 1553 list = nref->next; 1554 } 1555 else 1556 break; 1557 } 1558 list = nref->next; 1559 } 1560 1561 if (!nref) 1562 { 1563 if (!tref && expensive_validation) 1564 { 1565 /* Sigh... This might be AWFULLY slow, but if 1566 they want this feature, they'll have to pay! 1567 We do all the loop again expanding each 1568 containing_node reference as we go. */ 1569 char *tags_up = expand_node_name (tags->up); 1570 char *tem; 1571 1572 list = node_references; 1573 1574 for (;;) 1575 { 1576 nref = find_node_reference (tags->node, list); 1577 if (!nref) 1578 break; 1579 tem = expand_node_name (nref->containing_node); 1580 if (STREQ (tem, tags_up)) 1581 { 1582 if (nref->type != menu_reference) 1583 tref = nref; 1584 else 1585 { 1586 free (tem); 1587 break; 1588 } 1589 } 1590 free (tem); 1591 list = nref->next; 1592 } 1593 } 1594 if (!nref && !tref) 1595 { 1596 temp_tag = find_node (tags->up); 1597 file_line_error (temp_tag->filename, temp_tag->line_no, 1598 _("Node `%s' lacks menu item for `%s' despite being its Up target"), 1599 tags->up, tags->node); 1600 } 1601 } 1602 } 1603 } 1604 tags = tags->next_ent; 1605 } 1606 1607 validate_other_references (node_references); 1608 /* We have told the user about the references which didn't exist. 1609 Now tell him about the nodes which aren't referenced. */ 1610 1611 for (tags = tag_table; tags; tags = tags->next_ent) 1612 { 1613 /* If this node is a "no warn" node, do nothing. */ 1614 if (tags->flags & TAG_FLAG_NO_WARN) 1615 { 1616 tags = tags->next_ent; 1617 continue; 1618 } 1619 1620 /* Special hack. If the node in question appears to have 1621 been referenced more than REFERENCE_WARNING_LIMIT times, 1622 give a warning. */ 1623 if (tags->touched > reference_warning_limit) 1624 { 1625 input_filename = tags->filename; 1626 line_number = tags->line_no; 1627 warning (_("node `%s' has been referenced %d times"), 1628 tags->node, tags->touched); 1629 } 1630 1631 if (tags->touched == 0) 1632 { 1633 input_filename = tags->filename; 1634 line_number = tags->line_no; 1635 1636 /* Notice that the node "Top" is special, and doesn't have to 1637 be referenced. Anchors don't have to be referenced 1638 either, you might define them for another document. */ 1639 if (strcasecmp (tags->node, "Top") != 0 1640 && !(tags->flags & TAG_FLAG_ANCHOR)) 1641 warning (_("unreferenced node `%s'"), tags->node); 1642 } 1643 } 1644 input_filename = old_input_filename; 1645 } 1646 1647 1648 /* Splitting */ 1650 1651 /* Return true if the tag entry pointed to by TAGS is the last node. 1652 This means only anchors follow. */ 1653 1654 static int 1655 last_node_p (TAG_ENTRY *tags) 1656 { 1657 int last = 1; 1658 while (tags->next_ent) { 1659 tags = tags->next_ent; 1660 if (tags->flags & TAG_FLAG_ANCHOR) 1661 ; 1662 else 1663 { 1664 last = 0; 1665 break; 1666 } 1667 } 1668 1669 return last; 1670 } 1671 1672 1673 static char * 1674 enumerate_filename (char *pathname, char *basename, int number) 1675 { 1676 /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */ 1677 const int dos_file_names = !HAVE_LONG_FILENAMES (pathname ? pathname : "."); 1678 unsigned name_len = strlen (basename); 1679 char *filename = xmalloc (10 + strlen (pathname) + name_len); 1680 char *base_filename = xmalloc (10 + name_len); 1681 1682 sprintf (base_filename, "%s-%d", basename, number); 1683 1684 if (dos_file_names) 1685 { 1686 char *dot = strchr (base_filename, '.'); 1687 unsigned base_len = strlen (base_filename); 1688 1689 if (dot) 1690 { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */ 1691 dot[1] = 'i'; 1692 memmove (number <= 99 ? dot + 2 : dot + 1, 1693 base_filename + name_len + 1, 1694 strlen (base_filename + name_len + 1) + 1); 1695 } 1696 else if (base_len > 8) 1697 { 1698 /* Make foobar-1, .., fooba-10, .., foob-100, ... */ 1699 unsigned numlen = base_len - name_len; 1700 1701 memmove (base_filename + 8 - numlen, base_filename + name_len, numlen + 1); 1702 } 1703 } 1704 1705 sprintf (filename, "%s%s", pathname, base_filename); 1706 1707 return filename; 1708 } 1709 1710 /* Remove previously split files, to avoid 1711 lingering parts of shrinked documents. */ 1712 void 1713 clean_old_split_files (char *filename) 1714 { 1715 char *root_filename = filename_part (filename); 1716 char *root_pathname = pathname_part (filename); 1717 int i; 1718 1719 /* We break as soon as we hit an inexistent file, 1720 so looping until large numbers is harmless. */ 1721 for (i = 1; i < 1000; i++) 1722 { 1723 struct stat st; 1724 char *check_file = enumerate_filename (root_pathname, root_filename, i); 1725 1726 if (stat (check_file, &st) != 0) 1727 break; 1728 else if (!S_ISDIR (st.st_mode)) 1729 { 1730 /* Give feedback if requested, removing a file is important. */ 1731 if (verbose_mode) 1732 printf (_("Removing %s\n"), check_file); 1733 1734 /* Warn user that we cannot remove the file. */ 1735 if (unlink (check_file) != 0) 1736 warning (_("Can't remove file `%s': %s"), check_file, strerror (errno)); 1737 } 1738 1739 free (check_file); 1740 } 1741 } 1742 1743 1744 /* Split large output files into a series of smaller files. Each file 1745 is pointed to in the tag table, which then gets written out as the 1746 original file. The new files have the same name as the original file 1747 with a "-num" attached. SIZE is the largest number of bytes to allow 1748 in any single split file. */ 1749 void 1750 split_file (char *filename, int size) 1751 { 1752 char *root_filename, *root_pathname; 1753 char *the_file; 1754 struct stat fileinfo; 1755 long file_size; 1756 char *the_header; 1757 int header_size; 1758 1759 /* Can only do this to files with tag tables. */ 1760 if (!tag_table) 1761 return; 1762 1763 if (size == 0) 1764 size = DEFAULT_SPLIT_SIZE; 1765 1766 if ((stat (filename, &fileinfo) != 0) 1767 || (((long) fileinfo.st_size) < size)) 1768 return; 1769 file_size = (long) fileinfo.st_size; 1770 1771 the_file = find_and_load (filename, 0); 1772 if (!the_file) 1773 return; 1774 1775 root_filename = filename_part (filename); 1776 root_pathname = pathname_part (filename); 1777 1778 if (!root_pathname) 1779 root_pathname = xstrdup (""); 1780 1781 /* Start splitting the file. Walk along the tag table 1782 outputting sections of the file. When we have written 1783 all of the nodes in the tag table, make the top-level 1784 pointer file, which contains indirect pointers and 1785 tags for the nodes. */ 1786 { 1787 int which_file = 1; 1788 TAG_ENTRY *tags = tag_table; 1789 char *indirect_info = NULL; 1790 1791 /* Maybe we want a Local Variables section. */ 1792 char *trailer = info_trailer (); 1793 int trailer_len = trailer ? strlen (trailer) : 0; 1794 1795 /* Remember the `header' of this file. The first tag in the file is 1796 the bottom of the header; the top of the file is the start. */ 1797 the_header = xmalloc (1 + (header_size = tags->position)); 1798 memcpy (the_header, the_file, header_size); 1799 1800 while (tags) 1801 { 1802 int file_top, file_bot, limit; 1803 1804 /* Have to include the Control-_. */ 1805 file_top = file_bot = tags->position; 1806 limit = file_top + size; 1807 1808 /* If the rest of this file is only one node, then 1809 that is the entire subfile. */ 1810 if (last_node_p (tags)) 1811 { 1812 int i = tags->position + 1; 1813 char last_char = the_file[i]; 1814 1815 while (i < file_size) 1816 { 1817 if ((the_file[i] == '\037') && 1818 ((last_char == '\n') || 1819 (last_char == '\014'))) 1820 break; 1821 else 1822 last_char = the_file[i]; 1823 i++; 1824 } 1825 file_bot = i; 1826 tags = tags->next_ent; 1827 goto write_region; 1828 } 1829 1830 /* Otherwise, find the largest number of nodes that can fit in 1831 this subfile. */ 1832 for (; tags; tags = tags->next_ent) 1833 { 1834 if (last_node_p (tags)) 1835 { 1836 /* This entry is the last node. Search forward for the end 1837 of this node, and that is the end of this file. */ 1838 int i = tags->position + 1; 1839 char last_char = the_file[i]; 1840 1841 while (i < file_size) 1842 { 1843 if ((the_file[i] == '\037') && 1844 ((last_char == '\n') || 1845 (last_char == '\014'))) 1846 break; 1847 else 1848 last_char = the_file[i]; 1849 i++; 1850 } 1851 file_bot = i; 1852 1853 if (file_bot < limit) 1854 { 1855 tags = tags->next_ent; 1856 goto write_region; 1857 } 1858 else 1859 { 1860 /* Here we want to write out everything before the last 1861 node, and then write the last node out in a file 1862 by itself. */ 1863 file_bot = tags->position; 1864 goto write_region; 1865 } 1866 } 1867 1868 /* Write region only if this was a node, not an anchor. */ 1869 if (tags->next_ent->position > limit 1870 && !(tags->flags & TAG_FLAG_ANCHOR)) 1871 { 1872 if (tags->position == file_top) 1873 tags = tags->next_ent; 1874 1875 file_bot = tags->position; 1876 1877 write_region: 1878 { 1879 int fd; 1880 char *split_filename = enumerate_filename (root_pathname, 1881 root_filename, which_file); 1882 char *split_basename = filename_part (split_filename); 1883 1884 fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666); 1885 if (fd < 0 1886 || write (fd, the_header, header_size) != header_size 1887 || write (fd, the_file + file_top, file_bot - file_top) 1888 != (file_bot - file_top) 1889 || (trailer_len 1890 && write (fd, trailer, trailer_len) != trailer_len) 1891 || close (fd) < 0) 1892 { 1893 perror (split_filename); 1894 if (fd != -1) 1895 close (fd); 1896 xexit (1); 1897 } 1898 1899 if (!indirect_info) 1900 { 1901 indirect_info = the_file + file_top; 1902 sprintf (indirect_info, "\037\nIndirect:\n"); 1903 indirect_info += strlen (indirect_info); 1904 } 1905 1906 sprintf (indirect_info, "%s: %d\n", 1907 split_basename, file_top); 1908 1909 free (split_basename); 1910 free (split_filename); 1911 indirect_info += strlen (indirect_info); 1912 which_file++; 1913 break; 1914 } 1915 } 1916 } 1917 } 1918 1919 /* We have sucessfully created the subfiles. Now write out the 1920 original again. We must use `output_stream', or 1921 write_tag_table_indirect () won't know where to place the output. */ 1922 output_stream = fopen (filename, "w"); 1923 if (!output_stream) 1924 { 1925 perror (filename); 1926 xexit (1); 1927 } 1928 1929 { 1930 int distance = indirect_info - the_file; 1931 fwrite (the_file, 1, distance, output_stream); 1932 1933 /* Inhibit newlines. */ 1934 paragraph_is_open = 0; 1935 1936 /* Write the indirect tag table. */ 1937 write_tag_table_indirect (); 1938 1939 /* preserve local variables in info output. */ 1940 if (trailer) 1941 { 1942 fwrite (trailer, 1, trailer_len, output_stream); 1943 free (trailer); 1944 } 1945 1946 fclose (output_stream); 1947 free (the_header); 1948 free (the_file); 1949 return; 1950 } 1951 } 1952 } 1953