xref: /original-bsd/usr.bin/more/prim.c (revision 427e68f0)
1273e1e22Sbostic /*
2273e1e22Sbostic  * Copyright (c) 1988 Mark Nudleman
3273e1e22Sbostic  * Copyright (c) 1988 Regents of the University of California.
4273e1e22Sbostic  * All rights reserved.
5273e1e22Sbostic  *
6273e1e22Sbostic  * Redistribution and use in source and binary forms are permitted
7273e1e22Sbostic  * provided that the above copyright notice and this paragraph are
8273e1e22Sbostic  * duplicated in all such forms and that any documentation,
9273e1e22Sbostic  * advertising materials, and other materials related to such
10273e1e22Sbostic  * distribution and use acknowledge that the software was developed
1194cb6cb2Sbostic  * by Mark Nudleman and the University of California, Berkeley.  The
1294cb6cb2Sbostic  * name of Mark Nudleman or the
13273e1e22Sbostic  * University may not be used to endorse or promote products derived
14273e1e22Sbostic  * from this software without specific prior written permission.
15273e1e22Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16273e1e22Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17273e1e22Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18273e1e22Sbostic  */
19273e1e22Sbostic 
20273e1e22Sbostic #ifndef lint
21*427e68f0Sbostic static char sccsid[] = "@(#)prim.c	5.5 (Berkeley) 11/22/88";
22273e1e22Sbostic #endif /* not lint */
23273e1e22Sbostic 
24273e1e22Sbostic /*
25273e1e22Sbostic  * Primitives for displaying the file on the screen.
26273e1e22Sbostic  */
27273e1e22Sbostic 
28*427e68f0Sbostic #include <sys/types.h>
29*427e68f0Sbostic #include <stdio.h>
30*427e68f0Sbostic #include <ctype.h>
31*427e68f0Sbostic #include <less.h>
32273e1e22Sbostic 
33*427e68f0Sbostic int back_scroll = -1;
34*427e68f0Sbostic int hit_eof;		/* keeps track of how many times we hit end of file */
35*427e68f0Sbostic int screen_trashed;
36273e1e22Sbostic 
37273e1e22Sbostic static int squished;
38273e1e22Sbostic 
39273e1e22Sbostic extern int sigs;
40273e1e22Sbostic extern int top_scroll;
41273e1e22Sbostic extern int sc_width, sc_height;
42273e1e22Sbostic extern int caseless;
43273e1e22Sbostic extern int linenums;
44273e1e22Sbostic extern int tagoption;
45*427e68f0Sbostic extern char *line;
46273e1e22Sbostic 
47*427e68f0Sbostic off_t position(), forw_line(), back_line(), forw_raw_line(), back_raw_line();
48*427e68f0Sbostic off_t ch_length(), ch_tell();
49273e1e22Sbostic 
50273e1e22Sbostic /*
51273e1e22Sbostic  * Check to see if the end of file is currently "displayed".
52273e1e22Sbostic  */
53273e1e22Sbostic eof_check()
54273e1e22Sbostic {
55*427e68f0Sbostic 	off_t pos;
56273e1e22Sbostic 
57273e1e22Sbostic 	if (sigs)
58273e1e22Sbostic 		return;
59273e1e22Sbostic 	/*
60273e1e22Sbostic 	 * If the bottom line is empty, we are at EOF.
61273e1e22Sbostic 	 * If the bottom line ends at the file length,
62273e1e22Sbostic 	 * we must be just at EOF.
63273e1e22Sbostic 	 */
64273e1e22Sbostic 	pos = position(BOTTOM_PLUS_ONE);
65273e1e22Sbostic 	if (pos == NULL_POSITION || pos == ch_length())
66273e1e22Sbostic 		hit_eof++;
67273e1e22Sbostic }
68273e1e22Sbostic 
69273e1e22Sbostic /*
70273e1e22Sbostic  * If the screen is "squished", repaint it.
71273e1e22Sbostic  * "Squished" means the first displayed line is not at the top
72273e1e22Sbostic  * of the screen; this can happen when we display a short file
73273e1e22Sbostic  * for the first time.
74273e1e22Sbostic  */
75273e1e22Sbostic squish_check()
76273e1e22Sbostic {
77*427e68f0Sbostic 	if (squished) {
78273e1e22Sbostic 		squished = 0;
79273e1e22Sbostic 		repaint();
80273e1e22Sbostic 	}
81*427e68f0Sbostic }
82273e1e22Sbostic 
83273e1e22Sbostic /*
84*427e68f0Sbostic  * Display n lines, scrolling forward, starting at position pos in the
85*427e68f0Sbostic  * input file.  "force" means display the n lines even if we hit end of
86*427e68f0Sbostic  * file.  "only_last" means display only the last screenful if n > screen
87*427e68f0Sbostic  * size.
88273e1e22Sbostic  */
89273e1e22Sbostic forw(n, pos, force, only_last)
90273e1e22Sbostic 	register int n;
91*427e68f0Sbostic 	off_t pos;
92273e1e22Sbostic 	int force;
93273e1e22Sbostic 	int only_last;
94273e1e22Sbostic {
95273e1e22Sbostic 	static int first_time = 1;
96*427e68f0Sbostic 	int eof = 0, do_repaint;
97273e1e22Sbostic 
98273e1e22Sbostic 	squish_check();
99273e1e22Sbostic 
100273e1e22Sbostic 	/*
101273e1e22Sbostic 	 * do_repaint tells us not to display anything till the end,
102273e1e22Sbostic 	 * then just repaint the entire screen.
103273e1e22Sbostic 	 */
104273e1e22Sbostic 	do_repaint = (only_last && n > sc_height-1);
105273e1e22Sbostic 
106*427e68f0Sbostic 	if (!do_repaint) {
107*427e68f0Sbostic 		if (top_scroll && n >= sc_height - 1) {
108273e1e22Sbostic 			/*
109273e1e22Sbostic 			 * Start a new screen.
110273e1e22Sbostic 			 * {{ This is not really desirable if we happen
111273e1e22Sbostic 			 *    to hit eof in the middle of this screen,
112273e1e22Sbostic 			 *    but we don't yet know if that will happen. }}
113273e1e22Sbostic 			 */
114273e1e22Sbostic 			clear();
115273e1e22Sbostic 			home();
116273e1e22Sbostic 			force = 1;
117*427e68f0Sbostic 		} else {
118273e1e22Sbostic 			lower_left();
119273e1e22Sbostic 			clear_eol();
120273e1e22Sbostic 		}
121273e1e22Sbostic 
122273e1e22Sbostic 		/*
123*427e68f0Sbostic 		 * This is not contiguous with what is currently displayed.
124*427e68f0Sbostic 		 * Clear the screen image (position table) and start a new
125*427e68f0Sbostic 		 * screen.
126273e1e22Sbostic 		 */
127*427e68f0Sbostic 		if (pos != position(BOTTOM_PLUS_ONE)) {
128273e1e22Sbostic 			pos_clear();
129273e1e22Sbostic 			add_forw_pos(pos);
130273e1e22Sbostic 			force = 1;
131*427e68f0Sbostic 			if (top_scroll) {
132273e1e22Sbostic 				clear();
133273e1e22Sbostic 				home();
134273e1e22Sbostic 			} else if (!first_time)
135273e1e22Sbostic 				putstr("...skipping...\n");
136273e1e22Sbostic 		}
137273e1e22Sbostic 	}
138273e1e22Sbostic 
139*427e68f0Sbostic 	while (--n >= 0) {
140273e1e22Sbostic 		/*
141273e1e22Sbostic 		 * Read the next line of input.
142273e1e22Sbostic 		 */
143273e1e22Sbostic 		pos = forw_line(pos);
144*427e68f0Sbostic 		if (pos == NULL_POSITION) {
145273e1e22Sbostic 			/*
146273e1e22Sbostic 			 * End of file: stop here unless the top line
147273e1e22Sbostic 			 * is still empty, or "force" is true.
148273e1e22Sbostic 			 */
149273e1e22Sbostic 			eof = 1;
150273e1e22Sbostic 			if (!force && position(TOP) != NULL_POSITION)
151273e1e22Sbostic 				break;
152273e1e22Sbostic 			line = NULL;
153273e1e22Sbostic 		}
154273e1e22Sbostic 		/*
155273e1e22Sbostic 		 * Add the position of the next line to the position table.
156273e1e22Sbostic 		 * Display the current line on the screen.
157273e1e22Sbostic 		 */
158273e1e22Sbostic 		add_forw_pos(pos);
159273e1e22Sbostic 		if (do_repaint)
160273e1e22Sbostic 			continue;
161273e1e22Sbostic 		/*
162*427e68f0Sbostic 		 * If this is the first screen displayed and we hit an early
163*427e68f0Sbostic 		 * EOF (i.e. before the requested number of lines), we
164*427e68f0Sbostic 		 * "squish" the display down at the bottom of the screen.
165*427e68f0Sbostic 		 * But don't do this if a -t option was given; it can cause
166*427e68f0Sbostic 		 * us to start the display after the beginning of the file,
167273e1e22Sbostic 		 * and it is not appropriate to squish in that case.
168273e1e22Sbostic 		 */
169*427e68f0Sbostic 		if (first_time && line == NULL && !top_scroll && !tagoption) {
170273e1e22Sbostic 			squished = 1;
171273e1e22Sbostic 			continue;
172273e1e22Sbostic 		}
173273e1e22Sbostic 		put_line();
174273e1e22Sbostic 	}
175273e1e22Sbostic 
176273e1e22Sbostic 	if (eof && !sigs)
177273e1e22Sbostic 		hit_eof++;
178273e1e22Sbostic 	else
179273e1e22Sbostic 		eof_check();
180*427e68f0Sbostic 	if (do_repaint)
181273e1e22Sbostic 		repaint();
182273e1e22Sbostic 	first_time = 0;
183273e1e22Sbostic 	(void) currline(BOTTOM);
184273e1e22Sbostic }
185273e1e22Sbostic 
186273e1e22Sbostic /*
187273e1e22Sbostic  * Display n lines, scrolling backward.
188273e1e22Sbostic  */
189273e1e22Sbostic back(n, pos, force, only_last)
190273e1e22Sbostic 	register int n;
191*427e68f0Sbostic 	off_t pos;
192273e1e22Sbostic 	int force;
193273e1e22Sbostic 	int only_last;
194273e1e22Sbostic {
195273e1e22Sbostic 	int do_repaint;
196273e1e22Sbostic 
197273e1e22Sbostic 	squish_check();
198273e1e22Sbostic 	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
199273e1e22Sbostic 	hit_eof = 0;
200273e1e22Sbostic 	while (--n >= 0)
201273e1e22Sbostic 	{
202273e1e22Sbostic 		/*
203273e1e22Sbostic 		 * Get the previous line of input.
204273e1e22Sbostic 		 */
205273e1e22Sbostic 		pos = back_line(pos);
206273e1e22Sbostic 		if (pos == NULL_POSITION)
207273e1e22Sbostic 		{
208273e1e22Sbostic 			/*
209273e1e22Sbostic 			 * Beginning of file: stop here unless "force" is true.
210273e1e22Sbostic 			 */
211273e1e22Sbostic 			if (!force)
212273e1e22Sbostic 				break;
213273e1e22Sbostic 			line = NULL;
214273e1e22Sbostic 		}
215273e1e22Sbostic 		/*
216273e1e22Sbostic 		 * Add the position of the previous line to the position table.
217273e1e22Sbostic 		 * Display the line on the screen.
218273e1e22Sbostic 		 */
219273e1e22Sbostic 		add_back_pos(pos);
220273e1e22Sbostic 		if (!do_repaint)
221273e1e22Sbostic 		{
222273e1e22Sbostic 			home();
223273e1e22Sbostic 			add_line();
224273e1e22Sbostic 			put_line();
225273e1e22Sbostic 		}
226273e1e22Sbostic 	}
227273e1e22Sbostic 
228273e1e22Sbostic 	eof_check();
229*427e68f0Sbostic 	if (do_repaint)
230273e1e22Sbostic 		repaint();
231273e1e22Sbostic 	(void) currline(BOTTOM);
232273e1e22Sbostic }
233273e1e22Sbostic 
234273e1e22Sbostic /*
235273e1e22Sbostic  * Display n more lines, forward.
236273e1e22Sbostic  * Start just after the line currently displayed at the bottom of the screen.
237273e1e22Sbostic  */
238273e1e22Sbostic forward(n, only_last)
239273e1e22Sbostic 	int n;
240273e1e22Sbostic 	int only_last;
241273e1e22Sbostic {
242*427e68f0Sbostic 	off_t pos;
243273e1e22Sbostic 
244*427e68f0Sbostic 	if (hit_eof) {
245273e1e22Sbostic 		/*
246*427e68f0Sbostic 		 * If we're trying to go forward from end-of-file,
247*427e68f0Sbostic 		 * go on to the next file.
248273e1e22Sbostic 		 */
249273e1e22Sbostic 		next_file(1);
250273e1e22Sbostic 		return;
251273e1e22Sbostic 	}
252273e1e22Sbostic 
253273e1e22Sbostic 	pos = position(BOTTOM_PLUS_ONE);
254273e1e22Sbostic 	if (pos == NULL_POSITION)
255273e1e22Sbostic 	{
256273e1e22Sbostic 		hit_eof++;
257273e1e22Sbostic 		return;
258273e1e22Sbostic 	}
259273e1e22Sbostic 	forw(n, pos, 0, only_last);
260273e1e22Sbostic }
261273e1e22Sbostic 
262273e1e22Sbostic /*
263273e1e22Sbostic  * Display n more lines, backward.
264273e1e22Sbostic  * Start just before the line currently displayed at the top of the screen.
265273e1e22Sbostic  */
266273e1e22Sbostic backward(n, only_last)
267273e1e22Sbostic 	int n;
268273e1e22Sbostic 	int only_last;
269273e1e22Sbostic {
270*427e68f0Sbostic 	off_t pos;
271273e1e22Sbostic 
272273e1e22Sbostic 	pos = position(TOP);
273273e1e22Sbostic 	/*
274*427e68f0Sbostic 	 * This will almost never happen, because the top line is almost
275*427e68f0Sbostic 	 * never empty.
276273e1e22Sbostic 	 */
277*427e68f0Sbostic 	if (pos == NULL_POSITION)
278273e1e22Sbostic 		return;
279273e1e22Sbostic 	back(n, pos, 0, only_last);
280273e1e22Sbostic }
281273e1e22Sbostic 
282273e1e22Sbostic /*
283273e1e22Sbostic  * Repaint the screen, starting from a specified position.
284273e1e22Sbostic  */
285273e1e22Sbostic prepaint(pos)
286*427e68f0Sbostic 	off_t pos;
287273e1e22Sbostic {
288273e1e22Sbostic 	hit_eof = 0;
289273e1e22Sbostic 	forw(sc_height-1, pos, 1, 0);
290273e1e22Sbostic 	screen_trashed = 0;
291273e1e22Sbostic }
292273e1e22Sbostic 
293273e1e22Sbostic /*
294273e1e22Sbostic  * Repaint the screen.
295273e1e22Sbostic  */
296273e1e22Sbostic repaint()
297273e1e22Sbostic {
298273e1e22Sbostic 	/*
299273e1e22Sbostic 	 * Start at the line currently at the top of the screen
300273e1e22Sbostic 	 * and redisplay the screen.
301273e1e22Sbostic 	 */
302273e1e22Sbostic 	prepaint(position(TOP));
303273e1e22Sbostic }
304273e1e22Sbostic 
305273e1e22Sbostic /*
306273e1e22Sbostic  * Jump to the end of the file.
307273e1e22Sbostic  * It is more convenient to paint the screen backward,
308273e1e22Sbostic  * from the end of the file toward the beginning.
309273e1e22Sbostic  */
310273e1e22Sbostic jump_forw()
311273e1e22Sbostic {
312*427e68f0Sbostic 	off_t pos;
313273e1e22Sbostic 
314273e1e22Sbostic 	if (ch_end_seek())
315273e1e22Sbostic 	{
316273e1e22Sbostic 		error("Cannot seek to end of file");
317273e1e22Sbostic 		return;
318273e1e22Sbostic 	}
319273e1e22Sbostic 	lastmark();
320273e1e22Sbostic 	pos = ch_tell();
321273e1e22Sbostic 	clear();
322273e1e22Sbostic 	pos_clear();
323273e1e22Sbostic 	add_back_pos(pos);
324273e1e22Sbostic 	back(sc_height - 1, pos, 0, 0);
325273e1e22Sbostic }
326273e1e22Sbostic 
327273e1e22Sbostic /*
328273e1e22Sbostic  * Jump to line n in the file.
329273e1e22Sbostic  */
330273e1e22Sbostic jump_back(n)
331273e1e22Sbostic 	register int n;
332273e1e22Sbostic {
333*427e68f0Sbostic 	register int c, nlines;
334273e1e22Sbostic 
335273e1e22Sbostic 	/*
336273e1e22Sbostic 	 * This is done the slow way, by starting at the beginning
337273e1e22Sbostic 	 * of the file and counting newlines.
338273e1e22Sbostic 	 *
339273e1e22Sbostic 	 * {{ Now that we have line numbering (in linenum.c),
340273e1e22Sbostic 	 *    we could improve on this by starting at the
341273e1e22Sbostic 	 *    nearest known line rather than at the beginning. }}
342273e1e22Sbostic 	 */
343*427e68f0Sbostic 	if (ch_seek((off_t)0)) {
344273e1e22Sbostic 		/*
345273e1e22Sbostic 		 * Probably a pipe with beginning of file no longer buffered.
346273e1e22Sbostic 		 * If he wants to go to line 1, we do the best we can,
347273e1e22Sbostic 		 * by going to the first line which is still buffered.
348273e1e22Sbostic 		 */
349273e1e22Sbostic 		if (n <= 1 && ch_beg_seek() == 0)
350273e1e22Sbostic 			jump_loc(ch_tell());
351273e1e22Sbostic 		error("Cannot get to beginning of file");
352273e1e22Sbostic 		return;
353273e1e22Sbostic 	}
354273e1e22Sbostic 
355273e1e22Sbostic 	/*
356273e1e22Sbostic 	 * Start counting lines.
357273e1e22Sbostic 	 */
358273e1e22Sbostic 	for (nlines = 1;  nlines < n;  nlines++)
359273e1e22Sbostic 		while ((c = ch_forw_get()) != '\n')
360*427e68f0Sbostic 			if (c == EOI) {
361273e1e22Sbostic 				char message[40];
3628033092dSbostic 				(void)sprintf(message, "File has only %d lines",
363273e1e22Sbostic 				    nlines - 1);
364273e1e22Sbostic 				error(message);
365273e1e22Sbostic 				return;
366273e1e22Sbostic 			}
367273e1e22Sbostic 	jump_loc(ch_tell());
368273e1e22Sbostic }
369273e1e22Sbostic 
370273e1e22Sbostic /*
371273e1e22Sbostic  * Jump to a specified percentage into the file.
372273e1e22Sbostic  * This is a poor compensation for not being able to
373273e1e22Sbostic  * quickly jump to a specific line number.
374273e1e22Sbostic  */
375273e1e22Sbostic jump_percent(percent)
376273e1e22Sbostic 	int percent;
377273e1e22Sbostic {
378*427e68f0Sbostic 	off_t pos, len, ch_length();
379273e1e22Sbostic 	register int c;
380273e1e22Sbostic 
381273e1e22Sbostic 	/*
382273e1e22Sbostic 	 * Determine the position in the file
383273e1e22Sbostic 	 * (the specified percentage of the file's length).
384273e1e22Sbostic 	 */
385273e1e22Sbostic 	if ((len = ch_length()) == NULL_POSITION)
386273e1e22Sbostic 	{
387273e1e22Sbostic 		error("Don't know length of file");
388273e1e22Sbostic 		return;
389273e1e22Sbostic 	}
390273e1e22Sbostic 	pos = (percent * len) / 100;
391273e1e22Sbostic 
392273e1e22Sbostic 	/*
393273e1e22Sbostic 	 * Back up to the beginning of the line.
394273e1e22Sbostic 	 */
395273e1e22Sbostic 	if (ch_seek(pos) == 0)
396273e1e22Sbostic 	{
397273e1e22Sbostic 		while ((c = ch_back_get()) != '\n' && c != EOI)
398273e1e22Sbostic 			;
399273e1e22Sbostic 		if (c == '\n')
400273e1e22Sbostic 			(void) ch_forw_get();
401273e1e22Sbostic 		pos = ch_tell();
402273e1e22Sbostic 	}
403273e1e22Sbostic 	jump_loc(pos);
404273e1e22Sbostic }
405273e1e22Sbostic 
406273e1e22Sbostic /*
407273e1e22Sbostic  * Jump to a specified position in the file.
408273e1e22Sbostic  */
409273e1e22Sbostic jump_loc(pos)
410*427e68f0Sbostic 	off_t pos;
411273e1e22Sbostic {
412273e1e22Sbostic 	register int nline;
413*427e68f0Sbostic 	off_t tpos;
414273e1e22Sbostic 
415*427e68f0Sbostic 	if ((nline = onscreen(pos)) >= 0) {
416273e1e22Sbostic 		/*
417273e1e22Sbostic 		 * The line is currently displayed.
418273e1e22Sbostic 		 * Just scroll there.
419273e1e22Sbostic 		 */
420273e1e22Sbostic 		forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
421273e1e22Sbostic 		return;
422273e1e22Sbostic 	}
423273e1e22Sbostic 
424273e1e22Sbostic 	/*
425273e1e22Sbostic 	 * Line is not on screen.
426273e1e22Sbostic 	 * Seek to the desired location.
427273e1e22Sbostic 	 */
428*427e68f0Sbostic 	if (ch_seek(pos)) {
429273e1e22Sbostic 		error("Cannot seek to that position");
430273e1e22Sbostic 		return;
431273e1e22Sbostic 	}
432273e1e22Sbostic 
433273e1e22Sbostic 	/*
434*427e68f0Sbostic 	 * See if the desired line is BEFORE the currently displayed screen.
435*427e68f0Sbostic 	 * If so, then move forward far enough so the line we're on will be
436*427e68f0Sbostic 	 * at the bottom of the screen, in order to be able to call back()
437*427e68f0Sbostic 	 * to make the screen scroll backwards & put the line at the top of
438*427e68f0Sbostic 	 * the screen.
439273e1e22Sbostic 	 * {{ This seems inefficient, but it's not so bad,
440273e1e22Sbostic 	 *    since we can never move forward more than a
441273e1e22Sbostic 	 *    screenful before we stop to redraw the screen. }}
442273e1e22Sbostic 	 */
443273e1e22Sbostic 	tpos = position(TOP);
444*427e68f0Sbostic 	if (tpos != NULL_POSITION && pos < tpos) {
445*427e68f0Sbostic 		off_t npos = pos;
446273e1e22Sbostic 		/*
447273e1e22Sbostic 		 * Note that we can't forw_line() past tpos here,
448273e1e22Sbostic 		 * so there should be no EOI at this stage.
449273e1e22Sbostic 		 */
450273e1e22Sbostic 		for (nline = 0;  npos < tpos && nline < sc_height - 1;  nline++)
451273e1e22Sbostic 			npos = forw_line(npos);
452273e1e22Sbostic 
453*427e68f0Sbostic 		if (npos < tpos) {
454273e1e22Sbostic 			/*
455273e1e22Sbostic 			 * More than a screenful back.
456273e1e22Sbostic 			 */
457273e1e22Sbostic 			lastmark();
458273e1e22Sbostic 			clear();
459273e1e22Sbostic 			pos_clear();
460273e1e22Sbostic 			add_back_pos(npos);
461273e1e22Sbostic 		}
462273e1e22Sbostic 
463273e1e22Sbostic 		/*
464273e1e22Sbostic 		 * Note that back() will repaint() if nline > back_scroll.
465273e1e22Sbostic 		 */
466273e1e22Sbostic 		back(nline, npos, 1, 0);
467273e1e22Sbostic 		return;
468273e1e22Sbostic 	}
469273e1e22Sbostic 	/*
470273e1e22Sbostic 	 * Remember where we were; clear and paint the screen.
471273e1e22Sbostic 	 */
472273e1e22Sbostic 	lastmark();
473273e1e22Sbostic 	prepaint(pos);
474273e1e22Sbostic }
475273e1e22Sbostic 
476273e1e22Sbostic /*
477273e1e22Sbostic  * The table of marks.
478273e1e22Sbostic  * A mark is simply a position in the file.
479273e1e22Sbostic  */
480273e1e22Sbostic #define	NMARKS		(27)		/* 26 for a-z plus one for quote */
481273e1e22Sbostic #define	LASTMARK	(NMARKS-1)	/* For quote */
482*427e68f0Sbostic static off_t marks[NMARKS];
483273e1e22Sbostic 
484273e1e22Sbostic /*
485273e1e22Sbostic  * Initialize the mark table to show no marks are set.
486273e1e22Sbostic  */
487273e1e22Sbostic init_mark()
488273e1e22Sbostic {
489273e1e22Sbostic 	int i;
490273e1e22Sbostic 
491273e1e22Sbostic 	for (i = 0;  i < NMARKS;  i++)
492273e1e22Sbostic 		marks[i] = NULL_POSITION;
493273e1e22Sbostic }
494273e1e22Sbostic 
495273e1e22Sbostic /*
496273e1e22Sbostic  * See if a mark letter is valid (between a and z).
497273e1e22Sbostic  */
498273e1e22Sbostic 	static int
499273e1e22Sbostic badmark(c)
500273e1e22Sbostic 	int c;
501273e1e22Sbostic {
502273e1e22Sbostic 	if (c < 'a' || c > 'z')
503273e1e22Sbostic 	{
504273e1e22Sbostic 		error("Choose a letter between 'a' and 'z'");
505273e1e22Sbostic 		return (1);
506273e1e22Sbostic 	}
507273e1e22Sbostic 	return (0);
508273e1e22Sbostic }
509273e1e22Sbostic 
510273e1e22Sbostic /*
511273e1e22Sbostic  * Set a mark.
512273e1e22Sbostic  */
513273e1e22Sbostic setmark(c)
514273e1e22Sbostic 	int c;
515273e1e22Sbostic {
516273e1e22Sbostic 	if (badmark(c))
517273e1e22Sbostic 		return;
518273e1e22Sbostic 	marks[c-'a'] = position(TOP);
519273e1e22Sbostic }
520273e1e22Sbostic 
521273e1e22Sbostic lastmark()
522273e1e22Sbostic {
523273e1e22Sbostic 	marks[LASTMARK] = position(TOP);
524273e1e22Sbostic }
525273e1e22Sbostic 
526273e1e22Sbostic /*
527273e1e22Sbostic  * Go to a previously set mark.
528273e1e22Sbostic  */
529273e1e22Sbostic gomark(c)
530273e1e22Sbostic 	int c;
531273e1e22Sbostic {
532*427e68f0Sbostic 	off_t pos;
533273e1e22Sbostic 
534273e1e22Sbostic 	if (c == '\'')
535273e1e22Sbostic 		pos = marks[LASTMARK];
536273e1e22Sbostic 	else if (badmark(c))
537273e1e22Sbostic 		return;
538273e1e22Sbostic 	else
539273e1e22Sbostic 		pos = marks[c-'a'];
540273e1e22Sbostic 
541273e1e22Sbostic 	if (pos == NULL_POSITION)
542273e1e22Sbostic 		error("mark not set");
543273e1e22Sbostic 	else
544273e1e22Sbostic 		jump_loc(pos);
545273e1e22Sbostic }
546273e1e22Sbostic 
547273e1e22Sbostic /*
548273e1e22Sbostic  * Get the backwards scroll limit.
549273e1e22Sbostic  * Must call this function instead of just using the value of
550273e1e22Sbostic  * back_scroll, because the default case depends on sc_height and
551273e1e22Sbostic  * top_scroll, as well as back_scroll.
552273e1e22Sbostic  */
553273e1e22Sbostic get_back_scroll()
554273e1e22Sbostic {
555273e1e22Sbostic 	if (back_scroll >= 0)
556273e1e22Sbostic 		return (back_scroll);
557273e1e22Sbostic 	if (top_scroll)
558273e1e22Sbostic 		return (sc_height - 2);
559273e1e22Sbostic 	return (sc_height - 1);
560273e1e22Sbostic }
561273e1e22Sbostic 
562273e1e22Sbostic /*
563273e1e22Sbostic  * Search for the n-th occurence of a specified pattern,
564273e1e22Sbostic  * either forward or backward.
565273e1e22Sbostic  */
566273e1e22Sbostic search(search_forward, pattern, n, wantmatch)
567273e1e22Sbostic 	register int search_forward;
568273e1e22Sbostic 	register char *pattern;
569273e1e22Sbostic 	register int n;
570273e1e22Sbostic 	int wantmatch;
571273e1e22Sbostic {
572*427e68f0Sbostic 	off_t pos, linepos;
573273e1e22Sbostic 	register char *p;
574273e1e22Sbostic 	register char *q;
575273e1e22Sbostic 	int linenum;
576273e1e22Sbostic 	int linematch;
577*427e68f0Sbostic #ifdef RECOMP
578273e1e22Sbostic 	char *re_comp();
579273e1e22Sbostic 	char *errmsg;
580273e1e22Sbostic #else
581*427e68f0Sbostic #ifdef REGCMP
582273e1e22Sbostic 	char *regcmp();
583273e1e22Sbostic 	static char *cpattern = NULL;
584273e1e22Sbostic #else
585273e1e22Sbostic 	static char lpbuf[100];
586273e1e22Sbostic 	static char *last_pattern = NULL;
5878033092dSbostic 	char *strcpy();
588273e1e22Sbostic #endif
589273e1e22Sbostic #endif
590273e1e22Sbostic 
591273e1e22Sbostic 	/*
592*427e68f0Sbostic 	 * For a caseless search, convert any uppercase in the pattern to
593*427e68f0Sbostic 	 * lowercase.
594273e1e22Sbostic 	 */
595*427e68f0Sbostic 	if (caseless && pattern != NULL)
596*427e68f0Sbostic 		for (p = pattern;  *p;  p++)
597*427e68f0Sbostic 			if (isupper(*p))
598*427e68f0Sbostic 				*p = tolower(*p);
599*427e68f0Sbostic #ifdef RECOMP
600273e1e22Sbostic 
601273e1e22Sbostic 	/*
602273e1e22Sbostic 	 * (re_comp handles a null pattern internally,
603273e1e22Sbostic 	 *  so there is no need to check for a null pattern here.)
604273e1e22Sbostic 	 */
605273e1e22Sbostic 	if ((errmsg = re_comp(pattern)) != NULL)
606273e1e22Sbostic 	{
607273e1e22Sbostic 		error(errmsg);
608*427e68f0Sbostic 		return(0);
609273e1e22Sbostic 	}
610273e1e22Sbostic #else
611*427e68f0Sbostic #ifdef REGCMP
612273e1e22Sbostic 	if (pattern == NULL || *pattern == '\0')
613273e1e22Sbostic 	{
614273e1e22Sbostic 		/*
615273e1e22Sbostic 		 * A null pattern means use the previous pattern.
616273e1e22Sbostic 		 * The compiled previous pattern is in cpattern, so just use it.
617273e1e22Sbostic 		 */
618273e1e22Sbostic 		if (cpattern == NULL)
619273e1e22Sbostic 		{
620273e1e22Sbostic 			error("No previous regular expression");
621*427e68f0Sbostic 			return(0);
622273e1e22Sbostic 		}
623273e1e22Sbostic 	} else
624273e1e22Sbostic 	{
625273e1e22Sbostic 		/*
626273e1e22Sbostic 		 * Otherwise compile the given pattern.
627273e1e22Sbostic 		 */
628273e1e22Sbostic 		char *s;
629273e1e22Sbostic 		if ((s = regcmp(pattern, 0)) == NULL)
630273e1e22Sbostic 		{
631273e1e22Sbostic 			error("Invalid pattern");
632*427e68f0Sbostic 			return(0);
633273e1e22Sbostic 		}
634273e1e22Sbostic 		if (cpattern != NULL)
635273e1e22Sbostic 			free(cpattern);
636273e1e22Sbostic 		cpattern = s;
637273e1e22Sbostic 	}
638273e1e22Sbostic #else
639273e1e22Sbostic 	if (pattern == NULL || *pattern == '\0')
640273e1e22Sbostic 	{
641273e1e22Sbostic 		/*
642273e1e22Sbostic 		 * Null pattern means use the previous pattern.
643273e1e22Sbostic 		 */
644273e1e22Sbostic 		if (last_pattern == NULL)
645273e1e22Sbostic 		{
646273e1e22Sbostic 			error("No previous regular expression");
647*427e68f0Sbostic 			return(0);
648273e1e22Sbostic 		}
649273e1e22Sbostic 		pattern = last_pattern;
650273e1e22Sbostic 	} else
651273e1e22Sbostic 	{
6528033092dSbostic 		(void)strcpy(lpbuf, pattern);
653273e1e22Sbostic 		last_pattern = lpbuf;
654273e1e22Sbostic 	}
655273e1e22Sbostic #endif
656273e1e22Sbostic #endif
657273e1e22Sbostic 
658273e1e22Sbostic 	/*
659273e1e22Sbostic 	 * Figure out where to start the search.
660273e1e22Sbostic 	 */
661273e1e22Sbostic 
662*427e68f0Sbostic 	if (position(TOP) == NULL_POSITION) {
663273e1e22Sbostic 		/*
664*427e68f0Sbostic 		 * Nothing is currently displayed.  Start at the beginning
665*427e68f0Sbostic 		 * of the file.  (This case is mainly for searches from the
666*427e68f0Sbostic 		 * command line.
667273e1e22Sbostic 		 */
668*427e68f0Sbostic 		pos = (off_t)0;
669*427e68f0Sbostic 	} else if (!search_forward) {
670273e1e22Sbostic 		/*
671273e1e22Sbostic 		 * Backward search: start just before the top line
672273e1e22Sbostic 		 * displayed on the screen.
673273e1e22Sbostic 		 */
674273e1e22Sbostic 		pos = position(TOP);
675*427e68f0Sbostic 	} else {
676273e1e22Sbostic 		/*
677273e1e22Sbostic 		 * Start at the second screen line displayed on the screen.
678273e1e22Sbostic 		 */
679273e1e22Sbostic 		pos = position(TOP_PLUS_ONE);
680273e1e22Sbostic 	}
681273e1e22Sbostic 
682273e1e22Sbostic 	if (pos == NULL_POSITION)
683273e1e22Sbostic 	{
684273e1e22Sbostic 		/*
685273e1e22Sbostic 		 * Can't find anyplace to start searching from.
686273e1e22Sbostic 		 */
687273e1e22Sbostic 		error("Nothing to search");
688*427e68f0Sbostic 		return(0);
689273e1e22Sbostic 	}
690273e1e22Sbostic 
691273e1e22Sbostic 	linenum = find_linenum(pos);
692273e1e22Sbostic 	for (;;)
693273e1e22Sbostic 	{
694273e1e22Sbostic 		/*
695273e1e22Sbostic 		 * Get lines until we find a matching one or
696273e1e22Sbostic 		 * until we hit end-of-file (or beginning-of-file
697273e1e22Sbostic 		 * if we're going backwards).
698273e1e22Sbostic 		 */
699273e1e22Sbostic 		if (sigs)
700273e1e22Sbostic 			/*
701273e1e22Sbostic 			 * A signal aborts the search.
702273e1e22Sbostic 			 */
703*427e68f0Sbostic 			return(0);
704273e1e22Sbostic 
705273e1e22Sbostic 		if (search_forward)
706273e1e22Sbostic 		{
707273e1e22Sbostic 			/*
708273e1e22Sbostic 			 * Read the next line, and save the
709273e1e22Sbostic 			 * starting position of that line in linepos.
710273e1e22Sbostic 			 */
711273e1e22Sbostic 			linepos = pos;
712273e1e22Sbostic 			pos = forw_raw_line(pos);
713273e1e22Sbostic 			if (linenum != 0)
714273e1e22Sbostic 				linenum++;
715273e1e22Sbostic 		} else
716273e1e22Sbostic 		{
717273e1e22Sbostic 			/*
718273e1e22Sbostic 			 * Read the previous line and save the
719273e1e22Sbostic 			 * starting position of that line in linepos.
720273e1e22Sbostic 			 */
721273e1e22Sbostic 			pos = back_raw_line(pos);
722273e1e22Sbostic 			linepos = pos;
723273e1e22Sbostic 			if (linenum != 0)
724273e1e22Sbostic 				linenum--;
725273e1e22Sbostic 		}
726273e1e22Sbostic 
727273e1e22Sbostic 		if (pos == NULL_POSITION)
728273e1e22Sbostic 		{
729273e1e22Sbostic 			/*
730273e1e22Sbostic 			 * We hit EOF/BOF without a match.
731273e1e22Sbostic 			 */
732273e1e22Sbostic 			error("Pattern not found");
733*427e68f0Sbostic 			return(0);
734273e1e22Sbostic 		}
735273e1e22Sbostic 
736273e1e22Sbostic 		/*
737273e1e22Sbostic 		 * If we're using line numbers, we might as well
738273e1e22Sbostic 		 * remember the information we have now (the position
739273e1e22Sbostic 		 * and line number of the current line).
740273e1e22Sbostic 		 */
741273e1e22Sbostic 		if (linenums)
742273e1e22Sbostic 			add_lnum(linenum, pos);
743273e1e22Sbostic 
744273e1e22Sbostic 		/*
745*427e68f0Sbostic 		 * If this is a caseless search, convert uppercase in the
746*427e68f0Sbostic 		 * input line to lowercase.
747273e1e22Sbostic 		 */
748*427e68f0Sbostic 		if (caseless)
749*427e68f0Sbostic 			for (p = q = line;  *p;  p++, q++)
750*427e68f0Sbostic 				*q = isupper(*p) ? tolower(*p) : *p;
751*427e68f0Sbostic 
752*427e68f0Sbostic 		/*
753*427e68f0Sbostic 		 * Remove any backspaces along with the preceeding char.
754*427e68f0Sbostic 		 * This allows us to match text which is underlined or
755*427e68f0Sbostic 		 * overstruck.
756*427e68f0Sbostic 		 */
757*427e68f0Sbostic 		for (p = q = line;  *p;  p++, q++)
758*427e68f0Sbostic 			if (q > line && *p == '\b')
759273e1e22Sbostic 				/* Delete BS and preceeding char. */
760273e1e22Sbostic 				q -= 2;
761273e1e22Sbostic 			else
762273e1e22Sbostic 				/* Otherwise, just copy. */
763273e1e22Sbostic 				*q = *p;
764273e1e22Sbostic 
765273e1e22Sbostic 		/*
766273e1e22Sbostic 		 * Test the next line to see if we have a match.
767273e1e22Sbostic 		 * This is done in a variety of ways, depending
768273e1e22Sbostic 		 * on what pattern matching functions are available.
769273e1e22Sbostic 		 */
770*427e68f0Sbostic #ifdef REGCMP
771273e1e22Sbostic 		linematch = (regex(cpattern, line) != NULL);
772273e1e22Sbostic #else
773*427e68f0Sbostic #ifdef RECOMP
774273e1e22Sbostic 		linematch = (re_exec(line) == 1);
775273e1e22Sbostic #else
776273e1e22Sbostic 		linematch = match(pattern, line);
777273e1e22Sbostic #endif
778273e1e22Sbostic #endif
779273e1e22Sbostic 		/*
780273e1e22Sbostic 		 * We are successful if wantmatch and linematch are
781273e1e22Sbostic 		 * both true (want a match and got it),
782273e1e22Sbostic 		 * or both false (want a non-match and got it).
783273e1e22Sbostic 		 */
784273e1e22Sbostic 		if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
785273e1e22Sbostic 		      --n <= 0)
786273e1e22Sbostic 			/*
787273e1e22Sbostic 			 * Found the line.
788273e1e22Sbostic 			 */
789273e1e22Sbostic 			break;
790273e1e22Sbostic 	}
791273e1e22Sbostic 	jump_loc(linepos);
792*427e68f0Sbostic 	return(1);
793273e1e22Sbostic }
794273e1e22Sbostic 
795*427e68f0Sbostic #if !defined(REGCMP) && !defined(RECOMP)
796273e1e22Sbostic /*
797273e1e22Sbostic  * We have neither regcmp() nor re_comp().
798273e1e22Sbostic  * We use this function to do simple pattern matching.
799273e1e22Sbostic  * It supports no metacharacters like *, etc.
800273e1e22Sbostic  */
801*427e68f0Sbostic static
802273e1e22Sbostic match(pattern, buf)
803273e1e22Sbostic 	char *pattern, *buf;
804273e1e22Sbostic {
805273e1e22Sbostic 	register char *pp, *lp;
806273e1e22Sbostic 
807273e1e22Sbostic 	for ( ;  *buf != '\0';  buf++)
808273e1e22Sbostic 	{
809273e1e22Sbostic 		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
810273e1e22Sbostic 			if (*pp == '\0' || *lp == '\0')
811273e1e22Sbostic 				break;
812273e1e22Sbostic 		if (*pp == '\0')
813273e1e22Sbostic 			return (1);
814273e1e22Sbostic 	}
815273e1e22Sbostic 	return (0);
816273e1e22Sbostic }
817273e1e22Sbostic #endif
818