xref: /openbsd-src/gnu/usr.bin/texinfo/makeinfo/node.c (revision 1076333c323c9f213f0d653fc52002328f47dbe9)
11cc83814Sespie /* node.c -- nodes for Texinfo.
2*1076333cSespie    $Id: node.c,v 1.3 2006/07/17 16:12:36 espie Exp $
31cc83814Sespie 
4*1076333cSespie    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
5*1076333cSespie    Foundation, Inc.
61cc83814Sespie 
71cc83814Sespie    This program is free software; you can redistribute it and/or modify
81cc83814Sespie    it under the terms of the GNU General Public License as published by
91cc83814Sespie    the Free Software Foundation; either version 2, or (at your option)
101cc83814Sespie    any later version.
111cc83814Sespie 
121cc83814Sespie    This program is distributed in the hope that it will be useful,
131cc83814Sespie    but WITHOUT ANY WARRANTY; without even the implied warranty of
141cc83814Sespie    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
151cc83814Sespie    GNU General Public License for more details.
161cc83814Sespie 
171cc83814Sespie    You should have received a copy of the GNU General Public License
181cc83814Sespie    along with this program; if not, write to the Free Software Foundation,
191cc83814Sespie    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
201cc83814Sespie 
211cc83814Sespie #include "system.h"
221cc83814Sespie #include "cmds.h"
231cc83814Sespie #include "files.h"
24*1076333cSespie #include "float.h"
251cc83814Sespie #include "footnote.h"
261cc83814Sespie #include "macro.h"
271cc83814Sespie #include "makeinfo.h"
281cc83814Sespie #include "node.h"
293fb98d4aSespie #include "html.h"
301cc83814Sespie #include "sectioning.h"
311cc83814Sespie #include "insertion.h"
323fb98d4aSespie #include "xml.h"
331cc83814Sespie 
341cc83814Sespie /* See comments in node.h.  */
351cc83814Sespie NODE_REF *node_references = NULL;
361cc83814Sespie NODE_REF *node_node_references = NULL;
371cc83814Sespie TAG_ENTRY *tag_table = NULL;
381cc83814Sespie int node_number = -1;
39*1076333cSespie int node_order = 0;
401cc83814Sespie int current_section = 0;
411cc83814Sespie int outstanding_node = 0;
421cc83814Sespie 
431cc83814Sespie /* Adding nodes, and making tags.  */
441cc83814Sespie 
451cc83814Sespie /* Start a new tag table. */
461cc83814Sespie void
init_tag_table(void)47*1076333cSespie init_tag_table (void)
481cc83814Sespie {
491cc83814Sespie   while (tag_table)
501cc83814Sespie     {
511cc83814Sespie       TAG_ENTRY *temp = tag_table;
521cc83814Sespie       free (temp->node);
531cc83814Sespie       free (temp->prev);
541cc83814Sespie       free (temp->next);
551cc83814Sespie       free (temp->up);
561cc83814Sespie       tag_table = tag_table->next_ent;
571cc83814Sespie       free (temp);
581cc83814Sespie     }
591cc83814Sespie }
601cc83814Sespie 
611cc83814Sespie /* Write out the contents of the existing tag table.
621cc83814Sespie    INDIRECT_P says how to format the output (it depends on whether the
631cc83814Sespie    table is direct or indirect).  */
641cc83814Sespie static void
write_tag_table_internal(int indirect_p)65*1076333cSespie write_tag_table_internal (int indirect_p)
661cc83814Sespie {
671cc83814Sespie   TAG_ENTRY *node;
681cc83814Sespie   int old_indent = no_indent;
691cc83814Sespie 
703fb98d4aSespie   if (xml)
713fb98d4aSespie     {
723fb98d4aSespie       flush_output ();
733fb98d4aSespie       return;
743fb98d4aSespie     }
753fb98d4aSespie 
761cc83814Sespie   no_indent = 1;
771cc83814Sespie   filling_enabled = 0;
781cc83814Sespie   must_start_paragraph = 0;
791cc83814Sespie   close_paragraph ();
801cc83814Sespie 
811cc83814Sespie   if (!indirect_p)
821cc83814Sespie     {
831cc83814Sespie       no_indent = 1;
841cc83814Sespie       insert ('\n');
851cc83814Sespie     }
861cc83814Sespie 
871cc83814Sespie   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
881cc83814Sespie 
891cc83814Sespie   /* Do not collapse -- to -, etc., in node names.  */
901cc83814Sespie   in_fixed_width_font++;
911cc83814Sespie 
921cc83814Sespie   for (node = tag_table; node; node = node->next_ent)
931cc83814Sespie     {
941cc83814Sespie       if (node->flags & TAG_FLAG_ANCHOR)
951cc83814Sespie         { /* This reference is to an anchor.  */
961cc83814Sespie           execute_string ("Ref: %s", node->node);
971cc83814Sespie         }
981cc83814Sespie       else
991cc83814Sespie         { /* This reference is to a node.  */
1001cc83814Sespie           execute_string ("Node: %s", node->node);
1011cc83814Sespie         }
1021cc83814Sespie       add_word_args ("\177%d\n", node->position);
1031cc83814Sespie     }
1041cc83814Sespie 
1051cc83814Sespie   add_word ("\037\nEnd Tag Table\n");
1061cc83814Sespie 
1071cc83814Sespie   /* Do not collapse -- to -, etc., in node names.  */
1081cc83814Sespie   in_fixed_width_font--;
1091cc83814Sespie 
1101cc83814Sespie   flush_output ();
1111cc83814Sespie   no_indent = old_indent;
1121cc83814Sespie }
1131cc83814Sespie 
1141cc83814Sespie void
write_tag_table(char * filename)115*1076333cSespie write_tag_table (char *filename)
1161cc83814Sespie {
117*1076333cSespie   output_stream = fopen (filename, "a");
118*1076333cSespie   if (!output_stream)
119*1076333cSespie     {
120*1076333cSespie       fs_error (filename);
121*1076333cSespie       return;
1221cc83814Sespie     }
1231cc83814Sespie 
124*1076333cSespie   write_tag_table_internal (0); /* Not indirect. */
125*1076333cSespie 
126*1076333cSespie   if (fclose (output_stream) != 0)
127*1076333cSespie     fs_error (filename);
128*1076333cSespie }
129*1076333cSespie 
130*1076333cSespie static void
write_tag_table_indirect(void)131*1076333cSespie write_tag_table_indirect (void)
1321cc83814Sespie {
1331cc83814Sespie   write_tag_table_internal (1);
1341cc83814Sespie }
1351cc83814Sespie 
1361cc83814Sespie /* Convert "top" and friends into "Top". */
1371cc83814Sespie static void
normalize_node_name(char * string)138*1076333cSespie normalize_node_name (char *string)
1391cc83814Sespie {
1401cc83814Sespie   if (strcasecmp (string, "Top") == 0)
1411cc83814Sespie     strcpy (string, "Top");
1421cc83814Sespie }
1431cc83814Sespie 
144*1076333cSespie static char *
get_node_token(int expand)145*1076333cSespie get_node_token (int expand)
1461cc83814Sespie {
1471cc83814Sespie   char *string;
1481cc83814Sespie 
1491cc83814Sespie   get_until_in_line (expand, ",", &string);
1501cc83814Sespie 
1511cc83814Sespie   if (curchar () == ',')
1521cc83814Sespie     input_text_offset++;
1531cc83814Sespie 
1541cc83814Sespie   fix_whitespace (string);
1551cc83814Sespie 
1561cc83814Sespie   /* Force all versions of "top" to be "Top". */
1571cc83814Sespie   normalize_node_name (string);
1581cc83814Sespie 
1591cc83814Sespie   return string;
1601cc83814Sespie }
1611cc83814Sespie 
1621cc83814Sespie /* Expand any macros and other directives in a node name, and
1631cc83814Sespie    return the expanded name as an malloc'ed string.  */
1641cc83814Sespie char *
expand_node_name(char * node)165*1076333cSespie expand_node_name (char *node)
1661cc83814Sespie {
1671cc83814Sespie   char *result = node;
1681cc83814Sespie 
1691cc83814Sespie   if (node)
1701cc83814Sespie     {
1711cc83814Sespie       /* Don't expand --, `` etc., in case somebody will want
1721cc83814Sespie          to print the result.  */
1731cc83814Sespie       in_fixed_width_font++;
1741cc83814Sespie       result = expansion (node, 0);
1751cc83814Sespie       in_fixed_width_font--;
1761cc83814Sespie       fix_whitespace (result);
1771cc83814Sespie       normalize_node_name (result);
1781cc83814Sespie     }
1791cc83814Sespie   return result;
1801cc83814Sespie }
1811cc83814Sespie 
1821cc83814Sespie /* Look up NAME in the tag table, and return the associated
1831cc83814Sespie    tag_entry.  If the node is not in the table return NULL. */
1841cc83814Sespie TAG_ENTRY *
find_node(char * name)185*1076333cSespie find_node (char *name)
1861cc83814Sespie {
1871cc83814Sespie   TAG_ENTRY *tag = tag_table;
1881cc83814Sespie   char *expanded_name;
1891cc83814Sespie   char n1 = name[0];
1901cc83814Sespie 
1911cc83814Sespie   while (tag)
1921cc83814Sespie     {
1931cc83814Sespie       if (tag->node[0] == n1 && strcmp (tag->node, name) == 0)
1941cc83814Sespie         return tag;
1951cc83814Sespie       tag = tag->next_ent;
1961cc83814Sespie     }
1971cc83814Sespie 
1981cc83814Sespie   if (!expensive_validation)
1991cc83814Sespie     return NULL;
2001cc83814Sespie 
2011cc83814Sespie   /* Try harder.  Maybe TAG_TABLE has the expanded NAME, or maybe NAME
2021cc83814Sespie      is expanded while TAG_TABLE has its unexpanded form.  This may
2031cc83814Sespie      slow down the search, but if they want this feature, let them
2041cc83814Sespie      pay!  If they want it fast, they should write every node name
2051cc83814Sespie      consistently (either always expanded or always unexpaned).  */
2061cc83814Sespie   expanded_name = expand_node_name (name);
2071cc83814Sespie   for (tag = tag_table; tag; tag = tag->next_ent)
2081cc83814Sespie     {
2091cc83814Sespie       if (STREQ (tag->node, expanded_name))
2101cc83814Sespie         break;
2111cc83814Sespie       /* If the tag name doesn't have the command prefix, there's no
2121cc83814Sespie          chance it could expand into anything but itself.  */
2131cc83814Sespie       if (strchr (tag->node, COMMAND_PREFIX))
2141cc83814Sespie         {
2151cc83814Sespie           char *expanded_node = expand_node_name (tag->node);
2161cc83814Sespie 
2171cc83814Sespie           if (STREQ (expanded_node, expanded_name))
2181cc83814Sespie             {
2191cc83814Sespie               free (expanded_node);
2201cc83814Sespie               break;
2211cc83814Sespie             }
2221cc83814Sespie           free (expanded_node);
2231cc83814Sespie         }
2241cc83814Sespie     }
2251cc83814Sespie   free (expanded_name);
2261cc83814Sespie   return tag;
2271cc83814Sespie }
2281cc83814Sespie 
2293fb98d4aSespie /* Look in the tag table for a node whose file name is FNAME, and
2303fb98d4aSespie    return the associated tag_entry.  If there's no such node in the
2313fb98d4aSespie    table, return NULL. */
232*1076333cSespie static TAG_ENTRY *
find_node_by_fname(char * fname)233*1076333cSespie find_node_by_fname (char *fname)
2343fb98d4aSespie {
2353fb98d4aSespie   TAG_ENTRY *tag = tag_table;
2363fb98d4aSespie   while (tag)
2373fb98d4aSespie     {
2383fb98d4aSespie       if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0)
2393fb98d4aSespie 	return tag;
2403fb98d4aSespie       tag = tag->next_ent;
2413fb98d4aSespie     }
2423fb98d4aSespie 
2433fb98d4aSespie   return tag;
2443fb98d4aSespie }
2453fb98d4aSespie 
2463fb98d4aSespie /* Remember next, prev, etc. references in a @node command, where we
2471cc83814Sespie    don't care about most of the entries. */
2481cc83814Sespie static void
remember_node_node_reference(char * node)249*1076333cSespie remember_node_node_reference (char *node)
2501cc83814Sespie {
2511cc83814Sespie   NODE_REF *temp = xmalloc (sizeof (NODE_REF));
2521cc83814Sespie   int number;
2531cc83814Sespie 
2541cc83814Sespie   if (!node) return;
2551cc83814Sespie   temp->next = node_node_references;
2561cc83814Sespie   temp->node = xstrdup (node);
2571cc83814Sespie   temp->type = followed_reference;
2581cc83814Sespie   number = number_of_node (node);
2591cc83814Sespie   if (number)
2601cc83814Sespie     temp->number = number;      /* Already assigned. */
2611cc83814Sespie   else
2621cc83814Sespie     {
2631cc83814Sespie       node_number++;
2641cc83814Sespie       temp->number = node_number;
2651cc83814Sespie     }
2661cc83814Sespie   node_node_references = temp;
2671cc83814Sespie }
2681cc83814Sespie 
2691cc83814Sespie /* Remember NODE and associates. */
270*1076333cSespie static void
remember_node(char * node,char * prev,char * next,char * up,int position,int line_no,char * fname,int flags)271*1076333cSespie remember_node (char *node, char *prev, char *next, char *up,
272*1076333cSespie     int position, int line_no, char *fname, int flags)
2731cc83814Sespie {
2741cc83814Sespie   /* Check for existence of this tag already. */
2751cc83814Sespie   if (validating)
2761cc83814Sespie     {
2771cc83814Sespie       TAG_ENTRY *tag = find_node (node);
2781cc83814Sespie       if (tag)
2791cc83814Sespie         {
2801cc83814Sespie           line_error (_("Node `%s' previously defined at line %d"),
2811cc83814Sespie                       node, tag->line_no);
2821cc83814Sespie           return;
2831cc83814Sespie         }
2841cc83814Sespie     }
2851cc83814Sespie 
2861cc83814Sespie   if (!(flags & TAG_FLAG_ANCHOR))
2871cc83814Sespie     {
2881cc83814Sespie       /* Make this the current node. */
2891cc83814Sespie       current_node = node;
2901cc83814Sespie     }
2911cc83814Sespie 
2921cc83814Sespie   /* Add it to the list. */
2931cc83814Sespie   {
2941cc83814Sespie     int number = number_of_node (node);
2951cc83814Sespie 
2961cc83814Sespie     TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY));
2971cc83814Sespie     new->node = node;
2981cc83814Sespie     new->prev = prev;
2991cc83814Sespie     new->next = next;
3001cc83814Sespie     new->up = up;
3011cc83814Sespie     new->position = position;
3021cc83814Sespie     new->line_no = line_no;
3031cc83814Sespie     new->filename = node_filename;
3041cc83814Sespie     new->touched = 0;
3051cc83814Sespie     new->flags = flags;
3061cc83814Sespie     if (number)
3071cc83814Sespie       new->number = number;     /* Already assigned. */
3081cc83814Sespie     else
3091cc83814Sespie       {
3101cc83814Sespie         node_number++;
3111cc83814Sespie         new->number = node_number;
3121cc83814Sespie       }
313*1076333cSespie     if (fname)
3143fb98d4aSespie       new->html_fname = fname;
315*1076333cSespie     else
316*1076333cSespie       /* This happens for Top node under split-HTML, for example.  */
317*1076333cSespie       new->html_fname
318*1076333cSespie 	= normalize_filename (filename_part (current_output_filename));
3191cc83814Sespie     new->next_ent = tag_table;
320*1076333cSespie 
321*1076333cSespie     /* Increment the order counter, and save it.  */
322*1076333cSespie     node_order++;
323*1076333cSespie     new->order = node_order;
324*1076333cSespie 
3251cc83814Sespie     tag_table = new;
3261cc83814Sespie   }
3271cc83814Sespie 
3281cc83814Sespie   if (html)
3291cc83814Sespie     { /* Note the references to the next etc. nodes too.  */
3301cc83814Sespie       remember_node_node_reference (next);
3311cc83814Sespie       remember_node_node_reference (prev);
3321cc83814Sespie       remember_node_node_reference (up);
3331cc83814Sespie     }
3341cc83814Sespie }
3351cc83814Sespie 
3361cc83814Sespie /* Remember this node name for later validation use.  This is used to
3371cc83814Sespie    remember menu references while reading the input file.  After the
3381cc83814Sespie    output file has been written, if validation is on, then we use the
3391cc83814Sespie    contents of `node_references' as a list of nodes to validate.  */
3401cc83814Sespie void
remember_node_reference(char * node,int line,enum reftype type)341*1076333cSespie remember_node_reference (char *node, int line, enum reftype type)
3421cc83814Sespie {
3431cc83814Sespie   NODE_REF *temp = xmalloc (sizeof (NODE_REF));
3441cc83814Sespie   int number = number_of_node (node);
3451cc83814Sespie 
3461cc83814Sespie   temp->next = node_references;
3471cc83814Sespie   temp->node = xstrdup (node);
3481cc83814Sespie   temp->line_no = line;
3491cc83814Sespie   temp->section = current_section;
3501cc83814Sespie   temp->type = type;
3511cc83814Sespie   temp->containing_node = xstrdup (current_node ? current_node : "");
3521cc83814Sespie   temp->filename = node_filename;
3531cc83814Sespie   if (number)
3541cc83814Sespie     temp->number = number;      /* Already assigned. */
3551cc83814Sespie   else
3561cc83814Sespie     {
3571cc83814Sespie       node_number++;
3581cc83814Sespie       temp->number = node_number;
3591cc83814Sespie     }
3601cc83814Sespie 
3611cc83814Sespie   node_references = temp;
3621cc83814Sespie }
3631cc83814Sespie 
3641cc83814Sespie static void
isolate_nodename(char * nodename)365*1076333cSespie isolate_nodename (char *nodename)
3661cc83814Sespie {
3671cc83814Sespie   int i, c;
3681cc83814Sespie   int paren_seen, paren;
3691cc83814Sespie 
3701cc83814Sespie   if (!nodename)
3711cc83814Sespie     return;
3721cc83814Sespie 
3731cc83814Sespie   canon_white (nodename);
3741cc83814Sespie   paren_seen = paren = i = 0;
3751cc83814Sespie 
3761cc83814Sespie   if (*nodename == '.' || !*nodename)
3771cc83814Sespie     {
3781cc83814Sespie       *nodename = 0;
3791cc83814Sespie       return;
3801cc83814Sespie     }
3811cc83814Sespie 
3821cc83814Sespie   if (*nodename == '(')
3831cc83814Sespie     {
3841cc83814Sespie       paren++;
3851cc83814Sespie       paren_seen++;
3861cc83814Sespie       i++;
3871cc83814Sespie     }
3881cc83814Sespie 
3891cc83814Sespie   for (; (c = nodename[i]); i++)
3901cc83814Sespie     {
3911cc83814Sespie       if (paren)
3921cc83814Sespie         {
3931cc83814Sespie           if (c == '(')
3941cc83814Sespie             paren++;
3951cc83814Sespie           else if (c == ')')
3961cc83814Sespie             paren--;
3971cc83814Sespie 
3981cc83814Sespie           continue;
3991cc83814Sespie         }
4001cc83814Sespie 
4011cc83814Sespie       /* If the character following the close paren is a space, then this
4021cc83814Sespie          node has no more characters associated with it. */
4031cc83814Sespie       if (c == '\t' ||
4041cc83814Sespie           c == '\n' ||
4051cc83814Sespie           c == ','  ||
4061cc83814Sespie           ((paren_seen && nodename[i - 1] == ')') &&
4071cc83814Sespie            (c == ' ' || c == '.')) ||
4081cc83814Sespie           (c == '.' &&
4091cc83814Sespie            ((!nodename[i + 1] ||
4101cc83814Sespie              (cr_or_whitespace (nodename[i + 1])) ||
4111cc83814Sespie              (nodename[i + 1] == ')')))))
4121cc83814Sespie         break;
4131cc83814Sespie     }
4141cc83814Sespie   nodename[i] = 0;
4151cc83814Sespie }
4161cc83814Sespie 
4171cc83814Sespie /* This function gets called at the start of every line while inside a
4181cc83814Sespie    menu.  It checks to see if the line starts with "* ", and if so and
4191cc83814Sespie    REMEMBER_REF is nonzero, remembers the node reference as type
4201cc83814Sespie    REF_TYPE that this menu refers to.  input_text_offset is at the \n
4211cc83814Sespie    just before the menu line.  If REMEMBER_REF is zero, REF_TYPE is unused.  */
4221cc83814Sespie #define MENU_STARTER "* "
4231cc83814Sespie char *
glean_node_from_menu(int remember_ref,enum reftype ref_type)424*1076333cSespie glean_node_from_menu (int remember_ref, enum reftype ref_type)
4251cc83814Sespie {
4261cc83814Sespie   int i, orig_offset = input_text_offset;
4271cc83814Sespie   char *nodename;
4281cc83814Sespie   char *line, *expanded_line;
4291cc83814Sespie   char *old_input = input_text;
4303fb98d4aSespie   int old_size = input_text_length;
4311cc83814Sespie 
4321cc83814Sespie   if (strncmp (&input_text[input_text_offset + 1],
4331cc83814Sespie                MENU_STARTER,
4341cc83814Sespie                strlen (MENU_STARTER)) != 0)
4351cc83814Sespie     return NULL;
4361cc83814Sespie   else
4371cc83814Sespie     input_text_offset += strlen (MENU_STARTER) + 1;
4381cc83814Sespie 
4391cc83814Sespie   /* The menu entry might include macro calls, so we need to expand them.  */
4401cc83814Sespie   get_until ("\n", &line);
4411cc83814Sespie   only_macro_expansion++;       /* only expand macros in menu entries */
4421cc83814Sespie   expanded_line = expansion (line, 0);
4431cc83814Sespie   only_macro_expansion--;
4441cc83814Sespie   free (line);
4451cc83814Sespie   input_text = expanded_line;
4461cc83814Sespie   input_text_offset = 0;
4471cc83814Sespie   input_text_length = strlen (expanded_line);
4481cc83814Sespie 
4491cc83814Sespie   get_until_in_line (0, ":", &nodename);
4501cc83814Sespie   if (curchar () == ':')
4511cc83814Sespie     input_text_offset++;
4521cc83814Sespie 
4531cc83814Sespie   if (curchar () != ':')
4541cc83814Sespie     {
4551cc83814Sespie       free (nodename);
4561cc83814Sespie       get_until_in_line (0, "\n", &nodename);
4571cc83814Sespie       isolate_nodename (nodename);
4581cc83814Sespie     }
4591cc83814Sespie 
4601cc83814Sespie   input_text = old_input;
4611cc83814Sespie   input_text_offset = orig_offset;
4621cc83814Sespie   input_text_length = old_size;
4631cc83814Sespie   free (expanded_line);
4641cc83814Sespie   fix_whitespace (nodename);
4651cc83814Sespie   normalize_node_name (nodename);
4661cc83814Sespie   i = strlen (nodename);
4671cc83814Sespie   if (i && nodename[i - 1] == ':')
4681cc83814Sespie     nodename[i - 1] = 0;
4691cc83814Sespie 
4701cc83814Sespie   if (remember_ref)
4711cc83814Sespie     remember_node_reference (nodename, line_number, ref_type);
4721cc83814Sespie 
4731cc83814Sespie   return nodename;
4741cc83814Sespie }
4751cc83814Sespie 
4761cc83814Sespie /* Set the name of the current output file.  */
4771cc83814Sespie void
set_current_output_filename(const char * fname)478*1076333cSespie set_current_output_filename (const char *fname)
4791cc83814Sespie {
4801cc83814Sespie   if (current_output_filename)
4811cc83814Sespie     free (current_output_filename);
4821cc83814Sespie   current_output_filename = xstrdup (fname);
4831cc83814Sespie }
4841cc83814Sespie 
485*1076333cSespie 
486*1076333cSespie /* Output the <a name="..."></a> constructs for NODE.  We output both
487*1076333cSespie    the new-style conversion and the old-style, if they are different.
488*1076333cSespie    See comments at `add_escaped_anchor_name' in html.c.  */
489*1076333cSespie 
490*1076333cSespie static void
add_html_names(char * node)491*1076333cSespie add_html_names (char *node)
492*1076333cSespie {
493*1076333cSespie   char *tem = expand_node_name (node);
494*1076333cSespie   char *otem = xstrdup (tem);
495*1076333cSespie 
496*1076333cSespie   /* Determine if the old and new schemes come up with different names;
497*1076333cSespie      only output the old scheme if that is so.  We don't want to output
498*1076333cSespie      the same name twice.  */
499*1076333cSespie   canon_white (otem);
500*1076333cSespie   {
501*1076333cSespie     char *optr = otem;
502*1076333cSespie     int need_old = 0;
503*1076333cSespie 
504*1076333cSespie     for (; *optr; optr++)
505*1076333cSespie       {
506*1076333cSespie         if (!cr_or_whitespace (*optr) && !URL_SAFE_CHAR (*optr))
507*1076333cSespie           {
508*1076333cSespie             need_old = 1;
509*1076333cSespie             break;
510*1076333cSespie           }
511*1076333cSespie       }
512*1076333cSespie 
513*1076333cSespie     if (need_old)
514*1076333cSespie       {
515*1076333cSespie         add_word ("<a name=\"");
516*1076333cSespie         add_anchor_name (otem, -1);  /* old anchor name conversion */
517*1076333cSespie         add_word ("\"></a>\n");
518*1076333cSespie       }
519*1076333cSespie     free (otem);
520*1076333cSespie   }
521*1076333cSespie 
522*1076333cSespie   /* Always output the new scheme.  */
523*1076333cSespie   canon_white (tem);
524*1076333cSespie   add_word ("<a name=\"");
525*1076333cSespie   add_anchor_name (tem, 0);
526*1076333cSespie   add_word ("\"></a>\n");
527*1076333cSespie 
528*1076333cSespie   free (tem);
529*1076333cSespie }
530*1076333cSespie 
531*1076333cSespie 
5321cc83814Sespie /* The order is: nodename, nextnode, prevnode, upnode.
5331cc83814Sespie    If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
5341cc83814Sespie    You must follow a node command which has those fields defaulted
535*1076333cSespie    with a sectioning command (e.g., @chapter) giving the "level" of that node.
5361cc83814Sespie    It is an error not to do so.
5371cc83814Sespie    The defaults come from the menu in this node's parent. */
5381cc83814Sespie void
cm_node(void)539*1076333cSespie cm_node (void)
5401cc83814Sespie {
5413fb98d4aSespie   static long epilogue_len = 0L;
5421cc83814Sespie   char *node, *prev, *next, *up;
5431cc83814Sespie   int new_node_pos, defaulting, this_section;
5441cc83814Sespie   int no_warn = 0;
5453fb98d4aSespie   char *fname_for_this_node = NULL;
5463fb98d4aSespie   char *tem;
5473fb98d4aSespie   TAG_ENTRY *tag = NULL;
5481cc83814Sespie 
5491cc83814Sespie   if (strcmp (command, "nwnode") == 0)
5501cc83814Sespie     no_warn = TAG_FLAG_NO_WARN;
5511cc83814Sespie 
5521cc83814Sespie   /* Get rid of unmatched brace arguments from previous commands. */
5531cc83814Sespie   discard_braces ();
5541cc83814Sespie 
5551cc83814Sespie   /* There also might be insertions left lying around that haven't been
5561cc83814Sespie      ended yet.  Do that also. */
5571cc83814Sespie   discard_insertions (1);
5581cc83814Sespie 
5591cc83814Sespie   if (!html && !already_outputting_pending_notes)
5601cc83814Sespie     {
5611cc83814Sespie       close_paragraph ();
5621cc83814Sespie       output_pending_notes ();
5631cc83814Sespie     }
5641cc83814Sespie 
5651cc83814Sespie   new_node_pos = output_position;
5661cc83814Sespie 
5671cc83814Sespie   if (macro_expansion_output_stream && !executing_string)
5681cc83814Sespie     append_to_expansion_output (input_text_offset + 1);
5691cc83814Sespie 
5701cc83814Sespie   /* Do not collapse -- to -, etc., in node names.  */
5711cc83814Sespie   in_fixed_width_font++;
5721cc83814Sespie 
5731cc83814Sespie   /* While expanding the @node line, leave any non-macros
5741cc83814Sespie      intact, so that the macro-expanded output includes them.  */
5751cc83814Sespie   only_macro_expansion++;
5761cc83814Sespie   node = get_node_token (1);
5771cc83814Sespie   only_macro_expansion--;
5781cc83814Sespie   next = get_node_token (0);
5791cc83814Sespie   prev = get_node_token (0);
5801cc83814Sespie   up = get_node_token (0);
5811cc83814Sespie 
5823fb98d4aSespie   if (html && splitting
5833fb98d4aSespie       /* If there is a Top node, it always goes into index.html.  So
5843fb98d4aSespie 	 don't start a new HTML file for Top.  */
5853fb98d4aSespie       && (top_node_seen || strcasecmp (node, "Top") != 0))
5863fb98d4aSespie     {
5873fb98d4aSespie       /* We test *node here so that @node without a valid name won't
5883fb98d4aSespie 	 start a new file name with a bogus name such as ".html".
5893fb98d4aSespie 	 This could happen if we run under "--force", where we cannot
5903fb98d4aSespie 	 simply bail out.  Continuing to use the same file sounds like
5913fb98d4aSespie 	 the best we can do in such cases.  */
5923fb98d4aSespie       if (current_output_filename && output_stream && *node)
5933fb98d4aSespie 	{
5943fb98d4aSespie 	  char *fname_for_prev_node;
5953fb98d4aSespie 
5963fb98d4aSespie 	  if (current_node)
5973fb98d4aSespie 	    {
5983fb98d4aSespie 	      /* NOTE: current_node at this point still holds the name
5993fb98d4aSespie 		 of the previous node.  */
6003fb98d4aSespie 	      tem = expand_node_name (current_node);
6013fb98d4aSespie 	      fname_for_prev_node = nodename_to_filename (tem);
6023fb98d4aSespie 	      free (tem);
6033fb98d4aSespie 	    }
6043fb98d4aSespie 	  else /* could happen if their top node isn't named "Top" */
6053fb98d4aSespie 	    fname_for_prev_node = filename_part (current_output_filename);
6063fb98d4aSespie 	  tem = expand_node_name (node);
6073fb98d4aSespie 	  fname_for_this_node = nodename_to_filename (tem);
6083fb98d4aSespie 	  free (tem);
6093fb98d4aSespie 	  /* Don't close current output file, if next output file is
6103fb98d4aSespie              to have the same name.  This may happen at top level, or
6113fb98d4aSespie              if two nodes produce the same file name under --split.  */
6123fb98d4aSespie 	  if (FILENAME_CMP (fname_for_this_node, fname_for_prev_node) != 0)
6133fb98d4aSespie 	    {
6143fb98d4aSespie 	      long pos1 = 0;
6153fb98d4aSespie 
6163fb98d4aSespie 	      /* End the current split output file. */
6173fb98d4aSespie 	      close_paragraph ();
6183fb98d4aSespie 	      output_pending_notes ();
6193fb98d4aSespie 	      start_paragraph ();
6203fb98d4aSespie 	      /* Compute the length of the HTML file's epilogue.  We
6213fb98d4aSespie 		 cannot know the value until run time, due to the
6223fb98d4aSespie 		 text/binary nuisance on DOS/Windows platforms, where
6233fb98d4aSespie 		 2 `\r' characters could be added to the epilogue when
6243fb98d4aSespie 		 it is written in text mode.  */
6253fb98d4aSespie 	      if (epilogue_len == 0)
6263fb98d4aSespie 		{
6273fb98d4aSespie 		  flush_output ();
6283fb98d4aSespie 		  pos1 = ftell (output_stream);
6293fb98d4aSespie 		}
6303fb98d4aSespie 	      add_word ("</body></html>\n");
6313fb98d4aSespie 	      close_paragraph ();
6323fb98d4aSespie 	      if (epilogue_len == 0)
6333fb98d4aSespie 		epilogue_len = ftell (output_stream) - pos1;
6343fb98d4aSespie 	      fclose (output_stream);
6353fb98d4aSespie 	      output_stream = NULL;
636*1076333cSespie               output_position = 0;
6373fb98d4aSespie 	      tag = find_node_by_fname (fname_for_this_node);
6383fb98d4aSespie 	    }
6393fb98d4aSespie 	  free (fname_for_prev_node);
6403fb98d4aSespie 	}
6413fb98d4aSespie     }
6423fb98d4aSespie 
6433fb98d4aSespie   filling_enabled = indented_fill = 0;
6443fb98d4aSespie   if (!html || (html && splitting))
6453fb98d4aSespie     current_footnote_number = 1;
6463fb98d4aSespie 
6471cc83814Sespie   if (verbose_mode)
6481cc83814Sespie     printf (_("Formatting node %s...\n"), node);
6491cc83814Sespie 
6501cc83814Sespie   if (macro_expansion_output_stream && !executing_string)
6511cc83814Sespie     remember_itext (input_text, input_text_offset);
6521cc83814Sespie 
653*1076333cSespie   /* Reset the line number in each node for Info output, so that
654*1076333cSespie      index entries will save the line numbers of parent node.  */
655*1076333cSespie   node_line_number = 0;
656*1076333cSespie 
6571cc83814Sespie   no_indent = 1;
6583fb98d4aSespie   if (xml)
6593fb98d4aSespie     {
6603fb98d4aSespie       xml_begin_document (current_output_filename);
6613fb98d4aSespie       xml_begin_node ();
6623fb98d4aSespie       if (!docbook)
6633fb98d4aSespie 	{
6643fb98d4aSespie 	  xml_insert_element (NODENAME, START);
6653fb98d4aSespie 	  if (macro_expansion_output_stream && !executing_string)
6663fb98d4aSespie 	    me_execute_string (node);
6673fb98d4aSespie 	  else
6683fb98d4aSespie 	    execute_string ("%s", node);
6693fb98d4aSespie 	  xml_insert_element (NODENAME, END);
6703fb98d4aSespie 	}
6713fb98d4aSespie       else
6723fb98d4aSespie 	xml_node_id = xml_id (node);
6733fb98d4aSespie     }
6743fb98d4aSespie   else if (!no_headers && !html)
6751cc83814Sespie     {
676*1076333cSespie       /* Emacs Info reader cannot grok indented escape sequence.  */
677*1076333cSespie       kill_self_indent (-1);
678*1076333cSespie 
6791cc83814Sespie       add_word_args ("\037\nFile: %s,  Node: ", pretty_output_filename);
6801cc83814Sespie 
6811cc83814Sespie       if (macro_expansion_output_stream && !executing_string)
6821cc83814Sespie         me_execute_string (node);
6831cc83814Sespie       else
6841cc83814Sespie         execute_string ("%s", node);
6851cc83814Sespie       filling_enabled = indented_fill = 0;
6861cc83814Sespie     }
6871cc83814Sespie 
6881cc83814Sespie   /* Check for defaulting of this node's next, prev, and up fields. */
6891cc83814Sespie   defaulting = (*next == 0 && *prev == 0 && *up == 0);
6901cc83814Sespie 
691*1076333cSespie   this_section = what_section (input_text + input_text_offset, NULL);
6921cc83814Sespie 
6931cc83814Sespie   /* If we are defaulting, then look at the immediately following
6941cc83814Sespie      sectioning command (error if none) to determine the node's
6951cc83814Sespie      level.  Find the node that contains the menu mentioning this node
6961cc83814Sespie      that is one level up (error if not found).  That node is the "Up"
6971cc83814Sespie      of this node.  Default the "Next" and "Prev" from the menu. */
6981cc83814Sespie   if (defaulting)
6991cc83814Sespie     {
7001cc83814Sespie       NODE_REF *last_ref = NULL;
7011cc83814Sespie       NODE_REF *ref = node_references;
7021cc83814Sespie 
7031cc83814Sespie       if (this_section < 0 && !STREQ (node, "Top"))
7041cc83814Sespie         {
7051cc83814Sespie           char *polite_section_name = "top";
7061cc83814Sespie           int i;
7071cc83814Sespie 
7081cc83814Sespie           for (i = 0; section_alist[i].name; i++)
7091cc83814Sespie             if (section_alist[i].level == current_section + 1)
7101cc83814Sespie               {
7111cc83814Sespie                 polite_section_name = section_alist[i].name;
7121cc83814Sespie                 break;
7131cc83814Sespie               }
7141cc83814Sespie 
7151cc83814Sespie           line_error
716*1076333cSespie             (_("Node `%s' requires a sectioning command (e.g., %c%s)"),
7171cc83814Sespie              node, COMMAND_PREFIX, polite_section_name);
7181cc83814Sespie         }
7191cc83814Sespie       else
7201cc83814Sespie         {
7211cc83814Sespie           if (strcmp (node, "Top") == 0)
7221cc83814Sespie             {
7231cc83814Sespie               /* Default the NEXT pointer to be the first menu item in
7241cc83814Sespie                  this node, if there is a menu in this node.  We have to
7251cc83814Sespie                  try very hard to find the menu, as it may be obscured
7261cc83814Sespie                  by execution_strings which are on the filestack.  For
7271cc83814Sespie                  every member of the filestack which has a FILENAME
7281cc83814Sespie                  member which is identical to the current INPUT_FILENAME,
7291cc83814Sespie                  search forward from that offset. */
7301cc83814Sespie               int saved_input_text_offset = input_text_offset;
7311cc83814Sespie               int saved_input_text_length = input_text_length;
7321cc83814Sespie               char *saved_input_text = input_text;
7331cc83814Sespie               FSTACK *next_file = filestack;
7341cc83814Sespie 
7351cc83814Sespie               int orig_offset, orig_size;
7361cc83814Sespie 
737*1076333cSespie               int bye_offset = search_forward ("\n@bye", input_text_offset);
738*1076333cSespie 
7391cc83814Sespie               /* No matter what, make this file point back at `(dir)'. */
7401cc83814Sespie               free (up);
7411cc83814Sespie               up = xstrdup ("(dir)"); /* html fixxme */
7421cc83814Sespie 
7431cc83814Sespie               while (1)
7441cc83814Sespie                 {
7451cc83814Sespie                   orig_offset = input_text_offset;
7461cc83814Sespie                   orig_size =
7471cc83814Sespie                     search_forward (node_search_string, orig_offset);
7481cc83814Sespie 
7491cc83814Sespie                   if (orig_size < 0)
7501cc83814Sespie                     orig_size = input_text_length;
7511cc83814Sespie 
7521cc83814Sespie                   input_text_offset = search_forward ("\n@menu", orig_offset);
7531cc83814Sespie                   if (input_text_offset > -1
754*1076333cSespie                       && (bye_offset > -1 && input_text_offset < bye_offset)
7551cc83814Sespie                       && cr_or_whitespace (input_text[input_text_offset + 6]))
7561cc83814Sespie                     {
7571cc83814Sespie                       char *nodename_from_menu = NULL;
7581cc83814Sespie 
7591cc83814Sespie                       input_text_offset =
7601cc83814Sespie                         search_forward ("\n* ", input_text_offset);
7611cc83814Sespie 
7621cc83814Sespie                       if (input_text_offset != -1)
7631cc83814Sespie                         nodename_from_menu = glean_node_from_menu (0, 0);
7641cc83814Sespie 
7651cc83814Sespie                       if (nodename_from_menu)
7661cc83814Sespie                         {
7671cc83814Sespie                           free (next);
7681cc83814Sespie                           next = nodename_from_menu;
7691cc83814Sespie                           break;
7701cc83814Sespie                         }
7711cc83814Sespie                     }
7721cc83814Sespie 
7731cc83814Sespie                   /* We got here, so it hasn't been found yet.  Try
7741cc83814Sespie                      the next file on the filestack if there is one. */
7751cc83814Sespie                   if (next_file
7761cc83814Sespie                       && FILENAME_CMP (next_file->filename, input_filename)
7771cc83814Sespie                           == 0)
7781cc83814Sespie                     {
7791cc83814Sespie                       input_text = next_file->text;
7801cc83814Sespie                       input_text_offset = next_file->offset;
7811cc83814Sespie                       input_text_length = next_file->size;
7821cc83814Sespie                       next_file = next_file->next;
7831cc83814Sespie                     }
7841cc83814Sespie                   else
7851cc83814Sespie                     { /* No more input files to check. */
7861cc83814Sespie                       break;
7871cc83814Sespie                     }
7881cc83814Sespie                 }
7891cc83814Sespie 
7901cc83814Sespie               input_text = saved_input_text;
7911cc83814Sespie               input_text_offset = saved_input_text_offset;
7921cc83814Sespie               input_text_length = saved_input_text_length;
7931cc83814Sespie             }
7941cc83814Sespie         }
7951cc83814Sespie 
7961cc83814Sespie       /* Fix the level of the menu references in the Top node, iff it
7971cc83814Sespie          was declared with @top, and no subsequent reference was found. */
7981cc83814Sespie       if (top_node_seen && !non_top_node_seen)
7991cc83814Sespie         {
8001cc83814Sespie           /* Then this is the first non-@top node seen. */
8011cc83814Sespie           int level;
8021cc83814Sespie 
8031cc83814Sespie           level = set_top_section_level (this_section - 1);
8041cc83814Sespie           non_top_node_seen = 1;
8051cc83814Sespie 
8061cc83814Sespie           while (ref)
8071cc83814Sespie             {
8081cc83814Sespie               if (ref->section == level)
8091cc83814Sespie                 ref->section = this_section - 1;
8101cc83814Sespie               ref = ref->next;
8111cc83814Sespie             }
8121cc83814Sespie 
8131cc83814Sespie           ref = node_references;
8141cc83814Sespie         }
8151cc83814Sespie 
8161cc83814Sespie       while (ref)
8171cc83814Sespie         {
8181cc83814Sespie           if (ref->section == (this_section - 1)
8191cc83814Sespie               && ref->type == menu_reference
8201cc83814Sespie               && strcmp (ref->node, node) == 0)
8211cc83814Sespie             {
8221cc83814Sespie               char *containing_node = ref->containing_node;
8231cc83814Sespie 
8241cc83814Sespie               free (up);
8251cc83814Sespie               up = xstrdup (containing_node);
8261cc83814Sespie 
8271cc83814Sespie               if (last_ref
8281cc83814Sespie                   && last_ref->type == menu_reference
8291cc83814Sespie                   && strcmp (last_ref->containing_node, containing_node) == 0)
8301cc83814Sespie                 {
8311cc83814Sespie                   free (next);
8321cc83814Sespie                   next = xstrdup (last_ref->node);
8331cc83814Sespie                 }
8341cc83814Sespie 
8351cc83814Sespie               while (ref->section == this_section - 1
8361cc83814Sespie                      && ref->next
8371cc83814Sespie                      && ref->next->type != menu_reference)
8381cc83814Sespie                 ref = ref->next;
8391cc83814Sespie 
8401cc83814Sespie               if (ref->next && ref->type == menu_reference
8411cc83814Sespie                   && strcmp (ref->next->containing_node, containing_node) == 0)
8421cc83814Sespie                 {
8431cc83814Sespie                   free (prev);
8441cc83814Sespie                   prev = xstrdup (ref->next->node);
8451cc83814Sespie                 }
8461cc83814Sespie               else if (!ref->next
8471cc83814Sespie                        && strcasecmp (ref->containing_node, "Top") == 0)
8481cc83814Sespie                 {
8491cc83814Sespie                   free (prev);
8501cc83814Sespie                   prev = xstrdup (ref->containing_node);
8511cc83814Sespie                 }
8521cc83814Sespie               break;
8531cc83814Sespie             }
8541cc83814Sespie           last_ref = ref;
8551cc83814Sespie           ref = ref->next;
8561cc83814Sespie         }
8571cc83814Sespie     }
8581cc83814Sespie 
8591cc83814Sespie   /* Insert the correct args if we are expanding macros, and the node's
8601cc83814Sespie      pointers weren't defaulted. */
8611cc83814Sespie   if (macro_expansion_output_stream && !executing_string && !defaulting)
8621cc83814Sespie     {
8631cc83814Sespie       char *temp;
8641cc83814Sespie       int op_orig = output_paragraph_offset;
8651cc83814Sespie       int meta_pos_orig = meta_char_pos;
8661cc83814Sespie       int extra = html ? strlen (node) : 0;
8671cc83814Sespie 
8681cc83814Sespie       temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up));
8691cc83814Sespie       sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up);
8701cc83814Sespie       me_execute_string (temp);
8711cc83814Sespie       free (temp);
8721cc83814Sespie 
8731cc83814Sespie       output_paragraph_offset = op_orig;
8741cc83814Sespie       meta_char_pos = meta_pos_orig;
8751cc83814Sespie     }
8761cc83814Sespie 
8771cc83814Sespie   if (!*node)
8781cc83814Sespie     {
8791cc83814Sespie       line_error (_("No node name specified for `%c%s' command"),
8801cc83814Sespie                   COMMAND_PREFIX, command);
8811cc83814Sespie       free (node);
8821cc83814Sespie       free (next); next = NULL;
8831cc83814Sespie       free (prev); prev= NULL;
8841cc83814Sespie       free (up);   up = NULL;
8851cc83814Sespie       node_number++;            /* else it doesn't get bumped */
8861cc83814Sespie     }
8871cc83814Sespie   else
8881cc83814Sespie     {
8891cc83814Sespie       if (!*next) { free (next); next = NULL; }
8901cc83814Sespie       if (!*prev) { free (prev); prev = NULL; }
8911cc83814Sespie       if (!*up)   { free (up);   up = NULL;   }
8923fb98d4aSespie       remember_node (node, prev, next, up, new_node_pos, line_number,
8933fb98d4aSespie 		     fname_for_this_node, no_warn);
8941cc83814Sespie       outstanding_node = 1;
8951cc83814Sespie     }
8961cc83814Sespie 
8971cc83814Sespie   if (html)
8981cc83814Sespie     {
8993fb98d4aSespie       if (splitting && *node && output_stream == NULL)
9003fb98d4aSespie         {
9013fb98d4aSespie 	  char *dirname;
9023fb98d4aSespie 	  char filename[PATH_MAX];
9031cc83814Sespie 
9043fb98d4aSespie 	  dirname = pathname_part (current_output_filename);
9053fb98d4aSespie 	  strcpy (filename, dirname);
9063fb98d4aSespie 	  strcat (filename, fname_for_this_node);
9073fb98d4aSespie 	  free (dirname);
9081cc83814Sespie 
9093fb98d4aSespie 	  /* See if the node name converted to a file name clashes
9103fb98d4aSespie 	     with other nodes or anchors.  If it clashes with an
9113fb98d4aSespie 	     anchor, we complain and nuke that anchor's file.  */
9123fb98d4aSespie 	  if (!tag)
9133fb98d4aSespie 	    {
9141cc83814Sespie 	      output_stream = fopen (filename, "w");
9153fb98d4aSespie 	      html_output_head_p = 0; /* so that we generate HTML preamble */
9163fb98d4aSespie 	      html_output_head ();
9173fb98d4aSespie 	    }
9183fb98d4aSespie 	  else if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
9193fb98d4aSespie 	    {
9203fb98d4aSespie 	      line_error (_("Anchor `%s' and node `%s' map to the same file name"),
9213fb98d4aSespie 			  tag->node, node);
9223fb98d4aSespie 	      file_line_error (tag->filename, tag->line_no,
9233fb98d4aSespie 			       _("This @anchor command ignored; references to it will not work"));
9243fb98d4aSespie 	      file_line_error (tag->filename, tag->line_no,
9253fb98d4aSespie 			       _("Rename this anchor or use the `--no-split' option"));
9263fb98d4aSespie 	      /* Nuke the file name recorded in anchor's tag.
9273fb98d4aSespie 		 Since we are about to nuke the file itself, we
9283fb98d4aSespie 		 don't want find_node_by_fname to consider this
9293fb98d4aSespie 		 anchor anymore.  */
9303fb98d4aSespie 	      free (tag->html_fname);
9313fb98d4aSespie 	      tag->html_fname = NULL;
9323fb98d4aSespie 	      output_stream = fopen (filename, "w");
9333fb98d4aSespie 	      html_output_head_p = 0; /* so that we generate HTML preamble */
9343fb98d4aSespie 	      html_output_head ();
9353fb98d4aSespie 	    }
9363fb98d4aSespie 	  else
9373fb98d4aSespie 	    {
9383fb98d4aSespie 	      /* This node's file name clashes with another node.
9393fb98d4aSespie 		 We put them both on the same file.  */
9403fb98d4aSespie 	      output_stream = fopen (filename, "r+");
9413fb98d4aSespie 	      if (output_stream)
9423fb98d4aSespie 		{
9433fb98d4aSespie 		  static char html_end[] = "</body></html>\n";
9443fb98d4aSespie 		  char end_line[sizeof(html_end)];
9453fb98d4aSespie 		  int fpos = fseek (output_stream, -epilogue_len,
9463fb98d4aSespie 				    SEEK_END);
9473fb98d4aSespie 
9483fb98d4aSespie 		  if (fpos < 0
9493fb98d4aSespie 		      || fgets (end_line, sizeof (html_end),
9503fb98d4aSespie 				output_stream) == NULL
9513fb98d4aSespie 		      /* Paranoia: did someone change the way HTML
9523fb98d4aSespie 			 files are finished up?  */
9533fb98d4aSespie 		      || strcasecmp (end_line, html_end) != 0)
9543fb98d4aSespie 		    {
9553fb98d4aSespie 		      line_error (_("Unexpected string at end of split-HTML file `%s'"),
9563fb98d4aSespie 				  fname_for_this_node);
9573fb98d4aSespie 		      fclose (output_stream);
9583fb98d4aSespie 		      xexit (1);
9593fb98d4aSespie 		    }
9603fb98d4aSespie 		  fseek (output_stream, -epilogue_len, SEEK_END);
9613fb98d4aSespie 		}
9623fb98d4aSespie 	    }
9631cc83814Sespie           if (output_stream == NULL)
9641cc83814Sespie             {
9651cc83814Sespie               fs_error (filename);
9661cc83814Sespie               xexit (1);
9671cc83814Sespie             }
9681cc83814Sespie           set_current_output_filename (filename);
9691cc83814Sespie         }
9701cc83814Sespie 
9711cc83814Sespie       if (!splitting && no_headers)
972*1076333cSespie 	{ /* cross refs need a name="#anchor" even if not writing headers */
973*1076333cSespie           add_html_names (node);
9741cc83814Sespie 	}
9751cc83814Sespie 
9761cc83814Sespie       if (splitting || !no_headers)
977*1076333cSespie         { /* Navigation bar. */
978*1076333cSespie           add_html_block_elt ("<div class=\"node\">\n");
979*1076333cSespie           /* The <p> avoids the links area running on with old Lynxen. */
9801cc83814Sespie           add_word_args ("<p>%s\n", splitting ? "" : "<hr>");
981*1076333cSespie 
982*1076333cSespie           /* In the split HTML case, the filename is wrong for the
983*1076333cSespie              old-style converted names, but we'll add them anyway, for
984*1076333cSespie              consistency.  (And we need them in the normal (not
985*1076333cSespie              no_headers) nonsplit case.)  */
986*1076333cSespie           add_html_names (node);
9871cc83814Sespie 
9881cc83814Sespie           if (next)
9891cc83814Sespie             {
9903fb98d4aSespie               tem = expansion (next, 0);
991*1076333cSespie 	      add_word ((char *) _("Next:"));
992*1076333cSespie               add_word ("&nbsp;");
993*1076333cSespie 
994*1076333cSespie 	      add_word ("<a rel=\"next\" accesskey=\"n\" href=\"");
9951cc83814Sespie 	      add_anchor_name (tem, 1);
996*1076333cSespie               tem = escape_string (tem);
9971cc83814Sespie 	      add_word_args ("\">%s</a>", tem);
998*1076333cSespie 
9991cc83814Sespie               free (tem);
1000*1076333cSespie 
1001*1076333cSespie 	      if (prev || up)
1002*1076333cSespie 		add_word (",\n");
10031cc83814Sespie             }
10041cc83814Sespie           if (prev)
10051cc83814Sespie             {
10063fb98d4aSespie               tem = expansion (prev, 0);
1007*1076333cSespie 	      add_word ((char *) _("Previous:"));
1008*1076333cSespie               add_word ("&nbsp;");
1009*1076333cSespie 	      add_word ("<a rel=\"previous\" accesskey=\"p\" href=\"");
10101cc83814Sespie 	      add_anchor_name (tem, 1);
1011*1076333cSespie               tem = escape_string (tem);
10121cc83814Sespie 	      add_word_args ("\">%s</a>", tem);
10131cc83814Sespie               free (tem);
1014*1076333cSespie 
1015*1076333cSespie 	      if (up)
1016*1076333cSespie 		add_word (",\n");
10171cc83814Sespie             }
10181cc83814Sespie           if (up)
10191cc83814Sespie             {
10203fb98d4aSespie               tem = expansion (up, 0);
1021*1076333cSespie 	      add_word ((char *) _("Up:"));
1022*1076333cSespie               add_word ("&nbsp;");
1023*1076333cSespie 	      add_word ("<a rel=\"up\" accesskey=\"u\" href=\"");
10241cc83814Sespie 	      add_anchor_name (tem, 1);
1025*1076333cSespie               tem = escape_string (tem);
10261cc83814Sespie 	      add_word_args ("\">%s</a>", tem);
10271cc83814Sespie               free (tem);
10281cc83814Sespie             }
10291cc83814Sespie           /* html fixxme: we want a `top' or `contents' link here.  */
10301cc83814Sespie 
1031*1076333cSespie           add_word_args ("\n%s\n", splitting ? "<hr>" : "");
1032*1076333cSespie       	  add_word ("</div>\n");
10331cc83814Sespie         }
10341cc83814Sespie     }
10353fb98d4aSespie   else if (docbook)
10363fb98d4aSespie     ;
10373fb98d4aSespie   else if (xml)
10383fb98d4aSespie     {
10393fb98d4aSespie       if (next)
10403fb98d4aSespie 	{
10413fb98d4aSespie 	  xml_insert_element (NODENEXT, START);
10423fb98d4aSespie 	  execute_string ("%s", next);
10433fb98d4aSespie 	  xml_insert_element (NODENEXT, END);
10443fb98d4aSespie 	}
10453fb98d4aSespie       if (prev)
10463fb98d4aSespie 	{
10473fb98d4aSespie 	  xml_insert_element (NODEPREV, START);
10483fb98d4aSespie 	  execute_string ("%s", prev);
10493fb98d4aSespie 	  xml_insert_element (NODEPREV, END);
10503fb98d4aSespie 	}
10513fb98d4aSespie       if (up)
10523fb98d4aSespie 	{
10533fb98d4aSespie 	  xml_insert_element (NODEUP, START);
10543fb98d4aSespie 	  execute_string ("%s", up);
10553fb98d4aSespie 	  xml_insert_element (NODEUP, END);
10563fb98d4aSespie 	}
10573fb98d4aSespie     }
10581cc83814Sespie   else if (!no_headers)
10591cc83814Sespie     {
10601cc83814Sespie       if (macro_expansion_output_stream)
10611cc83814Sespie         me_inhibit_expansion++;
10621cc83814Sespie 
10631cc83814Sespie       /* These strings are not translatable.  */
10641cc83814Sespie       if (next)
10651cc83814Sespie         {
10661cc83814Sespie           execute_string (",  Next: %s", next);
10671cc83814Sespie           filling_enabled = indented_fill = 0;
10681cc83814Sespie         }
10691cc83814Sespie       if (prev)
10701cc83814Sespie         {
10711cc83814Sespie           execute_string (",  Prev: %s", prev);
10721cc83814Sespie           filling_enabled = indented_fill = 0;
10731cc83814Sespie         }
10741cc83814Sespie       if (up)
10751cc83814Sespie         {
10761cc83814Sespie           execute_string (",  Up: %s", up);
10771cc83814Sespie           filling_enabled = indented_fill = 0;
10781cc83814Sespie         }
10791cc83814Sespie       if (macro_expansion_output_stream)
10801cc83814Sespie         me_inhibit_expansion--;
10811cc83814Sespie     }
10821cc83814Sespie 
10831cc83814Sespie   close_paragraph ();
10841cc83814Sespie   no_indent = 0;
10851cc83814Sespie 
10861cc83814Sespie   /* Change the section only if there was a sectioning command. */
10871cc83814Sespie   if (this_section >= 0)
10881cc83814Sespie     current_section = this_section;
10891cc83814Sespie 
10901cc83814Sespie   if (current_node && STREQ (current_node, "Top"))
10911cc83814Sespie     top_node_seen = 1;
10921cc83814Sespie 
10931cc83814Sespie   filling_enabled = 1;
10941cc83814Sespie   in_fixed_width_font--;
10951cc83814Sespie }
10961cc83814Sespie 
10971cc83814Sespie /* Cross-reference target at an arbitrary spot.  */
10981cc83814Sespie void
cm_anchor(int arg)1099*1076333cSespie cm_anchor (int arg)
11001cc83814Sespie {
11011cc83814Sespie   char *anchor;
11023fb98d4aSespie   char *fname_for_anchor = NULL;
11031cc83814Sespie 
11041cc83814Sespie   if (arg == END)
11051cc83814Sespie     return;
11061cc83814Sespie 
11071cc83814Sespie   /* Parse the anchor text.  */
11081cc83814Sespie   anchor = get_xref_token (1);
11091cc83814Sespie 
1110*1076333cSespie   /* Force all versions of "top" to be "Top". */
1111*1076333cSespie   normalize_node_name (anchor);
1112*1076333cSespie 
11131cc83814Sespie   /* In HTML mode, need to actually produce some output.  */
11141cc83814Sespie   if (html)
11151cc83814Sespie     {
11161cc83814Sespie       /* If this anchor is at the beginning of a new paragraph, make
11171cc83814Sespie 	 sure a new paragraph is indeed started.  */
11181cc83814Sespie       if (!paragraph_is_open)
11191cc83814Sespie 	{
11203fb98d4aSespie 	  if (!executing_string && html)
11213fb98d4aSespie 	    html_output_head ();
11221cc83814Sespie 	  start_paragraph ();
11231cc83814Sespie 	  if (!in_fixed_width_font || in_menu || in_detailmenu)
11241cc83814Sespie 	    {
11251cc83814Sespie 	      insert_string ("<p>");
11261cc83814Sespie 	      in_paragraph = 1;
11271cc83814Sespie 	    }
11281cc83814Sespie 	}
11291cc83814Sespie       add_word ("<a name=\"");
11301cc83814Sespie       add_anchor_name (anchor, 0);
11311cc83814Sespie       add_word ("\"></a>");
11323fb98d4aSespie       if (splitting)
11333fb98d4aSespie 	{
11343fb98d4aSespie 	  /* If we are splitting, cm_xref will produce a reference to
11353fb98d4aSespie 	     a file whose name is derived from the anchor name.  So we
11363fb98d4aSespie 	     must create a file when we see an @anchor, otherwise
11373fb98d4aSespie 	     xref's to anchors won't work.  The file we create simply
11383fb98d4aSespie 	     redirects to the file of this anchor's node.  */
11393fb98d4aSespie 	  TAG_ENTRY *tag;
11401cc83814Sespie 
11413fb98d4aSespie 	  fname_for_anchor = nodename_to_filename (anchor);
11423fb98d4aSespie 	  /* See if the anchor name converted to a file name clashes
11433fb98d4aSespie 	     with other anchors or nodes.  */
11443fb98d4aSespie 	  tag = find_node_by_fname (fname_for_anchor);
11453fb98d4aSespie 	  if (tag)
11463fb98d4aSespie 	    {
11473fb98d4aSespie 	      if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
11483fb98d4aSespie 		line_error (_("Anchors `%s' and `%s' map to the same file name"),
11493fb98d4aSespie 			    anchor, tag->node);
11503fb98d4aSespie 	      else
11513fb98d4aSespie 		line_error (_("Anchor `%s' and node `%s' map to the same file name"),
11523fb98d4aSespie 			    anchor, tag->node);
11533fb98d4aSespie 	      line_error (_("@anchor command ignored; references to it will not work"));
11543fb98d4aSespie 	      line_error (_("Rename this anchor or use the `--no-split' option"));
11553fb98d4aSespie 	      free (fname_for_anchor);
11563fb98d4aSespie 	      /* We will not be creating a file for this anchor, so
11573fb98d4aSespie 		 set its name to NULL, so that remember_node stores a
11583fb98d4aSespie 		 NULL and find_node_by_fname won't consider this
11593fb98d4aSespie 		 anchor for clashes.  */
11603fb98d4aSespie 	      fname_for_anchor = NULL;
11613fb98d4aSespie 	    }
11623fb98d4aSespie 	  else
11633fb98d4aSespie 	    {
11643fb98d4aSespie 	      char *dirname, *p;
11653fb98d4aSespie 	      char filename[PATH_MAX];
11663fb98d4aSespie 	      FILE *anchor_stream;
11673fb98d4aSespie 
11683fb98d4aSespie 	      dirname = pathname_part (current_output_filename);
11693fb98d4aSespie 	      strcpy (filename, dirname);
11703fb98d4aSespie 	      strcat (filename, fname_for_anchor);
11713fb98d4aSespie 	      free (dirname);
11723fb98d4aSespie 
11733fb98d4aSespie 	      anchor_stream = fopen (filename, "w");
11743fb98d4aSespie 	      if (anchor_stream == NULL)
11753fb98d4aSespie 		{
11763fb98d4aSespie 		  fs_error (filename);
11773fb98d4aSespie 		  xexit (1);
11783fb98d4aSespie 		}
11793fb98d4aSespie 	      /* The HTML magic below will cause the browser to
11803fb98d4aSespie 		 immediately go to the anchor's node's file.  Lynx
11813fb98d4aSespie 		 seems not to support this redirection, but it looks
11823fb98d4aSespie 		 like a bug in Lynx, and they can work around it by
11833fb98d4aSespie 		 clicking on the link once more.  */
11843fb98d4aSespie 	      fputs ("<meta http-equiv=\"refresh\" content=\"0; url=",
11853fb98d4aSespie 		     anchor_stream);
11863fb98d4aSespie 	      /* Make the indirect link point to the current node's
11873fb98d4aSespie 		 file and anchor's "<a name" label.  If we don't have
11883fb98d4aSespie 		 a valid node name, refer to the current output file
11893fb98d4aSespie 		 instead.  */
11903fb98d4aSespie 	      if (current_node && *current_node)
11913fb98d4aSespie 		{
11923fb98d4aSespie 		  char *fn, *tem;
11933fb98d4aSespie 
11943fb98d4aSespie 		  tem = expand_node_name (current_node);
11953fb98d4aSespie 		  fn = nodename_to_filename (tem);
11963fb98d4aSespie 		  free (tem);
11973fb98d4aSespie 		  fputs (fn, anchor_stream);
11983fb98d4aSespie 		  free (fn);
11993fb98d4aSespie 		}
12003fb98d4aSespie 	      else
12013fb98d4aSespie 		{
12023fb98d4aSespie 		  char *base = filename_part (current_output_filename);
12033fb98d4aSespie 
12043fb98d4aSespie 		  fputs (base, anchor_stream);
12053fb98d4aSespie 		  free (base);
12063fb98d4aSespie 		}
12073fb98d4aSespie 	      fputs ("#", anchor_stream);
12083fb98d4aSespie 	      for (p = anchor; *p; p++)
12093fb98d4aSespie 		{
12103fb98d4aSespie 		  if (*p == '&')
12113fb98d4aSespie 		    fputs ("&amp;", anchor_stream);
12123fb98d4aSespie 		  else if (!URL_SAFE_CHAR (*p))
12133fb98d4aSespie 		    fprintf (anchor_stream, "%%%x", (unsigned char) *p);
12143fb98d4aSespie 		  else
12153fb98d4aSespie 		    fputc (*p, anchor_stream);
12163fb98d4aSespie 		}
12173fb98d4aSespie 	      fputs ("\">\n", anchor_stream);
12183fb98d4aSespie 	      fclose (anchor_stream);
12193fb98d4aSespie 	    }
12203fb98d4aSespie 	}
12213fb98d4aSespie     }
12223fb98d4aSespie   else if (xml)
12233fb98d4aSespie     {
12243fb98d4aSespie       xml_insert_element_with_attribute (ANCHOR, START, "name=\"%s\"", anchor);
12253fb98d4aSespie       xml_insert_element (ANCHOR, END);
12263fb98d4aSespie     }
12271cc83814Sespie   /* Save it in the tag table.  */
12283fb98d4aSespie   remember_node (anchor, NULL, NULL, NULL,
12293fb98d4aSespie                  output_position + output_paragraph_offset,
12303fb98d4aSespie                  line_number, fname_for_anchor, TAG_FLAG_ANCHOR);
12311cc83814Sespie }
12321cc83814Sespie 
12331cc83814Sespie /* Find NODE in REF_LIST. */
12341cc83814Sespie static NODE_REF *
find_node_reference(char * node,NODE_REF * ref_list)1235*1076333cSespie find_node_reference (char *node, NODE_REF *ref_list)
12361cc83814Sespie {
12371cc83814Sespie   NODE_REF *orig_ref_list = ref_list;
12381cc83814Sespie   char *expanded_node;
12391cc83814Sespie 
12401cc83814Sespie   while (ref_list)
12411cc83814Sespie     {
12421cc83814Sespie       if (strcmp (node, ref_list->node) == 0)
12431cc83814Sespie         break;
12441cc83814Sespie       ref_list = ref_list->next;
12451cc83814Sespie     }
12461cc83814Sespie 
12471cc83814Sespie   if (ref_list || !expensive_validation)
12481cc83814Sespie     return ref_list;
12491cc83814Sespie 
12501cc83814Sespie   /* Maybe NODE is not expanded yet.  This may be SLOW.  */
12511cc83814Sespie   expanded_node = expand_node_name (node);
12521cc83814Sespie   for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next)
12531cc83814Sespie     {
12541cc83814Sespie       if (STREQ (expanded_node, ref_list->node))
12551cc83814Sespie         break;
12561cc83814Sespie       if (strchr (ref_list->node, COMMAND_PREFIX))
12571cc83814Sespie         {
12581cc83814Sespie           char *expanded_ref = expand_node_name (ref_list->node);
12591cc83814Sespie 
12601cc83814Sespie           if (STREQ (expanded_node, expanded_ref))
12611cc83814Sespie             {
12621cc83814Sespie               free (expanded_ref);
12631cc83814Sespie               break;
12641cc83814Sespie             }
12651cc83814Sespie           free (expanded_ref);
12661cc83814Sespie         }
12671cc83814Sespie     }
12681cc83814Sespie   free (expanded_node);
12691cc83814Sespie   return ref_list;
12701cc83814Sespie }
12711cc83814Sespie 
12721cc83814Sespie void
free_node_references(void)1273*1076333cSespie free_node_references (void)
12741cc83814Sespie {
12751cc83814Sespie   NODE_REF *list, *temp;
12761cc83814Sespie 
12771cc83814Sespie   list = node_references;
12781cc83814Sespie 
12791cc83814Sespie   while (list)
12801cc83814Sespie     {
12811cc83814Sespie       temp = list;
12821cc83814Sespie       free (list->node);
12831cc83814Sespie       free (list->containing_node);
12841cc83814Sespie       list = list->next;
12851cc83814Sespie       free (temp);
12861cc83814Sespie     }
12871cc83814Sespie   node_references = NULL;
12881cc83814Sespie }
12891cc83814Sespie 
12901cc83814Sespie void
free_node_node_references(void)1291*1076333cSespie free_node_node_references (void)
12921cc83814Sespie {
12931cc83814Sespie   NODE_REF *list, *temp;
12941cc83814Sespie 
12951cc83814Sespie   list = node_references;
12961cc83814Sespie 
12971cc83814Sespie   while (list)
12981cc83814Sespie     {
12991cc83814Sespie       temp = list;
13001cc83814Sespie       free (list->node);
13011cc83814Sespie       list = list->next;
13021cc83814Sespie       free (temp);
13031cc83814Sespie     }
13041cc83814Sespie   node_node_references = NULL;
13051cc83814Sespie }
13061cc83814Sespie 
13071cc83814Sespie /* Return the number assigned to a named node in either the tag_table
13081cc83814Sespie    or node_references list or zero if no number has been assigned. */
13091cc83814Sespie int
number_of_node(char * node)1310*1076333cSespie number_of_node (char *node)
13111cc83814Sespie {
13121cc83814Sespie   NODE_REF *temp_ref;
13131cc83814Sespie   TAG_ENTRY *temp_node = find_node (node);
13141cc83814Sespie 
13151cc83814Sespie   if (temp_node)
13161cc83814Sespie     return temp_node->number;
13171cc83814Sespie   else if ((temp_ref = find_node_reference (node, node_references)))
13181cc83814Sespie     return temp_ref->number;
13191cc83814Sespie   else if ((temp_ref = find_node_reference (node, node_node_references)))
13201cc83814Sespie     return temp_ref->number;
13211cc83814Sespie   else
13221cc83814Sespie     return 0;
13231cc83814Sespie }
13241cc83814Sespie 
13251cc83814Sespie /* validation */
13261cc83814Sespie 
13271cc83814Sespie /* Return 1 if TAG (at LINE) correctly validated, or 0 if not.
13281cc83814Sespie    LABEL is the (translated) description of the type of reference --
13291cc83814Sespie    Menu, Cross, Next, etc.  */
13301cc83814Sespie 
13311cc83814Sespie static int
validate(char * tag,int line,const char * label)1332*1076333cSespie validate (char *tag, int line, const char *label)
13331cc83814Sespie {
13341cc83814Sespie   TAG_ENTRY *result;
13351cc83814Sespie 
13361cc83814Sespie   /* If there isn't a tag to verify, or if the tag is in another file,
13371cc83814Sespie      then it must be okay. */
13381cc83814Sespie   if (!tag || !*tag || *tag == '(')
13391cc83814Sespie     return 1;
13401cc83814Sespie 
13411cc83814Sespie   /* Otherwise, the tag must exist. */
13421cc83814Sespie   result = find_node (tag);
13431cc83814Sespie 
13441cc83814Sespie   if (!result)
13451cc83814Sespie     {
13461cc83814Sespie       line_number = line;
1347*1076333cSespie       line_error (_("%s reference to nonexistent node `%s' (perhaps incorrect sectioning?)"), label, tag);
13481cc83814Sespie       return 0;
13491cc83814Sespie     }
13501cc83814Sespie   result->touched++;
13511cc83814Sespie   return 1;
13521cc83814Sespie }
13531cc83814Sespie 
13541cc83814Sespie /* The strings here are followed in the message by `reference to...' in
13551cc83814Sespie    the `validate' routine.  They are only used in messages, thus are
13561cc83814Sespie    translated.  */
1357*1076333cSespie static const char *
reftype_type_string(enum reftype type)1358*1076333cSespie reftype_type_string (enum reftype type)
13591cc83814Sespie {
13601cc83814Sespie   switch (type)
13611cc83814Sespie     {
13621cc83814Sespie     case menu_reference:
13631cc83814Sespie       return _("Menu");
13641cc83814Sespie     case followed_reference:
13651cc83814Sespie       return _("Cross");
13661cc83814Sespie     default:
13671cc83814Sespie       return "Internal-bad-reference-type";
13681cc83814Sespie     }
13691cc83814Sespie }
13701cc83814Sespie 
13711cc83814Sespie static void
validate_other_references(NODE_REF * ref_list)1372*1076333cSespie validate_other_references (NODE_REF *ref_list)
13731cc83814Sespie {
13741cc83814Sespie   char *old_input_filename = input_filename;
13751cc83814Sespie 
13761cc83814Sespie   while (ref_list)
13771cc83814Sespie     {
13781cc83814Sespie       input_filename = ref_list->filename;
13791cc83814Sespie       validate (ref_list->node, ref_list->line_no,
13801cc83814Sespie                 reftype_type_string (ref_list->type));
13811cc83814Sespie       ref_list = ref_list->next;
13821cc83814Sespie     }
13831cc83814Sespie   input_filename = old_input_filename;
13841cc83814Sespie }
13851cc83814Sespie 
13861cc83814Sespie /* Validation of an info file.
13871cc83814Sespie    Scan through the list of tag entries touching the Prev, Next, and Up
13881cc83814Sespie    elements of each.  It is an error not to be able to touch one of them,
13891cc83814Sespie    except in the case of external node references, such as "(DIR)".
13901cc83814Sespie 
13911cc83814Sespie    If the Prev is different from the Up,
13921cc83814Sespie    then the Prev node must have a Next pointing at this node.
13931cc83814Sespie 
13941cc83814Sespie    Every node except Top must have an Up.
13951cc83814Sespie    The Up node must contain some sort of reference, other than a Next,
13961cc83814Sespie    to this node.
13971cc83814Sespie 
13981cc83814Sespie    If the Next is different from the Next of the Up,
13991cc83814Sespie    then the Next node must have a Prev pointing at this node. */
14001cc83814Sespie void
validate_file(TAG_ENTRY * tag_table)1401*1076333cSespie validate_file (TAG_ENTRY *tag_table)
14021cc83814Sespie {
14031cc83814Sespie   char *old_input_filename = input_filename;
14041cc83814Sespie   TAG_ENTRY *tags = tag_table;
14051cc83814Sespie 
14061cc83814Sespie   while (tags)
14071cc83814Sespie     {
14081cc83814Sespie       TAG_ENTRY *temp_tag;
14091cc83814Sespie       char *tem1, *tem2;
14101cc83814Sespie 
14111cc83814Sespie       input_filename = tags->filename;
14121cc83814Sespie       line_number = tags->line_no;
14131cc83814Sespie 
14141cc83814Sespie       /* If this is a "no warn" node, don't validate it in any way. */
14151cc83814Sespie       if (tags->flags & TAG_FLAG_NO_WARN)
14161cc83814Sespie         {
14171cc83814Sespie           tags = tags->next_ent;
14181cc83814Sespie           continue;
14191cc83814Sespie         }
14201cc83814Sespie 
14211cc83814Sespie       /* If this node has a Next, then make sure that the Next exists. */
14221cc83814Sespie       if (tags->next)
14231cc83814Sespie         {
14241cc83814Sespie           validate (tags->next, tags->line_no, _("Next"));
14251cc83814Sespie 
14261cc83814Sespie           /* If the Next node exists, and there is no Up, then make sure
14271cc83814Sespie              that the Prev of the Next points back.  But do nothing if
14281cc83814Sespie              we aren't supposed to issue warnings about this node. */
14291cc83814Sespie           temp_tag = find_node (tags->next);
14301cc83814Sespie           if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN))
14311cc83814Sespie             {
14321cc83814Sespie               char *prev = temp_tag->prev;
14331cc83814Sespie               int you_lose = !prev || !STREQ (prev, tags->node);
14341cc83814Sespie 
14351cc83814Sespie               if (you_lose && expensive_validation)
14361cc83814Sespie                 {
14371cc83814Sespie                   tem1 = expand_node_name (prev);
14381cc83814Sespie                   tem2 = expand_node_name (tags->node);
14391cc83814Sespie 
1440*1076333cSespie                   if (tem1 && tem2 && STREQ (tem1, tem2))
14411cc83814Sespie                     you_lose = 0;
14421cc83814Sespie                   free (tem1);
14431cc83814Sespie                   free (tem2);
14441cc83814Sespie                 }
14451cc83814Sespie               if (you_lose)
14461cc83814Sespie                 {
1447*1076333cSespie                   line_error (_("Next field of node `%s' not pointed to (perhaps incorrect sectioning?)"),
14481cc83814Sespie                               tags->node);
14493fb98d4aSespie                   file_line_error (temp_tag->filename, temp_tag->line_no,
14503fb98d4aSespie 				   _("This node (%s) has the bad Prev"),
14511cc83814Sespie 				   temp_tag->node);
14521cc83814Sespie                   temp_tag->flags |= TAG_FLAG_PREV_ERROR;
14531cc83814Sespie                 }
14541cc83814Sespie             }
14551cc83814Sespie         }
14561cc83814Sespie 
14571cc83814Sespie       /* Validate the Prev field if there is one, and we haven't already
14581cc83814Sespie          complained about it in some way.  You don't have to have a Prev
14591cc83814Sespie          field at this stage. */
14601cc83814Sespie       if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev)
14611cc83814Sespie         {
14621cc83814Sespie           int valid_p = validate (tags->prev, tags->line_no, _("Prev"));
14631cc83814Sespie 
14641cc83814Sespie           if (!valid_p)
14651cc83814Sespie             tags->flags |= TAG_FLAG_PREV_ERROR;
14661cc83814Sespie           else
14671cc83814Sespie             { /* If the Prev field is not the same as the Up field,
14681cc83814Sespie                  then the node pointed to by the Prev field must have
14691cc83814Sespie                  a Next field which points to this node. */
14701cc83814Sespie               int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up);
14711cc83814Sespie 
14721cc83814Sespie               if (!prev_equals_up && expensive_validation)
14731cc83814Sespie                 {
14741cc83814Sespie                   tem1 = expand_node_name (tags->prev);
14751cc83814Sespie                   tem2 = expand_node_name (tags->up);
14761cc83814Sespie                   prev_equals_up = STREQ (tem1, tem2);
14771cc83814Sespie                   free (tem1);
14781cc83814Sespie                   free (tem2);
14791cc83814Sespie                 }
14801cc83814Sespie               if (!prev_equals_up)
14811cc83814Sespie                 {
14821cc83814Sespie                   temp_tag = find_node (tags->prev);
14831cc83814Sespie 
14841cc83814Sespie                   /* If we aren't supposed to issue warnings about the
14851cc83814Sespie                      target node, do nothing. */
14861cc83814Sespie                   if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN))
14871cc83814Sespie                     /* Do nothing. */ ;
14881cc83814Sespie                   else
14891cc83814Sespie                     {
14901cc83814Sespie                       int you_lose = !temp_tag->next
14911cc83814Sespie                         || !STREQ (temp_tag->next, tags->node);
14921cc83814Sespie 
14931cc83814Sespie                       if (temp_tag->next && you_lose && expensive_validation)
14941cc83814Sespie                         {
14951cc83814Sespie                           tem1 = expand_node_name (temp_tag->next);
14961cc83814Sespie                           tem2 = expand_node_name (tags->node);
14971cc83814Sespie                           if (STREQ (tem1, tem2))
14981cc83814Sespie                             you_lose = 0;
14991cc83814Sespie                           free (tem1);
15001cc83814Sespie                           free (tem2);
15011cc83814Sespie                         }
15021cc83814Sespie                       if (you_lose)
15031cc83814Sespie                         {
15041cc83814Sespie                           line_error
15051cc83814Sespie                             (_("Prev field of node `%s' not pointed to"),
15061cc83814Sespie                              tags->node);
15073fb98d4aSespie                           file_line_error (temp_tag->filename,
15083fb98d4aSespie 					   temp_tag->line_no,
15093fb98d4aSespie 					   _("This node (%s) has the bad Next"),
15101cc83814Sespie 					   temp_tag->node);
15111cc83814Sespie                           temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
15121cc83814Sespie                         }
15131cc83814Sespie                     }
15141cc83814Sespie                 }
15151cc83814Sespie             }
15161cc83814Sespie         }
15171cc83814Sespie 
15181cc83814Sespie       if (!tags->up
15191cc83814Sespie           && !(tags->flags & TAG_FLAG_ANCHOR)
15201cc83814Sespie           && strcasecmp (tags->node, "Top") != 0)
1521*1076333cSespie         line_error (_("`%s' has no Up field (perhaps incorrect sectioning?)"), tags->node);
15221cc83814Sespie       else if (tags->up)
15231cc83814Sespie         {
15241cc83814Sespie           int valid_p = validate (tags->up, tags->line_no, _("Up"));
15251cc83814Sespie 
15261cc83814Sespie           /* If node X has Up: Y, then warn if Y fails to have a menu item
15271cc83814Sespie              or note pointing at X, if Y isn't of the form "(Y)". */
15281cc83814Sespie           if (valid_p && *tags->up != '(')
15291cc83814Sespie             {
15301cc83814Sespie               NODE_REF *nref;
15311cc83814Sespie               NODE_REF *tref = NULL;
15321cc83814Sespie               NODE_REF *list = node_references;
15331cc83814Sespie 
15341cc83814Sespie               for (;;)
15351cc83814Sespie                 {
15361cc83814Sespie                   nref = find_node_reference (tags->node, list);
15371cc83814Sespie                   if (!nref)
15381cc83814Sespie                     break;
15391cc83814Sespie 
15401cc83814Sespie                   if (strcmp (nref->containing_node, tags->up) == 0)
15411cc83814Sespie                     {
15421cc83814Sespie                       if (nref->type != menu_reference)
15431cc83814Sespie                         {
15441cc83814Sespie                           tref = nref;
15451cc83814Sespie                           list = nref->next;
15461cc83814Sespie                         }
15471cc83814Sespie                       else
15481cc83814Sespie                         break;
15491cc83814Sespie                     }
15501cc83814Sespie                   list = nref->next;
15511cc83814Sespie                 }
15521cc83814Sespie 
15531cc83814Sespie               if (!nref)
15541cc83814Sespie                 {
15551cc83814Sespie 		  if (!tref && expensive_validation)
15561cc83814Sespie 		    {
15571cc83814Sespie 		      /* Sigh...  This might be AWFULLY slow, but if
15581cc83814Sespie 		         they want this feature, they'll have to pay!
15591cc83814Sespie 		         We do all the loop again expanding each
15601cc83814Sespie 		         containing_node reference as we go.  */
15611cc83814Sespie 		      char *tags_up = expand_node_name (tags->up);
15621cc83814Sespie 		      char *tem;
15631cc83814Sespie 
15641cc83814Sespie 		      list = node_references;
15651cc83814Sespie 
15661cc83814Sespie 		      for (;;)
15671cc83814Sespie 			{
15681cc83814Sespie 			  nref = find_node_reference (tags->node, list);
15691cc83814Sespie 			  if (!nref)
15701cc83814Sespie 			    break;
15711cc83814Sespie 			  tem = expand_node_name (nref->containing_node);
15721cc83814Sespie 			  if (STREQ (tem, tags_up))
15731cc83814Sespie 			    {
15741cc83814Sespie 			      if (nref->type != menu_reference)
15751cc83814Sespie 				tref = nref;
15761cc83814Sespie 			      else
15771cc83814Sespie 				{
15781cc83814Sespie 				  free (tem);
15791cc83814Sespie 				  break;
15801cc83814Sespie 				}
15811cc83814Sespie 			    }
15821cc83814Sespie 			  free (tem);
15831cc83814Sespie 			  list = nref->next;
15841cc83814Sespie 			}
15851cc83814Sespie 		    }
15861cc83814Sespie                   if (!nref && !tref)
15871cc83814Sespie                     {
15881cc83814Sespie                       temp_tag = find_node (tags->up);
15893fb98d4aSespie                       file_line_error (temp_tag->filename, temp_tag->line_no,
15901cc83814Sespie            _("Node `%s' lacks menu item for `%s' despite being its Up target"),
15911cc83814Sespie                                   tags->up, tags->node);
15921cc83814Sespie                     }
15931cc83814Sespie                 }
15941cc83814Sespie             }
15951cc83814Sespie         }
15961cc83814Sespie       tags = tags->next_ent;
15971cc83814Sespie     }
15981cc83814Sespie 
15991cc83814Sespie   validate_other_references (node_references);
16001cc83814Sespie   /* We have told the user about the references which didn't exist.
16011cc83814Sespie      Now tell him about the nodes which aren't referenced. */
16021cc83814Sespie 
16031cc83814Sespie   for (tags = tag_table; tags; tags = tags->next_ent)
16041cc83814Sespie     {
16051cc83814Sespie       /* If this node is a "no warn" node, do nothing. */
16061cc83814Sespie       if (tags->flags & TAG_FLAG_NO_WARN)
16071cc83814Sespie         {
16081cc83814Sespie           tags = tags->next_ent;
16091cc83814Sespie           continue;
16101cc83814Sespie         }
16111cc83814Sespie 
16121cc83814Sespie       /* Special hack.  If the node in question appears to have
16131cc83814Sespie          been referenced more than REFERENCE_WARNING_LIMIT times,
16141cc83814Sespie          give a warning. */
16151cc83814Sespie       if (tags->touched > reference_warning_limit)
16161cc83814Sespie         {
16171cc83814Sespie           input_filename = tags->filename;
16181cc83814Sespie           line_number = tags->line_no;
16191cc83814Sespie           warning (_("node `%s' has been referenced %d times"),
16201cc83814Sespie                    tags->node, tags->touched);
16211cc83814Sespie         }
16221cc83814Sespie 
16231cc83814Sespie       if (tags->touched == 0)
16241cc83814Sespie         {
16251cc83814Sespie           input_filename = tags->filename;
16261cc83814Sespie           line_number = tags->line_no;
16271cc83814Sespie 
16281cc83814Sespie           /* Notice that the node "Top" is special, and doesn't have to
16291cc83814Sespie              be referenced.   Anchors don't have to be referenced
16301cc83814Sespie              either, you might define them for another document.  */
16311cc83814Sespie           if (strcasecmp (tags->node, "Top") != 0
16321cc83814Sespie               && !(tags->flags & TAG_FLAG_ANCHOR))
16331cc83814Sespie             warning (_("unreferenced node `%s'"), tags->node);
16341cc83814Sespie         }
16351cc83814Sespie     }
16361cc83814Sespie   input_filename = old_input_filename;
16371cc83814Sespie }
16381cc83814Sespie 
16391cc83814Sespie 
16401cc83814Sespie /* Splitting */
16411cc83814Sespie 
16421cc83814Sespie /* Return true if the tag entry pointed to by TAGS is the last node.
16431cc83814Sespie    This means only anchors follow.  */
16441cc83814Sespie 
16451cc83814Sespie static int
last_node_p(TAG_ENTRY * tags)1646*1076333cSespie last_node_p (TAG_ENTRY *tags)
16471cc83814Sespie {
16481cc83814Sespie   int last = 1;
16491cc83814Sespie   while (tags->next_ent) {
16501cc83814Sespie     tags = tags->next_ent;
16511cc83814Sespie     if (tags->flags & TAG_FLAG_ANCHOR)
16521cc83814Sespie       ;
16531cc83814Sespie     else
16541cc83814Sespie       {
16551cc83814Sespie         last = 0;
16561cc83814Sespie         break;
16571cc83814Sespie       }
16581cc83814Sespie   }
16591cc83814Sespie 
16601cc83814Sespie   return last;
16611cc83814Sespie }
16621cc83814Sespie 
16631cc83814Sespie 
1664*1076333cSespie static char *
enumerate_filename(char * pathname,char * basename,int number)1665*1076333cSespie enumerate_filename (char *pathname, char *basename, int number)
1666*1076333cSespie {
1667*1076333cSespie   /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */
1668*1076333cSespie   const int dos_file_names = !HAVE_LONG_FILENAMES (pathname ? pathname : ".");
1669*1076333cSespie   unsigned name_len = strlen (basename);
1670*1076333cSespie   char *filename = xmalloc (10 + strlen (pathname) + name_len);
1671*1076333cSespie   char *base_filename = xmalloc (10 + name_len);
1672*1076333cSespie 
1673*1076333cSespie   sprintf (base_filename, "%s-%d", basename, number);
1674*1076333cSespie 
1675*1076333cSespie   if (dos_file_names)
1676*1076333cSespie     {
1677*1076333cSespie       char *dot = strchr (base_filename, '.');
1678*1076333cSespie       unsigned base_len = strlen (base_filename);
1679*1076333cSespie 
1680*1076333cSespie       if (dot)
1681*1076333cSespie         { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
1682*1076333cSespie           dot[1] = 'i';
1683*1076333cSespie           memmove (number <= 99 ? dot + 2 : dot + 1,
1684*1076333cSespie               base_filename + name_len + 1,
1685*1076333cSespie               strlen (base_filename + name_len + 1) + 1);
1686*1076333cSespie         }
1687*1076333cSespie       else if (base_len > 8)
1688*1076333cSespie         {
1689*1076333cSespie           /* Make foobar-1, .., fooba-10, .., foob-100, ... */
1690*1076333cSespie           unsigned numlen = base_len - name_len;
1691*1076333cSespie 
1692*1076333cSespie           memmove (base_filename + 8 - numlen, base_filename + name_len, numlen + 1);
1693*1076333cSespie         }
1694*1076333cSespie     }
1695*1076333cSespie 
1696*1076333cSespie   sprintf (filename, "%s%s", pathname, base_filename);
1697*1076333cSespie 
1698*1076333cSespie   return filename;
1699*1076333cSespie }
1700*1076333cSespie 
1701*1076333cSespie /* Remove previously split files, to avoid
1702*1076333cSespie    lingering parts of shrinked documents.  */
1703*1076333cSespie void
clean_old_split_files(char * filename)1704*1076333cSespie clean_old_split_files (char *filename)
1705*1076333cSespie {
1706*1076333cSespie   char *root_filename = filename_part (filename);
1707*1076333cSespie   char *root_pathname = pathname_part (filename);
1708*1076333cSespie   int i;
1709*1076333cSespie 
1710*1076333cSespie   /* We break as soon as we hit an inexistent file,
1711*1076333cSespie      so looping until large numbers is harmless.  */
1712*1076333cSespie   for (i = 1; i < 1000; i++)
1713*1076333cSespie     {
1714*1076333cSespie       struct stat st;
1715*1076333cSespie       char *check_file = enumerate_filename (root_pathname, root_filename, i);
1716*1076333cSespie 
1717*1076333cSespie       if (stat (check_file, &st) != 0)
1718*1076333cSespie         break;
1719*1076333cSespie       else if (!S_ISDIR (st.st_mode))
1720*1076333cSespie         {
1721*1076333cSespie           /* Give feedback if requested, removing a file is important.  */
1722*1076333cSespie           if (verbose_mode)
1723*1076333cSespie             printf (_("Removing %s\n"), check_file);
1724*1076333cSespie 
1725*1076333cSespie           /* Warn user that we cannot remove the file.  */
1726*1076333cSespie           if (unlink (check_file) != 0)
1727*1076333cSespie             warning (_("Can't remove file `%s': %s"), check_file, strerror (errno));
1728*1076333cSespie         }
1729*1076333cSespie 
1730*1076333cSespie       free (check_file);
1731*1076333cSespie     }
1732*1076333cSespie }
1733*1076333cSespie 
1734*1076333cSespie 
17351cc83814Sespie /* Split large output files into a series of smaller files.  Each file
17361cc83814Sespie    is pointed to in the tag table, which then gets written out as the
17371cc83814Sespie    original file.  The new files have the same name as the original file
17381cc83814Sespie    with a "-num" attached.  SIZE is the largest number of bytes to allow
17391cc83814Sespie    in any single split file. */
17401cc83814Sespie void
split_file(char * filename,int size)1741*1076333cSespie split_file (char *filename, int size)
17421cc83814Sespie {
17431cc83814Sespie   char *root_filename, *root_pathname;
1744*1076333cSespie   char *the_file;
17451cc83814Sespie   struct stat fileinfo;
17461cc83814Sespie   long file_size;
17471cc83814Sespie   char *the_header;
17481cc83814Sespie   int header_size;
17491cc83814Sespie 
17501cc83814Sespie   /* Can only do this to files with tag tables. */
17511cc83814Sespie   if (!tag_table)
17521cc83814Sespie     return;
17531cc83814Sespie 
17541cc83814Sespie   if (size == 0)
17551cc83814Sespie     size = DEFAULT_SPLIT_SIZE;
17561cc83814Sespie 
1757*1076333cSespie   if ((stat (filename, &fileinfo) != 0)
1758*1076333cSespie       || (((long) fileinfo.st_size) < size))
17591cc83814Sespie     return;
17601cc83814Sespie   file_size = (long) fileinfo.st_size;
17611cc83814Sespie 
1762*1076333cSespie   the_file = find_and_load (filename, 0);
17631cc83814Sespie   if (!the_file)
17641cc83814Sespie     return;
17651cc83814Sespie 
17661cc83814Sespie   root_filename = filename_part (filename);
17671cc83814Sespie   root_pathname = pathname_part (filename);
17681cc83814Sespie 
17691cc83814Sespie   if (!root_pathname)
17701cc83814Sespie     root_pathname = xstrdup ("");
17711cc83814Sespie 
17721cc83814Sespie   /* Start splitting the file.  Walk along the tag table
17731cc83814Sespie      outputting sections of the file.  When we have written
17741cc83814Sespie      all of the nodes in the tag table, make the top-level
17751cc83814Sespie      pointer file, which contains indirect pointers and
17761cc83814Sespie      tags for the nodes. */
17771cc83814Sespie   {
17781cc83814Sespie     int which_file = 1;
17791cc83814Sespie     TAG_ENTRY *tags = tag_table;
17801cc83814Sespie     char *indirect_info = NULL;
17811cc83814Sespie 
1782*1076333cSespie     /* Maybe we want a Local Variables section.  */
1783*1076333cSespie     char *trailer = info_trailer ();
1784*1076333cSespie     int trailer_len = trailer ? strlen (trailer) : 0;
1785*1076333cSespie 
17861cc83814Sespie     /* Remember the `header' of this file.  The first tag in the file is
17871cc83814Sespie        the bottom of the header; the top of the file is the start. */
17881cc83814Sespie     the_header = xmalloc (1 + (header_size = tags->position));
17891cc83814Sespie     memcpy (the_header, the_file, header_size);
17901cc83814Sespie 
17911cc83814Sespie     while (tags)
17921cc83814Sespie       {
17931cc83814Sespie         int file_top, file_bot, limit;
17941cc83814Sespie 
17951cc83814Sespie         /* Have to include the Control-_. */
17961cc83814Sespie         file_top = file_bot = tags->position;
17971cc83814Sespie         limit = file_top + size;
17981cc83814Sespie 
17991cc83814Sespie         /* If the rest of this file is only one node, then
18001cc83814Sespie            that is the entire subfile. */
18011cc83814Sespie         if (last_node_p (tags))
18021cc83814Sespie           {
18031cc83814Sespie             int i = tags->position + 1;
18041cc83814Sespie             char last_char = the_file[i];
18051cc83814Sespie 
18061cc83814Sespie             while (i < file_size)
18071cc83814Sespie               {
18081cc83814Sespie                 if ((the_file[i] == '\037') &&
18091cc83814Sespie                     ((last_char == '\n') ||
18101cc83814Sespie                      (last_char == '\014')))
18111cc83814Sespie                   break;
18121cc83814Sespie                 else
18131cc83814Sespie                   last_char = the_file[i];
18141cc83814Sespie                 i++;
18151cc83814Sespie               }
18161cc83814Sespie             file_bot = i;
18171cc83814Sespie             tags = tags->next_ent;
18181cc83814Sespie             goto write_region;
18191cc83814Sespie           }
18201cc83814Sespie 
18211cc83814Sespie         /* Otherwise, find the largest number of nodes that can fit in
18221cc83814Sespie            this subfile. */
18231cc83814Sespie         for (; tags; tags = tags->next_ent)
18241cc83814Sespie           {
18251cc83814Sespie             if (last_node_p (tags))
18261cc83814Sespie               {
18271cc83814Sespie                 /* This entry is the last node.  Search forward for the end
18281cc83814Sespie                    of this node, and that is the end of this file. */
18291cc83814Sespie                 int i = tags->position + 1;
18301cc83814Sespie                 char last_char = the_file[i];
18311cc83814Sespie 
18321cc83814Sespie                 while (i < file_size)
18331cc83814Sespie                   {
18341cc83814Sespie                     if ((the_file[i] == '\037') &&
18351cc83814Sespie                         ((last_char == '\n') ||
18361cc83814Sespie                          (last_char == '\014')))
18371cc83814Sespie                       break;
18381cc83814Sespie                     else
18391cc83814Sespie                       last_char = the_file[i];
18401cc83814Sespie                     i++;
18411cc83814Sespie                   }
18421cc83814Sespie                 file_bot = i;
18431cc83814Sespie 
18441cc83814Sespie                 if (file_bot < limit)
18451cc83814Sespie                   {
18461cc83814Sespie                     tags = tags->next_ent;
18471cc83814Sespie                     goto write_region;
18481cc83814Sespie                   }
18491cc83814Sespie                 else
18501cc83814Sespie                   {
18511cc83814Sespie                     /* Here we want to write out everything before the last
18521cc83814Sespie                        node, and then write the last node out in a file
18531cc83814Sespie                        by itself. */
18541cc83814Sespie                     file_bot = tags->position;
18551cc83814Sespie                     goto write_region;
18561cc83814Sespie                   }
18571cc83814Sespie               }
18581cc83814Sespie 
18591cc83814Sespie             /* Write region only if this was a node, not an anchor.  */
18601cc83814Sespie             if (tags->next_ent->position > limit
18611cc83814Sespie                 && !(tags->flags & TAG_FLAG_ANCHOR))
18621cc83814Sespie               {
18631cc83814Sespie                 if (tags->position == file_top)
18641cc83814Sespie                   tags = tags->next_ent;
18651cc83814Sespie 
18661cc83814Sespie                 file_bot = tags->position;
18671cc83814Sespie 
18681cc83814Sespie               write_region:
18691cc83814Sespie                 {
18701cc83814Sespie                   int fd;
1871*1076333cSespie                   char *split_filename = enumerate_filename (root_pathname,
1872*1076333cSespie                       root_filename, which_file);
1873*1076333cSespie                   char *split_basename = filename_part (split_filename);
18741cc83814Sespie 
18751cc83814Sespie                   fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
18761cc83814Sespie                   if (fd < 0
18771cc83814Sespie                       || write (fd, the_header, header_size) != header_size
18781cc83814Sespie                       || write (fd, the_file + file_top, file_bot - file_top)
18791cc83814Sespie                          != (file_bot - file_top)
1880*1076333cSespie                       || (trailer_len
1881*1076333cSespie                           && write (fd, trailer, trailer_len) != trailer_len)
1882*1076333cSespie                       || close (fd) < 0)
18831cc83814Sespie                     {
18841cc83814Sespie                       perror (split_filename);
18851cc83814Sespie                       if (fd != -1)
18861cc83814Sespie                         close (fd);
18871cc83814Sespie                       xexit (1);
18881cc83814Sespie                     }
18891cc83814Sespie 
18901cc83814Sespie                   if (!indirect_info)
18911cc83814Sespie                     {
18921cc83814Sespie                       indirect_info = the_file + file_top;
18931cc83814Sespie                       sprintf (indirect_info, "\037\nIndirect:\n");
18941cc83814Sespie                       indirect_info += strlen (indirect_info);
18951cc83814Sespie                     }
18961cc83814Sespie 
18971cc83814Sespie                   sprintf (indirect_info, "%s: %d\n",
18981cc83814Sespie                            split_basename, file_top);
18991cc83814Sespie 
19001cc83814Sespie                   free (split_basename);
19011cc83814Sespie                   free (split_filename);
19021cc83814Sespie                   indirect_info += strlen (indirect_info);
19031cc83814Sespie                   which_file++;
19041cc83814Sespie                   break;
19051cc83814Sespie                 }
19061cc83814Sespie               }
19071cc83814Sespie           }
19081cc83814Sespie       }
19091cc83814Sespie 
1910f986e4fcSmillert     /* We have successfully created the subfiles.  Now write out the
19111cc83814Sespie        original again.  We must use `output_stream', or
19121cc83814Sespie        write_tag_table_indirect () won't know where to place the output. */
19131cc83814Sespie     output_stream = fopen (filename, "w");
19141cc83814Sespie     if (!output_stream)
19151cc83814Sespie       {
19161cc83814Sespie         perror (filename);
19171cc83814Sespie         xexit (1);
19181cc83814Sespie       }
19191cc83814Sespie 
19201cc83814Sespie     {
19211cc83814Sespie       int distance = indirect_info - the_file;
19221cc83814Sespie       fwrite (the_file, 1, distance, output_stream);
19231cc83814Sespie 
19241cc83814Sespie       /* Inhibit newlines. */
19251cc83814Sespie       paragraph_is_open = 0;
19261cc83814Sespie 
1927*1076333cSespie       /* Write the indirect tag table.  */
19281cc83814Sespie       write_tag_table_indirect ();
1929*1076333cSespie 
1930*1076333cSespie       /* preserve local variables in info output.  */
1931*1076333cSespie       if (trailer)
1932*1076333cSespie         {
1933*1076333cSespie           fwrite (trailer, 1, trailer_len, output_stream);
1934*1076333cSespie           free (trailer);
1935*1076333cSespie         }
1936*1076333cSespie 
19371cc83814Sespie       fclose (output_stream);
19381cc83814Sespie       free (the_header);
19391cc83814Sespie       free (the_file);
19401cc83814Sespie       return;
19411cc83814Sespie     }
19421cc83814Sespie   }
19431cc83814Sespie }
1944