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