Home | History | Annotate | Line # | Download | only in info
filesys.c revision 1.1.1.1
      1  1.1  christos /*	$NetBSD: filesys.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /* filesys.c -- filesystem specific functions.
      4  1.1  christos    Id: filesys.c,v 1.6 2004/07/30 17:17:40 karl Exp
      5  1.1  christos 
      6  1.1  christos    Copyright (C) 1993, 1997, 1998, 2000, 2002, 2003, 2004 Free Software
      7  1.1  christos    Foundation, Inc.
      8  1.1  christos 
      9  1.1  christos    This program is free software; you can redistribute it and/or modify
     10  1.1  christos    it under the terms of the GNU General Public License as published by
     11  1.1  christos    the Free Software Foundation; either version 2, or (at your option)
     12  1.1  christos    any later version.
     13  1.1  christos 
     14  1.1  christos    This program is distributed in the hope that it will be useful,
     15  1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  1.1  christos    GNU General Public License for more details.
     18  1.1  christos 
     19  1.1  christos    You should have received a copy of the GNU General Public License
     20  1.1  christos    along with this program; if not, write to the Free Software
     21  1.1  christos    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     22  1.1  christos 
     23  1.1  christos    Written by Brian Fox (bfox (at) ai.mit.edu). */
     24  1.1  christos 
     25  1.1  christos #include "info.h"
     26  1.1  christos 
     27  1.1  christos #include "tilde.h"
     28  1.1  christos #include "filesys.h"
     29  1.1  christos 
     30  1.1  christos /* Local to this file. */
     31  1.1  christos static char *info_file_in_path (char *filename, char *path);
     32  1.1  christos static char *lookup_info_filename (char *filename);
     33  1.1  christos static char *info_absolute_file (char *fname);
     34  1.1  christos 
     35  1.1  christos static void remember_info_filename (char *filename, char *expansion);
     36  1.1  christos static void maybe_initialize_infopath (void);
     37  1.1  christos 
     38  1.1  christos typedef struct
     39  1.1  christos {
     40  1.1  christos   char *suffix;
     41  1.1  christos   char *decompressor;
     42  1.1  christos } COMPRESSION_ALIST;
     43  1.1  christos 
     44  1.1  christos static char *info_suffixes[] = {
     45  1.1  christos   ".info",
     46  1.1  christos   "-info",
     47  1.1  christos   "/index",
     48  1.1  christos   ".inf",       /* 8+3 file on filesystem which supports long file names */
     49  1.1  christos #ifdef __MSDOS__
     50  1.1  christos   /* 8+3 file names strike again...  */
     51  1.1  christos   ".in",        /* for .inz, .igz etc. */
     52  1.1  christos   ".i",
     53  1.1  christos #endif
     54  1.1  christos   "",
     55  1.1  christos   NULL
     56  1.1  christos };
     57  1.1  christos 
     58  1.1  christos static COMPRESSION_ALIST compress_suffixes[] = {
     59  1.1  christos   { ".gz", "gunzip" },
     60  1.1  christos   { ".bz2", "bunzip2" },
     61  1.1  christos   { ".z", "gunzip" },
     62  1.1  christos   { ".Z", "uncompress" },
     63  1.1  christos   { ".Y", "unyabba" },
     64  1.1  christos #ifdef __MSDOS__
     65  1.1  christos   { "gz", "gunzip" },
     66  1.1  christos   { "z", "gunzip" },
     67  1.1  christos #endif
     68  1.1  christos   { (char *)NULL, (char *)NULL }
     69  1.1  christos };
     70  1.1  christos 
     71  1.1  christos /* The path on which we look for info files.  You can initialize this
     72  1.1  christos    from the environment variable INFOPATH if there is one, or you can
     73  1.1  christos    call info_add_path () to add paths to the beginning or end of it.
     74  1.1  christos    You can call zap_infopath () to make the path go away. */
     75  1.1  christos char *infopath = (char *)NULL;
     76  1.1  christos static int infopath_size = 0;
     77  1.1  christos 
     78  1.1  christos /* Expand the filename in PARTIAL to make a real name for this operating
     79  1.1  christos    system.  This looks in INFO_PATHS in order to find the correct file.
     80  1.1  christos    If it can't find the file, it returns NULL. */
     81  1.1  christos static char *local_temp_filename = (char *)NULL;
     82  1.1  christos static int local_temp_filename_size = 0;
     83  1.1  christos 
     84  1.1  christos char *
     85  1.1  christos info_find_fullpath (char *partial)
     86  1.1  christos {
     87  1.1  christos   int initial_character;
     88  1.1  christos   char *temp;
     89  1.1  christos 
     90  1.1  christos   filesys_error_number = 0;
     91  1.1  christos 
     92  1.1  christos   maybe_initialize_infopath ();
     93  1.1  christos 
     94  1.1  christos   if (partial && (initial_character = *partial))
     95  1.1  christos     {
     96  1.1  christos       char *expansion;
     97  1.1  christos 
     98  1.1  christos       expansion = lookup_info_filename (partial);
     99  1.1  christos 
    100  1.1  christos       if (expansion)
    101  1.1  christos         return (expansion);
    102  1.1  christos 
    103  1.1  christos       /* If we have the full path to this file, we still may have to add
    104  1.1  christos          various extensions to it.  I guess we have to stat this file
    105  1.1  christos          after all. */
    106  1.1  christos       if (IS_ABSOLUTE (partial))
    107  1.1  christos 	temp = info_absolute_file (partial);
    108  1.1  christos       else if (initial_character == '~')
    109  1.1  christos         {
    110  1.1  christos           expansion = tilde_expand_word (partial);
    111  1.1  christos           if (IS_ABSOLUTE (expansion))
    112  1.1  christos             {
    113  1.1  christos               temp = info_absolute_file (expansion);
    114  1.1  christos               free (expansion);
    115  1.1  christos             }
    116  1.1  christos           else
    117  1.1  christos             temp = expansion;
    118  1.1  christos         }
    119  1.1  christos       else if (initial_character == '.' &&
    120  1.1  christos                (IS_SLASH (partial[1]) ||
    121  1.1  christos 		(partial[1] == '.' && IS_SLASH (partial[2]))))
    122  1.1  christos         {
    123  1.1  christos           if (local_temp_filename_size < 1024)
    124  1.1  christos             local_temp_filename = (char *)xrealloc
    125  1.1  christos               (local_temp_filename, (local_temp_filename_size = 1024));
    126  1.1  christos #if defined (HAVE_GETCWD)
    127  1.1  christos           if (!getcwd (local_temp_filename, local_temp_filename_size))
    128  1.1  christos #else /*  !HAVE_GETCWD */
    129  1.1  christos           if (!getwd (local_temp_filename))
    130  1.1  christos #endif /* !HAVE_GETCWD */
    131  1.1  christos             {
    132  1.1  christos               filesys_error_number = errno;
    133  1.1  christos               return (partial);
    134  1.1  christos             }
    135  1.1  christos 
    136  1.1  christos           strcat (local_temp_filename, "/");
    137  1.1  christos           strcat (local_temp_filename, partial);
    138  1.1  christos 	  temp = info_absolute_file (local_temp_filename); /* try extensions */
    139  1.1  christos 	  if (!temp)
    140  1.1  christos 	    partial = local_temp_filename;
    141  1.1  christos         }
    142  1.1  christos       else
    143  1.1  christos         temp = info_file_in_path (partial, infopath);
    144  1.1  christos 
    145  1.1  christos       if (temp)
    146  1.1  christos         {
    147  1.1  christos           remember_info_filename (partial, temp);
    148  1.1  christos           if (strlen (temp) > (unsigned int) local_temp_filename_size)
    149  1.1  christos             local_temp_filename = (char *) xrealloc
    150  1.1  christos               (local_temp_filename,
    151  1.1  christos                (local_temp_filename_size = (50 + strlen (temp))));
    152  1.1  christos           strcpy (local_temp_filename, temp);
    153  1.1  christos           free (temp);
    154  1.1  christos           return (local_temp_filename);
    155  1.1  christos         }
    156  1.1  christos     }
    157  1.1  christos   return (partial);
    158  1.1  christos }
    159  1.1  christos 
    160  1.1  christos /* Scan the list of directories in PATH looking for FILENAME.  If we find
    161  1.1  christos    one that is a regular file, return it as a new string.  Otherwise, return
    162  1.1  christos    a NULL pointer. */
    163  1.1  christos static char *
    164  1.1  christos info_file_in_path (char *filename, char *path)
    165  1.1  christos {
    166  1.1  christos   struct stat finfo;
    167  1.1  christos   char *temp_dirname;
    168  1.1  christos   int statable, dirname_index;
    169  1.1  christos 
    170  1.1  christos   /* Reject ridiculous cases up front, to prevent infinite recursion
    171  1.1  christos      later on.  E.g., someone might say "info '(.)foo'"...  */
    172  1.1  christos   if (!*filename || STREQ (filename, ".") || STREQ (filename, ".."))
    173  1.1  christos     return NULL;
    174  1.1  christos 
    175  1.1  christos   dirname_index = 0;
    176  1.1  christos 
    177  1.1  christos   while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
    178  1.1  christos     {
    179  1.1  christos       register int i, pre_suffix_length;
    180  1.1  christos       char *temp;
    181  1.1  christos 
    182  1.1  christos       /* Expand a leading tilde if one is present. */
    183  1.1  christos       if (*temp_dirname == '~')
    184  1.1  christos         {
    185  1.1  christos           char *expanded_dirname;
    186  1.1  christos 
    187  1.1  christos           expanded_dirname = tilde_expand_word (temp_dirname);
    188  1.1  christos           free (temp_dirname);
    189  1.1  christos           temp_dirname = expanded_dirname;
    190  1.1  christos         }
    191  1.1  christos 
    192  1.1  christos       temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename));
    193  1.1  christos       strcpy (temp, temp_dirname);
    194  1.1  christos       if (!IS_SLASH (temp[(strlen (temp)) - 1]))
    195  1.1  christos         strcat (temp, "/");
    196  1.1  christos       strcat (temp, filename);
    197  1.1  christos 
    198  1.1  christos       pre_suffix_length = strlen (temp);
    199  1.1  christos 
    200  1.1  christos       free (temp_dirname);
    201  1.1  christos 
    202  1.1  christos       for (i = 0; info_suffixes[i]; i++)
    203  1.1  christos         {
    204  1.1  christos           strcpy (temp + pre_suffix_length, info_suffixes[i]);
    205  1.1  christos 
    206  1.1  christos           statable = (stat (temp, &finfo) == 0);
    207  1.1  christos 
    208  1.1  christos           /* If we have found a regular file, then use that.  Else, if we
    209  1.1  christos              have found a directory, look in that directory for this file. */
    210  1.1  christos           if (statable)
    211  1.1  christos             {
    212  1.1  christos               if (S_ISREG (finfo.st_mode))
    213  1.1  christos                 {
    214  1.1  christos                   return (temp);
    215  1.1  christos                 }
    216  1.1  christos               else if (S_ISDIR (finfo.st_mode))
    217  1.1  christos                 {
    218  1.1  christos                   char *newpath, *filename_only, *newtemp;
    219  1.1  christos 
    220  1.1  christos                   newpath = xstrdup (temp);
    221  1.1  christos                   filename_only = filename_non_directory (filename);
    222  1.1  christos                   newtemp = info_file_in_path (filename_only, newpath);
    223  1.1  christos 
    224  1.1  christos                   free (newpath);
    225  1.1  christos                   if (newtemp)
    226  1.1  christos                     {
    227  1.1  christos                       free (temp);
    228  1.1  christos                       return (newtemp);
    229  1.1  christos                     }
    230  1.1  christos                 }
    231  1.1  christos             }
    232  1.1  christos           else
    233  1.1  christos             {
    234  1.1  christos               /* Add various compression suffixes to the name to see if
    235  1.1  christos                  the file is present in compressed format. */
    236  1.1  christos               register int j, pre_compress_suffix_length;
    237  1.1  christos 
    238  1.1  christos               pre_compress_suffix_length = strlen (temp);
    239  1.1  christos 
    240  1.1  christos               for (j = 0; compress_suffixes[j].suffix; j++)
    241  1.1  christos                 {
    242  1.1  christos                   strcpy (temp + pre_compress_suffix_length,
    243  1.1  christos                           compress_suffixes[j].suffix);
    244  1.1  christos 
    245  1.1  christos                   statable = (stat (temp, &finfo) == 0);
    246  1.1  christos                   if (statable && (S_ISREG (finfo.st_mode)))
    247  1.1  christos                     return (temp);
    248  1.1  christos                 }
    249  1.1  christos             }
    250  1.1  christos         }
    251  1.1  christos       free (temp);
    252  1.1  christos     }
    253  1.1  christos   return ((char *)NULL);
    254  1.1  christos }
    255  1.1  christos 
    256  1.1  christos /* Assume FNAME is an absolute file name, and check whether it is
    257  1.1  christos    a regular file.  If it is, return it as a new string; otherwise
    258  1.1  christos    return a NULL pointer.  We do it by taking the file name apart
    259  1.1  christos    into its directory and basename parts, and calling info_file_in_path.*/
    260  1.1  christos static char *
    261  1.1  christos info_absolute_file (char *fname)
    262  1.1  christos {
    263  1.1  christos   char *containing_dir = xstrdup (fname);
    264  1.1  christos   char *base = filename_non_directory (containing_dir);
    265  1.1  christos 
    266  1.1  christos   if (base > containing_dir)
    267  1.1  christos     base[-1] = '\0';
    268  1.1  christos 
    269  1.1  christos   return info_file_in_path (filename_non_directory (fname), containing_dir);
    270  1.1  christos }
    271  1.1  christos 
    272  1.1  christos 
    273  1.1  christos /* Given a string containing units of information separated by the
    274  1.1  christos    PATH_SEP character, return the next one after IDX, or NULL if there
    275  1.1  christos    are no more.  Advance IDX to the character after the colon. */
    276  1.1  christos 
    277  1.1  christos char *
    278  1.1  christos extract_colon_unit (char *string, int *idx)
    279  1.1  christos {
    280  1.1  christos   unsigned int i = (unsigned int) *idx;
    281  1.1  christos   unsigned int start = i;
    282  1.1  christos 
    283  1.1  christos   if (!string || i >= strlen (string))
    284  1.1  christos     return NULL;
    285  1.1  christos 
    286  1.1  christos   if (!string[i]) /* end of string */
    287  1.1  christos     return NULL;
    288  1.1  christos 
    289  1.1  christos   /* Advance to next PATH_SEP.  */
    290  1.1  christos   while (string[i] && string[i] != PATH_SEP[0])
    291  1.1  christos     i++;
    292  1.1  christos 
    293  1.1  christos   {
    294  1.1  christos     char *value = xmalloc ((i - start) + 1);
    295  1.1  christos     strncpy (value, &string[start], (i - start));
    296  1.1  christos     value[i - start] = 0;
    297  1.1  christos 
    298  1.1  christos     i++; /* move past PATH_SEP */
    299  1.1  christos     *idx = i;
    300  1.1  christos     return value;
    301  1.1  christos   }
    302  1.1  christos }
    303  1.1  christos 
    304  1.1  christos /* A structure which associates a filename with its expansion. */
    305  1.1  christos typedef struct
    306  1.1  christos {
    307  1.1  christos   char *filename;
    308  1.1  christos   char *expansion;
    309  1.1  christos } FILENAME_LIST;
    310  1.1  christos 
    311  1.1  christos /* An array of remembered arguments and results. */
    312  1.1  christos static FILENAME_LIST **names_and_files = (FILENAME_LIST **)NULL;
    313  1.1  christos static int names_and_files_index = 0;
    314  1.1  christos static int names_and_files_slots = 0;
    315  1.1  christos 
    316  1.1  christos /* Find the result for having already called info_find_fullpath () with
    317  1.1  christos    FILENAME. */
    318  1.1  christos static char *
    319  1.1  christos lookup_info_filename (char *filename)
    320  1.1  christos {
    321  1.1  christos   if (filename && names_and_files)
    322  1.1  christos     {
    323  1.1  christos       register int i;
    324  1.1  christos       for (i = 0; names_and_files[i]; i++)
    325  1.1  christos         {
    326  1.1  christos           if (FILENAME_CMP (names_and_files[i]->filename, filename) == 0)
    327  1.1  christos             return (names_and_files[i]->expansion);
    328  1.1  christos         }
    329  1.1  christos     }
    330  1.1  christos   return (char *)NULL;;
    331  1.1  christos }
    332  1.1  christos 
    333  1.1  christos /* Add a filename and its expansion to our list. */
    334  1.1  christos static void
    335  1.1  christos remember_info_filename (char *filename, char *expansion)
    336  1.1  christos {
    337  1.1  christos   FILENAME_LIST *new;
    338  1.1  christos 
    339  1.1  christos   if (names_and_files_index + 2 > names_and_files_slots)
    340  1.1  christos     {
    341  1.1  christos       int alloc_size;
    342  1.1  christos       names_and_files_slots += 10;
    343  1.1  christos 
    344  1.1  christos       alloc_size = names_and_files_slots * sizeof (FILENAME_LIST *);
    345  1.1  christos 
    346  1.1  christos       names_and_files =
    347  1.1  christos         (FILENAME_LIST **) xrealloc (names_and_files, alloc_size);
    348  1.1  christos     }
    349  1.1  christos 
    350  1.1  christos   new = (FILENAME_LIST *)xmalloc (sizeof (FILENAME_LIST));
    351  1.1  christos   new->filename = xstrdup (filename);
    352  1.1  christos   new->expansion = expansion ? xstrdup (expansion) : (char *)NULL;
    353  1.1  christos 
    354  1.1  christos   names_and_files[names_and_files_index++] = new;
    355  1.1  christos   names_and_files[names_and_files_index] = (FILENAME_LIST *)NULL;
    356  1.1  christos }
    357  1.1  christos 
    358  1.1  christos static void
    359  1.1  christos maybe_initialize_infopath (void)
    360  1.1  christos {
    361  1.1  christos   if (!infopath_size)
    362  1.1  christos     {
    363  1.1  christos       infopath = (char *)
    364  1.1  christos         xmalloc (infopath_size = (1 + strlen (DEFAULT_INFOPATH)));
    365  1.1  christos 
    366  1.1  christos       strcpy (infopath, DEFAULT_INFOPATH);
    367  1.1  christos     }
    368  1.1  christos }
    369  1.1  christos 
    370  1.1  christos /* Add PATH to the list of paths found in INFOPATH.  2nd argument says
    371  1.1  christos    whether to put PATH at the front or end of INFOPATH. */
    372  1.1  christos void
    373  1.1  christos info_add_path (char *path, int where)
    374  1.1  christos {
    375  1.1  christos   int len;
    376  1.1  christos 
    377  1.1  christos   if (!infopath)
    378  1.1  christos     {
    379  1.1  christos       infopath = (char *)xmalloc (infopath_size = 200 + strlen (path));
    380  1.1  christos       infopath[0] = '\0';
    381  1.1  christos     }
    382  1.1  christos 
    383  1.1  christos   len = strlen (path) + strlen (infopath);
    384  1.1  christos 
    385  1.1  christos   if (len + 2 >= infopath_size)
    386  1.1  christos     infopath = (char *)xrealloc (infopath, (infopath_size += (2 * len) + 2));
    387  1.1  christos 
    388  1.1  christos   if (!*infopath)
    389  1.1  christos     strcpy (infopath, path);
    390  1.1  christos   else if (where == INFOPATH_APPEND)
    391  1.1  christos     {
    392  1.1  christos       strcat (infopath, PATH_SEP);
    393  1.1  christos       strcat (infopath, path);
    394  1.1  christos     }
    395  1.1  christos   else if (where == INFOPATH_PREPEND)
    396  1.1  christos     {
    397  1.1  christos       char *temp = xstrdup (infopath);
    398  1.1  christos       strcpy (infopath, path);
    399  1.1  christos       strcat (infopath, PATH_SEP);
    400  1.1  christos       strcat (infopath, temp);
    401  1.1  christos       free (temp);
    402  1.1  christos     }
    403  1.1  christos }
    404  1.1  christos 
    405  1.1  christos /* Make INFOPATH have absolutely nothing in it. */
    406  1.1  christos void
    407  1.1  christos zap_infopath (void)
    408  1.1  christos {
    409  1.1  christos   if (infopath)
    410  1.1  christos     free (infopath);
    411  1.1  christos 
    412  1.1  christos   infopath = (char *)NULL;
    413  1.1  christos   infopath_size = 0;
    414  1.1  christos }
    415  1.1  christos 
    416  1.1  christos /* Given a chunk of text and its length, convert all CRLF pairs at every
    417  1.1  christos    end-of-line into a single Newline character.  Return the length of
    418  1.1  christos    produced text.
    419  1.1  christos 
    420  1.1  christos    This is required because the rest of code is too entrenched in having
    421  1.1  christos    a single newline at each EOL; in particular, searching for various
    422  1.1  christos    Info headers and cookies can become extremely tricky if that assumption
    423  1.1  christos    breaks.
    424  1.1  christos 
    425  1.1  christos    FIXME: this could also support Mac-style text files with a single CR
    426  1.1  christos    at the EOL, but what about random CR characters in non-Mac files?  Can
    427  1.1  christos    we afford converting them into newlines as well?  Maybe implement some
    428  1.1  christos    heuristics here, like in Emacs 20.
    429  1.1  christos 
    430  1.1  christos    FIXME: is it a good idea to show the EOL type on the modeline?  */
    431  1.1  christos long
    432  1.1  christos convert_eols (char *text, long int textlen)
    433  1.1  christos {
    434  1.1  christos   register char *s = text;
    435  1.1  christos   register char *d = text;
    436  1.1  christos 
    437  1.1  christos   while (textlen--)
    438  1.1  christos     {
    439  1.1  christos       if (*s == '\r' && textlen && s[1] == '\n')
    440  1.1  christos 	{
    441  1.1  christos 	  s++;
    442  1.1  christos 	  textlen--;
    443  1.1  christos 	}
    444  1.1  christos       *d++ = *s++;
    445  1.1  christos     }
    446  1.1  christos 
    447  1.1  christos   return (long)(d - text);
    448  1.1  christos }
    449  1.1  christos 
    450  1.1  christos /* Read the contents of PATHNAME, returning a buffer with the contents of
    451  1.1  christos    that file in it, and returning the size of that buffer in FILESIZE.
    452  1.1  christos    FINFO is a stat struct which has already been filled in by the caller.
    453  1.1  christos    If the file turns out to be compressed, set IS_COMPRESSED to non-zero.
    454  1.1  christos    If the file cannot be read, return a NULL pointer. */
    455  1.1  christos char *
    456  1.1  christos filesys_read_info_file (char *pathname, long int *filesize,
    457  1.1  christos     struct stat *finfo, int *is_compressed)
    458  1.1  christos {
    459  1.1  christos   long st_size;
    460  1.1  christos 
    461  1.1  christos   *filesize = filesys_error_number = 0;
    462  1.1  christos 
    463  1.1  christos   if (compressed_filename_p (pathname))
    464  1.1  christos     {
    465  1.1  christos       *is_compressed = 1;
    466  1.1  christos       return (filesys_read_compressed (pathname, filesize));
    467  1.1  christos     }
    468  1.1  christos   else
    469  1.1  christos     {
    470  1.1  christos       int descriptor;
    471  1.1  christos       char *contents;
    472  1.1  christos 
    473  1.1  christos       *is_compressed = 0;
    474  1.1  christos       descriptor = open (pathname, O_RDONLY | O_BINARY, 0666);
    475  1.1  christos 
    476  1.1  christos       /* If the file couldn't be opened, give up. */
    477  1.1  christos       if (descriptor < 0)
    478  1.1  christos         {
    479  1.1  christos           filesys_error_number = errno;
    480  1.1  christos           return ((char *)NULL);
    481  1.1  christos         }
    482  1.1  christos 
    483  1.1  christos       /* Try to read the contents of this file. */
    484  1.1  christos       st_size = (long) finfo->st_size;
    485  1.1  christos       contents = (char *)xmalloc (1 + st_size);
    486  1.1  christos       if ((read (descriptor, contents, st_size)) != st_size)
    487  1.1  christos         {
    488  1.1  christos 	  filesys_error_number = errno;
    489  1.1  christos 	  close (descriptor);
    490  1.1  christos 	  free (contents);
    491  1.1  christos 	  return ((char *)NULL);
    492  1.1  christos         }
    493  1.1  christos 
    494  1.1  christos       close (descriptor);
    495  1.1  christos 
    496  1.1  christos       /* Convert any DOS-style CRLF EOLs into Unix-style NL.
    497  1.1  christos 	 Seems like a good idea to have even on Unix, in case the Info
    498  1.1  christos 	 files are coming from some Windows system across a network.  */
    499  1.1  christos       *filesize = convert_eols (contents, st_size);
    500  1.1  christos 
    501  1.1  christos       /* EOL conversion can shrink the text quite a bit.  We don't
    502  1.1  christos 	 want to waste storage.  */
    503  1.1  christos       if (*filesize < st_size)
    504  1.1  christos 	contents = (char *)xrealloc (contents, 1 + *filesize);
    505  1.1  christos       contents[*filesize] = '\0';
    506  1.1  christos 
    507  1.1  christos       return (contents);
    508  1.1  christos     }
    509  1.1  christos }
    510  1.1  christos 
    511  1.1  christos /* Typically, pipe buffers are 4k. */
    512  1.1  christos #define BASIC_PIPE_BUFFER (4 * 1024)
    513  1.1  christos 
    514  1.1  christos /* We use some large multiple of that. */
    515  1.1  christos #define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER)
    516  1.1  christos 
    517  1.1  christos char *
    518  1.1  christos filesys_read_compressed (char *pathname, long int *filesize)
    519  1.1  christos {
    520  1.1  christos   FILE *stream;
    521  1.1  christos   char *command, *decompressor;
    522  1.1  christos   char *contents = (char *)NULL;
    523  1.1  christos 
    524  1.1  christos   *filesize = filesys_error_number = 0;
    525  1.1  christos 
    526  1.1  christos   decompressor = filesys_decompressor_for_file (pathname);
    527  1.1  christos 
    528  1.1  christos   if (!decompressor)
    529  1.1  christos     return ((char *)NULL);
    530  1.1  christos 
    531  1.1  christos   command = (char *)xmalloc (15 + strlen (pathname) + strlen (decompressor));
    532  1.1  christos   /* Explicit .exe suffix makes the diagnostics of `popen'
    533  1.1  christos      better on systems where COMMAND.COM is the stock shell.  */
    534  1.1  christos   sprintf (command, "%s%s < %s",
    535  1.1  christos 	   decompressor, STRIP_DOT_EXE ? ".exe" : "", pathname);
    536  1.1  christos 
    537  1.1  christos #if !defined (BUILDING_LIBRARY)
    538  1.1  christos   if (info_windows_initialized_p)
    539  1.1  christos     {
    540  1.1  christos       char *temp;
    541  1.1  christos 
    542  1.1  christos       temp = (char *)xmalloc (5 + strlen (command));
    543  1.1  christos       sprintf (temp, "%s...", command);
    544  1.1  christos       message_in_echo_area ("%s", temp, NULL);
    545  1.1  christos       free (temp);
    546  1.1  christos     }
    547  1.1  christos #endif /* !BUILDING_LIBRARY */
    548  1.1  christos 
    549  1.1  christos   stream = popen (command, FOPEN_RBIN);
    550  1.1  christos   free (command);
    551  1.1  christos 
    552  1.1  christos   /* Read chunks from this file until there are none left to read. */
    553  1.1  christos   if (stream)
    554  1.1  christos     {
    555  1.1  christos       long offset, size;
    556  1.1  christos       char *chunk;
    557  1.1  christos 
    558  1.1  christos       offset = size = 0;
    559  1.1  christos       chunk = (char *)xmalloc (FILESYS_PIPE_BUFFER_SIZE);
    560  1.1  christos 
    561  1.1  christos       while (1)
    562  1.1  christos         {
    563  1.1  christos           int bytes_read;
    564  1.1  christos 
    565  1.1  christos           bytes_read = fread (chunk, 1, FILESYS_PIPE_BUFFER_SIZE, stream);
    566  1.1  christos 
    567  1.1  christos           if (bytes_read + offset >= size)
    568  1.1  christos             contents = (char *)xrealloc
    569  1.1  christos               (contents, size += (2 * FILESYS_PIPE_BUFFER_SIZE));
    570  1.1  christos 
    571  1.1  christos           memcpy (contents + offset, chunk, bytes_read);
    572  1.1  christos           offset += bytes_read;
    573  1.1  christos           if (bytes_read != FILESYS_PIPE_BUFFER_SIZE)
    574  1.1  christos             break;
    575  1.1  christos         }
    576  1.1  christos 
    577  1.1  christos       free (chunk);
    578  1.1  christos       if (pclose (stream) == -1)
    579  1.1  christos 	{
    580  1.1  christos 	  if (contents)
    581  1.1  christos 	    free (contents);
    582  1.1  christos 	  contents = (char *)NULL;
    583  1.1  christos 	  filesys_error_number = errno;
    584  1.1  christos 	}
    585  1.1  christos       else
    586  1.1  christos 	{
    587  1.1  christos 	  *filesize = convert_eols (contents, offset);
    588  1.1  christos 	  contents = (char *)xrealloc (contents, 1 + *filesize);
    589  1.1  christos 	  contents[*filesize] = '\0';
    590  1.1  christos 	}
    591  1.1  christos     }
    592  1.1  christos   else
    593  1.1  christos     {
    594  1.1  christos       filesys_error_number = errno;
    595  1.1  christos     }
    596  1.1  christos 
    597  1.1  christos #if !defined (BUILDING_LIBARARY)
    598  1.1  christos   if (info_windows_initialized_p)
    599  1.1  christos     unmessage_in_echo_area ();
    600  1.1  christos #endif /* !BUILDING_LIBRARY */
    601  1.1  christos   return (contents);
    602  1.1  christos }
    603  1.1  christos 
    604  1.1  christos /* Return non-zero if FILENAME belongs to a compressed file. */
    605  1.1  christos int
    606  1.1  christos compressed_filename_p (char *filename)
    607  1.1  christos {
    608  1.1  christos   char *decompressor;
    609  1.1  christos 
    610  1.1  christos   /* Find the final extension of this filename, and see if it matches one
    611  1.1  christos      of our known ones. */
    612  1.1  christos   decompressor = filesys_decompressor_for_file (filename);
    613  1.1  christos 
    614  1.1  christos   if (decompressor)
    615  1.1  christos     return (1);
    616  1.1  christos   else
    617  1.1  christos     return (0);
    618  1.1  christos }
    619  1.1  christos 
    620  1.1  christos /* Return the command string that would be used to decompress FILENAME. */
    621  1.1  christos char *
    622  1.1  christos filesys_decompressor_for_file (char *filename)
    623  1.1  christos {
    624  1.1  christos   register int i;
    625  1.1  christos   char *extension = (char *)NULL;
    626  1.1  christos 
    627  1.1  christos   /* Find the final extension of FILENAME, and see if it appears in our
    628  1.1  christos      list of known compression extensions. */
    629  1.1  christos   for (i = strlen (filename) - 1; i > 0; i--)
    630  1.1  christos     if (filename[i] == '.')
    631  1.1  christos       {
    632  1.1  christos         extension = filename + i;
    633  1.1  christos         break;
    634  1.1  christos       }
    635  1.1  christos 
    636  1.1  christos   if (!extension)
    637  1.1  christos     return ((char *)NULL);
    638  1.1  christos 
    639  1.1  christos   for (i = 0; compress_suffixes[i].suffix; i++)
    640  1.1  christos     if (FILENAME_CMP (extension, compress_suffixes[i].suffix) == 0)
    641  1.1  christos       return (compress_suffixes[i].decompressor);
    642  1.1  christos 
    643  1.1  christos #if defined (__MSDOS__)
    644  1.1  christos   /* If no other suffix matched, allow any extension which ends
    645  1.1  christos      with `z' to be decompressed by gunzip.  Due to limited 8+3 DOS
    646  1.1  christos      file namespace, we can expect many such cases, and supporting
    647  1.1  christos      every weird suffix thus produced would be a pain.  */
    648  1.1  christos   if (extension[strlen (extension) - 1] == 'z' ||
    649  1.1  christos       extension[strlen (extension) - 1] == 'Z')
    650  1.1  christos     return "gunzip";
    651  1.1  christos #endif
    652  1.1  christos 
    653  1.1  christos   return ((char *)NULL);
    654  1.1  christos }
    655  1.1  christos 
    656  1.1  christos /* The number of the most recent file system error. */
    657  1.1  christos int filesys_error_number = 0;
    658  1.1  christos 
    659  1.1  christos /* A function which returns a pointer to a static buffer containing
    660  1.1  christos    an error message for FILENAME and ERROR_NUM. */
    661  1.1  christos static char *errmsg_buf = (char *)NULL;
    662  1.1  christos static int errmsg_buf_size = 0;
    663  1.1  christos 
    664  1.1  christos char *
    665  1.1  christos filesys_error_string (char *filename, int error_num)
    666  1.1  christos {
    667  1.1  christos   int len;
    668  1.1  christos   char *result;
    669  1.1  christos 
    670  1.1  christos   if (error_num == 0)
    671  1.1  christos     return ((char *)NULL);
    672  1.1  christos 
    673  1.1  christos   result = strerror (error_num);
    674  1.1  christos 
    675  1.1  christos   len = 4 + strlen (filename) + strlen (result);
    676  1.1  christos   if (len >= errmsg_buf_size)
    677  1.1  christos     errmsg_buf = (char *)xrealloc (errmsg_buf, (errmsg_buf_size = 2 + len));
    678  1.1  christos 
    679  1.1  christos   sprintf (errmsg_buf, "%s: %s", filename, result);
    680  1.1  christos   return (errmsg_buf);
    681  1.1  christos }
    682  1.1  christos 
    683  1.1  christos 
    684  1.1  christos /* Check for "dir" with all the possible info and compression suffixes,
    686  1.1  christos    in combination.  */
    687  1.1  christos 
    688  1.1  christos int
    689  1.1  christos is_dir_name (char *filename)
    690  1.1  christos {
    691  1.1  christos   unsigned i;
    692  1.1  christos 
    693  1.1  christos   for (i = 0; info_suffixes[i]; i++)
    694  1.1  christos     {
    695  1.1  christos       unsigned c;
    696  1.1  christos       char trydir[50];
    697  1.1  christos       strcpy (trydir, "dir");
    698  1.1  christos       strcat (trydir, info_suffixes[i]);
    699  1.1  christos 
    700  1.1  christos       if (strcasecmp (filename, trydir) == 0)
    701  1.1  christos         return 1;
    702  1.1  christos 
    703  1.1  christos       for (c = 0; compress_suffixes[c].suffix; c++)
    704  1.1  christos         {
    705  1.1  christos           char dir_compressed[50]; /* can be short */
    706  1.1  christos           strcpy (dir_compressed, trydir);
    707  1.1  christos           strcat (dir_compressed, compress_suffixes[c].suffix);
    708  1.1  christos           if (strcasecmp (filename, dir_compressed) == 0)
    709  1.1  christos             return 1;
    710  1.1  christos         }
    711  1.1  christos     }
    712  1.1  christos 
    713  1.1  christos   return 0;
    714                }
    715