Annotation of src/usr.bin/less/filename.c, Revision 1.11
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: */
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.10 millert 827: char *cmd, *cp;
1.8 millert 828: FILE *fd;
1.10 millert 829: size_t i, len;
830: int found;
1.8 millert 831: #if HAVE_FILENO
1.1 etheisen 832: int returnfd = 0;
1.8 millert 833: #endif
1.1 etheisen 834:
1.8 millert 835: if (!use_lessopen || secure)
836: return (NULL);
1.1 etheisen 837: ch_ungetchar(-1);
1.8 millert 838: if ((lessopen = lgetenv("LESSOPEN")) == NULL)
1.1 etheisen 839: return (NULL);
840: if (strcmp(filename, "-") == 0)
841: return (NULL);
842: if (*lessopen == '|')
843: {
844: /*
845: * If LESSOPEN starts with a |, it indicates
846: * a "pipe preprocessor".
847: */
1.8 millert 848: #if HAVE_FILENO
1.1 etheisen 849: lessopen++;
850: returnfd = 1;
1.8 millert 851: #else
852: error("LESSOPEN pipe is not supported", NULL_PARG);
853: return (NULL);
854: #endif
1.1 etheisen 855: }
1.8 millert 856:
1.10 millert 857: /* strlen(filename) is guaranteed to be > 0 */
858: len = strlen(lessopen) + strlen(filename);
1.8 millert 859: cmd = (char *) ecalloc(len, sizeof(char));
1.10 millert 860: for (cp = cmd, i = 0, found = 0; i < strlen(lessopen); i++) {
861: if (!found && lessopen[i] == '%' && lessopen[i + 1] == 's') {
862: found = 1;
863: strlcat(cmd, filename, len);
864: cp += strlen(filename);
865: i++;
866: } else
867: *cp++ = lessopen[i];
868: }
1.8 millert 869: fd = shellcmd(cmd);
870: free(cmd);
1.1 etheisen 871: if (fd == NULL)
872: {
873: /*
874: * Cannot create the pipe.
875: */
876: return (NULL);
877: }
1.8 millert 878: #if HAVE_FILENO
1.1 etheisen 879: if (returnfd)
880: {
881: int f;
882: char c;
883:
884: /*
885: * Read one char to see if the pipe will produce any data.
886: * If it does, push the char back on the pipe.
887: */
888: f = fileno(fd);
1.8 millert 889: SET_BINARY(f);
1.1 etheisen 890: if (read(f, &c, 1) != 1)
891: {
892: /*
893: * Pipe is empty. This means there is no alt file.
894: */
895: pclose(fd);
896: return (NULL);
897: }
898: ch_ungetchar(c);
899: *pfd = (void *) fd;
900: *pf = f;
901: return (save("-"));
1.8 millert 902: }
1.1 etheisen 903: #endif
1.8 millert 904: cmd = readfd(fd);
1.1 etheisen 905: pclose(fd);
1.8 millert 906: if (*cmd == '\0')
1.1 etheisen 907: /*
908: * Pipe is empty. This means there is no alt file.
909: */
910: return (NULL);
1.8 millert 911: return (cmd);
912: #endif /* HAVE_POPEN */
1.1 etheisen 913: }
914:
915: /*
916: * Close a replacement file.
917: */
918: public void
919: close_altfile(altfilename, filename, pipefd)
920: char *altfilename;
921: char *filename;
922: void *pipefd;
923: {
1.8 millert 924: #if HAVE_POPEN
1.1 etheisen 925: char *lessclose;
926: FILE *fd;
1.10 millert 927: char *cmd, *cp;
928: size_t i, len;
929: int found;
1.1 etheisen 930:
1.8 millert 931: if (secure)
932: return;
1.1 etheisen 933: if (pipefd != NULL)
1.8 millert 934: {
935: #if OS2
936: /*
937: * The pclose function of OS/2 emx sometimes fails.
938: * Send SIGINT to the piped process before closing it.
939: */
940: kill(((FILE*)pipefd)->_pid, SIGINT);
941: #endif
1.1 etheisen 942: pclose((FILE*) pipefd);
1.8 millert 943: }
944: if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
1.1 etheisen 945: return;
1.10 millert 946: /* strlen(filename) is guaranteed to be > 0 */
947: len = strlen(lessclose) + strlen(filename) + strlen(altfilename);
1.8 millert 948: cmd = (char *) ecalloc(len, sizeof(char));
1.10 millert 949: for (cp = cmd, i = 0, found = 0; i < strlen(lessclose); i++) {
950: if (found < 2 && lessclose[i] == '%' && lessclose[i + 1] == 's') {
951: if (++found == 1) {
952: strlcat(cmd, filename, len);
953: cp += strlen(filename);
954: } else {
955: strlcat(cmd, altfilename, len);
956: cp += strlen(altfilename);
957: }
958: i++;
959: } else
960: *cp++ = lessclose[i];
961: }
1.8 millert 962: fd = shellcmd(cmd);
963: free(cmd);
964: if (fd != NULL)
965: pclose(fd);
966: #endif
1.1 etheisen 967: }
968:
1.8 millert 969: /*
970: * Is the specified file a directory?
971: */
972: public int
973: is_dir(filename)
1.1 etheisen 974: char *filename;
975: {
1.8 millert 976: int isdir = 0;
977:
978: filename = shell_unquote(filename);
979: #if HAVE_STAT
1.1 etheisen 980: {
1.8 millert 981: int r;
982: struct stat statbuf;
1.1 etheisen 983:
1.8 millert 984: r = stat(filename, &statbuf);
985: isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
1.1 etheisen 986: }
987: #else
1.8 millert 988: #ifdef _OSK
1.1 etheisen 989: {
1.8 millert 990: register int f;
1.1 etheisen 991:
1.8 millert 992: f = open(filename, S_IREAD | S_IFDIR);
993: if (f >= 0)
994: close(f);
995: isdir = (f >= 0);
1.1 etheisen 996: }
997: #endif
998: #endif
1.8 millert 999: free(filename);
1000: return (isdir);
1001: }
1.1 etheisen 1002:
1003: /*
1004: * Returns NULL if the file can be opened and
1005: * is an ordinary file, otherwise an error message
1006: * (if it cannot be opened or is a directory, etc.)
1007: */
1008: public char *
1009: bad_file(filename)
1010: char *filename;
1011: {
1.8 millert 1012: register char *m = NULL;
1013: size_t len;
1.1 etheisen 1014:
1.8 millert 1015: filename = shell_unquote(filename);
1016: if (is_dir(filename))
1.1 etheisen 1017: {
1.8 millert 1018: static char is_a_dir[] = " is a directory";
1.5 deraadt 1019:
1.8 millert 1020: len = strlen(filename) + sizeof(is_a_dir);
1.5 deraadt 1021: m = (char *) ecalloc(len, sizeof(char));
1022: strlcpy(m, filename, len);
1.8 millert 1023: strlcat(m, is_a_dir, len);
1024: } else
1.1 etheisen 1025: {
1.8 millert 1026: #if HAVE_STAT
1027: int r;
1028: struct stat statbuf;
1.5 deraadt 1029:
1.8 millert 1030: r = stat(filename, &statbuf);
1031: if (r < 0)
1032: {
1033: m = errno_message(filename);
1034: } else if (force_open)
1035: {
1036: m = NULL;
1037: } else if (!S_ISREG(statbuf.st_mode))
1038: {
1039: static char not_reg[] = " is not a regular file (use -f to see it)";
1040: len = strlen(filename) + sizeof(not_reg);
1041: m = (char *) ecalloc(len, sizeof(char));
1042: strlcpy(m, filename, len);
1043: strlcat(m, not_reg, len);
1044: }
1045: #endif
1.1 etheisen 1046: }
1.8 millert 1047: free(filename);
1048: return (m);
1.1 etheisen 1049: }
1050:
1051: /*
1052: * Return the size of a file, as cheaply as possible.
1053: * In Unix, we can stat the file.
1054: */
1055: public POSITION
1056: filesize(f)
1057: int f;
1058: {
1.8 millert 1059: #if HAVE_STAT
1.1 etheisen 1060: struct stat statbuf;
1061:
1.8 millert 1062: if (fstat(f, &statbuf) >= 0)
1063: return ((POSITION) statbuf.st_size);
1064: #else
1065: #ifdef _OSK
1066: long size;
1.1 etheisen 1067:
1.8 millert 1068: if ((size = (long) _gs_size(f)) >= 0)
1069: return ((POSITION) size);
1070: #endif
1071: #endif
1072: return (seek_filesize(f));
1.1 etheisen 1073: }
1074:
1075: /*
1.8 millert 1076: *
1.1 etheisen 1077: */
1078: public char *
1.8 millert 1079: shell_coption()
1.1 etheisen 1080: {
1.8 millert 1081: return ("-c");
1.1 etheisen 1082: }