1 /* 2 * Copyright (C) 1984-2000 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12 /* 13 * Routines to mess around with filenames (and files). 14 * Much of this is very OS dependent. 15 */ 16 17 #include "less.h" 18 #include "lglob.h" 19 #if MSDOS_COMPILER 20 #include <dos.h> 21 #if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER) 22 #include <dir.h> 23 #endif 24 #if MSDOS_COMPILER==DJGPPC 25 #include <glob.h> 26 #include <dir.h> 27 #include <limits.h> 28 #define _MAX_PATH PATH_MAX 29 #endif 30 #endif 31 #ifdef _OSK 32 #include <rbf.h> 33 #ifndef _OSK_MWC32 34 #include <modes.h> 35 #endif 36 #endif 37 38 #if HAVE_STAT 39 #include <sys/stat.h> 40 #ifndef S_ISDIR 41 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 42 #endif 43 #ifndef S_ISREG 44 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 45 #endif 46 #endif 47 48 49 extern int force_open; 50 extern int secure; 51 extern IFILE curr_ifile; 52 extern IFILE old_ifile; 53 #if SPACES_IN_FILENAMES 54 extern char openquote; 55 extern char closequote; 56 #endif 57 58 /* 59 * Remove quotes around a filename. 60 */ 61 public char * 62 unquote_file(str) 63 char *str; 64 { 65 #if SPACES_IN_FILENAMES 66 char *name; 67 char *p; 68 69 if (*str != openquote) 70 return (save(str)); 71 name = (char *) ecalloc(strlen(str), sizeof(char)); 72 strcpy(name, str+1); 73 p = name + strlen(name) - 1; 74 if (*p == closequote) 75 *p = '\0'; 76 return (name); 77 #else 78 return (save(str)); 79 #endif 80 } 81 82 /* 83 * Return a pathname that points to a specified file in a specified directory. 84 * Return NULL if the file does not exist in the directory. 85 */ 86 static char * 87 dirfile(dirname, filename) 88 char *dirname; 89 char *filename; 90 { 91 char *pathname; 92 char *qpathname; 93 int f; 94 95 if (dirname == NULL || *dirname == '\0') 96 return (NULL); 97 /* 98 * Construct the full pathname. 99 */ 100 pathname = (char *) calloc(strlen(dirname) + strlen(filename) + 2, 101 sizeof(char)); 102 if (pathname == NULL) 103 return (NULL); 104 sprintf(pathname, "%s%s%s", dirname, PATHNAME_SEP, filename); 105 /* 106 * Make sure the file exists. 107 */ 108 qpathname = unquote_file(pathname); 109 f = open(qpathname, OPEN_READ); 110 if (f < 0) 111 { 112 free(pathname); 113 pathname = NULL; 114 } else 115 { 116 close(f); 117 } 118 free(qpathname); 119 return (pathname); 120 } 121 122 /* 123 * Return the full pathname of the given file in the "home directory". 124 */ 125 public char * 126 homefile(filename) 127 char *filename; 128 { 129 register char *pathname; 130 131 /* 132 * Try $HOME/filename. 133 */ 134 pathname = dirfile(lgetenv("HOME"), filename); 135 if (pathname != NULL) 136 return (pathname); 137 #if OS2 138 /* 139 * Try $INIT/filename. 140 */ 141 pathname = dirfile(lgetenv("INIT"), filename); 142 if (pathname != NULL) 143 return (pathname); 144 #endif 145 #if MSDOS_COMPILER || OS2 146 /* 147 * Look for the file anywhere on search path. 148 */ 149 pathname = (char *) calloc(_MAX_PATH, sizeof(char)); 150 #if MSDOS_COMPILER==DJGPPC 151 { 152 char *res = searchpath(filename); 153 if (res == 0) 154 *pathname = '\0'; 155 else 156 strcpy(pathname, res); 157 } 158 #else 159 _searchenv(filename, "PATH", pathname); 160 #endif 161 if (*pathname != '\0') 162 return (pathname); 163 free(pathname); 164 #endif 165 return (NULL); 166 } 167 168 /* 169 * Expand a string, substituting any "%" with the current filename, 170 * and any "#" with the previous filename. 171 * But a string of N "%"s is just replaced with N-1 "%"s. 172 * Likewise for a string of N "#"s. 173 * {{ This is a lot of work just to support % and #. }} 174 */ 175 public char * 176 fexpand(s) 177 char *s; 178 { 179 register char *fr, *to; 180 register int n; 181 register char *e; 182 IFILE ifile; 183 184 #define fchar_ifile(c) \ 185 ((c) == '%' ? curr_ifile : \ 186 (c) == '#' ? old_ifile : NULL_IFILE) 187 188 /* 189 * Make one pass to see how big a buffer we 190 * need to allocate for the expanded string. 191 */ 192 n = 0; 193 for (fr = s; *fr != '\0'; fr++) 194 { 195 switch (*fr) 196 { 197 case '%': 198 case '#': 199 if (fr > s && fr[-1] == *fr) 200 { 201 /* 202 * Second (or later) char in a string 203 * of identical chars. Treat as normal. 204 */ 205 n++; 206 } else if (fr[1] != *fr) 207 { 208 /* 209 * Single char (not repeated). Treat specially. 210 */ 211 ifile = fchar_ifile(*fr); 212 if (ifile == NULL_IFILE) 213 n++; 214 else 215 n += strlen(get_filename(ifile)); 216 } 217 /* 218 * Else it is the first char in a string of 219 * identical chars. Just discard it. 220 */ 221 break; 222 default: 223 n++; 224 break; 225 } 226 } 227 228 e = (char *) ecalloc(n+1, sizeof(char)); 229 230 /* 231 * Now copy the string, expanding any "%" or "#". 232 */ 233 to = e; 234 for (fr = s; *fr != '\0'; fr++) 235 { 236 switch (*fr) 237 { 238 case '%': 239 case '#': 240 if (fr > s && fr[-1] == *fr) 241 { 242 *to++ = *fr; 243 } else if (fr[1] != *fr) 244 { 245 ifile = fchar_ifile(*fr); 246 if (ifile == NULL_IFILE) 247 *to++ = *fr; 248 else 249 { 250 strcpy(to, get_filename(ifile)); 251 to += strlen(to); 252 } 253 } 254 break; 255 default: 256 *to++ = *fr; 257 break; 258 } 259 } 260 *to = '\0'; 261 return (e); 262 } 263 264 #if TAB_COMPLETE_FILENAME 265 266 /* 267 * Return a blank-separated list of filenames which "complete" 268 * the given string. 269 */ 270 public char * 271 fcomplete(s) 272 char *s; 273 { 274 char *fpat; 275 276 if (secure) 277 return (NULL); 278 /* 279 * Complete the filename "s" by globbing "s*". 280 */ 281 #if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC) 282 /* 283 * But in DOS, we have to glob "s*.*". 284 * But if the final component of the filename already has 285 * a dot in it, just do "s*". 286 * (Thus, "FILE" is globbed as "FILE*.*", 287 * but "FILE.A" is globbed as "FILE.A*"). 288 */ 289 { 290 char *slash; 291 for (slash = s+strlen(s)-1; slash > s; slash--) 292 if (*slash == *PATHNAME_SEP || *slash == '/') 293 break; 294 fpat = (char *) ecalloc(strlen(s)+4, sizeof(char)); 295 if (strchr(slash, '.') == NULL) 296 sprintf(fpat, "%s*.*", s); 297 else 298 sprintf(fpat, "%s*", s); 299 } 300 #else 301 fpat = (char *) ecalloc(strlen(s)+2, sizeof(char)); 302 sprintf(fpat, "%s*", s); 303 #endif 304 s = lglob(fpat); 305 if (strcmp(s,fpat) == 0) 306 { 307 /* 308 * The filename didn't expand. 309 */ 310 free(s); 311 s = NULL; 312 } 313 free(fpat); 314 return (s); 315 } 316 #endif 317 318 /* 319 * Try to determine if a file is "binary". 320 * This is just a guess, and we need not try too hard to make it accurate. 321 */ 322 public int 323 bin_file(f) 324 int f; 325 { 326 int i; 327 int n; 328 unsigned char data[64]; 329 330 if (!seekable(f)) 331 return (0); 332 if (lseek(f, (off_t)0, 0) == BAD_LSEEK) 333 return (0); 334 n = read(f, data, sizeof(data)); 335 for (i = 0; i < n; i++) 336 if (binary_char(data[i])) 337 return (1); 338 return (0); 339 } 340 341 /* 342 * Try to determine the size of a file by seeking to the end. 343 */ 344 static POSITION 345 seek_filesize(f) 346 int f; 347 { 348 off_t spos; 349 350 spos = lseek(f, (off_t)0, 2); 351 if (spos == BAD_LSEEK) 352 return (NULL_POSITION); 353 return ((POSITION) spos); 354 } 355 356 /* 357 * Read a string from a file. 358 * Return a pointer to the string in memory. 359 */ 360 static char * 361 readfd(fd) 362 FILE *fd; 363 { 364 int len; 365 int ch; 366 char *buf; 367 char *p; 368 369 /* 370 * Make a guess about how many chars in the string 371 * and allocate a buffer to hold it. 372 */ 373 len = 100; 374 buf = (char *) ecalloc(len, sizeof(char)); 375 for (p = buf; ; p++) 376 { 377 if ((ch = getc(fd)) == '\n' || ch == EOF) 378 break; 379 if (p - buf >= len-1) 380 { 381 /* 382 * The string is too big to fit in the buffer we have. 383 * Allocate a new buffer, twice as big. 384 */ 385 len *= 2; 386 *p = '\0'; 387 p = (char *) ecalloc(len, sizeof(char)); 388 strcpy(p, buf); 389 free(buf); 390 buf = p; 391 p = buf + strlen(buf); 392 } 393 *p = ch; 394 } 395 *p = '\0'; 396 return (buf); 397 } 398 399 #if HAVE_SHELL 400 401 /* 402 * Get the shell's escape character. 403 */ 404 static char * 405 get_meta_escape() 406 { 407 char *s; 408 409 s = lgetenv("LESSMETAESCAPE"); 410 if (s == NULL) 411 s = DEF_METAESCAPE; 412 return (s); 413 } 414 415 /* 416 * Is this a shell metacharacter? 417 */ 418 static int 419 metachar(c) 420 char c; 421 { 422 static char *metachars = NULL; 423 424 if (metachars == NULL) 425 { 426 metachars = lgetenv("LESSMETACHARS"); 427 if (metachars == NULL) 428 metachars = DEF_METACHARS; 429 } 430 return (strchr(metachars, c) != NULL); 431 } 432 433 /* 434 * Insert a backslash before each metacharacter in a string. 435 */ 436 public char * 437 esc_metachars(s) 438 char *s; 439 { 440 char *p; 441 char *newstr; 442 int len; 443 char *esc; 444 int esclen; 445 446 /* 447 * Determine how big a string we need to allocate. 448 */ 449 esc = get_meta_escape(); 450 esclen = strlen(esc); 451 len = 1; /* Trailing null byte */ 452 for (p = s; *p != '\0'; p++) 453 { 454 len++; 455 if (metachar(*p)) 456 { 457 if (*esc == '\0') 458 { 459 /* 460 * We've got a metachar, but this shell 461 * doesn't support escape chars. Give up. 462 */ 463 return (NULL); 464 } 465 /* 466 * Allow space for the escape char. 467 */ 468 len += esclen; 469 } 470 } 471 /* 472 * Allocate and construct the new string. 473 */ 474 newstr = p = (char *) ecalloc(len, sizeof(char)); 475 while (*s != '\0') 476 { 477 if (metachar(*s)) 478 { 479 /* 480 * Add the escape char. 481 */ 482 strcpy(p, esc); 483 p += esclen; 484 } 485 *p++ = *s++; 486 } 487 *p = '\0'; 488 return (newstr); 489 } 490 491 #else /* HAVE_SHELL */ 492 493 public char * 494 esc_metachars(s) 495 char *s; 496 { 497 return (save(s)); 498 } 499 500 #endif /* HAVE_SHELL */ 501 502 503 #if HAVE_POPEN 504 505 FILE *popen(); 506 507 /* 508 * Execute a shell command. 509 * Return a pointer to a pipe connected to the shell command's standard output. 510 */ 511 static FILE * 512 shellcmd(cmd) 513 char *cmd; 514 { 515 FILE *fd; 516 517 #if HAVE_SHELL 518 char *shell; 519 520 shell = lgetenv("SHELL"); 521 if (shell != NULL && *shell != '\0') 522 { 523 char *scmd; 524 char *esccmd; 525 526 /* 527 * Try to escape any metacharacters in the command. 528 * If we can't do that, just put the command in quotes. 529 * (But that doesn't work well if the command itself 530 * contains quotes.) 531 */ 532 if ((esccmd = esc_metachars(cmd)) == NULL) 533 { 534 /* 535 * Cannot escape the metacharacters, so use quotes. 536 * Read the output of <$SHELL -c "cmd">. 537 */ 538 scmd = (char *) ecalloc(strlen(shell) + strlen(cmd) + 7, 539 sizeof(char)); 540 sprintf(scmd, "%s -c \"%s\"", shell, cmd); 541 } else 542 { 543 /* 544 * Read the output of <$SHELL -c cmd>. 545 * No quotes; use the escaped cmd. 546 */ 547 scmd = (char *) ecalloc(strlen(shell) + strlen(esccmd) + 5, 548 sizeof(char)); 549 sprintf(scmd, "%s -c %s", shell, esccmd); 550 free(esccmd); 551 } 552 fd = popen(scmd, "r"); 553 free(scmd); 554 } else 555 #endif 556 { 557 fd = popen(cmd, "r"); 558 /* 559 * Redirection in `popen' might have messed with the 560 * standard devices. Restore binary input mode. 561 */ 562 SET_BINARY(0); 563 } 564 return (fd); 565 } 566 567 #endif /* HAVE_POPEN */ 568 569 570 /* 571 * Expand a filename, doing any system-specific metacharacter substitutions. 572 */ 573 public char * 574 lglob(filename) 575 char *filename; 576 { 577 char *gfilename; 578 char *ofilename; 579 580 ofilename = fexpand(filename); 581 if (secure) 582 return (ofilename); 583 filename = unquote_file(ofilename); 584 585 #ifdef DECL_GLOB_LIST 586 { 587 /* 588 * The globbing function returns a list of names. 589 */ 590 int length; 591 char *p; 592 DECL_GLOB_LIST(list) 593 594 GLOB_LIST(filename, list); 595 if (GLOB_LIST_FAILED(list)) 596 { 597 free(filename); 598 return (ofilename); 599 } 600 length = 1; /* Room for trailing null byte */ 601 for (SCAN_GLOB_LIST(list, p)) 602 { 603 INIT_GLOB_LIST(list, p); 604 length += strlen(p) + 1; 605 #if SPACES_IN_FILENAMES 606 if (strchr(p, ' ') != NULL) 607 length += 2; /* Allow for quotes */ 608 #endif 609 } 610 gfilename = (char *) ecalloc(length, sizeof(char)); 611 for (SCAN_GLOB_LIST(list, p)) 612 { 613 INIT_GLOB_LIST(list, p); 614 #if SPACES_IN_FILENAMES 615 if (strchr(p, ' ') != NULL) 616 sprintf(gfilename + strlen(gfilename), "%c%s%c ", 617 openquote, p, closequote); 618 else 619 #endif 620 sprintf(gfilename + strlen(gfilename), "%s ", p); 621 } 622 /* 623 * Overwrite the final trailing space with a null terminator. 624 */ 625 *--p = '\0'; 626 GLOB_LIST_DONE(list); 627 } 628 #else 629 #ifdef DECL_GLOB_NAME 630 { 631 /* 632 * The globbing function returns a single name, and 633 * is called multiple times to walk thru all names. 634 */ 635 register char *p; 636 register int len; 637 register int n; 638 #if SPACES_IN_FILENAMES 639 register int spaces_in_file; 640 #endif 641 DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) 642 643 GLOB_FIRST_NAME(filename, &fnd, handle); 644 if (GLOB_FIRST_FAILED(handle)) 645 { 646 free(filename); 647 return (ofilename); 648 } 649 650 _splitpath(filename, drive, dir, fname, ext); 651 len = 100; 652 gfilename = (char *) ecalloc(len, sizeof(char)); 653 p = gfilename; 654 do { 655 n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1; 656 #if SPACES_IN_FILENAMES 657 spaces_in_file = 0; 658 if (strchr(fnd.GLOB_NAME, ' ') != NULL || 659 strchr(filename, ' ') != NULL) 660 { 661 spaces_in_file = 1; 662 n += 2; 663 } 664 #endif 665 while (p - gfilename + n+2 >= len) 666 { 667 /* 668 * No room in current buffer. Allocate a bigger one. 669 */ 670 len *= 2; 671 *p = '\0'; 672 p = (char *) ecalloc(len, sizeof(char)); 673 strcpy(p, gfilename); 674 free(gfilename); 675 gfilename = p; 676 p = gfilename + strlen(gfilename); 677 } 678 #if SPACES_IN_FILENAMES 679 if (spaces_in_file) 680 sprintf(p, "%c%s%s%s%c ", openquote, 681 drive, dir, fnd.GLOB_NAME, closequote); 682 else 683 #endif 684 sprintf(p, "%s%s%s ", drive, dir, fnd.GLOB_NAME); 685 p += n; 686 } while (GLOB_NEXT_NAME(handle, &fnd) == 0); 687 688 /* 689 * Overwrite the final trailing space with a null terminator. 690 */ 691 *--p = '\0'; 692 GLOB_NAME_DONE(handle); 693 } 694 #else 695 #if HAVE_POPEN 696 { 697 /* 698 * We get the shell to glob the filename for us by passing 699 * an "echo" command to the shell and reading its output. 700 */ 701 FILE *fd; 702 char *s; 703 char *lessecho; 704 char *cmd; 705 706 lessecho = lgetenv("LESSECHO"); 707 if (lessecho == NULL || *lessecho == '\0') 708 lessecho = "lessecho"; 709 s = esc_metachars(filename); 710 if (s == NULL) 711 { 712 /* 713 * There may be dangerous metachars in this name. 714 * We can't risk passing it to the shell. 715 * {{ For example, do "!;TAB" when the first file 716 * in the dir is named "rm". }} 717 */ 718 free(filename); 719 return (ofilename); 720 } 721 /* 722 * Invoke lessecho, and read its output (a globbed list of filenames). 723 */ 724 cmd = (char *) ecalloc(strlen(lessecho) + strlen(s) + 24, sizeof(char)); 725 sprintf(cmd, "%s -p0x%x -d0x%x -- %s", 726 lessecho, openquote, closequote, s); 727 fd = shellcmd(cmd); 728 free(s); 729 free(cmd); 730 if (fd == NULL) 731 { 732 /* 733 * Cannot create the pipe. 734 * Just return the original (fexpanded) filename. 735 */ 736 free(filename); 737 return (ofilename); 738 } 739 gfilename = readfd(fd); 740 pclose(fd); 741 if (*gfilename == '\0') 742 { 743 free(gfilename); 744 free(filename); 745 return (ofilename); 746 } 747 } 748 #else 749 /* 750 * No globbing functions at all. Just use the fexpanded filename. 751 */ 752 gfilename = save(filename); 753 #endif 754 #endif 755 #endif 756 free(filename); 757 free(ofilename); 758 return (gfilename); 759 } 760 761 /* 762 * See if we should open a "replacement file" 763 * instead of the file we're about to open. 764 */ 765 public char * 766 open_altfile(filename, pf, pfd) 767 char *filename; 768 int *pf; 769 void **pfd; 770 { 771 #if !HAVE_POPEN 772 return (NULL); 773 #else 774 char *lessopen; 775 char *gfilename; 776 char *cmd; 777 FILE *fd; 778 #if HAVE_FILENO 779 int returnfd = 0; 780 #endif 781 782 if (secure) 783 return (NULL); 784 ch_ungetchar(-1); 785 if ((lessopen = lgetenv("LESSOPEN")) == NULL) 786 return (NULL); 787 if (strcmp(filename, "-") == 0) 788 return (NULL); 789 if (*lessopen == '|') 790 { 791 /* 792 * If LESSOPEN starts with a |, it indicates 793 * a "pipe preprocessor". 794 */ 795 #if HAVE_FILENO 796 lessopen++; 797 returnfd = 1; 798 #else 799 error("LESSOPEN pipe is not supported", NULL_PARG); 800 return (NULL); 801 #endif 802 } 803 804 gfilename = esc_metachars(filename); 805 if (gfilename == NULL) 806 { 807 /* 808 * Cannot escape metacharacters. 809 */ 810 return (NULL); 811 } 812 cmd = (char *) ecalloc(strlen(lessopen) + strlen(gfilename) + 2, 813 sizeof(char)); 814 sprintf(cmd, lessopen, gfilename); 815 fd = shellcmd(cmd); 816 free(gfilename); 817 free(cmd); 818 if (fd == NULL) 819 { 820 /* 821 * Cannot create the pipe. 822 */ 823 return (NULL); 824 } 825 #if HAVE_FILENO 826 if (returnfd) 827 { 828 int f; 829 char c; 830 831 /* 832 * Read one char to see if the pipe will produce any data. 833 * If it does, push the char back on the pipe. 834 */ 835 f = fileno(fd); 836 SET_BINARY(f); 837 if (read(f, &c, 1) != 1) 838 { 839 /* 840 * Pipe is empty. This means there is no alt file. 841 */ 842 pclose(fd); 843 return (NULL); 844 } 845 ch_ungetchar(c); 846 *pfd = (void *) fd; 847 *pf = f; 848 return (save("-")); 849 } 850 #endif 851 gfilename = readfd(fd); 852 pclose(fd); 853 if (*gfilename == '\0') 854 /* 855 * Pipe is empty. This means there is no alt file. 856 */ 857 return (NULL); 858 return (gfilename); 859 #endif /* HAVE_POPEN */ 860 } 861 862 /* 863 * Close a replacement file. 864 */ 865 public void 866 close_altfile(altfilename, filename, pipefd) 867 char *altfilename; 868 char *filename; 869 void *pipefd; 870 { 871 #if HAVE_POPEN 872 char *lessclose; 873 char *gfilename; 874 char *galtfilename; 875 FILE *fd; 876 char *cmd; 877 878 if (secure) 879 return; 880 if (pipefd != NULL) 881 pclose((FILE*) pipefd); 882 if ((lessclose = lgetenv("LESSCLOSE")) == NULL) 883 return; 884 gfilename = esc_metachars(filename); 885 if (gfilename == NULL) 886 { 887 return; 888 } 889 galtfilename = esc_metachars(altfilename); 890 if (galtfilename == NULL) 891 { 892 free(gfilename); 893 return; 894 } 895 cmd = (char *) ecalloc(strlen(lessclose) + strlen(gfilename) + 896 strlen(galtfilename) + 2, sizeof(char)); 897 sprintf(cmd, lessclose, gfilename, galtfilename); 898 fd = shellcmd(cmd); 899 free(galtfilename); 900 free(gfilename); 901 free(cmd); 902 if (fd != NULL) 903 pclose(fd); 904 #endif 905 } 906 907 /* 908 * Is the specified file a directory? 909 */ 910 public int 911 is_dir(filename) 912 char *filename; 913 { 914 int isdir = 0; 915 916 filename = unquote_file(filename); 917 #if HAVE_STAT 918 { 919 int r; 920 struct stat statbuf; 921 922 r = stat(filename, &statbuf); 923 isdir = (r >= 0 && S_ISDIR(statbuf.st_mode)); 924 } 925 #else 926 #ifdef _OSK 927 { 928 register int f; 929 930 f = open(filename, S_IREAD | S_IFDIR); 931 if (f >= 0) 932 close(f); 933 isdir = (f >= 0); 934 } 935 #endif 936 #endif 937 free(filename); 938 return (isdir); 939 } 940 941 /* 942 * Returns NULL if the file can be opened and 943 * is an ordinary file, otherwise an error message 944 * (if it cannot be opened or is a directory, etc.) 945 */ 946 public char * 947 bad_file(filename) 948 char *filename; 949 { 950 register char *m = NULL; 951 952 filename = unquote_file(filename); 953 if (is_dir(filename)) 954 { 955 static char is_dir[] = " is a directory"; 956 957 m = (char *) ecalloc(strlen(filename) + sizeof(is_dir), 958 sizeof(char)); 959 strcpy(m, filename); 960 strcat(m, is_dir); 961 } else 962 { 963 #if HAVE_STAT 964 int r; 965 struct stat statbuf; 966 967 r = stat(filename, &statbuf); 968 if (r < 0) 969 { 970 m = errno_message(filename); 971 } else if (force_open) 972 { 973 m = NULL; 974 } else if (!S_ISREG(statbuf.st_mode)) 975 { 976 static char not_reg[] = " is not a regular file (use -f to see it)"; 977 m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 978 sizeof(char)); 979 strcpy(m, filename); 980 strcat(m, not_reg); 981 } 982 #endif 983 } 984 free(filename); 985 return (m); 986 } 987 988 /* 989 * Return the size of a file, as cheaply as possible. 990 * In Unix, we can stat the file. 991 */ 992 public POSITION 993 filesize(f) 994 int f; 995 { 996 #if HAVE_STAT 997 struct stat statbuf; 998 999 if (fstat(f, &statbuf) >= 0) 1000 return ((POSITION) statbuf.st_size); 1001 #else 1002 #ifdef _OSK 1003 long size; 1004 1005 if ((size = (long) _gs_size(f)) >= 0) 1006 return ((POSITION) size); 1007 #endif 1008 #endif 1009 return (seek_filesize(f)); 1010 } 1011 1012