1 /* $NetBSD: prim.c,v 1.9 2003/10/13 14:34:25 agc Exp $ */
4 * Copyright (c) 1988 Mark Nudelman
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <sys/cdefs.h>
36 static char sccsid
[] = "@(#)prim.c 8.1 (Berkeley) 6/6/93";
38 __RCSID("$NetBSD: prim.c,v 1.9 2003/10/13 14:34:25 agc Exp $");
43 * Primitives for displaying the file on the screen.
46 #include <sys/types.h>
55 int hit_eof
; /* keeps track of how many times we hit end of file */
61 static int match(char *, char *);
62 static int badmark
__P((int));
65 * Check to see if the end of file is currently "displayed".
69 /*###72 [cc] conflicting types for `eof_check'%%%*/
76 * If the bottom line is empty, we are at EOF.
77 * If the bottom line ends at the file length,
78 * we must be just at EOF.
80 pos
= position(BOTTOM_PLUS_ONE
);
81 if (pos
== NULL_POSITION
|| pos
== ch_length())
86 * If the screen is "squished", repaint it.
87 * "Squished" means the first displayed line is not at the top
88 * of the screen; this can happen when we display a short file
93 /*###95 [cc] conflicting types for `squish_check'%%%*/
102 * Display n lines, scrolling forward, starting at position pos in the
103 * input file. "only_last" means display only the last screenful if
107 forw(n
, pos
, only_last
)
108 /*###109 [cc] conflicting types for `forw'%%%*/
113 static int first_time
= 1;
114 int eof
= 0, do_repaint
;
119 * do_repaint tells us not to display anything till the end,
120 * then just repaint the entire screen.
122 do_repaint
= (only_last
&& n
> sc_height
-1);
125 if (top_scroll
&& n
>= sc_height
- 1) {
127 * Start a new screen.
128 * {{ This is not really desirable if we happen
129 * to hit eof in the middle of this screen,
130 * but we don't yet know if that will happen. }}
140 * This is not contiguous with what is currently displayed.
141 * Clear the screen image (position table) and start a new
144 if (pos
!= position(BOTTOM_PLUS_ONE
)) {
150 } else if (!first_time
)
151 putstr("...skipping...\n");
155 for (short_file
= 0; --n
>= 0;) {
157 * Read the next line of input.
159 pos
= forw_line(pos
);
160 if (pos
== NULL_POSITION
) {
162 * end of file; copy the table if the file was
163 * too small for an entire screen.
166 if (position(TOP
) == NULL_POSITION
) {
174 * Add the position of the next line to the position table.
175 * Display the current line on the screen.
181 * If this is the first screen displayed and we hit an early
182 * EOF (i.e. before the requested number of lines), we
183 * "squish" the display down at the bottom of the screen.
185 if (first_time
&& line
== NULL
&& !top_scroll
) {
199 (void) currline(BOTTOM
);
203 * Display n lines, scrolling backward.
206 back(n
, pos
, only_last
)
207 /*###207 [cc] conflicting types for `back'%%%*/
215 do_repaint
= (n
> get_back_scroll() || (only_last
&& n
> sc_height
-1));
220 * Get the previous line of input.
222 pos
= back_line(pos
);
223 if (pos
== NULL_POSITION
)
226 * Add the position of the previous line to the position table.
227 * Display the line on the screen.
246 (void) currline(BOTTOM
);
250 * Display n more lines, forward.
251 * Start just after the line currently displayed at the bottom of the screen.
254 forward(n
, only_last
)
255 /*###254 [cc] conflicting types for `forward'%%%*/
263 * If we're trying to go forward from end-of-file,
264 * go on to the next file.
270 pos
= position(BOTTOM_PLUS_ONE
);
271 if (pos
== NULL_POSITION
)
276 forw(n
, pos
, only_last
);
280 * Display n more lines, backward.
281 * Start just before the line currently displayed at the top of the screen.
284 backward(n
, only_last
)
285 /*###283 [cc] conflicting types for `backward'%%%*/
293 * This will almost never happen, because the top line is almost
296 if (pos
== NULL_POSITION
)
298 back(n
, pos
, only_last
);
302 * Repaint the screen, starting from a specified position.
306 /*###303 [cc] conflicting types for `prepaint'%%%*/
310 forw(sc_height
-1, pos
, 0);
315 * Repaint the screen.
319 /*###315 [cc] conflicting types for `repaint'%%%*/
322 * Start at the line currently at the top of the screen
323 * and redisplay the screen.
325 prepaint(position(TOP
));
329 * Jump to the end of the file.
330 * It is more convenient to paint the screen backward,
331 * from the end of the file toward the beginning.
335 /*###330 [cc] conflicting types for `jump_forw'%%%*/
341 error("Cannot seek to end of file");
349 back(sc_height
- 1, pos
, 0);
353 * Jump to line n in the file.
357 /*###351 [cc] conflicting types for `jump_back'%%%*/
363 * This is done the slow way, by starting at the beginning
364 * of the file and counting newlines.
366 * {{ Now that we have line numbering (in linenum.c),
367 * we could improve on this by starting at the
368 * nearest known line rather than at the beginning. }}
370 if (ch_seek((off_t
)0)) {
372 * Probably a pipe with beginning of file no longer buffered.
373 * If he wants to go to line 1, we do the best we can,
374 * by going to the first line which is still buffered.
376 if (n
<= 1 && ch_beg_seek() == 0)
378 error("Cannot get to beginning of file");
383 * Start counting lines.
385 for (nlines
= 1; nlines
< n
; nlines
++)
386 while ((c
= ch_forw_get()) != '\n')
389 (void)sprintf(message
, "File has only %d lines",
398 * Jump to a specified percentage into the file.
399 * This is a poor compensation for not being able to
400 * quickly jump to a specific line number.
403 jump_percent(percent
)
404 /*###397 [cc] conflicting types for `jump_percent'%%%*/
411 * Determine the position in the file
412 * (the specified percentage of the file's length).
414 if ((len
= ch_length()) == NULL_POSITION
)
416 error("Don't know length of file");
419 pos
= (percent
* len
) / 100;
422 * Back up to the beginning of the line.
424 if (ch_seek(pos
) == 0)
426 while ((c
= ch_back_get()) != '\n' && c
!= EOI
)
429 (void) ch_forw_get();
436 * Jump to a specified position in the file.
440 /*###432 [cc] conflicting types for `jump_loc'%%%*/
446 if ((nline
= onscreen(pos
)) >= 0) {
448 * The line is currently displayed.
451 forw(nline
, position(BOTTOM_PLUS_ONE
), 0);
456 * Line is not on screen.
457 * Seek to the desired location.
460 error("Cannot seek to that position");
465 * See if the desired line is BEFORE the currently displayed screen.
466 * If so, then move forward far enough so the line we're on will be
467 * at the bottom of the screen, in order to be able to call back()
468 * to make the screen scroll backwards & put the line at the top of
470 * {{ This seems inefficient, but it's not so bad,
471 * since we can never move forward more than a
472 * screenful before we stop to redraw the screen. }}
474 tpos
= position(TOP
);
475 if (tpos
!= NULL_POSITION
&& pos
< tpos
) {
478 * Note that we can't forw_line() past tpos here,
479 * so there should be no EOI at this stage.
481 for (nline
= 0; npos
< tpos
&& nline
< sc_height
- 1; nline
++)
482 npos
= forw_line(npos
);
486 * More than a screenful back.
495 * Note that back() will repaint() if nline > back_scroll.
497 back(nline
, npos
, 0);
501 * Remember where we were; clear and paint the screen.
508 * The table of marks.
509 * A mark is simply a position in the file.
511 #define NMARKS (27) /* 26 for a-z plus one for quote */
512 #define LASTMARK (NMARKS-1) /* For quote */
513 static off_t marks
[NMARKS
];
516 * Initialize the mark table to show no marks are set.
520 /*###511 [cc] conflicting types for `init_mark'%%%*/
524 for (i
= 0; i
< NMARKS
; i
++)
525 marks
[i
] = NULL_POSITION
;
529 * See if a mark letter is valid (between a and z).
535 if (c
< 'a' || c
> 'z')
537 error("Choose a letter between 'a' and 'z'");
548 /*###538 [cc] conflicting types for `setmark'%%%*/
553 marks
[c
-'a'] = position(TOP
);
558 /*###547 [cc] conflicting types for `lastmark'%%%*/
560 marks
[LASTMARK
] = position(TOP
);
564 * Go to a previously set mark.
568 /*###556 [cc] conflicting types for `gomark'%%%*/
574 pos
= marks
[LASTMARK
];
575 if (pos
== NULL_POSITION
)
582 if (pos
== NULL_POSITION
) {
583 error("mark not set");
591 * Get the backwards scroll limit.
592 * Must call this function instead of just using the value of
593 * back_scroll, because the default case depends on sc_height and
594 * top_scroll, as well as back_scroll.
599 if (back_scroll
>= 0)
600 return (back_scroll
);
602 return (sc_height
- 2);
603 return (sc_height
- 1);
607 * Search for the n-th occurence of a specified pattern,
608 * either forward or backward.
611 search(search_forward
, pattern
, n
, wantmatch
)
628 static char *cpattern
= NULL
;
630 static char lpbuf
[100];
631 static char *last_pattern
= NULL
;
636 * For a caseless search, convert any uppercase in the pattern to
639 if (caseless
&& pattern
!= NULL
)
640 for (p
= pattern
; *p
; p
++)
641 if (isupper((unsigned char)*p
))
642 *p
= tolower((unsigned char)*p
);
646 * (re_comp handles a null pattern internally,
647 * so there is no need to check for a null pattern here.)
649 if ((errmsg
= re_comp(pattern
)) != NULL
)
656 if (pattern
== NULL
|| *pattern
== '\0')
659 * A null pattern means use the previous pattern.
660 * The compiled previous pattern is in cpattern, so just use it.
662 if (cpattern
== NULL
)
664 error("No previous regular expression");
670 * Otherwise compile the given pattern.
673 if ((s
= regcmp(pattern
, 0)) == NULL
)
675 error("Invalid pattern");
678 if (cpattern
!= NULL
)
683 if (pattern
== NULL
|| *pattern
== '\0')
686 * Null pattern means use the previous pattern.
688 if (last_pattern
== NULL
)
690 error("No previous regular expression");
693 pattern
= last_pattern
;
696 (void)strlcpy(lpbuf
, pattern
, sizeof(lpbuf
));
697 last_pattern
= lpbuf
;
703 * Figure out where to start the search.
706 if (position(TOP
) == NULL_POSITION
) {
708 * Nothing is currently displayed. Start at the beginning
709 * of the file. (This case is mainly for searches from the
713 } else if (!search_forward
) {
715 * Backward search: start just before the top line
716 * displayed on the screen.
721 * Start at the second screen line displayed on the screen.
723 pos
= position(TOP_PLUS_ONE
);
726 if (pos
== NULL_POSITION
)
729 * Can't find anyplace to start searching from.
731 error("Nothing to search");
735 linenum
= find_linenum(pos
);
739 * Get lines until we find a matching one or
740 * until we hit end-of-file (or beginning-of-file
741 * if we're going backwards).
745 * A signal aborts the search.
752 * Read the next line, and save the
753 * starting position of that line in linepos.
756 pos
= forw_raw_line(pos
);
762 * Read the previous line and save the
763 * starting position of that line in linepos.
765 pos
= back_raw_line(pos
);
771 if (pos
== NULL_POSITION
)
774 * We hit EOF/BOF without a match.
776 error("Pattern not found");
781 * If we're using line numbers, we might as well
782 * remember the information we have now (the position
783 * and line number of the current line).
786 add_lnum(linenum
, pos
);
789 * If this is a caseless search, convert uppercase in the
790 * input line to lowercase.
793 for (p
= q
= line
; *p
; p
++, q
++)
794 *q
= isupper((unsigned char)*p
) ?
795 tolower((unsigned char)*p
) : *p
;
798 * Remove any backspaces along with the preceding char.
799 * This allows us to match text which is underlined or
802 for (p
= q
= line
; *p
; p
++, q
++)
803 if (q
> line
&& *p
== '\b')
804 /* Delete BS and preceding char. */
807 /* Otherwise, just copy. */
811 * Test the next line to see if we have a match.
812 * This is done in a variety of ways, depending
813 * on what pattern matching functions are available.
816 linematch
= (regex(cpattern
, line
) != NULL
);
819 linematch
= (re_exec(line
) == 1);
821 linematch
= match(pattern
, line
);
825 * We are successful if wantmatch and linematch are
826 * both true (want a match and got it),
827 * or both false (want a non-match and got it).
829 if (((wantmatch
&& linematch
) || (!wantmatch
&& !linematch
)) &&
840 #if !defined(REGCMP) && !defined(RECOMP)
842 * We have neither regcmp() nor re_comp().
843 * We use this function to do simple pattern matching.
844 * It supports no metacharacters like *, etc.
852 for ( ; *buf
!= '\0'; buf
++)
854 for (pp
= pattern
, lp
= buf
; *pp
== *lp
; pp
++, lp
++)
855 if (*pp
== '\0' || *lp
== '\0')