node.c revision 56160
1/* node.c -- nodes for Texinfo.
2   $Id: node.c,v 1.23 1999/09/20 12:31:21 karl Exp $
3
4   Copyright (C) 1998, 99 Free Software Foundation, Inc.
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software Foundation,
18   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20#include "system.h"
21#include "cmds.h"
22#include "files.h"
23#include "footnote.h"
24#include "macro.h"
25#include "makeinfo.h"
26#include "node.h"
27#include "sectioning.h"
28#include "insertion.h"
29
30
31/* See comments in node.h.  */
32NODE_REF *node_references = NULL;
33NODE_REF *node_node_references = NULL;
34TAG_ENTRY *tag_table = NULL;
35int node_number = -1;
36int current_section = 0;
37int outstanding_node = 0;
38
39/* Adding nodes, and making tags.  */
40
41/* Start a new tag table. */
42void
43init_tag_table ()
44{
45  while (tag_table)
46    {
47      TAG_ENTRY *temp = tag_table;
48      free (temp->node);
49      free (temp->prev);
50      free (temp->next);
51      free (temp->up);
52      tag_table = tag_table->next_ent;
53      free (temp);
54    }
55}
56
57/* Write out the contents of the existing tag table.
58   INDIRECT_P says how to format the output (it depends on whether the
59   table is direct or indirect).  */
60static void
61write_tag_table_internal (indirect_p)
62     int indirect_p;
63{
64  TAG_ENTRY *node;
65  int old_indent = no_indent;
66
67  no_indent = 1;
68  filling_enabled = 0;
69  must_start_paragraph = 0;
70  close_paragraph ();
71
72  if (!indirect_p)
73    {
74      no_indent = 1;
75      insert ('\n');
76    }
77
78  add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
79
80  /* Do not collapse -- to -, etc., in node names.  */
81  in_fixed_width_font++;
82
83  for (node = tag_table; node; node = node->next_ent)
84    {
85      if (node->flags & TAG_FLAG_ANCHOR)
86        { /* This reference is to an anchor.  */
87          execute_string ("Ref: %s", node->node);
88        }
89      else
90        { /* This reference is to a node.  */
91          execute_string ("Node: %s", node->node);
92        }
93      add_word_args ("\177%d\n", node->position);
94    }
95
96  add_word ("\037\nEnd Tag Table\n");
97
98  /* Do not collapse -- to -, etc., in node names.  */
99  in_fixed_width_font--;
100
101  flush_output ();
102  no_indent = old_indent;
103}
104
105void
106write_tag_table ()
107{
108  write_tag_table_internal (0); /* Not indirect. */
109}
110
111void
112write_tag_table_indirect ()
113{
114  write_tag_table_internal (1);
115}
116
117/* Convert "top" and friends into "Top". */
118static void
119normalize_node_name (string)
120     char *string;
121{
122  if (strcasecmp (string, "Top") == 0)
123    strcpy (string, "Top");
124}
125
126char *
127get_node_token (expand)
128      int expand;
129{
130  char *string;
131
132  get_until_in_line (expand, ",", &string);
133
134  if (curchar () == ',')
135    input_text_offset++;
136
137  fix_whitespace (string);
138
139  /* Force all versions of "top" to be "Top". */
140  normalize_node_name (string);
141
142  return string;
143}
144
145/* Expand any macros and other directives in a node name, and
146   return the expanded name as an malloc'ed string.  */
147char *
148expand_node_name (node)
149     char *node;
150{
151  char *result = node;
152
153  if (node)
154    {
155      /* Don't expand --, `` etc., in case somebody will want
156         to print the result.  */
157      in_fixed_width_font++;
158      result = expansion (node, 0);
159      in_fixed_width_font--;
160      fix_whitespace (result);
161      normalize_node_name (result);
162    }
163  return result;
164}
165
166/* Look up NAME in the tag table, and return the associated
167   tag_entry.  If the node is not in the table return NULL. */
168TAG_ENTRY *
169find_node (name)
170     char *name;
171{
172  TAG_ENTRY *tag = tag_table;
173  char *expanded_name;
174  char n1 = name[0];
175
176  while (tag)
177    {
178      if (tag->node[0] == n1 && strcmp (tag->node, name) == 0)
179        return tag;
180      tag = tag->next_ent;
181    }
182
183  if (!expensive_validation)
184    return NULL;
185
186  /* Try harder.  Maybe TAG_TABLE has the expanded NAME, or maybe NAME
187     is expanded while TAG_TABLE has its unexpanded form.  This may
188     slow down the search, but if they want this feature, let them
189     pay!  If they want it fast, they should write every node name
190     consistently (either always expanded or always unexpaned).  */
191  expanded_name = expand_node_name (name);
192  for (tag = tag_table; tag; tag = tag->next_ent)
193    {
194      if (STREQ (tag->node, expanded_name))
195        break;
196      /* If the tag name doesn't have the command prefix, there's no
197         chance it could expand into anything but itself.  */
198      if (strchr (tag->node, COMMAND_PREFIX))
199        {
200          char *expanded_node = expand_node_name (tag->node);
201
202          if (STREQ (expanded_node, expanded_name))
203            {
204              free (expanded_node);
205              break;
206            }
207          free (expanded_node);
208        }
209    }
210  free (expanded_name);
211  return tag;
212}
213
214/* Similarly for next etc. references in a @node command, where we
215   don't care about most of the entries. */
216static void
217remember_node_node_reference (node)
218     char *node;
219{
220  NODE_REF *temp = xmalloc (sizeof (NODE_REF));
221  int number;
222
223  if (!node) return;
224  temp->next = node_node_references;
225  temp->node = xstrdup (node);
226  temp->type = followed_reference;
227  number = number_of_node (node);
228  if (number)
229    temp->number = number;      /* Already assigned. */
230  else
231    {
232      node_number++;
233      temp->number = node_number;
234    }
235  node_node_references = temp;
236}
237
238/* Remember NODE and associates. */
239void
240remember_node (node, prev, next, up, position, line_no, flags)
241     char *node, *prev, *next, *up;
242     int position, line_no, flags;
243{
244  /* Check for existence of this tag already. */
245  if (validating)
246    {
247      TAG_ENTRY *tag = find_node (node);
248      if (tag)
249        {
250          line_error (_("Node `%s' previously defined at line %d"),
251                      node, tag->line_no);
252          return;
253        }
254    }
255
256  if (!(flags & TAG_FLAG_ANCHOR))
257    {
258      /* Make this the current node. */
259      current_node = node;
260    }
261
262  /* Add it to the list. */
263  {
264    int number = number_of_node (node);
265
266    TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY));
267    new->node = node;
268    new->prev = prev;
269    new->next = next;
270    new->up = up;
271    new->position = position;
272    new->line_no = line_no;
273    new->filename = node_filename;
274    new->touched = 0;
275    new->flags = flags;
276    if (number)
277      new->number = number;     /* Already assigned. */
278    else
279      {
280        node_number++;
281        new->number = node_number;
282      }
283    new->next_ent = tag_table;
284    tag_table = new;
285  }
286
287  if (html)
288    { /* Note the references to the next etc. nodes too.  */
289      remember_node_node_reference (next);
290      remember_node_node_reference (prev);
291      remember_node_node_reference (up);
292    }
293}
294
295/* Remember this node name for later validation use.  This is used to
296   remember menu references while reading the input file.  After the
297   output file has been written, if validation is on, then we use the
298   contents of `node_references' as a list of nodes to validate.  */
299void
300remember_node_reference (node, line, type)
301     char *node;
302     int line;
303     enum reftype type;
304{
305  NODE_REF *temp = xmalloc (sizeof (NODE_REF));
306  int number = number_of_node (node);
307
308  temp->next = node_references;
309  temp->node = xstrdup (node);
310  temp->line_no = line;
311  temp->section = current_section;
312  temp->type = type;
313  temp->containing_node = xstrdup (current_node ? current_node : "");
314  temp->filename = node_filename;
315  if (number)
316    temp->number = number;      /* Already assigned. */
317  else
318    {
319      node_number++;
320      temp->number = node_number;
321    }
322
323  node_references = temp;
324}
325
326static void
327isolate_nodename (nodename)
328     char *nodename;
329{
330  int i, c;
331  int paren_seen, paren;
332
333  if (!nodename)
334    return;
335
336  canon_white (nodename);
337  paren_seen = paren = i = 0;
338
339  if (*nodename == '.' || !*nodename)
340    {
341      *nodename = 0;
342      return;
343    }
344
345  if (*nodename == '(')
346    {
347      paren++;
348      paren_seen++;
349      i++;
350    }
351
352  for (; (c = nodename[i]); i++)
353    {
354      if (paren)
355        {
356          if (c == '(')
357            paren++;
358          else if (c == ')')
359            paren--;
360
361          continue;
362        }
363
364      /* If the character following the close paren is a space, then this
365         node has no more characters associated with it. */
366      if (c == '\t' ||
367          c == '\n' ||
368          c == ','  ||
369          ((paren_seen && nodename[i - 1] == ')') &&
370           (c == ' ' || c == '.')) ||
371          (c == '.' &&
372           ((!nodename[i + 1] ||
373             (cr_or_whitespace (nodename[i + 1])) ||
374             (nodename[i + 1] == ')')))))
375        break;
376    }
377  nodename[i] = 0;
378}
379
380/* This function gets called at the start of every line while inside a
381   menu.  It checks to see if the line starts with "* ", and if so and
382   REMEMBER_REF is nonzero, remembers the node reference as type
383   REF_TYPE that this menu refers to.  input_text_offset is at the \n
384   just before the menu line.  If REMEMBER_REF is zero, REF_TYPE is unused.  */
385#define MENU_STARTER "* "
386char *
387glean_node_from_menu (remember_ref, ref_type)
388     int remember_ref;
389     enum reftype ref_type;
390{
391  int i, orig_offset = input_text_offset;
392  char *nodename;
393  char *line, *expanded_line;
394  char *old_input = input_text;
395  size_t old_size = input_text_length;
396
397  if (strncmp (&input_text[input_text_offset + 1],
398               MENU_STARTER,
399               strlen (MENU_STARTER)) != 0)
400    return NULL;
401  else
402    input_text_offset += strlen (MENU_STARTER) + 1;
403
404  /* The menu entry might include macro calls, so we need to expand them.  */
405  get_until ("\n", &line);
406  only_macro_expansion++;       /* only expand macros in menu entries */
407  expanded_line = expansion (line, 0);
408  only_macro_expansion--;
409  free (line);
410  input_text = expanded_line;
411  input_text_offset = 0;
412  input_text_length = strlen (expanded_line);
413
414  get_until_in_line (0, ":", &nodename);
415  if (curchar () == ':')
416    input_text_offset++;
417
418  if (curchar () != ':')
419    {
420      free (nodename);
421      get_until_in_line (0, "\n", &nodename);
422      isolate_nodename (nodename);
423    }
424
425  input_text = old_input;
426  input_text_offset = orig_offset;
427  input_text_length = old_size;
428  free (expanded_line);
429  fix_whitespace (nodename);
430  normalize_node_name (nodename);
431  i = strlen (nodename);
432  if (i && nodename[i - 1] == ':')
433    nodename[i - 1] = 0;
434
435  if (remember_ref)
436    remember_node_reference (nodename, line_number, ref_type);
437
438  return nodename;
439}
440
441/* Set the name of the current output file.  */
442void
443set_current_output_filename (fname)
444     const char *fname;
445{
446  if (current_output_filename)
447    free (current_output_filename);
448  current_output_filename = xstrdup (fname);
449}
450
451/* The order is: nodename, nextnode, prevnode, upnode.
452   If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
453   You must follow a node command which has those fields defaulted
454   with a sectioning command (e.g. @chapter) giving the "level" of that node.
455   It is an error not to do so.
456   The defaults come from the menu in this node's parent. */
457void
458cm_node ()
459{
460  char *node, *prev, *next, *up;
461  int new_node_pos, defaulting, this_section;
462  int no_warn = 0;
463
464  if (strcmp (command, "nwnode") == 0)
465    no_warn = TAG_FLAG_NO_WARN;
466
467  /* Get rid of unmatched brace arguments from previous commands. */
468  discard_braces ();
469
470  /* There also might be insertions left lying around that haven't been
471     ended yet.  Do that also. */
472  discard_insertions (1);
473
474  if (!html && !already_outputting_pending_notes)
475    {
476      close_paragraph ();
477      output_pending_notes ();
478    }
479
480  if (html && splitting && top_node_seen)
481    {
482      /* End the current split output file. */
483      close_paragraph ();
484      output_pending_notes ();
485      start_paragraph ();
486      /* Fixme: html: need a navigation bar here. */
487      add_word ("</body></html>\n");
488      close_paragraph ();
489      fclose (output_stream);
490      output_stream = NULL;
491    }
492
493  filling_enabled = indented_fill = 0;
494  new_node_pos = output_position;
495  if (!html || (html && splitting))
496    current_footnote_number = 1;
497
498  if (macro_expansion_output_stream && !executing_string)
499    append_to_expansion_output (input_text_offset + 1);
500
501  /* Do not collapse -- to -, etc., in node names.  */
502  in_fixed_width_font++;
503
504  /* While expanding the @node line, leave any non-macros
505     intact, so that the macro-expanded output includes them.  */
506  only_macro_expansion++;
507  node = get_node_token (1);
508  only_macro_expansion--;
509  next = get_node_token (0);
510  prev = get_node_token (0);
511  up = get_node_token (0);
512
513  if (verbose_mode)
514    printf (_("Formatting node %s...\n"), node);
515
516  if (macro_expansion_output_stream && !executing_string)
517    remember_itext (input_text, input_text_offset);
518
519  no_indent = 1;
520  if (!no_headers && !html)
521    {
522      add_word_args ("\037\nFile: %s,  Node: ", pretty_output_filename);
523
524      if (macro_expansion_output_stream && !executing_string)
525        me_execute_string (node);
526      else
527        execute_string ("%s", node);
528      filling_enabled = indented_fill = 0;
529    }
530
531  /* Check for defaulting of this node's next, prev, and up fields. */
532  defaulting = (*next == 0 && *prev == 0 && *up == 0);
533
534  this_section = what_section (input_text + input_text_offset);
535
536  /* If we are defaulting, then look at the immediately following
537     sectioning command (error if none) to determine the node's
538     level.  Find the node that contains the menu mentioning this node
539     that is one level up (error if not found).  That node is the "Up"
540     of this node.  Default the "Next" and "Prev" from the menu. */
541  if (defaulting)
542    {
543      NODE_REF *last_ref = NULL;
544      NODE_REF *ref = node_references;
545
546      if (this_section < 0 && !STREQ (node, "Top"))
547        {
548          char *polite_section_name = "top";
549          int i;
550
551          for (i = 0; section_alist[i].name; i++)
552            if (section_alist[i].level == current_section + 1)
553              {
554                polite_section_name = section_alist[i].name;
555                break;
556              }
557
558          line_error
559            (_("Node `%s' requires a sectioning command (e.g. %c%s)"),
560             node, COMMAND_PREFIX, polite_section_name);
561        }
562      else
563        {
564          if (strcmp (node, "Top") == 0)
565            {
566              /* Default the NEXT pointer to be the first menu item in
567                 this node, if there is a menu in this node.  We have to
568                 try very hard to find the menu, as it may be obscured
569                 by execution_strings which are on the filestack.  For
570                 every member of the filestack which has a FILENAME
571                 member which is identical to the current INPUT_FILENAME,
572                 search forward from that offset. */
573              int saved_input_text_offset = input_text_offset;
574              int saved_input_text_length = input_text_length;
575              char *saved_input_text = input_text;
576              FSTACK *next_file = filestack;
577
578              int orig_offset, orig_size;
579
580              /* No matter what, make this file point back at `(dir)'. */
581              free (up);
582              up = xstrdup ("(dir)"); /* html fixxme */
583
584              while (1)
585                {
586                  orig_offset = input_text_offset;
587                  orig_size =
588                    search_forward (node_search_string, orig_offset);
589
590                  if (orig_size < 0)
591                    orig_size = input_text_length;
592
593                  input_text_offset = search_forward ("\n@menu", orig_offset);
594                  if (input_text_offset > -1
595                      && cr_or_whitespace (input_text[input_text_offset + 6]))
596                    {
597                      char *nodename_from_menu = NULL;
598
599                      input_text_offset =
600                        search_forward ("\n* ", input_text_offset);
601
602                      if (input_text_offset != -1)
603                        nodename_from_menu = glean_node_from_menu (0, 0);
604
605                      if (nodename_from_menu)
606                        {
607                          free (next);
608                          next = nodename_from_menu;
609                          break;
610                        }
611                    }
612
613                  /* We got here, so it hasn't been found yet.  Try
614                     the next file on the filestack if there is one. */
615                  if (next_file
616                      && FILENAME_CMP (next_file->filename, input_filename)
617                          == 0)
618                    {
619                      input_text = next_file->text;
620                      input_text_offset = next_file->offset;
621                      input_text_length = next_file->size;
622                      next_file = next_file->next;
623                    }
624                  else
625                    { /* No more input files to check. */
626                      break;
627                    }
628                }
629
630              input_text = saved_input_text;
631              input_text_offset = saved_input_text_offset;
632              input_text_length = saved_input_text_length;
633            }
634        }
635
636      /* Fix the level of the menu references in the Top node, iff it
637         was declared with @top, and no subsequent reference was found. */
638      if (top_node_seen && !non_top_node_seen)
639        {
640          /* Then this is the first non-@top node seen. */
641          int level;
642
643          level = set_top_section_level (this_section - 1);
644          non_top_node_seen = 1;
645
646          while (ref)
647            {
648              if (ref->section == level)
649                ref->section = this_section - 1;
650              ref = ref->next;
651            }
652
653          ref = node_references;
654        }
655
656      while (ref)
657        {
658          if (ref->section == (this_section - 1)
659              && ref->type == menu_reference
660              && strcmp (ref->node, node) == 0)
661            {
662              char *containing_node = ref->containing_node;
663
664              free (up);
665              up = xstrdup (containing_node);
666
667              if (last_ref
668                  && last_ref->type == menu_reference
669                  && strcmp (last_ref->containing_node, containing_node) == 0)
670                {
671                  free (next);
672                  next = xstrdup (last_ref->node);
673                }
674
675              while (ref->section == this_section - 1
676                     && ref->next
677                     && ref->next->type != menu_reference)
678                ref = ref->next;
679
680              if (ref->next && ref->type == menu_reference
681                  && strcmp (ref->next->containing_node, containing_node) == 0)
682                {
683                  free (prev);
684                  prev = xstrdup (ref->next->node);
685                }
686              else if (!ref->next
687                       && strcasecmp (ref->containing_node, "Top") == 0)
688                {
689                  free (prev);
690                  prev = xstrdup (ref->containing_node);
691                }
692              break;
693            }
694          last_ref = ref;
695          ref = ref->next;
696        }
697    }
698
699  /* Insert the correct args if we are expanding macros, and the node's
700     pointers weren't defaulted. */
701  if (macro_expansion_output_stream && !executing_string && !defaulting)
702    {
703      char *temp;
704      int op_orig = output_paragraph_offset;
705      int meta_pos_orig = meta_char_pos;
706      int extra = html ? strlen (node) : 0;
707
708      temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up));
709      sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up);
710      me_execute_string (temp);
711      free (temp);
712
713      output_paragraph_offset = op_orig;
714      meta_char_pos = meta_pos_orig;
715    }
716
717  if (!*node)
718    {
719      line_error (_("No node name specified for `%c%s' command"),
720                  COMMAND_PREFIX, command);
721      free (node);
722      free (next); next = NULL;
723      free (prev); prev= NULL;
724      free (up);   up = NULL;
725      node_number++;            /* else it doesn't get bumped */
726    }
727  else
728    {
729      if (!*next) { free (next); next = NULL; }
730      if (!*prev) { free (prev); prev = NULL; }
731      if (!*up)   { free (up);   up = NULL;   }
732      remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
733      outstanding_node = 1;
734    }
735
736  if (html)
737    {
738      char *tem;
739
740      if (splitting)
741        { /* this code not operational, we do not currently split html */
742          char filename[20];
743
744          sprintf (filename, "node%d.html", number_of_node (node));
745          output_stream = fopen (filename, "w");
746          if (output_stream == NULL)
747            {
748              fs_error (filename);
749              xexit (1);
750            }
751          set_current_output_filename (filename);
752          /* FIXME: when this code is operational, we will need to
753             expand node, next, prev, and up before output.  */
754          add_word_args ("<html><head><title>%s</title>", node);
755          if (next) add_link (next, "rel=next");
756          if (prev) add_link (prev, "rel=previous");
757          if (up) add_link (up, "rel=up");
758          add_word ("</head>\n<body>\n");
759        }
760
761      if (!splitting && no_headers)
762	{ /* cross refs need a name="#anchor" even if we're not writing headers*/
763          add_word ("<a name=\"");
764          tem = expand_node_name (node);
765          add_anchor_name (tem, 0);
766          add_word ("\"></a>");
767          free (tem);
768	}
769
770      if (splitting || !no_headers)
771        { /* Navigation bar.   The <p> avoids the links area running
772             on with old Lynxen.  */
773          add_word_args ("<p>%s\n", splitting ? "" : "<hr>");
774          add_word_args ("%s<a name=\"", _("Node:"));
775          tem = expand_node_name (node);
776          add_anchor_name (tem, 0);
777          add_word_args ("\">%s</a>", tem);
778          free (tem);
779
780          if (next)
781            {
782              add_word (",\n");
783              add_word (_("Next:"));
784              add_word ("<a rel=next href=\"");
785              tem = expansion (next, 0);
786              add_anchor_name (tem, 1);
787              add_word_args ("\">%s</a>", tem);
788              free (tem);
789            }
790          if (prev)
791            {
792              add_word (",\n");
793              add_word (_("Previous:"));
794              add_word ("<a rel=previous href=\"");
795              tem = expansion (prev, 0);
796              add_anchor_name (tem, 1);
797              add_word_args ("\">%s</a>", tem);
798              free (tem);
799            }
800          if (up)
801            {
802              add_word (",\n");
803              add_word (_("Up:"));
804              add_word ("<a rel=up href=\"");
805              tem = expansion (up, 0);
806              add_anchor_name (tem, 1);
807              add_word_args ("\">%s</a>", tem);
808              free (tem);
809            }
810          /* html fixxme: we want a `top' or `contents' link here.  */
811
812          add_word_args ("\n%s<br>\n", splitting ? "<hr>" : "");
813        }
814    }
815
816  else if (!no_headers)
817    {
818      if (macro_expansion_output_stream)
819        me_inhibit_expansion++;
820
821      /* These strings are not translatable.  */
822      if (next)
823        {
824          execute_string (",  Next: %s", next);
825          filling_enabled = indented_fill = 0;
826        }
827      if (prev)
828        {
829          execute_string (",  Prev: %s", prev);
830          filling_enabled = indented_fill = 0;
831        }
832      if (up)
833        {
834          execute_string (",  Up: %s", up);
835          filling_enabled = indented_fill = 0;
836        }
837      if (macro_expansion_output_stream)
838        me_inhibit_expansion--;
839    }
840
841  close_paragraph ();
842  no_indent = 0;
843
844  /* Change the section only if there was a sectioning command. */
845  if (this_section >= 0)
846    current_section = this_section;
847
848  if (current_node && STREQ (current_node, "Top"))
849    top_node_seen = 1;
850
851  filling_enabled = 1;
852  in_fixed_width_font--;
853}
854
855/* Cross-reference target at an arbitrary spot.  */
856void
857cm_anchor (arg)
858     int arg;
859{
860  char *anchor;
861
862  if (arg == END)
863    return;
864
865  /* Parse the anchor text.  */
866  anchor = get_xref_token (1);
867
868  /* In HTML mode, need to actually produce some output.  */
869  if (html)
870    {
871      /* If this anchor is at the beginning of a new paragraph, make
872	 sure a new paragraph is indeed started.  */
873      if (!paragraph_is_open)
874	{
875	  start_paragraph ();
876	  if (!in_fixed_width_font || in_menu || in_detailmenu)
877	    {
878	      insert_string ("<p>");
879	      in_paragraph = 1;
880	    }
881	}
882      add_word ("<a name=\"");
883      add_anchor_name (anchor, 0);
884      add_word ("\"></a>");
885    }
886
887  /* Save it in the tag table.  */
888  remember_node (anchor, NULL, NULL, NULL, output_position + output_column,
889                 line_number, TAG_FLAG_ANCHOR);
890}
891
892/* Find NODE in REF_LIST. */
893static NODE_REF *
894find_node_reference (node, ref_list)
895     char *node;
896     NODE_REF *ref_list;
897{
898  NODE_REF *orig_ref_list = ref_list;
899  char *expanded_node;
900
901  while (ref_list)
902    {
903      if (strcmp (node, ref_list->node) == 0)
904        break;
905      ref_list = ref_list->next;
906    }
907
908  if (ref_list || !expensive_validation)
909    return ref_list;
910
911  /* Maybe NODE is not expanded yet.  This may be SLOW.  */
912  expanded_node = expand_node_name (node);
913  for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next)
914    {
915      if (STREQ (expanded_node, ref_list->node))
916        break;
917      if (strchr (ref_list->node, COMMAND_PREFIX))
918        {
919          char *expanded_ref = expand_node_name (ref_list->node);
920
921          if (STREQ (expanded_node, expanded_ref))
922            {
923              free (expanded_ref);
924              break;
925            }
926          free (expanded_ref);
927        }
928    }
929  free (expanded_node);
930  return ref_list;
931}
932
933void
934free_node_references ()
935{
936  NODE_REF *list, *temp;
937
938  list = node_references;
939
940  while (list)
941    {
942      temp = list;
943      free (list->node);
944      free (list->containing_node);
945      list = list->next;
946      free (temp);
947    }
948  node_references = NULL;
949}
950
951void
952free_node_node_references ()
953{
954  NODE_REF *list, *temp;
955
956  list = node_references;
957
958  while (list)
959    {
960      temp = list;
961      free (list->node);
962      list = list->next;
963      free (temp);
964    }
965  node_node_references = NULL;
966}
967
968/* Return the number assigned to a named node in either the tag_table
969   or node_references list or zero if no number has been assigned. */
970int
971number_of_node (node)
972     char *node;
973{
974  NODE_REF *temp_ref;
975  TAG_ENTRY *temp_node = find_node (node);
976
977  if (temp_node)
978    return temp_node->number;
979  else if ((temp_ref = find_node_reference (node, node_references)))
980    return temp_ref->number;
981  else if ((temp_ref = find_node_reference (node, node_node_references)))
982    return temp_ref->number;
983  else
984    return 0;
985}
986
987/* validation */
988
989/* Return 1 if TAG (at LINE) correctly validated, or 0 if not.
990   LABEL is the (translated) description of the type of reference --
991   Menu, Cross, Next, etc.  */
992
993static int
994validate (tag, line, label)
995     char *tag;
996     int line;
997     char *label;
998{
999  TAG_ENTRY *result;
1000
1001  /* If there isn't a tag to verify, or if the tag is in another file,
1002     then it must be okay. */
1003  if (!tag || !*tag || *tag == '(')
1004    return 1;
1005
1006  /* Otherwise, the tag must exist. */
1007  result = find_node (tag);
1008
1009  if (!result)
1010    {
1011      line_number = line;
1012      line_error (_("%s reference to nonexistent node `%s'"), label, tag);
1013      return 0;
1014    }
1015  result->touched++;
1016  return 1;
1017}
1018
1019/* The strings here are followed in the message by `reference to...' in
1020   the `validate' routine.  They are only used in messages, thus are
1021   translated.  */
1022static char *
1023reftype_type_string (type)
1024     enum reftype type;
1025{
1026  switch (type)
1027    {
1028    case menu_reference:
1029      return _("Menu");
1030    case followed_reference:
1031      return _("Cross");
1032    default:
1033      return "Internal-bad-reference-type";
1034    }
1035}
1036
1037static void
1038validate_other_references (ref_list)
1039     NODE_REF *ref_list;
1040{
1041  char *old_input_filename = input_filename;
1042
1043  while (ref_list)
1044    {
1045      input_filename = ref_list->filename;
1046      validate (ref_list->node, ref_list->line_no,
1047                reftype_type_string (ref_list->type));
1048      ref_list = ref_list->next;
1049    }
1050  input_filename = old_input_filename;
1051}
1052
1053/* Validation of an info file.
1054   Scan through the list of tag entries touching the Prev, Next, and Up
1055   elements of each.  It is an error not to be able to touch one of them,
1056   except in the case of external node references, such as "(DIR)".
1057
1058   If the Prev is different from the Up,
1059   then the Prev node must have a Next pointing at this node.
1060
1061   Every node except Top must have an Up.
1062   The Up node must contain some sort of reference, other than a Next,
1063   to this node.
1064
1065   If the Next is different from the Next of the Up,
1066   then the Next node must have a Prev pointing at this node. */
1067void
1068validate_file (tag_table)
1069     TAG_ENTRY *tag_table;
1070{
1071  char *old_input_filename = input_filename;
1072  TAG_ENTRY *tags = tag_table;
1073
1074  while (tags)
1075    {
1076      TAG_ENTRY *temp_tag;
1077      char *tem1, *tem2;
1078
1079      input_filename = tags->filename;
1080      line_number = tags->line_no;
1081
1082      /* If this is a "no warn" node, don't validate it in any way. */
1083      if (tags->flags & TAG_FLAG_NO_WARN)
1084        {
1085          tags = tags->next_ent;
1086          continue;
1087        }
1088
1089      /* If this node has a Next, then make sure that the Next exists. */
1090      if (tags->next)
1091        {
1092          validate (tags->next, tags->line_no, _("Next"));
1093
1094          /* If the Next node exists, and there is no Up, then make sure
1095             that the Prev of the Next points back.  But do nothing if
1096             we aren't supposed to issue warnings about this node. */
1097          temp_tag = find_node (tags->next);
1098          if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN))
1099            {
1100              char *prev = temp_tag->prev;
1101              int you_lose = !prev || !STREQ (prev, tags->node);
1102
1103              if (you_lose && expensive_validation)
1104                {
1105                  tem1 = expand_node_name (prev);
1106                  tem2 = expand_node_name (tags->node);
1107
1108                  if (STREQ (tem1, tem2))
1109                    you_lose = 0;
1110                  free (tem1);
1111                  free (tem2);
1112                }
1113              if (you_lose)
1114                {
1115                  line_error (_("Next field of node `%s' not pointed to"),
1116                              tags->node);
1117                  line_number = temp_tag->line_no;
1118                  input_filename = temp_tag->filename;
1119                  line_error (_("This node (%s) has the bad Prev"),
1120                              temp_tag->node);
1121                  input_filename = tags->filename;
1122                  line_number = tags->line_no;
1123                  temp_tag->flags |= TAG_FLAG_PREV_ERROR;
1124                }
1125            }
1126        }
1127
1128      /* Validate the Prev field if there is one, and we haven't already
1129         complained about it in some way.  You don't have to have a Prev
1130         field at this stage. */
1131      if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev)
1132        {
1133          int valid_p = validate (tags->prev, tags->line_no, _("Prev"));
1134
1135          if (!valid_p)
1136            tags->flags |= TAG_FLAG_PREV_ERROR;
1137          else
1138            { /* If the Prev field is not the same as the Up field,
1139                 then the node pointed to by the Prev field must have
1140                 a Next field which points to this node. */
1141              int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up);
1142
1143              if (!prev_equals_up && expensive_validation)
1144                {
1145                  tem1 = expand_node_name (tags->prev);
1146                  tem2 = expand_node_name (tags->up);
1147                  prev_equals_up = STREQ (tem1, tem2);
1148                  free (tem1);
1149                  free (tem2);
1150                }
1151              if (!prev_equals_up)
1152                {
1153                  temp_tag = find_node (tags->prev);
1154
1155                  /* If we aren't supposed to issue warnings about the
1156                     target node, do nothing. */
1157                  if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN))
1158                    /* Do nothing. */ ;
1159                  else
1160                    {
1161                      int you_lose = !temp_tag->next
1162                        || !STREQ (temp_tag->next, tags->node);
1163
1164                      if (temp_tag->next && you_lose && expensive_validation)
1165                        {
1166                          tem1 = expand_node_name (temp_tag->next);
1167                          tem2 = expand_node_name (tags->node);
1168                          if (STREQ (tem1, tem2))
1169                            you_lose = 0;
1170                          free (tem1);
1171                          free (tem2);
1172                        }
1173                      if (you_lose)
1174                        {
1175                          line_error
1176                            (_("Prev field of node `%s' not pointed to"),
1177                             tags->node);
1178                          line_number = temp_tag->line_no;
1179                          input_filename = temp_tag->filename;
1180                          line_error (_("This node (%s) has the bad Next"),
1181                                      temp_tag->node);
1182                          input_filename = tags->filename;
1183                          line_number = tags->line_no;
1184                          temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
1185                        }
1186                    }
1187                }
1188            }
1189        }
1190
1191      if (!tags->up
1192          && !(tags->flags & TAG_FLAG_ANCHOR)
1193          && strcasecmp (tags->node, "Top") != 0)
1194        line_error (_("`%s' has no Up field"), tags->node);
1195      else if (tags->up)
1196        {
1197          int valid_p = validate (tags->up, tags->line_no, _("Up"));
1198
1199          /* If node X has Up: Y, then warn if Y fails to have a menu item
1200             or note pointing at X, if Y isn't of the form "(Y)". */
1201          if (valid_p && *tags->up != '(')
1202            {
1203              NODE_REF *nref;
1204              NODE_REF *tref = NULL;
1205              NODE_REF *list = node_references;
1206
1207              for (;;)
1208                {
1209                  nref = find_node_reference (tags->node, list);
1210                  if (!nref)
1211                    break;
1212
1213                  if (strcmp (nref->containing_node, tags->up) == 0)
1214                    {
1215                      if (nref->type != menu_reference)
1216                        {
1217                          tref = nref;
1218                          list = nref->next;
1219                        }
1220                      else
1221                        break;
1222                    }
1223                  list = nref->next;
1224                }
1225
1226              if (!nref)
1227                {
1228		  if (!tref && expensive_validation)
1229		    {
1230		      /* Sigh...  This might be AWFULLY slow, but if
1231		         they want this feature, they'll have to pay!
1232		         We do all the loop again expanding each
1233		         containing_node reference as we go.  */
1234		      char *tags_up = expand_node_name (tags->up);
1235		      char *tem;
1236
1237		      list = node_references;
1238
1239		      for (;;)
1240			{
1241			  nref = find_node_reference (tags->node, list);
1242			  if (!nref)
1243			    break;
1244			  tem = expand_node_name (nref->containing_node);
1245			  if (STREQ (tem, tags_up))
1246			    {
1247			      if (nref->type != menu_reference)
1248				tref = nref;
1249			      else
1250				{
1251				  free (tem);
1252				  break;
1253				}
1254			    }
1255			  free (tem);
1256			  list = nref->next;
1257			}
1258		    }
1259                  if (!nref && !tref)
1260                    {
1261                      temp_tag = find_node (tags->up);
1262                      line_number = temp_tag->line_no;
1263                      input_filename = temp_tag->filename;
1264                      line_error (
1265           _("Node `%s' lacks menu item for `%s' despite being its Up target"),
1266                                  tags->up, tags->node);
1267                      line_number = tags->line_no;
1268                      input_filename = tags->filename;
1269                    }
1270                }
1271            }
1272        }
1273      tags = tags->next_ent;
1274    }
1275
1276  validate_other_references (node_references);
1277  /* We have told the user about the references which didn't exist.
1278     Now tell him about the nodes which aren't referenced. */
1279
1280  for (tags = tag_table; tags; tags = tags->next_ent)
1281    {
1282      /* If this node is a "no warn" node, do nothing. */
1283      if (tags->flags & TAG_FLAG_NO_WARN)
1284        {
1285          tags = tags->next_ent;
1286          continue;
1287        }
1288
1289      /* Special hack.  If the node in question appears to have
1290         been referenced more than REFERENCE_WARNING_LIMIT times,
1291         give a warning. */
1292      if (tags->touched > reference_warning_limit)
1293        {
1294          input_filename = tags->filename;
1295          line_number = tags->line_no;
1296          warning (_("node `%s' has been referenced %d times"),
1297                   tags->node, tags->touched);
1298        }
1299
1300      if (tags->touched == 0)
1301        {
1302          input_filename = tags->filename;
1303          line_number = tags->line_no;
1304
1305          /* Notice that the node "Top" is special, and doesn't have to
1306             be referenced.   Anchors don't have to be referenced
1307             either, you might define them for another document.  */
1308          if (strcasecmp (tags->node, "Top") != 0
1309              && !(tags->flags & TAG_FLAG_ANCHOR))
1310            warning (_("unreferenced node `%s'"), tags->node);
1311        }
1312    }
1313  input_filename = old_input_filename;
1314}
1315
1316
1317/* Splitting */
1318
1319/* Return true if the tag entry pointed to by TAGS is the last node.
1320   This means only anchors follow.  */
1321
1322static int
1323last_node_p (tags)
1324     TAG_ENTRY *tags;
1325{
1326  int last = 1;
1327  while (tags->next_ent) {
1328    tags = tags->next_ent;
1329    if (tags->flags & TAG_FLAG_ANCHOR)
1330      ;
1331    else
1332      {
1333        last = 0;
1334        break;
1335      }
1336  }
1337
1338  return last;
1339}
1340
1341
1342/* Split large output files into a series of smaller files.  Each file
1343   is pointed to in the tag table, which then gets written out as the
1344   original file.  The new files have the same name as the original file
1345   with a "-num" attached.  SIZE is the largest number of bytes to allow
1346   in any single split file. */
1347void
1348split_file (filename, size)
1349     char *filename;
1350     int size;
1351{
1352  char *root_filename, *root_pathname;
1353  char *the_file, *filename_part ();
1354  struct stat fileinfo;
1355  long file_size;
1356  char *the_header;
1357  int header_size;
1358  int dos_file_names = 0;       /* if nonzero, don't exceed 8+3 limits */
1359
1360  /* Can only do this to files with tag tables. */
1361  if (!tag_table)
1362    return;
1363
1364  if (size == 0)
1365    size = DEFAULT_SPLIT_SIZE;
1366
1367  if ((stat (filename, &fileinfo) != 0) ||
1368      (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD))
1369    return;
1370  file_size = (long) fileinfo.st_size;
1371
1372  the_file = find_and_load (filename);
1373  if (!the_file)
1374    return;
1375
1376  root_filename = filename_part (filename);
1377  root_pathname = pathname_part (filename);
1378
1379  /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */
1380  dos_file_names = !HAVE_LONG_FILENAMES (root_pathname ? root_pathname : ".");
1381
1382  if (!root_pathname)
1383    root_pathname = xstrdup ("");
1384
1385  /* Start splitting the file.  Walk along the tag table
1386     outputting sections of the file.  When we have written
1387     all of the nodes in the tag table, make the top-level
1388     pointer file, which contains indirect pointers and
1389     tags for the nodes. */
1390  {
1391    int which_file = 1;
1392    TAG_ENTRY *tags = tag_table;
1393    char *indirect_info = NULL;
1394
1395    /* Remember the `header' of this file.  The first tag in the file is
1396       the bottom of the header; the top of the file is the start. */
1397    the_header = xmalloc (1 + (header_size = tags->position));
1398    memcpy (the_header, the_file, header_size);
1399
1400    while (tags)
1401      {
1402        int file_top, file_bot, limit;
1403
1404        /* Have to include the Control-_. */
1405        file_top = file_bot = tags->position;
1406        limit = file_top + size;
1407
1408        /* If the rest of this file is only one node, then
1409           that is the entire subfile. */
1410        if (last_node_p (tags))
1411          {
1412            int i = tags->position + 1;
1413            char last_char = the_file[i];
1414
1415            while (i < file_size)
1416              {
1417                if ((the_file[i] == '\037') &&
1418                    ((last_char == '\n') ||
1419                     (last_char == '\014')))
1420                  break;
1421                else
1422                  last_char = the_file[i];
1423                i++;
1424              }
1425            file_bot = i;
1426            tags = tags->next_ent;
1427            goto write_region;
1428          }
1429
1430        /* Otherwise, find the largest number of nodes that can fit in
1431           this subfile. */
1432        for (; tags; tags = tags->next_ent)
1433          {
1434            if (last_node_p (tags))
1435              {
1436                /* This entry is the last node.  Search forward for the end
1437                   of this node, and that is the end of this file. */
1438                int i = tags->position + 1;
1439                char last_char = the_file[i];
1440
1441                while (i < file_size)
1442                  {
1443                    if ((the_file[i] == '\037') &&
1444                        ((last_char == '\n') ||
1445                         (last_char == '\014')))
1446                      break;
1447                    else
1448                      last_char = the_file[i];
1449                    i++;
1450                  }
1451                file_bot = i;
1452
1453                if (file_bot < limit)
1454                  {
1455                    tags = tags->next_ent;
1456                    goto write_region;
1457                  }
1458                else
1459                  {
1460                    /* Here we want to write out everything before the last
1461                       node, and then write the last node out in a file
1462                       by itself. */
1463                    file_bot = tags->position;
1464                    goto write_region;
1465                  }
1466              }
1467
1468            /* Write region only if this was a node, not an anchor.  */
1469            if (tags->next_ent->position > limit
1470                && !(tags->flags & TAG_FLAG_ANCHOR))
1471              {
1472                if (tags->position == file_top)
1473                  tags = tags->next_ent;
1474
1475                file_bot = tags->position;
1476
1477              write_region:
1478                {
1479                  int fd;
1480                  char *split_filename, *split_basename;
1481                  unsigned root_len = strlen (root_filename);
1482
1483                  split_filename = xmalloc (10 + strlen (root_pathname)
1484                                            + root_len);
1485                  split_basename = xmalloc (10 + root_len);
1486                  sprintf (split_basename, "%s-%d", root_filename, which_file);
1487                  if (dos_file_names)
1488                    {
1489                      char *dot = strchr (split_basename, '.');
1490                      unsigned base_len = strlen (split_basename);
1491
1492                      if (dot)
1493                        { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
1494                          dot[1] = 'i';
1495                          memmove (which_file <= 99 ? dot + 2 : dot + 1,
1496                                   split_basename + root_len + 1,
1497                                   strlen (split_basename + root_len + 1) + 1);
1498                        }
1499                      else if (base_len > 8)
1500                        {
1501                          /* Make foobar-1, .., fooba-10, .., foob-100, ... */
1502                          unsigned numlen = base_len - root_len;
1503
1504                          memmove (split_basename + 8 - numlen,
1505                                   split_basename + root_len, numlen + 1);
1506                        }
1507                    }
1508                  sprintf (split_filename, "%s%s", root_pathname,
1509                           split_basename);
1510
1511                  fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
1512                  if (fd < 0
1513                      || write (fd, the_header, header_size) != header_size
1514                      || write (fd, the_file + file_top, file_bot - file_top)
1515                         != (file_bot - file_top)
1516                      || (close (fd)) < 0)
1517                    {
1518                      perror (split_filename);
1519                      if (fd != -1)
1520                        close (fd);
1521                      xexit (1);
1522                    }
1523
1524                  if (!indirect_info)
1525                    {
1526                      indirect_info = the_file + file_top;
1527                      sprintf (indirect_info, "\037\nIndirect:\n");
1528                      indirect_info += strlen (indirect_info);
1529                    }
1530
1531                  sprintf (indirect_info, "%s: %d\n",
1532                           split_basename, file_top);
1533
1534                  free (split_basename);
1535                  free (split_filename);
1536                  indirect_info += strlen (indirect_info);
1537                  which_file++;
1538                  break;
1539                }
1540              }
1541          }
1542      }
1543
1544    /* We have sucessfully created the subfiles.  Now write out the
1545       original again.  We must use `output_stream', or
1546       write_tag_table_indirect () won't know where to place the output. */
1547    output_stream = fopen (filename, "w");
1548    if (!output_stream)
1549      {
1550        perror (filename);
1551        xexit (1);
1552      }
1553
1554    {
1555      int distance = indirect_info - the_file;
1556      fwrite (the_file, 1, distance, output_stream);
1557
1558      /* Inhibit newlines. */
1559      paragraph_is_open = 0;
1560
1561      write_tag_table_indirect ();
1562      fclose (output_stream);
1563      free (the_header);
1564      free (the_file);
1565      return;
1566    }
1567  }
1568}
1569