OSDN Git Service

* vax.h (ASM_SPEC): Pass `-J' to assembler.
[pf3gnuchains/gcc-fork.git] / texinfo / info / filesys.c
1 /* filesys.c -- File system specific functions for hacking this system.
2    $Id: filesys.c,v 1.1.1.3 1998/03/24 18:20:10 law Exp $
3
4    Copyright (C) 1993, 97, 98 Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20    Written by Brian Fox (bfox@ai.mit.edu). */
21
22 #include "info.h"
23
24 #include "tilde.h"
25 #include "filesys.h"
26
27 /* Local to this file. */
28 static char *info_file_in_path (), *lookup_info_filename ();
29 static void remember_info_filename (), maybe_initialize_infopath ();
30
31 typedef struct
32 {
33   char *suffix;
34   char *decompressor;
35 } COMPRESSION_ALIST;
36
37 static char *info_suffixes[] = {
38   "",
39   ".info",
40   "-info",
41   "/index",
42   (char *)NULL
43 };
44
45 static COMPRESSION_ALIST compress_suffixes[] = {
46   { ".Z", "uncompress" },
47   { ".Y", "unyabba" },
48   { ".z", "gunzip" },
49   { ".gz", "gunzip" },
50   { (char *)NULL, (char *)NULL }
51 };
52
53 /* The path on which we look for info files.  You can initialize this
54    from the environment variable INFOPATH if there is one, or you can
55    call info_add_path () to add paths to the beginning or end of it.
56    You can call zap_infopath () to make the path go away. */
57 char *infopath = (char *)NULL;
58 static int infopath_size = 0;
59
60 /* Expand the filename in PARTIAL to make a real name for this operating
61    system.  This looks in INFO_PATHS in order to find the correct file.
62    If it can't find the file, it returns NULL. */
63 static char *local_temp_filename = (char *)NULL;
64 static int local_temp_filename_size = 0;
65
66 char *
67 info_find_fullpath (partial)
68      char *partial;
69 {
70   int initial_character;
71   char *temp;
72
73   filesys_error_number = 0;
74
75   maybe_initialize_infopath ();
76
77   if (partial && (initial_character = *partial))
78     {
79       char *expansion;
80
81       expansion = lookup_info_filename (partial);
82
83       if (expansion)
84         return (expansion);
85
86       /* If we have the full path to this file, we still may have to add
87          various extensions to it.  I guess we have to stat this file
88          after all. */
89       if (initial_character == '/')
90         temp = info_file_in_path (partial + 1, "/");
91       else if (initial_character == '~')
92         {
93           expansion = tilde_expand_word (partial);
94           if (*expansion == '/')
95             {
96               temp = info_file_in_path (expansion + 1, "/");
97               free (expansion);
98             }
99           else
100             temp = expansion;
101         }
102       else if (initial_character == '.' &&
103                (partial[1] == '/' || (partial[1] == '.' && partial[2] == '/')))
104         {
105           if (local_temp_filename_size < 1024)
106             local_temp_filename = (char *)xrealloc
107               (local_temp_filename, (local_temp_filename_size = 1024));
108 #if defined (HAVE_GETCWD)
109           if (!getcwd (local_temp_filename, local_temp_filename_size))
110 #else /*  !HAVE_GETCWD */
111           if (!getwd (local_temp_filename))
112 #endif /* !HAVE_GETCWD */
113             {
114               filesys_error_number = errno;
115               return (partial);
116             }
117
118           strcat (local_temp_filename, "/");
119           strcat (local_temp_filename, partial);
120           return (local_temp_filename);
121         }
122       else
123         temp = info_file_in_path (partial, infopath);
124
125       if (temp)
126         {
127           remember_info_filename (partial, temp);
128           if (strlen (temp) > local_temp_filename_size)
129             local_temp_filename = (char *) xrealloc
130               (local_temp_filename,
131                (local_temp_filename_size = (50 + strlen (temp))));
132           strcpy (local_temp_filename, temp);
133           free (temp);
134           return (local_temp_filename);
135         }
136     }
137   return (partial);
138 }
139
140 /* Scan the list of directories in PATH looking for FILENAME.  If we find
141    one that is a regular file, return it as a new string.  Otherwise, return
142    a NULL pointer. */
143 static char *
144 info_file_in_path (filename, path)
145      char *filename, *path;
146 {
147   struct stat finfo;
148   char *temp_dirname;
149   int statable, dirname_index;
150
151   dirname_index = 0;
152
153   while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
154     {
155       register int i, pre_suffix_length;
156       char *temp;
157
158       /* Expand a leading tilde if one is present. */
159       if (*temp_dirname == '~')
160         {
161           char *expanded_dirname;
162
163           expanded_dirname = tilde_expand_word (temp_dirname);
164           free (temp_dirname);
165           temp_dirname = expanded_dirname;
166         }
167
168       temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename));
169       strcpy (temp, temp_dirname);
170       if (temp[(strlen (temp)) - 1] != '/')
171         strcat (temp, "/");
172       strcat (temp, filename);
173
174       pre_suffix_length = strlen (temp);
175
176       free (temp_dirname);
177
178       for (i = 0; info_suffixes[i]; i++)
179         {
180           strcpy (temp + pre_suffix_length, info_suffixes[i]);
181
182           statable = (stat (temp, &finfo) == 0);
183
184           /* If we have found a regular file, then use that.  Else, if we
185              have found a directory, look in that directory for this file. */
186           if (statable)
187             {
188               if (S_ISREG (finfo.st_mode))
189                 {
190                   return (temp);
191                 }
192               else if (S_ISDIR (finfo.st_mode))
193                 {
194                   char *newpath, *filename_only, *newtemp;
195
196                   newpath = xstrdup (temp);
197                   filename_only = filename_non_directory (filename);
198                   newtemp = info_file_in_path (filename_only, newpath);
199
200                   free (newpath);
201                   if (newtemp)
202                     {
203                       free (temp);
204                       return (newtemp);
205                     }
206                 }
207             }
208           else
209             {
210               /* Add various compression suffixes to the name to see if
211                  the file is present in compressed format. */
212               register int j, pre_compress_suffix_length;
213
214               pre_compress_suffix_length = strlen (temp);
215
216               for (j = 0; compress_suffixes[j].suffix; j++)
217                 {
218                   strcpy (temp + pre_compress_suffix_length,
219                           compress_suffixes[j].suffix);
220
221                   statable = (stat (temp, &finfo) == 0);
222                   if (statable && (S_ISREG (finfo.st_mode)))
223                     return (temp);
224                 }
225             }
226         }
227       free (temp);
228     }
229   return ((char *)NULL);
230 }
231
232 /* Given a string containing units of information separated by colons,
233    return the next one pointed to by IDX, or NULL if there are no more.
234    Advance IDX to the character after the colon. */
235 char *
236 extract_colon_unit (string, idx)
237      char *string;
238      int *idx;
239 {
240   register int i, start;
241
242   i = start = *idx;
243   if ((i >= strlen (string)) || !string)
244     return ((char *) NULL);
245
246   while (string[i] && string[i] != ':')
247     i++;
248   if (i == start)
249     {
250       return ((char *) NULL);
251     }
252   else
253     {
254       char *value;
255
256       value = (char *) xmalloc (1 + (i - start));
257       strncpy (value, &string[start], (i - start));
258       value[i - start] = '\0';
259       if (string[i])
260         ++i;
261       *idx = i;
262       return (value);
263     }
264 }
265
266 /* A structure which associates a filename with its expansion. */
267 typedef struct {
268   char *filename;
269   char *expansion;
270 } FILENAME_LIST;
271
272 /* An array of remembered arguments and results. */
273 static FILENAME_LIST **names_and_files = (FILENAME_LIST **)NULL;
274 static int names_and_files_index = 0;
275 static int names_and_files_slots = 0;
276
277 /* Find the result for having already called info_find_fullpath () with
278    FILENAME. */
279 static char *
280 lookup_info_filename (filename)
281      char *filename;
282 {
283   if (filename && names_and_files)
284     {
285       register int i;
286       for (i = 0; names_and_files[i]; i++)
287         {
288           if (strcmp (names_and_files[i]->filename, filename) == 0)
289             return (names_and_files[i]->expansion);
290         }
291     }
292   return (char *)NULL;;
293 }
294
295 /* Add a filename and its expansion to our list. */
296 static void
297 remember_info_filename (filename, expansion)
298      char *filename, *expansion;
299 {
300   FILENAME_LIST *new;
301
302   if (names_and_files_index + 2 > names_and_files_slots)
303     {
304       int alloc_size;
305       names_and_files_slots += 10;
306
307       alloc_size = names_and_files_slots * sizeof (FILENAME_LIST *);
308
309       names_and_files =
310         (FILENAME_LIST **) xrealloc (names_and_files, alloc_size);
311     }
312
313   new = (FILENAME_LIST *)xmalloc (sizeof (FILENAME_LIST));
314   new->filename = xstrdup (filename);
315   new->expansion = expansion ? xstrdup (expansion) : (char *)NULL;
316
317   names_and_files[names_and_files_index++] = new;
318   names_and_files[names_and_files_index] = (FILENAME_LIST *)NULL;
319 }
320
321 static void
322 maybe_initialize_infopath ()
323 {
324   if (!infopath_size)
325     {
326       infopath = (char *)
327         xmalloc (infopath_size = (1 + strlen (DEFAULT_INFOPATH)));
328
329       strcpy (infopath, DEFAULT_INFOPATH);
330     }
331 }
332
333 /* Add PATH to the list of paths found in INFOPATH.  2nd argument says
334    whether to put PATH at the front or end of INFOPATH. */
335 void
336 info_add_path (path, where)
337      char *path;
338      int where;
339 {
340   int len;
341
342   if (!infopath)
343     {
344       infopath = (char *)xmalloc (infopath_size = 200 + strlen (path));
345       infopath[0] = '\0';
346     }
347
348   len = strlen (path) + strlen (infopath);
349
350   if (len + 2 >= infopath_size)
351     infopath = (char *)xrealloc (infopath, (infopath_size += (2 * len) + 2));
352
353   if (!*infopath)
354     strcpy (infopath, path);
355   else if (where == INFOPATH_APPEND)
356     {
357       strcat (infopath, ":");
358       strcat (infopath, path);
359     }
360   else if (where == INFOPATH_PREPEND)
361     {
362       char *temp = xstrdup (infopath);
363       strcpy (infopath, path);
364       strcat (infopath, ":");
365       strcat (infopath, temp);
366       free (temp);
367     }
368 }
369
370 /* Make INFOPATH have absolutely nothing in it. */
371 void
372 zap_infopath ()
373 {
374   if (infopath)
375     free (infopath);
376
377   infopath = (char *)NULL;
378   infopath_size = 0;
379 }
380
381 /* Read the contents of PATHNAME, returning a buffer with the contents of
382    that file in it, and returning the size of that buffer in FILESIZE.
383    FINFO is a stat struct which has already been filled in by the caller.
384    If the file cannot be read, return a NULL pointer. */
385 char *
386 filesys_read_info_file (pathname, filesize, finfo)
387      char *pathname;
388      long *filesize;
389      struct stat *finfo;
390 {
391   long st_size;
392
393   *filesize = filesys_error_number = 0;
394
395   if (compressed_filename_p (pathname))
396     return (filesys_read_compressed (pathname, filesize, finfo));
397   else
398     {
399       int descriptor;
400       char *contents;
401
402       descriptor = open (pathname, O_RDONLY, 0666);
403
404       /* If the file couldn't be opened, give up. */
405       if (descriptor < 0)
406         {
407           filesys_error_number = errno;
408           return ((char *)NULL);
409         }
410
411       /* Try to read the contents of this file. */
412       st_size = (long) finfo->st_size;
413       contents = (char *)xmalloc (1 + st_size);
414       if ((read (descriptor, contents, st_size)) != st_size)
415         {
416           filesys_error_number = errno;
417           close (descriptor);
418           free (contents);
419           return ((char *)NULL);
420         }
421
422       close (descriptor);
423
424       *filesize = st_size;
425       return (contents);
426     }
427 }
428
429 /* Typically, pipe buffers are 4k. */
430 #define BASIC_PIPE_BUFFER (4 * 1024)
431
432 /* We use some large multiple of that. */
433 #define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER)
434
435 char *
436 filesys_read_compressed (pathname, filesize, finfo)
437      char *pathname;
438      long *filesize;
439      struct stat *finfo;
440 {
441   FILE *stream;
442   char *command, *decompressor;
443   char *contents = (char *)NULL;
444
445   *filesize = filesys_error_number = 0;
446
447   decompressor = filesys_decompressor_for_file (pathname);
448
449   if (!decompressor)
450     return ((char *)NULL);
451
452   command = (char *)xmalloc (10 + strlen (pathname) + strlen (decompressor));
453   sprintf (command, "%s < %s", decompressor, pathname);
454
455 #if !defined (BUILDING_LIBRARY)
456   if (info_windows_initialized_p)
457     {
458       char *temp;
459
460       temp = (char *)xmalloc (5 + strlen (command));
461       sprintf (temp, "%s...", command);
462       message_in_echo_area ("%s", temp);
463       free (temp);
464     }
465 #endif /* !BUILDING_LIBRARY */
466
467   stream = popen (command, "r");
468   free (command);
469
470   /* Read chunks from this file until there are none left to read. */
471   if (stream)
472     {
473       int offset, size;
474       char *chunk;
475     
476       offset = size = 0;
477       chunk = (char *)xmalloc (FILESYS_PIPE_BUFFER_SIZE);
478
479       while (1)
480         {
481           int bytes_read;
482
483           bytes_read = fread (chunk, 1, FILESYS_PIPE_BUFFER_SIZE, stream);
484
485           if (bytes_read + offset >= size)
486             contents = (char *)xrealloc
487               (contents, size += (2 * FILESYS_PIPE_BUFFER_SIZE));
488
489           memcpy (contents + offset, chunk, bytes_read);
490           offset += bytes_read;
491           if (bytes_read != FILESYS_PIPE_BUFFER_SIZE)
492             break;
493         }
494
495       free (chunk);
496       pclose (stream);
497       contents = (char *)xrealloc (contents, offset + 1);
498       *filesize = offset;
499     }
500   else
501     {
502       filesys_error_number = errno;
503     }
504
505 #if !defined (BUILDING_LIBARARY)
506   if (info_windows_initialized_p)
507     unmessage_in_echo_area ();
508 #endif /* !BUILDING_LIBRARY */
509   return (contents);
510 }
511
512 /* Return non-zero if FILENAME belongs to a compressed file. */
513 int
514 compressed_filename_p (filename)
515      char *filename;
516 {
517   char *decompressor;
518
519   /* Find the final extension of this filename, and see if it matches one
520      of our known ones. */
521   decompressor = filesys_decompressor_for_file (filename);
522
523   if (decompressor)
524     return (1);
525   else
526     return (0);
527 }
528
529 /* Return the command string that would be used to decompress FILENAME. */
530 char *
531 filesys_decompressor_for_file (filename)
532      char *filename;
533 {
534   register int i;
535   char *extension = (char *)NULL;
536
537   /* Find the final extension of FILENAME, and see if it appears in our
538      list of known compression extensions. */
539   for (i = strlen (filename) - 1; i > 0; i--)
540     if (filename[i] == '.')
541       {
542         extension = filename + i;
543         break;
544       }
545
546   if (!extension)
547     return ((char *)NULL);
548
549   for (i = 0; compress_suffixes[i].suffix; i++)
550     if (strcmp (extension, compress_suffixes[i].suffix) == 0)
551       return (compress_suffixes[i].decompressor);
552
553   return ((char *)NULL);
554 }
555
556 /* The number of the most recent file system error. */
557 int filesys_error_number = 0;
558
559 /* A function which returns a pointer to a static buffer containing
560    an error message for FILENAME and ERROR_NUM. */
561 static char *errmsg_buf = (char *)NULL;
562 static int errmsg_buf_size = 0;
563
564 char *
565 filesys_error_string (filename, error_num)
566      char *filename;
567      int error_num;
568 {
569   int len;
570   char *result;
571
572   if (error_num == 0)
573     return ((char *)NULL);
574
575   result = strerror (error_num);
576
577   len = 4 + strlen (filename) + strlen (result);
578   if (len >= errmsg_buf_size)
579     errmsg_buf = (char *)xrealloc (errmsg_buf, (errmsg_buf_size = 2 + len));
580
581   sprintf (errmsg_buf, "%s: %s", filename, result);
582   return (errmsg_buf);
583 }
584