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