"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/filename.c" of archive less-424.tar.gz:


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