Annotation of src/usr.bin/less/filename.c, Revision 1.18
1.1 etheisen 1: /*
1.16 shadchin 2: * Copyright (C) 1984-2012 Mark Nudelman
1.18 ! nicm 3: * Modified for use with illumos by Garrett D'Amore.
! 4: * Copyright 2014 Garrett D'Amore <garrett@damore.org>
1.1 etheisen 5: *
1.8 millert 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.
1.1 etheisen 8: *
1.16 shadchin 9: * For more information, see the README file.
1.17 nicm 10: */
1.1 etheisen 11:
12: /*
13: * Routines to mess around with filenames (and files).
14: * Much of this is very OS dependent.
1.17 nicm 15: *
16: * Modified for illumos/POSIX -- it uses native glob(3C) rather than
17: * popen to a shell to perform the expansion.
1.1 etheisen 18: */
19:
20: #include "less.h"
1.8 millert 21:
22: #include <sys/stat.h>
1.17 nicm 23: #include <glob.h>
24: #include <stdarg.h>
1.8 millert 25:
1.1 etheisen 26: extern int force_open;
1.8 millert 27: extern int secure;
28: extern int use_lessopen;
1.15 shadchin 29: extern int ctldisp;
30: extern int utf_mode;
1.1 etheisen 31: extern IFILE curr_ifile;
32: extern IFILE old_ifile;
1.8 millert 33: extern char openquote;
34: extern char closequote;
35:
36: /*
37: * Remove quotes around a filename.
38: */
1.17 nicm 39: char *
40: shell_unquote(char *str)
1.8 millert 41: {
42: char *name;
43: char *p;
44:
1.17 nicm 45: name = p = ecalloc(strlen(str)+1, sizeof (char));
46: if (*str == openquote) {
1.8 millert 47: str++;
1.17 nicm 48: while (*str != '\0') {
49: if (*str == closequote) {
1.8 millert 50: if (str[1] != closequote)
51: break;
52: str++;
53: }
54: *p++ = *str++;
55: }
1.17 nicm 56: } else {
1.8 millert 57: char *esc = get_meta_escape();
58: int esclen = strlen(esc);
1.17 nicm 59: while (*str != '\0') {
1.8 millert 60: if (esclen > 0 && strncmp(str, esc, esclen) == 0)
61: str += esclen;
62: *p++ = *str++;
63: }
64: }
65: *p = '\0';
66: return (name);
67: }
68:
69: /*
70: * Get the shell's escape character.
71: */
1.17 nicm 72: char *
73: get_meta_escape(void)
1.8 millert 74: {
75: char *s;
76:
77: s = lgetenv("LESSMETAESCAPE");
78: if (s == NULL)
79: s = DEF_METAESCAPE;
80: return (s);
81: }
82:
83: /*
84: * Get the characters which the shell considers to be "metacharacters".
85: */
1.17 nicm 86: static char *
87: metachars(void)
1.8 millert 88: {
89: static char *mchars = NULL;
90:
1.17 nicm 91: if (mchars == NULL) {
1.8 millert 92: mchars = lgetenv("LESSMETACHARS");
93: if (mchars == NULL)
94: mchars = DEF_METACHARS;
95: }
96: return (mchars);
97: }
98:
99: /*
100: * Is this a shell metacharacter?
101: */
1.17 nicm 102: static int
103: metachar(char c)
1.8 millert 104: {
105: return (strchr(metachars(), c) != NULL);
106: }
107:
108: /*
109: * Insert a backslash before each metacharacter in a string.
110: */
1.17 nicm 111: char *
112: shell_quote(const char *s)
1.8 millert 113: {
1.17 nicm 114: const char *p;
115: char *r;
1.8 millert 116: char *newstr;
117: int len;
118: char *esc = get_meta_escape();
119: int esclen = strlen(esc);
120: int use_quotes = 0;
121: int have_quotes = 0;
122:
123: /*
124: * Determine how big a string we need to allocate.
125: */
126: len = 1; /* Trailing null byte */
1.17 nicm 127: for (p = s; *p != '\0'; p++) {
1.8 millert 128: len++;
129: if (*p == openquote || *p == closequote)
130: have_quotes = 1;
1.17 nicm 131: if (metachar(*p)) {
132: if (esclen == 0) {
1.8 millert 133: /*
1.17 nicm 134: * We've got a metachar, but this shell
1.8 millert 135: * doesn't support escape chars. Use quotes.
136: */
137: use_quotes = 1;
1.17 nicm 138: } else {
1.8 millert 139: /*
140: * Allow space for the escape char.
141: */
142: len += esclen;
143: }
144: }
145: }
146: /*
147: * Allocate and construct the new string.
148: */
1.17 nicm 149: if (use_quotes) {
150: /* We can't quote a string that contains quotes. */
151: if (have_quotes)
152: return (NULL);
153: newstr = easprintf("%c%s%c", openquote, s, closequote);
154: } else {
155: newstr = r = ecalloc(len, sizeof (char));
156: while (*s != '\0') {
157: if (metachar(*s)) {
1.8 millert 158: /*
159: * Add the escape char.
160: */
1.17 nicm 161: (void) strlcpy(r, esc, newstr + len - p);
162: r += esclen;
1.8 millert 163: }
1.17 nicm 164: *r++ = *s++;
1.8 millert 165: }
1.17 nicm 166: *r = '\0';
1.8 millert 167: }
168: return (newstr);
169: }
1.1 etheisen 170:
171: /*
172: * Return a pathname that points to a specified file in a specified directory.
173: * Return NULL if the file does not exist in the directory.
174: */
1.17 nicm 175: static char *
176: dirfile(const char *dirname, const char *filename)
1.1 etheisen 177: {
178: char *pathname;
1.8 millert 179: char *qpathname;
180: int f;
1.1 etheisen 181:
182: if (dirname == NULL || *dirname == '\0')
183: return (NULL);
184: /*
185: * Construct the full pathname.
186: */
1.17 nicm 187: pathname = easprintf("%s/%s", dirname, filename);
1.1 etheisen 188: /*
189: * Make sure the file exists.
190: */
1.8 millert 191: qpathname = shell_unquote(pathname);
1.17 nicm 192: f = open(qpathname, O_RDONLY);
193: if (f < 0) {
1.1 etheisen 194: free(pathname);
195: pathname = NULL;
1.17 nicm 196: } else {
197: (void) close(f);
1.1 etheisen 198: }
1.8 millert 199: free(qpathname);
1.1 etheisen 200: return (pathname);
201: }
202:
203: /*
204: * Return the full pathname of the given file in the "home directory".
205: */
1.17 nicm 206: char *
207: homefile(char *filename)
1.1 etheisen 208: {
1.17 nicm 209: return (dirfile(lgetenv("HOME"), filename));
1.1 etheisen 210: }
1.9 millert 211:
1.1 etheisen 212: /*
213: * Expand a string, substituting any "%" with the current filename,
214: * and any "#" with the previous filename.
1.8 millert 215: * But a string of N "%"s is just replaced with N-1 "%"s.
216: * Likewise for a string of N "#"s.
1.1 etheisen 217: * {{ This is a lot of work just to support % and #. }}
218: */
1.17 nicm 219: char *
220: fexpand(char *s)
1.1 etheisen 221: {
1.17 nicm 222: char *fr, *to;
223: int n;
224: char *e;
1.8 millert 225: IFILE ifile;
226:
227: #define fchar_ifile(c) \
1.17 nicm 228: ((c) == '%' ? curr_ifile : (c) == '#' ? old_ifile : NULL_IFILE)
1.1 etheisen 229:
230: /*
1.17 nicm 231: * Make one pass to see how big a buffer we
1.1 etheisen 232: * need to allocate for the expanded string.
233: */
234: n = 0;
1.17 nicm 235: for (fr = s; *fr != '\0'; fr++) {
236: switch (*fr) {
1.1 etheisen 237: case '%':
1.8 millert 238: case '#':
1.17 nicm 239: if (fr > s && fr[-1] == *fr) {
1.8 millert 240: /*
241: * Second (or later) char in a string
242: * of identical chars. Treat as normal.
243: */
244: n++;
1.17 nicm 245: } else if (fr[1] != *fr) {
1.8 millert 246: /*
247: * Single char (not repeated). Treat specially.
248: */
249: ifile = fchar_ifile(*fr);
250: if (ifile == NULL_IFILE)
251: n++;
252: else
253: n += strlen(get_filename(ifile));
1.1 etheisen 254: }
1.8 millert 255: /*
256: * Else it is the first char in a string of
257: * identical chars. Just discard it.
258: */
1.1 etheisen 259: break;
260: default:
261: n++;
262: break;
263: }
264: }
265:
1.17 nicm 266: e = ecalloc(n+1, sizeof (char));
1.1 etheisen 267:
268: /*
269: * Now copy the string, expanding any "%" or "#".
270: */
271: to = e;
1.17 nicm 272: for (fr = s; *fr != '\0'; fr++) {
273: switch (*fr) {
1.1 etheisen 274: case '%':
275: case '#':
1.17 nicm 276: if (fr > s && fr[-1] == *fr) {
1.8 millert 277: *to++ = *fr;
1.17 nicm 278: } else if (fr[1] != *fr) {
1.8 millert 279: ifile = fchar_ifile(*fr);
1.17 nicm 280: if (ifile == NULL_IFILE) {
1.8 millert 281: *to++ = *fr;
1.17 nicm 282: } else {
283: (void) strlcpy(to, get_filename(ifile),
1.8 millert 284: e + n + 1 - to);
285: to += strlen(to);
286: }
287: }
1.1 etheisen 288: break;
289: default:
290: *to++ = *fr;
291: break;
292: }
293: }
294: *to = '\0';
295: return (e);
296: }
297:
298: /*
299: * Return a blank-separated list of filenames which "complete"
300: * the given string.
301: */
1.17 nicm 302: char *
303: fcomplete(char *s)
1.1 etheisen 304: {
305: char *fpat;
1.8 millert 306: char *qs;
1.7 deraadt 307:
1.8 millert 308: if (secure)
309: return (NULL);
1.1 etheisen 310: /*
311: * Complete the filename "s" by globbing "s*".
312: */
1.17 nicm 313: fpat = easprintf("%s*", s);
314:
1.8 millert 315: qs = lglob(fpat);
316: s = shell_unquote(qs);
1.17 nicm 317: if (strcmp(s, fpat) == 0) {
1.1 etheisen 318: /*
319: * The filename didn't expand.
320: */
1.8 millert 321: free(qs);
322: qs = NULL;
1.1 etheisen 323: }
1.8 millert 324: free(s);
1.1 etheisen 325: free(fpat);
1.8 millert 326: return (qs);
1.1 etheisen 327: }
328:
329: /*
330: * Try to determine if a file is "binary".
331: * This is just a guess, and we need not try too hard to make it accurate.
332: */
1.17 nicm 333: int
334: bin_file(int f)
1.1 etheisen 335: {
336: int n;
1.15 shadchin 337: int bin_count = 0;
338: char data[256];
1.17 nicm 339: char *p;
340: char *pend;
1.1 etheisen 341:
342: if (!seekable(f))
343: return (0);
1.11 deraadt 344: if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
1.1 etheisen 345: return (0);
1.17 nicm 346: n = read(f, data, sizeof (data));
1.15 shadchin 347: pend = &data[n];
1.17 nicm 348: for (p = data; p < pend; ) {
1.15 shadchin 349: LWCHAR c = step_char(&p, +1, pend);
1.17 nicm 350: if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) {
1.15 shadchin 351: do {
352: c = step_char(&p, +1, pend);
353: } while (p < pend && is_ansi_middle(c));
354: } else if (binary_char(c))
355: bin_count++;
356: }
357: /*
358: * Call it a binary file if there are more than 5 binary characters
359: * in the first 256 bytes of the file.
360: */
361: return (bin_count > 5);
1.1 etheisen 362: }
363:
364: /*
365: * Try to determine the size of a file by seeking to the end.
366: */
1.17 nicm 367: static off_t
368: seek_filesize(int f)
1.1 etheisen 369: {
370: off_t spos;
371:
1.11 deraadt 372: spos = lseek(f, (off_t)0, SEEK_END);
1.1 etheisen 373: if (spos == BAD_LSEEK)
1.17 nicm 374: return (-1);
375: return (spos);
1.1 etheisen 376: }
377:
378: /*
379: * Read a string from a file.
380: * Return a pointer to the string in memory.
381: */
1.17 nicm 382: static char *
383: readfd(FILE *fd)
1.1 etheisen 384: {
385: int len;
386: int ch;
387: char *buf;
388: char *p;
1.17 nicm 389:
390: /*
1.1 etheisen 391: * Make a guess about how many chars in the string
392: * and allocate a buffer to hold it.
393: */
394: len = 100;
1.17 nicm 395: buf = ecalloc(len, sizeof (char));
396: for (p = buf; ; p++) {
1.1 etheisen 397: if ((ch = getc(fd)) == '\n' || ch == EOF)
398: break;
1.17 nicm 399: if (p >= buf + len-1) {
1.1 etheisen 400: /*
401: * The string is too big to fit in the buffer we have.
402: * Allocate a new buffer, twice as big.
403: */
404: len *= 2;
405: *p = '\0';
1.17 nicm 406: p = ecalloc(len, sizeof (char));
1.5 deraadt 407: strlcpy(p, buf, len);
1.1 etheisen 408: free(buf);
409: buf = p;
410: p = buf + strlen(buf);
411: }
1.17 nicm 412: *p = (char)ch;
1.1 etheisen 413: }
414: *p = '\0';
415: return (buf);
416: }
417:
418: /*
419: * Execute a shell command.
420: * Return a pointer to a pipe connected to the shell command's standard output.
421: */
1.17 nicm 422: static FILE *
423: shellcmd(char *cmd)
1.1 etheisen 424: {
425: FILE *fd;
1.8 millert 426:
427: char *shell;
428:
429: shell = lgetenv("SHELL");
1.17 nicm 430: if (shell != NULL && *shell != '\0') {
1.8 millert 431: char *scmd;
432: char *esccmd;
433:
1.1 etheisen 434: /*
1.17 nicm 435: * Read the output of <$SHELL -c cmd>.
1.8 millert 436: * Escape any metacharacters in the command.
1.1 etheisen 437: */
1.8 millert 438: esccmd = shell_quote(cmd);
1.17 nicm 439: if (esccmd == NULL) {
1.8 millert 440: fd = popen(cmd, "r");
1.17 nicm 441: } else {
442: scmd = easprintf("%s -c %s", shell, esccmd);
1.8 millert 443: free(esccmd);
444: fd = popen(scmd, "r");
445: free(scmd);
446: }
1.17 nicm 447: } else {
1.8 millert 448: fd = popen(cmd, "r");
1.1 etheisen 449: }
1.8 millert 450: /*
451: * Redirection in `popen' might have messed with the
452: * standard devices. Restore binary input mode.
453: */
1.1 etheisen 454: return (fd);
455: }
456:
457: /*
1.8 millert 458: * Expand a filename, doing any system-specific metacharacter substitutions.
1.1 etheisen 459: */
1.17 nicm 460: char *
461: lglob(char *filename)
1.1 etheisen 462: {
463: char *gfilename;
1.8 millert 464: char *ofilename;
1.17 nicm 465: glob_t list;
466: int i;
467: int length;
468: char *p;
469: char *qfilename;
1.1 etheisen 470:
1.8 millert 471: ofilename = fexpand(filename);
472: if (secure)
473: return (ofilename);
474: filename = shell_unquote(ofilename);
475:
476: /*
477: * The globbing function returns a list of names.
478: */
1.1 etheisen 479:
1.17 nicm 480: #ifndef GLOB_TILDE
481: #define GLOB_TILDE 0
482: #endif
483: #ifndef GLOB_LIMIT
484: #define GLOB_LIMIT 0
485: #endif
486: if (glob(filename, GLOB_TILDE | GLOB_LIMIT, NULL, &list) != 0) {
1.8 millert 487: free(filename);
488: return (ofilename);
489: }
490: length = 1; /* Room for trailing null byte */
1.17 nicm 491: for (i = 0; i < list.gl_pathc; i++) {
492: p = list.gl_pathv[i];
1.8 millert 493: qfilename = shell_quote(p);
1.17 nicm 494: if (qfilename != NULL) {
495: length += strlen(qfilename) + 1;
1.8 millert 496: free(qfilename);
497: }
498: }
1.17 nicm 499: gfilename = ecalloc(length, sizeof (char));
500: for (i = 0; i < list.gl_pathc; i++) {
501: p = list.gl_pathv[i];
1.8 millert 502: qfilename = shell_quote(p);
1.17 nicm 503: if (qfilename != NULL) {
504: if (i != 0) {
505: (void) strlcat(gfilename, " ", length);
506: }
507: (void) strlcat(gfilename, qfilename, length);
1.8 millert 508: free(qfilename);
509: }
1.1 etheisen 510: }
1.17 nicm 511: globfree(&list);
1.8 millert 512: free(filename);
513: free(ofilename);
1.1 etheisen 514: return (gfilename);
515: }
516:
517: /*
1.17 nicm 518: * Expand LESSOPEN or LESSCLOSE. Returns a newly allocated string
519: * on success, NULL otherwise.
1.16 shadchin 520: */
1.17 nicm 521: static char *
522: expand_pct_s(const char *fmt, ...)
1.16 shadchin 523: {
1.17 nicm 524: int n;
525: int len;
526: char *r, *d;
527: const char *f[3]; /* max expansions + 1 for NULL */
528: va_list ap;
529:
530: va_start(ap, fmt);
531: for (n = 0; n < ((sizeof (f)/sizeof (f[0])) - 1); n++) {
532: f[n] = (const char *)va_arg(ap, const char *);
533: if (f[n] == NULL) {
534: break;
535: }
536: }
537: va_end(ap);
538: f[n] = NULL; /* terminate list */
1.16 shadchin 539:
1.17 nicm 540: len = strlen(fmt) + 1;
541: for (n = 0; f[n] != NULL; n++) {
542: len += strlen(f[n]); /* technically could -2 for "%s" */
543: }
544: r = ecalloc(len, sizeof (char));
545:
546: for (n = 0, d = r; *fmt != 0; ) {
547: if (*fmt != '%') {
548: *d++ = *fmt++;
549: continue;
550: }
551: fmt++;
552: /* Permit embedded "%%" */
553: switch (*fmt) {
554: case '%':
555: *d++ = '%';
556: fmt++;
1.16 shadchin 557: break;
1.17 nicm 558: case 's':
559: if (f[n] == NULL) {
560: va_end(ap);
561: free(r);
562: return (NULL);
563: }
564: (void) strlcpy(d, f[n++], r + len - d);
565: fmt++;
566: d += strlen(d);
567: break;
568: default:
569: va_end(ap);
570: free(r);
571: return (NULL);
572: }
1.16 shadchin 573: }
1.17 nicm 574: *d = '\0';
575: return (r);
1.16 shadchin 576: }
577:
578: /*
1.17 nicm 579: * See if we should open a "replacement file"
1.1 etheisen 580: * instead of the file we're about to open.
581: */
1.17 nicm 582: char *
583: open_altfile(char *filename, int *pf, void **pfd)
584: {
1.1 etheisen 585: char *lessopen;
1.16 shadchin 586: char *cmd;
1.8 millert 587: FILE *fd;
1.1 etheisen 588: int returnfd = 0;
1.17 nicm 589:
1.8 millert 590: if (!use_lessopen || secure)
591: return (NULL);
1.1 etheisen 592: ch_ungetchar(-1);
1.8 millert 593: if ((lessopen = lgetenv("LESSOPEN")) == NULL)
1.1 etheisen 594: return (NULL);
1.17 nicm 595: while (*lessopen == '|') {
1.1 etheisen 596: /*
1.17 nicm 597: * If LESSOPEN starts with a |, it indicates
1.1 etheisen 598: * a "pipe preprocessor".
599: */
600: lessopen++;
1.16 shadchin 601: returnfd++;
1.1 etheisen 602: }
1.15 shadchin 603: if (*lessopen == '-') {
604: /*
605: * Lessopen preprocessor will accept "-" as a filename.
606: */
607: lessopen++;
608: } else {
609: if (strcmp(filename, "-") == 0)
610: return (NULL);
611: }
1.17 nicm 612:
613: if ((cmd = expand_pct_s(lessopen, filename, NULL)) == NULL) {
1.16 shadchin 614: error("Invalid LESSOPEN variable", NULL_PARG);
615: return (NULL);
616: }
1.8 millert 617: fd = shellcmd(cmd);
618: free(cmd);
1.17 nicm 619: if (fd == NULL) {
1.1 etheisen 620: /*
621: * Cannot create the pipe.
622: */
623: return (NULL);
624: }
1.17 nicm 625: if (returnfd) {
1.1 etheisen 626: int f;
627: char c;
628:
629: /*
630: * Read one char to see if the pipe will produce any data.
631: * If it does, push the char back on the pipe.
632: */
633: f = fileno(fd);
1.17 nicm 634: if (read(f, &c, 1) != 1) {
1.1 etheisen 635: /*
1.16 shadchin 636: * Pipe is empty.
637: * If more than 1 pipe char was specified,
1.17 nicm 638: * the exit status tells whether the file itself
1.16 shadchin 639: * is empty, or if there is no alt file.
640: * If only one pipe char, just assume no alt file.
1.1 etheisen 641: */
1.16 shadchin 642: int status = pclose(fd);
643: if (returnfd > 1 && status == 0) {
644: *pfd = NULL;
645: *pf = -1;
646: return (save(FAKE_EMPTYFILE));
647: }
1.1 etheisen 648: return (NULL);
649: }
650: ch_ungetchar(c);
651: *pfd = (void *) fd;
652: *pf = f;
653: return (save("-"));
1.8 millert 654: }
655: cmd = readfd(fd);
1.1 etheisen 656: pclose(fd);
1.8 millert 657: if (*cmd == '\0')
1.1 etheisen 658: /*
659: * Pipe is empty. This means there is no alt file.
660: */
661: return (NULL);
1.8 millert 662: return (cmd);
1.1 etheisen 663: }
664:
665: /*
666: * Close a replacement file.
667: */
1.17 nicm 668: void
669: close_altfile(char *altfilename, char *filename, void *pipefd)
1.1 etheisen 670: {
671: char *lessclose;
672: FILE *fd;
1.16 shadchin 673: char *cmd;
1.17 nicm 674:
1.8 millert 675: if (secure)
676: return;
1.17 nicm 677: if (pipefd != NULL) {
678: pclose((FILE *)pipefd);
1.8 millert 679: }
680: if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
1.17 nicm 681: return;
682: cmd = expand_pct_s(lessclose, filename, altfilename, NULL);
683: if (cmd == NULL) {
684: error("Invalid LESSCLOSE variable", NULL_PARG);
1.16 shadchin 685: return;
686: }
1.8 millert 687: fd = shellcmd(cmd);
688: free(cmd);
689: if (fd != NULL)
1.17 nicm 690: (void) pclose(fd);
1.1 etheisen 691: }
1.17 nicm 692:
1.8 millert 693: /*
694: * Is the specified file a directory?
695: */
1.17 nicm 696: int
697: is_dir(char *filename)
1.1 etheisen 698: {
1.8 millert 699: int isdir = 0;
1.17 nicm 700: int r;
701: struct stat statbuf;
1.8 millert 702:
703: filename = shell_unquote(filename);
1.1 etheisen 704:
1.8 millert 705: r = stat(filename, &statbuf);
706: isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
707: free(filename);
708: return (isdir);
709: }
1.1 etheisen 710:
711: /*
712: * Returns NULL if the file can be opened and
713: * is an ordinary file, otherwise an error message
714: * (if it cannot be opened or is a directory, etc.)
715: */
1.17 nicm 716: char *
717: bad_file(char *filename)
1.1 etheisen 718: {
1.17 nicm 719: char *m = NULL;
1.1 etheisen 720:
1.8 millert 721: filename = shell_unquote(filename);
1.17 nicm 722: if (!force_open && is_dir(filename)) {
723: m = easprintf("%s is a directory", filename);
724: } else {
1.8 millert 725: int r;
726: struct stat statbuf;
1.5 deraadt 727:
1.8 millert 728: r = stat(filename, &statbuf);
1.17 nicm 729: if (r < 0) {
1.8 millert 730: m = errno_message(filename);
1.17 nicm 731: } else if (force_open) {
1.8 millert 732: m = NULL;
1.17 nicm 733: } else if (!S_ISREG(statbuf.st_mode)) {
734: m = easprintf("%s is not a regular file (use -f to "
735: "see it)", filename);
1.8 millert 736: }
1.1 etheisen 737: }
1.8 millert 738: free(filename);
739: return (m);
1.1 etheisen 740: }
741:
742: /*
743: * Return the size of a file, as cheaply as possible.
744: * In Unix, we can stat the file.
745: */
1.17 nicm 746: off_t
747: filesize(int f)
1.1 etheisen 748: {
749: struct stat statbuf;
750:
1.8 millert 751: if (fstat(f, &statbuf) >= 0)
1.17 nicm 752: return (statbuf.st_size);
1.8 millert 753: return (seek_filesize(f));
1.1 etheisen 754: }
755:
756: /*
1.15 shadchin 757: * Return last component of a pathname.
758: */
1.17 nicm 759: char *
760: last_component(char *name)
1.15 shadchin 761: {
762: char *slash;
763:
1.17 nicm 764: for (slash = name + strlen(name); slash > name; ) {
1.15 shadchin 765: --slash;
1.17 nicm 766: if (*slash == '/')
1.15 shadchin 767: return (slash + 1);
768: }
769: return (name);
770: }