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