xref: /openbsd-src/gnu/usr.bin/texinfo/makeinfo/files.c (revision 82ba5b1992ac6fb1b1309394d5381ae9f04232ce)
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