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