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