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