156160Sru/* filesys.c -- filesystem specific functions.
2146515Sru   $Id: filesys.c,v 1.6 2004/07/30 17:17:40 karl Exp $
321495Sjmacd
4146515Sru   Copyright (C) 1993, 1997, 1998, 2000, 2002, 2003, 2004 Free Software
5114472Sru   Foundation, Inc.
621495Sjmacd
721495Sjmacd   This program is free software; you can redistribute it and/or modify
821495Sjmacd   it under the terms of the GNU General Public License as published by
921495Sjmacd   the Free Software Foundation; either version 2, or (at your option)
1021495Sjmacd   any later version.
1121495Sjmacd
1221495Sjmacd   This program is distributed in the hope that it will be useful,
1321495Sjmacd   but WITHOUT ANY WARRANTY; without even the implied warranty of
1421495Sjmacd   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1521495Sjmacd   GNU General Public License for more details.
1621495Sjmacd
1721495Sjmacd   You should have received a copy of the GNU General Public License
1821495Sjmacd   along with this program; if not, write to the Free Software
1921495Sjmacd   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2021495Sjmacd
2121495Sjmacd   Written by Brian Fox (bfox@ai.mit.edu). */
2221495Sjmacd
2342660Smarkm#include "info.h"
2442660Smarkm
2521495Sjmacd#include "tilde.h"
2621495Sjmacd#include "filesys.h"
2721495Sjmacd
2821495Sjmacd/* Local to this file. */
29146515Srustatic char *info_file_in_path (char *filename, char *path);
30146515Srustatic char *lookup_info_filename (char *filename);
31146515Srustatic char *info_absolute_file (char *fname);
3221495Sjmacd
33146515Srustatic void remember_info_filename (char *filename, char *expansion);
34146515Srustatic void maybe_initialize_infopath (void);
35146515Sru
3642660Smarkmtypedef struct
3742660Smarkm{
3821495Sjmacd  char *suffix;
3921495Sjmacd  char *decompressor;
4021495Sjmacd} COMPRESSION_ALIST;
4121495Sjmacd
4221495Sjmacdstatic char *info_suffixes[] = {
4321495Sjmacd  ".info",
4421495Sjmacd  "-info",
4542660Smarkm  "/index",
4656160Sru  ".inf",       /* 8+3 file on filesystem which supports long file names */
4756160Sru#ifdef __MSDOS__
4856160Sru  /* 8+3 file names strike again...  */
4956160Sru  ".in",        /* for .inz, .igz etc. */
5056160Sru  ".i",
5156160Sru#endif
5256160Sru  "",
5356160Sru  NULL
5421495Sjmacd};
5521495Sjmacd
5621495Sjmacdstatic COMPRESSION_ALIST compress_suffixes[] = {
5756160Sru  { ".gz", "gunzip" },
5856160Sru  { ".bz2", "bunzip2" },
5956160Sru  { ".z", "gunzip" },
6021495Sjmacd  { ".Z", "uncompress" },
6121495Sjmacd  { ".Y", "unyabba" },
6256160Sru#ifdef __MSDOS__
6356160Sru  { "gz", "gunzip" },
6456160Sru  { "z", "gunzip" },
6556160Sru#endif
6621495Sjmacd  { (char *)NULL, (char *)NULL }
6721495Sjmacd};
6821495Sjmacd
6921495Sjmacd/* The path on which we look for info files.  You can initialize this
7021495Sjmacd   from the environment variable INFOPATH if there is one, or you can
7121495Sjmacd   call info_add_path () to add paths to the beginning or end of it.
7221495Sjmacd   You can call zap_infopath () to make the path go away. */
7321495Sjmacdchar *infopath = (char *)NULL;
7421495Sjmacdstatic int infopath_size = 0;
7521495Sjmacd
7621495Sjmacd/* Expand the filename in PARTIAL to make a real name for this operating
7721495Sjmacd   system.  This looks in INFO_PATHS in order to find the correct file.
7821495Sjmacd   If it can't find the file, it returns NULL. */
7921495Sjmacdstatic char *local_temp_filename = (char *)NULL;
8021495Sjmacdstatic int local_temp_filename_size = 0;
8121495Sjmacd
8221495Sjmacdchar *
83146515Sruinfo_find_fullpath (char *partial)
8421495Sjmacd{
8521495Sjmacd  int initial_character;
8621495Sjmacd  char *temp;
8721495Sjmacd
8821495Sjmacd  filesys_error_number = 0;
8921495Sjmacd
9021495Sjmacd  maybe_initialize_infopath ();
9121495Sjmacd
9221495Sjmacd  if (partial && (initial_character = *partial))
9321495Sjmacd    {
9421495Sjmacd      char *expansion;
9521495Sjmacd
9621495Sjmacd      expansion = lookup_info_filename (partial);
9721495Sjmacd
9821495Sjmacd      if (expansion)
9942660Smarkm        return (expansion);
10021495Sjmacd
10121495Sjmacd      /* If we have the full path to this file, we still may have to add
10242660Smarkm         various extensions to it.  I guess we have to stat this file
10342660Smarkm         after all. */
10456160Sru      if (IS_ABSOLUTE (partial))
10556160Sru	temp = info_absolute_file (partial);
10621495Sjmacd      else if (initial_character == '~')
10742660Smarkm        {
10842660Smarkm          expansion = tilde_expand_word (partial);
10956160Sru          if (IS_ABSOLUTE (expansion))
11042660Smarkm            {
11156160Sru              temp = info_absolute_file (expansion);
11242660Smarkm              free (expansion);
11342660Smarkm            }
11442660Smarkm          else
11542660Smarkm            temp = expansion;
11642660Smarkm        }
11721495Sjmacd      else if (initial_character == '.' &&
11856160Sru               (IS_SLASH (partial[1]) ||
11956160Sru		(partial[1] == '.' && IS_SLASH (partial[2]))))
12042660Smarkm        {
12142660Smarkm          if (local_temp_filename_size < 1024)
12242660Smarkm            local_temp_filename = (char *)xrealloc
12342660Smarkm              (local_temp_filename, (local_temp_filename_size = 1024));
12421495Sjmacd#if defined (HAVE_GETCWD)
12542660Smarkm          if (!getcwd (local_temp_filename, local_temp_filename_size))
12621495Sjmacd#else /*  !HAVE_GETCWD */
12742660Smarkm          if (!getwd (local_temp_filename))
12821495Sjmacd#endif /* !HAVE_GETCWD */
12942660Smarkm            {
13042660Smarkm              filesys_error_number = errno;
13142660Smarkm              return (partial);
13242660Smarkm            }
13321495Sjmacd
13442660Smarkm          strcat (local_temp_filename, "/");
13542660Smarkm          strcat (local_temp_filename, partial);
13656160Sru	  temp = info_absolute_file (local_temp_filename); /* try extensions */
13756160Sru	  if (!temp)
13856160Sru	    partial = local_temp_filename;
13942660Smarkm        }
14021495Sjmacd      else
14142660Smarkm        temp = info_file_in_path (partial, infopath);
14221495Sjmacd
14321495Sjmacd      if (temp)
14442660Smarkm        {
14542660Smarkm          remember_info_filename (partial, temp);
146146515Sru          if (strlen (temp) > (unsigned int) local_temp_filename_size)
14742660Smarkm            local_temp_filename = (char *) xrealloc
14842660Smarkm              (local_temp_filename,
14942660Smarkm               (local_temp_filename_size = (50 + strlen (temp))));
15042660Smarkm          strcpy (local_temp_filename, temp);
15142660Smarkm          free (temp);
15242660Smarkm          return (local_temp_filename);
15342660Smarkm        }
15421495Sjmacd    }
15521495Sjmacd  return (partial);
15621495Sjmacd}
15721495Sjmacd
15821495Sjmacd/* Scan the list of directories in PATH looking for FILENAME.  If we find
15921495Sjmacd   one that is a regular file, return it as a new string.  Otherwise, return
16021495Sjmacd   a NULL pointer. */
16121495Sjmacdstatic char *
162146515Sruinfo_file_in_path (char *filename, char *path)
16321495Sjmacd{
16421495Sjmacd  struct stat finfo;
16521495Sjmacd  char *temp_dirname;
16621495Sjmacd  int statable, dirname_index;
16721495Sjmacd
16893139Sru  /* Reject ridiculous cases up front, to prevent infinite recursion
16993139Sru     later on.  E.g., someone might say "info '(.)foo'"...  */
17093139Sru  if (!*filename || STREQ (filename, ".") || STREQ (filename, ".."))
17193139Sru    return NULL;
17293139Sru
17321495Sjmacd  dirname_index = 0;
17421495Sjmacd
17542660Smarkm  while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
17621495Sjmacd    {
17721495Sjmacd      register int i, pre_suffix_length;
17821495Sjmacd      char *temp;
17921495Sjmacd
18021495Sjmacd      /* Expand a leading tilde if one is present. */
18121495Sjmacd      if (*temp_dirname == '~')
18242660Smarkm        {
18342660Smarkm          char *expanded_dirname;
18421495Sjmacd
18542660Smarkm          expanded_dirname = tilde_expand_word (temp_dirname);
18642660Smarkm          free (temp_dirname);
18742660Smarkm          temp_dirname = expanded_dirname;
18842660Smarkm        }
18921495Sjmacd
19021495Sjmacd      temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename));
19121495Sjmacd      strcpy (temp, temp_dirname);
19256160Sru      if (!IS_SLASH (temp[(strlen (temp)) - 1]))
19342660Smarkm        strcat (temp, "/");
19421495Sjmacd      strcat (temp, filename);
19521495Sjmacd
19621495Sjmacd      pre_suffix_length = strlen (temp);
19721495Sjmacd
19821495Sjmacd      free (temp_dirname);
19921495Sjmacd
20021495Sjmacd      for (i = 0; info_suffixes[i]; i++)
20142660Smarkm        {
20242660Smarkm          strcpy (temp + pre_suffix_length, info_suffixes[i]);
20321495Sjmacd
20442660Smarkm          statable = (stat (temp, &finfo) == 0);
20521495Sjmacd
20642660Smarkm          /* If we have found a regular file, then use that.  Else, if we
20742660Smarkm             have found a directory, look in that directory for this file. */
20842660Smarkm          if (statable)
20942660Smarkm            {
21042660Smarkm              if (S_ISREG (finfo.st_mode))
21142660Smarkm                {
21242660Smarkm                  return (temp);
21342660Smarkm                }
21442660Smarkm              else if (S_ISDIR (finfo.st_mode))
21542660Smarkm                {
21642660Smarkm                  char *newpath, *filename_only, *newtemp;
21721495Sjmacd
21842660Smarkm                  newpath = xstrdup (temp);
21942660Smarkm                  filename_only = filename_non_directory (filename);
22042660Smarkm                  newtemp = info_file_in_path (filename_only, newpath);
22121495Sjmacd
22242660Smarkm                  free (newpath);
22342660Smarkm                  if (newtemp)
22442660Smarkm                    {
22542660Smarkm                      free (temp);
22642660Smarkm                      return (newtemp);
22742660Smarkm                    }
22842660Smarkm                }
22942660Smarkm            }
23042660Smarkm          else
23142660Smarkm            {
23242660Smarkm              /* Add various compression suffixes to the name to see if
23342660Smarkm                 the file is present in compressed format. */
23442660Smarkm              register int j, pre_compress_suffix_length;
23521495Sjmacd
23642660Smarkm              pre_compress_suffix_length = strlen (temp);
23721495Sjmacd
23842660Smarkm              for (j = 0; compress_suffixes[j].suffix; j++)
23942660Smarkm                {
24042660Smarkm                  strcpy (temp + pre_compress_suffix_length,
24142660Smarkm                          compress_suffixes[j].suffix);
24221495Sjmacd
24342660Smarkm                  statable = (stat (temp, &finfo) == 0);
24442660Smarkm                  if (statable && (S_ISREG (finfo.st_mode)))
24542660Smarkm                    return (temp);
24642660Smarkm                }
24742660Smarkm            }
24842660Smarkm        }
24921495Sjmacd      free (temp);
25021495Sjmacd    }
25121495Sjmacd  return ((char *)NULL);
25221495Sjmacd}
25321495Sjmacd
25456160Sru/* Assume FNAME is an absolute file name, and check whether it is
25556160Sru   a regular file.  If it is, return it as a new string; otherwise
25656160Sru   return a NULL pointer.  We do it by taking the file name apart
25756160Sru   into its directory and basename parts, and calling info_file_in_path.*/
25856160Srustatic char *
259146515Sruinfo_absolute_file (char *fname)
26056160Sru{
26156160Sru  char *containing_dir = xstrdup (fname);
26256160Sru  char *base = filename_non_directory (containing_dir);
26356160Sru
26456160Sru  if (base > containing_dir)
26556160Sru    base[-1] = '\0';
26656160Sru
26756160Sru  return info_file_in_path (filename_non_directory (fname), containing_dir);
26856160Sru}
26956160Sru
270114472Sru
271114472Sru/* Given a string containing units of information separated by the
272114472Sru   PATH_SEP character, return the next one after IDX, or NULL if there
273114472Sru   are no more.  Advance IDX to the character after the colon. */
274114472Sru
27521495Sjmacdchar *
276146515Sruextract_colon_unit (char *string, int *idx)
27721495Sjmacd{
278146515Sru  unsigned int i = (unsigned int) *idx;
279146515Sru  unsigned int start = i;
28021495Sjmacd
281114472Sru  if (!string || i >= strlen (string))
282114472Sru    return NULL;
28321495Sjmacd
284146515Sru  if (!string[i]) /* end of string */
285146515Sru    return NULL;
286146515Sru
287114472Sru  /* Advance to next PATH_SEP.  */
28856160Sru  while (string[i] && string[i] != PATH_SEP[0])
28921495Sjmacd    i++;
29021495Sjmacd
291114472Sru  {
292114472Sru    char *value = xmalloc ((i - start) + 1);
293114472Sru    strncpy (value, &string[start], (i - start));
294114472Sru    value[i - start] = 0;
295114472Sru
296114472Sru    i++; /* move past PATH_SEP */
297114472Sru    *idx = i;
298114472Sru    return value;
299114472Sru  }
30021495Sjmacd}
30121495Sjmacd
30221495Sjmacd/* A structure which associates a filename with its expansion. */
303114472Srutypedef struct
304114472Sru{
30521495Sjmacd  char *filename;
30621495Sjmacd  char *expansion;
30721495Sjmacd} FILENAME_LIST;
30821495Sjmacd
30921495Sjmacd/* An array of remembered arguments and results. */
31021495Sjmacdstatic FILENAME_LIST **names_and_files = (FILENAME_LIST **)NULL;
31121495Sjmacdstatic int names_and_files_index = 0;
31221495Sjmacdstatic int names_and_files_slots = 0;
31321495Sjmacd
31421495Sjmacd/* Find the result for having already called info_find_fullpath () with
31521495Sjmacd   FILENAME. */
31621495Sjmacdstatic char *
317146515Srulookup_info_filename (char *filename)
31821495Sjmacd{
31921495Sjmacd  if (filename && names_and_files)
32021495Sjmacd    {
32121495Sjmacd      register int i;
32221495Sjmacd      for (i = 0; names_and_files[i]; i++)
32342660Smarkm        {
32456160Sru          if (FILENAME_CMP (names_and_files[i]->filename, filename) == 0)
32542660Smarkm            return (names_and_files[i]->expansion);
32642660Smarkm        }
32721495Sjmacd    }
32821495Sjmacd  return (char *)NULL;;
32921495Sjmacd}
33021495Sjmacd
33121495Sjmacd/* Add a filename and its expansion to our list. */
33221495Sjmacdstatic void
333146515Sruremember_info_filename (char *filename, char *expansion)
33421495Sjmacd{
33521495Sjmacd  FILENAME_LIST *new;
33621495Sjmacd
33721495Sjmacd  if (names_and_files_index + 2 > names_and_files_slots)
33821495Sjmacd    {
33921495Sjmacd      int alloc_size;
34021495Sjmacd      names_and_files_slots += 10;
34121495Sjmacd
34221495Sjmacd      alloc_size = names_and_files_slots * sizeof (FILENAME_LIST *);
34321495Sjmacd
34421495Sjmacd      names_and_files =
34542660Smarkm        (FILENAME_LIST **) xrealloc (names_and_files, alloc_size);
34621495Sjmacd    }
34721495Sjmacd
34821495Sjmacd  new = (FILENAME_LIST *)xmalloc (sizeof (FILENAME_LIST));
34942660Smarkm  new->filename = xstrdup (filename);
35042660Smarkm  new->expansion = expansion ? xstrdup (expansion) : (char *)NULL;
35121495Sjmacd
35221495Sjmacd  names_and_files[names_and_files_index++] = new;
35321495Sjmacd  names_and_files[names_and_files_index] = (FILENAME_LIST *)NULL;
35421495Sjmacd}
35521495Sjmacd
35621495Sjmacdstatic void
357146515Srumaybe_initialize_infopath (void)
35821495Sjmacd{
35921495Sjmacd  if (!infopath_size)
36021495Sjmacd    {
36121495Sjmacd      infopath = (char *)
36242660Smarkm        xmalloc (infopath_size = (1 + strlen (DEFAULT_INFOPATH)));
36321495Sjmacd
36421495Sjmacd      strcpy (infopath, DEFAULT_INFOPATH);
36521495Sjmacd    }
36621495Sjmacd}
36721495Sjmacd
36821495Sjmacd/* Add PATH to the list of paths found in INFOPATH.  2nd argument says
36921495Sjmacd   whether to put PATH at the front or end of INFOPATH. */
37021495Sjmacdvoid
371146515Sruinfo_add_path (char *path, int where)
37221495Sjmacd{
37321495Sjmacd  int len;
37421495Sjmacd
37521495Sjmacd  if (!infopath)
37621495Sjmacd    {
37721495Sjmacd      infopath = (char *)xmalloc (infopath_size = 200 + strlen (path));
37821495Sjmacd      infopath[0] = '\0';
37921495Sjmacd    }
38021495Sjmacd
38121495Sjmacd  len = strlen (path) + strlen (infopath);
38221495Sjmacd
38321495Sjmacd  if (len + 2 >= infopath_size)
38421495Sjmacd    infopath = (char *)xrealloc (infopath, (infopath_size += (2 * len) + 2));
38521495Sjmacd
38621495Sjmacd  if (!*infopath)
38721495Sjmacd    strcpy (infopath, path);
38821495Sjmacd  else if (where == INFOPATH_APPEND)
38921495Sjmacd    {
39056160Sru      strcat (infopath, PATH_SEP);
39121495Sjmacd      strcat (infopath, path);
39221495Sjmacd    }
39321495Sjmacd  else if (where == INFOPATH_PREPEND)
39421495Sjmacd    {
39542660Smarkm      char *temp = xstrdup (infopath);
39621495Sjmacd      strcpy (infopath, path);
39756160Sru      strcat (infopath, PATH_SEP);
39821495Sjmacd      strcat (infopath, temp);
39921495Sjmacd      free (temp);
40021495Sjmacd    }
40121495Sjmacd}
40221495Sjmacd
40321495Sjmacd/* Make INFOPATH have absolutely nothing in it. */
40421495Sjmacdvoid
405146515Sruzap_infopath (void)
40621495Sjmacd{
40721495Sjmacd  if (infopath)
40821495Sjmacd    free (infopath);
40921495Sjmacd
41021495Sjmacd  infopath = (char *)NULL;
41121495Sjmacd  infopath_size = 0;
41221495Sjmacd}
41321495Sjmacd
41456160Sru/* Given a chunk of text and its length, convert all CRLF pairs at every
41556160Sru   end-of-line into a single Newline character.  Return the length of
41656160Sru   produced text.
41756160Sru
41856160Sru   This is required because the rest of code is too entrenched in having
41956160Sru   a single newline at each EOL; in particular, searching for various
42056160Sru   Info headers and cookies can become extremely tricky if that assumption
42156160Sru   breaks.
42256160Sru
42356160Sru   FIXME: this could also support Mac-style text files with a single CR
42456160Sru   at the EOL, but what about random CR characters in non-Mac files?  Can
42556160Sru   we afford converting them into newlines as well?  Maybe implement some
42656160Sru   heuristics here, like in Emacs 20.
42756160Sru
42856160Sru   FIXME: is it a good idea to show the EOL type on the modeline?  */
42956160Srulong
430146515Sruconvert_eols (char *text, long int textlen)
43156160Sru{
43256160Sru  register char *s = text;
43356160Sru  register char *d = text;
43456160Sru
43556160Sru  while (textlen--)
43656160Sru    {
43756160Sru      if (*s == '\r' && textlen && s[1] == '\n')
43856160Sru	{
43956160Sru	  s++;
44056160Sru	  textlen--;
44156160Sru	}
44256160Sru      *d++ = *s++;
44356160Sru    }
44456160Sru
44556160Sru  return (long)(d - text);
44656160Sru}
44756160Sru
44821495Sjmacd/* Read the contents of PATHNAME, returning a buffer with the contents of
44921495Sjmacd   that file in it, and returning the size of that buffer in FILESIZE.
45021495Sjmacd   FINFO is a stat struct which has already been filled in by the caller.
45156160Sru   If the file turns out to be compressed, set IS_COMPRESSED to non-zero.
45221495Sjmacd   If the file cannot be read, return a NULL pointer. */
45321495Sjmacdchar *
454146515Srufilesys_read_info_file (char *pathname, long int *filesize,
455146515Sru    struct stat *finfo, int *is_compressed)
45621495Sjmacd{
45721495Sjmacd  long st_size;
45821495Sjmacd
45921495Sjmacd  *filesize = filesys_error_number = 0;
46021495Sjmacd
46121495Sjmacd  if (compressed_filename_p (pathname))
46256160Sru    {
46356160Sru      *is_compressed = 1;
464146515Sru      return (filesys_read_compressed (pathname, filesize));
46556160Sru    }
46621495Sjmacd  else
46721495Sjmacd    {
46821495Sjmacd      int descriptor;
46921495Sjmacd      char *contents;
47021495Sjmacd
47156160Sru      *is_compressed = 0;
47256160Sru      descriptor = open (pathname, O_RDONLY | O_BINARY, 0666);
47321495Sjmacd
47421495Sjmacd      /* If the file couldn't be opened, give up. */
47521495Sjmacd      if (descriptor < 0)
47642660Smarkm        {
47742660Smarkm          filesys_error_number = errno;
47842660Smarkm          return ((char *)NULL);
47942660Smarkm        }
48021495Sjmacd
48121495Sjmacd      /* Try to read the contents of this file. */
48221495Sjmacd      st_size = (long) finfo->st_size;
48321495Sjmacd      contents = (char *)xmalloc (1 + st_size);
48421495Sjmacd      if ((read (descriptor, contents, st_size)) != st_size)
48542660Smarkm        {
48656160Sru	  filesys_error_number = errno;
48756160Sru	  close (descriptor);
48856160Sru	  free (contents);
48956160Sru	  return ((char *)NULL);
49042660Smarkm        }
49121495Sjmacd
49221495Sjmacd      close (descriptor);
49321495Sjmacd
49456160Sru      /* Convert any DOS-style CRLF EOLs into Unix-style NL.
49556160Sru	 Seems like a good idea to have even on Unix, in case the Info
49656160Sru	 files are coming from some Windows system across a network.  */
49756160Sru      *filesize = convert_eols (contents, st_size);
49856160Sru
49956160Sru      /* EOL conversion can shrink the text quite a bit.  We don't
50056160Sru	 want to waste storage.  */
50156160Sru      if (*filesize < st_size)
50256160Sru	contents = (char *)xrealloc (contents, 1 + *filesize);
503100513Sru      contents[*filesize] = '\0';
50456160Sru
50521495Sjmacd      return (contents);
50621495Sjmacd    }
50721495Sjmacd}
50821495Sjmacd
50921495Sjmacd/* Typically, pipe buffers are 4k. */
51021495Sjmacd#define BASIC_PIPE_BUFFER (4 * 1024)
51121495Sjmacd
51221495Sjmacd/* We use some large multiple of that. */
51321495Sjmacd#define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER)
51421495Sjmacd
51521495Sjmacdchar *
516146515Srufilesys_read_compressed (char *pathname, long int *filesize)
51721495Sjmacd{
51821495Sjmacd  FILE *stream;
51921495Sjmacd  char *command, *decompressor;
52021495Sjmacd  char *contents = (char *)NULL;
52121495Sjmacd
52221495Sjmacd  *filesize = filesys_error_number = 0;
52321495Sjmacd
52421495Sjmacd  decompressor = filesys_decompressor_for_file (pathname);
52521495Sjmacd
52621495Sjmacd  if (!decompressor)
52721495Sjmacd    return ((char *)NULL);
52821495Sjmacd
52956160Sru  command = (char *)xmalloc (15 + strlen (pathname) + strlen (decompressor));
53056160Sru  /* Explicit .exe suffix makes the diagnostics of `popen'
53156160Sru     better on systems where COMMAND.COM is the stock shell.  */
53256160Sru  sprintf (command, "%s%s < %s",
53356160Sru	   decompressor, STRIP_DOT_EXE ? ".exe" : "", pathname);
53421495Sjmacd
53521495Sjmacd#if !defined (BUILDING_LIBRARY)
53621495Sjmacd  if (info_windows_initialized_p)
53721495Sjmacd    {
53821495Sjmacd      char *temp;
53921495Sjmacd
54021495Sjmacd      temp = (char *)xmalloc (5 + strlen (command));
54121495Sjmacd      sprintf (temp, "%s...", command);
542146515Sru      message_in_echo_area ("%s", temp, NULL);
54321495Sjmacd      free (temp);
54421495Sjmacd    }
54521495Sjmacd#endif /* !BUILDING_LIBRARY */
54621495Sjmacd
54756160Sru  stream = popen (command, FOPEN_RBIN);
54821495Sjmacd  free (command);
54921495Sjmacd
55021495Sjmacd  /* Read chunks from this file until there are none left to read. */
55121495Sjmacd  if (stream)
55221495Sjmacd    {
55393139Sru      long offset, size;
55421495Sjmacd      char *chunk;
55521495Sjmacd
55621495Sjmacd      offset = size = 0;
55721495Sjmacd      chunk = (char *)xmalloc (FILESYS_PIPE_BUFFER_SIZE);
55821495Sjmacd
55921495Sjmacd      while (1)
56042660Smarkm        {
56142660Smarkm          int bytes_read;
56221495Sjmacd
56342660Smarkm          bytes_read = fread (chunk, 1, FILESYS_PIPE_BUFFER_SIZE, stream);
56421495Sjmacd
56542660Smarkm          if (bytes_read + offset >= size)
56642660Smarkm            contents = (char *)xrealloc
56742660Smarkm              (contents, size += (2 * FILESYS_PIPE_BUFFER_SIZE));
56821495Sjmacd
56942660Smarkm          memcpy (contents + offset, chunk, bytes_read);
57042660Smarkm          offset += bytes_read;
57142660Smarkm          if (bytes_read != FILESYS_PIPE_BUFFER_SIZE)
57242660Smarkm            break;
57342660Smarkm        }
57421495Sjmacd
57521495Sjmacd      free (chunk);
57656160Sru      if (pclose (stream) == -1)
57756160Sru	{
57856160Sru	  if (contents)
57956160Sru	    free (contents);
58056160Sru	  contents = (char *)NULL;
58156160Sru	  filesys_error_number = errno;
58256160Sru	}
58356160Sru      else
58456160Sru	{
58556160Sru	  *filesize = convert_eols (contents, offset);
58656160Sru	  contents = (char *)xrealloc (contents, 1 + *filesize);
587100513Sru	  contents[*filesize] = '\0';
58856160Sru	}
58921495Sjmacd    }
59021495Sjmacd  else
59121495Sjmacd    {
59221495Sjmacd      filesys_error_number = errno;
59321495Sjmacd    }
59421495Sjmacd
59521495Sjmacd#if !defined (BUILDING_LIBARARY)
59621495Sjmacd  if (info_windows_initialized_p)
59721495Sjmacd    unmessage_in_echo_area ();
59821495Sjmacd#endif /* !BUILDING_LIBRARY */
59921495Sjmacd  return (contents);
60021495Sjmacd}
60121495Sjmacd
60221495Sjmacd/* Return non-zero if FILENAME belongs to a compressed file. */
60321495Sjmacdint
604146515Srucompressed_filename_p (char *filename)
60521495Sjmacd{
60621495Sjmacd  char *decompressor;
60721495Sjmacd
60821495Sjmacd  /* Find the final extension of this filename, and see if it matches one
60921495Sjmacd     of our known ones. */
61021495Sjmacd  decompressor = filesys_decompressor_for_file (filename);
61121495Sjmacd
61221495Sjmacd  if (decompressor)
61321495Sjmacd    return (1);
61421495Sjmacd  else
61521495Sjmacd    return (0);
61621495Sjmacd}
61721495Sjmacd
61821495Sjmacd/* Return the command string that would be used to decompress FILENAME. */
61921495Sjmacdchar *
620146515Srufilesys_decompressor_for_file (char *filename)
62121495Sjmacd{
62221495Sjmacd  register int i;
62321495Sjmacd  char *extension = (char *)NULL;
62421495Sjmacd
62521495Sjmacd  /* Find the final extension of FILENAME, and see if it appears in our
62621495Sjmacd     list of known compression extensions. */
62721495Sjmacd  for (i = strlen (filename) - 1; i > 0; i--)
62821495Sjmacd    if (filename[i] == '.')
62921495Sjmacd      {
63042660Smarkm        extension = filename + i;
63142660Smarkm        break;
63221495Sjmacd      }
63321495Sjmacd
63421495Sjmacd  if (!extension)
63521495Sjmacd    return ((char *)NULL);
63621495Sjmacd
63721495Sjmacd  for (i = 0; compress_suffixes[i].suffix; i++)
63856160Sru    if (FILENAME_CMP (extension, compress_suffixes[i].suffix) == 0)
63921495Sjmacd      return (compress_suffixes[i].decompressor);
64021495Sjmacd
64156160Sru#if defined (__MSDOS__)
64256160Sru  /* If no other suffix matched, allow any extension which ends
64356160Sru     with `z' to be decompressed by gunzip.  Due to limited 8+3 DOS
64456160Sru     file namespace, we can expect many such cases, and supporting
64556160Sru     every weird suffix thus produced would be a pain.  */
64656160Sru  if (extension[strlen (extension) - 1] == 'z' ||
64756160Sru      extension[strlen (extension) - 1] == 'Z')
64856160Sru    return "gunzip";
64956160Sru#endif
65056160Sru
65121495Sjmacd  return ((char *)NULL);
65221495Sjmacd}
65321495Sjmacd
65421495Sjmacd/* The number of the most recent file system error. */
65521495Sjmacdint filesys_error_number = 0;
65621495Sjmacd
65721495Sjmacd/* A function which returns a pointer to a static buffer containing
65821495Sjmacd   an error message for FILENAME and ERROR_NUM. */
65921495Sjmacdstatic char *errmsg_buf = (char *)NULL;
66021495Sjmacdstatic int errmsg_buf_size = 0;
66121495Sjmacd
66221495Sjmacdchar *
663146515Srufilesys_error_string (char *filename, int error_num)
66421495Sjmacd{
66521495Sjmacd  int len;
66621495Sjmacd  char *result;
66721495Sjmacd
66821495Sjmacd  if (error_num == 0)
66921495Sjmacd    return ((char *)NULL);
67021495Sjmacd
67121495Sjmacd  result = strerror (error_num);
67221495Sjmacd
67321495Sjmacd  len = 4 + strlen (filename) + strlen (result);
67421495Sjmacd  if (len >= errmsg_buf_size)
67521495Sjmacd    errmsg_buf = (char *)xrealloc (errmsg_buf, (errmsg_buf_size = 2 + len));
67621495Sjmacd
67721495Sjmacd  sprintf (errmsg_buf, "%s: %s", filename, result);
67821495Sjmacd  return (errmsg_buf);
67921495Sjmacd}
68021495Sjmacd
68156160Sru
68293139Sru/* Check for "dir" with all the possible info and compression suffixes,
68393139Sru   in combination.  */
68456160Sru
68556160Sruint
686146515Sruis_dir_name (char *filename)
68756160Sru{
68856160Sru  unsigned i;
68993139Sru
69093139Sru  for (i = 0; info_suffixes[i]; i++)
69156160Sru    {
69293139Sru      unsigned c;
69393139Sru      char trydir[50];
69493139Sru      strcpy (trydir, "dir");
69593139Sru      strcat (trydir, info_suffixes[i]);
69693139Sru
69793139Sru      if (strcasecmp (filename, trydir) == 0)
69856160Sru        return 1;
69993139Sru
70093139Sru      for (c = 0; compress_suffixes[c].suffix; c++)
70193139Sru        {
70293139Sru          char dir_compressed[50]; /* can be short */
70393139Sru          strcpy (dir_compressed, trydir);
70493139Sru          strcat (dir_compressed, compress_suffixes[c].suffix);
70593139Sru          if (strcasecmp (filename, dir_compressed) == 0)
70693139Sru            return 1;
70793139Sru        }
70893139Sru    }
70993139Sru
71056160Sru  return 0;
71156160Sru}
712