Annotation of src/usr.bin/less/filename.c, Revision 1.5
1.5 ! deraadt 1: /* $OpenBSD: filename.c,v 1.4 2003/03/13 09:09:32 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
66: sprintf(pathname, "%s\\%s", dirname, filename);
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 '%':
193: strcpy(to, get_filename(curr_ifile));
194: to += strlen(to);
195: break;
196: case '#':
197: strcpy(to, get_filename(old_ifile));
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;
220: /*
221: * Complete the filename "s" by globbing "s*".
222: */
223: #if MSOFTC
224: /*
225: * But in DOS, we have to glob "s*.*".
226: * But if the final component of the filename already has
227: * a dot in it, just do "s*".
228: * (Thus, "FILE" is globbed as "FILE*.*",
229: * but "FILE.A" is globbed as "FILE.A*").
230: */
231: char *slash;
232: for (slash = s+strlen(s)-1; slash > s; slash--)
233: if (*slash == '/' || *slash == '\\')
234: break;
235: fpat = (char *) ecalloc(strlen(s)+4, sizeof(char));
236: if (strchr(slash, '.') == NULL)
237: sprintf(fpat, "%s*.*", s);
238: else
239: sprintf(fpat, "%s*", s);
240: #else
241: fpat = (char *) ecalloc(strlen(s)+2, sizeof(char));
1.4 deraadt 242: snprintf(fpat, strlen(s)+2, "%s*", s);
1.1 etheisen 243: #endif
244: s = glob(fpat);
245: if (strcmp(s,fpat) == 0)
246: {
247: /*
248: * The filename didn't expand.
249: */
250: free(s);
251: s = NULL;
252: }
253: free(fpat);
254: return (s);
255: }
256: #endif
257:
258: /*
259: * Try to determine if a file is "binary".
260: * This is just a guess, and we need not try too hard to make it accurate.
261: */
262: public int
263: bin_file(f)
264: int f;
265: {
266: int i;
267: int n;
268: unsigned char data[64];
269:
270: if (!seekable(f))
271: return (0);
272: if (lseek(f, (off_t)0, 0) == BAD_LSEEK)
273: return (0);
274: n = read(f, data, sizeof(data));
275: for (i = 0; i < n; i++)
276: if (binary_char(data[i]))
277: return (1);
278: return (0);
279: }
280:
281: /*
282: * Try to determine the size of a file by seeking to the end.
283: */
284: static POSITION
285: seek_filesize(f)
286: int f;
287: {
288: off_t spos;
289:
290: spos = lseek(f, (off_t)0, 2);
291: if (spos == BAD_LSEEK)
292: return (NULL_POSITION);
293: return ((POSITION) spos);
294: }
295:
296: #if GLOB
297:
298: FILE *popen();
299:
300: /*
301: * Read a string from a file.
302: * Return a pointer to the string in memory.
303: */
304: static char *
305: readfd(fd)
306: FILE *fd;
307: {
308: int len;
309: int ch;
310: char *buf;
311: char *p;
312:
313: /*
314: * Make a guess about how many chars in the string
315: * and allocate a buffer to hold it.
316: */
317: len = 100;
318: buf = (char *) ecalloc(len, sizeof(char));
319: for (p = buf; ; p++)
320: {
321: if ((ch = getc(fd)) == '\n' || ch == EOF)
322: break;
323: if (p - buf >= len-1)
324: {
325: /*
326: * The string is too big to fit in the buffer we have.
327: * Allocate a new buffer, twice as big.
328: */
329: len *= 2;
330: *p = '\0';
331: p = (char *) ecalloc(len, sizeof(char));
1.5 ! deraadt 332: strlcpy(p, buf, len);
1.1 etheisen 333: free(buf);
334: buf = p;
335: p = buf + strlen(buf);
336: }
337: *p = ch;
338: }
339: *p = '\0';
340: return (buf);
341: }
342:
343: /*
344: * Execute a shell command.
345: * Return a pointer to a pipe connected to the shell command's standard output.
346: */
347: static FILE *
348: shellcmd(cmd, s1, s2)
349: char *cmd;
350: char *s1;
351: char *s2;
352: {
353: char *scmd;
354: char *scmd2;
355: char *shell;
356: FILE *fd;
357: int len;
358:
359: len = strlen(cmd) +
360: (s1 == NULL ? 0 : strlen(s1)) +
361: (s2 == NULL ? 0 : strlen(s2)) + 1;
362: scmd = (char *) ecalloc(len, sizeof(char));
1.4 deraadt 363: snprintf(scmd, len, cmd, s1, s2);
1.1 etheisen 364: #if HAVE_SHELL
365: shell = getenv("SHELL");
366: if (shell != NULL && *shell != '\0')
367: {
368: /*
369: * Read the output of <$SHELL -c "cmd">.
370: */
1.4 deraadt 371: len = strlen(shell) + strlen(scmd) + 7;
372: scmd2 = (char *) ecalloc(len, sizeof(char));
373: snprintf(scmd2, len, "%s -c \"%s\"", shell, scmd);
1.1 etheisen 374: free(scmd);
375: scmd = scmd2;
376: }
377: #endif
378: fd = popen(scmd, "r");
379: free(scmd);
380: return (fd);
381: }
382:
383: /*
384: * Expand a filename, doing any shell-level substitutions.
385: */
386: public char *
387: glob(filename)
388: char *filename;
389: {
390: char *gfilename;
391:
392: filename = fexpand(filename);
393: #if OS2
394: {
395: char **list;
396: int cnt;
397: int length;
398:
399: list = _fnexplode(filename);
400: if (list == NULL)
401: return (filename);
402: length = 0;
403: for (cnt = 0; list[cnt] != NULL; cnt++)
404: length += strlen(list[cnt]) + 1;
405: gfilename = (char *) ecalloc(length, sizeof(char));
406: for (cnt = 0; list[cnt] != NULL; cnt++)
407: {
408: strcat(gfilename, list[cnt]);
409: strcat(gfilename, " ");
410: }
411: _fnexplodefree(list);
412: }
413: #else
414: {
415: FILE *fd;
416:
417: /*
418: * We get the shell to expand the filename for us by passing
419: * an "echo" command to the shell and reading its output.
420: */
421: fd = shellcmd("echo %s", filename, (char*)NULL);
422: if (fd == NULL)
423: {
424: /*
425: * Cannot create the pipe.
426: * Just return the original (fexpanded) filename.
427: */
428: return (filename);
429: }
430: gfilename = readfd(fd);
431: pclose(fd);
432: if (*gfilename == '\0')
433: {
434: free(gfilename);
435: return (filename);
436: }
437: free(filename);
438: }
439: #endif
440: return (gfilename);
441: }
442:
443: /*
444: * See if we should open a "replacement file"
445: * instead of the file we're about to open.
446: */
447: public char *
448: open_altfile(filename, pf, pfd)
449: char *filename;
450: int *pf;
451: void **pfd;
452: {
453: char *lessopen;
454: char *gfilename;
455: int returnfd = 0;
456: FILE *fd;
457:
458: ch_ungetchar(-1);
459: if ((lessopen = getenv("LESSOPEN")) == NULL)
460: return (NULL);
461: if (strcmp(filename, "-") == 0)
462: return (NULL);
463: if (*lessopen == '|')
464: {
465: /*
466: * If LESSOPEN starts with a |, it indicates
467: * a "pipe preprocessor".
468: */
469: lessopen++;
470: returnfd = 1;
471: }
472: fd = shellcmd(lessopen, filename, (char*)NULL);
473: if (fd == NULL)
474: {
475: /*
476: * Cannot create the pipe.
477: */
478: return (NULL);
479: }
480: if (returnfd)
481: {
482: #if HAVE_FILENO
483: int f;
484: char c;
485:
486: /*
487: * Read one char to see if the pipe will produce any data.
488: * If it does, push the char back on the pipe.
489: */
490: f = fileno(fd);
491: if (read(f, &c, 1) != 1)
492: {
493: /*
494: * Pipe is empty. This means there is no alt file.
495: */
496: pclose(fd);
497: return (NULL);
498: }
499: ch_ungetchar(c);
500: *pfd = (void *) fd;
501: *pf = f;
502: return (save("-"));
503: #else
504: error("LESSOPEN pipe is not supported", NULL_PARG);
505: return (NULL);
506: #endif
507: }
508: gfilename = readfd(fd);
509: pclose(fd);
510: if (*gfilename == '\0')
511: /*
512: * Pipe is empty. This means there is no alt file.
513: */
514: return (NULL);
515: return (gfilename);
516: }
517:
518: /*
519: * Close a replacement file.
520: */
521: public void
522: close_altfile(altfilename, filename, pipefd)
523: char *altfilename;
524: char *filename;
525: void *pipefd;
526: {
527: char *lessclose;
528: FILE *fd;
529:
530: if (pipefd != NULL)
531: pclose((FILE*) pipefd);
532: if ((lessclose = getenv("LESSCLOSE")) == NULL)
533: return;
534: fd = shellcmd(lessclose, filename, altfilename);
535: pclose(fd);
536: }
537:
538: #else
539: #if MSOFTC
540:
541: public char *
542: glob(filename)
543: char *filename;
544: {
1.3 mpech 545: char *gfilename;
546: char *p;
547: int len;
548: int n;
1.1 etheisen 549: struct find_t fnd;
550: char drive[_MAX_DRIVE];
551: char dir[_MAX_DIR];
552: char fname[_MAX_FNAME];
553: char ext[_MAX_EXT];
554:
555: filename = fexpand(filename);
556: if (_dos_findfirst(filename, ~0, &fnd) != 0)
557: return (filename);
558:
559: _splitpath(filename, drive, dir, fname, ext);
560: len = 100;
561: gfilename = (char *) ecalloc(len, sizeof(char));
562: p = gfilename;
563: do {
564: n = strlen(drive) + strlen(dir) + strlen(fnd.name);
565: while (p - gfilename + n+2 >= len)
566: {
567: len *= 2;
568: *p = '\0';
569: p = (char *) ecalloc(len, sizeof(char));
1.5 ! deraadt 570: strlcpy(p, gfilename, len);
1.1 etheisen 571: free(gfilename);
572: gfilename = p;
573: p = gfilename + strlen(gfilename);
574: }
575: sprintf(p, "%s%s%s", drive, dir, fnd.name);
576: p += n;
577: *p++ = ' ';
578: } while (_dos_findnext(&fnd) == 0);
579:
580: *--p = '\0';
581: return (gfilename);
582: }
583:
584: public char *
585: open_altfile(filename)
586: char *filename;
587: {
588: return (NULL);
589: }
590:
591: public void
592: close_altfile(altfilename, filename)
593: char *altfilename;
594: char *filename;
595: {
596: }
597:
598: #else
599:
600: public char *
601: glob(filename)
602: char *filename;
603: {
604: return (fexpand(filename));
605: }
606:
607:
608: public char *
609: open_altfile(filename)
610: char *filename;
611: {
612: return (NULL);
613: }
614:
615: public void
616: close_altfile(altfilename, filename)
617: char *altfilename;
618: char *filename;
619: {
620: }
621:
622: #endif
623: #endif
624:
625:
626: #if HAVE_STAT
627:
628: #include <sys/stat.h>
629: #ifndef S_ISDIR
630: #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
631: #endif
632: #ifndef S_ISREG
633: #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
634: #endif
635:
636: /*
637: * Returns NULL if the file can be opened and
638: * is an ordinary file, otherwise an error message
639: * (if it cannot be opened or is a directory, etc.)
640: */
641: public char *
642: bad_file(filename)
643: char *filename;
644: {
1.3 mpech 645: char *m;
1.1 etheisen 646: struct stat statbuf;
647:
648: if (stat(filename, &statbuf) < 0)
649: return (errno_message(filename));
650:
651: if (force_open)
652: return (NULL);
653:
654: if (S_ISDIR(statbuf.st_mode))
655: {
656: static char is_dir[] = " is a directory";
1.5 ! deraadt 657: size_t len;
! 658:
! 659: len = strlen(filename) + sizeof(is_dir);
! 660: m = (char *) ecalloc(len, sizeof(char));
! 661: strlcpy(m, filename, len);
! 662: strlcat(m, is_dir, len);
1.1 etheisen 663: return (m);
664: }
665: if (!S_ISREG(statbuf.st_mode))
666: {
667: static char not_reg[] = " is not a regular file";
1.5 ! deraadt 668: size_t len;
! 669:
! 670: len = strlen(filename) + sizeof(not_reg);
! 671: m = (char *) ecalloc(len, sizeof(char));
! 672: strlcpy(m, filename, len);
! 673: strlcat(m, not_reg, len);
1.1 etheisen 674: return (m);
675: }
676:
677: return (NULL);
678: }
679:
680: /*
681: * Return the size of a file, as cheaply as possible.
682: * In Unix, we can stat the file.
683: */
684: public POSITION
685: filesize(f)
686: int f;
687: {
688: struct stat statbuf;
689:
690: if (fstat(f, &statbuf) < 0)
691: /*
692: * Can't stat; try seeking to the end.
693: */
694: return (seek_filesize(f));
695:
696: return ((POSITION) statbuf.st_size);
697: }
698:
699: #else
700:
701: /*
702: * If we have no way to find out, just say the file is good.
703: */
704: public char *
705: bad_file(filename)
706: char *filename;
707: {
708: return (NULL);
709: }
710:
711: /*
712: * We can find the file size by seeking.
713: */
714: public POSITION
715: filesize(f)
716: int f;
717: {
718: return (seek_filesize(f));
719: }
720:
721: #endif