156160Sru/* node.c -- nodes for Texinfo. 2146515Sru $Id: node.c,v 1.27 2004/12/20 23:56:07 karl Exp $ 356160Sru 4146515Sru Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software 5116525Sru Foundation, Inc. 656160Sru 756160Sru This program is free software; you can redistribute it and/or modify 856160Sru it under the terms of the GNU General Public License as published by 956160Sru the Free Software Foundation; either version 2, or (at your option) 1056160Sru any later version. 1156160Sru 1256160Sru This program is distributed in the hope that it will be useful, 1356160Sru but WITHOUT ANY WARRANTY; without even the implied warranty of 1456160Sru MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1556160Sru GNU General Public License for more details. 1656160Sru 1756160Sru You should have received a copy of the GNU General Public License 1856160Sru along with this program; if not, write to the Free Software Foundation, 1956160Sru Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 2056160Sru 2156160Sru#include "system.h" 2256160Sru#include "cmds.h" 2356160Sru#include "files.h" 24146515Sru#include "float.h" 2556160Sru#include "footnote.h" 2656160Sru#include "macro.h" 2756160Sru#include "makeinfo.h" 2856160Sru#include "node.h" 2993139Sru#include "html.h" 3056160Sru#include "sectioning.h" 3156160Sru#include "insertion.h" 3293139Sru#include "xml.h" 3356160Sru 3456160Sru/* See comments in node.h. */ 3556160SruNODE_REF *node_references = NULL; 3656160SruNODE_REF *node_node_references = NULL; 3756160SruTAG_ENTRY *tag_table = NULL; 3856160Sruint node_number = -1; 39146515Sruint node_order = 0; 4056160Sruint current_section = 0; 4156160Sruint outstanding_node = 0; 4256160Sru 4356160Sru/* Adding nodes, and making tags. */ 4456160Sru 4556160Sru/* Start a new tag table. */ 4656160Sruvoid 47146515Sruinit_tag_table (void) 4856160Sru{ 4956160Sru while (tag_table) 5056160Sru { 5156160Sru TAG_ENTRY *temp = tag_table; 5256160Sru free (temp->node); 5356160Sru free (temp->prev); 5456160Sru free (temp->next); 5556160Sru free (temp->up); 5656160Sru tag_table = tag_table->next_ent; 5756160Sru free (temp); 5856160Sru } 5956160Sru} 6056160Sru 6156160Sru/* Write out the contents of the existing tag table. 6256160Sru INDIRECT_P says how to format the output (it depends on whether the 6356160Sru table is direct or indirect). */ 6456160Srustatic void 65146515Sruwrite_tag_table_internal (int indirect_p) 6656160Sru{ 6756160Sru TAG_ENTRY *node; 6856160Sru int old_indent = no_indent; 6956160Sru 7093139Sru if (xml) 7193139Sru { 7293139Sru flush_output (); 7393139Sru return; 7493139Sru } 7593139Sru 7656160Sru no_indent = 1; 7756160Sru filling_enabled = 0; 7856160Sru must_start_paragraph = 0; 7956160Sru close_paragraph (); 8056160Sru 8156160Sru if (!indirect_p) 8256160Sru { 8356160Sru no_indent = 1; 8456160Sru insert ('\n'); 8556160Sru } 8656160Sru 8756160Sru add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : ""); 8856160Sru 8956160Sru /* Do not collapse -- to -, etc., in node names. */ 9056160Sru in_fixed_width_font++; 9156160Sru 9256160Sru for (node = tag_table; node; node = node->next_ent) 9356160Sru { 9456160Sru if (node->flags & TAG_FLAG_ANCHOR) 9556160Sru { /* This reference is to an anchor. */ 9656160Sru execute_string ("Ref: %s", node->node); 9756160Sru } 9856160Sru else 9956160Sru { /* This reference is to a node. */ 10056160Sru execute_string ("Node: %s", node->node); 10156160Sru } 10256160Sru add_word_args ("\177%d\n", node->position); 10356160Sru } 10456160Sru 10556160Sru add_word ("\037\nEnd Tag Table\n"); 10656160Sru 10756160Sru /* Do not collapse -- to -, etc., in node names. */ 10856160Sru in_fixed_width_font--; 10956160Sru 11056160Sru flush_output (); 11156160Sru no_indent = old_indent; 11256160Sru} 11356160Sru 11456160Sruvoid 115146515Sruwrite_tag_table (char *filename) 11656160Sru{ 117146515Sru output_stream = fopen (filename, "a"); 118146515Sru if (!output_stream) 119146515Sru { 120146515Sru fs_error (filename); 121146515Sru return; 122146515Sru } 123146515Sru 12456160Sru write_tag_table_internal (0); /* Not indirect. */ 125146515Sru 126146515Sru if (fclose (output_stream) != 0) 127146515Sru fs_error (filename); 12856160Sru} 12956160Sru 130146515Srustatic void 131146515Sruwrite_tag_table_indirect (void) 13256160Sru{ 13356160Sru write_tag_table_internal (1); 13456160Sru} 13556160Sru 13656160Sru/* Convert "top" and friends into "Top". */ 13756160Srustatic void 138146515Srunormalize_node_name (char *string) 13956160Sru{ 14056160Sru if (strcasecmp (string, "Top") == 0) 14156160Sru strcpy (string, "Top"); 14256160Sru} 14356160Sru 144146515Srustatic char * 145146515Sruget_node_token (int expand) 14656160Sru{ 14756160Sru char *string; 14856160Sru 14956160Sru get_until_in_line (expand, ",", &string); 15056160Sru 15156160Sru if (curchar () == ',') 15256160Sru input_text_offset++; 15356160Sru 15456160Sru fix_whitespace (string); 15556160Sru 15656160Sru /* Force all versions of "top" to be "Top". */ 15756160Sru normalize_node_name (string); 15856160Sru 15956160Sru return string; 16056160Sru} 16156160Sru 16256160Sru/* Expand any macros and other directives in a node name, and 16356160Sru return the expanded name as an malloc'ed string. */ 16456160Sruchar * 165146515Sruexpand_node_name (char *node) 16656160Sru{ 16756160Sru char *result = node; 16856160Sru 16956160Sru if (node) 17056160Sru { 17156160Sru /* Don't expand --, `` etc., in case somebody will want 17256160Sru to print the result. */ 17356160Sru in_fixed_width_font++; 17456160Sru result = expansion (node, 0); 17556160Sru in_fixed_width_font--; 17656160Sru fix_whitespace (result); 17756160Sru normalize_node_name (result); 17856160Sru } 17956160Sru return result; 18056160Sru} 18156160Sru 18256160Sru/* Look up NAME in the tag table, and return the associated 18356160Sru tag_entry. If the node is not in the table return NULL. */ 18456160SruTAG_ENTRY * 185146515Srufind_node (char *name) 18656160Sru{ 18756160Sru TAG_ENTRY *tag = tag_table; 18856160Sru char *expanded_name; 18956160Sru char n1 = name[0]; 19056160Sru 19156160Sru while (tag) 19256160Sru { 19356160Sru if (tag->node[0] == n1 && strcmp (tag->node, name) == 0) 19456160Sru return tag; 19556160Sru tag = tag->next_ent; 19656160Sru } 19756160Sru 19856160Sru if (!expensive_validation) 19956160Sru return NULL; 20056160Sru 20156160Sru /* Try harder. Maybe TAG_TABLE has the expanded NAME, or maybe NAME 20256160Sru is expanded while TAG_TABLE has its unexpanded form. This may 20356160Sru slow down the search, but if they want this feature, let them 20456160Sru pay! If they want it fast, they should write every node name 20556160Sru consistently (either always expanded or always unexpaned). */ 20656160Sru expanded_name = expand_node_name (name); 20756160Sru for (tag = tag_table; tag; tag = tag->next_ent) 20856160Sru { 20956160Sru if (STREQ (tag->node, expanded_name)) 21056160Sru break; 21156160Sru /* If the tag name doesn't have the command prefix, there's no 21256160Sru chance it could expand into anything but itself. */ 21356160Sru if (strchr (tag->node, COMMAND_PREFIX)) 21456160Sru { 21556160Sru char *expanded_node = expand_node_name (tag->node); 21656160Sru 21756160Sru if (STREQ (expanded_node, expanded_name)) 21856160Sru { 21956160Sru free (expanded_node); 22056160Sru break; 22156160Sru } 22256160Sru free (expanded_node); 22356160Sru } 22456160Sru } 22556160Sru free (expanded_name); 22656160Sru return tag; 22756160Sru} 22856160Sru 22993139Sru/* Look in the tag table for a node whose file name is FNAME, and 23093139Sru return the associated tag_entry. If there's no such node in the 23193139Sru table, return NULL. */ 232146515Srustatic TAG_ENTRY * 233146515Srufind_node_by_fname (char *fname) 23493139Sru{ 23593139Sru TAG_ENTRY *tag = tag_table; 23693139Sru while (tag) 23793139Sru { 23893139Sru if (tag->html_fname && FILENAME_CMP (tag->html_fname, fname) == 0) 23993139Sru return tag; 24093139Sru tag = tag->next_ent; 24193139Sru } 24293139Sru 24393139Sru return tag; 24493139Sru} 24593139Sru 24693139Sru/* Remember next, prev, etc. references in a @node command, where we 24756160Sru don't care about most of the entries. */ 24856160Srustatic void 249146515Sruremember_node_node_reference (char *node) 25056160Sru{ 25156160Sru NODE_REF *temp = xmalloc (sizeof (NODE_REF)); 25256160Sru int number; 25356160Sru 25456160Sru if (!node) return; 25556160Sru temp->next = node_node_references; 25656160Sru temp->node = xstrdup (node); 25756160Sru temp->type = followed_reference; 25856160Sru number = number_of_node (node); 25956160Sru if (number) 26056160Sru temp->number = number; /* Already assigned. */ 26156160Sru else 26256160Sru { 26356160Sru node_number++; 26456160Sru temp->number = node_number; 26556160Sru } 26656160Sru node_node_references = temp; 26756160Sru} 26856160Sru 26956160Sru/* Remember NODE and associates. */ 270146515Srustatic void 271146515Sruremember_node (char *node, char *prev, char *next, char *up, 272146515Sru int position, int line_no, char *fname, int flags) 27356160Sru{ 27456160Sru /* Check for existence of this tag already. */ 27556160Sru if (validating) 27656160Sru { 27756160Sru TAG_ENTRY *tag = find_node (node); 27856160Sru if (tag) 27956160Sru { 28056160Sru line_error (_("Node `%s' previously defined at line %d"), 28156160Sru node, tag->line_no); 28256160Sru return; 28356160Sru } 28456160Sru } 28556160Sru 28656160Sru if (!(flags & TAG_FLAG_ANCHOR)) 28756160Sru { 28856160Sru /* Make this the current node. */ 28956160Sru current_node = node; 29056160Sru } 29156160Sru 29256160Sru /* Add it to the list. */ 29356160Sru { 29456160Sru int number = number_of_node (node); 29556160Sru 29656160Sru TAG_ENTRY *new = xmalloc (sizeof (TAG_ENTRY)); 29756160Sru new->node = node; 29856160Sru new->prev = prev; 29956160Sru new->next = next; 30056160Sru new->up = up; 30156160Sru new->position = position; 30256160Sru new->line_no = line_no; 30356160Sru new->filename = node_filename; 30456160Sru new->touched = 0; 30556160Sru new->flags = flags; 30656160Sru if (number) 30756160Sru new->number = number; /* Already assigned. */ 30856160Sru else 30956160Sru { 31056160Sru node_number++; 31156160Sru new->number = node_number; 31256160Sru } 313114472Sru if (fname) 314114472Sru new->html_fname = fname; 315114472Sru else 316114472Sru /* This happens for Top node under split-HTML, for example. */ 317114472Sru new->html_fname 318114472Sru = normalize_filename (filename_part (current_output_filename)); 31956160Sru new->next_ent = tag_table; 320146515Sru 321146515Sru /* Increment the order counter, and save it. */ 322146515Sru node_order++; 323146515Sru new->order = node_order; 324146515Sru 32556160Sru tag_table = new; 32656160Sru } 32756160Sru 32856160Sru if (html) 32956160Sru { /* Note the references to the next etc. nodes too. */ 33056160Sru remember_node_node_reference (next); 33156160Sru remember_node_node_reference (prev); 33256160Sru remember_node_node_reference (up); 33356160Sru } 33456160Sru} 33556160Sru 33656160Sru/* Remember this node name for later validation use. This is used to 33756160Sru remember menu references while reading the input file. After the 33856160Sru output file has been written, if validation is on, then we use the 33956160Sru contents of `node_references' as a list of nodes to validate. */ 34056160Sruvoid 341146515Sruremember_node_reference (char *node, int line, enum reftype type) 34256160Sru{ 34356160Sru NODE_REF *temp = xmalloc (sizeof (NODE_REF)); 34456160Sru int number = number_of_node (node); 34556160Sru 34656160Sru temp->next = node_references; 34756160Sru temp->node = xstrdup (node); 34856160Sru temp->line_no = line; 34956160Sru temp->section = current_section; 35056160Sru temp->type = type; 35156160Sru temp->containing_node = xstrdup (current_node ? current_node : ""); 35256160Sru temp->filename = node_filename; 35356160Sru if (number) 35456160Sru temp->number = number; /* Already assigned. */ 35556160Sru else 35656160Sru { 35756160Sru node_number++; 35856160Sru temp->number = node_number; 35956160Sru } 36056160Sru 36156160Sru node_references = temp; 36256160Sru} 36356160Sru 36456160Srustatic void 365146515Sruisolate_nodename (char *nodename) 36656160Sru{ 36756160Sru int i, c; 36856160Sru int paren_seen, paren; 36956160Sru 37056160Sru if (!nodename) 37156160Sru return; 37256160Sru 37356160Sru canon_white (nodename); 37456160Sru paren_seen = paren = i = 0; 37556160Sru 37656160Sru if (*nodename == '.' || !*nodename) 37756160Sru { 37856160Sru *nodename = 0; 37956160Sru return; 38056160Sru } 38156160Sru 38256160Sru if (*nodename == '(') 38356160Sru { 38456160Sru paren++; 38556160Sru paren_seen++; 38656160Sru i++; 38756160Sru } 38856160Sru 38956160Sru for (; (c = nodename[i]); i++) 39056160Sru { 39156160Sru if (paren) 39256160Sru { 39356160Sru if (c == '(') 39456160Sru paren++; 39556160Sru else if (c == ')') 39656160Sru paren--; 39756160Sru 39856160Sru continue; 39956160Sru } 40056160Sru 40156160Sru /* If the character following the close paren is a space, then this 40256160Sru node has no more characters associated with it. */ 40356160Sru if (c == '\t' || 40456160Sru c == '\n' || 40556160Sru c == ',' || 40656160Sru ((paren_seen && nodename[i - 1] == ')') && 40756160Sru (c == ' ' || c == '.')) || 40856160Sru (c == '.' && 40956160Sru ((!nodename[i + 1] || 41056160Sru (cr_or_whitespace (nodename[i + 1])) || 41156160Sru (nodename[i + 1] == ')'))))) 41256160Sru break; 41356160Sru } 41456160Sru nodename[i] = 0; 41556160Sru} 41656160Sru 41756160Sru/* This function gets called at the start of every line while inside a 41856160Sru menu. It checks to see if the line starts with "* ", and if so and 41956160Sru REMEMBER_REF is nonzero, remembers the node reference as type 42056160Sru REF_TYPE that this menu refers to. input_text_offset is at the \n 42156160Sru just before the menu line. If REMEMBER_REF is zero, REF_TYPE is unused. */ 42256160Sru#define MENU_STARTER "* " 42356160Sruchar * 424146515Sruglean_node_from_menu (int remember_ref, enum reftype ref_type) 42556160Sru{ 42656160Sru int i, orig_offset = input_text_offset; 42756160Sru char *nodename; 42856160Sru char *line, *expanded_line; 42956160Sru char *old_input = input_text; 43093139Sru int old_size = input_text_length; 43156160Sru 43256160Sru if (strncmp (&input_text[input_text_offset + 1], 43356160Sru MENU_STARTER, 43456160Sru strlen (MENU_STARTER)) != 0) 43556160Sru return NULL; 43656160Sru else 43756160Sru input_text_offset += strlen (MENU_STARTER) + 1; 43856160Sru 43956160Sru /* The menu entry might include macro calls, so we need to expand them. */ 44056160Sru get_until ("\n", &line); 44156160Sru only_macro_expansion++; /* only expand macros in menu entries */ 44256160Sru expanded_line = expansion (line, 0); 44356160Sru only_macro_expansion--; 44456160Sru free (line); 44556160Sru input_text = expanded_line; 44656160Sru input_text_offset = 0; 44756160Sru input_text_length = strlen (expanded_line); 44856160Sru 44956160Sru get_until_in_line (0, ":", &nodename); 45056160Sru if (curchar () == ':') 45156160Sru input_text_offset++; 45256160Sru 45356160Sru if (curchar () != ':') 45456160Sru { 45556160Sru free (nodename); 45656160Sru get_until_in_line (0, "\n", &nodename); 45756160Sru isolate_nodename (nodename); 45856160Sru } 45956160Sru 46056160Sru input_text = old_input; 46156160Sru input_text_offset = orig_offset; 46256160Sru input_text_length = old_size; 46356160Sru free (expanded_line); 46456160Sru fix_whitespace (nodename); 46556160Sru normalize_node_name (nodename); 46656160Sru i = strlen (nodename); 46756160Sru if (i && nodename[i - 1] == ':') 46856160Sru nodename[i - 1] = 0; 46956160Sru 47056160Sru if (remember_ref) 47156160Sru remember_node_reference (nodename, line_number, ref_type); 47256160Sru 47356160Sru return nodename; 47456160Sru} 47556160Sru 47656160Sru/* Set the name of the current output file. */ 47756160Sruvoid 478146515Sruset_current_output_filename (const char *fname) 47956160Sru{ 48056160Sru if (current_output_filename) 48156160Sru free (current_output_filename); 48256160Sru current_output_filename = xstrdup (fname); 48356160Sru} 48456160Sru 485146515Sru 486146515Sru/* Output the <a name="..."></a> constructs for NODE. We output both 487146515Sru the new-style conversion and the old-style, if they are different. 488146515Sru See comments at `add_escaped_anchor_name' in html.c. */ 489146515Sru 490146515Srustatic void 491146515Sruadd_html_names (char *node) 492146515Sru{ 493146515Sru char *tem = expand_node_name (node); 494146515Sru char *otem = xstrdup (tem); 495146515Sru 496146515Sru /* Determine if the old and new schemes come up with different names; 497146515Sru only output the old scheme if that is so. We don't want to output 498146515Sru the same name twice. */ 499146515Sru canon_white (otem); 500146515Sru { 501146515Sru char *optr = otem; 502146515Sru int need_old = 0; 503146515Sru 504146515Sru for (; *optr; optr++) 505146515Sru { 506146515Sru if (!cr_or_whitespace (*optr) && !URL_SAFE_CHAR (*optr)) 507146515Sru { 508146515Sru need_old = 1; 509146515Sru break; 510146515Sru } 511146515Sru } 512146515Sru 513146515Sru if (need_old) 514146515Sru { 515146515Sru add_word ("<a name=\""); 516146515Sru add_anchor_name (otem, -1); /* old anchor name conversion */ 517146515Sru add_word ("\"></a>\n"); 518146515Sru } 519146515Sru free (otem); 520146515Sru } 521146515Sru 522146515Sru /* Always output the new scheme. */ 523146515Sru canon_white (tem); 524146515Sru add_word ("<a name=\""); 525146515Sru add_anchor_name (tem, 0); 526146515Sru add_word ("\"></a>\n"); 527146515Sru 528146515Sru free (tem); 529146515Sru} 530146515Sru 531146515Sru 53256160Sru/* The order is: nodename, nextnode, prevnode, upnode. 53356160Sru If all of the NEXT, PREV, and UP fields are empty, they are defaulted. 53456160Sru You must follow a node command which has those fields defaulted 535114472Sru with a sectioning command (e.g., @chapter) giving the "level" of that node. 53656160Sru It is an error not to do so. 53756160Sru The defaults come from the menu in this node's parent. */ 53856160Sruvoid 539146515Srucm_node (void) 54056160Sru{ 54193139Sru static long epilogue_len = 0L; 54256160Sru char *node, *prev, *next, *up; 54356160Sru int new_node_pos, defaulting, this_section; 54456160Sru int no_warn = 0; 54593139Sru char *fname_for_this_node = NULL; 54693139Sru char *tem; 54793139Sru TAG_ENTRY *tag = NULL; 54856160Sru 54956160Sru if (strcmp (command, "nwnode") == 0) 55056160Sru no_warn = TAG_FLAG_NO_WARN; 55156160Sru 55256160Sru /* Get rid of unmatched brace arguments from previous commands. */ 55356160Sru discard_braces (); 55456160Sru 55556160Sru /* There also might be insertions left lying around that haven't been 55656160Sru ended yet. Do that also. */ 55756160Sru discard_insertions (1); 55856160Sru 55956160Sru if (!html && !already_outputting_pending_notes) 56056160Sru { 56156160Sru close_paragraph (); 56256160Sru output_pending_notes (); 56356160Sru } 56456160Sru 56556160Sru new_node_pos = output_position; 56656160Sru 56756160Sru if (macro_expansion_output_stream && !executing_string) 56856160Sru append_to_expansion_output (input_text_offset + 1); 56956160Sru 57056160Sru /* Do not collapse -- to -, etc., in node names. */ 57156160Sru in_fixed_width_font++; 57256160Sru 57356160Sru /* While expanding the @node line, leave any non-macros 57456160Sru intact, so that the macro-expanded output includes them. */ 57556160Sru only_macro_expansion++; 57656160Sru node = get_node_token (1); 57756160Sru only_macro_expansion--; 57856160Sru next = get_node_token (0); 57956160Sru prev = get_node_token (0); 58056160Sru up = get_node_token (0); 58156160Sru 58293139Sru if (html && splitting 58393139Sru /* If there is a Top node, it always goes into index.html. So 58493139Sru don't start a new HTML file for Top. */ 58593139Sru && (top_node_seen || strcasecmp (node, "Top") != 0)) 58693139Sru { 58793139Sru /* We test *node here so that @node without a valid name won't 58893139Sru start a new file name with a bogus name such as ".html". 58993139Sru This could happen if we run under "--force", where we cannot 59093139Sru simply bail out. Continuing to use the same file sounds like 59193139Sru the best we can do in such cases. */ 59293139Sru if (current_output_filename && output_stream && *node) 59393139Sru { 59493139Sru char *fname_for_prev_node; 59593139Sru 59693139Sru if (current_node) 59793139Sru { 59893139Sru /* NOTE: current_node at this point still holds the name 59993139Sru of the previous node. */ 60093139Sru tem = expand_node_name (current_node); 60193139Sru fname_for_prev_node = nodename_to_filename (tem); 60293139Sru free (tem); 60393139Sru } 60493139Sru else /* could happen if their top node isn't named "Top" */ 60593139Sru fname_for_prev_node = filename_part (current_output_filename); 60693139Sru tem = expand_node_name (node); 60793139Sru fname_for_this_node = nodename_to_filename (tem); 60893139Sru free (tem); 60993139Sru /* Don't close current output file, if next output file is 61093139Sru to have the same name. This may happen at top level, or 61193139Sru if two nodes produce the same file name under --split. */ 61293139Sru if (FILENAME_CMP (fname_for_this_node, fname_for_prev_node) != 0) 61393139Sru { 61493139Sru long pos1 = 0; 61593139Sru 61693139Sru /* End the current split output file. */ 61793139Sru close_paragraph (); 61893139Sru output_pending_notes (); 61993139Sru start_paragraph (); 62093139Sru /* Compute the length of the HTML file's epilogue. We 62193139Sru cannot know the value until run time, due to the 62293139Sru text/binary nuisance on DOS/Windows platforms, where 62393139Sru 2 `\r' characters could be added to the epilogue when 62493139Sru it is written in text mode. */ 62593139Sru if (epilogue_len == 0) 62693139Sru { 62793139Sru flush_output (); 62893139Sru pos1 = ftell (output_stream); 62993139Sru } 63093139Sru add_word ("</body></html>\n"); 63193139Sru close_paragraph (); 63293139Sru if (epilogue_len == 0) 63393139Sru epilogue_len = ftell (output_stream) - pos1; 63493139Sru fclose (output_stream); 63593139Sru output_stream = NULL; 636146515Sru output_position = 0; 63793139Sru tag = find_node_by_fname (fname_for_this_node); 63893139Sru } 63993139Sru free (fname_for_prev_node); 64093139Sru } 64193139Sru } 64293139Sru 64393139Sru filling_enabled = indented_fill = 0; 64493139Sru if (!html || (html && splitting)) 64593139Sru current_footnote_number = 1; 646116525Sru 64756160Sru if (verbose_mode) 64856160Sru printf (_("Formatting node %s...\n"), node); 64956160Sru 65056160Sru if (macro_expansion_output_stream && !executing_string) 65156160Sru remember_itext (input_text, input_text_offset); 65256160Sru 653146515Sru /* Reset the line number in each node for Info output, so that 654146515Sru index entries will save the line numbers of parent node. */ 655146515Sru node_line_number = 0; 656146515Sru 65756160Sru no_indent = 1; 65893139Sru if (xml) 65956160Sru { 660100513Sru xml_begin_document (current_output_filename); 66193139Sru xml_begin_node (); 66293139Sru if (!docbook) 66393139Sru { 664116525Sru xml_insert_element (NODENAME, START); 66593139Sru if (macro_expansion_output_stream && !executing_string) 66693139Sru me_execute_string (node); 66793139Sru else 66893139Sru execute_string ("%s", node); 66993139Sru xml_insert_element (NODENAME, END); 67093139Sru } 67193139Sru else 67293139Sru xml_node_id = xml_id (node); 67393139Sru } 67493139Sru else if (!no_headers && !html) 67593139Sru { 676146515Sru /* Emacs Info reader cannot grok indented escape sequence. */ 677146515Sru kill_self_indent (-1); 678146515Sru 67956160Sru add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename); 68056160Sru 68156160Sru if (macro_expansion_output_stream && !executing_string) 68256160Sru me_execute_string (node); 68356160Sru else 68456160Sru execute_string ("%s", node); 68556160Sru filling_enabled = indented_fill = 0; 68656160Sru } 68756160Sru 68856160Sru /* Check for defaulting of this node's next, prev, and up fields. */ 68956160Sru defaulting = (*next == 0 && *prev == 0 && *up == 0); 69056160Sru 691146515Sru this_section = what_section (input_text + input_text_offset, NULL); 69256160Sru 69356160Sru /* If we are defaulting, then look at the immediately following 69456160Sru sectioning command (error if none) to determine the node's 69556160Sru level. Find the node that contains the menu mentioning this node 69656160Sru that is one level up (error if not found). That node is the "Up" 69756160Sru of this node. Default the "Next" and "Prev" from the menu. */ 69856160Sru if (defaulting) 69956160Sru { 70056160Sru NODE_REF *last_ref = NULL; 70156160Sru NODE_REF *ref = node_references; 70256160Sru 70356160Sru if (this_section < 0 && !STREQ (node, "Top")) 70456160Sru { 70556160Sru char *polite_section_name = "top"; 70656160Sru int i; 70756160Sru 70856160Sru for (i = 0; section_alist[i].name; i++) 70956160Sru if (section_alist[i].level == current_section + 1) 71056160Sru { 71156160Sru polite_section_name = section_alist[i].name; 71256160Sru break; 71356160Sru } 71456160Sru 71556160Sru line_error 716114472Sru (_("Node `%s' requires a sectioning command (e.g., %c%s)"), 71756160Sru node, COMMAND_PREFIX, polite_section_name); 71856160Sru } 71956160Sru else 72056160Sru { 72156160Sru if (strcmp (node, "Top") == 0) 72256160Sru { 72356160Sru /* Default the NEXT pointer to be the first menu item in 72456160Sru this node, if there is a menu in this node. We have to 72556160Sru try very hard to find the menu, as it may be obscured 72656160Sru by execution_strings which are on the filestack. For 72756160Sru every member of the filestack which has a FILENAME 72856160Sru member which is identical to the current INPUT_FILENAME, 72956160Sru search forward from that offset. */ 73056160Sru int saved_input_text_offset = input_text_offset; 73156160Sru int saved_input_text_length = input_text_length; 73256160Sru char *saved_input_text = input_text; 73356160Sru FSTACK *next_file = filestack; 73456160Sru 73556160Sru int orig_offset, orig_size; 73656160Sru 737146515Sru int bye_offset = search_forward ("\n@bye", input_text_offset); 738146515Sru 73956160Sru /* No matter what, make this file point back at `(dir)'. */ 74056160Sru free (up); 74156160Sru up = xstrdup ("(dir)"); /* html fixxme */ 74256160Sru 74356160Sru while (1) 74456160Sru { 74556160Sru orig_offset = input_text_offset; 74656160Sru orig_size = 74756160Sru search_forward (node_search_string, orig_offset); 74856160Sru 74956160Sru if (orig_size < 0) 75056160Sru orig_size = input_text_length; 75156160Sru 75256160Sru input_text_offset = search_forward ("\n@menu", orig_offset); 75356160Sru if (input_text_offset > -1 754146515Sru && (bye_offset > -1 && input_text_offset < bye_offset) 75556160Sru && cr_or_whitespace (input_text[input_text_offset + 6])) 75656160Sru { 75756160Sru char *nodename_from_menu = NULL; 75856160Sru 75956160Sru input_text_offset = 76056160Sru search_forward ("\n* ", input_text_offset); 76156160Sru 76256160Sru if (input_text_offset != -1) 76356160Sru nodename_from_menu = glean_node_from_menu (0, 0); 76456160Sru 76556160Sru if (nodename_from_menu) 76656160Sru { 76756160Sru free (next); 76856160Sru next = nodename_from_menu; 76956160Sru break; 77056160Sru } 77156160Sru } 77256160Sru 77356160Sru /* We got here, so it hasn't been found yet. Try 77456160Sru the next file on the filestack if there is one. */ 77556160Sru if (next_file 77656160Sru && FILENAME_CMP (next_file->filename, input_filename) 77756160Sru == 0) 77856160Sru { 77956160Sru input_text = next_file->text; 78056160Sru input_text_offset = next_file->offset; 78156160Sru input_text_length = next_file->size; 78256160Sru next_file = next_file->next; 78356160Sru } 78456160Sru else 78556160Sru { /* No more input files to check. */ 78656160Sru break; 78756160Sru } 78856160Sru } 78956160Sru 79056160Sru input_text = saved_input_text; 79156160Sru input_text_offset = saved_input_text_offset; 79256160Sru input_text_length = saved_input_text_length; 79356160Sru } 79456160Sru } 79556160Sru 79656160Sru /* Fix the level of the menu references in the Top node, iff it 79756160Sru was declared with @top, and no subsequent reference was found. */ 79856160Sru if (top_node_seen && !non_top_node_seen) 79956160Sru { 80056160Sru /* Then this is the first non-@top node seen. */ 80156160Sru int level; 80256160Sru 80356160Sru level = set_top_section_level (this_section - 1); 80456160Sru non_top_node_seen = 1; 80556160Sru 80656160Sru while (ref) 80756160Sru { 80856160Sru if (ref->section == level) 80956160Sru ref->section = this_section - 1; 81056160Sru ref = ref->next; 81156160Sru } 81256160Sru 81356160Sru ref = node_references; 81456160Sru } 81556160Sru 81656160Sru while (ref) 81756160Sru { 81856160Sru if (ref->section == (this_section - 1) 81956160Sru && ref->type == menu_reference 82056160Sru && strcmp (ref->node, node) == 0) 82156160Sru { 82256160Sru char *containing_node = ref->containing_node; 82356160Sru 82456160Sru free (up); 82556160Sru up = xstrdup (containing_node); 82656160Sru 82756160Sru if (last_ref 82856160Sru && last_ref->type == menu_reference 82956160Sru && strcmp (last_ref->containing_node, containing_node) == 0) 83056160Sru { 83156160Sru free (next); 83256160Sru next = xstrdup (last_ref->node); 83356160Sru } 83456160Sru 83556160Sru while (ref->section == this_section - 1 83656160Sru && ref->next 83756160Sru && ref->next->type != menu_reference) 83856160Sru ref = ref->next; 83956160Sru 84056160Sru if (ref->next && ref->type == menu_reference 84156160Sru && strcmp (ref->next->containing_node, containing_node) == 0) 84256160Sru { 84356160Sru free (prev); 84456160Sru prev = xstrdup (ref->next->node); 84556160Sru } 84656160Sru else if (!ref->next 84756160Sru && strcasecmp (ref->containing_node, "Top") == 0) 84856160Sru { 84956160Sru free (prev); 85056160Sru prev = xstrdup (ref->containing_node); 85156160Sru } 85256160Sru break; 85356160Sru } 85456160Sru last_ref = ref; 85556160Sru ref = ref->next; 85656160Sru } 85756160Sru } 85856160Sru 85956160Sru /* Insert the correct args if we are expanding macros, and the node's 86056160Sru pointers weren't defaulted. */ 86156160Sru if (macro_expansion_output_stream && !executing_string && !defaulting) 86256160Sru { 86356160Sru char *temp; 86456160Sru int op_orig = output_paragraph_offset; 86556160Sru int meta_pos_orig = meta_char_pos; 86656160Sru int extra = html ? strlen (node) : 0; 86756160Sru 86856160Sru temp = xmalloc (7 + extra + strlen (next) + strlen (prev) + strlen (up)); 86956160Sru sprintf (temp, "%s, %s, %s, %s", html ? node : "", next, prev, up); 87056160Sru me_execute_string (temp); 87156160Sru free (temp); 87256160Sru 87356160Sru output_paragraph_offset = op_orig; 87456160Sru meta_char_pos = meta_pos_orig; 87556160Sru } 87656160Sru 87756160Sru if (!*node) 87856160Sru { 87956160Sru line_error (_("No node name specified for `%c%s' command"), 88056160Sru COMMAND_PREFIX, command); 88156160Sru free (node); 88256160Sru free (next); next = NULL; 88356160Sru free (prev); prev= NULL; 88456160Sru free (up); up = NULL; 88556160Sru node_number++; /* else it doesn't get bumped */ 88656160Sru } 88756160Sru else 88856160Sru { 88956160Sru if (!*next) { free (next); next = NULL; } 89056160Sru if (!*prev) { free (prev); prev = NULL; } 89156160Sru if (!*up) { free (up); up = NULL; } 89293139Sru remember_node (node, prev, next, up, new_node_pos, line_number, 89393139Sru fname_for_this_node, no_warn); 89456160Sru outstanding_node = 1; 89556160Sru } 89656160Sru 89756160Sru if (html) 89856160Sru { 89993139Sru if (splitting && *node && output_stream == NULL) 90093139Sru { 90193139Sru char *dirname; 90293139Sru char filename[PATH_MAX]; 90356160Sru 90493139Sru dirname = pathname_part (current_output_filename); 90593139Sru strcpy (filename, dirname); 90693139Sru strcat (filename, fname_for_this_node); 90793139Sru free (dirname); 90856160Sru 90993139Sru /* See if the node name converted to a file name clashes 91093139Sru with other nodes or anchors. If it clashes with an 91193139Sru anchor, we complain and nuke that anchor's file. */ 91293139Sru if (!tag) 91393139Sru { 91493139Sru output_stream = fopen (filename, "w"); 91593139Sru html_output_head_p = 0; /* so that we generate HTML preamble */ 91693139Sru html_output_head (); 91793139Sru } 91893139Sru else if ((tag->flags & TAG_FLAG_ANCHOR) != 0) 91993139Sru { 92093139Sru line_error (_("Anchor `%s' and node `%s' map to the same file name"), 92193139Sru tag->node, node); 92293139Sru file_line_error (tag->filename, tag->line_no, 92393139Sru _("This @anchor command ignored; references to it will not work")); 92493139Sru file_line_error (tag->filename, tag->line_no, 92593139Sru _("Rename this anchor or use the `--no-split' option")); 92693139Sru /* Nuke the file name recorded in anchor's tag. 92793139Sru Since we are about to nuke the file itself, we 92893139Sru don't want find_node_by_fname to consider this 92993139Sru anchor anymore. */ 93093139Sru free (tag->html_fname); 93193139Sru tag->html_fname = NULL; 93293139Sru output_stream = fopen (filename, "w"); 93393139Sru html_output_head_p = 0; /* so that we generate HTML preamble */ 93493139Sru html_output_head (); 93593139Sru } 93693139Sru else 93793139Sru { 93893139Sru /* This node's file name clashes with another node. 93993139Sru We put them both on the same file. */ 94093139Sru output_stream = fopen (filename, "r+"); 94193139Sru if (output_stream) 94293139Sru { 94393139Sru static char html_end[] = "</body></html>\n"; 94493139Sru char end_line[sizeof(html_end)]; 94593139Sru int fpos = fseek (output_stream, -epilogue_len, 94693139Sru SEEK_END); 94793139Sru 94893139Sru if (fpos < 0 94993139Sru || fgets (end_line, sizeof (html_end), 95093139Sru output_stream) == NULL 95193139Sru /* Paranoia: did someone change the way HTML 95293139Sru files are finished up? */ 95393139Sru || strcasecmp (end_line, html_end) != 0) 95493139Sru { 95593139Sru line_error (_("Unexpected string at end of split-HTML file `%s'"), 95693139Sru fname_for_this_node); 95793139Sru fclose (output_stream); 95893139Sru xexit (1); 95993139Sru } 96093139Sru fseek (output_stream, -epilogue_len, SEEK_END); 96193139Sru } 96293139Sru } 96356160Sru if (output_stream == NULL) 96456160Sru { 96556160Sru fs_error (filename); 96656160Sru xexit (1); 96756160Sru } 96856160Sru set_current_output_filename (filename); 96956160Sru } 97056160Sru 97156160Sru if (!splitting && no_headers) 972146515Sru { /* cross refs need a name="#anchor" even if not writing headers */ 973146515Sru add_html_names (node); 97456160Sru } 97556160Sru 97656160Sru if (splitting || !no_headers) 977114472Sru { /* Navigation bar. */ 978146515Sru add_html_block_elt ("<div class=\"node\">\n"); 979114472Sru /* The <p> avoids the links area running on with old Lynxen. */ 98056160Sru add_word_args ("<p>%s\n", splitting ? "" : "<hr>"); 98156160Sru 982146515Sru /* In the split HTML case, the filename is wrong for the 983146515Sru old-style converted names, but we'll add them anyway, for 984146515Sru consistency. (And we need them in the normal (not 985146515Sru no_headers) nonsplit case.) */ 986146515Sru add_html_names (node); 987146515Sru 98856160Sru if (next) 98956160Sru { 99056160Sru tem = expansion (next, 0); 991146515Sru add_word ((char *) _("Next:")); 992116525Sru add_word (" "); 993146515Sru 994114472Sru add_word ("<a rel=\"next\" accesskey=\"n\" href=\""); 99593139Sru add_anchor_name (tem, 1); 996146515Sru tem = escape_string (tem); 99793139Sru add_word_args ("\">%s</a>", tem); 998146515Sru 99956160Sru free (tem); 1000146515Sru 1001146515Sru if (prev || up) 1002146515Sru add_word (",\n"); 100356160Sru } 100456160Sru if (prev) 100556160Sru { 100656160Sru tem = expansion (prev, 0); 1007146515Sru add_word ((char *) _("Previous:")); 1008116525Sru add_word (" "); 1009114472Sru add_word ("<a rel=\"previous\" accesskey=\"p\" href=\""); 101093139Sru add_anchor_name (tem, 1); 1011146515Sru tem = escape_string (tem); 101293139Sru add_word_args ("\">%s</a>", tem); 101356160Sru free (tem); 1014146515Sru 1015146515Sru if (up) 1016146515Sru add_word (",\n"); 101756160Sru } 101856160Sru if (up) 101956160Sru { 102056160Sru tem = expansion (up, 0); 1021146515Sru add_word ((char *) _("Up:")); 1022116525Sru add_word (" "); 1023114472Sru add_word ("<a rel=\"up\" accesskey=\"u\" href=\""); 102493139Sru add_anchor_name (tem, 1); 1025146515Sru tem = escape_string (tem); 102693139Sru add_word_args ("\">%s</a>", tem); 102756160Sru free (tem); 102856160Sru } 102956160Sru /* html fixxme: we want a `top' or `contents' link here. */ 103056160Sru 1031146515Sru add_word_args ("\n%s\n", splitting ? "<hr>" : ""); 1032114472Sru add_word ("</div>\n"); 103356160Sru } 103456160Sru } 103593139Sru else if (docbook) 103693139Sru ; 103793139Sru else if (xml) 103893139Sru { 103993139Sru if (next) 104093139Sru { 104193139Sru xml_insert_element (NODENEXT, START); 104293139Sru execute_string ("%s", next); 104393139Sru xml_insert_element (NODENEXT, END); 104493139Sru } 104593139Sru if (prev) 104693139Sru { 104793139Sru xml_insert_element (NODEPREV, START); 1048116525Sru execute_string ("%s", prev); 104993139Sru xml_insert_element (NODEPREV, END); 105093139Sru } 105193139Sru if (up) 105293139Sru { 105393139Sru xml_insert_element (NODEUP, START); 105493139Sru execute_string ("%s", up); 105593139Sru xml_insert_element (NODEUP, END); 105693139Sru } 105793139Sru } 105856160Sru else if (!no_headers) 105956160Sru { 106056160Sru if (macro_expansion_output_stream) 106156160Sru me_inhibit_expansion++; 106256160Sru 106356160Sru /* These strings are not translatable. */ 106456160Sru if (next) 106556160Sru { 106656160Sru execute_string (", Next: %s", next); 106756160Sru filling_enabled = indented_fill = 0; 106856160Sru } 106956160Sru if (prev) 107056160Sru { 107156160Sru execute_string (", Prev: %s", prev); 107256160Sru filling_enabled = indented_fill = 0; 107356160Sru } 107456160Sru if (up) 107556160Sru { 107656160Sru execute_string (", Up: %s", up); 107756160Sru filling_enabled = indented_fill = 0; 107856160Sru } 107956160Sru if (macro_expansion_output_stream) 108056160Sru me_inhibit_expansion--; 108156160Sru } 108256160Sru 108356160Sru close_paragraph (); 108456160Sru no_indent = 0; 108556160Sru 108656160Sru /* Change the section only if there was a sectioning command. */ 108756160Sru if (this_section >= 0) 108856160Sru current_section = this_section; 108956160Sru 109056160Sru if (current_node && STREQ (current_node, "Top")) 109156160Sru top_node_seen = 1; 109256160Sru 109356160Sru filling_enabled = 1; 109456160Sru in_fixed_width_font--; 109556160Sru} 109656160Sru 109756160Sru/* Cross-reference target at an arbitrary spot. */ 109856160Sruvoid 1099146515Srucm_anchor (int arg) 110056160Sru{ 110156160Sru char *anchor; 110293139Sru char *fname_for_anchor = NULL; 110356160Sru 110456160Sru if (arg == END) 110556160Sru return; 110656160Sru 110756160Sru /* Parse the anchor text. */ 110856160Sru anchor = get_xref_token (1); 110956160Sru 1110116525Sru /* Force all versions of "top" to be "Top". */ 1111116525Sru normalize_node_name (anchor); 1112116525Sru 111356160Sru /* In HTML mode, need to actually produce some output. */ 111456160Sru if (html) 111556160Sru { 111656160Sru /* If this anchor is at the beginning of a new paragraph, make 111756160Sru sure a new paragraph is indeed started. */ 111856160Sru if (!paragraph_is_open) 111956160Sru { 112093139Sru if (!executing_string && html) 112193139Sru html_output_head (); 112256160Sru start_paragraph (); 112356160Sru if (!in_fixed_width_font || in_menu || in_detailmenu) 112456160Sru { 112556160Sru insert_string ("<p>"); 112656160Sru in_paragraph = 1; 112756160Sru } 112856160Sru } 112956160Sru add_word ("<a name=\""); 113056160Sru add_anchor_name (anchor, 0); 113156160Sru add_word ("\"></a>"); 113293139Sru if (splitting) 113393139Sru { 113493139Sru /* If we are splitting, cm_xref will produce a reference to 113593139Sru a file whose name is derived from the anchor name. So we 113693139Sru must create a file when we see an @anchor, otherwise 113793139Sru xref's to anchors won't work. The file we create simply 113893139Sru redirects to the file of this anchor's node. */ 113993139Sru TAG_ENTRY *tag; 114093139Sru 114193139Sru fname_for_anchor = nodename_to_filename (anchor); 114293139Sru /* See if the anchor name converted to a file name clashes 114393139Sru with other anchors or nodes. */ 114493139Sru tag = find_node_by_fname (fname_for_anchor); 114593139Sru if (tag) 114693139Sru { 114793139Sru if ((tag->flags & TAG_FLAG_ANCHOR) != 0) 114893139Sru line_error (_("Anchors `%s' and `%s' map to the same file name"), 114993139Sru anchor, tag->node); 115093139Sru else 115193139Sru line_error (_("Anchor `%s' and node `%s' map to the same file name"), 115293139Sru anchor, tag->node); 115393139Sru line_error (_("@anchor command ignored; references to it will not work")); 115493139Sru line_error (_("Rename this anchor or use the `--no-split' option")); 115593139Sru free (fname_for_anchor); 115693139Sru /* We will not be creating a file for this anchor, so 115793139Sru set its name to NULL, so that remember_node stores a 115893139Sru NULL and find_node_by_fname won't consider this 115993139Sru anchor for clashes. */ 116093139Sru fname_for_anchor = NULL; 116193139Sru } 116293139Sru else 116393139Sru { 116493139Sru char *dirname, *p; 116593139Sru char filename[PATH_MAX]; 116693139Sru FILE *anchor_stream; 116793139Sru 116893139Sru dirname = pathname_part (current_output_filename); 116993139Sru strcpy (filename, dirname); 117093139Sru strcat (filename, fname_for_anchor); 117193139Sru free (dirname); 117293139Sru 117393139Sru anchor_stream = fopen (filename, "w"); 117493139Sru if (anchor_stream == NULL) 117593139Sru { 117693139Sru fs_error (filename); 117793139Sru xexit (1); 117893139Sru } 117993139Sru /* The HTML magic below will cause the browser to 118093139Sru immediately go to the anchor's node's file. Lynx 118193139Sru seems not to support this redirection, but it looks 118293139Sru like a bug in Lynx, and they can work around it by 118393139Sru clicking on the link once more. */ 118493139Sru fputs ("<meta http-equiv=\"refresh\" content=\"0; url=", 118593139Sru anchor_stream); 118693139Sru /* Make the indirect link point to the current node's 118793139Sru file and anchor's "<a name" label. If we don't have 118893139Sru a valid node name, refer to the current output file 118993139Sru instead. */ 119093139Sru if (current_node && *current_node) 119193139Sru { 119293139Sru char *fn, *tem; 119393139Sru 119493139Sru tem = expand_node_name (current_node); 119593139Sru fn = nodename_to_filename (tem); 119693139Sru free (tem); 119793139Sru fputs (fn, anchor_stream); 119893139Sru free (fn); 119993139Sru } 120093139Sru else 120193139Sru { 120293139Sru char *base = filename_part (current_output_filename); 120393139Sru 120493139Sru fputs (base, anchor_stream); 120593139Sru free (base); 120693139Sru } 120793139Sru fputs ("#", anchor_stream); 120893139Sru for (p = anchor; *p; p++) 120993139Sru { 121093139Sru if (*p == '&') 121193139Sru fputs ("&", anchor_stream); 121293139Sru else if (!URL_SAFE_CHAR (*p)) 121393139Sru fprintf (anchor_stream, "%%%x", (unsigned char) *p); 121493139Sru else 121593139Sru fputc (*p, anchor_stream); 121693139Sru } 121793139Sru fputs ("\">\n", anchor_stream); 121893139Sru fclose (anchor_stream); 121993139Sru } 122093139Sru } 122156160Sru } 122293139Sru else if (xml) 122393139Sru { 122493139Sru xml_insert_element_with_attribute (ANCHOR, START, "name=\"%s\"", anchor); 122593139Sru xml_insert_element (ANCHOR, END); 122693139Sru } 122756160Sru /* Save it in the tag table. */ 1228100513Sru remember_node (anchor, NULL, NULL, NULL, 1229100513Sru output_position + output_paragraph_offset, 123093139Sru line_number, fname_for_anchor, TAG_FLAG_ANCHOR); 123156160Sru} 123256160Sru 123356160Sru/* Find NODE in REF_LIST. */ 123456160Srustatic NODE_REF * 1235146515Srufind_node_reference (char *node, NODE_REF *ref_list) 123656160Sru{ 123756160Sru NODE_REF *orig_ref_list = ref_list; 123856160Sru char *expanded_node; 123956160Sru 124056160Sru while (ref_list) 124156160Sru { 124256160Sru if (strcmp (node, ref_list->node) == 0) 124356160Sru break; 124456160Sru ref_list = ref_list->next; 124556160Sru } 124656160Sru 124756160Sru if (ref_list || !expensive_validation) 124856160Sru return ref_list; 124956160Sru 125056160Sru /* Maybe NODE is not expanded yet. This may be SLOW. */ 125156160Sru expanded_node = expand_node_name (node); 125256160Sru for (ref_list = orig_ref_list; ref_list; ref_list = ref_list->next) 125356160Sru { 125456160Sru if (STREQ (expanded_node, ref_list->node)) 125556160Sru break; 125656160Sru if (strchr (ref_list->node, COMMAND_PREFIX)) 125756160Sru { 125856160Sru char *expanded_ref = expand_node_name (ref_list->node); 125956160Sru 126056160Sru if (STREQ (expanded_node, expanded_ref)) 126156160Sru { 126256160Sru free (expanded_ref); 126356160Sru break; 126456160Sru } 126556160Sru free (expanded_ref); 126656160Sru } 126756160Sru } 126856160Sru free (expanded_node); 126956160Sru return ref_list; 127056160Sru} 127156160Sru 127256160Sruvoid 1273146515Srufree_node_references (void) 127456160Sru{ 127556160Sru NODE_REF *list, *temp; 127656160Sru 127756160Sru list = node_references; 127856160Sru 127956160Sru while (list) 128056160Sru { 128156160Sru temp = list; 128256160Sru free (list->node); 128356160Sru free (list->containing_node); 128456160Sru list = list->next; 128556160Sru free (temp); 128656160Sru } 128756160Sru node_references = NULL; 128856160Sru} 128956160Sru 129056160Sruvoid 1291146515Srufree_node_node_references (void) 129256160Sru{ 129356160Sru NODE_REF *list, *temp; 129456160Sru 129556160Sru list = node_references; 129656160Sru 129756160Sru while (list) 129856160Sru { 129956160Sru temp = list; 130056160Sru free (list->node); 130156160Sru list = list->next; 130256160Sru free (temp); 130356160Sru } 130456160Sru node_node_references = NULL; 130556160Sru} 130656160Sru 130756160Sru/* Return the number assigned to a named node in either the tag_table 130856160Sru or node_references list or zero if no number has been assigned. */ 130956160Sruint 1310146515Srunumber_of_node (char *node) 131156160Sru{ 131256160Sru NODE_REF *temp_ref; 131356160Sru TAG_ENTRY *temp_node = find_node (node); 131456160Sru 131556160Sru if (temp_node) 131656160Sru return temp_node->number; 131756160Sru else if ((temp_ref = find_node_reference (node, node_references))) 131856160Sru return temp_ref->number; 131956160Sru else if ((temp_ref = find_node_reference (node, node_node_references))) 132056160Sru return temp_ref->number; 132156160Sru else 132256160Sru return 0; 132356160Sru} 132456160Sru 132556160Sru/* validation */ 132656160Sru 132756160Sru/* Return 1 if TAG (at LINE) correctly validated, or 0 if not. 132856160Sru LABEL is the (translated) description of the type of reference -- 132956160Sru Menu, Cross, Next, etc. */ 133056160Sru 133156160Srustatic int 1332146515Sruvalidate (char *tag, int line, const char *label) 133356160Sru{ 133456160Sru TAG_ENTRY *result; 133556160Sru 133656160Sru /* If there isn't a tag to verify, or if the tag is in another file, 133756160Sru then it must be okay. */ 133856160Sru if (!tag || !*tag || *tag == '(') 133956160Sru return 1; 134056160Sru 134156160Sru /* Otherwise, the tag must exist. */ 134256160Sru result = find_node (tag); 134356160Sru 134456160Sru if (!result) 134556160Sru { 134656160Sru line_number = line; 1347114472Sru line_error (_("%s reference to nonexistent node `%s' (perhaps incorrect sectioning?)"), label, tag); 134856160Sru return 0; 134956160Sru } 135056160Sru result->touched++; 135156160Sru return 1; 135256160Sru} 135356160Sru 135456160Sru/* The strings here are followed in the message by `reference to...' in 135556160Sru the `validate' routine. They are only used in messages, thus are 135656160Sru translated. */ 1357116525Srustatic const char * 1358146515Srureftype_type_string (enum reftype type) 135956160Sru{ 136056160Sru switch (type) 136156160Sru { 136256160Sru case menu_reference: 136356160Sru return _("Menu"); 136456160Sru case followed_reference: 136556160Sru return _("Cross"); 136656160Sru default: 136756160Sru return "Internal-bad-reference-type"; 136856160Sru } 136956160Sru} 137056160Sru 137156160Srustatic void 1372146515Sruvalidate_other_references (NODE_REF *ref_list) 137356160Sru{ 137456160Sru char *old_input_filename = input_filename; 137556160Sru 137656160Sru while (ref_list) 137756160Sru { 137856160Sru input_filename = ref_list->filename; 137956160Sru validate (ref_list->node, ref_list->line_no, 138056160Sru reftype_type_string (ref_list->type)); 138156160Sru ref_list = ref_list->next; 138256160Sru } 138356160Sru input_filename = old_input_filename; 138456160Sru} 138556160Sru 138656160Sru/* Validation of an info file. 138756160Sru Scan through the list of tag entries touching the Prev, Next, and Up 138856160Sru elements of each. It is an error not to be able to touch one of them, 138956160Sru except in the case of external node references, such as "(DIR)". 139056160Sru 139156160Sru If the Prev is different from the Up, 139256160Sru then the Prev node must have a Next pointing at this node. 139356160Sru 139456160Sru Every node except Top must have an Up. 139556160Sru The Up node must contain some sort of reference, other than a Next, 139656160Sru to this node. 139756160Sru 139856160Sru If the Next is different from the Next of the Up, 139956160Sru then the Next node must have a Prev pointing at this node. */ 140056160Sruvoid 1401146515Sruvalidate_file (TAG_ENTRY *tag_table) 140256160Sru{ 140356160Sru char *old_input_filename = input_filename; 140456160Sru TAG_ENTRY *tags = tag_table; 140556160Sru 140656160Sru while (tags) 140756160Sru { 140856160Sru TAG_ENTRY *temp_tag; 140956160Sru char *tem1, *tem2; 141056160Sru 141156160Sru input_filename = tags->filename; 141256160Sru line_number = tags->line_no; 141356160Sru 141456160Sru /* If this is a "no warn" node, don't validate it in any way. */ 141556160Sru if (tags->flags & TAG_FLAG_NO_WARN) 141656160Sru { 141756160Sru tags = tags->next_ent; 141856160Sru continue; 141956160Sru } 142056160Sru 142156160Sru /* If this node has a Next, then make sure that the Next exists. */ 142256160Sru if (tags->next) 142356160Sru { 142456160Sru validate (tags->next, tags->line_no, _("Next")); 142556160Sru 142656160Sru /* If the Next node exists, and there is no Up, then make sure 142756160Sru that the Prev of the Next points back. But do nothing if 142856160Sru we aren't supposed to issue warnings about this node. */ 142956160Sru temp_tag = find_node (tags->next); 143056160Sru if (temp_tag && !(temp_tag->flags & TAG_FLAG_NO_WARN)) 143156160Sru { 143256160Sru char *prev = temp_tag->prev; 143356160Sru int you_lose = !prev || !STREQ (prev, tags->node); 143456160Sru 143556160Sru if (you_lose && expensive_validation) 143656160Sru { 143756160Sru tem1 = expand_node_name (prev); 143856160Sru tem2 = expand_node_name (tags->node); 143956160Sru 1440146515Sru if (tem1 && tem2 && STREQ (tem1, tem2)) 144156160Sru you_lose = 0; 144256160Sru free (tem1); 144356160Sru free (tem2); 144456160Sru } 144556160Sru if (you_lose) 144656160Sru { 1447114472Sru line_error (_("Next field of node `%s' not pointed to (perhaps incorrect sectioning?)"), 144856160Sru tags->node); 144993139Sru file_line_error (temp_tag->filename, temp_tag->line_no, 145093139Sru _("This node (%s) has the bad Prev"), 145193139Sru temp_tag->node); 145256160Sru temp_tag->flags |= TAG_FLAG_PREV_ERROR; 145356160Sru } 145456160Sru } 145556160Sru } 145656160Sru 145756160Sru /* Validate the Prev field if there is one, and we haven't already 145856160Sru complained about it in some way. You don't have to have a Prev 145956160Sru field at this stage. */ 146056160Sru if (!(tags->flags & TAG_FLAG_PREV_ERROR) && tags->prev) 146156160Sru { 146256160Sru int valid_p = validate (tags->prev, tags->line_no, _("Prev")); 146356160Sru 146456160Sru if (!valid_p) 146556160Sru tags->flags |= TAG_FLAG_PREV_ERROR; 146656160Sru else 146756160Sru { /* If the Prev field is not the same as the Up field, 146856160Sru then the node pointed to by the Prev field must have 146956160Sru a Next field which points to this node. */ 147056160Sru int prev_equals_up = !tags->up || STREQ (tags->prev, tags->up); 147156160Sru 147256160Sru if (!prev_equals_up && expensive_validation) 147356160Sru { 147456160Sru tem1 = expand_node_name (tags->prev); 147556160Sru tem2 = expand_node_name (tags->up); 147656160Sru prev_equals_up = STREQ (tem1, tem2); 147756160Sru free (tem1); 147856160Sru free (tem2); 147956160Sru } 148056160Sru if (!prev_equals_up) 148156160Sru { 148256160Sru temp_tag = find_node (tags->prev); 148356160Sru 148456160Sru /* If we aren't supposed to issue warnings about the 148556160Sru target node, do nothing. */ 148656160Sru if (!temp_tag || (temp_tag->flags & TAG_FLAG_NO_WARN)) 148756160Sru /* Do nothing. */ ; 148856160Sru else 148956160Sru { 149056160Sru int you_lose = !temp_tag->next 149156160Sru || !STREQ (temp_tag->next, tags->node); 149256160Sru 149356160Sru if (temp_tag->next && you_lose && expensive_validation) 149456160Sru { 149556160Sru tem1 = expand_node_name (temp_tag->next); 149656160Sru tem2 = expand_node_name (tags->node); 149756160Sru if (STREQ (tem1, tem2)) 149856160Sru you_lose = 0; 149956160Sru free (tem1); 150056160Sru free (tem2); 150156160Sru } 150256160Sru if (you_lose) 150356160Sru { 150456160Sru line_error 150556160Sru (_("Prev field of node `%s' not pointed to"), 150656160Sru tags->node); 150793139Sru file_line_error (temp_tag->filename, 150893139Sru temp_tag->line_no, 150993139Sru _("This node (%s) has the bad Next"), 151093139Sru temp_tag->node); 151156160Sru temp_tag->flags |= TAG_FLAG_NEXT_ERROR; 151256160Sru } 151356160Sru } 151456160Sru } 151556160Sru } 151656160Sru } 151756160Sru 151856160Sru if (!tags->up 151956160Sru && !(tags->flags & TAG_FLAG_ANCHOR) 152056160Sru && strcasecmp (tags->node, "Top") != 0) 1521114472Sru line_error (_("`%s' has no Up field (perhaps incorrect sectioning?)"), tags->node); 152256160Sru else if (tags->up) 152356160Sru { 152456160Sru int valid_p = validate (tags->up, tags->line_no, _("Up")); 152556160Sru 152656160Sru /* If node X has Up: Y, then warn if Y fails to have a menu item 152756160Sru or note pointing at X, if Y isn't of the form "(Y)". */ 152856160Sru if (valid_p && *tags->up != '(') 152956160Sru { 153056160Sru NODE_REF *nref; 153156160Sru NODE_REF *tref = NULL; 153256160Sru NODE_REF *list = node_references; 153356160Sru 153456160Sru for (;;) 153556160Sru { 153656160Sru nref = find_node_reference (tags->node, list); 153756160Sru if (!nref) 153856160Sru break; 153956160Sru 154056160Sru if (strcmp (nref->containing_node, tags->up) == 0) 154156160Sru { 154256160Sru if (nref->type != menu_reference) 154356160Sru { 154456160Sru tref = nref; 154556160Sru list = nref->next; 154656160Sru } 154756160Sru else 154856160Sru break; 154956160Sru } 155056160Sru list = nref->next; 155156160Sru } 155256160Sru 155356160Sru if (!nref) 155456160Sru { 155556160Sru if (!tref && expensive_validation) 155656160Sru { 155756160Sru /* Sigh... This might be AWFULLY slow, but if 155856160Sru they want this feature, they'll have to pay! 155956160Sru We do all the loop again expanding each 156056160Sru containing_node reference as we go. */ 156156160Sru char *tags_up = expand_node_name (tags->up); 156256160Sru char *tem; 156356160Sru 156456160Sru list = node_references; 156556160Sru 156656160Sru for (;;) 156756160Sru { 156856160Sru nref = find_node_reference (tags->node, list); 156956160Sru if (!nref) 157056160Sru break; 157156160Sru tem = expand_node_name (nref->containing_node); 157256160Sru if (STREQ (tem, tags_up)) 157356160Sru { 157456160Sru if (nref->type != menu_reference) 157556160Sru tref = nref; 157656160Sru else 157756160Sru { 157856160Sru free (tem); 157956160Sru break; 158056160Sru } 158156160Sru } 158256160Sru free (tem); 158356160Sru list = nref->next; 158456160Sru } 158556160Sru } 158656160Sru if (!nref && !tref) 158756160Sru { 158856160Sru temp_tag = find_node (tags->up); 158993139Sru file_line_error (temp_tag->filename, temp_tag->line_no, 159056160Sru _("Node `%s' lacks menu item for `%s' despite being its Up target"), 159156160Sru tags->up, tags->node); 159256160Sru } 159356160Sru } 159456160Sru } 159556160Sru } 159656160Sru tags = tags->next_ent; 159756160Sru } 159856160Sru 159956160Sru validate_other_references (node_references); 160056160Sru /* We have told the user about the references which didn't exist. 160156160Sru Now tell him about the nodes which aren't referenced. */ 160256160Sru 160356160Sru for (tags = tag_table; tags; tags = tags->next_ent) 160456160Sru { 160556160Sru /* If this node is a "no warn" node, do nothing. */ 160656160Sru if (tags->flags & TAG_FLAG_NO_WARN) 160756160Sru { 160856160Sru tags = tags->next_ent; 160956160Sru continue; 161056160Sru } 161156160Sru 161256160Sru /* Special hack. If the node in question appears to have 161356160Sru been referenced more than REFERENCE_WARNING_LIMIT times, 161456160Sru give a warning. */ 161556160Sru if (tags->touched > reference_warning_limit) 161656160Sru { 161756160Sru input_filename = tags->filename; 161856160Sru line_number = tags->line_no; 161956160Sru warning (_("node `%s' has been referenced %d times"), 162056160Sru tags->node, tags->touched); 162156160Sru } 162256160Sru 162356160Sru if (tags->touched == 0) 162456160Sru { 162556160Sru input_filename = tags->filename; 162656160Sru line_number = tags->line_no; 162756160Sru 162856160Sru /* Notice that the node "Top" is special, and doesn't have to 162956160Sru be referenced. Anchors don't have to be referenced 163056160Sru either, you might define them for another document. */ 163156160Sru if (strcasecmp (tags->node, "Top") != 0 163256160Sru && !(tags->flags & TAG_FLAG_ANCHOR)) 163356160Sru warning (_("unreferenced node `%s'"), tags->node); 163456160Sru } 163556160Sru } 163656160Sru input_filename = old_input_filename; 163756160Sru} 163856160Sru 163956160Sru 164056160Sru/* Splitting */ 164156160Sru 164256160Sru/* Return true if the tag entry pointed to by TAGS is the last node. 164356160Sru This means only anchors follow. */ 164456160Sru 164556160Srustatic int 1646146515Srulast_node_p (TAG_ENTRY *tags) 164756160Sru{ 164856160Sru int last = 1; 164956160Sru while (tags->next_ent) { 165056160Sru tags = tags->next_ent; 165156160Sru if (tags->flags & TAG_FLAG_ANCHOR) 165256160Sru ; 165356160Sru else 165456160Sru { 165556160Sru last = 0; 165656160Sru break; 165756160Sru } 165856160Sru } 1659116525Sru 166056160Sru return last; 166156160Sru} 166256160Sru 166356160Sru 1664146515Srustatic char * 1665146515Sruenumerate_filename (char *pathname, char *basename, int number) 1666146515Sru{ 1667146515Sru /* Do we need to generate names of subfiles which don't exceed 8+3 limits? */ 1668146515Sru const int dos_file_names = !HAVE_LONG_FILENAMES (pathname ? pathname : "."); 1669146515Sru unsigned name_len = strlen (basename); 1670146515Sru char *filename = xmalloc (10 + strlen (pathname) + name_len); 1671146515Sru char *base_filename = xmalloc (10 + name_len); 1672146515Sru 1673146515Sru sprintf (base_filename, "%s-%d", basename, number); 1674146515Sru 1675146515Sru if (dos_file_names) 1676146515Sru { 1677146515Sru char *dot = strchr (base_filename, '.'); 1678146515Sru unsigned base_len = strlen (base_filename); 1679146515Sru 1680146515Sru if (dot) 1681146515Sru { /* Make foobar.i1, .., foobar.i99, foobar.100, ... */ 1682146515Sru dot[1] = 'i'; 1683146515Sru memmove (number <= 99 ? dot + 2 : dot + 1, 1684146515Sru base_filename + name_len + 1, 1685146515Sru strlen (base_filename + name_len + 1) + 1); 1686146515Sru } 1687146515Sru else if (base_len > 8) 1688146515Sru { 1689146515Sru /* Make foobar-1, .., fooba-10, .., foob-100, ... */ 1690146515Sru unsigned numlen = base_len - name_len; 1691146515Sru 1692146515Sru memmove (base_filename + 8 - numlen, base_filename + name_len, numlen + 1); 1693146515Sru } 1694146515Sru } 1695146515Sru 1696146515Sru sprintf (filename, "%s%s", pathname, base_filename); 1697146515Sru 1698146515Sru return filename; 1699146515Sru} 1700146515Sru 1701146515Sru/* Remove previously split files, to avoid 1702146515Sru lingering parts of shrinked documents. */ 1703146515Sruvoid 1704146515Sruclean_old_split_files (char *filename) 1705146515Sru{ 1706146515Sru char *root_filename = filename_part (filename); 1707146515Sru char *root_pathname = pathname_part (filename); 1708146515Sru int i; 1709146515Sru 1710146515Sru /* We break as soon as we hit an inexistent file, 1711146515Sru so looping until large numbers is harmless. */ 1712146515Sru for (i = 1; i < 1000; i++) 1713146515Sru { 1714146515Sru struct stat st; 1715146515Sru char *check_file = enumerate_filename (root_pathname, root_filename, i); 1716146515Sru 1717146515Sru if (stat (check_file, &st) != 0) 1718146515Sru break; 1719146515Sru else if (!S_ISDIR (st.st_mode)) 1720146515Sru { 1721146515Sru /* Give feedback if requested, removing a file is important. */ 1722146515Sru if (verbose_mode) 1723146515Sru printf (_("Removing %s\n"), check_file); 1724146515Sru 1725146515Sru /* Warn user that we cannot remove the file. */ 1726146515Sru if (unlink (check_file) != 0) 1727146515Sru warning (_("Can't remove file `%s': %s"), check_file, strerror (errno)); 1728146515Sru } 1729146515Sru 1730146515Sru free (check_file); 1731146515Sru } 1732146515Sru} 1733146515Sru 1734146515Sru 173556160Sru/* Split large output files into a series of smaller files. Each file 173656160Sru is pointed to in the tag table, which then gets written out as the 173756160Sru original file. The new files have the same name as the original file 173856160Sru with a "-num" attached. SIZE is the largest number of bytes to allow 173956160Sru in any single split file. */ 174056160Sruvoid 1741146515Srusplit_file (char *filename, int size) 174256160Sru{ 174356160Sru char *root_filename, *root_pathname; 1744146515Sru char *the_file; 174556160Sru struct stat fileinfo; 174656160Sru long file_size; 174756160Sru char *the_header; 174856160Sru int header_size; 174956160Sru 175056160Sru /* Can only do this to files with tag tables. */ 175156160Sru if (!tag_table) 175256160Sru return; 175356160Sru 175456160Sru if (size == 0) 175556160Sru size = DEFAULT_SPLIT_SIZE; 175656160Sru 1757116525Sru if ((stat (filename, &fileinfo) != 0) 1758116525Sru || (((long) fileinfo.st_size) < size)) 175956160Sru return; 176056160Sru file_size = (long) fileinfo.st_size; 176156160Sru 1762146515Sru the_file = find_and_load (filename, 0); 176356160Sru if (!the_file) 176456160Sru return; 176556160Sru 176656160Sru root_filename = filename_part (filename); 176756160Sru root_pathname = pathname_part (filename); 176856160Sru 176956160Sru if (!root_pathname) 177056160Sru root_pathname = xstrdup (""); 177156160Sru 177256160Sru /* Start splitting the file. Walk along the tag table 177356160Sru outputting sections of the file. When we have written 177456160Sru all of the nodes in the tag table, make the top-level 177556160Sru pointer file, which contains indirect pointers and 177656160Sru tags for the nodes. */ 177756160Sru { 177856160Sru int which_file = 1; 177956160Sru TAG_ENTRY *tags = tag_table; 178056160Sru char *indirect_info = NULL; 178156160Sru 1782116525Sru /* Maybe we want a Local Variables section. */ 1783116525Sru char *trailer = info_trailer (); 1784116525Sru int trailer_len = trailer ? strlen (trailer) : 0; 1785116525Sru 178656160Sru /* Remember the `header' of this file. The first tag in the file is 178756160Sru the bottom of the header; the top of the file is the start. */ 178856160Sru the_header = xmalloc (1 + (header_size = tags->position)); 178956160Sru memcpy (the_header, the_file, header_size); 179056160Sru 179156160Sru while (tags) 179256160Sru { 179356160Sru int file_top, file_bot, limit; 179456160Sru 179556160Sru /* Have to include the Control-_. */ 179656160Sru file_top = file_bot = tags->position; 179756160Sru limit = file_top + size; 179856160Sru 179956160Sru /* If the rest of this file is only one node, then 180056160Sru that is the entire subfile. */ 180156160Sru if (last_node_p (tags)) 180256160Sru { 180356160Sru int i = tags->position + 1; 180456160Sru char last_char = the_file[i]; 180556160Sru 180656160Sru while (i < file_size) 180756160Sru { 180856160Sru if ((the_file[i] == '\037') && 180956160Sru ((last_char == '\n') || 181056160Sru (last_char == '\014'))) 181156160Sru break; 181256160Sru else 181356160Sru last_char = the_file[i]; 181456160Sru i++; 181556160Sru } 181656160Sru file_bot = i; 181756160Sru tags = tags->next_ent; 181856160Sru goto write_region; 181956160Sru } 182056160Sru 182156160Sru /* Otherwise, find the largest number of nodes that can fit in 182256160Sru this subfile. */ 182356160Sru for (; tags; tags = tags->next_ent) 182456160Sru { 182556160Sru if (last_node_p (tags)) 182656160Sru { 182756160Sru /* This entry is the last node. Search forward for the end 182856160Sru of this node, and that is the end of this file. */ 182956160Sru int i = tags->position + 1; 183056160Sru char last_char = the_file[i]; 183156160Sru 183256160Sru while (i < file_size) 183356160Sru { 183456160Sru if ((the_file[i] == '\037') && 183556160Sru ((last_char == '\n') || 183656160Sru (last_char == '\014'))) 183756160Sru break; 183856160Sru else 183956160Sru last_char = the_file[i]; 184056160Sru i++; 184156160Sru } 184256160Sru file_bot = i; 184356160Sru 184456160Sru if (file_bot < limit) 184556160Sru { 184656160Sru tags = tags->next_ent; 184756160Sru goto write_region; 184856160Sru } 184956160Sru else 185056160Sru { 185156160Sru /* Here we want to write out everything before the last 185256160Sru node, and then write the last node out in a file 185356160Sru by itself. */ 185456160Sru file_bot = tags->position; 185556160Sru goto write_region; 185656160Sru } 185756160Sru } 185856160Sru 185956160Sru /* Write region only if this was a node, not an anchor. */ 186056160Sru if (tags->next_ent->position > limit 186156160Sru && !(tags->flags & TAG_FLAG_ANCHOR)) 186256160Sru { 186356160Sru if (tags->position == file_top) 186456160Sru tags = tags->next_ent; 186556160Sru 186656160Sru file_bot = tags->position; 186756160Sru 186856160Sru write_region: 186956160Sru { 187056160Sru int fd; 1871146515Sru char *split_filename = enumerate_filename (root_pathname, 1872146515Sru root_filename, which_file); 1873146515Sru char *split_basename = filename_part (split_filename); 187456160Sru 187556160Sru fd = open (split_filename, O_WRONLY|O_TRUNC|O_CREAT, 0666); 187656160Sru if (fd < 0 187756160Sru || write (fd, the_header, header_size) != header_size 187856160Sru || write (fd, the_file + file_top, file_bot - file_top) 187956160Sru != (file_bot - file_top) 1880116525Sru || (trailer_len 1881116525Sru && write (fd, trailer, trailer_len) != trailer_len) 1882116525Sru || close (fd) < 0) 188356160Sru { 188456160Sru perror (split_filename); 188556160Sru if (fd != -1) 188656160Sru close (fd); 188756160Sru xexit (1); 188856160Sru } 188956160Sru 189056160Sru if (!indirect_info) 189156160Sru { 189256160Sru indirect_info = the_file + file_top; 189356160Sru sprintf (indirect_info, "\037\nIndirect:\n"); 189456160Sru indirect_info += strlen (indirect_info); 189556160Sru } 189656160Sru 189756160Sru sprintf (indirect_info, "%s: %d\n", 189856160Sru split_basename, file_top); 189956160Sru 190056160Sru free (split_basename); 190156160Sru free (split_filename); 190256160Sru indirect_info += strlen (indirect_info); 190356160Sru which_file++; 190456160Sru break; 190556160Sru } 190656160Sru } 190756160Sru } 190856160Sru } 190956160Sru 191056160Sru /* We have sucessfully created the subfiles. Now write out the 191156160Sru original again. We must use `output_stream', or 191256160Sru write_tag_table_indirect () won't know where to place the output. */ 191356160Sru output_stream = fopen (filename, "w"); 191456160Sru if (!output_stream) 191556160Sru { 191656160Sru perror (filename); 191756160Sru xexit (1); 191856160Sru } 191956160Sru 192056160Sru { 192156160Sru int distance = indirect_info - the_file; 192256160Sru fwrite (the_file, 1, distance, output_stream); 192356160Sru 192456160Sru /* Inhibit newlines. */ 192556160Sru paragraph_is_open = 0; 192656160Sru 1927116525Sru /* Write the indirect tag table. */ 192856160Sru write_tag_table_indirect (); 1929116525Sru 1930116525Sru /* preserve local variables in info output. */ 1931116525Sru if (trailer) 1932116525Sru { 1933146515Sru fwrite (trailer, 1, trailer_len, output_stream); 1934116525Sru free (trailer); 1935116525Sru } 1936116525Sru 193756160Sru fclose (output_stream); 193856160Sru free (the_header); 193956160Sru free (the_file); 194056160Sru return; 194156160Sru } 194256160Sru } 194356160Sru} 1944