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