1 /* filesys.c -- filesystem specific functions.
2 $Id: filesys.c,v 1.15 2002/03/23 20:45:24 karl Exp $
4 Copyright (C) 1993, 97, 98, 2000 Free Software Foundation, Inc.
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)
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.
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.
20 Written by Brian Fox (bfox@ai.mit.edu). */
27 /* Local to this file. */
28 static char *info_file_in_path (), *lookup_info_filename ();
29 static char *info_absolute_file ();
30 static void remember_info_filename (), maybe_initialize_infopath ();
38 static char *info_suffixes[] = {
42 ".inf", /* 8+3 file on filesystem which supports long file names */
44 /* 8+3 file names strike again... */
45 ".in", /* for .inz, .igz etc. */
52 static COMPRESSION_ALIST compress_suffixes[] = {
54 { ".bz2", "bunzip2" },
56 { ".Z", "uncompress" },
62 { (char *)NULL, (char *)NULL }
65 /* The path on which we look for info files. You can initialize this
66 from the environment variable INFOPATH if there is one, or you can
67 call info_add_path () to add paths to the beginning or end of it.
68 You can call zap_infopath () to make the path go away. */
69 char *infopath = (char *)NULL;
70 static int infopath_size = 0;
72 /* Expand the filename in PARTIAL to make a real name for this operating
73 system. This looks in INFO_PATHS in order to find the correct file.
74 If it can't find the file, it returns NULL. */
75 static char *local_temp_filename = (char *)NULL;
76 static int local_temp_filename_size = 0;
79 info_find_fullpath (partial)
82 int initial_character;
85 filesys_error_number = 0;
87 maybe_initialize_infopath ();
89 if (partial && (initial_character = *partial))
93 expansion = lookup_info_filename (partial);
98 /* If we have the full path to this file, we still may have to add
99 various extensions to it. I guess we have to stat this file
101 if (IS_ABSOLUTE (partial))
102 temp = info_absolute_file (partial);
103 else if (initial_character == '~')
105 expansion = tilde_expand_word (partial);
106 if (IS_ABSOLUTE (expansion))
108 temp = info_absolute_file (expansion);
114 else if (initial_character == '.' &&
115 (IS_SLASH (partial[1]) ||
116 (partial[1] == '.' && IS_SLASH (partial[2]))))
118 if (local_temp_filename_size < 1024)
119 local_temp_filename = (char *)xrealloc
120 (local_temp_filename, (local_temp_filename_size = 1024));
121 #if defined (HAVE_GETCWD)
122 if (!getcwd (local_temp_filename, local_temp_filename_size))
123 #else /* !HAVE_GETCWD */
124 if (!getwd (local_temp_filename))
125 #endif /* !HAVE_GETCWD */
127 filesys_error_number = errno;
131 strcat (local_temp_filename, "/");
132 strcat (local_temp_filename, partial);
133 temp = info_absolute_file (local_temp_filename); /* try extensions */
135 partial = local_temp_filename;
138 temp = info_file_in_path (partial, infopath);
142 remember_info_filename (partial, temp);
143 if (strlen (temp) > local_temp_filename_size)
144 local_temp_filename = (char *) xrealloc
145 (local_temp_filename,
146 (local_temp_filename_size = (50 + strlen (temp))));
147 strcpy (local_temp_filename, temp);
149 return (local_temp_filename);
155 /* Scan the list of directories in PATH looking for FILENAME. If we find
156 one that is a regular file, return it as a new string. Otherwise, return
159 info_file_in_path (filename, path)
160 char *filename, *path;
164 int statable, dirname_index;
166 /* Reject ridiculous cases up front, to prevent infinite recursion
167 later on. E.g., someone might say "info '(.)foo'"... */
168 if (!*filename || STREQ (filename, ".") || STREQ (filename, ".."))
173 while ((temp_dirname = extract_colon_unit (path, &dirname_index)))
175 register int i, pre_suffix_length;
178 /* Expand a leading tilde if one is present. */
179 if (*temp_dirname == '~')
181 char *expanded_dirname;
183 expanded_dirname = tilde_expand_word (temp_dirname);
185 temp_dirname = expanded_dirname;
188 temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename));
189 strcpy (temp, temp_dirname);
190 if (!IS_SLASH (temp[(strlen (temp)) - 1]))
192 strcat (temp, filename);
194 pre_suffix_length = strlen (temp);
198 for (i = 0; info_suffixes[i]; i++)
200 strcpy (temp + pre_suffix_length, info_suffixes[i]);
202 statable = (stat (temp, &finfo) == 0);
204 /* If we have found a regular file, then use that. Else, if we
205 have found a directory, look in that directory for this file. */
208 if (S_ISREG (finfo.st_mode))
212 else if (S_ISDIR (finfo.st_mode))
214 char *newpath, *filename_only, *newtemp;
216 newpath = xstrdup (temp);
217 filename_only = filename_non_directory (filename);
218 newtemp = info_file_in_path (filename_only, newpath);
230 /* Add various compression suffixes to the name to see if
231 the file is present in compressed format. */
232 register int j, pre_compress_suffix_length;
234 pre_compress_suffix_length = strlen (temp);
236 for (j = 0; compress_suffixes[j].suffix; j++)
238 strcpy (temp + pre_compress_suffix_length,
239 compress_suffixes[j].suffix);
241 statable = (stat (temp, &finfo) == 0);
242 if (statable && (S_ISREG (finfo.st_mode)))
249 return ((char *)NULL);
252 /* Assume FNAME is an absolute file name, and check whether it is
253 a regular file. If it is, return it as a new string; otherwise
254 return a NULL pointer. We do it by taking the file name apart
255 into its directory and basename parts, and calling info_file_in_path.*/
257 info_absolute_file (fname)
260 char *containing_dir = xstrdup (fname);
261 char *base = filename_non_directory (containing_dir);
263 if (base > containing_dir)
266 return info_file_in_path (filename_non_directory (fname), containing_dir);
269 /* Given a string containing units of information separated by
270 the PATH_SEP character, return the next one pointed to by
271 IDX, or NULL if there are no more.
272 Advance IDX to the character after the colon. */
274 extract_colon_unit (string, idx)
278 register int i, start;
281 if ((i >= strlen (string)) || !string)
282 return ((char *) NULL);
284 while (string[i] && string[i] != PATH_SEP[0])
288 return ((char *) NULL);
294 value = (char *) xmalloc (1 + (i - start));
295 strncpy (value, &string[start], (i - start));
296 value[i - start] = '\0';
304 /* A structure which associates a filename with its expansion. */
310 /* An array of remembered arguments and results. */
311 static FILENAME_LIST **names_and_files = (FILENAME_LIST **)NULL;
312 static int names_and_files_index = 0;
313 static int names_and_files_slots = 0;
315 /* Find the result for having already called info_find_fullpath () with
318 lookup_info_filename (filename)
321 if (filename && names_and_files)
324 for (i = 0; names_and_files[i]; i++)
326 if (FILENAME_CMP (names_and_files[i]->filename, filename) == 0)
327 return (names_and_files[i]->expansion);
330 return (char *)NULL;;
333 /* Add a filename and its expansion to our list. */
335 remember_info_filename (filename, expansion)
336 char *filename, *expansion;
340 if (names_and_files_index + 2 > names_and_files_slots)
343 names_and_files_slots += 10;
345 alloc_size = names_and_files_slots * sizeof (FILENAME_LIST *);
348 (FILENAME_LIST **) xrealloc (names_and_files, alloc_size);
351 new = (FILENAME_LIST *)xmalloc (sizeof (FILENAME_LIST));
352 new->filename = xstrdup (filename);
353 new->expansion = expansion ? xstrdup (expansion) : (char *)NULL;
355 names_and_files[names_and_files_index++] = new;
356 names_and_files[names_and_files_index] = (FILENAME_LIST *)NULL;
360 maybe_initialize_infopath ()
365 xmalloc (infopath_size = (1 + strlen (DEFAULT_INFOPATH)));
367 strcpy (infopath, DEFAULT_INFOPATH);
371 /* Add PATH to the list of paths found in INFOPATH. 2nd argument says
372 whether to put PATH at the front or end of INFOPATH. */
374 info_add_path (path, where)
382 infopath = (char *)xmalloc (infopath_size = 200 + strlen (path));
386 len = strlen (path) + strlen (infopath);
388 if (len + 2 >= infopath_size)
389 infopath = (char *)xrealloc (infopath, (infopath_size += (2 * len) + 2));
392 strcpy (infopath, path);
393 else if (where == INFOPATH_APPEND)
395 strcat (infopath, PATH_SEP);
396 strcat (infopath, path);
398 else if (where == INFOPATH_PREPEND)
400 char *temp = xstrdup (infopath);
401 strcpy (infopath, path);
402 strcat (infopath, PATH_SEP);
403 strcat (infopath, temp);
408 /* Make INFOPATH have absolutely nothing in it. */
415 infopath = (char *)NULL;
419 /* Given a chunk of text and its length, convert all CRLF pairs at every
420 end-of-line into a single Newline character. Return the length of
423 This is required because the rest of code is too entrenched in having
424 a single newline at each EOL; in particular, searching for various
425 Info headers and cookies can become extremely tricky if that assumption
428 FIXME: this could also support Mac-style text files with a single CR
429 at the EOL, but what about random CR characters in non-Mac files? Can
430 we afford converting them into newlines as well? Maybe implement some
431 heuristics here, like in Emacs 20.
433 FIXME: is it a good idea to show the EOL type on the modeline? */
435 convert_eols (text, textlen)
439 register char *s = text;
440 register char *d = text;
444 if (*s == '\r' && textlen && s[1] == '\n')
452 return (long)(d - text);
455 /* Read the contents of PATHNAME, returning a buffer with the contents of
456 that file in it, and returning the size of that buffer in FILESIZE.
457 FINFO is a stat struct which has already been filled in by the caller.
458 If the file turns out to be compressed, set IS_COMPRESSED to non-zero.
459 If the file cannot be read, return a NULL pointer. */
461 filesys_read_info_file (pathname, filesize, finfo, is_compressed)
469 *filesize = filesys_error_number = 0;
471 if (compressed_filename_p (pathname))
474 return (filesys_read_compressed (pathname, filesize, finfo));
482 descriptor = open (pathname, O_RDONLY | O_BINARY, 0666);
484 /* If the file couldn't be opened, give up. */
487 filesys_error_number = errno;
488 return ((char *)NULL);
491 /* Try to read the contents of this file. */
492 st_size = (long) finfo->st_size;
493 contents = (char *)xmalloc (1 + st_size);
494 if ((read (descriptor, contents, st_size)) != st_size)
496 filesys_error_number = errno;
499 return ((char *)NULL);
504 /* Convert any DOS-style CRLF EOLs into Unix-style NL.
505 Seems like a good idea to have even on Unix, in case the Info
506 files are coming from some Windows system across a network. */
507 *filesize = convert_eols (contents, st_size);
509 /* EOL conversion can shrink the text quite a bit. We don't
510 want to waste storage. */
511 if (*filesize < st_size)
512 contents = (char *)xrealloc (contents, 1 + *filesize);
513 contents[*filesize] = '\0';
519 /* Typically, pipe buffers are 4k. */
520 #define BASIC_PIPE_BUFFER (4 * 1024)
522 /* We use some large multiple of that. */
523 #define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER)
526 filesys_read_compressed (pathname, filesize, finfo)
532 char *command, *decompressor;
533 char *contents = (char *)NULL;
535 *filesize = filesys_error_number = 0;
537 decompressor = filesys_decompressor_for_file (pathname);
540 return ((char *)NULL);
542 command = (char *)xmalloc (15 + strlen (pathname) + strlen (decompressor));
543 /* Explicit .exe suffix makes the diagnostics of `popen'
544 better on systems where COMMAND.COM is the stock shell. */
545 sprintf (command, "%s%s < %s",
546 decompressor, STRIP_DOT_EXE ? ".exe" : "", pathname);
548 #if !defined (BUILDING_LIBRARY)
549 if (info_windows_initialized_p)
553 temp = (char *)xmalloc (5 + strlen (command));
554 sprintf (temp, "%s...", command);
555 message_in_echo_area ("%s", temp);
558 #endif /* !BUILDING_LIBRARY */
560 stream = popen (command, FOPEN_RBIN);
563 /* Read chunks from this file until there are none left to read. */
570 chunk = (char *)xmalloc (FILESYS_PIPE_BUFFER_SIZE);
576 bytes_read = fread (chunk, 1, FILESYS_PIPE_BUFFER_SIZE, stream);
578 if (bytes_read + offset >= size)
579 contents = (char *)xrealloc
580 (contents, size += (2 * FILESYS_PIPE_BUFFER_SIZE));
582 memcpy (contents + offset, chunk, bytes_read);
583 offset += bytes_read;
584 if (bytes_read != FILESYS_PIPE_BUFFER_SIZE)
589 if (pclose (stream) == -1)
593 contents = (char *)NULL;
594 filesys_error_number = errno;
598 *filesize = convert_eols (contents, offset);
599 contents = (char *)xrealloc (contents, 1 + *filesize);
600 contents[*filesize] = '\0';
605 filesys_error_number = errno;
608 #if !defined (BUILDING_LIBARARY)
609 if (info_windows_initialized_p)
610 unmessage_in_echo_area ();
611 #endif /* !BUILDING_LIBRARY */
615 /* Return non-zero if FILENAME belongs to a compressed file. */
617 compressed_filename_p (filename)
622 /* Find the final extension of this filename, and see if it matches one
623 of our known ones. */
624 decompressor = filesys_decompressor_for_file (filename);
632 /* Return the command string that would be used to decompress FILENAME. */
634 filesys_decompressor_for_file (filename)
638 char *extension = (char *)NULL;
640 /* Find the final extension of FILENAME, and see if it appears in our
641 list of known compression extensions. */
642 for (i = strlen (filename) - 1; i > 0; i--)
643 if (filename[i] == '.')
645 extension = filename + i;
650 return ((char *)NULL);
652 for (i = 0; compress_suffixes[i].suffix; i++)
653 if (FILENAME_CMP (extension, compress_suffixes[i].suffix) == 0)
654 return (compress_suffixes[i].decompressor);
656 #if defined (__MSDOS__)
657 /* If no other suffix matched, allow any extension which ends
658 with `z' to be decompressed by gunzip. Due to limited 8+3 DOS
659 file namespace, we can expect many such cases, and supporting
660 every weird suffix thus produced would be a pain. */
661 if (extension[strlen (extension) - 1] == 'z' ||
662 extension[strlen (extension) - 1] == 'Z')
666 return ((char *)NULL);
669 /* The number of the most recent file system error. */
670 int filesys_error_number = 0;
672 /* A function which returns a pointer to a static buffer containing
673 an error message for FILENAME and ERROR_NUM. */
674 static char *errmsg_buf = (char *)NULL;
675 static int errmsg_buf_size = 0;
678 filesys_error_string (filename, error_num)
686 return ((char *)NULL);
688 result = strerror (error_num);
690 len = 4 + strlen (filename) + strlen (result);
691 if (len >= errmsg_buf_size)
692 errmsg_buf = (char *)xrealloc (errmsg_buf, (errmsg_buf_size = 2 + len));
694 sprintf (errmsg_buf, "%s: %s", filename, result);
699 /* Check for "dir" with all the possible info and compression suffixes,
703 is_dir_name (filename)
708 for (i = 0; info_suffixes[i]; i++)
712 strcpy (trydir, "dir");
713 strcat (trydir, info_suffixes[i]);
715 if (strcasecmp (filename, trydir) == 0)
718 for (c = 0; compress_suffixes[c].suffix; c++)
720 char dir_compressed[50]; /* can be short */
721 strcpy (dir_compressed, trydir);
722 strcat (dir_compressed, compress_suffixes[c].suffix);
723 if (strcasecmp (filename, dir_compressed) == 0)