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