11cc83814Sespie /* node.c -- nodes for Texinfo.
2*1076333cSespie $Id: node.c,v 1.3 2006/07/17 16:12:36 espie Exp $
31cc83814Sespie
4*1076333cSespie Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
5*1076333cSespie Foundation, Inc.
61cc83814Sespie
71cc83814Sespie This program is free software; you can redistribute it and/or modify
81cc83814Sespie it under the terms of the GNU General Public License as published by
91cc83814Sespie the Free Software Foundation; either version 2, or (at your option)
101cc83814Sespie any later version.
111cc83814Sespie
121cc83814Sespie This program is distributed in the hope that it will be useful,
131cc83814Sespie but WITHOUT ANY WARRANTY; without even the implied warranty of
141cc83814Sespie MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
151cc83814Sespie GNU General Public License for more details.
161cc83814Sespie
171cc83814Sespie You should have received a copy of the GNU General Public License
181cc83814Sespie along with this program; if not, write to the Free Software Foundation,
191cc83814Sespie Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
201cc83814Sespie
211cc83814Sespie #include "system.h"
221cc83814Sespie #include "cmds.h"
231cc83814Sespie #include "files.h"
24*1076333cSespie #include "float.h"
251cc83814Sespie #include "footnote.h"
261cc83814Sespie #include "macro.h"
271cc83814Sespie #include "makeinfo.h"
281cc83814Sespie #include "node.h"
293fb98d4aSespie #include "html.h"
301cc83814Sespie #include "sectioning.h"
311cc83814Sespie #include "insertion.h"
323fb98d4aSespie #include "xml.h"
331cc83814Sespie
341cc83814Sespie /* See comments in node.h. */
351cc83814Sespie NODE_REF *node_references = NULL;
361cc83814Sespie NODE_REF *node_node_references = NULL;
371cc83814Sespie TAG_ENTRY *tag_table = NULL;
381cc83814Sespie int node_number = -1;
39*1076333cSespie int node_order = 0;
401cc83814Sespie int current_section = 0;
411cc83814Sespie int outstanding_node = 0;
421cc83814Sespie
431cc83814Sespie /* Adding nodes, and making tags. */
441cc83814Sespie
451cc83814Sespie /* Start a new tag table. */
461cc83814Sespie void
init_tag_table(void)47*1076333cSespie init_tag_table (void)
481cc83814Sespie {
491cc83814Sespie while (tag_table)
501cc83814Sespie {
511cc83814Sespie TAG_ENTRY *temp = tag_table;
521cc83814Sespie free (temp->node);
531cc83814Sespie free (temp->prev);
541cc83814Sespie free (temp->next);
551cc83814Sespie free (temp->up);
561cc83814Sespie tag_table = tag_table->next_ent;
571cc83814Sespie free (temp);
581cc83814Sespie }
591cc83814Sespie }
601cc83814Sespie
611cc83814Sespie /* Write out the contents of the existing tag table.
621cc83814Sespie INDIRECT_P says how to format the output (it depends on whether the
631cc83814Sespie table is direct or indirect). */
641cc83814Sespie static void
write_tag_table_internal(int indirect_p)65*1076333cSespie write_tag_table_internal (int indirect_p)
661cc83814Sespie {
671cc83814Sespie TAG_ENTRY *node;
681cc83814Sespie int old_indent = no_indent;
691cc83814Sespie
703fb98d4aSespie if (xml)
713fb98d4aSespie {
723fb98d4aSespie flush_output ();
733fb98d4aSespie return;
743fb98d4aSespie }
753fb98d4aSespie
761cc83814Sespie no_indent = 1;
771cc83814Sespie filling_enabled = 0;
781cc83814Sespie must_start_paragraph = 0;
791cc83814Sespie close_paragraph ();
801cc83814Sespie
811cc83814Sespie if (!indirect_p)
821cc83814Sespie {
831cc83814Sespie no_indent = 1;
841cc83814Sespie insert ('\n');
851cc83814Sespie }
861cc83814Sespie
871cc83814Sespie add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
881cc83814Sespie
891cc83814Sespie /* Do not collapse -- to -, etc., in node names. */
901cc83814Sespie in_fixed_width_font++;
911cc83814Sespie
921cc83814Sespie for (node = tag_table; node; node = node->next_ent)
931cc83814Sespie {
941cc83814Sespie if (node->flags & TAG_FLAG_ANCHOR)
951cc83814Sespie { /* This reference is to an anchor. */
961cc83814Sespie execute_string ("Ref: %s", node->node);
971cc83814Sespie }
981cc83814Sespie else
991cc83814Sespie { /* This reference is to a node. */
1001cc83814Sespie execute_string ("Node: %s", node->node);
1011cc83814Sespie }
1021cc83814Sespie add_word_args ("\177%d\n", node->position);
1031cc83814Sespie }
1041cc83814Sespie
1051cc83814Sespie add_word ("\037\nEnd Tag Table\n");
1061cc83814Sespie
1071cc83814Sespie /* Do not collapse -- to -, etc., in node names. */
1081cc83814Sespie in_fixed_width_font--;
1091cc83814Sespie
1101cc83814Sespie flush_output ();
1111cc83814Sespie no_indent = old_indent;
1121cc83814Sespie }
1131cc83814Sespie
1141cc83814Sespie void
write_tag_table(char * filename)115*1076333cSespie write_tag_table (char *filename)
1161cc83814Sespie {
117*1076333cSespie output_stream = fopen (filename, "a");
118*1076333cSespie if (!output_stream)
119*1076333cSespie {
120*1076333cSespie fs_error (filename);
121*1076333cSespie return;
1221cc83814Sespie }
1231cc83814Sespie
124*1076333cSespie write_tag_table_internal (0); /* Not indirect. */
125*1076333cSespie
126*1076333cSespie if (fclose (output_stream) != 0)
127*1076333cSespie fs_error (filename);
128*1076333cSespie }
129*1076333cSespie
130*1076333cSespie static void
write_tag_table_indirect(void)131*1076333cSespie write_tag_table_indirect (void)
1321cc83814Sespie {
1331cc83814Sespie write_tag_table_internal (1);
1341cc83814Sespie }
1351cc83814Sespie
1361cc83814Sespie /* Convert "top" and friends into "Top". */
1371cc83814Sespie static void
normalize_node_name(char * string)138*1076333cSespie normalize_node_name (char *string)
1391cc83814Sespie {
1401cc83814Sespie if (strcasecmp (string, "Top") == 0)
1411cc83814Sespie strcpy (string, "Top");
1421cc83814Sespie }
1431cc83814Sespie
144*1076333cSespie static char *
get_node_token(int expand)145*1076333cSespie get_node_token (int expand)
1461cc83814Sespie {
1471cc83814Sespie char *string;
1481cc83814Sespie
1491cc83814Sespie get_until_in_line (expand, ",", &string);
1501cc83814Sespie
1511cc83814Sespie if (curchar () == ',')
1521cc83814Sespie input_text_offset++;
1531cc83814Sespie
1541cc83814Sespie fix_whitespace (string);
1551cc83814Sespie
1561cc83814Sespie /* Force all versions of "top" to be "Top". */
1571cc83814Sespie normalize_node_name (string);
1581cc83814Sespie
1591cc83814Sespie return string;
1601cc83814Sespie }
1611cc83814Sespie
1621cc83814Sespie /* Expand any macros and other directives in a node name, and
1631cc83814Sespie return the expanded name as an malloc'ed string. */
1641cc83814Sespie char *
expand_node_name(char * node)165*1076333cSespie expand_node_name (char *node)
1661cc83814Sespie {
1671cc83814Sespie char *result = node;
1681cc83814Sespie
1691cc83814Sespie if (node)
1701cc83814Sespie {
1711cc83814Sespie /* Don't expand --, `` etc., in case somebody will want
1721cc83814Sespie to print the result. */
1731cc83814Sespie in_fixed_width_font++;
1741cc83814Sespie result = expansion (node, 0);
1751cc83814Sespie in_fixed_width_font--;
1761cc83814Sespie fix_whitespace (result);
1771cc83814Sespie normalize_node_name (result);
1781cc83814Sespie }
1791cc83814Sespie return result;
1801cc83814Sespie }
1811cc83814Sespie
1821cc83814Sespie /* Look up NAME in the tag table, and return the associated
1831cc83814Sespie tag_entry. If the node is not in the table return NULL. */
1841cc83814Sespie TAG_ENTRY *
find_node(char * name)185*1076333cSespie find_node (char *name)
1861cc83814Sespie {
1871cc83814Sespie TAG_ENTRY *tag = tag_table;
1881cc83814Sespie char *expanded_name;
1891cc83814Sespie char n1 = name[0];
1901cc83814Sespie
1911cc83814Sespie while (tag)
1921cc83814Sespie {
1931cc83814Sespie if (tag->node[0] == n1 && strcmp (tag->node, name) == 0)
1941cc83814Sespie return tag;
1951cc83814Sespie tag = tag->next_ent;
1961cc83814Sespie }
1971cc83814Sespie
1981cc83814Sespie if (!expensive_validation)
1991cc83814Sespie return NULL;
2001cc83814Sespie
2011cc83814Sespie /* Try harder. Maybe TAG_TABLE has the expanded NAME, or maybe NAME
2021cc83814Sespie is expanded while TAG_TABLE has its unexpanded form. This may
2031cc83814Sespie slow down the search, but if they want this feature, let them
2041cc83814Sespie pay! If they want it fast, they should write every node name
2051cc83814Sespie consistently (either always expanded or always unexpaned). */
2061cc83814Sespie expanded_name = expand_node_name (name);
2071cc83814Sespie for (tag = tag_table; tag; tag = tag->next_ent)
2081cc83814Sespie {
2091cc83814Sespie if (STREQ (tag->node, expanded_name))
2101cc83814Sespie break;
2111cc83814Sespie /* If the tag name doesn't have the command prefix, there's no
2121cc83814Sespie chance it could expand into anything but itself. */
2131cc83814Sespie if (strchr (tag->node, COMMAND_PREFIX))
2141cc83814Sespie {
2151cc83814Sespie char *expanded_node = expand_node_name (tag->node);
2161cc83814Sespie
2171cc83814Sespie if (STREQ (expanded_node, expanded_name))
2181cc83814Sespie {
2191cc83814Sespie free (expanded_node);
2201cc83814Sespie break;
2211cc83814Sespie }
2221cc83814Sespie free (expanded_node);
2231cc83814Sespie }
2241cc83814Sespie }
2251cc83814Sespie free (expanded_name);
2261cc83814Sespie return tag;
2271cc83814Sespie }
2281cc83814Sespie
2293fb98d4aSespie /* Look in the tag table for a node whose file name is FNAME, and
2303fb98d4aSespie return the associated tag_entry. If there's no such node in the
2313fb98d4aSespie table, return NULL. */
232*1076333cSespie static TAG_ENTRY *
find_node_by_fname(char * fname)233*1076333cSespie find_node_by_fname (char *fname)
2343fb98d4aSespie {
2353fb98d4aSespie TAG_ENTRY *tag = tag_table;
2363fb98d4aSespie while (tag)
2373fb98d4aSespie {
2383fb98d4aSespie if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0)
2393fb98d4aSespie return tag;
2403fb98d4aSespie tag = tag->next_ent;
2413fb98d4aSespie }
2423fb98d4aSespie
2433fb98d4aSespie return tag;
2443fb98d4aSespie }
2453fb98d4aSespie
2463fb98d4aSespie /* Remember next, prev, etc. references in a @node command, where we
2471cc83814Sespie don't care about most of the entries. */
2481cc83814Sespie static void
remember_node_node_reference(char * node)249*1076333cSespie remember_node_node_reference (char *node)
2501cc83814Sespie {
2511cc83814Sespie NODE_REF *temp = xmalloc (sizeof (NODE_REF));
2521cc83814Sespie int number;
2531cc83814Sespie
2541cc83814Sespie if (!node) return;
2551cc83814Sespie temp->next = node_node_references;
2561cc83814Sespie temp->node = xstrdup (node);
2571cc83814Sespie temp->type = followed_reference;
2581cc83814Sespie number = number_of_node (node);
2591cc83814Sespie if (number)
2601cc83814Sespie temp->number = number; /* Already assigned. */
2611cc83814Sespie else
2621cc83814Sespie {
2631cc83814Sespie node_number++;
2641cc83814Sespie temp->number = node_number;
2651cc83814Sespie }
2661cc83814Sespie node_node_references = temp;
2671cc83814Sespie }
2681cc83814Sespie
2691cc83814Sespie /* Remember NODE and associates. */
270*1076333cSespie static void
remember_node(char * node,char * prev,char * next,char * up,int position,int line_no,char * fname,int flags)271*1076333cSespie remember_node (char *node, char *prev, char *next, char *up,
272*1076333cSespie int position, int line_no, char *fname, int flags)
2731cc83814Sespie {
2741cc83814Sespie /* Check for existence of this tag already. */
2751cc83814Sespie if (validating)
2761cc83814Sespie {
2771cc83814Sespie TAG_ENTRY *tag = find_node (node);
2781cc83814Sespie if (tag)
2791cc83814Sespie {
2801cc83814Sespie line_error (_("Node `%s' previously defined at line %d"),
2811cc83814Sespie node, tag->line_no);
2821cc83814Sespie return;
2831cc83814Sespie }
2841cc83814Sespie }
2851cc83814Sespie
2861cc83814Sespie if (!(flags & TAG_FLAG_ANCHOR))
2871cc83814Sespie {
2881cc83814Sespie /* Make this the current node. */
2891cc83814Sespie current_node = node;
2901cc83814Sespie }
2911cc83814Sespie
2921cc83814Sespie /* Add it to the list. */
2931cc83814Sespie {
2941cc83814Sespie int number = number_of_node (node);
2951cc83814Sespie
2961cc83814Sespie TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY));
2971cc83814Sespie new->node = node;
2981cc83814Sespie new->prev = prev;
2991cc83814Sespie new->next = next;
3001cc83814Sespie new->up = up;
3011cc83814Sespie new->position = position;
3021cc83814Sespie new->line_no = line_no;
3031cc83814Sespie new->filename = node_filename;
3041cc83814Sespie new->touched = 0;
3051cc83814Sespie new->flags = flags;
3061cc83814Sespie if (number)
3071cc83814Sespie new->number = number; /* Already assigned. */
3081cc83814Sespie else
3091cc83814Sespie {
3101cc83814Sespie node_number++;
3111cc83814Sespie new->number = node_number;
3121cc83814Sespie }
313*1076333cSespie if (fname)
3143fb98d4aSespie new->html_fname = fname;
315*1076333cSespie else
316*1076333cSespie /* This happens for Top node under split-HTML, for example. */
317*1076333cSespie new->html_fname
318*1076333cSespie = normalize_filename (filename_part (current_output_filename));
3191cc83814Sespie new->next_ent = tag_table;
320*1076333cSespie
321*1076333cSespie /* Increment the order counter, and save it. */
322*1076333cSespie node_order++;
323*1076333cSespie new->order = node_order;
324*1076333cSespie
3251cc83814Sespie tag_table = new;
3261cc83814Sespie }
3271cc83814Sespie
3281cc83814Sespie if (html)
3291cc83814Sespie { /* Note the references to the next etc. nodes too. */
3301cc83814Sespie remember_node_node_reference (next);
3311cc83814Sespie remember_node_node_reference (prev);
3321cc83814Sespie remember_node_node_reference (up);
3331cc83814Sespie }
3341cc83814Sespie }
3351cc83814Sespie
3361cc83814Sespie /* Remember this node name for later validation use. This is used to
3371cc83814Sespie remember menu references while reading the input file. After the
3381cc83814Sespie output file has been written, if validation is on, then we use the
3391cc83814Sespie contents of `node_references' as a list of nodes to validate. */
3401cc83814Sespie void
remember_node_reference(char * node,int line,enum reftype type)341*1076333cSespie remember_node_reference (char *node, int line, enum reftype type)
3421cc83814Sespie {
3431cc83814Sespie NODE_REF *temp = xmalloc (sizeof (NODE_REF));
3441cc83814Sespie int number = number_of_node (node);
3451cc83814Sespie
3461cc83814Sespie temp->next = node_references;
3471cc83814Sespie temp->node = xstrdup (node);
3481cc83814Sespie temp->line_no = line;
3491cc83814Sespie temp->section = current_section;
3501cc83814Sespie temp->type = type;
3511cc83814Sespie temp->containing_node = xstrdup (current_node ? current_node : "");
3521cc83814Sespie temp->filename = node_filename;
3531cc83814Sespie if (number)
3541cc83814Sespie temp->number = number; /* Already assigned. */
3551cc83814Sespie else
3561cc83814Sespie {
3571cc83814Sespie node_number++;
3581cc83814Sespie temp->number = node_number;
3591cc83814Sespie }
3601cc83814Sespie
3611cc83814Sespie node_references = temp;
3621cc83814Sespie }
3631cc83814Sespie
3641cc83814Sespie static void
isolate_nodename(char * nodename)365*1076333cSespie isolate_nodename (char *nodename)
3661cc83814Sespie {
3671cc83814Sespie int i, c;
3681cc83814Sespie int paren_seen, paren;
3691cc83814Sespie
3701cc83814Sespie if (!nodename)
3711cc83814Sespie return;
3721cc83814Sespie
3731cc83814Sespie canon_white (nodename);
3741cc83814Sespie paren_seen = paren = i = 0;
3751cc83814Sespie
3761cc83814Sespie if (*nodename == '.' || !*nodename)
3771cc83814Sespie {
3781cc83814Sespie *nodename = 0;
3791cc83814Sespie return;
3801cc83814Sespie }
3811cc83814Sespie
3821cc83814Sespie if (*nodename == '(')
3831cc83814Sespie {
3841cc83814Sespie paren++;
3851cc83814Sespie paren_seen++;
3861cc83814Sespie i++;
3871cc83814Sespie }
3881cc83814Sespie
3891cc83814Sespie for (; (c = nodename[i]); i++)
3901cc83814Sespie {
3911cc83814Sespie if (paren)
3921cc83814Sespie {
3931cc83814Sespie if (c == '(')
3941cc83814Sespie paren++;
3951cc83814Sespie else if (c == ')')
3961cc83814Sespie paren--;
3971cc83814Sespie
3981cc83814Sespie continue;
3991cc83814Sespie }
4001cc83814Sespie
4011cc83814Sespie /* If the character following the close paren is a space, then this
4021cc83814Sespie node has no more characters associated with it. */
4031cc83814Sespie if (c == '\t' ||
4041cc83814Sespie c == '\n' ||
4051cc83814Sespie c == ',' ||
4061cc83814Sespie ((paren_seen && nodename[i - 1] == ')') &&
4071cc83814Sespie (c == ' ' || c == '.')) ||
4081cc83814Sespie (c == '.' &&
4091cc83814Sespie ((!nodename[i + 1] ||
4101cc83814Sespie (cr_or_whitespace (nodename[i + 1])) ||
4111cc83814Sespie (nodename[i + 1] == ')')))))
4121cc83814Sespie break;
4131cc83814Sespie }
4141cc83814Sespie nodename[i] = 0;
4151cc83814Sespie }
4161cc83814Sespie
4171cc83814Sespie /* This function gets called at the start of every line while inside a
4181cc83814Sespie menu. It checks to see if the line starts with "* ", and if so and
4191cc83814Sespie REMEMBER_REF is nonzero, remembers the node reference as type
4201cc83814Sespie REF_TYPE that this menu refers to. input_text_offset is at the \n
4211cc83814Sespie just before the menu line. If REMEMBER_REF is zero, REF_TYPE is unused. */
4221cc83814Sespie #define MENU_STARTER "* "
4231cc83814Sespie char *
glean_node_from_menu(int remember_ref,enum reftype ref_type)424*1076333cSespie glean_node_from_menu (int remember_ref, enum reftype ref_type)
4251cc83814Sespie {
4261cc83814Sespie int i, orig_offset = input_text_offset;
4271cc83814Sespie char *nodename;
4281cc83814Sespie char *line, *expanded_line;
4291cc83814Sespie char *old_input = input_text;
4303fb98d4aSespie int old_size = input_text_length;
4311cc83814Sespie
4321cc83814Sespie if (strncmp (&input_text[input_text_offset + 1],
4331cc83814Sespie MENU_STARTER,
4341cc83814Sespie strlen (MENU_STARTER)) != 0)
4351cc83814Sespie return NULL;
4361cc83814Sespie else
4371cc83814Sespie input_text_offset += strlen (MENU_STARTER) + 1;
4381cc83814Sespie
4391cc83814Sespie /* The menu entry might include macro calls, so we need to expand them. */
4401cc83814Sespie get_until ("\n", &line);
4411cc83814Sespie only_macro_expansion++; /* only expand macros in menu entries */
4421cc83814Sespie expanded_line = expansion (line, 0);
4431cc83814Sespie only_macro_expansion--;
4441cc83814Sespie free (line);
4451cc83814Sespie input_text = expanded_line;
4461cc83814Sespie input_text_offset = 0;
4471cc83814Sespie input_text_length = strlen (expanded_line);
4481cc83814Sespie
4491cc83814Sespie get_until_in_line (0, ":", &nodename);
4501cc83814Sespie if (curchar () == ':')
4511cc83814Sespie input_text_offset++;
4521cc83814Sespie
4531cc83814Sespie if (curchar () != ':')
4541cc83814Sespie {
4551cc83814Sespie free (nodename);
4561cc83814Sespie get_until_in_line (0, "\n", &nodename);
4571cc83814Sespie isolate_nodename (nodename);
4581cc83814Sespie }
4591cc83814Sespie
4601cc83814Sespie input_text = old_input;
4611cc83814Sespie input_text_offset = orig_offset;
4621cc83814Sespie input_text_length = old_size;
4631cc83814Sespie free (expanded_line);
4641cc83814Sespie fix_whitespace (nodename);
4651cc83814Sespie normalize_node_name (nodename);
4661cc83814Sespie i = strlen (nodename);
4671cc83814Sespie if (i && nodename[i - 1] == ':')
4681cc83814Sespie nodename[i - 1] = 0;
4691cc83814Sespie
4701cc83814Sespie if (remember_ref)
4711cc83814Sespie remember_node_reference (nodename, line_number, ref_type);
4721cc83814Sespie
4731cc83814Sespie return nodename;
4741cc83814Sespie }
4751cc83814Sespie
4761cc83814Sespie /* Set the name of the current output file. */
4771cc83814Sespie void
set_current_output_filename(const char * fname)478*1076333cSespie set_current_output_filename (const char *fname)
4791cc83814Sespie {
4801cc83814Sespie if (current_output_filename)
4811cc83814Sespie free (current_output_filename);
4821cc83814Sespie current_output_filename = xstrdup (fname);
4831cc83814Sespie }
4841cc83814Sespie
485*1076333cSespie
486*1076333cSespie /* Output the <a name="..."></a> constructs for NODE. We output both
487*1076333cSespie the new-style conversion and the old-style, if they are different.
488*1076333cSespie See comments at `add_escaped_anchor_name' in html.c. */
489*1076333cSespie
490*1076333cSespie static void
add_html_names(char * node)491*1076333cSespie add_html_names (char *node)
492*1076333cSespie {
493*1076333cSespie char *tem = expand_node_name (node);
494*1076333cSespie char *otem = xstrdup (tem);
495*1076333cSespie
496*1076333cSespie /* Determine if the old and new schemes come up with different names;
497*1076333cSespie only output the old scheme if that is so. We don't want to output
498*1076333cSespie the same name twice. */
499*1076333cSespie canon_white (otem);
500*1076333cSespie {
501*1076333cSespie char *optr = otem;
502*1076333cSespie int need_old = 0;
503*1076333cSespie
504*1076333cSespie for (; *optr; optr++)
505*1076333cSespie {
506*1076333cSespie if (!cr_or_whitespace (*optr) && !URL_SAFE_CHAR (*optr))
507*1076333cSespie {
508*1076333cSespie need_old = 1;
509*1076333cSespie break;
510*1076333cSespie }
511*1076333cSespie }
512*1076333cSespie
513*1076333cSespie if (need_old)
514*1076333cSespie {
515*1076333cSespie add_word ("<a name=\"");
516*1076333cSespie add_anchor_name (otem, -1); /* old anchor name conversion */
517*1076333cSespie add_word ("\"></a>\n");
518*1076333cSespie }
519*1076333cSespie free (otem);
520*1076333cSespie }
521*1076333cSespie
522*1076333cSespie /* Always output the new scheme. */
523*1076333cSespie canon_white (tem);
524*1076333cSespie add_word ("<a name=\"");
525*1076333cSespie add_anchor_name (tem, 0);
526*1076333cSespie add_word ("\"></a>\n");
527*1076333cSespie
528*1076333cSespie free (tem);
529*1076333cSespie }
530*1076333cSespie
531*1076333cSespie
5321cc83814Sespie /* The order is: nodename, nextnode, prevnode, upnode.
5331cc83814Sespie If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
5341cc83814Sespie You must follow a node command which has those fields defaulted
535*1076333cSespie with a sectioning command (e.g., @chapter) giving the "level" of that node.
5361cc83814Sespie It is an error not to do so.
5371cc83814Sespie The defaults come from the menu in this node's parent. */
5381cc83814Sespie void
cm_node(void)539*1076333cSespie cm_node (void)
5401cc83814Sespie {
5413fb98d4aSespie static long epilogue_len = 0L;
5421cc83814Sespie char *node, *prev, *next, *up;
5431cc83814Sespie int new_node_pos, defaulting, this_section;
5441cc83814Sespie int no_warn = 0;
5453fb98d4aSespie char *fname_for_this_node = NULL;
5463fb98d4aSespie char *tem;
5473fb98d4aSespie TAG_ENTRY *tag = NULL;
5481cc83814Sespie
5491cc83814Sespie if (strcmp (command, "nwnode") == 0)
5501cc83814Sespie no_warn = TAG_FLAG_NO_WARN;
5511cc83814Sespie
5521cc83814Sespie /* Get rid of unmatched brace arguments from previous commands. */
5531cc83814Sespie discard_braces ();
5541cc83814Sespie
5551cc83814Sespie /* There also might be insertions left lying around that haven't been
5561cc83814Sespie ended yet. Do that also. */
5571cc83814Sespie discard_insertions (1);
5581cc83814Sespie
5591cc83814Sespie if (!html && !already_outputting_pending_notes)
5601cc83814Sespie {
5611cc83814Sespie close_paragraph ();
5621cc83814Sespie output_pending_notes ();
5631cc83814Sespie }
5641cc83814Sespie
5651cc83814Sespie new_node_pos = output_position;
5661cc83814Sespie
5671cc83814Sespie if (macro_expansion_output_stream && !executing_string)
5681cc83814Sespie append_to_expansion_output (input_text_offset + 1);
5691cc83814Sespie
5701cc83814Sespie /* Do not collapse -- to -, etc., in node names. */
5711cc83814Sespie in_fixed_width_font++;
5721cc83814Sespie
5731cc83814Sespie /* While expanding the @node line, leave any non-macros
5741cc83814Sespie intact, so that the macro-expanded output includes them. */
5751cc83814Sespie only_macro_expansion++;
5761cc83814Sespie node = get_node_token (1);
5771cc83814Sespie only_macro_expansion--;
5781cc83814Sespie next = get_node_token (0);
5791cc83814Sespie prev = get_node_token (0);
5801cc83814Sespie up = get_node_token (0);
5811cc83814Sespie
5823fb98d4aSespie if (html && splitting
5833fb98d4aSespie /* If there is a Top node, it always goes into index.html. So
5843fb98d4aSespie don't start a new HTML file for Top. */
5853fb98d4aSespie && (top_node_seen || strcasecmp (node, "Top") != 0))
5863fb98d4aSespie {
5873fb98d4aSespie /* We test *node here so that @node without a valid name won't
5883fb98d4aSespie start a new file name with a bogus name such as ".html".
5893fb98d4aSespie This could happen if we run under "--force", where we cannot
5903fb98d4aSespie simply bail out. Continuing to use the same file sounds like
5913fb98d4aSespie the best we can do in such cases. */
5923fb98d4aSespie if (current_output_filename && output_stream && *node)
5933fb98d4aSespie {
5943fb98d4aSespie char *fname_for_prev_node;
5953fb98d4aSespie
5963fb98d4aSespie if (current_node)
5973fb98d4aSespie {
5983fb98d4aSespie /* NOTE: current_node at this point still holds the name
5993fb98d4aSespie of the previous node. */
6003fb98d4aSespie tem = expand_node_name (current_node);
6013fb98d4aSespie fname_for_prev_node = nodename_to_filename (tem);
6023fb98d4aSespie free (tem);
6033fb98d4aSespie }
6043fb98d4aSespie else /* could happen if their top node isn't named "Top" */
6053fb98d4aSespie fname_for_prev_node = filename_part (current_output_filename);
6063fb98d4aSespie tem = expand_node_name (node);
6073fb98d4aSespie fname_for_this_node = nodename_to_filename (tem);
6083fb98d4aSespie free (tem);
6093fb98d4aSespie /* Don't close current output file, if next output file is
6103fb98d4aSespie to have the same name. This may happen at top level, or
6113fb98d4aSespie if two nodes produce the same file name under --split. */
6123fb98d4aSespie if (FILENAME_CMP (fname_for_this_node, fname_for_prev_node) != 0)
6133fb98d4aSespie {
6143fb98d4aSespie long pos1 = 0;
6153fb98d4aSespie
6163fb98d4aSespie /* End the current split output file. */
6173fb98d4aSespie close_paragraph ();
6183fb98d4aSespie output_pending_notes ();
6193fb98d4aSespie start_paragraph ();
6203fb98d4aSespie /* Compute the length of the HTML file's epilogue. We
6213fb98d4aSespie cannot know the value until run time, due to the
6223fb98d4aSespie text/binary nuisance on DOS/Windows platforms, where
6233fb98d4aSespie 2 `\r' characters could be added to the epilogue when
6243fb98d4aSespie it is written in text mode. */
6253fb98d4aSespie if (epilogue_len == 0)
6263fb98d4aSespie {
6273fb98d4aSespie flush_output ();
6283fb98d4aSespie pos1 = ftell (output_stream);
6293fb98d4aSespie }
6303fb98d4aSespie add_word ("</body></html>\n");
6313fb98d4aSespie close_paragraph ();
6323fb98d4aSespie if (epilogue_len == 0)
6333fb98d4aSespie epilogue_len = ftell (output_stream) - pos1;
6343fb98d4aSespie fclose (output_stream);
6353fb98d4aSespie output_stream = NULL;
636*1076333cSespie output_position = 0;
6373fb98d4aSespie tag = find_node_by_fname (fname_for_this_node);
6383fb98d4aSespie }
6393fb98d4aSespie free (fname_for_prev_node);
6403fb98d4aSespie }
6413fb98d4aSespie }
6423fb98d4aSespie
6433fb98d4aSespie filling_enabled = indented_fill = 0;
6443fb98d4aSespie if (!html || (html && splitting))
6453fb98d4aSespie current_footnote_number = 1;
6463fb98d4aSespie
6471cc83814Sespie if (verbose_mode)
6481cc83814Sespie printf (_("Formatting node %s...\n"), node);
6491cc83814Sespie
6501cc83814Sespie if (macro_expansion_output_stream && !executing_string)
6511cc83814Sespie remember_itext (input_text, input_text_offset);
6521cc83814Sespie
653*1076333cSespie /* Reset the line number in each node for Info output, so that
654*1076333cSespie index entries will save the line numbers of parent node. */
655*1076333cSespie node_line_number = 0;
656*1076333cSespie
6571cc83814Sespie no_indent = 1;
6583fb98d4aSespie if (xml)
6593fb98d4aSespie {
6603fb98d4aSespie xml_begin_document (current_output_filename);
6613fb98d4aSespie xml_begin_node ();
6623fb98d4aSespie if (!docbook)
6633fb98d4aSespie {
6643fb98d4aSespie xml_insert_element (NODENAME, START);
6653fb98d4aSespie if (macro_expansion_output_stream && !executing_string)
6663fb98d4aSespie me_execute_string (node);
6673fb98d4aSespie else
6683fb98d4aSespie execute_string ("%s", node);
6693fb98d4aSespie xml_insert_element (NODENAME, END);
6703fb98d4aSespie }
6713fb98d4aSespie else
6723fb98d4aSespie xml_node_id = xml_id (node);
6733fb98d4aSespie }
6743fb98d4aSespie else if (!no_headers && !html)
6751cc83814Sespie {
676*1076333cSespie /* Emacs Info reader cannot grok indented escape sequence. */
677*1076333cSespie kill_self_indent (-1);
678*1076333cSespie
6791cc83814Sespie add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename);
6801cc83814Sespie
6811cc83814Sespie if (macro_expansion_output_stream && !executing_string)
6821cc83814Sespie me_execute_string (node);
6831cc83814Sespie else
6841cc83814Sespie execute_string ("%s", node);
6851cc83814Sespie filling_enabled = indented_fill = 0;
6861cc83814Sespie }
6871cc83814Sespie
6881cc83814Sespie /* Check for defaulting of this node's next, prev, and up fields. */
6891cc83814Sespie defaulting = (*next == 0 && *prev == 0 && *up == 0);
6901cc83814Sespie
691*1076333cSespie this_section = what_section (input_text + input_text_offset, NULL);
6921cc83814Sespie
6931cc83814Sespie /* If we are defaulting, then look at the immediately following
6941cc83814Sespie sectioning command (error if none) to determine the node's
6951cc83814Sespie level. Find the node that contains the menu mentioning this node
6961cc83814Sespie that is one level up (error if not found). That node is the "Up"
6971cc83814Sespie of this node. Default the "Next" and "Prev" from the menu. */
6981cc83814Sespie if (defaulting)
6991cc83814Sespie {
7001cc83814Sespie NODE_REF *last_ref = NULL;
7011cc83814Sespie NODE_REF *ref = node_references;
7021cc83814Sespie
7031cc83814Sespie if (this_section < 0 && !STREQ (node, "Top"))
7041cc83814Sespie {
7051cc83814Sespie char *polite_section_name = "top";
7061cc83814Sespie int i;
7071cc83814Sespie
7081cc83814Sespie for (i = 0; section_alist[i].name; i++)
7091cc83814Sespie if (section_alist[i].level == current_section + 1)
7101cc83814Sespie {
7111cc83814Sespie polite_section_name = section_alist[i].name;
7121cc83814Sespie break;
7131cc83814Sespie }
7141cc83814Sespie
7151cc83814Sespie line_error
716*1076333cSespie (_("Node `%s' requires a sectioning command (e.g., %c%s)"),
7171cc83814Sespie node, COMMAND_PREFIX, polite_section_name);
7181cc83814Sespie }
7191cc83814Sespie else
7201cc83814Sespie {
7211cc83814Sespie if (strcmp (node, "Top") == 0)
7221cc83814Sespie {
7231cc83814Sespie /* Default the NEXT pointer to be the first menu item in
7241cc83814Sespie this node, if there is a menu in this node. We have to
7251cc83814Sespie try very hard to find the menu, as it may be obscured
7261cc83814Sespie by execution_strings which are on the filestack. For
7271cc83814Sespie every member of the filestack which has a FILENAME
7281cc83814Sespie member which is identical to the current INPUT_FILENAME,
7291cc83814Sespie search forward from that offset. */
7301cc83814Sespie int saved_input_text_offset = input_text_offset;
7311cc83814Sespie int saved_input_text_length = input_text_length;
7321cc83814Sespie char *saved_input_text = input_text;
7331cc83814Sespie FSTACK *next_file = filestack;
7341cc83814Sespie
7351cc83814Sespie int orig_offset, orig_size;
7361cc83814Sespie
737*1076333cSespie int bye_offset = search_forward ("\n@bye", input_text_offset);
738*1076333cSespie
7391cc83814Sespie /* No matter what, make this file point back at `(dir)'. */
7401cc83814Sespie free (up);
7411cc83814Sespie up = xstrdup ("(dir)"); /* html fixxme */
7421cc83814Sespie
7431cc83814Sespie while (1)
7441cc83814Sespie {
7451cc83814Sespie orig_offset = input_text_offset;
7461cc83814Sespie orig_size =
7471cc83814Sespie search_forward (node_search_string, orig_offset);
7481cc83814Sespie
7491cc83814Sespie if (orig_size < 0)
7501cc83814Sespie orig_size = input_text_length;
7511cc83814Sespie
7521cc83814Sespie input_text_offset = search_forward ("\n@menu", orig_offset);
7531cc83814Sespie if (input_text_offset > -1
754*1076333cSespie && (bye_offset > -1 && input_text_offset < bye_offset)
7551cc83814Sespie && cr_or_whitespace (input_text[input_text_offset + 6]))
7561cc83814Sespie {
7571cc83814Sespie char *nodename_from_menu = NULL;
7581cc83814Sespie
7591cc83814Sespie input_text_offset =
7601cc83814Sespie search_forward ("\n* ", input_text_offset);
7611cc83814Sespie
7621cc83814Sespie if (input_text_offset != -1)
7631cc83814Sespie nodename_from_menu = glean_node_from_menu (0, 0);
7641cc83814Sespie
7651cc83814Sespie if (nodename_from_menu)
7661cc83814Sespie {
7671cc83814Sespie free (next);
7681cc83814Sespie next = nodename_from_menu;
7691cc83814Sespie break;
7701cc83814Sespie }
7711cc83814Sespie }
7721cc83814Sespie
7731cc83814Sespie /* We got here, so it hasn't been found yet. Try
7741cc83814Sespie the next file on the filestack if there is one. */
7751cc83814Sespie if (next_file
7761cc83814Sespie && FILENAME_CMP (next_file->filename, input_filename)
7771cc83814Sespie == 0)
7781cc83814Sespie {
7791cc83814Sespie input_text = next_file->text;
7801cc83814Sespie input_text_offset = next_file->offset;
7811cc83814Sespie input_text_length = next_file->size;
7821cc83814Sespie next_file = next_file->next;
7831cc83814Sespie }
7841cc83814Sespie else
7851cc83814Sespie { /* No more input files to check. */
7861cc83814Sespie break;
7871cc83814Sespie }
7881cc83814Sespie }
7891cc83814Sespie
7901cc83814Sespie input_text = saved_input_text;
7911cc83814Sespie input_text_offset = saved_input_text_offset;
7921cc83814Sespie input_text_length = saved_input_text_length;
7931cc83814Sespie }
7941cc83814Sespie }
7951cc83814Sespie
7961cc83814Sespie /* Fix the level of the menu references in the Top node, iff it
7971cc83814Sespie was declared with @top, and no subsequent reference was found. */
7981cc83814Sespie if (top_node_seen && !non_top_node_seen)
7991cc83814Sespie {
8001cc83814Sespie /* Then this is the first non-@top node seen. */
8011cc83814Sespie int level;
8021cc83814Sespie
8031cc83814Sespie level = set_top_section_level (this_section - 1);
8041cc83814Sespie non_top_node_seen = 1;
8051cc83814Sespie
8061cc83814Sespie while (ref)
8071cc83814Sespie {
8081cc83814Sespie if (ref->section == level)
8091cc83814Sespie ref->section = this_section - 1;
8101cc83814Sespie ref = ref->next;
8111cc83814Sespie }
8121cc83814Sespie
8131cc83814Sespie ref = node_references;
8141cc83814Sespie }
8151cc83814Sespie
8161cc83814Sespie while (ref)
8171cc83814Sespie {
8181cc83814Sespie if (ref->section == (this_section - 1)
8191cc83814Sespie && ref->type == menu_reference
8201cc83814Sespie && strcmp (ref->node, node) == 0)
8211cc83814Sespie {
8221cc83814Sespie char *containing_node = ref->containing_node;
8231cc83814Sespie
8241cc83814Sespie free (up);
8251cc83814Sespie up = xstrdup (containing_node);
8261cc83814Sespie
8271cc83814Sespie if (last_ref
8281cc83814Sespie && last_ref->type == menu_reference
8291cc83814Sespie && strcmp (last_ref->containing_node, containing_node) == 0)
8301cc83814Sespie {
8311cc83814Sespie free (next);
8321cc83814Sespie next = xstrdup (last_ref->node);
8331cc83814Sespie }
8341cc83814Sespie
8351cc83814Sespie while (ref->section == this_section - 1
8361cc83814Sespie && ref->next
8371cc83814Sespie && ref->next->type != menu_reference)
8381cc83814Sespie ref = ref->next;
8391cc83814Sespie
8401cc83814Sespie if (ref->next && ref->type == menu_reference
8411cc83814Sespie && strcmp (ref->next->containing_node, containing_node) == 0)
8421cc83814Sespie {
8431cc83814Sespie free (prev);
8441cc83814Sespie prev = xstrdup (ref->next->node);
8451cc83814Sespie }
8461cc83814Sespie else if (!ref->next
8471cc83814Sespie && strcasecmp (ref->containing_node, "Top") == 0)
8481cc83814Sespie {
8491cc83814Sespie free (prev);
8501cc83814Sespie prev = xstrdup (ref->containing_node);
8511cc83814Sespie }
8521cc83814Sespie break;
8531cc83814Sespie }
8541cc83814Sespie last_ref = ref;
8551cc83814Sespie ref = ref->next;
8561cc83814Sespie }
8571cc83814Sespie }
8581cc83814Sespie
8591cc83814Sespie /* Insert the correct args if we are expanding macros, and the node's
8601cc83814Sespie pointers weren't defaulted. */
8611cc83814Sespie if (macro_expansion_output_stream && !executing_string && !defaulting)
8621cc83814Sespie {
8631cc83814Sespie char *temp;
8641cc83814Sespie int op_orig = output_paragraph_offset;
8651cc83814Sespie int meta_pos_orig = meta_char_pos;
8661cc83814Sespie int extra = html ? strlen (node) : 0;
8671cc83814Sespie
8681cc83814Sespie temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up));
8691cc83814Sespie sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up);
8701cc83814Sespie me_execute_string (temp);
8711cc83814Sespie free (temp);
8721cc83814Sespie
8731cc83814Sespie output_paragraph_offset = op_orig;
8741cc83814Sespie meta_char_pos = meta_pos_orig;
8751cc83814Sespie }
8761cc83814Sespie
8771cc83814Sespie if (!*node)
8781cc83814Sespie {
8791cc83814Sespie line_error (_("No node name specified for `%c%s' command"),
8801cc83814Sespie COMMAND_PREFIX, command);
8811cc83814Sespie free (node);
8821cc83814Sespie free (next); next = NULL;
8831cc83814Sespie free (prev); prev= NULL;
8841cc83814Sespie free (up); up = NULL;
8851cc83814Sespie node_number++; /* else it doesn't get bumped */
8861cc83814Sespie }
8871cc83814Sespie else
8881cc83814Sespie {
8891cc83814Sespie if (!*next) { free (next); next = NULL; }
8901cc83814Sespie if (!*prev) { free (prev); prev = NULL; }
8911cc83814Sespie if (!*up) { free (up); up = NULL; }
8923fb98d4aSespie remember_node (node, prev, next, up, new_node_pos, line_number,
8933fb98d4aSespie fname_for_this_node, no_warn);
8941cc83814Sespie outstanding_node = 1;
8951cc83814Sespie }
8961cc83814Sespie
8971cc83814Sespie if (html)
8981cc83814Sespie {
8993fb98d4aSespie if (splitting && *node && output_stream == NULL)
9003fb98d4aSespie {
9013fb98d4aSespie char *dirname;
9023fb98d4aSespie char filename[PATH_MAX];
9031cc83814Sespie
9043fb98d4aSespie dirname = pathname_part (current_output_filename);
9053fb98d4aSespie strcpy (filename, dirname);
9063fb98d4aSespie strcat (filename, fname_for_this_node);
9073fb98d4aSespie free (dirname);
9081cc83814Sespie
9093fb98d4aSespie /* See if the node name converted to a file name clashes
9103fb98d4aSespie with other nodes or anchors. If it clashes with an
9113fb98d4aSespie anchor, we complain and nuke that anchor's file. */
9123fb98d4aSespie if (!tag)
9133fb98d4aSespie {
9141cc83814Sespie output_stream = fopen (filename, "w");
9153fb98d4aSespie html_output_head_p = 0; /* so that we generate HTML preamble */
9163fb98d4aSespie html_output_head ();
9173fb98d4aSespie }
9183fb98d4aSespie else if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
9193fb98d4aSespie {
9203fb98d4aSespie line_error (_("Anchor `%s' and node `%s' map to the same file name"),
9213fb98d4aSespie tag->node, node);
9223fb98d4aSespie file_line_error (tag->filename, tag->line_no,
9233fb98d4aSespie _("This @anchor command ignored; references to it will not work"));
9243fb98d4aSespie file_line_error (tag->filename, tag->line_no,
9253fb98d4aSespie _("Rename this anchor or use the `--no-split' option"));
9263fb98d4aSespie /* Nuke the file name recorded in anchor's tag.
9273fb98d4aSespie Since we are about to nuke the file itself, we
9283fb98d4aSespie don't want find_node_by_fname to consider this
9293fb98d4aSespie anchor anymore. */
9303fb98d4aSespie free (tag->html_fname);
9313fb98d4aSespie tag->html_fname = NULL;
9323fb98d4aSespie output_stream = fopen (filename, "w");
9333fb98d4aSespie html_output_head_p = 0; /* so that we generate HTML preamble */
9343fb98d4aSespie html_output_head ();
9353fb98d4aSespie }
9363fb98d4aSespie else
9373fb98d4aSespie {
9383fb98d4aSespie /* This node's file name clashes with another node.
9393fb98d4aSespie We put them both on the same file. */
9403fb98d4aSespie output_stream = fopen (filename, "r+");
9413fb98d4aSespie if (output_stream)
9423fb98d4aSespie {
9433fb98d4aSespie static char html_end[] = "</body></html>\n";
9443fb98d4aSespie char end_line[sizeof(html_end)];
9453fb98d4aSespie int fpos = fseek (output_stream, -epilogue_len,
9463fb98d4aSespie SEEK_END);
9473fb98d4aSespie
9483fb98d4aSespie if (fpos < 0
9493fb98d4aSespie || fgets (end_line, sizeof (html_end),
9503fb98d4aSespie output_stream) == NULL
9513fb98d4aSespie /* Paranoia: did someone change the way HTML
9523fb98d4aSespie files are finished up? */
9533fb98d4aSespie || strcasecmp (end_line, html_end) != 0)
9543fb98d4aSespie {
9553fb98d4aSespie line_error (_("Unexpected string at end of split-HTML file `%s'"),
9563fb98d4aSespie fname_for_this_node);
9573fb98d4aSespie fclose (output_stream);
9583fb98d4aSespie xexit (1);
9593fb98d4aSespie }
9603fb98d4aSespie fseek (output_stream, -epilogue_len, SEEK_END);
9613fb98d4aSespie }
9623fb98d4aSespie }
9631cc83814Sespie if (output_stream == NULL)
9641cc83814Sespie {
9651cc83814Sespie fs_error (filename);
9661cc83814Sespie xexit (1);
9671cc83814Sespie }
9681cc83814Sespie set_current_output_filename (filename);
9691cc83814Sespie }
9701cc83814Sespie
9711cc83814Sespie if (!splitting && no_headers)
972*1076333cSespie { /* cross refs need a name="#anchor" even if not writing headers */
973*1076333cSespie add_html_names (node);
9741cc83814Sespie }
9751cc83814Sespie
9761cc83814Sespie if (splitting || !no_headers)
977*1076333cSespie { /* Navigation bar. */
978*1076333cSespie add_html_block_elt ("<div class=\"node\">\n");
979*1076333cSespie /* The <p> avoids the links area running on with old Lynxen. */
9801cc83814Sespie add_word_args ("<p>%s\n", splitting ? "" : "<hr>");
981*1076333cSespie
982*1076333cSespie /* In the split HTML case, the filename is wrong for the
983*1076333cSespie old-style converted names, but we'll add them anyway, for
984*1076333cSespie consistency. (And we need them in the normal (not
985*1076333cSespie no_headers) nonsplit case.) */
986*1076333cSespie add_html_names (node);
9871cc83814Sespie
9881cc83814Sespie if (next)
9891cc83814Sespie {
9903fb98d4aSespie tem = expansion (next, 0);
991*1076333cSespie add_word ((char *) _("Next:"));
992*1076333cSespie add_word (" ");
993*1076333cSespie
994*1076333cSespie add_word ("<a rel=\"next\" accesskey=\"n\" href=\"");
9951cc83814Sespie add_anchor_name (tem, 1);
996*1076333cSespie tem = escape_string (tem);
9971cc83814Sespie add_word_args ("\">%s</a>", tem);
998*1076333cSespie
9991cc83814Sespie free (tem);
1000*1076333cSespie
1001*1076333cSespie if (prev || up)
1002*1076333cSespie add_word (",\n");
10031cc83814Sespie }
10041cc83814Sespie if (prev)
10051cc83814Sespie {
10063fb98d4aSespie tem = expansion (prev, 0);
1007*1076333cSespie add_word ((char *) _("Previous:"));
1008*1076333cSespie add_word (" ");
1009*1076333cSespie add_word ("<a rel=\"previous\" accesskey=\"p\" href=\"");
10101cc83814Sespie add_anchor_name (tem, 1);
1011*1076333cSespie tem = escape_string (tem);
10121cc83814Sespie add_word_args ("\">%s</a>", tem);
10131cc83814Sespie free (tem);
1014*1076333cSespie
1015*1076333cSespie if (up)
1016*1076333cSespie add_word (",\n");
10171cc83814Sespie }
10181cc83814Sespie if (up)
10191cc83814Sespie {
10203fb98d4aSespie tem = expansion (up, 0);
1021*1076333cSespie add_word ((char *) _("Up:"));
1022*1076333cSespie add_word (" ");
1023*1076333cSespie add_word ("<a rel=\"up\" accesskey=\"u\" href=\"");
10241cc83814Sespie add_anchor_name (tem, 1);
1025*1076333cSespie tem = escape_string (tem);
10261cc83814Sespie add_word_args ("\">%s</a>", tem);
10271cc83814Sespie free (tem);
10281cc83814Sespie }
10291cc83814Sespie /* html fixxme: we want a `top' or `contents' link here. */
10301cc83814Sespie
1031*1076333cSespie add_word_args ("\n%s\n", splitting ? "<hr>" : "");
1032*1076333cSespie add_word ("</div>\n");
10331cc83814Sespie }
10341cc83814Sespie }
10353fb98d4aSespie else if (docbook)
10363fb98d4aSespie ;
10373fb98d4aSespie else if (xml)
10383fb98d4aSespie {
10393fb98d4aSespie if (next)
10403fb98d4aSespie {
10413fb98d4aSespie xml_insert_element (NODENEXT, START);
10423fb98d4aSespie execute_string ("%s", next);
10433fb98d4aSespie xml_insert_element (NODENEXT, END);
10443fb98d4aSespie }
10453fb98d4aSespie if (prev)
10463fb98d4aSespie {
10473fb98d4aSespie xml_insert_element (NODEPREV, START);
10483fb98d4aSespie execute_string ("%s", prev);
10493fb98d4aSespie xml_insert_element (NODEPREV, END);
10503fb98d4aSespie }
10513fb98d4aSespie if (up)
10523fb98d4aSespie {
10533fb98d4aSespie xml_insert_element (NODEUP, START);
10543fb98d4aSespie execute_string ("%s", up);
10553fb98d4aSespie xml_insert_element (NODEUP, END);
10563fb98d4aSespie }
10573fb98d4aSespie }
10581cc83814Sespie else if (!no_headers)
10591cc83814Sespie {
10601cc83814Sespie if (macro_expansion_output_stream)
10611cc83814Sespie me_inhibit_expansion++;
10621cc83814Sespie
10631cc83814Sespie /* These strings are not translatable. */
10641cc83814Sespie if (next)
10651cc83814Sespie {
10661cc83814Sespie execute_string (", Next: %s", next);
10671cc83814Sespie filling_enabled = indented_fill = 0;
10681cc83814Sespie }
10691cc83814Sespie if (prev)
10701cc83814Sespie {
10711cc83814Sespie execute_string (", Prev: %s", prev);
10721cc83814Sespie filling_enabled = indented_fill = 0;
10731cc83814Sespie }
10741cc83814Sespie if (up)
10751cc83814Sespie {
10761cc83814Sespie execute_string (", Up: %s", up);
10771cc83814Sespie filling_enabled = indented_fill = 0;
10781cc83814Sespie }
10791cc83814Sespie if (macro_expansion_output_stream)
10801cc83814Sespie me_inhibit_expansion--;
10811cc83814Sespie }
10821cc83814Sespie
10831cc83814Sespie close_paragraph ();
10841cc83814Sespie no_indent = 0;
10851cc83814Sespie
10861cc83814Sespie /* Change the section only if there was a sectioning command. */
10871cc83814Sespie if (this_section >= 0)
10881cc83814Sespie current_section = this_section;
10891cc83814Sespie
10901cc83814Sespie if (current_node && STREQ (current_node, "Top"))
10911cc83814Sespie top_node_seen = 1;
10921cc83814Sespie
10931cc83814Sespie filling_enabled = 1;
10941cc83814Sespie in_fixed_width_font--;
10951cc83814Sespie }
10961cc83814Sespie
10971cc83814Sespie /* Cross-reference target at an arbitrary spot. */
10981cc83814Sespie void
cm_anchor(int arg)1099*1076333cSespie cm_anchor (int arg)
11001cc83814Sespie {
11011cc83814Sespie char *anchor;
11023fb98d4aSespie char *fname_for_anchor = NULL;
11031cc83814Sespie
11041cc83814Sespie if (arg == END)
11051cc83814Sespie return;
11061cc83814Sespie
11071cc83814Sespie /* Parse the anchor text. */
11081cc83814Sespie anchor = get_xref_token (1);
11091cc83814Sespie
1110*1076333cSespie /* Force all versions of "top" to be "Top". */
1111*1076333cSespie normalize_node_name (anchor);
1112*1076333cSespie
11131cc83814Sespie /* In HTML mode, need to actually produce some output. */
11141cc83814Sespie if (html)
11151cc83814Sespie {
11161cc83814Sespie /* If this anchor is at the beginning of a new paragraph, make
11171cc83814Sespie sure a new paragraph is indeed started. */
11181cc83814Sespie if (!paragraph_is_open)
11191cc83814Sespie {
11203fb98d4aSespie if (!executing_string && html)
11213fb98d4aSespie html_output_head ();
11221cc83814Sespie start_paragraph ();
11231cc83814Sespie if (!in_fixed_width_font || in_menu || in_detailmenu)
11241cc83814Sespie {
11251cc83814Sespie insert_string ("<p>");
11261cc83814Sespie in_paragraph = 1;
11271cc83814Sespie }
11281cc83814Sespie }
11291cc83814Sespie add_word ("<a name=\"");
11301cc83814Sespie add_anchor_name (anchor, 0);
11311cc83814Sespie add_word ("\"></a>");
11323fb98d4aSespie if (splitting)
11333fb98d4aSespie {
11343fb98d4aSespie /* If we are splitting, cm_xref will produce a reference to
11353fb98d4aSespie a file whose name is derived from the anchor name. So we
11363fb98d4aSespie must create a file when we see an @anchor, otherwise
11373fb98d4aSespie xref's to anchors won't work. The file we create simply
11383fb98d4aSespie redirects to the file of this anchor's node. */
11393fb98d4aSespie TAG_ENTRY *tag;
11401cc83814Sespie
11413fb98d4aSespie fname_for_anchor = nodename_to_filename (anchor);
11423fb98d4aSespie /* See if the anchor name converted to a file name clashes
11433fb98d4aSespie with other anchors or nodes. */
11443fb98d4aSespie tag = find_node_by_fname (fname_for_anchor);
11453fb98d4aSespie if (tag)
11463fb98d4aSespie {
11473fb98d4aSespie if ((tag->flags & TAG_FLAG_ANCHOR) != 0)
11483fb98d4aSespie line_error (_("Anchors `%s' and `%s' map to the same file name"),
11493fb98d4aSespie anchor, tag->node);
11503fb98d4aSespie else
11513fb98d4aSespie line_error (_("Anchor `%s' and node `%s' map to the same file name"),
11523fb98d4aSespie anchor, tag->node);
11533fb98d4aSespie line_error (_("@anchor command ignored; references to it will not work"));
11543fb98d4aSespie line_error (_("Rename this anchor or use the `--no-split' option"));
11553fb98d4aSespie free (fname_for_anchor);
11563fb98d4aSespie /* We will not be creating a file for this anchor, so
11573fb98d4aSespie set its name to NULL, so that remember_node stores a
11583fb98d4aSespie NULL and find_node_by_fname won't consider this
11593fb98d4aSespie anchor for clashes. */
11603fb98d4aSespie fname_for_anchor = NULL;
11613fb98d4aSespie }
11623fb98d4aSespie else
11633fb98d4aSespie {
11643fb98d4aSespie char *dirname, *p;
11653fb98d4aSespie char filename[PATH_MAX];
11663fb98d4aSespie FILE *anchor_stream;
11673fb98d4aSespie
11683fb98d4aSespie dirname = pathname_part (current_output_filename);
11693fb98d4aSespie strcpy (filename, dirname);
11703fb98d4aSespie strcat (filename, fname_for_anchor);
11713fb98d4aSespie free (dirname);
11723fb98d4aSespie
11733fb98d4aSespie anchor_stream = fopen (filename, "w");
11743fb98d4aSespie if (anchor_stream == NULL)
11753fb98d4aSespie {
11763fb98d4aSespie fs_error (filename);
11773fb98d4aSespie xexit (1);
11783fb98d4aSespie }
11793fb98d4aSespie /* The HTML magic below will cause the browser to
11803fb98d4aSespie immediately go to the anchor's node's file. Lynx
11813fb98d4aSespie seems not to support this redirection, but it looks
11823fb98d4aSespie like a bug in Lynx, and they can work around it by
11833fb98d4aSespie clicking on the link once more. */
11843fb98d4aSespie fputs ("<meta http-equiv=\"refresh\" content=\"0; url=",
11853fb98d4aSespie anchor_stream);
11863fb98d4aSespie /* Make the indirect link point to the current node's
11873fb98d4aSespie file and anchor's "<a name" label. If we don't have
11883fb98d4aSespie a valid node name, refer to the current output file
11893fb98d4aSespie instead. */
11903fb98d4aSespie if (current_node && *current_node)
11913fb98d4aSespie {
11923fb98d4aSespie char *fn, *tem;
11933fb98d4aSespie
11943fb98d4aSespie tem = expand_node_name (current_node);
11953fb98d4aSespie fn = nodename_to_filename (tem);
11963fb98d4aSespie free (tem);
11973fb98d4aSespie fputs (fn, anchor_stream);
11983fb98d4aSespie free (fn);
11993fb98d4aSespie }
12003fb98d4aSespie else
12013fb98d4aSespie {
12023fb98d4aSespie char *base = filename_part (current_output_filename);
12033fb98d4aSespie
12043fb98d4aSespie fputs (base, anchor_stream);
12053fb98d4aSespie free (base);
12063fb98d4aSespie }
12073fb98d4aSespie fputs ("#", anchor_stream);
12083fb98d4aSespie for (p = anchor; *p; p++)
12093fb98d4aSespie {
12103fb98d4aSespie if (*p == '&')
12113fb98d4aSespie fputs ("&", anchor_stream);
12123fb98d4aSespie else if (!URL_SAFE_CHAR (*p))
12133fb98d4aSespie fprintf (anchor_stream, "%%%x", (unsigned char) *p);
12143fb98d4aSespie else
12153fb98d4aSespie fputc (*p, anchor_stream);
12163fb98d4aSespie }
12173fb98d4aSespie fputs ("\">\n", anchor_stream);
12183fb98d4aSespie fclose (anchor_stream);
12193fb98d4aSespie }
12203fb98d4aSespie }
12213fb98d4aSespie }
12223fb98d4aSespie else if (xml)
12233fb98d4aSespie {
12243fb98d4aSespie xml_insert_element_with_attribute (ANCHOR, START, "name=\"%s\"", anchor);
12253fb98d4aSespie xml_insert_element (ANCHOR, END);
12263fb98d4aSespie }
12271cc83814Sespie /* Save it in the tag table. */
12283fb98d4aSespie remember_node (anchor, NULL, NULL, NULL,
12293fb98d4aSespie output_position + output_paragraph_offset,
12303fb98d4aSespie line_number, fname_for_anchor, TAG_FLAG_ANCHOR);
12311cc83814Sespie }
12321cc83814Sespie
12331cc83814Sespie /* Find NODE in REF_LIST. */
12341cc83814Sespie static NODE_REF *
find_node_reference(char * node,NODE_REF * ref_list)1235*1076333cSespie find_node_reference (char *node, NODE_REF *ref_list)
12361cc83814Sespie {
12371cc83814Sespie NODE_REF *orig_ref_list = ref_list;
12381cc83814Sespie char *expanded_node;
12391cc83814Sespie
12401cc83814Sespie while (ref_list)
12411cc83814Sespie {
12421cc83814Sespie if (strcmp (node, ref_list->node) == 0)
12431cc83814Sespie break;
12441cc83814Sespie ref_list = ref_list->next;
12451cc83814Sespie }
12461cc83814Sespie
12471cc83814Sespie if (ref_list || !expensive_validation)
12481cc83814Sespie return ref_list;
12491cc83814Sespie
12501cc83814Sespie /* Maybe NODE is not expanded yet. This may be SLOW. */
12511cc83814Sespie expanded_node = expand_node_name (node);
12521cc83814Sespie for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next)
12531cc83814Sespie {
12541cc83814Sespie if (STREQ (expanded_node, ref_list->node))
12551cc83814Sespie break;
12561cc83814Sespie if (strchr (ref_list->node, COMMAND_PREFIX))
12571cc83814Sespie {
12581cc83814Sespie char *expanded_ref = expand_node_name (ref_list->node);
12591cc83814Sespie
12601cc83814Sespie if (STREQ (expanded_node, expanded_ref))
12611cc83814Sespie {
12621cc83814Sespie free (expanded_ref);
12631cc83814Sespie break;
12641cc83814Sespie }
12651cc83814Sespie free (expanded_ref);
12661cc83814Sespie }
12671cc83814Sespie }
12681cc83814Sespie free (expanded_node);
12691cc83814Sespie return ref_list;
12701cc83814Sespie }
12711cc83814Sespie
12721cc83814Sespie void
free_node_references(void)1273*1076333cSespie free_node_references (void)
12741cc83814Sespie {
12751cc83814Sespie NODE_REF *list, *temp;
12761cc83814Sespie
12771cc83814Sespie list = node_references;
12781cc83814Sespie
12791cc83814Sespie while (list)
12801cc83814Sespie {
12811cc83814Sespie temp = list;
12821cc83814Sespie free (list->node);
12831cc83814Sespie free (list->containing_node);
12841cc83814Sespie list = list->next;
12851cc83814Sespie free (temp);
12861cc83814Sespie }
12871cc83814Sespie node_references = NULL;
12881cc83814Sespie }
12891cc83814Sespie
12901cc83814Sespie void
free_node_node_references(void)1291*1076333cSespie free_node_node_references (void)
12921cc83814Sespie {
12931cc83814Sespie NODE_REF *list, *temp;
12941cc83814Sespie
12951cc83814Sespie list = node_references;
12961cc83814Sespie
12971cc83814Sespie while (list)
12981cc83814Sespie {
12991cc83814Sespie temp = list;
13001cc83814Sespie free (list->node);
13011cc83814Sespie list = list->next;
13021cc83814Sespie free (temp);
13031cc83814Sespie }
13041cc83814Sespie node_node_references = NULL;
13051cc83814Sespie }
13061cc83814Sespie
13071cc83814Sespie /* Return the number assigned to a named node in either the tag_table
13081cc83814Sespie or node_references list or zero if no number has been assigned. */
13091cc83814Sespie int
number_of_node(char * node)1310*1076333cSespie number_of_node (char *node)
13111cc83814Sespie {
13121cc83814Sespie NODE_REF *temp_ref;
13131cc83814Sespie TAG_ENTRY *temp_node = find_node (node);
13141cc83814Sespie
13151cc83814Sespie if (temp_node)
13161cc83814Sespie return temp_node->number;
13171cc83814Sespie else if ((temp_ref = find_node_reference (node, node_references)))
13181cc83814Sespie return temp_ref->number;
13191cc83814Sespie else if ((temp_ref = find_node_reference (node, node_node_references)))
13201cc83814Sespie return temp_ref->number;
13211cc83814Sespie else
13221cc83814Sespie return 0;
13231cc83814Sespie }
13241cc83814Sespie
13251cc83814Sespie /* validation */
13261cc83814Sespie
13271cc83814Sespie /* Return 1 if TAG (at LINE) correctly validated, or 0 if not.
13281cc83814Sespie LABEL is the (translated) description of the type of reference --
13291cc83814Sespie Menu, Cross, Next, etc. */
13301cc83814Sespie
13311cc83814Sespie static int
validate(char * tag,int line,const char * label)1332*1076333cSespie validate (char *tag, int line, const char *label)
13331cc83814Sespie {
13341cc83814Sespie TAG_ENTRY *result;
13351cc83814Sespie
13361cc83814Sespie /* If there isn't a tag to verify, or if the tag is in another file,
13371cc83814Sespie then it must be okay. */
13381cc83814Sespie if (!tag || !*tag || *tag == '(')
13391cc83814Sespie return 1;
13401cc83814Sespie
13411cc83814Sespie /* Otherwise, the tag must exist. */
13421cc83814Sespie result = find_node (tag);
13431cc83814Sespie
13441cc83814Sespie if (!result)
13451cc83814Sespie {
13461cc83814Sespie line_number = line;
1347*1076333cSespie line_error (_("%s reference to nonexistent node `%s' (perhaps incorrect sectioning?)"), label, tag);
13481cc83814Sespie return 0;
13491cc83814Sespie }
13501cc83814Sespie result->touched++;
13511cc83814Sespie return 1;
13521cc83814Sespie }
13531cc83814Sespie
13541cc83814Sespie /* The strings here are followed in the message by `reference to...' in
13551cc83814Sespie the `validate' routine. They are only used in messages, thus are
13561cc83814Sespie translated. */
1357*1076333cSespie static const char *
reftype_type_string(enum reftype type)1358*1076333cSespie reftype_type_string (enum reftype type)
13591cc83814Sespie {
13601cc83814Sespie switch (type)
13611cc83814Sespie {
13621cc83814Sespie case menu_reference:
13631cc83814Sespie return _("Menu");
13641cc83814Sespie case followed_reference:
13651cc83814Sespie return _("Cross");
13661cc83814Sespie default:
13671cc83814Sespie return "Internal-bad-reference-type";
13681cc83814Sespie }
13691cc83814Sespie }
13701cc83814Sespie
13711cc83814Sespie static void
validate_other_references(NODE_REF * ref_list)1372*1076333cSespie validate_other_references (NODE_REF *ref_list)
13731cc83814Sespie {
13741cc83814Sespie char *old_input_filename = input_filename;
13751cc83814Sespie
13761cc83814Sespie while (ref_list)
13771cc83814Sespie {
13781cc83814Sespie input_filename = ref_list->filename;
13791cc83814Sespie validate (ref_list->node, ref_list->line_no,
13801cc83814Sespie reftype_type_string (ref_list->type));
13811cc83814Sespie ref_list = ref_list->next;
13821cc83814Sespie }
13831cc83814Sespie input_filename = old_input_filename;
13841cc83814Sespie }
13851cc83814Sespie
13861cc83814Sespie /* Validation of an info file.
13871cc83814Sespie Scan through the list of tag entries touching the Prev, Next, and Up
13881cc83814Sespie elements of each. It is an error not to be able to touch one of them,
13891cc83814Sespie except in the case of external node references, such as "(DIR)".
13901cc83814Sespie
13911cc83814Sespie If the Prev is different from the Up,
13921cc83814Sespie then the Prev node must have a Next pointing at this node.
13931cc83814Sespie
13941cc83814Sespie Every node except Top must have an Up.
13951cc83814Sespie The Up node must contain some sort of reference, other than a Next,
13961cc83814Sespie to this node.
13971cc83814Sespie
13981cc83814Sespie If the Next is different from the Next of the Up,
13991cc83814Sespie then the Next node must have a Prev pointing at this node. */
14001cc83814Sespie void
validate_file(TAG_ENTRY * tag_table)1401*1076333cSespie validate_file (TAG_ENTRY *tag_table)
14021cc83814Sespie {
14031cc83814Sespie char *old_input_filename = input_filename;
14041cc83814Sespie TAG_ENTRY *tags = tag_table;
14051cc83814Sespie
14061cc83814Sespie while (tags)
14071cc83814Sespie {
14081cc83814Sespie TAG_ENTRY *temp_tag;
14091cc83814Sespie char *tem1, *tem2;
14101cc83814Sespie
14111cc83814Sespie input_filename = tags->filename;
14121cc83814Sespie line_number = tags->line_no;
14131cc83814Sespie
14141cc83814Sespie /* If this is a "no warn" node, don't validate it in any way. */
14151cc83814Sespie if (tags->flags & TAG_FLAG_NO_WARN)
14161cc83814Sespie {
14171cc83814Sespie tags = tags->next_ent;
14181cc83814Sespie continue;
14191cc83814Sespie }
14201cc83814Sespie
14211cc83814Sespie /* If this node has a Next, then make sure that the Next exists. */
14221cc83814Sespie if (tags->next)
14231cc83814Sespie {
14241cc83814Sespie validate (tags->next, tags->line_no, _("Next"));
14251cc83814Sespie
14261cc83814Sespie /* If the Next node exists, and there is no Up, then make sure
14271cc83814Sespie that the Prev of the Next points back. But do nothing if
14281cc83814Sespie we aren't supposed to issue warnings about this node. */
14291cc83814Sespie temp_tag = find_node (tags->next);
14301cc83814Sespie if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN))
14311cc83814Sespie {
14321cc83814Sespie char *prev = temp_tag->prev;
14331cc83814Sespie int you_lose = !prev || !STREQ (prev, tags->node);
14341cc83814Sespie
14351cc83814Sespie if (you_lose && expensive_validation)
14361cc83814Sespie {
14371cc83814Sespie tem1 = expand_node_name (prev);
14381cc83814Sespie tem2 = expand_node_name (tags->node);
14391cc83814Sespie
1440*1076333cSespie if (tem1 && tem2 && STREQ (tem1, tem2))
14411cc83814Sespie you_lose = 0;
14421cc83814Sespie free (tem1);
14431cc83814Sespie free (tem2);
14441cc83814Sespie }
14451cc83814Sespie if (you_lose)
14461cc83814Sespie {
1447*1076333cSespie line_error (_("Next field of node `%s' not pointed to (perhaps incorrect sectioning?)"),
14481cc83814Sespie tags->node);
14493fb98d4aSespie file_line_error (temp_tag->filename, temp_tag->line_no,
14503fb98d4aSespie _("This node (%s) has the bad Prev"),
14511cc83814Sespie temp_tag->node);
14521cc83814Sespie temp_tag->flags |= TAG_FLAG_PREV_ERROR;
14531cc83814Sespie }
14541cc83814Sespie }
14551cc83814Sespie }
14561cc83814Sespie
14571cc83814Sespie /* Validate the Prev field if there is one, and we haven't already
14581cc83814Sespie complained about it in some way. You don't have to have a Prev
14591cc83814Sespie field at this stage. */
14601cc83814Sespie if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev)
14611cc83814Sespie {
14621cc83814Sespie int valid_p = validate (tags->prev, tags->line_no, _("Prev"));
14631cc83814Sespie
14641cc83814Sespie if (!valid_p)
14651cc83814Sespie tags->flags |= TAG_FLAG_PREV_ERROR;
14661cc83814Sespie else
14671cc83814Sespie { /* If the Prev field is not the same as the Up field,
14681cc83814Sespie then the node pointed to by the Prev field must have
14691cc83814Sespie a Next field which points to this node. */
14701cc83814Sespie int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up);
14711cc83814Sespie
14721cc83814Sespie if (!prev_equals_up && expensive_validation)
14731cc83814Sespie {
14741cc83814Sespie tem1 = expand_node_name (tags->prev);
14751cc83814Sespie tem2 = expand_node_name (tags->up);
14761cc83814Sespie prev_equals_up = STREQ (tem1, tem2);
14771cc83814Sespie free (tem1);
14781cc83814Sespie free (tem2);
14791cc83814Sespie }
14801cc83814Sespie if (!prev_equals_up)
14811cc83814Sespie {
14821cc83814Sespie temp_tag = find_node (tags->prev);
14831cc83814Sespie
14841cc83814Sespie /* If we aren't supposed to issue warnings about the
14851cc83814Sespie target node, do nothing. */
14861cc83814Sespie if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN))
14871cc83814Sespie /* Do nothing. */ ;
14881cc83814Sespie else
14891cc83814Sespie {
14901cc83814Sespie int you_lose = !temp_tag->next
14911cc83814Sespie || !STREQ (temp_tag->next, tags->node);
14921cc83814Sespie
14931cc83814Sespie if (temp_tag->next && you_lose && expensive_validation)
14941cc83814Sespie {
14951cc83814Sespie tem1 = expand_node_name (temp_tag->next);
14961cc83814Sespie tem2 = expand_node_name (tags->node);
14971cc83814Sespie if (STREQ (tem1, tem2))
14981cc83814Sespie you_lose = 0;
14991cc83814Sespie free (tem1);
15001cc83814Sespie free (tem2);
15011cc83814Sespie }
15021cc83814Sespie if (you_lose)
15031cc83814Sespie {
15041cc83814Sespie line_error
15051cc83814Sespie (_("Prev field of node `%s' not pointed to"),
15061cc83814Sespie tags->node);
15073fb98d4aSespie file_line_error (temp_tag->filename,
15083fb98d4aSespie temp_tag->line_no,
15093fb98d4aSespie _("This node (%s) has the bad Next"),
15101cc83814Sespie temp_tag->node);
15111cc83814Sespie temp_tag->flags |= TAG_FLAG_NEXT_ERROR;
15121cc83814Sespie }
15131cc83814Sespie }
15141cc83814Sespie }
15151cc83814Sespie }
15161cc83814Sespie }
15171cc83814Sespie
15181cc83814Sespie if (!tags->up
15191cc83814Sespie && !(tags->flags & TAG_FLAG_ANCHOR)
15201cc83814Sespie && strcasecmp (tags->node, "Top") != 0)
1521*1076333cSespie line_error (_("`%s' has no Up field (perhaps incorrect sectioning?)"), tags->node);
15221cc83814Sespie else if (tags->up)
15231cc83814Sespie {
15241cc83814Sespie int valid_p = validate (tags->up, tags->line_no, _("Up"));
15251cc83814Sespie
15261cc83814Sespie /* If node X has Up: Y, then warn if Y fails to have a menu item
15271cc83814Sespie or note pointing at X, if Y isn't of the form "(Y)". */
15281cc83814Sespie if (valid_p && *tags->up != '(')
15291cc83814Sespie {
15301cc83814Sespie NODE_REF *nref;
15311cc83814Sespie NODE_REF *tref = NULL;
15321cc83814Sespie NODE_REF *list = node_references;
15331cc83814Sespie
15341cc83814Sespie for (;;)
15351cc83814Sespie {
15361cc83814Sespie nref = find_node_reference (tags->node, list);
15371cc83814Sespie if (!nref)
15381cc83814Sespie break;
15391cc83814Sespie
15401cc83814Sespie if (strcmp (nref->containing_node, tags->up) == 0)
15411cc83814Sespie {
15421cc83814Sespie if (nref->type != menu_reference)
15431cc83814Sespie {
15441cc83814Sespie tref = nref;
15451cc83814Sespie list = nref->next;
15461cc83814Sespie }
15471cc83814Sespie else
15481cc83814Sespie break;
15491cc83814Sespie }
15501cc83814Sespie list = nref->next;
15511cc83814Sespie }
15521cc83814Sespie
15531cc83814Sespie if (!nref)
15541cc83814Sespie {
15551cc83814Sespie if (!tref && expensive_validation)
15561cc83814Sespie {
15571cc83814Sespie /* Sigh... This might be AWFULLY slow, but if
15581cc83814Sespie they want this feature, they'll have to pay!
15591cc83814Sespie We do all the loop again expanding each
15601cc83814Sespie containing_node reference as we go. */
15611cc83814Sespie char *tags_up = expand_node_name (tags->up);
15621cc83814Sespie char *tem;
15631cc83814Sespie
15641cc83814Sespie list = node_references;
15651cc83814Sespie
15661cc83814Sespie for (;;)
15671cc83814Sespie {
15681cc83814Sespie nref = find_node_reference (tags->node, list);
15691cc83814Sespie if (!nref)
15701cc83814Sespie break;
15711cc83814Sespie tem = expand_node_name (nref->containing_node);
15721cc83814Sespie if (STREQ (tem, tags_up))
15731cc83814Sespie {
15741cc83814Sespie if (nref->type != menu_reference)
15751cc83814Sespie tref = nref;
15761cc83814Sespie else
15771cc83814Sespie {
15781cc83814Sespie free (tem);
15791cc83814Sespie break;
15801cc83814Sespie }
15811cc83814Sespie }
15821cc83814Sespie free (tem);
15831cc83814Sespie list = nref->next;
15841cc83814Sespie }
15851cc83814Sespie }
15861cc83814Sespie if (!nref && !tref)
15871cc83814Sespie {
15881cc83814Sespie temp_tag = find_node (tags->up);
15893fb98d4aSespie file_line_error (temp_tag->filename, temp_tag->line_no,
15901cc83814Sespie _("Node `%s' lacks menu item for `%s' despite being its Up target"),
15911cc83814Sespie tags->up, tags->node);
15921cc83814Sespie }
15931cc83814Sespie }
15941cc83814Sespie }
15951cc83814Sespie }
15961cc83814Sespie tags = tags->next_ent;
15971cc83814Sespie }
15981cc83814Sespie
15991cc83814Sespie validate_other_references (node_references);
16001cc83814Sespie /* We have told the user about the references which didn't exist.
16011cc83814Sespie Now tell him about the nodes which aren't referenced. */
16021cc83814Sespie
16031cc83814Sespie for (tags = tag_table; tags; tags = tags->next_ent)
16041cc83814Sespie {
16051cc83814Sespie /* If this node is a "no warn" node, do nothing. */
16061cc83814Sespie if (tags->flags & TAG_FLAG_NO_WARN)
16071cc83814Sespie {
16081cc83814Sespie tags = tags->next_ent;
16091cc83814Sespie continue;
16101cc83814Sespie }
16111cc83814Sespie
16121cc83814Sespie /* Special hack. If the node in question appears to have
16131cc83814Sespie been referenced more than REFERENCE_WARNING_LIMIT times,
16141cc83814Sespie give a warning. */
16151cc83814Sespie if (tags->touched > reference_warning_limit)
16161cc83814Sespie {
16171cc83814Sespie input_filename = tags->filename;
16181cc83814Sespie line_number = tags->line_no;
16191cc83814Sespie warning (_("node `%s' has been referenced %d times"),
16201cc83814Sespie tags->node, tags->touched);
16211cc83814Sespie }
16221cc83814Sespie
16231cc83814Sespie if (tags->touched == 0)
16241cc83814Sespie {
16251cc83814Sespie input_filename = tags->filename;
16261cc83814Sespie line_number = tags->line_no;
16271cc83814Sespie
16281cc83814Sespie /* Notice that the node "Top" is special, and doesn't have to
16291cc83814Sespie be referenced. Anchors don't have to be referenced
16301cc83814Sespie either, you might define them for another document. */
16311cc83814Sespie if (strcasecmp (tags->node, "Top") != 0
16321cc83814Sespie && !(tags->flags & TAG_FLAG_ANCHOR))
16331cc83814Sespie warning (_("unreferenced node `%s'"), tags->node);
16341cc83814Sespie }
16351cc83814Sespie }
16361cc83814Sespie input_filename = old_input_filename;
16371cc83814Sespie }
16381cc83814Sespie
16391cc83814Sespie
16401cc83814Sespie /* Splitting */
16411cc83814Sespie
16421cc83814Sespie /* Return true if the tag entry pointed to by TAGS is the last node.
16431cc83814Sespie This means only anchors follow. */
16441cc83814Sespie
16451cc83814Sespie static int
last_node_p(TAG_ENTRY * tags)1646*1076333cSespie last_node_p (TAG_ENTRY *tags)
16471cc83814Sespie {
16481cc83814Sespie int last = 1;
16491cc83814Sespie while (tags->next_ent) {
16501cc83814Sespie tags = tags->next_ent;
16511cc83814Sespie if (tags->flags & TAG_FLAG_ANCHOR)
16521cc83814Sespie ;
16531cc83814Sespie else
16541cc83814Sespie {
16551cc83814Sespie last = 0;
16561cc83814Sespie break;
16571cc83814Sespie }
16581cc83814Sespie }
16591cc83814Sespie
16601cc83814Sespie return last;
16611cc83814Sespie }
16621cc83814Sespie
16631cc83814Sespie
1664*1076333cSespie static char *
enumerate_filename(char * pathname,char * basename,int number)1665*1076333cSespie enumerate_filename (char *pathname, char *basename, int number)
1666*1076333cSespie {
1667*1076333cSespie /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */
1668*1076333cSespie const int dos_file_names = !HAVE_LONG_FILENAMES (pathname ? pathname : ".");
1669*1076333cSespie unsigned name_len = strlen (basename);
1670*1076333cSespie char *filename = xmalloc (10 + strlen (pathname) + name_len);
1671*1076333cSespie char *base_filename = xmalloc (10 + name_len);
1672*1076333cSespie
1673*1076333cSespie sprintf (base_filename, "%s-%d", basename, number);
1674*1076333cSespie
1675*1076333cSespie if (dos_file_names)
1676*1076333cSespie {
1677*1076333cSespie char *dot = strchr (base_filename, '.');
1678*1076333cSespie unsigned base_len = strlen (base_filename);
1679*1076333cSespie
1680*1076333cSespie if (dot)
1681*1076333cSespie { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */
1682*1076333cSespie dot[1] = 'i';
1683*1076333cSespie memmove (number <= 99 ? dot + 2 : dot + 1,
1684*1076333cSespie base_filename + name_len + 1,
1685*1076333cSespie strlen (base_filename + name_len + 1) + 1);
1686*1076333cSespie }
1687*1076333cSespie else if (base_len > 8)
1688*1076333cSespie {
1689*1076333cSespie /* Make foobar-1, .., fooba-10, .., foob-100, ... */
1690*1076333cSespie unsigned numlen = base_len - name_len;
1691*1076333cSespie
1692*1076333cSespie memmove (base_filename + 8 - numlen, base_filename + name_len, numlen + 1);
1693*1076333cSespie }
1694*1076333cSespie }
1695*1076333cSespie
1696*1076333cSespie sprintf (filename, "%s%s", pathname, base_filename);
1697*1076333cSespie
1698*1076333cSespie return filename;
1699*1076333cSespie }
1700*1076333cSespie
1701*1076333cSespie /* Remove previously split files, to avoid
1702*1076333cSespie lingering parts of shrinked documents. */
1703*1076333cSespie void
clean_old_split_files(char * filename)1704*1076333cSespie clean_old_split_files (char *filename)
1705*1076333cSespie {
1706*1076333cSespie char *root_filename = filename_part (filename);
1707*1076333cSespie char *root_pathname = pathname_part (filename);
1708*1076333cSespie int i;
1709*1076333cSespie
1710*1076333cSespie /* We break as soon as we hit an inexistent file,
1711*1076333cSespie so looping until large numbers is harmless. */
1712*1076333cSespie for (i = 1; i < 1000; i++)
1713*1076333cSespie {
1714*1076333cSespie struct stat st;
1715*1076333cSespie char *check_file = enumerate_filename (root_pathname, root_filename, i);
1716*1076333cSespie
1717*1076333cSespie if (stat (check_file, &st) != 0)
1718*1076333cSespie break;
1719*1076333cSespie else if (!S_ISDIR (st.st_mode))
1720*1076333cSespie {
1721*1076333cSespie /* Give feedback if requested, removing a file is important. */
1722*1076333cSespie if (verbose_mode)
1723*1076333cSespie printf (_("Removing %s\n"), check_file);
1724*1076333cSespie
1725*1076333cSespie /* Warn user that we cannot remove the file. */
1726*1076333cSespie if (unlink (check_file) != 0)
1727*1076333cSespie warning (_("Can't remove file `%s': %s"), check_file, strerror (errno));
1728*1076333cSespie }
1729*1076333cSespie
1730*1076333cSespie free (check_file);
1731*1076333cSespie }
1732*1076333cSespie }
1733*1076333cSespie
1734*1076333cSespie
17351cc83814Sespie /* Split large output files into a series of smaller files. Each file
17361cc83814Sespie is pointed to in the tag table, which then gets written out as the
17371cc83814Sespie original file. The new files have the same name as the original file
17381cc83814Sespie with a "-num" attached. SIZE is the largest number of bytes to allow
17391cc83814Sespie in any single split file. */
17401cc83814Sespie void
split_file(char * filename,int size)1741*1076333cSespie split_file (char *filename, int size)
17421cc83814Sespie {
17431cc83814Sespie char *root_filename, *root_pathname;
1744*1076333cSespie char *the_file;
17451cc83814Sespie struct stat fileinfo;
17461cc83814Sespie long file_size;
17471cc83814Sespie char *the_header;
17481cc83814Sespie int header_size;
17491cc83814Sespie
17501cc83814Sespie /* Can only do this to files with tag tables. */
17511cc83814Sespie if (!tag_table)
17521cc83814Sespie return;
17531cc83814Sespie
17541cc83814Sespie if (size == 0)
17551cc83814Sespie size = DEFAULT_SPLIT_SIZE;
17561cc83814Sespie
1757*1076333cSespie if ((stat (filename, &fileinfo) != 0)
1758*1076333cSespie || (((long) fileinfo.st_size) < size))
17591cc83814Sespie return;
17601cc83814Sespie file_size = (long) fileinfo.st_size;
17611cc83814Sespie
1762*1076333cSespie the_file = find_and_load (filename, 0);
17631cc83814Sespie if (!the_file)
17641cc83814Sespie return;
17651cc83814Sespie
17661cc83814Sespie root_filename = filename_part (filename);
17671cc83814Sespie root_pathname = pathname_part (filename);
17681cc83814Sespie
17691cc83814Sespie if (!root_pathname)
17701cc83814Sespie root_pathname = xstrdup ("");
17711cc83814Sespie
17721cc83814Sespie /* Start splitting the file. Walk along the tag table
17731cc83814Sespie outputting sections of the file. When we have written
17741cc83814Sespie all of the nodes in the tag table, make the top-level
17751cc83814Sespie pointer file, which contains indirect pointers and
17761cc83814Sespie tags for the nodes. */
17771cc83814Sespie {
17781cc83814Sespie int which_file = 1;
17791cc83814Sespie TAG_ENTRY *tags = tag_table;
17801cc83814Sespie char *indirect_info = NULL;
17811cc83814Sespie
1782*1076333cSespie /* Maybe we want a Local Variables section. */
1783*1076333cSespie char *trailer = info_trailer ();
1784*1076333cSespie int trailer_len = trailer ? strlen (trailer) : 0;
1785*1076333cSespie
17861cc83814Sespie /* Remember the `header' of this file. The first tag in the file is
17871cc83814Sespie the bottom of the header; the top of the file is the start. */
17881cc83814Sespie the_header = xmalloc (1 + (header_size = tags->position));
17891cc83814Sespie memcpy (the_header, the_file, header_size);
17901cc83814Sespie
17911cc83814Sespie while (tags)
17921cc83814Sespie {
17931cc83814Sespie int file_top, file_bot, limit;
17941cc83814Sespie
17951cc83814Sespie /* Have to include the Control-_. */
17961cc83814Sespie file_top = file_bot = tags->position;
17971cc83814Sespie limit = file_top + size;
17981cc83814Sespie
17991cc83814Sespie /* If the rest of this file is only one node, then
18001cc83814Sespie that is the entire subfile. */
18011cc83814Sespie if (last_node_p (tags))
18021cc83814Sespie {
18031cc83814Sespie int i = tags->position + 1;
18041cc83814Sespie char last_char = the_file[i];
18051cc83814Sespie
18061cc83814Sespie while (i < file_size)
18071cc83814Sespie {
18081cc83814Sespie if ((the_file[i] == '\037') &&
18091cc83814Sespie ((last_char == '\n') ||
18101cc83814Sespie (last_char == '\014')))
18111cc83814Sespie break;
18121cc83814Sespie else
18131cc83814Sespie last_char = the_file[i];
18141cc83814Sespie i++;
18151cc83814Sespie }
18161cc83814Sespie file_bot = i;
18171cc83814Sespie tags = tags->next_ent;
18181cc83814Sespie goto write_region;
18191cc83814Sespie }
18201cc83814Sespie
18211cc83814Sespie /* Otherwise, find the largest number of nodes that can fit in
18221cc83814Sespie this subfile. */
18231cc83814Sespie for (; tags; tags = tags->next_ent)
18241cc83814Sespie {
18251cc83814Sespie if (last_node_p (tags))
18261cc83814Sespie {
18271cc83814Sespie /* This entry is the last node. Search forward for the end
18281cc83814Sespie of this node, and that is the end of this file. */
18291cc83814Sespie int i = tags->position + 1;
18301cc83814Sespie char last_char = the_file[i];
18311cc83814Sespie
18321cc83814Sespie while (i < file_size)
18331cc83814Sespie {
18341cc83814Sespie if ((the_file[i] == '\037') &&
18351cc83814Sespie ((last_char == '\n') ||
18361cc83814Sespie (last_char == '\014')))
18371cc83814Sespie break;
18381cc83814Sespie else
18391cc83814Sespie last_char = the_file[i];
18401cc83814Sespie i++;
18411cc83814Sespie }
18421cc83814Sespie file_bot = i;
18431cc83814Sespie
18441cc83814Sespie if (file_bot < limit)
18451cc83814Sespie {
18461cc83814Sespie tags = tags->next_ent;
18471cc83814Sespie goto write_region;
18481cc83814Sespie }
18491cc83814Sespie else
18501cc83814Sespie {
18511cc83814Sespie /* Here we want to write out everything before the last
18521cc83814Sespie node, and then write the last node out in a file
18531cc83814Sespie by itself. */
18541cc83814Sespie file_bot = tags->position;
18551cc83814Sespie goto write_region;
18561cc83814Sespie }
18571cc83814Sespie }
18581cc83814Sespie
18591cc83814Sespie /* Write region only if this was a node, not an anchor. */
18601cc83814Sespie if (tags->next_ent->position > limit
18611cc83814Sespie && !(tags->flags & TAG_FLAG_ANCHOR))
18621cc83814Sespie {
18631cc83814Sespie if (tags->position == file_top)
18641cc83814Sespie tags = tags->next_ent;
18651cc83814Sespie
18661cc83814Sespie file_bot = tags->position;
18671cc83814Sespie
18681cc83814Sespie write_region:
18691cc83814Sespie {
18701cc83814Sespie int fd;
1871*1076333cSespie char *split_filename = enumerate_filename (root_pathname,
1872*1076333cSespie root_filename, which_file);
1873*1076333cSespie char *split_basename = filename_part (split_filename);
18741cc83814Sespie
18751cc83814Sespie fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
18761cc83814Sespie if (fd < 0
18771cc83814Sespie || write (fd, the_header, header_size) != header_size
18781cc83814Sespie || write (fd, the_file + file_top, file_bot - file_top)
18791cc83814Sespie != (file_bot - file_top)
1880*1076333cSespie || (trailer_len
1881*1076333cSespie && write (fd, trailer, trailer_len) != trailer_len)
1882*1076333cSespie || close (fd) < 0)
18831cc83814Sespie {
18841cc83814Sespie perror (split_filename);
18851cc83814Sespie if (fd != -1)
18861cc83814Sespie close (fd);
18871cc83814Sespie xexit (1);
18881cc83814Sespie }
18891cc83814Sespie
18901cc83814Sespie if (!indirect_info)
18911cc83814Sespie {
18921cc83814Sespie indirect_info = the_file + file_top;
18931cc83814Sespie sprintf (indirect_info, "\037\nIndirect:\n");
18941cc83814Sespie indirect_info += strlen (indirect_info);
18951cc83814Sespie }
18961cc83814Sespie
18971cc83814Sespie sprintf (indirect_info, "%s: %d\n",
18981cc83814Sespie split_basename, file_top);
18991cc83814Sespie
19001cc83814Sespie free (split_basename);
19011cc83814Sespie free (split_filename);
19021cc83814Sespie indirect_info += strlen (indirect_info);
19031cc83814Sespie which_file++;
19041cc83814Sespie break;
19051cc83814Sespie }
19061cc83814Sespie }
19071cc83814Sespie }
19081cc83814Sespie }
19091cc83814Sespie
1910f986e4fcSmillert /* We have successfully created the subfiles. Now write out the
19111cc83814Sespie original again. We must use `output_stream', or
19121cc83814Sespie write_tag_table_indirect () won't know where to place the output. */
19131cc83814Sespie output_stream = fopen (filename, "w");
19141cc83814Sespie if (!output_stream)
19151cc83814Sespie {
19161cc83814Sespie perror (filename);
19171cc83814Sespie xexit (1);
19181cc83814Sespie }
19191cc83814Sespie
19201cc83814Sespie {
19211cc83814Sespie int distance = indirect_info - the_file;
19221cc83814Sespie fwrite (the_file, 1, distance, output_stream);
19231cc83814Sespie
19241cc83814Sespie /* Inhibit newlines. */
19251cc83814Sespie paragraph_is_open = 0;
19261cc83814Sespie
1927*1076333cSespie /* Write the indirect tag table. */
19281cc83814Sespie write_tag_table_indirect ();
1929*1076333cSespie
1930*1076333cSespie /* preserve local variables in info output. */
1931*1076333cSespie if (trailer)
1932*1076333cSespie {
1933*1076333cSespie fwrite (trailer, 1, trailer_len, output_stream);
1934*1076333cSespie free (trailer);
1935*1076333cSespie }
1936*1076333cSespie
19371cc83814Sespie fclose (output_stream);
19381cc83814Sespie free (the_header);
19391cc83814Sespie free (the_file);
19401cc83814Sespie return;
19411cc83814Sespie }
19421cc83814Sespie }
19431cc83814Sespie }
1944