156160Sru/* node.c -- nodes for Texinfo.
2146515Sru   $Id: node.c,v 1.27 2004/12/20 23:56:07 karl Exp $
356160Sru
4146515Sru   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
5116525Sru   Foundation, Inc.
656160Sru
756160Sru   This program is free software; you can redistribute it and/or modify
856160Sru   it under the terms of the GNU General Public License as published by
956160Sru   the Free Software Foundation; either version 2, or (at your option)
1056160Sru   any later version.
1156160Sru
1256160Sru   This program is distributed in the hope that it will be useful,
1356160Sru   but WITHOUT ANY WARRANTY; without even the implied warranty of
1456160Sru   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1556160Sru   GNU General Public License for more details.
1656160Sru
1756160Sru   You should have received a copy of the GNU General Public License
1856160Sru   along with this program; if not, write to the Free Software Foundation,
1956160Sru   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2056160Sru
2156160Sru#include "system.h"
2256160Sru#include "cmds.h"
2356160Sru#include "files.h"
24146515Sru#include "float.h"
2556160Sru#include "footnote.h"
2656160Sru#include "macro.h"
2756160Sru#include "makeinfo.h"
2856160Sru#include "node.h"
2993139Sru#include "html.h"
3056160Sru#include "sectioning.h"
3156160Sru#include "insertion.h"
3293139Sru#include "xml.h"
3356160Sru
3456160Sru/* See comments in node.h.  */
3556160SruNODE_REF *node_references = NULL;
3656160SruNODE_REF *node_node_references = NULL;
3756160SruTAG_ENTRY *tag_table = NULL;
3856160Sruint node_number = -1;
39146515Sruint node_order = 0;
4056160Sruint current_section = 0;
4156160Sruint outstanding_node = 0;
4256160Sru
4356160Sru/* Adding nodes, and making tags.  */
4456160Sru
4556160Sru/* Start a new tag table. */
4656160Sruvoid
47146515Sruinit_tag_table (void)
4856160Sru{
4956160Sru  while (tag_table)
5056160Sru    {
5156160Sru      TAG_ENTRY *temp = tag_table;
5256160Sru      free (temp->node);
5356160Sru      free (temp->prev);
5456160Sru      free (temp->next);
5556160Sru      free (temp->up);
5656160Sru      tag_table = tag_table->next_ent;
5756160Sru      free (temp);
5856160Sru    }
5956160Sru}
6056160Sru
6156160Sru/* Write out the contents of the existing tag table.
6256160Sru   INDIRECT_P says how to format the output (it depends on whether the
6356160Sru   table is direct or indirect).  */
6456160Srustatic void
65146515Sruwrite_tag_table_internal (int indirect_p)
6656160Sru{
6756160Sru  TAG_ENTRY *node;
6856160Sru  int old_indent = no_indent;
6956160Sru
7093139Sru  if (xml)
7193139Sru    {
7293139Sru      flush_output ();
7393139Sru      return;
7493139Sru    }
7593139Sru
7656160Sru  no_indent = 1;
7756160Sru  filling_enabled = 0;
7856160Sru  must_start_paragraph = 0;
7956160Sru  close_paragraph ();
8056160Sru
8156160Sru  if (!indirect_p)
8256160Sru    {
8356160Sru      no_indent = 1;
8456160Sru      insert ('\n');
8556160Sru    }
8656160Sru
8756160Sru  add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
8856160Sru
8956160Sru  /* Do not collapse -- to -, etc., in node names.  */
9056160Sru  in_fixed_width_font++;
9156160Sru
9256160Sru  for (node = tag_table; node; node = node->next_ent)
9356160Sru    {
9456160Sru      if (node->flags & TAG_FLAG_ANCHOR)
9556160Sru        { /* This reference is to an anchor.  */
9656160Sru          execute_string ("Ref: %s", node->node);
9756160Sru        }
9856160Sru      else
9956160Sru        { /* This reference is to a node.  */
10056160Sru          execute_string ("Node: %s", node->node);
10156160Sru        }
10256160Sru      add_word_args ("\177%d\n", node->position);
10356160Sru    }
10456160Sru
10556160Sru  add_word ("\037\nEnd Tag Table\n");
10656160Sru
10756160Sru  /* Do not collapse -- to -, etc., in node names.  */
10856160Sru  in_fixed_width_font--;
10956160Sru
11056160Sru  flush_output ();
11156160Sru  no_indent = old_indent;
11256160Sru}
11356160Sru
11456160Sruvoid
115146515Sruwrite_tag_table (char *filename)
11656160Sru{
117146515Sru  output_stream = fopen (filename, "a");
118146515Sru  if (!output_stream)
119146515Sru    {
120146515Sru      fs_error (filename);
121146515Sru      return;
122146515Sru    }
123146515Sru
12456160Sru  write_tag_table_internal (0); /* Not indirect. */
125146515Sru
126146515Sru  if (fclose (output_stream) != 0)
127146515Sru    fs_error (filename);
12856160Sru}
12956160Sru
130146515Srustatic void
131146515Sruwrite_tag_table_indirect (void)
13256160Sru{
13356160Sru  write_tag_table_internal (1);
13456160Sru}
13556160Sru
13656160Sru/* Convert "top" and friends into "Top". */
13756160Srustatic void
138146515Srunormalize_node_name (char *string)
13956160Sru{
14056160Sru  if (strcasecmp (string, "Top") == 0)
14156160Sru    strcpy (string, "Top");
14256160Sru}
14356160Sru
144146515Srustatic char *
145146515Sruget_node_token (int expand)
14656160Sru{
14756160Sru  char *string;
14856160Sru
14956160Sru  get_until_in_line (expand, ",", &string);
15056160Sru
15156160Sru  if (curchar () == ',')
15256160Sru    input_text_offset++;
15356160Sru
15456160Sru  fix_whitespace (string);
15556160Sru
15656160Sru  /* Force all versions of "top" to be "Top". */
15756160Sru  normalize_node_name (string);
15856160Sru
15956160Sru  return string;
16056160Sru}
16156160Sru
16256160Sru/* Expand any macros and other directives in a node name, and
16356160Sru   return the expanded name as an malloc'ed string.  */
16456160Sruchar *
165146515Sruexpand_node_name (char *node)
16656160Sru{
16756160Sru  char *result = node;
16856160Sru
16956160Sru  if (node)
17056160Sru    {
17156160Sru      /* Don't expand --, `` etc., in case somebody will want
17256160Sru         to print the result.  */
17356160Sru      in_fixed_width_font++;
17456160Sru      result = expansion (node, 0);
17556160Sru      in_fixed_width_font--;
17656160Sru      fix_whitespace (result);
17756160Sru      normalize_node_name (result);
17856160Sru    }
17956160Sru  return result;
18056160Sru}
18156160Sru
18256160Sru/* Look up NAME in the tag table, and return the associated
18356160Sru   tag_entry.  If the node is not in the table return NULL. */
18456160SruTAG_ENTRY *
185146515Srufind_node (char *name)
18656160Sru{
18756160Sru  TAG_ENTRY *tag = tag_table;
18856160Sru  char *expanded_name;
18956160Sru  char n1 = name[0];
19056160Sru
19156160Sru  while (tag)
19256160Sru    {
19356160Sru      if (tag->node[0] == n1 && strcmp (tag->node, name) == 0)
19456160Sru        return tag;
19556160Sru      tag = tag->next_ent;
19656160Sru    }
19756160Sru
19856160Sru  if (!expensive_validation)
19956160Sru    return NULL;
20056160Sru
20156160Sru  /* Try harder.  Maybe TAG_TABLE has the expanded NAME, or maybe NAME
20256160Sru     is expanded while TAG_TABLE has its unexpanded form.  This may
20356160Sru     slow down the search, but if they want this feature, let them
20456160Sru     pay!  If they want it fast, they should write every node name
20556160Sru     consistently (either always expanded or always unexpaned).  */
20656160Sru  expanded_name = expand_node_name (name);
20756160Sru  for (tag = tag_table; tag; tag = tag->next_ent)
20856160Sru    {
20956160Sru      if (STREQ (tag->node, expanded_name))
21056160Sru        break;
21156160Sru      /* If the tag name doesn't have the command prefix, there's no
21256160Sru         chance it could expand into anything but itself.  */
21356160Sru      if (strchr (tag->node, COMMAND_PREFIX))
21456160Sru        {
21556160Sru          char *expanded_node = expand_node_name (tag->node);
21656160Sru
21756160Sru          if (STREQ (expanded_node, expanded_name))
21856160Sru            {
21956160Sru              free (expanded_node);
22056160Sru              break;
22156160Sru            }
22256160Sru          free (expanded_node);
22356160Sru        }
22456160Sru    }
22556160Sru  free (expanded_name);
22656160Sru  return tag;
22756160Sru}
22856160Sru
22993139Sru/* Look in the tag table for a node whose file name is FNAME, and
23093139Sru   return the associated tag_entry.  If there's no such node in the
23193139Sru   table, return NULL. */
232146515Srustatic TAG_ENTRY *
233146515Srufind_node_by_fname (char *fname)
23493139Sru{
23593139Sru  TAG_ENTRY *tag = tag_table;
23693139Sru  while (tag)
23793139Sru    {
23893139Sru      if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0)
23993139Sru	return tag;
24093139Sru      tag = tag->next_ent;
24193139Sru    }
24293139Sru
24393139Sru  return tag;
24493139Sru}
24593139Sru
24693139Sru/* Remember next, prev, etc. references in a @node command, where we
24756160Sru   don't care about most of the entries. */
24856160Srustatic void
249146515Sruremember_node_node_reference (char *node)
25056160Sru{
25156160Sru  NODE_REF *temp = xmalloc (sizeof (NODE_REF));
25256160Sru  int number;
25356160Sru
25456160Sru  if (!node) return;
25556160Sru  temp->next = node_node_references;
25656160Sru  temp->node = xstrdup (node);
25756160Sru  temp->type = followed_reference;
25856160Sru  number = number_of_node (node);
25956160Sru  if (number)
26056160Sru    temp->number = number;      /* Already assigned. */
26156160Sru  else
26256160Sru    {
26356160Sru      node_number++;
26456160Sru      temp->number = node_number;
26556160Sru    }
26656160Sru  node_node_references = temp;
26756160Sru}
26856160Sru
26956160Sru/* Remember NODE and associates. */
270146515Srustatic void
271146515Sruremember_node (char *node, char *prev, char *next, char *up,
272146515Sru    int position, int line_no, char *fname, int flags)
27356160Sru{
27456160Sru  /* Check for existence of this tag already. */
27556160Sru  if (validating)
27656160Sru    {
27756160Sru      TAG_ENTRY *tag = find_node (node);
27856160Sru      if (tag)
27956160Sru        {
28056160Sru          line_error (_("Node `%s' previously defined at line %d"),
28156160Sru                      node, tag->line_no);
28256160Sru          return;
28356160Sru        }
28456160Sru    }
28556160Sru
28656160Sru  if (!(flags & TAG_FLAG_ANCHOR))
28756160Sru    {
28856160Sru      /* Make this the current node. */
28956160Sru      current_node = node;
29056160Sru    }
29156160Sru
29256160Sru  /* Add it to the list. */
29356160Sru  {
29456160Sru    int number = number_of_node (node);
29556160Sru
29656160Sru    TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY));
29756160Sru    new->node = node;
29856160Sru    new->prev = prev;
29956160Sru    new->next = next;
30056160Sru    new->up = up;
30156160Sru    new->position = position;
30256160Sru    new->line_no = line_no;
30356160Sru    new->filename = node_filename;
30456160Sru    new->touched = 0;
30556160Sru    new->flags = flags;
30656160Sru    if (number)
30756160Sru      new->number = number;     /* Already assigned. */
30856160Sru    else
30956160Sru      {
31056160Sru        node_number++;
31156160Sru        new->number = node_number;
31256160Sru      }
313114472Sru    if (fname)
314114472Sru      new->html_fname = fname;
315114472Sru    else
316114472Sru      /* This happens for Top node under split-HTML, for example.  */
317114472Sru      new->html_fname
318114472Sru	= normalize_filename (filename_part (current_output_filename));
31956160Sru    new->next_ent = tag_table;
320146515Sru
321146515Sru    /* Increment the order counter, and save it.  */
322146515Sru    node_order++;
323146515Sru    new->order = node_order;
324146515Sru
32556160Sru    tag_table = new;
32656160Sru  }
32756160Sru
32856160Sru  if (html)
32956160Sru    { /* Note the references to the next etc. nodes too.  */
33056160Sru      remember_node_node_reference (next);
33156160Sru      remember_node_node_reference (prev);
33256160Sru      remember_node_node_reference (up);
33356160Sru    }
33456160Sru}
33556160Sru
33656160Sru/* Remember this node name for later validation use.  This is used to
33756160Sru   remember menu references while reading the input file.  After the
33856160Sru   output file has been written, if validation is on, then we use the
33956160Sru   contents of `node_references' as a list of nodes to validate.  */
34056160Sruvoid
341146515Sruremember_node_reference (char *node, int line, enum reftype type)
34256160Sru{
34356160Sru  NODE_REF *temp = xmalloc (sizeof (NODE_REF));
34456160Sru  int number = number_of_node (node);
34556160Sru
34656160Sru  temp->next = node_references;
34756160Sru  temp->node = xstrdup (node);
34856160Sru  temp->line_no = line;
34956160Sru  temp->section = current_section;
35056160Sru  temp->type = type;
35156160Sru  temp->containing_node = xstrdup (current_node ? current_node : "");
35256160Sru  temp->filename = node_filename;
35356160Sru  if (number)
35456160Sru    temp->number = number;      /* Already assigned. */
35556160Sru  else
35656160Sru    {
35756160Sru      node_number++;
35856160Sru      temp->number = node_number;
35956160Sru    }
36056160Sru
36156160Sru  node_references = temp;
36256160Sru}
36356160Sru
36456160Srustatic void
365146515Sruisolate_nodename (char *nodename)
36656160Sru{
36756160Sru  int i, c;
36856160Sru  int paren_seen, paren;
36956160Sru
37056160Sru  if (!nodename)
37156160Sru    return;
37256160Sru
37356160Sru  canon_white (nodename);
37456160Sru  paren_seen = paren = i = 0;
37556160Sru
37656160Sru  if (*nodename == '.' || !*nodename)
37756160Sru    {
37856160Sru      *nodename = 0;
37956160Sru      return;
38056160Sru    }
38156160Sru
38256160Sru  if (*nodename == '(')
38356160Sru    {
38456160Sru      paren++;
38556160Sru      paren_seen++;
38656160Sru      i++;
38756160Sru    }
38856160Sru
38956160Sru  for (; (c = nodename[i]); i++)
39056160Sru    {
39156160Sru      if (paren)
39256160Sru        {
39356160Sru          if (c == '(')
39456160Sru            paren++;
39556160Sru          else if (c == ')')
39656160Sru            paren--;
39756160Sru
39856160Sru          continue;
39956160Sru        }
40056160Sru
40156160Sru      /* If the character following the close paren is a space, then this
40256160Sru         node has no more characters associated with it. */
40356160Sru      if (c == '\t' ||
40456160Sru          c == '\n' ||
40556160Sru          c == ','  ||
40656160Sru          ((paren_seen && nodename[i - 1] == ')') &&
40756160Sru           (c == ' ' || c == '.')) ||
40856160Sru          (c == '.' &&
40956160Sru           ((!nodename[i + 1] ||
41056160Sru             (cr_or_whitespace (nodename[i + 1])) ||
41156160Sru             (nodename[i + 1] == ')')))))
41256160Sru        break;
41356160Sru    }
41456160Sru  nodename[i] = 0;
41556160Sru}
41656160Sru
41756160Sru/* This function gets called at the start of every line while inside a
41856160Sru   menu.  It checks to see if the line starts with "* ", and if so and
41956160Sru   REMEMBER_REF is nonzero, remembers the node reference as type
42056160Sru   REF_TYPE that this menu refers to.  input_text_offset is at the \n
42156160Sru   just before the menu line.  If REMEMBER_REF is zero, REF_TYPE is unused.  */
42256160Sru#define MENU_STARTER "* "
42356160Sruchar *
424146515Sruglean_node_from_menu (int remember_ref, enum reftype ref_type)
42556160Sru{
42656160Sru  int i, orig_offset = input_text_offset;
42756160Sru  char *nodename;
42856160Sru  char *line, *expanded_line;
42956160Sru  char *old_input = input_text;
43093139Sru  int old_size = input_text_length;
43156160Sru
43256160Sru  if (strncmp (&input_text[input_text_offset + 1],
43356160Sru               MENU_STARTER,
43456160Sru               strlen (MENU_STARTER)) != 0)
43556160Sru    return NULL;
43656160Sru  else
43756160Sru    input_text_offset += strlen (MENU_STARTER) + 1;
43856160Sru
43956160Sru  /* The menu entry might include macro calls, so we need to expand them.  */
44056160Sru  get_until ("\n", &line);
44156160Sru  only_macro_expansion++;       /* only expand macros in menu entries */
44256160Sru  expanded_line = expansion (line, 0);
44356160Sru  only_macro_expansion--;
44456160Sru  free (line);
44556160Sru  input_text = expanded_line;
44656160Sru  input_text_offset = 0;
44756160Sru  input_text_length = strlen (expanded_line);
44856160Sru
44956160Sru  get_until_in_line (0, ":", &nodename);
45056160Sru  if (curchar () == ':')
45156160Sru    input_text_offset++;
45256160Sru
45356160Sru  if (curchar () != ':')
45456160Sru    {
45556160Sru      free (nodename);
45656160Sru      get_until_in_line (0, "\n", &nodename);
45756160Sru      isolate_nodename (nodename);
45856160Sru    }
45956160Sru
46056160Sru  input_text = old_input;
46156160Sru  input_text_offset = orig_offset;
46256160Sru  input_text_length = old_size;
46356160Sru  free (expanded_line);
46456160Sru  fix_whitespace (nodename);
46556160Sru  normalize_node_name (nodename);
46656160Sru  i = strlen (nodename);
46756160Sru  if (i && nodename[i - 1] == ':')
46856160Sru    nodename[i - 1] = 0;
46956160Sru
47056160Sru  if (remember_ref)
47156160Sru    remember_node_reference (nodename, line_number, ref_type);
47256160Sru
47356160Sru  return nodename;
47456160Sru}
47556160Sru
47656160Sru/* Set the name of the current output file.  */
47756160Sruvoid
478146515Sruset_current_output_filename (const char *fname)
47956160Sru{
48056160Sru  if (current_output_filename)
48156160Sru    free (current_output_filename);
48256160Sru  current_output_filename = xstrdup (fname);
48356160Sru}
48456160Sru
485146515Sru
486146515Sru/* Output the <a name="..."></a> constructs for NODE.  We output both
487146515Sru   the new-style conversion and the old-style, if they are different.
488146515Sru   See comments at `add_escaped_anchor_name' in html.c.  */
489146515Sru
490146515Srustatic void
491146515Sruadd_html_names (char *node)
492146515Sru{
493146515Sru  char *tem = expand_node_name (node);
494146515Sru  char *otem = xstrdup (tem);
495146515Sru
496146515Sru  /* Determine if the old and new schemes come up with different names;
497146515Sru     only output the old scheme if that is so.  We don't want to output
498146515Sru     the same name twice.  */
499146515Sru  canon_white (otem);
500146515Sru  {
501146515Sru    char *optr = otem;
502146515Sru    int need_old = 0;
503146515Sru
504146515Sru    for (; *optr; optr++)
505146515Sru      {
506146515Sru        if (!cr_or_whitespace (*optr) && !URL_SAFE_CHAR (*optr))
507146515Sru          {
508146515Sru            need_old = 1;
509146515Sru            break;
510146515Sru          }
511146515Sru      }
512146515Sru
513146515Sru    if (need_old)
514146515Sru      {
515146515Sru        add_word ("<a name=\"");
516146515Sru        add_anchor_name (otem, -1);  /* old anchor name conversion */
517146515Sru        add_word ("\"></a>\n");
518146515Sru      }
519146515Sru    free (otem);
520146515Sru  }
521146515Sru
522146515Sru  /* Always output the new scheme.  */
523146515Sru  canon_white (tem);
524146515Sru  add_word ("<a name=\"");
525146515Sru  add_anchor_name (tem, 0);
526146515Sru  add_word ("\"></a>\n");
527146515Sru
528146515Sru  free (tem);
529146515Sru}
530146515Sru
531146515Sru
53256160Sru/* The order is: nodename, nextnode, prevnode, upnode.
53356160Sru   If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
53456160Sru   You must follow a node command which has those fields defaulted
535114472Sru   with a sectioning command (e.g., @chapter) giving the "level" of that node.
53656160Sru   It is an error not to do so.
53756160Sru   The defaults come from the menu in this node's parent. */
53856160Sruvoid
539146515Srucm_node (void)
54056160Sru{
54193139Sru  static long epilogue_len = 0L;
54256160Sru  char *node, *prev, *next, *up;
54356160Sru  int new_node_pos, defaulting, this_section;
54456160Sru  int no_warn = 0;
54593139Sru  char *fname_for_this_node = NULL;
54693139Sru  char *tem;
54793139Sru  TAG_ENTRY *tag = NULL;
54856160Sru
54956160Sru  if (strcmp (command, "nwnode") == 0)
55056160Sru    no_warn = TAG_FLAG_NO_WARN;
55156160Sru
55256160Sru  /* Get rid of unmatched brace arguments from previous commands. */
55356160Sru  discard_braces ();
55456160Sru
55556160Sru  /* There also might be insertions left lying around that haven't been
55656160Sru     ended yet.  Do that also. */
55756160Sru  discard_insertions (1);
55856160Sru
55956160Sru  if (!html && !already_outputting_pending_notes)
56056160Sru    {
56156160Sru      close_paragraph ();
56256160Sru      output_pending_notes ();
56356160Sru    }
56456160Sru
56556160Sru  new_node_pos = output_position;
56656160Sru
56756160Sru  if (macro_expansion_output_stream && !executing_string)
56856160Sru    append_to_expansion_output (input_text_offset + 1);
56956160Sru
57056160Sru  /* Do not collapse -- to -, etc., in node names.  */
57156160Sru  in_fixed_width_font++;
57256160Sru
57356160Sru  /* While expanding the @node line, leave any non-macros
57456160Sru     intact, so that the macro-expanded output includes them.  */
57556160Sru  only_macro_expansion++;
57656160Sru  node = get_node_token (1);
57756160Sru  only_macro_expansion--;
57856160Sru  next = get_node_token (0);
57956160Sru  prev = get_node_token (0);
58056160Sru  up = get_node_token (0);
58156160Sru
58293139Sru  if (html && splitting
58393139Sru      /* If there is a Top node, it always goes into index.html.  So
58493139Sru	 don't start a new HTML file for Top.  */
58593139Sru      && (top_node_seen || strcasecmp (node, "Top") != 0))
58693139Sru    {
58793139Sru      /* We test *node here so that @node without a valid name won't
58893139Sru	 start a new file name with a bogus name such as ".html".
58993139Sru	 This could happen if we run under "--force", where we cannot
59093139Sru	 simply bail out.  Continuing to use the same file sounds like
59193139Sru	 the best we can do in such cases.  */
59293139Sru      if (current_output_filename && output_stream && *node)
59393139Sru	{
59493139Sru	  char *fname_for_prev_node;
59593139Sru
59693139Sru	  if (current_node)
59793139Sru	    {
59893139Sru	      /* NOTE: current_node at this point still holds the name
59993139Sru		 of the previous node.  */
60093139Sru	      tem = expand_node_name (current_node);
60193139Sru	      fname_for_prev_node = nodename_to_filename (tem);
60293139Sru	      free (tem);
60393139Sru	    }
60493139Sru	  else /* could happen if their top node isn't named "Top" */
60593139Sru	    fname_for_prev_node = filename_part (current_output_filename);
60693139Sru	  tem = expand_node_name (node);
60793139Sru	  fname_for_this_node = nodename_to_filename (tem);
60893139Sru	  free (tem);
60993139Sru	  /* Don't close current output file, if next output file is
61093139Sru             to have the same name.  This may happen at top level, or
61193139Sru             if two nodes produce the same file name under --split.  */
61293139Sru	  if (FILENAME_CMP (fname_for_this_node, fname_for_prev_node) != 0)
61393139Sru	    {
61493139Sru	      long pos1 = 0;
61593139Sru
61693139Sru	      /* End the current split output file. */
61793139Sru	      close_paragraph ();
61893139Sru	      output_pending_notes ();
61993139Sru	      start_paragraph ();
62093139Sru	      /* Compute the length of the HTML file's epilogue.  We
62193139Sru		 cannot know the value until run time, due to the
62293139Sru		 text/binary nuisance on DOS/Windows platforms, where
62393139Sru		 2 `\r' characters could be added to the epilogue when
62493139Sru		 it is written in text mode.  */
62593139Sru	      if (epilogue_len == 0)
62693139Sru		{
62793139Sru		  flush_output ();
62893139Sru		  pos1 = ftell (output_stream);
62993139Sru		}
63093139Sru	      add_word ("</body></html>\n");
63193139Sru	      close_paragraph ();
63293139Sru	      if (epilogue_len == 0)
63393139Sru		epilogue_len = ftell (output_stream) - pos1;
63493139Sru	      fclose (output_stream);
63593139Sru	      output_stream = NULL;
636146515Sru              output_position = 0;
63793139Sru	      tag = find_node_by_fname (fname_for_this_node);
63893139Sru	    }
63993139Sru	  free (fname_for_prev_node);
64093139Sru	}
64193139Sru    }
64293139Sru
64393139Sru  filling_enabled = indented_fill = 0;
64493139Sru  if (!html || (html && splitting))
64593139Sru    current_footnote_number = 1;
646116525Sru
64756160Sru  if (verbose_mode)
64856160Sru    printf (_("Formatting node %s...\n"), node);
64956160Sru
65056160Sru  if (macro_expansion_output_stream && !executing_string)
65156160Sru    remember_itext (input_text, input_text_offset);
65256160Sru
653146515Sru  /* Reset the line number in each node for Info output, so that
654146515Sru     index entries will save the line numbers of parent node.  */
655146515Sru  node_line_number = 0;
656146515Sru
65756160Sru  no_indent = 1;
65893139Sru  if (xml)
65956160Sru    {
660100513Sru      xml_begin_document (current_output_filename);
66193139Sru      xml_begin_node ();
66293139Sru      if (!docbook)
66393139Sru	{
664116525Sru	  xml_insert_element (NODENAME, START);
66593139Sru	  if (macro_expansion_output_stream && !executing_string)
66693139Sru	    me_execute_string (node);
66793139Sru	  else
66893139Sru	    execute_string ("%s", node);
66993139Sru	  xml_insert_element (NODENAME, END);
67093139Sru	}
67193139Sru      else
67293139Sru	xml_node_id = xml_id (node);
67393139Sru    }
67493139Sru  else if (!no_headers && !html)
67593139Sru    {
676146515Sru      /* Emacs Info reader cannot grok indented escape sequence.  */
677146515Sru      kill_self_indent (-1);
678146515Sru
67956160Sru      add_word_args ("\037\nFile: %s,  Node: ", pretty_output_filename);
68056160Sru
68156160Sru      if (macro_expansion_output_stream && !executing_string)
68256160Sru        me_execute_string (node);
68356160Sru      else
68456160Sru        execute_string ("%s", node);
68556160Sru      filling_enabled = indented_fill = 0;
68656160Sru    }
68756160Sru
68856160Sru  /* Check for defaulting of this node's next, prev, and up fields. */
68956160Sru  defaulting = (*next == 0 && *prev == 0 && *up == 0);
69056160Sru
691146515Sru  this_section = what_section (input_text + input_text_offset, NULL);
69256160Sru
69356160Sru  /* If we are defaulting, then look at the immediately following
69456160Sru     sectioning command (error if none) to determine the node's
69556160Sru     level.  Find the node that contains the menu mentioning this node
69656160Sru     that is one level up (error if not found).  That node is the "Up"
69756160Sru     of this node.  Default the "Next" and "Prev" from the menu. */
69856160Sru  if (defaulting)
69956160Sru    {
70056160Sru      NODE_REF *last_ref = NULL;
70156160Sru      NODE_REF *ref = node_references;
70256160Sru
70356160Sru      if (this_section < 0 && !STREQ (node, "Top"))
70456160Sru        {
70556160Sru          char *polite_section_name = "top";
70656160Sru          int i;
70756160Sru
70856160Sru          for (i = 0; section_alist[i].name; i++)
70956160Sru            if (section_alist[i].level == current_section + 1)
71056160Sru              {
71156160Sru                polite_section_name = section_alist[i].name;
71256160Sru                break;
71356160Sru              }
71456160Sru
71556160Sru          line_error
716114472Sru            (_("Node `%s' requires a sectioning command (e.g., %c%s)"),
71756160Sru             node, COMMAND_PREFIX, polite_section_name);
71856160Sru        }
71956160Sru      else
72056160Sru        {
72156160Sru          if (strcmp (node, "Top") == 0)
72256160Sru            {
72356160Sru              /* Default the NEXT pointer to be the first menu item in
72456160Sru                 this node, if there is a menu in this node.  We have to
72556160Sru                 try very hard to find the menu, as it may be obscured
72656160Sru                 by execution_strings which are on the filestack.  For
72756160Sru                 every member of the filestack which has a FILENAME
72856160Sru                 member which is identical to the current INPUT_FILENAME,
72956160Sru                 search forward from that offset. */
73056160Sru              int saved_input_text_offset = input_text_offset;
73156160Sru              int saved_input_text_length = input_text_length;
73256160Sru              char *saved_input_text = input_text;
73356160Sru              FSTACK *next_file = filestack;
73456160Sru
73556160Sru              int orig_offset, orig_size;
73656160Sru
737146515Sru              int bye_offset = search_forward ("\n@bye", input_text_offset);
738146515Sru
73956160Sru              /* No matter what, make this file point back at `(dir)'. */
74056160Sru              free (up);
74156160Sru              up = xstrdup ("(dir)"); /* html fixxme */
74256160Sru
74356160Sru              while (1)
74456160Sru                {
74556160Sru                  orig_offset = input_text_offset;
74656160Sru                  orig_size =
74756160Sru                    search_forward (node_search_string, orig_offset);
74856160Sru
74956160Sru                  if (orig_size < 0)
75056160Sru                    orig_size = input_text_length;
75156160Sru
75256160Sru                  input_text_offset = search_forward ("\n@menu", orig_offset);
75356160Sru                  if (input_text_offset > -1
754146515Sru                      && (bye_offset > -1 && input_text_offset < bye_offset)
75556160Sru                      && cr_or_whitespace (input_text[input_text_offset + 6]))
75656160Sru                    {
75756160Sru                      char *nodename_from_menu = NULL;
75856160Sru
75956160Sru                      input_text_offset =
76056160Sru                        search_forward ("\n* ", input_text_offset);
76156160Sru
76256160Sru                      if (input_text_offset != -1)
76356160Sru                        nodename_from_menu = glean_node_from_menu (0, 0);
76456160Sru
76556160Sru                      if (nodename_from_menu)
76656160Sru                        {
76756160Sru                          free (next);
76856160Sru                          next = nodename_from_menu;
76956160Sru                          break;
77056160Sru                        }
77156160Sru                    }
77256160Sru
77356160Sru                  /* We got here, so it hasn't been found yet.  Try
77456160Sru                     the next file on the filestack if there is one. */
77556160Sru                  if (next_file
77656160Sru                      && FILENAME_CMP (next_file->filename, input_filename)
77756160Sru                          == 0)
77856160Sru                    {
77956160Sru                      input_text = next_file->text;
78056160Sru                      input_text_offset = next_file->offset;
78156160Sru                      input_text_length = next_file->size;
78256160Sru                      next_file = next_file->next;
78356160Sru                    }
78456160Sru                  else
78556160Sru                    { /* No more input files to check. */
78656160Sru                      break;
78756160Sru                    }
78856160Sru                }
78956160Sru
79056160Sru              input_text = saved_input_text;
79156160Sru              input_text_offset = saved_input_text_offset;
79256160Sru              input_text_length = saved_input_text_length;
79356160Sru            }
79456160Sru        }
79556160Sru
79656160Sru      /* Fix the level of the menu references in the Top node, iff it
79756160Sru         was declared with @top, and no subsequent reference was found. */
79856160Sru      if (top_node_seen && !non_top_node_seen)
79956160Sru        {
80056160Sru          /* Then this is the first non-@top node seen. */
80156160Sru          int level;
80256160Sru
80356160Sru          level = set_top_section_level (this_section - 1);
80456160Sru          non_top_node_seen = 1;
80556160Sru
80656160Sru          while (ref)
80756160Sru            {
80856160Sru              if (ref->section == level)
80956160Sru                ref->section = this_section - 1;
81056160Sru              ref = ref->next;
81156160Sru            }
81256160Sru
81356160Sru          ref = node_references;
81456160Sru        }
81556160Sru
81656160Sru      while (ref)
81756160Sru        {
81856160Sru          if (ref->section == (this_section - 1)
81956160Sru              && ref->type == menu_reference
82056160Sru              && strcmp (ref->node, node) == 0)
82156160Sru            {
82256160Sru              char *containing_node = ref->containing_node;
82356160Sru
82456160Sru              free (up);
82556160Sru              up = xstrdup (containing_node);
82656160Sru
82756160Sru              if (last_ref
82856160Sru                  && last_ref->type == menu_reference
82956160Sru                  && strcmp (last_ref->containing_node, containing_node) == 0)
83056160Sru                {
83156160Sru                  free (next);
83256160Sru                  next = xstrdup (last_ref->node);
83356160Sru                }
83456160Sru
83556160Sru              while (ref->section == this_section - 1
83656160Sru                     && ref->next
83756160Sru                     && ref->next->type != menu_reference)
83856160Sru                ref = ref->next;
83956160Sru
84056160Sru              if (ref->next && ref->type == menu_reference
84156160Sru                  && strcmp (ref->next->containing_node, containing_node) == 0)
84256160Sru                {
84356160Sru                  free (prev);
84456160Sru                  prev = xstrdup (ref->next->node);
84556160Sru                }
84656160Sru              else if (!ref->next
84756160Sru                       && strcasecmp (ref->containing_node, "Top") == 0)
84856160Sru                {
84956160Sru                  free (prev);
85056160Sru                  prev = xstrdup (ref->containing_node);
85156160Sru                }
85256160Sru              break;
85356160Sru            }
85456160Sru          last_ref = ref;
85556160Sru          ref = ref->next;
85656160Sru        }
85756160Sru    }
85856160Sru
85956160Sru  /* Insert the correct args if we are expanding macros, and the node's
86056160Sru     pointers weren't defaulted. */
86156160Sru  if (macro_expansion_output_stream && !executing_string && !defaulting)
86256160Sru    {
86356160Sru      char *temp;
86456160Sru      int op_orig = output_paragraph_offset;
86556160Sru      int meta_pos_orig = meta_char_pos;
86656160Sru      int extra = html ? strlen (node) : 0;
86756160Sru
86856160Sru      temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up));
86956160Sru      sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up);
87056160Sru      me_execute_string (temp);
87156160Sru      free (temp);
87256160Sru
87356160Sru      output_paragraph_offset = op_orig;
87456160Sru      meta_char_pos = meta_pos_orig;
87556160Sru    }
87656160Sru
87756160Sru  if (!*node)
87856160Sru    {
87956160Sru      line_error (_("No node name specified for `%c%s' command"),
88056160Sru                  COMMAND_PREFIX, command);
88156160Sru      free (node);
88256160Sru      free (next); next = NULL;
88356160Sru      free (prev); prev= NULL;
88456160Sru      free (up);   up = NULL;
88556160Sru      node_number++;            /* else it doesn't get bumped */
88656160Sru    }
88756160Sru  else
88856160Sru    {
88956160Sru      if (!*next) { free (next); next = NULL; }
89056160Sru      if (!*prev) { free (prev); prev = NULL; }
89156160Sru      if (!*up)   { free (up);   up = NULL;   }
89293139Sru      remember_node (node, prev, next, up, new_node_pos, line_number,
89393139Sru		     fname_for_this_node, no_warn);
89456160Sru      outstanding_node = 1;
89556160Sru    }
89656160Sru
89756160Sru  if (html)
89856160Sru    {
89993139Sru      if (splitting && *node && output_stream == NULL)
90093139Sru        {
90193139Sru	  char *dirname;
90293139Sru	  char filename[PATH_MAX];
90356160Sru
90493139Sru	  dirname = pathname_part (current_output_filename);
90593139Sru	  strcpy (filename, dirname);
90693139Sru	  strcat (filename, fname_for_this_node);
90793139Sru	  free (dirname);
90856160Sru
90993139Sru	  /* See if the node name converted to a file name clashes
91093139Sru	     with other nodes or anchors.  If it clashes with an
91193139Sru	     anchor, we complain and nuke that anchor's file.  */
91293139Sru	  if (!tag)
91393139Sru	    {
91493139Sru	      output_stream = fopen (filename, "w");
91593139Sru	      html_output_head_p = 0; /* so that we generate HTML preamble */
91693139Sru	      html_output_head ();
91793139Sru	    }
91893139Sru	  else if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
91993139Sru	    {
92093139Sru	      line_error (_("Anchor `%s' and node `%s' map to the same file name"),
92193139Sru			  tag->node, node);
92293139Sru	      file_line_error (tag->filename, tag->line_no,
92393139Sru			       _("This @anchor command ignored; references to it will not work"));
92493139Sru	      file_line_error (tag->filename, tag->line_no,
92593139Sru			       _("Rename this anchor or use the `--no-split' option"));
92693139Sru	      /* Nuke the file name recorded in anchor's tag.
92793139Sru		 Since we are about to nuke the file itself, we
92893139Sru		 don't want find_node_by_fname to consider this
92993139Sru		 anchor anymore.  */
93093139Sru	      free (tag->html_fname);
93193139Sru	      tag->html_fname = NULL;
93293139Sru	      output_stream = fopen (filename, "w");
93393139Sru	      html_output_head_p = 0; /* so that we generate HTML preamble */
93493139Sru	      html_output_head ();
93593139Sru	    }
93693139Sru	  else
93793139Sru	    {
93893139Sru	      /* This node's file name clashes with another node.
93993139Sru		 We put them both on the same file.  */
94093139Sru	      output_stream = fopen (filename, "r+");
94193139Sru	      if (output_stream)
94293139Sru		{
94393139Sru		  static char html_end[] = "</body></html>\n";
94493139Sru		  char end_line[sizeof(html_end)];
94593139Sru		  int fpos = fseek (output_stream, -epilogue_len,
94693139Sru				    SEEK_END);
94793139Sru
94893139Sru		  if (fpos < 0
94993139Sru		      || fgets (end_line, sizeof (html_end),
95093139Sru				output_stream) == NULL
95193139Sru		      /* Paranoia: did someone change the way HTML
95293139Sru			 files are finished up?  */
95393139Sru		      || strcasecmp (end_line, html_end) != 0)
95493139Sru		    {
95593139Sru		      line_error (_("Unexpected string at end of split-HTML file `%s'"),
95693139Sru				  fname_for_this_node);
95793139Sru		      fclose (output_stream);
95893139Sru		      xexit (1);
95993139Sru		    }
96093139Sru		  fseek (output_stream, -epilogue_len, SEEK_END);
96193139Sru		}
96293139Sru	    }
96356160Sru          if (output_stream == NULL)
96456160Sru            {
96556160Sru              fs_error (filename);
96656160Sru              xexit (1);
96756160Sru            }
96856160Sru          set_current_output_filename (filename);
96956160Sru        }
97056160Sru
97156160Sru      if (!splitting && no_headers)
972146515Sru	{ /* cross refs need a name="#anchor" even if not writing headers */
973146515Sru          add_html_names (node);
97456160Sru	}
97556160Sru
97656160Sru      if (splitting || !no_headers)
977114472Sru        { /* Navigation bar. */
978146515Sru          add_html_block_elt ("<div class=\"node\">\n");
979114472Sru          /* The <p> avoids the links area running on with old Lynxen. */
98056160Sru          add_word_args ("<p>%s\n", splitting ? "" : "<hr>");
98156160Sru
982146515Sru          /* In the split HTML case, the filename is wrong for the
983146515Sru             old-style converted names, but we'll add them anyway, for
984146515Sru             consistency.  (And we need them in the normal (not
985146515Sru             no_headers) nonsplit case.)  */
986146515Sru          add_html_names (node);
987146515Sru
98856160Sru          if (next)
98956160Sru            {
99056160Sru              tem = expansion (next, 0);
991146515Sru	      add_word ((char *) _("Next:"));
992116525Sru              add_word ("&nbsp;");
993146515Sru
994114472Sru	      add_word ("<a rel=\"next\" accesskey=\"n\" href=\"");
99593139Sru	      add_anchor_name (tem, 1);
996146515Sru              tem = escape_string (tem);
99793139Sru	      add_word_args ("\">%s</a>", tem);
998146515Sru
99956160Sru              free (tem);
1000146515Sru
1001146515Sru	      if (prev || up)
1002146515Sru		add_word (",\n");
100356160Sru            }
100456160Sru          if (prev)
100556160Sru            {
100656160Sru              tem = expansion (prev, 0);
1007146515Sru	      add_word ((char *) _("Previous:"));
1008116525Sru              add_word ("&nbsp;");
1009114472Sru	      add_word ("<a rel=\"previous\" accesskey=\"p\" href=\"");
101093139Sru	      add_anchor_name (tem, 1);
1011146515Sru              tem = escape_string (tem);
101293139Sru	      add_word_args ("\">%s</a>", tem);
101356160Sru              free (tem);
1014146515Sru
1015146515Sru	      if (up)
1016146515Sru		add_word (",\n");
101756160Sru            }
101856160Sru          if (up)
101956160Sru            {
102056160Sru              tem = expansion (up, 0);
1021146515Sru	      add_word ((char *) _("Up:"));
1022116525Sru              add_word ("&nbsp;");
1023114472Sru	      add_word ("<a rel=\"up\" accesskey=\"u\" href=\"");
102493139Sru	      add_anchor_name (tem, 1);
1025146515Sru              tem = escape_string (tem);
102693139Sru	      add_word_args ("\">%s</a>", tem);
102756160Sru              free (tem);
102856160Sru            }
102956160Sru          /* html fixxme: we want a `top' or `contents' link here.  */
103056160Sru
1031146515Sru          add_word_args ("\n%s\n", splitting ? "<hr>" : "");
1032114472Sru      	  add_word ("</div>\n");
103356160Sru        }
103456160Sru    }
103593139Sru  else if (docbook)
103693139Sru    ;
103793139Sru  else if (xml)
103893139Sru    {
103993139Sru      if (next)
104093139Sru	{
104193139Sru	  xml_insert_element (NODENEXT, START);
104293139Sru	  execute_string ("%s", next);
104393139Sru	  xml_insert_element (NODENEXT, END);
104493139Sru	}
104593139Sru      if (prev)
104693139Sru	{
104793139Sru	  xml_insert_element (NODEPREV, START);
1048116525Sru	  execute_string ("%s", prev);
104993139Sru	  xml_insert_element (NODEPREV, END);
105093139Sru	}
105193139Sru      if (up)
105293139Sru	{
105393139Sru	  xml_insert_element (NODEUP, START);
105493139Sru	  execute_string ("%s", up);
105593139Sru	  xml_insert_element (NODEUP, END);
105693139Sru	}
105793139Sru    }
105856160Sru  else if (!no_headers)
105956160Sru    {
106056160Sru      if (macro_expansion_output_stream)
106156160Sru        me_inhibit_expansion++;
106256160Sru
106356160Sru      /* These strings are not translatable.  */
106456160Sru      if (next)
106556160Sru        {
106656160Sru          execute_string (",  Next: %s", next);
106756160Sru          filling_enabled = indented_fill = 0;
106856160Sru        }
106956160Sru      if (prev)
107056160Sru        {
107156160Sru          execute_string (",  Prev: %s", prev);
107256160Sru          filling_enabled = indented_fill = 0;
107356160Sru        }
107456160Sru      if (up)
107556160Sru        {
107656160Sru          execute_string (",  Up: %s", up);
107756160Sru          filling_enabled = indented_fill = 0;
107856160Sru        }
107956160Sru      if (macro_expansion_output_stream)
108056160Sru        me_inhibit_expansion--;
108156160Sru    }
108256160Sru
108356160Sru  close_paragraph ();
108456160Sru  no_indent = 0;
108556160Sru
108656160Sru  /* Change the section only if there was a sectioning command. */
108756160Sru  if (this_section >= 0)
108856160Sru    current_section = this_section;
108956160Sru
109056160Sru  if (current_node && STREQ (current_node, "Top"))
109156160Sru    top_node_seen = 1;
109256160Sru
109356160Sru  filling_enabled = 1;
109456160Sru  in_fixed_width_font--;
109556160Sru}
109656160Sru
109756160Sru/* Cross-reference target at an arbitrary spot.  */
109856160Sruvoid
1099146515Srucm_anchor (int arg)
110056160Sru{
110156160Sru  char *anchor;
110293139Sru  char *fname_for_anchor = NULL;
110356160Sru
110456160Sru  if (arg == END)
110556160Sru    return;
110656160Sru
110756160Sru  /* Parse the anchor text.  */
110856160Sru  anchor = get_xref_token (1);
110956160Sru
1110116525Sru  /* Force all versions of "top" to be "Top". */
1111116525Sru  normalize_node_name (anchor);
1112116525Sru
111356160Sru  /* In HTML mode, need to actually produce some output.  */
111456160Sru  if (html)
111556160Sru    {
111656160Sru      /* If this anchor is at the beginning of a new paragraph, make
111756160Sru	 sure a new paragraph is indeed started.  */
111856160Sru      if (!paragraph_is_open)
111956160Sru	{
112093139Sru	  if (!executing_string && html)
112193139Sru	    html_output_head ();
112256160Sru	  start_paragraph ();
112356160Sru	  if (!in_fixed_width_font || in_menu || in_detailmenu)
112456160Sru	    {
112556160Sru	      insert_string ("<p>");
112656160Sru	      in_paragraph = 1;
112756160Sru	    }
112856160Sru	}
112956160Sru      add_word ("<a name=\"");
113056160Sru      add_anchor_name (anchor, 0);
113156160Sru      add_word ("\"></a>");
113293139Sru      if (splitting)
113393139Sru	{
113493139Sru	  /* If we are splitting, cm_xref will produce a reference to
113593139Sru	     a file whose name is derived from the anchor name.  So we
113693139Sru	     must create a file when we see an @anchor, otherwise
113793139Sru	     xref's to anchors won't work.  The file we create simply
113893139Sru	     redirects to the file of this anchor's node.  */
113993139Sru	  TAG_ENTRY *tag;
114093139Sru
114193139Sru	  fname_for_anchor = nodename_to_filename (anchor);
114293139Sru	  /* See if the anchor name converted to a file name clashes
114393139Sru	     with other anchors or nodes.  */
114493139Sru	  tag = find_node_by_fname (fname_for_anchor);
114593139Sru	  if (tag)
114693139Sru	    {
114793139Sru	      if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
114893139Sru		line_error (_("Anchors `%s' and `%s' map to the same file name"),
114993139Sru			    anchor, tag->node);
115093139Sru	      else
115193139Sru		line_error (_("Anchor `%s' and node `%s' map to the same file name"),
115293139Sru			    anchor, tag->node);
115393139Sru	      line_error (_("@anchor command ignored; references to it will not work"));
115493139Sru	      line_error (_("Rename this anchor or use the `--no-split' option"));
115593139Sru	      free (fname_for_anchor);
115693139Sru	      /* We will not be creating a file for this anchor, so
115793139Sru		 set its name to NULL, so that remember_node stores a
115893139Sru		 NULL and find_node_by_fname won't consider this
115993139Sru		 anchor for clashes.  */
116093139Sru	      fname_for_anchor = NULL;
116193139Sru	    }
116293139Sru	  else
116393139Sru	    {
116493139Sru	      char *dirname, *p;
116593139Sru	      char filename[PATH_MAX];
116693139Sru	      FILE *anchor_stream;
116793139Sru
116893139Sru	      dirname = pathname_part (current_output_filename);
116993139Sru	      strcpy (filename, dirname);
117093139Sru	      strcat (filename, fname_for_anchor);
117193139Sru	      free (dirname);
117293139Sru
117393139Sru	      anchor_stream = fopen (filename, "w");
117493139Sru	      if (anchor_stream == NULL)
117593139Sru		{
117693139Sru		  fs_error (filename);
117793139Sru		  xexit (1);
117893139Sru		}
117993139Sru	      /* The HTML magic below will cause the browser to
118093139Sru		 immediately go to the anchor's node's file.  Lynx
118193139Sru		 seems not to support this redirection, but it looks
118293139Sru		 like a bug in Lynx, and they can work around it by
118393139Sru		 clicking on the link once more.  */
118493139Sru	      fputs ("<meta http-equiv=\"refresh\" content=\"0; url=",
118593139Sru		     anchor_stream);
118693139Sru	      /* Make the indirect link point to the current node's
118793139Sru		 file and anchor's "<a name" label.  If we don't have
118893139Sru		 a valid node name, refer to the current output file
118993139Sru		 instead.  */
119093139Sru	      if (current_node && *current_node)
119193139Sru		{
119293139Sru		  char *fn, *tem;
119393139Sru
119493139Sru		  tem = expand_node_name (current_node);
119593139Sru		  fn = nodename_to_filename (tem);
119693139Sru		  free (tem);
119793139Sru		  fputs (fn, anchor_stream);
119893139Sru		  free (fn);
119993139Sru		}
120093139Sru	      else
120193139Sru		{
120293139Sru		  char *base = filename_part (current_output_filename);
120393139Sru
120493139Sru		  fputs (base, anchor_stream);
120593139Sru		  free (base);
120693139Sru		}
120793139Sru	      fputs ("#", anchor_stream);
120893139Sru	      for (p = anchor; *p; p++)
120993139Sru		{
121093139Sru		  if (*p == '&')
121193139Sru		    fputs ("&amp;", anchor_stream);
121293139Sru		  else if (!URL_SAFE_CHAR (*p))
121393139Sru		    fprintf (anchor_stream, "%%%x", (unsigned char) *p);
121493139Sru		  else
121593139Sru		    fputc (*p, anchor_stream);
121693139Sru		}
121793139Sru	      fputs ("\">\n", anchor_stream);
121893139Sru	      fclose (anchor_stream);
121993139Sru	    }
122093139Sru	}
122156160Sru    }
122293139Sru  else if (xml)
122393139Sru    {
122493139Sru      xml_insert_element_with_attribute (ANCHOR, START, "name=\"%s\"", anchor);
122593139Sru      xml_insert_element (ANCHOR, END);
122693139Sru    }
122756160Sru  /* Save it in the tag table.  */
1228100513Sru  remember_node (anchor, NULL, NULL, NULL,
1229100513Sru                 output_position + output_paragraph_offset,
123093139Sru                 line_number, fname_for_anchor, TAG_FLAG_ANCHOR);
123156160Sru}
123256160Sru
123356160Sru/* Find NODE in REF_LIST. */
123456160Srustatic NODE_REF *
1235146515Srufind_node_reference (char *node, NODE_REF *ref_list)
123656160Sru{
123756160Sru  NODE_REF *orig_ref_list = ref_list;
123856160Sru  char *expanded_node;
123956160Sru
124056160Sru  while (ref_list)
124156160Sru    {
124256160Sru      if (strcmp (node, ref_list->node) == 0)
124356160Sru        break;
124456160Sru      ref_list = ref_list->next;
124556160Sru    }
124656160Sru
124756160Sru  if (ref_list || !expensive_validation)
124856160Sru    return ref_list;
124956160Sru
125056160Sru  /* Maybe NODE is not expanded yet.  This may be SLOW.  */
125156160Sru  expanded_node = expand_node_name (node);
125256160Sru  for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next)
125356160Sru    {
125456160Sru      if (STREQ (expanded_node, ref_list->node))
125556160Sru        break;
125656160Sru      if (strchr (ref_list->node, COMMAND_PREFIX))
125756160Sru        {
125856160Sru          char *expanded_ref = expand_node_name (ref_list->node);
125956160Sru
126056160Sru          if (STREQ (expanded_node, expanded_ref))
126156160Sru            {
126256160Sru              free (expanded_ref);
126356160Sru              break;
126456160Sru            }
126556160Sru          free (expanded_ref);
126656160Sru        }
126756160Sru    }
126856160Sru  free (expanded_node);
126956160Sru  return ref_list;
127056160Sru}
127156160Sru
127256160Sruvoid
1273146515Srufree_node_references (void)
127456160Sru{
127556160Sru  NODE_REF *list, *temp;
127656160Sru
127756160Sru  list = node_references;
127856160Sru
127956160Sru  while (list)
128056160Sru    {
128156160Sru      temp = list;
128256160Sru      free (list->node);
128356160Sru      free (list->containing_node);
128456160Sru      list = list->next;
128556160Sru      free (temp);
128656160Sru    }
128756160Sru  node_references = NULL;
128856160Sru}
128956160Sru
129056160Sruvoid
1291146515Srufree_node_node_references (void)
129256160Sru{
129356160Sru  NODE_REF *list, *temp;
129456160Sru
129556160Sru  list = node_references;
129656160Sru
129756160Sru  while (list)
129856160Sru    {
129956160Sru      temp = list;
130056160Sru      free (list->node);
130156160Sru      list = list->next;
130256160Sru      free (temp);
130356160Sru    }
130456160Sru  node_node_references = NULL;
130556160Sru}
130656160Sru
130756160Sru/* Return the number assigned to a named node in either the tag_table
130856160Sru   or node_references list or zero if no number has been assigned. */
130956160Sruint
1310146515Srunumber_of_node (char *node)
131156160Sru{
131256160Sru  NODE_REF *temp_ref;
131356160Sru  TAG_ENTRY *temp_node = find_node (node);
131456160Sru
131556160Sru  if (temp_node)
131656160Sru    return temp_node->number;
131756160Sru  else if ((temp_ref = find_node_reference (node, node_references)))
131856160Sru    return temp_ref->number;
131956160Sru  else if ((temp_ref = find_node_reference (node, node_node_references)))
132056160Sru    return temp_ref->number;
132156160Sru  else
132256160Sru    return 0;
132356160Sru}
132456160Sru
132556160Sru/* validation */
132656160Sru
132756160Sru/* Return 1 if TAG (at LINE) correctly validated, or 0 if not.
132856160Sru   LABEL is the (translated) description of the type of reference --
132956160Sru   Menu, Cross, Next, etc.  */
133056160Sru
133156160Srustatic int
1332146515Sruvalidate (char *tag, int line, const char *label)
133356160Sru{
133456160Sru  TAG_ENTRY *result;
133556160Sru
133656160Sru  /* If there isn't a tag to verify, or if the tag is in another file,
133756160Sru     then it must be okay. */
133856160Sru  if (!tag || !*tag || *tag == '(')
133956160Sru    return 1;
134056160Sru
134156160Sru  /* Otherwise, the tag must exist. */
134256160Sru  result = find_node (tag);
134356160Sru
134456160Sru  if (!result)
134556160Sru    {
134656160Sru      line_number = line;
1347114472Sru      line_error (_("%s reference to nonexistent node `%s' (perhaps incorrect sectioning?)"), label, tag);
134856160Sru      return 0;
134956160Sru    }
135056160Sru  result->touched++;
135156160Sru  return 1;
135256160Sru}
135356160Sru
135456160Sru/* The strings here are followed in the message by `reference to...' in
135556160Sru   the `validate' routine.  They are only used in messages, thus are
135656160Sru   translated.  */
1357116525Srustatic const char *
1358146515Srureftype_type_string (enum reftype type)
135956160Sru{
136056160Sru  switch (type)
136156160Sru    {
136256160Sru    case menu_reference:
136356160Sru      return _("Menu");
136456160Sru    case followed_reference:
136556160Sru      return _("Cross");
136656160Sru    default:
136756160Sru      return "Internal-bad-reference-type";
136856160Sru    }
136956160Sru}
137056160Sru
137156160Srustatic void
1372146515Sruvalidate_other_references (NODE_REF *ref_list)
137356160Sru{
137456160Sru  char *old_input_filename = input_filename;
137556160Sru
137656160Sru  while (ref_list)
137756160Sru    {
137856160Sru      input_filename = ref_list->filename;
137956160Sru      validate (ref_list->node, ref_list->line_no,
138056160Sru                reftype_type_string (ref_list->type));
138156160Sru      ref_list = ref_list->next;
138256160Sru    }
138356160Sru  input_filename = old_input_filename;
138456160Sru}
138556160Sru
138656160Sru/* Validation of an info file.
138756160Sru   Scan through the list of tag entries touching the Prev, Next, and Up
138856160Sru   elements of each.  It is an error not to be able to touch one of them,
138956160Sru   except in the case of external node references, such as "(DIR)".
139056160Sru
139156160Sru   If the Prev is different from the Up,
139256160Sru   then the Prev node must have a Next pointing at this node.
139356160Sru
139456160Sru   Every node except Top must have an Up.
139556160Sru   The Up node must contain some sort of reference, other than a Next,
139656160Sru   to this node.
139756160Sru
139856160Sru   If the Next is different from the Next of the Up,
139956160Sru   then the Next node must have a Prev pointing at this node. */
140056160Sruvoid
1401146515Sruvalidate_file (TAG_ENTRY *tag_table)
140256160Sru{
140356160Sru  char *old_input_filename = input_filename;
140456160Sru  TAG_ENTRY *tags = tag_table;
140556160Sru
140656160Sru  while (tags)
140756160Sru    {
140856160Sru      TAG_ENTRY *temp_tag;
140956160Sru      char *tem1, *tem2;
141056160Sru
141156160Sru      input_filename = tags->filename;
141256160Sru      line_number = tags->line_no;
141356160Sru
141456160Sru      /* If this is a "no warn" node, don't validate it in any way. */
141556160Sru      if (tags->flags & TAG_FLAG_NO_WARN)
141656160Sru        {
141756160Sru          tags = tags->next_ent;
141856160Sru          continue;
141956160Sru        }
142056160Sru
142156160Sru      /* If this node has a Next, then make sure that the Next exists. */
142256160Sru      if (tags->next)
142356160Sru        {
142456160Sru          validate (tags->next, tags->line_no, _("Next"));
142556160Sru
142656160Sru          /* If the Next node exists, and there is no Up, then make sure
142756160Sru             that the Prev of the Next points back.  But do nothing if
142856160Sru             we aren't supposed to issue warnings about this node. */
142956160Sru          temp_tag = find_node (tags->next);
143056160Sru          if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN))
143156160Sru            {
143256160Sru              char *prev = temp_tag->prev;
143356160Sru              int you_lose = !prev || !STREQ (prev, tags->node);
143456160Sru
143556160Sru              if (you_lose && expensive_validation)
143656160Sru                {
143756160Sru                  tem1 = expand_node_name (prev);
143856160Sru                  tem2 = expand_node_name (tags->node);
143956160Sru
1440146515Sru                  if (tem1 && tem2 && STREQ (tem1, tem2))
144156160Sru                    you_lose = 0;
144256160Sru                  free (tem1);
144356160Sru                  free (tem2);
144456160Sru                }
144556160Sru              if (you_lose)
144656160Sru                {
1447114472Sru                  line_error (_("Next field of node `%s' not pointed to (perhaps incorrect sectioning?)"),
144856160Sru                              tags->node);
144993139Sru                  file_line_error (temp_tag->filename, temp_tag->line_no,
145093139Sru				   _("This node (%s) has the bad Prev"),
145193139Sru				   temp_tag->node);
145256160Sru                  temp_tag->flags |= TAG_FLAG_PREV_ERROR;
145356160Sru                }
145456160Sru            }
145556160Sru        }
145656160Sru
145756160Sru      /* Validate the Prev field if there is one, and we haven't already
145856160Sru         complained about it in some way.  You don't have to have a Prev
145956160Sru         field at this stage. */
146056160Sru      if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev)
146156160Sru        {
146256160Sru          int valid_p = validate (tags->prev, tags->line_no, _("Prev"));
146356160Sru
146456160Sru          if (!valid_p)
146556160Sru            tags->flags |= TAG_FLAG_PREV_ERROR;
146656160Sru          else
146756160Sru            { /* If the Prev field is not the same as the Up field,
146856160Sru                 then the node pointed to by the Prev field must have
146956160Sru                 a Next field which points to this node. */
147056160Sru              int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up);
147156160Sru
147256160Sru              if (!prev_equals_up && expensive_validation)
147356160Sru                {
147456160Sru                  tem1 = expand_node_name (tags->prev);
147556160Sru                  tem2 = expand_node_name (tags->up);
147656160Sru                  prev_equals_up = STREQ (tem1, tem2);
147756160Sru                  free (tem1);
147856160Sru                  free (tem2);
147956160Sru                }
148056160Sru              if (!prev_equals_up)
148156160Sru                {
148256160Sru                  temp_tag = find_node (tags->prev);
148356160Sru
148456160Sru                  /* If we aren't supposed to issue warnings about the
148556160Sru                     target node, do nothing. */
148656160Sru                  if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN))
148756160Sru                    /* Do nothing. */ ;
148856160Sru                  else
148956160Sru                    {
149056160Sru                      int you_lose = !temp_tag->next
149156160Sru                        || !STREQ (temp_tag->next, tags->node);
149256160Sru
149356160Sru                      if (temp_tag->next && you_lose && expensive_validation)
149456160Sru                        {
149556160Sru                          tem1 = expand_node_name (temp_tag->next);
149656160Sru                          tem2 = expand_node_name (tags->node);
149756160Sru                          if (STREQ (tem1, tem2))
149856160Sru                            you_lose = 0;
149956160Sru                          free (tem1);
150056160Sru                          free (tem2);
150156160Sru                        }
150256160Sru                      if (you_lose)
150356160Sru                        {
150456160Sru                          line_error
150556160Sru                            (_("Prev field of node `%s' not pointed to"),
150656160Sru                             tags->node);
150793139Sru                          file_line_error (temp_tag->filename,
150893139Sru					   temp_tag->line_no,
150993139Sru					   _("This node (%s) has the bad Next"),
151093139Sru					   temp_tag->node);
151156160Sru                          temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
151256160Sru                        }
151356160Sru                    }
151456160Sru                }
151556160Sru            }
151656160Sru        }
151756160Sru
151856160Sru      if (!tags->up
151956160Sru          && !(tags->flags & TAG_FLAG_ANCHOR)
152056160Sru          && strcasecmp (tags->node, "Top") != 0)
1521114472Sru        line_error (_("`%s' has no Up field (perhaps incorrect sectioning?)"), tags->node);
152256160Sru      else if (tags->up)
152356160Sru        {
152456160Sru          int valid_p = validate (tags->up, tags->line_no, _("Up"));
152556160Sru
152656160Sru          /* If node X has Up: Y, then warn if Y fails to have a menu item
152756160Sru             or note pointing at X, if Y isn't of the form "(Y)". */
152856160Sru          if (valid_p && *tags->up != '(')
152956160Sru            {
153056160Sru              NODE_REF *nref;
153156160Sru              NODE_REF *tref = NULL;
153256160Sru              NODE_REF *list = node_references;
153356160Sru
153456160Sru              for (;;)
153556160Sru                {
153656160Sru                  nref = find_node_reference (tags->node, list);
153756160Sru                  if (!nref)
153856160Sru                    break;
153956160Sru
154056160Sru                  if (strcmp (nref->containing_node, tags->up) == 0)
154156160Sru                    {
154256160Sru                      if (nref->type != menu_reference)
154356160Sru                        {
154456160Sru                          tref = nref;
154556160Sru                          list = nref->next;
154656160Sru                        }
154756160Sru                      else
154856160Sru                        break;
154956160Sru                    }
155056160Sru                  list = nref->next;
155156160Sru                }
155256160Sru
155356160Sru              if (!nref)
155456160Sru                {
155556160Sru		  if (!tref && expensive_validation)
155656160Sru		    {
155756160Sru		      /* Sigh...  This might be AWFULLY slow, but if
155856160Sru		         they want this feature, they'll have to pay!
155956160Sru		         We do all the loop again expanding each
156056160Sru		         containing_node reference as we go.  */
156156160Sru		      char *tags_up = expand_node_name (tags->up);
156256160Sru		      char *tem;
156356160Sru
156456160Sru		      list = node_references;
156556160Sru
156656160Sru		      for (;;)
156756160Sru			{
156856160Sru			  nref = find_node_reference (tags->node, list);
156956160Sru			  if (!nref)
157056160Sru			    break;
157156160Sru			  tem = expand_node_name (nref->containing_node);
157256160Sru			  if (STREQ (tem, tags_up))
157356160Sru			    {
157456160Sru			      if (nref->type != menu_reference)
157556160Sru				tref = nref;
157656160Sru			      else
157756160Sru				{
157856160Sru				  free (tem);
157956160Sru				  break;
158056160Sru				}
158156160Sru			    }
158256160Sru			  free (tem);
158356160Sru			  list = nref->next;
158456160Sru			}
158556160Sru		    }
158656160Sru                  if (!nref && !tref)
158756160Sru                    {
158856160Sru                      temp_tag = find_node (tags->up);
158993139Sru                      file_line_error (temp_tag->filename, temp_tag->line_no,
159056160Sru           _("Node `%s' lacks menu item for `%s' despite being its Up target"),
159156160Sru                                  tags->up, tags->node);
159256160Sru                    }
159356160Sru                }
159456160Sru            }
159556160Sru        }
159656160Sru      tags = tags->next_ent;
159756160Sru    }
159856160Sru
159956160Sru  validate_other_references (node_references);
160056160Sru  /* We have told the user about the references which didn't exist.
160156160Sru     Now tell him about the nodes which aren't referenced. */
160256160Sru
160356160Sru  for (tags = tag_table; tags; tags = tags->next_ent)
160456160Sru    {
160556160Sru      /* If this node is a "no warn" node, do nothing. */
160656160Sru      if (tags->flags & TAG_FLAG_NO_WARN)
160756160Sru        {
160856160Sru          tags = tags->next_ent;
160956160Sru          continue;
161056160Sru        }
161156160Sru
161256160Sru      /* Special hack.  If the node in question appears to have
161356160Sru         been referenced more than REFERENCE_WARNING_LIMIT times,
161456160Sru         give a warning. */
161556160Sru      if (tags->touched > reference_warning_limit)
161656160Sru        {
161756160Sru          input_filename = tags->filename;
161856160Sru          line_number = tags->line_no;
161956160Sru          warning (_("node `%s' has been referenced %d times"),
162056160Sru                   tags->node, tags->touched);
162156160Sru        }
162256160Sru
162356160Sru      if (tags->touched == 0)
162456160Sru        {
162556160Sru          input_filename = tags->filename;
162656160Sru          line_number = tags->line_no;
162756160Sru
162856160Sru          /* Notice that the node "Top" is special, and doesn't have to
162956160Sru             be referenced.   Anchors don't have to be referenced
163056160Sru             either, you might define them for another document.  */
163156160Sru          if (strcasecmp (tags->node, "Top") != 0
163256160Sru              && !(tags->flags & TAG_FLAG_ANCHOR))
163356160Sru            warning (_("unreferenced node `%s'"), tags->node);
163456160Sru        }
163556160Sru    }
163656160Sru  input_filename = old_input_filename;
163756160Sru}
163856160Sru
163956160Sru
164056160Sru/* Splitting */
164156160Sru
164256160Sru/* Return true if the tag entry pointed to by TAGS is the last node.
164356160Sru   This means only anchors follow.  */
164456160Sru
164556160Srustatic int
1646146515Srulast_node_p (TAG_ENTRY *tags)
164756160Sru{
164856160Sru  int last = 1;
164956160Sru  while (tags->next_ent) {
165056160Sru    tags = tags->next_ent;
165156160Sru    if (tags->flags & TAG_FLAG_ANCHOR)
165256160Sru      ;
165356160Sru    else
165456160Sru      {
165556160Sru        last = 0;
165656160Sru        break;
165756160Sru      }
165856160Sru  }
1659116525Sru
166056160Sru  return last;
166156160Sru}
166256160Sru
166356160Sru
1664146515Srustatic char *
1665146515Sruenumerate_filename (char *pathname, char *basename, int number)
1666146515Sru{
1667146515Sru  /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */
1668146515Sru  const int dos_file_names = !HAVE_LONG_FILENAMES (pathname ? pathname : ".");
1669146515Sru  unsigned name_len = strlen (basename);
1670146515Sru  char *filename = xmalloc (10 + strlen (pathname) + name_len);
1671146515Sru  char *base_filename = xmalloc (10 + name_len);
1672146515Sru
1673146515Sru  sprintf (base_filename, "%s-%d", basename, number);
1674146515Sru
1675146515Sru  if (dos_file_names)
1676146515Sru    {
1677146515Sru      char *dot = strchr (base_filename, '.');
1678146515Sru      unsigned base_len = strlen (base_filename);
1679146515Sru
1680146515Sru      if (dot)
1681146515Sru        { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
1682146515Sru          dot[1] = 'i';
1683146515Sru          memmove (number <= 99 ? dot + 2 : dot + 1,
1684146515Sru              base_filename + name_len + 1,
1685146515Sru              strlen (base_filename + name_len + 1) + 1);
1686146515Sru        }
1687146515Sru      else if (base_len > 8)
1688146515Sru        {
1689146515Sru          /* Make foobar-1, .., fooba-10, .., foob-100, ... */
1690146515Sru          unsigned numlen = base_len - name_len;
1691146515Sru
1692146515Sru          memmove (base_filename + 8 - numlen, base_filename + name_len, numlen + 1);
1693146515Sru        }
1694146515Sru    }
1695146515Sru
1696146515Sru  sprintf (filename, "%s%s", pathname, base_filename);
1697146515Sru
1698146515Sru  return filename;
1699146515Sru}
1700146515Sru
1701146515Sru/* Remove previously split files, to avoid
1702146515Sru   lingering parts of shrinked documents.  */
1703146515Sruvoid
1704146515Sruclean_old_split_files (char *filename)
1705146515Sru{
1706146515Sru  char *root_filename = filename_part (filename);
1707146515Sru  char *root_pathname = pathname_part (filename);
1708146515Sru  int i;
1709146515Sru
1710146515Sru  /* We break as soon as we hit an inexistent file,
1711146515Sru     so looping until large numbers is harmless.  */
1712146515Sru  for (i = 1; i < 1000; i++)
1713146515Sru    {
1714146515Sru      struct stat st;
1715146515Sru      char *check_file = enumerate_filename (root_pathname, root_filename, i);
1716146515Sru
1717146515Sru      if (stat (check_file, &st) != 0)
1718146515Sru        break;
1719146515Sru      else if (!S_ISDIR (st.st_mode))
1720146515Sru        {
1721146515Sru          /* Give feedback if requested, removing a file is important.  */
1722146515Sru          if (verbose_mode)
1723146515Sru            printf (_("Removing %s\n"), check_file);
1724146515Sru
1725146515Sru          /* Warn user that we cannot remove the file.  */
1726146515Sru          if (unlink (check_file) != 0)
1727146515Sru            warning (_("Can't remove file `%s': %s"), check_file, strerror (errno));
1728146515Sru        }
1729146515Sru
1730146515Sru      free (check_file);
1731146515Sru    }
1732146515Sru}
1733146515Sru
1734146515Sru
173556160Sru/* Split large output files into a series of smaller files.  Each file
173656160Sru   is pointed to in the tag table, which then gets written out as the
173756160Sru   original file.  The new files have the same name as the original file
173856160Sru   with a "-num" attached.  SIZE is the largest number of bytes to allow
173956160Sru   in any single split file. */
174056160Sruvoid
1741146515Srusplit_file (char *filename, int size)
174256160Sru{
174356160Sru  char *root_filename, *root_pathname;
1744146515Sru  char *the_file;
174556160Sru  struct stat fileinfo;
174656160Sru  long file_size;
174756160Sru  char *the_header;
174856160Sru  int header_size;
174956160Sru
175056160Sru  /* Can only do this to files with tag tables. */
175156160Sru  if (!tag_table)
175256160Sru    return;
175356160Sru
175456160Sru  if (size == 0)
175556160Sru    size = DEFAULT_SPLIT_SIZE;
175656160Sru
1757116525Sru  if ((stat (filename, &fileinfo) != 0)
1758116525Sru      || (((long) fileinfo.st_size) < size))
175956160Sru    return;
176056160Sru  file_size = (long) fileinfo.st_size;
176156160Sru
1762146515Sru  the_file = find_and_load (filename, 0);
176356160Sru  if (!the_file)
176456160Sru    return;
176556160Sru
176656160Sru  root_filename = filename_part (filename);
176756160Sru  root_pathname = pathname_part (filename);
176856160Sru
176956160Sru  if (!root_pathname)
177056160Sru    root_pathname = xstrdup ("");
177156160Sru
177256160Sru  /* Start splitting the file.  Walk along the tag table
177356160Sru     outputting sections of the file.  When we have written
177456160Sru     all of the nodes in the tag table, make the top-level
177556160Sru     pointer file, which contains indirect pointers and
177656160Sru     tags for the nodes. */
177756160Sru  {
177856160Sru    int which_file = 1;
177956160Sru    TAG_ENTRY *tags = tag_table;
178056160Sru    char *indirect_info = NULL;
178156160Sru
1782116525Sru    /* Maybe we want a Local Variables section.  */
1783116525Sru    char *trailer = info_trailer ();
1784116525Sru    int trailer_len = trailer ? strlen (trailer) : 0;
1785116525Sru
178656160Sru    /* Remember the `header' of this file.  The first tag in the file is
178756160Sru       the bottom of the header; the top of the file is the start. */
178856160Sru    the_header = xmalloc (1 + (header_size = tags->position));
178956160Sru    memcpy (the_header, the_file, header_size);
179056160Sru
179156160Sru    while (tags)
179256160Sru      {
179356160Sru        int file_top, file_bot, limit;
179456160Sru
179556160Sru        /* Have to include the Control-_. */
179656160Sru        file_top = file_bot = tags->position;
179756160Sru        limit = file_top + size;
179856160Sru
179956160Sru        /* If the rest of this file is only one node, then
180056160Sru           that is the entire subfile. */
180156160Sru        if (last_node_p (tags))
180256160Sru          {
180356160Sru            int i = tags->position + 1;
180456160Sru            char last_char = the_file[i];
180556160Sru
180656160Sru            while (i < file_size)
180756160Sru              {
180856160Sru                if ((the_file[i] == '\037') &&
180956160Sru                    ((last_char == '\n') ||
181056160Sru                     (last_char == '\014')))
181156160Sru                  break;
181256160Sru                else
181356160Sru                  last_char = the_file[i];
181456160Sru                i++;
181556160Sru              }
181656160Sru            file_bot = i;
181756160Sru            tags = tags->next_ent;
181856160Sru            goto write_region;
181956160Sru          }
182056160Sru
182156160Sru        /* Otherwise, find the largest number of nodes that can fit in
182256160Sru           this subfile. */
182356160Sru        for (; tags; tags = tags->next_ent)
182456160Sru          {
182556160Sru            if (last_node_p (tags))
182656160Sru              {
182756160Sru                /* This entry is the last node.  Search forward for the end
182856160Sru                   of this node, and that is the end of this file. */
182956160Sru                int i = tags->position + 1;
183056160Sru                char last_char = the_file[i];
183156160Sru
183256160Sru                while (i < file_size)
183356160Sru                  {
183456160Sru                    if ((the_file[i] == '\037') &&
183556160Sru                        ((last_char == '\n') ||
183656160Sru                         (last_char == '\014')))
183756160Sru                      break;
183856160Sru                    else
183956160Sru                      last_char = the_file[i];
184056160Sru                    i++;
184156160Sru                  }
184256160Sru                file_bot = i;
184356160Sru
184456160Sru                if (file_bot < limit)
184556160Sru                  {
184656160Sru                    tags = tags->next_ent;
184756160Sru                    goto write_region;
184856160Sru                  }
184956160Sru                else
185056160Sru                  {
185156160Sru                    /* Here we want to write out everything before the last
185256160Sru                       node, and then write the last node out in a file
185356160Sru                       by itself. */
185456160Sru                    file_bot = tags->position;
185556160Sru                    goto write_region;
185656160Sru                  }
185756160Sru              }
185856160Sru
185956160Sru            /* Write region only if this was a node, not an anchor.  */
186056160Sru            if (tags->next_ent->position > limit
186156160Sru                && !(tags->flags & TAG_FLAG_ANCHOR))
186256160Sru              {
186356160Sru                if (tags->position == file_top)
186456160Sru                  tags = tags->next_ent;
186556160Sru
186656160Sru                file_bot = tags->position;
186756160Sru
186856160Sru              write_region:
186956160Sru                {
187056160Sru                  int fd;
1871146515Sru                  char *split_filename = enumerate_filename (root_pathname,
1872146515Sru                      root_filename, which_file);
1873146515Sru                  char *split_basename = filename_part (split_filename);
187456160Sru
187556160Sru                  fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
187656160Sru                  if (fd < 0
187756160Sru                      || write (fd, the_header, header_size) != header_size
187856160Sru                      || write (fd, the_file + file_top, file_bot - file_top)
187956160Sru                         != (file_bot - file_top)
1880116525Sru                      || (trailer_len
1881116525Sru                          && write (fd, trailer, trailer_len) != trailer_len)
1882116525Sru                      || close (fd) < 0)
188356160Sru                    {
188456160Sru                      perror (split_filename);
188556160Sru                      if (fd != -1)
188656160Sru                        close (fd);
188756160Sru                      xexit (1);
188856160Sru                    }
188956160Sru
189056160Sru                  if (!indirect_info)
189156160Sru                    {
189256160Sru                      indirect_info = the_file + file_top;
189356160Sru                      sprintf (indirect_info, "\037\nIndirect:\n");
189456160Sru                      indirect_info += strlen (indirect_info);
189556160Sru                    }
189656160Sru
189756160Sru                  sprintf (indirect_info, "%s: %d\n",
189856160Sru                           split_basename, file_top);
189956160Sru
190056160Sru                  free (split_basename);
190156160Sru                  free (split_filename);
190256160Sru                  indirect_info += strlen (indirect_info);
190356160Sru                  which_file++;
190456160Sru                  break;
190556160Sru                }
190656160Sru              }
190756160Sru          }
190856160Sru      }
190956160Sru
191056160Sru    /* We have sucessfully created the subfiles.  Now write out the
191156160Sru       original again.  We must use `output_stream', or
191256160Sru       write_tag_table_indirect () won't know where to place the output. */
191356160Sru    output_stream = fopen (filename, "w");
191456160Sru    if (!output_stream)
191556160Sru      {
191656160Sru        perror (filename);
191756160Sru        xexit (1);
191856160Sru      }
191956160Sru
192056160Sru    {
192156160Sru      int distance = indirect_info - the_file;
192256160Sru      fwrite (the_file, 1, distance, output_stream);
192356160Sru
192456160Sru      /* Inhibit newlines. */
192556160Sru      paragraph_is_open = 0;
192656160Sru
1927116525Sru      /* Write the indirect tag table.  */
192856160Sru      write_tag_table_indirect ();
1929116525Sru
1930116525Sru      /* preserve local variables in info output.  */
1931116525Sru      if (trailer)
1932116525Sru        {
1933146515Sru          fwrite (trailer, 1, trailer_len, output_stream);
1934116525Sru          free (trailer);
1935116525Sru        }
1936116525Sru
193756160Sru      fclose (output_stream);
193856160Sru      free (the_header);
193956160Sru      free (the_file);
194056160Sru      return;
194156160Sru    }
194256160Sru  }
194356160Sru}
1944