Back to index

tetex-bin  3.0
node.c
Go to the documentation of this file.
00001 /* node.c -- nodes for Texinfo.
00002    $Id: node.c,v 1.27 2004/12/20 23:56:07 karl Exp $
00003 
00004    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
00005    Foundation, Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software Foundation,
00019    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00020 
00021 #include "system.h"
00022 #include "cmds.h"
00023 #include "files.h"
00024 #include "float.h"
00025 #include "footnote.h"
00026 #include "macro.h"
00027 #include "makeinfo.h"
00028 #include "node.h"
00029 #include "html.h"
00030 #include "sectioning.h"
00031 #include "insertion.h"
00032 #include "xml.h"
00033 
00034 /* See comments in node.h.  */
00035 NODE_REF *node_references = NULL;
00036 NODE_REF *node_node_references = NULL;
00037 TAG_ENTRY *tag_table = NULL;
00038 int node_number = -1;
00039 int node_order = 0;
00040 int current_section = 0;
00041 int outstanding_node = 0;
00042 
00043 /* Adding nodes, and making tags.  */
00044 
00045 /* Start a new tag table. */
00046 void
00047 init_tag_table (void)
00048 {
00049   while (tag_table)
00050     {
00051       TAG_ENTRY *temp = tag_table;
00052       free (temp->node);
00053       free (temp->prev);
00054       free (temp->next);
00055       free (temp->up);
00056       tag_table = tag_table->next_ent;
00057       free (temp);
00058     }
00059 }
00060 
00061 /* Write out the contents of the existing tag table.
00062    INDIRECT_P says how to format the output (it depends on whether the
00063    table is direct or indirect).  */
00064 static void
00065 write_tag_table_internal (int indirect_p)
00066 {
00067   TAG_ENTRY *node;
00068   int old_indent = no_indent;
00069 
00070   if (xml)
00071     {
00072       flush_output ();
00073       return;
00074     }
00075 
00076   no_indent = 1;
00077   filling_enabled = 0;
00078   must_start_paragraph = 0;
00079   close_paragraph ();
00080 
00081   if (!indirect_p)
00082     {
00083       no_indent = 1;
00084       insert ('\n');
00085     }
00086 
00087   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
00088 
00089   /* Do not collapse -- to -, etc., in node names.  */
00090   in_fixed_width_font++;
00091 
00092   for (node = tag_table; node; node = node->next_ent)
00093     {
00094       if (node->flags & TAG_FLAG_ANCHOR)
00095         { /* This reference is to an anchor.  */
00096           execute_string ("Ref: %s", node->node);
00097         }
00098       else
00099         { /* This reference is to a node.  */
00100           execute_string ("Node: %s", node->node);
00101         }
00102       add_word_args ("\177%d\n", node->position);
00103     }
00104 
00105   add_word ("\037\nEnd Tag Table\n");
00106 
00107   /* Do not collapse -- to -, etc., in node names.  */
00108   in_fixed_width_font--;
00109 
00110   flush_output ();
00111   no_indent = old_indent;
00112 }
00113 
00114 void
00115 write_tag_table (char *filename)
00116 {
00117   output_stream = fopen (filename, "a");
00118   if (!output_stream)
00119     {
00120       fs_error (filename);
00121       return;
00122     }
00123 
00124   write_tag_table_internal (0); /* Not indirect. */
00125 
00126   if (fclose (output_stream) != 0)
00127     fs_error (filename);
00128 }
00129 
00130 static void
00131 write_tag_table_indirect (void)
00132 {
00133   write_tag_table_internal (1);
00134 }
00135 
00136 /* Convert "top" and friends into "Top". */
00137 static void
00138 normalize_node_name (char *string)
00139 {
00140   if (strcasecmp (string, "Top") == 0)
00141     strcpy (string, "Top");
00142 }
00143 
00144 static char *
00145 get_node_token (int expand)
00146 {
00147   char *string;
00148 
00149   get_until_in_line (expand, ",", &string);
00150 
00151   if (curchar () == ',')
00152     input_text_offset++;
00153 
00154   fix_whitespace (string);
00155 
00156   /* Force all versions of "top" to be "Top". */
00157   normalize_node_name (string);
00158 
00159   return string;
00160 }
00161 
00162 /* Expand any macros and other directives in a node name, and
00163    return the expanded name as an malloc'ed string.  */
00164 char *
00165 expand_node_name (char *node)
00166 {
00167   char *result = node;
00168 
00169   if (node)
00170     {
00171       /* Don't expand --, `` etc., in case somebody will want
00172          to print the result.  */
00173       in_fixed_width_font++;
00174       result = expansion (node, 0);
00175       in_fixed_width_font--;
00176       fix_whitespace (result);
00177       normalize_node_name (result);
00178     }
00179   return result;
00180 }
00181 
00182 /* Look up NAME in the tag table, and return the associated
00183    tag_entry.  If the node is not in the table return NULL. */
00184 TAG_ENTRY *
00185 find_node (char *name)
00186 {
00187   TAG_ENTRY *tag = tag_table;
00188   char *expanded_name;
00189   char n1 = name[0];
00190 
00191   while (tag)
00192     {
00193       if (tag->node[0] == n1 && strcmp (tag->node, name) == 0)
00194         return tag;
00195       tag = tag->next_ent;
00196     }
00197 
00198   if (!expensive_validation)
00199     return NULL;
00200 
00201   /* Try harder.  Maybe TAG_TABLE has the expanded NAME, or maybe NAME
00202      is expanded while TAG_TABLE has its unexpanded form.  This may
00203      slow down the search, but if they want this feature, let them
00204      pay!  If they want it fast, they should write every node name
00205      consistently (either always expanded or always unexpaned).  */
00206   expanded_name = expand_node_name (name);
00207   for (tag = tag_table; tag; tag = tag->next_ent)
00208     {
00209       if (STREQ (tag->node, expanded_name))
00210         break;
00211       /* If the tag name doesn't have the command prefix, there's no
00212          chance it could expand into anything but itself.  */
00213       if (strchr (tag->node, COMMAND_PREFIX))
00214         {
00215           char *expanded_node = expand_node_name (tag->node);
00216 
00217           if (STREQ (expanded_node, expanded_name))
00218             {
00219               free (expanded_node);
00220               break;
00221             }
00222           free (expanded_node);
00223         }
00224     }
00225   free (expanded_name);
00226   return tag;
00227 }
00228 
00229 /* Look in the tag table for a node whose file name is FNAME, and
00230    return the associated tag_entry.  If there's no such node in the
00231    table, return NULL. */
00232 static TAG_ENTRY *
00233 find_node_by_fname (char *fname)
00234 {
00235   TAG_ENTRY *tag = tag_table;
00236   while (tag)
00237     {
00238       if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0)
00239        return tag;
00240       tag = tag->next_ent;
00241     }
00242 
00243   return tag;
00244 }
00245 
00246 /* Remember next, prev, etc. references in a @node command, where we
00247    don't care about most of the entries. */
00248 static void
00249 remember_node_node_reference (char *node)
00250 {
00251   NODE_REF *temp = xmalloc (sizeof (NODE_REF));
00252   int number;
00253 
00254   if (!node) return;
00255   temp->next = node_node_references;
00256   temp->node = xstrdup (node);
00257   temp->type = followed_reference;
00258   number = number_of_node (node);
00259   if (number)
00260     temp->number = number;      /* Already assigned. */
00261   else
00262     {
00263       node_number++;
00264       temp->number = node_number;
00265     }
00266   node_node_references = temp;
00267 }
00268 
00269 /* Remember NODE and associates. */
00270 static void
00271 remember_node (char *node, char *prev, char *next, char *up,
00272     int position, int line_no, char *fname, int flags)
00273 {
00274   /* Check for existence of this tag already. */
00275   if (validating)
00276     {
00277       TAG_ENTRY *tag = find_node (node);
00278       if (tag)
00279         {
00280           line_error (_("Node `%s' previously defined at line %d"),
00281                       node, tag->line_no);
00282           return;
00283         }
00284     }
00285 
00286   if (!(flags & TAG_FLAG_ANCHOR))
00287     {
00288       /* Make this the current node. */
00289       current_node = node;
00290     }
00291 
00292   /* Add it to the list. */
00293   {
00294     int number = number_of_node (node);
00295 
00296     TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY));
00297     new->node = node;
00298     new->prev = prev;
00299     new->next = next;
00300     new->up = up;
00301     new->position = position;
00302     new->line_no = line_no;
00303     new->filename = node_filename;
00304     new->touched = 0;
00305     new->flags = flags;
00306     if (number)
00307       new->number = number;     /* Already assigned. */
00308     else
00309       {
00310         node_number++;
00311         new->number = node_number;
00312       }
00313     if (fname)
00314       new->html_fname = fname;
00315     else
00316       /* This happens for Top node under split-HTML, for example.  */
00317       new->html_fname
00318        = normalize_filename (filename_part (current_output_filename));
00319     new->next_ent = tag_table;
00320 
00321     /* Increment the order counter, and save it.  */
00322     node_order++;
00323     new->order = node_order;
00324 
00325     tag_table = new;
00326   }
00327 
00328   if (html)
00329     { /* Note the references to the next etc. nodes too.  */
00330       remember_node_node_reference (next);
00331       remember_node_node_reference (prev);
00332       remember_node_node_reference (up);
00333     }
00334 }
00335 
00336 /* Remember this node name for later validation use.  This is used to
00337    remember menu references while reading the input file.  After the
00338    output file has been written, if validation is on, then we use the
00339    contents of `node_references' as a list of nodes to validate.  */
00340 void
00341 remember_node_reference (char *node, int line, enum reftype type)
00342 {
00343   NODE_REF *temp = xmalloc (sizeof (NODE_REF));
00344   int number = number_of_node (node);
00345 
00346   temp->next = node_references;
00347   temp->node = xstrdup (node);
00348   temp->line_no = line;
00349   temp->section = current_section;
00350   temp->type = type;
00351   temp->containing_node = xstrdup (current_node ? current_node : "");
00352   temp->filename = node_filename;
00353   if (number)
00354     temp->number = number;      /* Already assigned. */
00355   else
00356     {
00357       node_number++;
00358       temp->number = node_number;
00359     }
00360 
00361   node_references = temp;
00362 }
00363 
00364 static void
00365 isolate_nodename (char *nodename)
00366 {
00367   int i, c;
00368   int paren_seen, paren;
00369 
00370   if (!nodename)
00371     return;
00372 
00373   canon_white (nodename);
00374   paren_seen = paren = i = 0;
00375 
00376   if (*nodename == '.' || !*nodename)
00377     {
00378       *nodename = 0;
00379       return;
00380     }
00381 
00382   if (*nodename == '(')
00383     {
00384       paren++;
00385       paren_seen++;
00386       i++;
00387     }
00388 
00389   for (; (c = nodename[i]); i++)
00390     {
00391       if (paren)
00392         {
00393           if (c == '(')
00394             paren++;
00395           else if (c == ')')
00396             paren--;
00397 
00398           continue;
00399         }
00400 
00401       /* If the character following the close paren is a space, then this
00402          node has no more characters associated with it. */
00403       if (c == '\t' ||
00404           c == '\n' ||
00405           c == ','  ||
00406           ((paren_seen && nodename[i - 1] == ')') &&
00407            (c == ' ' || c == '.')) ||
00408           (c == '.' &&
00409            ((!nodename[i + 1] ||
00410              (cr_or_whitespace (nodename[i + 1])) ||
00411              (nodename[i + 1] == ')')))))
00412         break;
00413     }
00414   nodename[i] = 0;
00415 }
00416 
00417 /* This function gets called at the start of every line while inside a
00418    menu.  It checks to see if the line starts with "* ", and if so and
00419    REMEMBER_REF is nonzero, remembers the node reference as type
00420    REF_TYPE that this menu refers to.  input_text_offset is at the \n
00421    just before the menu line.  If REMEMBER_REF is zero, REF_TYPE is unused.  */
00422 #define MENU_STARTER "* "
00423 char *
00424 glean_node_from_menu (int remember_ref, enum reftype ref_type)
00425 {
00426   int i, orig_offset = input_text_offset;
00427   char *nodename;
00428   char *line, *expanded_line;
00429   char *old_input = input_text;
00430   int old_size = input_text_length;
00431 
00432   if (strncmp (&input_text[input_text_offset + 1],
00433                MENU_STARTER,
00434                strlen (MENU_STARTER)) != 0)
00435     return NULL;
00436   else
00437     input_text_offset += strlen (MENU_STARTER) + 1;
00438 
00439   /* The menu entry might include macro calls, so we need to expand them.  */
00440   get_until ("\n", &line);
00441   only_macro_expansion++;       /* only expand macros in menu entries */
00442   expanded_line = expansion (line, 0);
00443   only_macro_expansion--;
00444   free (line);
00445   input_text = expanded_line;
00446   input_text_offset = 0;
00447   input_text_length = strlen (expanded_line);
00448 
00449   get_until_in_line (0, ":", &nodename);
00450   if (curchar () == ':')
00451     input_text_offset++;
00452 
00453   if (curchar () != ':')
00454     {
00455       free (nodename);
00456       get_until_in_line (0, "\n", &nodename);
00457       isolate_nodename (nodename);
00458     }
00459 
00460   input_text = old_input;
00461   input_text_offset = orig_offset;
00462   input_text_length = old_size;
00463   free (expanded_line);
00464   fix_whitespace (nodename);
00465   normalize_node_name (nodename);
00466   i = strlen (nodename);
00467   if (i && nodename[i - 1] == ':')
00468     nodename[i - 1] = 0;
00469 
00470   if (remember_ref)
00471     remember_node_reference (nodename, line_number, ref_type);
00472 
00473   return nodename;
00474 }
00475 
00476 /* Set the name of the current output file.  */
00477 void
00478 set_current_output_filename (const char *fname)
00479 {
00480   if (current_output_filename)
00481     free (current_output_filename);
00482   current_output_filename = xstrdup (fname);
00483 }
00484 
00485 
00486 /* Output the <a name="..."></a> constructs for NODE.  We output both
00487    the new-style conversion and the old-style, if they are different.
00488    See comments at `add_escaped_anchor_name' in html.c.  */
00489 
00490 static void
00491 add_html_names (char *node)
00492 {
00493   char *tem = expand_node_name (node);
00494   char *otem = xstrdup (tem);
00495 
00496   /* Determine if the old and new schemes come up with different names;
00497      only output the old scheme if that is so.  We don't want to output
00498      the same name twice.  */
00499   canon_white (otem);
00500   {
00501     char *optr = otem;
00502     int need_old = 0;
00503     
00504     for (; *optr; optr++)
00505       {
00506         if (!cr_or_whitespace (*optr) && !URL_SAFE_CHAR (*optr))
00507           {
00508             need_old = 1;
00509             break;
00510           }
00511       }
00512     
00513     if (need_old)
00514       {
00515         add_word ("<a name=\"");
00516         add_anchor_name (otem, -1);  /* old anchor name conversion */
00517         add_word ("\"></a>\n");
00518       }
00519     free (otem);
00520   }
00521 
00522   /* Always output the new scheme.  */
00523   canon_white (tem);
00524   add_word ("<a name=\"");
00525   add_anchor_name (tem, 0);
00526   add_word ("\"></a>\n");
00527 
00528   free (tem);
00529 }
00530 
00531 
00532 /* The order is: nodename, nextnode, prevnode, upnode.
00533    If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
00534    You must follow a node command which has those fields defaulted
00535    with a sectioning command (e.g., @chapter) giving the "level" of that node.
00536    It is an error not to do so.
00537    The defaults come from the menu in this node's parent. */
00538 void
00539 cm_node (void)
00540 {
00541   static long epilogue_len = 0L;
00542   char *node, *prev, *next, *up;
00543   int new_node_pos, defaulting, this_section;
00544   int no_warn = 0;
00545   char *fname_for_this_node = NULL;
00546   char *tem;
00547   TAG_ENTRY *tag = NULL;
00548 
00549   if (strcmp (command, "nwnode") == 0)
00550     no_warn = TAG_FLAG_NO_WARN;
00551 
00552   /* Get rid of unmatched brace arguments from previous commands. */
00553   discard_braces ();
00554 
00555   /* There also might be insertions left lying around that haven't been
00556      ended yet.  Do that also. */
00557   discard_insertions (1);
00558 
00559   if (!html && !already_outputting_pending_notes)
00560     {
00561       close_paragraph ();
00562       output_pending_notes ();
00563     }
00564 
00565   new_node_pos = output_position;
00566 
00567   if (macro_expansion_output_stream && !executing_string)
00568     append_to_expansion_output (input_text_offset + 1);
00569 
00570   /* Do not collapse -- to -, etc., in node names.  */
00571   in_fixed_width_font++;
00572 
00573   /* While expanding the @node line, leave any non-macros
00574      intact, so that the macro-expanded output includes them.  */
00575   only_macro_expansion++;
00576   node = get_node_token (1);
00577   only_macro_expansion--;
00578   next = get_node_token (0);
00579   prev = get_node_token (0);
00580   up = get_node_token (0);
00581 
00582   if (html && splitting
00583       /* If there is a Top node, it always goes into index.html.  So
00584         don't start a new HTML file for Top.  */
00585       && (top_node_seen || strcasecmp (node, "Top") != 0))
00586     {
00587       /* We test *node here so that @node without a valid name won't
00588         start a new file name with a bogus name such as ".html".
00589         This could happen if we run under "--force", where we cannot
00590         simply bail out.  Continuing to use the same file sounds like
00591         the best we can do in such cases.  */
00592       if (current_output_filename && output_stream && *node)
00593        {
00594          char *fname_for_prev_node;
00595 
00596          if (current_node)
00597            {
00598              /* NOTE: current_node at this point still holds the name
00599                of the previous node.  */
00600              tem = expand_node_name (current_node);
00601              fname_for_prev_node = nodename_to_filename (tem);
00602              free (tem);
00603            }
00604          else /* could happen if their top node isn't named "Top" */
00605            fname_for_prev_node = filename_part (current_output_filename);
00606          tem = expand_node_name (node);
00607          fname_for_this_node = nodename_to_filename (tem);
00608          free (tem);
00609          /* Don't close current output file, if next output file is
00610              to have the same name.  This may happen at top level, or
00611              if two nodes produce the same file name under --split.  */
00612          if (FILENAME_CMP (fname_for_this_node, fname_for_prev_node) != 0)
00613            {
00614              long pos1 = 0;
00615 
00616              /* End the current split output file. */
00617              close_paragraph ();
00618              output_pending_notes ();
00619              start_paragraph ();
00620              /* Compute the length of the HTML file's epilogue.  We
00621                cannot know the value until run time, due to the
00622                text/binary nuisance on DOS/Windows platforms, where
00623                2 `\r' characters could be added to the epilogue when
00624                it is written in text mode.  */
00625              if (epilogue_len == 0)
00626               {
00627                 flush_output ();
00628                 pos1 = ftell (output_stream);
00629               }
00630              add_word ("</body></html>\n");
00631              close_paragraph ();
00632              if (epilogue_len == 0)
00633               epilogue_len = ftell (output_stream) - pos1;
00634              fclose (output_stream);
00635              output_stream = NULL;
00636               output_position = 0;
00637              tag = find_node_by_fname (fname_for_this_node);
00638            }
00639          free (fname_for_prev_node);
00640        }
00641     }
00642 
00643   filling_enabled = indented_fill = 0;
00644   if (!html || (html && splitting))
00645     current_footnote_number = 1;
00646 
00647   if (verbose_mode)
00648     printf (_("Formatting node %s...\n"), node);
00649 
00650   if (macro_expansion_output_stream && !executing_string)
00651     remember_itext (input_text, input_text_offset);
00652 
00653   /* Reset the line number in each node for Info output, so that
00654      index entries will save the line numbers of parent node.  */
00655   node_line_number = 0;
00656 
00657   no_indent = 1;
00658   if (xml)
00659     {
00660       xml_begin_document (current_output_filename);
00661       xml_begin_node ();
00662       if (!docbook)
00663        {
00664          xml_insert_element (NODENAME, START);
00665          if (macro_expansion_output_stream && !executing_string)
00666            me_execute_string (node);
00667          else
00668            execute_string ("%s", node);
00669          xml_insert_element (NODENAME, END);
00670        }
00671       else
00672        xml_node_id = xml_id (node);
00673     }
00674   else if (!no_headers && !html)
00675     {
00676       /* Emacs Info reader cannot grok indented escape sequence.  */
00677       kill_self_indent (-1);
00678 
00679       add_word_args ("\037\nFile: %s,  Node: ", pretty_output_filename);
00680 
00681       if (macro_expansion_output_stream && !executing_string)
00682         me_execute_string (node);
00683       else
00684         execute_string ("%s", node);
00685       filling_enabled = indented_fill = 0;
00686     }
00687 
00688   /* Check for defaulting of this node's next, prev, and up fields. */
00689   defaulting = (*next == 0 && *prev == 0 && *up == 0);
00690 
00691   this_section = what_section (input_text + input_text_offset, NULL);
00692 
00693   /* If we are defaulting, then look at the immediately following
00694      sectioning command (error if none) to determine the node's
00695      level.  Find the node that contains the menu mentioning this node
00696      that is one level up (error if not found).  That node is the "Up"
00697      of this node.  Default the "Next" and "Prev" from the menu. */
00698   if (defaulting)
00699     {
00700       NODE_REF *last_ref = NULL;
00701       NODE_REF *ref = node_references;
00702 
00703       if (this_section < 0 && !STREQ (node, "Top"))
00704         {
00705           char *polite_section_name = "top";
00706           int i;
00707 
00708           for (i = 0; section_alist[i].name; i++)
00709             if (section_alist[i].level == current_section + 1)
00710               {
00711                 polite_section_name = section_alist[i].name;
00712                 break;
00713               }
00714 
00715           line_error
00716             (_("Node `%s' requires a sectioning command (e.g., %c%s)"),
00717              node, COMMAND_PREFIX, polite_section_name);
00718         }
00719       else
00720         {
00721           if (strcmp (node, "Top") == 0)
00722             {
00723               /* Default the NEXT pointer to be the first menu item in
00724                  this node, if there is a menu in this node.  We have to
00725                  try very hard to find the menu, as it may be obscured
00726                  by execution_strings which are on the filestack.  For
00727                  every member of the filestack which has a FILENAME
00728                  member which is identical to the current INPUT_FILENAME,
00729                  search forward from that offset. */
00730               int saved_input_text_offset = input_text_offset;
00731               int saved_input_text_length = input_text_length;
00732               char *saved_input_text = input_text;
00733               FSTACK *next_file = filestack;
00734 
00735               int orig_offset, orig_size;
00736 
00737               int bye_offset = search_forward ("\n@bye", input_text_offset);
00738 
00739               /* No matter what, make this file point back at `(dir)'. */
00740               free (up);
00741               up = xstrdup ("(dir)"); /* html fixxme */
00742 
00743               while (1)
00744                 {
00745                   orig_offset = input_text_offset;
00746                   orig_size =
00747                     search_forward (node_search_string, orig_offset);
00748 
00749                   if (orig_size < 0)
00750                     orig_size = input_text_length;
00751 
00752                   input_text_offset = search_forward ("\n@menu", orig_offset);
00753                   if (input_text_offset > -1
00754                       && (bye_offset > -1 && input_text_offset < bye_offset)
00755                       && cr_or_whitespace (input_text[input_text_offset + 6]))
00756                     {
00757                       char *nodename_from_menu = NULL;
00758 
00759                       input_text_offset =
00760                         search_forward ("\n* ", input_text_offset);
00761 
00762                       if (input_text_offset != -1)
00763                         nodename_from_menu = glean_node_from_menu (0, 0);
00764 
00765                       if (nodename_from_menu)
00766                         {
00767                           free (next);
00768                           next = nodename_from_menu;
00769                           break;
00770                         }
00771                     }
00772 
00773                   /* We got here, so it hasn't been found yet.  Try
00774                      the next file on the filestack if there is one. */
00775                   if (next_file
00776                       && FILENAME_CMP (next_file->filename, input_filename)
00777                           == 0)
00778                     {
00779                       input_text = next_file->text;
00780                       input_text_offset = next_file->offset;
00781                       input_text_length = next_file->size;
00782                       next_file = next_file->next;
00783                     }
00784                   else
00785                     { /* No more input files to check. */
00786                       break;
00787                     }
00788                 }
00789 
00790               input_text = saved_input_text;
00791               input_text_offset = saved_input_text_offset;
00792               input_text_length = saved_input_text_length;
00793             }
00794         }
00795 
00796       /* Fix the level of the menu references in the Top node, iff it
00797          was declared with @top, and no subsequent reference was found. */
00798       if (top_node_seen && !non_top_node_seen)
00799         {
00800           /* Then this is the first non-@top node seen. */
00801           int level;
00802 
00803           level = set_top_section_level (this_section - 1);
00804           non_top_node_seen = 1;
00805 
00806           while (ref)
00807             {
00808               if (ref->section == level)
00809                 ref->section = this_section - 1;
00810               ref = ref->next;
00811             }
00812 
00813           ref = node_references;
00814         }
00815 
00816       while (ref)
00817         {
00818           if (ref->section == (this_section - 1)
00819               && ref->type == menu_reference
00820               && strcmp (ref->node, node) == 0)
00821             {
00822               char *containing_node = ref->containing_node;
00823 
00824               free (up);
00825               up = xstrdup (containing_node);
00826 
00827               if (last_ref
00828                   && last_ref->type == menu_reference
00829                   && strcmp (last_ref->containing_node, containing_node) == 0)
00830                 {
00831                   free (next);
00832                   next = xstrdup (last_ref->node);
00833                 }
00834 
00835               while (ref->section == this_section - 1
00836                      && ref->next
00837                      && ref->next->type != menu_reference)
00838                 ref = ref->next;
00839 
00840               if (ref->next && ref->type == menu_reference
00841                   && strcmp (ref->next->containing_node, containing_node) == 0)
00842                 {
00843                   free (prev);
00844                   prev = xstrdup (ref->next->node);
00845                 }
00846               else if (!ref->next
00847                        && strcasecmp (ref->containing_node, "Top") == 0)
00848                 {
00849                   free (prev);
00850                   prev = xstrdup (ref->containing_node);
00851                 }
00852               break;
00853             }
00854           last_ref = ref;
00855           ref = ref->next;
00856         }
00857     }
00858 
00859   /* Insert the correct args if we are expanding macros, and the node's
00860      pointers weren't defaulted. */
00861   if (macro_expansion_output_stream && !executing_string && !defaulting)
00862     {
00863       char *temp;
00864       int op_orig = output_paragraph_offset;
00865       int meta_pos_orig = meta_char_pos;
00866       int extra = html ? strlen (node) : 0;
00867 
00868       temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up));
00869       sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up);
00870       me_execute_string (temp);
00871       free (temp);
00872 
00873       output_paragraph_offset = op_orig;
00874       meta_char_pos = meta_pos_orig;
00875     }
00876 
00877   if (!*node)
00878     {
00879       line_error (_("No node name specified for `%c%s' command"),
00880                   COMMAND_PREFIX, command);
00881       free (node);
00882       free (next); next = NULL;
00883       free (prev); prev= NULL;
00884       free (up);   up = NULL;
00885       node_number++;            /* else it doesn't get bumped */
00886     }
00887   else
00888     {
00889       if (!*next) { free (next); next = NULL; }
00890       if (!*prev) { free (prev); prev = NULL; }
00891       if (!*up)   { free (up);   up = NULL;   }
00892       remember_node (node, prev, next, up, new_node_pos, line_number,
00893                    fname_for_this_node, no_warn);
00894       outstanding_node = 1;
00895     }
00896 
00897   if (html)
00898     {
00899       if (splitting && *node && output_stream == NULL)
00900         {
00901          char *dirname;
00902          char filename[PATH_MAX];
00903 
00904          dirname = pathname_part (current_output_filename);
00905          strcpy (filename, dirname);
00906          strcat (filename, fname_for_this_node);
00907          free (dirname);
00908 
00909          /* See if the node name converted to a file name clashes
00910             with other nodes or anchors.  If it clashes with an
00911             anchor, we complain and nuke that anchor's file.  */
00912          if (!tag)
00913            {
00914              output_stream = fopen (filename, "w");
00915              html_output_head_p = 0; /* so that we generate HTML preamble */
00916              html_output_head ();
00917            }
00918          else if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
00919            {
00920              line_error (_("Anchor `%s' and node `%s' map to the same file name"),
00921                        tag->node, node);
00922              file_line_error (tag->filename, tag->line_no,
00923                             _("This @anchor command ignored; references to it will not work"));
00924              file_line_error (tag->filename, tag->line_no,
00925                             _("Rename this anchor or use the `--no-split' option"));
00926              /* Nuke the file name recorded in anchor's tag.
00927                Since we are about to nuke the file itself, we
00928                don't want find_node_by_fname to consider this
00929                anchor anymore.  */
00930              free (tag->html_fname);
00931              tag->html_fname = NULL;
00932              output_stream = fopen (filename, "w");
00933              html_output_head_p = 0; /* so that we generate HTML preamble */
00934              html_output_head ();
00935            }
00936          else
00937            {
00938              /* This node's file name clashes with another node.
00939                We put them both on the same file.  */
00940              output_stream = fopen (filename, "r+");
00941              if (output_stream)
00942               {
00943                 static char html_end[] = "</body></html>\n";
00944                 char end_line[sizeof(html_end)];
00945                 int fpos = fseek (output_stream, -epilogue_len,
00946                                 SEEK_END);
00947 
00948                 if (fpos < 0
00949                     || fgets (end_line, sizeof (html_end),
00950                             output_stream) == NULL
00951                     /* Paranoia: did someone change the way HTML
00952                       files are finished up?  */
00953                     || strcasecmp (end_line, html_end) != 0)
00954                   {
00955                     line_error (_("Unexpected string at end of split-HTML file `%s'"),
00956                               fname_for_this_node);
00957                     fclose (output_stream);
00958                     xexit (1);
00959                   }
00960                 fseek (output_stream, -epilogue_len, SEEK_END);
00961               }
00962            }
00963           if (output_stream == NULL)
00964             {
00965               fs_error (filename);
00966               xexit (1);
00967             }
00968           set_current_output_filename (filename);
00969         }
00970 
00971       if (!splitting && no_headers)
00972        { /* cross refs need a name="#anchor" even if not writing headers */ 
00973           add_html_names (node);
00974        }
00975 
00976       if (splitting || !no_headers)
00977         { /* Navigation bar. */
00978           add_html_block_elt ("<div class=\"node\">\n");
00979           /* The <p> avoids the links area running on with old Lynxen. */
00980           add_word_args ("<p>%s\n", splitting ? "" : "<hr>");
00981 
00982           /* In the split HTML case, the filename is wrong for the 
00983              old-style converted names, but we'll add them anyway, for
00984              consistency.  (And we need them in the normal (not
00985              no_headers) nonsplit case.)  */
00986           add_html_names (node);
00987 
00988           if (next)
00989             {
00990               tem = expansion (next, 0);
00991              add_word ((char *) _("Next:"));
00992               add_word ("&nbsp;");
00993               
00994              add_word ("<a rel=\"next\" accesskey=\"n\" href=\"");
00995              add_anchor_name (tem, 1);
00996               tem = escape_string (tem);
00997              add_word_args ("\">%s</a>", tem);
00998              
00999               free (tem);
01000 
01001              if (prev || up)
01002               add_word (",\n");
01003             }
01004           if (prev)
01005             {
01006               tem = expansion (prev, 0);
01007              add_word ((char *) _("Previous:"));
01008               add_word ("&nbsp;");
01009              add_word ("<a rel=\"previous\" accesskey=\"p\" href=\"");
01010              add_anchor_name (tem, 1);
01011               tem = escape_string (tem);
01012              add_word_args ("\">%s</a>", tem);
01013               free (tem);
01014 
01015              if (up)
01016               add_word (",\n");
01017             }
01018           if (up)
01019             {
01020               tem = expansion (up, 0);
01021              add_word ((char *) _("Up:"));
01022               add_word ("&nbsp;");
01023              add_word ("<a rel=\"up\" accesskey=\"u\" href=\"");
01024              add_anchor_name (tem, 1);
01025               tem = escape_string (tem);
01026              add_word_args ("\">%s</a>", tem);
01027               free (tem);
01028             }
01029           /* html fixxme: we want a `top' or `contents' link here.  */
01030 
01031           add_word_args ("\n%s\n", splitting ? "<hr>" : "");
01032          add_word ("</div>\n");
01033         }
01034     }
01035   else if (docbook)
01036     ;
01037   else if (xml)
01038     {
01039       if (next)
01040        {
01041          xml_insert_element (NODENEXT, START);
01042          execute_string ("%s", next);
01043          xml_insert_element (NODENEXT, END);
01044        }
01045       if (prev)
01046        {
01047          xml_insert_element (NODEPREV, START);
01048          execute_string ("%s", prev);
01049          xml_insert_element (NODEPREV, END);
01050        }
01051       if (up)
01052        {
01053          xml_insert_element (NODEUP, START);
01054          execute_string ("%s", up);
01055          xml_insert_element (NODEUP, END);
01056        }
01057     }
01058   else if (!no_headers)
01059     {
01060       if (macro_expansion_output_stream)
01061         me_inhibit_expansion++;
01062 
01063       /* These strings are not translatable.  */
01064       if (next)
01065         {
01066           execute_string (",  Next: %s", next);
01067           filling_enabled = indented_fill = 0;
01068         }
01069       if (prev)
01070         {
01071           execute_string (",  Prev: %s", prev);
01072           filling_enabled = indented_fill = 0;
01073         }
01074       if (up)
01075         {
01076           execute_string (",  Up: %s", up);
01077           filling_enabled = indented_fill = 0;
01078         }
01079       if (macro_expansion_output_stream)
01080         me_inhibit_expansion--;
01081     }
01082 
01083   close_paragraph ();
01084   no_indent = 0;
01085 
01086   /* Change the section only if there was a sectioning command. */
01087   if (this_section >= 0)
01088     current_section = this_section;
01089 
01090   if (current_node && STREQ (current_node, "Top"))
01091     top_node_seen = 1;
01092 
01093   filling_enabled = 1;
01094   in_fixed_width_font--;
01095 }
01096 
01097 /* Cross-reference target at an arbitrary spot.  */
01098 void
01099 cm_anchor (int arg)
01100 {
01101   char *anchor;
01102   char *fname_for_anchor = NULL;
01103 
01104   if (arg == END)
01105     return;
01106 
01107   /* Parse the anchor text.  */
01108   anchor = get_xref_token (1);
01109 
01110   /* Force all versions of "top" to be "Top". */
01111   normalize_node_name (anchor);
01112 
01113   /* In HTML mode, need to actually produce some output.  */
01114   if (html)
01115     {
01116       /* If this anchor is at the beginning of a new paragraph, make
01117         sure a new paragraph is indeed started.  */
01118       if (!paragraph_is_open)
01119        {
01120          if (!executing_string && html)
01121            html_output_head ();
01122          start_paragraph ();
01123          if (!in_fixed_width_font || in_menu || in_detailmenu)
01124            {
01125              insert_string ("<p>");
01126              in_paragraph = 1;
01127            }
01128        }
01129       add_word ("<a name=\"");
01130       add_anchor_name (anchor, 0);
01131       add_word ("\"></a>");
01132       if (splitting)
01133        {
01134          /* If we are splitting, cm_xref will produce a reference to
01135             a file whose name is derived from the anchor name.  So we
01136             must create a file when we see an @anchor, otherwise
01137             xref's to anchors won't work.  The file we create simply
01138             redirects to the file of this anchor's node.  */
01139          TAG_ENTRY *tag;
01140 
01141          fname_for_anchor = nodename_to_filename (anchor);
01142          /* See if the anchor name converted to a file name clashes
01143             with other anchors or nodes.  */
01144          tag = find_node_by_fname (fname_for_anchor);
01145          if (tag)
01146            {
01147              if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
01148               line_error (_("Anchors `%s' and `%s' map to the same file name"),
01149                          anchor, tag->node);
01150              else
01151               line_error (_("Anchor `%s' and node `%s' map to the same file name"),
01152                          anchor, tag->node);
01153              line_error (_("@anchor command ignored; references to it will not work"));
01154              line_error (_("Rename this anchor or use the `--no-split' option"));
01155              free (fname_for_anchor);
01156              /* We will not be creating a file for this anchor, so
01157                set its name to NULL, so that remember_node stores a
01158                NULL and find_node_by_fname won't consider this
01159                anchor for clashes.  */
01160              fname_for_anchor = NULL;
01161            }
01162          else
01163            {
01164              char *dirname, *p;
01165              char filename[PATH_MAX];
01166              FILE *anchor_stream;
01167 
01168              dirname = pathname_part (current_output_filename);
01169              strcpy (filename, dirname);
01170              strcat (filename, fname_for_anchor);
01171              free (dirname);
01172 
01173              anchor_stream = fopen (filename, "w");
01174              if (anchor_stream == NULL)
01175               {
01176                 fs_error (filename);
01177                 xexit (1);
01178               }
01179              /* The HTML magic below will cause the browser to
01180                immediately go to the anchor's node's file.  Lynx
01181                seems not to support this redirection, but it looks
01182                like a bug in Lynx, and they can work around it by
01183                clicking on the link once more.  */
01184              fputs ("<meta http-equiv=\"refresh\" content=\"0; url=",
01185                    anchor_stream);
01186              /* Make the indirect link point to the current node's
01187                file and anchor's "<a name" label.  If we don't have
01188                a valid node name, refer to the current output file
01189                instead.  */
01190              if (current_node && *current_node)
01191               {
01192                 char *fn, *tem;
01193 
01194                 tem = expand_node_name (current_node);
01195                 fn = nodename_to_filename (tem);
01196                 free (tem);
01197                 fputs (fn, anchor_stream);
01198                 free (fn);
01199               }
01200              else
01201               {
01202                 char *base = filename_part (current_output_filename);
01203 
01204                 fputs (base, anchor_stream);
01205                 free (base);
01206               }
01207              fputs ("#", anchor_stream);
01208              for (p = anchor; *p; p++)
01209               {
01210                 if (*p == '&')
01211                   fputs ("&amp;", anchor_stream);
01212                 else if (!URL_SAFE_CHAR (*p))
01213                   fprintf (anchor_stream, "%%%x", (unsigned char) *p);
01214                 else
01215                   fputc (*p, anchor_stream);
01216               }
01217              fputs ("\">\n", anchor_stream);
01218              fclose (anchor_stream);
01219            }
01220        }
01221     }
01222   else if (xml)
01223     {
01224       xml_insert_element_with_attribute (ANCHOR, START, "name=\"%s\"", anchor);
01225       xml_insert_element (ANCHOR, END);
01226     }
01227   /* Save it in the tag table.  */
01228   remember_node (anchor, NULL, NULL, NULL,
01229                  output_position + output_paragraph_offset,
01230                  line_number, fname_for_anchor, TAG_FLAG_ANCHOR);
01231 }
01232 
01233 /* Find NODE in REF_LIST. */
01234 static NODE_REF *
01235 find_node_reference (char *node, NODE_REF *ref_list)
01236 {
01237   NODE_REF *orig_ref_list = ref_list;
01238   char *expanded_node;
01239 
01240   while (ref_list)
01241     {
01242       if (strcmp (node, ref_list->node) == 0)
01243         break;
01244       ref_list = ref_list->next;
01245     }
01246 
01247   if (ref_list || !expensive_validation)
01248     return ref_list;
01249 
01250   /* Maybe NODE is not expanded yet.  This may be SLOW.  */
01251   expanded_node = expand_node_name (node);
01252   for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next)
01253     {
01254       if (STREQ (expanded_node, ref_list->node))
01255         break;
01256       if (strchr (ref_list->node, COMMAND_PREFIX))
01257         {
01258           char *expanded_ref = expand_node_name (ref_list->node);
01259 
01260           if (STREQ (expanded_node, expanded_ref))
01261             {
01262               free (expanded_ref);
01263               break;
01264             }
01265           free (expanded_ref);
01266         }
01267     }
01268   free (expanded_node);
01269   return ref_list;
01270 }
01271 
01272 void
01273 free_node_references (void)
01274 {
01275   NODE_REF *list, *temp;
01276 
01277   list = node_references;
01278 
01279   while (list)
01280     {
01281       temp = list;
01282       free (list->node);
01283       free (list->containing_node);
01284       list = list->next;
01285       free (temp);
01286     }
01287   node_references = NULL;
01288 }
01289 
01290 void
01291 free_node_node_references (void)
01292 {
01293   NODE_REF *list, *temp;
01294 
01295   list = node_references;
01296 
01297   while (list)
01298     {
01299       temp = list;
01300       free (list->node);
01301       list = list->next;
01302       free (temp);
01303     }
01304   node_node_references = NULL;
01305 }
01306 
01307 /* Return the number assigned to a named node in either the tag_table
01308    or node_references list or zero if no number has been assigned. */
01309 int
01310 number_of_node (char *node)
01311 {
01312   NODE_REF *temp_ref;
01313   TAG_ENTRY *temp_node = find_node (node);
01314 
01315   if (temp_node)
01316     return temp_node->number;
01317   else if ((temp_ref = find_node_reference (node, node_references)))
01318     return temp_ref->number;
01319   else if ((temp_ref = find_node_reference (node, node_node_references)))
01320     return temp_ref->number;
01321   else
01322     return 0;
01323 }
01324 
01325 /* validation */
01326 
01327 /* Return 1 if TAG (at LINE) correctly validated, or 0 if not.
01328    LABEL is the (translated) description of the type of reference --
01329    Menu, Cross, Next, etc.  */
01330 
01331 static int
01332 validate (char *tag, int line, const char *label)
01333 {
01334   TAG_ENTRY *result;
01335 
01336   /* If there isn't a tag to verify, or if the tag is in another file,
01337      then it must be okay. */
01338   if (!tag || !*tag || *tag == '(')
01339     return 1;
01340 
01341   /* Otherwise, the tag must exist. */
01342   result = find_node (tag);
01343 
01344   if (!result)
01345     {
01346       line_number = line;
01347       line_error (_("%s reference to nonexistent node `%s' (perhaps incorrect sectioning?)"), label, tag);
01348       return 0;
01349     }
01350   result->touched++;
01351   return 1;
01352 }
01353 
01354 /* The strings here are followed in the message by `reference to...' in
01355    the `validate' routine.  They are only used in messages, thus are
01356    translated.  */
01357 static const char *
01358 reftype_type_string (enum reftype type)
01359 {
01360   switch (type)
01361     {
01362     case menu_reference:
01363       return _("Menu");
01364     case followed_reference:
01365       return _("Cross");
01366     default:
01367       return "Internal-bad-reference-type";
01368     }
01369 }
01370 
01371 static void
01372 validate_other_references (NODE_REF *ref_list)
01373 {
01374   char *old_input_filename = input_filename;
01375 
01376   while (ref_list)
01377     {
01378       input_filename = ref_list->filename;
01379       validate (ref_list->node, ref_list->line_no,
01380                 reftype_type_string (ref_list->type));
01381       ref_list = ref_list->next;
01382     }
01383   input_filename = old_input_filename;
01384 }
01385 
01386 /* Validation of an info file.
01387    Scan through the list of tag entries touching the Prev, Next, and Up
01388    elements of each.  It is an error not to be able to touch one of them,
01389    except in the case of external node references, such as "(DIR)".
01390 
01391    If the Prev is different from the Up,
01392    then the Prev node must have a Next pointing at this node.
01393 
01394    Every node except Top must have an Up.
01395    The Up node must contain some sort of reference, other than a Next,
01396    to this node.
01397 
01398    If the Next is different from the Next of the Up,
01399    then the Next node must have a Prev pointing at this node. */
01400 void
01401 validate_file (TAG_ENTRY *tag_table)
01402 {
01403   char *old_input_filename = input_filename;
01404   TAG_ENTRY *tags = tag_table;
01405 
01406   while (tags)
01407     {
01408       TAG_ENTRY *temp_tag;
01409       char *tem1, *tem2;
01410 
01411       input_filename = tags->filename;
01412       line_number = tags->line_no;
01413 
01414       /* If this is a "no warn" node, don't validate it in any way. */
01415       if (tags->flags & TAG_FLAG_NO_WARN)
01416         {
01417           tags = tags->next_ent;
01418           continue;
01419         }
01420 
01421       /* If this node has a Next, then make sure that the Next exists. */
01422       if (tags->next)
01423         {
01424           validate (tags->next, tags->line_no, _("Next"));
01425 
01426           /* If the Next node exists, and there is no Up, then make sure
01427              that the Prev of the Next points back.  But do nothing if
01428              we aren't supposed to issue warnings about this node. */
01429           temp_tag = find_node (tags->next);
01430           if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN))
01431             {
01432               char *prev = temp_tag->prev;
01433               int you_lose = !prev || !STREQ (prev, tags->node);
01434 
01435               if (you_lose && expensive_validation)
01436                 {
01437                   tem1 = expand_node_name (prev);
01438                   tem2 = expand_node_name (tags->node);
01439 
01440                   if (tem1 && tem2 && STREQ (tem1, tem2))
01441                     you_lose = 0;
01442                   free (tem1);
01443                   free (tem2);
01444                 }
01445               if (you_lose)
01446                 {
01447                   line_error (_("Next field of node `%s' not pointed to (perhaps incorrect sectioning?)"),
01448                               tags->node);
01449                   file_line_error (temp_tag->filename, temp_tag->line_no,
01450                                _("This node (%s) has the bad Prev"),
01451                                temp_tag->node);
01452                   temp_tag->flags |= TAG_FLAG_PREV_ERROR;
01453                 }
01454             }
01455         }
01456 
01457       /* Validate the Prev field if there is one, and we haven't already
01458          complained about it in some way.  You don't have to have a Prev
01459          field at this stage. */
01460       if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev)
01461         {
01462           int valid_p = validate (tags->prev, tags->line_no, _("Prev"));
01463 
01464           if (!valid_p)
01465             tags->flags |= TAG_FLAG_PREV_ERROR;
01466           else
01467             { /* If the Prev field is not the same as the Up field,
01468                  then the node pointed to by the Prev field must have
01469                  a Next field which points to this node. */
01470               int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up);
01471 
01472               if (!prev_equals_up && expensive_validation)
01473                 {
01474                   tem1 = expand_node_name (tags->prev);
01475                   tem2 = expand_node_name (tags->up);
01476                   prev_equals_up = STREQ (tem1, tem2);
01477                   free (tem1);
01478                   free (tem2);
01479                 }
01480               if (!prev_equals_up)
01481                 {
01482                   temp_tag = find_node (tags->prev);
01483 
01484                   /* If we aren't supposed to issue warnings about the
01485                      target node, do nothing. */
01486                   if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN))
01487                     /* Do nothing. */ ;
01488                   else
01489                     {
01490                       int you_lose = !temp_tag->next
01491                         || !STREQ (temp_tag->next, tags->node);
01492 
01493                       if (temp_tag->next && you_lose && expensive_validation)
01494                         {
01495                           tem1 = expand_node_name (temp_tag->next);
01496                           tem2 = expand_node_name (tags->node);
01497                           if (STREQ (tem1, tem2))
01498                             you_lose = 0;
01499                           free (tem1);
01500                           free (tem2);
01501                         }
01502                       if (you_lose)
01503                         {
01504                           line_error
01505                             (_("Prev field of node `%s' not pointed to"),
01506                              tags->node);
01507                           file_line_error (temp_tag->filename,
01508                                       temp_tag->line_no,
01509                                       _("This node (%s) has the bad Next"),
01510                                       temp_tag->node);
01511                           temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
01512                         }
01513                     }
01514                 }
01515             }
01516         }
01517 
01518       if (!tags->up
01519           && !(tags->flags & TAG_FLAG_ANCHOR)
01520           && strcasecmp (tags->node, "Top") != 0)
01521         line_error (_("`%s' has no Up field (perhaps incorrect sectioning?)"), tags->node);
01522       else if (tags->up)
01523         {
01524           int valid_p = validate (tags->up, tags->line_no, _("Up"));
01525 
01526           /* If node X has Up: Y, then warn if Y fails to have a menu item
01527              or note pointing at X, if Y isn't of the form "(Y)". */
01528           if (valid_p && *tags->up != '(')
01529             {
01530               NODE_REF *nref;
01531               NODE_REF *tref = NULL;
01532               NODE_REF *list = node_references;
01533 
01534               for (;;)
01535                 {
01536                   nref = find_node_reference (tags->node, list);
01537                   if (!nref)
01538                     break;
01539 
01540                   if (strcmp (nref->containing_node, tags->up) == 0)
01541                     {
01542                       if (nref->type != menu_reference)
01543                         {
01544                           tref = nref;
01545                           list = nref->next;
01546                         }
01547                       else
01548                         break;
01549                     }
01550                   list = nref->next;
01551                 }
01552 
01553               if (!nref)
01554                 {
01555                 if (!tref && expensive_validation)
01556                   {
01557                     /* Sigh...  This might be AWFULLY slow, but if
01558                        they want this feature, they'll have to pay!
01559                        We do all the loop again expanding each
01560                        containing_node reference as we go.  */
01561                     char *tags_up = expand_node_name (tags->up);
01562                     char *tem;
01563 
01564                     list = node_references;
01565 
01566                     for (;;)
01567                      {
01568                        nref = find_node_reference (tags->node, list);
01569                        if (!nref)
01570                          break;
01571                        tem = expand_node_name (nref->containing_node);
01572                        if (STREQ (tem, tags_up))
01573                          {
01574                            if (nref->type != menu_reference)
01575                             tref = nref;
01576                            else
01577                             {
01578                               free (tem);
01579                               break;
01580                             }
01581                          }
01582                        free (tem);
01583                        list = nref->next;
01584                      }
01585                   }
01586                   if (!nref && !tref)
01587                     {
01588                       temp_tag = find_node (tags->up);
01589                       file_line_error (temp_tag->filename, temp_tag->line_no,
01590            _("Node `%s' lacks menu item for `%s' despite being its Up target"),
01591                                   tags->up, tags->node);
01592                     }
01593                 }
01594             }
01595         }
01596       tags = tags->next_ent;
01597     }
01598 
01599   validate_other_references (node_references);
01600   /* We have told the user about the references which didn't exist.
01601      Now tell him about the nodes which aren't referenced. */
01602 
01603   for (tags = tag_table; tags; tags = tags->next_ent)
01604     {
01605       /* If this node is a "no warn" node, do nothing. */
01606       if (tags->flags & TAG_FLAG_NO_WARN)
01607         {
01608           tags = tags->next_ent;
01609           continue;
01610         }
01611 
01612       /* Special hack.  If the node in question appears to have
01613          been referenced more than REFERENCE_WARNING_LIMIT times,
01614          give a warning. */
01615       if (tags->touched > reference_warning_limit)
01616         {
01617           input_filename = tags->filename;
01618           line_number = tags->line_no;
01619           warning (_("node `%s' has been referenced %d times"),
01620                    tags->node, tags->touched);
01621         }
01622 
01623       if (tags->touched == 0)
01624         {
01625           input_filename = tags->filename;
01626           line_number = tags->line_no;
01627 
01628           /* Notice that the node "Top" is special, and doesn't have to
01629              be referenced.   Anchors don't have to be referenced
01630              either, you might define them for another document.  */
01631           if (strcasecmp (tags->node, "Top") != 0
01632               && !(tags->flags & TAG_FLAG_ANCHOR))
01633             warning (_("unreferenced node `%s'"), tags->node);
01634         }
01635     }
01636   input_filename = old_input_filename;
01637 }
01638 
01639 
01640 /* Splitting */
01641 
01642 /* Return true if the tag entry pointed to by TAGS is the last node.
01643    This means only anchors follow.  */
01644 
01645 static int
01646 last_node_p (TAG_ENTRY *tags)
01647 {
01648   int last = 1;
01649   while (tags->next_ent) {
01650     tags = tags->next_ent;
01651     if (tags->flags & TAG_FLAG_ANCHOR)
01652       ;
01653     else
01654       {
01655         last = 0;
01656         break;
01657       }
01658   }
01659 
01660   return last;
01661 }
01662 
01663 
01664 static char *
01665 enumerate_filename (char *pathname, char *basename, int number)
01666 {
01667   /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */
01668   const int dos_file_names = !HAVE_LONG_FILENAMES (pathname ? pathname : ".");
01669   unsigned name_len = strlen (basename);
01670   char *filename = xmalloc (10 + strlen (pathname) + name_len);
01671   char *base_filename = xmalloc (10 + name_len);
01672 
01673   sprintf (base_filename, "%s-%d", basename, number);
01674 
01675   if (dos_file_names)
01676     {
01677       char *dot = strchr (base_filename, '.');
01678       unsigned base_len = strlen (base_filename);
01679 
01680       if (dot)
01681         { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
01682           dot[1] = 'i';
01683           memmove (number <= 99 ? dot + 2 : dot + 1,
01684               base_filename + name_len + 1,
01685               strlen (base_filename + name_len + 1) + 1);
01686         }
01687       else if (base_len > 8)
01688         {
01689           /* Make foobar-1, .., fooba-10, .., foob-100, ... */
01690           unsigned numlen = base_len - name_len;
01691 
01692           memmove (base_filename + 8 - numlen, base_filename + name_len, numlen + 1);
01693         }
01694     }
01695 
01696   sprintf (filename, "%s%s", pathname, base_filename);
01697 
01698   return filename;
01699 }
01700 
01701 /* Remove previously split files, to avoid
01702    lingering parts of shrinked documents.  */
01703 void
01704 clean_old_split_files (char *filename)
01705 {
01706   char *root_filename = filename_part (filename);
01707   char *root_pathname = pathname_part (filename);
01708   int i;
01709 
01710   /* We break as soon as we hit an inexistent file,
01711      so looping until large numbers is harmless.  */
01712   for (i = 1; i < 1000; i++)
01713     {
01714       struct stat st;
01715       char *check_file = enumerate_filename (root_pathname, root_filename, i);
01716 
01717       if (stat (check_file, &st) != 0)
01718         break;
01719       else if (!S_ISDIR (st.st_mode))
01720         {
01721           /* Give feedback if requested, removing a file is important.  */
01722           if (verbose_mode)
01723             printf (_("Removing %s\n"), check_file);
01724 
01725           /* Warn user that we cannot remove the file.  */
01726           if (unlink (check_file) != 0)
01727             warning (_("Can't remove file `%s': %s"), check_file, strerror (errno));
01728         }
01729 
01730       free (check_file);
01731     }
01732 }
01733 
01734 
01735 /* Split large output files into a series of smaller files.  Each file
01736    is pointed to in the tag table, which then gets written out as the
01737    original file.  The new files have the same name as the original file
01738    with a "-num" attached.  SIZE is the largest number of bytes to allow
01739    in any single split file. */
01740 void
01741 split_file (char *filename, int size)
01742 {
01743   char *root_filename, *root_pathname;
01744   char *the_file;
01745   struct stat fileinfo;
01746   long file_size;
01747   char *the_header;
01748   int header_size;
01749 
01750   /* Can only do this to files with tag tables. */
01751   if (!tag_table)
01752     return;
01753 
01754   if (size == 0)
01755     size = DEFAULT_SPLIT_SIZE;
01756 
01757   if ((stat (filename, &fileinfo) != 0)
01758       || (((long) fileinfo.st_size) < size))
01759     return;
01760   file_size = (long) fileinfo.st_size;
01761 
01762   the_file = find_and_load (filename, 0);
01763   if (!the_file)
01764     return;
01765 
01766   root_filename = filename_part (filename);
01767   root_pathname = pathname_part (filename);
01768 
01769   if (!root_pathname)
01770     root_pathname = xstrdup ("");
01771 
01772   /* Start splitting the file.  Walk along the tag table
01773      outputting sections of the file.  When we have written
01774      all of the nodes in the tag table, make the top-level
01775      pointer file, which contains indirect pointers and
01776      tags for the nodes. */
01777   {
01778     int which_file = 1;
01779     TAG_ENTRY *tags = tag_table;
01780     char *indirect_info = NULL;
01781 
01782     /* Maybe we want a Local Variables section.  */
01783     char *trailer = info_trailer ();
01784     int trailer_len = trailer ? strlen (trailer) : 0;
01785 
01786     /* Remember the `header' of this file.  The first tag in the file is
01787        the bottom of the header; the top of the file is the start. */
01788     the_header = xmalloc (1 + (header_size = tags->position));
01789     memcpy (the_header, the_file, header_size);
01790 
01791     while (tags)
01792       {
01793         int file_top, file_bot, limit;
01794 
01795         /* Have to include the Control-_. */
01796         file_top = file_bot = tags->position;
01797         limit = file_top + size;
01798 
01799         /* If the rest of this file is only one node, then
01800            that is the entire subfile. */
01801         if (last_node_p (tags))
01802           {
01803             int i = tags->position + 1;
01804             char last_char = the_file[i];
01805 
01806             while (i < file_size)
01807               {
01808                 if ((the_file[i] == '\037') &&
01809                     ((last_char == '\n') ||
01810                      (last_char == '\014')))
01811                   break;
01812                 else
01813                   last_char = the_file[i];
01814                 i++;
01815               }
01816             file_bot = i;
01817             tags = tags->next_ent;
01818             goto write_region;
01819           }
01820 
01821         /* Otherwise, find the largest number of nodes that can fit in
01822            this subfile. */
01823         for (; tags; tags = tags->next_ent)
01824           {
01825             if (last_node_p (tags))
01826               {
01827                 /* This entry is the last node.  Search forward for the end
01828                    of this node, and that is the end of this file. */
01829                 int i = tags->position + 1;
01830                 char last_char = the_file[i];
01831 
01832                 while (i < file_size)
01833                   {
01834                     if ((the_file[i] == '\037') &&
01835                         ((last_char == '\n') ||
01836                          (last_char == '\014')))
01837                       break;
01838                     else
01839                       last_char = the_file[i];
01840                     i++;
01841                   }
01842                 file_bot = i;
01843 
01844                 if (file_bot < limit)
01845                   {
01846                     tags = tags->next_ent;
01847                     goto write_region;
01848                   }
01849                 else
01850                   {
01851                     /* Here we want to write out everything before the last
01852                        node, and then write the last node out in a file
01853                        by itself. */
01854                     file_bot = tags->position;
01855                     goto write_region;
01856                   }
01857               }
01858 
01859             /* Write region only if this was a node, not an anchor.  */
01860             if (tags->next_ent->position > limit
01861                 && !(tags->flags & TAG_FLAG_ANCHOR))
01862               {
01863                 if (tags->position == file_top)
01864                   tags = tags->next_ent;
01865 
01866                 file_bot = tags->position;
01867 
01868               write_region:
01869                 {
01870                   int fd;
01871                   char *split_filename = enumerate_filename (root_pathname,
01872                       root_filename, which_file);
01873                   char *split_basename = filename_part (split_filename);
01874 
01875                   fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
01876                   if (fd < 0
01877                       || write (fd, the_header, header_size) != header_size
01878                       || write (fd, the_file + file_top, file_bot - file_top)
01879                          != (file_bot - file_top)
01880                       || (trailer_len
01881                           && write (fd, trailer, trailer_len) != trailer_len)
01882                       || close (fd) < 0)
01883                     {
01884                       perror (split_filename);
01885                       if (fd != -1)
01886                         close (fd);
01887                       xexit (1);
01888                     }
01889 
01890                   if (!indirect_info)
01891                     {
01892                       indirect_info = the_file + file_top;
01893                       sprintf (indirect_info, "\037\nIndirect:\n");
01894                       indirect_info += strlen (indirect_info);
01895                     }
01896 
01897                   sprintf (indirect_info, "%s: %d\n",
01898                            split_basename, file_top);
01899 
01900                   free (split_basename);
01901                   free (split_filename);
01902                   indirect_info += strlen (indirect_info);
01903                   which_file++;
01904                   break;
01905                 }
01906               }
01907           }
01908       }
01909 
01910     /* We have sucessfully created the subfiles.  Now write out the
01911        original again.  We must use `output_stream', or
01912        write_tag_table_indirect () won't know where to place the output. */
01913     output_stream = fopen (filename, "w");
01914     if (!output_stream)
01915       {
01916         perror (filename);
01917         xexit (1);
01918       }
01919 
01920     {
01921       int distance = indirect_info - the_file;
01922       fwrite (the_file, 1, distance, output_stream);
01923 
01924       /* Inhibit newlines. */
01925       paragraph_is_open = 0;
01926 
01927       /* Write the indirect tag table.  */
01928       write_tag_table_indirect ();
01929 
01930       /* preserve local variables in info output.  */
01931       if (trailer)
01932         {
01933           fwrite (trailer, 1, trailer_len, output_stream);
01934           free (trailer);
01935         }
01936 
01937       fclose (output_stream);
01938       free (the_header);
01939       free (the_file);
01940       return;
01941     }
01942   }
01943 }