Annotation of src/usr.bin/less/filename.c, Revision 1.13
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);
1.11 deraadt 495: if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
1.1 etheisen 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:
1.11 deraadt 513: spos = lseek(f, (off_t)0, SEEK_END);
1.1 etheisen 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: */
1.13 ! ray 679: if (gfilename[0] != '\0' && gfilename[strlen(gfilename) - 1] == ' ')
! 680: gfilename[strlen(gfilename) - 1] = '\0';
1.8 millert 681: GLOB_LIST_DONE(list);
1.1 etheisen 682: }
683: #else
1.8 millert 684: #ifdef DECL_GLOB_NAME
1.1 etheisen 685: {
1.8 millert 686: /*
687: * The globbing function returns a single name, and
688: * is called multiple times to walk thru all names.
689: */
690: register char *p;
691: register int len;
692: register int n;
693: char *pathname;
694: char *qpathname;
695: DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
696:
697: GLOB_FIRST_NAME(filename, &fnd, handle);
698: if (GLOB_FIRST_FAILED(handle))
699: {
700: free(filename);
701: return (ofilename);
702: }
703:
704: _splitpath(filename, drive, dir, fname, ext);
705: len = 100;
706: gfilename = (char *) ecalloc(len, sizeof(char));
707: p = gfilename;
708: do {
709: n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
710: pathname = (char *) ecalloc(n, sizeof(char));
711: snprintf(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME);
712: qpathname = shell_quote(pathname);
713: free(pathname);
714: if (qpathname != NULL)
715: {
716: n = strlen(qpathname);
717: while (p - gfilename + n + 2 >= len)
718: {
719: /*
720: * No room in current buffer.
721: * Allocate a bigger one.
722: */
723: len *= 2;
724: *p = '\0';
725: p = (char *) ecalloc(len, sizeof(char));
726: strlcpy(p, gfilename, len);
727: free(gfilename);
728: gfilename = p;
729: p = gfilename + strlen(gfilename);
730: }
731: strlcpy(p, qpathname, gfilename + len - p);
732: free(qpathname);
733: p += n;
734: *p++ = ' ';
735: }
736: } while (GLOB_NEXT_NAME(handle, &fnd) == 0);
737:
738: /*
739: * Overwrite the final trailing space with a null terminator.
740: */
741: *--p = '\0';
742: GLOB_NAME_DONE(handle);
743: }
744: #else
745: #if HAVE_POPEN
746: {
747: /*
748: * We get the shell to glob the filename for us by passing
749: * an "echo" command to the shell and reading its output.
750: */
1.1 etheisen 751: FILE *fd;
1.8 millert 752: char *s;
753: char *lessecho;
754: char *cmd;
755: char *esc;
756: size_t len;
1.1 etheisen 757:
1.8 millert 758: esc = get_meta_escape();
759: if (strlen(esc) == 0)
760: esc = "-";
761: esc = shell_quote(esc);
762: if (esc == NULL)
763: {
764: free(filename);
765: return (ofilename);
766: }
767: lessecho = lgetenv("LESSECHO");
768: if (lessecho == NULL || *lessecho == '\0')
769: lessecho = "lessecho";
1.1 etheisen 770: /*
1.8 millert 771: * Invoke lessecho, and read its output (a globbed list of filenames).
1.1 etheisen 772: */
1.8 millert 773: len = strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24;
774: cmd = (char *) ecalloc(len, sizeof(char));
775: snprintf(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote,
776: closequote, esc);
777: free(esc);
778: for (s = metachars(); *s != '\0'; s++)
779: snprintf(cmd + strlen(cmd), len - strlen(cmd), "-n0x%x ", *s);
780: snprintf(cmd + strlen(cmd), len - strlen(cmd), "-- %s", ofilename);
781: fd = shellcmd(cmd);
782: free(cmd);
1.1 etheisen 783: if (fd == NULL)
784: {
785: /*
786: * Cannot create the pipe.
787: * Just return the original (fexpanded) filename.
788: */
1.8 millert 789: free(filename);
790: return (ofilename);
1.1 etheisen 791: }
792: gfilename = readfd(fd);
793: pclose(fd);
794: if (*gfilename == '\0')
795: {
796: free(gfilename);
1.8 millert 797: free(filename);
798: return (ofilename);
1.1 etheisen 799: }
800: }
1.8 millert 801: #else
802: /*
803: * No globbing functions at all. Just use the fexpanded filename.
804: */
805: gfilename = save(filename);
806: #endif
807: #endif
1.1 etheisen 808: #endif
1.8 millert 809: free(filename);
810: free(ofilename);
1.1 etheisen 811: return (gfilename);
812: }
813:
814: /*
815: * See if we should open a "replacement file"
816: * instead of the file we're about to open.
817: */
818: public char *
819: open_altfile(filename, pf, pfd)
820: char *filename;
821: int *pf;
822: void **pfd;
823: {
1.8 millert 824: #if !HAVE_POPEN
825: return (NULL);
826: #else
1.1 etheisen 827: char *lessopen;
1.10 millert 828: char *cmd, *cp;
1.8 millert 829: FILE *fd;
1.10 millert 830: size_t i, len;
831: int found;
1.8 millert 832: #if HAVE_FILENO
1.1 etheisen 833: int returnfd = 0;
1.8 millert 834: #endif
1.1 etheisen 835:
1.8 millert 836: if (!use_lessopen || secure)
837: return (NULL);
1.1 etheisen 838: ch_ungetchar(-1);
1.8 millert 839: if ((lessopen = lgetenv("LESSOPEN")) == NULL)
1.1 etheisen 840: return (NULL);
841: if (strcmp(filename, "-") == 0)
842: return (NULL);
843: if (*lessopen == '|')
844: {
845: /*
846: * If LESSOPEN starts with a |, it indicates
847: * a "pipe preprocessor".
848: */
1.8 millert 849: #if HAVE_FILENO
1.1 etheisen 850: lessopen++;
851: returnfd = 1;
1.8 millert 852: #else
853: error("LESSOPEN pipe is not supported", NULL_PARG);
854: return (NULL);
855: #endif
1.1 etheisen 856: }
1.8 millert 857:
1.10 millert 858: /* strlen(filename) is guaranteed to be > 0 */
859: len = strlen(lessopen) + strlen(filename);
1.8 millert 860: cmd = (char *) ecalloc(len, sizeof(char));
1.10 millert 861: for (cp = cmd, i = 0, found = 0; i < strlen(lessopen); i++) {
862: if (!found && lessopen[i] == '%' && lessopen[i + 1] == 's') {
863: found = 1;
864: strlcat(cmd, filename, len);
865: cp += strlen(filename);
866: i++;
867: } else
868: *cp++ = lessopen[i];
869: }
1.8 millert 870: fd = shellcmd(cmd);
871: free(cmd);
1.1 etheisen 872: if (fd == NULL)
873: {
874: /*
875: * Cannot create the pipe.
876: */
877: return (NULL);
878: }
1.8 millert 879: #if HAVE_FILENO
1.1 etheisen 880: if (returnfd)
881: {
882: int f;
883: char c;
884:
885: /*
886: * Read one char to see if the pipe will produce any data.
887: * If it does, push the char back on the pipe.
888: */
889: f = fileno(fd);
1.8 millert 890: SET_BINARY(f);
1.1 etheisen 891: if (read(f, &c, 1) != 1)
892: {
893: /*
894: * Pipe is empty. This means there is no alt file.
895: */
896: pclose(fd);
897: return (NULL);
898: }
899: ch_ungetchar(c);
900: *pfd = (void *) fd;
901: *pf = f;
902: return (save("-"));
1.8 millert 903: }
1.1 etheisen 904: #endif
1.8 millert 905: cmd = readfd(fd);
1.1 etheisen 906: pclose(fd);
1.8 millert 907: if (*cmd == '\0')
1.1 etheisen 908: /*
909: * Pipe is empty. This means there is no alt file.
910: */
911: return (NULL);
1.8 millert 912: return (cmd);
913: #endif /* HAVE_POPEN */
1.1 etheisen 914: }
915:
916: /*
917: * Close a replacement file.
918: */
919: public void
920: close_altfile(altfilename, filename, pipefd)
921: char *altfilename;
922: char *filename;
923: void *pipefd;
924: {
1.8 millert 925: #if HAVE_POPEN
1.1 etheisen 926: char *lessclose;
927: FILE *fd;
1.10 millert 928: char *cmd, *cp;
929: size_t i, len;
930: int found;
1.1 etheisen 931:
1.8 millert 932: if (secure)
933: return;
1.1 etheisen 934: if (pipefd != NULL)
1.8 millert 935: {
936: #if OS2
937: /*
938: * The pclose function of OS/2 emx sometimes fails.
939: * Send SIGINT to the piped process before closing it.
940: */
941: kill(((FILE*)pipefd)->_pid, SIGINT);
942: #endif
1.1 etheisen 943: pclose((FILE*) pipefd);
1.8 millert 944: }
945: if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
1.1 etheisen 946: return;
1.10 millert 947: /* strlen(filename) is guaranteed to be > 0 */
948: len = strlen(lessclose) + strlen(filename) + strlen(altfilename);
1.8 millert 949: cmd = (char *) ecalloc(len, sizeof(char));
1.10 millert 950: for (cp = cmd, i = 0, found = 0; i < strlen(lessclose); i++) {
951: if (found < 2 && lessclose[i] == '%' && lessclose[i + 1] == 's') {
952: if (++found == 1) {
953: strlcat(cmd, filename, len);
954: cp += strlen(filename);
955: } else {
956: strlcat(cmd, altfilename, len);
957: cp += strlen(altfilename);
958: }
959: i++;
960: } else
961: *cp++ = lessclose[i];
962: }
1.8 millert 963: fd = shellcmd(cmd);
964: free(cmd);
965: if (fd != NULL)
966: pclose(fd);
967: #endif
1.1 etheisen 968: }
969:
1.8 millert 970: /*
971: * Is the specified file a directory?
972: */
973: public int
974: is_dir(filename)
1.1 etheisen 975: char *filename;
976: {
1.8 millert 977: int isdir = 0;
978:
979: filename = shell_unquote(filename);
980: #if HAVE_STAT
1.1 etheisen 981: {
1.8 millert 982: int r;
983: struct stat statbuf;
1.1 etheisen 984:
1.8 millert 985: r = stat(filename, &statbuf);
986: isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
1.1 etheisen 987: }
988: #else
1.8 millert 989: #ifdef _OSK
1.1 etheisen 990: {
1.8 millert 991: register int f;
1.1 etheisen 992:
1.8 millert 993: f = open(filename, S_IREAD | S_IFDIR);
994: if (f >= 0)
995: close(f);
996: isdir = (f >= 0);
1.1 etheisen 997: }
998: #endif
999: #endif
1.8 millert 1000: free(filename);
1001: return (isdir);
1002: }
1.1 etheisen 1003:
1004: /*
1005: * Returns NULL if the file can be opened and
1006: * is an ordinary file, otherwise an error message
1007: * (if it cannot be opened or is a directory, etc.)
1008: */
1009: public char *
1010: bad_file(filename)
1011: char *filename;
1012: {
1.8 millert 1013: register char *m = NULL;
1014: size_t len;
1.1 etheisen 1015:
1.8 millert 1016: filename = shell_unquote(filename);
1017: if (is_dir(filename))
1.1 etheisen 1018: {
1.8 millert 1019: static char is_a_dir[] = " is a directory";
1.5 deraadt 1020:
1.8 millert 1021: len = strlen(filename) + sizeof(is_a_dir);
1.5 deraadt 1022: m = (char *) ecalloc(len, sizeof(char));
1023: strlcpy(m, filename, len);
1.8 millert 1024: strlcat(m, is_a_dir, len);
1025: } else
1.1 etheisen 1026: {
1.8 millert 1027: #if HAVE_STAT
1028: int r;
1029: struct stat statbuf;
1.5 deraadt 1030:
1.8 millert 1031: r = stat(filename, &statbuf);
1032: if (r < 0)
1033: {
1034: m = errno_message(filename);
1035: } else if (force_open)
1036: {
1037: m = NULL;
1038: } else if (!S_ISREG(statbuf.st_mode))
1039: {
1040: static char not_reg[] = " is not a regular file (use -f to see it)";
1041: len = strlen(filename) + sizeof(not_reg);
1042: m = (char *) ecalloc(len, sizeof(char));
1043: strlcpy(m, filename, len);
1044: strlcat(m, not_reg, len);
1045: }
1046: #endif
1.1 etheisen 1047: }
1.8 millert 1048: free(filename);
1049: return (m);
1.1 etheisen 1050: }
1051:
1052: /*
1053: * Return the size of a file, as cheaply as possible.
1054: * In Unix, we can stat the file.
1055: */
1056: public POSITION
1057: filesize(f)
1058: int f;
1059: {
1.8 millert 1060: #if HAVE_STAT
1.1 etheisen 1061: struct stat statbuf;
1062:
1.8 millert 1063: if (fstat(f, &statbuf) >= 0)
1064: return ((POSITION) statbuf.st_size);
1065: #else
1066: #ifdef _OSK
1067: long size;
1.1 etheisen 1068:
1.8 millert 1069: if ((size = (long) _gs_size(f)) >= 0)
1070: return ((POSITION) size);
1071: #endif
1072: #endif
1073: return (seek_filesize(f));
1.1 etheisen 1074: }
1075:
1076: /*
1.8 millert 1077: *
1.1 etheisen 1078: */
1079: public char *
1.8 millert 1080: shell_coption()
1.1 etheisen 1081: {
1.8 millert 1082: return ("-c");
1.1 etheisen 1083: }