193139Sru/* files.c -- file-related functions for makeinfo. 2146515Sru $Id: files.c,v 1.5 2004/07/27 00:06:31 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 "files.h" 23146515Sru#include "html.h" 24146515Sru#include "index.h" 2556160Sru#include "macro.h" 2656160Sru#include "makeinfo.h" 27146515Sru#include "node.h" 2856160Sru 2956160SruFSTACK *filestack = NULL; 3056160Sru 3156160Srustatic int node_filename_stack_index = 0; 3256160Srustatic int node_filename_stack_size = 0; 3356160Srustatic char **node_filename_stack = NULL; 3456160Sru 3556160Sru/* Looking for include files. */ 3656160Sru 3756160Sru/* Given a string containing units of information separated by colons, 3856160Sru return the next one pointed to by INDEX, or NULL if there are no more. 3956160Sru Advance INDEX to the character after the colon. */ 4056160Srustatic char * 41146515Sruextract_colon_unit (char *string, int *index) 4256160Sru{ 4356160Sru int start; 4456160Sru int path_sep_char = PATH_SEP[0]; 4556160Sru int i = *index; 4656160Sru 4756160Sru if (!string || (i >= strlen (string))) 4856160Sru return NULL; 4956160Sru 5056160Sru /* Each call to this routine leaves the index pointing at a colon if 5156160Sru there is more to the path. If i > 0, then increment past the 5256160Sru `:'. If i == 0, then the path has a leading colon. Trailing colons 5356160Sru are handled OK by the `else' part of the if statement; an empty 5456160Sru string is returned in that case. */ 5556160Sru if (i && string[i] == path_sep_char) 5656160Sru i++; 5756160Sru 5856160Sru start = i; 5956160Sru while (string[i] && string[i] != path_sep_char) i++; 6056160Sru *index = i; 6156160Sru 6256160Sru if (i == start) 6356160Sru { 6456160Sru if (string[i]) 6556160Sru (*index)++; 6656160Sru 6756160Sru /* Return "" in the case of a trailing `:'. */ 6856160Sru return xstrdup (""); 6956160Sru } 7056160Sru else 7156160Sru { 7256160Sru char *value; 7356160Sru 7456160Sru value = xmalloc (1 + (i - start)); 7556160Sru memcpy (value, &string[start], (i - start)); 7656160Sru value [i - start] = 0; 7756160Sru 7856160Sru return value; 7956160Sru } 8056160Sru} 8156160Sru 8256160Sru/* Return the full pathname for FILENAME by searching along PATH. 8356160Sru When found, return the stat () info for FILENAME in FINFO. 8456160Sru If PATH is NULL, only the current directory is searched. 8556160Sru If the file could not be found, return a NULL pointer. */ 86116525Sruchar * 87146515Sruget_file_info_in_path (char *filename, char *path, struct stat *finfo) 8856160Sru{ 8956160Sru char *dir; 9056160Sru int result, index = 0; 9156160Sru 9256160Sru if (path == NULL) 9356160Sru path = "."; 9456160Sru 9556160Sru /* Handle absolute pathnames. */ 9656160Sru if (IS_ABSOLUTE (filename) 9756160Sru || (*filename == '.' 9856160Sru && (IS_SLASH (filename[1]) 9956160Sru || (filename[1] == '.' && IS_SLASH (filename[2]))))) 10056160Sru { 10156160Sru if (stat (filename, finfo) == 0) 10256160Sru return xstrdup (filename); 10356160Sru else 10456160Sru return NULL; 10556160Sru } 10656160Sru 10756160Sru while ((dir = extract_colon_unit (path, &index))) 10856160Sru { 10956160Sru char *fullpath; 11056160Sru 11156160Sru if (!*dir) 11256160Sru { 11356160Sru free (dir); 11456160Sru dir = xstrdup ("."); 11556160Sru } 11656160Sru 11756160Sru fullpath = xmalloc (2 + strlen (dir) + strlen (filename)); 11856160Sru sprintf (fullpath, "%s/%s", dir, filename); 11956160Sru free (dir); 12056160Sru 12156160Sru result = stat (fullpath, finfo); 12256160Sru 12356160Sru if (result == 0) 12456160Sru return fullpath; 12556160Sru else 12656160Sru free (fullpath); 12756160Sru } 12856160Sru return NULL; 12956160Sru} 130146515Sru 131146515Sru/* Prepend and append new paths to include_files_path. */ 132146515Sruvoid 133146515Sruprepend_to_include_path (char *path) 134146515Sru{ 135146515Sru if (!include_files_path) 136146515Sru { 137146515Sru include_files_path = xstrdup (path); 138146515Sru include_files_path = xrealloc (include_files_path, 139146515Sru strlen (include_files_path) + 3); /* 3 for ":.\0" */ 140146515Sru strcat (strcat (include_files_path, PATH_SEP), "."); 141146515Sru } 142146515Sru else 143146515Sru { 144146515Sru char *tmp = xstrdup (include_files_path); 145146515Sru include_files_path = xrealloc (include_files_path, 146146515Sru strlen (include_files_path) + strlen (path) + 2); /* 2 for ":\0" */ 147146515Sru strcpy (include_files_path, path); 148146515Sru strcat (include_files_path, PATH_SEP); 149146515Sru strcat (include_files_path, tmp); 150146515Sru free (tmp); 151146515Sru } 152146515Sru} 153146515Sru 154146515Sruvoid 155146515Sruappend_to_include_path (char *path) 156146515Sru{ 157146515Sru if (!include_files_path) 158146515Sru include_files_path = xstrdup ("."); 159146515Sru 160146515Sru include_files_path = (char *) xrealloc (include_files_path, 161146515Sru 2 + strlen (include_files_path) + strlen (path)); 162146515Sru strcat (include_files_path, PATH_SEP); 163146515Sru strcat (include_files_path, path); 164146515Sru} 165146515Sru 166146515Sru/* Remove the first path from the include_files_path. */ 167146515Sruvoid 168146515Srupop_path_from_include_path (void) 169146515Sru{ 170146515Sru int i = 0; 171146515Sru char *tmp; 172146515Sru 173146515Sru if (include_files_path) 174146515Sru for (i = 0; i < strlen (include_files_path) 175146515Sru && include_files_path[i] != ':'; i++); 176146515Sru 177146515Sru /* Advance include_files_path to the next char from ':' */ 178146515Sru tmp = (char *) xmalloc (strlen (include_files_path) - i); 179146515Sru strcpy (tmp, (char *) include_files_path + i + 1); 180146515Sru 181146515Sru free (include_files_path); 182146515Sru include_files_path = tmp; 183146515Sru} 18456160Sru 18556160Sru/* Find and load the file named FILENAME. Return a pointer to 186146515Sru the loaded file, or NULL if it can't be loaded. If USE_PATH is zero, 187146515Sru just look for the given file (this is used in handle_delayed_writes), 188146515Sru else search along include_files_path. */ 189146515Sru 19056160Sruchar * 191146515Srufind_and_load (char *filename, int use_path) 19256160Sru{ 19356160Sru struct stat fileinfo; 19456160Sru long file_size; 19556160Sru int file = -1, count = 0; 19656160Sru char *fullpath, *result; 19793139Sru int n, bytes_to_read; 19856160Sru 19956160Sru result = fullpath = NULL; 20056160Sru 201146515Sru fullpath 202146515Sru = get_file_info_in_path (filename, use_path ? include_files_path : NULL, 203146515Sru &fileinfo); 20456160Sru 20556160Sru if (!fullpath) 20656160Sru goto error_exit; 20756160Sru 20856160Sru filename = fullpath; 20956160Sru file_size = (long) fileinfo.st_size; 21056160Sru 21156160Sru file = open (filename, O_RDONLY); 21256160Sru if (file < 0) 21356160Sru goto error_exit; 21456160Sru 21556160Sru /* Load the file, with enough room for a newline and a null. */ 21656160Sru result = xmalloc (file_size + 2); 21756160Sru 21856160Sru /* VMS stat lies about the st_size value. The actual number of 21956160Sru readable bytes is always less than this value. The arcane 22056160Sru mysteries of VMS/RMS are too much to probe, so this hack 22193139Sru suffices to make things work. It's also needed on Cygwin. And so 22293139Sru we might as well use it everywhere. */ 22393139Sru bytes_to_read = file_size; 22493139Sru while ((n = read (file, result + count, bytes_to_read)) > 0) 22593139Sru { 22693139Sru count += n; 22793139Sru bytes_to_read -= n; 22893139Sru } 22956160Sru if (0 < count && count < file_size) 23056160Sru result = xrealloc (result, count + 2); /* why waste the slack? */ 23156160Sru else if (n == -1) 23293139Sruerror_exit: 23356160Sru { 23456160Sru if (result) 23556160Sru free (result); 23656160Sru 23756160Sru if (fullpath) 23856160Sru free (fullpath); 23956160Sru 24056160Sru if (file != -1) 24156160Sru close (file); 24256160Sru 24356160Sru return NULL; 24456160Sru } 24556160Sru close (file); 24656160Sru 24756160Sru /* Set the globals to the new file. */ 24856160Sru input_text = result; 24956160Sru input_text_length = count; 25056160Sru input_filename = fullpath; 25156160Sru node_filename = xstrdup (fullpath); 25256160Sru input_text_offset = 0; 25356160Sru line_number = 1; 25456160Sru /* Not strictly necessary. This magic prevents read_token () from doing 25556160Sru extra unnecessary work each time it is called (that is a lot of times). 25656160Sru INPUT_TEXT_LENGTH is one past the actual end of the text. */ 25756160Sru input_text[input_text_length] = '\n'; 25856160Sru /* This, on the other hand, is always necessary. */ 25956160Sru input_text[input_text_length+1] = 0; 26056160Sru return result; 26156160Sru} 26256160Sru 26356160Sru/* Pushing and popping files. */ 264146515Srustatic void 265146515Srupush_node_filename (void) 26656160Sru{ 26756160Sru if (node_filename_stack_index + 1 > node_filename_stack_size) 26856160Sru node_filename_stack = xrealloc 26956160Sru (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *)); 27056160Sru 27156160Sru node_filename_stack[node_filename_stack_index] = node_filename; 27256160Sru node_filename_stack_index++; 27356160Sru} 27456160Sru 275146515Srustatic void 276146515Srupop_node_filename (void) 27756160Sru{ 27856160Sru node_filename = node_filename_stack[--node_filename_stack_index]; 27956160Sru} 28056160Sru 28156160Sru/* Save the state of the current input file. */ 28256160Sruvoid 283146515Srupushfile (void) 28456160Sru{ 28556160Sru FSTACK *newstack = xmalloc (sizeof (FSTACK)); 28656160Sru newstack->filename = input_filename; 28756160Sru newstack->text = input_text; 28856160Sru newstack->size = input_text_length; 28956160Sru newstack->offset = input_text_offset; 29056160Sru newstack->line_number = line_number; 29156160Sru newstack->next = filestack; 29256160Sru 29356160Sru filestack = newstack; 29456160Sru push_node_filename (); 29556160Sru} 29656160Sru 29756160Sru/* Make the current file globals be what is on top of the file stack. */ 29856160Sruvoid 299146515Srupopfile (void) 30056160Sru{ 30156160Sru FSTACK *tos = filestack; 30256160Sru 30356160Sru if (!tos) 30456160Sru abort (); /* My fault. I wonder what I did? */ 30556160Sru 30656160Sru if (macro_expansion_output_stream) 30756160Sru { 30856160Sru maybe_write_itext (input_text, input_text_offset); 30956160Sru forget_itext (input_text); 31056160Sru } 31156160Sru 31256160Sru /* Pop the stack. */ 31356160Sru filestack = filestack->next; 31456160Sru 31556160Sru /* Make sure that commands with braces have been satisfied. */ 31656160Sru if (!executing_string && !me_executing_string) 31756160Sru discard_braces (); 31856160Sru 31956160Sru /* Get the top of the stack into the globals. */ 32056160Sru input_filename = tos->filename; 32156160Sru input_text = tos->text; 32256160Sru input_text_length = tos->size; 32356160Sru input_text_offset = tos->offset; 32456160Sru line_number = tos->line_number; 32556160Sru free (tos); 32656160Sru 32756160Sru /* Go back to the (now) current node. */ 32856160Sru pop_node_filename (); 32956160Sru} 33056160Sru 33156160Sru/* Flush all open files on the file stack. */ 33256160Sruvoid 333146515Sruflush_file_stack (void) 33456160Sru{ 33556160Sru while (filestack) 33656160Sru { 33756160Sru char *fname = input_filename; 33856160Sru char *text = input_text; 33956160Sru popfile (); 34056160Sru free (fname); 34156160Sru free (text); 34256160Sru } 34356160Sru} 34456160Sru 34556160Sru/* Return the index of the first character in the filename 34656160Sru which is past all the leading directory characters. */ 34756160Srustatic int 348146515Sruskip_directory_part (char *filename) 34956160Sru{ 35056160Sru int i = strlen (filename) - 1; 35156160Sru 35256160Sru while (i && !IS_SLASH (filename[i])) 35356160Sru i--; 35456160Sru if (IS_SLASH (filename[i])) 35556160Sru i++; 35656160Sru else if (filename[i] && HAVE_DRIVE (filename)) 35756160Sru i = 2; 35856160Sru 35956160Sru return i; 36056160Sru} 36156160Sru 362146515Srustatic char * 363146515Srufilename_non_directory (char *name) 36456160Sru{ 36556160Sru return xstrdup (name + skip_directory_part (name)); 36656160Sru} 36756160Sru 36856160Sru/* Return just the simple part of the filename; i.e. the 36956160Sru filename without the path information, or extensions. 37056160Sru This conses up a new string. */ 37156160Sruchar * 372146515Srufilename_part (char *filename) 37356160Sru{ 37456160Sru char *basename = filename_non_directory (filename); 37556160Sru 37656160Sru#ifdef REMOVE_OUTPUT_EXTENSIONS 37756160Sru /* See if there is an extension to remove. If so, remove it. */ 37856160Sru { 379116525Sru char *temp = strrchr (basename, '.'); 38056160Sru if (temp) 38156160Sru *temp = 0; 38256160Sru } 38356160Sru#endif /* REMOVE_OUTPUT_EXTENSIONS */ 38456160Sru return basename; 38556160Sru} 38656160Sru 38756160Sru/* Return the pathname part of filename. This can be NULL. */ 38856160Sruchar * 389146515Srupathname_part (char *filename) 39056160Sru{ 39156160Sru char *result = NULL; 39256160Sru int i; 39356160Sru 39456160Sru filename = expand_filename (filename, ""); 39556160Sru 39656160Sru i = skip_directory_part (filename); 39756160Sru if (i) 39856160Sru { 39956160Sru result = xmalloc (1 + i); 40056160Sru strncpy (result, filename, i); 40156160Sru result[i] = 0; 40256160Sru } 40356160Sru free (filename); 40456160Sru return result; 40556160Sru} 40656160Sru 40756160Sru/* Return the full path to FILENAME. */ 408146515Srustatic char * 409146515Srufull_pathname (char *filename) 41056160Sru{ 41156160Sru int initial_character; 41256160Sru char *result; 41356160Sru 41456160Sru /* No filename given? */ 41556160Sru if (!filename || !*filename) 41656160Sru return xstrdup (""); 41756160Sru 41856160Sru /* Already absolute? */ 41956160Sru if (IS_ABSOLUTE (filename) || 42056160Sru (*filename == '.' && 42156160Sru (IS_SLASH (filename[1]) || 42256160Sru (filename[1] == '.' && IS_SLASH (filename[2]))))) 42356160Sru return xstrdup (filename); 42456160Sru 42556160Sru initial_character = *filename; 42656160Sru if (initial_character != '~') 42756160Sru { 42856160Sru char *localdir = xmalloc (1025); 42956160Sru#ifdef HAVE_GETCWD 43056160Sru if (!getcwd (localdir, 1024)) 43156160Sru#else 43256160Sru if (!getwd (localdir)) 43356160Sru#endif 43456160Sru { 43556160Sru fprintf (stderr, _("%s: getwd: %s, %s\n"), 43656160Sru progname, filename, localdir); 43756160Sru xexit (1); 43856160Sru } 43956160Sru 44056160Sru strcat (localdir, "/"); 44156160Sru strcat (localdir, filename); 44256160Sru result = xstrdup (localdir); 44356160Sru free (localdir); 44456160Sru } 44556160Sru else 44656160Sru { /* Does anybody know why WIN32 doesn't want to support $HOME? 44756160Sru If the reason is they don't have getpwnam, they should 44856160Sru only disable the else clause below. */ 44956160Sru#ifndef WIN32 45056160Sru if (IS_SLASH (filename[1])) 45156160Sru { 45256160Sru /* Return the concatenation of the environment variable HOME 45356160Sru and the rest of the string. */ 45456160Sru char *temp_home; 45556160Sru 45656160Sru temp_home = (char *) getenv ("HOME"); 45756160Sru result = xmalloc (strlen (&filename[1]) 45856160Sru + 1 45956160Sru + temp_home ? strlen (temp_home) 46056160Sru : 0); 46156160Sru *result = 0; 46256160Sru 46356160Sru if (temp_home) 46456160Sru strcpy (result, temp_home); 46556160Sru 46656160Sru strcat (result, &filename[1]); 46756160Sru } 46856160Sru else 46956160Sru { 47056160Sru struct passwd *user_entry; 47156160Sru int i, c; 47256160Sru char *username = xmalloc (257); 47356160Sru 47456160Sru for (i = 1; (c = filename[i]); i++) 47556160Sru { 47656160Sru if (IS_SLASH (c)) 47756160Sru break; 47856160Sru else 47956160Sru username[i - 1] = c; 48056160Sru } 48156160Sru if (c) 48256160Sru username[i - 1] = 0; 48356160Sru 48456160Sru user_entry = getpwnam (username); 48556160Sru 48656160Sru if (!user_entry) 48756160Sru return xstrdup (filename); 48856160Sru 48956160Sru result = xmalloc (1 + strlen (user_entry->pw_dir) 49056160Sru + strlen (&filename[i])); 49156160Sru strcpy (result, user_entry->pw_dir); 49256160Sru strcat (result, &filename[i]); 49356160Sru } 49456160Sru#endif /* not WIN32 */ 49556160Sru } 49656160Sru return result; 49756160Sru} 49856160Sru 499146515Sru/* Return the expansion of FILENAME. */ 50056160Sruchar * 501146515Sruexpand_filename (char *filename, char *input_name) 50256160Sru{ 503146515Sru int i; 504146515Sru 505146515Sru if (filename) 506146515Sru { 507146515Sru filename = full_pathname (filename); 508146515Sru if (IS_ABSOLUTE (filename) 509146515Sru || (*filename == '.' && 510146515Sru (IS_SLASH (filename[1]) || 511146515Sru (filename[1] == '.' && IS_SLASH (filename[2]))))) 512146515Sru return filename; 513146515Sru } 514146515Sru else 515146515Sru { 516146515Sru filename = filename_non_directory (input_name); 517146515Sru 518146515Sru if (!*filename) 519146515Sru { 520146515Sru free (filename); 521146515Sru filename = xstrdup ("noname.texi"); 522146515Sru } 523146515Sru 524146515Sru for (i = strlen (filename) - 1; i; i--) 525146515Sru if (filename[i] == '.') 526146515Sru break; 527146515Sru 528146515Sru if (!i) 529146515Sru i = strlen (filename); 530146515Sru 531146515Sru if (i + 6 > (strlen (filename))) 532146515Sru filename = xrealloc (filename, i + 6); 533146515Sru strcpy (filename + i, html ? ".html" : ".info"); 534146515Sru return filename; 535146515Sru } 536146515Sru 537146515Sru if (IS_ABSOLUTE (input_name)) 538146515Sru { 539146515Sru /* Make it so that relative names work. */ 540146515Sru char *result; 541146515Sru 542146515Sru i = strlen (input_name) - 1; 543146515Sru 544146515Sru result = xmalloc (1 + strlen (input_name) + strlen (filename)); 545146515Sru strcpy (result, input_name); 546146515Sru 547146515Sru while (!IS_SLASH (result[i]) && i) 548146515Sru i--; 549146515Sru if (IS_SLASH (result[i])) 550146515Sru i++; 551146515Sru 552146515Sru strcpy (&result[i], filename); 553146515Sru free (filename); 554146515Sru return result; 555146515Sru } 556146515Sru return filename; 557146515Sru} 558146515Sru 559146515Sruchar * 560146515Sruoutput_name_from_input_name (char *name) 561146515Sru{ 56256160Sru return expand_filename (NULL, name); 56356160Sru} 56493139Sru 56593139Sru 56693139Sru/* Modify the file name FNAME so that it fits the limitations of the 56793139Sru underlying filesystem. In particular, truncate the file name as it 56893139Sru would be truncated by the filesystem. We assume the result can 56993139Sru never be longer than the original, otherwise we couldn't be sure we 57093139Sru have enough space in the original string to modify it in place. */ 57193139Sruchar * 572146515Srunormalize_filename (char *fname) 57393139Sru{ 57493139Sru int maxlen; 57593139Sru char orig[PATH_MAX + 1]; 57693139Sru int i; 57793139Sru char *lastdot, *p; 57893139Sru 57993139Sru#ifdef _PC_NAME_MAX 58093139Sru maxlen = pathconf (fname, _PC_NAME_MAX); 58193139Sru if (maxlen < 1) 58293139Sru#endif 58393139Sru maxlen = PATH_MAX; 58493139Sru 58593139Sru i = skip_directory_part (fname); 58693139Sru if (fname[i] == '\0') 58793139Sru return fname; /* only a directory name -- don't modify */ 58893139Sru strcpy (orig, fname + i); 58993139Sru 59093139Sru switch (maxlen) 59193139Sru { 59293139Sru case 12: /* MS-DOS 8+3 filesystem */ 59393139Sru if (orig[0] == '.') /* leading dots are not allowed */ 59493139Sru orig[0] = '_'; 59593139Sru lastdot = strrchr (orig, '.'); 59693139Sru if (!lastdot) 59793139Sru lastdot = orig + strlen (orig); 59893139Sru strncpy (fname + i, orig, lastdot - orig); 59993139Sru for (p = fname + i; 60093139Sru p < fname + i + (lastdot - orig) && p < fname + i + 8; 60193139Sru p++) 60293139Sru if (*p == '.') 60393139Sru *p = '_'; 60493139Sru *p = '\0'; 60593139Sru if (*lastdot == '.') 60693139Sru strncat (fname + i, lastdot, 4); 60793139Sru break; 60893139Sru case 14: /* old Unix systems with 14-char limitation */ 60993139Sru strcpy (fname + i, orig); 61093139Sru if (strlen (fname + i) > 14) 61193139Sru fname[i + 14] = '\0'; 61293139Sru break; 61393139Sru default: 61493139Sru strcpy (fname + i, orig); 61593139Sru if (strlen (fname) > maxlen - 1) 61693139Sru fname[maxlen - 1] = '\0'; 61793139Sru break; 61893139Sru } 61993139Sru 62093139Sru return fname; 62193139Sru} 622146515Sru 623146515Sru/* Delayed writing functions. A few of the commands 624146515Sru needs to be handled at the end, namely @contents, 625146515Sru @shortcontents, @printindex and @listoffloats. 626146515Sru These functions take care of that. */ 627146515Srustatic DELAYED_WRITE *delayed_writes = NULL; 628146515Sruint handling_delayed_writes = 0; 629146515Sru 630146515Sruvoid 631146515Sruregister_delayed_write (char *delayed_command) 632146515Sru{ 633146515Sru DELAYED_WRITE *new; 634146515Sru 635146515Sru if (!current_output_filename || !*current_output_filename) 636146515Sru { 637146515Sru /* Cannot register if we don't know what the output file is. */ 638146515Sru warning (_("`%s' omitted before output filename"), delayed_command); 639146515Sru return; 640146515Sru } 641146515Sru 642146515Sru if (STREQ (current_output_filename, "-")) 643146515Sru { 644146515Sru /* Do not register a new write if the output file is not seekable. 645146515Sru Let the user know about it first, though. */ 646146515Sru warning (_("`%s' omitted since writing to stdout"), delayed_command); 647146515Sru return; 648146515Sru } 649146515Sru 650146515Sru /* Don't complain if the user is writing /dev/null, since surely they 651146515Sru don't care, but don't register the delayed write, either. */ 652146515Sru if (FILENAME_CMP (current_output_filename, NULL_DEVICE) == 0 653146515Sru || FILENAME_CMP (current_output_filename, ALSO_NULL_DEVICE) == 0) 654146515Sru return; 655146515Sru 656146515Sru /* We need the HTML header in the output, 657146515Sru to get a proper output_position. */ 658146515Sru if (!executing_string && html) 659146515Sru html_output_head (); 660146515Sru /* Get output_position updated. */ 661146515Sru flush_output (); 662146515Sru 663146515Sru new = xmalloc (sizeof (DELAYED_WRITE)); 664146515Sru new->command = xstrdup (delayed_command); 665146515Sru new->filename = xstrdup (current_output_filename); 666146515Sru new->input_filename = xstrdup (input_filename); 667146515Sru new->position = output_position; 668146515Sru new->calling_line = line_number; 669146515Sru new->node = current_node ? xstrdup (current_node): ""; 670146515Sru 671146515Sru new->node_order = node_order; 672146515Sru new->index_order = index_counter; 673146515Sru 674146515Sru new->next = delayed_writes; 675146515Sru delayed_writes = new; 676146515Sru} 677146515Sru 678146515Sruvoid 679146515Sruhandle_delayed_writes (void) 680146515Sru{ 681146515Sru DELAYED_WRITE *temp = (DELAYED_WRITE *) reverse_list 682146515Sru ((GENERIC_LIST *) delayed_writes); 683146515Sru int position_shift_amount, line_number_shift_amount; 684146515Sru char *delayed_buf; 685146515Sru 686146515Sru handling_delayed_writes = 1; 687146515Sru 688146515Sru while (temp) 689146515Sru { 690146515Sru delayed_buf = find_and_load (temp->filename, 0); 691146515Sru 692146515Sru if (output_paragraph_offset > 0) 693146515Sru { 694146515Sru error (_("Output buffer not empty.")); 695146515Sru return; 696146515Sru } 697146515Sru 698146515Sru if (!delayed_buf) 699146515Sru { 700146515Sru fs_error (temp->filename); 701146515Sru return; 702146515Sru } 703146515Sru 704146515Sru output_stream = fopen (temp->filename, "w"); 705146515Sru if (!output_stream) 706146515Sru { 707146515Sru fs_error (temp->filename); 708146515Sru return; 709146515Sru } 710146515Sru 711146515Sru if (fwrite (delayed_buf, 1, temp->position, output_stream) != temp->position) 712146515Sru { 713146515Sru fs_error (temp->filename); 714146515Sru return; 715146515Sru } 716146515Sru 717146515Sru { 718146515Sru int output_position_at_start = output_position; 719146515Sru int line_number_at_start = output_line_number; 720146515Sru 721146515Sru /* In order to make warnings and errors 722146515Sru refer to the correct line number. */ 723146515Sru input_filename = temp->input_filename; 724146515Sru line_number = temp->calling_line; 725146515Sru 726146515Sru execute_string ("%s", temp->command); 727146515Sru flush_output (); 728146515Sru 729146515Sru /* Since the output file is modified, following delayed writes 730146515Sru need to be updated by this amount. */ 731146515Sru position_shift_amount = output_position - output_position_at_start; 732146515Sru line_number_shift_amount = output_line_number - line_number_at_start; 733146515Sru } 734146515Sru 735146515Sru if (fwrite (delayed_buf + temp->position, 1, 736146515Sru input_text_length - temp->position, output_stream) 737146515Sru != input_text_length - temp->position 738146515Sru || fclose (output_stream) != 0) 739146515Sru fs_error (temp->filename); 740146515Sru 741146515Sru /* Done with the buffer. */ 742146515Sru free (delayed_buf); 743146515Sru 744146515Sru /* Update positions in tag table for nodes that are defined after 745146515Sru the line this delayed write is registered. */ 746146515Sru if (!html && !xml) 747146515Sru { 748146515Sru TAG_ENTRY *node; 749146515Sru for (node = tag_table; node; node = node->next_ent) 750146515Sru if (node->order > temp->node_order) 751146515Sru node->position += position_shift_amount; 752146515Sru } 753146515Sru 754146515Sru /* Something similar for the line numbers in all of the defined 755146515Sru indices. */ 756146515Sru { 757146515Sru int i; 758146515Sru for (i = 0; i < defined_indices; i++) 759146515Sru if (name_index_alist[i]) 760146515Sru { 761146515Sru char *name = ((INDEX_ALIST *) name_index_alist[i])->name; 762146515Sru INDEX_ELT *index; 763146515Sru for (index = index_list (name); index; index = index->next) 764146515Sru if ((no_headers || STREQ (index->node, temp->node)) 765146515Sru && index->entry_number > temp->index_order) 766146515Sru index->output_line += line_number_shift_amount; 767146515Sru } 768146515Sru } 769146515Sru 770146515Sru /* Shift remaining delayed positions 771146515Sru by the length of this write. */ 772146515Sru { 773146515Sru DELAYED_WRITE *future_write = temp->next; 774146515Sru while (future_write) 775146515Sru { 776146515Sru if (STREQ (temp->filename, future_write->filename)) 777146515Sru future_write->position += position_shift_amount; 778146515Sru future_write = future_write->next; 779146515Sru } 780146515Sru } 781146515Sru 782146515Sru temp = temp->next; 783146515Sru } 784146515Sru} 785