less (part 5 of 6)

Mark Nudelman mark at unix386.Convergent.COM
Wed Mar 6 10:14:29 AEST 1991


#! /bin/sh
# This is a shell archive.
# Remove anything before this line, then unpack it
# by saving it into a file and typing "sh file".

echo shar: Extracting \"charset.c\"
sed "s/^X//" >'charset.c' <<'END_OF_FILE'
X/*
X * Functions to define the character set
X * and do things specific to the character set.
X */
X
X#include "less.h"
X
X/*
X * Predefined character sets,
X * selected by the LESSCHARSET environment variable.
X */
Xstruct charset {
X	char *name;
X	char *desc;
X} charsets[] = {
X	{ "ascii",	"8bcccbcc18b95.b"	},
X	{ "latin1",	"8bcccbcc18b95.33b."	},
X	{ NULL }
X};
X
X#define	IS_BINARY_CHAR	01
X#define	IS_CONTROL_CHAR	02
X
Xstatic char chardef[256];
Xstatic char *binfmt = "\\%o";
X
Xextern char *getenv();
X
X/*
X * Define a charset, given a description string.
X * The string consists of 256 letters,
X * one for each character in the charset.
X * If the string is shorter than 256 letters, missing letters
X * are taken to be identical to the last one.
X * A decimal number followed by a letter is taken to be a 
X * repetition of the letter.
X *
X * Each letter is one of:
X *	. normal character
X *	b binary character
X *	c control character
X */
X	static void
Xichardef(s)
X	char *s;
X{
X	register char *cp;
X	register int n;
X	register char v;
X
X	n = 0;
X	cp = chardef;
X	while (*s != '\0')
X	{
X		switch (*s++)
X		{
X		case '.':
X			v = 0;
X			break;
X		case 'c':
X			v = IS_CONTROL_CHAR;
X			break;
X		case 'b':
X			v = IS_BINARY_CHAR|IS_CONTROL_CHAR;
X			break;
X
X		case '0': case '1': case '2': case '3': case '4':
X		case '5': case '6': case '7': case '8': case '9':
X			n = (10 * n) + (s[-1] - '0');
X			continue;
X
X		default:
X			error("invalid chardef", NULL_PARG);
X			quit(1);
X			/*NOTREACHED*/
X		}
X
X		do
X		{
X			if (cp >= chardef + sizeof(chardef))
X			{
X				error("chardef longer than 256", NULL_PARG);
X				quit(1);
X				/*NOTREACHED*/
X			}
X			*cp++ = v;
X		} while (--n > 0);
X		n = 0;
X	}
X
X	while (cp < chardef + sizeof(chardef))
X		*cp++ = v;
X}
X
X/*
X * Define a charset, given a charset name.
X * The valid charset names are listed in the "charsets" array.
X */
X	static int
Xicharset(name)
X	register char *name;
X{
X	register struct charset *p;
X
X	if (name == NULL || *name == '\0')
X		return (0);
X
X	for (p = charsets;  p->name != NULL;  p++)
X	{
X		if (strcmp(name, p->name) == 0)
X		{
X			ichardef(p->desc);
X			return (1);
X		}
X	}
X
X	error("invalid charset name", NULL_PARG);
X	quit(1);
X	/*NOTREACHED*/
X}
X
X/*
X * Initialize charset data structures.
X */
X	public void
Xinit_charset()
X{
X	register char *s;
X
X	/*
X	 * Try environment variable LESSCHARSET.
X	 * If LESSCHARSET is not set, try LESSCHARDEF.
X	 * If LESSCHARDEF is not set, default to "ascii" charset.
X	 */
X	s = getenv("LESSCHARSET");
X	if (icharset(s))
X		return;
X
X	s = getenv("LESSCHARDEF");
X	if (s != NULL && *s != '\0')
X	{
X		ichardef(s);
X		return;
X	}
X
X	(void) icharset("ascii");
X
X	s = getenv("LESSBINFMT");
X	if (s != NULL && *s != '\0')
X		binfmt = s;
X}
X
X/*
X * Is a given character a "binary" character?
X */
X	public int
Xbinary_char(c)
X	int c;
X{
X	return (chardef[c] & IS_BINARY_CHAR);
X}
X
X/*
X * Is a given character a "control" character?
X */
X	public int
Xcontrol_char(c)
X	int c;
X{
X	return (chardef[c] & IS_CONTROL_CHAR);
X}
X
X/*
X * Return the printable form of a character.
X * For example, in the "ascii" charset '\3' is printed as "^C".
X */
X	public char *
Xprchar(c)
X	int c;
X{
X	static char buf[8];
X
X	if (!control_char(c))
X		sprintf(buf, "%c", c);
X	else if (!control_char(c ^ 0100))
X		sprintf(buf, "^%c", c ^ 0100);
X	else
X		sprintf(buf, binfmt, c);
X	return (buf);
X}
END_OF_FILE
echo shar: Extracting \"filename.c\"
sed "s/^X//" >'filename.c' <<'END_OF_FILE'
X/*
X * Routines to mess around with filenames (and files).
X * Much of this is very OS dependent.
X */
X
X#include <stdio.h>
X#include "less.h"
X
Xextern char *getenv();
X
Xextern int force_open;
Xextern IFILE curr_ifile;
Xextern IFILE old_ifile;
X
X/*
X * Return the full pathname of the given file in the "home directory".
X */
X	public char *
Xhomefile(filename)
X	char *filename;
X{
X	register char *pathname;
X	register char *homedir;
X
X	homedir = getenv("HOME");
X#if __MSDOS__
X	/*
X	 * Most MSDOS users do not have $HOME defined,
X	 * so if no $HOME then look for "_less" anywhere 
X	 * on search path (always begins at current directory).
X	 */
X	if (homedir == NULL)
X	{
X		extern char *searchpath();
X		pathname = searchpath(filename);
X		if (pathname == NULL)
X			return (NULL);
X		pathname = save(pathname);
X	} else
X	{
X		pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2, 
X					sizeof(char));
X		if (pathname == NULL)
X			return (NULL);
X		sprintf(pathname, "%s\\%s", homedir, filename);
X	}
X#else
X	if (homedir == NULL)
X		return (NULL);
X	pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2,
X				sizeof(char));
X	if (pathname == NULL)
X		return (NULL);
X	sprintf(pathname, "%s/%s", homedir, filename);
X#endif
X	return (pathname);
X}
X
X/*
X * Find out where the help file is.
X */
X	public char *
Xfind_helpfile()
X{
X#if __MSDOS__
X	extern char *searchpath();
X
X	/*
X	 * Look in current directory.
X	 */
X	if (access(HELPFILE,0) == 0)
X		return (HELPFILE);
X	/*
X	 * Find the basename of HELPFILE,
X	 * and look for it in each directory in the search path.
X	 */
X	if ((helpfile = strrchr(HELPFILE, '\\')) == NULL)
X		helpfile = HELPFILE;
X	else
X		helpfile++;
X	return (searchpath(helpfile));
X#else
X	return (save(HELPFILE));
X#endif
X}
X
X/*
X * Expand a string, substituting any "%" with the current filename,
X * and any "#" with the previous filename.
X */
X	public char *
Xfexpand(s)
X	char *s;
X{
X	register char *fr, *to;
X	register int n;
X	register char *e;
X
X	/*
X	 * Make one pass to see how big a buffer we 
X	 * need to allocate for the expanded string.
X	 */
X	n = 0;
X	for (fr = s;  *fr != '\0';  fr++)
X	{
X		switch (*fr)
X		{
X		case '%':
X			n += strlen(get_filename(curr_ifile));
X			break;
X		case '#':
X			if (old_ifile == NULL_IFILE)
X			{
X				error("No previous file", NULL_PARG);
X				return (NULL);
X			}
X			n += strlen(get_filename(old_ifile));
X			break;
X		default:
X			n++;
X			break;
X		}
X	}
X
X	e = (char *) ecalloc(n+1, sizeof(char));
X
X	/*
X	 * Now copy the string, expanding any "%" or "#".
X	 */
X	to = e;
X	for (fr = s;  *fr != '\0';  fr++)
X	{
X		switch (*fr)
X		{
X		case '%':
X			strcpy(to, get_filename(curr_ifile));
X			to += strlen(to);
X			break;
X		case '#':
X			strcpy(to, get_filename(old_ifile));
X			to += strlen(to);
X			break;
X		default:
X			*to++ = *fr;
X			break;
X		}
X	}
X	*to = '\0';
X	return (e);
X}
X
X/*
X * Try to determine if a file is "binary".
X * This is just a guess, and we need not try too hard to make it accurate.
X */
X	int
Xbinary_file(f)
X	int f;
X{
X	int i;
X	int n;
X	char data[64];
X
X	n = read(f, data, sizeof(data));
X	for (i = 0;  i < n;  i++)
X		if (binary_char(data[i]))
X			return (1);
X	return (0);
X}
X
X/*
X * Try to determine the size of a file by seeking to the end.
X */
X	static POSITION
Xseek_filesize(f)
X	int f;
X{
X	offset_t spos;
X
X	spos = lseek(f, (offset_t)0, 2);
X	if (spos == BAD_LSEEK)
X		return (NULL_POSITION);
X	return ((POSITION) spos);
X}
X
X/*
X * Expand a filename, substituting any environment variables, etc.
X */
X#if GLOB
X
XFILE *popen();
X
X	public char *
Xglob(filename)
X	char *filename;
X{
X	FILE *f;
X	char *p;
X	int ch;
X	int len;
X	char *cmd;
X	char *gfilename;
X
X	filename = fexpand(filename);
X	if (filename == NULL)
X		return (NULL);
X
X	/*
X	 * We get the shell to expand the filename for us by passing
X	 * an "echo" command to the shell and reading its output.
X	 */
X	p = getenv("SHELL");
X	if (p == NULL || *p == '\0')
X	{
X		/*
X		 * Read the output of <echo filename>.
X		 */
X		cmd = (char *) ecalloc(strlen(filename)+6, sizeof(char));
X		sprintf(cmd, "echo %s", filename);
X	} else
X	{
X		/*
X		 * Read the output of <$SHELL -c "echo filename">.
X		 */
X		cmd = (char *) ecalloc(strlen(p)+strlen(filename)+12, sizeof(char));
X		sprintf(cmd, "%s -c \"echo %s\"", p, filename);
X	}
X
X	f = popen(cmd, "r");
X	free(cmd);
X	if (f == NULL)
X		return (filename);
X	free(filename);
X
X	len = 100;
X	gfilename = (char *) ecalloc(len, sizeof(char));
X	for (p = gfilename;  ;  p++)
X	{
X		if ((ch = getc(f)) == '\n' || ch == EOF)
X			break;
X		if (p - gfilename >= len-1)
X		{
X			len *= 2;
X			*p = '\0';
X			p = (char *) ecalloc(len, sizeof(char));
X			strcpy(p, gfilename);
X			free(gfilename);
X			gfilename = p;
X			p = gfilename + strlen(gfilename);
X		}
X		*p = ch;
X	}
X	*p = '\0';
X	pclose(f);
X	if (*gfilename == '\0')
X		return (NULL);
X	return (gfilename);
X}
X
X#else
X
X	public char *
Xglob(filename)
X	char *filename;
X{
X	return (fexpand(filename));
X}
X
X#endif
X
X
X#if STAT
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
X/*
X * Returns NULL if the file can be opened and
X * is an ordinary file, otherwise an error message
X * (if it cannot be opened or is a directory, etc.)
X */
X	public char *
Xbad_file(filename)
X	char *filename;
X{
X	register char *m;
X	struct stat statbuf;
X
X	if (stat(filename, &statbuf) < 0)
X		return (errno_message(filename));
X
X	if (force_open)
X		return (NULL);
X
X	if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
X	{
X		static char is_dir[] = " is a directory";
X		m = (char *) ecalloc(strlen(filename) + sizeof(is_dir), 
X			sizeof(char));
X		strcpy(m, filename);
X		strcat(m, is_dir);
X		return (m);
X	}
X	if ((statbuf.st_mode & S_IFMT) != S_IFREG)
X	{
X		static char not_reg[] = " is not a regular file";
X		m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 
X			sizeof(char));
X		strcpy(m, filename);
X		strcat(m, not_reg);
X		return (m);
X	}
X
X	return (NULL);
X}
X
X/*
X * Return the size of a file, as cheaply as possible.
X * In Unix, we can stat the file.
X */
X	public POSITION
Xfilesize(f)
X	int f;
X{
X	struct stat statbuf;
X
X	if (fstat(f, &statbuf) < 0)
X		/*
X		 * Can't stat; try seeking to the end.
X		 */
X		return (seek_filesize(f));
X
X	return ((POSITION) statbuf.st_size);
X}
X
X#else
X
X/*
X * If we have no way to find out, just say the file is good.
X */
X	public char *
Xbad_file(filename)
X	char *filename;
X{
X	return (NULL);
X}
X
X/*
X * We can find the file size by seeking.
X */
X	public POSITION
Xfilesize(f)
X	int f;
X{
X	return (seek_filesize(f));
X}
X
X#endif
END_OF_FILE
echo shar: Extracting \"lsystem.c\"
sed "s/^X//" >'lsystem.c' <<'END_OF_FILE'
X/*
X * Routines to execute other programs.
X * Necessarily very OS dependent.
X */
X
X#include <stdio.h>
X#include <signal.h>
X
X#include "less.h"
X#include "position.h"
X
X#if __MSDOS__
X#include <process.h>
X#include <dos.h>
X#include <fcntl.h>
X#include <io.h>
X#include <errno.h>
X#include <dir.h>
X#include <string.h>
X#include <stdlib.h>
X#include <time.h>
X#include <ctype.h>
Xchar get_swchar();
Xvoid swchar_to_dos();
Xvoid swchar_to_unix();
X#endif
X
Xextern char *getenv();
X
Xextern int screen_trashed;
Xextern IFILE curr_ifile;
X
X
X/*
X * Pass the specified command to a shell to be executed.
X * Like plain "system()", but handles resetting terminal modes, etc.
X */
X	public void
Xlsystem(cmd)
X	char *cmd;
X{
X	register int inp;
X	register char *shell;
X	register char *p;
X	register char *curr_filename;
X
X	/*
X	 * Print the command which is to be executed,
X	 * unless the command starts with a "-".
X	 */
X	if (cmd[0] == '-')
X		cmd++;
X	else
X	{
X		lower_left();
X		clear_eol();
X		putstr("!");
X		putstr(cmd);
X		putstr("\n");
X	}
X
X	/*
X	 * Close the current input file.
X	 */
X	curr_filename = get_filename(curr_ifile);
X	(void) edit(NULL, 0);
X
X	/*
X	 * De-initialize the terminal and take out of raw mode.
X	 */
X	deinit();
X	flush();	/* Make sure the deinit chars get out */
X	raw_mode(0);
X
X	/*
X	 * Restore signals to their defaults.
X	 */
X	init_signals(0);
X
X	/*
X	 * Force standard input to be the user's terminal
X	 * (the normal standard input), even if less's standard input 
X	 * is coming from a pipe.
X	 */
X#if __MSDOS__
X{
X	register int inp2;
X
X	inp = dup(0);
X	inp2 = open("CON", O_TEXT|O_RDONLY);
X	dup2(0,inp2);
X}
X#else
X	inp = dup(0);
X	close(0);
X	if (open("/dev/tty", 0) < 0)
X		dup(inp);
X#endif
X
X	/*
X	 * Pass the command to the system to be executed.
X	 * If we have a SHELL environment variable, use
X	 * <$SHELL -c "command"> instead of just <command>.
X	 * If the command is empty, just invoke a shell.
X	 */
X#if __MSDOS__
X{
X	int result;
X	char sw_char;
X
X	sw_char = get_swchar();
X	swchar_to_dos();
X	result = system(cmd);
X	if (result != 0)
X		perror("less");
X	if (sw_char == '-')
X		swchar_to_unix();
X}
X#else
X	p = NULL;
X	if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
X	{
X		if (*cmd == '\0')
X			p = save(shell);
X		else
X		{
X			p = (char *) ecalloc(strlen(shell) + strlen(cmd) + 7, 
X					sizeof(char));
X			sprintf(p, "%s -c \"%s\"", shell, cmd);
X		}
X	}
X	if (p == NULL)
X	{
X		if (*cmd == '\0')
X			p = save("sh");
X		else
X			p = save(cmd);
X	}
X
X	system(p);
X	free(p);
X#endif
X
X	/*
X	 * Restore standard input, reset signals, raw mode, etc.
X	 */
X#if __MSDOS__
X	close(inp2);
X	dup2(0,inp);
X	close(inp);
X#else
X	close(0);
X	dup(inp);
X	close(inp);
X#endif
X
X	init_signals(1);
X	raw_mode(1);
X	init();
X	screen_trashed = 1;
X
X	/*
X	 * Reopen the current input file.
X	 */
X	(void) edit(curr_filename, 0);
X
X#if defined(SIGWINCH) || defined(SIGWIND)
X	/*
X	 * Since we were ignoring window change signals while we executed
X	 * the system command, we must assume the window changed.
X	 * Warning: this leaves a signal pending (in "sigs"),
X	 * so psignals() should be called soon after lsystem().
X	 */
X	winch();
X#endif
X}
X
X#if PIPEC
X
X/*
X * Pipe a section of the input file into the given shell command.
X * The section to be piped is the section "between" the current
X * position and the position marked by the given letter.
X *
X * The "current" position means the top line displayed if the mark
X * is after the current screen, or the bottom line displayed if
X * the mark is before the current screen.
X * If the mark is on the current screen, the whole screen is displayed.
X */
X	public int
Xpipe_mark(c, cmd)
X	int c;
X	char *cmd;
X{
X	POSITION mpos, tpos, bpos;
X
X	/*
X	 * mpos = the marked position.
X	 * tpos = top of screen.
X	 * bpos = bottom of screen.
X	 */
X	mpos = markpos(c);
X	if (mpos == NULL_POSITION)
X		return (-1);
X	tpos = position(TOP);
X	if (tpos == NULL_POSITION)
X		tpos = ch_zero();
X	bpos = position(BOTTOM_PLUS_ONE);
X
X	if (mpos <= tpos)
X		return (pipe_data(cmd, mpos, bpos));
X	else if (bpos == NULL_POSITION || mpos <= bpos)
X		return (pipe_data(cmd, tpos, bpos));
X	else
X		return (pipe_data(cmd, tpos, mpos));
X}
X
X/*
X * Create a pipe to the given shell command.
X * Feed it the file contents between the positions spos and epos.
X */
X	public int
Xpipe_data(cmd, spos, epos)
X	char *cmd;
X	POSITION spos;
X	POSITION epos;
X{
X	register FILE *f;
X	register int c;
X	int inp;
X	extern FILE *popen();
X
X	/*
X	 * This is structured much like lsystem().
X	 * Since we're running a shell program, we must be careful
X	 * to perform the necessary deinitialization before running
X	 * the command, and reinitialization after it.
X	 */
X	if (ch_seek(spos) != 0)
X	{
X		error("Cannot seek to start position", NULL_PARG);
X		return (-1);
X	}
X
X	if ((f = popen(cmd, "w")) == NULL)
X	{
X		error("Cannot create pipe", NULL_PARG);
X		return (-1);
X	}
X	lower_left();
X	clear_eol();
X	putstr("!");
X	putstr(cmd);
X	putstr("\n");
X
X	deinit();
X	flush();
X	raw_mode(0);
X	init_signals(0);
X
X	while (epos == NULL_POSITION || spos++ < epos)
X	{
X		/*
X		 * Read a character from the file and give it to the pipe.
X		 */
X		c = ch_forw_get();
X		if (c == EOI)
X			break;
X		putc(c, f);
X	}
X	pclose(f);
X
X	init_signals(1);
X	raw_mode(1);
X	init();
X	screen_trashed = 1;
X#if defined(SIGWINCH) || defined(SIGWIND)
X	/* {{ Probably don't need this here. }} */
X	winch();
X#endif
X	return (0);
X}
X
X#endif
END_OF_FILE
echo shar: Extracting \"output.c\"
sed "s/^X//" >'output.c' <<'END_OF_FILE'
X/*
X * High level routines dealing with the output to the screen.
X */
X
X#include "less.h"
X
Xpublic int errmsgs;	/* Count of messages displayed by error() */
Xpublic int need_clr;
X
Xextern int sigs;
Xextern int sc_width;
Xextern int so_s_width, so_e_width;
Xextern int screen_trashed;
Xextern int any_display;
X#if __MSDOS__
Xextern int output_mode;
X#endif
X
X/*
X * Display the line which is in the line buffer.
X */
X	public void
Xput_line()
X{
X	register int c;
X	register int i;
X	int a;
X	int curr_attr;
X
X	if (sigs)
X	{
X		/*
X		 * Don't output if a signal is pending.
X		 */
X		screen_trashed = 1;
X		return;
X	}
X
X	curr_attr = NORMAL;
X
X	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
X	{
X		if (a != curr_attr)
X		{
X			/*
X			 * Changing attributes.
X			 * Display the exit sequence for the old attribute
X			 * and the enter sequence for the new one.
X			 */
X			switch (curr_attr)
X			{
X			case UNDERLINE:	ul_exit();	break;
X			case BOLD:	bo_exit();	break;
X			case BLINK:	bl_exit();	break;
X			}
X			switch (a)
X			{
X			case UNDERLINE:	ul_enter();	break;
X			case BOLD:	bo_enter();	break;
X			case BLINK:	bl_enter();	break;
X			}
X			curr_attr = a;
X		}
X		if (curr_attr == INVIS)
X			continue;
X		if (c == '\b')
X			putbs();
X		else
X			putchr(c);
X	}
X}
X
Xstatic char obuf[1024];
Xstatic char *ob = obuf;
X
X/*
X * Flush buffered output.
X *
X * If we haven't displayed any file data yet,
X * output messages on error output (file descriptor 2),
X * otherwise output on standard output (file descriptor 1).
X *
X * This has the desirable effect of producing all
X * error messages on error output if standard output
X * is directed to a file.  It also does the same if
X * we never produce any real output; for example, if
X * the input file(s) cannot be opened.  If we do
X * eventually produce output, code in edit() makes
X * sure these messages can be seen before they are
X * overwritten or scrolled away.
X */
X	public void
Xflush()
X{
X	register int n;
X	register int fd;
X
X#if __MSDOS__
X	if (output_mode == 0)
X	{
X		*ob = '\0';
X		cputs(obuf);
X		ob = obuf;
X		return;
X	}
X#endif
X	n = ob - obuf;
X	if (n == 0)
X		return;
X	fd = (any_display) ? 1 : 2;
X	if (write(fd, obuf, n) != n)
X		screen_trashed = 1;
X	ob = obuf;
X}
X
X/*
X * Output a character.
X */
X	public void
Xputchr(c)
X	int c;
X{
X	if (ob >= &obuf[sizeof(obuf)])
X		flush();
X	if (need_clr)
X	{
X		need_clr = 0;
X		lower_left();
X		clear_eol();
X	}
X#if __MSDOS__
X	if (c == '\n')
X		*ob++ = '\r';
X#endif
X	*ob++ = c;
X}
X
X/*
X * Output a string.
X */
X	public void
Xputstr(s)
X	register char *s;
X{
X	while (*s != '\0')
X		putchr(*s++);
X}
X
X
X/*
X * Output an integer in a given radix.
X */
X	static int
Xiprintnum(num, radix)
X	int num;
X	int radix;
X{
X	register char *s;
X	int r;
X	int neg;
X	char buf[10];
X
X	if (neg = (num < 0))
X		num = -num;
X
X	s = buf;
X	do
X	{
X		*s++ = (num % radix) + '0';
X	} while ((num /= radix) != 0);
X
X	if (neg)
X		*s++ = '-';
X	r = s - buf;
X
X	while (s > buf)
X		putchr(*--s);
X	return (r);
X}
X
X/*
X * This function implements printf-like functionality
X * using a more portable argument list mechanism than printf's.
X */
X	static int
Xiprintf(fmt, parg)
X	register char *fmt;
X	PARG *parg;
X{
X	register char *s;
X	register int n;
X	register int col;
X
X	col = 0;
X	while (*fmt != '\0')
X	{
X		if (*fmt != '%')
X		{
X			putchr(*fmt++);
X			col++;
X		} else
X		{
X			++fmt;
X			switch (*fmt++) {
X			case 's':
X				s = parg->p_string;
X				parg++;
X				while (*s != '\0')
X				{
X					putchr(*s++);
X					col++;
X				}
X				break;
X			case 'd':
X				n = parg->p_int;
X				parg++;
X				col += iprintnum(n, 10);
X				break;
X			}
X		}
X	}
X	return (col);
X}
X
X/*
X * Output a message in the lower left corner of the screen
X * and wait for carriage return.
X */
X	public void
Xerror(fmt, parg)
X	char *fmt;
X	PARG *parg;
X{
X	int c;
X	int col = 0;
X	static char return_to_continue[] = "  (press RETURN)";
X
X	errmsgs++;
X
X	if (any_display)
X	{
X		lower_left();
X		clear_eol();
X		so_enter();
X		col += so_s_width;
X	}
X
X	col += iprintf(fmt, parg);
X
X	if (!any_display)
X	{
X		putchr('\n');
X		return;
X	}
X
X	putstr(return_to_continue);
X	so_exit();
X	col += sizeof(return_to_continue) + so_e_width;
X
X#if ONLY_RETURN
X	while ((c = getchr()) != '\n' && c != '\r')
X		bell();
X#else
X	c = getchr();
X	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
X		ungetcc(c);
X#endif
X	lower_left();
X
X	if (col >= sc_width)
X		/*
X		 * Printing the message has probably scrolled the screen.
X		 * {{ Unless the terminal doesn't have auto margins,
X		 *    in which case we just hammered on the right margin. }}
X		 */
X		screen_trashed = 1;
X
X	flush();
X}
X
Xstatic char intr_to_abort[] = "... (interrupt to abort)";
X
X/*
X * Output a message in the lower left corner of the screen
X * and don't wait for carriage return.
X * Usually used to warn that we are beginning a potentially
X * time-consuming operation.
X */
X	public void
Xierror(fmt, parg)
X	char *fmt;
X	PARG *parg;
X{
X	lower_left();
X	clear_eol();
X	so_enter();
X	(void) iprintf(fmt, parg);
X	putstr(intr_to_abort);
X	so_exit();
X	flush();
X	need_clr = 1;
X}
X
X/*
X * Output a message in the lower left corner of the screen
X * and return a single-character response.
X */
X	public int
Xquery(fmt, parg)
X	char *fmt;
X	PARG *parg;
X{
X	register int c;
X	int col = 0;
X
X	if (any_display)
X	{
X		lower_left();
X		clear_eol();
X	}
X
X	(void) iprintf(fmt, parg);
X	c = getchr();
X
X	if (!any_display)
X	{
X		putchr('\n');
X		return (c);
X	}
X
X	lower_left();
X	if (col >= sc_width)
X		screen_trashed = 1;
X	flush();
X
X	return (c);
X}
END_OF_FILE
echo shar: Extracting \"position.c\"
sed "s/^X//" >'position.c' <<'END_OF_FILE'
X/*
X * Routines dealing with the "position" table.
X * This is a table which tells the position (in the input file) of the
X * first char on each currently displayed line.
X *
X * {{ The position table is scrolled by moving all the entries.
X *    Would be better to have a circular table 
X *    and just change a couple of pointers. }}
X */
X
X#include "less.h"
X#include "position.h"
X
Xstatic POSITION *table = NULL;	/* The position table */
Xstatic int table_size;
X
Xextern int sc_width, sc_height;
X
X/*
X * Return the starting file position of a line displayed on the screen.
X * The line may be specified as a line number relative to the top
X * of the screen, but is usually one of these special cases:
X *	the top (first) line on the screen
X *	the second line on the screen
X *	the bottom line on the screen
X *	the line after the bottom line on the screen
X */
X	public POSITION
Xposition(where)
X	int where;
X{
X	switch (where)
X	{
X	case BOTTOM:
X		where = sc_height - 2;
X		break;
X	case BOTTOM_PLUS_ONE:
X		where = sc_height - 1;
X		break;
X	case MIDDLE:
X		where = sc_height / 2;
X	}
X	return (table[where]);
X}
X
X/*
X * Add a new file position to the bottom of the position table.
X */
X	public void
Xadd_forw_pos(pos)
X	POSITION pos;
X{
X	register int i;
X
X	/*
X	 * Scroll the position table up.
X	 */
X	for (i = 1;  i < sc_height;  i++)
X		table[i-1] = table[i];
X	table[sc_height - 1] = pos;
X}
X
X/*
X * Add a new file position to the top of the position table.
X */
X	public void
Xadd_back_pos(pos)
X	POSITION pos;
X{
X	register int i;
X
X	/*
X	 * Scroll the position table down.
X	 */
X	for (i = sc_height - 1;  i > 0;  i--)
X		table[i] = table[i-1];
X	table[0] = pos;
X}
X
X/*
X * Initialize the position table, done whenever we clear the screen.
X */
X	public void
Xpos_clear()
X{
X	register int i;
X
X	for (i = 0;  i < sc_height;  i++)
X		table[i] = NULL_POSITION;
X}
X
X/*
X * Allocate the position table.
X */
X	public void
Xpos_init()
X{
X	if (sc_height <= table_size)
X		return;
X	if (table != NULL)
X		free((char*)table);
X	table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
X	table_size = sc_height;
X}
X
X/*
X * See if the byte at a specified position is currently on the screen.
X * Check the position table to see if the position falls within its range.
X * Return the position table entry if found, -1 if not.
X */
X	public int
Xonscreen(pos)
X	POSITION pos;
X{
X	register int i;
X
X	if (pos < table[0])
X		return (-1);
X	for (i = 1;  i < sc_height;  i++)
X		if (pos < table[i])
X			return (i-1);
X	return (-1);
X}
X
X/*
X * See if the entire screen is empty.
X */
X	public int
Xempty_screen()
X{
X	return (empty_lines(0, sc_height-1));
X}
X
X	public int
Xempty_lines(s, e)
X	int s;
X	int e;
X{
X	register int i;
X
X	for (i = s;  i <= e;  i++)
X		if (table[i] != NULL_POSITION)
X			return (0);
X	return (1);
X}
X
X/*
X * Get the current screen position.
X * The screen position consists of both a file position and
X * a screen line number where the file position is placed on the screen.
X * Normally the screen line number is 0, but if we are positioned
X * such that the top few lines are empty, we may have to set
X * the screen line to a number > 0.
X */
X	public void
Xget_scrpos(scrpos)
X	struct scrpos *scrpos;
X{
X	register int i;
X
X	/*
X	 * Find the first line on the screen which has something on it,
X	 * and return the screen line number and the file position.
X	 */
X	for (i = 0; i < sc_height;  i++)
X		if (table[i] != NULL_POSITION)
X		{
X			scrpos->ln = i+1;
X			scrpos->pos = table[i];
X			return;
X		}
X	/*
X	 * The screen is empty.
X	 */
X	scrpos->pos = NULL_POSITION;
X}
X
X/*
X * Adjust a screen line number to be a simple positive integer
X * in the range { 0 .. sc_height-2 }.
X * (The bottom line, sc_height-1, is reserved for prompts, etc.)
X * The given "sline" may be in the range { 1 .. sc_height-1 }
X * to refer to lines relative to the top of the screen (starting from 1),
X * or it may be in { -1 .. -(sc_height-1) } to refer to lines
X * relative to the bottom of the screen.
X */
X	public int
Xadjsline(sline)
X	int sline;
X{
X	/*
X	 * Negative screen line number means
X	 * relative to the bottom of the screen.
X	 */
X	if (sline < 0)
X		sline += sc_height;
X	/*
X	 * Can't be less than 1 or greater than sc_height-1.
X	 */
X	if (sline <= 0)
X		sline = 1;
X	if (sline >= sc_height)
X		sline = sc_height - 1;
X	/*
X	 * Return zero-based line number, not one-based.
X	 */
X	return (sline-1);
X}
END_OF_FILE
echo shar: Extracting \"ifile.c\"
sed "s/^X//" >'ifile.c' <<'END_OF_FILE'
X/*
X * An IFILE represents an input file.
X *
X * It is actually a pointer to an ifile structure,
X * but is opaque outside this module.
X * Ifile structures are kept in a linked list in the order they 
X * appear on the command line.
X * Any new file which does not already appear in the list is
X * inserted after the current file.
X */
X
X#include "less.h"
X
Xstruct ifile {
X	struct ifile *h_next;		/* Links for command line list */
X	struct ifile *h_prev;
X	int h_index;			/* Index within command line list */
X	char *h_filename;		/* Name of the file */
X	struct scrpos h_scrpos;		/* Saved position within the file */
X};
X
X/*
X * Convert an IFILE (external representation)
X * to a struct file (internal representation), and vice versa.
X */
X#define int_ifile(h)	((struct ifile *)(h))
X#define ext_ifile(h)	((IFILE)(h))
X
X/*
X * Anchor for linked list.
X */
Xstatic struct ifile anchor = { &anchor, &anchor, 0 };
Xstatic int ifiles = 0;
X
X/*
X * Allocate a new ifile structure and stick a filename in it.
X * It should go after "prev" in the list
X * (or at the beginning of the list if "prev" is NULL).
X * Return a pointer to the new ifile structure.
X */
X	static struct ifile *
Xnew_ifile(filename, prev)
X	char *filename;
X	struct ifile *prev;
X{
X	register struct ifile *p;
X	register struct ifile *np;
X
X	/*
X	 * Allocate and initialize structure.
X	 */
X	p = (struct ifile *) ecalloc(1, sizeof(struct ifile));
X	p->h_filename = filename;
X	p->h_scrpos.pos = NULL_POSITION;
X
X	/*
X	 * Link into list.
X	 */
X	if (prev == NULL)
X		prev = &anchor;
X	p->h_next = prev->h_next;
X	p->h_prev = prev;
X	prev->h_next->h_prev = p;
X	prev->h_next = p;
X
X	/*
X	 * Calculate index for the new one,
X	 * and adjust the indexes for subsequent ifiles in the list.
X	 */
X	p->h_index = prev->h_index + 1;
X	for (np = p->h_next;  np != &anchor;  np = np->h_next)
X		np->h_index++;
X
X	ifiles++;
X	return (p);
X}
X
X/*
X * Get the ifile after a given one in the list.
X */
X	public IFILE
Xnext_ifile(h)
X	IFILE h;
X{
X	register struct ifile *p;
X
X	p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
X	if (p->h_next == &anchor)
X		return (NULL_IFILE);
X	return (ext_ifile(p->h_next));
X}
X
X/*
X * Get the ifile before a given one in the list.
X */
X	public IFILE
Xprev_ifile(h)
X	IFILE h;
X{
X	register struct ifile *p;
X
X	p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
X	if (p->h_prev == &anchor)
X		return (NULL_IFILE);
X	return (ext_ifile(p->h_prev));
X}
X
X/*
X * Return the number of ifiles.
X */
X	public int
Xnifile()
X{
X	return (ifiles);
X}
X
X/*
X * Find an ifile structure, given a filename.
X */
X	static struct ifile *
Xfind_ifile(filename)
X	char *filename;
X{
X	register struct ifile *p;
X
X	for (p = anchor.h_next;  p != &anchor;  p = p->h_next)
X		if (strcmp(filename, p->h_filename) == 0)
X			return (p);
X	return (NULL);
X}
X
X/*
X * Get the ifile associated with a filename.
X * If the filename has not been seen before,
X * insert the new ifile after "prev" in the list.
X */
X	public IFILE
Xget_ifile(filename, prev)
X	char *filename;
X	IFILE prev;
X{
X	register struct ifile *p;
X
X	if ((p = find_ifile(filename)) == NULL)
X		p = new_ifile(save(filename), int_ifile(prev));
X	return (ext_ifile(p));
X}
X
X/*
X * Get the filename associated with a ifile.
X */
X	public char *
Xget_filename(ifile)
X	IFILE ifile;
X{
X	return (int_ifile(ifile)->h_filename);
X}
X
X/*
X * Get the index of the file associated with a ifile.
X */
X	public int
Xget_index(ifile)
X	IFILE ifile;
X{
X	return (int_ifile(ifile)->h_index); 
X}
X
X/*
X * Save the file position to be associated with a given file.
X */
X	public void
Xstore_pos(ifile, scrpos)
X	IFILE ifile;
X	struct scrpos *scrpos;
X{
X	int_ifile(ifile)->h_scrpos = *scrpos;
X}
X
X/*
X * Recall the file position associated with a file.
X * If no position has been associated with the file, return NULL_POSITION.
X */
X	public void
Xget_pos(ifile, scrpos)
X	IFILE ifile;
X	struct scrpos *scrpos;
X{
X	*scrpos = int_ifile(ifile)->h_scrpos;
X}
END_OF_FILE
echo shar: Extracting \"brac.c\"
sed "s/^X//" >'brac.c' <<'END_OF_FILE'
X/*
X * Routines to perform bracket matching functions.
X */
X
X#include "less.h"
X#include "position.h"
X
X/*
X * Try to match the n-th open bracket 
X *  which appears in the top displayed line (forwdir),
X * or the n-th close bracket 
X *  which appears in the bottom displayed line (!forwdir).
X * The characters which serve as "open bracket" and 
X * "close bracket" are given.
X */
X	public void
Xmatch_brac(obrac, cbrac, forwdir, n)
X	register int obrac;
X	register int cbrac;
X	int forwdir;
X	int n;
X{
X	register int c;
X	register int nest;
X	POSITION pos;
X	int (*chget)();
X
X	extern int ch_forw_get(), ch_back_get();
X
X	/*
X	 * Seek to the line containing the open bracket.
X	 * This is either the top or bottom line on the screen,
X	 * depending on the type of bracket.
X	 */
X	pos = position((forwdir) ? TOP : BOTTOM);
X	if (pos == NULL_POSITION || ch_seek(pos))
X	{
X		if (forwdir)
X			error("Nothing in top line", NULL_PARG);
X		else
X			error("Nothing in bottom line", NULL_PARG);
X		return;
X	}
X
X	/*
X	 * Look thru the line to find the open bracket to match.
X	 */
X	do
X	{
X		if ((c = ch_forw_get()) == '\n' || c == EOI)
X		{
X			if (forwdir)
X				error("No bracket in top line", NULL_PARG);
X			else
X				error("No bracket in bottom line", NULL_PARG);
X			return;
X		}
X	} while (c != obrac || --n > 0);
X
X	/*
X	 * Position the file just "after" the open bracket
X	 * (in the direction in which we will be searching).
X	 * If searching forward, we are already after the bracket.
X	 * If searching backward, skip back over the open bracket.
X	 */
X	if (!forwdir)
X		(void) ch_back_get();
X
X	/*
X	 * Search the file for the matching bracket.
X	 */
X	chget = (forwdir) ? ch_forw_get : ch_back_get;
X	nest = 0;
X	while ((c = (*chget)()) != EOI)
X	{
X		if (c == obrac)
X			nest++;
X		else if (c == cbrac && --nest < 0)
X		{
X			/*
X			 * Found the matching bracket.
X			 * If searching backward, put it on the top line.
X			 * If searching forward, put it on the bottom line.
X			 */
X			jump_line_loc(ch_tell(), forwdir ? -1 : 1);
X			return;
X		}
X	}
X	error("No matching bracket", NULL_PARG);
X}
END_OF_FILE
echo shar: Extracting \"forwback.c\"
sed "s/^X//" >'forwback.c' <<'END_OF_FILE'
X/*
X * Primitives for displaying the file on the screen,
X * scrolling either forward or backward.
X */
X
X#include "less.h"
X#include "position.h"
X
Xpublic int hit_eof;	/* Keeps track of how many times we hit end of file */
Xpublic int screen_trashed;
Xpublic int squished;
X
Xextern int sigs;
Xextern int top_scroll;
Xextern int quiet;
Xextern int sc_width, sc_height;
Xextern int quit_at_eof;
Xextern int plusoption;
Xextern int forw_scroll;
Xextern int back_scroll;
Xextern int need_clr;
Xextern int ignore_eoi;
X#if TAGS
Xextern int tagoption;
X#endif
X
X/*
X * Sound the bell to indicate user is trying to move past end of file.
X */
X	static void
Xeof_bell()
X{
X	if (quiet == NOT_QUIET)
X		bell();
X	else
X		vbell();
X}
X
X/*
X * Check to see if the end of file is currently "displayed".
X */
X	static void
Xeof_check()
X{
X	POSITION pos;
X
X	if (sigs)
X		return;
X	/*
X	 * If the bottom line is empty, we are at EOF.
X	 * If the bottom line ends at the file length,
X	 * we must be just at EOF.
X	 */
X	pos = position(BOTTOM_PLUS_ONE);
X	if (pos == NULL_POSITION || pos == ch_length())
X		hit_eof++;
X}
X
X/*
X * If the screen is "squished", repaint it.
X * "Squished" means the first displayed line is not at the top
X * of the screen; this can happen when we display a short file
X * for the first time.
X */
X	static void
Xsquish_check()
X{
X	if (!squished)
X		return;
X	squished = 0;
X	repaint();
X}
X
X/*
X * Display n lines, scrolling forward, 
X * starting at position pos in the input file.
X * "force" means display the n lines even if we hit end of file.
X * "only_last" means display only the last screenful if n > screen size.
X * "nblank" is the number of blank lines to draw before the first
X *   real line.  If nblank > 0, the pos must be NULL_POSITION.
X *   The first real line after the blanks will start at ch_zero().
X */
X	public void
Xforw(n, pos, force, only_last, nblank)
X	register int n;
X	POSITION pos;
X	int force;
X	int only_last;
X	int nblank;
X{
X	int eof = 0;
X	int nlines = 0;
X	int do_repaint;
X	static int first_time = 1;
X
X	squish_check();
X
X	/*
X	 * do_repaint tells us not to display anything till the end, 
X	 * then just repaint the entire screen.
X	 * We repaint if we are supposed to display only the last 
X	 * screenful and the request is for more than a screenful.
X	 * Also if the request exceeds the forward scroll limit
X	 * (but not if the request is for exactly a screenful, since
X	 * repainting itself involves scrolling forward a screenful).
X	 */
X	do_repaint = (only_last && n > sc_height-1) || 
X		(forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
X
X	if (!do_repaint)
X	{
X		if (top_scroll && n >= sc_height - 1 && pos != ch_length())
X		{
X			/*
X			 * Start a new screen.
X			 * {{ This is not really desirable if we happen
X			 *    to hit eof in the middle of this screen,
X			 *    but we don't yet know if that will happen. }}
X			 */
X			if (top_scroll == 2 || first_time)
X				clear();
X			home();
X			force = 1;
X		} else
X		{
X			lower_left();
X			clear_eol();
X		}
X
X		if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
X		{
X			/*
X			 * This is not contiguous with what is
X			 * currently displayed.  Clear the screen image 
X			 * (position table) and start a new screen.
X			 */
X			pos_clear();
X			add_forw_pos(pos);
X			force = 1;
X			if (top_scroll)
X			{
X				if (top_scroll == 2)
X					clear();
X				home();
X			} else if (!first_time)
X			{
X				putstr("...skipping...\n");
X			}
X		}
X	}
X
X	while (--n >= 0)
X	{
X		/*
X		 * Read the next line of input.
X		 */
X		if (nblank > 0)
X		{
X			/*
X			 * Still drawing blanks; don't get a line 
X			 * from the file yet.
X			 * If this is the last blank line, get ready to
X			 * read a line starting at ch_zero() next time.
X			 */
X			if (--nblank == 0)
X				pos = ch_zero();
X		} else
X		{
X			/* 
X			 * Get the next line from the file.
X			 */
X			pos = forw_line(pos);
X			if (pos == NULL_POSITION)
X			{
X				/*
X				 * End of file: stop here unless the top line 
X				 * is still empty, or "force" is true.
X				 */
X				eof = 1;
X				if (!force && position(TOP) != NULL_POSITION)
X					break;
X			}
X		}
X		/*
X		 * Add the position of the next line to the position table.
X		 * Display the current line on the screen.
X		 */
X		add_forw_pos(pos);
X		nlines++;
X		if (do_repaint)
X			continue;
X		/*
X		 * If this is the first screen displayed and
X		 * we hit an early EOF (i.e. before the requested
X		 * number of lines), we "squish" the display down
X		 * at the bottom of the screen.
X		 * But don't do this if a + option or a -t option
X		 * was given.  These options can cause us to
X		 * start the display after the beginning of the file,
X		 * and it is not appropriate to squish in that case.
X		 */
X		if (first_time && pos == NULL_POSITION && !top_scroll && 
X#if TAGS
X		    !tagoption &&
X#endif
X		    !plusoption)
X		{
X			squished = 1;
X			continue;
X		}
X		if (top_scroll == 1)
X			clear_eol();
X		put_line();
X	}
X
X	if (eof && !sigs)
X		hit_eof++;
X	else
X		eof_check();
X	if (nlines == 0)
X		eof_bell();
X	else if (do_repaint)
X		repaint();
X	first_time = 0;
X	(void) currline(BOTTOM);
X}
X
X/*
X * Display n lines, scrolling backward.
X */
X	public void
Xback(n, pos, force, only_last)
X	register int n;
X	POSITION pos;
X	int force;
X	int only_last;
X{
X	int nlines = 0;
X	int do_repaint;
X
X	squish_check();
X	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
X	hit_eof = 0;
X	while (--n >= 0)
X	{
X		/*
X		 * Get the previous line of input.
X		 */
X		pos = back_line(pos);
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * Beginning of file: stop here unless "force" is true.
X			 */
X			if (!force)
X				break;
X		}
X		/*
X		 * Add the position of the previous line to the position table.
X		 * Display the line on the screen.
X		 */
X		add_back_pos(pos);
X		nlines++;
X		if (!do_repaint)
X		{
X			home();
X			add_line();
X			put_line();
X		}
X	}
X
X	eof_check();
X	if (nlines == 0)
X		eof_bell();
X	else if (do_repaint)
X		repaint();
X	(void) currline(BOTTOM);
X}
X
X/*
X * Display n more lines, forward.
X * Start just after the line currently displayed at the bottom of the screen.
X */
X	public void
Xforward(n, force, only_last)
X	int n;
X	int force;
X	int only_last;
X{
X	POSITION pos;
X
X	if (quit_at_eof && hit_eof)
X	{
X		/*
X		 * If the -e flag is set and we're trying to go
X		 * forward from end-of-file, go on to the next file.
X		 */
X		if (edit_next(1))
X			quit(0);
X		return;
X	}
X
X	pos = position(BOTTOM_PLUS_ONE);
X	if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
X	{
X		if (ignore_eoi)
X		{
X			/*
X			 * ignore_eoi is to support A_F_FOREVER.
X			 * Back up until there is a line at the bottom
X			 * of the screen.
X			 */
X			if (empty_screen())
X				pos = ch_zero();
X			else
X			{
X				do
X				{
X					back(1, position(TOP), 1, 0);
X					pos = position(BOTTOM_PLUS_ONE);
X				} while (pos == NULL_POSITION);
X			}
X		} else
X		{
X			eof_bell();
X			hit_eof++;
X			return;
X		}
X	}
X	forw(n, pos, force, only_last, 0);
X}
X
X/*
X * Display n more lines, backward.
X * Start just before the line currently displayed at the top of the screen.
X */
X	public void
Xbackward(n, force, only_last)
X	int n;
X	int force;
X	int only_last;
X{
X	POSITION pos;
X
X	pos = position(TOP);
X	if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
X	{
X		eof_bell();
X		return;   
X	}
X	back(n, pos, force, only_last);
X}
X
X/*
X * Get the backwards scroll limit.
X * Must call this function instead of just using the value of
X * back_scroll, because the default case depends on sc_height and
X * top_scroll, as well as back_scroll.
X */
X	public int
Xget_back_scroll()
X{
X	if (back_scroll >= 0)
X		return (back_scroll);
X	if (top_scroll)
X		return (sc_height - 2);
X	return (10000); /* infinity */
X}
END_OF_FILE
echo shar: Extracting \"jump.c\"
sed "s/^X//" >'jump.c' <<'END_OF_FILE'
X/*
X * Routines which jump to a new location in the file.
X */
X
X#include "less.h"
X#include "position.h"
X
Xextern int hit_eof;
Xextern int jump_sline;
Xextern int squished;
Xextern int screen_trashed;
Xextern int sc_width, sc_height;
X
X/*
X * Jump to the end of the file.
X */
X	public void
Xjump_forw()
X{
X	if (ch_end_seek())
X	{
X		error("Cannot seek to end of file", NULL_PARG);
X		return;
X	}
X	/*
X	 * Position the last line in the file at the last screen line.
X	 */
X	jump_loc(back_line(ch_tell()), sc_height-1);
X}
X
X/*
X * Jump to line n in the file.
X */
X	public void
Xjump_back(n)
X	int n;
X{
X	POSITION pos;
X	PARG parg;
X
X	/*
X	 * Find the position of the specified line.
X	 * If we can seek there, just jump to it.
X	 * If we can't seek, but we're trying to go to line number 1,
X	 * use ch_beg_seek() to get as close as we can.
X	 */
X	pos = find_pos(n);
X	if (pos != NULL_POSITION && ch_seek(pos) == 0)
X	{
X		jump_loc(pos, jump_sline);
X	} else if (n <= 1 && ch_beg_seek() == 0)
X	{
X		jump_loc(ch_tell(), jump_sline);
X		error("Cannot seek to beginning of file", NULL_PARG);
X	} else
X	{
X		parg.p_int = n;
X		error("Cannot seek to line number %d", &parg);
X	}
X}
X
X/*
X * Repaint the screen.
X */
X	public void
Xrepaint()
X{
X	struct scrpos scrpos;
X	/*
X	 * Start at the line currently at the top of the screen
X	 * and redisplay the screen.
X	 */
X	get_scrpos(&scrpos);
X	pos_clear();
X	jump_loc(scrpos.pos, scrpos.ln);
X}
X
X/*
X * Jump to a specified percentage into the file.
X */
X	public void
Xjump_percent(percent)
X	int percent;
X{
X	POSITION pos, len;
X
X	/*
X	 * Determine the position in the file
X	 * (the specified percentage of the file's length).
X	 */
X	if ((len = ch_length()) == NULL_POSITION)
X	{
X		error("Don't know length of file", NULL_PARG);
X		return;
X	}
X	/*
X	 * {{ This calculation may overflow! }}
X	 */
X	pos = (percent * len) / 100;
X	if (pos >= len)
X		pos = len-1;
X
X	jump_line_loc(pos, jump_sline);
X}
X
X/*
X * Jump to a specified position in the file.
X * Like jump_loc, but the position need not be 
X * the first character in a line.
X */
X	public void
Xjump_line_loc(pos, sline)
X	POSITION pos;
X	int sline;
X{
X	int c;
X
X	if (ch_seek(pos) == 0)
X	{
X		/*
X		 * Back up to the beginning of the line.
X		 */
X		while ((c = ch_back_get()) != '\n' && c != EOI)
X			;
X		if (c == '\n')
X			(void) ch_forw_get();
X		pos = ch_tell();
X	}
X	jump_loc(pos, sline);
X}
X
X/*
X * Jump to a specified position in the file.
X * The position must be the first character in a line.
X * Place the target line on a specified line on the screen.
X */
X	public void
Xjump_loc(pos, sline)
X	POSITION pos;
X	int sline;
X{
X	register int nline;
X	POSITION tpos;
X	POSITION bpos;
X
X	/*
X	 * Normalize sline.
X	 */
X	sline = adjsline(sline);
X
X	if ((nline = onscreen(pos)) >= 0)
X	{
X		/*
X		 * The line is currently displayed.  
X		 * Just scroll there.
X		 */
X		nline -= sline;
X		if (nline > 0)
X			forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0);
X		else
X			back(-nline, position(TOP), 1, 0);
X		return;
X	}
X
X	/*
X	 * Line is not on screen.
X	 * Seek to the desired location.
X	 */
X	if (ch_seek(pos))
X	{
X		error("Cannot seek to that file position", NULL_PARG);
X		return;
X	}
X
X	/*
X	 * See if the desired line is before or after 
X	 * the currently displayed screen.
X	 */
X	tpos = position(TOP);
X	bpos = position(BOTTOM_PLUS_ONE);
X	if (tpos == NULL_POSITION || pos >= tpos)
X	{
X		/*
X		 * The desired line is after the current screen.
X		 * Move back in the file far enough so that we can
X		 * call forw() and put the desired line at the 
X		 * sline-th line on the screen.
X		 */
X		for (nline = 0;  nline < sline;  nline++)
X		{
X			if (bpos != NULL_POSITION && pos <= bpos)
X			{
X				/*
X				 * Surprise!  The desired line is
X				 * close enough to the current screen
X				 * that we can just scroll there after all.
X				 */
X				forw(sc_height-sline+nline-1, bpos, 1, 0, 0);
X				return;
X			}
X			pos = back_line(pos);
X			if (pos == NULL_POSITION)
X			{
X				/*
X				 * Oops.  Ran into the beginning of the file.
X				 * Exit the loop here and rely on forw()
X				 * below to draw the required number of
X				 * blank lines at the top of the screen.
X				 */
X				break;
X			}
X		}
X		lastmark();
X		hit_eof = 0;
X		squished = 0;
X		screen_trashed = 0;
X		forw(sc_height-1, pos, 1, 0, sline-nline);
X	} else
X	{
X		/*
X		 * The desired line is before the current screen.
X		 * Move forward in the file far enough so that we
X		 * can call back() and put the desired line at the 
X		 * sline-th line on the screen.
X		 */
X		for (nline = sline;  nline < sc_height - 1;  nline++)
X		{
X			pos = forw_line(pos);
X			if (pos == NULL_POSITION)
X			{
X				/* Cannot happen! */
X				error("Program error: EOI in jump_loc (forw)",
X					NULL_PARG);
X				quit(1);
X			}
X			if (pos >= tpos)
X			{
X				/* 
X				 * Surprise!  The desired line is
X				 * close enough to the current screen
X				 * that we can just scroll there after all.
X				 */
X				back(nline+1, tpos, 1, 0);
X				return;
X			}
X		}
X		lastmark();
X		clear();
X		screen_trashed = 0;
X		add_back_pos(pos);
X		back(sc_height-1, pos, 1, 0);
X	}
X}
END_OF_FILE
echo shar: Extracting \"search.c\"
sed "s/^X//" >'search.c' <<'END_OF_FILE'
X/*
X * Routines to search a file for a pattern.
X */
X
X#include "less.h"
X#include "position.h"
X#if REGCOMP
X#include "regexp.h"
X#endif
X
Xextern int sigs;
Xextern int how_search;
Xextern int top_scroll;
Xextern int back_scroll;
Xextern int caseless;
Xextern int linenums;
Xextern int sc_height;
Xextern int jump_sline;
X
X/*
X * Search for the n-th occurrence of a specified pattern, 
X * either forward or backward.
X * Return the number of matches not yet found in this file
X * (that is, n minus the number of matches found).
X * Return -1 if the search should be aborted.
X * Caller may continue the search in another file 
X * if less than n matches are found in this file.
X */
X	public int
Xsearch(search_type, pattern, n)
X	int search_type;
X	char *pattern;
X	int n;
X{
X	POSITION pos, linepos;
X	register char *p;
X	register char *q;
X	register int goforw;
X	register int want_match;
X	char *line;
X	int linenum;
X	int line_match;
X	static int is_caseless;
X#if RECOMP
X	char *re_comp();
X	PARG parg;
X#else
X#if REGCMP
X	char *regcmp();
X	static char *cpattern = NULL;
X#else
X#if REGCOMP
X	static struct regexp *regpattern = NULL;
X#else
X	static char lpbuf[100];
X	static char *last_pattern = NULL;
X#endif
X#endif
X#endif
X
X	/*
X	 * Extract flags and type of search.
X	 */
X	goforw = (SRCH_DIR(search_type) == SRCH_FORW);
X	want_match = !(search_type & SRCH_NOMATCH);
X
X	if (pattern != NULL && (is_caseless = caseless))
X	{
X		/*
X		 * Search will ignore case, unless
X		 * there are any uppercase letters in the pattern.
X		 */
X		for (p = pattern;  *p != '\0';  p++)
X			if (*p >= 'A' && *p <= 'Z')
X			{
X				is_caseless = 0;
X				break;
X			}
X	}
X#if RECOMP
X
X	/*
X	 * (re_comp handles a null pattern internally, 
X	 *  so there is no need to check for a null pattern here.)
X	 */
X	if ((parg.p_string = re_comp(pattern)) != NULL)
X	{
X		error("%s", &parg);
X		return (-1);
X	}
X#else
X#if REGCMP
X	if (pattern == NULL || *pattern == '\0')
X	{
X		/*
X		 * A null pattern means use the previous pattern.
X		 * The compiled previous pattern is in cpattern, so just use it.
X		 */
X		if (cpattern == NULL)
X		{
X			error("No previous regular expression", NULL_PARG);
X			return (-1);
X		}
X	} else
X	{
X		/*
X		 * Otherwise compile the given pattern.
X		 */
X		char *s;
X		if ((s = regcmp(pattern, 0)) == NULL)
X		{
X			error("Invalid pattern", NULL_PARG);
X			return (-1);
X		}
X		if (cpattern != NULL)
X			free(cpattern);
X		cpattern = s;
X	}
X#else
X#if REGCOMP
X	if (pattern == NULL || *pattern == '\0')
X	{
X		/*
X		 * A null pattern means use the previous pattern.
X		 * The compiled previous pattern is in regpattern, 
X		 * so just use it.
X		 */
X		if (regpattern == NULL)
X		{
X			error("No previous regular expression", NULL_PARG);
X			return (-1);
X		}
X	} else
X	{
X		/*
X		 * Otherwise compile the given pattern.
X		 */
X		struct regexp *s;
X		if ((s = regcomp(pattern)) == NULL)
X		{
X			error("Invalid pattern", NULL_PARG);
X			return (-1);
X		}
X		if (regpattern != NULL)
X			free(regpattern);
X		regpattern = s;
X	}
X#else
X	if (pattern == NULL || *pattern == '\0')
X	{
X		/*
X		 * Null pattern means use the previous pattern.
X		 */
X		if (last_pattern == NULL)
X		{
X			error("No previous regular expression", NULL_PARG);
X			return (-1);
X		}
X		pattern = last_pattern;
X	} else
X	{
X		strcpy(lpbuf, pattern);
X		last_pattern = lpbuf;
X	}
X#endif
X#endif
X#endif
X
X	/*
X	 * Figure out where to start the search.
X	 */
X	if (empty_screen())
X	{
X		/*
X		 * Start at the beginning (or end) of the file.
X		 * (The empty_screen() case is mainly for 
X		 * command line initiated searches;
X		 * for example, "+/xyz" on the command line.)
X		 */
X		if (goforw)
X			pos = ch_zero();
X		else 
X		{
X			pos = ch_length();
X			if (pos == NULL_POSITION)
X				pos = ch_zero();
X		}
X	} else 
X	{
X		if (how_search)
X		{
X			if (goforw)
X				linenum = BOTTOM_PLUS_ONE;
X			else
X				linenum = TOP;
X			pos = position(linenum);
X		} else
X		{
X			linenum = adjsline(jump_sline);
X			pos = position(linenum);
X			if (goforw)
X				pos = forw_raw_line(pos, (char **)NULL);
X		}
X	}
X
X	if (pos == NULL_POSITION)
X	{
X		/*
X		 * Can't find anyplace to start searching from.
X		 */
X		error("Nothing to search", NULL_PARG);
X		return (-1);
X	}
X
X	linenum = find_linenum(pos);
X	for (;;)
X	{
X		/*
X		 * Get lines until we find a matching one or 
X		 * until we hit end-of-file (or beginning-of-file 
X		 * if we're going backwards).
X		 */
X		if (sigs)
X			/*
X			 * A signal aborts the search.
X			 */
X			return (-1);
X
X		if (goforw)
X		{
X			/*
X			 * Read the next line, and save the 
X			 * starting position of that line in linepos.
X			 */
X			linepos = pos;
X			pos = forw_raw_line(pos, &line);
X			if (linenum != 0)
X				linenum++;
X		} else
X		{
X			/*
X			 * Read the previous line and save the
X			 * starting position of that line in linepos.
X			 */
X			pos = back_raw_line(pos, &line);
X			linepos = pos;
X			if (linenum != 0)
X				linenum--;
X		}
X
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * We hit EOF/BOF without a match.
X			 */
X			return (n);
X		}
X
X		/*
X		 * If we're using line numbers, we might as well
X		 * remember the information we have now (the position
X		 * and line number of the current line).
X		 */
X		if (linenums)
X			add_lnum(linenum, pos);
X
X		if (is_caseless)
X		{
X			/*
X			 * If this is a caseless search, convert 
X			 * uppercase in the input line to lowercase.
X			 * While we're at it, remove any backspaces
X			 * along with the preceding char.
X			 * This allows us to match text which is 
X			 * underlined or overstruck.
X			 */
X			for (p = q = line;  *p != '\0';  p++, q++)
X			{
X				if (*p >= 'A' && *p <= 'Z')
X					/* Convert uppercase to lowercase. */
X					*q = *p + 'a' - 'A';
X				else if (q > line && *p == '\b')
X					/* Delete BS and preceding char. */
X					q -= 2;
X				else
X					/* Otherwise, just copy. */
X					*q = *p;
X			}
X		}
X
X		/*
X		 * Test the next line to see if we have a match.
X		 * This is done in a variety of ways, depending
X		 * on what pattern matching functions are available.
X		 */
X#if REGCMP
X		line_match = (regex(cpattern, line) != NULL);
X#else
X#if RECOMP
X		line_match = (re_exec(line) == 1);
X#else
X#if REGCOMP
X		linematch = regexec(regpattern, line);
X#else
X		line_match = match(pattern, line);
X#endif
X#endif
X#endif
X		/*
X		 * We are successful if want_match and line_match are
X		 * both true (want a match and got it),
X		 * or both false (want a non-match and got it).
X		 */
X		if (((want_match && line_match) || (!want_match && !line_match)) &&
X		      --n <= 0)
X			/*
X			 * Found the line.
X			 */
X			break;
X	}
X
X	jump_loc(linepos, jump_sline);
X	return (0);
X}
X
X#if (!REGCMP) && (!RECOMP) && (!REGCOMP)
X/*
X * We have neither regcmp() nor re_comp().
X * We use this function to do simple pattern matching.
X * It supports no metacharacters like *, etc.
X */
X	static int
Xmatch(pattern, buf)
X	char *pattern, *buf;
X{
X	register char *pp, *lp;
X
X	for ( ;  *buf != '\0';  buf++)
X	{
X		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
X			if (*pp == '\0' || *lp == '\0')
X				break;
X		if (*pp == '\0')
X			return (1);
X	}
X	return (0);
X}
X#endif
END_OF_FILE
echo shar: Extracting \"less.hlp\"
sed "s/^X//" >'less.hlp' <<'END_OF_FILE'
X
X                     SUMMARY OF COMMANDS
X
X      Commands marked with * may be preceded by a number, N.
X      Notes in parentheses indicate the behavior if N is given.
X
X  h  H                 Display this help.
X  q  :q  :Q  ZZ        Exit.
X
X  e  ^E  j  ^N  CR  *  Forward  one line (or N lines).
X  y  ^Y  k  ^K  ^P  *  Backward one line (or N lines).
X  f  ^F  ^V  SPACE  *  Forward  one window (or N lines).
X  b  ^B  ESC-v      *  Backward one window (or N lines).
X  z                 *  Forward  one window (and set window to N).
X  w                 *  Backward one window (and set window to N).
X  d  ^D             *  Forward  one half-window (and set half-window to N).
X  u  ^U             *  Backward one half-window (and set half-window to N).
X  F                    Forward forever; like "tail -f".
X  r  ^R  ^L            Repaint screen.
X  R                    Repaint screen, discarding buffered input.
X
X  NOTE: default "window" is the screen height.
X        default "half-window" is half of the screen height.
X
X  /pattern          *  Search forward for (N-th) matching line.
X  ?pattern          *  Search backward for (N-th) matching line.
X  ESC-/pattern      *  Search all files for (N-th) matching line.
X
X  /!pattern         *  Search forward for (N-th) NON-matching line.
X  ?!pattern         *  Search backward for (N-th) NON-matching line.
X  ESC-/!pattern     *  Search from all files for (N-th) NON-matching line.
X
X  n                 *  Repeat previous search (for N-th occurrence).
X  N                 *  Repeat previous search in reverse direction.
X  ESC-n             *  Repeat previous search, spanning files.
X  ESC-N             *  Repeat previous search, reverse dir. & spanning files.
X
X  g  <  ESC-<       *  Go to first line in file (or line N).
X  G  >  ESC->       *  Go to last line in file (or line N).
X  p  %              *  Go to beginning of file (or N percent into file).
X  {                 *  Go to the } which matches the (N-th) { in the top line.
X  }                 *  Go to the { which matches the (N-th) } in the top line.
X  (                 *  Go to the ) which matches the (N-th) ( in the top line.
X  )                 *  Go to the ( which matches the (N-th) ) in the top line.
X  [                 *  Go to the ] which matches the (N-th) [ in the top line.
X  ]                 *  Go to the [ which matches the (N-th) ] in the top line.
X  m<letter>            Mark the current position with <letter>.
X  '<letter>            Go to a previously marked position.
X  ''                   Go to the previous position.
X  ^X^X                 Same as '.
X
X  E [file]             Examine a new file.
X  :e  ^X^V             Same as E.
X  :n                *  Examine the (N-th) next file from the command line.
X  :p                *  Examine the (N-th) previous file from the command line.
X  =  ^G  :f            Print current file name.
X  V                    Print version number of "less".
X
X  -<flag>              Toggle a command line flag [see FLAGS below].
X  _<flag>              Display the setting of a command line flag.
X  +cmd                 Execute the less cmd each time a new file is examined.
X
X  !command             Passes the command to $SHELL to be executed.
X  |Xcommand            Pipe file between current pos & mark X to shell command.
X  v                    Edit the current file with $EDITOR.
X
X
X                         FLAGS
X
X        Most flags may be changed either on the command line,
X        or from within less by using the - command.
X
X  -a            Set forward search starting location.
X  -b [N]        Number of buffers.
X  -B            Automatically allocate buffers.
X  -c  -C        Repaint by scrolling/clearing.
X  -d            Dumb terminal.
X  -e  -E        Quit at end of file.
X  -f            Force open non-regular files.
X  -h [N]        Backward scroll limit.
X  -i            Ignore case in searches.
X  -j [N]        Screen position of target lines.
X  -k [file]     Use a lesskey file.
X  -l [file]     Log file.
X  -L [file]     Log file (unconditionally overwrite).
X  -m  -M        Set prompt style.
X  -n  -N        Use line numbers.
X  -P [prompt]   Define new prompt.
X  -q  -Q        Quiet the terminal bell.
X  -r  -R        Translate control characters.
X  -s            Squeeze multiple blank lines.
X  -t [tag]      Find a tag.
X  -T [tagsfile] Use an alternate tags file.
X  -u  -U        Change handling of backspaces.
X  -w            Display ~ for lines after end-of-file.
X  -x [N]        Set tab stops.
X  -y [N]        Forward scroll limit.
X  -z [N]        Set size of window.
X
END_OF_FILE



More information about the Alt.sources mailing list