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