Annotation of src/usr.bin/less/filename.c, Revision 1.14
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:
1.14 ! deraadt 259: #ifndef SMALL_PROGRAM
1.1 etheisen 260: /*
261: * Return the full pathname of the given file in the "home directory".
262: */
263: public char *
264: homefile(filename)
265: char *filename;
266: {
1.8 millert 267: register char *pathname;
1.1 etheisen 268:
269: /*
270: * Try $HOME/filename.
271: */
1.8 millert 272: pathname = dirfile(lgetenv("HOME"), filename);
1.1 etheisen 273: if (pathname != NULL)
274: return (pathname);
275: #if OS2
276: /*
277: * Try $INIT/filename.
278: */
1.8 millert 279: pathname = dirfile(lgetenv("INIT"), filename);
1.1 etheisen 280: if (pathname != NULL)
281: return (pathname);
282: #endif
1.8 millert 283: #if MSDOS_COMPILER || OS2
1.1 etheisen 284: /*
285: * Look for the file anywhere on search path.
286: */
287: pathname = (char *) calloc(_MAX_PATH, sizeof(char));
1.8 millert 288: #if MSDOS_COMPILER==DJGPPC
289: {
290: char *res = searchpath(filename);
291: if (res == 0)
292: *pathname = '\0';
293: else
294: strlcpy(pathname, res, _MAX_PATH);
295: }
296: #else
1.1 etheisen 297: _searchenv(filename, "PATH", pathname);
1.8 millert 298: #endif
1.1 etheisen 299: if (*pathname != '\0')
300: return (pathname);
301: free(pathname);
302: #endif
303: return (NULL);
304: }
1.14 ! deraadt 305: #endif /* SMALL_PROGRAM */
1.1 etheisen 306:
1.9 millert 307: #ifdef HELPFILE
308: /*
309: * Find out where the help file is.
310: */
311: public char *
312: find_helpfile()
313: {
314: char *helpfile;
315:
316: if ((helpfile = getenv("LESSHELP")) != NULL && *helpfile != '\0')
317: return (save(helpfile));
318: #if MSDOS_COMPILER || OS2
319: return (homefile(HELPFILE));
320: #else
321: return (save(HELPFILE));
322: #endif
323: }
324: #endif
325:
1.1 etheisen 326: /*
327: * Expand a string, substituting any "%" with the current filename,
328: * and any "#" with the previous filename.
1.8 millert 329: * But a string of N "%"s is just replaced with N-1 "%"s.
330: * Likewise for a string of N "#"s.
1.1 etheisen 331: * {{ This is a lot of work just to support % and #. }}
332: */
333: public char *
334: fexpand(s)
335: char *s;
336: {
1.8 millert 337: register char *fr, *to;
338: register int n;
339: register char *e;
340: IFILE ifile;
341:
342: #define fchar_ifile(c) \
343: ((c) == '%' ? curr_ifile : \
344: (c) == '#' ? old_ifile : NULL_IFILE)
1.1 etheisen 345:
346: /*
347: * Make one pass to see how big a buffer we
348: * need to allocate for the expanded string.
349: */
350: n = 0;
351: for (fr = s; *fr != '\0'; fr++)
352: {
353: switch (*fr)
354: {
355: case '%':
1.8 millert 356: case '#':
357: if (fr > s && fr[-1] == *fr)
1.1 etheisen 358: {
1.8 millert 359: /*
360: * Second (or later) char in a string
361: * of identical chars. Treat as normal.
362: */
363: n++;
364: } else if (fr[1] != *fr)
1.1 etheisen 365: {
1.8 millert 366: /*
367: * Single char (not repeated). Treat specially.
368: */
369: ifile = fchar_ifile(*fr);
370: if (ifile == NULL_IFILE)
371: n++;
372: else
373: n += strlen(get_filename(ifile));
1.1 etheisen 374: }
1.8 millert 375: /*
376: * Else it is the first char in a string of
377: * identical chars. Just discard it.
378: */
1.1 etheisen 379: break;
380: default:
381: n++;
382: break;
383: }
384: }
385:
386: e = (char *) ecalloc(n+1, sizeof(char));
387:
388: /*
389: * Now copy the string, expanding any "%" or "#".
390: */
391: to = e;
392: for (fr = s; *fr != '\0'; fr++)
393: {
394: switch (*fr)
395: {
396: case '%':
397: case '#':
1.8 millert 398: if (fr > s && fr[-1] == *fr)
399: {
400: *to++ = *fr;
401: } else if (fr[1] != *fr)
402: {
403: ifile = fchar_ifile(*fr);
404: if (ifile == NULL_IFILE)
405: *to++ = *fr;
406: else
407: {
408: strlcpy(to, get_filename(ifile),
409: e + n + 1 - to);
410: to += strlen(to);
411: }
412: }
1.1 etheisen 413: break;
414: default:
415: *to++ = *fr;
416: break;
417: }
418: }
419: *to = '\0';
420: return (e);
421: }
422:
423: #if TAB_COMPLETE_FILENAME
424:
425: /*
426: * Return a blank-separated list of filenames which "complete"
427: * the given string.
428: */
429: public char *
430: fcomplete(s)
431: char *s;
432: {
433: char *fpat;
1.8 millert 434: char *qs;
435: size_t len;
1.7 deraadt 436:
1.8 millert 437: if (secure)
438: return (NULL);
1.1 etheisen 439: /*
440: * Complete the filename "s" by globbing "s*".
441: */
1.8 millert 442: #if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC)
1.1 etheisen 443: /*
444: * But in DOS, we have to glob "s*.*".
445: * But if the final component of the filename already has
446: * a dot in it, just do "s*".
447: * (Thus, "FILE" is globbed as "FILE*.*",
448: * but "FILE.A" is globbed as "FILE.A*").
449: */
1.8 millert 450: {
451: char *slash;
452: for (slash = s+strlen(s)-1; slash > s; slash--)
453: if (*slash == *PATHNAME_SEP || *slash == '/')
454: break;
455: len = strlen(s) + 4;
456: fpat = (char *) ecalloc(len, sizeof(char));
457: if (strchr(slash, '.') == NULL)
458: snprintf(fpat, len, "%s*.*", s);
459: else
460: snprintf(fpat, len, "%s*", s);
461: }
1.1 etheisen 462: #else
1.8 millert 463: len = strlen(s) + 2;
464: fpat = (char *) ecalloc(len, sizeof(char));
465: snprintf(fpat, len, "%s*", s);
1.1 etheisen 466: #endif
1.8 millert 467: qs = lglob(fpat);
468: s = shell_unquote(qs);
1.1 etheisen 469: if (strcmp(s,fpat) == 0)
470: {
471: /*
472: * The filename didn't expand.
473: */
1.8 millert 474: free(qs);
475: qs = NULL;
1.1 etheisen 476: }
1.8 millert 477: free(s);
1.1 etheisen 478: free(fpat);
1.8 millert 479: return (qs);
1.1 etheisen 480: }
481: #endif
482:
483: /*
484: * Try to determine if a file is "binary".
485: * This is just a guess, and we need not try too hard to make it accurate.
486: */
487: public int
488: bin_file(f)
489: int f;
490: {
491: int i;
492: int n;
493: unsigned char data[64];
494:
495: if (!seekable(f))
496: return (0);
1.11 deraadt 497: if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
1.1 etheisen 498: return (0);
499: n = read(f, data, sizeof(data));
500: for (i = 0; i < n; i++)
501: if (binary_char(data[i]))
502: return (1);
503: return (0);
504: }
505:
506: /*
507: * Try to determine the size of a file by seeking to the end.
508: */
509: static POSITION
510: seek_filesize(f)
511: int f;
512: {
513: off_t spos;
514:
1.11 deraadt 515: spos = lseek(f, (off_t)0, SEEK_END);
1.1 etheisen 516: if (spos == BAD_LSEEK)
517: return (NULL_POSITION);
518: return ((POSITION) spos);
519: }
520:
521: /*
522: * Read a string from a file.
523: * Return a pointer to the string in memory.
524: */
525: static char *
526: readfd(fd)
527: FILE *fd;
528: {
529: int len;
530: int ch;
531: char *buf;
532: char *p;
533:
534: /*
535: * Make a guess about how many chars in the string
536: * and allocate a buffer to hold it.
537: */
538: len = 100;
539: buf = (char *) ecalloc(len, sizeof(char));
540: for (p = buf; ; p++)
541: {
542: if ((ch = getc(fd)) == '\n' || ch == EOF)
543: break;
544: if (p - buf >= len-1)
545: {
546: /*
547: * The string is too big to fit in the buffer we have.
548: * Allocate a new buffer, twice as big.
549: */
550: len *= 2;
551: *p = '\0';
552: p = (char *) ecalloc(len, sizeof(char));
1.5 deraadt 553: strlcpy(p, buf, len);
1.1 etheisen 554: free(buf);
555: buf = p;
556: p = buf + strlen(buf);
557: }
558: *p = ch;
559: }
560: *p = '\0';
561: return (buf);
562: }
563:
1.8 millert 564:
565:
566: #if HAVE_POPEN
567:
568: FILE *popen();
569:
1.1 etheisen 570: /*
571: * Execute a shell command.
572: * Return a pointer to a pipe connected to the shell command's standard output.
573: */
574: static FILE *
1.8 millert 575: shellcmd(cmd)
1.1 etheisen 576: char *cmd;
577: {
578: FILE *fd;
1.8 millert 579: size_t len;
580:
1.1 etheisen 581: #if HAVE_SHELL
1.8 millert 582: char *shell;
583:
584: shell = lgetenv("SHELL");
1.1 etheisen 585: if (shell != NULL && *shell != '\0')
586: {
1.8 millert 587: char *scmd;
588: char *esccmd;
589:
1.1 etheisen 590: /*
1.8 millert 591: * Read the output of <$SHELL -c cmd>.
592: * Escape any metacharacters in the command.
1.1 etheisen 593: */
1.8 millert 594: esccmd = shell_quote(cmd);
595: if (esccmd == NULL)
596: {
597: fd = popen(cmd, "r");
598: } else
599: {
600: len = strlen(shell) + strlen(esccmd) + 5;
601: scmd = (char *) ecalloc(len, sizeof(char));
602: snprintf(scmd, len, "%s %s %s", shell, shell_coption(),
603: esccmd);
604: free(esccmd);
605: fd = popen(scmd, "r");
606: free(scmd);
607: }
608: } else
609: #endif
610: {
611: fd = popen(cmd, "r");
1.1 etheisen 612: }
1.8 millert 613: /*
614: * Redirection in `popen' might have messed with the
615: * standard devices. Restore binary input mode.
616: */
617: SET_BINARY(0);
1.1 etheisen 618: return (fd);
619: }
620:
1.8 millert 621: #endif /* HAVE_POPEN */
622:
623:
1.1 etheisen 624: /*
1.8 millert 625: * Expand a filename, doing any system-specific metacharacter substitutions.
1.1 etheisen 626: */
627: public char *
1.8 millert 628: lglob(filename)
1.1 etheisen 629: char *filename;
630: {
631: char *gfilename;
1.8 millert 632: char *ofilename;
1.1 etheisen 633:
1.8 millert 634: ofilename = fexpand(filename);
635: if (secure)
636: return (ofilename);
637: filename = shell_unquote(ofilename);
638:
639: #ifdef DECL_GLOB_LIST
1.1 etheisen 640: {
1.8 millert 641: /*
642: * The globbing function returns a list of names.
643: */
1.1 etheisen 644: int length;
1.8 millert 645: char *p;
646: char *qfilename;
647: DECL_GLOB_LIST(list)
1.1 etheisen 648:
1.8 millert 649: GLOB_LIST(filename, list);
650: if (GLOB_LIST_FAILED(list))
651: {
652: free(filename);
653: return (ofilename);
654: }
655: length = 1; /* Room for trailing null byte */
656: for (SCAN_GLOB_LIST(list, p))
657: {
658: INIT_GLOB_LIST(list, p);
659: qfilename = shell_quote(p);
660: if (qfilename != NULL)
661: {
662: length += strlen(qfilename) + 1;
663: free(qfilename);
664: }
665: }
1.1 etheisen 666: gfilename = (char *) ecalloc(length, sizeof(char));
1.8 millert 667: for (SCAN_GLOB_LIST(list, p))
1.1 etheisen 668: {
1.8 millert 669: INIT_GLOB_LIST(list, p);
670: qfilename = shell_quote(p);
671: if (qfilename != NULL)
672: {
673: snprintf(gfilename + strlen(gfilename),
674: length - strlen(gfilename), "%s ", qfilename);
675: free(qfilename);
676: }
1.1 etheisen 677: }
1.8 millert 678: /*
679: * Overwrite the final trailing space with a null terminator.
680: */
1.13 ray 681: if (gfilename[0] != '\0' && gfilename[strlen(gfilename) - 1] == ' ')
682: gfilename[strlen(gfilename) - 1] = '\0';
1.8 millert 683: GLOB_LIST_DONE(list);
1.1 etheisen 684: }
685: #else
1.8 millert 686: #ifdef DECL_GLOB_NAME
1.1 etheisen 687: {
1.8 millert 688: /*
689: * The globbing function returns a single name, and
690: * is called multiple times to walk thru all names.
691: */
692: register char *p;
693: register int len;
694: register int n;
695: char *pathname;
696: char *qpathname;
697: DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
698:
699: GLOB_FIRST_NAME(filename, &fnd, handle);
700: if (GLOB_FIRST_FAILED(handle))
701: {
702: free(filename);
703: return (ofilename);
704: }
705:
706: _splitpath(filename, drive, dir, fname, ext);
707: len = 100;
708: gfilename = (char *) ecalloc(len, sizeof(char));
709: p = gfilename;
710: do {
711: n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
712: pathname = (char *) ecalloc(n, sizeof(char));
713: snprintf(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME);
714: qpathname = shell_quote(pathname);
715: free(pathname);
716: if (qpathname != NULL)
717: {
718: n = strlen(qpathname);
719: while (p - gfilename + n + 2 >= len)
720: {
721: /*
722: * No room in current buffer.
723: * Allocate a bigger one.
724: */
725: len *= 2;
726: *p = '\0';
727: p = (char *) ecalloc(len, sizeof(char));
728: strlcpy(p, gfilename, len);
729: free(gfilename);
730: gfilename = p;
731: p = gfilename + strlen(gfilename);
732: }
733: strlcpy(p, qpathname, gfilename + len - p);
734: free(qpathname);
735: p += n;
736: *p++ = ' ';
737: }
738: } while (GLOB_NEXT_NAME(handle, &fnd) == 0);
739:
740: /*
741: * Overwrite the final trailing space with a null terminator.
742: */
743: *--p = '\0';
744: GLOB_NAME_DONE(handle);
745: }
746: #else
747: #if HAVE_POPEN
748: {
749: /*
750: * We get the shell to glob the filename for us by passing
751: * an "echo" command to the shell and reading its output.
752: */
1.1 etheisen 753: FILE *fd;
1.8 millert 754: char *s;
755: char *lessecho;
756: char *cmd;
757: char *esc;
758: size_t len;
1.1 etheisen 759:
1.8 millert 760: esc = get_meta_escape();
761: if (strlen(esc) == 0)
762: esc = "-";
763: esc = shell_quote(esc);
764: if (esc == NULL)
765: {
766: free(filename);
767: return (ofilename);
768: }
769: lessecho = lgetenv("LESSECHO");
770: if (lessecho == NULL || *lessecho == '\0')
771: lessecho = "lessecho";
1.1 etheisen 772: /*
1.8 millert 773: * Invoke lessecho, and read its output (a globbed list of filenames).
1.1 etheisen 774: */
1.8 millert 775: len = strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24;
776: cmd = (char *) ecalloc(len, sizeof(char));
777: snprintf(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote,
778: closequote, esc);
779: free(esc);
780: for (s = metachars(); *s != '\0'; s++)
781: snprintf(cmd + strlen(cmd), len - strlen(cmd), "-n0x%x ", *s);
782: snprintf(cmd + strlen(cmd), len - strlen(cmd), "-- %s", ofilename);
783: fd = shellcmd(cmd);
784: free(cmd);
1.1 etheisen 785: if (fd == NULL)
786: {
787: /*
788: * Cannot create the pipe.
789: * Just return the original (fexpanded) filename.
790: */
1.8 millert 791: free(filename);
792: return (ofilename);
1.1 etheisen 793: }
794: gfilename = readfd(fd);
795: pclose(fd);
796: if (*gfilename == '\0')
797: {
798: free(gfilename);
1.8 millert 799: free(filename);
800: return (ofilename);
1.1 etheisen 801: }
802: }
1.8 millert 803: #else
804: /*
805: * No globbing functions at all. Just use the fexpanded filename.
806: */
807: gfilename = save(filename);
808: #endif
809: #endif
1.1 etheisen 810: #endif
1.8 millert 811: free(filename);
812: free(ofilename);
1.1 etheisen 813: return (gfilename);
814: }
815:
816: /*
817: * See if we should open a "replacement file"
818: * instead of the file we're about to open.
819: */
820: public char *
821: open_altfile(filename, pf, pfd)
822: char *filename;
823: int *pf;
824: void **pfd;
825: {
1.8 millert 826: #if !HAVE_POPEN
827: return (NULL);
828: #else
1.1 etheisen 829: char *lessopen;
1.10 millert 830: char *cmd, *cp;
1.8 millert 831: FILE *fd;
1.10 millert 832: size_t i, len;
833: int found;
1.8 millert 834: #if HAVE_FILENO
1.1 etheisen 835: int returnfd = 0;
1.8 millert 836: #endif
1.1 etheisen 837:
1.8 millert 838: if (!use_lessopen || secure)
839: return (NULL);
1.1 etheisen 840: ch_ungetchar(-1);
1.8 millert 841: if ((lessopen = lgetenv("LESSOPEN")) == NULL)
1.1 etheisen 842: return (NULL);
843: if (strcmp(filename, "-") == 0)
844: return (NULL);
845: if (*lessopen == '|')
846: {
847: /*
848: * If LESSOPEN starts with a |, it indicates
849: * a "pipe preprocessor".
850: */
1.8 millert 851: #if HAVE_FILENO
1.1 etheisen 852: lessopen++;
853: returnfd = 1;
1.8 millert 854: #else
855: error("LESSOPEN pipe is not supported", NULL_PARG);
856: return (NULL);
857: #endif
1.1 etheisen 858: }
1.8 millert 859:
1.10 millert 860: /* strlen(filename) is guaranteed to be > 0 */
861: len = strlen(lessopen) + strlen(filename);
1.8 millert 862: cmd = (char *) ecalloc(len, sizeof(char));
1.10 millert 863: for (cp = cmd, i = 0, found = 0; i < strlen(lessopen); i++) {
864: if (!found && lessopen[i] == '%' && lessopen[i + 1] == 's') {
865: found = 1;
866: strlcat(cmd, filename, len);
867: cp += strlen(filename);
868: i++;
869: } else
870: *cp++ = lessopen[i];
871: }
1.8 millert 872: fd = shellcmd(cmd);
873: free(cmd);
1.1 etheisen 874: if (fd == NULL)
875: {
876: /*
877: * Cannot create the pipe.
878: */
879: return (NULL);
880: }
1.8 millert 881: #if HAVE_FILENO
1.1 etheisen 882: if (returnfd)
883: {
884: int f;
885: char c;
886:
887: /*
888: * Read one char to see if the pipe will produce any data.
889: * If it does, push the char back on the pipe.
890: */
891: f = fileno(fd);
1.8 millert 892: SET_BINARY(f);
1.1 etheisen 893: if (read(f, &c, 1) != 1)
894: {
895: /*
896: * Pipe is empty. This means there is no alt file.
897: */
898: pclose(fd);
899: return (NULL);
900: }
901: ch_ungetchar(c);
902: *pfd = (void *) fd;
903: *pf = f;
904: return (save("-"));
1.8 millert 905: }
1.1 etheisen 906: #endif
1.8 millert 907: cmd = readfd(fd);
1.1 etheisen 908: pclose(fd);
1.8 millert 909: if (*cmd == '\0')
1.1 etheisen 910: /*
911: * Pipe is empty. This means there is no alt file.
912: */
913: return (NULL);
1.8 millert 914: return (cmd);
915: #endif /* HAVE_POPEN */
1.1 etheisen 916: }
917:
918: /*
919: * Close a replacement file.
920: */
921: public void
922: close_altfile(altfilename, filename, pipefd)
923: char *altfilename;
924: char *filename;
925: void *pipefd;
926: {
1.8 millert 927: #if HAVE_POPEN
1.1 etheisen 928: char *lessclose;
929: FILE *fd;
1.10 millert 930: char *cmd, *cp;
931: size_t i, len;
932: int found;
1.1 etheisen 933:
1.8 millert 934: if (secure)
935: return;
1.1 etheisen 936: if (pipefd != NULL)
1.8 millert 937: {
938: #if OS2
939: /*
940: * The pclose function of OS/2 emx sometimes fails.
941: * Send SIGINT to the piped process before closing it.
942: */
943: kill(((FILE*)pipefd)->_pid, SIGINT);
944: #endif
1.1 etheisen 945: pclose((FILE*) pipefd);
1.8 millert 946: }
947: if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
1.1 etheisen 948: return;
1.10 millert 949: /* strlen(filename) is guaranteed to be > 0 */
950: len = strlen(lessclose) + strlen(filename) + strlen(altfilename);
1.8 millert 951: cmd = (char *) ecalloc(len, sizeof(char));
1.10 millert 952: for (cp = cmd, i = 0, found = 0; i < strlen(lessclose); i++) {
953: if (found < 2 && lessclose[i] == '%' && lessclose[i + 1] == 's') {
954: if (++found == 1) {
955: strlcat(cmd, filename, len);
956: cp += strlen(filename);
957: } else {
958: strlcat(cmd, altfilename, len);
959: cp += strlen(altfilename);
960: }
961: i++;
962: } else
963: *cp++ = lessclose[i];
964: }
1.8 millert 965: fd = shellcmd(cmd);
966: free(cmd);
967: if (fd != NULL)
968: pclose(fd);
969: #endif
1.1 etheisen 970: }
971:
1.8 millert 972: /*
973: * Is the specified file a directory?
974: */
975: public int
976: is_dir(filename)
1.1 etheisen 977: char *filename;
978: {
1.8 millert 979: int isdir = 0;
980:
981: filename = shell_unquote(filename);
982: #if HAVE_STAT
1.1 etheisen 983: {
1.8 millert 984: int r;
985: struct stat statbuf;
1.1 etheisen 986:
1.8 millert 987: r = stat(filename, &statbuf);
988: isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
1.1 etheisen 989: }
990: #else
1.8 millert 991: #ifdef _OSK
1.1 etheisen 992: {
1.8 millert 993: register int f;
1.1 etheisen 994:
1.8 millert 995: f = open(filename, S_IREAD | S_IFDIR);
996: if (f >= 0)
997: close(f);
998: isdir = (f >= 0);
1.1 etheisen 999: }
1000: #endif
1001: #endif
1.8 millert 1002: free(filename);
1003: return (isdir);
1004: }
1.1 etheisen 1005:
1006: /*
1007: * Returns NULL if the file can be opened and
1008: * is an ordinary file, otherwise an error message
1009: * (if it cannot be opened or is a directory, etc.)
1010: */
1011: public char *
1012: bad_file(filename)
1013: char *filename;
1014: {
1.8 millert 1015: register char *m = NULL;
1016: size_t len;
1.1 etheisen 1017:
1.8 millert 1018: filename = shell_unquote(filename);
1019: if (is_dir(filename))
1.1 etheisen 1020: {
1.8 millert 1021: static char is_a_dir[] = " is a directory";
1.5 deraadt 1022:
1.8 millert 1023: len = strlen(filename) + sizeof(is_a_dir);
1.5 deraadt 1024: m = (char *) ecalloc(len, sizeof(char));
1025: strlcpy(m, filename, len);
1.8 millert 1026: strlcat(m, is_a_dir, len);
1027: } else
1.1 etheisen 1028: {
1.8 millert 1029: #if HAVE_STAT
1030: int r;
1031: struct stat statbuf;
1.5 deraadt 1032:
1.8 millert 1033: r = stat(filename, &statbuf);
1034: if (r < 0)
1035: {
1036: m = errno_message(filename);
1037: } else if (force_open)
1038: {
1039: m = NULL;
1040: } else if (!S_ISREG(statbuf.st_mode))
1041: {
1042: static char not_reg[] = " is not a regular file (use -f to see it)";
1043: len = strlen(filename) + sizeof(not_reg);
1044: m = (char *) ecalloc(len, sizeof(char));
1045: strlcpy(m, filename, len);
1046: strlcat(m, not_reg, len);
1047: }
1048: #endif
1.1 etheisen 1049: }
1.8 millert 1050: free(filename);
1051: return (m);
1.1 etheisen 1052: }
1053:
1054: /*
1055: * Return the size of a file, as cheaply as possible.
1056: * In Unix, we can stat the file.
1057: */
1058: public POSITION
1059: filesize(f)
1060: int f;
1061: {
1.8 millert 1062: #if HAVE_STAT
1.1 etheisen 1063: struct stat statbuf;
1064:
1.8 millert 1065: if (fstat(f, &statbuf) >= 0)
1066: return ((POSITION) statbuf.st_size);
1067: #else
1068: #ifdef _OSK
1069: long size;
1.1 etheisen 1070:
1.8 millert 1071: if ((size = (long) _gs_size(f)) >= 0)
1072: return ((POSITION) size);
1073: #endif
1074: #endif
1075: return (seek_filesize(f));
1.1 etheisen 1076: }
1077:
1078: /*
1.8 millert 1079: *
1.1 etheisen 1080: */
1081: public char *
1.8 millert 1082: shell_coption()
1.1 etheisen 1083: {
1.8 millert 1084: return ("-c");
1.1 etheisen 1085: }