less part 2 of 2
sources-request at panda.UUCP
sources-request at panda.UUCP
Wed Feb 12 09:31:51 AEST 1986
Mod.sources: Volume 3, Issue 121
Submitted by: ihnp4!nsc!nsc-pdc!rgb (Robert Bond)
#!/bin/sh-----cut here-----cut here-----cut here-----cut here-----
# shar: Shell Archiver
# Run the following text with /bin/sh to create:
# ch.c
# position.c
# input.c
# output.c
# screen.c
# prompt.c
# line.c
# signal.c
# help.c
# ttyin.c
# command.c
# version.c
echo shar: extracting ch.c
cat - << \SHAR_EOF > ch.c
/*
* Low level character input from the input file.
* We use these special purpose routines which optimize moving
* both forward and backward from the current read pointer.
*/
#include "less.h"
public int file = -1; /* File descriptor of the input file */
/*
* Pool of buffers holding the most recently used blocks of the input file.
*/
#define BUFSIZ 1024
static struct buf {
struct buf *next, *prev;
long block;
char data[BUFSIZ];
};
static struct buf *bufs = NULL;
public int nbufs;
/*
* The buffer pool is kept as a doubly-linked circular list,
* in order from most- to least-recently used.
* The circular list is anchored by buf_anchor.
*/
static struct {
struct buf *next, *prev;
} buf_anchor;
#define END_OF_CHAIN ((struct buf *)&buf_anchor)
#define buf_head buf_anchor.next
#define buf_tail buf_anchor.prev
/*
* If we fail to allocate enough memory for buffers, we try to limp
* along with a minimum number of buffers.
*/
#define DEF_NBUFS 2 /* Minimum number of buffers */
extern int clean_data;
extern int ispipe;
/*
* Current position in file.
* Stored as a block number and an offset into the block.
*/
static long ch_block;
static int ch_offset;
/*
* Length of file, needed if input is a pipe.
*/
static POSITION ch_fsize;
/*
* Largest block number read if input is standard input (a pipe).
*/
static long last_piped_block;
/*
* Get the character pointed to by the read pointer.
* ch_get() is a macro which is more efficient to call
* than fch_get (the function), in the usual case
* that the block desired is at the head of the chain.
*/
#define ch_get() ((buf_head->block == ch_block) ? \
buf_head->data[ch_offset] : fch_get())
static int
fch_get()
{
register struct buf *bp;
register int n;
register int end;
POSITION pos;
/*
* Look for a buffer holding the desired block.
*/
for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
if (bp->block == ch_block)
goto found;
/*
* Block is not in a buffer.
* Take the least recently used buffer
* and read the desired block into it.
*/
bp = buf_tail;
bp->block = ch_block;
pos = ch_block * BUFSIZ;
if (ispipe)
{
/*
* The block requested should be one more than
* the last block read.
*/
if (ch_block != ++last_piped_block)
{
/* This "should not happen". */
char message[80];
sprintf(message, "Pipe error: last %ld, want %ld\n",
last_piped_block-1, ch_block);
error(message);
quit();
}
} else
lseek(file, pos, 0);
/*
* Read the block. This may take several reads if the input
* is coming from standard input, due to the nature of pipes.
*/
end = 0;
while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
if ((end += n) >= BUFSIZ)
break;
if (n < 0)
{
error("read error");
quit();
}
/*
* Set an EOF marker in the buffered data itself.
* Then ensure the data is "clean": there are no
* extra EOF chars in the data and that the "meta"
* bit (the 0200 bit) is reset in each char.
*/
if (end < BUFSIZ)
{
ch_fsize = pos + end;
bp->data[end] = EOF;
}
if (!clean_data)
while (--end >= 0)
{
bp->data[end] &= 0177;
if (bp->data[end] == EOF)
bp->data[end] = '@';
}
found:
/* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
{
/*
* Move the buffer to the head of the buffer chain.
* This orders the buffer chain, most- to least-recently used.
*/
bp->next->prev = bp->prev;
bp->prev->next = bp->next;
bp->next = buf_head;
bp->prev = END_OF_CHAIN;
buf_head->prev = bp;
buf_head = bp;
}
return (bp->data[ch_offset]);
}
/*
* Determine if a specific block is currently in one of the buffers.
*/
static int
buffered(block)
long block;
{
register struct buf *bp;
for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
if (bp->block == block)
return (1);
return (0);
}
/*
* Seek to a specified position in the file.
* Return 0 if successful, non-zero if can't seek there.
*/
public int
ch_seek(pos)
register POSITION pos;
{
long new_block;
new_block = pos / BUFSIZ;
if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
{
/*
* Set read pointer.
*/
ch_block = new_block;
ch_offset = pos % BUFSIZ;
return (0);
}
return (1);
}
/*
* Seek to the end of the file.
*/
public int
ch_end_seek()
{
if (ispipe)
{
/*
* Do it the slow way: read till end of data.
*/
while (ch_forw_get() != EOF)
;
} else
{
(void) ch_seek((POSITION)(lseek(file, (off_t)0, 2)));
}
return (0);
}
/*
* Return the length of the file, if known.
*/
public POSITION
ch_length()
{
if (ispipe)
return (ch_fsize);
return ((POSITION)(lseek(file, (off_t)0, 2)));
}
/*
* Return the current position in the file.
*/
public POSITION
ch_tell()
{
return (ch_block * BUFSIZ + ch_offset);
}
/*
* Get the current char and post-increment the read pointer.
*/
public int
ch_forw_get()
{
register int c;
c = ch_get();
if (c != EOF && ++ch_offset >= BUFSIZ)
{
ch_offset = 0;
ch_block ++;
}
return (c);
}
/*
* Pre-decrement the read pointer and get the new current char.
*/
public int
ch_back_get()
{
register int c;
if (--ch_offset < 0)
{
if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
{
ch_offset = 0;
return (EOF);
}
ch_offset = BUFSIZ - 1;
ch_block--;
}
c = ch_get();
return (c);
}
/*
* Initialize the buffer pool to all empty.
* Caller suggests that we use want_nbufs buffers.
*/
public void
ch_init(want_nbufs)
int want_nbufs;
{
register struct buf *bp;
char *calloc();
if (nbufs < want_nbufs)
{
/*
* We don't have enough buffers.
* Free what we have (if any) and allocate some new ones.
*/
if (bufs != NULL)
free((char *)bufs);
bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
nbufs = want_nbufs;
if (bufs == NULL)
{
/*
* Couldn't get that many.
* Try for a small default number of buffers.
*/
char message[80];
sprintf(message,
"Cannot allocate %d buffers. Using %d buffers.",
nbufs, DEF_NBUFS);
error(message);
bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
nbufs = DEF_NBUFS;
if (bufs == NULL)
{
/*
* Couldn't even get the smaller number of bufs.
* Something is wrong here, don't continue.
*/
sprintf(message,
"Cannot even allocate %d buffers! Quitting.\n",
DEF_NBUFS);
error(message);
quit();
/*NOTREACHED*/
}
}
}
/*
* Initialize the buffers to empty.
* Set up the circular list.
*/
for (bp = &bufs[0]; bp < &bufs[nbufs]; bp++)
{
bp->next = bp + 1;
bp->prev = bp - 1;
bp->block = (long)(-1);
}
bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
buf_head = &bufs[0];
buf_tail = &bufs[nbufs-1];
last_piped_block = -1;
ch_fsize = NULL_POSITION;
(void) ch_seek((POSITION)0);
}
SHAR_EOF
echo shar: extracting position.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;
{
switch (where)
{
case BOTTOM:
where = sc_height - 2;
break;
case BOTTOM_PLUS_ONE:
where = sc_height - 1;
break;
}
return (table[where]);
}
/*
* 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;
}
/*
* See if the byte at a specified position is currently on the screen.
* Check the position table to see if the position falls within its range.
* Return the position table entry if found, -1 if not.
*/
public int
onscreen(pos)
POSITION pos;
{
register int i;
if (pos < table[0])
return (-1);
for (i = 1; i < sc_height; i++)
if (pos < table[i])
return (i-1);
return (-1);
}
SHAR_EOF
echo shar: extracting input.c
cat - << \SHAR_EOF > input.c
/*
* High level routines dealing with getting lines of input
* from the file being viewed.
*
* When we speak of "lines" here, we mean PRINTABLE lines;
* lines processed with respect to the screen width.
* We use the term "raw line" to refer to lines simply
* delimited by newlines; not processed with respect to screen width.
*/
#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))
return (NULL_POSITION);
c = ch_forw_get();
if (c == EOF)
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))
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)
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))
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
echo shar: extracting output.c
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 int tabstop;
extern int twiddle;
extern char *line;
extern char *first_cmd;
/*
* 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 = (twiddle) ? "~" : "";
column = 0;
for (p = line; *p != '\0'; p++)
{
switch (c = *p)
{
case UL_CHAR:
ul_enter();
column += ul_width;
break;
case UE_CHAR:
ul_exit();
column += ue_width;
break;
case '\t':
do
{
putc(' ');
column++;
} while ((column % tabstop) != 0);
break;
case '\b':
putbs();
column--;
break;
default:
if (c & 0200)
{
putc('^');
putc(c & 0177);
column += 2;
} 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;
static char buf[2];
lower_left();
clear_eol();
so_enter();
puts(s);
puts(return_to_continue);
so_exit();
#if ONLY_RETURN
while ((c = getc()) != '\n' && c != '\r')
bell();
#else
c = getc();
if (c != '\n' && c != '\r' && c != ' ')
{
buf[0] = c;
first_cmd = buf;
}
#endif
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
echo shar: extracting screen.c
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 */
extern int back_scroll;
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, TIOCSETN, &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 = "";
/* Force repaint on any backward movement */
back_scroll = 0;
}
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
echo shar: extracting prompt.c
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 ispipe;
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 (!ispipe)
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.
* If we cannot find the percentage and must_print is true,
* the use the byte offset.
*/
static void
ap_percent(must_print)
{
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);
else if (must_print)
ap_byte();
}
/*
* 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(0);
/*
* 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
ap_percent(1);
break;
case PR_LONG:
ap_filename();
if (new_file)
ap_of();
ap_byte();
if (hit_eof)
ap_eof();
else
ap_percent(0);
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
echo shar: extracting line.c
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')
break;
if (c != '_' && curr[-2] != '_')
{
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);
} while ((column % tabstop) != 0);
*curr++ = '\t';
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);
}
/*
* Analogous to forw_line(), but deals with "raw lines":
* lines which are not split for screen width.
* {{ This is supposed to be more efficient than forw_line(). }}
*/
public POSITION
forw_raw_line(curr_pos)
POSITION curr_pos;
{
register char *p;
register int c;
POSITION new_pos;
if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
(c = ch_forw_get()) == EOF)
return (NULL_POSITION);
p = linebuf;
for (;;)
{
if (c == '\n' || c == EOF)
{
new_pos = ch_tell();
break;
}
if (p >= &linebuf[sizeof(linebuf)-1])
{
/*
* Overflowed the input buffer.
* Pretend the line ended here.
* {{ The line buffer is supposed to be big
* enough that this never happens. }}
*/
new_pos = ch_tell() - 1;
break;
}
*p++ = c;
c = ch_forw_get();
}
*p = '\0';
line = linebuf;
return (new_pos);
}
/*
* Analogous to back_line(), but deals with "raw lines".
* {{ This is supposed to be more efficient than back_line(). }}
*/
public POSITION
back_raw_line(curr_pos)
POSITION curr_pos;
{
register char *p;
register int c;
POSITION new_pos;
if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
ch_seek(curr_pos-1))
return (NULL_POSITION);
p = &linebuf[sizeof(linebuf)];
*--p = '\0';
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;
}
if (p <= linebuf)
{
/*
* Overflowed the input buffer.
* Pretend the line ended here.
*/
new_pos = ch_tell() + 1;
break;
}
*--p = c;
}
line = p;
return (new_pos);
}
SHAR_EOF
echo shar: extracting signal.c
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.
*/
#ifdef SIGTTOU
SIGNAL(SIGTTOU, SIG_IGN);
#endif
lower_left();
clear_eol();
flush();
raw_mode(0);
#ifdef SIGTTOU
SIGNAL(SIGTTOU, SIG_DFL);
#endif
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);
}
/*
* Pass the specified command to a shell to be executed.
* Like plain "system()", but handles resetting terminal modes, etc.
*/
public void
lsystem(cmd)
char *cmd;
{
int inp;
/*
* Print the command which is to be executed.
*/
lower_left();
clear_eol();
puts("!");
puts(cmd);
puts("\n");
/*
* De-initialize the terminal and take out of raw mode.
*/
deinit();
flush();
raw_mode(0);
/*
* Restore signals to their defaults.
*/
SIGNAL(SIGINT, SIG_DFL);
#ifdef SIGTSTP
SIGNAL(SIGTSTP, SIG_DFL);
#endif
/*
* Pass the command to the system to be executed.
*/
inp = dup(0);
close(0);
open("/dev/tty", 0);
system(cmd);
close(0);
dup(inp);
close(inp);
/*
* Reset signals, raw mode, etc.
*/
init_signals();
raw_mode(1);
init();
}
SHAR_EOF
echo shar: extracting help.c
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");
puts("m<letter> Mark the current position with <letter>.\n");
puts("'<letter> Return to a previously marked position.\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
sprintf(message,
"v Edit the current file with $EDITOR (default %s).\n",
EDIT_PGM);
puts(message);
#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
echo shar: extracting ttyin.c
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
echo shar: extracting command.c
cat - << \SHAR_EOF > command.c
/*
* User-level command processor.
*/
#include "less.h"
#include "position.h"
#include <setjmp.h>
extern jmp_buf main_loop;
extern int erase_char, kill_char;
extern int pr_type;
extern int sigs;
extern int ispipe;
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. '/') */
static char last_mcc; /* The previous mcc */
/*
* 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';
cp = cmdbuf;
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 && position(TOP) == NULL_POSITION)
{
/*
* 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;
mcc = last_mcc = 0;
setjmp(main_loop);
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 == '!')
{
lsystem(cmdbuf);
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 ' ':
case CONTROL('F'):
/*
* Forward one screen.
*/
n = cmd_int();
if (n <= 0)
n = sc_height - 1;
forward(n, 1);
break;
case 'b':
case CONTROL('B'):
/*
* Backward one screen.
*/
n = cmd_int();
if (n <= 0)
n = sc_height - 1;
backward(n, 1);
break;
case 'e':
case 'j':
case '\r':
case '\n':
case CONTROL('E'):
/*
* Forward N (default 1) line.
*/
n = cmd_int();
if (n <= 0)
n = 1;
forward(n, 0);
break;
case 'y':
case 'k':
case CONTROL('K'):
case CONTROL('Y'):
/*
* Backward N (default 1) line.
*/
n = cmd_int();
if (n <= 0)
n = 1;
backward(n, 0);
break;
case 'd':
case CONTROL('D'):
/*
* Forward N lines
* (default same as last 'd' or 'u' command).
*/
n = cmd_int();
if (n > 0)
scroll = n;
forward(scroll, 0);
break;
case 'u':
case CONTROL('U'):
/*
* Forward N lines
* (default same as last 'd' or 'u' command).
*/
n = cmd_int();
if (n > 0)
scroll = n;
backward(scroll, 0);
break;
case 'R':
/*
* Flush buffers, then repaint screen.
*/
ch_init(0);
/* Fall thru */
case 'r':
case CONTROL('R'):
case CONTROL('L'):
/*
* 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':
case '%':
/*
* 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 '=':
case CONTROL('G'):
/*
* 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 (ispipe)
{
error("Cannot edit standard input");
break;
}
sprintf(cmdbuf, "%s %s", editor, current_file);
lsystem(cmdbuf);
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)
break;
toggle_option(c);
break;
case 'm':
/*
* Set a mark.
*/
lower_left();
clear_eol();
puts("mark: ");
c = getcc();
if (c == erase_char || c == kill_char)
break;
setmark(c);
break;
case '\'':
/*
* Go to a mark.
*/
lower_left();
clear_eol();
puts("goto mark: ");
c = getcc();
if (c == erase_char || c == kill_char)
break;
gomark(c);
break;
default:
bell();
break;
}
}
}
SHAR_EOF
echo shar: extracting version.c
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
* v41: ONLY_RETURN, control char commands, 9/17/85 mark
* faster search, other minor fixes.
* v42: Added ++ command line syntax; 9/25/85 mark
* ch_fsize for pipes.
* v43: Added -h flag, changed prim.c algorithms. 10/15/85 mark
* v44: Made END print in all cases of eof; 10/16/85 mark
* ignore SIGTTOU after receiving SIGTSTP.
* v45: Never print backspaces unless -u. 10/16/85 mark
* v46: Backwards scroll in jump_loc. 10/24/85 mark
* v47: Fixed bug in edit(): *first_cmd==0 10/30/85 mark
* v48: Use TIOCSETN instead of TIOCSETP. 11/16/85 mark
* Added marks (m and ' commands).
* -----------------------------------------------------------------
*/
char version[] = "@(#) less version 48";
SHAR_EOF
More information about the Mod.sources
mailing list