filename.c revision 60786
160786Sps/*
260786Sps * Copyright (C) 1984-2000  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
760786Sps * For more information about less, or for information on how to
860786Sps * contact the author, see the README file.
960786Sps */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps * Routines to mess around with filenames (and files).
1460786Sps * Much of this is very OS dependent.
1560786Sps */
1660786Sps
1760786Sps#include "less.h"
1860786Sps#include "lglob.h"
1960786Sps#if MSDOS_COMPILER
2060786Sps#include <dos.h>
2160786Sps#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER)
2260786Sps#include <dir.h>
2360786Sps#endif
2460786Sps#if MSDOS_COMPILER==DJGPPC
2560786Sps#include <glob.h>
2660786Sps#include <dir.h>
2760786Sps#include <limits.h>
2860786Sps#define _MAX_PATH	PATH_MAX
2960786Sps#endif
3060786Sps#endif
3160786Sps#ifdef _OSK
3260786Sps#include <rbf.h>
3360786Sps#ifndef _OSK_MWC32
3460786Sps#include <modes.h>
3560786Sps#endif
3660786Sps#endif
3760786Sps
3860786Sps#if HAVE_STAT
3960786Sps#include <sys/stat.h>
4060786Sps#ifndef S_ISDIR
4160786Sps#define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
4260786Sps#endif
4360786Sps#ifndef S_ISREG
4460786Sps#define	S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
4560786Sps#endif
4660786Sps#endif
4760786Sps
4860786Sps
4960786Spsextern int force_open;
5060786Spsextern int secure;
5160786Spsextern IFILE curr_ifile;
5260786Spsextern IFILE old_ifile;
5360786Sps#if SPACES_IN_FILENAMES
5460786Spsextern char openquote;
5560786Spsextern char closequote;
5660786Sps#endif
5760786Sps
5860786Sps/*
5960786Sps * Remove quotes around a filename.
6060786Sps */
6160786Sps	public char *
6260786Spsunquote_file(str)
6360786Sps	char *str;
6460786Sps{
6560786Sps#if SPACES_IN_FILENAMES
6660786Sps	char *name;
6760786Sps	char *p;
6860786Sps
6960786Sps	if (*str != openquote)
7060786Sps		return (save(str));
7160786Sps	name = (char *) ecalloc(strlen(str), sizeof(char));
7260786Sps	strcpy(name, str+1);
7360786Sps	p = name + strlen(name) - 1;
7460786Sps	if (*p == closequote)
7560786Sps		*p = '\0';
7660786Sps	return (name);
7760786Sps#else
7860786Sps	return (save(str));
7960786Sps#endif
8060786Sps}
8160786Sps
8260786Sps/*
8360786Sps * Return a pathname that points to a specified file in a specified directory.
8460786Sps * Return NULL if the file does not exist in the directory.
8560786Sps */
8660786Sps	static char *
8760786Spsdirfile(dirname, filename)
8860786Sps	char *dirname;
8960786Sps	char *filename;
9060786Sps{
9160786Sps	char *pathname;
9260786Sps	char *qpathname;
9360786Sps	int f;
9460786Sps
9560786Sps	if (dirname == NULL || *dirname == '\0')
9660786Sps		return (NULL);
9760786Sps	/*
9860786Sps	 * Construct the full pathname.
9960786Sps	 */
10060786Sps	pathname = (char *) calloc(strlen(dirname) + strlen(filename) + 2,
10160786Sps					sizeof(char));
10260786Sps	if (pathname == NULL)
10360786Sps		return (NULL);
10460786Sps	sprintf(pathname, "%s%s%s", dirname, PATHNAME_SEP, filename);
10560786Sps	/*
10660786Sps	 * Make sure the file exists.
10760786Sps	 */
10860786Sps	qpathname = unquote_file(pathname);
10960786Sps	f = open(qpathname, OPEN_READ);
11060786Sps	if (f < 0)
11160786Sps	{
11260786Sps		free(pathname);
11360786Sps		pathname = NULL;
11460786Sps	} else
11560786Sps	{
11660786Sps		close(f);
11760786Sps	}
11860786Sps	free(qpathname);
11960786Sps	return (pathname);
12060786Sps}
12160786Sps
12260786Sps/*
12360786Sps * Return the full pathname of the given file in the "home directory".
12460786Sps */
12560786Sps	public char *
12660786Spshomefile(filename)
12760786Sps	char *filename;
12860786Sps{
12960786Sps	register char *pathname;
13060786Sps
13160786Sps	/*
13260786Sps	 * Try $HOME/filename.
13360786Sps	 */
13460786Sps	pathname = dirfile(lgetenv("HOME"), filename);
13560786Sps	if (pathname != NULL)
13660786Sps		return (pathname);
13760786Sps#if OS2
13860786Sps	/*
13960786Sps	 * Try $INIT/filename.
14060786Sps	 */
14160786Sps	pathname = dirfile(lgetenv("INIT"), filename);
14260786Sps	if (pathname != NULL)
14360786Sps		return (pathname);
14460786Sps#endif
14560786Sps#if MSDOS_COMPILER || OS2
14660786Sps	/*
14760786Sps	 * Look for the file anywhere on search path.
14860786Sps	 */
14960786Sps	pathname = (char *) calloc(_MAX_PATH, sizeof(char));
15060786Sps#if MSDOS_COMPILER==DJGPPC
15160786Sps	{
15260786Sps		char *res = searchpath(filename);
15360786Sps		if (res == 0)
15460786Sps			*pathname = '\0';
15560786Sps		else
15660786Sps			strcpy(pathname, res);
15760786Sps	}
15860786Sps#else
15960786Sps	_searchenv(filename, "PATH", pathname);
16060786Sps#endif
16160786Sps	if (*pathname != '\0')
16260786Sps		return (pathname);
16360786Sps	free(pathname);
16460786Sps#endif
16560786Sps	return (NULL);
16660786Sps}
16760786Sps
16860786Sps/*
16960786Sps * Expand a string, substituting any "%" with the current filename,
17060786Sps * and any "#" with the previous filename.
17160786Sps * But a string of N "%"s is just replaced with N-1 "%"s.
17260786Sps * Likewise for a string of N "#"s.
17360786Sps * {{ This is a lot of work just to support % and #. }}
17460786Sps */
17560786Sps	public char *
17660786Spsfexpand(s)
17760786Sps	char *s;
17860786Sps{
17960786Sps	register char *fr, *to;
18060786Sps	register int n;
18160786Sps	register char *e;
18260786Sps	IFILE ifile;
18360786Sps
18460786Sps#define	fchar_ifile(c) \
18560786Sps	((c) == '%' ? curr_ifile : \
18660786Sps	 (c) == '#' ? old_ifile : NULL_IFILE)
18760786Sps
18860786Sps	/*
18960786Sps	 * Make one pass to see how big a buffer we
19060786Sps	 * need to allocate for the expanded string.
19160786Sps	 */
19260786Sps	n = 0;
19360786Sps	for (fr = s;  *fr != '\0';  fr++)
19460786Sps	{
19560786Sps		switch (*fr)
19660786Sps		{
19760786Sps		case '%':
19860786Sps		case '#':
19960786Sps			if (fr > s && fr[-1] == *fr)
20060786Sps			{
20160786Sps				/*
20260786Sps				 * Second (or later) char in a string
20360786Sps				 * of identical chars.  Treat as normal.
20460786Sps				 */
20560786Sps				n++;
20660786Sps			} else if (fr[1] != *fr)
20760786Sps			{
20860786Sps				/*
20960786Sps				 * Single char (not repeated).  Treat specially.
21060786Sps				 */
21160786Sps				ifile = fchar_ifile(*fr);
21260786Sps				if (ifile == NULL_IFILE)
21360786Sps					n++;
21460786Sps				else
21560786Sps					n += strlen(get_filename(ifile));
21660786Sps			}
21760786Sps			/*
21860786Sps			 * Else it is the first char in a string of
21960786Sps			 * identical chars.  Just discard it.
22060786Sps			 */
22160786Sps			break;
22260786Sps		default:
22360786Sps			n++;
22460786Sps			break;
22560786Sps		}
22660786Sps	}
22760786Sps
22860786Sps	e = (char *) ecalloc(n+1, sizeof(char));
22960786Sps
23060786Sps	/*
23160786Sps	 * Now copy the string, expanding any "%" or "#".
23260786Sps	 */
23360786Sps	to = e;
23460786Sps	for (fr = s;  *fr != '\0';  fr++)
23560786Sps	{
23660786Sps		switch (*fr)
23760786Sps		{
23860786Sps		case '%':
23960786Sps		case '#':
24060786Sps			if (fr > s && fr[-1] == *fr)
24160786Sps			{
24260786Sps				*to++ = *fr;
24360786Sps			} else if (fr[1] != *fr)
24460786Sps			{
24560786Sps				ifile = fchar_ifile(*fr);
24660786Sps				if (ifile == NULL_IFILE)
24760786Sps					*to++ = *fr;
24860786Sps				else
24960786Sps				{
25060786Sps					strcpy(to, get_filename(ifile));
25160786Sps					to += strlen(to);
25260786Sps				}
25360786Sps			}
25460786Sps			break;
25560786Sps		default:
25660786Sps			*to++ = *fr;
25760786Sps			break;
25860786Sps		}
25960786Sps	}
26060786Sps	*to = '\0';
26160786Sps	return (e);
26260786Sps}
26360786Sps
26460786Sps#if TAB_COMPLETE_FILENAME
26560786Sps
26660786Sps/*
26760786Sps * Return a blank-separated list of filenames which "complete"
26860786Sps * the given string.
26960786Sps */
27060786Sps	public char *
27160786Spsfcomplete(s)
27260786Sps	char *s;
27360786Sps{
27460786Sps	char *fpat;
27560786Sps
27660786Sps	if (secure)
27760786Sps		return (NULL);
27860786Sps	/*
27960786Sps	 * Complete the filename "s" by globbing "s*".
28060786Sps	 */
28160786Sps#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC)
28260786Sps	/*
28360786Sps	 * But in DOS, we have to glob "s*.*".
28460786Sps	 * But if the final component of the filename already has
28560786Sps	 * a dot in it, just do "s*".
28660786Sps	 * (Thus, "FILE" is globbed as "FILE*.*",
28760786Sps	 *  but "FILE.A" is globbed as "FILE.A*").
28860786Sps	 */
28960786Sps	{
29060786Sps		char *slash;
29160786Sps		for (slash = s+strlen(s)-1;  slash > s;  slash--)
29260786Sps			if (*slash == *PATHNAME_SEP || *slash == '/')
29360786Sps				break;
29460786Sps		fpat = (char *) ecalloc(strlen(s)+4, sizeof(char));
29560786Sps		if (strchr(slash, '.') == NULL)
29660786Sps			sprintf(fpat, "%s*.*", s);
29760786Sps		else
29860786Sps			sprintf(fpat, "%s*", s);
29960786Sps	}
30060786Sps#else
30160786Sps	fpat = (char *) ecalloc(strlen(s)+2, sizeof(char));
30260786Sps	sprintf(fpat, "%s*", s);
30360786Sps#endif
30460786Sps	s = lglob(fpat);
30560786Sps	if (strcmp(s,fpat) == 0)
30660786Sps	{
30760786Sps		/*
30860786Sps		 * The filename didn't expand.
30960786Sps		 */
31060786Sps		free(s);
31160786Sps		s = NULL;
31260786Sps	}
31360786Sps	free(fpat);
31460786Sps	return (s);
31560786Sps}
31660786Sps#endif
31760786Sps
31860786Sps/*
31960786Sps * Try to determine if a file is "binary".
32060786Sps * This is just a guess, and we need not try too hard to make it accurate.
32160786Sps */
32260786Sps	public int
32360786Spsbin_file(f)
32460786Sps	int f;
32560786Sps{
32660786Sps	int i;
32760786Sps	int n;
32860786Sps	unsigned char data[64];
32960786Sps
33060786Sps	if (!seekable(f))
33160786Sps		return (0);
33260786Sps	if (lseek(f, (off_t)0, 0) == BAD_LSEEK)
33360786Sps		return (0);
33460786Sps	n = read(f, data, sizeof(data));
33560786Sps	for (i = 0;  i < n;  i++)
33660786Sps		if (binary_char(data[i]))
33760786Sps			return (1);
33860786Sps	return (0);
33960786Sps}
34060786Sps
34160786Sps/*
34260786Sps * Try to determine the size of a file by seeking to the end.
34360786Sps */
34460786Sps	static POSITION
34560786Spsseek_filesize(f)
34660786Sps	int f;
34760786Sps{
34860786Sps	off_t spos;
34960786Sps
35060786Sps	spos = lseek(f, (off_t)0, 2);
35160786Sps	if (spos == BAD_LSEEK)
35260786Sps		return (NULL_POSITION);
35360786Sps	return ((POSITION) spos);
35460786Sps}
35560786Sps
35660786Sps/*
35760786Sps * Read a string from a file.
35860786Sps * Return a pointer to the string in memory.
35960786Sps */
36060786Sps	static char *
36160786Spsreadfd(fd)
36260786Sps	FILE *fd;
36360786Sps{
36460786Sps	int len;
36560786Sps	int ch;
36660786Sps	char *buf;
36760786Sps	char *p;
36860786Sps
36960786Sps	/*
37060786Sps	 * Make a guess about how many chars in the string
37160786Sps	 * and allocate a buffer to hold it.
37260786Sps	 */
37360786Sps	len = 100;
37460786Sps	buf = (char *) ecalloc(len, sizeof(char));
37560786Sps	for (p = buf;  ;  p++)
37660786Sps	{
37760786Sps		if ((ch = getc(fd)) == '\n' || ch == EOF)
37860786Sps			break;
37960786Sps		if (p - buf >= len-1)
38060786Sps		{
38160786Sps			/*
38260786Sps			 * The string is too big to fit in the buffer we have.
38360786Sps			 * Allocate a new buffer, twice as big.
38460786Sps			 */
38560786Sps			len *= 2;
38660786Sps			*p = '\0';
38760786Sps			p = (char *) ecalloc(len, sizeof(char));
38860786Sps			strcpy(p, buf);
38960786Sps			free(buf);
39060786Sps			buf = p;
39160786Sps			p = buf + strlen(buf);
39260786Sps		}
39360786Sps		*p = ch;
39460786Sps	}
39560786Sps	*p = '\0';
39660786Sps	return (buf);
39760786Sps}
39860786Sps
39960786Sps#if HAVE_SHELL
40060786Sps
40160786Sps/*
40260786Sps * Get the shell's escape character.
40360786Sps */
40460786Sps	static char *
40560786Spsget_meta_escape()
40660786Sps{
40760786Sps	char *s;
40860786Sps
40960786Sps	s = lgetenv("LESSMETAESCAPE");
41060786Sps	if (s == NULL)
41160786Sps		s = DEF_METAESCAPE;
41260786Sps	return (s);
41360786Sps}
41460786Sps
41560786Sps/*
41660786Sps * Is this a shell metacharacter?
41760786Sps */
41860786Sps	static int
41960786Spsmetachar(c)
42060786Sps	char c;
42160786Sps{
42260786Sps	static char *metachars = NULL;
42360786Sps
42460786Sps	if (metachars == NULL)
42560786Sps	{
42660786Sps		metachars = lgetenv("LESSMETACHARS");
42760786Sps		if (metachars == NULL)
42860786Sps			metachars = DEF_METACHARS;
42960786Sps	}
43060786Sps	return (strchr(metachars, c) != NULL);
43160786Sps}
43260786Sps
43360786Sps/*
43460786Sps * Insert a backslash before each metacharacter in a string.
43560786Sps */
43660786Sps	public char *
43760786Spsesc_metachars(s)
43860786Sps	char *s;
43960786Sps{
44060786Sps	char *p;
44160786Sps	char *newstr;
44260786Sps	int len;
44360786Sps	char *esc;
44460786Sps	int esclen;
44560786Sps
44660786Sps	/*
44760786Sps	 * Determine how big a string we need to allocate.
44860786Sps	 */
44960786Sps	esc = get_meta_escape();
45060786Sps	esclen = strlen(esc);
45160786Sps	len = 1; /* Trailing null byte */
45260786Sps	for (p = s;  *p != '\0';  p++)
45360786Sps	{
45460786Sps		len++;
45560786Sps		if (metachar(*p))
45660786Sps		{
45760786Sps			if (*esc == '\0')
45860786Sps			{
45960786Sps				/*
46060786Sps				 * We've got a metachar, but this shell
46160786Sps				 * doesn't support escape chars.  Give up.
46260786Sps				 */
46360786Sps				return (NULL);
46460786Sps			}
46560786Sps			/*
46660786Sps			 * Allow space for the escape char.
46760786Sps			 */
46860786Sps			len += esclen;
46960786Sps		}
47060786Sps	}
47160786Sps	/*
47260786Sps	 * Allocate and construct the new string.
47360786Sps	 */
47460786Sps	newstr = p = (char *) ecalloc(len, sizeof(char));
47560786Sps	while (*s != '\0')
47660786Sps	{
47760786Sps		if (metachar(*s))
47860786Sps		{
47960786Sps			/*
48060786Sps			 * Add the escape char.
48160786Sps			 */
48260786Sps			strcpy(p, esc);
48360786Sps			p += esclen;
48460786Sps		}
48560786Sps		*p++ = *s++;
48660786Sps	}
48760786Sps	*p = '\0';
48860786Sps	return (newstr);
48960786Sps}
49060786Sps
49160786Sps#else /* HAVE_SHELL */
49260786Sps
49360786Sps	public char *
49460786Spsesc_metachars(s)
49560786Sps	char *s;
49660786Sps{
49760786Sps	return (save(s));
49860786Sps}
49960786Sps
50060786Sps#endif /* HAVE_SHELL */
50160786Sps
50260786Sps
50360786Sps#if HAVE_POPEN
50460786Sps
50560786SpsFILE *popen();
50660786Sps
50760786Sps/*
50860786Sps * Execute a shell command.
50960786Sps * Return a pointer to a pipe connected to the shell command's standard output.
51060786Sps */
51160786Sps	static FILE *
51260786Spsshellcmd(cmd)
51360786Sps	char *cmd;
51460786Sps{
51560786Sps	FILE *fd;
51660786Sps
51760786Sps#if HAVE_SHELL
51860786Sps	char *shell;
51960786Sps
52060786Sps	shell = lgetenv("SHELL");
52160786Sps	if (shell != NULL && *shell != '\0')
52260786Sps	{
52360786Sps		char *scmd;
52460786Sps		char *esccmd;
52560786Sps
52660786Sps		/*
52760786Sps		 * Try to escape any metacharacters in the command.
52860786Sps		 * If we can't do that, just put the command in quotes.
52960786Sps		 * (But that doesn't work well if the command itself
53060786Sps		 * contains quotes.)
53160786Sps		 */
53260786Sps		if ((esccmd = esc_metachars(cmd)) == NULL)
53360786Sps		{
53460786Sps			/*
53560786Sps			 * Cannot escape the metacharacters, so use quotes.
53660786Sps			 * Read the output of <$SHELL -c "cmd">.
53760786Sps			 */
53860786Sps			scmd = (char *) ecalloc(strlen(shell) + strlen(cmd) + 7,
53960786Sps						sizeof(char));
54060786Sps			sprintf(scmd, "%s -c \"%s\"", shell, cmd);
54160786Sps		} else
54260786Sps		{
54360786Sps			/*
54460786Sps			 * Read the output of <$SHELL -c cmd>.
54560786Sps			 * No quotes; use the escaped cmd.
54660786Sps			 */
54760786Sps			scmd = (char *) ecalloc(strlen(shell) + strlen(esccmd) + 5,
54860786Sps						sizeof(char));
54960786Sps			sprintf(scmd, "%s -c %s", shell, esccmd);
55060786Sps			free(esccmd);
55160786Sps		}
55260786Sps		fd = popen(scmd, "r");
55360786Sps		free(scmd);
55460786Sps	} else
55560786Sps#endif
55660786Sps	{
55760786Sps		fd = popen(cmd, "r");
55860786Sps		/*
55960786Sps		 * Redirection in `popen' might have messed with the
56060786Sps		 * standard devices.  Restore binary input mode.
56160786Sps		 */
56260786Sps		SET_BINARY(0);
56360786Sps	}
56460786Sps	return (fd);
56560786Sps}
56660786Sps
56760786Sps#endif /* HAVE_POPEN */
56860786Sps
56960786Sps
57060786Sps/*
57160786Sps * Expand a filename, doing any system-specific metacharacter substitutions.
57260786Sps */
57360786Sps	public char *
57460786Spslglob(filename)
57560786Sps	char *filename;
57660786Sps{
57760786Sps	char *gfilename;
57860786Sps	char *ofilename;
57960786Sps
58060786Sps	ofilename = fexpand(filename);
58160786Sps	if (secure)
58260786Sps		return (ofilename);
58360786Sps	filename = unquote_file(ofilename);
58460786Sps
58560786Sps#ifdef DECL_GLOB_LIST
58660786Sps{
58760786Sps	/*
58860786Sps	 * The globbing function returns a list of names.
58960786Sps	 */
59060786Sps	int length;
59160786Sps	char *p;
59260786Sps	DECL_GLOB_LIST(list)
59360786Sps
59460786Sps	GLOB_LIST(filename, list);
59560786Sps	if (GLOB_LIST_FAILED(list))
59660786Sps	{
59760786Sps		free(filename);
59860786Sps		return (ofilename);
59960786Sps	}
60060786Sps	length = 1; /* Room for trailing null byte */
60160786Sps	for (SCAN_GLOB_LIST(list, p))
60260786Sps	{
60360786Sps		INIT_GLOB_LIST(list, p);
60460786Sps	  	length += strlen(p) + 1;
60560786Sps#if SPACES_IN_FILENAMES
60660786Sps		if (strchr(p, ' ') != NULL)
60760786Sps			length += 2; /* Allow for quotes */
60860786Sps#endif
60960786Sps	}
61060786Sps	gfilename = (char *) ecalloc(length, sizeof(char));
61160786Sps	for (SCAN_GLOB_LIST(list, p))
61260786Sps	{
61360786Sps		INIT_GLOB_LIST(list, p);
61460786Sps#if SPACES_IN_FILENAMES
61560786Sps		if (strchr(p, ' ') != NULL)
61660786Sps			sprintf(gfilename + strlen(gfilename), "%c%s%c ",
61760786Sps				openquote, p, closequote);
61860786Sps		else
61960786Sps#endif
62060786Sps			sprintf(gfilename + strlen(gfilename), "%s ", p);
62160786Sps	}
62260786Sps	/*
62360786Sps	 * Overwrite the final trailing space with a null terminator.
62460786Sps	 */
62560786Sps	*--p = '\0';
62660786Sps	GLOB_LIST_DONE(list);
62760786Sps}
62860786Sps#else
62960786Sps#ifdef DECL_GLOB_NAME
63060786Sps{
63160786Sps	/*
63260786Sps	 * The globbing function returns a single name, and
63360786Sps	 * is called multiple times to walk thru all names.
63460786Sps	 */
63560786Sps	register char *p;
63660786Sps	register int len;
63760786Sps	register int n;
63860786Sps#if SPACES_IN_FILENAMES
63960786Sps	register int spaces_in_file;
64060786Sps#endif
64160786Sps	DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
64260786Sps
64360786Sps	GLOB_FIRST_NAME(filename, &fnd, handle);
64460786Sps	if (GLOB_FIRST_FAILED(handle))
64560786Sps	{
64660786Sps		free(filename);
64760786Sps		return (ofilename);
64860786Sps	}
64960786Sps
65060786Sps	_splitpath(filename, drive, dir, fname, ext);
65160786Sps	len = 100;
65260786Sps	gfilename = (char *) ecalloc(len, sizeof(char));
65360786Sps	p = gfilename;
65460786Sps	do {
65560786Sps		n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
65660786Sps#if SPACES_IN_FILENAMES
65760786Sps		spaces_in_file = 0;
65860786Sps		if (strchr(fnd.GLOB_NAME, ' ') != NULL ||
65960786Sps		    strchr(filename, ' ') != NULL)
66060786Sps		{
66160786Sps			spaces_in_file = 1;
66260786Sps			n += 2;
66360786Sps		}
66460786Sps#endif
66560786Sps		while (p - gfilename + n+2 >= len)
66660786Sps		{
66760786Sps			/*
66860786Sps			 * No room in current buffer.  Allocate a bigger one.
66960786Sps			 */
67060786Sps			len *= 2;
67160786Sps			*p = '\0';
67260786Sps			p = (char *) ecalloc(len, sizeof(char));
67360786Sps			strcpy(p, gfilename);
67460786Sps			free(gfilename);
67560786Sps			gfilename = p;
67660786Sps			p = gfilename + strlen(gfilename);
67760786Sps		}
67860786Sps#if SPACES_IN_FILENAMES
67960786Sps		if (spaces_in_file)
68060786Sps			sprintf(p, "%c%s%s%s%c ", openquote,
68160786Sps				drive, dir, fnd.GLOB_NAME, closequote);
68260786Sps		else
68360786Sps#endif
68460786Sps			sprintf(p, "%s%s%s ", drive, dir, fnd.GLOB_NAME);
68560786Sps		p += n;
68660786Sps	} while (GLOB_NEXT_NAME(handle, &fnd) == 0);
68760786Sps
68860786Sps	/*
68960786Sps	 * Overwrite the final trailing space with a null terminator.
69060786Sps	 */
69160786Sps	*--p = '\0';
69260786Sps	GLOB_NAME_DONE(handle);
69360786Sps}
69460786Sps#else
69560786Sps#if HAVE_POPEN
69660786Sps{
69760786Sps	/*
69860786Sps	 * We get the shell to glob the filename for us by passing
69960786Sps	 * an "echo" command to the shell and reading its output.
70060786Sps	 */
70160786Sps	FILE *fd;
70260786Sps	char *s;
70360786Sps	char *lessecho;
70460786Sps	char *cmd;
70560786Sps
70660786Sps	lessecho = lgetenv("LESSECHO");
70760786Sps	if (lessecho == NULL || *lessecho == '\0')
70860786Sps		lessecho = "lessecho";
70960786Sps	s = esc_metachars(filename);
71060786Sps	if (s == NULL)
71160786Sps	{
71260786Sps		/*
71360786Sps		 * There may be dangerous metachars in this name.
71460786Sps		 * We can't risk passing it to the shell.
71560786Sps		 * {{ For example, do "!;TAB" when the first file
71660786Sps		 *    in the dir is named "rm". }}
71760786Sps		 */
71860786Sps		free(filename);
71960786Sps		return (ofilename);
72060786Sps	}
72160786Sps	/*
72260786Sps	 * Invoke lessecho, and read its output (a globbed list of filenames).
72360786Sps	 */
72460786Sps	cmd = (char *) ecalloc(strlen(lessecho) + strlen(s) + 24, sizeof(char));
72560786Sps	sprintf(cmd, "%s -p0x%x -d0x%x -- %s",
72660786Sps		lessecho, openquote, closequote, s);
72760786Sps	fd = shellcmd(cmd);
72860786Sps	free(s);
72960786Sps	free(cmd);
73060786Sps	if (fd == NULL)
73160786Sps	{
73260786Sps		/*
73360786Sps		 * Cannot create the pipe.
73460786Sps		 * Just return the original (fexpanded) filename.
73560786Sps		 */
73660786Sps		free(filename);
73760786Sps		return (ofilename);
73860786Sps	}
73960786Sps	gfilename = readfd(fd);
74060786Sps	pclose(fd);
74160786Sps	if (*gfilename == '\0')
74260786Sps	{
74360786Sps		free(gfilename);
74460786Sps		free(filename);
74560786Sps		return (ofilename);
74660786Sps	}
74760786Sps}
74860786Sps#else
74960786Sps	/*
75060786Sps	 * No globbing functions at all.  Just use the fexpanded filename.
75160786Sps	 */
75260786Sps	gfilename = save(filename);
75360786Sps#endif
75460786Sps#endif
75560786Sps#endif
75660786Sps	free(filename);
75760786Sps	free(ofilename);
75860786Sps	return (gfilename);
75960786Sps}
76060786Sps
76160786Sps/*
76260786Sps * See if we should open a "replacement file"
76360786Sps * instead of the file we're about to open.
76460786Sps */
76560786Sps	public char *
76660786Spsopen_altfile(filename, pf, pfd)
76760786Sps	char *filename;
76860786Sps	int *pf;
76960786Sps	void **pfd;
77060786Sps{
77160786Sps#if !HAVE_POPEN
77260786Sps	return (NULL);
77360786Sps#else
77460786Sps	char *lessopen;
77560786Sps	char *gfilename;
77660786Sps	char *cmd;
77760786Sps	FILE *fd;
77860786Sps#if HAVE_FILENO
77960786Sps	int returnfd = 0;
78060786Sps#endif
78160786Sps
78260786Sps	if (secure)
78360786Sps		return (NULL);
78460786Sps	ch_ungetchar(-1);
78560786Sps	if ((lessopen = lgetenv("LESSOPEN")) == NULL)
78660786Sps		return (NULL);
78760786Sps	if (strcmp(filename, "-") == 0)
78860786Sps		return (NULL);
78960786Sps	if (*lessopen == '|')
79060786Sps	{
79160786Sps		/*
79260786Sps		 * If LESSOPEN starts with a |, it indicates
79360786Sps		 * a "pipe preprocessor".
79460786Sps		 */
79560786Sps#if HAVE_FILENO
79660786Sps		lessopen++;
79760786Sps		returnfd = 1;
79860786Sps#else
79960786Sps		error("LESSOPEN pipe is not supported", NULL_PARG);
80060786Sps		return (NULL);
80160786Sps#endif
80260786Sps	}
80360786Sps
80460786Sps	gfilename = esc_metachars(filename);
80560786Sps	if (gfilename == NULL)
80660786Sps	{
80760786Sps		/*
80860786Sps		 * Cannot escape metacharacters.
80960786Sps		 */
81060786Sps		return (NULL);
81160786Sps	}
81260786Sps	cmd = (char *) ecalloc(strlen(lessopen) + strlen(gfilename) + 2,
81360786Sps			sizeof(char));
81460786Sps	sprintf(cmd, lessopen, gfilename);
81560786Sps	fd = shellcmd(cmd);
81660786Sps	free(gfilename);
81760786Sps	free(cmd);
81860786Sps	if (fd == NULL)
81960786Sps	{
82060786Sps		/*
82160786Sps		 * Cannot create the pipe.
82260786Sps		 */
82360786Sps		return (NULL);
82460786Sps	}
82560786Sps#if HAVE_FILENO
82660786Sps	if (returnfd)
82760786Sps	{
82860786Sps		int f;
82960786Sps		char c;
83060786Sps
83160786Sps		/*
83260786Sps		 * Read one char to see if the pipe will produce any data.
83360786Sps		 * If it does, push the char back on the pipe.
83460786Sps		 */
83560786Sps		f = fileno(fd);
83660786Sps		SET_BINARY(f);
83760786Sps		if (read(f, &c, 1) != 1)
83860786Sps		{
83960786Sps			/*
84060786Sps			 * Pipe is empty.  This means there is no alt file.
84160786Sps			 */
84260786Sps			pclose(fd);
84360786Sps			return (NULL);
84460786Sps		}
84560786Sps		ch_ungetchar(c);
84660786Sps		*pfd = (void *) fd;
84760786Sps		*pf = f;
84860786Sps		return (save("-"));
84960786Sps	}
85060786Sps#endif
85160786Sps	gfilename = readfd(fd);
85260786Sps	pclose(fd);
85360786Sps	if (*gfilename == '\0')
85460786Sps		/*
85560786Sps		 * Pipe is empty.  This means there is no alt file.
85660786Sps		 */
85760786Sps		return (NULL);
85860786Sps	return (gfilename);
85960786Sps#endif /* HAVE_POPEN */
86060786Sps}
86160786Sps
86260786Sps/*
86360786Sps * Close a replacement file.
86460786Sps */
86560786Sps	public void
86660786Spsclose_altfile(altfilename, filename, pipefd)
86760786Sps	char *altfilename;
86860786Sps	char *filename;
86960786Sps	void *pipefd;
87060786Sps{
87160786Sps#if HAVE_POPEN
87260786Sps	char *lessclose;
87360786Sps	char *gfilename;
87460786Sps	char *galtfilename;
87560786Sps	FILE *fd;
87660786Sps	char *cmd;
87760786Sps
87860786Sps	if (secure)
87960786Sps		return;
88060786Sps	if (pipefd != NULL)
88160786Sps		pclose((FILE*) pipefd);
88260786Sps	if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
88360786Sps	     	return;
88460786Sps	gfilename = esc_metachars(filename);
88560786Sps	if (gfilename == NULL)
88660786Sps	{
88760786Sps		return;
88860786Sps	}
88960786Sps	galtfilename = esc_metachars(altfilename);
89060786Sps	if (galtfilename == NULL)
89160786Sps	{
89260786Sps		free(gfilename);
89360786Sps		return;
89460786Sps	}
89560786Sps	cmd = (char *) ecalloc(strlen(lessclose) + strlen(gfilename) +
89660786Sps			strlen(galtfilename) + 2, sizeof(char));
89760786Sps	sprintf(cmd, lessclose, gfilename, galtfilename);
89860786Sps	fd = shellcmd(cmd);
89960786Sps	free(galtfilename);
90060786Sps	free(gfilename);
90160786Sps	free(cmd);
90260786Sps	if (fd != NULL)
90360786Sps		pclose(fd);
90460786Sps#endif
90560786Sps}
90660786Sps
90760786Sps/*
90860786Sps * Is the specified file a directory?
90960786Sps */
91060786Sps	public int
91160786Spsis_dir(filename)
91260786Sps	char *filename;
91360786Sps{
91460786Sps	int isdir = 0;
91560786Sps
91660786Sps	filename = unquote_file(filename);
91760786Sps#if HAVE_STAT
91860786Sps{
91960786Sps	int r;
92060786Sps	struct stat statbuf;
92160786Sps
92260786Sps	r = stat(filename, &statbuf);
92360786Sps	isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
92460786Sps}
92560786Sps#else
92660786Sps#ifdef _OSK
92760786Sps{
92860786Sps	register int f;
92960786Sps
93060786Sps	f = open(filename, S_IREAD | S_IFDIR);
93160786Sps	if (f >= 0)
93260786Sps		close(f);
93360786Sps	isdir = (f >= 0);
93460786Sps}
93560786Sps#endif
93660786Sps#endif
93760786Sps	free(filename);
93860786Sps	return (isdir);
93960786Sps}
94060786Sps
94160786Sps/*
94260786Sps * Returns NULL if the file can be opened and
94360786Sps * is an ordinary file, otherwise an error message
94460786Sps * (if it cannot be opened or is a directory, etc.)
94560786Sps */
94660786Sps	public char *
94760786Spsbad_file(filename)
94860786Sps	char *filename;
94960786Sps{
95060786Sps	register char *m = NULL;
95160786Sps
95260786Sps	filename = unquote_file(filename);
95360786Sps	if (is_dir(filename))
95460786Sps	{
95560786Sps		static char is_dir[] = " is a directory";
95660786Sps
95760786Sps		m = (char *) ecalloc(strlen(filename) + sizeof(is_dir),
95860786Sps			sizeof(char));
95960786Sps		strcpy(m, filename);
96060786Sps		strcat(m, is_dir);
96160786Sps	} else
96260786Sps	{
96360786Sps#if HAVE_STAT
96460786Sps		int r;
96560786Sps		struct stat statbuf;
96660786Sps
96760786Sps		r = stat(filename, &statbuf);
96860786Sps		if (r < 0)
96960786Sps		{
97060786Sps			m = errno_message(filename);
97160786Sps		} else if (force_open)
97260786Sps		{
97360786Sps			m = NULL;
97460786Sps		} else if (!S_ISREG(statbuf.st_mode))
97560786Sps		{
97660786Sps			static char not_reg[] = " is not a regular file (use -f to see it)";
97760786Sps			m = (char *) ecalloc(strlen(filename) + sizeof(not_reg),
97860786Sps				sizeof(char));
97960786Sps			strcpy(m, filename);
98060786Sps			strcat(m, not_reg);
98160786Sps		}
98260786Sps#endif
98360786Sps	}
98460786Sps	free(filename);
98560786Sps	return (m);
98660786Sps}
98760786Sps
98860786Sps/*
98960786Sps * Return the size of a file, as cheaply as possible.
99060786Sps * In Unix, we can stat the file.
99160786Sps */
99260786Sps	public POSITION
99360786Spsfilesize(f)
99460786Sps	int f;
99560786Sps{
99660786Sps#if HAVE_STAT
99760786Sps	struct stat statbuf;
99860786Sps
99960786Sps	if (fstat(f, &statbuf) >= 0)
100060786Sps		return ((POSITION) statbuf.st_size);
100160786Sps#else
100260786Sps#ifdef _OSK
100360786Sps	long size;
100460786Sps
100560786Sps	if ((size = (long) _gs_size(f)) >= 0)
100660786Sps		return ((POSITION) size);
100760786Sps#endif
100860786Sps#endif
100960786Sps	return (seek_filesize(f));
101060786Sps}
101160786Sps
1012