xref: /openbsd/gnu/usr.bin/texinfo/makeinfo/node.c (revision 1076333c)
1 /* node.c -- nodes for Texinfo.
2    $Id: node.c,v 1.3 2006/07/17 16:12:36 espie 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 
43 /* Adding nodes, and making tags.  */
44 
45 /* Start a new tag table. */
46 void
init_tag_table(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 
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
write_tag_table_internal(int indirect_p)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
write_tag_table(char * filename)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
write_tag_table_indirect(void)131 write_tag_table_indirect (void)
132 {
133   write_tag_table_internal (1);
134 }
135 
136 /* Convert "top" and friends into "Top". */
137 static void
normalize_node_name(char * string)138 normalize_node_name (char *string)
139 {
140   if (strcasecmp (string, "Top") == 0)
141     strcpy (string, "Top");
142 }
143 
144 static char *
get_node_token(int expand)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 *
expand_node_name(char * node)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 *
find_node(char * name)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 *
find_node_by_fname(char * fname)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
remember_node_node_reference(char * node)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
remember_node(char * node,char * prev,char * next,char * up,int position,int line_no,char * fname,int flags)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
remember_node_reference(char * node,int line,enum reftype type)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
isolate_nodename(char * nodename)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 *
glean_node_from_menu(int remember_ref,enum reftype ref_type)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
set_current_output_filename(const char * fname)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
add_html_names(char * node)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 
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
cm_node(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
cm_anchor(int arg)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 
1233 /* Find NODE in REF_LIST. */
1234 static NODE_REF *
find_node_reference(char * node,NODE_REF * ref_list)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
free_node_references(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
free_node_node_references(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
number_of_node(char * node)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 
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
validate(char * tag,int line,const char * label)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 *
reftype_type_string(enum reftype type)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
validate_other_references(NODE_REF * ref_list)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
validate_file(TAG_ENTRY * tag_table)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 
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
last_node_p(TAG_ENTRY * tags)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 *
enumerate_filename(char * pathname,char * basename,int number)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
clean_old_split_files(char * filename)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
split_file(char * filename,int size)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 successfully 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 }
1944