source: trunk/texinfo/makeinfo/node.c@ 2619

Last change on this file since 2619 was 2619, checked in by bird, 19 years ago

applied OS/2 patches (manually).

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