* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)prim.c 5.8 (Berkeley) 6/1/90";
* Primitives for displaying the file on the screen.
int hit_eof
; /* keeps track of how many times we hit end of file */
extern int sc_width
, sc_height
;
off_t
position(), forw_line(), back_line(), forw_raw_line(), back_raw_line();
off_t
ch_length(), ch_tell();
* Check to see if the end of file is currently "displayed".
* 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())
* If the screen is "squished", repaint it.
* "Squished" means the first displayed line is not at the top
* of the screen; this can happen when we display a short file
* Display n lines, scrolling forward, starting at position pos in the
* input file. "only_last" means display only the last screenful if
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 (top_scroll
&& n
>= sc_height
- 1) {
* {{ 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. }}
* This is not contiguous with what is currently displayed.
* Clear the screen image (position table) and start a new
if (pos
!= position(BOTTOM_PLUS_ONE
)) {
putstr("...skipping...\n");
for (short_file
= 0; --n
>= 0;) {
* Read the next line of input.
if (pos
== NULL_POSITION
) {
* end of file; copy the table if the file was
* too small for an entire screen.
if (position(TOP
) == NULL_POSITION
) {
* Add the position of the next line to the position table.
* Display the current line on the screen.
* If this is the first screen displayed and we hit an early
* EOF (i.e. before the requested number of lines), we
* "squish" the display down at the bottom of the screen.
* But don't do this if a -t option was given; it can cause
* us to start the display after the beginning of the file,
* and it is not appropriate to squish in that case.
if (first_time
&& line
== NULL
&& !top_scroll
&& !tagoption
) {
* Display n lines, scrolling backward.
do_repaint
= (n
> get_back_scroll() || (only_last
&& n
> sc_height
-1));
* Get the previous line of input.
if (pos
== NULL_POSITION
)
* Add the position of the previous line to the position table.
* Display the line on the screen.
* Display n more lines, forward.
* Start just after the line currently displayed at the bottom of the screen.
* If we're trying to go forward from end-of-file,
* go on to the next file.
pos
= position(BOTTOM_PLUS_ONE
);
if (pos
== NULL_POSITION
)
* Display n more lines, backward.
* Start just before the line currently displayed at the top of the screen.
* This will almost never happen, because the top line is almost
if (pos
== NULL_POSITION
)
* Repaint the screen, starting from a specified position.
forw(sc_height
-1, pos
, 0);
* Start at the line currently at the top of the screen
* and redisplay the screen.
* 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.
error("Cannot seek to end of file");
back(sc_height
- 1, pos
, 0);
* Jump to line n in the file.
* This is done the slow way, by starting at the beginning
* of the file and counting newlines.
* {{ Now that we have line numbering (in linenum.c),
* we could improve on this by starting at the
* nearest known line rather than at the beginning. }}
* 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)
error("Cannot get to beginning of file");
for (nlines
= 1; nlines
< n
; nlines
++)
while ((c
= ch_forw_get()) != '\n')
(void)sprintf(message
, "File has only %d lines",
* 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.
off_t pos
, len
, ch_length();
* 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");
pos
= (percent
* len
) / 100;
* Back up to the beginning of the line.
while ((c
= ch_back_get()) != '\n' && c
!= EOI
)
* Jump to a specified position in the file.
if ((nline
= onscreen(pos
)) >= 0) {
* The line is currently displayed.
forw(nline
, position(BOTTOM_PLUS_ONE
), 0);
* Seek to the desired location.
error("Cannot seek to that position");
* See if the desired line is BEFORE the currently displayed screen.
* If so, then move forward far enough so the line we're on will be
* at the bottom of the screen, in order to be able to call back()
* to make the screen scroll backwards & put the line at the top of
* {{ This seems inefficient, but it's not so bad,
* since we can never move forward more than a
* screenful before we stop to redraw the screen. }}
if (tpos
!= NULL_POSITION
&& pos
< tpos
) {
* Note that we can't forw_line() past tpos here,
* so there should be no EOI at this stage.
for (nline
= 0; npos
< tpos
&& nline
< sc_height
- 1; nline
++)
* More than a screenful back.
* Note that back() will repaint() if nline > back_scroll.
* Remember where we were; clear and paint the screen.
* 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 off_t marks
[NMARKS
];
* Initialize the mark table to show no marks are set.
for (i
= 0; i
< NMARKS
; i
++)
marks
[i
] = NULL_POSITION
;
* See if a mark letter is valid (between a and z).
error("Choose a letter between 'a' and 'z'");
marks
[c
-'a'] = position(TOP
);
marks
[LASTMARK
] = position(TOP
);
* Go to a previously set mark.
if (pos
== NULL_POSITION
)
if (pos
== NULL_POSITION
) {
* 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.
* Search for the n-th occurence of a specified pattern,
* either forward or backward.
search(search_forward
, pattern
, n
, wantmatch
)
register int search_forward
;
static regex_t
*cpattern
= NULL
;
static char *cpattern
= NULL
;
static char *last_pattern
= NULL
;
* For a caseless search, convert any uppercase in the pattern to
if (caseless
&& pattern
!= NULL
)
for (p
= pattern
; *p
; p
++)
if (pattern
== NULL
|| *pattern
== '\0')
* A null pattern means use the previous pattern.
* The compiled previous pattern is in cpattern, so just use it.
error("No previous regular expression");
* Otherwise compile the given pattern.
&& (cpattern
= (regex_t
*) malloc(sizeof(regex_t
))) == NULL
) {
error("cannot allocate memory");
if (regcomp(cpattern
, pattern
, 0))
error("Invalid pattern");
* (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
)
if (pattern
== NULL
|| *pattern
== '\0')
* A null pattern means use the previous pattern.
* The compiled previous pattern is in cpattern, so just use it.
error("No previous regular expression");
* Otherwise compile the given pattern.
if ((s
= regcmp(pattern
, 0)) == NULL
)
error("Invalid pattern");
if (pattern
== NULL
|| *pattern
== '\0')
* Null pattern means use the previous pattern.
if (last_pattern
== NULL
)
error("No previous regular expression");
(void)strcpy(lpbuf
, pattern
);
* 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 searches from the
} else if (!search_forward
) {
* Backward search: start just before the top line
* displayed on the screen.
* Start at the second screen line displayed on the screen.
pos
= position(TOP_PLUS_ONE
);
if (pos
== NULL_POSITION
)
* Can't find anyplace to start searching from.
error("Nothing to search");
linenum
= find_linenum(pos
);
* Get lines until we find a matching one or
* until we hit end-of-file (or beginning-of-file
* if we're going backwards).
* A signal aborts the search.
* Read the next line, and save the
* starting position of that line in linepos.
pos
= forw_raw_line(pos
);
* Read the previous line and save the
* starting position of that line in linepos.
pos
= back_raw_line(pos
);
if (pos
== NULL_POSITION
)
* We hit EOF/BOF without a match.
error("Pattern not found");
* If we're using line numbers, we might as well
* remember the information we have now (the position
* and line number of the current line).
* If this is a caseless search, convert uppercase in the
* input line to lowercase.
for (p
= q
= line
; *p
; p
++, q
++)
*q
= isupper(*p
) ? tolower(*p
) : *p
;
* Remove any backspaces along with the preceeding char.
* This allows us to match text which is underlined or
for (p
= q
= line
; *p
; p
++, q
++)
if (q
> line
&& *p
== '\b')
/* Delete BS and preceeding char. */
/* Otherwise, just copy. */
* 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.
linematch
= !regexec(cpattern
, line
, 0, NULL
, 0);
linematch
= (regex(cpattern
, line
) != NULL
);
linematch
= (re_exec(line
) == 1);
linematch
= match(pattern
, line
);
* We are successful if wantmatch and linematch are
* both true (want a match and got it),
* or both false (want a non-match and got it).
if (((wantmatch
&& linematch
) || (!wantmatch
&& !linematch
)) &&
#if !defined(REGCMP) && !defined(RECOMP) && !defined(RECOMP)
* We have neither regcmp() nor re_comp().
* We use this function to do simple pattern matching.
* It supports no metacharacters like *, etc.
for ( ; *buf
!= '\0'; buf
++)
for (pp
= pattern
, lp
= buf
; *pp
== *lp
; pp
++, lp
++)
if (*pp
== '\0' || *lp
== '\0')