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