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