less, part 2 of 2

Mark Nudelman mark at nsc-pdc.UUCP
Sun Sep 8 02:55:42 AEST 1985


#!/bin/sh-----cut here-----cut here-----cut here-----cut here-----
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	position.c 
#	input.c 
#	output.c 
#	screen.c 
#	prompt.c 
#	line.c 
#	signal.c 
#	help.c 
#	ttyin.c 
#	command.c 
#	version.c 
cat - << \SHAR_EOF > position.c
/*
 * Routines dealing with the "position" table.
 * This is a table which tells the position (in the input file) of the
 * first char on each currently displayed line.
 *
 * {{ The position table is scrolled by moving all the entries.
 *    Would be better to have a circular table 
 *    and just change a couple of pointers. }}
 */

#include "less.h"
#include "position.h"

#define	NPOS	100		/* {{ sc_height must be less than NPOS }} */
static POSITION table[NPOS];	/* The position table */

extern int sc_width, sc_height;

/*
 * Return the position of one of:
 *	the top (first) line on the screen
 *	the second line on the screen
 *	the bottom line on the screen
 *	the line after the bottom line on the screen
 */
	public POSITION
position(where)
	int where;
{
	register int n;

	switch (where)
	{
	case TOP:
		n = 0;
		break;
	case TOP_PLUS_ONE:
		n = 1;
		break;
	case BOTTOM:
		n = sc_height - 2;
		break;
	case BOTTOM_PLUS_ONE:
		n = sc_height - 1;
		break;
	}
	return (table[n]);
}

/*
 * Add a new file position to the bottom of the position table.
 */
	public void
add_forw_pos(pos)
	POSITION pos;
{
	register int i;

	/*
	 * Scroll the position table up.
	 */
	for (i = 1;  i < sc_height;  i++)
		table[i-1] = table[i];
	table[sc_height - 1] = pos;
}

/*
 * Add a new file position to the top of the position table.
 */
	public void
add_back_pos(pos)
	POSITION pos;
{
	register int i;

	/*
	 * Scroll the position table down.
	 */
	for (i = sc_height - 1;  i > 0;  i--)
		table[i] = table[i-1];
	table[0] = pos;
}

/*
 * Initialize the position table, done whenever we clear the screen.
 */
	public void
pos_clear()
{
	register int i;

	for (i = 0;  i < sc_height;  i++)
		table[i] = NULL_POSITION;
}
SHAR_EOF
cat - << \SHAR_EOF > input.c
/*
 * High level routines dealing with getting input from the file being viewed.
 * When we speak of "lines" here, we mean PRINTABLE lines.
 */

#include "less.h"

extern int do_bs;
extern int squeeze;
extern char *line;

/*
 * Get the next line.
 * A "current" position is passed and a "new" position is returned.
 * The current position is the position of the first character of
 * a line.  The new position is the position of the first character
 * of the NEXT line.  The line obtained is the line starting at curr_pos.
 */
	public POSITION
forw_line(curr_pos)
	POSITION curr_pos;
{
	POSITION new_pos;
	register int c;

	if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
	{
		line = NULL;
		return (NULL_POSITION);
	}

	c = ch_forw_get();
	if (c == EOF)
	{
		line = NULL;
		return (NULL_POSITION);
	}

	prewind();
	for (;;)
	{
		if (c == '\n' || c == EOF)
		{
			/*
			 * End of the line.
			 */
			new_pos = ch_tell();
			break;
		}

		/*
		 * Append the char to the line and get the next char.
		 */
		if (pappend(c))
		{
			/*
			 * The char won't fit in the line; the line
			 * is too long to print in the screen width.
			 * End the line here.
			 */
			new_pos = ch_tell() - 1;
			break;
		}
		c = ch_forw_get();
	}
	(void) pappend('\0');

	if (squeeze && *line == '\0')
	{
		/*
		 * This line is blank.
		 * Skip down to the last contiguous blank line
		 * and pretend it is the one which we are returning.
		 */
		while ((c = ch_forw_get()) == '\n')
			;
		if (c != EOF)
			(void) ch_back_get();
		new_pos = ch_tell();
	}

	return (new_pos);
}

/*
 * Get the previous line.
 * A "current" position is passed and a "new" position is returned.
 * The current position is the position of the first character of
 * a line.  The new position is the position of the first character
 * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
 */
	public POSITION
back_line(curr_pos)
	POSITION curr_pos;
{
	POSITION new_pos, begin_new_pos;
	int c;

	if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
		ch_seek(curr_pos-1))
	{
		line = NULL;
		return (NULL_POSITION);
	}

	if (squeeze)
	{
		/*
		 * Find out if the "current" line was blank.
		 */
		(void) ch_forw_get();	/* Skip the newline */
		c = ch_forw_get();	/* First char of "current" line */
		(void) ch_back_get();	/* Restore our position */
		(void) ch_back_get();

		if (c == '\n')
		{
			/*
			 * The "current" line was blank.
			 * Skip over any preceeding blank lines,
			 * since we skipped them in forw_line().
			 */
			while ((c = ch_back_get()) == '\n')
				;
			if (c == EOF)
			{
				line = NULL;
				return (NULL_POSITION);
			}
			(void) ch_forw_get();
		}
	}

	/*
	 * Scan backwards until we hit the beginning of the line.
	 */
	for (;;)
	{
		c = ch_back_get();
		if (c == '\n')
		{
			/*
			 * This is the newline ending the previous line.
			 * We have hit the beginning of the line.
			 */
			new_pos = ch_tell() + 1;
			break;
		}
		if (c == EOF)
		{
			/*
			 * We have hit the beginning of the file.
			 * This must be the first line in the file.
			 * This must, of course, be the beginning of the line.
			 */
			new_pos = (POSITION)0;
			break;
		}
	}

	/*
	 * Now scan forwards from the beginning of this line.
	 * We keep discarding "printable lines" (based on screen width)
	 * until we reach the curr_pos.
	 *
	 * {{ This algorithm is pretty inefficient if the lines
	 *    are much longer than the screen width, 
	 *    but I don't know of any better way. }}
	 */
	if (ch_seek(new_pos))
	{
		line = NULL;
		return (NULL_POSITION);
	}
    loop:
	begin_new_pos = new_pos;
	prewind();

	do
	{
		c = ch_forw_get();
		new_pos++;
		if (c == '\n')
			break;
		if (pappend(c))
		{
			/*
			 * Got a full printable line, but we haven't
			 * reached our curr_pos yet.  Discard the line
			 * and start a new one.
			 */
			(void) pappend('\0');
			(void) ch_back_get();
			new_pos--;
			goto loop;
		}
	} while (new_pos < curr_pos);

	(void) pappend('\0');

	return (begin_new_pos);
}
SHAR_EOF
cat - << \SHAR_EOF > output.c
/*
 * High level routines dealing with the output to the screen.
 */

#include "less.h"

extern int sigs;
extern int sc_width, sc_height;
extern int ul_width, ue_width;
extern int so_width, se_width;
extern char *line;
	
/*
 * Display the line which is in the line buffer.
 */
	public void
put_line()
{
	register char *p;
	register int c;
	register int column;
	extern int auto_wrap, ignaw;

	if (sigs)
		/*
		 * Don't output if a signal is pending.
		 */
		return;

	if (line == NULL)
		line = "~";

	column = 0;
	for (p = line;  *p != '\0';  p++)
	{
		c = *p;
		if (c == UL_CHAR)
		{
			ul_enter();
			column += ul_width;
		} else if (c == UE_CHAR)
		{
			ul_exit();
			column += ue_width;
		} else if (c & 0200)
		{
			putc('^');
			putc(c & 0177);
			column += 2;
		} else if (c == '\b')
		{
			putbs();
			column--;
		} else
		{
			putc(c);
			column++;
		}
	}
	if (column < sc_width || !auto_wrap || ignaw)
		putc('\n');
}

/*
 * Is a given character a "control" character?
 * {{ ASCII DEPENDENT }}
 */
	public int
control_char(c)
	int c;
{
	return (c < ' ' || c == '\177');
}

/*
 * Return the printable character used to identify a control character
 * (printed after a carat; e.g. '\3' => "^C").
 * {{ ASCII DEPENDENT }}
 */
	public int
carat_char(c)
	int c;
{
	return ((c == '\177') ? '?' : (c | 0100));
}


static char obuf[1024];
static char *ob = obuf;

/*
 * Flush buffered output.
 */
	public void
flush()
{
	write(1, obuf, ob-obuf);
	ob = obuf;
}

/*
 * Discard buffered output.
 */
	public void
dropout()
{
	ob = obuf;
}

/*
 * Output a character.
 */
	public void
putc(c)
	int c;
{
	if (ob >= &obuf[sizeof(obuf)])
		flush();
	*ob++ = c;
}

/*
 * Output a string.
 */
	public void
puts(s)
	register char *s;
{
	while (*s != '\0')
		putc(*s++);
}

/*
 * Output a message in the lower left corner of the screen
 * and wait for carriage return.
 */

static char return_to_continue[] = "  (press RETURN)";

	public void
error(s)
	char *s;
{
	register int c;

	lower_left();
	clear_eol();
	so_enter();
	puts(s);
	puts(return_to_continue);
	so_exit();

	while ((c = getc()) != '\n' && c != '\r')
		bell();

	if (strlen(s) > sc_width)
		repaint();
}

	public int
error_width()
{
	/*
	 * Don't use the last position, because some terminals
	 * will scroll if you write in the last char of the last line.
	 */
	return (sc_width - 
		(sizeof(return_to_continue) + so_width + se_width + 1));
}
SHAR_EOF
cat - << \SHAR_EOF > screen.c
/*
 * Routines which deal with the characteristics of the terminal.
 * Uses termcap to be as terminal-independent as possible.
 *
 * {{ Someday this should be rewritten to use curses. }}
 */

#include "less.h"
#if XENIX
#include <sys/types.h>
#include <sys/ioctl.h>
#endif

#if TERMIO
#include <termio.h>
#else
#include <sgtty.h>
#endif

/*
 * Strings passed to tputs() to do various terminal functions.
 */
static char
	*sc_pad,		/* Pad string */
	*sc_home,		/* Cursor home */
	*sc_addline,		/* Add line, scroll down following lines */
	*sc_lower_left,		/* Cursor to last line, first column */
	*sc_move,		/* General cursor positioning */
	*sc_clear,		/* Clear screen */
	*sc_eol_clear,		/* Clear to end of line */
	*sc_s_in,		/* Enter standout (highlighted) mode */
	*sc_s_out,		/* Exit standout mode */
	*sc_u_in,		/* Enter underline mode */
	*sc_u_out,		/* Exit underline mode */
	*sc_visual_bell,	/* Visual bell (flash screen) sequence */
	*sc_backspace,		/* Backspace cursor */
	*sc_init,		/* Startup terminal initialization */
	*sc_deinit;		/* Exit terminal de-intialization */
static int dumb;
static int hard;

public int auto_wrap;		/* Terminal does \r\n when write past margin */
public int ignaw;		/* Terminal ignores \n immediately after wrap */
public int erase_char, kill_char; /* The user's erase and line-kill chars */
public int sc_width, sc_height;	/* Height & width of screen */
public int ul_width, ue_width;	/* Printing width of underline sequences */
public int so_width, se_width;	/* Printing width of standout sequences */

/*
 * These two variables are sometimes defined in,
 * and needed by, the termcap library.
 * It may be necessary on some systems to declare them extern here.
 */
/*extern*/ short ospeed;	/* Terminal output baud rate */
/*extern*/ char PC;		/* Pad character */

extern int quiet;		/* If VERY_QUIET, use visual bell for bell */
extern int know_dumb;		/* Don't complain about a dumb terminal */
char *tgetstr();
char *tgoto();

/*
 * Change terminal to "raw mode", or restore to "normal" mode.
 * "Raw mode" means 
 *	1. An outstanding read will complete on receipt of a single keystroke.
 *	2. Input is not echoed.  
 *	3. On output, \n is mapped to \r\n.
 *	4. \t is NOT be expanded into spaces.
 *	5. Signal-causing characters such as ctrl-C (interrupt),
 *	   etc. are NOT disabled.
 * It doesn't matter whether an input \n is mapped to \r, or vice versa.
 */
	public void
raw_mode(on)
	int on;
{
#if TERMIO
	struct termio s;
	static struct termio save_term;

	if (on)
	{
		/*
		 * Get terminal modes.
		 */
		ioctl(2, TCGETA, &s);

		/*
		 * Save modes and set certain variables dependent on modes.
		 */
		save_term = s;
		ospeed = s.c_cflag & CBAUD;
		erase_char = s.c_cc[VERASE];
		kill_char = s.c_cc[VKILL];

		/*
		 * Set the modes to the way we want them.
		 */
		s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
		s.c_oflag |=  (OPOST|ONLCR|TAB3);
		s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
		s.c_cc[VMIN] = 1;
		s.c_cc[VTIME] = 0;
	} else
	{
		/*
		 * Restore saved modes.
		 */
		s = save_term;
	}
	ioctl(2, TCSETAW, &s);
#else
	struct sgttyb s;
	static struct sgttyb save_term;

	if (on)
	{
		/*
		 * Get terminal modes.
		 */
		ioctl(2, TIOCGETP, &s);

		/*
		 * Save modes and set certain variables dependent on modes.
		 */
		save_term = s;
		ospeed = s.sg_ospeed;
		erase_char = s.sg_erase;
		kill_char = s.sg_kill;

		/*
		 * Set the modes to the way we want them.
		 */
		s.sg_flags |= CBREAK;
		s.sg_flags &= ~(ECHO|XTABS);
	} else
	{
		/*
		 * Restore saved modes.
		 */
		s = save_term;
	}
	ioctl(2, TIOCSETP, &s);
#endif
}

static int couldnt = 0;

	static void
cannot(s)
	char *s;
{
	if (know_dumb)
		/* 
		 * He knows he has a dumb terminal, so don't tell him. 
		 */
		return;

	printf("WARNING: terminal cannot \"%s\"\n", s);
	couldnt = 1;
}

/*
 * Get terminal capabilities via termcap.
 */
	public void
get_term()
{
	char termbuf[1024];
	char *sp;
	static char sbuf[150];

	char *getenv();

	/*
	 * Find out what kind of terminal this is.
	 */
	if (tgetent(termbuf, getenv("TERM")) <= 0)
		dumb = 1;

	/*
	 * Get size of the screen.
	 */
	if (dumb || (sc_height = tgetnum("li")) < 0 || tgetflag("hc"))
	{
		/* Oh no, this is a hardcopy terminal. */
		hard = 1;
		sc_height = 24;
	}
	if (dumb || (sc_width = tgetnum("co")) < 0)
		sc_width = 80;

	auto_wrap = tgetflag("am");
	ignaw = tgetflag("xn");

	/*
	 * Assumes termcap variable "sg" is the printing width of
	 * the standout sequence, the end standout sequence,
	 * the underline sequence, and the end underline sequence.
	 */
	if ((ul_width = tgetnum("sg")) < 0)
		ul_width = 0;
	so_width = se_width = ue_width = ul_width;

	/*
	 * Get various string-valued capabilities.
	 */
	sp = sbuf;

	sc_pad = (dumb) ? NULL : tgetstr("pc", &sp);
	if (sc_pad != NULL)
		PC = *sc_pad;

	sc_init = (dumb) ? NULL : tgetstr("ti", &sp);
	if (sc_init == NULL)
		sc_init = "";

	sc_deinit= (dumb) ? NULL : tgetstr("te", &sp);
	if (sc_deinit == NULL)
		sc_deinit = "";

	sc_eol_clear = (dumb) ? NULL : tgetstr("ce", &sp);
	if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
	{
		cannot("clear to end of line");
		sc_eol_clear = "";
	}

	sc_clear = (dumb) ? NULL : tgetstr("cl", &sp);
	if (hard || sc_clear == NULL || *sc_clear == '\0')
	{
		cannot("clear screen");
		sc_clear = "\n\n";
	}

	sc_move = (dumb) ? NULL : tgetstr("cm", &sp);
	if (hard || sc_move == NULL || *sc_move == '\0')
	{
		/*
		 * This is not an error here, because we don't 
		 * always need sc_move.
		 * We need it only if we don't have home or lower-left.
		 */
		sc_move = "";
	}

	sc_s_in = (dumb) ? NULL : tgetstr("so", &sp);
	if (hard || sc_s_in == NULL)
		sc_s_in = "";

	sc_s_out = (dumb) ? NULL : tgetstr("se", &sp);
	if (hard || sc_s_out == NULL)
		sc_s_out = "";

	sc_u_in = (dumb) ? NULL : tgetstr("us", &sp);
	if (hard || sc_u_in == NULL)
		sc_u_in = sc_s_in;

	sc_u_out = (dumb) ? NULL : tgetstr("ue", &sp);
	if (hard || sc_u_out == NULL)
		sc_u_out = sc_s_out;

	sc_visual_bell = (dumb) ? NULL : tgetstr("vb", &sp);
	if (hard || sc_visual_bell == NULL)
		sc_visual_bell = "";

	sc_home = (dumb) ? NULL : tgetstr("ho", &sp);
	if (hard || sc_home == NULL || *sc_home == '\0')
	{
		if (*sc_move == '\0')
		{
			cannot("home cursor");
			/*
			 * This last resort for sc_home is supposed to
			 * be an up-arrow suggesting moving to the 
			 * top of the "virtual screen". (The one in
			 * your imagination as you try to use this on
			 * a hard copy terminal.)
			 */
			sc_home = "|\b^";		
		} else
		{
			/* 
			 * No "home" string,
			 * but we can use "move(0,0)".
			 */
			strcpy(sp, tgoto(sc_move, 0, 0));
			sc_home = sp;
			sp += strlen(sp) + 1;
		}
	}

	sc_lower_left = (dumb) ? NULL : tgetstr("ll", &sp);
	if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
	{
		if (*sc_move == '\0')
		{
			cannot("move cursor to lower left of screen");
			sc_lower_left = "\r";
		} else
		{
			/*
			 * No "lower-left" string, 
			 * but we can use "move(0,last-line)".
			 */
			strcpy(sp, tgoto(sc_move, 0, sc_height-1));
			sc_lower_left = sp;
			sp += strlen(sp) + 1;
		}
	}

	/*
	 * To add a line at top of screen and scroll the display down,
	 * we use "al" (add line) or "sr" (scroll reverse).
	 */
	if (dumb)
		sc_addline = NULL;
	else if ((sc_addline = tgetstr("al", &sp)) == NULL || 
		 *sc_addline == '\0')
		sc_addline = tgetstr("sr", &sp);

	if (hard || sc_addline == NULL || *sc_addline == '\0')
	{
		cannot("scroll backwards");
		sc_addline = "";
	}

	if (dumb || tgetflag("bs"))
		sc_backspace = "\b";
	else
	{
		sc_backspace = tgetstr("bc", &sp);
		if (sc_backspace == NULL || *sc_backspace == '\0')
			sc_backspace = "\b";
	}

	if (couldnt)
		/* Give him time to read all the "cannot" messages. */
		error("");
}


/*
 * Below are the functions which perform all the 
 * terminal-specific screen manipulation.
 */


/*
 * Initialize terminal
 */
	public void
init()
{
	tputs(sc_init, sc_height, putc);
}

/*
 * Deinitialize terminal
 */
	public void
deinit()
{
	tputs(sc_deinit, sc_height, putc);
}

/*
 * Home cursor (move to upper left corner of screen).
 */
	public void
home()
{
	tputs(sc_home, 1, putc);
}

/*
 * Add a blank line (called with cursor at home).
 * Should scroll the display down.
 */
	public void
add_line()
{
	tputs(sc_addline, sc_height, putc);
}

/*
 * Move cursor to lower left corner of screen.
 */
	public void
lower_left()
{
	tputs(sc_lower_left, 1, putc);
}

/*
 * Ring the terminal bell.
 */
	public void
bell()
{
	if (quiet == VERY_QUIET)
		vbell();
	else
		putc('\7');
}

/*
 * Output the "visual bell", if there is one.
 */
	public void
vbell()
{
	if (*sc_visual_bell == '\0')
		return;
	tputs(sc_visual_bell, sc_height, putc);
}

/*
 * Clear the screen.
 */
	public void
clear()
{
	tputs(sc_clear, sc_height, putc);
}

/*
 * Clear from the cursor to the end of the cursor's line.
 * {{ This must not move the cursor. }}
 */
	public void
clear_eol()
{
	tputs(sc_eol_clear, 1, putc);
}

/*
 * Begin "standout" (bold, underline, or whatever).
 */
	public void
so_enter()
{
	tputs(sc_s_in, 1, putc);
}

/*
 * End "standout".
 */
	public void
so_exit()
{
	tputs(sc_s_out, 1, putc);
}

/*
 * Begin "underline" (hopefully real underlining, 
 * otherwise whatever the terminal provides).
 */
	public void
ul_enter()
{
	tputs(sc_u_in, 1, putc);
}

/*
 * End "underline".
 */
	public void
ul_exit()
{
	tputs(sc_u_out, 1, putc);
}

/*
 * Erase the character to the left of the cursor 
 * and move the cursor left.
 */
	public void
backspace()
{
	/* 
	 * Try to erase the previous character by overstriking with a space.
	 */
	tputs(sc_backspace, 1, putc);
	putc(' ');
	tputs(sc_backspace, 1, putc);
}

/*
 * Output a plain backspace, without erasing the previous char.
 */
	public void
putbs()
{
	tputs(sc_backspace, 1, putc);
}
SHAR_EOF
cat - << \SHAR_EOF > prompt.c
/*
 * Prompting and other messages.
 * There are three flavors of prompts, SHORT, MEDIUM and LONG,
 * selected by the -m/-M options.
 * A prompt is either a colon or a message composed of various
 * pieces, such as the name of the file being viewed, the percentage
 * into the file, etc.
 */

#include "less.h"
#include "position.h"

extern int pr_type;
extern int pipe;
extern int hit_eof;
extern int new_file;
extern int sc_width;
extern char current_file[];
extern int ac;
extern char **av;
extern int curr_ac;

static char message[500];

/*
 * Append the name of the current file (to the message buffer).
 */
	static void
ap_filename()
{
	if (!pipe)
		sprintf(message + strlen(message), 
			"%s", current_file);
}

/*
 * Append the "file N of M" message.
 */
	static void
ap_of()
{
	if (ac > 1)
		sprintf(message + strlen(message), 
			" (file %d of %d)", curr_ac+1, ac);
}

/*
 * Append the byte offset into the current file.
 */
	static void
ap_byte()
{
	POSITION pos, len;

	pos = position(BOTTOM_PLUS_ONE);
	if (pos != NULL_POSITION)
	{
		sprintf(message + strlen(message), 
			" byte %ld", pos);
		len = ch_length();
		if (len > 0)
			sprintf(message + strlen(message), 
				"/%ld", len);
	}
}

/*
 * Append the percentage into the current file.
 */
	static void
ap_percent()
{
	POSITION pos,len;

	pos = position(BOTTOM_PLUS_ONE);
	len = ch_length();
	if (len > 0 && pos != NULL_POSITION)
		sprintf(message + strlen(message),
			" (%ld%%)", (100 * pos) / len);
}

/*
 * Append the end-of-file message.
 */
	static void
ap_eof()
{
	strcat(message, " END");
	if (curr_ac + 1 < ac)
		sprintf(message + strlen(message),
			" - Next: %s", av[curr_ac+1]);
}

/*
 * Return a message suitable for printing by the "=" command.
 */
	public char *
eq_message()
{
	message[0] = '\0';
	ap_filename();
	ap_of();
	ap_byte();
	ap_percent();
	/*
	 * Truncate to the screen width.
	 * {{ This isn't very nice. }}
	 */
	message[error_width()] = '\0';
	return (message);
}

/*
 * Return a prompt.
 * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
 * If we can't come up with an appropriate prompt, return NULL
 * and the caller will prompt with a colon.
 */
	public char *
pr_string()
{
	message[0] = '\0';
	switch (pr_type)
	{
	case PR_SHORT:
		if (new_file)
		{
			ap_filename();
			ap_of();
		}
		if (hit_eof)
			ap_eof();
		break;
	case PR_MEDIUM:
		if (new_file)
		{
			ap_filename();
			ap_of();
		}
		if (hit_eof)
			ap_eof();
		else if (pipe)
			ap_byte();
		else
			ap_percent();
		break;
	case PR_LONG:
		ap_filename();
		if (new_file)
			ap_of();
		ap_byte();
		if (hit_eof)
			ap_eof();
		else
			ap_percent();
		break;
	}
	new_file = 0;
	if (message[0] == '\0')
		return (NULL);
	/*
	 * Truncate to the screen width.
	 * {{ This isn't very nice. }}
	 */
	message[sc_width-2] = '\0';
	return (message);
}
SHAR_EOF
cat - << \SHAR_EOF > line.c
/*
 * Routines to manipulate the "line buffer".
 * The line buffer holds a line of output as it is being built
 * in preparation for output to the screen.
 * We keep track of the PRINTABLE length of the line as it is being built.
 */

#include "less.h"

static char linebuf[1024];	/* Buffer which holds the current output line */
static char *curr;		/* Pointer into linebuf */
static int column;		/* Printable length, accounting for
				   backspaces, etc. */
/*
 * A ridiculously complex state machine takes care of backspaces 
 * when in BS_UNDERLINE mode.  The complexity arises from the attempt
 * to deal with all cases, especially involving long lines with underlining.
 * There are still some cases which will break it.
 *
 * There are four states:
 *	UL_NORMAL is the normal state (not in underline mode).
 *	UL_YES means we are in underline mode.  We expect to get
 *		either a sequence like "_\bX" or "X\b_" to continue
 *		underline mode, or just some ordinary characters
 *		(no backspaces) to end underline mode.
 *	UL_X means we are one character after UL_YES
 *		(we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
 *	UL_XB means we are one character after UL_X 
 *		(we have gotten the backspace in "_\bX" or "X\b_";
 *		we expect one more ordinary character, 
 *		which will put us back in state UL_YES).
 */
static int ul_state;		/* Currently in underline mode? */
#define	UL_NORMAL	0	/* Not in underline mode */
#define	UL_YES		1	/* In underline, need next char */
#define	UL_X		2	/* In underline, got char, need \b */
#define	UL_XB		3	/* In underline, got char & \b, need one more */

public char *line;		/* Pointer to the current line.
				   Usually points to linebuf. */

extern int bs_mode;
extern int tabstop;
extern int ul_width, ue_width;
extern int sc_width, sc_height;

/*
 * Rewind the line buffer.
 */
	public void
prewind()
{
	line = curr = linebuf;
	ul_state = UL_NORMAL;
	column = 0;
}

/*
 * Append a character to the line buffer.
 * Expand tabs into spaces, handle underlining.
 * Returns 0 if ok, 1 if couldn't fit in buffer.
 */

#define	NEW_COLUMN(newcol)	if ((newcol) + ((ul_state)?ue_width:0) > sc_width) \
					return (1); else column = (newcol)

	public int
pappend(c)
	int c;
{
	if (c == '\0')
	{
		/*
		 * Terminate underline mode, if necessary.
		 * Append a '\0' to the end of the line.
		 */
		switch (ul_state)
		{
		case UL_X:
			curr[0] = curr[-1];
			curr[-1] = UE_CHAR;
			curr++;
			break;
		case UL_XB:
		case UL_YES:
			*curr++ = UE_CHAR;
			break;
		}
		ul_state = UL_NORMAL;
		*curr = '\0';
		return (0);
	}

	if (curr > linebuf + sizeof(linebuf) - 12)
		/*
		 * Almost out of room in the line buffer.
		 * Don't take any chances.
		 * {{ Linebuf is supposed to be big enough that this
		 *    will never happen, but may need to be made 
		 *    bigger for wide screens or lots of backspaces. }}
		 */
		return (1);

	if (bs_mode == BS_UNDERLINE)
	{
		/*
		 * Advance the state machine.
		 */
		switch (ul_state)
		{
		case UL_NORMAL:
			if (curr <= linebuf + 1 || curr[-1] != '\b' ||
				(c != '_' && curr[-2] != '_'))
				break;
			/*
			 * We have either "_\bX" or "X\b_" (including
			 * the current char).  Switch into underline mode.
			 */
			if (column + ul_width + ue_width + 1 >= sc_width)
				/*
				 * Not enough room left on the screen to 
				 * enter and exit underline mode.
				 */
				return (1);

			if (ul_width > 0 && 
			    curr > linebuf + 2 && curr[-3] == ' ')
			{
				/*
				 * Special case for magic cookie terminals:
				 * if the previous char was a space, replace 
				 * it with the "enter underline" sequence.
				 */
				curr[-3] = UL_CHAR;
				column += ul_width-1;
			} else
			{
				curr[-1] = curr[-2];
				curr[-2] = UL_CHAR;
				column += ul_width;
				curr++;
			}
			/* Fall thru */
		case UL_XB:
			/*
			 * Termination of a sequnce "_\bX" or "X\b_".
			 */
			if (c == '_')
				c = curr[-2];
			curr -= 2;
			ul_state = UL_YES;
			break;
		case UL_YES:
			if (column + ue_width + 1 >= sc_width)
				/*
				 * We have just barely enough room to 
				 * exit underline mode.  
				 */
				return (1);
			ul_state = UL_X;
			break;
		case UL_X:
			if (c == '\b')
				ul_state = UL_XB;
			else
			{
				/*
				 * Exit underline mode.
				 * We have to shuffle the chars a bit
				 * to make this work.
				 */
				curr[0] = curr[-1];
				curr[-1] = UE_CHAR;
				column += ue_width;
				if (ul_width > 0 && curr[0] == ' ')
					/*
					 * Another special case for magic
					 * cookie terminals: if the next
					 * char is a space, replace it
					 * with the "exit underline" sequence.
					 */
					column--;
				else
					curr++;
				ul_state = UL_NORMAL;
			} 
			break;
		}
	}
	
	if (c == '\t') 
	{
		/*
		 * Expand a tab into spaces.
		 */
		do
		{
			NEW_COLUMN(column+1);
			*curr++ = ' ';
		} while ((column % tabstop) != 0);
		return (0);
	}

	if (c == '\b')
	{
		if (bs_mode == BS_CONTROL)
		{
			/*
			 * Treat backspace as a control char: output "^H".
			 */
			NEW_COLUMN(column+2);
			*curr++ = ('H' | 0200);
		} else
		{
			/*
			 * Output a real backspace.
			 */
			column--;
			*curr++ = '\b';
		}
		return (0);
	} 

	if (control_char(c))
	{
		/*
		 * Put a "^X" into the buffer.
		 * The 0200 bit is used to tell put_line() to prefix
		 * the char with a ^.  We don't actually put the ^
		 * in the buffer because we sometimes need to move
		 * chars around, and such movement might separate 
		 * the ^ from its following character.
		 */
		NEW_COLUMN(column+2);
		*curr++ = (carat_char(c) | 0200);
		return (0);
	}

	/*
	 * Ordinary character.  Just put it in the buffer.
	 */
	NEW_COLUMN(column+1);
	*curr++ = c;
	return (0);
}
SHAR_EOF
cat - << \SHAR_EOF > signal.c
/*
 * Routines dealing with signals.
 *
 * A signal usually merely causes a bit to be set in the "signals" word.
 * At some convenient time, the mainline code checks to see if any
 * signals need processing by calling psignal().
 * An exception is made if we are reading from the keyboard when the
 * signal is received.  Some operating systems will simply call the
 * signal handler and NOT return from the read (with EINTR).
 * To handle this case, we service the interrupt directly from
 * the handler if we are reading from the keyboard.
 */

#include "less.h"
#include <signal.h>
#include <setjmp.h>

/*
 * The type of signal handler functions.
 * Usually int, although it should be void.
 */
typedef	int		HANDLER;

/*
 * "sigs" contains bits indicating signals which need to be processed.
 */
public int sigs;
#define	S_INTERRUPT	01
#ifdef SIGTSTP
#define	S_STOP		02
#endif

extern int reading;
extern char *first_cmd;
extern jmp_buf main_loop;

/*
 * Interrupt signal handler.
 */
	static HANDLER
interrupt()
{
	SIGNAL(SIGINT, interrupt);
	sigs |= S_INTERRUPT;
	if (reading)
		psignals();
}

#ifdef SIGTSTP
/*
 * "Stop" (^Z) signal handler.
 */
	static HANDLER
stop()
{
	SIGNAL(SIGTSTP, stop);
	sigs |= S_STOP;
	if (reading)
		psignals();
}
#endif

/*
 * Set up the signal handlers.
 */
	public void
init_signals()
{
	(void) SIGNAL(SIGINT, interrupt);
#ifdef SIGTSTP
	(void) SIGNAL(SIGTSTP, stop);
#endif
}

/*
 * Process any signals we have recieved.
 * A received signal cause a bit to be set in "sigs".
 */
	public void 
psignals()
{
	register int tsignals;

	tsignals = sigs;
	sigs = 0;
	if (tsignals == 0)
		return;

	dropout();		/* Discard any buffered output */

#ifdef SIGTSTP
	if (tsignals & S_STOP)
	{
		/*
		 * Clean up the terminal.
		 */
		lower_left();
		clear_eol();
		flush();
		raw_mode(0);
		SIGNAL(SIGTSTP, SIG_DFL);
#if SIGSETMASK
		/*
		 * This system will not allow us to send a 
		 * stop signal (SIGTSTP) to ourself
		 * while we are in the signal handler, like maybe now.
		 * (This can be the case if we are reading; see comment above.)
		 * So we ask the silly system for permission to do so.
		 */
		sigsetmask(0);
#endif
		kill(getpid(), SIGTSTP);
		/*
		 * ... Bye bye. ...
		 * Hopefully we'll be back later and resume here...
		 * Reset the terminal and arrange to repaint the
		 * screen when we get back to the main command loop.
		 */
		SIGNAL(SIGTSTP, stop);
		raw_mode(1);
		first_cmd = "r";
		longjmp(main_loop, 1);
	}
#endif
	if (tsignals & S_INTERRUPT)
	{
		bell();
		/*
		 * {{ You may wish to replace the bell() with 
		 *    error("Interrupt"); }}
		 */
	}

	longjmp(main_loop, 1);
}
SHAR_EOF
cat - << \SHAR_EOF > help.c
#include  "less.h"

/*
 * Display some help.
 * Help is in two pages.
 */
	static void
help0()
{
	puts("f, SPACE       Forward one screen.\n");
	puts("b              Backward one screen.\n");
	puts("e, j, CR    *  Forward N lines, default 1.\n");
	puts("y, k        *  Backward N lines, default 1.\n");
	puts("d           *  Forward N lines, default 10 or last N to d or u command.\n");
	puts("u           *  Backward N lines, default 10 or last N to d or u command.\n");
	puts("r              Repaint screen.\n");
	puts("g           *  Go to line N, default 1.\n");
	puts("G           *  Like g, but default is last line in file.\n");
	puts("=              Print current file name\n");
	puts("/pattern    *  Search forward for N-th occurence of pattern.\n");
	puts("?pattern    *  Search backward for N-th occurence of pattern.\n");
	puts("n           *  Repeat previous search (for N-th occurence).\n");
	puts("q              Exit.\n");
	error("More help...");
}

	static void
help1()
{
	char message[100];
	extern char all_options[];

	puts("R              Repaint screen, discarding buffered input.\n");
	puts("p           *  Position to N percent into the file.\n");
	sprintf(message, 
	     "-X             Toggle a flag (X may be one of \"%s\").\n", 
				all_options);
	puts(message);
	puts("E [file]       Examine a new file.\n");
	puts("N              Examine the next file (from the command line).\n");
	puts("P              Examine the previous file (from the command line).\n");
	puts("V              Print version number.\n");
#if SHELL_ESCAPE
	puts("!command       Passes the command to a shell to be executed.\n");
#endif
#if EDITOR
	puts("v              Edit the current file with $EDITOR (default vi).\n");
#endif
	error("");
}

	public void
help()
{
	register int i;

	for (i = 0;  i < 2;  i++)
	{
		clear();
		puts("Commands marked with * may be preceeded by a number, N.\n\n");

		switch (i)
		{
		case 0:		help0();	break;
		case 1:		help1();	break;
		}
	}
}
SHAR_EOF
cat - << \SHAR_EOF > ttyin.c
/*
 * Routines dealing with getting input from the keyboard (i.e. from the user).
 */

#include "less.h"

/*
 * The boolean "reading" is set true or false according to whether
 * we are currently reading from the keyboard.
 * This information is used by the signal handling stuff in signal.c.
 * {{ There are probably some race conditions here
 *    involving the variable "reading". }}
 */
public int reading;

static int tty;

/*
 * Open keyboard for input.
 * (Just use file descriptor 2.)
 */
	public void
open_getc()
{
	tty = 2;
}

/*
 * Get a character from the keyboard.
 */
	public int
getc()
{
	char c;
	int result;

	reading = 1;
	do
	{
		flush();
		result = read(tty, &c, 1);
	} while (result != 1);
	reading = 0;
	return (c & 0177);
}
SHAR_EOF
cat - << \SHAR_EOF > command.c
/*
 * User-level command processor.
 */

#include "less.h"

extern int erase_char, kill_char;
extern int pr_type;
extern int sigs;
extern int pipe;
extern int quit_at_eof;
extern int hit_eof;
extern int sc_width, sc_height;
extern char *first_cmd;
extern char version[];
extern char current_file[];
extern char *editor;

static char cmdbuf[90];		/* Buffer for holding a multi-char command */
static char *cp;		/* Pointer into cmdbuf */
static int cmd_col;		/* Current column of the multi-char command */
static char mcc;		/* The multi-char command letter (e.g. '/') */

/*
 * Reset command buffer (to empty).
 */
cmd_reset()
{
	cp = cmdbuf;
}

/*
 * Backspace in command buffer.
 */
	static int
cmd_erase()
{
	if (cp == cmdbuf)
		/*
		 * Backspace past beginning of the string:
		 * this usually means abort the command.
		 */
		return (1);

	if (control_char(*--cp))
	{
		/*
		 * Erase an extra character, for the carat.
		 */
		backspace();
		cmd_col--;
	}
	backspace();
	cmd_col--;
	return (0);
}

/*
 * Set up the display to start a new multi-character command.
 */
start_mcc()
{
	lower_left();
	clear_eol();
	putc(mcc);
	cmd_col = 1;
}

/*
 * Process a single character of a multi-character command, such as
 * a number, or the pattern of a search command.
 */
	static int
cmd_char(c)
	int c;
{
	if (c == erase_char)
	{
		if (cmd_erase())
			return (1);
	} else if (c == kill_char)
	{
		/* {{ Could do this faster, but who cares? }} */
		while (cmd_erase() == 0)
			;
	} else
	{
		/*
		 * Append the character to the string,
		 * if there is room in the buffer and on the screen.
		 */
		if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3)
		{
			*cp++ = c;
			if (control_char(c))
			{
				putc('^');
				cmd_col++;
				c = carat_char(c);
			}
			putc(c);
			cmd_col++;
		} else
			bell();
	}
	return (0);
}

/*
 * Return the number currently in the command buffer.
 */
	static int
cmd_int()
{
	*cp = '\0';
	return (atoi(cmdbuf));
}

/*
 * Move the cursor to lower left before executing a command.
 * This looks nicer if the command takes a long time before
 * updating the screen.
 */
	static void
cmd_exec()
{
	lower_left();
	flush();
}

/*
 * Display the appropriate prompt.
 */
	static void
prompt()
{
	register char *p;

	if (first_cmd != NULL && *first_cmd != '\0')
		/*
		 * No prompt necessary if commands are from first_cmd
		 * rather than from the user.
		 */
		return;

	/*
	 * Select the proper prompt and display it.
	 */
	p = pr_string();
	if (p == NULL)
		putc(':');
	else
	{
		so_enter();
		puts(p);
		so_exit();
	}
}

/*
 * Get command character.
 * The character normally comes from the keyboard,
 * but may come from the "first_cmd" string.
 */
	static int
getcc()
{
	if (first_cmd == NULL)
		return (getc());

	if (*first_cmd == '\0')
	{
		/*
		 * Reached end of first_cmd input.
		 */
		first_cmd = NULL;
		if (cp > cmdbuf) 
		{
			/*
			 * Command is incomplete, so try to complete it.
			 * There are only two cases:
			 * 1. We have "/string" but no newline.  Add the \n.
			 * 2. We have a number but no command.  Treat as #g.
			 * (This is all pretty hokey.)
			 */
			if (mcc != ':')
				return ('\n'); 
			else
				return ('g');
		}
		return (getc());
	}
	return (*first_cmd++);
}

/*
 * Main command processor.
 * Accept and execute commands until a quit command, then return.
 */
	public void
commands()
{
	register int c;
	register int n;
	register int scroll = 10;
	register int last_mcc = 0;

	mcc = 0;

	for (;;)
	{
		/*
		 * Display prompt and accept a character.
		 */
		psignals();	/* See if any signals need processing */

		if (quit_at_eof && hit_eof > 1)
			/*
			 * After hitting end-of-file for the second time,
			 * automatically advance to the next file.
			 * If there are no more files, quit.
			 */
			next_file(1);

		cmd_reset();
		lower_left();
		clear_eol();
		prompt();
		c = getcc();

	again:
		if (sigs)
			continue;

		if (mcc)
		{
			/*
			 * We are in a multi-character command.  
			 * All chars until newline go into the command buffer.
			 * (Note that mcc == ':' is a special case that
			 *  means a number is being entered.)
			 */
			if (mcc != ':' && (c == '\n' || c == '\r'))
			{
				/*
				 * Execute the command.
				 */
				*cp = '\0';
				cmd_exec();
				if (mcc == 'E')
				{
					char *p;
					/*
					 * Ignore leading spaces 
					 * in the filename.
					 */
					for (p = cmdbuf;  *p == ' ';  p++) ;
					edit(p);
#if SHELL_ESCAPE
				} else if (mcc == '!')
				{
					lower_left();
					putc('\n');
					flush();
					raw_mode(0);
					system(cmdbuf);
					raw_mode(1);
					error("!done");
					first_cmd = "r";	/* Repaint */
#endif
				} else
					search(mcc, cmdbuf, n);
				mcc = 0;
			} else
			{
				if (mcc == ':' && (c < '0' || c > '9') &&
					c != erase_char && c != kill_char)
				{
					/*
					 * This is not part of the number
					 * we were entering.  Process
					 * it as a regular character.
					 */
					mcc = 0;
					goto again;
				}

				/*
				 * Append the char to the command buffer.
				 */
				if (cmd_char(c))
				{
					/* Abort the multi-char command. */
					mcc = 0;
					continue;
				}
				c = getcc();
				goto again;
			}
		} else switch (c)
		{
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			/*
			 * First digit of a number.
			 */
			mcc = ':';
			start_mcc();
			goto again;

		case 'f':
		case ' ':
			/*
			 * Forward one screen.
			 */
			forward(sc_height - 1);
			break;

		case 'b':
			/*
			 * Backward one screen.
			 */
			backward(sc_height - 1);
			break;

		case 'e':
		case 'j':
		case '\r':
		case '\n':
			/*
			 * Forward N (default 1) line.
			 */
			n = cmd_int();
			if (n <= 0)
				n = 1;
			forward(n);
			break;

		case 'y':
		case 'k':
			/*
			 * Backward N (default 1) line.
			 */
			n = cmd_int();
			if (n <= 0)
				n = 1;
			backward(n);
			break;

		case 'd':
			/*
			 * Forward N lines 
			 * (default same as last 'd' or 'u' command).
			 */
			n = cmd_int();
			if (n > 0)
				scroll = n;
			forward(scroll);
			break;

		case 'u':
			/*
			 * Forward N lines 
			 * (default same as last 'd' or 'u' command).
			 */
			n = cmd_int();
			if (n > 0)
				scroll = n;
			backward(scroll);
			break;

		case 'R':
			/*
			 * Flush buffers, then repaint screen.
			 */
			ch_init(0);
			/* Fall thru */
		case 'r':
			/*
			 * Repaint screen.
			 */
			repaint();
			break;

		case 'g':
			/*
			 * Go to line N, default beginning of file.
			 */
			n = cmd_int();
			if (n <= 0)
				n = 1;
			cmd_exec();
			jump_back(n);
			break;

		case 'p':
			/*
			 * Go to a specified percentage into the file.
			 */
			n = cmd_int();
			if (n < 0)
				n = 0;
			if (n > 100)
				n = 100;
			cmd_exec();
			jump_percent(n);
			break;

		case 'G':
			/*
			 * Go to line N, default end of file.
			 */
			n = cmd_int();
			cmd_exec();
			if (n <= 0)
				jump_forw();
			else
				jump_back(n);
			break;

		case '=':
			/*
			 * Print file name, etc.
			 */
			error(eq_message());
			break;
			
		case 'V':
			/*
			 * Print version number, without the "@(#)".
			 */
			error(version+4);
			break;

		case 'q':
			/*
			 * Exit.
			 */
			return;

		case '/':
		case '?':
			/*
			 * Search for a pattern.
			 * Accept chars of the pattern until \n.
			 */
			n = cmd_int();
			if (n <= 0)
				n = 1;
			mcc = last_mcc = c;
			start_mcc();
			c = getcc();
			goto again;

		case 'n':
			/*
			 * Repeat previous search.
			 */
			n = cmd_int();
			if (n <= 0)
				n = 1;
			mcc = last_mcc;
			start_mcc();
			cmd_exec();
			search(mcc, (char *)NULL, n);
			mcc = 0;
			break;

		case 'h':
			/*
			 * Help.
			 */
			help();
			repaint();
			break;

		case 'E':
			/*
			 * Edit a new file.  Get the filename.
			 */
			cmd_reset();
			mcc = 'E';
			start_mcc();
			puts("dit: ");	/* This looks nicer */
			cmd_col += 5;
			c = getcc();
			goto again;
			
#if SHELL_ESCAPE
		case '!':
			/*
			 * Shell escape.
			 */
			cmd_reset();
			mcc = '!';
			start_mcc();
			c = getcc();
			goto again;
#endif

#if EDITOR
		case 'v':
			if (pipe)
			{
				error("Cannot edit standard input");
				break;
			}
			lower_left();
			clear_eol();
			flush();
			raw_mode(0);
			sprintf(cmdbuf, "%s %s", editor, current_file);
			system(cmdbuf);
			raw_mode(1);
			first_cmd = "R";
			break;
#endif

		case 'N':
			/*
			 * Examine next file.
			 */
			n = cmd_int();
			if (n <= 0)
				n = 1;
			next_file(n);
			break;

		case 'P':
			/*
			 * Examine previous file.
			 */
			n = cmd_int();
			if (n <= 0)
				n = 1;
			prev_file(n);
			break;

		case '-':
			/*
			 * Toggle a flag setting.
			 */
			mcc = '-';
			start_mcc();
			c = getcc();
			mcc = 0;
			if (c == erase_char || c == kill_char)
				/* Abort the "-" command. */
				break;
			toggle_option(c);
			break;

		default:
			bell();
			break;
		}
	}
}
SHAR_EOF
cat - << \SHAR_EOF > version.c
/*
 *		less
 *	Copyright (c) 1984,1985  Mark Nudelman
 *
 *	This program may be freely used and/or modified, 
 *	with the following provisions:
 *	1. This notice and the above copyright notice must remain intact.
 *	2. Neither this program, nor any modification of it,
 *	   may not be sold for profit without written consent of the author.
 *
 *	-----------------------------------------------------------------
 *
 *	This program is a paginator similar to "more", 
 *	but allows you to move both forward and backward in the file.  
 *	Commands are based on "more" and "vi".
 *
 *	----------------------- CHANGES ---------------------------------
 *
 *	    Allowed use on standard input		1/29/84   markn
 *	    Added E, N, P commands			2/1/84    markn
 *	    Added '=' command, 'stop' signal handling	4/17/84   markn
 *	    Added line folding				4/20/84   markn
 *	v2: Fixed '=' command to use BOTTOM_PLUS_ONE, 
 *	    instead of TOP, added 'p' & 'v' commands	4/27/84   markn
 *	v3: Added -m and -t options, '-' command	5/3/84    markn
 *	v4: Added LESS environment variable		5/3/84    markn
 *	v5: New comments, fixed '-' command slightly	5/3/84    markn
 *	v6: Added -Q, visual bell			5/15/84   markn
 *	v7: Fixed jump_back(n) bug: n should count real
 *	    lines, not folded lines.  Also allow number
 *	    on G command.				5/24/84   markn
 *	v8: Re-do -q and -Q commands			5/30/84   markn
 *	v9: Added "+<cmd>" argument			9/25/84   markn
 *	v10: Fixed bug in -b<n> argument processing	10/10/84  markn
 *	v11: Made error() ring bell if \n not entered.	10/18/84  markn
 *	-----------------------------------------------------------------
 *	v12: Reorganized signal handling and made
 *	     portable to 4.2bsd.			2/13/85   mark
 *	v13: Reword error message for '-' command.	2/16/85   mark
 *	v14: Added -bf and -bp variants of -b.		2/22/85   mark
 *	v15: Miscellaneous changes.			2/25/85   mark
 *	v16: Added -u flag for backspace processing.	3/13/85   mark
 *	v17: Added j and k commands, 
 *		changed -t default.			4/13/85   mark
 *	v18: Rewrote signal handling code.		4/20/85   mark
 *	v19: Got rid of "verbose" eq_message().		5/2/85    mark
 *	     Made search() scroll in some cases.
 *	v20: Fixed screen.c ioctls for System V.	5/21/85   mark
 *	v21: Fixed some first_cmd bugs.			5/23/85   mark
 *	v22: Added support for no RECOMP nor REGCMP.	5/24/85   mark
 *	v23: Miscellanous changes and prettying up.	5/25/85   mark
 *      v24: Added ti,te terminal init & de-init       6/3/85 Mike Kersenbrock
 *	v25: Added -U flag, standout mode underlining.	6/8/85    mark
 *	v26: Added -M flag.				6/9/85    mark
 *	     Use underline termcap (us) if it exists.
 *	v27: Renamed some variables to make unique in	6/15/85   mark
 *	     6 chars.  Minor fix to -m.
 *	v28: Fixed right margin bug.			6/28/85   mark
 *	v29: Incorporated M.Rose's changes to signal.c	6/28/85   mark
 *	v30: Fixed stupid bug in argument processing.	6/29/85   mark
 *	v31: Added -p flag, changed repaint algorithm.  7/15/85   mark
 *	     Added kludge for magic cookie terminals.
 *	v32: Added cat_file if output not a tty.	7/16/85   mark
 *	v33: Added -e flag and EDITOR.			7/23/85   mark
 *	v34: Added -s flag.				7/26/85   mark
 *	v35: Rewrote option handling; added option.c.	7/27/85   mark
 *	v36: Fixed -e flag to work if not last file.	7/29/85   mark
 *	v37: Added -x flag.				8/10/85   mark
 *	v38: Changed prompting; created prompt.c.	8/19/85   mark
 *	v39: (Not -p) does not initially clear screen.	8/24/85   mark
 *	v40: Added "skipping" indicator in forw().	8/26/85   mark
 *	-----------------------------------------------------------------
 */

char version[] = "@(#) less  version 40";
SHAR_EOF



More information about the Comp.sources.unix mailing list