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