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