filename.c revision 128345
1/*
2 * Copyright (C) 1984-2002  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information about less, or for information on how to
8 * contact the author, see the README file.
9 */
10
11
12/*
13 * Routines to mess around with filenames (and files).
14 * Much of this is very OS dependent.
15 */
16
17#include "less.h"
18#include "lglob.h"
19#if MSDOS_COMPILER
20#include <dos.h>
21#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER)
22#include <dir.h>
23#endif
24#if MSDOS_COMPILER==DJGPPC
25#include <glob.h>
26#include <dir.h>
27#define _MAX_PATH	PATH_MAX
28#endif
29#endif
30#ifdef _OSK
31#include <rbf.h>
32#ifndef _OSK_MWC32
33#include <modes.h>
34#endif
35#endif
36#if OS2
37#include <signal.h>
38#endif
39
40#if HAVE_STAT
41#include <sys/stat.h>
42#ifndef S_ISDIR
43#define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
44#endif
45#ifndef S_ISREG
46#define	S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
47#endif
48#endif
49
50
51extern int force_open;
52extern int secure;
53extern int use_lessopen;
54extern IFILE curr_ifile;
55extern IFILE old_ifile;
56#if SPACES_IN_FILENAMES
57extern char openquote;
58extern char closequote;
59#endif
60
61/*
62 * Remove quotes around a filename.
63 */
64	public char *
65shell_unquote(str)
66	char *str;
67{
68	char *name;
69	char *p;
70
71	name = p = (char *) ecalloc(strlen(str)+1, sizeof(char));
72	if (*str == openquote)
73	{
74		str++;
75		while (*str != '\0')
76		{
77			if (*str == closequote)
78			{
79				if (str[1] != closequote)
80					break;
81				str++;
82			}
83			*p++ = *str++;
84		}
85	} else
86	{
87		char *esc = get_meta_escape();
88		int esclen = strlen(esc);
89		while (*str != '\0')
90		{
91			if (esclen > 0 && strncmp(str, esc, esclen) == 0)
92				str += esclen;
93			*p++ = *str++;
94		}
95	}
96	*p = '\0';
97	return (name);
98}
99
100/*
101 * Get the shell's escape character.
102 */
103	public char *
104get_meta_escape()
105{
106	char *s;
107
108	s = lgetenv("LESSMETAESCAPE");
109	if (s == NULL)
110		s = DEF_METAESCAPE;
111	return (s);
112}
113
114/*
115 * Get the characters which the shell considers to be "metacharacters".
116 */
117	static char *
118metachars()
119{
120	static char *mchars = NULL;
121
122	if (mchars == NULL)
123	{
124		mchars = lgetenv("LESSMETACHARS");
125		if (mchars == NULL)
126			mchars = DEF_METACHARS;
127	}
128	return (mchars);
129}
130
131/*
132 * Is this a shell metacharacter?
133 */
134	static int
135metachar(c)
136	char c;
137{
138	return (strchr(metachars(), c) != NULL);
139}
140
141/*
142 * Insert a backslash before each metacharacter in a string.
143 */
144	public char *
145shell_quote(s)
146	char *s;
147{
148	char *p;
149	char *newstr;
150	int len;
151	char *esc = get_meta_escape();
152	int esclen = strlen(esc);
153	int use_quotes = 0;
154	int have_quotes = 0;
155
156	/*
157	 * Determine how big a string we need to allocate.
158	 */
159	len = 1; /* Trailing null byte */
160	for (p = s;  *p != '\0';  p++)
161	{
162		len++;
163		if (*p == openquote || *p == closequote)
164			have_quotes = 1;
165		if (metachar(*p))
166		{
167			if (esclen == 0)
168			{
169				/*
170				 * We've got a metachar, but this shell
171				 * doesn't support escape chars.  Use quotes.
172				 */
173				use_quotes = 1;
174			} else
175			{
176				/*
177				 * Allow space for the escape char.
178				 */
179				len += esclen;
180			}
181		}
182	}
183	if (use_quotes)
184	{
185		if (have_quotes)
186			/*
187			 * We can't quote a string that contains quotes.
188			 */
189			return (NULL);
190		len = strlen(s) + 3;
191	}
192	/*
193	 * Allocate and construct the new string.
194	 */
195	newstr = p = (char *) ecalloc(len, sizeof(char));
196	if (use_quotes)
197	{
198		sprintf(newstr, "%c%s%c", openquote, s, closequote);
199	} else
200	{
201		while (*s != '\0')
202		{
203			if (metachar(*s))
204			{
205				/*
206				 * Add the escape char.
207				 */
208				strcpy(p, esc);
209				p += esclen;
210			}
211			*p++ = *s++;
212		}
213		*p = '\0';
214	}
215	return (newstr);
216}
217
218/*
219 * Return a pathname that points to a specified file in a specified directory.
220 * Return NULL if the file does not exist in the directory.
221 */
222	static char *
223dirfile(dirname, filename)
224	char *dirname;
225	char *filename;
226{
227	char *pathname;
228	char *qpathname;
229	int f;
230
231	if (dirname == NULL || *dirname == '\0')
232		return (NULL);
233	/*
234	 * Construct the full pathname.
235	 */
236	pathname = (char *) calloc(strlen(dirname) + strlen(filename) + 2,
237					sizeof(char));
238	if (pathname == NULL)
239		return (NULL);
240	sprintf(pathname, "%s%s%s", dirname, PATHNAME_SEP, filename);
241	/*
242	 * Make sure the file exists.
243	 */
244	qpathname = shell_unquote(pathname);
245	f = open(qpathname, OPEN_READ);
246	if (f < 0)
247	{
248		free(pathname);
249		pathname = NULL;
250	} else
251	{
252		close(f);
253	}
254	free(qpathname);
255	return (pathname);
256}
257
258/*
259 * Return the full pathname of the given file in the "home directory".
260 */
261	public char *
262homefile(filename)
263	char *filename;
264{
265	register char *pathname;
266
267	/*
268	 * Try $HOME/filename.
269	 */
270	pathname = dirfile(lgetenv("HOME"), filename);
271	if (pathname != NULL)
272		return (pathname);
273#if OS2
274	/*
275	 * Try $INIT/filename.
276	 */
277	pathname = dirfile(lgetenv("INIT"), filename);
278	if (pathname != NULL)
279		return (pathname);
280#endif
281#if MSDOS_COMPILER || OS2
282	/*
283	 * Look for the file anywhere on search path.
284	 */
285	pathname = (char *) calloc(_MAX_PATH, sizeof(char));
286#if MSDOS_COMPILER==DJGPPC
287	{
288		char *res = searchpath(filename);
289		if (res == 0)
290			*pathname = '\0';
291		else
292			strcpy(pathname, res);
293	}
294#else
295	_searchenv(filename, "PATH", pathname);
296#endif
297	if (*pathname != '\0')
298		return (pathname);
299	free(pathname);
300#endif
301	return (NULL);
302}
303
304/*
305 * Expand a string, substituting any "%" with the current filename,
306 * and any "#" with the previous filename.
307 * But a string of N "%"s is just replaced with N-1 "%"s.
308 * Likewise for a string of N "#"s.
309 * {{ This is a lot of work just to support % and #. }}
310 */
311	public char *
312fexpand(s)
313	char *s;
314{
315	register char *fr, *to;
316	register int n;
317	register char *e;
318	IFILE ifile;
319
320#define	fchar_ifile(c) \
321	((c) == '%' ? curr_ifile : \
322	 (c) == '#' ? old_ifile : NULL_IFILE)
323
324	/*
325	 * Make one pass to see how big a buffer we
326	 * need to allocate for the expanded string.
327	 */
328	n = 0;
329	for (fr = s;  *fr != '\0';  fr++)
330	{
331		switch (*fr)
332		{
333		case '%':
334		case '#':
335			if (fr > s && fr[-1] == *fr)
336			{
337				/*
338				 * Second (or later) char in a string
339				 * of identical chars.  Treat as normal.
340				 */
341				n++;
342			} else if (fr[1] != *fr)
343			{
344				/*
345				 * Single char (not repeated).  Treat specially.
346				 */
347				ifile = fchar_ifile(*fr);
348				if (ifile == NULL_IFILE)
349					n++;
350				else
351					n += strlen(get_filename(ifile));
352			}
353			/*
354			 * Else it is the first char in a string of
355			 * identical chars.  Just discard it.
356			 */
357			break;
358		default:
359			n++;
360			break;
361		}
362	}
363
364	e = (char *) ecalloc(n+1, sizeof(char));
365
366	/*
367	 * Now copy the string, expanding any "%" or "#".
368	 */
369	to = e;
370	for (fr = s;  *fr != '\0';  fr++)
371	{
372		switch (*fr)
373		{
374		case '%':
375		case '#':
376			if (fr > s && fr[-1] == *fr)
377			{
378				*to++ = *fr;
379			} else if (fr[1] != *fr)
380			{
381				ifile = fchar_ifile(*fr);
382				if (ifile == NULL_IFILE)
383					*to++ = *fr;
384				else
385				{
386					strcpy(to, get_filename(ifile));
387					to += strlen(to);
388				}
389			}
390			break;
391		default:
392			*to++ = *fr;
393			break;
394		}
395	}
396	*to = '\0';
397	return (e);
398}
399
400#if TAB_COMPLETE_FILENAME
401
402/*
403 * Return a blank-separated list of filenames which "complete"
404 * the given string.
405 */
406	public char *
407fcomplete(s)
408	char *s;
409{
410	char *fpat;
411	char *qs;
412
413	if (secure)
414		return (NULL);
415	/*
416	 * Complete the filename "s" by globbing "s*".
417	 */
418#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC)
419	/*
420	 * But in DOS, we have to glob "s*.*".
421	 * But if the final component of the filename already has
422	 * a dot in it, just do "s*".
423	 * (Thus, "FILE" is globbed as "FILE*.*",
424	 *  but "FILE.A" is globbed as "FILE.A*").
425	 */
426	{
427		char *slash;
428		for (slash = s+strlen(s)-1;  slash > s;  slash--)
429			if (*slash == *PATHNAME_SEP || *slash == '/')
430				break;
431		fpat = (char *) ecalloc(strlen(s)+4, sizeof(char));
432		if (strchr(slash, '.') == NULL)
433			sprintf(fpat, "%s*.*", s);
434		else
435			sprintf(fpat, "%s*", s);
436	}
437#else
438	fpat = (char *) ecalloc(strlen(s)+2, sizeof(char));
439	sprintf(fpat, "%s*", s);
440#endif
441	qs = lglob(fpat);
442	s = shell_unquote(qs);
443	if (strcmp(s,fpat) == 0)
444	{
445		/*
446		 * The filename didn't expand.
447		 */
448		free(qs);
449		qs = NULL;
450	}
451	free(s);
452	free(fpat);
453	return (qs);
454}
455#endif
456
457/*
458 * Try to determine if a file is "binary".
459 * This is just a guess, and we need not try too hard to make it accurate.
460 */
461	public int
462bin_file(f)
463	int f;
464{
465	int i;
466	int n;
467	unsigned char data[64];
468
469	if (!seekable(f))
470		return (0);
471	if (lseek(f, (off_t)0, 0) == BAD_LSEEK)
472		return (0);
473	n = read(f, data, sizeof(data));
474	for (i = 0;  i < n;  i++)
475		if (binary_char(data[i]))
476			return (1);
477	return (0);
478}
479
480/*
481 * Try to determine the size of a file by seeking to the end.
482 */
483	static POSITION
484seek_filesize(f)
485	int f;
486{
487	off_t spos;
488
489	spos = lseek(f, (off_t)0, 2);
490	if (spos == BAD_LSEEK)
491		return (NULL_POSITION);
492	return ((POSITION) spos);
493}
494
495/*
496 * Read a string from a file.
497 * Return a pointer to the string in memory.
498 */
499	static char *
500readfd(fd)
501	FILE *fd;
502{
503	int len;
504	int ch;
505	char *buf;
506	char *p;
507
508	/*
509	 * Make a guess about how many chars in the string
510	 * and allocate a buffer to hold it.
511	 */
512	len = 100;
513	buf = (char *) ecalloc(len, sizeof(char));
514	for (p = buf;  ;  p++)
515	{
516		if ((ch = getc(fd)) == '\n' || ch == EOF)
517			break;
518		if (p - buf >= len-1)
519		{
520			/*
521			 * The string is too big to fit in the buffer we have.
522			 * Allocate a new buffer, twice as big.
523			 */
524			len *= 2;
525			*p = '\0';
526			p = (char *) ecalloc(len, sizeof(char));
527			strcpy(p, buf);
528			free(buf);
529			buf = p;
530			p = buf + strlen(buf);
531		}
532		*p = ch;
533	}
534	*p = '\0';
535	return (buf);
536}
537
538
539
540#if HAVE_POPEN
541
542FILE *popen();
543
544/*
545 * Execute a shell command.
546 * Return a pointer to a pipe connected to the shell command's standard output.
547 */
548	static FILE *
549shellcmd(cmd)
550	char *cmd;
551{
552	FILE *fd;
553
554#if HAVE_SHELL
555	char *shell;
556
557	shell = lgetenv("SHELL");
558	if (shell != NULL && *shell != '\0')
559	{
560		char *scmd;
561		char *esccmd;
562
563		/*
564		 * Read the output of <$SHELL -c cmd>.
565		 * Escape any metacharacters in the command.
566		 */
567		esccmd = shell_quote(cmd);
568		if (esccmd == NULL)
569		{
570			fd = popen(cmd, "r");
571		} else
572		{
573			scmd = (char *) ecalloc(strlen(shell) + strlen(esccmd) + 5,
574						sizeof(char));
575			sprintf(scmd, "%s %s %s", shell, shell_coption(), esccmd);
576			free(esccmd);
577			fd = popen(scmd, "r");
578			free(scmd);
579		}
580	} else
581#endif
582	{
583		fd = popen(cmd, "r");
584	}
585	/*
586	 * Redirection in `popen' might have messed with the
587	 * standard devices.  Restore binary input mode.
588	 */
589	SET_BINARY(0);
590	return (fd);
591}
592
593#endif /* HAVE_POPEN */
594
595
596/*
597 * Expand a filename, doing any system-specific metacharacter substitutions.
598 */
599	public char *
600lglob(filename)
601	char *filename;
602{
603	char *gfilename;
604	char *ofilename;
605
606	ofilename = fexpand(filename);
607	if (secure)
608		return (ofilename);
609	filename = shell_unquote(ofilename);
610
611#ifdef DECL_GLOB_LIST
612{
613	/*
614	 * The globbing function returns a list of names.
615	 */
616	int length;
617	char *p;
618	char *qfilename;
619	DECL_GLOB_LIST(list)
620
621	GLOB_LIST(filename, list);
622	if (GLOB_LIST_FAILED(list))
623	{
624		free(filename);
625		return (ofilename);
626	}
627	length = 1; /* Room for trailing null byte */
628	for (SCAN_GLOB_LIST(list, p))
629	{
630		INIT_GLOB_LIST(list, p);
631		qfilename = shell_quote(p);
632		if (qfilename != NULL)
633		{
634	  		length += strlen(qfilename) + 1;
635			free(qfilename);
636		}
637	}
638	gfilename = (char *) ecalloc(length, sizeof(char));
639	for (SCAN_GLOB_LIST(list, p))
640	{
641		INIT_GLOB_LIST(list, p);
642		qfilename = shell_quote(p);
643		if (qfilename != NULL)
644		{
645			sprintf(gfilename + strlen(gfilename), "%s ", qfilename);
646			free(qfilename);
647		}
648	}
649	/*
650	 * Overwrite the final trailing space with a null terminator.
651	 */
652	*--p = '\0';
653	GLOB_LIST_DONE(list);
654}
655#else
656#ifdef DECL_GLOB_NAME
657{
658	/*
659	 * The globbing function returns a single name, and
660	 * is called multiple times to walk thru all names.
661	 */
662	register char *p;
663	register int len;
664	register int n;
665	char *pathname;
666	char *qpathname;
667	DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
668
669	GLOB_FIRST_NAME(filename, &fnd, handle);
670	if (GLOB_FIRST_FAILED(handle))
671	{
672		free(filename);
673		return (ofilename);
674	}
675
676	_splitpath(filename, drive, dir, fname, ext);
677	len = 100;
678	gfilename = (char *) ecalloc(len, sizeof(char));
679	p = gfilename;
680	do {
681		n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
682		pathname = (char *) ecalloc(n, sizeof(char));
683		sprintf(pathname, "%s%s%s", drive, dir, fnd.GLOB_NAME);
684		qpathname = shell_quote(pathname);
685		free(pathname);
686		if (qpathname != NULL)
687		{
688			n = strlen(qpathname);
689			while (p - gfilename + n + 2 >= len)
690			{
691				/*
692				 * No room in current buffer.
693				 * Allocate a bigger one.
694				 */
695				len *= 2;
696				*p = '\0';
697				p = (char *) ecalloc(len, sizeof(char));
698				strcpy(p, gfilename);
699				free(gfilename);
700				gfilename = p;
701				p = gfilename + strlen(gfilename);
702			}
703			strcpy(p, qpathname);
704			free(qpathname);
705			p += n;
706			*p++ = ' ';
707		}
708	} while (GLOB_NEXT_NAME(handle, &fnd) == 0);
709
710	/*
711	 * Overwrite the final trailing space with a null terminator.
712	 */
713	*--p = '\0';
714	GLOB_NAME_DONE(handle);
715}
716#else
717#if HAVE_POPEN
718{
719	/*
720	 * We get the shell to glob the filename for us by passing
721	 * an "echo" command to the shell and reading its output.
722	 */
723	FILE *fd;
724	char *s;
725	char *lessecho;
726	char *cmd;
727	char *esc;
728
729	esc = get_meta_escape();
730	if (strlen(esc) == 0)
731		esc = "-";
732	esc = shell_quote(esc);
733	if (esc == NULL)
734	{
735		free(filename);
736		return (ofilename);
737	}
738	lessecho = lgetenv("LESSECHO");
739	if (lessecho == NULL || *lessecho == '\0')
740		lessecho = "lessecho";
741	/*
742	 * Invoke lessecho, and read its output (a globbed list of filenames).
743	 */
744	cmd = (char *) ecalloc(strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24, sizeof(char));
745	sprintf(cmd, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc);
746	free(esc);
747	for (s = metachars();  *s != '\0';  s++)
748		sprintf(cmd + strlen(cmd), "-n0x%x ", *s);
749	sprintf(cmd + strlen(cmd), "-- %s", ofilename);
750	fd = shellcmd(cmd);
751	free(cmd);
752	if (fd == NULL)
753	{
754		/*
755		 * Cannot create the pipe.
756		 * Just return the original (fexpanded) filename.
757		 */
758		free(filename);
759		return (ofilename);
760	}
761	gfilename = readfd(fd);
762	pclose(fd);
763	if (*gfilename == '\0')
764	{
765		free(gfilename);
766		free(filename);
767		return (ofilename);
768	}
769}
770#else
771	/*
772	 * No globbing functions at all.  Just use the fexpanded filename.
773	 */
774	gfilename = save(filename);
775#endif
776#endif
777#endif
778	free(filename);
779	free(ofilename);
780	return (gfilename);
781}
782
783/*
784 * See if we should open a "replacement file"
785 * instead of the file we're about to open.
786 */
787	public char *
788open_altfile(filename, pf, pfd)
789	char *filename;
790	int *pf;
791	void **pfd;
792{
793#if !HAVE_POPEN
794	return (NULL);
795#else
796	char *lessopen;
797	char *cmd;
798	FILE *fd;
799#if HAVE_FILENO
800	int returnfd = 0;
801#endif
802
803	if (!use_lessopen || secure)
804		return (NULL);
805	ch_ungetchar(-1);
806	if ((lessopen = lgetenv("LESSOPEN")) == NULL)
807		return (NULL);
808	if (strcmp(filename, "-") == 0)
809		return (NULL);
810	if (*lessopen == '|')
811	{
812		/*
813		 * If LESSOPEN starts with a |, it indicates
814		 * a "pipe preprocessor".
815		 */
816#if HAVE_FILENO
817		lessopen++;
818		returnfd = 1;
819#else
820		error("LESSOPEN pipe is not supported", NULL_PARG);
821		return (NULL);
822#endif
823	}
824
825	cmd = (char *) ecalloc(strlen(lessopen) + strlen(filename) + 2,
826			sizeof(char));
827	sprintf(cmd, lessopen, filename);
828	fd = shellcmd(cmd);
829	free(cmd);
830	if (fd == NULL)
831	{
832		/*
833		 * Cannot create the pipe.
834		 */
835		return (NULL);
836	}
837#if HAVE_FILENO
838	if (returnfd)
839	{
840		int f;
841		char c;
842
843		/*
844		 * Read one char to see if the pipe will produce any data.
845		 * If it does, push the char back on the pipe.
846		 */
847		f = fileno(fd);
848		SET_BINARY(f);
849		if (read(f, &c, 1) != 1)
850		{
851			/*
852			 * Pipe is empty.  This means there is no alt file.
853			 */
854			pclose(fd);
855			return (NULL);
856		}
857		ch_ungetchar(c);
858		*pfd = (void *) fd;
859		*pf = f;
860		return (save("-"));
861	}
862#endif
863	cmd = readfd(fd);
864	pclose(fd);
865	if (*cmd == '\0')
866		/*
867		 * Pipe is empty.  This means there is no alt file.
868		 */
869		return (NULL);
870	return (cmd);
871#endif /* HAVE_POPEN */
872}
873
874/*
875 * Close a replacement file.
876 */
877	public void
878close_altfile(altfilename, filename, pipefd)
879	char *altfilename;
880	char *filename;
881	void *pipefd;
882{
883#if HAVE_POPEN
884	char *lessclose;
885	FILE *fd;
886	char *cmd;
887
888	if (secure)
889		return;
890	if (pipefd != NULL)
891	{
892#if OS2
893		/*
894		 * The pclose function of OS/2 emx sometimes fails.
895		 * Send SIGINT to the piped process before closing it.
896		 */
897		kill(((FILE*)pipefd)->_pid, SIGINT);
898#endif
899		pclose((FILE*) pipefd);
900	}
901	if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
902	     	return;
903	cmd = (char *) ecalloc(strlen(lessclose) + strlen(filename) +
904			strlen(altfilename) + 2, sizeof(char));
905	sprintf(cmd, lessclose, filename, altfilename);
906	fd = shellcmd(cmd);
907	free(cmd);
908	if (fd != NULL)
909		pclose(fd);
910#endif
911}
912
913/*
914 * Is the specified file a directory?
915 */
916	public int
917is_dir(filename)
918	char *filename;
919{
920	int isdir = 0;
921
922	filename = shell_unquote(filename);
923#if HAVE_STAT
924{
925	int r;
926	struct stat statbuf;
927
928	r = stat(filename, &statbuf);
929	isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
930}
931#else
932#ifdef _OSK
933{
934	register int f;
935
936	f = open(filename, S_IREAD | S_IFDIR);
937	if (f >= 0)
938		close(f);
939	isdir = (f >= 0);
940}
941#endif
942#endif
943	free(filename);
944	return (isdir);
945}
946
947/*
948 * Returns NULL if the file can be opened and
949 * is an ordinary file, otherwise an error message
950 * (if it cannot be opened or is a directory, etc.)
951 */
952	public char *
953bad_file(filename)
954	char *filename;
955{
956	register char *m = NULL;
957
958	filename = shell_unquote(filename);
959	if (is_dir(filename))
960	{
961		static char is_a_dir[] = " is a directory";
962
963		m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir),
964			sizeof(char));
965		strcpy(m, filename);
966		strcat(m, is_a_dir);
967	} else
968	{
969#if HAVE_STAT
970		int r;
971		struct stat statbuf;
972
973		r = stat(filename, &statbuf);
974		if (r < 0)
975		{
976			m = errno_message(filename);
977		} else if (force_open)
978		{
979			m = NULL;
980		} else if (!S_ISREG(statbuf.st_mode))
981		{
982			static char not_reg[] = " is not a regular file (use -f to see it)";
983			m = (char *) ecalloc(strlen(filename) + sizeof(not_reg),
984				sizeof(char));
985			strcpy(m, filename);
986			strcat(m, not_reg);
987		}
988#endif
989	}
990	free(filename);
991	return (m);
992}
993
994/*
995 * Return the size of a file, as cheaply as possible.
996 * In Unix, we can stat the file.
997 */
998	public POSITION
999filesize(f)
1000	int f;
1001{
1002#if HAVE_STAT
1003	struct stat statbuf;
1004
1005	if (fstat(f, &statbuf) >= 0)
1006		return ((POSITION) statbuf.st_size);
1007#else
1008#ifdef _OSK
1009	long size;
1010
1011	if ((size = (long) _gs_size(f)) >= 0)
1012		return ((POSITION) size);
1013#endif
1014#endif
1015	return (seek_filesize(f));
1016}
1017
1018/*
1019 *
1020 */
1021	public char *
1022shell_coption()
1023{
1024	return ("-c");
1025}
1026