13fb98d4aSespie /* files.c -- file-related functions for makeinfo. 2*82ba5b19Sguenther $Id: files.c,v 1.2 2024/08/16 22:53:32 guenther Exp $ 31cc83814Sespie 4a1acfa9bSespie Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software 5a1acfa9bSespie Foundation, Inc. 61cc83814Sespie 71cc83814Sespie This program is free software; you can redistribute it and/or modify 81cc83814Sespie it under the terms of the GNU General Public License as published by 91cc83814Sespie the Free Software Foundation; either version 2, or (at your option) 101cc83814Sespie any later version. 111cc83814Sespie 121cc83814Sespie This program is distributed in the hope that it will be useful, 131cc83814Sespie but WITHOUT ANY WARRANTY; without even the implied warranty of 141cc83814Sespie MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151cc83814Sespie GNU General Public License for more details. 161cc83814Sespie 171cc83814Sespie You should have received a copy of the GNU General Public License 181cc83814Sespie along with this program; if not, write to the Free Software Foundation, 191cc83814Sespie Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 201cc83814Sespie 211cc83814Sespie #include "system.h" 221cc83814Sespie #include "files.h" 23a1acfa9bSespie #include "html.h" 24a1acfa9bSespie #include "index.h" 251cc83814Sespie #include "macro.h" 261cc83814Sespie #include "makeinfo.h" 27a1acfa9bSespie #include "node.h" 281cc83814Sespie 291cc83814Sespie FSTACK *filestack = NULL; 301cc83814Sespie 311cc83814Sespie static int node_filename_stack_index = 0; 321cc83814Sespie static int node_filename_stack_size = 0; 331cc83814Sespie static char **node_filename_stack = NULL; 341cc83814Sespie 351cc83814Sespie /* Looking for include files. */ 361cc83814Sespie 371cc83814Sespie /* Given a string containing units of information separated by colons, 381cc83814Sespie return the next one pointed to by INDEX, or NULL if there are no more. 391cc83814Sespie Advance INDEX to the character after the colon. */ 401cc83814Sespie static char * 41a1acfa9bSespie extract_colon_unit (char *string, int *index) 421cc83814Sespie { 431cc83814Sespie int start; 441cc83814Sespie int path_sep_char = PATH_SEP[0]; 451cc83814Sespie int i = *index; 461cc83814Sespie 471cc83814Sespie if (!string || (i >= strlen (string))) 481cc83814Sespie return NULL; 491cc83814Sespie 501cc83814Sespie /* Each call to this routine leaves the index pointing at a colon if 511cc83814Sespie there is more to the path. If i > 0, then increment past the 521cc83814Sespie `:'. If i == 0, then the path has a leading colon. Trailing colons 531cc83814Sespie are handled OK by the `else' part of the if statement; an empty 541cc83814Sespie string is returned in that case. */ 551cc83814Sespie if (i && string[i] == path_sep_char) 561cc83814Sespie i++; 571cc83814Sespie 581cc83814Sespie start = i; 591cc83814Sespie while (string[i] && string[i] != path_sep_char) i++; 601cc83814Sespie *index = i; 611cc83814Sespie 621cc83814Sespie if (i == start) 631cc83814Sespie { 641cc83814Sespie if (string[i]) 651cc83814Sespie (*index)++; 661cc83814Sespie 671cc83814Sespie /* Return "" in the case of a trailing `:'. */ 681cc83814Sespie return xstrdup (""); 691cc83814Sespie } 701cc83814Sespie else 711cc83814Sespie { 721cc83814Sespie char *value; 731cc83814Sespie 741cc83814Sespie value = xmalloc (1 + (i - start)); 751cc83814Sespie memcpy (value, &string[start], (i - start)); 761cc83814Sespie value [i - start] = 0; 771cc83814Sespie 781cc83814Sespie return value; 791cc83814Sespie } 801cc83814Sespie } 811cc83814Sespie 821cc83814Sespie /* Return the full pathname for FILENAME by searching along PATH. 831cc83814Sespie When found, return the stat () info for FILENAME in FINFO. 841cc83814Sespie If PATH is NULL, only the current directory is searched. 851cc83814Sespie If the file could not be found, return a NULL pointer. */ 86a1acfa9bSespie char * 87a1acfa9bSespie get_file_info_in_path (char *filename, char *path, struct stat *finfo) 881cc83814Sespie { 891cc83814Sespie char *dir; 901cc83814Sespie int result, index = 0; 911cc83814Sespie 921cc83814Sespie if (path == NULL) 931cc83814Sespie path = "."; 941cc83814Sespie 951cc83814Sespie /* Handle absolute pathnames. */ 961cc83814Sespie if (IS_ABSOLUTE (filename) 971cc83814Sespie || (*filename == '.' 981cc83814Sespie && (IS_SLASH (filename[1]) 991cc83814Sespie || (filename[1] == '.' && IS_SLASH (filename[2]))))) 1001cc83814Sespie { 1011cc83814Sespie if (stat (filename, finfo) == 0) 1021cc83814Sespie return xstrdup (filename); 1031cc83814Sespie else 1041cc83814Sespie return NULL; 1051cc83814Sespie } 1061cc83814Sespie 1071cc83814Sespie while ((dir = extract_colon_unit (path, &index))) 1081cc83814Sespie { 1091cc83814Sespie char *fullpath; 1101cc83814Sespie 1111cc83814Sespie if (!*dir) 1121cc83814Sespie { 1131cc83814Sespie free (dir); 1141cc83814Sespie dir = xstrdup ("."); 1151cc83814Sespie } 1161cc83814Sespie 1171cc83814Sespie fullpath = xmalloc (2 + strlen (dir) + strlen (filename)); 1181cc83814Sespie sprintf (fullpath, "%s/%s", dir, filename); 1191cc83814Sespie free (dir); 1201cc83814Sespie 1211cc83814Sespie result = stat (fullpath, finfo); 1221cc83814Sespie 1231cc83814Sespie if (result == 0) 1241cc83814Sespie return fullpath; 1251cc83814Sespie else 1261cc83814Sespie free (fullpath); 1271cc83814Sespie } 1281cc83814Sespie return NULL; 1291cc83814Sespie } 130a1acfa9bSespie 131a1acfa9bSespie /* Prepend and append new paths to include_files_path. */ 132a1acfa9bSespie void 133a1acfa9bSespie prepend_to_include_path (char *path) 134a1acfa9bSespie { 135a1acfa9bSespie if (!include_files_path) 136a1acfa9bSespie { 137a1acfa9bSespie include_files_path = xstrdup (path); 138a1acfa9bSespie include_files_path = xrealloc (include_files_path, 139a1acfa9bSespie strlen (include_files_path) + 3); /* 3 for ":.\0" */ 140a1acfa9bSespie strcat (strcat (include_files_path, PATH_SEP), "."); 141a1acfa9bSespie } 142a1acfa9bSespie else 143a1acfa9bSespie { 144a1acfa9bSespie char *tmp = xstrdup (include_files_path); 145a1acfa9bSespie include_files_path = xrealloc (include_files_path, 146a1acfa9bSespie strlen (include_files_path) + strlen (path) + 2); /* 2 for ":\0" */ 147a1acfa9bSespie strcpy (include_files_path, path); 148a1acfa9bSespie strcat (include_files_path, PATH_SEP); 149a1acfa9bSespie strcat (include_files_path, tmp); 150a1acfa9bSespie free (tmp); 151a1acfa9bSespie } 152a1acfa9bSespie } 153a1acfa9bSespie 154a1acfa9bSespie void 155a1acfa9bSespie append_to_include_path (char *path) 156a1acfa9bSespie { 157a1acfa9bSespie if (!include_files_path) 158a1acfa9bSespie include_files_path = xstrdup ("."); 159a1acfa9bSespie 160a1acfa9bSespie include_files_path = (char *) xrealloc (include_files_path, 161a1acfa9bSespie 2 + strlen (include_files_path) + strlen (path)); 162a1acfa9bSespie strcat (include_files_path, PATH_SEP); 163a1acfa9bSespie strcat (include_files_path, path); 164a1acfa9bSespie } 165a1acfa9bSespie 166a1acfa9bSespie /* Remove the first path from the include_files_path. */ 167a1acfa9bSespie void 168a1acfa9bSespie pop_path_from_include_path (void) 169a1acfa9bSespie { 170a1acfa9bSespie int i = 0; 171a1acfa9bSespie char *tmp; 172a1acfa9bSespie 173a1acfa9bSespie if (include_files_path) 174a1acfa9bSespie for (i = 0; i < strlen (include_files_path) 175a1acfa9bSespie && include_files_path[i] != ':'; i++); 176a1acfa9bSespie 177a1acfa9bSespie /* Advance include_files_path to the next char from ':' */ 178a1acfa9bSespie tmp = (char *) xmalloc (strlen (include_files_path) - i); 179a1acfa9bSespie strcpy (tmp, (char *) include_files_path + i + 1); 180a1acfa9bSespie 181a1acfa9bSespie free (include_files_path); 182a1acfa9bSespie include_files_path = tmp; 183a1acfa9bSespie } 1841cc83814Sespie 1851cc83814Sespie /* Find and load the file named FILENAME. Return a pointer to 186a1acfa9bSespie the loaded file, or NULL if it can't be loaded. If USE_PATH is zero, 187a1acfa9bSespie just look for the given file (this is used in handle_delayed_writes), 188a1acfa9bSespie else search along include_files_path. */ 189a1acfa9bSespie 1901cc83814Sespie char * 191a1acfa9bSespie find_and_load (char *filename, int use_path) 1921cc83814Sespie { 1931cc83814Sespie struct stat fileinfo; 1941cc83814Sespie long file_size; 1951cc83814Sespie int file = -1, count = 0; 1961cc83814Sespie char *fullpath, *result; 1973fb98d4aSespie int n, bytes_to_read; 1981cc83814Sespie 1991cc83814Sespie result = fullpath = NULL; 2001cc83814Sespie 201a1acfa9bSespie fullpath 202a1acfa9bSespie = get_file_info_in_path (filename, use_path ? include_files_path : NULL, 203a1acfa9bSespie &fileinfo); 2041cc83814Sespie 2051cc83814Sespie if (!fullpath) 2061cc83814Sespie goto error_exit; 2071cc83814Sespie 2081cc83814Sespie filename = fullpath; 2091cc83814Sespie file_size = (long) fileinfo.st_size; 2101cc83814Sespie 2111cc83814Sespie file = open (filename, O_RDONLY); 2121cc83814Sespie if (file < 0) 2131cc83814Sespie goto error_exit; 2141cc83814Sespie 2151cc83814Sespie /* Load the file, with enough room for a newline and a null. */ 2161cc83814Sespie result = xmalloc (file_size + 2); 2171cc83814Sespie 2181cc83814Sespie /* VMS stat lies about the st_size value. The actual number of 2191cc83814Sespie readable bytes is always less than this value. The arcane 2201cc83814Sespie mysteries of VMS/RMS are too much to probe, so this hack 2213fb98d4aSespie suffices to make things work. It's also needed on Cygwin. And so 2223fb98d4aSespie we might as well use it everywhere. */ 2233fb98d4aSespie bytes_to_read = file_size; 2243fb98d4aSespie while ((n = read (file, result + count, bytes_to_read)) > 0) 2253fb98d4aSespie { 2261cc83814Sespie count += n; 2273fb98d4aSespie bytes_to_read -= n; 2283fb98d4aSespie } 2291cc83814Sespie if (0 < count && count < file_size) 2301cc83814Sespie result = xrealloc (result, count + 2); /* why waste the slack? */ 2311cc83814Sespie else if (n == -1) 2321cc83814Sespie error_exit: 2331cc83814Sespie { 2341cc83814Sespie if (result) 2351cc83814Sespie free (result); 2361cc83814Sespie 2371cc83814Sespie if (fullpath) 2381cc83814Sespie free (fullpath); 2391cc83814Sespie 2401cc83814Sespie if (file != -1) 2411cc83814Sespie close (file); 2421cc83814Sespie 2431cc83814Sespie return NULL; 2441cc83814Sespie } 2451cc83814Sespie close (file); 2461cc83814Sespie 2471cc83814Sespie /* Set the globals to the new file. */ 2481cc83814Sespie input_text = result; 2491cc83814Sespie input_text_length = count; 2501cc83814Sespie input_filename = fullpath; 2511cc83814Sespie node_filename = xstrdup (fullpath); 2521cc83814Sespie input_text_offset = 0; 2531cc83814Sespie line_number = 1; 2541cc83814Sespie /* Not strictly necessary. This magic prevents read_token () from doing 2551cc83814Sespie extra unnecessary work each time it is called (that is a lot of times). 2561cc83814Sespie INPUT_TEXT_LENGTH is one past the actual end of the text. */ 2571cc83814Sespie input_text[input_text_length] = '\n'; 2581cc83814Sespie /* This, on the other hand, is always necessary. */ 2591cc83814Sespie input_text[input_text_length+1] = 0; 2601cc83814Sespie return result; 2611cc83814Sespie } 2621cc83814Sespie 2631cc83814Sespie /* Pushing and popping files. */ 264a1acfa9bSespie static void 265a1acfa9bSespie push_node_filename (void) 2661cc83814Sespie { 2671cc83814Sespie if (node_filename_stack_index + 1 > node_filename_stack_size) 2681cc83814Sespie node_filename_stack = xrealloc 2691cc83814Sespie (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *)); 2701cc83814Sespie 2711cc83814Sespie node_filename_stack[node_filename_stack_index] = node_filename; 2721cc83814Sespie node_filename_stack_index++; 2731cc83814Sespie } 2741cc83814Sespie 275a1acfa9bSespie static void 276a1acfa9bSespie pop_node_filename (void) 2771cc83814Sespie { 2781cc83814Sespie node_filename = node_filename_stack[--node_filename_stack_index]; 2791cc83814Sespie } 2801cc83814Sespie 2811cc83814Sespie /* Save the state of the current input file. */ 2821cc83814Sespie void 283a1acfa9bSespie pushfile (void) 2841cc83814Sespie { 2851cc83814Sespie FSTACK *newstack = xmalloc (sizeof (FSTACK)); 2861cc83814Sespie newstack->filename = input_filename; 2871cc83814Sespie newstack->text = input_text; 2881cc83814Sespie newstack->size = input_text_length; 2891cc83814Sespie newstack->offset = input_text_offset; 2901cc83814Sespie newstack->line_number = line_number; 2911cc83814Sespie newstack->next = filestack; 2921cc83814Sespie 2931cc83814Sespie filestack = newstack; 2941cc83814Sespie push_node_filename (); 2951cc83814Sespie } 2961cc83814Sespie 2971cc83814Sespie /* Make the current file globals be what is on top of the file stack. */ 2981cc83814Sespie void 299a1acfa9bSespie popfile (void) 3001cc83814Sespie { 3011cc83814Sespie FSTACK *tos = filestack; 3021cc83814Sespie 3031cc83814Sespie if (!tos) 3041cc83814Sespie abort (); /* My fault. I wonder what I did? */ 3051cc83814Sespie 3061cc83814Sespie if (macro_expansion_output_stream) 3071cc83814Sespie { 3081cc83814Sespie maybe_write_itext (input_text, input_text_offset); 3091cc83814Sespie forget_itext (input_text); 3101cc83814Sespie } 3111cc83814Sespie 3121cc83814Sespie /* Pop the stack. */ 3131cc83814Sespie filestack = filestack->next; 3141cc83814Sespie 3151cc83814Sespie /* Make sure that commands with braces have been satisfied. */ 3161cc83814Sespie if (!executing_string && !me_executing_string) 3171cc83814Sespie discard_braces (); 3181cc83814Sespie 3191cc83814Sespie /* Get the top of the stack into the globals. */ 3201cc83814Sespie input_filename = tos->filename; 3211cc83814Sespie input_text = tos->text; 3221cc83814Sespie input_text_length = tos->size; 3231cc83814Sespie input_text_offset = tos->offset; 3241cc83814Sespie line_number = tos->line_number; 3251cc83814Sespie free (tos); 3261cc83814Sespie 3271cc83814Sespie /* Go back to the (now) current node. */ 3281cc83814Sespie pop_node_filename (); 3291cc83814Sespie } 3301cc83814Sespie 3311cc83814Sespie /* Flush all open files on the file stack. */ 3321cc83814Sespie void 333a1acfa9bSespie flush_file_stack (void) 3341cc83814Sespie { 3351cc83814Sespie while (filestack) 3361cc83814Sespie { 3371cc83814Sespie char *fname = input_filename; 3381cc83814Sespie char *text = input_text; 3391cc83814Sespie popfile (); 3401cc83814Sespie free (fname); 3411cc83814Sespie free (text); 3421cc83814Sespie } 3431cc83814Sespie } 3441cc83814Sespie 3451cc83814Sespie /* Return the index of the first character in the filename 3461cc83814Sespie which is past all the leading directory characters. */ 3471cc83814Sespie static int 348a1acfa9bSespie skip_directory_part (char *filename) 3491cc83814Sespie { 3501cc83814Sespie int i = strlen (filename) - 1; 3511cc83814Sespie 3521cc83814Sespie while (i && !IS_SLASH (filename[i])) 3531cc83814Sespie i--; 3541cc83814Sespie if (IS_SLASH (filename[i])) 3551cc83814Sespie i++; 3561cc83814Sespie else if (filename[i] && HAVE_DRIVE (filename)) 3571cc83814Sespie i = 2; 3581cc83814Sespie 3591cc83814Sespie return i; 3601cc83814Sespie } 3611cc83814Sespie 362a1acfa9bSespie static char * 363a1acfa9bSespie filename_non_directory (char *name) 3641cc83814Sespie { 3651cc83814Sespie return xstrdup (name + skip_directory_part (name)); 3661cc83814Sespie } 3671cc83814Sespie 3681cc83814Sespie /* Return just the simple part of the filename; i.e. the 3691cc83814Sespie filename without the path information, or extensions. 3701cc83814Sespie This conses up a new string. */ 3711cc83814Sespie char * 372a1acfa9bSespie filename_part (char *filename) 3731cc83814Sespie { 3741cc83814Sespie char *basename = filename_non_directory (filename); 3751cc83814Sespie 3761cc83814Sespie #ifdef REMOVE_OUTPUT_EXTENSIONS 3771cc83814Sespie /* See if there is an extension to remove. If so, remove it. */ 3781cc83814Sespie { 379a1acfa9bSespie char *temp = strrchr (basename, '.'); 3801cc83814Sespie if (temp) 3811cc83814Sespie *temp = 0; 3821cc83814Sespie } 3831cc83814Sespie #endif /* REMOVE_OUTPUT_EXTENSIONS */ 3841cc83814Sespie return basename; 3851cc83814Sespie } 3861cc83814Sespie 3871cc83814Sespie /* Return the pathname part of filename. This can be NULL. */ 3881cc83814Sespie char * 389a1acfa9bSespie pathname_part (char *filename) 3901cc83814Sespie { 3911cc83814Sespie char *result = NULL; 3921cc83814Sespie int i; 3931cc83814Sespie 3941cc83814Sespie filename = expand_filename (filename, ""); 3951cc83814Sespie 3961cc83814Sespie i = skip_directory_part (filename); 3971cc83814Sespie if (i) 3981cc83814Sespie { 3991cc83814Sespie result = xmalloc (1 + i); 4001cc83814Sespie strncpy (result, filename, i); 4011cc83814Sespie result[i] = 0; 4021cc83814Sespie } 4031cc83814Sespie free (filename); 4041cc83814Sespie return result; 4051cc83814Sespie } 4061cc83814Sespie 4071cc83814Sespie /* Return the full path to FILENAME. */ 408a1acfa9bSespie static char * 409a1acfa9bSespie full_pathname (char *filename) 4101cc83814Sespie { 4111cc83814Sespie int initial_character; 4121cc83814Sespie char *result; 4131cc83814Sespie 4141cc83814Sespie /* No filename given? */ 4151cc83814Sespie if (!filename || !*filename) 4161cc83814Sespie return xstrdup (""); 4171cc83814Sespie 4181cc83814Sespie /* Already absolute? */ 4191cc83814Sespie if (IS_ABSOLUTE (filename) || 4201cc83814Sespie (*filename == '.' && 4211cc83814Sespie (IS_SLASH (filename[1]) || 4221cc83814Sespie (filename[1] == '.' && IS_SLASH (filename[2]))))) 4231cc83814Sespie return xstrdup (filename); 4241cc83814Sespie 4251cc83814Sespie initial_character = *filename; 4261cc83814Sespie if (initial_character != '~') 4271cc83814Sespie { 4281cc83814Sespie char *localdir = xmalloc (1025); 4291cc83814Sespie #ifdef HAVE_GETCWD 4301cc83814Sespie if (!getcwd (localdir, 1024)) 4311cc83814Sespie #else 4321cc83814Sespie if (!getwd (localdir)) 4331cc83814Sespie #endif 4341cc83814Sespie { 4351cc83814Sespie fprintf (stderr, _("%s: getwd: %s, %s\n"), 4361cc83814Sespie progname, filename, localdir); 4371cc83814Sespie xexit (1); 4381cc83814Sespie } 4391cc83814Sespie 4401cc83814Sespie strcat (localdir, "/"); 4411cc83814Sespie strcat (localdir, filename); 4421cc83814Sespie result = xstrdup (localdir); 4431cc83814Sespie free (localdir); 4441cc83814Sespie } 4451cc83814Sespie else 4461cc83814Sespie { /* Does anybody know why WIN32 doesn't want to support $HOME? 4471cc83814Sespie If the reason is they don't have getpwnam, they should 4481cc83814Sespie only disable the else clause below. */ 4491cc83814Sespie #ifndef WIN32 4501cc83814Sespie if (IS_SLASH (filename[1])) 4511cc83814Sespie { 4521cc83814Sespie /* Return the concatenation of the environment variable HOME 4531cc83814Sespie and the rest of the string. */ 4541cc83814Sespie char *temp_home; 4551cc83814Sespie 4561cc83814Sespie temp_home = (char *) getenv ("HOME"); 4571cc83814Sespie result = xmalloc (strlen (&filename[1]) 4581cc83814Sespie + 1 459*82ba5b19Sguenther + (temp_home ? strlen (temp_home) 460*82ba5b19Sguenther : 0)); 4611cc83814Sespie *result = 0; 4621cc83814Sespie 4631cc83814Sespie if (temp_home) 4641cc83814Sespie strcpy (result, temp_home); 4651cc83814Sespie 4661cc83814Sespie strcat (result, &filename[1]); 4671cc83814Sespie } 4681cc83814Sespie else 4691cc83814Sespie { 4701cc83814Sespie struct passwd *user_entry; 4711cc83814Sespie int i, c; 4721cc83814Sespie char *username = xmalloc (257); 4731cc83814Sespie 4741cc83814Sespie for (i = 1; (c = filename[i]); i++) 4751cc83814Sespie { 4761cc83814Sespie if (IS_SLASH (c)) 4771cc83814Sespie break; 4781cc83814Sespie else 4791cc83814Sespie username[i - 1] = c; 4801cc83814Sespie } 4811cc83814Sespie if (c) 4821cc83814Sespie username[i - 1] = 0; 4831cc83814Sespie 4841cc83814Sespie user_entry = getpwnam (username); 4851cc83814Sespie 4861cc83814Sespie if (!user_entry) 4871cc83814Sespie return xstrdup (filename); 4881cc83814Sespie 4891cc83814Sespie result = xmalloc (1 + strlen (user_entry->pw_dir) 4901cc83814Sespie + strlen (&filename[i])); 4911cc83814Sespie strcpy (result, user_entry->pw_dir); 4921cc83814Sespie strcat (result, &filename[i]); 4931cc83814Sespie } 4941cc83814Sespie #endif /* not WIN32 */ 4951cc83814Sespie } 4961cc83814Sespie return result; 4971cc83814Sespie } 4981cc83814Sespie 499a1acfa9bSespie /* Return the expansion of FILENAME. */ 5001cc83814Sespie char * 501a1acfa9bSespie expand_filename (char *filename, char *input_name) 502a1acfa9bSespie { 503a1acfa9bSespie int i; 504a1acfa9bSespie 505a1acfa9bSespie if (filename) 506a1acfa9bSespie { 507a1acfa9bSespie filename = full_pathname (filename); 508a1acfa9bSespie if (IS_ABSOLUTE (filename) 509a1acfa9bSespie || (*filename == '.' && 510a1acfa9bSespie (IS_SLASH (filename[1]) || 511a1acfa9bSespie (filename[1] == '.' && IS_SLASH (filename[2]))))) 512a1acfa9bSespie return filename; 513a1acfa9bSespie } 514a1acfa9bSespie else 515a1acfa9bSespie { 516a1acfa9bSespie filename = filename_non_directory (input_name); 517a1acfa9bSespie 518a1acfa9bSespie if (!*filename) 519a1acfa9bSespie { 520a1acfa9bSespie free (filename); 521a1acfa9bSespie filename = xstrdup ("noname.texi"); 522a1acfa9bSespie } 523a1acfa9bSespie 524a1acfa9bSespie for (i = strlen (filename) - 1; i; i--) 525a1acfa9bSespie if (filename[i] == '.') 526a1acfa9bSespie break; 527a1acfa9bSespie 528a1acfa9bSespie if (!i) 529a1acfa9bSespie i = strlen (filename); 530a1acfa9bSespie 531a1acfa9bSespie if (i + 6 > (strlen (filename))) 532a1acfa9bSespie filename = xrealloc (filename, i + 6); 533a1acfa9bSespie strcpy (filename + i, html ? ".html" : ".info"); 534a1acfa9bSespie return filename; 535a1acfa9bSespie } 536a1acfa9bSespie 537a1acfa9bSespie if (IS_ABSOLUTE (input_name)) 538a1acfa9bSespie { 539a1acfa9bSespie /* Make it so that relative names work. */ 540a1acfa9bSespie char *result; 541a1acfa9bSespie 542a1acfa9bSespie i = strlen (input_name) - 1; 543a1acfa9bSespie 544a1acfa9bSespie result = xmalloc (1 + strlen (input_name) + strlen (filename)); 545a1acfa9bSespie strcpy (result, input_name); 546a1acfa9bSespie 547a1acfa9bSespie while (!IS_SLASH (result[i]) && i) 548a1acfa9bSespie i--; 549a1acfa9bSespie if (IS_SLASH (result[i])) 550a1acfa9bSespie i++; 551a1acfa9bSespie 552a1acfa9bSespie strcpy (&result[i], filename); 553a1acfa9bSespie free (filename); 554a1acfa9bSespie return result; 555a1acfa9bSespie } 556a1acfa9bSespie return filename; 557a1acfa9bSespie } 558a1acfa9bSespie 559a1acfa9bSespie char * 560a1acfa9bSespie output_name_from_input_name (char *name) 5611cc83814Sespie { 5621cc83814Sespie return expand_filename (NULL, name); 5631cc83814Sespie } 5643fb98d4aSespie 5653fb98d4aSespie 5663fb98d4aSespie /* Modify the file name FNAME so that it fits the limitations of the 5673fb98d4aSespie underlying filesystem. In particular, truncate the file name as it 5683fb98d4aSespie would be truncated by the filesystem. We assume the result can 5693fb98d4aSespie never be longer than the original, otherwise we couldn't be sure we 5703fb98d4aSespie have enough space in the original string to modify it in place. */ 5713fb98d4aSespie char * 572a1acfa9bSespie normalize_filename (char *fname) 5733fb98d4aSespie { 5743fb98d4aSespie int maxlen; 5753fb98d4aSespie char orig[PATH_MAX + 1]; 5763fb98d4aSespie int i; 5773fb98d4aSespie char *lastdot, *p; 5783fb98d4aSespie 5793fb98d4aSespie #ifdef _PC_NAME_MAX 5803fb98d4aSespie maxlen = pathconf (fname, _PC_NAME_MAX); 5813fb98d4aSespie if (maxlen < 1) 5823fb98d4aSespie #endif 5833fb98d4aSespie maxlen = PATH_MAX; 5843fb98d4aSespie 5853fb98d4aSespie i = skip_directory_part (fname); 5863fb98d4aSespie if (fname[i] == '\0') 5873fb98d4aSespie return fname; /* only a directory name -- don't modify */ 5883fb98d4aSespie strcpy (orig, fname + i); 5893fb98d4aSespie 5903fb98d4aSespie switch (maxlen) 5913fb98d4aSespie { 5923fb98d4aSespie case 12: /* MS-DOS 8+3 filesystem */ 5933fb98d4aSespie if (orig[0] == '.') /* leading dots are not allowed */ 5943fb98d4aSespie orig[0] = '_'; 5953fb98d4aSespie lastdot = strrchr (orig, '.'); 5963fb98d4aSespie if (!lastdot) 5973fb98d4aSespie lastdot = orig + strlen (orig); 5983fb98d4aSespie strncpy (fname + i, orig, lastdot - orig); 5993fb98d4aSespie for (p = fname + i; 6003fb98d4aSespie p < fname + i + (lastdot - orig) && p < fname + i + 8; 6013fb98d4aSespie p++) 6023fb98d4aSespie if (*p == '.') 6033fb98d4aSespie *p = '_'; 6043fb98d4aSespie *p = '\0'; 6053fb98d4aSespie if (*lastdot == '.') 6063fb98d4aSespie strncat (fname + i, lastdot, 4); 6073fb98d4aSespie break; 6083fb98d4aSespie case 14: /* old Unix systems with 14-char limitation */ 6093fb98d4aSespie strcpy (fname + i, orig); 6103fb98d4aSespie if (strlen (fname + i) > 14) 6113fb98d4aSespie fname[i + 14] = '\0'; 6123fb98d4aSespie break; 6133fb98d4aSespie default: 6143fb98d4aSespie strcpy (fname + i, orig); 6153fb98d4aSespie if (strlen (fname) > maxlen - 1) 6163fb98d4aSespie fname[maxlen - 1] = '\0'; 6173fb98d4aSespie break; 6183fb98d4aSespie } 6193fb98d4aSespie 6203fb98d4aSespie return fname; 6213fb98d4aSespie } 622a1acfa9bSespie 623a1acfa9bSespie /* Delayed writing functions. A few of the commands 624a1acfa9bSespie needs to be handled at the end, namely @contents, 625a1acfa9bSespie @shortcontents, @printindex and @listoffloats. 626a1acfa9bSespie These functions take care of that. */ 627a1acfa9bSespie static DELAYED_WRITE *delayed_writes = NULL; 628a1acfa9bSespie int handling_delayed_writes = 0; 629a1acfa9bSespie 630a1acfa9bSespie void 631a1acfa9bSespie register_delayed_write (char *delayed_command) 632a1acfa9bSespie { 633a1acfa9bSespie DELAYED_WRITE *new; 634a1acfa9bSespie 635a1acfa9bSespie if (!current_output_filename || !*current_output_filename) 636a1acfa9bSespie { 637a1acfa9bSespie /* Cannot register if we don't know what the output file is. */ 638a1acfa9bSespie warning (_("`%s' omitted before output filename"), delayed_command); 639a1acfa9bSespie return; 640a1acfa9bSespie } 641a1acfa9bSespie 642a1acfa9bSespie if (STREQ (current_output_filename, "-")) 643a1acfa9bSespie { 644a1acfa9bSespie /* Do not register a new write if the output file is not seekable. 645a1acfa9bSespie Let the user know about it first, though. */ 646a1acfa9bSespie warning (_("`%s' omitted since writing to stdout"), delayed_command); 647a1acfa9bSespie return; 648a1acfa9bSespie } 649a1acfa9bSespie 650a1acfa9bSespie /* Don't complain if the user is writing /dev/null, since surely they 651a1acfa9bSespie don't care, but don't register the delayed write, either. */ 652a1acfa9bSespie if (FILENAME_CMP (current_output_filename, NULL_DEVICE) == 0 653a1acfa9bSespie || FILENAME_CMP (current_output_filename, ALSO_NULL_DEVICE) == 0) 654a1acfa9bSespie return; 655a1acfa9bSespie 656a1acfa9bSespie /* We need the HTML header in the output, 657a1acfa9bSespie to get a proper output_position. */ 658a1acfa9bSespie if (!executing_string && html) 659a1acfa9bSespie html_output_head (); 660a1acfa9bSespie /* Get output_position updated. */ 661a1acfa9bSespie flush_output (); 662a1acfa9bSespie 663a1acfa9bSespie new = xmalloc (sizeof (DELAYED_WRITE)); 664a1acfa9bSespie new->command = xstrdup (delayed_command); 665a1acfa9bSespie new->filename = xstrdup (current_output_filename); 666a1acfa9bSespie new->input_filename = xstrdup (input_filename); 667a1acfa9bSespie new->position = output_position; 668a1acfa9bSespie new->calling_line = line_number; 669a1acfa9bSespie new->node = current_node ? xstrdup (current_node): ""; 670a1acfa9bSespie 671a1acfa9bSespie new->node_order = node_order; 672a1acfa9bSespie new->index_order = index_counter; 673a1acfa9bSespie 674a1acfa9bSespie new->next = delayed_writes; 675a1acfa9bSespie delayed_writes = new; 676a1acfa9bSespie } 677a1acfa9bSespie 678a1acfa9bSespie void 679a1acfa9bSespie handle_delayed_writes (void) 680a1acfa9bSespie { 681a1acfa9bSespie DELAYED_WRITE *temp = (DELAYED_WRITE *) reverse_list 682a1acfa9bSespie ((GENERIC_LIST *) delayed_writes); 683a1acfa9bSespie int position_shift_amount, line_number_shift_amount; 684a1acfa9bSespie char *delayed_buf; 685a1acfa9bSespie 686a1acfa9bSespie handling_delayed_writes = 1; 687a1acfa9bSespie 688a1acfa9bSespie while (temp) 689a1acfa9bSespie { 690a1acfa9bSespie delayed_buf = find_and_load (temp->filename, 0); 691a1acfa9bSespie 692a1acfa9bSespie if (output_paragraph_offset > 0) 693a1acfa9bSespie { 694a1acfa9bSespie error (_("Output buffer not empty.")); 695a1acfa9bSespie return; 696a1acfa9bSespie } 697a1acfa9bSespie 698a1acfa9bSespie if (!delayed_buf) 699a1acfa9bSespie { 700a1acfa9bSespie fs_error (temp->filename); 701a1acfa9bSespie return; 702a1acfa9bSespie } 703a1acfa9bSespie 704a1acfa9bSespie output_stream = fopen (temp->filename, "w"); 705a1acfa9bSespie if (!output_stream) 706a1acfa9bSespie { 707a1acfa9bSespie fs_error (temp->filename); 708a1acfa9bSespie return; 709a1acfa9bSespie } 710a1acfa9bSespie 711a1acfa9bSespie if (fwrite (delayed_buf, 1, temp->position, output_stream) != temp->position) 712a1acfa9bSespie { 713a1acfa9bSespie fs_error (temp->filename); 714a1acfa9bSespie return; 715a1acfa9bSespie } 716a1acfa9bSespie 717a1acfa9bSespie { 718a1acfa9bSespie int output_position_at_start = output_position; 719a1acfa9bSespie int line_number_at_start = output_line_number; 720a1acfa9bSespie 721a1acfa9bSespie /* In order to make warnings and errors 722a1acfa9bSespie refer to the correct line number. */ 723a1acfa9bSespie input_filename = temp->input_filename; 724a1acfa9bSespie line_number = temp->calling_line; 725a1acfa9bSespie 726a1acfa9bSespie execute_string ("%s", temp->command); 727a1acfa9bSespie flush_output (); 728a1acfa9bSespie 729a1acfa9bSespie /* Since the output file is modified, following delayed writes 730a1acfa9bSespie need to be updated by this amount. */ 731a1acfa9bSespie position_shift_amount = output_position - output_position_at_start; 732a1acfa9bSespie line_number_shift_amount = output_line_number - line_number_at_start; 733a1acfa9bSespie } 734a1acfa9bSespie 735a1acfa9bSespie if (fwrite (delayed_buf + temp->position, 1, 736a1acfa9bSespie input_text_length - temp->position, output_stream) 737a1acfa9bSespie != input_text_length - temp->position 738a1acfa9bSespie || fclose (output_stream) != 0) 739a1acfa9bSespie fs_error (temp->filename); 740a1acfa9bSespie 741a1acfa9bSespie /* Done with the buffer. */ 742a1acfa9bSespie free (delayed_buf); 743a1acfa9bSespie 744a1acfa9bSespie /* Update positions in tag table for nodes that are defined after 745a1acfa9bSespie the line this delayed write is registered. */ 746a1acfa9bSespie if (!html && !xml) 747a1acfa9bSespie { 748a1acfa9bSespie TAG_ENTRY *node; 749a1acfa9bSespie for (node = tag_table; node; node = node->next_ent) 750a1acfa9bSespie if (node->order > temp->node_order) 751a1acfa9bSespie node->position += position_shift_amount; 752a1acfa9bSespie } 753a1acfa9bSespie 754a1acfa9bSespie /* Something similar for the line numbers in all of the defined 755a1acfa9bSespie indices. */ 756a1acfa9bSespie { 757a1acfa9bSespie int i; 758a1acfa9bSespie for (i = 0; i < defined_indices; i++) 759a1acfa9bSespie if (name_index_alist[i]) 760a1acfa9bSespie { 761a1acfa9bSespie char *name = ((INDEX_ALIST *) name_index_alist[i])->name; 762a1acfa9bSespie INDEX_ELT *index; 763a1acfa9bSespie for (index = index_list (name); index; index = index->next) 764a1acfa9bSespie if ((no_headers || STREQ (index->node, temp->node)) 765a1acfa9bSespie && index->entry_number > temp->index_order) 766a1acfa9bSespie index->output_line += line_number_shift_amount; 767a1acfa9bSespie } 768a1acfa9bSespie } 769a1acfa9bSespie 770a1acfa9bSespie /* Shift remaining delayed positions 771a1acfa9bSespie by the length of this write. */ 772a1acfa9bSespie { 773a1acfa9bSespie DELAYED_WRITE *future_write = temp->next; 774a1acfa9bSespie while (future_write) 775a1acfa9bSespie { 776a1acfa9bSespie if (STREQ (temp->filename, future_write->filename)) 777a1acfa9bSespie future_write->position += position_shift_amount; 778a1acfa9bSespie future_write = future_write->next; 779a1acfa9bSespie } 780a1acfa9bSespie } 781a1acfa9bSespie 782a1acfa9bSespie temp = temp->next; 783a1acfa9bSespie } 784a1acfa9bSespie } 785