xref: /openbsd-src/gnu/usr.bin/texinfo/info/filesys.c (revision 1076333c323c9f213f0d653fc52002328f47dbe9)
1672dff93Sespie /* filesys.c -- filesystem specific functions.
2*1076333cSespie    $Id: filesys.c,v 1.5 2006/07/17 16:12:36 espie Exp $
3fbc94a17Sniklas 
4*1076333cSespie    Copyright (C) 1993, 1997, 1998, 2000, 2002, 2003, 2004 Free Software
5*1076333cSespie    Foundation, Inc.
6fbc94a17Sniklas 
7fbc94a17Sniklas    This program is free software; you can redistribute it and/or modify
8fbc94a17Sniklas    it under the terms of the GNU General Public License as published by
9fbc94a17Sniklas    the Free Software Foundation; either version 2, or (at your option)
10fbc94a17Sniklas    any later version.
11fbc94a17Sniklas 
12fbc94a17Sniklas    This program is distributed in the hope that it will be useful,
13fbc94a17Sniklas    but WITHOUT ANY WARRANTY; without even the implied warranty of
14fbc94a17Sniklas    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15fbc94a17Sniklas    GNU General Public License for more details.
16fbc94a17Sniklas 
17fbc94a17Sniklas    You should have received a copy of the GNU General Public License
18fbc94a17Sniklas    along with this program; if not, write to the Free Software
19fbc94a17Sniklas    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20fbc94a17Sniklas 
21fbc94a17Sniklas    Written by Brian Fox (bfox@ai.mit.edu). */
22fbc94a17Sniklas 
23840175f0Skstailey #include "info.h"
24840175f0Skstailey 
25fbc94a17Sniklas #include "tilde.h"
26fbc94a17Sniklas #include "filesys.h"
27fbc94a17Sniklas 
28fbc94a17Sniklas /* Local to this file. */
29*1076333cSespie static char *info_file_in_path (char *filename, char *path);
30*1076333cSespie static char *lookup_info_filename (char *filename);
31*1076333cSespie static char *info_absolute_file (char *fname);
32*1076333cSespie 
33*1076333cSespie static void remember_info_filename (char *filename, char *expansion);
34*1076333cSespie static void maybe_initialize_infopath (void);
35fbc94a17Sniklas 
36840175f0Skstailey typedef struct
37840175f0Skstailey {
38fbc94a17Sniklas   char *suffix;
39fbc94a17Sniklas   char *decompressor;
40fbc94a17Sniklas } COMPRESSION_ALIST;
41fbc94a17Sniklas 
42fbc94a17Sniklas static char *info_suffixes[] = {
43fbc94a17Sniklas   ".info",
44fbc94a17Sniklas   "-info",
4528ea187bSespie   "/index",
46672dff93Sespie   ".inf",       /* 8+3 file on filesystem which supports long file names */
47672dff93Sespie #ifdef __MSDOS__
48672dff93Sespie   /* 8+3 file names strike again...  */
49672dff93Sespie   ".in",        /* for .inz, .igz etc. */
50672dff93Sespie   ".i",
51672dff93Sespie #endif
52672dff93Sespie   "",
53672dff93Sespie   NULL
54fbc94a17Sniklas };
55fbc94a17Sniklas 
56fbc94a17Sniklas static COMPRESSION_ALIST compress_suffixes[] = {
57672dff93Sespie   { ".gz", "gunzip" },
58672dff93Sespie   { ".bz2", "bunzip2" },
59672dff93Sespie   { ".z", "gunzip" },
60fbc94a17Sniklas   { ".Z", "uncompress" },
61fbc94a17Sniklas   { ".Y", "unyabba" },
62672dff93Sespie #ifdef __MSDOS__
63672dff93Sespie   { "gz", "gunzip" },
64672dff93Sespie   { "z", "gunzip" },
65672dff93Sespie #endif
66fbc94a17Sniklas   { (char *)NULL, (char *)NULL }
67fbc94a17Sniklas };
68fbc94a17Sniklas 
69fbc94a17Sniklas /* The path on which we look for info files.  You can initialize this
70fbc94a17Sniklas    from the environment variable INFOPATH if there is one, or you can
71fbc94a17Sniklas    call info_add_path () to add paths to the beginning or end of it.
72fbc94a17Sniklas    You can call zap_infopath () to make the path go away. */
73fbc94a17Sniklas char *infopath = (char *)NULL;
74fbc94a17Sniklas static int infopath_size = 0;
75fbc94a17Sniklas 
76fbc94a17Sniklas /* Expand the filename in PARTIAL to make a real name for this operating
77fbc94a17Sniklas    system.  This looks in INFO_PATHS in order to find the correct file.
78fbc94a17Sniklas    If it can't find the file, it returns NULL. */
79fbc94a17Sniklas static char *local_temp_filename = (char *)NULL;
80fbc94a17Sniklas static int local_temp_filename_size = 0;
81fbc94a17Sniklas 
82fbc94a17Sniklas char *
info_find_fullpath(char * partial)83*1076333cSespie info_find_fullpath (char *partial)
84fbc94a17Sniklas {
85fbc94a17Sniklas   int initial_character;
86fbc94a17Sniklas   char *temp;
87fbc94a17Sniklas 
88fbc94a17Sniklas   filesys_error_number = 0;
89fbc94a17Sniklas 
90fbc94a17Sniklas   maybe_initialize_infopath ();
91fbc94a17Sniklas 
92fbc94a17Sniklas   if (partial && (initial_character = *partial))
93fbc94a17Sniklas     {
94fbc94a17Sniklas       char *expansion;
95fbc94a17Sniklas 
96fbc94a17Sniklas       expansion = lookup_info_filename (partial);
97fbc94a17Sniklas 
98fbc94a17Sniklas       if (expansion)
99fbc94a17Sniklas         return (expansion);
100fbc94a17Sniklas 
101fbc94a17Sniklas       /* If we have the full path to this file, we still may have to add
102fbc94a17Sniklas          various extensions to it.  I guess we have to stat this file
103fbc94a17Sniklas          after all. */
104672dff93Sespie       if (IS_ABSOLUTE (partial))
105672dff93Sespie 	temp = info_absolute_file (partial);
106fbc94a17Sniklas       else if (initial_character == '~')
107fbc94a17Sniklas         {
108fbc94a17Sniklas           expansion = tilde_expand_word (partial);
109672dff93Sespie           if (IS_ABSOLUTE (expansion))
110fbc94a17Sniklas             {
111672dff93Sespie               temp = info_absolute_file (expansion);
112fbc94a17Sniklas               free (expansion);
113fbc94a17Sniklas             }
114fbc94a17Sniklas           else
115fbc94a17Sniklas             temp = expansion;
116fbc94a17Sniklas         }
117fbc94a17Sniklas       else if (initial_character == '.' &&
118672dff93Sespie                (IS_SLASH (partial[1]) ||
119672dff93Sespie 		(partial[1] == '.' && IS_SLASH (partial[2]))))
120fbc94a17Sniklas         {
121fbc94a17Sniklas           if (local_temp_filename_size < 1024)
122fbc94a17Sniklas             local_temp_filename = (char *)xrealloc
123fbc94a17Sniklas               (local_temp_filename, (local_temp_filename_size = 1024));
124fbc94a17Sniklas #if defined (HAVE_GETCWD)
125fbc94a17Sniklas           if (!getcwd (local_temp_filename, local_temp_filename_size))
126fbc94a17Sniklas #else /*  !HAVE_GETCWD */
127fbc94a17Sniklas           if (!getwd (local_temp_filename))
128fbc94a17Sniklas #endif /* !HAVE_GETCWD */
129fbc94a17Sniklas             {
130fbc94a17Sniklas               filesys_error_number = errno;
131fbc94a17Sniklas               return (partial);
132fbc94a17Sniklas             }
133fbc94a17Sniklas 
134fbc94a17Sniklas           strcat (local_temp_filename, "/");
135fbc94a17Sniklas           strcat (local_temp_filename, partial);
136672dff93Sespie 	  temp = info_absolute_file (local_temp_filename); /* try extensions */
137672dff93Sespie 	  if (!temp)
138672dff93Sespie 	    partial = local_temp_filename;
139fbc94a17Sniklas         }
140fbc94a17Sniklas       else
141fbc94a17Sniklas         temp = info_file_in_path (partial, infopath);
142fbc94a17Sniklas 
143fbc94a17Sniklas       if (temp)
144fbc94a17Sniklas         {
145fbc94a17Sniklas           remember_info_filename (partial, temp);
146*1076333cSespie           if (strlen (temp) > (unsigned int) local_temp_filename_size)
147fbc94a17Sniklas             local_temp_filename = (char *) xrealloc
148fbc94a17Sniklas               (local_temp_filename,
149fbc94a17Sniklas                (local_temp_filename_size = (50 + strlen (temp))));
150fbc94a17Sniklas           strcpy (local_temp_filename, temp);
151fbc94a17Sniklas           free (temp);
152fbc94a17Sniklas           return (local_temp_filename);
153fbc94a17Sniklas         }
154fbc94a17Sniklas     }
155fbc94a17Sniklas   return (partial);
156fbc94a17Sniklas }
157fbc94a17Sniklas 
158fbc94a17Sniklas /* Scan the list of directories in PATH looking for FILENAME.  If we find
159fbc94a17Sniklas    one that is a regular file, return it as a new string.  Otherwise, return
160fbc94a17Sniklas    a NULL pointer. */
161fbc94a17Sniklas static char *
info_file_in_path(char * filename,char * path)162*1076333cSespie info_file_in_path (char *filename, char *path)
163fbc94a17Sniklas {
164fbc94a17Sniklas   struct stat finfo;
165fbc94a17Sniklas   char *temp_dirname;
166fbc94a17Sniklas   int statable, dirname_index;
167fbc94a17Sniklas 
1683aa90977Sespie   /* Reject ridiculous cases up front, to prevent infinite recursion
1693aa90977Sespie      later on.  E.g., someone might say "info '(.)foo'"...  */
1703aa90977Sespie   if (!*filename || STREQ (filename, ".") || STREQ (filename, ".."))
1713aa90977Sespie     return NULL;
1723aa90977Sespie 
173fbc94a17Sniklas   dirname_index = 0;
174fbc94a17Sniklas 
175840175f0Skstailey   while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
176fbc94a17Sniklas     {
177fbc94a17Sniklas       register int i, pre_suffix_length;
178fbc94a17Sniklas       char *temp;
179fbc94a17Sniklas 
180fbc94a17Sniklas       /* Expand a leading tilde if one is present. */
181fbc94a17Sniklas       if (*temp_dirname == '~')
182fbc94a17Sniklas         {
183fbc94a17Sniklas           char *expanded_dirname;
184fbc94a17Sniklas 
185fbc94a17Sniklas           expanded_dirname = tilde_expand_word (temp_dirname);
186fbc94a17Sniklas           free (temp_dirname);
187fbc94a17Sniklas           temp_dirname = expanded_dirname;
188fbc94a17Sniklas         }
189fbc94a17Sniklas 
190fbc94a17Sniklas       temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename));
191fbc94a17Sniklas       strcpy (temp, temp_dirname);
192672dff93Sespie       if (!IS_SLASH (temp[(strlen (temp)) - 1]))
193fbc94a17Sniklas         strcat (temp, "/");
194fbc94a17Sniklas       strcat (temp, filename);
195fbc94a17Sniklas 
196fbc94a17Sniklas       pre_suffix_length = strlen (temp);
197fbc94a17Sniklas 
198fbc94a17Sniklas       free (temp_dirname);
199fbc94a17Sniklas 
200fbc94a17Sniklas       for (i = 0; info_suffixes[i]; i++)
201fbc94a17Sniklas         {
202fbc94a17Sniklas           strcpy (temp + pre_suffix_length, info_suffixes[i]);
203fbc94a17Sniklas 
204fbc94a17Sniklas           statable = (stat (temp, &finfo) == 0);
205fbc94a17Sniklas 
206fbc94a17Sniklas           /* If we have found a regular file, then use that.  Else, if we
207fbc94a17Sniklas              have found a directory, look in that directory for this file. */
208fbc94a17Sniklas           if (statable)
209fbc94a17Sniklas             {
210fbc94a17Sniklas               if (S_ISREG (finfo.st_mode))
211fbc94a17Sniklas                 {
212fbc94a17Sniklas                   return (temp);
213fbc94a17Sniklas                 }
214fbc94a17Sniklas               else if (S_ISDIR (finfo.st_mode))
215fbc94a17Sniklas                 {
216fbc94a17Sniklas                   char *newpath, *filename_only, *newtemp;
217fbc94a17Sniklas 
218840175f0Skstailey                   newpath = xstrdup (temp);
219fbc94a17Sniklas                   filename_only = filename_non_directory (filename);
220fbc94a17Sniklas                   newtemp = info_file_in_path (filename_only, newpath);
221fbc94a17Sniklas 
222fbc94a17Sniklas                   free (newpath);
223fbc94a17Sniklas                   if (newtemp)
224fbc94a17Sniklas                     {
225fbc94a17Sniklas                       free (temp);
226fbc94a17Sniklas                       return (newtemp);
227fbc94a17Sniklas                     }
228fbc94a17Sniklas                 }
229fbc94a17Sniklas             }
230fbc94a17Sniklas           else
231fbc94a17Sniklas             {
232fbc94a17Sniklas               /* Add various compression suffixes to the name to see if
233fbc94a17Sniklas                  the file is present in compressed format. */
234fbc94a17Sniklas               register int j, pre_compress_suffix_length;
235fbc94a17Sniklas 
236fbc94a17Sniklas               pre_compress_suffix_length = strlen (temp);
237fbc94a17Sniklas 
238fbc94a17Sniklas               for (j = 0; compress_suffixes[j].suffix; j++)
239fbc94a17Sniklas                 {
240fbc94a17Sniklas                   strcpy (temp + pre_compress_suffix_length,
241fbc94a17Sniklas                           compress_suffixes[j].suffix);
242fbc94a17Sniklas 
243fbc94a17Sniklas                   statable = (stat (temp, &finfo) == 0);
244fbc94a17Sniklas                   if (statable && (S_ISREG (finfo.st_mode)))
245fbc94a17Sniklas                     return (temp);
246fbc94a17Sniklas                 }
247fbc94a17Sniklas             }
248fbc94a17Sniklas         }
249fbc94a17Sniklas       free (temp);
250fbc94a17Sniklas     }
251fbc94a17Sniklas   return ((char *)NULL);
252fbc94a17Sniklas }
253fbc94a17Sniklas 
254672dff93Sespie /* Assume FNAME is an absolute file name, and check whether it is
255672dff93Sespie    a regular file.  If it is, return it as a new string; otherwise
256672dff93Sespie    return a NULL pointer.  We do it by taking the file name apart
257672dff93Sespie    into its directory and basename parts, and calling info_file_in_path.*/
258672dff93Sespie static char *
info_absolute_file(char * fname)259*1076333cSespie info_absolute_file (char *fname)
260672dff93Sespie {
261672dff93Sespie   char *containing_dir = xstrdup (fname);
262672dff93Sespie   char *base = filename_non_directory (containing_dir);
263672dff93Sespie 
264672dff93Sespie   if (base > containing_dir)
265672dff93Sespie     base[-1] = '\0';
266672dff93Sespie 
267672dff93Sespie   return info_file_in_path (filename_non_directory (fname), containing_dir);
268672dff93Sespie }
269672dff93Sespie 
270*1076333cSespie 
271*1076333cSespie /* Given a string containing units of information separated by the
272*1076333cSespie    PATH_SEP character, return the next one after IDX, or NULL if there
273*1076333cSespie    are no more.  Advance IDX to the character after the colon. */
274*1076333cSespie 
275fbc94a17Sniklas char *
extract_colon_unit(char * string,int * idx)276*1076333cSespie extract_colon_unit (char *string, int *idx)
277fbc94a17Sniklas {
278*1076333cSespie   unsigned int i = (unsigned int) *idx;
279*1076333cSespie   unsigned int start = i;
280fbc94a17Sniklas 
281*1076333cSespie   if (!string || i >= strlen (string))
282*1076333cSespie     return NULL;
283fbc94a17Sniklas 
284*1076333cSespie   if (!string[i]) /* end of string */
285*1076333cSespie     return NULL;
286*1076333cSespie 
287*1076333cSespie   /* Advance to next PATH_SEP.  */
288672dff93Sespie   while (string[i] && string[i] != PATH_SEP[0])
289fbc94a17Sniklas     i++;
290fbc94a17Sniklas 
291*1076333cSespie   {
292*1076333cSespie     char *value = xmalloc ((i - start) + 1);
293fbc94a17Sniklas     strncpy (value, &string[start], (i - start));
294*1076333cSespie     value[i - start] = 0;
295*1076333cSespie 
296*1076333cSespie     i++; /* move past PATH_SEP */
297fbc94a17Sniklas     *idx = i;
298*1076333cSespie     return value;
299fbc94a17Sniklas   }
300fbc94a17Sniklas }
301fbc94a17Sniklas 
302fbc94a17Sniklas /* A structure which associates a filename with its expansion. */
303*1076333cSespie typedef struct
304*1076333cSespie {
305fbc94a17Sniklas   char *filename;
306fbc94a17Sniklas   char *expansion;
307fbc94a17Sniklas } FILENAME_LIST;
308fbc94a17Sniklas 
309fbc94a17Sniklas /* An array of remembered arguments and results. */
310fbc94a17Sniklas static FILENAME_LIST **names_and_files = (FILENAME_LIST **)NULL;
311fbc94a17Sniklas static int names_and_files_index = 0;
312fbc94a17Sniklas static int names_and_files_slots = 0;
313fbc94a17Sniklas 
314fbc94a17Sniklas /* Find the result for having already called info_find_fullpath () with
315fbc94a17Sniklas    FILENAME. */
316fbc94a17Sniklas static char *
lookup_info_filename(char * filename)317*1076333cSespie lookup_info_filename (char *filename)
318fbc94a17Sniklas {
319fbc94a17Sniklas   if (filename && names_and_files)
320fbc94a17Sniklas     {
321fbc94a17Sniklas       register int i;
322fbc94a17Sniklas       for (i = 0; names_and_files[i]; i++)
323fbc94a17Sniklas         {
324672dff93Sespie           if (FILENAME_CMP (names_and_files[i]->filename, filename) == 0)
325fbc94a17Sniklas             return (names_and_files[i]->expansion);
326fbc94a17Sniklas         }
327fbc94a17Sniklas     }
328fbc94a17Sniklas   return (char *)NULL;;
329fbc94a17Sniklas }
330fbc94a17Sniklas 
331fbc94a17Sniklas /* Add a filename and its expansion to our list. */
332fbc94a17Sniklas static void
remember_info_filename(char * filename,char * expansion)333*1076333cSespie remember_info_filename (char *filename, char *expansion)
334fbc94a17Sniklas {
335fbc94a17Sniklas   FILENAME_LIST *new;
336fbc94a17Sniklas 
337fbc94a17Sniklas   if (names_and_files_index + 2 > names_and_files_slots)
338fbc94a17Sniklas     {
339fbc94a17Sniklas       int alloc_size;
340fbc94a17Sniklas       names_and_files_slots += 10;
341fbc94a17Sniklas 
342fbc94a17Sniklas       alloc_size = names_and_files_slots * sizeof (FILENAME_LIST *);
343fbc94a17Sniklas 
344fbc94a17Sniklas       names_and_files =
345fbc94a17Sniklas         (FILENAME_LIST **) xrealloc (names_and_files, alloc_size);
346fbc94a17Sniklas     }
347fbc94a17Sniklas 
348fbc94a17Sniklas   new = (FILENAME_LIST *)xmalloc (sizeof (FILENAME_LIST));
349840175f0Skstailey   new->filename = xstrdup (filename);
350840175f0Skstailey   new->expansion = expansion ? xstrdup (expansion) : (char *)NULL;
351fbc94a17Sniklas 
352fbc94a17Sniklas   names_and_files[names_and_files_index++] = new;
353fbc94a17Sniklas   names_and_files[names_and_files_index] = (FILENAME_LIST *)NULL;
354fbc94a17Sniklas }
355fbc94a17Sniklas 
356fbc94a17Sniklas static void
maybe_initialize_infopath(void)357*1076333cSespie maybe_initialize_infopath (void)
358fbc94a17Sniklas {
359fbc94a17Sniklas   if (!infopath_size)
360fbc94a17Sniklas     {
361fbc94a17Sniklas       infopath = (char *)
362fbc94a17Sniklas         xmalloc (infopath_size = (1 + strlen (DEFAULT_INFOPATH)));
363fbc94a17Sniklas 
364fbc94a17Sniklas       strcpy (infopath, DEFAULT_INFOPATH);
365fbc94a17Sniklas     }
366fbc94a17Sniklas }
367fbc94a17Sniklas 
368fbc94a17Sniklas /* Add PATH to the list of paths found in INFOPATH.  2nd argument says
369fbc94a17Sniklas    whether to put PATH at the front or end of INFOPATH. */
370fbc94a17Sniklas void
info_add_path(char * path,int where)371*1076333cSespie info_add_path (char *path, int where)
372fbc94a17Sniklas {
373fbc94a17Sniklas   int len;
374fbc94a17Sniklas 
375fbc94a17Sniklas   if (!infopath)
376fbc94a17Sniklas     {
377fbc94a17Sniklas       infopath = (char *)xmalloc (infopath_size = 200 + strlen (path));
378fbc94a17Sniklas       infopath[0] = '\0';
379fbc94a17Sniklas     }
380fbc94a17Sniklas 
381fbc94a17Sniklas   len = strlen (path) + strlen (infopath);
382fbc94a17Sniklas 
383fbc94a17Sniklas   if (len + 2 >= infopath_size)
384fbc94a17Sniklas     infopath = (char *)xrealloc (infopath, (infopath_size += (2 * len) + 2));
385fbc94a17Sniklas 
386fbc94a17Sniklas   if (!*infopath)
387fbc94a17Sniklas     strcpy (infopath, path);
388fbc94a17Sniklas   else if (where == INFOPATH_APPEND)
389fbc94a17Sniklas     {
390672dff93Sespie       strcat (infopath, PATH_SEP);
391fbc94a17Sniklas       strcat (infopath, path);
392fbc94a17Sniklas     }
393fbc94a17Sniklas   else if (where == INFOPATH_PREPEND)
394fbc94a17Sniklas     {
395840175f0Skstailey       char *temp = xstrdup (infopath);
396fbc94a17Sniklas       strcpy (infopath, path);
397672dff93Sespie       strcat (infopath, PATH_SEP);
398fbc94a17Sniklas       strcat (infopath, temp);
399fbc94a17Sniklas       free (temp);
400fbc94a17Sniklas     }
401fbc94a17Sniklas }
402fbc94a17Sniklas 
403fbc94a17Sniklas /* Make INFOPATH have absolutely nothing in it. */
404fbc94a17Sniklas void
zap_infopath(void)405*1076333cSespie zap_infopath (void)
406fbc94a17Sniklas {
407fbc94a17Sniklas   if (infopath)
408fbc94a17Sniklas     free (infopath);
409fbc94a17Sniklas 
410fbc94a17Sniklas   infopath = (char *)NULL;
411fbc94a17Sniklas   infopath_size = 0;
412fbc94a17Sniklas }
413fbc94a17Sniklas 
414672dff93Sespie /* Given a chunk of text and its length, convert all CRLF pairs at every
415672dff93Sespie    end-of-line into a single Newline character.  Return the length of
416672dff93Sespie    produced text.
417672dff93Sespie 
418672dff93Sespie    This is required because the rest of code is too entrenched in having
419672dff93Sespie    a single newline at each EOL; in particular, searching for various
420672dff93Sespie    Info headers and cookies can become extremely tricky if that assumption
421672dff93Sespie    breaks.
422672dff93Sespie 
423672dff93Sespie    FIXME: this could also support Mac-style text files with a single CR
424672dff93Sespie    at the EOL, but what about random CR characters in non-Mac files?  Can
425672dff93Sespie    we afford converting them into newlines as well?  Maybe implement some
426672dff93Sespie    heuristics here, like in Emacs 20.
427672dff93Sespie 
428672dff93Sespie    FIXME: is it a good idea to show the EOL type on the modeline?  */
429672dff93Sespie long
convert_eols(char * text,long int textlen)430*1076333cSespie convert_eols (char *text, long int textlen)
431672dff93Sespie {
432672dff93Sespie   register char *s = text;
433672dff93Sespie   register char *d = text;
434672dff93Sespie 
435672dff93Sespie   while (textlen--)
436672dff93Sespie     {
437672dff93Sespie       if (*s == '\r' && textlen && s[1] == '\n')
438672dff93Sespie 	{
439672dff93Sespie 	  s++;
440672dff93Sespie 	  textlen--;
441672dff93Sespie 	}
442672dff93Sespie       *d++ = *s++;
443672dff93Sespie     }
444672dff93Sespie 
445672dff93Sespie   return (long)(d - text);
446672dff93Sespie }
447672dff93Sespie 
448fbc94a17Sniklas /* Read the contents of PATHNAME, returning a buffer with the contents of
449fbc94a17Sniklas    that file in it, and returning the size of that buffer in FILESIZE.
450fbc94a17Sniklas    FINFO is a stat struct which has already been filled in by the caller.
451672dff93Sespie    If the file turns out to be compressed, set IS_COMPRESSED to non-zero.
452fbc94a17Sniklas    If the file cannot be read, return a NULL pointer. */
453fbc94a17Sniklas char *
filesys_read_info_file(char * pathname,long int * filesize,struct stat * finfo,int * is_compressed)454*1076333cSespie filesys_read_info_file (char *pathname, long int *filesize,
455*1076333cSespie     struct stat *finfo, int *is_compressed)
456fbc94a17Sniklas {
457fbc94a17Sniklas   long st_size;
458fbc94a17Sniklas 
459fbc94a17Sniklas   *filesize = filesys_error_number = 0;
460fbc94a17Sniklas 
461fbc94a17Sniklas   if (compressed_filename_p (pathname))
462672dff93Sespie     {
463672dff93Sespie       *is_compressed = 1;
464*1076333cSespie       return (filesys_read_compressed (pathname, filesize));
465672dff93Sespie     }
466fbc94a17Sniklas   else
467fbc94a17Sniklas     {
468fbc94a17Sniklas       int descriptor;
469fbc94a17Sniklas       char *contents;
470fbc94a17Sniklas 
471672dff93Sespie       *is_compressed = 0;
472672dff93Sespie       descriptor = open (pathname, O_RDONLY | O_BINARY, 0666);
473fbc94a17Sniklas 
474fbc94a17Sniklas       /* If the file couldn't be opened, give up. */
475fbc94a17Sniklas       if (descriptor < 0)
476fbc94a17Sniklas         {
477fbc94a17Sniklas           filesys_error_number = errno;
478fbc94a17Sniklas           return ((char *)NULL);
479fbc94a17Sniklas         }
480fbc94a17Sniklas 
481fbc94a17Sniklas       /* Try to read the contents of this file. */
482fbc94a17Sniklas       st_size = (long) finfo->st_size;
483fbc94a17Sniklas       contents = (char *)xmalloc (1 + st_size);
484fbc94a17Sniklas       if ((read (descriptor, contents, st_size)) != st_size)
485fbc94a17Sniklas         {
486fbc94a17Sniklas 	  filesys_error_number = errno;
487fbc94a17Sniklas 	  close (descriptor);
488fbc94a17Sniklas 	  free (contents);
489fbc94a17Sniklas 	  return ((char *)NULL);
490fbc94a17Sniklas         }
491fbc94a17Sniklas 
492fbc94a17Sniklas       close (descriptor);
493fbc94a17Sniklas 
494672dff93Sespie       /* Convert any DOS-style CRLF EOLs into Unix-style NL.
495672dff93Sespie 	 Seems like a good idea to have even on Unix, in case the Info
496672dff93Sespie 	 files are coming from some Windows system across a network.  */
497672dff93Sespie       *filesize = convert_eols (contents, st_size);
498672dff93Sespie 
499672dff93Sespie       /* EOL conversion can shrink the text quite a bit.  We don't
500672dff93Sespie 	 want to waste storage.  */
501672dff93Sespie       if (*filesize < st_size)
502672dff93Sespie 	contents = (char *)xrealloc (contents, 1 + *filesize);
5033aa90977Sespie       contents[*filesize] = '\0';
504672dff93Sespie 
505fbc94a17Sniklas       return (contents);
506fbc94a17Sniklas     }
507fbc94a17Sniklas }
508fbc94a17Sniklas 
509fbc94a17Sniklas /* Typically, pipe buffers are 4k. */
510fbc94a17Sniklas #define BASIC_PIPE_BUFFER (4 * 1024)
511fbc94a17Sniklas 
512fbc94a17Sniklas /* We use some large multiple of that. */
513fbc94a17Sniklas #define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER)
514fbc94a17Sniklas 
515fbc94a17Sniklas char *
filesys_read_compressed(char * pathname,long int * filesize)516*1076333cSespie filesys_read_compressed (char *pathname, long int *filesize)
517fbc94a17Sniklas {
518fbc94a17Sniklas   FILE *stream;
519fbc94a17Sniklas   char *command, *decompressor;
520fbc94a17Sniklas   char *contents = (char *)NULL;
521fbc94a17Sniklas 
522fbc94a17Sniklas   *filesize = filesys_error_number = 0;
523fbc94a17Sniklas 
524fbc94a17Sniklas   decompressor = filesys_decompressor_for_file (pathname);
525fbc94a17Sniklas 
526fbc94a17Sniklas   if (!decompressor)
527fbc94a17Sniklas     return ((char *)NULL);
528fbc94a17Sniklas 
529672dff93Sespie   command = (char *)xmalloc (15 + strlen (pathname) + strlen (decompressor));
530672dff93Sespie   /* Explicit .exe suffix makes the diagnostics of `popen'
531672dff93Sespie      better on systems where COMMAND.COM is the stock shell.  */
532672dff93Sespie   sprintf (command, "%s%s < %s",
533672dff93Sespie 	   decompressor, STRIP_DOT_EXE ? ".exe" : "", pathname);
534fbc94a17Sniklas 
535fbc94a17Sniklas #if !defined (BUILDING_LIBRARY)
536fbc94a17Sniklas   if (info_windows_initialized_p)
537fbc94a17Sniklas     {
538fbc94a17Sniklas       char *temp;
539fbc94a17Sniklas 
540fbc94a17Sniklas       temp = (char *)xmalloc (5 + strlen (command));
541fbc94a17Sniklas       sprintf (temp, "%s...", command);
542*1076333cSespie       message_in_echo_area ("%s", temp, NULL);
543fbc94a17Sniklas       free (temp);
544fbc94a17Sniklas     }
545fbc94a17Sniklas #endif /* !BUILDING_LIBRARY */
546fbc94a17Sniklas 
547672dff93Sespie   stream = popen (command, FOPEN_RBIN);
548fbc94a17Sniklas   free (command);
549fbc94a17Sniklas 
550fbc94a17Sniklas   /* Read chunks from this file until there are none left to read. */
551fbc94a17Sniklas   if (stream)
552fbc94a17Sniklas     {
5533aa90977Sespie       long offset, size;
554fbc94a17Sniklas       char *chunk;
555fbc94a17Sniklas 
556fbc94a17Sniklas       offset = size = 0;
557fbc94a17Sniklas       chunk = (char *)xmalloc (FILESYS_PIPE_BUFFER_SIZE);
558fbc94a17Sniklas 
559fbc94a17Sniklas       while (1)
560fbc94a17Sniklas         {
561fbc94a17Sniklas           int bytes_read;
562fbc94a17Sniklas 
563fbc94a17Sniklas           bytes_read = fread (chunk, 1, FILESYS_PIPE_BUFFER_SIZE, stream);
564fbc94a17Sniklas 
565fbc94a17Sniklas           if (bytes_read + offset >= size)
566fbc94a17Sniklas             contents = (char *)xrealloc
567fbc94a17Sniklas               (contents, size += (2 * FILESYS_PIPE_BUFFER_SIZE));
568fbc94a17Sniklas 
569fbc94a17Sniklas           memcpy (contents + offset, chunk, bytes_read);
570fbc94a17Sniklas           offset += bytes_read;
571fbc94a17Sniklas           if (bytes_read != FILESYS_PIPE_BUFFER_SIZE)
572fbc94a17Sniklas             break;
573fbc94a17Sniklas         }
574fbc94a17Sniklas 
575fbc94a17Sniklas       free (chunk);
576672dff93Sespie       if (pclose (stream) == -1)
577672dff93Sespie 	{
578672dff93Sespie 	  if (contents)
579672dff93Sespie 	    free (contents);
580672dff93Sespie 	  contents = (char *)NULL;
581672dff93Sespie 	  filesys_error_number = errno;
582672dff93Sespie 	}
583672dff93Sespie       else
584672dff93Sespie 	{
585672dff93Sespie 	  *filesize = convert_eols (contents, offset);
586672dff93Sespie 	  contents = (char *)xrealloc (contents, 1 + *filesize);
5873aa90977Sespie 	  contents[*filesize] = '\0';
588672dff93Sespie 	}
589fbc94a17Sniklas     }
590fbc94a17Sniklas   else
591fbc94a17Sniklas     {
592fbc94a17Sniklas       filesys_error_number = errno;
593fbc94a17Sniklas     }
594fbc94a17Sniklas 
595fbc94a17Sniklas #if !defined (BUILDING_LIBARARY)
596fbc94a17Sniklas   if (info_windows_initialized_p)
597fbc94a17Sniklas     unmessage_in_echo_area ();
598fbc94a17Sniklas #endif /* !BUILDING_LIBRARY */
599fbc94a17Sniklas   return (contents);
600fbc94a17Sniklas }
601fbc94a17Sniklas 
602fbc94a17Sniklas /* Return non-zero if FILENAME belongs to a compressed file. */
603fbc94a17Sniklas int
compressed_filename_p(char * filename)604*1076333cSespie compressed_filename_p (char *filename)
605fbc94a17Sniklas {
606fbc94a17Sniklas   char *decompressor;
607fbc94a17Sniklas 
608fbc94a17Sniklas   /* Find the final extension of this filename, and see if it matches one
609fbc94a17Sniklas      of our known ones. */
610fbc94a17Sniklas   decompressor = filesys_decompressor_for_file (filename);
611fbc94a17Sniklas 
612fbc94a17Sniklas   if (decompressor)
613fbc94a17Sniklas     return (1);
614fbc94a17Sniklas   else
615fbc94a17Sniklas     return (0);
616fbc94a17Sniklas }
617fbc94a17Sniklas 
618fbc94a17Sniklas /* Return the command string that would be used to decompress FILENAME. */
619fbc94a17Sniklas char *
filesys_decompressor_for_file(char * filename)620*1076333cSespie filesys_decompressor_for_file (char *filename)
621fbc94a17Sniklas {
622fbc94a17Sniklas   register int i;
623fbc94a17Sniklas   char *extension = (char *)NULL;
624fbc94a17Sniklas 
625fbc94a17Sniklas   /* Find the final extension of FILENAME, and see if it appears in our
626fbc94a17Sniklas      list of known compression extensions. */
627fbc94a17Sniklas   for (i = strlen (filename) - 1; i > 0; i--)
628fbc94a17Sniklas     if (filename[i] == '.')
629fbc94a17Sniklas       {
630fbc94a17Sniklas         extension = filename + i;
631fbc94a17Sniklas         break;
632fbc94a17Sniklas       }
633fbc94a17Sniklas 
634fbc94a17Sniklas   if (!extension)
635fbc94a17Sniklas     return ((char *)NULL);
636fbc94a17Sniklas 
637fbc94a17Sniklas   for (i = 0; compress_suffixes[i].suffix; i++)
638672dff93Sespie     if (FILENAME_CMP (extension, compress_suffixes[i].suffix) == 0)
639fbc94a17Sniklas       return (compress_suffixes[i].decompressor);
640fbc94a17Sniklas 
641672dff93Sespie #if defined (__MSDOS__)
642672dff93Sespie   /* If no other suffix matched, allow any extension which ends
643672dff93Sespie      with `z' to be decompressed by gunzip.  Due to limited 8+3 DOS
644672dff93Sespie      file namespace, we can expect many such cases, and supporting
645672dff93Sespie      every weird suffix thus produced would be a pain.  */
646672dff93Sespie   if (extension[strlen (extension) - 1] == 'z' ||
647672dff93Sespie       extension[strlen (extension) - 1] == 'Z')
648672dff93Sespie     return "gunzip";
649672dff93Sespie #endif
650672dff93Sespie 
651fbc94a17Sniklas   return ((char *)NULL);
652fbc94a17Sniklas }
653fbc94a17Sniklas 
654fbc94a17Sniklas /* The number of the most recent file system error. */
655fbc94a17Sniklas int filesys_error_number = 0;
656fbc94a17Sniklas 
657fbc94a17Sniklas /* A function which returns a pointer to a static buffer containing
658fbc94a17Sniklas    an error message for FILENAME and ERROR_NUM. */
659fbc94a17Sniklas static char *errmsg_buf = (char *)NULL;
660fbc94a17Sniklas static int errmsg_buf_size = 0;
661fbc94a17Sniklas 
662fbc94a17Sniklas char *
filesys_error_string(char * filename,int error_num)663*1076333cSespie filesys_error_string (char *filename, int error_num)
664fbc94a17Sniklas {
665fbc94a17Sniklas   int len;
666fbc94a17Sniklas   char *result;
667fbc94a17Sniklas 
668fbc94a17Sniklas   if (error_num == 0)
669fbc94a17Sniklas     return ((char *)NULL);
670fbc94a17Sniklas 
671fbc94a17Sniklas   result = strerror (error_num);
672fbc94a17Sniklas 
673fbc94a17Sniklas   len = 4 + strlen (filename) + strlen (result);
674fbc94a17Sniklas   if (len >= errmsg_buf_size)
675fbc94a17Sniklas     errmsg_buf = (char *)xrealloc (errmsg_buf, (errmsg_buf_size = 2 + len));
676fbc94a17Sniklas 
677fbc94a17Sniklas   sprintf (errmsg_buf, "%s: %s", filename, result);
678fbc94a17Sniklas   return (errmsg_buf);
679fbc94a17Sniklas }
680fbc94a17Sniklas 
681672dff93Sespie 
6823aa90977Sespie /* Check for "dir" with all the possible info and compression suffixes,
6833aa90977Sespie    in combination.  */
684672dff93Sespie 
685672dff93Sespie int
is_dir_name(char * filename)686*1076333cSespie is_dir_name (char *filename)
687672dff93Sespie {
688672dff93Sespie   unsigned i;
6893aa90977Sespie 
6903aa90977Sespie   for (i = 0; info_suffixes[i]; i++)
6913aa90977Sespie     {
6923aa90977Sespie       unsigned c;
6933aa90977Sespie       char trydir[50];
6943aa90977Sespie       strcpy (trydir, "dir");
6953aa90977Sespie       strcat (trydir, info_suffixes[i]);
6963aa90977Sespie 
6973aa90977Sespie       if (strcasecmp (filename, trydir) == 0)
698672dff93Sespie         return 1;
699672dff93Sespie 
7003aa90977Sespie       for (c = 0; compress_suffixes[c].suffix; c++)
701672dff93Sespie         {
702672dff93Sespie           char dir_compressed[50]; /* can be short */
7033aa90977Sespie           strcpy (dir_compressed, trydir);
7043aa90977Sespie           strcat (dir_compressed, compress_suffixes[c].suffix);
705672dff93Sespie           if (strcasecmp (filename, dir_compressed) == 0)
706672dff93Sespie             return 1;
707672dff93Sespie         }
7083aa90977Sespie     }
709672dff93Sespie 
710672dff93Sespie   return 0;
711672dff93Sespie }
712