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