1273e1e22Sbostic /*
2273e1e22Sbostic * Copyright (c) 1988 Mark Nudleman
3*76f06662Sbostic * Copyright (c) 1988, 1993
4*76f06662Sbostic * The Regents of the University of California. All rights reserved.
5273e1e22Sbostic *
69918a12eSbostic * %sccs.include.redist.c%
7273e1e22Sbostic */
8273e1e22Sbostic
9273e1e22Sbostic #ifndef lint
10*76f06662Sbostic static char sccsid[] = "@(#)prim.c 8.1 (Berkeley) 06/06/93";
11273e1e22Sbostic #endif /* not lint */
12273e1e22Sbostic
13273e1e22Sbostic /*
14273e1e22Sbostic * Primitives for displaying the file on the screen.
15273e1e22Sbostic */
16273e1e22Sbostic
17427e68f0Sbostic #include <sys/types.h>
18427e68f0Sbostic #include <stdio.h>
19427e68f0Sbostic #include <ctype.h>
20427e68f0Sbostic #include <less.h>
21273e1e22Sbostic
22427e68f0Sbostic int back_scroll = -1;
23427e68f0Sbostic int hit_eof; /* keeps track of how many times we hit end of file */
24427e68f0Sbostic int screen_trashed;
25273e1e22Sbostic
26273e1e22Sbostic static int squished;
27273e1e22Sbostic
28273e1e22Sbostic extern int sigs;
29273e1e22Sbostic extern int top_scroll;
30273e1e22Sbostic extern int sc_width, sc_height;
31273e1e22Sbostic extern int caseless;
32273e1e22Sbostic extern int linenums;
33273e1e22Sbostic extern int tagoption;
34427e68f0Sbostic extern char *line;
354046c8e7Sedward extern int retain_below;
36273e1e22Sbostic
37427e68f0Sbostic off_t position(), forw_line(), back_line(), forw_raw_line(), back_raw_line();
38427e68f0Sbostic off_t ch_length(), ch_tell();
39273e1e22Sbostic
40273e1e22Sbostic /*
41273e1e22Sbostic * Check to see if the end of file is currently "displayed".
42273e1e22Sbostic */
eof_check()43273e1e22Sbostic eof_check()
44273e1e22Sbostic {
45427e68f0Sbostic off_t pos;
46273e1e22Sbostic
47273e1e22Sbostic if (sigs)
48273e1e22Sbostic return;
49273e1e22Sbostic /*
50273e1e22Sbostic * If the bottom line is empty, we are at EOF.
51273e1e22Sbostic * If the bottom line ends at the file length,
52273e1e22Sbostic * we must be just at EOF.
53273e1e22Sbostic */
54273e1e22Sbostic pos = position(BOTTOM_PLUS_ONE);
55273e1e22Sbostic if (pos == NULL_POSITION || pos == ch_length())
56273e1e22Sbostic hit_eof++;
57273e1e22Sbostic }
58273e1e22Sbostic
59273e1e22Sbostic /*
60273e1e22Sbostic * If the screen is "squished", repaint it.
61273e1e22Sbostic * "Squished" means the first displayed line is not at the top
62273e1e22Sbostic * of the screen; this can happen when we display a short file
63273e1e22Sbostic * for the first time.
64273e1e22Sbostic */
squish_check()65273e1e22Sbostic squish_check()
66273e1e22Sbostic {
67427e68f0Sbostic if (squished) {
68273e1e22Sbostic squished = 0;
69273e1e22Sbostic repaint();
70273e1e22Sbostic }
71427e68f0Sbostic }
72273e1e22Sbostic
73273e1e22Sbostic /*
74427e68f0Sbostic * Display n lines, scrolling forward, starting at position pos in the
753f4685f3Sbostic * input file. "only_last" means display only the last screenful if
763f4685f3Sbostic * n > screen size.
77273e1e22Sbostic */
forw(n,pos,only_last)783f4685f3Sbostic forw(n, pos, only_last)
79273e1e22Sbostic register int n;
80427e68f0Sbostic off_t pos;
81273e1e22Sbostic int only_last;
82273e1e22Sbostic {
833f4685f3Sbostic extern int short_file;
84273e1e22Sbostic static int first_time = 1;
85427e68f0Sbostic int eof = 0, do_repaint;
86273e1e22Sbostic
87273e1e22Sbostic squish_check();
88273e1e22Sbostic
89273e1e22Sbostic /*
90273e1e22Sbostic * do_repaint tells us not to display anything till the end,
91273e1e22Sbostic * then just repaint the entire screen.
92273e1e22Sbostic */
93273e1e22Sbostic do_repaint = (only_last && n > sc_height-1);
94273e1e22Sbostic
95427e68f0Sbostic if (!do_repaint) {
96427e68f0Sbostic if (top_scroll && n >= sc_height - 1) {
97273e1e22Sbostic /*
98273e1e22Sbostic * Start a new screen.
99273e1e22Sbostic * {{ This is not really desirable if we happen
100273e1e22Sbostic * to hit eof in the middle of this screen,
101273e1e22Sbostic * but we don't yet know if that will happen. }}
102273e1e22Sbostic */
103273e1e22Sbostic clear();
104273e1e22Sbostic home();
105427e68f0Sbostic } else {
106273e1e22Sbostic lower_left();
107273e1e22Sbostic clear_eol();
108273e1e22Sbostic }
109273e1e22Sbostic
110273e1e22Sbostic /*
111427e68f0Sbostic * This is not contiguous with what is currently displayed.
112427e68f0Sbostic * Clear the screen image (position table) and start a new
113427e68f0Sbostic * screen.
114273e1e22Sbostic */
115427e68f0Sbostic if (pos != position(BOTTOM_PLUS_ONE)) {
116273e1e22Sbostic pos_clear();
117273e1e22Sbostic add_forw_pos(pos);
118427e68f0Sbostic if (top_scroll) {
119273e1e22Sbostic clear();
120273e1e22Sbostic home();
121273e1e22Sbostic } else if (!first_time)
122273e1e22Sbostic putstr("...skipping...\n");
123273e1e22Sbostic }
124273e1e22Sbostic }
125273e1e22Sbostic
1263f4685f3Sbostic for (short_file = 0; --n >= 0;) {
127273e1e22Sbostic /*
128273e1e22Sbostic * Read the next line of input.
129273e1e22Sbostic */
130273e1e22Sbostic pos = forw_line(pos);
131427e68f0Sbostic if (pos == NULL_POSITION) {
132273e1e22Sbostic /*
1333f4685f3Sbostic * end of file; copy the table if the file was
1343f4685f3Sbostic * too small for an entire screen.
135273e1e22Sbostic */
136273e1e22Sbostic eof = 1;
1373f4685f3Sbostic if (position(TOP) == NULL_POSITION) {
1383f4685f3Sbostic copytable();
1393f4685f3Sbostic if (!position(TOP))
1403f4685f3Sbostic short_file = 1;
1413f4685f3Sbostic }
142273e1e22Sbostic break;
143273e1e22Sbostic }
144273e1e22Sbostic /*
145273e1e22Sbostic * Add the position of the next line to the position table.
146273e1e22Sbostic * Display the current line on the screen.
147273e1e22Sbostic */
148273e1e22Sbostic add_forw_pos(pos);
149273e1e22Sbostic if (do_repaint)
150273e1e22Sbostic continue;
151273e1e22Sbostic /*
152427e68f0Sbostic * If this is the first screen displayed and we hit an early
153427e68f0Sbostic * EOF (i.e. before the requested number of lines), we
154427e68f0Sbostic * "squish" the display down at the bottom of the screen.
155427e68f0Sbostic * But don't do this if a -t option was given; it can cause
156427e68f0Sbostic * us to start the display after the beginning of the file,
157273e1e22Sbostic * and it is not appropriate to squish in that case.
158273e1e22Sbostic */
159427e68f0Sbostic if (first_time && line == NULL && !top_scroll && !tagoption) {
160273e1e22Sbostic squished = 1;
161273e1e22Sbostic continue;
162273e1e22Sbostic }
163273e1e22Sbostic put_line();
164273e1e22Sbostic }
165273e1e22Sbostic
166273e1e22Sbostic if (eof && !sigs)
167273e1e22Sbostic hit_eof++;
168273e1e22Sbostic else
169273e1e22Sbostic eof_check();
170427e68f0Sbostic if (do_repaint)
171273e1e22Sbostic repaint();
172273e1e22Sbostic first_time = 0;
173273e1e22Sbostic (void) currline(BOTTOM);
174273e1e22Sbostic }
175273e1e22Sbostic
176273e1e22Sbostic /*
177273e1e22Sbostic * Display n lines, scrolling backward.
178273e1e22Sbostic */
back(n,pos,only_last)1793f4685f3Sbostic back(n, pos, only_last)
180273e1e22Sbostic register int n;
181427e68f0Sbostic off_t pos;
182273e1e22Sbostic int only_last;
183273e1e22Sbostic {
184273e1e22Sbostic int do_repaint;
185273e1e22Sbostic
186273e1e22Sbostic squish_check();
187273e1e22Sbostic do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
188273e1e22Sbostic hit_eof = 0;
189273e1e22Sbostic while (--n >= 0)
190273e1e22Sbostic {
191273e1e22Sbostic /*
192273e1e22Sbostic * Get the previous line of input.
193273e1e22Sbostic */
194273e1e22Sbostic pos = back_line(pos);
195273e1e22Sbostic if (pos == NULL_POSITION)
196273e1e22Sbostic break;
197273e1e22Sbostic /*
198273e1e22Sbostic * Add the position of the previous line to the position table.
199273e1e22Sbostic * Display the line on the screen.
200273e1e22Sbostic */
201273e1e22Sbostic add_back_pos(pos);
202273e1e22Sbostic if (!do_repaint)
203273e1e22Sbostic {
2044046c8e7Sedward if (retain_below)
2054046c8e7Sedward {
2064046c8e7Sedward lower_left();
2074046c8e7Sedward clear_eol();
2084046c8e7Sedward }
209273e1e22Sbostic home();
210273e1e22Sbostic add_line();
211273e1e22Sbostic put_line();
212273e1e22Sbostic }
213273e1e22Sbostic }
214273e1e22Sbostic
215273e1e22Sbostic eof_check();
216427e68f0Sbostic if (do_repaint)
217273e1e22Sbostic repaint();
218273e1e22Sbostic (void) currline(BOTTOM);
219273e1e22Sbostic }
220273e1e22Sbostic
221273e1e22Sbostic /*
222273e1e22Sbostic * Display n more lines, forward.
223273e1e22Sbostic * Start just after the line currently displayed at the bottom of the screen.
224273e1e22Sbostic */
forward(n,only_last)225273e1e22Sbostic forward(n, only_last)
226273e1e22Sbostic int n;
227273e1e22Sbostic int only_last;
228273e1e22Sbostic {
229427e68f0Sbostic off_t pos;
230273e1e22Sbostic
231427e68f0Sbostic if (hit_eof) {
232273e1e22Sbostic /*
233427e68f0Sbostic * If we're trying to go forward from end-of-file,
234427e68f0Sbostic * go on to the next file.
235273e1e22Sbostic */
236273e1e22Sbostic next_file(1);
237273e1e22Sbostic return;
238273e1e22Sbostic }
239273e1e22Sbostic
240273e1e22Sbostic pos = position(BOTTOM_PLUS_ONE);
241273e1e22Sbostic if (pos == NULL_POSITION)
242273e1e22Sbostic {
243273e1e22Sbostic hit_eof++;
244273e1e22Sbostic return;
245273e1e22Sbostic }
2463f4685f3Sbostic forw(n, pos, only_last);
247273e1e22Sbostic }
248273e1e22Sbostic
249273e1e22Sbostic /*
250273e1e22Sbostic * Display n more lines, backward.
251273e1e22Sbostic * Start just before the line currently displayed at the top of the screen.
252273e1e22Sbostic */
backward(n,only_last)253273e1e22Sbostic backward(n, only_last)
254273e1e22Sbostic int n;
255273e1e22Sbostic int only_last;
256273e1e22Sbostic {
257427e68f0Sbostic off_t pos;
258273e1e22Sbostic
259273e1e22Sbostic pos = position(TOP);
260273e1e22Sbostic /*
261427e68f0Sbostic * This will almost never happen, because the top line is almost
262427e68f0Sbostic * never empty.
263273e1e22Sbostic */
264427e68f0Sbostic if (pos == NULL_POSITION)
265273e1e22Sbostic return;
2663f4685f3Sbostic back(n, pos, only_last);
267273e1e22Sbostic }
268273e1e22Sbostic
269273e1e22Sbostic /*
270273e1e22Sbostic * Repaint the screen, starting from a specified position.
271273e1e22Sbostic */
prepaint(pos)272273e1e22Sbostic prepaint(pos)
273427e68f0Sbostic off_t pos;
274273e1e22Sbostic {
275273e1e22Sbostic hit_eof = 0;
2763f4685f3Sbostic forw(sc_height-1, pos, 0);
277273e1e22Sbostic screen_trashed = 0;
278273e1e22Sbostic }
279273e1e22Sbostic
280273e1e22Sbostic /*
281273e1e22Sbostic * Repaint the screen.
282273e1e22Sbostic */
repaint()283273e1e22Sbostic repaint()
284273e1e22Sbostic {
285273e1e22Sbostic /*
286273e1e22Sbostic * Start at the line currently at the top of the screen
287273e1e22Sbostic * and redisplay the screen.
288273e1e22Sbostic */
289273e1e22Sbostic prepaint(position(TOP));
290273e1e22Sbostic }
291273e1e22Sbostic
292273e1e22Sbostic /*
293273e1e22Sbostic * Jump to the end of the file.
294273e1e22Sbostic * It is more convenient to paint the screen backward,
295273e1e22Sbostic * from the end of the file toward the beginning.
296273e1e22Sbostic */
jump_forw()297273e1e22Sbostic jump_forw()
298273e1e22Sbostic {
299427e68f0Sbostic off_t pos;
300273e1e22Sbostic
301273e1e22Sbostic if (ch_end_seek())
302273e1e22Sbostic {
303273e1e22Sbostic error("Cannot seek to end of file");
304273e1e22Sbostic return;
305273e1e22Sbostic }
306273e1e22Sbostic lastmark();
307273e1e22Sbostic pos = ch_tell();
308273e1e22Sbostic clear();
309273e1e22Sbostic pos_clear();
310273e1e22Sbostic add_back_pos(pos);
3113f4685f3Sbostic back(sc_height - 1, pos, 0);
312273e1e22Sbostic }
313273e1e22Sbostic
314273e1e22Sbostic /*
315273e1e22Sbostic * Jump to line n in the file.
316273e1e22Sbostic */
jump_back(n)317273e1e22Sbostic jump_back(n)
318273e1e22Sbostic register int n;
319273e1e22Sbostic {
320427e68f0Sbostic register int c, nlines;
321273e1e22Sbostic
322273e1e22Sbostic /*
323273e1e22Sbostic * This is done the slow way, by starting at the beginning
324273e1e22Sbostic * of the file and counting newlines.
325273e1e22Sbostic *
326273e1e22Sbostic * {{ Now that we have line numbering (in linenum.c),
327273e1e22Sbostic * we could improve on this by starting at the
328273e1e22Sbostic * nearest known line rather than at the beginning. }}
329273e1e22Sbostic */
330427e68f0Sbostic if (ch_seek((off_t)0)) {
331273e1e22Sbostic /*
332273e1e22Sbostic * Probably a pipe with beginning of file no longer buffered.
333273e1e22Sbostic * If he wants to go to line 1, we do the best we can,
334273e1e22Sbostic * by going to the first line which is still buffered.
335273e1e22Sbostic */
336273e1e22Sbostic if (n <= 1 && ch_beg_seek() == 0)
337273e1e22Sbostic jump_loc(ch_tell());
338273e1e22Sbostic error("Cannot get to beginning of file");
339273e1e22Sbostic return;
340273e1e22Sbostic }
341273e1e22Sbostic
342273e1e22Sbostic /*
343273e1e22Sbostic * Start counting lines.
344273e1e22Sbostic */
345273e1e22Sbostic for (nlines = 1; nlines < n; nlines++)
346273e1e22Sbostic while ((c = ch_forw_get()) != '\n')
347427e68f0Sbostic if (c == EOI) {
348273e1e22Sbostic char message[40];
3498033092dSbostic (void)sprintf(message, "File has only %d lines",
350273e1e22Sbostic nlines - 1);
351273e1e22Sbostic error(message);
352273e1e22Sbostic return;
353273e1e22Sbostic }
354273e1e22Sbostic jump_loc(ch_tell());
355273e1e22Sbostic }
356273e1e22Sbostic
357273e1e22Sbostic /*
358273e1e22Sbostic * Jump to a specified percentage into the file.
359273e1e22Sbostic * This is a poor compensation for not being able to
360273e1e22Sbostic * quickly jump to a specific line number.
361273e1e22Sbostic */
jump_percent(percent)362273e1e22Sbostic jump_percent(percent)
363273e1e22Sbostic int percent;
364273e1e22Sbostic {
365427e68f0Sbostic off_t pos, len, ch_length();
366273e1e22Sbostic register int c;
367273e1e22Sbostic
368273e1e22Sbostic /*
369273e1e22Sbostic * Determine the position in the file
370273e1e22Sbostic * (the specified percentage of the file's length).
371273e1e22Sbostic */
372273e1e22Sbostic if ((len = ch_length()) == NULL_POSITION)
373273e1e22Sbostic {
374273e1e22Sbostic error("Don't know length of file");
375273e1e22Sbostic return;
376273e1e22Sbostic }
377273e1e22Sbostic pos = (percent * len) / 100;
378273e1e22Sbostic
379273e1e22Sbostic /*
380273e1e22Sbostic * Back up to the beginning of the line.
381273e1e22Sbostic */
382273e1e22Sbostic if (ch_seek(pos) == 0)
383273e1e22Sbostic {
384273e1e22Sbostic while ((c = ch_back_get()) != '\n' && c != EOI)
385273e1e22Sbostic ;
386273e1e22Sbostic if (c == '\n')
387273e1e22Sbostic (void) ch_forw_get();
388273e1e22Sbostic pos = ch_tell();
389273e1e22Sbostic }
390273e1e22Sbostic jump_loc(pos);
391273e1e22Sbostic }
392273e1e22Sbostic
393273e1e22Sbostic /*
394273e1e22Sbostic * Jump to a specified position in the file.
395273e1e22Sbostic */
jump_loc(pos)396273e1e22Sbostic jump_loc(pos)
397427e68f0Sbostic off_t pos;
398273e1e22Sbostic {
399273e1e22Sbostic register int nline;
400427e68f0Sbostic off_t tpos;
401273e1e22Sbostic
402427e68f0Sbostic if ((nline = onscreen(pos)) >= 0) {
403273e1e22Sbostic /*
404273e1e22Sbostic * The line is currently displayed.
405273e1e22Sbostic * Just scroll there.
406273e1e22Sbostic */
4073f4685f3Sbostic forw(nline, position(BOTTOM_PLUS_ONE), 0);
408273e1e22Sbostic return;
409273e1e22Sbostic }
410273e1e22Sbostic
411273e1e22Sbostic /*
412273e1e22Sbostic * Line is not on screen.
413273e1e22Sbostic * Seek to the desired location.
414273e1e22Sbostic */
415427e68f0Sbostic if (ch_seek(pos)) {
416273e1e22Sbostic error("Cannot seek to that position");
417273e1e22Sbostic return;
418273e1e22Sbostic }
419273e1e22Sbostic
420273e1e22Sbostic /*
421427e68f0Sbostic * See if the desired line is BEFORE the currently displayed screen.
422427e68f0Sbostic * If so, then move forward far enough so the line we're on will be
423427e68f0Sbostic * at the bottom of the screen, in order to be able to call back()
424427e68f0Sbostic * to make the screen scroll backwards & put the line at the top of
425427e68f0Sbostic * the screen.
426273e1e22Sbostic * {{ This seems inefficient, but it's not so bad,
427273e1e22Sbostic * since we can never move forward more than a
428273e1e22Sbostic * screenful before we stop to redraw the screen. }}
429273e1e22Sbostic */
430273e1e22Sbostic tpos = position(TOP);
431427e68f0Sbostic if (tpos != NULL_POSITION && pos < tpos) {
432427e68f0Sbostic off_t npos = pos;
433273e1e22Sbostic /*
434273e1e22Sbostic * Note that we can't forw_line() past tpos here,
435273e1e22Sbostic * so there should be no EOI at this stage.
436273e1e22Sbostic */
437273e1e22Sbostic for (nline = 0; npos < tpos && nline < sc_height - 1; nline++)
438273e1e22Sbostic npos = forw_line(npos);
439273e1e22Sbostic
440427e68f0Sbostic if (npos < tpos) {
441273e1e22Sbostic /*
442273e1e22Sbostic * More than a screenful back.
443273e1e22Sbostic */
444273e1e22Sbostic lastmark();
445273e1e22Sbostic clear();
446273e1e22Sbostic pos_clear();
447273e1e22Sbostic add_back_pos(npos);
448273e1e22Sbostic }
449273e1e22Sbostic
450273e1e22Sbostic /*
451273e1e22Sbostic * Note that back() will repaint() if nline > back_scroll.
452273e1e22Sbostic */
4533f4685f3Sbostic back(nline, npos, 0);
454273e1e22Sbostic return;
455273e1e22Sbostic }
456273e1e22Sbostic /*
457273e1e22Sbostic * Remember where we were; clear and paint the screen.
458273e1e22Sbostic */
459273e1e22Sbostic lastmark();
460273e1e22Sbostic prepaint(pos);
461273e1e22Sbostic }
462273e1e22Sbostic
463273e1e22Sbostic /*
464273e1e22Sbostic * The table of marks.
465273e1e22Sbostic * A mark is simply a position in the file.
466273e1e22Sbostic */
467273e1e22Sbostic #define NMARKS (27) /* 26 for a-z plus one for quote */
468273e1e22Sbostic #define LASTMARK (NMARKS-1) /* For quote */
469427e68f0Sbostic static off_t marks[NMARKS];
470273e1e22Sbostic
471273e1e22Sbostic /*
472273e1e22Sbostic * Initialize the mark table to show no marks are set.
473273e1e22Sbostic */
init_mark()474273e1e22Sbostic init_mark()
475273e1e22Sbostic {
476273e1e22Sbostic int i;
477273e1e22Sbostic
478273e1e22Sbostic for (i = 0; i < NMARKS; i++)
479273e1e22Sbostic marks[i] = NULL_POSITION;
480273e1e22Sbostic }
481273e1e22Sbostic
482273e1e22Sbostic /*
483273e1e22Sbostic * See if a mark letter is valid (between a and z).
484273e1e22Sbostic */
485273e1e22Sbostic static int
badmark(c)486273e1e22Sbostic badmark(c)
487273e1e22Sbostic int c;
488273e1e22Sbostic {
489273e1e22Sbostic if (c < 'a' || c > 'z')
490273e1e22Sbostic {
491273e1e22Sbostic error("Choose a letter between 'a' and 'z'");
492273e1e22Sbostic return (1);
493273e1e22Sbostic }
494273e1e22Sbostic return (0);
495273e1e22Sbostic }
496273e1e22Sbostic
497273e1e22Sbostic /*
498273e1e22Sbostic * Set a mark.
499273e1e22Sbostic */
setmark(c)500273e1e22Sbostic setmark(c)
501273e1e22Sbostic int c;
502273e1e22Sbostic {
503273e1e22Sbostic if (badmark(c))
504273e1e22Sbostic return;
505273e1e22Sbostic marks[c-'a'] = position(TOP);
506273e1e22Sbostic }
507273e1e22Sbostic
lastmark()508273e1e22Sbostic lastmark()
509273e1e22Sbostic {
510273e1e22Sbostic marks[LASTMARK] = position(TOP);
511273e1e22Sbostic }
512273e1e22Sbostic
513273e1e22Sbostic /*
514273e1e22Sbostic * Go to a previously set mark.
515273e1e22Sbostic */
gomark(c)516273e1e22Sbostic gomark(c)
517273e1e22Sbostic int c;
518273e1e22Sbostic {
519427e68f0Sbostic off_t pos;
520273e1e22Sbostic
5212dc77915Sbostic if (c == '\'') {
522273e1e22Sbostic pos = marks[LASTMARK];
523273e1e22Sbostic if (pos == NULL_POSITION)
5242dc77915Sbostic pos = 0;
5252dc77915Sbostic }
5262dc77915Sbostic else {
5272dc77915Sbostic if (badmark(c))
5282dc77915Sbostic return;
5292dc77915Sbostic pos = marks[c-'a'];
5302dc77915Sbostic if (pos == NULL_POSITION) {
531273e1e22Sbostic error("mark not set");
5322dc77915Sbostic return;
5332dc77915Sbostic }
5342dc77915Sbostic }
535273e1e22Sbostic jump_loc(pos);
536273e1e22Sbostic }
537273e1e22Sbostic
538273e1e22Sbostic /*
539273e1e22Sbostic * Get the backwards scroll limit.
540273e1e22Sbostic * Must call this function instead of just using the value of
541273e1e22Sbostic * back_scroll, because the default case depends on sc_height and
542273e1e22Sbostic * top_scroll, as well as back_scroll.
543273e1e22Sbostic */
get_back_scroll()544273e1e22Sbostic get_back_scroll()
545273e1e22Sbostic {
546273e1e22Sbostic if (back_scroll >= 0)
547273e1e22Sbostic return (back_scroll);
548273e1e22Sbostic if (top_scroll)
549273e1e22Sbostic return (sc_height - 2);
550273e1e22Sbostic return (sc_height - 1);
551273e1e22Sbostic }
552273e1e22Sbostic
553273e1e22Sbostic /*
554273e1e22Sbostic * Search for the n-th occurence of a specified pattern,
555273e1e22Sbostic * either forward or backward.
556273e1e22Sbostic */
search(search_forward,pattern,n,wantmatch)557273e1e22Sbostic search(search_forward, pattern, n, wantmatch)
558273e1e22Sbostic register int search_forward;
559273e1e22Sbostic register char *pattern;
560273e1e22Sbostic register int n;
561273e1e22Sbostic int wantmatch;
562273e1e22Sbostic {
563427e68f0Sbostic off_t pos, linepos;
564273e1e22Sbostic register char *p;
565273e1e22Sbostic register char *q;
566273e1e22Sbostic int linenum;
567273e1e22Sbostic int linematch;
568427e68f0Sbostic #ifdef RECOMP
569273e1e22Sbostic char *re_comp();
570273e1e22Sbostic char *errmsg;
571273e1e22Sbostic #else
572427e68f0Sbostic #ifdef REGCMP
573273e1e22Sbostic char *regcmp();
574273e1e22Sbostic static char *cpattern = NULL;
575273e1e22Sbostic #else
576273e1e22Sbostic static char lpbuf[100];
577273e1e22Sbostic static char *last_pattern = NULL;
5788033092dSbostic char *strcpy();
579273e1e22Sbostic #endif
580273e1e22Sbostic #endif
581273e1e22Sbostic
582273e1e22Sbostic /*
583427e68f0Sbostic * For a caseless search, convert any uppercase in the pattern to
584427e68f0Sbostic * lowercase.
585273e1e22Sbostic */
586427e68f0Sbostic if (caseless && pattern != NULL)
587427e68f0Sbostic for (p = pattern; *p; p++)
588427e68f0Sbostic if (isupper(*p))
589427e68f0Sbostic *p = tolower(*p);
590427e68f0Sbostic #ifdef RECOMP
591273e1e22Sbostic
592273e1e22Sbostic /*
593273e1e22Sbostic * (re_comp handles a null pattern internally,
594273e1e22Sbostic * so there is no need to check for a null pattern here.)
595273e1e22Sbostic */
596273e1e22Sbostic if ((errmsg = re_comp(pattern)) != NULL)
597273e1e22Sbostic {
598273e1e22Sbostic error(errmsg);
599427e68f0Sbostic return(0);
600273e1e22Sbostic }
601273e1e22Sbostic #else
602427e68f0Sbostic #ifdef REGCMP
603273e1e22Sbostic if (pattern == NULL || *pattern == '\0')
604273e1e22Sbostic {
605273e1e22Sbostic /*
606273e1e22Sbostic * A null pattern means use the previous pattern.
607273e1e22Sbostic * The compiled previous pattern is in cpattern, so just use it.
608273e1e22Sbostic */
609273e1e22Sbostic if (cpattern == NULL)
610273e1e22Sbostic {
611273e1e22Sbostic error("No previous regular expression");
612427e68f0Sbostic return(0);
613273e1e22Sbostic }
614273e1e22Sbostic } else
615273e1e22Sbostic {
616273e1e22Sbostic /*
617273e1e22Sbostic * Otherwise compile the given pattern.
618273e1e22Sbostic */
619273e1e22Sbostic char *s;
620273e1e22Sbostic if ((s = regcmp(pattern, 0)) == NULL)
621273e1e22Sbostic {
622273e1e22Sbostic error("Invalid pattern");
623427e68f0Sbostic return(0);
624273e1e22Sbostic }
625273e1e22Sbostic if (cpattern != NULL)
626273e1e22Sbostic free(cpattern);
627273e1e22Sbostic cpattern = s;
628273e1e22Sbostic }
629273e1e22Sbostic #else
630273e1e22Sbostic if (pattern == NULL || *pattern == '\0')
631273e1e22Sbostic {
632273e1e22Sbostic /*
633273e1e22Sbostic * Null pattern means use the previous pattern.
634273e1e22Sbostic */
635273e1e22Sbostic if (last_pattern == NULL)
636273e1e22Sbostic {
637273e1e22Sbostic error("No previous regular expression");
638427e68f0Sbostic return(0);
639273e1e22Sbostic }
640273e1e22Sbostic pattern = last_pattern;
641273e1e22Sbostic } else
642273e1e22Sbostic {
6438033092dSbostic (void)strcpy(lpbuf, pattern);
644273e1e22Sbostic last_pattern = lpbuf;
645273e1e22Sbostic }
646273e1e22Sbostic #endif
647273e1e22Sbostic #endif
648273e1e22Sbostic
649273e1e22Sbostic /*
650273e1e22Sbostic * Figure out where to start the search.
651273e1e22Sbostic */
652273e1e22Sbostic
653427e68f0Sbostic if (position(TOP) == NULL_POSITION) {
654273e1e22Sbostic /*
655427e68f0Sbostic * Nothing is currently displayed. Start at the beginning
656427e68f0Sbostic * of the file. (This case is mainly for searches from the
657427e68f0Sbostic * command line.
658273e1e22Sbostic */
659427e68f0Sbostic pos = (off_t)0;
660427e68f0Sbostic } else if (!search_forward) {
661273e1e22Sbostic /*
662273e1e22Sbostic * Backward search: start just before the top line
663273e1e22Sbostic * displayed on the screen.
664273e1e22Sbostic */
665273e1e22Sbostic pos = position(TOP);
666427e68f0Sbostic } else {
667273e1e22Sbostic /*
668273e1e22Sbostic * Start at the second screen line displayed on the screen.
669273e1e22Sbostic */
670273e1e22Sbostic pos = position(TOP_PLUS_ONE);
671273e1e22Sbostic }
672273e1e22Sbostic
673273e1e22Sbostic if (pos == NULL_POSITION)
674273e1e22Sbostic {
675273e1e22Sbostic /*
676273e1e22Sbostic * Can't find anyplace to start searching from.
677273e1e22Sbostic */
678273e1e22Sbostic error("Nothing to search");
679427e68f0Sbostic return(0);
680273e1e22Sbostic }
681273e1e22Sbostic
682273e1e22Sbostic linenum = find_linenum(pos);
683273e1e22Sbostic for (;;)
684273e1e22Sbostic {
685273e1e22Sbostic /*
686273e1e22Sbostic * Get lines until we find a matching one or
687273e1e22Sbostic * until we hit end-of-file (or beginning-of-file
688273e1e22Sbostic * if we're going backwards).
689273e1e22Sbostic */
690273e1e22Sbostic if (sigs)
691273e1e22Sbostic /*
692273e1e22Sbostic * A signal aborts the search.
693273e1e22Sbostic */
694427e68f0Sbostic return(0);
695273e1e22Sbostic
696273e1e22Sbostic if (search_forward)
697273e1e22Sbostic {
698273e1e22Sbostic /*
699273e1e22Sbostic * Read the next line, and save the
700273e1e22Sbostic * starting position of that line in linepos.
701273e1e22Sbostic */
702273e1e22Sbostic linepos = pos;
703273e1e22Sbostic pos = forw_raw_line(pos);
704273e1e22Sbostic if (linenum != 0)
705273e1e22Sbostic linenum++;
706273e1e22Sbostic } else
707273e1e22Sbostic {
708273e1e22Sbostic /*
709273e1e22Sbostic * Read the previous line and save the
710273e1e22Sbostic * starting position of that line in linepos.
711273e1e22Sbostic */
712273e1e22Sbostic pos = back_raw_line(pos);
713273e1e22Sbostic linepos = pos;
714273e1e22Sbostic if (linenum != 0)
715273e1e22Sbostic linenum--;
716273e1e22Sbostic }
717273e1e22Sbostic
718273e1e22Sbostic if (pos == NULL_POSITION)
719273e1e22Sbostic {
720273e1e22Sbostic /*
721273e1e22Sbostic * We hit EOF/BOF without a match.
722273e1e22Sbostic */
723273e1e22Sbostic error("Pattern not found");
724427e68f0Sbostic return(0);
725273e1e22Sbostic }
726273e1e22Sbostic
727273e1e22Sbostic /*
728273e1e22Sbostic * If we're using line numbers, we might as well
729273e1e22Sbostic * remember the information we have now (the position
730273e1e22Sbostic * and line number of the current line).
731273e1e22Sbostic */
732273e1e22Sbostic if (linenums)
733273e1e22Sbostic add_lnum(linenum, pos);
734273e1e22Sbostic
735273e1e22Sbostic /*
736427e68f0Sbostic * If this is a caseless search, convert uppercase in the
737427e68f0Sbostic * input line to lowercase.
738273e1e22Sbostic */
739427e68f0Sbostic if (caseless)
740427e68f0Sbostic for (p = q = line; *p; p++, q++)
741427e68f0Sbostic *q = isupper(*p) ? tolower(*p) : *p;
742427e68f0Sbostic
743427e68f0Sbostic /*
744427e68f0Sbostic * Remove any backspaces along with the preceeding char.
745427e68f0Sbostic * This allows us to match text which is underlined or
746427e68f0Sbostic * overstruck.
747427e68f0Sbostic */
748427e68f0Sbostic for (p = q = line; *p; p++, q++)
749427e68f0Sbostic if (q > line && *p == '\b')
750273e1e22Sbostic /* Delete BS and preceeding char. */
751273e1e22Sbostic q -= 2;
752273e1e22Sbostic else
753273e1e22Sbostic /* Otherwise, just copy. */
754273e1e22Sbostic *q = *p;
755273e1e22Sbostic
756273e1e22Sbostic /*
757273e1e22Sbostic * Test the next line to see if we have a match.
758273e1e22Sbostic * This is done in a variety of ways, depending
759273e1e22Sbostic * on what pattern matching functions are available.
760273e1e22Sbostic */
761427e68f0Sbostic #ifdef REGCMP
762273e1e22Sbostic linematch = (regex(cpattern, line) != NULL);
763273e1e22Sbostic #else
764427e68f0Sbostic #ifdef RECOMP
765273e1e22Sbostic linematch = (re_exec(line) == 1);
766273e1e22Sbostic #else
767273e1e22Sbostic linematch = match(pattern, line);
768273e1e22Sbostic #endif
769273e1e22Sbostic #endif
770273e1e22Sbostic /*
771273e1e22Sbostic * We are successful if wantmatch and linematch are
772273e1e22Sbostic * both true (want a match and got it),
773273e1e22Sbostic * or both false (want a non-match and got it).
774273e1e22Sbostic */
775273e1e22Sbostic if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
776273e1e22Sbostic --n <= 0)
777273e1e22Sbostic /*
778273e1e22Sbostic * Found the line.
779273e1e22Sbostic */
780273e1e22Sbostic break;
781273e1e22Sbostic }
782273e1e22Sbostic jump_loc(linepos);
783427e68f0Sbostic return(1);
784273e1e22Sbostic }
785273e1e22Sbostic
786427e68f0Sbostic #if !defined(REGCMP) && !defined(RECOMP)
787273e1e22Sbostic /*
788273e1e22Sbostic * We have neither regcmp() nor re_comp().
789273e1e22Sbostic * We use this function to do simple pattern matching.
790273e1e22Sbostic * It supports no metacharacters like *, etc.
791273e1e22Sbostic */
792427e68f0Sbostic static
match(pattern,buf)793273e1e22Sbostic match(pattern, buf)
794273e1e22Sbostic char *pattern, *buf;
795273e1e22Sbostic {
796273e1e22Sbostic register char *pp, *lp;
797273e1e22Sbostic
798273e1e22Sbostic for ( ; *buf != '\0'; buf++)
799273e1e22Sbostic {
800273e1e22Sbostic for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
801273e1e22Sbostic if (*pp == '\0' || *lp == '\0')
802273e1e22Sbostic break;
803273e1e22Sbostic if (*pp == '\0')
804273e1e22Sbostic return (1);
805273e1e22Sbostic }
806273e1e22Sbostic return (0);
807273e1e22Sbostic }
808273e1e22Sbostic #endif
809