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