v16i031: Less, a pager that's more than more, Part02/04

Rich Salz rsalz at uunet.uu.net
Sat Sep 17 04:33:09 AEST 1988


Submitted-by: ctnews!UNIX386!mark
Posting-number: Volume 16, Issue 31
Archive-name: less5/part02

#! /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 \"screen.c\"
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
X/*
X * Routines which deal with the characteristics of the terminal.
X * Uses termcap to be as terminal-independent as possible.
X *
X * {{ Someday this should be rewritten to use curses. }}
X */
X
X#include "less.h"
X#if XENIX
X#include <sys/types.h>
X#include <sys/ioctl.h>
X#endif
X
X#if TERMIO
X#include <termio.h>
X#else
X#include <sgtty.h>
X#endif
X
X#ifdef TIOCGWINSZ
X#include <sys/ioctl.h>
X#else
X/*
X * For the Unix PC (ATT 7300 & 3B1):
X * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
X * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
X */
X#include <sys/signal.h>
X#ifdef SIGPHONE
X#include <sys/window.h>
X#endif
X#endif
X
X/*
X * Strings passed to tputs() to do various terminal functions.
X */
Xstatic char
X	*sc_pad,		/* Pad string */
X	*sc_home,		/* Cursor home */
X	*sc_addline,		/* Add line, scroll down following lines */
X	*sc_lower_left,		/* Cursor to last line, first column */
X	*sc_move,		/* General cursor positioning */
X	*sc_clear,		/* Clear screen */
X	*sc_eol_clear,		/* Clear to end of line */
X	*sc_s_in,		/* Enter standout (highlighted) mode */
X	*sc_s_out,		/* Exit standout mode */
X	*sc_u_in,		/* Enter underline mode */
X	*sc_u_out,		/* Exit underline mode */
X	*sc_b_in,		/* Enter bold mode */
X	*sc_b_out,		/* Exit bold mode */
X	*sc_visual_bell,	/* Visual bell (flash screen) sequence */
X	*sc_backspace,		/* Backspace cursor */
X	*sc_init,		/* Startup terminal initialization */
X	*sc_deinit;		/* Exit terminal de-intialization */
X
Xpublic int auto_wrap;		/* Terminal does \r\n when write past margin */
Xpublic int ignaw;		/* Terminal ignores \n immediately after wrap */
Xpublic int erase_char, kill_char; /* The user's erase and line-kill chars */
Xpublic int sc_width, sc_height;	/* Height & width of screen */
Xpublic int sc_window = -1;	/* window size for forward and backward */
Xpublic int bo_width, be_width;	/* Printing width of boldface sequences */
Xpublic int ul_width, ue_width;	/* Printing width of underline sequences */
Xpublic int so_width, se_width;	/* Printing width of standout sequences */
X
X/*
X * These two variables are sometimes defined in,
X * and needed by, the termcap library.
X * It may be necessary on some systems to declare them extern here.
X */
X/*extern*/ short ospeed;	/* Terminal output baud rate */
X/*extern*/ char PC;		/* Pad character */
X
Xextern int quiet;		/* If VERY_QUIET, use visual bell for bell */
Xextern int know_dumb;		/* Don't complain about a dumb terminal */
Xextern int back_scroll;
Xchar *tgetstr();
Xchar *tgoto();
X
X/*
X * Change terminal to "raw mode", or restore to "normal" mode.
X * "Raw mode" means 
X *	1. An outstanding read will complete on receipt of a single keystroke.
X *	2. Input is not echoed.  
X *	3. On output, \n is mapped to \r\n.
X *	4. \t is NOT expanded into spaces.
X *	5. Signal-causing characters such as ctrl-C (interrupt),
X *	   etc. are NOT disabled.
X * It doesn't matter whether an input \n is mapped to \r, or vice versa.
X */
X	public void
Xraw_mode(on)
X	int on;
X{
X#if TERMIO
X	struct termio s;
X	static struct termio save_term;
X
X	if (on)
X	{
X		/*
X		 * Get terminal modes.
X		 */
X		ioctl(2, TCGETA, &s);
X
X		/*
X		 * Save modes and set certain variables dependent on modes.
X		 */
X		save_term = s;
X		ospeed = s.c_cflag & CBAUD;
X		erase_char = s.c_cc[VERASE];
X		kill_char = s.c_cc[VKILL];
X
X		/*
X		 * Set the modes to the way we want them.
X		 */
X		s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
X		s.c_oflag |=  (OPOST|ONLCR|TAB3);
X		s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
X		s.c_cc[VMIN] = 1;
X		s.c_cc[VTIME] = 0;
X	} else
X	{
X		/*
X		 * Restore saved modes.
X		 */
X		s = save_term;
X	}
X	ioctl(2, TCSETAW, &s);
X#else
X	struct sgttyb s;
X	static struct sgttyb save_term;
X
X	if (on)
X	{
X		/*
X		 * Get terminal modes.
X		 */
X		ioctl(2, TIOCGETP, &s);
X
X		/*
X		 * Save modes and set certain variables dependent on modes.
X		 */
X		save_term = s;
X		ospeed = s.sg_ospeed;
X		erase_char = s.sg_erase;
X		kill_char = s.sg_kill;
X
X		/*
X		 * Set the modes to the way we want them.
X		 */
X		s.sg_flags |= CBREAK;
X		s.sg_flags &= ~(ECHO|XTABS);
X	} else
X	{
X		/*
X		 * Restore saved modes.
X		 */
X		s = save_term;
X	}
X	ioctl(2, TIOCSETN, &s);
X#endif
X}
X
X	static void
Xcannot(s)
X	char *s;
X{
X	char message[100];
X
X	if (know_dumb)
X		/* 
X		 * He knows he has a dumb terminal, so don't tell him. 
X		 */
X		return;
X
X	sprintf(message, "WARNING: terminal cannot \"%s\"", s);
X	error(message);
X}
X
X/*
X * Get terminal capabilities via termcap.
X */
X	public void
Xget_term()
X{
X	char termbuf[2048];
X	char *sp;
X	char *term;
X	int hard;
X#ifdef TIOCGWINSZ
X	struct winsize w;
X#else
X#ifdef WIOCGETD
X	struct uwdata w;
X#endif
X#endif
X	static char sbuf[1024];
X
X	char *getenv();
X
X	/*
X	 * Find out what kind of terminal this is.
X	 */
X 	if ((term = getenv("TERM")) == NULL)
X 		term = "unknown";
X 	if (tgetent(termbuf, term) <= 0)
X 		strcpy(termbuf, "dumb:co#80:hc:");
X
X	/*
X	 * Get size of the screen.
X	 */
X#ifdef TIOCGWINSZ
X	if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
X		sc_height = w.ws_row;
X	else
X#else
X#ifdef WIOCGETD
X	if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
X		sc_height = w.uw_height/w.uw_vs;
X	else
X#endif
X#endif
X 		sc_height = tgetnum("li");
X 	hard = (sc_height < 0 || tgetflag("hc"));
X	if (hard)
X	{
X		/* Oh no, this is a hardcopy terminal. */
X		sc_height = 24;
X	}
X
X#ifdef TIOCGWINSZ
X 	if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
X		sc_width = w.ws_col;
X	else
X#ifdef WIOCGETD
X	if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
X		sc_width = w.uw_width/w.uw_hs;
X	else
X#endif
X#endif
X 		sc_width = tgetnum("co");
X 	if (sc_width < 0)
X  		sc_width = 80;
X
X	auto_wrap = tgetflag("am");
X	ignaw = tgetflag("xn");
X
X	/*
X	 * Assumes termcap variable "sg" is the printing width of
X	 * the standout sequence, the end standout sequence,
X	 * the underline sequence, the end underline sequence,
X	 * the boldface sequence, and the end boldface sequence.
X	 */
X	if ((so_width = tgetnum("sg")) < 0)
X		so_width = 0;
X	be_width = bo_width = ue_width = ul_width = se_width = so_width;
X
X	/*
X	 * Get various string-valued capabilities.
X	 */
X	sp = sbuf;
X
X	sc_pad = tgetstr("pc", &sp);
X	if (sc_pad != NULL)
X		PC = *sc_pad;
X
X	sc_init = tgetstr("ti", &sp);
X	if (sc_init == NULL)
X		sc_init = "";
X
X	sc_deinit= tgetstr("te", &sp);
X	if (sc_deinit == NULL)
X		sc_deinit = "";
X
X	sc_eol_clear = tgetstr("ce", &sp);
X	if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
X	{
X		cannot("clear to end of line");
X		sc_eol_clear = "";
X	}
X
X	sc_clear = tgetstr("cl", &sp);
X	if (hard || sc_clear == NULL || *sc_clear == '\0')
X	{
X		cannot("clear screen");
X		sc_clear = "\n\n";
X	}
X
X	sc_move = tgetstr("cm", &sp);
X	if (hard || sc_move == NULL || *sc_move == '\0')
X	{
X		/*
X		 * This is not an error here, because we don't 
X		 * always need sc_move.
X		 * We need it only if we don't have home or lower-left.
X		 */
X		sc_move = "";
X	}
X
X	sc_s_in = tgetstr("so", &sp);
X	if (hard || sc_s_in == NULL)
X		sc_s_in = "";
X
X	sc_s_out = tgetstr("se", &sp);
X	if (hard || sc_s_out == NULL)
X		sc_s_out = "";
X
X	sc_u_in = tgetstr("us", &sp);
X	if (hard || sc_u_in == NULL)
X		sc_u_in = sc_s_in;
X
X	sc_u_out = tgetstr("ue", &sp);
X	if (hard || sc_u_out == NULL)
X		sc_u_out = sc_s_out;
X
X	sc_b_in = tgetstr("md", &sp);
X	if (hard || sc_b_in == NULL)
X	{
X		sc_b_in = sc_s_in;
X		sc_b_out = sc_s_out;
X	} else
X	{
X		sc_b_out = tgetstr("me", &sp);
X		if (hard || sc_b_out == NULL)
X			sc_b_out = "";
X	}
X
X	sc_visual_bell = tgetstr("vb", &sp);
X	if (hard || sc_visual_bell == NULL)
X		sc_visual_bell = "";
X
X	sc_home = tgetstr("ho", &sp);
X	if (hard || sc_home == NULL || *sc_home == '\0')
X	{
X		if (*sc_move == '\0')
X		{
X			cannot("home cursor");
X			/*
X			 * This last resort for sc_home is supposed to
X			 * be an up-arrow suggesting moving to the 
X			 * top of the "virtual screen". (The one in
X			 * your imagination as you try to use this on
X			 * a hard copy terminal.)
X			 */
X			sc_home = "|\b^";		
X		} else
X		{
X			/* 
X			 * No "home" string,
X			 * but we can use "move(0,0)".
X			 */
X			strcpy(sp, tgoto(sc_move, 0, 0));
X			sc_home = sp;
X			sp += strlen(sp) + 1;
X		}
X	}
X
X	sc_lower_left = tgetstr("ll", &sp);
X	if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
X	{
X		if (*sc_move == '\0')
X		{
X			cannot("move cursor to lower left of screen");
X			sc_lower_left = "\r";
X		} else
X		{
X			/*
X			 * No "lower-left" string, 
X			 * but we can use "move(0,last-line)".
X			 */
X			strcpy(sp, tgoto(sc_move, 0, sc_height-1));
X			sc_lower_left = sp;
X			sp += strlen(sp) + 1;
X		}
X	}
X
X	/*
X	 * To add a line at top of screen and scroll the display down,
X	 * we use "al" (add line) or "sr" (scroll reverse).
X	 */
X	if ((sc_addline = tgetstr("al", &sp)) == NULL || 
X		 *sc_addline == '\0')
X		sc_addline = tgetstr("sr", &sp);
X
X	if (hard || sc_addline == NULL || *sc_addline == '\0')
X	{
X		cannot("scroll backwards");
X		sc_addline = "";
X		/* Force repaint on any backward movement */
X		back_scroll = 0;
X	}
X
X	if (tgetflag("bs"))
X		sc_backspace = "\b";
X	else
X	{
X		sc_backspace = tgetstr("bc", &sp);
X		if (sc_backspace == NULL || *sc_backspace == '\0')
X			sc_backspace = "\b";
X	}
X}
X
X
X/*
X * Below are the functions which perform all the 
X * terminal-specific screen manipulation.
X */
X
X
X/*
X * Initialize terminal
X */
X	public void
Xinit()
X{
X	tputs(sc_init, sc_height, putchr);
X}
X
X/*
X * Deinitialize terminal
X */
X	public void
Xdeinit()
X{
X	tputs(sc_deinit, sc_height, putchr);
X}
X
X/*
X * Home cursor (move to upper left corner of screen).
X */
X	public void
Xhome()
X{
X	tputs(sc_home, 1, putchr);
X}
X
X/*
X * Add a blank line (called with cursor at home).
X * Should scroll the display down.
X */
X	public void
Xadd_line()
X{
X	tputs(sc_addline, sc_height, putchr);
X}
X
X/*
X * Move cursor to lower left corner of screen.
X */
X	public void
Xlower_left()
X{
X	tputs(sc_lower_left, 1, putchr);
X}
X
X/*
X * Ring the terminal bell.
X */
X	public void
Xbell()
X{
X	if (quiet == VERY_QUIET)
X		vbell();
X	else
X		putchr('\7');
X}
X
X/*
X * Output the "visual bell", if there is one.
X */
X	public void
Xvbell()
X{
X	if (*sc_visual_bell == '\0')
X		return;
X	tputs(sc_visual_bell, sc_height, putchr);
X}
X
X/*
X * Clear the screen.
X */
X	public void
Xclear()
X{
X	tputs(sc_clear, sc_height, putchr);
X}
X
X/*
X * Clear from the cursor to the end of the cursor's line.
X * {{ This must not move the cursor. }}
X */
X	public void
Xclear_eol()
X{
X	tputs(sc_eol_clear, 1, putchr);
X}
X
X/*
X * Begin "standout" (bold, underline, or whatever).
X */
X	public void
Xso_enter()
X{
X	tputs(sc_s_in, 1, putchr);
X}
X
X/*
X * End "standout".
X */
X	public void
Xso_exit()
X{
X	tputs(sc_s_out, 1, putchr);
X}
X
X/*
X * Begin "underline" (hopefully real underlining, 
X * otherwise whatever the terminal provides).
X */
X	public void
Xul_enter()
X{
X	tputs(sc_u_in, 1, putchr);
X}
X
X/*
X * End "underline".
X */
X	public void
Xul_exit()
X{
X	tputs(sc_u_out, 1, putchr);
X}
X
X/*
X * Begin "bold"
X */
X	public void
Xbo_enter()
X{
X	tputs(sc_b_in, 1, putchr);
X}
X
X/*
X * End "bold".
X */
X	public void
Xbo_exit()
X{
X	tputs(sc_b_out, 1, putchr);
X}
X
X/*
X * Erase the character to the left of the cursor 
X * and move the cursor left.
X */
X	public void
Xbackspace()
X{
X	/* 
X	 * Try to erase the previous character by overstriking with a space.
X	 */
X	tputs(sc_backspace, 1, putchr);
X	putchr(' ');
X	tputs(sc_backspace, 1, putchr);
X}
X
X/*
X * Output a plain backspace, without erasing the previous char.
X */
X	public void
Xputbs()
X{
X	tputs(sc_backspace, 1, putchr);
X}
END_OF_FILE
echo shar: Extracting \"prompt.c\"
sed "s/^X//" >'prompt.c' <<'END_OF_FILE'
X/*
X * Prompting and other messages.
X * There are three flavors of prompts, SHORT, MEDIUM and LONG,
X * selected by the -m/-M options.
X * There is also the "equals message", printed by the = command.
X * A prompt is a message composed of various pieces, such as the 
X * name of the file being viewed, the percentage into the file, etc.
X */
X
X#include "less.h"
X#include "position.h"
X
Xextern int pr_type;
Xextern int ispipe;
Xextern int hit_eof;
Xextern int new_file;
Xextern int sc_width;
Xextern int so_width, se_width;
Xextern char *current_file;
Xextern int ac;
Xextern char **av;
Xextern int curr_ac;
Xextern int linenums;
X
X/*
X * Prototypes for the three flavors of prompts.
X * These strings are expanded by pr_expand().
X */
Xstatic char s_proto[] =
X  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
Xstatic char m_proto[] =
X  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
Xstatic char M_proto[] =
X  "?f%f .?n?m(file %i of %m) ..?ltline %lt :byte %bB?s/%s ..?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
Xstatic char e_proto[] =
X  "?f%f .?m(file %i of %m) .?ltline %lt .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
X
Xchar *prproto[3];
Xchar *eqproto = e_proto;
X
Xstatic char message[250];
Xstatic char *mp;
X
X/*
X * Initialize the prompt prototype strings.
X */
X	public void
Xinit_prompt()
X{
X	prproto[0] = save(s_proto);
X	prproto[1] = save(m_proto);
X	prproto[2] = save(M_proto);
X	eqproto = save(e_proto);
X}
X
X/*
X * Set the message pointer to the end of the message string.
X */
X	static void
Xsetmp()
X{
X	while (*mp != '\0')
X		mp++;
X}
X
X/*
X * Append a POSITION (as a decimal integer) to the end of the message.
X */
X	static void
Xap_pos(pos)
X	POSITION pos;
X{
X	sprintf(mp, "%ld", (long)pos);
X	setmp();
X}
X
X/*
X * Append an integer to the end of the message.
X */
X	static void
Xap_int(n)
X	int n;
X{
X	sprintf(mp, "%d", n);
X	setmp();
X}
X
X/*
X * Append a question mark to the end of the message.
X */
X	static void
Xap_quest()
X{
X	*mp++ = '?';
X}
X
X/*
X * Return the "current" byte offset in the file.
X */
X	static POSITION
Xcurr_byte(where)
X	int where;
X{
X	POSITION pos;
X
X	pos = position(where);
X	if (pos == NULL_POSITION)
X		pos = ch_length();
X	return (pos);
X}
X
X/*
X * Return the value of a prototype conditional.
X * A prototype string may include conditionals which consist of a 
X * question mark followed by a single letter.
X * Here we decode that letter and return the appropriate boolean value.
X */
X	static int
Xcond(c, where)
X	char c;
X	int where;
X{
X	switch (c)
X	{
X	case 'a':	/* Anything in the message yet? */
X		return (mp > message);
X	case 'b':	/* Current byte offset known? */
X		return (curr_byte(where) != NULL_POSITION);
X	case 'e':	/* At end of file? */
X		return (hit_eof);
X	case 'f':	/* Filename known? */
X		return (!ispipe);
X	case 'l':	/* Line number known? */
X		return (linenums);
X	case 'm':	/* More than one file? */
X		return (ac > 1);
X	case 'n':	/* First prompt in a new file? */
X		return (new_file);
X	case 'p':	/* Percent into file known? */
X		return (curr_byte(where) != NULL_POSITION && 
X				ch_length() > 0);
X	case 's':	/* Size of file known? */
X		return (ch_length() != NULL_POSITION);
X	case 'x':	/* Is there a "next" file? */
X		return (curr_ac + 1 < ac);
X	}
X	return (0);
X}
X
X/*
X * Decode a "percent" prototype character.
X * A prototype string may include various "percent" escapes;
X * that is, a percent sign followed by a single letter.
X * Here we decode that letter and take the appropriate action,
X * usually by appending something to the message being built.
X */
X	static void
Xprotochar(c, where)
X	int c;
X	int where;
X{
X	POSITION pos;
X	POSITION len;
X	int n;
X
X	switch (c)
X	{
X	case 'b':	/* Current byte offset */
X		pos = curr_byte(where);
X		if (pos != NULL_POSITION)
X			ap_pos(pos);
X		else
X			ap_quest();
X		break;
X	case 'f':	/* File name */
X		strtcpy(mp, current_file,
X			(unsigned int)(&message[sizeof(message)] - mp));
X		setmp();
X		break;
X	case 'i':	/* Index into list of files */
X		ap_int(curr_ac + 1);
X		break;
X	case 'l':	/* Current line number */
X		n = currline(where);
X		if (n != 0)
X			ap_int(n);
X		else
X			ap_quest();
X		break;
X	case 'm':	/* Number of files */
X		ap_int(ac);
X		break;
X	case 'p':	/* Percent into file */
X		pos = curr_byte(where);
X		len = ch_length();
X		if (pos != NULL_POSITION && len > 0)
X			ap_int((int)(100*pos / len));
X		else
X			ap_quest();
X		break;
X	case 's':	/* Size of file */
X		len = ch_length();
X		if (len != NULL_POSITION)
X			ap_pos(len);
X		else
X			ap_quest();
X		break;
X	case 't':	/* Truncate trailing spaces in the message */
X		while (mp > message && mp[-1] == ' ')
X			mp--;
X		break;
X	case 'x':	/* Name of next file */
X		if (curr_ac + 1 < ac)
X		{
X			strtcpy(mp, av[curr_ac+1],
X				(unsigned int)(&message[sizeof(message)] - mp));
X			setmp();
X		} else
X			ap_quest();
X		break;
X	}
X}
X
X/*
X * Skip a false conditional.
X * When a false condition is found (either a false IF or the ELSE part 
X * of a true IF), this routine scans the prototype string to decide
X * where to resume parsing the string.
X * We must keep track of nested IFs and skip them properly.
X */
X	static char *
Xskipcond(p)
X	register char *p;
X{
X	register int iflevel = 1;
X
X	for (;;) switch (*++p)
X	{
X	case '?':
X		/*
X		 * Start of a nested IF.
X		 */
X		iflevel++;
X		break;
X	case ':':
X		/*
X		 * Else.
X		 * If this matches the IF we came in here with,
X		 * then we're done.
X		 */
X		if (iflevel == 1)
X			return (p);
X		break;
X	case '.':
X		/*
X		 * Endif.
X		 * If this matches the IF we came in here with,
X		 * then we're done.
X		 */
X		if (--iflevel == 0)
X			return (p);
X		break;
X	case '\\':
X		/*
X		 * Backslash escapes the next character.
X		 */
X		++p;
X		break;
X	case '\0':
X		/*
X		 * Whoops.  Hit end of string.
X		 * This is a malformed conditional, but just treat it
X		 * as if all active conditionals ends here.
X		 */
X		return (p-1);
X	}
X	/*NOTREACHED*/
X}
X
X	static char *
Xwherechar(p, wp)
X	char *p;
X	int *wp;
X{
X	int c;
X
X	switch (c = *p)
X	{
X	case 'b': case 'l': case 'p':
X		switch (*++p)
X		{
X		case 't':   *wp = TOP;			break;
X		case 'm':   *wp = MIDDLE;		break;
X		case 'b':   *wp = BOTTOM;		break;
X		case 'B':   *wp = BOTTOM_PLUS_ONE;	break;
X		default:    *wp = TOP;			break;
X		}
X	}
X	return (p);
X}
X
X/*
X * Construct a message based on a prototype string.
X */
X	static char *
Xpr_expand(proto, maxwidth)
X	char *proto;
X	int maxwidth;
X{
X	register char *p;
X	register int c;
X	int where;
X
X	mp = message;
X
X	if (*proto == '\0')
X		return ("");
X
X	for (p = proto;  *p != '\0';  p++)
X	{
X		switch (*p)
X		{
X		default:	/* Just put the character in the message */
X			*mp++ = *p;
X			break;
X		case '\\':	/* Backslash escapes the next character */
X			p++;
X			*mp++ = *p;
X			break;
X		case '?':	/* Conditional (IF) */
X			if ((c = *++p) == '\0')
X				--p;
X			else
X			{
X				p = wherechar(p, &where);
X				if (!cond(c, where))
X					p = skipcond(p);
X			}
X			break;
X		case ':':	/* ELSE */
X			p = skipcond(p);
X			break;
X		case '.':	/* ENDIF */
X			break;
X		case '%':	/* Percent escape */
X			if ((c = *++p) == '\0')
X				--p;
X			else
X			{
X				p = wherechar(p, &where);
X				protochar(c, where);
X			}
X			break;
X		}
X	}
X
X	new_file = 0;
X	if (mp == message)
X		return (NULL);
X	*mp = '\0';
X	if (maxwidth > 0 && mp >= message + maxwidth)
X	{
X		/*
X		 * Message is too long.
X		 * Return just the final portion of it.
X		 */
X		return (mp - maxwidth);
X	}
X	return (message);
X}
X
X/*
X * Return a message suitable for printing by the "=" command.
X */
X	public char *
Xeq_message()
X{
X	return (pr_expand(eqproto, 0));
X}
X
X/*
X * Return a prompt.
X * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
X * If we can't come up with an appropriate prompt, return NULL
X * and the caller will prompt with a colon.
X */
X	public char *
Xpr_string()
X{
X	return (pr_expand(prproto[pr_type], sc_width-so_width-se_width-2));
X}
END_OF_FILE
echo shar: Extracting \"line.c\"
sed "s/^X//" >'line.c' <<'END_OF_FILE'
X/*
X * Routines to manipulate the "line buffer".
X * The line buffer holds a line of output as it is being built
X * in preparation for output to the screen.
X * We keep track of the PRINTABLE length of the line as it is being built.
X */
X
X#include "less.h"
X
Xstatic char linebuf[1024];	/* Buffer which holds the current output line */
Xstatic char *curr;		/* Pointer into linebuf */
Xstatic int column;		/* Printable length, accounting for
X				   backspaces, etc. */
X/*
X * A ridiculously complex state machine takes care of backspaces 
X * when in BS_SPECIAL mode.  The complexity arises from the attempt
X * to deal with all cases, especially involving long lines with underlining,
X * boldfacing or whatever.  There are still some cases which will break it.
X *
X * There are four states:
X *	LN_NORMAL is the normal state (not in underline mode).
X *	LN_UNDERLINE means we are in underline mode.  We expect to get
X *		either a sequence like "_\bX" or "X\b_" to continue
X *		underline mode, or anything else to end underline mode.
X *	LN_BOLDFACE means we are in boldface mode.  We expect to get sequences
X *		like "X\bX\b...X\bX" to continue boldface mode, or anything
X *		else to end boldface mode.
X *	LN_UL_X means we are one character after LN_UNDERLINE
X *		(we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
X *	LN_UL_XB means we are one character after LN_UL_X 
X *		(we have gotten the backspace in "_\bX" or "X\b_";
X *		we expect one more ordinary character, 
X *		which will put us back in state LN_UNDERLINE).
X *	LN_BO_X means we are one character after LN_BOLDFACE
X *		(we have gotten the 'X' in "X\bX").
X *	LN_BO_XB means we are one character after LN_BO_X
X *		(we have gotten the backspace in "X\bX";
X *		we expect one more 'X' which will put us back
X *		in LN_BOLDFACE).
X */
Xstatic int ln_state;		/* Currently in normal/underline/bold/etc mode? */
X#define	LN_NORMAL	0	/* Not in underline, boldface or whatever mode */
X#define	LN_UNDERLINE	1	/* In underline, need next char */
X#define	LN_UL_X		2	/* In underline, got char, need \b */
X#define	LN_UL_XB	3	/* In underline, got char & \b, need one more */
X#define	LN_BOLDFACE	4	/* In boldface, need next char */
X#define	LN_BO_X		5	/* In boldface, got char, need \b */
X#define	LN_BO_XB	6	/* In boldface, got char & \b, need same char */
X
Xpublic char *line;		/* Pointer to the current line.
X				   Usually points to linebuf. */
X
Xextern int bs_mode;
Xextern int tabstop;
Xextern int bo_width, be_width;
Xextern int ul_width, ue_width;
Xextern int sc_width, sc_height;
X
X/*
X * Rewind the line buffer.
X */
X	public void
Xprewind()
X{
X	line = curr = linebuf;
X	ln_state = LN_NORMAL;
X	column = 0;
X}
X
X/*
X * Append a character to the line buffer.
X * Expand tabs into spaces, handle underlining, boldfacing, etc.
X * Returns 0 if ok, 1 if couldn't fit in buffer.
X */
X
X#define	NEW_COLUMN(newcol)	if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
X					return (1); else column = (newcol)
X
X	public int
Xpappend(c)
X	int c;
X{
X	if (c == '\0')
X	{
X		/*
X		 * Terminate any special modes, if necessary.
X		 * Append a '\0' to the end of the line.
X		 */
X		switch (ln_state)
X		{
X		case LN_UL_X:
X			curr[0] = curr[-1];
X			curr[-1] = UE_CHAR;
X			curr++;
X			break;
X		case LN_BO_X:
X			curr[0] = curr[-1];
X			curr[-1] = BE_CHAR;
X			curr++;
X			break;
X		case LN_UL_XB:
X		case LN_UNDERLINE:
X			*curr++ = UE_CHAR;
X			break;
X		case LN_BO_XB:
X		case LN_BOLDFACE:
X			*curr++ = BE_CHAR;
X			break;
X		}
X		ln_state = LN_NORMAL;
X		*curr = '\0';
X		return (0);
X	}
X
X	if (curr > linebuf + sizeof(linebuf) - 12)
X		/*
X		 * Almost out of room in the line buffer.
X		 * Don't take any chances.
X		 * {{ Linebuf is supposed to be big enough that this
X		 *    will never happen, but may need to be made 
X		 *    bigger for wide screens or lots of backspaces. }}
X		 */
X		return (1);
X
X	if (bs_mode == BS_SPECIAL)
X	{
X		/*
X		 * Advance the state machine.
X		 */
X		switch (ln_state)
X		{
X		case LN_NORMAL:
X			if (curr <= linebuf + 1 || curr[-1] != '\b')
X				break;
X
X			if (c == curr[-2])
X				goto enter_boldface;
X			if (c == '_' || curr[-2] == '_')
X				goto enter_underline;
X			curr -= 2;
X			break;
X
Xenter_boldface:
X			/*
X			 * We have "X\bX" (including the current char).
X			 * Switch into boldface mode.
X			 */
X			if (column + bo_width + be_width + 1 >= sc_width)
X				/*
X				 * Not enough room left on the screen to 
X				 * enter and exit boldface mode.
X				 */
X				return (1);
X
X			if (bo_width > 0 && 
X			    curr > linebuf + 2 && curr[-3] == ' ')
X			{
X				/*
X				 * Special case for magic cookie terminals:
X				 * if the previous char was a space, replace 
X				 * it with the "enter boldface" sequence.
X				 */
X				curr[-3] = BO_CHAR;
X				column += bo_width-1;
X			} else
X			{
X				curr[-1] = curr[-2];
X				curr[-2] = BO_CHAR;
X				column += bo_width;
X				curr++;
X			}
X			goto ln_bo_xb_case;
X
Xenter_underline:
X			/*
X			 * We have either "_\bX" or "X\b_" (including
X			 * the current char).  Switch into underline mode.
X			 */
X			if (column + ul_width + ue_width + 1 >= sc_width)
X				/*
X				 * Not enough room left on the screen to 
X				 * enter and exit underline mode.
X				 */
X				return (1);
X
X			if (ul_width > 0 && 
X			    curr > linebuf + 2 && curr[-3] == ' ')
X			{
X				/*
X				 * Special case for magic cookie terminals:
X				 * if the previous char was a space, replace 
X				 * it with the "enter underline" sequence.
X				 */
X				curr[-3] = UL_CHAR;
X				column += ul_width-1;
X			} else
X			{
X				curr[-1] = curr[-2];
X				curr[-2] = UL_CHAR;
X				column += ul_width;
X				curr++;
X			}
X			goto ln_ul_xb_case;
X			/*NOTREACHED*/
X		case LN_UL_XB:
X			/*
X			 * Termination of a sequence "_\bX" or "X\b_".
X			 */
X			if (c != '_' && curr[-2] != '_' && c == curr[-2])
X			{
X				/*
X				 * We seem to have run on from underlining
X				 * into boldfacing - this is a nasty fix, but
X				 * until this whole routine is rewritten as a
X				 * real DFA, ...  well ...
X				 */
X				curr[0] = curr[-2];
X				curr[-2] = UE_CHAR;
X				curr[-1] = BO_CHAR;
X				curr += 2; /* char & non-existent backspace */
X				ln_state = LN_BO_XB;
X				goto ln_bo_xb_case;
X			}
Xln_ul_xb_case:
X			if (c == '_')
X				c = curr[-2];
X			curr -= 2;
X			ln_state = LN_UNDERLINE;
X			break;
X		case LN_BO_XB:
X			/*
X			 * Termination of a sequnce "X\bX".
X			 */
X			if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
X			{
X				/*
X				 * We seem to have run on from
X				 * boldfacing into underlining.
X				 */
X				curr[0] = curr[-2];
X				curr[-2] = BE_CHAR;
X				curr[-1] = UL_CHAR;
X				curr += 2; /* char & non-existent backspace */
X				ln_state = LN_UL_XB;
X				goto ln_ul_xb_case;
X			}
Xln_bo_xb_case:
X			curr -= 2;
X			ln_state = LN_BOLDFACE;
X			break;
X		case LN_UNDERLINE:
X			if (column + ue_width + bo_width + 1 + be_width >= sc_width)
X				/*
X				 * We have just barely enough room to 
X				 * exit underline mode and handle a possible
X				 * underline/boldface run on mixup.
X				 */
X				return (1);
X			ln_state = LN_UL_X;
X			break;
X		case LN_BOLDFACE:
X			if (c == '\b')
X			{
X				ln_state = LN_BO_XB;
X				break;
X			}
X			if (column + be_width + ul_width + 1 + ue_width >= sc_width)
X				/*
X				 * We have just barely enough room to 
X				 * exit underline mode and handle a possible
X				 * underline/boldface run on mixup.
X				 */
X				return (1);
X			ln_state = LN_BO_X;
X			break;
X		case LN_UL_X:
X			if (c == '\b')
X				ln_state = LN_UL_XB;
X			else
X			{
X				/*
X				 * Exit underline mode.
X				 * We have to shuffle the chars a bit
X				 * to make this work.
X				 */
X				curr[0] = curr[-1];
X				curr[-1] = UE_CHAR;
X				column += ue_width;
X				if (ue_width > 0 && curr[0] == ' ')
X					/*
X					 * Another special case for magic
X					 * cookie terminals: if the next
X					 * char is a space, replace it
X					 * with the "exit underline" sequence.
X					 */
X					column--;
X				else
X					curr++;
X				ln_state = LN_NORMAL;
X			} 
X			break;
X		case LN_BO_X:
X			if (c == '\b')
X				ln_state = LN_BO_XB;
X			else
X			{
X				/*
X				 * Exit boldface mode.
X				 * We have to shuffle the chars a bit
X				 * to make this work.
X				 */
X				curr[0] = curr[-1];
X				curr[-1] = BE_CHAR;
X				column += be_width;
X				if (be_width > 0 && curr[0] == ' ')
X					/*
X					 * Another special case for magic
X					 * cookie terminals: if the next
X					 * char is a space, replace it
X					 * with the "exit boldface" sequence.
X					 */
X					column--;
X				else
X					curr++;
X				ln_state = LN_NORMAL;
X			} 
X			break;
X		}
X	}
X	
X	if (c == '\t') 
X	{
X		/*
X		 * Expand a tab into spaces.
X		 */
X		do
X		{
X			NEW_COLUMN(column+1);
X		} while ((column % tabstop) != 0);
X		*curr++ = '\t';
X		return (0);
X	}
X
X	if (c == '\b')
X	{
X		if (bs_mode == BS_CONTROL)
X		{
X			/*
X			 * Treat backspace as a control char: output "^H".
X			 */
X			NEW_COLUMN(column+2);
X			*curr++ = ('H' | 0200);
X		} else
X		{
X			/*
X			 * Output a real backspace.
X			 */
X			column--;
X			*curr++ = '\b';
X		}
X		return (0);
X	} 
X
X	if (control_char(c))
X	{
X		/*
X		 * Put a "^X" into the buffer.
X		 * The 0200 bit is used to tell put_line() to prefix
X		 * the char with a ^.  We don't actually put the ^
X		 * in the buffer because we sometimes need to move
X		 * chars around, and such movement might separate 
X		 * the ^ from its following character.
X		 * {{ This should be redone so that we can use an
X		 *    8 bit (e.g. international) character set. }}
X		 */
X		NEW_COLUMN(column+2);
X		*curr++ = (carat_char(c) | 0200);
X		return (0);
X	}
X
X	/*
X	 * Ordinary character.  Just put it in the buffer.
X	 */
X	NEW_COLUMN(column+1);
X	*curr++ = c;
X	return (0);
X}
X
X/*
X * Analogous to forw_line(), but deals with "raw lines":
X * lines which are not split for screen width.
X * {{ This is supposed to be more efficient than forw_line(). }}
X */
X	public POSITION
Xforw_raw_line(curr_pos)
X	POSITION curr_pos;
X{
X	register char *p;
X	register int c;
X	POSITION new_pos;
X
X	if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
X		(c = ch_forw_get()) == EOI)
X		return (NULL_POSITION);
X
X	p = linebuf;
X
X	for (;;)
X	{
X		if (c == '\n' || c == EOI)
X		{
X			new_pos = ch_tell();
X			break;
X		}
X		if (p >= &linebuf[sizeof(linebuf)-1])
X		{
X			/*
X			 * Overflowed the input buffer.
X			 * Pretend the line ended here.
X			 * {{ The line buffer is supposed to be big
X			 *    enough that this never happens. }}
X			 */
X			new_pos = ch_tell() - 1;
X			break;
X		}
X		*p++ = c;
X		c = ch_forw_get();
X	}
X	*p = '\0';
X	line = linebuf;
X	return (new_pos);
X}
X
X/*
X * Analogous to back_line(), but deals with "raw lines".
X * {{ This is supposed to be more efficient than back_line(). }}
X */
X	public POSITION
Xback_raw_line(curr_pos)
X	POSITION curr_pos;
X{
X	register char *p;
X	register int c;
X	POSITION new_pos;
X
X	if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
X		ch_seek(curr_pos-1))
X		return (NULL_POSITION);
X
X	p = &linebuf[sizeof(linebuf)];
X	*--p = '\0';
X
X	for (;;)
X	{
X		c = ch_back_get();
X		if (c == '\n')
X		{
X			/*
X			 * This is the newline ending the previous line.
X			 * We have hit the beginning of the line.
X			 */
X			new_pos = ch_tell() + 1;
X			break;
X		}
X		if (c == EOI)
X		{
X			/*
X			 * We have hit the beginning of the file.
X			 * This must be the first line in the file.
X			 * This must, of course, be the beginning of the line.
X			 */
X			new_pos = (POSITION)0;
X			break;
X		}
X		if (p <= linebuf)
X		{
X			/*
X			 * Overflowed the input buffer.
X			 * Pretend the line ended here.
X			 */
X			new_pos = ch_tell() + 1;
X			break;
X		}
X		*--p = c;
X	}
X	line = p;
X	return (new_pos);
X}
END_OF_FILE
echo shar: Extracting \"signal.c\"
sed "s/^X//" >'signal.c' <<'END_OF_FILE'
X/*
X * Routines dealing with signals.
X *
X * A signal usually merely causes a bit to be set in the "signals" word.
X * At some convenient time, the mainline code checks to see if any
X * signals need processing by calling psignal().
X * If we happen to be reading from a file [in iread()] at the time
X * the signal is received, we call intread to interrupt the iread.
X */
X
X#include "less.h"
X#include <signal.h>
X
X/*
X * "sigs" contains bits indicating signals which need to be processed.
X */
Xpublic int sigs;
X
X#define	S_INTERRUPT	01
X#ifdef SIGTSTP
X#define	S_STOP		02
X#endif
X#if defined(SIGWINCH) || defined(SIGWIND)
X#define S_WINCH		04
X#endif
X
Xextern int sc_width, sc_height;
Xextern int screen_trashed;
Xextern int lnloop;
Xextern int linenums;
Xextern int scroll;
Xextern int reading;
X
X/*
X * Interrupt signal handler.
X */
X	static HANDLER
Xinterrupt()
X{
X	SIGNAL(SIGINT, interrupt);
X	sigs |= S_INTERRUPT;
X	if (reading)
X		intread();
X}
X
X#ifdef SIGTSTP
X/*
X * "Stop" (^Z) signal handler.
X */
X	static HANDLER
Xstop()
X{
X	SIGNAL(SIGTSTP, stop);
X	sigs |= S_STOP;
X	if (reading)
X		intread();
X}
X#endif
X
X#ifdef SIGWINCH
X/*
X * "Window" change handler
X */
X	public HANDLER
Xwinch()
X{
X	SIGNAL(SIGWINCH, winch);
X	sigs |= S_WINCH;
X	if (reading)
X		intread();
X}
X#else
X#ifdef SIGWIND
X/*
X * "Window" change handler
X */
X	public HANDLER
Xwinch()
X{
X	SIGNAL(SIGWIND, winch);
X	sigs |= S_WINCH;
X	if (reading)
X		intread();
X}
X#endif
X#endif
X
X/*
X * Set up the signal handlers.
X */
X	public void
Xinit_signals(on)
X	int on;
X{
X	if (on)
X	{
X		/*
X		 * Set signal handlers.
X		 */
X		(void) SIGNAL(SIGINT, interrupt);
X#ifdef SIGTSTP
X		(void) SIGNAL(SIGTSTP, stop);
X#endif
X#ifdef SIGWINCH
X		(void) SIGNAL(SIGWINCH, winch);
X#else
X#ifdef SIGWIND
X		(void) SIGNAL(SIGWIND, winch);
X#endif
X#endif
X	} else
X	{
X		/*
X		 * Restore signals to defaults.
X		 */
X		(void) SIGNAL(SIGINT, SIG_DFL);
X#ifdef SIGTSTP
X		(void) SIGNAL(SIGTSTP, SIG_DFL);
X#endif
X#ifdef SIGWINCH
X		(void) SIGNAL(SIGWINCH, SIG_IGN);
X#endif
X#ifdef SIGWIND
X		(void) SIGNAL(SIGWIND, SIG_IGN);
X#endif
X	}
X}
X
X/*
X * Process any signals we have received.
X * A received signal cause a bit to be set in "sigs".
X */
X	public int
Xpsignals()
X{
X	register int tsignals;
X
X	if ((tsignals = sigs) == 0)
X		return (0);
X	sigs = 0;
X
X#ifdef S_WINCH
X	if (tsignals & S_WINCH)
X	{
X		int old_width, old_height;
X		/*
X		 * Re-execute get_term() to read the new window size.
X		 */
X		old_width = sc_width;
X		old_height = sc_height;
X		get_term();
X		if (sc_width != old_width || sc_height != old_height)
X		{
X			scroll = (sc_height + 1) / 2;
X			screen_trashed = 1;
X		}
X	}
X#endif
X#ifdef SIGTSTP
X	if (tsignals & S_STOP)
X	{
X		/*
X		 * Clean up the terminal.
X		 */
X#ifdef SIGTTOU
X		SIGNAL(SIGTTOU, SIG_IGN);
X#endif
X		lower_left();
X		clear_eol();
X		deinit();
X		flush();
X		raw_mode(0);
X#ifdef SIGTTOU
X		SIGNAL(SIGTTOU, SIG_DFL);
X#endif
X		SIGNAL(SIGTSTP, SIG_DFL);
X		kill(getpid(), SIGTSTP);
X		/*
X		 * ... Bye bye. ...
X		 * Hopefully we'll be back later and resume here...
X		 * Reset the terminal and arrange to repaint the
X		 * screen when we get back to the main command loop.
X		 */
X		SIGNAL(SIGTSTP, stop);
X		raw_mode(1);
X		init();
X		screen_trashed = 1;
X	}
X#endif
X	if (tsignals & S_INTERRUPT)
X	{
X		bell();
X		/*
X		 * {{ You may wish to replace the bell() with 
X		 *    error("Interrupt"); }}
X		 */
X
X		/*
X		 * If we were interrupted while in the "calculating 
X		 * line numbers" loop, turn off line numbers.
X		 */
X		if (lnloop)
X		{
X			lnloop = 0;
X			linenums = 0;
X			error("Line numbers turned off");
X		}
X
X	}
X
X	return (1);
X}
END_OF_FILE
echo shar: Extracting \"os.c\"
sed "s/^X//" >'os.c' <<'END_OF_FILE'
X/*
X * Operating system dependent routines.
X *
X * Most of the stuff in here is based on Unix, but an attempt
X * has been made to make things work on other operating systems.
X * This will sometimes result in a loss of functionality, unless
X * someone rewrites code specifically for the new operating system.
X *
X * The makefile provides defines to decide whether various
X * Unix features are present.
X */
X
X#include <stdio.h>
X#include <signal.h>
X#include <setjmp.h>
X#include "less.h"
X
Xchar *getenv();
X
Xpublic int reading;
X
Xextern int screen_trashed;
X
Xstatic jmp_buf read_label;
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	int inp;
X	char cmdbuf[256];
X	char *shell;
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	 * De-initialize the terminal and take out of raw mode.
X	 */
X	deinit();
X	flush();
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 terminal, "/dev/tty",
X	 * even if less's standard input is coming from a pipe.
X	 */
X	inp = dup(0);
X	close(0);
X	if (open("/dev/tty", 0) < 0)
X		dup(inp);
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 ((shell = getenv("SHELL")) != NULL && *shell != '\0')
X	{
X		if (*cmd == '\0')
X			cmd = shell;
X		else
X		{
X			sprintf(cmdbuf, "%s -c \"%s\"", shell, cmd);
X			cmd = cmdbuf;
X		}
X	}
X	if (*cmd == '\0')
X		cmd = "sh";
X
X	system(cmd);
X
X	/*
X	 * Restore standard input, reset signals, raw mode, etc.
X	 */
X	close(0);
X	dup(inp);
X	close(inp);
X
X	init_signals(1);
X	raw_mode(1);
X	init();
X	screen_trashed = 1;
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	 */
X	winch();
X#endif
X}
X
X/*
X * Like read() system call, but is deliberately interruptable.
X * A call to intread() from a signal handler will interrupt
X * any pending iread().
X */
X	public int
Xiread(fd, buf, len)
X	int fd;
X	char *buf;
X	int len;
X{
X	register int n;
X
X	if (setjmp(read_label))
X		/*
X		 * We jumped here from intread.
X		 */
X		return (READ_INTR);
X
X	flush();
X	reading = 1;
X	n = read(fd, buf, len);
X	reading = 0;
X	if (n < 0)
X		return (-1);
X	return (n);
X}
X
X	public void
Xintread()
X{
X#if SIGSETMASK
X	sigsetmask(0);
X#endif
X	longjmp(read_label, 1);
X}
X
X#if GET_TIME
X	public long
Xget_time()
X{
X	long t;
X
X	time(&t);
X	return (t);
X}
X#endif
X
X/*
X * Expand a filename, substituting any environment variables, etc.
X * The implementation of this is necessarily very operating system
X * dependent.  This implementation is unabashedly only for Unix systems.
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	char *cmd;
X	static char buffer[FILENAME];
X
X	if (filename[0] == '#')
X		return (filename);
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 = calloc(strlen(filename)+8, sizeof(char));
X		if (cmd == NULL)
X			return (filename);
X		sprintf(cmd, "echo \"%s\"", filename);
X	} else
X	{
X		/*
X		 * Read the output of <$SHELL -c "echo filename">.
X		 */
X		cmd = calloc(strlen(p)+12);
X		if (cmd == NULL)
X			return (filename);
X		sprintf(cmd, "%s -c \"echo %s\"", p, filename);
X	}
X
X	if ((f = popen(cmd, "r")) == NULL)
X		return (filename);
X	free(cmd);
X
X	for (p = buffer;  p < &buffer[sizeof(buffer)-1];  p++)
X	{
X		if ((ch = getc(f)) == '\n' || ch == EOF)
X			break;
X		*p = ch;
X	}
X	*p = '\0';
X	pclose(f);
X	return (buffer);
X}
X
X#else
X
X	public char *
Xglob(filename)
X	char *filename;
X{
X	return (filename);
X}
X
X#endif
X
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
X#if STAT
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
X	public char *
Xbad_file(filename, message, len)
X	char *filename;
X	char *message;
X	unsigned int len;
X{
X	struct stat statbuf;
X
X	if (stat(filename, &statbuf) < 0)
X		return (errno_message(filename, message, len));
X
X	if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
X	{
X		static char is_dir[] = " is a directory";
X		strtcpy(message, filename, len-sizeof(is_dir)-1);
X		strcat(message, is_dir);
X		return (message);
X	}
X	if ((statbuf.st_mode & S_IFMT) != S_IFREG)
X	{
X		static char not_reg[] = " is not a regular file";
X		strtcpy(message, filename, len-sizeof(not_reg)-1);
X		strcat(message, not_reg);
X		return (message);
X	}
X	return (NULL);
X}
X
X#else
X
X	public char *
Xbad_file(filename, message, len)
X	char *filename;
X	char *message;
X	unsigned int len;
X{
X	return (NULL);
X}
X
X#endif
X
X/*
X * errno_message: Return an error message based on the value of "errno".
X * okreadfail: Return true if the previous failure of a read
X *	(on the input tty) should be considered ok.
X */
X
X#if PERROR
X
Xextern char *sys_errlist[];
Xextern int sys_nerr;
Xextern int errno;
X
X	public char *
Xerrno_message(filename, message, len)
X	char *filename;
X	char *message;
X	unsigned int len;
X{
X	char *p;
X	char msg[16];
X
X	if (errno < sys_nerr)
X		p = sys_errlist[errno];
X	else
X	{
X		sprintf(msg, "Error %d", errno);
X		p = msg;
X	}
X	strtcpy(message, filename, len-strlen(p)-3);
X	strcat(message, ": ");
X	strcat(message, p);
X	return (message);
X}
X
X#else
X
X	public char *
Xerrno_message(filename, message, len)
X	char *filename;
X	char *message;
X	unsigned int len;
X{
X	static char msg[] = ": cannot open";
X
X	strtcpy(message, filename, len-sizeof(msg)-1);
X	strcat(message, msg);
X	return (message);
X}
X
X#endif
END_OF_FILE
echo shar: Extracting \"help.c\"
sed "s/^X//" >'help.c' <<'END_OF_FILE'
X#include  "less.h"
X
X/*
X * Display some help.
X * Just invoke another "less" to display the help file.
X *
X * {{ This makes this function very simple, and makes changing the
X *    help file very easy, but it may present difficulties on
X *    (non-Unix) systems which do not supply the "system()" function. }}
X */
X
X	public void
Xhelp()
X{
X	char cmd[FILENAME+100];
X
X	sprintf(cmd, 
X	 "-less -m '-PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done ' %s",
X	 HELPFILE);
X	lsystem(cmd);
X	error("End of help");
X}
END_OF_FILE
echo shar: Extracting \"ttyin.c\"
sed "s/^X//" >'ttyin.c' <<'END_OF_FILE'
X/*
X * Routines dealing with getting input from the keyboard (i.e. from the user).
X */
X
X#include "less.h"
X
Xstatic int tty;
X
X/*
X * Open keyboard for input.
X * (Just use file descriptor 2.)
X */
X	public void
Xopen_getchr()
X{
X	tty = 2;
X}
X
X/*
X * Get a character from the keyboard.
X */
X	public int
Xgetchr()
X{
X	char c;
X	int result;
X
X	do
X	{
X		result = iread(tty, &c, 1);
X		if (result == READ_INTR)
X			return (READ_INTR);
X		if (result < 0)
X		{
X			/*
X			 * Don't call error() here,
X			 * because error calls getchr!
X			 */
X			quit();
X		}
X	} while (result != 1);
X	return (c & 0177);
X}
END_OF_FILE
echo shar: Extracting \"command.c\"
sed "s/^X//" >'command.c' <<'END_OF_FILE'
X/*
X * User-level command processor.
X */
X
X#include "less.h"
X#include "position.h"
X#include "cmd.h"
X
X#define	NO_MCA		0
X#define	MCA_DONE	1
X#define	MCA_MORE	2
X
Xextern int erase_char, kill_char;
Xextern int ispipe;
Xextern int sigs;
Xextern int quit_at_eof;
Xextern int hit_eof;
Xextern int sc_width;
Xextern int sc_height;
Xextern int sc_window;
Xextern int curr_ac;
Xextern int ac;
Xextern int quitting;
Xextern int scroll;
Xextern char *first_cmd;
Xextern char *every_first_cmd;
Xextern char version[];
Xextern char *current_file;
X#if EDITOR
Xextern char *editor;
X#endif
Xextern int screen_trashed;	/* The screen has been overwritten */
X
Xstatic char cmdbuf[120];	/* Buffer for holding a multi-char command */
X#if SHELL_ESCAPE
Xstatic char *shellcmd = NULL;	/* For holding last shell command for "!!" */
X#endif
Xstatic char *cp;		/* Pointer into cmdbuf */
Xstatic int cmd_col;		/* Current column of the multi-char command */
Xstatic int mca;			/* The multicharacter command (action) */
Xstatic int last_mca;		/* The previous mca */
Xstatic int number;		/* The number typed by the user */
Xstatic int wsearch;		/* Search for matches (1) or non-matches (0) */
X
X/*
X * Reset command buffer (to empty).
X */
Xcmd_reset()
X{
X	cp = cmdbuf;
X}
X
X/*
X * Backspace in command buffer.
X */
X	static int
Xcmd_erase()
X{
X	if (cp == cmdbuf)
X		/*
X		 * Backspace past beginning of the string:
X		 * this usually means abort the command.
X		 */
X		return (1);
X
X	if (control_char(*--cp))
X	{
X		/*
X		 * Erase an extra character, for the carat.
X		 */
X		backspace();
X		cmd_col--;
X	}
X	backspace();
X	cmd_col--;
X	return (0);
X}
X
X/*
X * Set up the display to start a new multi-character command.
X */
Xstart_mca(action, prompt)
X	int action;
X	char *prompt;
X{
X	lower_left();
X	clear_eol();
X	putstr(prompt);
X	cmd_col = strlen(prompt);
X	mca = action;
X}
X
X/*
X * Process a single character of a multi-character command, such as
X * a number, or the pattern of a search command.
X */
X	static int
Xcmd_char(c)
X	int c;
X{
X	if (c == erase_char)
X	{
X		if (cmd_erase())
X			return (1);
X	} else if (c == kill_char)
X	{
X		/* {{ Could do this faster, but who cares? }} */
X		while (cmd_erase() == 0)
X			;
X	} else if (cp >= &cmdbuf[sizeof(cmdbuf)-1])
X	{
X		/*
X		 * No room in the command buffer.
X		 */
X		bell();
X	} else if (cmd_col >= sc_width-3)
X	{
X		/*
X		 * No room on the screen.
X		 * {{ Could get fancy here; maybe shift the displayed
X		 *    line and make room for more chars, like ksh. }}
X		 */
X		bell();
X	} else
X	{
X		/*
X		 * Append the character to the string.
X		 */
X		*cp++ = c;
X		if (control_char(c))
X		{
X			putchr('^');
X			cmd_col++;
X			c = carat_char(c);
X		}
X		putchr(c);
X		cmd_col++;
X	}
X	return (0);
X}
X
X/*
X * Return the number currently in the command buffer.
X */
X	static int
Xcmd_int()
X{
X	*cp = '\0';
X	cp = cmdbuf;
X	return (atoi(cmdbuf));
X}
X
X/*
X * Move the cursor to lower left before executing a command.
X * This looks nicer if the command takes a long time before
X * updating the screen.
X */
X	static void
Xcmd_exec()
X{
X	lower_left();
X	flush();
X}
X
X/*
X * Display the appropriate prompt.
X */
X	static void
Xprompt()
X{
X	register char *p;
X
X	if (first_cmd != NULL && *first_cmd != '\0')
X	{
X		/*
X		 * No prompt necessary if commands are from first_cmd
X		 * rather than from the user.
X		 */
X		return;
X	}
X
X	/*
X	 * If nothing is displayed yet, display starting from line 1.
X	 */
X	if (position(TOP) == NULL_POSITION)
X		jump_back(1);
X	else if (screen_trashed)
X		repaint();
X
X	/*
X	 * If the -E flag is set and we've hit EOF on the last file, quit.
X	 */
X	if (quit_at_eof == 2 && hit_eof && curr_ac + 1 >= ac)
X		quit();
X
X	/*
X	 * Select the proper prompt and display it.
X	 */
X	lower_left();
X	clear_eol();
X	p = pr_string();
X	if (p == NULL)
X		putchr(':');
X	else
X	{
X		so_enter();
X		putstr(p);
X		so_exit();
X	}
X}
X
X/*
X * Get command character.
X * The character normally comes from the keyboard,
X * but may come from the "first_cmd" string.
X */
X	static int
Xgetcc()
X{
X	if (first_cmd == NULL)
X		return (getchr());
X
X	if (*first_cmd == '\0')
X	{
X		/*
X		 * Reached end of first_cmd input.
X		 */
X		first_cmd = NULL;
X		if (cp > cmdbuf && position(TOP) == NULL_POSITION)
X		{
X			/*
X			 * Command is incomplete, so try to complete it.
X			 * There are only two cases:
X			 * 1. We have "/string" but no newline.  Add the \n.
X			 * 2. We have a number but no command.  Treat as #g.
X			 * (This is all pretty hokey.)
X			 */
X			if (mca != A_DIGIT)
X				/* Not a number; must be search string */
X				return ('\n'); 
X			else
X				/* A number; append a 'g' */
X				return ('g');
X		}
X		return (getchr());
X	}
X	return (*first_cmd++);
X}
X
X/*
X * Execute a multicharacter command.
X */
X	static void
Xexec_mca()
X{
X	register char *p;
X	register int n;
X
X	*cp = '\0';
X	cmd_exec();
X	switch (mca)
X	{
X	case A_F_SEARCH:
X		search(1, cmdbuf, number, wsearch);
X		break;
X	case A_B_SEARCH:
X		search(0, cmdbuf, number, wsearch);
X		break;
X	case A_FIRSTCMD:
X		/*
X		 * Skip leading spaces or + signs in the string.
X		 */
X		for (p = cmdbuf;  *p == '+' || *p == ' ';  p++)
X			;
X		if (every_first_cmd != NULL)
X			free(every_first_cmd);
X		if (*p == '\0')
X			every_first_cmd = NULL;
X		else
X			every_first_cmd = save(p);
X		break;
X	case A_TOGGLE_OPTION:
X		toggle_option(cmdbuf, 1);
X		break;
X	case A_EXAMINE:
X		/*
X		 * Ignore leading spaces in the filename.
X		 */
X		for (p = cmdbuf;  *p == ' ';  p++)
X			;
X		edit(glob(p));
X		break;
X#if SHELL_ESCAPE
X	case A_SHELL:
X		/*
X		 * !! just uses whatever is in shellcmd.
X		 * Otherwise, copy cmdbuf to shellcmd,
X		 * replacing any '%' with the current
X		 * file name.
X		 */
X		if (*cmdbuf != '!')
X		{
X			register char *fr, *to;
X
X			/*
X			 * Make one pass to see how big a buffer we 
X			 * need to allocate for the expanded shell cmd.
X			 */
X			for (fr = cmdbuf;  *fr != '\0';  fr++)
X				if (*fr == '%')
X					n += strlen(current_file);
X				else
X					n++;
X
X			if (shellcmd != NULL)
X				free(shellcmd);
X			shellcmd = calloc(n+1, sizeof(char));
X			if (shellcmd == NULL)
X			{
X				error("cannot allocate memory");
X				break;
X			}
X
X			/*
X			 * Now copy the shell cmd, expanding any "%"
X			 * into the current filename.
X			 */
X			to = shellcmd;
X			for (fr = cmdbuf;  *fr != '\0';  fr++)
X			{
X				if (*fr != '%')
X					*to++ = *fr;
X				else
X				{
X					strcpy(to, current_file);
X					to += strlen(to);
X				}
X			}
X			*to = '\0';
X		}
X
X		if (shellcmd == NULL)
X			lsystem("");
X		else
X			lsystem(shellcmd);
X		error("!done");
X		break;
X#endif
X	}
X}
X
X/*
X * Add a character to a multi-character command.
X */
X	static int
Xmca_char(c)
X	int c;
X{
X	switch (mca)
X	{
X	case 0:
X		/*
X		 * Not in a multicharacter command.
X		 */
X		return (NO_MCA);
X
X	case A_PREFIX:
X		/*
X		 * In the prefix of a command.
X		 */
X		return (NO_MCA);
X
X	case A_DIGIT:
X		/*
X		 * Entering digits of a number.
X		 * Terminated by a non-digit.
X		 */
X		if ((c < '0' || c > '9') &&
X			c != erase_char && c != kill_char)
X		{
X			/*
X			 * Not part of the number.
X			 * Treat as a normal command character.
X			 */
X			number = cmd_int();
X			mca = 0;
X			return (NO_MCA);
X		}
X		break;
X
X	case A_TOGGLE_OPTION:
X		/*
X		 * Special case for the TOGGLE_OPTION command.
X		 * if the option letter which was entered is a
X		 * single-char option, execute the command immediately,
X		 * so he doesn't have to hit RETURN.
X		 */
X		if (cp == cmdbuf && c != erase_char && c != kill_char &&
X		    single_char_option(c))
X		{
X			cmdbuf[0] = c;
X			cmdbuf[1] = '\0';
X			toggle_option(cmdbuf, 1);
X			return (MCA_DONE);
X		}
X		break;
X	}
X
X	/*
X	 * Any other multicharacter command
X	 * is terminated by a newline.
X	 */
X	if (c == '\n' || c == '\r')
X	{
X		/*
X		 * Execute the command.
X		 */
X		exec_mca();
X		return (MCA_DONE);
X	}
X	/*
X	 * Append the char to the command buffer.
X	 */
X	if (cmd_char(c))
X		/*
X		 * Abort the multi-char command.
X		 */
X		return (MCA_DONE);
X	/*
X	 * Need another character.
X	 */
X	return (MCA_MORE);
X}
X
X/*
X * Main command processor.
X * Accept and execute commands until a quit command, then return.
X */
X	public void
Xcommands()
X{
X	register int c;
X	register int action;
X
X	last_mca = 0;
X	scroll = (sc_height + 1) / 2;
X
X	for (;;)
X	{
X		mca = 0;
X		number = 0;
X
X		/*
X		 * See if any signals need processing.
X		 */
X		if (sigs)
X		{
X			psignals();
X			if (quitting)
X				quit();
X		}
X			
X		/*
X		 * Display prompt and accept a character.
X		 */
X		cmd_reset();
X		prompt();
X		noprefix();
X		c = getcc();
X
X	again:
X		if (sigs)
X			continue;
X
X		/*
X		 * If we are in a multicharacter command, call mca_char.
X		 * Otherwise we call cmd_decode to determine the
X		 * action to be performed.
X		 */
X		if (mca)
X			switch (mca_char(c))
X			{
X			case MCA_MORE:
X				/*
X				 * Need another character.
X				 */
X				c = getcc();
X				goto again;
X			case MCA_DONE:
X				/*
X				 * Command has been handled by mca_char.
X				 * Start clean with a prompt.
X				 */
X				continue;
X			case NO_MCA:
X				/*
X				 * Not a multi-char command
X				 * (at least, not anymore).
X				 */
X				break;
X			}
X
X		/*
X		 * Decode the command character and decide what to do.
X		 */
X		switch (action = cmd_decode(c))
X		{
X		case A_DIGIT:
X			/*
X			 * First digit of a number.
X			 */
X			start_mca(A_DIGIT, ":");
X			goto again;
X
X		case A_F_SCREEN:
X			/*
X			 * Forward one screen.
X			 */
X			if (number <= 0)
X				number = sc_window;
X			if (number <= 0)
X				number = sc_height - 1;
X			cmd_exec();
X			forward(number, 1);
X			break;
X
X		case A_B_SCREEN:
X			/*
X			 * Backward one screen.
X			 */
X			if (number <= 0)
X				number = sc_window;
X			if (number <= 0)
X				number = sc_height - 1;
X			cmd_exec();
X			backward(number, 1);
X			break;
X
X		case A_F_LINE:
X			/*
X			 * Forward N (default 1) line.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			forward(number, 0);
X			break;
X
X		case A_B_LINE:
X			/*
X			 * Backward N (default 1) line.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			backward(number, 0);
X			break;
X
X		case A_F_SCROLL:
X			/*
X			 * Forward N lines 
X			 * (default same as last 'd' or 'u' command).
X			 */
X			if (number > 0)
X				scroll = number;
X			cmd_exec();
X			forward(scroll, 0);
X			break;
X
X		case A_B_SCROLL:
X			/*
X			 * Forward N lines 
X			 * (default same as last 'd' or 'u' command).
X			 */
X			if (number > 0)
X				scroll = number;
X			cmd_exec();
X			backward(scroll, 0);
X			break;
X
X		case A_FREPAINT:
X			/*
X			 * Flush buffers, then repaint screen.
X			 * Don't flush the buffers on a pipe!
X			 */
X			if (!ispipe)
X			{
X				ch_init(0, 0);
X				clr_linenum();
X			}
X			/* FALLTHRU */
X		case A_REPAINT:
X			/*
X			 * Repaint screen.
X			 */
X			cmd_exec();
X			repaint();
X			break;
X
X		case A_GOLINE:
X			/*
X			 * Go to line N, default beginning of file.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			jump_back(number);
X			break;
X
X		case A_PERCENT:
X			/*
X			 * Go to a specified percentage into the file.
X			 */
X			if (number < 0)
X				number = 0;
X			if (number > 100)
X				number = 100;
X			cmd_exec();
X			jump_percent(number);
X			break;
X
X		case A_GOEND:
X			/*
X			 * Go to line N, default end of file.
X			 */
X			cmd_exec();
X			if (number <= 0)
X				jump_forw();
X			else
X				jump_back(number);
X			break;
X
X		case A_STAT:
X			/*
X			 * Print file name, etc.
X			 */
X			cmd_exec();
X			error(eq_message());
X			break;
X			
X		case A_VERSION:
X			/*
X			 * Print version number, without the "@(#)".
X			 */
X			cmd_exec();
X			error(version+4);
X			break;
X
X		case A_QUIT:
X			/*
X			 * Exit.
X			 */
X			quit();
X
X		case A_F_SEARCH:
X		case A_B_SEARCH:
X			/*
X			 * Search for a pattern.
X			 * Accept chars of the pattern until \n.
X			 */
X			if (number <= 0)
X				number = 1;
X			start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
X			last_mca = mca;
X			wsearch = 1;
X			c = getcc();
X			if (c == '!')
X			{
X				/*
X				 * Invert the sense of the search.
X				 * Set wsearch to 0 and get a new
X				 * character for the start of the pattern.
X				 */
X				start_mca(action, 
X					(action==A_F_SEARCH) ? "!/" : "!?");
X				wsearch = 0;
X				c = getcc();
X			}
X			goto again;
X
X		case A_AGAIN_SEARCH:
X			/*
X			 * Repeat previous search.
X			 */
X			if (number <= 0)
X				number = 1;
X			if (wsearch)
X				start_mca(last_mca, 
X					(last_mca==A_F_SEARCH) ? "/" : "?");
X			else
X				start_mca(last_mca, 
X					(last_mca==A_F_SEARCH) ? "!/" : "!?");
X			cmd_exec();
X			search(mca==A_F_SEARCH, (char *)NULL, number, wsearch);
X			break;
X
X		case A_HELP:
X			/*
X			 * Help.
X			 */
X			lower_left();
X			clear_eol();
X			putstr("help");
X			cmd_exec();
X			help();
X			break;
X
X		case A_EXAMINE:
X			/*
X			 * Edit a new file.  Get the filename.
X			 */
X			cmd_reset();
X			start_mca(A_EXAMINE, "Examine: ");
X			c = getcc();
X			goto again;
X			
X		case A_VISUAL:
X			/*
X			 * Invoke an editor on the input file.
X			 */
X#if EDITOR
X			if (ispipe)
X			{
X				error("Cannot edit standard input");
X				break;
X			}
X			/*
X			 * Try to pass the line number to the editor.
X			 */
X			cmd_exec();
X			c = currline(MIDDLE);
X			if (c == 0)
X				sprintf(cmdbuf, "%s %s",
X					editor, current_file);
X			else
X				sprintf(cmdbuf, "%s +%d %s",
X					editor, c, current_file);
X			lsystem(cmdbuf);
X			ch_init(0, 0);
X			clr_linenum();
X			break;
X#else
X			error("Command not available");
X			break;
X#endif
X
X		case A_NEXT_FILE:
X			/*
X			 * Examine next file.
X			 */
X			if (number <= 0)
X				number = 1;
X			next_file(number);
X			break;
X
X		case A_PREV_FILE:
X			/*
X			 * Examine previous file.
X			 */
X			if (number <= 0)
X				number = 1;
X			prev_file(number);
X			break;
X
X		case A_TOGGLE_OPTION:
X			/*
X			 * Toggle a flag setting.
X			 */
X			cmd_reset();
X			start_mca(A_TOGGLE_OPTION, "-");
X			c = getcc();
X			goto again;
X
X		case A_DISP_OPTION:
X			/*
X			 * Report a flag setting.
X			 */
X			cmd_reset();
X			start_mca(A_DISP_OPTION, "_");
X			c = getcc();
X			if (c == erase_char || c == kill_char)
X				break;
X			cmdbuf[0] = c;
X			cmdbuf[1] = '\0';
X			toggle_option(cmdbuf, 0);
X			break;
X
X		case A_FIRSTCMD:
X			/*
X			 * Set an initial command for new files.
X			 */
X			cmd_reset();
X			start_mca(A_FIRSTCMD, "+");
X			c = getcc();
X			goto again;
X
X		case A_SHELL:
X			/*
X			 * Shell escape.
X			 */
X#if SHELL_ESCAPE
X			cmd_reset();
X			start_mca(A_SHELL, "!");
X			c = getcc();
X			goto again;
X#else
X			error("Command not available");
X			break;
X#endif
X
X		case A_SETMARK:
X			/*
X			 * Set a mark.
X			 */
X			lower_left();
X			clear_eol();
X			start_mca(A_SETMARK, "mark: ");
X			c = getcc();
X			if (c == erase_char || c == kill_char)
X				break;
X			setmark(c);
X			break;
X
X		case A_GOMARK:
X			/*
X			 * Go to a mark.
X			 */
X			lower_left();
X			clear_eol();
X			start_mca(A_GOMARK, "goto mark: ");
X			c = getcc();
X			if (c == erase_char || c == kill_char)
X				break;
X			gomark(c);
X			break;
X
X		case A_PREFIX:
X			/*
X			 * The command is incomplete (more chars are needed).
X			 * Display the current char so the user knows
X			 * what's going on and get another character.
X			 */
X			if (mca != A_PREFIX)
X				start_mca(A_PREFIX, "& ");
X			if (control_char(c))
X			{
X				putchr('^');
X				c = carat_char(c);
X			}
X			putchr(c);
X			c = getcc();
X			goto again;
X
X		default:
X			bell();
X			break;
X		}
X	}
X}
END_OF_FILE


-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.



More information about the Comp.sources.unix mailing list