Merge from vendor branch BIND:
[dragonfly.git] / contrib / texinfo / makeinfo / node.c
1 /* node.c -- nodes for Texinfo.
2    $Id: node.c,v 1.34 2002/03/26 16:16:29 karl Exp $
3
4    Copyright (C) 1998, 99, 2000, 01, 02 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 "html.h"
28 #include "sectioning.h"
29 #include "insertion.h"
30 #include "xml.h"
31
32
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;
37 int node_number = -1;
38 int current_section = 0;
39 int outstanding_node = 0;
40 \f
41 /* Adding nodes, and making tags.  */
42
43 /* Start a new tag table. */
44 void
45 init_tag_table ()
46 {
47   while (tag_table)
48     {
49       TAG_ENTRY *temp = tag_table;
50       free (temp->node);
51       free (temp->prev);
52       free (temp->next);
53       free (temp->up);
54       tag_table = tag_table->next_ent;
55       free (temp);
56     }
57 }
58 \f
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).  */
62 static void
63 write_tag_table_internal (indirect_p)
64      int indirect_p;
65 {
66   TAG_ENTRY *node;
67   int old_indent = no_indent;
68
69   if (xml)
70     {
71       flush_output ();
72       return;
73     }
74
75   no_indent = 1;
76   filling_enabled = 0;
77   must_start_paragraph = 0;
78   close_paragraph ();
79
80   if (!indirect_p)
81     {
82       no_indent = 1;
83       insert ('\n');
84     }
85
86   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
87
88   /* Do not collapse -- to -, etc., in node names.  */
89   in_fixed_width_font++;
90
91   for (node = tag_table; node; node = node->next_ent)
92     {
93       if (node->flags & TAG_FLAG_ANCHOR)
94         { /* This reference is to an anchor.  */
95           execute_string ("Ref: %s", node->node);
96         }
97       else
98         { /* This reference is to a node.  */
99           execute_string ("Node: %s", node->node);
100         }
101       add_word_args ("\177%d\n", node->position);
102     }
103
104   add_word ("\037\nEnd Tag Table\n");
105
106   /* Do not collapse -- to -, etc., in node names.  */
107   in_fixed_width_font--;
108
109   flush_output ();
110   no_indent = old_indent;
111 }
112
113 void
114 write_tag_table ()
115 {
116   write_tag_table_internal (0); /* Not indirect. */
117 }
118
119 void
120 write_tag_table_indirect ()
121 {
122   write_tag_table_internal (1);
123 }
124 \f
125 /* Convert "top" and friends into "Top". */
126 static void
127 normalize_node_name (string)
128      char *string;
129 {
130   if (strcasecmp (string, "Top") == 0)
131     strcpy (string, "Top");
132 }
133
134 char *
135 get_node_token (expand)
136       int expand;
137 {
138   char *string;
139
140   get_until_in_line (expand, ",", &string);
141
142   if (curchar () == ',')
143     input_text_offset++;
144
145   fix_whitespace (string);
146
147   /* Force all versions of "top" to be "Top". */
148   normalize_node_name (string);
149
150   return string;
151 }
152
153 /* Expand any macros and other directives in a node name, and
154    return the expanded name as an malloc'ed string.  */
155 char *
156 expand_node_name (node)
157      char *node;
158 {
159   char *result = node;
160
161   if (node)
162     {
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);
170     }
171   return result;
172 }
173
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. */
176 TAG_ENTRY *
177 find_node (name)
178      char *name;
179 {
180   TAG_ENTRY *tag = tag_table;
181   char *expanded_name;
182   char n1 = name[0];
183
184   while (tag)
185     {
186       if (tag->node[0] == n1 && strcmp (tag->node, name) == 0)
187         return tag;
188       tag = tag->next_ent;
189     }
190
191   if (!expensive_validation)
192     return NULL;
193
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)
201     {
202       if (STREQ (tag->node, expanded_name))
203         break;
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))
207         {
208           char *expanded_node = expand_node_name (tag->node);
209
210           if (STREQ (expanded_node, expanded_name))
211             {
212               free (expanded_node);
213               break;
214             }
215           free (expanded_node);
216         }
217     }
218   free (expanded_name);
219   return tag;
220 }
221
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. */
225 TAG_ENTRY *
226 find_node_by_fname (fname)
227      char *fname;
228 {
229   TAG_ENTRY *tag = tag_table;
230   while (tag)
231     {
232       if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0)
233         return tag;
234       tag = tag->next_ent;
235     }
236
237   return tag;
238 }
239
240 /* Remember next, prev, etc. references in a @node command, where we
241    don't care about most of the entries. */
242 static void
243 remember_node_node_reference (node)
244      char *node;
245 {
246   NODE_REF *temp = xmalloc (sizeof (NODE_REF));
247   int number;
248
249   if (!node) return;
250   temp->next = node_node_references;
251   temp->node = xstrdup (node);
252   temp->type = followed_reference;
253   number = number_of_node (node);
254   if (number)
255     temp->number = number;      /* Already assigned. */
256   else
257     {
258       node_number++;
259       temp->number = node_number;
260     }
261   node_node_references = temp;
262 }
263
264 /* Remember NODE and associates. */
265 void
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;
269 {
270   /* Check for existence of this tag already. */
271   if (validating)
272     {
273       TAG_ENTRY *tag = find_node (node);
274       if (tag)
275         {
276           line_error (_("Node `%s' previously defined at line %d"),
277                       node, tag->line_no);
278           return;
279         }
280     }
281
282   if (!(flags & TAG_FLAG_ANCHOR))
283     {
284       /* Make this the current node. */
285       current_node = node;
286     }
287
288   /* Add it to the list. */
289   {
290     int number = number_of_node (node);
291
292     TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY));
293     new->node = node;
294     new->prev = prev;
295     new->next = next;
296     new->up = up;
297     new->position = position;
298     new->line_no = line_no;
299     new->filename = node_filename;
300     new->touched = 0;
301     new->flags = flags;
302     if (number)
303       new->number = number;     /* Already assigned. */
304     else
305       {
306         node_number++;
307         new->number = node_number;
308       }
309     new->html_fname = fname;
310     new->next_ent = tag_table;
311     tag_table = new;
312   }
313
314   if (html)
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);
319     }
320 }
321
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.  */
326 void
327 remember_node_reference (node, line, type)
328      char *node;
329      int line;
330      enum reftype type;
331 {
332   NODE_REF *temp = xmalloc (sizeof (NODE_REF));
333   int number = number_of_node (node);
334
335   temp->next = node_references;
336   temp->node = xstrdup (node);
337   temp->line_no = line;
338   temp->section = current_section;
339   temp->type = type;
340   temp->containing_node = xstrdup (current_node ? current_node : "");
341   temp->filename = node_filename;
342   if (number)
343     temp->number = number;      /* Already assigned. */
344   else
345     {
346       node_number++;
347       temp->number = node_number;
348     }
349
350   node_references = temp;
351 }
352
353 static void
354 isolate_nodename (nodename)
355      char *nodename;
356 {
357   int i, c;
358   int paren_seen, paren;
359
360   if (!nodename)
361     return;
362
363   canon_white (nodename);
364   paren_seen = paren = i = 0;
365
366   if (*nodename == '.' || !*nodename)
367     {
368       *nodename = 0;
369       return;
370     }
371
372   if (*nodename == '(')
373     {
374       paren++;
375       paren_seen++;
376       i++;
377     }
378
379   for (; (c = nodename[i]); i++)
380     {
381       if (paren)
382         {
383           if (c == '(')
384             paren++;
385           else if (c == ')')
386             paren--;
387
388           continue;
389         }
390
391       /* If the character following the close paren is a space, then this
392          node has no more characters associated with it. */
393       if (c == '\t' ||
394           c == '\n' ||
395           c == ','  ||
396           ((paren_seen && nodename[i - 1] == ')') &&
397            (c == ' ' || c == '.')) ||
398           (c == '.' &&
399            ((!nodename[i + 1] ||
400              (cr_or_whitespace (nodename[i + 1])) ||
401              (nodename[i + 1] == ')')))))
402         break;
403     }
404   nodename[i] = 0;
405 }
406
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 "* "
413 char *
414 glean_node_from_menu (remember_ref, ref_type)
415      int remember_ref;
416      enum reftype ref_type;
417 {
418   int i, orig_offset = input_text_offset;
419   char *nodename;
420   char *line, *expanded_line;
421   char *old_input = input_text;
422   int old_size = input_text_length;
423
424   if (strncmp (&input_text[input_text_offset + 1],
425                MENU_STARTER,
426                strlen (MENU_STARTER)) != 0)
427     return NULL;
428   else
429     input_text_offset += strlen (MENU_STARTER) + 1;
430
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--;
436   free (line);
437   input_text = expanded_line;
438   input_text_offset = 0;
439   input_text_length = strlen (expanded_line);
440
441   get_until_in_line (0, ":", &nodename);
442   if (curchar () == ':')
443     input_text_offset++;
444
445   if (curchar () != ':')
446     {
447       free (nodename);
448       get_until_in_line (0, "\n", &nodename);
449       isolate_nodename (nodename);
450     }
451
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] == ':')
460     nodename[i - 1] = 0;
461
462   if (remember_ref)
463     remember_node_reference (nodename, line_number, ref_type);
464
465   return nodename;
466 }
467
468 /* Set the name of the current output file.  */
469 void
470 set_current_output_filename (fname)
471      const char *fname;
472 {
473   if (current_output_filename)
474     free (current_output_filename);
475   current_output_filename = xstrdup (fname);
476 }
477
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. */
484 void
485 cm_node ()
486 {
487   static long epilogue_len = 0L;
488   char *node, *prev, *next, *up;
489   int new_node_pos, defaulting, this_section;
490   int no_warn = 0;
491   char *fname_for_this_node = NULL;
492   char *tem;
493   TAG_ENTRY *tag = NULL;
494
495   if (strcmp (command, "nwnode") == 0)
496     no_warn = TAG_FLAG_NO_WARN;
497
498   /* Get rid of unmatched brace arguments from previous commands. */
499   discard_braces ();
500
501   /* There also might be insertions left lying around that haven't been
502      ended yet.  Do that also. */
503   discard_insertions (1);
504
505   if (!html && !already_outputting_pending_notes)
506     {
507       if (!xml)
508       close_paragraph ();
509       output_pending_notes ();
510     }
511
512   new_node_pos = output_position;
513
514   if (macro_expansion_output_stream && !executing_string)
515     append_to_expansion_output (input_text_offset + 1);
516
517   /* Do not collapse -- to -, etc., in node names.  */
518   in_fixed_width_font++;
519
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);
528
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))
533     {
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)
540         {
541           char *fname_for_prev_node;
542
543           if (current_node)
544             {
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);
549               free (tem);
550             }
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);
555           free (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)
560             {
561               long pos1 = 0;
562
563               /* End the current split output file. */
564               close_paragraph ();
565               output_pending_notes ();
566               start_paragraph ();
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)
573                 {
574                   flush_output ();
575                   pos1 = ftell (output_stream);
576                 }
577               add_word ("</body></html>\n");
578               close_paragraph ();
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);
584             }
585           free (fname_for_prev_node);
586         }
587     }
588
589   filling_enabled = indented_fill = 0;
590   if (!html || (html && splitting))
591     current_footnote_number = 1;
592   
593   if (verbose_mode)
594     printf (_("Formatting node %s...\n"), node);
595
596   if (macro_expansion_output_stream && !executing_string)
597     remember_itext (input_text, input_text_offset);
598
599   no_indent = 1;
600   if (xml)
601     {
602       xml_begin_document (current_output_filename);
603       xml_begin_node ();
604       if (!docbook)
605         {
606           xml_insert_element (NODENAME, START);      
607           if (macro_expansion_output_stream && !executing_string)
608             me_execute_string (node);
609           else
610             execute_string ("%s", node);
611           xml_insert_element (NODENAME, END);
612         }
613       else
614         xml_node_id = xml_id (node);
615     }
616   else if (!no_headers && !html)
617     {
618       add_word_args ("\037\nFile: %s,  Node: ", pretty_output_filename);
619
620       if (macro_expansion_output_stream && !executing_string)
621         me_execute_string (node);
622       else
623         execute_string ("%s", node);
624       filling_enabled = indented_fill = 0;
625     }
626
627   /* Check for defaulting of this node's next, prev, and up fields. */
628   defaulting = (*next == 0 && *prev == 0 && *up == 0);
629
630   this_section = what_section (input_text + input_text_offset);
631
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. */
637   if (defaulting)
638     {
639       NODE_REF *last_ref = NULL;
640       NODE_REF *ref = node_references;
641
642       if (this_section < 0 && !STREQ (node, "Top"))
643         {
644           char *polite_section_name = "top";
645           int i;
646
647           for (i = 0; section_alist[i].name; i++)
648             if (section_alist[i].level == current_section + 1)
649               {
650                 polite_section_name = section_alist[i].name;
651                 break;
652               }
653
654           line_error
655             (_("Node `%s' requires a sectioning command (e.g. %c%s)"),
656              node, COMMAND_PREFIX, polite_section_name);
657         }
658       else
659         {
660           if (strcmp (node, "Top") == 0)
661             {
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;
673
674               int orig_offset, orig_size;
675
676               /* No matter what, make this file point back at `(dir)'. */
677               free (up);
678               up = xstrdup ("(dir)"); /* html fixxme */
679
680               while (1)
681                 {
682                   orig_offset = input_text_offset;
683                   orig_size =
684                     search_forward (node_search_string, orig_offset);
685
686                   if (orig_size < 0)
687                     orig_size = input_text_length;
688
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]))
692                     {
693                       char *nodename_from_menu = NULL;
694
695                       input_text_offset =
696                         search_forward ("\n* ", input_text_offset);
697
698                       if (input_text_offset != -1)
699                         nodename_from_menu = glean_node_from_menu (0, 0);
700
701                       if (nodename_from_menu)
702                         {
703                           free (next);
704                           next = nodename_from_menu;
705                           break;
706                         }
707                     }
708
709                   /* We got here, so it hasn't been found yet.  Try
710                      the next file on the filestack if there is one. */
711                   if (next_file
712                       && FILENAME_CMP (next_file->filename, input_filename)
713                           == 0)
714                     {
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;
719                     }
720                   else
721                     { /* No more input files to check. */
722                       break;
723                     }
724                 }
725
726               input_text = saved_input_text;
727               input_text_offset = saved_input_text_offset;
728               input_text_length = saved_input_text_length;
729             }
730         }
731
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)
735         {
736           /* Then this is the first non-@top node seen. */
737           int level;
738
739           level = set_top_section_level (this_section - 1);
740           non_top_node_seen = 1;
741
742           while (ref)
743             {
744               if (ref->section == level)
745                 ref->section = this_section - 1;
746               ref = ref->next;
747             }
748
749           ref = node_references;
750         }
751
752       while (ref)
753         {
754           if (ref->section == (this_section - 1)
755               && ref->type == menu_reference
756               && strcmp (ref->node, node) == 0)
757             {
758               char *containing_node = ref->containing_node;
759
760               free (up);
761               up = xstrdup (containing_node);
762
763               if (last_ref
764                   && last_ref->type == menu_reference
765                   && strcmp (last_ref->containing_node, containing_node) == 0)
766                 {
767                   free (next);
768                   next = xstrdup (last_ref->node);
769                 }
770
771               while (ref->section == this_section - 1
772                      && ref->next
773                      && ref->next->type != menu_reference)
774                 ref = ref->next;
775
776               if (ref->next && ref->type == menu_reference
777                   && strcmp (ref->next->containing_node, containing_node) == 0)
778                 {
779                   free (prev);
780                   prev = xstrdup (ref->next->node);
781                 }
782               else if (!ref->next
783                        && strcasecmp (ref->containing_node, "Top") == 0)
784                 {
785                   free (prev);
786                   prev = xstrdup (ref->containing_node);
787                 }
788               break;
789             }
790           last_ref = ref;
791           ref = ref->next;
792         }
793     }
794
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)
798     {
799       char *temp;
800       int op_orig = output_paragraph_offset;
801       int meta_pos_orig = meta_char_pos;
802       int extra = html ? strlen (node) : 0;
803
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);
807       free (temp);
808
809       output_paragraph_offset = op_orig;
810       meta_char_pos = meta_pos_orig;
811     }
812
813   if (!*node)
814     {
815       line_error (_("No node name specified for `%c%s' command"),
816                   COMMAND_PREFIX, command);
817       free (node);
818       free (next); next = NULL;
819       free (prev); prev= NULL;
820       free (up);   up = NULL;
821       node_number++;            /* else it doesn't get bumped */
822     }
823   else
824     {
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;
831     }
832
833   if (html)
834     {
835       if (splitting && *node && output_stream == NULL)
836         {
837           char *dirname;
838           char filename[PATH_MAX];
839
840           dirname = pathname_part (current_output_filename);
841           strcpy (filename, dirname);
842           strcat (filename, fname_for_this_node);
843           free (dirname);
844
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.  */
848           if (!tag)
849             {
850               output_stream = fopen (filename, "w");
851               html_output_head_p = 0; /* so that we generate HTML preamble */
852               html_output_head ();
853             }
854           else if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
855             {
856               line_error (_("Anchor `%s' and node `%s' map to the same file name"),
857                           tag->node, node);
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
865                  anchor anymore.  */
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 */
870               html_output_head ();
871             }
872           else
873             {
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+");
877               if (output_stream)
878                 {
879                   static char html_end[] = "</body></html>\n";
880                   char end_line[sizeof(html_end)];
881                   int fpos = fseek (output_stream, -epilogue_len,
882                                     SEEK_END);
883
884                   if (fpos < 0
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)
890                     {
891                       line_error (_("Unexpected string at end of split-HTML file `%s'"),
892                                   fname_for_this_node);
893                       fclose (output_stream);
894                       xexit (1);
895                     }
896                   fseek (output_stream, -epilogue_len, SEEK_END);
897                 }
898             }
899           if (output_stream == NULL)
900             {
901               fs_error (filename);
902               xexit (1);
903             }
904           set_current_output_filename (filename);
905         }
906
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>");
913           free (tem);
914         }
915
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);
924           free (tem);
925
926           if (next)
927             {
928               tem = expansion (next, 0);
929               add_word (",\n");
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);
934               free (tem);
935             }
936           if (prev)
937             {
938               tem = expansion (prev, 0);
939               add_word (",\n");
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);
944               free (tem);
945             }
946           if (up)
947             {
948               tem = expansion (up, 0);
949               add_word (",\n");
950               add_word (_("Up:"));
951               add_word ("<a rel=up accesskey=u href=\"");
952               add_anchor_name (tem, 1);
953               add_word_args ("\">%s</a>", tem);
954               free (tem);
955             }
956           /* html fixxme: we want a `top' or `contents' link here.  */
957
958           add_word_args ("\n%s<br>\n", splitting ? "<hr>" : "");
959         }
960     }
961   else if (docbook)
962     ;
963   else if (xml)
964     {
965       if (next)
966         {
967           xml_insert_element (NODENEXT, START);
968           execute_string ("%s", next);
969           xml_insert_element (NODENEXT, END);
970         }
971       if (prev)
972         {
973           xml_insert_element (NODEPREV, START);
974           execute_string ("%s", prev);      
975           xml_insert_element (NODEPREV, END);
976         }
977       if (up)
978         {
979           xml_insert_element (NODEUP, START);
980           execute_string ("%s", up);
981           xml_insert_element (NODEUP, END);
982         }
983     }
984   else if (!no_headers)
985     {
986       if (macro_expansion_output_stream)
987         me_inhibit_expansion++;
988
989       /* These strings are not translatable.  */
990       if (next)
991         {
992           execute_string (",  Next: %s", next);
993           filling_enabled = indented_fill = 0;
994         }
995       if (prev)
996         {
997           execute_string (",  Prev: %s", prev);
998           filling_enabled = indented_fill = 0;
999         }
1000       if (up)
1001         {
1002           execute_string (",  Up: %s", up);
1003           filling_enabled = indented_fill = 0;
1004         }
1005       if (macro_expansion_output_stream)
1006         me_inhibit_expansion--;
1007     }
1008
1009   close_paragraph ();
1010   no_indent = 0;
1011
1012   /* Change the section only if there was a sectioning command. */
1013   if (this_section >= 0)
1014     current_section = this_section;
1015
1016   if (current_node && STREQ (current_node, "Top"))
1017     top_node_seen = 1;
1018
1019   filling_enabled = 1;
1020   in_fixed_width_font--;
1021 }
1022
1023 /* Cross-reference target at an arbitrary spot.  */
1024 void
1025 cm_anchor (arg)
1026      int arg;
1027 {
1028   char *anchor;
1029   char *fname_for_anchor = NULL;
1030
1031   if (arg == END)
1032     return;
1033
1034   /* Parse the anchor text.  */
1035   anchor = get_xref_token (1);
1036
1037   /* In HTML mode, need to actually produce some output.  */
1038   if (html)
1039     {
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)
1043         {
1044           if (!executing_string && html)
1045             html_output_head ();
1046           start_paragraph ();
1047           if (!in_fixed_width_font || in_menu || in_detailmenu)
1048             {
1049               insert_string ("<p>");
1050               in_paragraph = 1;
1051             }
1052         }
1053       add_word ("<a name=\"");
1054       add_anchor_name (anchor, 0);
1055       add_word ("\"></a>");
1056       if (splitting)
1057         {
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.  */
1063           TAG_ENTRY *tag;
1064
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);
1069           if (tag)
1070             {
1071               if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
1072                 line_error (_("Anchors `%s' and `%s' map to the same file name"),
1073                             anchor, tag->node);
1074               else
1075                 line_error (_("Anchor `%s' and node `%s' map to the same file name"),
1076                             anchor, tag->node);
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;
1085             }
1086           else
1087             {
1088               char *dirname, *p;
1089               char filename[PATH_MAX];
1090               FILE *anchor_stream;
1091
1092               dirname = pathname_part (current_output_filename);
1093               strcpy (filename, dirname);
1094               strcat (filename, fname_for_anchor);
1095               free (dirname);
1096
1097               anchor_stream = fopen (filename, "w");
1098               if (anchor_stream == NULL)
1099                 {
1100                   fs_error (filename);
1101                   xexit (1);
1102                 }
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=",
1109                      anchor_stream);
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
1113                  instead.  */
1114               if (current_node && *current_node)
1115                 {
1116                   char *fn, *tem;
1117
1118                   tem = expand_node_name (current_node);
1119                   fn = nodename_to_filename (tem);
1120                   free (tem);
1121                   fputs (fn, anchor_stream);
1122                   free (fn);
1123                 }
1124               else
1125                 {
1126                   char *base = filename_part (current_output_filename);
1127
1128                   fputs (base, anchor_stream);
1129                   free (base);
1130                 }
1131               fputs ("#", anchor_stream);
1132               for (p = anchor; *p; p++)
1133                 {
1134                   if (*p == '&')
1135                     fputs ("&amp;", anchor_stream);
1136                   else if (!URL_SAFE_CHAR (*p))
1137                     fprintf (anchor_stream, "%%%x", (unsigned char) *p);
1138                   else
1139                     fputc (*p, anchor_stream);
1140                 }
1141               fputs ("\">\n", anchor_stream);
1142               fclose (anchor_stream);
1143             }
1144         }
1145     }
1146   else if (xml)
1147     {
1148       xml_insert_element_with_attribute (ANCHOR, START, "name=\"%s\"", anchor);
1149       xml_insert_element (ANCHOR, END);
1150     }
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);
1155 }
1156 \f
1157 /* Find NODE in REF_LIST. */
1158 static NODE_REF *
1159 find_node_reference (node, ref_list)
1160      char *node;
1161      NODE_REF *ref_list;
1162 {
1163   NODE_REF *orig_ref_list = ref_list;
1164   char *expanded_node;
1165
1166   while (ref_list)
1167     {
1168       if (strcmp (node, ref_list->node) == 0)
1169         break;
1170       ref_list = ref_list->next;
1171     }
1172
1173   if (ref_list || !expensive_validation)
1174     return ref_list;
1175
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)
1179     {
1180       if (STREQ (expanded_node, ref_list->node))
1181         break;
1182       if (strchr (ref_list->node, COMMAND_PREFIX))
1183         {
1184           char *expanded_ref = expand_node_name (ref_list->node);
1185
1186           if (STREQ (expanded_node, expanded_ref))
1187             {
1188               free (expanded_ref);
1189               break;
1190             }
1191           free (expanded_ref);
1192         }
1193     }
1194   free (expanded_node);
1195   return ref_list;
1196 }
1197
1198 void
1199 free_node_references ()
1200 {
1201   NODE_REF *list, *temp;
1202
1203   list = node_references;
1204
1205   while (list)
1206     {
1207       temp = list;
1208       free (list->node);
1209       free (list->containing_node);
1210       list = list->next;
1211       free (temp);
1212     }
1213   node_references = NULL;
1214 }
1215
1216 void
1217 free_node_node_references ()
1218 {
1219   NODE_REF *list, *temp;
1220
1221   list = node_references;
1222
1223   while (list)
1224     {
1225       temp = list;
1226       free (list->node);
1227       list = list->next;
1228       free (temp);
1229     }
1230   node_node_references = NULL;
1231 }
1232
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. */
1235 int
1236 number_of_node (node)
1237      char *node;
1238 {
1239   NODE_REF *temp_ref;
1240   TAG_ENTRY *temp_node = find_node (node);
1241
1242   if (temp_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;
1248   else
1249     return 0;
1250 }
1251 \f
1252 /* validation */
1253
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.  */
1257
1258 static int
1259 validate (tag, line, label)
1260      char *tag;
1261      int line;
1262      char *label;
1263 {
1264   TAG_ENTRY *result;
1265
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 == '(')
1269     return 1;
1270
1271   /* Otherwise, the tag must exist. */
1272   result = find_node (tag);
1273
1274   if (!result)
1275     {
1276       line_number = line;
1277       line_error (_("%s reference to nonexistent node `%s'"), label, tag);
1278       return 0;
1279     }
1280   result->touched++;
1281   return 1;
1282 }
1283
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
1286    translated.  */
1287 static char *
1288 reftype_type_string (type)
1289      enum reftype type;
1290 {
1291   switch (type)
1292     {
1293     case menu_reference:
1294       return _("Menu");
1295     case followed_reference:
1296       return _("Cross");
1297     default:
1298       return "Internal-bad-reference-type";
1299     }
1300 }
1301
1302 static void
1303 validate_other_references (ref_list)
1304      NODE_REF *ref_list;
1305 {
1306   char *old_input_filename = input_filename;
1307
1308   while (ref_list)
1309     {
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;
1314     }
1315   input_filename = old_input_filename;
1316 }
1317
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)".
1322
1323    If the Prev is different from the Up,
1324    then the Prev node must have a Next pointing at this node.
1325
1326    Every node except Top must have an Up.
1327    The Up node must contain some sort of reference, other than a Next,
1328    to this node.
1329
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. */
1332 void
1333 validate_file (tag_table)
1334      TAG_ENTRY *tag_table;
1335 {
1336   char *old_input_filename = input_filename;
1337   TAG_ENTRY *tags = tag_table;
1338
1339   while (tags)
1340     {
1341       TAG_ENTRY *temp_tag;
1342       char *tem1, *tem2;
1343
1344       input_filename = tags->filename;
1345       line_number = tags->line_no;
1346
1347       /* If this is a "no warn" node, don't validate it in any way. */
1348       if (tags->flags & TAG_FLAG_NO_WARN)
1349         {
1350           tags = tags->next_ent;
1351           continue;
1352         }
1353
1354       /* If this node has a Next, then make sure that the Next exists. */
1355       if (tags->next)
1356         {
1357           validate (tags->next, tags->line_no, _("Next"));
1358
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))
1364             {
1365               char *prev = temp_tag->prev;
1366               int you_lose = !prev || !STREQ (prev, tags->node);
1367
1368               if (you_lose && expensive_validation)
1369                 {
1370                   tem1 = expand_node_name (prev);
1371                   tem2 = expand_node_name (tags->node);
1372
1373                   if (STREQ (tem1, tem2))
1374                     you_lose = 0;
1375                   free (tem1);
1376                   free (tem2);
1377                 }
1378               if (you_lose)
1379                 {
1380                   line_error (_("Next field of node `%s' not pointed to"),
1381                               tags->node);
1382                   file_line_error (temp_tag->filename, temp_tag->line_no,
1383                                    _("This node (%s) has the bad Prev"),
1384                                    temp_tag->node);
1385                   temp_tag->flags |= TAG_FLAG_PREV_ERROR;
1386                 }
1387             }
1388         }
1389
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)
1394         {
1395           int valid_p = validate (tags->prev, tags->line_no, _("Prev"));
1396
1397           if (!valid_p)
1398             tags->flags |= TAG_FLAG_PREV_ERROR;
1399           else
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);
1404
1405               if (!prev_equals_up && expensive_validation)
1406                 {
1407                   tem1 = expand_node_name (tags->prev);
1408                   tem2 = expand_node_name (tags->up);
1409                   prev_equals_up = STREQ (tem1, tem2);
1410                   free (tem1);
1411                   free (tem2);
1412                 }
1413               if (!prev_equals_up)
1414                 {
1415                   temp_tag = find_node (tags->prev);
1416
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))
1420                     /* Do nothing. */ ;
1421                   else
1422                     {
1423                       int you_lose = !temp_tag->next
1424                         || !STREQ (temp_tag->next, tags->node);
1425
1426                       if (temp_tag->next && you_lose && expensive_validation)
1427                         {
1428                           tem1 = expand_node_name (temp_tag->next);
1429                           tem2 = expand_node_name (tags->node);
1430                           if (STREQ (tem1, tem2))
1431                             you_lose = 0;
1432                           free (tem1);
1433                           free (tem2);
1434                         }
1435                       if (you_lose)
1436                         {
1437                           line_error
1438                             (_("Prev field of node `%s' not pointed to"),
1439                              tags->node);
1440                           file_line_error (temp_tag->filename,
1441                                            temp_tag->line_no,
1442                                            _("This node (%s) has the bad Next"),
1443                                            temp_tag->node);
1444                           temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
1445                         }
1446                     }
1447                 }
1448             }
1449         }
1450
1451       if (!tags->up
1452           && !(tags->flags & TAG_FLAG_ANCHOR)
1453           && strcasecmp (tags->node, "Top") != 0)
1454         line_error (_("`%s' has no Up field"), tags->node);
1455       else if (tags->up)
1456         {
1457           int valid_p = validate (tags->up, tags->line_no, _("Up"));
1458
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 != '(')
1462             {
1463               NODE_REF *nref;
1464               NODE_REF *tref = NULL;
1465               NODE_REF *list = node_references;
1466
1467               for (;;)
1468                 {
1469                   nref = find_node_reference (tags->node, list);
1470                   if (!nref)
1471                     break;
1472
1473                   if (strcmp (nref->containing_node, tags->up) == 0)
1474                     {
1475                       if (nref->type != menu_reference)
1476                         {
1477                           tref = nref;
1478                           list = nref->next;
1479                         }
1480                       else
1481                         break;
1482                     }
1483                   list = nref->next;
1484                 }
1485
1486               if (!nref)
1487                 {
1488                   if (!tref && expensive_validation)
1489                     {
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);
1495                       char *tem;
1496
1497                       list = node_references;
1498
1499                       for (;;)
1500                         {
1501                           nref = find_node_reference (tags->node, list);
1502                           if (!nref)
1503                             break;
1504                           tem = expand_node_name (nref->containing_node);
1505                           if (STREQ (tem, tags_up))
1506                             {
1507                               if (nref->type != menu_reference)
1508                                 tref = nref;
1509                               else
1510                                 {
1511                                   free (tem);
1512                                   break;
1513                                 }
1514                             }
1515                           free (tem);
1516                           list = nref->next;
1517                         }
1518                     }
1519                   if (!nref && !tref)
1520                     {
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);
1525                     }
1526                 }
1527             }
1528         }
1529       tags = tags->next_ent;
1530     }
1531
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. */
1535
1536   for (tags = tag_table; tags; tags = tags->next_ent)
1537     {
1538       /* If this node is a "no warn" node, do nothing. */
1539       if (tags->flags & TAG_FLAG_NO_WARN)
1540         {
1541           tags = tags->next_ent;
1542           continue;
1543         }
1544
1545       /* Special hack.  If the node in question appears to have
1546          been referenced more than REFERENCE_WARNING_LIMIT times,
1547          give a warning. */
1548       if (tags->touched > reference_warning_limit)
1549         {
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);
1554         }
1555
1556       if (tags->touched == 0)
1557         {
1558           input_filename = tags->filename;
1559           line_number = tags->line_no;
1560
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);
1567         }
1568     }
1569   input_filename = old_input_filename;
1570 }
1571
1572 \f
1573 /* Splitting */
1574
1575 /* Return true if the tag entry pointed to by TAGS is the last node.
1576    This means only anchors follow.  */
1577
1578 static int
1579 last_node_p (tags)
1580      TAG_ENTRY *tags;
1581 {
1582   int last = 1;
1583   while (tags->next_ent) {
1584     tags = tags->next_ent;
1585     if (tags->flags & TAG_FLAG_ANCHOR)
1586       ;
1587     else
1588       {
1589         last = 0;
1590         break;
1591       }
1592   }
1593   
1594   return last;
1595 }
1596
1597
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. */
1603 void
1604 split_file (filename, size)
1605      char *filename;
1606      int size;
1607 {
1608   char *root_filename, *root_pathname;
1609   char *the_file, *filename_part ();
1610   struct stat fileinfo;
1611   long file_size;
1612   char *the_header;
1613   int header_size;
1614   int dos_file_names = 0;       /* if nonzero, don't exceed 8+3 limits */
1615
1616   /* Can only do this to files with tag tables. */
1617   if (!tag_table)
1618     return;
1619
1620   if (size == 0)
1621     size = DEFAULT_SPLIT_SIZE;
1622
1623   if ((stat (filename, &fileinfo) != 0) ||
1624       (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
1625     return;
1626   file_size = (long) fileinfo.st_size;
1627
1628   the_file = find_and_load (filename);
1629   if (!the_file)
1630     return;
1631
1632   root_filename = filename_part (filename);
1633   root_pathname = pathname_part (filename);
1634
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 : ".");
1637
1638   if (!root_pathname)
1639     root_pathname = xstrdup ("");
1640
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. */
1646   {
1647     int which_file = 1;
1648     TAG_ENTRY *tags = tag_table;
1649     char *indirect_info = NULL;
1650
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);
1655
1656     while (tags)
1657       {
1658         int file_top, file_bot, limit;
1659
1660         /* Have to include the Control-_. */
1661         file_top = file_bot = tags->position;
1662         limit = file_top + size;
1663
1664         /* If the rest of this file is only one node, then
1665            that is the entire subfile. */
1666         if (last_node_p (tags))
1667           {
1668             int i = tags->position + 1;
1669             char last_char = the_file[i];
1670
1671             while (i < file_size)
1672               {
1673                 if ((the_file[i] == '\037') &&
1674                     ((last_char == '\n') ||
1675                      (last_char == '\014')))
1676                   break;
1677                 else
1678                   last_char = the_file[i];
1679                 i++;
1680               }
1681             file_bot = i;
1682             tags = tags->next_ent;
1683             goto write_region;
1684           }
1685
1686         /* Otherwise, find the largest number of nodes that can fit in
1687            this subfile. */
1688         for (; tags; tags = tags->next_ent)
1689           {
1690             if (last_node_p (tags))
1691               {
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];
1696
1697                 while (i < file_size)
1698                   {
1699                     if ((the_file[i] == '\037') &&
1700                         ((last_char == '\n') ||
1701                          (last_char == '\014')))
1702                       break;
1703                     else
1704                       last_char = the_file[i];
1705                     i++;
1706                   }
1707                 file_bot = i;
1708
1709                 if (file_bot < limit)
1710                   {
1711                     tags = tags->next_ent;
1712                     goto write_region;
1713                   }
1714                 else
1715                   {
1716                     /* Here we want to write out everything before the last
1717                        node, and then write the last node out in a file
1718                        by itself. */
1719                     file_bot = tags->position;
1720                     goto write_region;
1721                   }
1722               }
1723
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))
1727               {
1728                 if (tags->position == file_top)
1729                   tags = tags->next_ent;
1730
1731                 file_bot = tags->position;
1732
1733               write_region:
1734                 {
1735                   int fd;
1736                   char *split_filename, *split_basename;
1737                   unsigned root_len = strlen (root_filename);
1738
1739                   split_filename = xmalloc (10 + strlen (root_pathname)
1740                                             + root_len);
1741                   split_basename = xmalloc (10 + root_len);
1742                   sprintf (split_basename, "%s-%d", root_filename, which_file);
1743                   if (dos_file_names)
1744                     {
1745                       char *dot = strchr (split_basename, '.');
1746                       unsigned base_len = strlen (split_basename);
1747
1748                       if (dot)
1749                         { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
1750                           dot[1] = 'i';
1751                           memmove (which_file <= 99 ? dot + 2 : dot + 1,
1752                                    split_basename + root_len + 1,
1753                                    strlen (split_basename + root_len + 1) + 1);
1754                         }
1755                       else if (base_len > 8)
1756                         {
1757                           /* Make foobar-1, .., fooba-10, .., foob-100, ... */
1758                           unsigned numlen = base_len - root_len;
1759
1760                           memmove (split_basename + 8 - numlen,
1761                                    split_basename + root_len, numlen + 1);
1762                         }
1763                     }
1764                   sprintf (split_filename, "%s%s", root_pathname,
1765                            split_basename);
1766
1767                   fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
1768                   if (fd < 0
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)
1773                     {
1774                       perror (split_filename);
1775                       if (fd != -1)
1776                         close (fd);
1777                       xexit (1);
1778                     }
1779
1780                   if (!indirect_info)
1781                     {
1782                       indirect_info = the_file + file_top;
1783                       sprintf (indirect_info, "\037\nIndirect:\n");
1784                       indirect_info += strlen (indirect_info);
1785                     }
1786
1787                   sprintf (indirect_info, "%s: %d\n",
1788                            split_basename, file_top);
1789
1790                   free (split_basename);
1791                   free (split_filename);
1792                   indirect_info += strlen (indirect_info);
1793                   which_file++;
1794                   break;
1795                 }
1796               }
1797           }
1798       }
1799
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");
1804     if (!output_stream)
1805       {
1806         perror (filename);
1807         xexit (1);
1808       }
1809
1810     {
1811       int distance = indirect_info - the_file;
1812       fwrite (the_file, 1, distance, output_stream);
1813
1814       /* Inhibit newlines. */
1815       paragraph_is_open = 0;
1816
1817       write_tag_table_indirect ();
1818       fclose (output_stream);
1819       free (the_header);
1820       free (the_file);
1821       return;
1822     }
1823   }
1824 }