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