xref: /original-bsd/usr.bin/more/prim.c (revision 8033092d)
1273e1e22Sbostic /*
2273e1e22Sbostic  * Copyright (c) 1988 Mark Nudleman
3273e1e22Sbostic  * Copyright (c) 1988 Regents of the University of California.
4273e1e22Sbostic  * All rights reserved.
5273e1e22Sbostic  *
6273e1e22Sbostic  * This code is derived from software contributed to Berkeley by
7273e1e22Sbostic  * Mark Nudleman.
8273e1e22Sbostic  *
9273e1e22Sbostic  * Redistribution and use in source and binary forms are permitted
10273e1e22Sbostic  * provided that the above copyright notice and this paragraph are
11273e1e22Sbostic  * duplicated in all such forms and that any documentation,
12273e1e22Sbostic  * advertising materials, and other materials related to such
13273e1e22Sbostic  * distribution and use acknowledge that the software was developed
14273e1e22Sbostic  * by the University of California, Berkeley.  The name of the
15273e1e22Sbostic  * University may not be used to endorse or promote products derived
16273e1e22Sbostic  * from this software without specific prior written permission.
17273e1e22Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
18273e1e22Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
19273e1e22Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20273e1e22Sbostic  */
21273e1e22Sbostic 
22273e1e22Sbostic #ifndef lint
23*8033092dSbostic static char sccsid[] = "@(#)prim.c	5.3 (Berkeley) 07/22/88";
24273e1e22Sbostic #endif /* not lint */
25273e1e22Sbostic 
26273e1e22Sbostic /*
27273e1e22Sbostic  * Primitives for displaying the file on the screen.
28273e1e22Sbostic  */
29273e1e22Sbostic 
30273e1e22Sbostic #include "less.h"
31273e1e22Sbostic #include "position.h"
32273e1e22Sbostic 
33273e1e22Sbostic public int hit_eof;	/* Keeps track of how many times we hit end of file */
34273e1e22Sbostic public int screen_trashed;
35273e1e22Sbostic 
36273e1e22Sbostic static int squished;
37273e1e22Sbostic 
38273e1e22Sbostic extern int quiet;
39273e1e22Sbostic extern int sigs;
40273e1e22Sbostic extern int how_search;
41273e1e22Sbostic extern int top_scroll;
42273e1e22Sbostic extern int back_scroll;
43273e1e22Sbostic extern int sc_width, sc_height;
44273e1e22Sbostic extern int quit_at_eof;
45273e1e22Sbostic extern int caseless;
46273e1e22Sbostic extern int linenums;
47273e1e22Sbostic extern int plusoption;
48273e1e22Sbostic extern char *line;
49273e1e22Sbostic extern char *first_cmd;
50273e1e22Sbostic extern int tagoption;
51273e1e22Sbostic 
52273e1e22Sbostic /*
53273e1e22Sbostic  * Sound the bell to indicate he is trying to move past end of file.
54273e1e22Sbostic  */
55273e1e22Sbostic 	static void
56273e1e22Sbostic eof_bell()
57273e1e22Sbostic {
58273e1e22Sbostic 	if (quiet == NOT_QUIET)
59273e1e22Sbostic 		bell();
60273e1e22Sbostic 	else
61273e1e22Sbostic 		vbell();
62273e1e22Sbostic }
63273e1e22Sbostic 
64273e1e22Sbostic /*
65273e1e22Sbostic  * Check to see if the end of file is currently "displayed".
66273e1e22Sbostic  */
67273e1e22Sbostic 	static void
68273e1e22Sbostic eof_check()
69273e1e22Sbostic {
70273e1e22Sbostic 	POSITION pos;
71273e1e22Sbostic 
72273e1e22Sbostic 	if (sigs)
73273e1e22Sbostic 		return;
74273e1e22Sbostic 	/*
75273e1e22Sbostic 	 * If the bottom line is empty, we are at EOF.
76273e1e22Sbostic 	 * If the bottom line ends at the file length,
77273e1e22Sbostic 	 * we must be just at EOF.
78273e1e22Sbostic 	 */
79273e1e22Sbostic 	pos = position(BOTTOM_PLUS_ONE);
80273e1e22Sbostic 	if (pos == NULL_POSITION || pos == ch_length())
81273e1e22Sbostic 		hit_eof++;
82273e1e22Sbostic }
83273e1e22Sbostic 
84273e1e22Sbostic /*
85273e1e22Sbostic  * If the screen is "squished", repaint it.
86273e1e22Sbostic  * "Squished" means the first displayed line is not at the top
87273e1e22Sbostic  * of the screen; this can happen when we display a short file
88273e1e22Sbostic  * for the first time.
89273e1e22Sbostic  */
90273e1e22Sbostic 	static void
91273e1e22Sbostic squish_check()
92273e1e22Sbostic {
93273e1e22Sbostic 	if (!squished)
94273e1e22Sbostic 		return;
95273e1e22Sbostic 	squished = 0;
96273e1e22Sbostic 	repaint();
97273e1e22Sbostic }
98273e1e22Sbostic 
99273e1e22Sbostic /*
100273e1e22Sbostic  * Display n lines, scrolling forward,
101273e1e22Sbostic  * starting at position pos in the input file.
102273e1e22Sbostic  * "force" means display the n lines even if we hit end of file.
103273e1e22Sbostic  * "only_last" means display only the last screenful if n > screen size.
104273e1e22Sbostic  */
105273e1e22Sbostic 	static void
106273e1e22Sbostic forw(n, pos, force, only_last)
107273e1e22Sbostic 	register int n;
108273e1e22Sbostic 	POSITION pos;
109273e1e22Sbostic 	int force;
110273e1e22Sbostic 	int only_last;
111273e1e22Sbostic {
112273e1e22Sbostic 	int eof = 0;
113273e1e22Sbostic 	int nlines = 0;
114273e1e22Sbostic 	int do_repaint;
115273e1e22Sbostic 	static int first_time = 1;
116273e1e22Sbostic 
117273e1e22Sbostic 	squish_check();
118273e1e22Sbostic 
119273e1e22Sbostic 	/*
120273e1e22Sbostic 	 * do_repaint tells us not to display anything till the end,
121273e1e22Sbostic 	 * then just repaint the entire screen.
122273e1e22Sbostic 	 */
123273e1e22Sbostic 	do_repaint = (only_last && n > sc_height-1);
124273e1e22Sbostic 
125273e1e22Sbostic 	if (!do_repaint)
126273e1e22Sbostic 	{
127273e1e22Sbostic 		if (top_scroll && n >= sc_height - 1)
128273e1e22Sbostic 		{
129273e1e22Sbostic 			/*
130273e1e22Sbostic 			 * Start a new screen.
131273e1e22Sbostic 			 * {{ This is not really desirable if we happen
132273e1e22Sbostic 			 *    to hit eof in the middle of this screen,
133273e1e22Sbostic 			 *    but we don't yet know if that will happen. }}
134273e1e22Sbostic 			 */
135273e1e22Sbostic 			if (top_scroll == 2)
136273e1e22Sbostic 				clear();
137273e1e22Sbostic 			home();
138273e1e22Sbostic 			force = 1;
139273e1e22Sbostic 		} else
140273e1e22Sbostic 		{
141273e1e22Sbostic 			lower_left();
142273e1e22Sbostic 			clear_eol();
143273e1e22Sbostic 		}
144273e1e22Sbostic 
145273e1e22Sbostic 		if (pos != position(BOTTOM_PLUS_ONE))
146273e1e22Sbostic 		{
147273e1e22Sbostic 			/*
148273e1e22Sbostic 			 * This is not contiguous with what is
149273e1e22Sbostic 			 * currently displayed.  Clear the screen image
150273e1e22Sbostic 			 * (position table) and start a new screen.
151273e1e22Sbostic 			 */
152273e1e22Sbostic 			pos_clear();
153273e1e22Sbostic 			add_forw_pos(pos);
154273e1e22Sbostic 			force = 1;
155273e1e22Sbostic 			if (top_scroll)
156273e1e22Sbostic 			{
157273e1e22Sbostic 				if (top_scroll == 2)
158273e1e22Sbostic 					clear();
159273e1e22Sbostic 				home();
160273e1e22Sbostic 			} else if (!first_time)
161273e1e22Sbostic 			{
162273e1e22Sbostic 				putstr("...skipping...\n");
163273e1e22Sbostic 			}
164273e1e22Sbostic 		}
165273e1e22Sbostic 	}
166273e1e22Sbostic 
167273e1e22Sbostic 	while (--n >= 0)
168273e1e22Sbostic 	{
169273e1e22Sbostic 		/*
170273e1e22Sbostic 		 * Read the next line of input.
171273e1e22Sbostic 		 */
172273e1e22Sbostic 		pos = forw_line(pos);
173273e1e22Sbostic 		if (pos == NULL_POSITION)
174273e1e22Sbostic 		{
175273e1e22Sbostic 			/*
176273e1e22Sbostic 			 * End of file: stop here unless the top line
177273e1e22Sbostic 			 * is still empty, or "force" is true.
178273e1e22Sbostic 			 */
179273e1e22Sbostic 			eof = 1;
180273e1e22Sbostic 			if (!force && position(TOP) != NULL_POSITION)
181273e1e22Sbostic 				break;
182273e1e22Sbostic 			line = NULL;
183273e1e22Sbostic 		}
184273e1e22Sbostic 		/*
185273e1e22Sbostic 		 * Add the position of the next line to the position table.
186273e1e22Sbostic 		 * Display the current line on the screen.
187273e1e22Sbostic 		 */
188273e1e22Sbostic 		add_forw_pos(pos);
189273e1e22Sbostic 		nlines++;
190273e1e22Sbostic 		if (do_repaint)
191273e1e22Sbostic 			continue;
192273e1e22Sbostic 		/*
193273e1e22Sbostic 		 * If this is the first screen displayed and
194273e1e22Sbostic 		 * we hit an early EOF (i.e. before the requested
195273e1e22Sbostic 		 * number of lines), we "squish" the display down
196273e1e22Sbostic 		 * at the bottom of the screen.
197273e1e22Sbostic 		 * But don't do this if a + option or a -t option
198273e1e22Sbostic 		 * was given.  These options can cause us to
199273e1e22Sbostic 		 * start the display after the beginning of the file,
200273e1e22Sbostic 		 * and it is not appropriate to squish in that case.
201273e1e22Sbostic 		 */
202273e1e22Sbostic 		if (first_time && line == NULL && !top_scroll &&
2032cee6194Sbostic 		    !tagoption && !plusoption)
204273e1e22Sbostic 		{
205273e1e22Sbostic 			squished = 1;
206273e1e22Sbostic 			continue;
207273e1e22Sbostic 		}
208273e1e22Sbostic 		if (top_scroll == 1)
209273e1e22Sbostic 			clear_eol();
210273e1e22Sbostic 		put_line();
211273e1e22Sbostic 	}
212273e1e22Sbostic 
213273e1e22Sbostic 	if (eof && !sigs)
214273e1e22Sbostic 		hit_eof++;
215273e1e22Sbostic 	else
216273e1e22Sbostic 		eof_check();
217273e1e22Sbostic 	if (nlines == 0)
218273e1e22Sbostic 		eof_bell();
219273e1e22Sbostic 	else if (do_repaint)
220273e1e22Sbostic 		repaint();
221273e1e22Sbostic 	first_time = 0;
222273e1e22Sbostic 	(void) currline(BOTTOM);
223273e1e22Sbostic }
224273e1e22Sbostic 
225273e1e22Sbostic /*
226273e1e22Sbostic  * Display n lines, scrolling backward.
227273e1e22Sbostic  */
228273e1e22Sbostic 	static void
229273e1e22Sbostic back(n, pos, force, only_last)
230273e1e22Sbostic 	register int n;
231273e1e22Sbostic 	POSITION pos;
232273e1e22Sbostic 	int force;
233273e1e22Sbostic 	int only_last;
234273e1e22Sbostic {
235273e1e22Sbostic 	int nlines = 0;
236273e1e22Sbostic 	int do_repaint;
237273e1e22Sbostic 
238273e1e22Sbostic 	squish_check();
239273e1e22Sbostic 	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
240273e1e22Sbostic 	hit_eof = 0;
241273e1e22Sbostic 	while (--n >= 0)
242273e1e22Sbostic 	{
243273e1e22Sbostic 		/*
244273e1e22Sbostic 		 * Get the previous line of input.
245273e1e22Sbostic 		 */
246273e1e22Sbostic 		pos = back_line(pos);
247273e1e22Sbostic 		if (pos == NULL_POSITION)
248273e1e22Sbostic 		{
249273e1e22Sbostic 			/*
250273e1e22Sbostic 			 * Beginning of file: stop here unless "force" is true.
251273e1e22Sbostic 			 */
252273e1e22Sbostic 			if (!force)
253273e1e22Sbostic 				break;
254273e1e22Sbostic 			line = NULL;
255273e1e22Sbostic 		}
256273e1e22Sbostic 		/*
257273e1e22Sbostic 		 * Add the position of the previous line to the position table.
258273e1e22Sbostic 		 * Display the line on the screen.
259273e1e22Sbostic 		 */
260273e1e22Sbostic 		add_back_pos(pos);
261273e1e22Sbostic 		nlines++;
262273e1e22Sbostic 		if (!do_repaint)
263273e1e22Sbostic 		{
264273e1e22Sbostic 			home();
265273e1e22Sbostic 			add_line();
266273e1e22Sbostic 			put_line();
267273e1e22Sbostic 		}
268273e1e22Sbostic 	}
269273e1e22Sbostic 
270273e1e22Sbostic 	eof_check();
271273e1e22Sbostic 	if (nlines == 0)
272273e1e22Sbostic 		eof_bell();
273273e1e22Sbostic 	else if (do_repaint)
274273e1e22Sbostic 		repaint();
275273e1e22Sbostic 	(void) currline(BOTTOM);
276273e1e22Sbostic }
277273e1e22Sbostic 
278273e1e22Sbostic /*
279273e1e22Sbostic  * Display n more lines, forward.
280273e1e22Sbostic  * Start just after the line currently displayed at the bottom of the screen.
281273e1e22Sbostic  */
282273e1e22Sbostic 	public void
283273e1e22Sbostic forward(n, only_last)
284273e1e22Sbostic 	int n;
285273e1e22Sbostic 	int only_last;
286273e1e22Sbostic {
287273e1e22Sbostic 	POSITION pos;
288273e1e22Sbostic 
289273e1e22Sbostic 	if (quit_at_eof && hit_eof)
290273e1e22Sbostic 	{
291273e1e22Sbostic 		/*
292273e1e22Sbostic 		 * If the -e flag is set and we're trying to go
293273e1e22Sbostic 		 * forward from end-of-file, go on to the next file.
294273e1e22Sbostic 		 */
295273e1e22Sbostic 		next_file(1);
296273e1e22Sbostic 		return;
297273e1e22Sbostic 	}
298273e1e22Sbostic 
299273e1e22Sbostic 	pos = position(BOTTOM_PLUS_ONE);
300273e1e22Sbostic 	if (pos == NULL_POSITION)
301273e1e22Sbostic 	{
302273e1e22Sbostic 		eof_bell();
303273e1e22Sbostic 		hit_eof++;
304273e1e22Sbostic 		return;
305273e1e22Sbostic 	}
306273e1e22Sbostic 	forw(n, pos, 0, only_last);
307273e1e22Sbostic }
308273e1e22Sbostic 
309273e1e22Sbostic /*
310273e1e22Sbostic  * Display n more lines, backward.
311273e1e22Sbostic  * Start just before the line currently displayed at the top of the screen.
312273e1e22Sbostic  */
313273e1e22Sbostic 	public void
314273e1e22Sbostic backward(n, only_last)
315273e1e22Sbostic 	int n;
316273e1e22Sbostic 	int only_last;
317273e1e22Sbostic {
318273e1e22Sbostic 	POSITION pos;
319273e1e22Sbostic 
320273e1e22Sbostic 	pos = position(TOP);
321273e1e22Sbostic 	if (pos == NULL_POSITION)
322273e1e22Sbostic 	{
323273e1e22Sbostic 		/*
324273e1e22Sbostic 		 * This will almost never happen,
325273e1e22Sbostic 		 * because the top line is almost never empty.
326273e1e22Sbostic 		 */
327273e1e22Sbostic 		eof_bell();
328273e1e22Sbostic 		return;
329273e1e22Sbostic 	}
330273e1e22Sbostic 	back(n, pos, 0, only_last);
331273e1e22Sbostic }
332273e1e22Sbostic 
333273e1e22Sbostic /*
334273e1e22Sbostic  * Repaint the screen, starting from a specified position.
335273e1e22Sbostic  */
336273e1e22Sbostic 	static void
337273e1e22Sbostic prepaint(pos)
338273e1e22Sbostic 	POSITION pos;
339273e1e22Sbostic {
340273e1e22Sbostic 	hit_eof = 0;
341273e1e22Sbostic 	forw(sc_height-1, pos, 1, 0);
342273e1e22Sbostic 	screen_trashed = 0;
343273e1e22Sbostic }
344273e1e22Sbostic 
345273e1e22Sbostic /*
346273e1e22Sbostic  * Repaint the screen.
347273e1e22Sbostic  */
348273e1e22Sbostic 	public void
349273e1e22Sbostic repaint()
350273e1e22Sbostic {
351273e1e22Sbostic 	/*
352273e1e22Sbostic 	 * Start at the line currently at the top of the screen
353273e1e22Sbostic 	 * and redisplay the screen.
354273e1e22Sbostic 	 */
355273e1e22Sbostic 	prepaint(position(TOP));
356273e1e22Sbostic }
357273e1e22Sbostic 
358273e1e22Sbostic /*
359273e1e22Sbostic  * Jump to the end of the file.
360273e1e22Sbostic  * It is more convenient to paint the screen backward,
361273e1e22Sbostic  * from the end of the file toward the beginning.
362273e1e22Sbostic  */
363273e1e22Sbostic 	public void
364273e1e22Sbostic jump_forw()
365273e1e22Sbostic {
366273e1e22Sbostic 	POSITION pos;
367273e1e22Sbostic 
368273e1e22Sbostic 	if (ch_end_seek())
369273e1e22Sbostic 	{
370273e1e22Sbostic 		error("Cannot seek to end of file");
371273e1e22Sbostic 		return;
372273e1e22Sbostic 	}
373273e1e22Sbostic 	lastmark();
374273e1e22Sbostic 	pos = ch_tell();
375273e1e22Sbostic 	clear();
376273e1e22Sbostic 	pos_clear();
377273e1e22Sbostic 	add_back_pos(pos);
378273e1e22Sbostic 	back(sc_height - 1, pos, 0, 0);
379273e1e22Sbostic }
380273e1e22Sbostic 
381273e1e22Sbostic /*
382273e1e22Sbostic  * Jump to line n in the file.
383273e1e22Sbostic  */
384273e1e22Sbostic 	public void
385273e1e22Sbostic jump_back(n)
386273e1e22Sbostic 	register int n;
387273e1e22Sbostic {
388273e1e22Sbostic 	register int c;
389273e1e22Sbostic 	int nlines;
390273e1e22Sbostic 
391273e1e22Sbostic 	/*
392273e1e22Sbostic 	 * This is done the slow way, by starting at the beginning
393273e1e22Sbostic 	 * of the file and counting newlines.
394273e1e22Sbostic 	 *
395273e1e22Sbostic 	 * {{ Now that we have line numbering (in linenum.c),
396273e1e22Sbostic 	 *    we could improve on this by starting at the
397273e1e22Sbostic 	 *    nearest known line rather than at the beginning. }}
398273e1e22Sbostic 	 */
399273e1e22Sbostic 	if (ch_seek((POSITION)0))
400273e1e22Sbostic 	{
401273e1e22Sbostic 		/*
402273e1e22Sbostic 		 * Probably a pipe with beginning of file no longer buffered.
403273e1e22Sbostic 		 * If he wants to go to line 1, we do the best we can,
404273e1e22Sbostic 		 * by going to the first line which is still buffered.
405273e1e22Sbostic 		 */
406273e1e22Sbostic 		if (n <= 1 && ch_beg_seek() == 0)
407273e1e22Sbostic 			jump_loc(ch_tell());
408273e1e22Sbostic 		error("Cannot get to beginning of file");
409273e1e22Sbostic 		return;
410273e1e22Sbostic 	}
411273e1e22Sbostic 
412273e1e22Sbostic 	/*
413273e1e22Sbostic 	 * Start counting lines.
414273e1e22Sbostic 	 */
415273e1e22Sbostic 	for (nlines = 1;  nlines < n;  nlines++)
416273e1e22Sbostic 	{
417273e1e22Sbostic 		while ((c = ch_forw_get()) != '\n')
418273e1e22Sbostic 			if (c == EOI)
419273e1e22Sbostic 			{
420273e1e22Sbostic 				char message[40];
421*8033092dSbostic 				(void)sprintf(message, "File has only %d lines",
422273e1e22Sbostic 					nlines-1);
423273e1e22Sbostic 				error(message);
424273e1e22Sbostic 				return;
425273e1e22Sbostic 			}
426273e1e22Sbostic 	}
427273e1e22Sbostic 
428273e1e22Sbostic 	jump_loc(ch_tell());
429273e1e22Sbostic }
430273e1e22Sbostic 
431273e1e22Sbostic /*
432273e1e22Sbostic  * Jump to a specified percentage into the file.
433273e1e22Sbostic  * This is a poor compensation for not being able to
434273e1e22Sbostic  * quickly jump to a specific line number.
435273e1e22Sbostic  */
436273e1e22Sbostic 	public void
437273e1e22Sbostic jump_percent(percent)
438273e1e22Sbostic 	int percent;
439273e1e22Sbostic {
440273e1e22Sbostic 	POSITION pos, len;
441273e1e22Sbostic 	register int c;
442273e1e22Sbostic 
443273e1e22Sbostic 	/*
444273e1e22Sbostic 	 * Determine the position in the file
445273e1e22Sbostic 	 * (the specified percentage of the file's length).
446273e1e22Sbostic 	 */
447273e1e22Sbostic 	if ((len = ch_length()) == NULL_POSITION)
448273e1e22Sbostic 	{
449273e1e22Sbostic 		error("Don't know length of file");
450273e1e22Sbostic 		return;
451273e1e22Sbostic 	}
452273e1e22Sbostic 	pos = (percent * len) / 100;
453273e1e22Sbostic 
454273e1e22Sbostic 	/*
455273e1e22Sbostic 	 * Back up to the beginning of the line.
456273e1e22Sbostic 	 */
457273e1e22Sbostic 	if (ch_seek(pos) == 0)
458273e1e22Sbostic 	{
459273e1e22Sbostic 		while ((c = ch_back_get()) != '\n' && c != EOI)
460273e1e22Sbostic 			;
461273e1e22Sbostic 		if (c == '\n')
462273e1e22Sbostic 			(void) ch_forw_get();
463273e1e22Sbostic 		pos = ch_tell();
464273e1e22Sbostic 	}
465273e1e22Sbostic 	jump_loc(pos);
466273e1e22Sbostic }
467273e1e22Sbostic 
468273e1e22Sbostic /*
469273e1e22Sbostic  * Jump to a specified position in the file.
470273e1e22Sbostic  */
471273e1e22Sbostic 	public void
472273e1e22Sbostic jump_loc(pos)
473273e1e22Sbostic 	POSITION pos;
474273e1e22Sbostic {
475273e1e22Sbostic 	register int nline;
476273e1e22Sbostic 	POSITION tpos;
477273e1e22Sbostic 
478273e1e22Sbostic 	if ((nline = onscreen(pos)) >= 0)
479273e1e22Sbostic 	{
480273e1e22Sbostic 		/*
481273e1e22Sbostic 		 * The line is currently displayed.
482273e1e22Sbostic 		 * Just scroll there.
483273e1e22Sbostic 		 */
484273e1e22Sbostic 		forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
485273e1e22Sbostic 		return;
486273e1e22Sbostic 	}
487273e1e22Sbostic 
488273e1e22Sbostic 	/*
489273e1e22Sbostic 	 * Line is not on screen.
490273e1e22Sbostic 	 * Seek to the desired location.
491273e1e22Sbostic 	 */
492273e1e22Sbostic 	if (ch_seek(pos))
493273e1e22Sbostic 	{
494273e1e22Sbostic 		error("Cannot seek to that position");
495273e1e22Sbostic 		return;
496273e1e22Sbostic 	}
497273e1e22Sbostic 
498273e1e22Sbostic 	/*
499273e1e22Sbostic 	 * See if the desired line is BEFORE the currently
500273e1e22Sbostic 	 * displayed screen.  If so, then move forward far
501273e1e22Sbostic 	 * enough so the line we're on will be at the bottom
502273e1e22Sbostic 	 * of the screen, in order to be able to call back()
503273e1e22Sbostic 	 * to make the screen scroll backwards & put the line
504273e1e22Sbostic 	 * at the top of the screen.
505273e1e22Sbostic 	 * {{ This seems inefficient, but it's not so bad,
506273e1e22Sbostic 	 *    since we can never move forward more than a
507273e1e22Sbostic 	 *    screenful before we stop to redraw the screen. }}
508273e1e22Sbostic 	 */
509273e1e22Sbostic 	tpos = position(TOP);
510273e1e22Sbostic 	if (tpos != NULL_POSITION && pos < tpos)
511273e1e22Sbostic 	{
512273e1e22Sbostic 		POSITION npos = pos;
513273e1e22Sbostic 		/*
514273e1e22Sbostic 		 * Note that we can't forw_line() past tpos here,
515273e1e22Sbostic 		 * so there should be no EOI at this stage.
516273e1e22Sbostic 		 */
517273e1e22Sbostic 		for (nline = 0;  npos < tpos && nline < sc_height - 1;  nline++)
518273e1e22Sbostic 			npos = forw_line(npos);
519273e1e22Sbostic 
520273e1e22Sbostic 		if (npos < tpos)
521273e1e22Sbostic 		{
522273e1e22Sbostic 			/*
523273e1e22Sbostic 			 * More than a screenful back.
524273e1e22Sbostic 			 */
525273e1e22Sbostic 			lastmark();
526273e1e22Sbostic 			clear();
527273e1e22Sbostic 			pos_clear();
528273e1e22Sbostic 			add_back_pos(npos);
529273e1e22Sbostic 		}
530273e1e22Sbostic 
531273e1e22Sbostic 		/*
532273e1e22Sbostic 		 * Note that back() will repaint() if nline > back_scroll.
533273e1e22Sbostic 		 */
534273e1e22Sbostic 		back(nline, npos, 1, 0);
535273e1e22Sbostic 		return;
536273e1e22Sbostic 	}
537273e1e22Sbostic 	/*
538273e1e22Sbostic 	 * Remember where we were; clear and paint the screen.
539273e1e22Sbostic 	 */
540273e1e22Sbostic   	lastmark();
541273e1e22Sbostic   	prepaint(pos);
542273e1e22Sbostic }
543273e1e22Sbostic 
544273e1e22Sbostic /*
545273e1e22Sbostic  * The table of marks.
546273e1e22Sbostic  * A mark is simply a position in the file.
547273e1e22Sbostic  */
548273e1e22Sbostic #define	NMARKS		(27)		/* 26 for a-z plus one for quote */
549273e1e22Sbostic #define	LASTMARK	(NMARKS-1)	/* For quote */
550273e1e22Sbostic static POSITION marks[NMARKS];
551273e1e22Sbostic 
552273e1e22Sbostic /*
553273e1e22Sbostic  * Initialize the mark table to show no marks are set.
554273e1e22Sbostic  */
555273e1e22Sbostic 	public void
556273e1e22Sbostic init_mark()
557273e1e22Sbostic {
558273e1e22Sbostic 	int i;
559273e1e22Sbostic 
560273e1e22Sbostic 	for (i = 0;  i < NMARKS;  i++)
561273e1e22Sbostic 		marks[i] = NULL_POSITION;
562273e1e22Sbostic }
563273e1e22Sbostic 
564273e1e22Sbostic /*
565273e1e22Sbostic  * See if a mark letter is valid (between a and z).
566273e1e22Sbostic  */
567273e1e22Sbostic 	static int
568273e1e22Sbostic badmark(c)
569273e1e22Sbostic 	int c;
570273e1e22Sbostic {
571273e1e22Sbostic 	if (c < 'a' || c > 'z')
572273e1e22Sbostic 	{
573273e1e22Sbostic 		error("Choose a letter between 'a' and 'z'");
574273e1e22Sbostic 		return (1);
575273e1e22Sbostic 	}
576273e1e22Sbostic 	return (0);
577273e1e22Sbostic }
578273e1e22Sbostic 
579273e1e22Sbostic /*
580273e1e22Sbostic  * Set a mark.
581273e1e22Sbostic  */
582273e1e22Sbostic 	public void
583273e1e22Sbostic setmark(c)
584273e1e22Sbostic 	int c;
585273e1e22Sbostic {
586273e1e22Sbostic 	if (badmark(c))
587273e1e22Sbostic 		return;
588273e1e22Sbostic 	marks[c-'a'] = position(TOP);
589273e1e22Sbostic }
590273e1e22Sbostic 
591273e1e22Sbostic 	public void
592273e1e22Sbostic lastmark()
593273e1e22Sbostic {
594273e1e22Sbostic 	marks[LASTMARK] = position(TOP);
595273e1e22Sbostic }
596273e1e22Sbostic 
597273e1e22Sbostic /*
598273e1e22Sbostic  * Go to a previously set mark.
599273e1e22Sbostic  */
600273e1e22Sbostic 	public void
601273e1e22Sbostic gomark(c)
602273e1e22Sbostic 	int c;
603273e1e22Sbostic {
604273e1e22Sbostic 	POSITION pos;
605273e1e22Sbostic 
606273e1e22Sbostic 	if (c == '\'')
607273e1e22Sbostic 		pos = marks[LASTMARK];
608273e1e22Sbostic 	else if (badmark(c))
609273e1e22Sbostic 		return;
610273e1e22Sbostic 	else
611273e1e22Sbostic 		pos = marks[c-'a'];
612273e1e22Sbostic 
613273e1e22Sbostic 	if (pos == NULL_POSITION)
614273e1e22Sbostic 		error("mark not set");
615273e1e22Sbostic 	else
616273e1e22Sbostic 		jump_loc(pos);
617273e1e22Sbostic }
618273e1e22Sbostic 
619273e1e22Sbostic /*
620273e1e22Sbostic  * Get the backwards scroll limit.
621273e1e22Sbostic  * Must call this function instead of just using the value of
622273e1e22Sbostic  * back_scroll, because the default case depends on sc_height and
623273e1e22Sbostic  * top_scroll, as well as back_scroll.
624273e1e22Sbostic  */
625273e1e22Sbostic 	public int
626273e1e22Sbostic get_back_scroll()
627273e1e22Sbostic {
628273e1e22Sbostic 	if (back_scroll >= 0)
629273e1e22Sbostic 		return (back_scroll);
630273e1e22Sbostic 	if (top_scroll)
631273e1e22Sbostic 		return (sc_height - 2);
632273e1e22Sbostic 	return (sc_height - 1);
633273e1e22Sbostic }
634273e1e22Sbostic 
635273e1e22Sbostic /*
636273e1e22Sbostic  * Search for the n-th occurence of a specified pattern,
637273e1e22Sbostic  * either forward or backward.
638273e1e22Sbostic  */
639273e1e22Sbostic 	public void
640273e1e22Sbostic search(search_forward, pattern, n, wantmatch)
641273e1e22Sbostic 	register int search_forward;
642273e1e22Sbostic 	register char *pattern;
643273e1e22Sbostic 	register int n;
644273e1e22Sbostic 	int wantmatch;
645273e1e22Sbostic {
646273e1e22Sbostic 	POSITION pos, linepos;
647273e1e22Sbostic 	register char *p;
648273e1e22Sbostic 	register char *q;
649273e1e22Sbostic 	int linenum;
650273e1e22Sbostic 	int linematch;
651273e1e22Sbostic #if RECOMP
652273e1e22Sbostic 	char *re_comp();
653273e1e22Sbostic 	char *errmsg;
654273e1e22Sbostic #else
655273e1e22Sbostic #if REGCMP
656273e1e22Sbostic 	char *regcmp();
657273e1e22Sbostic 	static char *cpattern = NULL;
658273e1e22Sbostic #else
659273e1e22Sbostic 	static char lpbuf[100];
660273e1e22Sbostic 	static char *last_pattern = NULL;
661*8033092dSbostic 	char *strcpy();
662273e1e22Sbostic #endif
663273e1e22Sbostic #endif
664273e1e22Sbostic 
665273e1e22Sbostic 	if (caseless && pattern != NULL)
666273e1e22Sbostic 	{
667273e1e22Sbostic 		/*
668273e1e22Sbostic 		 * For a caseless search, convert any uppercase
669273e1e22Sbostic 		 * in the pattern to lowercase.
670273e1e22Sbostic 		 */
671273e1e22Sbostic 		for (p = pattern;  *p != '\0';  p++)
672273e1e22Sbostic 			if (*p >= 'A' && *p <= 'Z')
673273e1e22Sbostic 				*p += 'a' - 'A';
674273e1e22Sbostic 	}
675273e1e22Sbostic #if RECOMP
676273e1e22Sbostic 
677273e1e22Sbostic 	/*
678273e1e22Sbostic 	 * (re_comp handles a null pattern internally,
679273e1e22Sbostic 	 *  so there is no need to check for a null pattern here.)
680273e1e22Sbostic 	 */
681273e1e22Sbostic 	if ((errmsg = re_comp(pattern)) != NULL)
682273e1e22Sbostic 	{
683273e1e22Sbostic 		error(errmsg);
684273e1e22Sbostic 		return;
685273e1e22Sbostic 	}
686273e1e22Sbostic #else
687273e1e22Sbostic #if REGCMP
688273e1e22Sbostic 	if (pattern == NULL || *pattern == '\0')
689273e1e22Sbostic 	{
690273e1e22Sbostic 		/*
691273e1e22Sbostic 		 * A null pattern means use the previous pattern.
692273e1e22Sbostic 		 * The compiled previous pattern is in cpattern, so just use it.
693273e1e22Sbostic 		 */
694273e1e22Sbostic 		if (cpattern == NULL)
695273e1e22Sbostic 		{
696273e1e22Sbostic 			error("No previous regular expression");
697273e1e22Sbostic 			return;
698273e1e22Sbostic 		}
699273e1e22Sbostic 	} else
700273e1e22Sbostic 	{
701273e1e22Sbostic 		/*
702273e1e22Sbostic 		 * Otherwise compile the given pattern.
703273e1e22Sbostic 		 */
704273e1e22Sbostic 		char *s;
705273e1e22Sbostic 		if ((s = regcmp(pattern, 0)) == NULL)
706273e1e22Sbostic 		{
707273e1e22Sbostic 			error("Invalid pattern");
708273e1e22Sbostic 			return;
709273e1e22Sbostic 		}
710273e1e22Sbostic 		if (cpattern != NULL)
711273e1e22Sbostic 			free(cpattern);
712273e1e22Sbostic 		cpattern = s;
713273e1e22Sbostic 	}
714273e1e22Sbostic #else
715273e1e22Sbostic 	if (pattern == NULL || *pattern == '\0')
716273e1e22Sbostic 	{
717273e1e22Sbostic 		/*
718273e1e22Sbostic 		 * Null pattern means use the previous pattern.
719273e1e22Sbostic 		 */
720273e1e22Sbostic 		if (last_pattern == NULL)
721273e1e22Sbostic 		{
722273e1e22Sbostic 			error("No previous regular expression");
723273e1e22Sbostic 			return;
724273e1e22Sbostic 		}
725273e1e22Sbostic 		pattern = last_pattern;
726273e1e22Sbostic 	} else
727273e1e22Sbostic 	{
728*8033092dSbostic 		(void)strcpy(lpbuf, pattern);
729273e1e22Sbostic 		last_pattern = lpbuf;
730273e1e22Sbostic 	}
731273e1e22Sbostic #endif
732273e1e22Sbostic #endif
733273e1e22Sbostic 
734273e1e22Sbostic 	/*
735273e1e22Sbostic 	 * Figure out where to start the search.
736273e1e22Sbostic 	 */
737273e1e22Sbostic 
738273e1e22Sbostic 	if (position(TOP) == NULL_POSITION)
739273e1e22Sbostic 	{
740273e1e22Sbostic 		/*
741273e1e22Sbostic 		 * Nothing is currently displayed.
742273e1e22Sbostic 		 * Start at the beginning of the file.
743273e1e22Sbostic 		 * (This case is mainly for first_cmd searches,
744273e1e22Sbostic 		 * for example, "+/xyz" on the command line.)
745273e1e22Sbostic 		 */
746273e1e22Sbostic 		pos = (POSITION)0;
747273e1e22Sbostic 	} else if (!search_forward)
748273e1e22Sbostic 	{
749273e1e22Sbostic 		/*
750273e1e22Sbostic 		 * Backward search: start just before the top line
751273e1e22Sbostic 		 * displayed on the screen.
752273e1e22Sbostic 		 */
753273e1e22Sbostic 		pos = position(TOP);
754273e1e22Sbostic 	} else if (how_search == 0)
755273e1e22Sbostic 	{
756273e1e22Sbostic 		/*
757273e1e22Sbostic 		 * Start at the second real line displayed on the screen.
758273e1e22Sbostic 		 */
759273e1e22Sbostic 		pos = position(TOP);
760273e1e22Sbostic 		do
761273e1e22Sbostic 			pos = forw_raw_line(pos);
762273e1e22Sbostic 		while (pos < position(TOP+1));
763273e1e22Sbostic 	} else if (how_search == 1)
764273e1e22Sbostic 	{
765273e1e22Sbostic 		/*
766273e1e22Sbostic 		 * Start just after the bottom line displayed on the screen.
767273e1e22Sbostic 		 */
768273e1e22Sbostic 		pos = position(BOTTOM_PLUS_ONE);
769273e1e22Sbostic 	} else
770273e1e22Sbostic 	{
771273e1e22Sbostic 		/*
772273e1e22Sbostic 		 * Start at the second screen line displayed on the screen.
773273e1e22Sbostic 		 */
774273e1e22Sbostic 		pos = position(TOP_PLUS_ONE);
775273e1e22Sbostic 	}
776273e1e22Sbostic 
777273e1e22Sbostic 	if (pos == NULL_POSITION)
778273e1e22Sbostic 	{
779273e1e22Sbostic 		/*
780273e1e22Sbostic 		 * Can't find anyplace to start searching from.
781273e1e22Sbostic 		 */
782273e1e22Sbostic 		error("Nothing to search");
783273e1e22Sbostic 		return;
784273e1e22Sbostic 	}
785273e1e22Sbostic 
786273e1e22Sbostic 	linenum = find_linenum(pos);
787273e1e22Sbostic 	for (;;)
788273e1e22Sbostic 	{
789273e1e22Sbostic 		/*
790273e1e22Sbostic 		 * Get lines until we find a matching one or
791273e1e22Sbostic 		 * until we hit end-of-file (or beginning-of-file
792273e1e22Sbostic 		 * if we're going backwards).
793273e1e22Sbostic 		 */
794273e1e22Sbostic 		if (sigs)
795273e1e22Sbostic 			/*
796273e1e22Sbostic 			 * A signal aborts the search.
797273e1e22Sbostic 			 */
798273e1e22Sbostic 			return;
799273e1e22Sbostic 
800273e1e22Sbostic 		if (search_forward)
801273e1e22Sbostic 		{
802273e1e22Sbostic 			/*
803273e1e22Sbostic 			 * Read the next line, and save the
804273e1e22Sbostic 			 * starting position of that line in linepos.
805273e1e22Sbostic 			 */
806273e1e22Sbostic 			linepos = pos;
807273e1e22Sbostic 			pos = forw_raw_line(pos);
808273e1e22Sbostic 			if (linenum != 0)
809273e1e22Sbostic 				linenum++;
810273e1e22Sbostic 		} else
811273e1e22Sbostic 		{
812273e1e22Sbostic 			/*
813273e1e22Sbostic 			 * Read the previous line and save the
814273e1e22Sbostic 			 * starting position of that line in linepos.
815273e1e22Sbostic 			 */
816273e1e22Sbostic 			pos = back_raw_line(pos);
817273e1e22Sbostic 			linepos = pos;
818273e1e22Sbostic 			if (linenum != 0)
819273e1e22Sbostic 				linenum--;
820273e1e22Sbostic 		}
821273e1e22Sbostic 
822273e1e22Sbostic 		if (pos == NULL_POSITION)
823273e1e22Sbostic 		{
824273e1e22Sbostic 			/*
825273e1e22Sbostic 			 * We hit EOF/BOF without a match.
826273e1e22Sbostic 			 */
827273e1e22Sbostic 			error("Pattern not found");
828273e1e22Sbostic 			return;
829273e1e22Sbostic 		}
830273e1e22Sbostic 
831273e1e22Sbostic 		/*
832273e1e22Sbostic 		 * If we're using line numbers, we might as well
833273e1e22Sbostic 		 * remember the information we have now (the position
834273e1e22Sbostic 		 * and line number of the current line).
835273e1e22Sbostic 		 */
836273e1e22Sbostic 		if (linenums)
837273e1e22Sbostic 			add_lnum(linenum, pos);
838273e1e22Sbostic 
839273e1e22Sbostic 		if (caseless)
840273e1e22Sbostic 		{
841273e1e22Sbostic 			/*
842273e1e22Sbostic 			 * If this is a caseless search, convert
843273e1e22Sbostic 			 * uppercase in the input line to lowercase.
844273e1e22Sbostic 			 * While we're at it, remove any backspaces
845273e1e22Sbostic 			 * along with the preceeding char.
846273e1e22Sbostic 			 * This allows us to match text which is
847273e1e22Sbostic 			 * underlined or overstruck.
848273e1e22Sbostic 			 */
849273e1e22Sbostic 			for (p = q = line;  *p != '\0';  p++, q++)
850273e1e22Sbostic 			{
851273e1e22Sbostic 				if (*p >= 'A' && *p <= 'Z')
852273e1e22Sbostic 					/* Convert uppercase to lowercase. */
853273e1e22Sbostic 					*q = *p + 'a' - 'A';
854273e1e22Sbostic 				else if (q > line && *p == '\b')
855273e1e22Sbostic 					/* Delete BS and preceeding char. */
856273e1e22Sbostic 					q -= 2;
857273e1e22Sbostic 				else
858273e1e22Sbostic 					/* Otherwise, just copy. */
859273e1e22Sbostic 					*q = *p;
860273e1e22Sbostic 			}
861273e1e22Sbostic 		}
862273e1e22Sbostic 
863273e1e22Sbostic 		/*
864273e1e22Sbostic 		 * Test the next line to see if we have a match.
865273e1e22Sbostic 		 * This is done in a variety of ways, depending
866273e1e22Sbostic 		 * on what pattern matching functions are available.
867273e1e22Sbostic 		 */
868273e1e22Sbostic #if REGCMP
869273e1e22Sbostic 		linematch = (regex(cpattern, line) != NULL);
870273e1e22Sbostic #else
871273e1e22Sbostic #if RECOMP
872273e1e22Sbostic 		linematch = (re_exec(line) == 1);
873273e1e22Sbostic #else
874273e1e22Sbostic 		linematch = match(pattern, line);
875273e1e22Sbostic #endif
876273e1e22Sbostic #endif
877273e1e22Sbostic 		/*
878273e1e22Sbostic 		 * We are successful if wantmatch and linematch are
879273e1e22Sbostic 		 * both true (want a match and got it),
880273e1e22Sbostic 		 * or both false (want a non-match and got it).
881273e1e22Sbostic 		 */
882273e1e22Sbostic 		if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
883273e1e22Sbostic 		      --n <= 0)
884273e1e22Sbostic 			/*
885273e1e22Sbostic 			 * Found the line.
886273e1e22Sbostic 			 */
887273e1e22Sbostic 			break;
888273e1e22Sbostic 	}
889273e1e22Sbostic 
890273e1e22Sbostic 	jump_loc(linepos);
891273e1e22Sbostic }
892273e1e22Sbostic 
893273e1e22Sbostic #if (!REGCMP) && (!RECOMP)
894273e1e22Sbostic /*
895273e1e22Sbostic  * We have neither regcmp() nor re_comp().
896273e1e22Sbostic  * We use this function to do simple pattern matching.
897273e1e22Sbostic  * It supports no metacharacters like *, etc.
898273e1e22Sbostic  */
899273e1e22Sbostic 	static int
900273e1e22Sbostic match(pattern, buf)
901273e1e22Sbostic 	char *pattern, *buf;
902273e1e22Sbostic {
903273e1e22Sbostic 	register char *pp, *lp;
904273e1e22Sbostic 
905273e1e22Sbostic 	for ( ;  *buf != '\0';  buf++)
906273e1e22Sbostic 	{
907273e1e22Sbostic 		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
908273e1e22Sbostic 			if (*pp == '\0' || *lp == '\0')
909273e1e22Sbostic 				break;
910273e1e22Sbostic 		if (*pp == '\0')
911273e1e22Sbostic 			return (1);
912273e1e22Sbostic 	}
913273e1e22Sbostic 	return (0);
914273e1e22Sbostic }
915273e1e22Sbostic #endif
916