filename.c revision 191930
1130803Smarcel/*
2130803Smarcel * Copyright (C) 1984-2008  Mark Nudelman
3130803Smarcel *
4130803Smarcel * You may distribute under the terms of either the GNU General Public
5130803Smarcel * License or the Less License, as specified in the README file.
6130803Smarcel *
7130803Smarcel * For more information about less, or for information on how to
8130803Smarcel * contact the author, see the README file.
9130803Smarcel */
10130803Smarcel
11130803Smarcel
12130803Smarcel/*
13130803Smarcel * Routines to mess around with filenames (and files).
14130803Smarcel * Much of this is very OS dependent.
15130803Smarcel */
16130803Smarcel
17130803Smarcel#include "less.h"
18130803Smarcel#include "lglob.h"
19130803Smarcel#if MSDOS_COMPILER
20130803Smarcel#include <dos.h>
21130803Smarcel#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER)
22130803Smarcel#include <dir.h>
23130803Smarcel#endif
24130803Smarcel#if MSDOS_COMPILER==DJGPPC
25130803Smarcel#include <glob.h>
26130803Smarcel#include <dir.h>
27130803Smarcel#define _MAX_PATH	PATH_MAX
28130803Smarcel#endif
29130803Smarcel#endif
30130803Smarcel#ifdef _OSK
31130803Smarcel#include <rbf.h>
32130803Smarcel#ifndef _OSK_MWC32
33130803Smarcel#include <modes.h>
34130803Smarcel#endif
35130803Smarcel#endif
36130803Smarcel#if OS2
37130803Smarcel#include <signal.h>
38130803Smarcel#endif
39130803Smarcel
40130803Smarcel#if HAVE_STAT
41130803Smarcel#include <sys/stat.h>
42130803Smarcel#ifndef S_ISDIR
43130803Smarcel#define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
44130803Smarcel#endif
45130803Smarcel#ifndef S_ISREG
46130803Smarcel#define	S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
47130803Smarcel#endif
48130803Smarcel#endif
49130803Smarcel
50130803Smarcel
51130803Smarcelextern int force_open;
52130803Smarcelextern int secure;
53130803Smarcelextern int use_lessopen;
54130803Smarcelextern int ctldisp;
55130803Smarcelextern int utf_mode;
56130803Smarcelextern IFILE curr_ifile;
57130803Smarcelextern IFILE old_ifile;
58130803Smarcel#if SPACES_IN_FILENAMES
59130803Smarcelextern char openquote;
60130803Smarcelextern char closequote;
61130803Smarcel#endif
62130803Smarcel
63130803Smarcel/*
64130803Smarcel * Remove quotes around a filename.
65130803Smarcel */
66130803Smarcel	public char *
67130803Smarcelshell_unquote(str)
68130803Smarcel	char *str;
69130803Smarcel{
70130803Smarcel	char *name;
71130803Smarcel	char *p;
72130803Smarcel
73130803Smarcel	name = p = (char *) ecalloc(strlen(str)+1, sizeof(char));
74130803Smarcel	if (*str == openquote)
75130803Smarcel	{
76130803Smarcel		str++;
77130803Smarcel		while (*str != '\0')
78130803Smarcel		{
79130803Smarcel			if (*str == closequote)
80130803Smarcel			{
81130803Smarcel				if (str[1] != closequote)
82130803Smarcel					break;
83130803Smarcel				str++;
84130803Smarcel			}
85130803Smarcel			*p++ = *str++;
86130803Smarcel		}
87130803Smarcel	} else
88130803Smarcel	{
89130803Smarcel		char *esc = get_meta_escape();
90130803Smarcel		int esclen = strlen(esc);
91130803Smarcel		while (*str != '\0')
92130803Smarcel		{
93130803Smarcel			if (esclen > 0 && strncmp(str, esc, esclen) == 0)
94130803Smarcel				str += esclen;
95130803Smarcel			*p++ = *str++;
96130803Smarcel		}
97130803Smarcel	}
98130803Smarcel	*p = '\0';
99130803Smarcel	return (name);
100130803Smarcel}
101130803Smarcel
102130803Smarcel/*
103130803Smarcel * Get the shell's escape character.
104130803Smarcel */
105130803Smarcel	public char *
106130803Smarcelget_meta_escape()
107130803Smarcel{
108130803Smarcel	char *s;
109130803Smarcel
110130803Smarcel	s = lgetenv("LESSMETAESCAPE");
111130803Smarcel	if (s == NULL)
112130803Smarcel		s = DEF_METAESCAPE;
113130803Smarcel	return (s);
114130803Smarcel}
115130803Smarcel
116130803Smarcel/*
117130803Smarcel * Get the characters which the shell considers to be "metacharacters".
118130803Smarcel */
119130803Smarcel	static char *
120130803Smarcelmetachars()
121130803Smarcel{
122130803Smarcel	static char *mchars = NULL;
123130803Smarcel
124130803Smarcel	if (mchars == NULL)
125130803Smarcel	{
126130803Smarcel		mchars = lgetenv("LESSMETACHARS");
127130803Smarcel		if (mchars == NULL)
128130803Smarcel			mchars = DEF_METACHARS;
129130803Smarcel	}
130130803Smarcel	return (mchars);
131130803Smarcel}
132130803Smarcel
133130803Smarcel/*
134130803Smarcel * Is this a shell metacharacter?
135130803Smarcel */
136130803Smarcel	static int
137130803Smarcelmetachar(c)
138130803Smarcel	char c;
139130803Smarcel{
140130803Smarcel	return (strchr(metachars(), c) != NULL);
141130803Smarcel}
142130803Smarcel
143130803Smarcel/*
144130803Smarcel * Insert a backslash before each metacharacter in a string.
145130803Smarcel */
146130803Smarcel	public char *
147130803Smarcelshell_quote(s)
148130803Smarcel	char *s;
149130803Smarcel{
150130803Smarcel	char *p;
151130803Smarcel	char *newstr;
152130803Smarcel	int len;
153130803Smarcel	char *esc = get_meta_escape();
154130803Smarcel	int esclen = strlen(esc);
155130803Smarcel	int use_quotes = 0;
156130803Smarcel	int have_quotes = 0;
157130803Smarcel
158130803Smarcel	/*
159130803Smarcel	 * Determine how big a string we need to allocate.
160130803Smarcel	 */
161130803Smarcel	len = 1; /* Trailing null byte */
162130803Smarcel	for (p = s;  *p != '\0';  p++)
163130803Smarcel	{
164130803Smarcel		len++;
165130803Smarcel		if (*p == openquote || *p == closequote)
166130803Smarcel			have_quotes = 1;
167130803Smarcel		if (metachar(*p))
168130803Smarcel		{
169130803Smarcel			if (esclen == 0)
170130803Smarcel			{
171130803Smarcel				/*
172130803Smarcel				 * We've got a metachar, but this shell
173130803Smarcel				 * doesn't support escape chars.  Use quotes.
174130803Smarcel				 */
175130803Smarcel				use_quotes = 1;
176130803Smarcel			} else
177130803Smarcel			{
178130803Smarcel				/*
179130803Smarcel				 * Allow space for the escape char.
180130803Smarcel				 */
181130803Smarcel				len += esclen;
182130803Smarcel			}
183130803Smarcel		}
184130803Smarcel	}
185130803Smarcel	if (use_quotes)
186130803Smarcel	{
187130803Smarcel		if (have_quotes)
188130803Smarcel			/*
189130803Smarcel			 * We can't quote a string that contains quotes.
190130803Smarcel			 */
191130803Smarcel			return (NULL);
192130803Smarcel		len = strlen(s) + 3;
193130803Smarcel	}
194130803Smarcel	/*
195130803Smarcel	 * Allocate and construct the new string.
196130803Smarcel	 */
197130803Smarcel	newstr = p = (char *) ecalloc(len, sizeof(char));
198130803Smarcel	if (use_quotes)
199130803Smarcel	{
200130803Smarcel		SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote);
201130803Smarcel	} else
202130803Smarcel	{
203130803Smarcel		while (*s != '\0')
204130803Smarcel		{
205130803Smarcel			if (metachar(*s))
206130803Smarcel			{
207130803Smarcel				/*
208130803Smarcel				 * Add the escape char.
209130803Smarcel				 */
210130803Smarcel				strcpy(p, esc);
211130803Smarcel				p += esclen;
212130803Smarcel			}
213130803Smarcel			*p++ = *s++;
214130803Smarcel		}
215130803Smarcel		*p = '\0';
216130803Smarcel	}
217130803Smarcel	return (newstr);
218130803Smarcel}
219130803Smarcel
220130803Smarcel/*
221130803Smarcel * Return a pathname that points to a specified file in a specified directory.
222130803Smarcel * Return NULL if the file does not exist in the directory.
223130803Smarcel */
224130803Smarcel	static char *
225130803Smarceldirfile(dirname, filename)
226130803Smarcel	char *dirname;
227130803Smarcel	char *filename;
228130803Smarcel{
229130803Smarcel	char *pathname;
230130803Smarcel	char *qpathname;
231130803Smarcel	int len;
232130803Smarcel	int f;
233130803Smarcel
234130803Smarcel	if (dirname == NULL || *dirname == '\0')
235130803Smarcel		return (NULL);
236130803Smarcel	/*
237130803Smarcel	 * Construct the full pathname.
238130803Smarcel	 */
239130803Smarcel	len= strlen(dirname) + strlen(filename) + 2;
240130803Smarcel	pathname = (char *) calloc(len, sizeof(char));
241130803Smarcel	if (pathname == NULL)
242130803Smarcel		return (NULL);
243130803Smarcel	SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename);
244130803Smarcel	/*
245130803Smarcel	 * Make sure the file exists.
246130803Smarcel	 */
247130803Smarcel	qpathname = shell_unquote(pathname);
248130803Smarcel	f = open(qpathname, OPEN_READ);
249130803Smarcel	if (f < 0)
250130803Smarcel	{
251130803Smarcel		free(pathname);
252130803Smarcel		pathname = NULL;
253130803Smarcel	} else
254130803Smarcel	{
255130803Smarcel		close(f);
256130803Smarcel	}
257130803Smarcel	free(qpathname);
258130803Smarcel	return (pathname);
259130803Smarcel}
260130803Smarcel
261130803Smarcel/*
262130803Smarcel * Return the full pathname of the given file in the "home directory".
263130803Smarcel */
264130803Smarcel	public char *
265130803Smarcelhomefile(filename)
266130803Smarcel	char *filename;
267130803Smarcel{
268130803Smarcel	register char *pathname;
269130803Smarcel
270130803Smarcel	/*
271130803Smarcel	 * Try $HOME/filename.
272130803Smarcel	 */
273130803Smarcel	pathname = dirfile(lgetenv("HOME"), filename);
274130803Smarcel	if (pathname != NULL)
275130803Smarcel		return (pathname);
276130803Smarcel#if OS2
277130803Smarcel	/*
278130803Smarcel	 * Try $INIT/filename.
279130803Smarcel	 */
280130803Smarcel	pathname = dirfile(lgetenv("INIT"), filename);
281130803Smarcel	if (pathname != NULL)
282130803Smarcel		return (pathname);
283130803Smarcel#endif
284130803Smarcel#if MSDOS_COMPILER || OS2
285130803Smarcel	/*
286130803Smarcel	 * Look for the file anywhere on search path.
287130803Smarcel	 */
288130803Smarcel	pathname = (char *) calloc(_MAX_PATH, sizeof(char));
289130803Smarcel#if MSDOS_COMPILER==DJGPPC
290130803Smarcel	{
291130803Smarcel		char *res = searchpath(filename);
292130803Smarcel		if (res == 0)
293130803Smarcel			*pathname = '\0';
294130803Smarcel		else
295130803Smarcel			strcpy(pathname, res);
296130803Smarcel	}
297130803Smarcel#else
298130803Smarcel	_searchenv(filename, "PATH", pathname);
299130803Smarcel#endif
300130803Smarcel	if (*pathname != '\0')
301130803Smarcel		return (pathname);
302130803Smarcel	free(pathname);
303130803Smarcel#endif
304130803Smarcel	return (NULL);
305130803Smarcel}
306130803Smarcel
307130803Smarcel/*
308130803Smarcel * Expand a string, substituting any "%" with the current filename,
309130803Smarcel * and any "#" with the previous filename.
310130803Smarcel * But a string of N "%"s is just replaced with N-1 "%"s.
311130803Smarcel * Likewise for a string of N "#"s.
312130803Smarcel * {{ This is a lot of work just to support % and #. }}
313130803Smarcel */
314130803Smarcel	public char *
315130803Smarcelfexpand(s)
316130803Smarcel	char *s;
317130803Smarcel{
318130803Smarcel	register char *fr, *to;
319130803Smarcel	register int n;
320130803Smarcel	register char *e;
321130803Smarcel	IFILE ifile;
322130803Smarcel
323130803Smarcel#define	fchar_ifile(c) \
324130803Smarcel	((c) == '%' ? curr_ifile : \
325130803Smarcel	 (c) == '#' ? old_ifile : NULL_IFILE)
326130803Smarcel
327130803Smarcel	/*
328130803Smarcel	 * Make one pass to see how big a buffer we
329130803Smarcel	 * need to allocate for the expanded string.
330130803Smarcel	 */
331130803Smarcel	n = 0;
332130803Smarcel	for (fr = s;  *fr != '\0';  fr++)
333130803Smarcel	{
334130803Smarcel		switch (*fr)
335130803Smarcel		{
336130803Smarcel		case '%':
337130803Smarcel		case '#':
338130803Smarcel			if (fr > s && fr[-1] == *fr)
339130803Smarcel			{
340130803Smarcel				/*
341130803Smarcel				 * Second (or later) char in a string
342130803Smarcel				 * of identical chars.  Treat as normal.
343130803Smarcel				 */
344130803Smarcel				n++;
345130803Smarcel			} else if (fr[1] != *fr)
346130803Smarcel			{
347130803Smarcel				/*
348130803Smarcel				 * Single char (not repeated).  Treat specially.
349130803Smarcel				 */
350130803Smarcel				ifile = fchar_ifile(*fr);
351130803Smarcel				if (ifile == NULL_IFILE)
352130803Smarcel					n++;
353130803Smarcel				else
354130803Smarcel					n += strlen(get_filename(ifile));
355130803Smarcel			}
356130803Smarcel			/*
357130803Smarcel			 * Else it is the first char in a string of
358130803Smarcel			 * identical chars.  Just discard it.
359130803Smarcel			 */
360130803Smarcel			break;
361130803Smarcel		default:
362130803Smarcel			n++;
363130803Smarcel			break;
364130803Smarcel		}
365130803Smarcel	}
366130803Smarcel
367130803Smarcel	e = (char *) ecalloc(n+1, sizeof(char));
368130803Smarcel
369130803Smarcel	/*
370130803Smarcel	 * Now copy the string, expanding any "%" or "#".
371130803Smarcel	 */
372130803Smarcel	to = e;
373130803Smarcel	for (fr = s;  *fr != '\0';  fr++)
374130803Smarcel	{
375130803Smarcel		switch (*fr)
376130803Smarcel		{
377130803Smarcel		case '%':
378130803Smarcel		case '#':
379130803Smarcel			if (fr > s && fr[-1] == *fr)
380130803Smarcel			{
381130803Smarcel				*to++ = *fr;
382130803Smarcel			} else if (fr[1] != *fr)
383130803Smarcel			{
384130803Smarcel				ifile = fchar_ifile(*fr);
385130803Smarcel				if (ifile == NULL_IFILE)
386130803Smarcel					*to++ = *fr;
387130803Smarcel				else
388130803Smarcel				{
389130803Smarcel					strcpy(to, get_filename(ifile));
390130803Smarcel					to += strlen(to);
391130803Smarcel				}
392130803Smarcel			}
393130803Smarcel			break;
394130803Smarcel		default:
395130803Smarcel			*to++ = *fr;
396130803Smarcel			break;
397130803Smarcel		}
398130803Smarcel	}
399130803Smarcel	*to = '\0';
400130803Smarcel	return (e);
401130803Smarcel}
402130803Smarcel
403130803Smarcel#if TAB_COMPLETE_FILENAME
404130803Smarcel
405130803Smarcel/*
406130803Smarcel * Return a blank-separated list of filenames which "complete"
407130803Smarcel * the given string.
408130803Smarcel */
409130803Smarcel	public char *
410130803Smarcelfcomplete(s)
411130803Smarcel	char *s;
412130803Smarcel{
413130803Smarcel	char *fpat;
414130803Smarcel	char *qs;
415130803Smarcel
416130803Smarcel	if (secure)
417130803Smarcel		return (NULL);
418130803Smarcel	/*
419130803Smarcel	 * Complete the filename "s" by globbing "s*".
420130803Smarcel	 */
421130803Smarcel#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC)
422130803Smarcel	/*
423130803Smarcel	 * But in DOS, we have to glob "s*.*".
424130803Smarcel	 * But if the final component of the filename already has
425130803Smarcel	 * a dot in it, just do "s*".
426130803Smarcel	 * (Thus, "FILE" is globbed as "FILE*.*",
427130803Smarcel	 *  but "FILE.A" is globbed as "FILE.A*").
428130803Smarcel	 */
429130803Smarcel	{
430130803Smarcel		char *slash;
431130803Smarcel		int len;
432130803Smarcel		for (slash = s+strlen(s)-1;  slash > s;  slash--)
433130803Smarcel			if (*slash == *PATHNAME_SEP || *slash == '/')
434130803Smarcel				break;
435130803Smarcel		len = strlen(s) + 4;
436130803Smarcel		fpat = (char *) ecalloc(len, sizeof(char));
437130803Smarcel		if (strchr(slash, '.') == NULL)
438130803Smarcel			SNPRINTF1(fpat, len, "%s*.*", s);
439130803Smarcel		else
440130803Smarcel			SNPRINTF1(fpat, len, "%s*", s);
441130803Smarcel	}
442130803Smarcel#else
443130803Smarcel	{
444130803Smarcel	int len = strlen(s) + 2;
445130803Smarcel	fpat = (char *) ecalloc(len, sizeof(char));
446130803Smarcel	SNPRINTF1(fpat, len, "%s*", s);
447130803Smarcel	}
448130803Smarcel#endif
449130803Smarcel	qs = lglob(fpat);
450130803Smarcel	s = shell_unquote(qs);
451130803Smarcel	if (strcmp(s,fpat) == 0)
452130803Smarcel	{
453130803Smarcel		/*
454130803Smarcel		 * The filename didn't expand.
455130803Smarcel		 */
456130803Smarcel		free(qs);
457130803Smarcel		qs = NULL;
458130803Smarcel	}
459130803Smarcel	free(s);
460130803Smarcel	free(fpat);
461130803Smarcel	return (qs);
462130803Smarcel}
463130803Smarcel#endif
464130803Smarcel
465130803Smarcel/*
466130803Smarcel * Try to determine if a file is "binary".
467130803Smarcel * This is just a guess, and we need not try too hard to make it accurate.
468130803Smarcel */
469130803Smarcel	public int
470130803Smarcelbin_file(f)
471130803Smarcel	int f;
472130803Smarcel{
473130803Smarcel	int n;
474130803Smarcel	int bin_count = 0;
475130803Smarcel	char data[256];
476130803Smarcel	char* p;
477130803Smarcel	char* pend;
478130803Smarcel
479130803Smarcel	if (!seekable(f))
480130803Smarcel		return (0);
481130803Smarcel	if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
482130803Smarcel		return (0);
483130803Smarcel	n = read(f, data, sizeof(data));
484130803Smarcel	pend = &data[n];
485130803Smarcel	for (p = data;  p < pend;  )
486130803Smarcel	{
487130803Smarcel		LWCHAR c = step_char(&p, +1, pend);
488130803Smarcel		if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
489130803Smarcel		{
490130803Smarcel			do {
491130803Smarcel				c = step_char(&p, +1, pend);
492130803Smarcel			} while (p < pend && is_ansi_middle(c));
493130803Smarcel		} else if (binary_char(c))
494130803Smarcel			bin_count++;
495130803Smarcel	}
496130803Smarcel	/*
497130803Smarcel	 * Call it a binary file if there are more than 5 binary characters
498130803Smarcel	 * in the first 256 bytes of the file.
499130803Smarcel	 */
500130803Smarcel	return (bin_count > 5);
501130803Smarcel}
502130803Smarcel
503130803Smarcel/*
504130803Smarcel * Try to determine the size of a file by seeking to the end.
505130803Smarcel */
506130803Smarcel	static POSITION
507130803Smarcelseek_filesize(f)
508130803Smarcel	int f;
509130803Smarcel{
510130803Smarcel	off_t spos;
511130803Smarcel
512130803Smarcel	spos = lseek(f, (off_t)0, SEEK_END);
513130803Smarcel	if (spos == BAD_LSEEK)
514130803Smarcel		return (NULL_POSITION);
515130803Smarcel	return ((POSITION) spos);
516130803Smarcel}
517130803Smarcel
518130803Smarcel/*
519130803Smarcel * Read a string from a file.
520130803Smarcel * Return a pointer to the string in memory.
521130803Smarcel */
522130803Smarcel	static char *
523130803Smarcelreadfd(fd)
524130803Smarcel	FILE *fd;
525130803Smarcel{
526130803Smarcel	int len;
527130803Smarcel	int ch;
528130803Smarcel	char *buf;
529130803Smarcel	char *p;
530130803Smarcel
531130803Smarcel	/*
532130803Smarcel	 * Make a guess about how many chars in the string
533130803Smarcel	 * and allocate a buffer to hold it.
534130803Smarcel	 */
535130803Smarcel	len = 100;
536130803Smarcel	buf = (char *) ecalloc(len, sizeof(char));
537130803Smarcel	for (p = buf;  ;  p++)
538130803Smarcel	{
539130803Smarcel		if ((ch = getc(fd)) == '\n' || ch == EOF)
540130803Smarcel			break;
541130803Smarcel		if (p - buf >= len-1)
542130803Smarcel		{
543130803Smarcel			/*
544130803Smarcel			 * The string is too big to fit in the buffer we have.
545130803Smarcel			 * Allocate a new buffer, twice as big.
546130803Smarcel			 */
547130803Smarcel			len *= 2;
548130803Smarcel			*p = '\0';
549130803Smarcel			p = (char *) ecalloc(len, sizeof(char));
550130803Smarcel			strcpy(p, buf);
551130803Smarcel			free(buf);
552130803Smarcel			buf = p;
553130803Smarcel			p = buf + strlen(buf);
554130803Smarcel		}
555130803Smarcel		*p = ch;
556130803Smarcel	}
557130803Smarcel	*p = '\0';
558130803Smarcel	return (buf);
559130803Smarcel}
560130803Smarcel
561130803Smarcel
562130803Smarcel
563130803Smarcel#if HAVE_POPEN
564130803Smarcel
565130803SmarcelFILE *popen();
566130803Smarcel
567130803Smarcel/*
568130803Smarcel * Execute a shell command.
569130803Smarcel * Return a pointer to a pipe connected to the shell command's standard output.
570130803Smarcel */
571130803Smarcel	static FILE *
572130803Smarcelshellcmd(cmd)
573130803Smarcel	char *cmd;
574130803Smarcel{
575130803Smarcel	FILE *fd;
576130803Smarcel
577130803Smarcel#if HAVE_SHELL
578130803Smarcel	char *shell;
579130803Smarcel
580130803Smarcel	shell = lgetenv("SHELL");
581130803Smarcel	if (shell != NULL && *shell != '\0')
582130803Smarcel	{
583130803Smarcel		char *scmd;
584130803Smarcel		char *esccmd;
585130803Smarcel
586130803Smarcel		/*
587130803Smarcel		 * Read the output of <$SHELL -c cmd>.
588130803Smarcel		 * Escape any metacharacters in the command.
589130803Smarcel		 */
590130803Smarcel		esccmd = shell_quote(cmd);
591130803Smarcel		if (esccmd == NULL)
592130803Smarcel		{
593130803Smarcel			fd = popen(cmd, "r");
594130803Smarcel		} else
595130803Smarcel		{
596130803Smarcel			int len = strlen(shell) + strlen(esccmd) + 5;
597130803Smarcel			scmd = (char *) ecalloc(len, sizeof(char));
598130803Smarcel			SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd);
599130803Smarcel			free(esccmd);
600130803Smarcel			fd = popen(scmd, "r");
601130803Smarcel			free(scmd);
602130803Smarcel		}
603130803Smarcel	} else
604130803Smarcel#endif
605130803Smarcel	{
606130803Smarcel		fd = popen(cmd, "r");
607130803Smarcel	}
608130803Smarcel	/*
609130803Smarcel	 * Redirection in `popen' might have messed with the
610130803Smarcel	 * standard devices.  Restore binary input mode.
611130803Smarcel	 */
612130803Smarcel	SET_BINARY(0);
613130803Smarcel	return (fd);
614130803Smarcel}
615130803Smarcel
616130803Smarcel#endif /* HAVE_POPEN */
617130803Smarcel
618130803Smarcel
619130803Smarcel/*
620130803Smarcel * Expand a filename, doing any system-specific metacharacter substitutions.
621130803Smarcel */
622130803Smarcel	public char *
623130803Smarcellglob(filename)
624130803Smarcel	char *filename;
625130803Smarcel{
626130803Smarcel	char *gfilename;
627130803Smarcel	char *ofilename;
628130803Smarcel
629130803Smarcel	ofilename = fexpand(filename);
630130803Smarcel	if (secure)
631130803Smarcel		return (ofilename);
632130803Smarcel	filename = shell_unquote(ofilename);
633130803Smarcel
634130803Smarcel#ifdef DECL_GLOB_LIST
635130803Smarcel{
636130803Smarcel	/*
637130803Smarcel	 * The globbing function returns a list of names.
638130803Smarcel	 */
639130803Smarcel	int length;
640130803Smarcel	char *p;
641130803Smarcel	char *qfilename;
642130803Smarcel	DECL_GLOB_LIST(list)
643130803Smarcel
644130803Smarcel	GLOB_LIST(filename, list);
645130803Smarcel	if (GLOB_LIST_FAILED(list))
646130803Smarcel	{
647130803Smarcel		free(filename);
648130803Smarcel		return (ofilename);
649130803Smarcel	}
650130803Smarcel	length = 1; /* Room for trailing null byte */
651130803Smarcel	for (SCAN_GLOB_LIST(list, p))
652130803Smarcel	{
653130803Smarcel		INIT_GLOB_LIST(list, p);
654130803Smarcel		qfilename = shell_quote(p);
655130803Smarcel		if (qfilename != NULL)
656130803Smarcel		{
657130803Smarcel	  		length += strlen(qfilename) + 1;
658130803Smarcel			free(qfilename);
659130803Smarcel		}
660130803Smarcel	}
661130803Smarcel	gfilename = (char *) ecalloc(length, sizeof(char));
662130803Smarcel	for (SCAN_GLOB_LIST(list, p))
663130803Smarcel	{
664130803Smarcel		INIT_GLOB_LIST(list, p);
665130803Smarcel		qfilename = shell_quote(p);
666130803Smarcel		if (qfilename != NULL)
667130803Smarcel		{
668130803Smarcel			sprintf(gfilename + strlen(gfilename), "%s ", qfilename);
669130803Smarcel			free(qfilename);
670130803Smarcel		}
671130803Smarcel	}
672130803Smarcel	/*
673130803Smarcel	 * Overwrite the final trailing space with a null terminator.
674130803Smarcel	 */
675130803Smarcel	*--p = '\0';
676130803Smarcel	GLOB_LIST_DONE(list);
677130803Smarcel}
678130803Smarcel#else
679130803Smarcel#ifdef DECL_GLOB_NAME
680130803Smarcel{
681130803Smarcel	/*
682130803Smarcel	 * The globbing function returns a single name, and
683130803Smarcel	 * is called multiple times to walk thru all names.
684130803Smarcel	 */
685130803Smarcel	register char *p;
686130803Smarcel	register int len;
687130803Smarcel	register int n;
688130803Smarcel	char *pathname;
689130803Smarcel	char *qpathname;
690130803Smarcel	DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
691130803Smarcel
692130803Smarcel	GLOB_FIRST_NAME(filename, &fnd, handle);
693130803Smarcel	if (GLOB_FIRST_FAILED(handle))
694130803Smarcel	{
695130803Smarcel		free(filename);
696130803Smarcel		return (ofilename);
697130803Smarcel	}
698130803Smarcel
699130803Smarcel	_splitpath(filename, drive, dir, fname, ext);
700130803Smarcel	len = 100;
701130803Smarcel	gfilename = (char *) ecalloc(len, sizeof(char));
702130803Smarcel	p = gfilename;
703130803Smarcel	do {
704130803Smarcel		n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
705130803Smarcel		pathname = (char *) ecalloc(n, sizeof(char));
706130803Smarcel		SNPRINTF3(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME);
707130803Smarcel		qpathname = shell_quote(pathname);
708130803Smarcel		free(pathname);
709		if (qpathname != NULL)
710		{
711			n = strlen(qpathname);
712			while (p - gfilename + n + 2 >= len)
713			{
714				/*
715				 * No room in current buffer.
716				 * Allocate a bigger one.
717				 */
718				len *= 2;
719				*p = '\0';
720				p = (char *) ecalloc(len, sizeof(char));
721				strcpy(p, gfilename);
722				free(gfilename);
723				gfilename = p;
724				p = gfilename + strlen(gfilename);
725			}
726			strcpy(p, qpathname);
727			free(qpathname);
728			p += n;
729			*p++ = ' ';
730		}
731	} while (GLOB_NEXT_NAME(handle, &fnd) == 0);
732
733	/*
734	 * Overwrite the final trailing space with a null terminator.
735	 */
736	*--p = '\0';
737	GLOB_NAME_DONE(handle);
738}
739#else
740#if HAVE_POPEN
741{
742	/*
743	 * We get the shell to glob the filename for us by passing
744	 * an "echo" command to the shell and reading its output.
745	 */
746	FILE *fd;
747	char *s;
748	char *lessecho;
749	char *cmd;
750	char *esc;
751	int len;
752
753	esc = get_meta_escape();
754	if (strlen(esc) == 0)
755		esc = "-";
756	esc = shell_quote(esc);
757	if (esc == NULL)
758	{
759		free(filename);
760		return (ofilename);
761	}
762	lessecho = lgetenv("LESSECHO");
763	if (lessecho == NULL || *lessecho == '\0')
764		lessecho = "lessecho";
765	/*
766	 * Invoke lessecho, and read its output (a globbed list of filenames).
767	 */
768	len = strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24;
769	cmd = (char *) ecalloc(len, sizeof(char));
770	SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc);
771	free(esc);
772	for (s = metachars();  *s != '\0';  s++)
773		sprintf(cmd + strlen(cmd), "-n0x%x ", *s);
774	sprintf(cmd + strlen(cmd), "-- %s", ofilename);
775	fd = shellcmd(cmd);
776	free(cmd);
777	if (fd == NULL)
778	{
779		/*
780		 * Cannot create the pipe.
781		 * Just return the original (fexpanded) filename.
782		 */
783		free(filename);
784		return (ofilename);
785	}
786	gfilename = readfd(fd);
787	pclose(fd);
788	if (*gfilename == '\0')
789	{
790		free(gfilename);
791		free(filename);
792		return (ofilename);
793	}
794}
795#else
796	/*
797	 * No globbing functions at all.  Just use the fexpanded filename.
798	 */
799	gfilename = save(filename);
800#endif
801#endif
802#endif
803	free(filename);
804	free(ofilename);
805	return (gfilename);
806}
807
808/*
809 * See if we should open a "replacement file"
810 * instead of the file we're about to open.
811 */
812	public char *
813open_altfile(filename, pf, pfd)
814	char *filename;
815	int *pf;
816	void **pfd;
817{
818#if !HAVE_POPEN
819	return (NULL);
820#else
821	char *lessopen;
822	char *cmd;
823	int len;
824	FILE *fd;
825#if HAVE_FILENO
826	int returnfd = 0;
827#endif
828
829	if (!use_lessopen || secure)
830		return (NULL);
831	ch_ungetchar(-1);
832	if ((lessopen = lgetenv("LESSOPEN")) == NULL)
833		return (NULL);
834	if (*lessopen == '|')
835	{
836		/*
837		 * If LESSOPEN starts with a |, it indicates
838		 * a "pipe preprocessor".
839		 */
840#if !HAVE_FILENO
841		error("LESSOPEN pipe is not supported", NULL_PARG);
842		return (NULL);
843#else
844		lessopen++;
845		returnfd = 1;
846		if (*lessopen == '-') {
847			/*
848			 * Lessopen preprocessor will accept "-" as a filename.
849			 */
850			lessopen++;
851		} else {
852			if (strcmp(filename, "-") == 0)
853				return (NULL);
854		}
855#endif
856	}
857
858	len = strlen(lessopen) + strlen(filename) + 2;
859	cmd = (char *) ecalloc(len, sizeof(char));
860	SNPRINTF1(cmd, len, lessopen, filename);
861	fd = shellcmd(cmd);
862	free(cmd);
863	if (fd == NULL)
864	{
865		/*
866		 * Cannot create the pipe.
867		 */
868		return (NULL);
869	}
870#if HAVE_FILENO
871	if (returnfd)
872	{
873		int f;
874		char c;
875
876		/*
877		 * Read one char to see if the pipe will produce any data.
878		 * If it does, push the char back on the pipe.
879		 */
880		f = fileno(fd);
881		SET_BINARY(f);
882		if (read(f, &c, 1) != 1)
883		{
884			/*
885			 * Pipe is empty.  This means there is no alt file.
886			 */
887			pclose(fd);
888			return (NULL);
889		}
890		ch_ungetchar(c);
891		*pfd = (void *) fd;
892		*pf = f;
893		return (save("-"));
894	}
895#endif
896	cmd = readfd(fd);
897	pclose(fd);
898	if (*cmd == '\0')
899		/*
900		 * Pipe is empty.  This means there is no alt file.
901		 */
902		return (NULL);
903	return (cmd);
904#endif /* HAVE_POPEN */
905}
906
907/*
908 * Close a replacement file.
909 */
910	public void
911close_altfile(altfilename, filename, pipefd)
912	char *altfilename;
913	char *filename;
914	void *pipefd;
915{
916#if HAVE_POPEN
917	char *lessclose;
918	FILE *fd;
919	char *cmd;
920	int len;
921
922	if (secure)
923		return;
924	if (pipefd != NULL)
925	{
926#if OS2
927		/*
928		 * The pclose function of OS/2 emx sometimes fails.
929		 * Send SIGINT to the piped process before closing it.
930		 */
931		kill(((FILE*)pipefd)->_pid, SIGINT);
932#endif
933		pclose((FILE*) pipefd);
934	}
935	if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
936	     	return;
937	len = strlen(lessclose) + strlen(filename) + strlen(altfilename) + 2;
938	cmd = (char *) ecalloc(len, sizeof(char));
939	SNPRINTF2(cmd, len, lessclose, filename, altfilename);
940	fd = shellcmd(cmd);
941	free(cmd);
942	if (fd != NULL)
943		pclose(fd);
944#endif
945}
946
947/*
948 * Is the specified file a directory?
949 */
950	public int
951is_dir(filename)
952	char *filename;
953{
954	int isdir = 0;
955
956	filename = shell_unquote(filename);
957#if HAVE_STAT
958{
959	int r;
960	struct stat statbuf;
961
962	r = stat(filename, &statbuf);
963	isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
964}
965#else
966#ifdef _OSK
967{
968	register int f;
969
970	f = open(filename, S_IREAD | S_IFDIR);
971	if (f >= 0)
972		close(f);
973	isdir = (f >= 0);
974}
975#endif
976#endif
977	free(filename);
978	return (isdir);
979}
980
981/*
982 * Returns NULL if the file can be opened and
983 * is an ordinary file, otherwise an error message
984 * (if it cannot be opened or is a directory, etc.)
985 */
986	public char *
987bad_file(filename)
988	char *filename;
989{
990	register char *m = NULL;
991
992	filename = shell_unquote(filename);
993	if (!force_open && is_dir(filename))
994	{
995		static char is_a_dir[] = " is a directory";
996
997		m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir),
998			sizeof(char));
999		strcpy(m, filename);
1000		strcat(m, is_a_dir);
1001	} else
1002	{
1003#if HAVE_STAT
1004		int r;
1005		struct stat statbuf;
1006
1007		r = stat(filename, &statbuf);
1008		if (r < 0)
1009		{
1010			m = errno_message(filename);
1011		} else if (force_open)
1012		{
1013			m = NULL;
1014		} else if (!S_ISREG(statbuf.st_mode))
1015		{
1016			static char not_reg[] = " is not a regular file (use -f to see it)";
1017			m = (char *) ecalloc(strlen(filename) + sizeof(not_reg),
1018				sizeof(char));
1019			strcpy(m, filename);
1020			strcat(m, not_reg);
1021		}
1022#endif
1023	}
1024	free(filename);
1025	return (m);
1026}
1027
1028/*
1029 * Return the size of a file, as cheaply as possible.
1030 * In Unix, we can stat the file.
1031 */
1032	public POSITION
1033filesize(f)
1034	int f;
1035{
1036#if HAVE_STAT
1037	struct stat statbuf;
1038
1039	if (fstat(f, &statbuf) >= 0)
1040		return ((POSITION) statbuf.st_size);
1041#else
1042#ifdef _OSK
1043	long size;
1044
1045	if ((size = (long) _gs_size(f)) >= 0)
1046		return ((POSITION) size);
1047#endif
1048#endif
1049	return (seek_filesize(f));
1050}
1051
1052/*
1053 *
1054 */
1055	public char *
1056shell_coption()
1057{
1058	return ("-c");
1059}
1060