xref: /openbsd-src/usr.bin/less/jump.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: jump.c,v 1.2 2001/01/29 01:58:02 niklas Exp $	*/
2 
3 /*
4  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice in the documentation and/or other materials provided with
14  *    the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 /*
31  * Routines which jump to a new location in the file.
32  */
33 
34 #include "less.h"
35 #include "position.h"
36 
37 extern int hit_eof;
38 extern int jump_sline;
39 extern int squished;
40 extern int screen_trashed;
41 extern int sc_width, sc_height;
42 
43 /*
44  * Jump to the end of the file.
45  */
46 	public void
47 jump_forw()
48 {
49 	POSITION pos;
50 
51 	if (ch_end_seek())
52 	{
53 		error("Cannot seek to end of file", NULL_PARG);
54 		return;
55 	}
56 	/*
57 	 * Position the last line in the file at the last screen line.
58 	 * Go back one line from the end of the file
59 	 * to get to the beginning of the last line.
60 	 */
61 	pos = back_line(ch_tell());
62 	if (pos == NULL_POSITION)
63 		jump_loc((POSITION)0, sc_height-1);
64 	else
65 		jump_loc(pos, sc_height-1);
66 }
67 
68 /*
69  * Jump to line n in the file.
70  */
71 	public void
72 jump_back(n)
73 	int n;
74 {
75 	POSITION pos;
76 	PARG parg;
77 
78 	/*
79 	 * Find the position of the specified line.
80 	 * If we can seek there, just jump to it.
81 	 * If we can't seek, but we're trying to go to line number 1,
82 	 * use ch_beg_seek() to get as close as we can.
83 	 */
84 	pos = find_pos(n);
85 	if (pos != NULL_POSITION && ch_seek(pos) == 0)
86 	{
87 		jump_loc(pos, jump_sline);
88 	} else if (n <= 1 && ch_beg_seek() == 0)
89 	{
90 		jump_loc(ch_tell(), jump_sline);
91 		error("Cannot seek to beginning of file", NULL_PARG);
92 	} else
93 	{
94 		parg.p_int = n;
95 		error("Cannot seek to line number %d", &parg);
96 	}
97 }
98 
99 /*
100  * Repaint the screen.
101  */
102 	public void
103 repaint()
104 {
105 	struct scrpos scrpos;
106 	/*
107 	 * Start at the line currently at the top of the screen
108 	 * and redisplay the screen.
109 	 */
110 	get_scrpos(&scrpos);
111 	pos_clear();
112 	jump_loc(scrpos.pos, scrpos.ln);
113 }
114 
115 /*
116  * Jump to a specified percentage into the file.
117  */
118 	public void
119 jump_percent(percent)
120 	int percent;
121 {
122 	POSITION pos, len;
123 
124 	/*
125 	 * Determine the position in the file
126 	 * (the specified percentage of the file's length).
127 	 */
128 	if ((len = ch_length()) == NULL_POSITION)
129 	{
130 		ierror("Determining length of file", NULL_PARG);
131 		ch_end_seek();
132 	}
133 	if ((len = ch_length()) == NULL_POSITION)
134 	{
135 		error("Don't know length of file", NULL_PARG);
136 		return;
137 	}
138 	/*
139 	 * {{ This calculation may overflow! }}
140 	 */
141 	pos = (percent * len) / 100;
142 	if (pos >= len)
143 		pos = len-1;
144 
145 	jump_line_loc(pos, jump_sline);
146 }
147 
148 /*
149  * Jump to a specified position in the file.
150  * Like jump_loc, but the position need not be
151  * the first character in a line.
152  */
153 	public void
154 jump_line_loc(pos, sline)
155 	POSITION pos;
156 	int sline;
157 {
158 	int c;
159 
160 	if (ch_seek(pos) == 0)
161 	{
162 		/*
163 		 * Back up to the beginning of the line.
164 		 */
165 		while ((c = ch_back_get()) != '\n' && c != EOI)
166 			;
167 		if (c == '\n')
168 			(void) ch_forw_get();
169 		pos = ch_tell();
170 	}
171 	jump_loc(pos, sline);
172 }
173 
174 /*
175  * Jump to a specified position in the file.
176  * The position must be the first character in a line.
177  * Place the target line on a specified line on the screen.
178  */
179 	public void
180 jump_loc(pos, sline)
181 	POSITION pos;
182 	int sline;
183 {
184 	register int nline;
185 	POSITION tpos;
186 	POSITION bpos;
187 
188 	/*
189 	 * Normalize sline.
190 	 */
191 	sline = adjsline(sline);
192 
193 	if ((nline = onscreen(pos)) >= 0)
194 	{
195 		/*
196 		 * The line is currently displayed.
197 		 * Just scroll there.
198 		 */
199 		nline -= sline;
200 		if (nline > 0)
201 			forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0);
202 		else
203 			back(-nline, position(TOP), 1, 0);
204 		return;
205 	}
206 
207 	/*
208 	 * Line is not on screen.
209 	 * Seek to the desired location.
210 	 */
211 	if (ch_seek(pos))
212 	{
213 		error("Cannot seek to that file position", NULL_PARG);
214 		return;
215 	}
216 
217 	/*
218 	 * See if the desired line is before or after
219 	 * the currently displayed screen.
220 	 */
221 	tpos = position(TOP);
222 	bpos = position(BOTTOM_PLUS_ONE);
223 	if (tpos == NULL_POSITION || pos >= tpos)
224 	{
225 		/*
226 		 * The desired line is after the current screen.
227 		 * Move back in the file far enough so that we can
228 		 * call forw() and put the desired line at the
229 		 * sline-th line on the screen.
230 		 */
231 		for (nline = 0;  nline < sline;  nline++)
232 		{
233 			if (bpos != NULL_POSITION && pos <= bpos)
234 			{
235 				/*
236 				 * Surprise!  The desired line is
237 				 * close enough to the current screen
238 				 * that we can just scroll there after all.
239 				 */
240 				forw(sc_height-sline+nline-1, bpos, 1, 0, 0);
241 				return;
242 			}
243 			pos = back_line(pos);
244 			if (pos == NULL_POSITION)
245 			{
246 				/*
247 				 * Oops.  Ran into the beginning of the file.
248 				 * Exit the loop here and rely on forw()
249 				 * below to draw the required number of
250 				 * blank lines at the top of the screen.
251 				 */
252 				break;
253 			}
254 		}
255 		lastmark();
256 		hit_eof = 0;
257 		squished = 0;
258 		screen_trashed = 0;
259 		forw(sc_height-1, pos, 1, 0, sline-nline);
260 	} else
261 	{
262 		/*
263 		 * The desired line is before the current screen.
264 		 * Move forward in the file far enough so that we
265 		 * can call back() and put the desired line at the
266 		 * sline-th line on the screen.
267 		 */
268 		for (nline = sline;  nline < sc_height - 1;  nline++)
269 		{
270 			pos = forw_line(pos);
271 			if (pos == NULL_POSITION)
272 			{
273 				/*
274 				 * Ran into end of file.
275 				 * This shouldn't normally happen,
276 				 * but may if there is some kind of read error.
277 				 */
278 				break;
279 			}
280 			if (pos >= tpos)
281 			{
282 				/*
283 				 * Surprise!  The desired line is
284 				 * close enough to the current screen
285 				 * that we can just scroll there after all.
286 				 */
287 				back(nline+1, tpos, 1, 0);
288 				return;
289 			}
290 		}
291 		lastmark();
292 		clear();
293 		screen_trashed = 0;
294 		add_back_pos(pos);
295 		back(sc_height-1, pos, 1, 0);
296 	}
297 }
298