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