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