* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by Mark Nudleman and the University of California, Berkeley. The
* name of Mark Nudleman or the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
static char sccsid
[] = "@(#)prim.c 5.4 (Berkeley) %G%";
* Primitives for displaying the file on the screen.
public int hit_eof
; /* Keeps track of how many times we hit end of file */
public int screen_trashed
;
extern int sc_width
, sc_height
;
* Sound the bell to indicate he is trying to move past end of file.
* 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.
* "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.
forw(n
, pos
, force
, only_last
)
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. }}
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.
putstr("...skipping...\n");
* Read the next line of input.
if (pos
== NULL_POSITION
)
* End of file: stop here unless the top line
* is still empty, or "force" is true.
if (!force
&& 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 + option or a -t option
* was given. These options 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
&& !plusoption
)
* Display n lines, scrolling backward.
back(n
, pos
, force
, only_last
)
do_repaint
= (n
> get_back_scroll() || (only_last
&& n
> sc_height
-1));
* Get the previous line of input.
if (pos
== NULL_POSITION
)
* Beginning of file: stop here unless "force" is true.
* 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 (quit_at_eof
&& hit_eof
)
* If the -e flag is set and 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
)
forw(n
, pos
, 0, only_last
);
* Display n more lines, backward.
* Start just before the line currently displayed at the top of the screen.
if (pos
== NULL_POSITION
)
* This will almost never happen,
* because the top line is almost never empty.
back(n
, pos
, 0, only_last
);
* Repaint the screen, starting from a specified position.
forw(sc_height
-1, pos
, 1, 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, 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. }}
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)
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.
* 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
), 1, 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 the screen.
* {{ 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 POSITION 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
)
* 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 char *cpattern
= NULL
;
static char *last_pattern
= NULL
;
if (caseless
&& pattern
!= NULL
)
* For a caseless search, convert any uppercase
* in the pattern to lowercase.
for (p
= pattern
; *p
!= '\0'; p
++)
if (*p
>= 'A' && *p
<= 'Z')
* (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 first_cmd searches,
* for example, "+/xyz" on the command line.)
} else if (!search_forward
)
* Backward search: start just before the top line
* displayed on the screen.
} else if (how_search
== 0)
* Start at the second real line displayed on the screen.
pos
= forw_raw_line(pos
);
while (pos
< position(TOP
+1));
} else if (how_search
== 1)
* Start just after the bottom line displayed on the screen.
pos
= position(BOTTOM_PLUS_ONE
);
* 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.
* While we're at it, remove any backspaces
* along with the preceeding char.
* This allows us to match text which is
* underlined or overstruck.
for (p
= q
= line
; *p
!= '\0'; p
++, q
++)
if (*p
>= 'A' && *p
<= 'Z')
/* Convert uppercase to lowercase. */
else 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
= (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 (!REGCMP) && (!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')