3 /* filesys.c -- filesystem specific functions.
4 Id: filesys.c,v 1.6 2004/07/30 17:17:40 karl Exp
6 Copyright (C) 1993, 1997, 1998, 2000, 2002, 2003, 2004 Free Software
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 Written by Brian Fox (bfox@ai.mit.edu). */
30 /* Local to this file. */
31 static char *info_file_in_path (char *filename
, char *path
);
32 static char *lookup_info_filename (char *filename
);
33 static char *info_absolute_file (char *fname
);
35 static void remember_info_filename (char *filename
, char *expansion
);
36 static void maybe_initialize_infopath (void);
44 static char *info_suffixes
[] = {
48 ".inf", /* 8+3 file on filesystem which supports long file names */
50 /* 8+3 file names strike again... */
51 ".in", /* for .inz, .igz etc. */
58 static COMPRESSION_ALIST compress_suffixes
[] = {
60 { ".bz2", "bunzip2" },
62 { ".Z", "uncompress" },
68 { (char *)NULL
, (char *)NULL
}
71 /* The path on which we look for info files. You can initialize this
72 from the environment variable INFOPATH if there is one, or you can
73 call info_add_path () to add paths to the beginning or end of it.
74 You can call zap_infopath () to make the path go away. */
75 char *infopath
= (char *)NULL
;
76 static int infopath_size
= 0;
78 /* Expand the filename in PARTIAL to make a real name for this operating
79 system. This looks in INFO_PATHS in order to find the correct file.
80 If it can't find the file, it returns NULL. */
81 static char *local_temp_filename
= (char *)NULL
;
82 static int local_temp_filename_size
= 0;
85 info_find_fullpath (char *partial
)
87 int initial_character
;
90 filesys_error_number
= 0;
92 maybe_initialize_infopath ();
94 if (partial
&& (initial_character
= *partial
))
98 expansion
= lookup_info_filename (partial
);
103 /* If we have the full path to this file, we still may have to add
104 various extensions to it. I guess we have to stat this file
106 if (IS_ABSOLUTE (partial
))
107 temp
= info_absolute_file (partial
);
108 else if (initial_character
== '~')
110 expansion
= tilde_expand_word (partial
);
111 if (IS_ABSOLUTE (expansion
))
113 temp
= info_absolute_file (expansion
);
119 else if (initial_character
== '.' &&
120 (IS_SLASH (partial
[1]) ||
121 (partial
[1] == '.' && IS_SLASH (partial
[2]))))
123 if (local_temp_filename_size
< 1024)
124 local_temp_filename
= (char *)xrealloc
125 (local_temp_filename
, (local_temp_filename_size
= 1024));
126 #if defined (HAVE_GETCWD)
127 if (!getcwd (local_temp_filename
, local_temp_filename_size
))
128 #else /* !HAVE_GETCWD */
129 if (!getwd (local_temp_filename
))
130 #endif /* !HAVE_GETCWD */
132 filesys_error_number
= errno
;
136 strcat (local_temp_filename
, "/");
137 strcat (local_temp_filename
, partial
);
138 temp
= info_absolute_file (local_temp_filename
); /* try extensions */
140 partial
= local_temp_filename
;
143 temp
= info_file_in_path (partial
, infopath
);
147 remember_info_filename (partial
, temp
);
148 if (strlen (temp
) > (unsigned int) local_temp_filename_size
)
149 local_temp_filename
= (char *) xrealloc
150 (local_temp_filename
,
151 (local_temp_filename_size
= (50 + strlen (temp
))));
152 strcpy (local_temp_filename
, temp
);
154 return (local_temp_filename
);
160 /* Scan the list of directories in PATH looking for FILENAME. If we find
161 one that is a regular file, return it as a new string. Otherwise, return
164 info_file_in_path (char *filename
, char *path
)
168 int statable
, dirname_index
;
170 /* Reject ridiculous cases up front, to prevent infinite recursion
171 later on. E.g., someone might say "info '(.)foo'"... */
172 if (!*filename
|| STREQ (filename
, ".") || STREQ (filename
, ".."))
177 while ((temp_dirname
= extract_colon_unit (path
, &dirname_index
)))
179 register int i
, pre_suffix_length
;
182 /* Expand a leading tilde if one is present. */
183 if (*temp_dirname
== '~')
185 char *expanded_dirname
;
187 expanded_dirname
= tilde_expand_word (temp_dirname
);
189 temp_dirname
= expanded_dirname
;
192 temp
= (char *)xmalloc (30 + strlen (temp_dirname
) + strlen (filename
));
193 strcpy (temp
, temp_dirname
);
194 if (!IS_SLASH (temp
[(strlen (temp
)) - 1]))
196 strcat (temp
, filename
);
198 pre_suffix_length
= strlen (temp
);
202 for (i
= 0; info_suffixes
[i
]; i
++)
204 strcpy (temp
+ pre_suffix_length
, info_suffixes
[i
]);
206 statable
= (stat (temp
, &finfo
) == 0);
208 /* If we have found a regular file, then use that. Else, if we
209 have found a directory, look in that directory for this file. */
212 if (S_ISREG (finfo
.st_mode
))
216 else if (S_ISDIR (finfo
.st_mode
))
218 char *newpath
, *filename_only
, *newtemp
;
220 newpath
= xstrdup (temp
);
221 filename_only
= filename_non_directory (filename
);
222 newtemp
= info_file_in_path (filename_only
, newpath
);
234 /* Add various compression suffixes to the name to see if
235 the file is present in compressed format. */
236 register int j
, pre_compress_suffix_length
;
238 pre_compress_suffix_length
= strlen (temp
);
240 for (j
= 0; compress_suffixes
[j
].suffix
; j
++)
242 strcpy (temp
+ pre_compress_suffix_length
,
243 compress_suffixes
[j
].suffix
);
245 statable
= (stat (temp
, &finfo
) == 0);
246 if (statable
&& (S_ISREG (finfo
.st_mode
)))
253 return ((char *)NULL
);
256 /* Assume FNAME is an absolute file name, and check whether it is
257 a regular file. If it is, return it as a new string; otherwise
258 return a NULL pointer. We do it by taking the file name apart
259 into its directory and basename parts, and calling info_file_in_path.*/
261 info_absolute_file (char *fname
)
263 char *containing_dir
= xstrdup (fname
);
264 char *base
= filename_non_directory (containing_dir
);
266 if (base
> containing_dir
)
269 return info_file_in_path (filename_non_directory (fname
), containing_dir
);
273 /* Given a string containing units of information separated by the
274 PATH_SEP character, return the next one after IDX, or NULL if there
275 are no more. Advance IDX to the character after the colon. */
278 extract_colon_unit (char *string
, int *idx
)
280 unsigned int i
= (unsigned int) *idx
;
281 unsigned int start
= i
;
283 if (!string
|| i
>= strlen (string
))
286 if (!string
[i
]) /* end of string */
289 /* Advance to next PATH_SEP. */
290 while (string
[i
] && string
[i
] != PATH_SEP
[0])
294 char *value
= xmalloc ((i
- start
) + 1);
295 strncpy (value
, &string
[start
], (i
- start
));
296 value
[i
- start
] = 0;
298 i
++; /* move past PATH_SEP */
304 /* A structure which associates a filename with its expansion. */
311 /* An array of remembered arguments and results. */
312 static FILENAME_LIST
**names_and_files
= (FILENAME_LIST
**)NULL
;
313 static int names_and_files_index
= 0;
314 static int names_and_files_slots
= 0;
316 /* Find the result for having already called info_find_fullpath () with
319 lookup_info_filename (char *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 (char *filename
, char *expansion
)
339 if (names_and_files_index
+ 2 > names_and_files_slots
)
342 names_and_files_slots
+= 10;
344 alloc_size
= names_and_files_slots
* sizeof (FILENAME_LIST
*);
347 (FILENAME_LIST
**) xrealloc (names_and_files
, alloc_size
);
350 new = (FILENAME_LIST
*)xmalloc (sizeof (FILENAME_LIST
));
351 new->filename
= xstrdup (filename
);
352 new->expansion
= expansion
? xstrdup (expansion
) : (char *)NULL
;
354 names_and_files
[names_and_files_index
++] = new;
355 names_and_files
[names_and_files_index
] = (FILENAME_LIST
*)NULL
;
359 maybe_initialize_infopath (void)
364 xmalloc (infopath_size
= (1 + strlen (DEFAULT_INFOPATH
)));
366 strcpy (infopath
, DEFAULT_INFOPATH
);
370 /* Add PATH to the list of paths found in INFOPATH. 2nd argument says
371 whether to put PATH at the front or end of INFOPATH. */
373 info_add_path (char *path
, int where
)
379 infopath
= (char *)xmalloc (infopath_size
= 200 + strlen (path
));
383 len
= strlen (path
) + strlen (infopath
);
385 if (len
+ 2 >= infopath_size
)
386 infopath
= (char *)xrealloc (infopath
, (infopath_size
+= (2 * len
) + 2));
389 strcpy (infopath
, path
);
390 else if (where
== INFOPATH_APPEND
)
392 strcat (infopath
, PATH_SEP
);
393 strcat (infopath
, path
);
395 else if (where
== INFOPATH_PREPEND
)
397 char *temp
= xstrdup (infopath
);
398 strcpy (infopath
, path
);
399 strcat (infopath
, PATH_SEP
);
400 strcat (infopath
, temp
);
405 /* Make INFOPATH have absolutely nothing in it. */
412 infopath
= (char *)NULL
;
416 /* Given a chunk of text and its length, convert all CRLF pairs at every
417 end-of-line into a single Newline character. Return the length of
420 This is required because the rest of code is too entrenched in having
421 a single newline at each EOL; in particular, searching for various
422 Info headers and cookies can become extremely tricky if that assumption
425 FIXME: this could also support Mac-style text files with a single CR
426 at the EOL, but what about random CR characters in non-Mac files? Can
427 we afford converting them into newlines as well? Maybe implement some
428 heuristics here, like in Emacs 20.
430 FIXME: is it a good idea to show the EOL type on the modeline? */
432 convert_eols (char *text
, long int textlen
)
434 register char *s
= text
;
435 register char *d
= text
;
439 if (*s
== '\r' && textlen
&& s
[1] == '\n')
447 return (long)(d
- text
);
450 /* Read the contents of PATHNAME, returning a buffer with the contents of
451 that file in it, and returning the size of that buffer in FILESIZE.
452 FINFO is a stat struct which has already been filled in by the caller.
453 If the file turns out to be compressed, set IS_COMPRESSED to non-zero.
454 If the file cannot be read, return a NULL pointer. */
456 filesys_read_info_file (char *pathname
, long int *filesize
,
457 struct stat
*finfo
, int *is_compressed
)
461 *filesize
= filesys_error_number
= 0;
463 if (compressed_filename_p (pathname
))
466 return (filesys_read_compressed (pathname
, filesize
));
474 descriptor
= open (pathname
, O_RDONLY
| O_BINARY
, 0666);
476 /* If the file couldn't be opened, give up. */
479 filesys_error_number
= errno
;
480 return ((char *)NULL
);
483 /* Try to read the contents of this file. */
484 st_size
= (long) finfo
->st_size
;
485 contents
= (char *)xmalloc (1 + st_size
);
486 if ((read (descriptor
, contents
, st_size
)) != st_size
)
488 filesys_error_number
= errno
;
491 return ((char *)NULL
);
496 /* Convert any DOS-style CRLF EOLs into Unix-style NL.
497 Seems like a good idea to have even on Unix, in case the Info
498 files are coming from some Windows system across a network. */
499 *filesize
= convert_eols (contents
, st_size
);
501 /* EOL conversion can shrink the text quite a bit. We don't
502 want to waste storage. */
503 if (*filesize
< st_size
)
504 contents
= (char *)xrealloc (contents
, 1 + *filesize
);
505 contents
[*filesize
] = '\0';
511 /* Typically, pipe buffers are 4k. */
512 #define BASIC_PIPE_BUFFER (4 * 1024)
514 /* We use some large multiple of that. */
515 #define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER)
518 filesys_read_compressed (char *pathname
, long int *filesize
)
521 char *command
, *decompressor
;
522 char *contents
= (char *)NULL
;
524 *filesize
= filesys_error_number
= 0;
526 decompressor
= filesys_decompressor_for_file (pathname
);
529 return ((char *)NULL
);
531 command
= (char *)xmalloc (15 + strlen (pathname
) + strlen (decompressor
));
532 /* Explicit .exe suffix makes the diagnostics of `popen'
533 better on systems where COMMAND.COM is the stock shell. */
534 sprintf (command
, "%s%s < %s",
535 decompressor
, STRIP_DOT_EXE
? ".exe" : "", pathname
);
537 #if !defined (BUILDING_LIBRARY)
538 if (info_windows_initialized_p
)
542 temp
= (char *)xmalloc (5 + strlen (command
));
543 sprintf (temp
, "%s...", command
);
544 message_in_echo_area ("%s", temp
, NULL
);
547 #endif /* !BUILDING_LIBRARY */
549 stream
= popen (command
, FOPEN_RBIN
);
552 /* Read chunks from this file until there are none left to read. */
559 chunk
= (char *)xmalloc (FILESYS_PIPE_BUFFER_SIZE
);
565 bytes_read
= fread (chunk
, 1, FILESYS_PIPE_BUFFER_SIZE
, stream
);
567 if (bytes_read
+ offset
>= size
)
568 contents
= (char *)xrealloc
569 (contents
, size
+= (2 * FILESYS_PIPE_BUFFER_SIZE
));
571 memcpy (contents
+ offset
, chunk
, bytes_read
);
572 offset
+= bytes_read
;
573 if (bytes_read
!= FILESYS_PIPE_BUFFER_SIZE
)
578 if (pclose (stream
) == -1)
582 contents
= (char *)NULL
;
583 filesys_error_number
= errno
;
587 *filesize
= convert_eols (contents
, offset
);
588 contents
= (char *)xrealloc (contents
, 1 + *filesize
);
589 contents
[*filesize
] = '\0';
594 filesys_error_number
= errno
;
597 #if !defined (BUILDING_LIBARARY)
598 if (info_windows_initialized_p
)
599 unmessage_in_echo_area ();
600 #endif /* !BUILDING_LIBRARY */
604 /* Return non-zero if FILENAME belongs to a compressed file. */
606 compressed_filename_p (char *filename
)
610 /* Find the final extension of this filename, and see if it matches one
611 of our known ones. */
612 decompressor
= filesys_decompressor_for_file (filename
);
620 /* Return the command string that would be used to decompress FILENAME. */
622 filesys_decompressor_for_file (char *filename
)
625 char *extension
= (char *)NULL
;
627 /* Find the final extension of FILENAME, and see if it appears in our
628 list of known compression extensions. */
629 for (i
= strlen (filename
) - 1; i
> 0; i
--)
630 if (filename
[i
] == '.')
632 extension
= filename
+ i
;
637 return ((char *)NULL
);
639 for (i
= 0; compress_suffixes
[i
].suffix
; i
++)
640 if (FILENAME_CMP (extension
, compress_suffixes
[i
].suffix
) == 0)
641 return (compress_suffixes
[i
].decompressor
);
643 #if defined (__MSDOS__)
644 /* If no other suffix matched, allow any extension which ends
645 with `z' to be decompressed by gunzip. Due to limited 8+3 DOS
646 file namespace, we can expect many such cases, and supporting
647 every weird suffix thus produced would be a pain. */
648 if (extension
[strlen (extension
) - 1] == 'z' ||
649 extension
[strlen (extension
) - 1] == 'Z')
653 return ((char *)NULL
);
656 /* The number of the most recent file system error. */
657 int filesys_error_number
= 0;
659 /* A function which returns a pointer to a static buffer containing
660 an error message for FILENAME and ERROR_NUM. */
661 static char *errmsg_buf
= (char *)NULL
;
662 static int errmsg_buf_size
= 0;
665 filesys_error_string (char *filename
, int error_num
)
671 return ((char *)NULL
);
673 result
= strerror (error_num
);
675 len
= 4 + strlen (filename
) + strlen (result
);
676 if (len
>= errmsg_buf_size
)
677 errmsg_buf
= (char *)xrealloc (errmsg_buf
, (errmsg_buf_size
= 2 + len
));
679 sprintf (errmsg_buf
, "%s: %s", filename
, result
);
684 /* Check for "dir" with all the possible info and compression suffixes,
688 is_dir_name (char *filename
)
692 for (i
= 0; info_suffixes
[i
]; i
++)
696 strcpy (trydir
, "dir");
697 strcat (trydir
, info_suffixes
[i
]);
699 if (strcasecmp (filename
, trydir
) == 0)
702 for (c
= 0; compress_suffixes
[c
].suffix
; c
++)
704 char dir_compressed
[50]; /* can be short */
705 strcpy (dir_compressed
, trydir
);
706 strcat (dir_compressed
, compress_suffixes
[c
].suffix
);
707 if (strcasecmp (filename
, dir_compressed
) == 0)