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