Bug Summary

File:src/gnu/usr.bin/texinfo/makeinfo/node.c
Warning:line 750, column 21
Value stored to 'orig_size' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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