1 /* $OpenBSD: filename.c,v 1.2 2001/01/29 01:58:01 niklas Exp $ */ 2 3 /* 4 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice in the documentation and/or other materials provided with 14 * the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 22 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 30 /* 31 * Routines to mess around with filenames (and files). 32 * Much of this is very OS dependent. 33 */ 34 35 #include "less.h" 36 #if MSOFTC 37 #include <dos.h> 38 #endif 39 40 extern int force_open; 41 extern IFILE curr_ifile; 42 extern IFILE old_ifile; 43 44 /* 45 * Return a pathname that points to a specified file in a specified directory. 46 * Return NULL if the file does not exist in the directory. 47 */ 48 static char * 49 dirfile(dirname, filename) 50 char *dirname; 51 char *filename; 52 { 53 char *pathname; 54 int f; 55 56 if (dirname == NULL || *dirname == '\0') 57 return (NULL); 58 /* 59 * Construct the full pathname. 60 */ 61 pathname = (char *) calloc(strlen(dirname) + strlen(filename) + 2, 62 sizeof(char)); 63 if (pathname == NULL) 64 return (NULL); 65 #if MSOFTC || OS2 66 sprintf(pathname, "%s\\%s", dirname, filename); 67 #else 68 sprintf(pathname, "%s/%s", dirname, filename); 69 #endif 70 /* 71 * Make sure the file exists. 72 */ 73 f = open(pathname, OPEN_READ); 74 if (f < 0) 75 { 76 free(pathname); 77 pathname = NULL; 78 } else 79 { 80 close (f); 81 } 82 return (pathname); 83 } 84 85 /* 86 * Return the full pathname of the given file in the "home directory". 87 */ 88 public char * 89 homefile(filename) 90 char *filename; 91 { 92 register char *pathname; 93 94 /* 95 * Try $HOME/filename. 96 */ 97 pathname = dirfile(getenv("HOME"), filename); 98 if (pathname != NULL) 99 return (pathname); 100 #if OS2 101 /* 102 * Try $INIT/filename. 103 */ 104 pathname = dirfile(getenv("INIT"), filename); 105 if (pathname != NULL) 106 return (pathname); 107 #endif 108 #if MSOFTC || OS2 109 /* 110 * Look for the file anywhere on search path. 111 */ 112 pathname = (char *) calloc(_MAX_PATH, sizeof(char)); 113 _searchenv(filename, "PATH", pathname); 114 if (*pathname != '\0') 115 return (pathname); 116 free(pathname); 117 #endif 118 return (NULL); 119 } 120 121 /* 122 * Find out where the help file is. 123 */ 124 public char * 125 find_helpfile() 126 { 127 register char *helpfile; 128 129 if ((helpfile = getenv("LESSHELP")) != NULL) 130 return (save(helpfile)); 131 #if MSOFTC || OS2 132 return (homefile(HELPFILE)); 133 #else 134 return (save(HELPFILE)); 135 #endif 136 } 137 138 /* 139 * Expand a string, substituting any "%" with the current filename, 140 * and any "#" with the previous filename. 141 * {{ This is a lot of work just to support % and #. }} 142 */ 143 public char * 144 fexpand(s) 145 char *s; 146 { 147 register char *fr, *to; 148 register int n; 149 register char *e; 150 151 /* 152 * Make one pass to see how big a buffer we 153 * need to allocate for the expanded string. 154 */ 155 n = 0; 156 for (fr = s; *fr != '\0'; fr++) 157 { 158 switch (*fr) 159 { 160 case '%': 161 if (curr_ifile == NULL_IFILE) 162 { 163 /* error("No current file", NULL_PARG); */ 164 return (save(s)); 165 } 166 n += strlen(get_filename(curr_ifile)); 167 break; 168 case '#': 169 if (old_ifile == NULL_IFILE) 170 { 171 /* error("No previous file", NULL_PARG); */ 172 return (save(s)); 173 } 174 n += strlen(get_filename(old_ifile)); 175 break; 176 default: 177 n++; 178 break; 179 } 180 } 181 182 e = (char *) ecalloc(n+1, sizeof(char)); 183 184 /* 185 * Now copy the string, expanding any "%" or "#". 186 */ 187 to = e; 188 for (fr = s; *fr != '\0'; fr++) 189 { 190 switch (*fr) 191 { 192 case '%': 193 strcpy(to, get_filename(curr_ifile)); 194 to += strlen(to); 195 break; 196 case '#': 197 strcpy(to, get_filename(old_ifile)); 198 to += strlen(to); 199 break; 200 default: 201 *to++ = *fr; 202 break; 203 } 204 } 205 *to = '\0'; 206 return (e); 207 } 208 209 #if TAB_COMPLETE_FILENAME 210 211 /* 212 * Return a blank-separated list of filenames which "complete" 213 * the given string. 214 */ 215 public char * 216 fcomplete(s) 217 char *s; 218 { 219 char *fpat; 220 /* 221 * Complete the filename "s" by globbing "s*". 222 */ 223 #if MSOFTC 224 /* 225 * But in DOS, we have to glob "s*.*". 226 * But if the final component of the filename already has 227 * a dot in it, just do "s*". 228 * (Thus, "FILE" is globbed as "FILE*.*", 229 * but "FILE.A" is globbed as "FILE.A*"). 230 */ 231 char *slash; 232 for (slash = s+strlen(s)-1; slash > s; slash--) 233 if (*slash == '/' || *slash == '\\') 234 break; 235 fpat = (char *) ecalloc(strlen(s)+4, sizeof(char)); 236 if (strchr(slash, '.') == NULL) 237 sprintf(fpat, "%s*.*", s); 238 else 239 sprintf(fpat, "%s*", s); 240 #else 241 fpat = (char *) ecalloc(strlen(s)+2, sizeof(char)); 242 sprintf(fpat, "%s*", s); 243 #endif 244 s = glob(fpat); 245 if (strcmp(s,fpat) == 0) 246 { 247 /* 248 * The filename didn't expand. 249 */ 250 free(s); 251 s = NULL; 252 } 253 free(fpat); 254 return (s); 255 } 256 #endif 257 258 /* 259 * Try to determine if a file is "binary". 260 * This is just a guess, and we need not try too hard to make it accurate. 261 */ 262 public int 263 bin_file(f) 264 int f; 265 { 266 int i; 267 int n; 268 unsigned char data[64]; 269 270 if (!seekable(f)) 271 return (0); 272 if (lseek(f, (off_t)0, 0) == BAD_LSEEK) 273 return (0); 274 n = read(f, data, sizeof(data)); 275 for (i = 0; i < n; i++) 276 if (binary_char(data[i])) 277 return (1); 278 return (0); 279 } 280 281 /* 282 * Try to determine the size of a file by seeking to the end. 283 */ 284 static POSITION 285 seek_filesize(f) 286 int f; 287 { 288 off_t spos; 289 290 spos = lseek(f, (off_t)0, 2); 291 if (spos == BAD_LSEEK) 292 return (NULL_POSITION); 293 return ((POSITION) spos); 294 } 295 296 #if GLOB 297 298 FILE *popen(); 299 300 /* 301 * Read a string from a file. 302 * Return a pointer to the string in memory. 303 */ 304 static char * 305 readfd(fd) 306 FILE *fd; 307 { 308 int len; 309 int ch; 310 char *buf; 311 char *p; 312 313 /* 314 * Make a guess about how many chars in the string 315 * and allocate a buffer to hold it. 316 */ 317 len = 100; 318 buf = (char *) ecalloc(len, sizeof(char)); 319 for (p = buf; ; p++) 320 { 321 if ((ch = getc(fd)) == '\n' || ch == EOF) 322 break; 323 if (p - buf >= len-1) 324 { 325 /* 326 * The string is too big to fit in the buffer we have. 327 * Allocate a new buffer, twice as big. 328 */ 329 len *= 2; 330 *p = '\0'; 331 p = (char *) ecalloc(len, sizeof(char)); 332 strcpy(p, buf); 333 free(buf); 334 buf = p; 335 p = buf + strlen(buf); 336 } 337 *p = ch; 338 } 339 *p = '\0'; 340 return (buf); 341 } 342 343 /* 344 * Execute a shell command. 345 * Return a pointer to a pipe connected to the shell command's standard output. 346 */ 347 static FILE * 348 shellcmd(cmd, s1, s2) 349 char *cmd; 350 char *s1; 351 char *s2; 352 { 353 char *scmd; 354 char *scmd2; 355 char *shell; 356 FILE *fd; 357 int len; 358 359 len = strlen(cmd) + 360 (s1 == NULL ? 0 : strlen(s1)) + 361 (s2 == NULL ? 0 : strlen(s2)) + 1; 362 scmd = (char *) ecalloc(len, sizeof(char)); 363 sprintf(scmd, cmd, s1, s2); 364 #if HAVE_SHELL 365 shell = getenv("SHELL"); 366 if (shell != NULL && *shell != '\0') 367 { 368 /* 369 * Read the output of <$SHELL -c "cmd">. 370 */ 371 scmd2 = (char *) ecalloc(strlen(shell) + strlen(scmd) + 7, 372 sizeof(char)); 373 sprintf(scmd2, "%s -c \"%s\"", shell, scmd); 374 free(scmd); 375 scmd = scmd2; 376 } 377 #endif 378 fd = popen(scmd, "r"); 379 free(scmd); 380 return (fd); 381 } 382 383 /* 384 * Expand a filename, doing any shell-level substitutions. 385 */ 386 public char * 387 glob(filename) 388 char *filename; 389 { 390 char *gfilename; 391 392 filename = fexpand(filename); 393 #if OS2 394 { 395 char **list; 396 int cnt; 397 int length; 398 399 list = _fnexplode(filename); 400 if (list == NULL) 401 return (filename); 402 length = 0; 403 for (cnt = 0; list[cnt] != NULL; cnt++) 404 length += strlen(list[cnt]) + 1; 405 gfilename = (char *) ecalloc(length, sizeof(char)); 406 for (cnt = 0; list[cnt] != NULL; cnt++) 407 { 408 strcat(gfilename, list[cnt]); 409 strcat(gfilename, " "); 410 } 411 _fnexplodefree(list); 412 } 413 #else 414 { 415 FILE *fd; 416 417 /* 418 * We get the shell to expand the filename for us by passing 419 * an "echo" command to the shell and reading its output. 420 */ 421 fd = shellcmd("echo %s", filename, (char*)NULL); 422 if (fd == NULL) 423 { 424 /* 425 * Cannot create the pipe. 426 * Just return the original (fexpanded) filename. 427 */ 428 return (filename); 429 } 430 gfilename = readfd(fd); 431 pclose(fd); 432 if (*gfilename == '\0') 433 { 434 free(gfilename); 435 return (filename); 436 } 437 free(filename); 438 } 439 #endif 440 return (gfilename); 441 } 442 443 /* 444 * See if we should open a "replacement file" 445 * instead of the file we're about to open. 446 */ 447 public char * 448 open_altfile(filename, pf, pfd) 449 char *filename; 450 int *pf; 451 void **pfd; 452 { 453 char *lessopen; 454 char *gfilename; 455 int returnfd = 0; 456 FILE *fd; 457 458 ch_ungetchar(-1); 459 if ((lessopen = getenv("LESSOPEN")) == NULL) 460 return (NULL); 461 if (strcmp(filename, "-") == 0) 462 return (NULL); 463 if (*lessopen == '|') 464 { 465 /* 466 * If LESSOPEN starts with a |, it indicates 467 * a "pipe preprocessor". 468 */ 469 lessopen++; 470 returnfd = 1; 471 } 472 fd = shellcmd(lessopen, filename, (char*)NULL); 473 if (fd == NULL) 474 { 475 /* 476 * Cannot create the pipe. 477 */ 478 return (NULL); 479 } 480 if (returnfd) 481 { 482 #if HAVE_FILENO 483 int f; 484 char c; 485 486 /* 487 * Read one char to see if the pipe will produce any data. 488 * If it does, push the char back on the pipe. 489 */ 490 f = fileno(fd); 491 if (read(f, &c, 1) != 1) 492 { 493 /* 494 * Pipe is empty. This means there is no alt file. 495 */ 496 pclose(fd); 497 return (NULL); 498 } 499 ch_ungetchar(c); 500 *pfd = (void *) fd; 501 *pf = f; 502 return (save("-")); 503 #else 504 error("LESSOPEN pipe is not supported", NULL_PARG); 505 return (NULL); 506 #endif 507 } 508 gfilename = readfd(fd); 509 pclose(fd); 510 if (*gfilename == '\0') 511 /* 512 * Pipe is empty. This means there is no alt file. 513 */ 514 return (NULL); 515 return (gfilename); 516 } 517 518 /* 519 * Close a replacement file. 520 */ 521 public void 522 close_altfile(altfilename, filename, pipefd) 523 char *altfilename; 524 char *filename; 525 void *pipefd; 526 { 527 char *lessclose; 528 FILE *fd; 529 530 if (pipefd != NULL) 531 pclose((FILE*) pipefd); 532 if ((lessclose = getenv("LESSCLOSE")) == NULL) 533 return; 534 fd = shellcmd(lessclose, filename, altfilename); 535 pclose(fd); 536 } 537 538 #else 539 #if MSOFTC 540 541 public char * 542 glob(filename) 543 char *filename; 544 { 545 register char *gfilename; 546 register char *p; 547 register int len; 548 register int n; 549 struct find_t fnd; 550 char drive[_MAX_DRIVE]; 551 char dir[_MAX_DIR]; 552 char fname[_MAX_FNAME]; 553 char ext[_MAX_EXT]; 554 555 filename = fexpand(filename); 556 if (_dos_findfirst(filename, ~0, &fnd) != 0) 557 return (filename); 558 559 _splitpath(filename, drive, dir, fname, ext); 560 len = 100; 561 gfilename = (char *) ecalloc(len, sizeof(char)); 562 p = gfilename; 563 do { 564 n = strlen(drive) + strlen(dir) + strlen(fnd.name); 565 while (p - gfilename + n+2 >= len) 566 { 567 len *= 2; 568 *p = '\0'; 569 p = (char *) ecalloc(len, sizeof(char)); 570 strcpy(p, gfilename); 571 free(gfilename); 572 gfilename = p; 573 p = gfilename + strlen(gfilename); 574 } 575 sprintf(p, "%s%s%s", drive, dir, fnd.name); 576 p += n; 577 *p++ = ' '; 578 } while (_dos_findnext(&fnd) == 0); 579 580 *--p = '\0'; 581 return (gfilename); 582 } 583 584 public char * 585 open_altfile(filename) 586 char *filename; 587 { 588 return (NULL); 589 } 590 591 public void 592 close_altfile(altfilename, filename) 593 char *altfilename; 594 char *filename; 595 { 596 } 597 598 #else 599 600 public char * 601 glob(filename) 602 char *filename; 603 { 604 return (fexpand(filename)); 605 } 606 607 608 public char * 609 open_altfile(filename) 610 char *filename; 611 { 612 return (NULL); 613 } 614 615 public void 616 close_altfile(altfilename, filename) 617 char *altfilename; 618 char *filename; 619 { 620 } 621 622 #endif 623 #endif 624 625 626 #if HAVE_STAT 627 628 #include <sys/stat.h> 629 #ifndef S_ISDIR 630 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 631 #endif 632 #ifndef S_ISREG 633 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 634 #endif 635 636 /* 637 * Returns NULL if the file can be opened and 638 * is an ordinary file, otherwise an error message 639 * (if it cannot be opened or is a directory, etc.) 640 */ 641 public char * 642 bad_file(filename) 643 char *filename; 644 { 645 register char *m; 646 struct stat statbuf; 647 648 if (stat(filename, &statbuf) < 0) 649 return (errno_message(filename)); 650 651 if (force_open) 652 return (NULL); 653 654 if (S_ISDIR(statbuf.st_mode)) 655 { 656 static char is_dir[] = " is a directory"; 657 m = (char *) ecalloc(strlen(filename) + sizeof(is_dir), 658 sizeof(char)); 659 strcpy(m, filename); 660 strcat(m, is_dir); 661 return (m); 662 } 663 if (!S_ISREG(statbuf.st_mode)) 664 { 665 static char not_reg[] = " is not a regular file"; 666 m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 667 sizeof(char)); 668 strcpy(m, filename); 669 strcat(m, not_reg); 670 return (m); 671 } 672 673 return (NULL); 674 } 675 676 /* 677 * Return the size of a file, as cheaply as possible. 678 * In Unix, we can stat the file. 679 */ 680 public POSITION 681 filesize(f) 682 int f; 683 { 684 struct stat statbuf; 685 686 if (fstat(f, &statbuf) < 0) 687 /* 688 * Can't stat; try seeking to the end. 689 */ 690 return (seek_filesize(f)); 691 692 return ((POSITION) statbuf.st_size); 693 } 694 695 #else 696 697 /* 698 * If we have no way to find out, just say the file is good. 699 */ 700 public char * 701 bad_file(filename) 702 char *filename; 703 { 704 return (NULL); 705 } 706 707 /* 708 * We can find the file size by seeking. 709 */ 710 public POSITION 711 filesize(f) 712 int f; 713 { 714 return (seek_filesize(f)); 715 } 716 717 #endif 718