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