source: trunk/third/texinfo/makeinfo/node.c @ 18945

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