xref: /netbsd-src/external/gpl2/texinfo/dist/makeinfo/files.c (revision d3737e9cfd8cdb680cae0994d1d5f26b365d6d47)
1*d3737e9cSchristos /*	$NetBSD: files.c,v 1.2 2016/01/14 00:34:53 christos Exp $	*/
229619d2aSchristos 
329619d2aSchristos /* files.c -- file-related functions for makeinfo.
429619d2aSchristos    Id: files.c,v 1.5 2004/07/27 00:06:31 karl Exp
529619d2aSchristos 
629619d2aSchristos    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
729619d2aSchristos    Foundation, Inc.
829619d2aSchristos 
929619d2aSchristos    This program is free software; you can redistribute it and/or modify
1029619d2aSchristos    it under the terms of the GNU General Public License as published by
1129619d2aSchristos    the Free Software Foundation; either version 2, or (at your option)
1229619d2aSchristos    any later version.
1329619d2aSchristos 
1429619d2aSchristos    This program is distributed in the hope that it will be useful,
1529619d2aSchristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
1629619d2aSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1729619d2aSchristos    GNU General Public License for more details.
1829619d2aSchristos 
1929619d2aSchristos    You should have received a copy of the GNU General Public License
2029619d2aSchristos    along with this program; if not, write to the Free Software Foundation,
2129619d2aSchristos    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2229619d2aSchristos 
2329619d2aSchristos #include "system.h"
2429619d2aSchristos #include "files.h"
2529619d2aSchristos #include "html.h"
2629619d2aSchristos #include "index.h"
2729619d2aSchristos #include "macro.h"
2829619d2aSchristos #include "makeinfo.h"
2929619d2aSchristos #include "node.h"
3029619d2aSchristos 
3129619d2aSchristos FSTACK *filestack = NULL;
3229619d2aSchristos 
3329619d2aSchristos static int node_filename_stack_index = 0;
3429619d2aSchristos static int node_filename_stack_size = 0;
3529619d2aSchristos static char **node_filename_stack = NULL;
3629619d2aSchristos 
3729619d2aSchristos /* Looking for include files.  */
3829619d2aSchristos 
3929619d2aSchristos /* Given a string containing units of information separated by colons,
4029619d2aSchristos    return the next one pointed to by INDEX, or NULL if there are no more.
4129619d2aSchristos    Advance INDEX to the character after the colon. */
4229619d2aSchristos static char *
extract_colon_unit(char * string,int * index)4329619d2aSchristos extract_colon_unit (char *string, int *index)
4429619d2aSchristos {
4529619d2aSchristos   int start;
4629619d2aSchristos   int path_sep_char = PATH_SEP[0];
4729619d2aSchristos   int i = *index;
4829619d2aSchristos 
4929619d2aSchristos   if (!string || (i >= strlen (string)))
5029619d2aSchristos     return NULL;
5129619d2aSchristos 
5229619d2aSchristos   /* Each call to this routine leaves the index pointing at a colon if
5329619d2aSchristos      there is more to the path.  If i > 0, then increment past the
5429619d2aSchristos      `:'.  If i == 0, then the path has a leading colon.  Trailing colons
5529619d2aSchristos      are handled OK by the `else' part of the if statement; an empty
5629619d2aSchristos      string is returned in that case. */
5729619d2aSchristos   if (i && string[i] == path_sep_char)
5829619d2aSchristos     i++;
5929619d2aSchristos 
6029619d2aSchristos   start = i;
6129619d2aSchristos   while (string[i] && string[i] != path_sep_char) i++;
6229619d2aSchristos   *index = i;
6329619d2aSchristos 
6429619d2aSchristos   if (i == start)
6529619d2aSchristos     {
6629619d2aSchristos       if (string[i])
6729619d2aSchristos         (*index)++;
6829619d2aSchristos 
6929619d2aSchristos       /* Return "" in the case of a trailing `:'. */
7029619d2aSchristos       return xstrdup ("");
7129619d2aSchristos     }
7229619d2aSchristos   else
7329619d2aSchristos     {
7429619d2aSchristos       char *value;
7529619d2aSchristos 
7629619d2aSchristos       value = xmalloc (1 + (i - start));
7729619d2aSchristos       memcpy (value, &string[start], (i - start));
7829619d2aSchristos       value [i - start] = 0;
7929619d2aSchristos 
8029619d2aSchristos       return value;
8129619d2aSchristos     }
8229619d2aSchristos }
8329619d2aSchristos 
8429619d2aSchristos /* Return the full pathname for FILENAME by searching along PATH.
8529619d2aSchristos    When found, return the stat () info for FILENAME in FINFO.
8629619d2aSchristos    If PATH is NULL, only the current directory is searched.
8729619d2aSchristos    If the file could not be found, return a NULL pointer. */
8829619d2aSchristos char *
get_file_info_in_path(char * filename,char * path,struct stat * finfo)8929619d2aSchristos get_file_info_in_path (char *filename, char *path, struct stat *finfo)
9029619d2aSchristos {
9129619d2aSchristos   char *dir;
9229619d2aSchristos   int result, index = 0;
9329619d2aSchristos 
9429619d2aSchristos   if (path == NULL)
9529619d2aSchristos     path = ".";
9629619d2aSchristos 
9729619d2aSchristos   /* Handle absolute pathnames.  */
9829619d2aSchristos   if (IS_ABSOLUTE (filename)
9929619d2aSchristos       || (*filename == '.'
10029619d2aSchristos           && (IS_SLASH (filename[1])
10129619d2aSchristos               || (filename[1] == '.' && IS_SLASH (filename[2])))))
10229619d2aSchristos     {
10329619d2aSchristos       if (stat (filename, finfo) == 0)
10429619d2aSchristos         return xstrdup (filename);
10529619d2aSchristos       else
10629619d2aSchristos         return NULL;
10729619d2aSchristos     }
10829619d2aSchristos 
10929619d2aSchristos   while ((dir = extract_colon_unit (path, &index)))
11029619d2aSchristos     {
11129619d2aSchristos       char *fullpath;
11229619d2aSchristos 
11329619d2aSchristos       if (!*dir)
11429619d2aSchristos         {
11529619d2aSchristos           free (dir);
11629619d2aSchristos           dir = xstrdup (".");
11729619d2aSchristos         }
11829619d2aSchristos 
11929619d2aSchristos       fullpath = xmalloc (2 + strlen (dir) + strlen (filename));
12029619d2aSchristos       sprintf (fullpath, "%s/%s", dir, filename);
12129619d2aSchristos       free (dir);
12229619d2aSchristos 
12329619d2aSchristos       result = stat (fullpath, finfo);
12429619d2aSchristos 
12529619d2aSchristos       if (result == 0)
12629619d2aSchristos         return fullpath;
12729619d2aSchristos       else
12829619d2aSchristos         free (fullpath);
12929619d2aSchristos     }
13029619d2aSchristos   return NULL;
13129619d2aSchristos }
13229619d2aSchristos 
13329619d2aSchristos /* Prepend and append new paths to include_files_path.  */
13429619d2aSchristos void
prepend_to_include_path(char * path)13529619d2aSchristos prepend_to_include_path (char *path)
13629619d2aSchristos {
13729619d2aSchristos   if (!include_files_path)
13829619d2aSchristos     {
13929619d2aSchristos       include_files_path = xstrdup (path);
14029619d2aSchristos       include_files_path = xrealloc (include_files_path,
14129619d2aSchristos           strlen (include_files_path) + 3); /* 3 for ":.\0" */
14229619d2aSchristos       strcat (strcat (include_files_path, PATH_SEP), ".");
14329619d2aSchristos     }
14429619d2aSchristos   else
14529619d2aSchristos     {
14629619d2aSchristos       char *tmp = xstrdup (include_files_path);
14729619d2aSchristos       include_files_path = xrealloc (include_files_path,
14829619d2aSchristos           strlen (include_files_path) + strlen (path) + 2); /* 2 for ":\0" */
14929619d2aSchristos       strcpy (include_files_path, path);
15029619d2aSchristos       strcat (include_files_path, PATH_SEP);
15129619d2aSchristos       strcat (include_files_path, tmp);
15229619d2aSchristos       free (tmp);
15329619d2aSchristos     }
15429619d2aSchristos }
15529619d2aSchristos 
15629619d2aSchristos void
append_to_include_path(char * path)15729619d2aSchristos append_to_include_path (char *path)
15829619d2aSchristos {
15929619d2aSchristos   if (!include_files_path)
16029619d2aSchristos     include_files_path = xstrdup (".");
16129619d2aSchristos 
16229619d2aSchristos   include_files_path = (char *) xrealloc (include_files_path,
16329619d2aSchristos         2 + strlen (include_files_path) + strlen (path));
16429619d2aSchristos   strcat (include_files_path, PATH_SEP);
16529619d2aSchristos   strcat (include_files_path, path);
16629619d2aSchristos }
16729619d2aSchristos 
16829619d2aSchristos /* Remove the first path from the include_files_path.  */
16929619d2aSchristos void
pop_path_from_include_path(void)17029619d2aSchristos pop_path_from_include_path (void)
17129619d2aSchristos {
17229619d2aSchristos   int i = 0;
17329619d2aSchristos   char *tmp;
17429619d2aSchristos 
17529619d2aSchristos   if (include_files_path)
17629619d2aSchristos     for (i = 0; i < strlen (include_files_path)
17729619d2aSchristos         && include_files_path[i] != ':'; i++);
17829619d2aSchristos 
17929619d2aSchristos   /* Advance include_files_path to the next char from ':'  */
18029619d2aSchristos   tmp = (char *) xmalloc (strlen (include_files_path) - i);
18129619d2aSchristos   strcpy (tmp, (char *) include_files_path + i + 1);
18229619d2aSchristos 
18329619d2aSchristos   free (include_files_path);
18429619d2aSchristos   include_files_path = tmp;
18529619d2aSchristos }
18629619d2aSchristos 
18729619d2aSchristos /* Find and load the file named FILENAME.  Return a pointer to
18829619d2aSchristos    the loaded file, or NULL if it can't be loaded.  If USE_PATH is zero,
18929619d2aSchristos    just look for the given file (this is used in handle_delayed_writes),
19029619d2aSchristos    else search along include_files_path.   */
19129619d2aSchristos 
19229619d2aSchristos char *
find_and_load(char * filename,int use_path)19329619d2aSchristos find_and_load (char *filename, int use_path)
19429619d2aSchristos {
19529619d2aSchristos   struct stat fileinfo;
19629619d2aSchristos   long file_size;
19729619d2aSchristos   int file = -1, count = 0;
19829619d2aSchristos   char *fullpath, *result;
19929619d2aSchristos   int n, bytes_to_read;
20029619d2aSchristos 
20129619d2aSchristos   result = fullpath = NULL;
20229619d2aSchristos 
20329619d2aSchristos   fullpath
20429619d2aSchristos     = get_file_info_in_path (filename, use_path ? include_files_path : NULL,
20529619d2aSchristos                              &fileinfo);
20629619d2aSchristos 
20729619d2aSchristos   if (!fullpath)
20829619d2aSchristos     goto error_exit;
20929619d2aSchristos 
21029619d2aSchristos   filename = fullpath;
21129619d2aSchristos   file_size = (long) fileinfo.st_size;
21229619d2aSchristos 
21329619d2aSchristos   file = open (filename, O_RDONLY);
21429619d2aSchristos   if (file < 0)
21529619d2aSchristos     goto error_exit;
21629619d2aSchristos 
21729619d2aSchristos   /* Load the file, with enough room for a newline and a null. */
21829619d2aSchristos   result = xmalloc (file_size + 2);
21929619d2aSchristos 
22029619d2aSchristos   /* VMS stat lies about the st_size value.  The actual number of
22129619d2aSchristos      readable bytes is always less than this value.  The arcane
22229619d2aSchristos      mysteries of VMS/RMS are too much to probe, so this hack
22329619d2aSchristos     suffices to make things work.  It's also needed on Cygwin.  And so
22429619d2aSchristos     we might as well use it everywhere.  */
22529619d2aSchristos   bytes_to_read = file_size;
22629619d2aSchristos   while ((n = read (file, result + count, bytes_to_read)) > 0)
22729619d2aSchristos     {
22829619d2aSchristos       count += n;
22929619d2aSchristos       bytes_to_read -= n;
23029619d2aSchristos     }
23129619d2aSchristos   if (0 < count && count < file_size)
23229619d2aSchristos     result = xrealloc (result, count + 2); /* why waste the slack? */
23329619d2aSchristos   else if (n == -1)
23429619d2aSchristos error_exit:
23529619d2aSchristos     {
23629619d2aSchristos       if (result)
23729619d2aSchristos         free (result);
23829619d2aSchristos 
23929619d2aSchristos       if (fullpath)
24029619d2aSchristos         free (fullpath);
24129619d2aSchristos 
24229619d2aSchristos       if (file != -1)
24329619d2aSchristos         close (file);
24429619d2aSchristos 
24529619d2aSchristos       return NULL;
24629619d2aSchristos     }
24729619d2aSchristos   close (file);
24829619d2aSchristos 
24929619d2aSchristos   /* Set the globals to the new file. */
25029619d2aSchristos   input_text = result;
25129619d2aSchristos   input_text_length = count;
25229619d2aSchristos   input_filename = fullpath;
25329619d2aSchristos   node_filename = xstrdup (fullpath);
25429619d2aSchristos   input_text_offset = 0;
25529619d2aSchristos   line_number = 1;
25629619d2aSchristos   /* Not strictly necessary.  This magic prevents read_token () from doing
25729619d2aSchristos      extra unnecessary work each time it is called (that is a lot of times).
25829619d2aSchristos      INPUT_TEXT_LENGTH is one past the actual end of the text. */
25929619d2aSchristos   input_text[input_text_length] = '\n';
26029619d2aSchristos   /* This, on the other hand, is always necessary.  */
26129619d2aSchristos   input_text[input_text_length+1] = 0;
26229619d2aSchristos   return result;
26329619d2aSchristos }
26429619d2aSchristos 
26529619d2aSchristos /* Pushing and popping files.  */
26629619d2aSchristos static void
push_node_filename(void)26729619d2aSchristos push_node_filename (void)
26829619d2aSchristos {
26929619d2aSchristos   if (node_filename_stack_index + 1 > node_filename_stack_size)
27029619d2aSchristos     node_filename_stack = xrealloc
27129619d2aSchristos     (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *));
27229619d2aSchristos 
27329619d2aSchristos   node_filename_stack[node_filename_stack_index] = node_filename;
27429619d2aSchristos   node_filename_stack_index++;
27529619d2aSchristos }
27629619d2aSchristos 
27729619d2aSchristos static void
pop_node_filename(void)27829619d2aSchristos pop_node_filename (void)
27929619d2aSchristos {
28029619d2aSchristos   node_filename = node_filename_stack[--node_filename_stack_index];
28129619d2aSchristos }
28229619d2aSchristos 
28329619d2aSchristos /* Save the state of the current input file. */
28429619d2aSchristos void
pushfile(void)28529619d2aSchristos pushfile (void)
28629619d2aSchristos {
28729619d2aSchristos   FSTACK *newstack = xmalloc (sizeof (FSTACK));
28829619d2aSchristos   newstack->filename = input_filename;
28929619d2aSchristos   newstack->text = input_text;
29029619d2aSchristos   newstack->size = input_text_length;
29129619d2aSchristos   newstack->offset = input_text_offset;
29229619d2aSchristos   newstack->line_number = line_number;
29329619d2aSchristos   newstack->next = filestack;
29429619d2aSchristos 
29529619d2aSchristos   filestack = newstack;
29629619d2aSchristos   push_node_filename ();
29729619d2aSchristos }
29829619d2aSchristos 
29929619d2aSchristos /* Make the current file globals be what is on top of the file stack. */
30029619d2aSchristos void
popfile(void)30129619d2aSchristos popfile (void)
30229619d2aSchristos {
30329619d2aSchristos   FSTACK *tos = filestack;
30429619d2aSchristos 
30529619d2aSchristos   if (!tos)
30629619d2aSchristos     abort ();                   /* My fault.  I wonder what I did? */
30729619d2aSchristos 
30829619d2aSchristos   if (macro_expansion_output_stream)
30929619d2aSchristos     {
31029619d2aSchristos       maybe_write_itext (input_text, input_text_offset);
31129619d2aSchristos       forget_itext (input_text);
31229619d2aSchristos     }
31329619d2aSchristos 
31429619d2aSchristos   /* Pop the stack. */
31529619d2aSchristos   filestack = filestack->next;
31629619d2aSchristos 
31729619d2aSchristos   /* Make sure that commands with braces have been satisfied. */
31829619d2aSchristos   if (!executing_string && !me_executing_string)
31929619d2aSchristos     discard_braces ();
32029619d2aSchristos 
32129619d2aSchristos   /* Get the top of the stack into the globals. */
32229619d2aSchristos   input_filename = tos->filename;
32329619d2aSchristos   input_text = tos->text;
32429619d2aSchristos   input_text_length = tos->size;
32529619d2aSchristos   input_text_offset = tos->offset;
32629619d2aSchristos   line_number = tos->line_number;
32729619d2aSchristos   free (tos);
32829619d2aSchristos 
32929619d2aSchristos   /* Go back to the (now) current node. */
33029619d2aSchristos   pop_node_filename ();
33129619d2aSchristos }
33229619d2aSchristos 
33329619d2aSchristos /* Flush all open files on the file stack. */
33429619d2aSchristos void
flush_file_stack(void)33529619d2aSchristos flush_file_stack (void)
33629619d2aSchristos {
33729619d2aSchristos   while (filestack)
33829619d2aSchristos     {
33929619d2aSchristos       char *fname = input_filename;
34029619d2aSchristos       char *text = input_text;
34129619d2aSchristos       popfile ();
34229619d2aSchristos       free (fname);
34329619d2aSchristos       free (text);
34429619d2aSchristos     }
34529619d2aSchristos }
34629619d2aSchristos 
34729619d2aSchristos /* Return the index of the first character in the filename
34829619d2aSchristos    which is past all the leading directory characters.  */
34929619d2aSchristos static int
skip_directory_part(char * filename)35029619d2aSchristos skip_directory_part (char *filename)
35129619d2aSchristos {
35229619d2aSchristos   int i = strlen (filename) - 1;
35329619d2aSchristos 
35429619d2aSchristos   while (i && !IS_SLASH (filename[i]))
35529619d2aSchristos     i--;
35629619d2aSchristos   if (IS_SLASH (filename[i]))
35729619d2aSchristos     i++;
35829619d2aSchristos   else if (filename[i] && HAVE_DRIVE (filename))
35929619d2aSchristos     i = 2;
36029619d2aSchristos 
36129619d2aSchristos   return i;
36229619d2aSchristos }
36329619d2aSchristos 
36429619d2aSchristos static char *
filename_non_directory(char * name)36529619d2aSchristos filename_non_directory (char *name)
36629619d2aSchristos {
36729619d2aSchristos   return xstrdup (name + skip_directory_part (name));
36829619d2aSchristos }
36929619d2aSchristos 
37029619d2aSchristos /* Return just the simple part of the filename; i.e. the
37129619d2aSchristos    filename without the path information, or extensions.
37229619d2aSchristos    This conses up a new string. */
37329619d2aSchristos char *
filename_part(char * filename)37429619d2aSchristos filename_part (char *filename)
37529619d2aSchristos {
37629619d2aSchristos   char *basename = filename_non_directory (filename);
37729619d2aSchristos 
37829619d2aSchristos #ifdef REMOVE_OUTPUT_EXTENSIONS
37929619d2aSchristos   /* See if there is an extension to remove.  If so, remove it. */
38029619d2aSchristos   {
38129619d2aSchristos     char *temp = strrchr (basename, '.');
38229619d2aSchristos     if (temp)
38329619d2aSchristos       *temp = 0;
38429619d2aSchristos   }
38529619d2aSchristos #endif /* REMOVE_OUTPUT_EXTENSIONS */
38629619d2aSchristos   return basename;
38729619d2aSchristos }
38829619d2aSchristos 
38929619d2aSchristos /* Return the pathname part of filename.  This can be NULL. */
39029619d2aSchristos char *
pathname_part(char * filename)39129619d2aSchristos pathname_part (char *filename)
39229619d2aSchristos {
39329619d2aSchristos   char *result = NULL;
39429619d2aSchristos   int i;
39529619d2aSchristos 
39629619d2aSchristos   filename = expand_filename (filename, "");
39729619d2aSchristos 
39829619d2aSchristos   i = skip_directory_part (filename);
39929619d2aSchristos   if (i)
40029619d2aSchristos     {
40129619d2aSchristos       result = xmalloc (1 + i);
40229619d2aSchristos       strncpy (result, filename, i);
40329619d2aSchristos       result[i] = 0;
40429619d2aSchristos     }
40529619d2aSchristos   free (filename);
40629619d2aSchristos   return result;
40729619d2aSchristos }
40829619d2aSchristos 
40929619d2aSchristos /* Return the full path to FILENAME. */
41029619d2aSchristos static char *
full_pathname(char * filename)41129619d2aSchristos full_pathname (char *filename)
41229619d2aSchristos {
41329619d2aSchristos   int initial_character;
41429619d2aSchristos   char *result;
41529619d2aSchristos 
41629619d2aSchristos   /* No filename given? */
41729619d2aSchristos   if (!filename || !*filename)
41829619d2aSchristos     return xstrdup ("");
41929619d2aSchristos 
42029619d2aSchristos   /* Already absolute? */
42129619d2aSchristos   if (IS_ABSOLUTE (filename) ||
42229619d2aSchristos       (*filename == '.' &&
42329619d2aSchristos        (IS_SLASH (filename[1]) ||
42429619d2aSchristos         (filename[1] == '.' && IS_SLASH (filename[2])))))
42529619d2aSchristos     return xstrdup (filename);
42629619d2aSchristos 
42729619d2aSchristos   initial_character = *filename;
42829619d2aSchristos   if (initial_character != '~')
42929619d2aSchristos     {
43029619d2aSchristos       char *localdir = xmalloc (1025);
43129619d2aSchristos #ifdef HAVE_GETCWD
43229619d2aSchristos       if (!getcwd (localdir, 1024))
43329619d2aSchristos #else
43429619d2aSchristos       if (!getwd (localdir))
43529619d2aSchristos #endif
43629619d2aSchristos         {
43729619d2aSchristos           fprintf (stderr, _("%s: getwd: %s, %s\n"),
43829619d2aSchristos                    progname, filename, localdir);
43929619d2aSchristos           xexit (1);
44029619d2aSchristos         }
44129619d2aSchristos 
44229619d2aSchristos       strcat (localdir, "/");
44329619d2aSchristos       strcat (localdir, filename);
44429619d2aSchristos       result = xstrdup (localdir);
44529619d2aSchristos       free (localdir);
44629619d2aSchristos     }
44729619d2aSchristos   else
44829619d2aSchristos     { /* Does anybody know why WIN32 doesn't want to support $HOME?
44929619d2aSchristos          If the reason is they don't have getpwnam, they should
45029619d2aSchristos          only disable the else clause below.  */
45129619d2aSchristos #ifndef WIN32
45229619d2aSchristos       if (IS_SLASH (filename[1]))
45329619d2aSchristos         {
45429619d2aSchristos           /* Return the concatenation of the environment variable HOME
45529619d2aSchristos              and the rest of the string. */
45629619d2aSchristos           char *temp_home;
45729619d2aSchristos 
45829619d2aSchristos           temp_home = (char *) getenv ("HOME");
45929619d2aSchristos           result = xmalloc (strlen (&filename[1])
46029619d2aSchristos                                     + 1
461*d3737e9cSchristos                                     + (temp_home ? strlen (temp_home) : 0));
46229619d2aSchristos           *result = 0;
46329619d2aSchristos 
46429619d2aSchristos           if (temp_home)
46529619d2aSchristos             strcpy (result, temp_home);
46629619d2aSchristos 
46729619d2aSchristos           strcat (result, &filename[1]);
46829619d2aSchristos         }
46929619d2aSchristos       else
47029619d2aSchristos         {
47129619d2aSchristos           struct passwd *user_entry;
47229619d2aSchristos           int i, c;
47329619d2aSchristos           char *username = xmalloc (257);
47429619d2aSchristos 
47529619d2aSchristos           for (i = 1; (c = filename[i]); i++)
47629619d2aSchristos             {
47729619d2aSchristos               if (IS_SLASH (c))
47829619d2aSchristos                 break;
47929619d2aSchristos               else
48029619d2aSchristos                 username[i - 1] = c;
48129619d2aSchristos             }
48229619d2aSchristos           if (c)
48329619d2aSchristos             username[i - 1] = 0;
48429619d2aSchristos 
48529619d2aSchristos           user_entry = getpwnam (username);
48629619d2aSchristos 
48729619d2aSchristos           if (!user_entry)
48829619d2aSchristos             return xstrdup (filename);
48929619d2aSchristos 
49029619d2aSchristos           result = xmalloc (1 + strlen (user_entry->pw_dir)
49129619d2aSchristos                                     + strlen (&filename[i]));
49229619d2aSchristos           strcpy (result, user_entry->pw_dir);
49329619d2aSchristos           strcat (result, &filename[i]);
49429619d2aSchristos         }
49529619d2aSchristos #endif /* not WIN32 */
49629619d2aSchristos     }
49729619d2aSchristos   return result;
49829619d2aSchristos }
49929619d2aSchristos 
50029619d2aSchristos /* Return the expansion of FILENAME. */
50129619d2aSchristos char *
expand_filename(char * filename,char * input_name)50229619d2aSchristos expand_filename (char *filename, char *input_name)
50329619d2aSchristos {
50429619d2aSchristos   int i;
50529619d2aSchristos 
50629619d2aSchristos   if (filename)
50729619d2aSchristos     {
50829619d2aSchristos       filename = full_pathname (filename);
50929619d2aSchristos       if (IS_ABSOLUTE (filename)
51029619d2aSchristos 	  || (*filename == '.' &&
51129619d2aSchristos 	      (IS_SLASH (filename[1]) ||
51229619d2aSchristos 	       (filename[1] == '.' && IS_SLASH (filename[2])))))
51329619d2aSchristos 	return filename;
51429619d2aSchristos     }
51529619d2aSchristos   else
51629619d2aSchristos     {
51729619d2aSchristos       filename = filename_non_directory (input_name);
51829619d2aSchristos 
51929619d2aSchristos       if (!*filename)
52029619d2aSchristos         {
52129619d2aSchristos           free (filename);
52229619d2aSchristos           filename = xstrdup ("noname.texi");
52329619d2aSchristos         }
52429619d2aSchristos 
52529619d2aSchristos       for (i = strlen (filename) - 1; i; i--)
52629619d2aSchristos         if (filename[i] == '.')
52729619d2aSchristos           break;
52829619d2aSchristos 
52929619d2aSchristos       if (!i)
53029619d2aSchristos         i = strlen (filename);
53129619d2aSchristos 
53229619d2aSchristos       if (i + 6 > (strlen (filename)))
53329619d2aSchristos         filename = xrealloc (filename, i + 6);
53429619d2aSchristos       strcpy (filename + i, html ? ".html" : ".info");
53529619d2aSchristos       return filename;
53629619d2aSchristos     }
53729619d2aSchristos 
53829619d2aSchristos   if (IS_ABSOLUTE (input_name))
53929619d2aSchristos     {
54029619d2aSchristos       /* Make it so that relative names work. */
54129619d2aSchristos       char *result;
54229619d2aSchristos 
54329619d2aSchristos       i = strlen (input_name) - 1;
54429619d2aSchristos 
54529619d2aSchristos       result = xmalloc (1 + strlen (input_name) + strlen (filename));
54629619d2aSchristos       strcpy (result, input_name);
54729619d2aSchristos 
54829619d2aSchristos       while (!IS_SLASH (result[i]) && i)
54929619d2aSchristos         i--;
55029619d2aSchristos       if (IS_SLASH (result[i]))
55129619d2aSchristos         i++;
55229619d2aSchristos 
55329619d2aSchristos       strcpy (&result[i], filename);
55429619d2aSchristos       free (filename);
55529619d2aSchristos       return result;
55629619d2aSchristos     }
55729619d2aSchristos   return filename;
55829619d2aSchristos }
55929619d2aSchristos 
56029619d2aSchristos char *
output_name_from_input_name(char * name)56129619d2aSchristos output_name_from_input_name (char *name)
56229619d2aSchristos {
56329619d2aSchristos   return expand_filename (NULL, name);
56429619d2aSchristos }
56529619d2aSchristos 
56629619d2aSchristos 
56729619d2aSchristos /* Modify the file name FNAME so that it fits the limitations of the
56829619d2aSchristos    underlying filesystem.  In particular, truncate the file name as it
56929619d2aSchristos    would be truncated by the filesystem.  We assume the result can
57029619d2aSchristos    never be longer than the original, otherwise we couldn't be sure we
57129619d2aSchristos    have enough space in the original string to modify it in place.  */
57229619d2aSchristos char *
normalize_filename(char * fname)57329619d2aSchristos normalize_filename (char *fname)
57429619d2aSchristos {
57529619d2aSchristos   int maxlen;
57629619d2aSchristos   char orig[PATH_MAX + 1];
57729619d2aSchristos   int i;
57829619d2aSchristos   char *lastdot, *p;
57929619d2aSchristos 
58029619d2aSchristos #ifdef _PC_NAME_MAX
58129619d2aSchristos   maxlen = pathconf (fname, _PC_NAME_MAX);
58229619d2aSchristos   if (maxlen < 1)
58329619d2aSchristos #endif
58429619d2aSchristos     maxlen = PATH_MAX;
58529619d2aSchristos 
58629619d2aSchristos   i = skip_directory_part (fname);
58729619d2aSchristos   if (fname[i] == '\0')
58829619d2aSchristos     return fname;	/* only a directory name -- don't modify */
58929619d2aSchristos   strcpy (orig, fname + i);
59029619d2aSchristos 
59129619d2aSchristos   switch (maxlen)
59229619d2aSchristos     {
59329619d2aSchristos       case 12:	/* MS-DOS 8+3 filesystem */
59429619d2aSchristos 	if (orig[0] == '.')	/* leading dots are not allowed */
59529619d2aSchristos 	  orig[0] = '_';
59629619d2aSchristos 	lastdot = strrchr (orig, '.');
59729619d2aSchristos 	if (!lastdot)
59829619d2aSchristos 	  lastdot = orig + strlen (orig);
59929619d2aSchristos 	strncpy (fname + i, orig, lastdot - orig);
60029619d2aSchristos 	for (p = fname + i;
60129619d2aSchristos 	     p < fname + i + (lastdot - orig) && p < fname + i + 8;
60229619d2aSchristos 	     p++)
60329619d2aSchristos 	  if (*p == '.')
60429619d2aSchristos 	    *p = '_';
60529619d2aSchristos 	*p = '\0';
60629619d2aSchristos 	if (*lastdot == '.')
60729619d2aSchristos 	  strncat (fname + i, lastdot, 4);
60829619d2aSchristos 	break;
60929619d2aSchristos       case 14:	/* old Unix systems with 14-char limitation */
61029619d2aSchristos 	strcpy (fname + i, orig);
61129619d2aSchristos 	if (strlen (fname + i) > 14)
61229619d2aSchristos 	  fname[i + 14] = '\0';
61329619d2aSchristos 	break;
61429619d2aSchristos       default:
61529619d2aSchristos 	strcpy (fname + i, orig);
61629619d2aSchristos 	if (strlen (fname) > maxlen - 1)
61729619d2aSchristos 	  fname[maxlen - 1] = '\0';
61829619d2aSchristos 	break;
61929619d2aSchristos     }
62029619d2aSchristos 
62129619d2aSchristos   return fname;
62229619d2aSchristos }
62329619d2aSchristos 
62429619d2aSchristos /* Delayed writing functions.  A few of the commands
62529619d2aSchristos    needs to be handled at the end, namely @contents,
62629619d2aSchristos    @shortcontents, @printindex and @listoffloats.
62729619d2aSchristos    These functions take care of that.  */
62829619d2aSchristos static DELAYED_WRITE *delayed_writes = NULL;
62929619d2aSchristos int handling_delayed_writes = 0;
63029619d2aSchristos 
63129619d2aSchristos void
register_delayed_write(char * delayed_command)63229619d2aSchristos register_delayed_write (char *delayed_command)
63329619d2aSchristos {
63429619d2aSchristos   DELAYED_WRITE *new;
63529619d2aSchristos 
63629619d2aSchristos   if (!current_output_filename || !*current_output_filename)
63729619d2aSchristos     {
63829619d2aSchristos       /* Cannot register if we don't know what the output file is.  */
63929619d2aSchristos       warning (_("`%s' omitted before output filename"), delayed_command);
64029619d2aSchristos       return;
64129619d2aSchristos     }
64229619d2aSchristos 
64329619d2aSchristos   if (STREQ (current_output_filename, "-"))
64429619d2aSchristos     {
64529619d2aSchristos       /* Do not register a new write if the output file is not seekable.
64629619d2aSchristos          Let the user know about it first, though.  */
64729619d2aSchristos       warning (_("`%s' omitted since writing to stdout"), delayed_command);
64829619d2aSchristos       return;
64929619d2aSchristos     }
65029619d2aSchristos 
65129619d2aSchristos   /* Don't complain if the user is writing /dev/null, since surely they
65229619d2aSchristos      don't care, but don't register the delayed write, either.  */
65329619d2aSchristos   if (FILENAME_CMP (current_output_filename, NULL_DEVICE) == 0
65429619d2aSchristos       || FILENAME_CMP (current_output_filename, ALSO_NULL_DEVICE) == 0)
65529619d2aSchristos     return;
65629619d2aSchristos 
65729619d2aSchristos   /* We need the HTML header in the output,
65829619d2aSchristos      to get a proper output_position.  */
65929619d2aSchristos   if (!executing_string && html)
66029619d2aSchristos     html_output_head ();
66129619d2aSchristos   /* Get output_position updated.  */
66229619d2aSchristos   flush_output ();
66329619d2aSchristos 
66429619d2aSchristos   new = xmalloc (sizeof (DELAYED_WRITE));
66529619d2aSchristos   new->command = xstrdup (delayed_command);
66629619d2aSchristos   new->filename = xstrdup (current_output_filename);
66729619d2aSchristos   new->input_filename = xstrdup (input_filename);
66829619d2aSchristos   new->position = output_position;
66929619d2aSchristos   new->calling_line = line_number;
67029619d2aSchristos   new->node = current_node ? xstrdup (current_node): "";
67129619d2aSchristos 
67229619d2aSchristos   new->node_order = node_order;
67329619d2aSchristos   new->index_order = index_counter;
67429619d2aSchristos 
67529619d2aSchristos   new->next = delayed_writes;
67629619d2aSchristos   delayed_writes = new;
67729619d2aSchristos }
67829619d2aSchristos 
67929619d2aSchristos void
handle_delayed_writes(void)68029619d2aSchristos handle_delayed_writes (void)
68129619d2aSchristos {
68229619d2aSchristos   DELAYED_WRITE *temp = (DELAYED_WRITE *) reverse_list
68329619d2aSchristos     ((GENERIC_LIST *) delayed_writes);
68429619d2aSchristos   int position_shift_amount, line_number_shift_amount;
68529619d2aSchristos   char *delayed_buf;
68629619d2aSchristos 
68729619d2aSchristos   handling_delayed_writes = 1;
68829619d2aSchristos 
68929619d2aSchristos   while (temp)
69029619d2aSchristos     {
69129619d2aSchristos       delayed_buf = find_and_load (temp->filename, 0);
69229619d2aSchristos 
69329619d2aSchristos       if (output_paragraph_offset > 0)
69429619d2aSchristos         {
69529619d2aSchristos           error (_("Output buffer not empty."));
69629619d2aSchristos           return;
69729619d2aSchristos         }
69829619d2aSchristos 
69929619d2aSchristos       if (!delayed_buf)
70029619d2aSchristos         {
70129619d2aSchristos           fs_error (temp->filename);
70229619d2aSchristos           return;
70329619d2aSchristos         }
70429619d2aSchristos 
70529619d2aSchristos       output_stream = fopen (temp->filename, "w");
70629619d2aSchristos       if (!output_stream)
70729619d2aSchristos         {
70829619d2aSchristos           fs_error (temp->filename);
70929619d2aSchristos           return;
71029619d2aSchristos         }
71129619d2aSchristos 
71229619d2aSchristos       if (fwrite (delayed_buf, 1, temp->position, output_stream) != temp->position)
71329619d2aSchristos         {
71429619d2aSchristos           fs_error (temp->filename);
71529619d2aSchristos           return;
71629619d2aSchristos         }
71729619d2aSchristos 
71829619d2aSchristos       {
71929619d2aSchristos         int output_position_at_start = output_position;
72029619d2aSchristos         int line_number_at_start = output_line_number;
72129619d2aSchristos 
72229619d2aSchristos         /* In order to make warnings and errors
72329619d2aSchristos            refer to the correct line number.  */
72429619d2aSchristos         input_filename = temp->input_filename;
72529619d2aSchristos         line_number = temp->calling_line;
72629619d2aSchristos 
72729619d2aSchristos         execute_string ("%s", temp->command);
72829619d2aSchristos         flush_output ();
72929619d2aSchristos 
73029619d2aSchristos         /* Since the output file is modified, following delayed writes
73129619d2aSchristos            need to be updated by this amount.  */
73229619d2aSchristos         position_shift_amount = output_position - output_position_at_start;
73329619d2aSchristos         line_number_shift_amount = output_line_number - line_number_at_start;
73429619d2aSchristos       }
73529619d2aSchristos 
73629619d2aSchristos       if (fwrite (delayed_buf + temp->position, 1,
73729619d2aSchristos             input_text_length - temp->position, output_stream)
73829619d2aSchristos           != input_text_length - temp->position
73929619d2aSchristos           || fclose (output_stream) != 0)
74029619d2aSchristos         fs_error (temp->filename);
74129619d2aSchristos 
74229619d2aSchristos       /* Done with the buffer.  */
74329619d2aSchristos       free (delayed_buf);
74429619d2aSchristos 
74529619d2aSchristos       /* Update positions in tag table for nodes that are defined after
74629619d2aSchristos          the line this delayed write is registered.  */
74729619d2aSchristos       if (!html && !xml)
74829619d2aSchristos         {
74929619d2aSchristos           TAG_ENTRY *node;
75029619d2aSchristos           for (node = tag_table; node; node = node->next_ent)
75129619d2aSchristos             if (node->order > temp->node_order)
75229619d2aSchristos               node->position += position_shift_amount;
75329619d2aSchristos         }
75429619d2aSchristos 
75529619d2aSchristos       /* Something similar for the line numbers in all of the defined
75629619d2aSchristos          indices.  */
75729619d2aSchristos       {
75829619d2aSchristos         int i;
75929619d2aSchristos         for (i = 0; i < defined_indices; i++)
76029619d2aSchristos           if (name_index_alist[i])
76129619d2aSchristos             {
76229619d2aSchristos               char *name = ((INDEX_ALIST *) name_index_alist[i])->name;
76329619d2aSchristos               INDEX_ELT *index;
76429619d2aSchristos               for (index = index_list (name); index; index = index->next)
76529619d2aSchristos                 if ((no_headers || STREQ (index->node, temp->node))
76629619d2aSchristos                     && index->entry_number > temp->index_order)
76729619d2aSchristos                   index->output_line += line_number_shift_amount;
76829619d2aSchristos             }
76929619d2aSchristos       }
77029619d2aSchristos 
77129619d2aSchristos       /* Shift remaining delayed positions
77229619d2aSchristos          by the length of this write.  */
77329619d2aSchristos       {
77429619d2aSchristos         DELAYED_WRITE *future_write = temp->next;
77529619d2aSchristos         while (future_write)
77629619d2aSchristos           {
77729619d2aSchristos             if (STREQ (temp->filename, future_write->filename))
77829619d2aSchristos               future_write->position += position_shift_amount;
77929619d2aSchristos             future_write = future_write->next;
78029619d2aSchristos           }
78129619d2aSchristos       }
78229619d2aSchristos 
78329619d2aSchristos       temp = temp->next;
78429619d2aSchristos     }
78529619d2aSchristos }
786