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