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