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