xref: /netbsd-src/external/bsd/less/dist/jump.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: jump.c,v 1.2 2011/07/03 19:51:26 tron Exp $	*/
2 
3 /*
4  * Copyright (C) 1984-2011  Mark Nudelman
5  *
6  * You may distribute under the terms of either the GNU General Public
7  * License or the Less License, as specified in the README file.
8  *
9  * For more information about less, or for information on how to
10  * contact the author, see the README file.
11  */
12 
13 
14 /*
15  * Routines which jump to a new location in the file.
16  */
17 
18 #include "less.h"
19 #include "position.h"
20 
21 extern int jump_sline;
22 extern int squished;
23 extern int screen_trashed;
24 extern int sc_width, sc_height;
25 extern int show_attn;
26 extern int top_scroll;
27 
28 /*
29  * Jump to the end of the file.
30  */
31 	public void
32 jump_forw()
33 {
34 	POSITION pos;
35 	POSITION end_pos;
36 
37 	if (ch_end_seek())
38 	{
39 		error("Cannot seek to end of file", NULL_PARG);
40 		return;
41 	}
42 	/*
43 	 * Note; lastmark will be called later by jump_loc, but it fails
44 	 * because the position table has been cleared by pos_clear below.
45 	 * So call it here before calling pos_clear.
46 	 */
47 	lastmark();
48 	/*
49 	 * Position the last line in the file at the last screen line.
50 	 * Go back one line from the end of the file
51 	 * to get to the beginning of the last line.
52 	 */
53 	pos_clear();
54 	end_pos = ch_tell();
55 	pos = back_line(end_pos);
56 	if (pos == NULL_POSITION)
57 		jump_loc((POSITION)0, sc_height-1);
58 	else
59 	{
60 		jump_loc(pos, sc_height-1);
61 		if (position(sc_height-1) != end_pos)
62 			repaint();
63 	}
64 }
65 
66 /*
67  * Jump to line n in the file.
68  */
69 	public void
70 jump_back(linenum)
71 	LINENUM linenum;
72 {
73 	POSITION pos;
74 	PARG parg;
75 
76 	/*
77 	 * Find the position of the specified line.
78 	 * If we can seek there, just jump to it.
79 	 * If we can't seek, but we're trying to go to line number 1,
80 	 * use ch_beg_seek() to get as close as we can.
81 	 */
82 	pos = find_pos(linenum);
83 	if (pos != NULL_POSITION && ch_seek(pos) == 0)
84 	{
85 		if (show_attn)
86 			set_attnpos(pos);
87 		jump_loc(pos, jump_sline);
88 	} else if (linenum <= 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_linenum = linenum;
95 		error("Cannot seek to line number %n", &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, fraction)
120 	int percent;
121 	long fraction;
122 {
123 	POSITION pos, len;
124 
125 	/*
126 	 * Determine the position in the file
127 	 * (the specified percentage of the file's length).
128 	 */
129 	if ((len = ch_length()) == NULL_POSITION)
130 	{
131 		ierror("Determining length of file", NULL_PARG);
132 		ch_end_seek();
133 	}
134 	if ((len = ch_length()) == NULL_POSITION)
135 	{
136 		error("Don't know length of file", NULL_PARG);
137 		return;
138 	}
139 	pos = percent_pos(len, percent, fraction);
140 	if (pos >= len)
141 		pos = len-1;
142 
143 	jump_line_loc(pos, jump_sline);
144 }
145 
146 /*
147  * Jump to a specified position in the file.
148  * Like jump_loc, but the position need not be
149  * the first character in a line.
150  */
151 	public void
152 jump_line_loc(pos, sline)
153 	POSITION pos;
154 	int sline;
155 {
156 	int c;
157 
158 	if (ch_seek(pos) == 0)
159 	{
160 		/*
161 		 * Back up to the beginning of the line.
162 		 */
163 		while ((c = ch_back_get()) != '\n' && c != EOI)
164 			;
165 		if (c == '\n')
166 			(void) ch_forw_get();
167 		pos = ch_tell();
168 	}
169 	if (show_attn)
170 		set_attnpos(pos);
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 #if HILITE_SEARCH
205 		if (show_attn)
206 			repaint_hilite(1);
207 #endif
208 		return;
209 	}
210 
211 	/*
212 	 * Line is not on screen.
213 	 * Seek to the desired location.
214 	 */
215 	if (ch_seek(pos))
216 	{
217 		error("Cannot seek to that file position", NULL_PARG);
218 		return;
219 	}
220 
221 	/*
222 	 * See if the desired line is before or after
223 	 * the currently displayed screen.
224 	 */
225 	tpos = position(TOP);
226 	bpos = position(BOTTOM_PLUS_ONE);
227 	if (tpos == NULL_POSITION || pos >= tpos)
228 	{
229 		/*
230 		 * The desired line is after the current screen.
231 		 * Move back in the file far enough so that we can
232 		 * call forw() and put the desired line at the
233 		 * sline-th line on the screen.
234 		 */
235 		for (nline = 0;  nline < sline;  nline++)
236 		{
237 			if (bpos != NULL_POSITION && pos <= bpos)
238 			{
239 				/*
240 				 * Surprise!  The desired line is
241 				 * close enough to the current screen
242 				 * that we can just scroll there after all.
243 				 */
244 				forw(sc_height-sline+nline-1, bpos, 1, 0, 0);
245 #if HILITE_SEARCH
246 				if (show_attn)
247 					repaint_hilite(1);
248 #endif
249 				return;
250 			}
251 			pos = back_line(pos);
252 			if (pos == NULL_POSITION)
253 			{
254 				/*
255 				 * Oops.  Ran into the beginning of the file.
256 				 * Exit the loop here and rely on forw()
257 				 * below to draw the required number of
258 				 * blank lines at the top of the screen.
259 				 */
260 				break;
261 			}
262 		}
263 		lastmark();
264 		squished = 0;
265 		screen_trashed = 0;
266 		forw(sc_height-1, pos, 1, 0, sline-nline);
267 	} else
268 	{
269 		/*
270 		 * The desired line is before the current screen.
271 		 * Move forward in the file far enough so that we
272 		 * can call back() and put the desired line at the
273 		 * sline-th line on the screen.
274 		 */
275 		for (nline = sline;  nline < sc_height - 1;  nline++)
276 		{
277 			pos = forw_line(pos);
278 			if (pos == NULL_POSITION)
279 			{
280 				/*
281 				 * Ran into end of file.
282 				 * This shouldn't normally happen,
283 				 * but may if there is some kind of read error.
284 				 */
285 				break;
286 			}
287 			if (pos >= tpos)
288 			{
289 				/*
290 				 * Surprise!  The desired line is
291 				 * close enough to the current screen
292 				 * that we can just scroll there after all.
293 				 */
294 				back(nline+1, tpos, 1, 0);
295 #if HILITE_SEARCH
296 				if (show_attn)
297 					repaint_hilite(1);
298 #endif
299 				return;
300 			}
301 		}
302 		lastmark();
303 		if (!top_scroll)
304 			clear();
305 		else
306 			home();
307 		screen_trashed = 0;
308 		add_back_pos(pos);
309 		back(sc_height-1, pos, 1, 0);
310 	}
311 }
312