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