Annotation of src/usr.bin/less/filename.c, Revision 1.1.1.1
1.1 etheisen 1: /*
2: * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice in the documentation and/or other materials provided with
12: * the distribution.
13: *
14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
18: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27:
28: /*
29: * Routines to mess around with filenames (and files).
30: * Much of this is very OS dependent.
31: */
32:
33: #include "less.h"
34: #if MSOFTC
35: #include <dos.h>
36: #endif
37:
38: extern int force_open;
39: extern IFILE curr_ifile;
40: extern IFILE old_ifile;
41:
42: /*
43: * Return a pathname that points to a specified file in a specified directory.
44: * Return NULL if the file does not exist in the directory.
45: */
46: static char *
47: dirfile(dirname, filename)
48: char *dirname;
49: char *filename;
50: {
51: char *pathname;
52: int f;
53:
54: if (dirname == NULL || *dirname == '\0')
55: return (NULL);
56: /*
57: * Construct the full pathname.
58: */
59: pathname = (char *) calloc(strlen(dirname) + strlen(filename) + 2,
60: sizeof(char));
61: if (pathname == NULL)
62: return (NULL);
63: #if MSOFTC || OS2
64: sprintf(pathname, "%s\\%s", dirname, filename);
65: #else
66: sprintf(pathname, "%s/%s", dirname, filename);
67: #endif
68: /*
69: * Make sure the file exists.
70: */
71: f = open(pathname, OPEN_READ);
72: if (f < 0)
73: {
74: free(pathname);
75: pathname = NULL;
76: } else
77: {
78: close (f);
79: }
80: return (pathname);
81: }
82:
83: /*
84: * Return the full pathname of the given file in the "home directory".
85: */
86: public char *
87: homefile(filename)
88: char *filename;
89: {
90: register char *pathname;
91:
92: /*
93: * Try $HOME/filename.
94: */
95: pathname = dirfile(getenv("HOME"), filename);
96: if (pathname != NULL)
97: return (pathname);
98: #if OS2
99: /*
100: * Try $INIT/filename.
101: */
102: pathname = dirfile(getenv("INIT"), filename);
103: if (pathname != NULL)
104: return (pathname);
105: #endif
106: #if MSOFTC || OS2
107: /*
108: * Look for the file anywhere on search path.
109: */
110: pathname = (char *) calloc(_MAX_PATH, sizeof(char));
111: _searchenv(filename, "PATH", pathname);
112: if (*pathname != '\0')
113: return (pathname);
114: free(pathname);
115: #endif
116: return (NULL);
117: }
118:
119: /*
120: * Find out where the help file is.
121: */
122: public char *
123: find_helpfile()
124: {
125: register char *helpfile;
126:
127: if ((helpfile = getenv("LESSHELP")) != NULL)
128: return (save(helpfile));
129: #if MSOFTC || OS2
130: return (homefile(HELPFILE));
131: #else
132: return (save(HELPFILE));
133: #endif
134: }
135:
136: /*
137: * Expand a string, substituting any "%" with the current filename,
138: * and any "#" with the previous filename.
139: * {{ This is a lot of work just to support % and #. }}
140: */
141: public char *
142: fexpand(s)
143: char *s;
144: {
145: register char *fr, *to;
146: register int n;
147: register char *e;
148:
149: /*
150: * Make one pass to see how big a buffer we
151: * need to allocate for the expanded string.
152: */
153: n = 0;
154: for (fr = s; *fr != '\0'; fr++)
155: {
156: switch (*fr)
157: {
158: case '%':
159: if (curr_ifile == NULL_IFILE)
160: {
161: /* error("No current file", NULL_PARG); */
162: return (save(s));
163: }
164: n += strlen(get_filename(curr_ifile));
165: break;
166: case '#':
167: if (old_ifile == NULL_IFILE)
168: {
169: /* error("No previous file", NULL_PARG); */
170: return (save(s));
171: }
172: n += strlen(get_filename(old_ifile));
173: break;
174: default:
175: n++;
176: break;
177: }
178: }
179:
180: e = (char *) ecalloc(n+1, sizeof(char));
181:
182: /*
183: * Now copy the string, expanding any "%" or "#".
184: */
185: to = e;
186: for (fr = s; *fr != '\0'; fr++)
187: {
188: switch (*fr)
189: {
190: case '%':
191: strcpy(to, get_filename(curr_ifile));
192: to += strlen(to);
193: break;
194: case '#':
195: strcpy(to, get_filename(old_ifile));
196: to += strlen(to);
197: break;
198: default:
199: *to++ = *fr;
200: break;
201: }
202: }
203: *to = '\0';
204: return (e);
205: }
206:
207: #if TAB_COMPLETE_FILENAME
208:
209: /*
210: * Return a blank-separated list of filenames which "complete"
211: * the given string.
212: */
213: public char *
214: fcomplete(s)
215: char *s;
216: {
217: char *fpat;
218: /*
219: * Complete the filename "s" by globbing "s*".
220: */
221: #if MSOFTC
222: /*
223: * But in DOS, we have to glob "s*.*".
224: * But if the final component of the filename already has
225: * a dot in it, just do "s*".
226: * (Thus, "FILE" is globbed as "FILE*.*",
227: * but "FILE.A" is globbed as "FILE.A*").
228: */
229: char *slash;
230: for (slash = s+strlen(s)-1; slash > s; slash--)
231: if (*slash == '/' || *slash == '\\')
232: break;
233: fpat = (char *) ecalloc(strlen(s)+4, sizeof(char));
234: if (strchr(slash, '.') == NULL)
235: sprintf(fpat, "%s*.*", s);
236: else
237: sprintf(fpat, "%s*", s);
238: #else
239: fpat = (char *) ecalloc(strlen(s)+2, sizeof(char));
240: sprintf(fpat, "%s*", s);
241: #endif
242: s = glob(fpat);
243: if (strcmp(s,fpat) == 0)
244: {
245: /*
246: * The filename didn't expand.
247: */
248: free(s);
249: s = NULL;
250: }
251: free(fpat);
252: return (s);
253: }
254: #endif
255:
256: /*
257: * Try to determine if a file is "binary".
258: * This is just a guess, and we need not try too hard to make it accurate.
259: */
260: public int
261: bin_file(f)
262: int f;
263: {
264: int i;
265: int n;
266: unsigned char data[64];
267:
268: if (!seekable(f))
269: return (0);
270: if (lseek(f, (off_t)0, 0) == BAD_LSEEK)
271: return (0);
272: n = read(f, data, sizeof(data));
273: for (i = 0; i < n; i++)
274: if (binary_char(data[i]))
275: return (1);
276: return (0);
277: }
278:
279: /*
280: * Try to determine the size of a file by seeking to the end.
281: */
282: static POSITION
283: seek_filesize(f)
284: int f;
285: {
286: off_t spos;
287:
288: spos = lseek(f, (off_t)0, 2);
289: if (spos == BAD_LSEEK)
290: return (NULL_POSITION);
291: return ((POSITION) spos);
292: }
293:
294: #if GLOB
295:
296: FILE *popen();
297:
298: /*
299: * Read a string from a file.
300: * Return a pointer to the string in memory.
301: */
302: static char *
303: readfd(fd)
304: FILE *fd;
305: {
306: int len;
307: int ch;
308: char *buf;
309: char *p;
310:
311: /*
312: * Make a guess about how many chars in the string
313: * and allocate a buffer to hold it.
314: */
315: len = 100;
316: buf = (char *) ecalloc(len, sizeof(char));
317: for (p = buf; ; p++)
318: {
319: if ((ch = getc(fd)) == '\n' || ch == EOF)
320: break;
321: if (p - buf >= len-1)
322: {
323: /*
324: * The string is too big to fit in the buffer we have.
325: * Allocate a new buffer, twice as big.
326: */
327: len *= 2;
328: *p = '\0';
329: p = (char *) ecalloc(len, sizeof(char));
330: strcpy(p, buf);
331: free(buf);
332: buf = p;
333: p = buf + strlen(buf);
334: }
335: *p = ch;
336: }
337: *p = '\0';
338: return (buf);
339: }
340:
341: /*
342: * Execute a shell command.
343: * Return a pointer to a pipe connected to the shell command's standard output.
344: */
345: static FILE *
346: shellcmd(cmd, s1, s2)
347: char *cmd;
348: char *s1;
349: char *s2;
350: {
351: char *scmd;
352: char *scmd2;
353: char *shell;
354: FILE *fd;
355: int len;
356:
357: len = strlen(cmd) +
358: (s1 == NULL ? 0 : strlen(s1)) +
359: (s2 == NULL ? 0 : strlen(s2)) + 1;
360: scmd = (char *) ecalloc(len, sizeof(char));
361: sprintf(scmd, cmd, s1, s2);
362: #if HAVE_SHELL
363: shell = getenv("SHELL");
364: if (shell != NULL && *shell != '\0')
365: {
366: /*
367: * Read the output of <$SHELL -c "cmd">.
368: */
369: scmd2 = (char *) ecalloc(strlen(shell) + strlen(scmd) + 7,
370: sizeof(char));
371: sprintf(scmd2, "%s -c \"%s\"", shell, scmd);
372: free(scmd);
373: scmd = scmd2;
374: }
375: #endif
376: fd = popen(scmd, "r");
377: free(scmd);
378: return (fd);
379: }
380:
381: /*
382: * Expand a filename, doing any shell-level substitutions.
383: */
384: public char *
385: glob(filename)
386: char *filename;
387: {
388: char *gfilename;
389:
390: filename = fexpand(filename);
391: #if OS2
392: {
393: char **list;
394: int cnt;
395: int length;
396:
397: list = _fnexplode(filename);
398: if (list == NULL)
399: return (filename);
400: length = 0;
401: for (cnt = 0; list[cnt] != NULL; cnt++)
402: length += strlen(list[cnt]) + 1;
403: gfilename = (char *) ecalloc(length, sizeof(char));
404: for (cnt = 0; list[cnt] != NULL; cnt++)
405: {
406: strcat(gfilename, list[cnt]);
407: strcat(gfilename, " ");
408: }
409: _fnexplodefree(list);
410: }
411: #else
412: {
413: FILE *fd;
414:
415: /*
416: * We get the shell to expand the filename for us by passing
417: * an "echo" command to the shell and reading its output.
418: */
419: fd = shellcmd("echo %s", filename, (char*)NULL);
420: if (fd == NULL)
421: {
422: /*
423: * Cannot create the pipe.
424: * Just return the original (fexpanded) filename.
425: */
426: return (filename);
427: }
428: gfilename = readfd(fd);
429: pclose(fd);
430: if (*gfilename == '\0')
431: {
432: free(gfilename);
433: return (filename);
434: }
435: free(filename);
436: }
437: #endif
438: return (gfilename);
439: }
440:
441: /*
442: * See if we should open a "replacement file"
443: * instead of the file we're about to open.
444: */
445: public char *
446: open_altfile(filename, pf, pfd)
447: char *filename;
448: int *pf;
449: void **pfd;
450: {
451: char *lessopen;
452: char *gfilename;
453: int returnfd = 0;
454: FILE *fd;
455:
456: ch_ungetchar(-1);
457: if ((lessopen = getenv("LESSOPEN")) == NULL)
458: return (NULL);
459: if (strcmp(filename, "-") == 0)
460: return (NULL);
461: if (*lessopen == '|')
462: {
463: /*
464: * If LESSOPEN starts with a |, it indicates
465: * a "pipe preprocessor".
466: */
467: lessopen++;
468: returnfd = 1;
469: }
470: fd = shellcmd(lessopen, filename, (char*)NULL);
471: if (fd == NULL)
472: {
473: /*
474: * Cannot create the pipe.
475: */
476: return (NULL);
477: }
478: if (returnfd)
479: {
480: #if HAVE_FILENO
481: int f;
482: char c;
483:
484: /*
485: * Read one char to see if the pipe will produce any data.
486: * If it does, push the char back on the pipe.
487: */
488: f = fileno(fd);
489: if (read(f, &c, 1) != 1)
490: {
491: /*
492: * Pipe is empty. This means there is no alt file.
493: */
494: pclose(fd);
495: return (NULL);
496: }
497: ch_ungetchar(c);
498: *pfd = (void *) fd;
499: *pf = f;
500: return (save("-"));
501: #else
502: error("LESSOPEN pipe is not supported", NULL_PARG);
503: return (NULL);
504: #endif
505: }
506: gfilename = readfd(fd);
507: pclose(fd);
508: if (*gfilename == '\0')
509: /*
510: * Pipe is empty. This means there is no alt file.
511: */
512: return (NULL);
513: return (gfilename);
514: }
515:
516: /*
517: * Close a replacement file.
518: */
519: public void
520: close_altfile(altfilename, filename, pipefd)
521: char *altfilename;
522: char *filename;
523: void *pipefd;
524: {
525: char *lessclose;
526: FILE *fd;
527:
528: if (pipefd != NULL)
529: pclose((FILE*) pipefd);
530: if ((lessclose = getenv("LESSCLOSE")) == NULL)
531: return;
532: fd = shellcmd(lessclose, filename, altfilename);
533: pclose(fd);
534: }
535:
536: #else
537: #if MSOFTC
538:
539: public char *
540: glob(filename)
541: char *filename;
542: {
543: register char *gfilename;
544: register char *p;
545: register int len;
546: register int n;
547: struct find_t fnd;
548: char drive[_MAX_DRIVE];
549: char dir[_MAX_DIR];
550: char fname[_MAX_FNAME];
551: char ext[_MAX_EXT];
552:
553: filename = fexpand(filename);
554: if (_dos_findfirst(filename, ~0, &fnd) != 0)
555: return (filename);
556:
557: _splitpath(filename, drive, dir, fname, ext);
558: len = 100;
559: gfilename = (char *) ecalloc(len, sizeof(char));
560: p = gfilename;
561: do {
562: n = strlen(drive) + strlen(dir) + strlen(fnd.name);
563: while (p - gfilename + n+2 >= len)
564: {
565: len *= 2;
566: *p = '\0';
567: p = (char *) ecalloc(len, sizeof(char));
568: strcpy(p, gfilename);
569: free(gfilename);
570: gfilename = p;
571: p = gfilename + strlen(gfilename);
572: }
573: sprintf(p, "%s%s%s", drive, dir, fnd.name);
574: p += n;
575: *p++ = ' ';
576: } while (_dos_findnext(&fnd) == 0);
577:
578: *--p = '\0';
579: return (gfilename);
580: }
581:
582: public char *
583: open_altfile(filename)
584: char *filename;
585: {
586: return (NULL);
587: }
588:
589: public void
590: close_altfile(altfilename, filename)
591: char *altfilename;
592: char *filename;
593: {
594: }
595:
596: #else
597:
598: public char *
599: glob(filename)
600: char *filename;
601: {
602: return (fexpand(filename));
603: }
604:
605:
606: public char *
607: open_altfile(filename)
608: char *filename;
609: {
610: return (NULL);
611: }
612:
613: public void
614: close_altfile(altfilename, filename)
615: char *altfilename;
616: char *filename;
617: {
618: }
619:
620: #endif
621: #endif
622:
623:
624: #if HAVE_STAT
625:
626: #include <sys/stat.h>
627: #ifndef S_ISDIR
628: #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
629: #endif
630: #ifndef S_ISREG
631: #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
632: #endif
633:
634: /*
635: * Returns NULL if the file can be opened and
636: * is an ordinary file, otherwise an error message
637: * (if it cannot be opened or is a directory, etc.)
638: */
639: public char *
640: bad_file(filename)
641: char *filename;
642: {
643: register char *m;
644: struct stat statbuf;
645:
646: if (stat(filename, &statbuf) < 0)
647: return (errno_message(filename));
648:
649: if (force_open)
650: return (NULL);
651:
652: if (S_ISDIR(statbuf.st_mode))
653: {
654: static char is_dir[] = " is a directory";
655: m = (char *) ecalloc(strlen(filename) + sizeof(is_dir),
656: sizeof(char));
657: strcpy(m, filename);
658: strcat(m, is_dir);
659: return (m);
660: }
661: if (!S_ISREG(statbuf.st_mode))
662: {
663: static char not_reg[] = " is not a regular file";
664: m = (char *) ecalloc(strlen(filename) + sizeof(not_reg),
665: sizeof(char));
666: strcpy(m, filename);
667: strcat(m, not_reg);
668: return (m);
669: }
670:
671: return (NULL);
672: }
673:
674: /*
675: * Return the size of a file, as cheaply as possible.
676: * In Unix, we can stat the file.
677: */
678: public POSITION
679: filesize(f)
680: int f;
681: {
682: struct stat statbuf;
683:
684: if (fstat(f, &statbuf) < 0)
685: /*
686: * Can't stat; try seeking to the end.
687: */
688: return (seek_filesize(f));
689:
690: return ((POSITION) statbuf.st_size);
691: }
692:
693: #else
694:
695: /*
696: * If we have no way to find out, just say the file is good.
697: */
698: public char *
699: bad_file(filename)
700: char *filename;
701: {
702: return (NULL);
703: }
704:
705: /*
706: * We can find the file size by seeking.
707: */
708: public POSITION
709: filesize(f)
710: int f;
711: {
712: return (seek_filesize(f));
713: }
714:
715: #endif