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