home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d9xx
/
d902
/
less.lha
/
Less
/
Source
/
source.lha
/
prim.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-22
|
23KB
|
894 lines
/*
* Primitives for displaying the file on the screen.
*/
#ifdef AMIGA
#define REGCMP 1
#define regex regexec
#define regcmp(x,y) regcomp(x)
#include "regexp.h"
#endif
#include "less.h"
#include "position.h"
public int hit_eof; /* Keeps track of how many times we hit end of file */
extern int quiet;
extern int top_search;
extern int top_scroll;
extern int back_scroll;
extern int sc_width, sc_height;
extern int sigs;
extern int quit_at_eof;
extern int ac;
extern char *line;
extern char *first_cmd;
/* Prototypes for functions defined in prim.c */
static void eof_bell __PROTO((void));
static void eof_check __PROTO((void));
static void forw __PROTO((register int n,
POSITION pos,
int force,
int only_last));
static void back __PROTO((register int n,
POSITION pos,
int force,
int only_last));
static void prepaint __PROTO((POSITION pos));
static int badmark __PROTO((int c));
#if (!REGCMP) && (!RECOMP)
static int match __PROTO((char *pattern,
char *buf));
#endif
/*
* Sound the bell to indicate he is trying to move past end of file.
*/
#ifdef __STDC__
static void eof_bell (void)
#else
static void
eof_bell()
#endif
{
if (quiet == NOT_QUIET)
bell();
else
vbell();
}
/*
* Check to see if the end of file is currently "displayed".
*/
#ifdef __STDC__
static void eof_check (void)
#else
static void
eof_check()
#endif
{
POSITION pos;
/*
* If the bottom line is empty, we are at EOF.
* If the bottom line ends at the file length,
* we must be just at EOF.
*/
pos = position(BOTTOM_PLUS_ONE);
if (pos == NULL_POSITION || pos == ch_length())
hit_eof++;
}
/*
* Display n lines, scrolling forward,
* starting at position pos in the input file.
* "force" means display the n lines even if we hit end of file.
* "only_last" means display only the last screenful if n > screen size.
*/
#ifdef __STDC__
static void forw (register int n, POSITION pos, int force, int only_last)
#else
static void
forw(n, pos, force, only_last)
register int n;
POSITION pos;
int force;
int only_last;
#endif
{
int eof = 0;
int nlines = 0;
int do_repaint;
static int first_time = 1;
/*
* do_repaint tells us not to display anything till the end,
* then just repaint the entire screen.
*/
do_repaint = (only_last && n > sc_height-1);
if (!do_repaint)
{
if (top_scroll && n >= sc_height - 1)
{
/*
* Start a new screen.
* {{ This is not really desirable if we happen
* to hit eof in the middle of this screen,
* but we don't yet know if that will happen. }}
*/
if (top_scroll == 2)
clear();
home();
force = 1;
} else
{
lower_left();
clear_eol();
}
if (pos != position(BOTTOM_PLUS_ONE))
{
/*
* This is not contiguous with what is
* currently displayed. Clear the screen image
* (position table) and start a new screen.
*/
pos_clear();
add_forw_pos(pos);
force = 1;
if (top_scroll)
{
if (top_scroll == 2)
clear();
home();
} else if (!first_time)
{
putstr("...skipping...\n");
}
}
}
while (--n >= 0)
{
/*
* Read the next line of input.
*/
pos = forw_line(pos);
if (pos == NULL_POSITION)
{
/*
* End of file: stop here unless the top line
* is still empty, or "force" is true.
*/
eof = 1;
if (!force && position(TOP) != NULL_POSITION)
break;
line = NULL;
}
/*
* Add the position of the next line to the position table.
* Display the current line on the screen.
*/
add_forw_pos(pos);
nlines++;
if (do_repaint ||
(first_time && line == NULL && !top_scroll))
continue;
if (top_scroll == 1)
clear_eol();
put_line();
}
if (eof)
hit_eof++;
else
eof_check();
if (nlines == 0)
eof_bell();
else if (do_repaint)
repaint();
#ifndef AMIGA
if (first_time && hit_eof && quit_at_eof && ac <= 1)
quit();
#endif
first_time = 0;
}
/*
* Display n lines, scrolling backward.
*/
#ifdef __STDC__
static void back (register int n, POSITION pos, int force, int only_last)
#else
static void
back(n, pos, force, only_last)
register int n;
POSITION pos;
int force;
int only_last;
#endif
{
int nlines = 0;
int do_repaint;
do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
hit_eof = 0;
while (--n >= 0)
{
/*
* Get the previous line of input.
*/
pos = back_line(pos);
if (pos == NULL_POSITION)
{
/*
* Beginning of file: stop here unless "force" is true.
*/
if (!force)
break;
line = NULL;
}
/*
* Add the position of the previous line to the position table.
* Display the line on the screen.
*/
add_back_pos(pos);
nlines++;
if (!do_repaint)
{
home();
add_line();
put_line();
}
}
eof_check();
if (nlines == 0)
eof_bell();
else if (do_repaint)
repaint();
}
/*
* Display n more lines, forward.
* Start just after the line currently displayed at the bottom of the screen.
*/
#ifdef __STDC__
void forward (int n, int only_last)
#else
public void
forward(n, only_last)
int n;
int only_last;
#endif
{
POSITION pos;
pos = position(BOTTOM_PLUS_ONE);
if (pos == NULL_POSITION)
{
eof_bell();
hit_eof++;
return;
}
forw(n, pos, 0, only_last);
}
/*
* Display n more lines, backward.
* Start just before the line currently displayed at the top of the screen.
*/
#ifdef __STDC__
void backward (int n, int only_last)
#else
public void
backward(n, only_last)
int n;
int only_last;
#endif
{
POSITION pos;
pos = position(TOP);
if (pos == NULL_POSITION)
{
/*
* This will almost never happen,
* because the top line is almost never empty.
*/
eof_bell();
return;
}
back(n, pos, 0, only_last);
}
/*
* Repaint the screen, starting from a specified position.
*/
#ifdef __STDC__
static void prepaint (POSITION pos)
#else
static void
prepaint(pos)
POSITION pos;
#endif
{
hit_eof = 0;
forw(sc_height-1, pos, 1, 0);
}
/*
* Repaint the screen.
*/
#ifdef __STDC__
void repaint (void)
#else
public void
repaint()
#endif
{
/*
* Start at the line currently at the top of the screen
* and redisplay the screen.
*/
#ifndef AMIGA
/* screen might have been resized */
POSITION savepos;
savepos = position(TOP);
pos_clear();
add_forw_pos(savepos);
prepaint(savepos);
#else
extern int file;
if ( file >= 0 )
prepaint(position(TOP));
else
clear();
#endif
}
/*
* Jump to the end of the file.
* It is more convenient to paint the screen backward,
* from the end of the file toward the beginning.
*/
#ifdef __STDC__
void jump_forw (void)
#else
public void
jump_forw()
#endif
{
POSITION pos;
if (ch_end_seek())
{
error("Cannot seek to end of file");
return;
}
lastmark();
pos = ch_tell();
clear();
pos_clear();
add_back_pos(pos);
back(sc_height - 1, pos, 0, 0);
}
/*
* Jump to line n in the file.
*/
#ifdef __STDC__
void jump_back (register int n)
#else
public void
jump_back(n)
register int n;
#endif
{
register int c;
int nlines;
/*
* This is done the slow way, by starting at the beginning
* of the file and counting newlines.
*/
if (ch_seek((POSITION)0))
{
/*
* Probably a pipe with beginning of file no longer buffered.
* If he wants to go to line 1, we do the best we can,
* by going to the first line which is still buffered.
*/
if (n <= 1 && ch_beg_seek() == 0)
jump_loc(ch_tell());
error("Cannot get to beginning of file");
return;
}
/*
* Start counting lines.
*/
for (nlines = 1; nlines < n; nlines++)
{
while ((c = ch_forw_get()) != '\n')
if (c == EOF)
{
char message[40];
sprintf(message, "File has only %ld lines",
nlines-1);
error(message);
return;
}
}
jump_loc(ch_tell());
}
/*
* Jump to a specified percentage into the file.
* This is a poor compensation for not being able to
* quickly jump to a specific line number.
*/
#ifdef __STDC__
void jump_percent (int percent)
#else
public void
jump_percent(percent)
int percent;
#endif
{
POSITION pos, len;
register int c;
/*
* Determine the position in the file
* (the specified percentage of the file's length).
*/
if ((len = ch_length()) == NULL_POSITION)
{
error("Don't know length of file");
return;
}
pos = (percent * len) / 100;
/*
* Back up to the beginning of the line.
*/
if (ch_seek(pos) == 0)
{
while ((c = ch_back_get()) != '\n' && c != EOF)
;
if (c == '\n')
(void) ch_forw_get();
pos = ch_tell();
}
jump_loc(pos);
}
/*
* Jump to a specified position in the file.
*/
#ifdef __STDC__
void jump_loc (POSITION pos)
#else
public void
jump_loc(pos)
POSITION pos;
#endif
{
register int nline;
POSITION tpos;
/*
* See if the desired line is BEFORE the currently
* displayed screen. If so, see if it is close enough
* to scroll backwards to it.
* {{ This can be expensive if he has specified a very
* large back_scroll count. Perhaps we should put
* some sanity limit on the loop count here. }}
*/
tpos = position(TOP);
if (tpos != NULL_POSITION && pos < tpos)
{
int bs = get_back_scroll();
for (nline = 1; nline <= bs; nline++)
{
tpos = back_line(tpos);
if (tpos == NULL_POSITION)
break;
if (tpos <= pos)
{
back(nline, position(TOP), 1, 0);
return;
}
}
} else if ((nline = onscreen(pos)) >= 0)
{
/*
* The line is currently displayed.
* Just scroll there.
*/
forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
return;
}
/*
* Line is not on screen.
* Remember where we were; clear and paint the screen.
*/
if (ch_seek(pos))
{
error("Cannot seek to that position");
return;
}
lastmark();
prepaint(pos);
}
/*
* The table of marks.
* A mark is simply a position in the file.
*/
#define NMARKS (27) /* 26 for a-z plus one for quote */
#define LASTMARK (NMARKS-1) /* For quote */
static POSITION marks[NMARKS];
/*
* Initialize the mark table to show no marks are set.
*/
#ifdef __STDC__
void init_mark (void)
#else
public void
init_mark()
#endif
{
int i;
for (i = 0; i < NMARKS; i++)
marks[i] = NULL_POSITION;
}
/*
* See if a mark letter is valid (between a and z).
*/
#ifdef __STDC__
static int badmark (int c)
#else
static int
badmark(c)
int c;
#endif
{
if (c < 'a' || c > 'z')
{
error("Choose a letter between 'a' and 'z'");
return (1);
}
return (0);
}
/*
* Set a mark.
*/
#ifdef __STDC__
void setmark (int c)
#else
public void
setmark(c)
int c;
#endif
{
if (badmark(c))
return;
marks[c-'a'] = position(TOP);
}
#ifdef __STDC__
void lastmark (void)
#else
public void
lastmark()
#endif
{
marks[LASTMARK] = position(TOP);
}
/*
* Go to a previously set mark.
*/
#ifdef __STDC__
void gomark (int c)
#else
public void
gomark(c)
int c;
#endif
{
POSITION pos;
if (c == '\'')
pos = marks[LASTMARK];
else if (badmark(c))
return;
else
pos = marks[c-'a'];
if (pos == NULL_POSITION)
error("mark not set");
else
jump_loc(pos);
}
/*
* Get the backwards scroll limit.
* Must call this function instead of just using the value of
* back_scroll, because the default case depends on sc_height and
* top_scroll, as well as back_scroll.
*/
#ifdef __STDC__
int get_back_scroll (void)
#else
public int
get_back_scroll()
#endif
{
if (back_scroll >= 0)
#ifdef AMIGA
return (back_scroll < sc_height? back_scroll: sc_height - 1);
#else
return (back_scroll);
#endif
if (top_scroll)
return (sc_height - 2);
return (sc_height - 1);
}
/*
* Search for the n-th occurence of a specified pattern,
* either forward (direction == '/'), or backwards (direction == '?').
*/
#ifdef __STDC__
void search (int direction, char *pattern, register int n)
#else
public void
search(direction, pattern, n)
int direction;
char *pattern;
register int n;
#endif
{
register int search_forward = (direction == '/');
POSITION pos, linepos;
#if RECOMP
char *re_comp();
char *errmsg;
/*
* (re_comp handles a null pattern internally,
* so there is no need to check for a null pattern here.)
*/
if ((errmsg = re_comp(pattern)) != NULL)
{
error(errmsg);
return;
}
#else
#if REGCMP
#ifdef AMIGA
static regexp *cpattern = NULL;
#else
char *regcmp();
static char *cpattern = NULL;
#endif
if (pattern == NULL || *pattern == '\0')
{
/*
* A null pattern means use the previous pattern.
* The compiled previous pattern is in cpattern, so just use it.
*/
if (cpattern == NULL)
{
error("No previous regular expression");
return;
}
} else
{
/*
* Otherwise compile the given pattern.
*/
#ifdef AMIGA
regexp *s;
#else
char *s;
#endif
if ((s = regcmp(pattern, 0)) == NULL)
{
#ifdef AMIGA
/* regexp had already displayed a more specific
error message
*/
#else
error("Invalid pattern");
#endif
return;
}
if (cpattern != NULL)
free((char *)cpattern);
cpattern = s;
}
#else
static char lpbuf[100];
static char *last_pattern = NULL;
if (pattern == NULL || *pattern == '\0')
{
/*
* Null pattern means use the previous pattern.
*/
if (last_pattern == NULL)
{
error("No previous regular expression");
return;
}
pattern = last_pattern;
} else
{
strcpy(lpbuf, pattern);
last_pattern = lpbuf;
}
#endif
#endif
/*
* Figure out where to start the search.
*/
if (position(TOP) == NULL_POSITION)
{
/*
* Nothing is currently displayed.
* Start at the beginning of the file.
* (This case is mainly for first_cmd searches,
* for example, "+/xyz" on the command line.)
*/
pos = (POSITION)0;
} else if (!search_forward)
{
/*
* Backward search: start just before the top line
* displayed on the screen.
*/
pos = position(TOP);
} else if (top_search)
{
/*
* Forward search and "start from top".
* Start at the second line displayed on the screen.
*/
pos = position(TOP_PLUS_ONE);
} else
{
/*
* Forward search but don't "start from top".
* Start just after the bottom line displayed on the screen.
*/
pos = position(BOTTOM_PLUS_ONE);
}
if (pos == NULL_POSITION)
{
/*
* Can't find anyplace to start searching from.
*/
error("Nothing to search");
return;
}
for (;;)
{
/*
* Get lines until we find a matching one or
* until we hit end-of-file (or beginning-of-file
* if we're going backwards).
*/
#ifdef AMIGA
if (chk_sigs())
#else
if (sigs)
#endif
/*
* A signal aborts the search.
*/
return;
if (search_forward)
{
/*
* Read the next line, and save the
* starting position of that line in linepos.
*/
linepos = pos;
pos = forw_raw_line(pos);
} else
{
/*
* Read the previous line and save the
* starting position of that line in linepos.
*/
pos = back_raw_line(pos);
linepos = pos;
}
if (pos == NULL_POSITION)
{
/*
* We hit EOF/BOF without a match.
*/
error("Pattern not found");
return;
}
/*
* Test the next line to see if we have a match.
* This is done in a variety of ways, depending
* on what pattern matching functions are available.
*/
#if REGCMP
if ( (regex(cpattern, line) != NULL)
#else
#if RECOMP
if ( (re_exec(line) == 1)
#else
if ( (match(pattern, line))
#endif
#endif
&& (--n <= 0) )
/*
* Found the matching line.
*/
break;
}
jump_loc(linepos);
}
#if (!REGCMP) && (!RECOMP)
/*
* We have neither regcmp() nor re_comp().
* We use this function to do simple pattern matching.
* It supports no metacharacters like *, etc.
*/
static int
match(pattern, buf)
char *pattern, *buf;
{
register char *pp, *lp;
for ( ; *buf != '\0'; buf++)
{
for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
if (*pp == '\0' || *lp == '\0')
break;
if (*pp == '\0')
return (1);
}
return (0);
}
#endif