1 /* node.c -- nodes for Texinfo.
2 $Id: node.c,v 1.34 2002/03/26 16:16:29 karl Exp $
4 Copyright (C) 1998, 99, 2000, 01, 02 Free Software Foundation, Inc.
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)
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.
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. */
28 #include "sectioning.h"
29 #include "insertion.h"
33 /* See comments in node.h. */
34 NODE_REF *node_references = NULL;
35 NODE_REF *node_node_references = NULL;
36 TAG_ENTRY *tag_table = NULL;
38 int current_section = 0;
39 int outstanding_node = 0;
41 /* Adding nodes, and making tags. */
43 /* Start a new tag table. */
49 TAG_ENTRY *temp = tag_table;
54 tag_table = tag_table->next_ent;
59 /* Write out the contents of the existing tag table.
60 INDIRECT_P says how to format the output (it depends on whether the
61 table is direct or indirect). */
63 write_tag_table_internal (indirect_p)
67 int old_indent = no_indent;
77 must_start_paragraph = 0;
86 add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
88 /* Do not collapse -- to -, etc., in node names. */
89 in_fixed_width_font++;
91 for (node = tag_table; node; node = node->next_ent)
93 if (node->flags & TAG_FLAG_ANCHOR)
94 { /* This reference is to an anchor. */
95 execute_string ("Ref: %s", node->node);
98 { /* This reference is to a node. */
99 execute_string ("Node: %s", node->node);
101 add_word_args ("\177%d\n", node->position);
104 add_word ("\037\nEnd Tag Table\n");
106 /* Do not collapse -- to -, etc., in node names. */
107 in_fixed_width_font--;
110 no_indent = old_indent;
116 write_tag_table_internal (0); /* Not indirect. */
120 write_tag_table_indirect ()
122 write_tag_table_internal (1);
125 /* Convert "top" and friends into "Top". */
127 normalize_node_name (string)
130 if (strcasecmp (string, "Top") == 0)
131 strcpy (string, "Top");
135 get_node_token (expand)
140 get_until_in_line (expand, ",", &string);
142 if (curchar () == ',')
145 fix_whitespace (string);
147 /* Force all versions of "top" to be "Top". */
148 normalize_node_name (string);
153 /* Expand any macros and other directives in a node name, and
154 return the expanded name as an malloc'ed string. */
156 expand_node_name (node)
163 /* Don't expand --, `` etc., in case somebody will want
164 to print the result. */
165 in_fixed_width_font++;
166 result = expansion (node, 0);
167 in_fixed_width_font--;
168 fix_whitespace (result);
169 normalize_node_name (result);
174 /* Look up NAME in the tag table, and return the associated
175 tag_entry. If the node is not in the table return NULL. */
180 TAG_ENTRY *tag = tag_table;
186 if (tag->node[0] == n1 && strcmp (tag->node, name) == 0)
191 if (!expensive_validation)
194 /* Try harder. Maybe TAG_TABLE has the expanded NAME, or maybe NAME
195 is expanded while TAG_TABLE has its unexpanded form. This may
196 slow down the search, but if they want this feature, let them
197 pay! If they want it fast, they should write every node name
198 consistently (either always expanded or always unexpaned). */
199 expanded_name = expand_node_name (name);
200 for (tag = tag_table; tag; tag = tag->next_ent)
202 if (STREQ (tag->node, expanded_name))
204 /* If the tag name doesn't have the command prefix, there's no
205 chance it could expand into anything but itself. */
206 if (strchr (tag->node, COMMAND_PREFIX))
208 char *expanded_node = expand_node_name (tag->node);
210 if (STREQ (expanded_node, expanded_name))
212 free (expanded_node);
215 free (expanded_node);
218 free (expanded_name);
222 /* Look in the tag table for a node whose file name is FNAME, and
223 return the associated tag_entry. If there's no such node in the
224 table, return NULL. */
226 find_node_by_fname (fname)
229 TAG_ENTRY *tag = tag_table;
232 if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0)
240 /* Remember next, prev, etc. references in a @node command, where we
241 don't care about most of the entries. */
243 remember_node_node_reference (node)
246 NODE_REF *temp = xmalloc (sizeof (NODE_REF));
250 temp->next = node_node_references;
251 temp->node = xstrdup (node);
252 temp->type = followed_reference;
253 number = number_of_node (node);
255 temp->number = number; /* Already assigned. */
259 temp->number = node_number;
261 node_node_references = temp;
264 /* Remember NODE and associates. */
266 remember_node (node, prev, next, up, position, line_no, fname, flags)
267 char *node, *prev, *next, *up, *fname;
268 int position, line_no, flags;
270 /* Check for existence of this tag already. */
273 TAG_ENTRY *tag = find_node (node);
276 line_error (_("Node `%s' previously defined at line %d"),
282 if (!(flags & TAG_FLAG_ANCHOR))
284 /* Make this the current node. */
288 /* Add it to the list. */
290 int number = number_of_node (node);
292 TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY));
297 new->position = position;
298 new->line_no = line_no;
299 new->filename = node_filename;
303 new->number = number; /* Already assigned. */
307 new->number = node_number;
309 new->html_fname = fname;
310 new->next_ent = tag_table;
315 { /* Note the references to the next etc. nodes too. */
316 remember_node_node_reference (next);
317 remember_node_node_reference (prev);
318 remember_node_node_reference (up);
322 /* Remember this node name for later validation use. This is used to
323 remember menu references while reading the input file. After the
324 output file has been written, if validation is on, then we use the
325 contents of `node_references' as a list of nodes to validate. */
327 remember_node_reference (node, line, type)
332 NODE_REF *temp = xmalloc (sizeof (NODE_REF));
333 int number = number_of_node (node);
335 temp->next = node_references;
336 temp->node = xstrdup (node);
337 temp->line_no = line;
338 temp->section = current_section;
340 temp->containing_node = xstrdup (current_node ? current_node : "");
341 temp->filename = node_filename;
343 temp->number = number; /* Already assigned. */
347 temp->number = node_number;
350 node_references = temp;
354 isolate_nodename (nodename)
358 int paren_seen, paren;
363 canon_white (nodename);
364 paren_seen = paren = i = 0;
366 if (*nodename == '.' || !*nodename)
372 if (*nodename == '(')
379 for (; (c = nodename[i]); i++)
391 /* If the character following the close paren is a space, then this
392 node has no more characters associated with it. */
396 ((paren_seen && nodename[i - 1] == ')') &&
397 (c == ' ' || c == '.')) ||
399 ((!nodename[i + 1] ||
400 (cr_or_whitespace (nodename[i + 1])) ||
401 (nodename[i + 1] == ')')))))
407 /* This function gets called at the start of every line while inside a
408 menu. It checks to see if the line starts with "* ", and if so and
409 REMEMBER_REF is nonzero, remembers the node reference as type
410 REF_TYPE that this menu refers to. input_text_offset is at the \n
411 just before the menu line. If REMEMBER_REF is zero, REF_TYPE is unused. */
412 #define MENU_STARTER "* "
414 glean_node_from_menu (remember_ref, ref_type)
416 enum reftype ref_type;
418 int i, orig_offset = input_text_offset;
420 char *line, *expanded_line;
421 char *old_input = input_text;
422 int old_size = input_text_length;
424 if (strncmp (&input_text[input_text_offset + 1],
426 strlen (MENU_STARTER)) != 0)
429 input_text_offset += strlen (MENU_STARTER) + 1;
431 /* The menu entry might include macro calls, so we need to expand them. */
432 get_until ("\n", &line);
433 only_macro_expansion++; /* only expand macros in menu entries */
434 expanded_line = expansion (line, 0);
435 only_macro_expansion--;
437 input_text = expanded_line;
438 input_text_offset = 0;
439 input_text_length = strlen (expanded_line);
441 get_until_in_line (0, ":", &nodename);
442 if (curchar () == ':')
445 if (curchar () != ':')
448 get_until_in_line (0, "\n", &nodename);
449 isolate_nodename (nodename);
452 input_text = old_input;
453 input_text_offset = orig_offset;
454 input_text_length = old_size;
455 free (expanded_line);
456 fix_whitespace (nodename);
457 normalize_node_name (nodename);
458 i = strlen (nodename);
459 if (i && nodename[i - 1] == ':')
463 remember_node_reference (nodename, line_number, ref_type);
468 /* Set the name of the current output file. */
470 set_current_output_filename (fname)
473 if (current_output_filename)
474 free (current_output_filename);
475 current_output_filename = xstrdup (fname);
478 /* The order is: nodename, nextnode, prevnode, upnode.
479 If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
480 You must follow a node command which has those fields defaulted
481 with a sectioning command (e.g. @chapter) giving the "level" of that node.
482 It is an error not to do so.
483 The defaults come from the menu in this node's parent. */
487 static long epilogue_len = 0L;
488 char *node, *prev, *next, *up;
489 int new_node_pos, defaulting, this_section;
491 char *fname_for_this_node = NULL;
493 TAG_ENTRY *tag = NULL;
495 if (strcmp (command, "nwnode") == 0)
496 no_warn = TAG_FLAG_NO_WARN;
498 /* Get rid of unmatched brace arguments from previous commands. */
501 /* There also might be insertions left lying around that haven't been
502 ended yet. Do that also. */
503 discard_insertions (1);
505 if (!html && !already_outputting_pending_notes)
509 output_pending_notes ();
512 new_node_pos = output_position;
514 if (macro_expansion_output_stream && !executing_string)
515 append_to_expansion_output (input_text_offset + 1);
517 /* Do not collapse -- to -, etc., in node names. */
518 in_fixed_width_font++;
520 /* While expanding the @node line, leave any non-macros
521 intact, so that the macro-expanded output includes them. */
522 only_macro_expansion++;
523 node = get_node_token (1);
524 only_macro_expansion--;
525 next = get_node_token (0);
526 prev = get_node_token (0);
527 up = get_node_token (0);
529 if (html && splitting
530 /* If there is a Top node, it always goes into index.html. So
531 don't start a new HTML file for Top. */
532 && (top_node_seen || strcasecmp (node, "Top") != 0))
534 /* We test *node here so that @node without a valid name won't
535 start a new file name with a bogus name such as ".html".
536 This could happen if we run under "--force", where we cannot
537 simply bail out. Continuing to use the same file sounds like
538 the best we can do in such cases. */
539 if (current_output_filename && output_stream && *node)
541 char *fname_for_prev_node;
545 /* NOTE: current_node at this point still holds the name
546 of the previous node. */
547 tem = expand_node_name (current_node);
548 fname_for_prev_node = nodename_to_filename (tem);
551 else /* could happen if their top node isn't named "Top" */
552 fname_for_prev_node = filename_part (current_output_filename);
553 tem = expand_node_name (node);
554 fname_for_this_node = nodename_to_filename (tem);
556 /* Don't close current output file, if next output file is
557 to have the same name. This may happen at top level, or
558 if two nodes produce the same file name under --split. */
559 if (FILENAME_CMP (fname_for_this_node, fname_for_prev_node) != 0)
563 /* End the current split output file. */
565 output_pending_notes ();
567 /* Compute the length of the HTML file's epilogue. We
568 cannot know the value until run time, due to the
569 text/binary nuisance on DOS/Windows platforms, where
570 2 `\r' characters could be added to the epilogue when
571 it is written in text mode. */
572 if (epilogue_len == 0)
575 pos1 = ftell (output_stream);
577 add_word ("</body></html>\n");
579 if (epilogue_len == 0)
580 epilogue_len = ftell (output_stream) - pos1;
581 fclose (output_stream);
582 output_stream = NULL;
583 tag = find_node_by_fname (fname_for_this_node);
585 free (fname_for_prev_node);
589 filling_enabled = indented_fill = 0;
590 if (!html || (html && splitting))
591 current_footnote_number = 1;
594 printf (_("Formatting node %s...\n"), node);
596 if (macro_expansion_output_stream && !executing_string)
597 remember_itext (input_text, input_text_offset);
602 xml_begin_document (current_output_filename);
606 xml_insert_element (NODENAME, START);
607 if (macro_expansion_output_stream && !executing_string)
608 me_execute_string (node);
610 execute_string ("%s", node);
611 xml_insert_element (NODENAME, END);
614 xml_node_id = xml_id (node);
616 else if (!no_headers && !html)
618 add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename);
620 if (macro_expansion_output_stream && !executing_string)
621 me_execute_string (node);
623 execute_string ("%s", node);
624 filling_enabled = indented_fill = 0;
627 /* Check for defaulting of this node's next, prev, and up fields. */
628 defaulting = (*next == 0 && *prev == 0 && *up == 0);
630 this_section = what_section (input_text + input_text_offset);
632 /* If we are defaulting, then look at the immediately following
633 sectioning command (error if none) to determine the node's
634 level. Find the node that contains the menu mentioning this node
635 that is one level up (error if not found). That node is the "Up"
636 of this node. Default the "Next" and "Prev" from the menu. */
639 NODE_REF *last_ref = NULL;
640 NODE_REF *ref = node_references;
642 if (this_section < 0 && !STREQ (node, "Top"))
644 char *polite_section_name = "top";
647 for (i = 0; section_alist[i].name; i++)
648 if (section_alist[i].level == current_section + 1)
650 polite_section_name = section_alist[i].name;
655 (_("Node `%s' requires a sectioning command (e.g. %c%s)"),
656 node, COMMAND_PREFIX, polite_section_name);
660 if (strcmp (node, "Top") == 0)
662 /* Default the NEXT pointer to be the first menu item in
663 this node, if there is a menu in this node. We have to
664 try very hard to find the menu, as it may be obscured
665 by execution_strings which are on the filestack. For
666 every member of the filestack which has a FILENAME
667 member which is identical to the current INPUT_FILENAME,
668 search forward from that offset. */
669 int saved_input_text_offset = input_text_offset;
670 int saved_input_text_length = input_text_length;
671 char *saved_input_text = input_text;
672 FSTACK *next_file = filestack;
674 int orig_offset, orig_size;
676 /* No matter what, make this file point back at `(dir)'. */
678 up = xstrdup ("(dir)"); /* html fixxme */
682 orig_offset = input_text_offset;
684 search_forward (node_search_string, orig_offset);
687 orig_size = input_text_length;
689 input_text_offset = search_forward ("\n@menu", orig_offset);
690 if (input_text_offset > -1
691 && cr_or_whitespace (input_text[input_text_offset + 6]))
693 char *nodename_from_menu = NULL;
696 search_forward ("\n* ", input_text_offset);
698 if (input_text_offset != -1)
699 nodename_from_menu = glean_node_from_menu (0, 0);
701 if (nodename_from_menu)
704 next = nodename_from_menu;
709 /* We got here, so it hasn't been found yet. Try
710 the next file on the filestack if there is one. */
712 && FILENAME_CMP (next_file->filename, input_filename)
715 input_text = next_file->text;
716 input_text_offset = next_file->offset;
717 input_text_length = next_file->size;
718 next_file = next_file->next;
721 { /* No more input files to check. */
726 input_text = saved_input_text;
727 input_text_offset = saved_input_text_offset;
728 input_text_length = saved_input_text_length;
732 /* Fix the level of the menu references in the Top node, iff it
733 was declared with @top, and no subsequent reference was found. */
734 if (top_node_seen && !non_top_node_seen)
736 /* Then this is the first non-@top node seen. */
739 level = set_top_section_level (this_section - 1);
740 non_top_node_seen = 1;
744 if (ref->section == level)
745 ref->section = this_section - 1;
749 ref = node_references;
754 if (ref->section == (this_section - 1)
755 && ref->type == menu_reference
756 && strcmp (ref->node, node) == 0)
758 char *containing_node = ref->containing_node;
761 up = xstrdup (containing_node);
764 && last_ref->type == menu_reference
765 && strcmp (last_ref->containing_node, containing_node) == 0)
768 next = xstrdup (last_ref->node);
771 while (ref->section == this_section - 1
773 && ref->next->type != menu_reference)
776 if (ref->next && ref->type == menu_reference
777 && strcmp (ref->next->containing_node, containing_node) == 0)
780 prev = xstrdup (ref->next->node);
783 && strcasecmp (ref->containing_node, "Top") == 0)
786 prev = xstrdup (ref->containing_node);
795 /* Insert the correct args if we are expanding macros, and the node's
796 pointers weren't defaulted. */
797 if (macro_expansion_output_stream && !executing_string && !defaulting)
800 int op_orig = output_paragraph_offset;
801 int meta_pos_orig = meta_char_pos;
802 int extra = html ? strlen (node) : 0;
804 temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up));
805 sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up);
806 me_execute_string (temp);
809 output_paragraph_offset = op_orig;
810 meta_char_pos = meta_pos_orig;
815 line_error (_("No node name specified for `%c%s' command"),
816 COMMAND_PREFIX, command);
818 free (next); next = NULL;
819 free (prev); prev= NULL;
820 free (up); up = NULL;
821 node_number++; /* else it doesn't get bumped */
825 if (!*next) { free (next); next = NULL; }
826 if (!*prev) { free (prev); prev = NULL; }
827 if (!*up) { free (up); up = NULL; }
828 remember_node (node, prev, next, up, new_node_pos, line_number,
829 fname_for_this_node, no_warn);
830 outstanding_node = 1;
835 if (splitting && *node && output_stream == NULL)
838 char filename[PATH_MAX];
840 dirname = pathname_part (current_output_filename);
841 strcpy (filename, dirname);
842 strcat (filename, fname_for_this_node);
845 /* See if the node name converted to a file name clashes
846 with other nodes or anchors. If it clashes with an
847 anchor, we complain and nuke that anchor's file. */
850 output_stream = fopen (filename, "w");
851 html_output_head_p = 0; /* so that we generate HTML preamble */
854 else if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
856 line_error (_("Anchor `%s' and node `%s' map to the same file name"),
858 file_line_error (tag->filename, tag->line_no,
859 _("This @anchor command ignored; references to it will not work"));
860 file_line_error (tag->filename, tag->line_no,
861 _("Rename this anchor or use the `--no-split' option"));
862 /* Nuke the file name recorded in anchor's tag.
863 Since we are about to nuke the file itself, we
864 don't want find_node_by_fname to consider this
866 free (tag->html_fname);
867 tag->html_fname = NULL;
868 output_stream = fopen (filename, "w");
869 html_output_head_p = 0; /* so that we generate HTML preamble */
874 /* This node's file name clashes with another node.
875 We put them both on the same file. */
876 output_stream = fopen (filename, "r+");
879 static char html_end[] = "</body></html>\n";
880 char end_line[sizeof(html_end)];
881 int fpos = fseek (output_stream, -epilogue_len,
885 || fgets (end_line, sizeof (html_end),
886 output_stream) == NULL
887 /* Paranoia: did someone change the way HTML
888 files are finished up? */
889 || strcasecmp (end_line, html_end) != 0)
891 line_error (_("Unexpected string at end of split-HTML file `%s'"),
892 fname_for_this_node);
893 fclose (output_stream);
896 fseek (output_stream, -epilogue_len, SEEK_END);
899 if (output_stream == NULL)
904 set_current_output_filename (filename);
907 if (!splitting && no_headers)
908 { /* cross refs need a name="#anchor" even if we're not writing headers*/
909 add_word ("<a name=\"");
910 tem = expand_node_name (node);
911 add_anchor_name (tem, 0);
912 add_word ("\"></a>");
916 if (splitting || !no_headers)
917 { /* Navigation bar. The <p> avoids the links area running
918 on with old Lynxen. */
919 add_word_args ("<p>%s\n", splitting ? "" : "<hr>");
920 add_word_args ("%s<a name=\"", _("Node:"));
921 tem = expand_node_name (node);
922 add_anchor_name (tem, 0);
923 add_word_args ("\">%s</a>", tem);
928 tem = expansion (next, 0);
930 add_word (_("Next:"));
931 add_word ("<a rel=next accesskey=n href=\"");
932 add_anchor_name (tem, 1);
933 add_word_args ("\">%s</a>", tem);
938 tem = expansion (prev, 0);
940 add_word (_("Previous:"));
941 add_word ("<a rel=previous accesskey=p href=\"");
942 add_anchor_name (tem, 1);
943 add_word_args ("\">%s</a>", tem);
948 tem = expansion (up, 0);
951 add_word ("<a rel=up accesskey=u href=\"");
952 add_anchor_name (tem, 1);
953 add_word_args ("\">%s</a>", tem);
956 /* html fixxme: we want a `top' or `contents' link here. */
958 add_word_args ("\n%s<br>\n", splitting ? "<hr>" : "");
967 xml_insert_element (NODENEXT, START);
968 execute_string ("%s", next);
969 xml_insert_element (NODENEXT, END);
973 xml_insert_element (NODEPREV, START);
974 execute_string ("%s", prev);
975 xml_insert_element (NODEPREV, END);
979 xml_insert_element (NODEUP, START);
980 execute_string ("%s", up);
981 xml_insert_element (NODEUP, END);
984 else if (!no_headers)
986 if (macro_expansion_output_stream)
987 me_inhibit_expansion++;
989 /* These strings are not translatable. */
992 execute_string (", Next: %s", next);
993 filling_enabled = indented_fill = 0;
997 execute_string (", Prev: %s", prev);
998 filling_enabled = indented_fill = 0;
1002 execute_string (", Up: %s", up);
1003 filling_enabled = indented_fill = 0;
1005 if (macro_expansion_output_stream)
1006 me_inhibit_expansion--;
1012 /* Change the section only if there was a sectioning command. */
1013 if (this_section >= 0)
1014 current_section = this_section;
1016 if (current_node && STREQ (current_node, "Top"))
1019 filling_enabled = 1;
1020 in_fixed_width_font--;
1023 /* Cross-reference target at an arbitrary spot. */
1029 char *fname_for_anchor = NULL;
1034 /* Parse the anchor text. */
1035 anchor = get_xref_token (1);
1037 /* In HTML mode, need to actually produce some output. */
1040 /* If this anchor is at the beginning of a new paragraph, make
1041 sure a new paragraph is indeed started. */
1042 if (!paragraph_is_open)
1044 if (!executing_string && html)
1045 html_output_head ();
1047 if (!in_fixed_width_font || in_menu || in_detailmenu)
1049 insert_string ("<p>");
1053 add_word ("<a name=\"");
1054 add_anchor_name (anchor, 0);
1055 add_word ("\"></a>");
1058 /* If we are splitting, cm_xref will produce a reference to
1059 a file whose name is derived from the anchor name. So we
1060 must create a file when we see an @anchor, otherwise
1061 xref's to anchors won't work. The file we create simply
1062 redirects to the file of this anchor's node. */
1065 fname_for_anchor = nodename_to_filename (anchor);
1066 /* See if the anchor name converted to a file name clashes
1067 with other anchors or nodes. */
1068 tag = find_node_by_fname (fname_for_anchor);
1071 if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
1072 line_error (_("Anchors `%s' and `%s' map to the same file name"),
1075 line_error (_("Anchor `%s' and node `%s' map to the same file name"),
1077 line_error (_("@anchor command ignored; references to it will not work"));
1078 line_error (_("Rename this anchor or use the `--no-split' option"));
1079 free (fname_for_anchor);
1080 /* We will not be creating a file for this anchor, so
1081 set its name to NULL, so that remember_node stores a
1082 NULL and find_node_by_fname won't consider this
1083 anchor for clashes. */
1084 fname_for_anchor = NULL;
1089 char filename[PATH_MAX];
1090 FILE *anchor_stream;
1092 dirname = pathname_part (current_output_filename);
1093 strcpy (filename, dirname);
1094 strcat (filename, fname_for_anchor);
1097 anchor_stream = fopen (filename, "w");
1098 if (anchor_stream == NULL)
1100 fs_error (filename);
1103 /* The HTML magic below will cause the browser to
1104 immediately go to the anchor's node's file. Lynx
1105 seems not to support this redirection, but it looks
1106 like a bug in Lynx, and they can work around it by
1107 clicking on the link once more. */
1108 fputs ("<meta http-equiv=\"refresh\" content=\"0; url=",
1110 /* Make the indirect link point to the current node's
1111 file and anchor's "<a name" label. If we don't have
1112 a valid node name, refer to the current output file
1114 if (current_node && *current_node)
1118 tem = expand_node_name (current_node);
1119 fn = nodename_to_filename (tem);
1121 fputs (fn, anchor_stream);
1126 char *base = filename_part (current_output_filename);
1128 fputs (base, anchor_stream);
1131 fputs ("#", anchor_stream);
1132 for (p = anchor; *p; p++)
1135 fputs ("&", anchor_stream);
1136 else if (!URL_SAFE_CHAR (*p))
1137 fprintf (anchor_stream, "%%%x", (unsigned char) *p);
1139 fputc (*p, anchor_stream);
1141 fputs ("\">\n", anchor_stream);
1142 fclose (anchor_stream);
1148 xml_insert_element_with_attribute (ANCHOR, START, "name=\"%s\"", anchor);
1149 xml_insert_element (ANCHOR, END);
1151 /* Save it in the tag table. */
1152 remember_node (anchor, NULL, NULL, NULL,
1153 output_position + output_paragraph_offset,
1154 line_number, fname_for_anchor, TAG_FLAG_ANCHOR);
1157 /* Find NODE in REF_LIST. */
1159 find_node_reference (node, ref_list)
1163 NODE_REF *orig_ref_list = ref_list;
1164 char *expanded_node;
1168 if (strcmp (node, ref_list->node) == 0)
1170 ref_list = ref_list->next;
1173 if (ref_list || !expensive_validation)
1176 /* Maybe NODE is not expanded yet. This may be SLOW. */
1177 expanded_node = expand_node_name (node);
1178 for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next)
1180 if (STREQ (expanded_node, ref_list->node))
1182 if (strchr (ref_list->node, COMMAND_PREFIX))
1184 char *expanded_ref = expand_node_name (ref_list->node);
1186 if (STREQ (expanded_node, expanded_ref))
1188 free (expanded_ref);
1191 free (expanded_ref);
1194 free (expanded_node);
1199 free_node_references ()
1201 NODE_REF *list, *temp;
1203 list = node_references;
1209 free (list->containing_node);
1213 node_references = NULL;
1217 free_node_node_references ()
1219 NODE_REF *list, *temp;
1221 list = node_references;
1230 node_node_references = NULL;
1233 /* Return the number assigned to a named node in either the tag_table
1234 or node_references list or zero if no number has been assigned. */
1236 number_of_node (node)
1240 TAG_ENTRY *temp_node = find_node (node);
1243 return temp_node->number;
1244 else if ((temp_ref = find_node_reference (node, node_references)))
1245 return temp_ref->number;
1246 else if ((temp_ref = find_node_reference (node, node_node_references)))
1247 return temp_ref->number;
1254 /* Return 1 if TAG (at LINE) correctly validated, or 0 if not.
1255 LABEL is the (translated) description of the type of reference --
1256 Menu, Cross, Next, etc. */
1259 validate (tag, line, label)
1266 /* If there isn't a tag to verify, or if the tag is in another file,
1267 then it must be okay. */
1268 if (!tag || !*tag || *tag == '(')
1271 /* Otherwise, the tag must exist. */
1272 result = find_node (tag);
1277 line_error (_("%s reference to nonexistent node `%s'"), label, tag);
1284 /* The strings here are followed in the message by `reference to...' in
1285 the `validate' routine. They are only used in messages, thus are
1288 reftype_type_string (type)
1293 case menu_reference:
1295 case followed_reference:
1298 return "Internal-bad-reference-type";
1303 validate_other_references (ref_list)
1306 char *old_input_filename = input_filename;
1310 input_filename = ref_list->filename;
1311 validate (ref_list->node, ref_list->line_no,
1312 reftype_type_string (ref_list->type));
1313 ref_list = ref_list->next;
1315 input_filename = old_input_filename;
1318 /* Validation of an info file.
1319 Scan through the list of tag entries touching the Prev, Next, and Up
1320 elements of each. It is an error not to be able to touch one of them,
1321 except in the case of external node references, such as "(DIR)".
1323 If the Prev is different from the Up,
1324 then the Prev node must have a Next pointing at this node.
1326 Every node except Top must have an Up.
1327 The Up node must contain some sort of reference, other than a Next,
1330 If the Next is different from the Next of the Up,
1331 then the Next node must have a Prev pointing at this node. */
1333 validate_file (tag_table)
1334 TAG_ENTRY *tag_table;
1336 char *old_input_filename = input_filename;
1337 TAG_ENTRY *tags = tag_table;
1341 TAG_ENTRY *temp_tag;
1344 input_filename = tags->filename;
1345 line_number = tags->line_no;
1347 /* If this is a "no warn" node, don't validate it in any way. */
1348 if (tags->flags & TAG_FLAG_NO_WARN)
1350 tags = tags->next_ent;
1354 /* If this node has a Next, then make sure that the Next exists. */
1357 validate (tags->next, tags->line_no, _("Next"));
1359 /* If the Next node exists, and there is no Up, then make sure
1360 that the Prev of the Next points back. But do nothing if
1361 we aren't supposed to issue warnings about this node. */
1362 temp_tag = find_node (tags->next);
1363 if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN))
1365 char *prev = temp_tag->prev;
1366 int you_lose = !prev || !STREQ (prev, tags->node);
1368 if (you_lose && expensive_validation)
1370 tem1 = expand_node_name (prev);
1371 tem2 = expand_node_name (tags->node);
1373 if (STREQ (tem1, tem2))
1380 line_error (_("Next field of node `%s' not pointed to"),
1382 file_line_error (temp_tag->filename, temp_tag->line_no,
1383 _("This node (%s) has the bad Prev"),
1385 temp_tag->flags |= TAG_FLAG_PREV_ERROR;
1390 /* Validate the Prev field if there is one, and we haven't already
1391 complained about it in some way. You don't have to have a Prev
1392 field at this stage. */
1393 if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev)
1395 int valid_p = validate (tags->prev, tags->line_no, _("Prev"));
1398 tags->flags |= TAG_FLAG_PREV_ERROR;
1400 { /* If the Prev field is not the same as the Up field,
1401 then the node pointed to by the Prev field must have
1402 a Next field which points to this node. */
1403 int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up);
1405 if (!prev_equals_up && expensive_validation)
1407 tem1 = expand_node_name (tags->prev);
1408 tem2 = expand_node_name (tags->up);
1409 prev_equals_up = STREQ (tem1, tem2);
1413 if (!prev_equals_up)
1415 temp_tag = find_node (tags->prev);
1417 /* If we aren't supposed to issue warnings about the
1418 target node, do nothing. */
1419 if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN))
1423 int you_lose = !temp_tag->next
1424 || !STREQ (temp_tag->next, tags->node);
1426 if (temp_tag->next && you_lose && expensive_validation)
1428 tem1 = expand_node_name (temp_tag->next);
1429 tem2 = expand_node_name (tags->node);
1430 if (STREQ (tem1, tem2))
1438 (_("Prev field of node `%s' not pointed to"),
1440 file_line_error (temp_tag->filename,
1442 _("This node (%s) has the bad Next"),
1444 temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
1452 && !(tags->flags & TAG_FLAG_ANCHOR)
1453 && strcasecmp (tags->node, "Top") != 0)
1454 line_error (_("`%s' has no Up field"), tags->node);
1457 int valid_p = validate (tags->up, tags->line_no, _("Up"));
1459 /* If node X has Up: Y, then warn if Y fails to have a menu item
1460 or note pointing at X, if Y isn't of the form "(Y)". */
1461 if (valid_p && *tags->up != '(')
1464 NODE_REF *tref = NULL;
1465 NODE_REF *list = node_references;
1469 nref = find_node_reference (tags->node, list);
1473 if (strcmp (nref->containing_node, tags->up) == 0)
1475 if (nref->type != menu_reference)
1488 if (!tref && expensive_validation)
1490 /* Sigh... This might be AWFULLY slow, but if
1491 they want this feature, they'll have to pay!
1492 We do all the loop again expanding each
1493 containing_node reference as we go. */
1494 char *tags_up = expand_node_name (tags->up);
1497 list = node_references;
1501 nref = find_node_reference (tags->node, list);
1504 tem = expand_node_name (nref->containing_node);
1505 if (STREQ (tem, tags_up))
1507 if (nref->type != menu_reference)
1521 temp_tag = find_node (tags->up);
1522 file_line_error (temp_tag->filename, temp_tag->line_no,
1523 _("Node `%s' lacks menu item for `%s' despite being its Up target"),
1524 tags->up, tags->node);
1529 tags = tags->next_ent;
1532 validate_other_references (node_references);
1533 /* We have told the user about the references which didn't exist.
1534 Now tell him about the nodes which aren't referenced. */
1536 for (tags = tag_table; tags; tags = tags->next_ent)
1538 /* If this node is a "no warn" node, do nothing. */
1539 if (tags->flags & TAG_FLAG_NO_WARN)
1541 tags = tags->next_ent;
1545 /* Special hack. If the node in question appears to have
1546 been referenced more than REFERENCE_WARNING_LIMIT times,
1548 if (tags->touched > reference_warning_limit)
1550 input_filename = tags->filename;
1551 line_number = tags->line_no;
1552 warning (_("node `%s' has been referenced %d times"),
1553 tags->node, tags->touched);
1556 if (tags->touched == 0)
1558 input_filename = tags->filename;
1559 line_number = tags->line_no;
1561 /* Notice that the node "Top" is special, and doesn't have to
1562 be referenced. Anchors don't have to be referenced
1563 either, you might define them for another document. */
1564 if (strcasecmp (tags->node, "Top") != 0
1565 && !(tags->flags & TAG_FLAG_ANCHOR))
1566 warning (_("unreferenced node `%s'"), tags->node);
1569 input_filename = old_input_filename;
1575 /* Return true if the tag entry pointed to by TAGS is the last node.
1576 This means only anchors follow. */
1583 while (tags->next_ent) {
1584 tags = tags->next_ent;
1585 if (tags->flags & TAG_FLAG_ANCHOR)
1598 /* Split large output files into a series of smaller files. Each file
1599 is pointed to in the tag table, which then gets written out as the
1600 original file. The new files have the same name as the original file
1601 with a "-num" attached. SIZE is the largest number of bytes to allow
1602 in any single split file. */
1604 split_file (filename, size)
1608 char *root_filename, *root_pathname;
1609 char *the_file, *filename_part ();
1610 struct stat fileinfo;
1614 int dos_file_names = 0; /* if nonzero, don't exceed 8+3 limits */
1616 /* Can only do this to files with tag tables. */
1621 size = DEFAULT_SPLIT_SIZE;
1623 if ((stat (filename, &fileinfo) != 0) ||
1624 (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
1626 file_size = (long) fileinfo.st_size;
1628 the_file = find_and_load (filename);
1632 root_filename = filename_part (filename);
1633 root_pathname = pathname_part (filename);
1635 /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */
1636 dos_file_names = !HAVE_LONG_FILENAMES (root_pathname ? root_pathname : ".");
1639 root_pathname = xstrdup ("");
1641 /* Start splitting the file. Walk along the tag table
1642 outputting sections of the file. When we have written
1643 all of the nodes in the tag table, make the top-level
1644 pointer file, which contains indirect pointers and
1645 tags for the nodes. */
1648 TAG_ENTRY *tags = tag_table;
1649 char *indirect_info = NULL;
1651 /* Remember the `header' of this file. The first tag in the file is
1652 the bottom of the header; the top of the file is the start. */
1653 the_header = xmalloc (1 + (header_size = tags->position));
1654 memcpy (the_header, the_file, header_size);
1658 int file_top, file_bot, limit;
1660 /* Have to include the Control-_. */
1661 file_top = file_bot = tags->position;
1662 limit = file_top + size;
1664 /* If the rest of this file is only one node, then
1665 that is the entire subfile. */
1666 if (last_node_p (tags))
1668 int i = tags->position + 1;
1669 char last_char = the_file[i];
1671 while (i < file_size)
1673 if ((the_file[i] == '\037') &&
1674 ((last_char == '\n') ||
1675 (last_char == '\014')))
1678 last_char = the_file[i];
1682 tags = tags->next_ent;
1686 /* Otherwise, find the largest number of nodes that can fit in
1688 for (; tags; tags = tags->next_ent)
1690 if (last_node_p (tags))
1692 /* This entry is the last node. Search forward for the end
1693 of this node, and that is the end of this file. */
1694 int i = tags->position + 1;
1695 char last_char = the_file[i];
1697 while (i < file_size)
1699 if ((the_file[i] == '\037') &&
1700 ((last_char == '\n') ||
1701 (last_char == '\014')))
1704 last_char = the_file[i];
1709 if (file_bot < limit)
1711 tags = tags->next_ent;
1716 /* Here we want to write out everything before the last
1717 node, and then write the last node out in a file
1719 file_bot = tags->position;
1724 /* Write region only if this was a node, not an anchor. */
1725 if (tags->next_ent->position > limit
1726 && !(tags->flags & TAG_FLAG_ANCHOR))
1728 if (tags->position == file_top)
1729 tags = tags->next_ent;
1731 file_bot = tags->position;
1736 char *split_filename, *split_basename;
1737 unsigned root_len = strlen (root_filename);
1739 split_filename = xmalloc (10 + strlen (root_pathname)
1741 split_basename = xmalloc (10 + root_len);
1742 sprintf (split_basename, "%s-%d", root_filename, which_file);
1745 char *dot = strchr (split_basename, '.');
1746 unsigned base_len = strlen (split_basename);
1749 { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
1751 memmove (which_file <= 99 ? dot + 2 : dot + 1,
1752 split_basename + root_len + 1,
1753 strlen (split_basename + root_len + 1) + 1);
1755 else if (base_len > 8)
1757 /* Make foobar-1, .., fooba-10, .., foob-100, ... */
1758 unsigned numlen = base_len - root_len;
1760 memmove (split_basename + 8 - numlen,
1761 split_basename + root_len, numlen + 1);
1764 sprintf (split_filename, "%s%s", root_pathname,
1767 fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
1769 || write (fd, the_header, header_size) != header_size
1770 || write (fd, the_file + file_top, file_bot - file_top)
1771 != (file_bot - file_top)
1772 || (close (fd)) < 0)
1774 perror (split_filename);
1782 indirect_info = the_file + file_top;
1783 sprintf (indirect_info, "\037\nIndirect:\n");
1784 indirect_info += strlen (indirect_info);
1787 sprintf (indirect_info, "%s: %d\n",
1788 split_basename, file_top);
1790 free (split_basename);
1791 free (split_filename);
1792 indirect_info += strlen (indirect_info);
1800 /* We have sucessfully created the subfiles. Now write out the
1801 original again. We must use `output_stream', or
1802 write_tag_table_indirect () won't know where to place the output. */
1803 output_stream = fopen (filename, "w");
1811 int distance = indirect_info - the_file;
1812 fwrite (the_file, 1, distance, output_stream);
1814 /* Inhibit newlines. */
1815 paragraph_is_open = 0;
1817 write_tag_table_indirect ();
1818 fclose (output_stream);