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