1 /* $FreeBSD$ */ 2 /* 3 * Copyright (C) 1984-2020 Mark Nudelman 4 * 5 * You may distribute under the terms of either the GNU General Public 6 * License or the Less License, as specified in the README file. 7 * 8 * For more information, see the README file. 9 */ 10 11 12 /* 13 * Primitives for displaying the file on the screen, 14 * scrolling either forward or backward. 15 */ 16 17 #include "less.h" 18 #include "position.h" 19 20 public int screen_trashed; 21 public int squished; 22 public int no_back_scroll = 0; 23 public int forw_prompt; 24 25 extern int sigs; 26 extern int top_scroll; 27 extern int quiet; 28 extern int sc_width, sc_height; 29 extern int less_is_more; 30 extern int plusoption; 31 extern int forw_scroll; 32 extern int back_scroll; 33 extern int ignore_eoi; 34 extern int clear_bg; 35 extern int final_attr; 36 extern int oldbot; 37 #if HILITE_SEARCH 38 extern int size_linebuf; 39 extern int hilite_search; 40 extern int status_col; 41 #endif 42 #if TAGS 43 extern char *tagoption; 44 #endif 45 46 /* 47 * Sound the bell to indicate user is trying to move past end of file. 48 */ 49 static void 50 eof_bell(VOID_PARAM) 51 { 52 if (quiet == NOT_QUIET) 53 bell(); 54 else 55 vbell(); 56 } 57 58 /* 59 * Check to see if the end of file is currently displayed. 60 */ 61 public int 62 eof_displayed(VOID_PARAM) 63 { 64 POSITION pos; 65 66 if (ignore_eoi) 67 return (0); 68 69 if (ch_length() == NULL_POSITION) 70 /* 71 * If the file length is not known, 72 * we can't possibly be displaying EOF. 73 */ 74 return (0); 75 76 /* 77 * If the bottom line is empty, we are at EOF. 78 * If the bottom line ends at the file length, 79 * we must be just at EOF. 80 */ 81 pos = position(BOTTOM_PLUS_ONE); 82 return (pos == NULL_POSITION || pos == ch_length()); 83 } 84 85 /* 86 * Check to see if the entire file is currently displayed. 87 */ 88 public int 89 entire_file_displayed(VOID_PARAM) 90 { 91 POSITION pos; 92 93 /* Make sure last line of file is displayed. */ 94 if (!eof_displayed()) 95 return (0); 96 97 /* Make sure first line of file is displayed. */ 98 pos = position(0); 99 return (pos == NULL_POSITION || pos == 0); 100 } 101 102 /* 103 * If the screen is "squished", repaint it. 104 * "Squished" means the first displayed line is not at the top 105 * of the screen; this can happen when we display a short file 106 * for the first time. 107 */ 108 public void 109 squish_check(VOID_PARAM) 110 { 111 if (!squished) 112 return; 113 squished = 0; 114 repaint(); 115 } 116 117 /* 118 * Display n lines, scrolling forward, 119 * starting at position pos in the input file. 120 * "force" means display the n lines even if we hit end of file. 121 * "only_last" means display only the last screenful if n > screen size. 122 * "nblank" is the number of blank lines to draw before the first 123 * real line. If nblank > 0, the pos must be NULL_POSITION. 124 * The first real line after the blanks will start at ch_zero(). 125 */ 126 public void 127 forw(n, pos, force, only_last, nblank) 128 int n; 129 POSITION pos; 130 int force; 131 int only_last; 132 int nblank; 133 { 134 int nlines = 0; 135 int do_repaint; 136 static int first_time = 1; 137 138 squish_check(); 139 140 /* 141 * do_repaint tells us not to display anything till the end, 142 * then just repaint the entire screen. 143 * We repaint if we are supposed to display only the last 144 * screenful and the request is for more than a screenful. 145 * Also if the request exceeds the forward scroll limit 146 * (but not if the request is for exactly a screenful, since 147 * repainting itself involves scrolling forward a screenful). 148 */ 149 do_repaint = (only_last && n > sc_height-1) || 150 (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1); 151 152 #if HILITE_SEARCH 153 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) { 154 prep_hilite(pos, pos + 4*size_linebuf, ignore_eoi ? 1 : -1); 155 pos = next_unfiltered(pos); 156 } 157 #endif 158 159 if (!do_repaint) 160 { 161 if (top_scroll && n >= sc_height - 1 && pos != ch_length()) 162 { 163 /* 164 * Start a new screen. 165 * {{ This is not really desirable if we happen 166 * to hit eof in the middle of this screen, 167 * but we don't yet know if that will happen. }} 168 */ 169 pos_clear(); 170 add_forw_pos(pos); 171 force = 1; 172 if (less_is_more == 0) { 173 clear(); 174 home(); 175 } 176 } 177 178 if (pos != position(BOTTOM_PLUS_ONE) || empty_screen()) 179 { 180 /* 181 * This is not contiguous with what is 182 * currently displayed. Clear the screen image 183 * (position table) and start a new screen. 184 */ 185 pos_clear(); 186 add_forw_pos(pos); 187 force = 1; 188 if (top_scroll) 189 { 190 clear(); 191 home(); 192 } else if (!first_time) 193 { 194 putstr("...skipping...\n"); 195 } 196 } 197 } 198 199 while (--n >= 0) 200 { 201 /* 202 * Read the next line of input. 203 */ 204 if (nblank > 0) 205 { 206 /* 207 * Still drawing blanks; don't get a line 208 * from the file yet. 209 * If this is the last blank line, get ready to 210 * read a line starting at ch_zero() next time. 211 */ 212 if (--nblank == 0) 213 pos = ch_zero(); 214 } else 215 { 216 /* 217 * Get the next line from the file. 218 */ 219 pos = forw_line(pos); 220 #if HILITE_SEARCH 221 pos = next_unfiltered(pos); 222 #endif 223 if (pos == NULL_POSITION) 224 { 225 /* 226 * End of file: stop here unless the top line 227 * is still empty, or "force" is true. 228 * Even if force is true, stop when the last 229 * line in the file reaches the top of screen. 230 */ 231 if (!force && position(TOP) != NULL_POSITION) 232 break; 233 if (!empty_lines(0, 0) && 234 !empty_lines(1, 1) && 235 empty_lines(2, sc_height-1)) 236 break; 237 } 238 } 239 /* 240 * Add the position of the next line to the position table. 241 * Display the current line on the screen. 242 */ 243 add_forw_pos(pos); 244 nlines++; 245 if (do_repaint) 246 continue; 247 /* 248 * If this is the first screen displayed and 249 * we hit an early EOF (i.e. before the requested 250 * number of lines), we "squish" the display down 251 * at the bottom of the screen. 252 * But don't do this if a + option or a -t option 253 * was given. These options can cause us to 254 * start the display after the beginning of the file, 255 * and it is not appropriate to squish in that case. 256 */ 257 if ((first_time || less_is_more) && 258 pos == NULL_POSITION && !top_scroll && 259 #if TAGS 260 tagoption == NULL && 261 #endif 262 !plusoption) 263 { 264 squished = 1; 265 continue; 266 } 267 put_line(); 268 #if 0 269 /* {{ 270 * Can't call clear_eol here. The cursor might be at end of line 271 * on an ignaw terminal, so clear_eol would clear the last char 272 * of the current line instead of all of the next line. 273 * If we really need to do this on clear_bg terminals, we need 274 * to find a better way. 275 * }} 276 */ 277 if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL) 278 { 279 /* 280 * Writing the last character on the last line 281 * of the display may have scrolled the screen. 282 * If we were in standout mode, clear_bg terminals 283 * will fill the new line with the standout color. 284 * Now we're in normal mode again, so clear the line. 285 */ 286 clear_eol(); 287 } 288 #endif 289 forw_prompt = 1; 290 } 291 292 if (nlines == 0 && !ignore_eoi) 293 eof_bell(); 294 else if (do_repaint) 295 repaint(); 296 first_time = 0; 297 (void) currline(BOTTOM); 298 } 299 300 /* 301 * Display n lines, scrolling backward. 302 */ 303 public void 304 back(n, pos, force, only_last) 305 int n; 306 POSITION pos; 307 int force; 308 int only_last; 309 { 310 int nlines = 0; 311 int do_repaint; 312 313 squish_check(); 314 do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); 315 #if HILITE_SEARCH 316 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) { 317 prep_hilite((pos < 3*size_linebuf) ? 0 : pos - 3*size_linebuf, pos, -1); 318 } 319 #endif 320 while (--n >= 0) 321 { 322 /* 323 * Get the previous line of input. 324 */ 325 #if HILITE_SEARCH 326 pos = prev_unfiltered(pos); 327 #endif 328 329 pos = back_line(pos); 330 if (pos == NULL_POSITION) 331 { 332 /* 333 * Beginning of file: stop here unless "force" is true. 334 */ 335 if (!force) 336 break; 337 } 338 /* 339 * Add the position of the previous line to the position table. 340 * Display the line on the screen. 341 */ 342 add_back_pos(pos); 343 nlines++; 344 if (!do_repaint) 345 { 346 home(); 347 add_line(); 348 put_line(); 349 } 350 } 351 352 if (nlines == 0) 353 eof_bell(); 354 else if (do_repaint) 355 repaint(); 356 else if (!oldbot) 357 lower_left(); 358 (void) currline(BOTTOM); 359 } 360 361 /* 362 * Display n more lines, forward. 363 * Start just after the line currently displayed at the bottom of the screen. 364 */ 365 public void 366 forward(n, force, only_last) 367 int n; 368 int force; 369 int only_last; 370 { 371 POSITION pos; 372 373 if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE)) 374 { 375 /* 376 * If the -e flag is set and we're trying to go 377 * forward from end-of-file, go on to the next file. 378 */ 379 if (edit_next(1)) 380 quit(QUIT_OK); 381 return; 382 } 383 384 pos = position(BOTTOM_PLUS_ONE); 385 if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1))) 386 { 387 if (ignore_eoi) 388 { 389 /* 390 * ignore_eoi is to support A_F_FOREVER. 391 * Back up until there is a line at the bottom 392 * of the screen. 393 */ 394 if (empty_screen()) 395 pos = ch_zero(); 396 else 397 { 398 do 399 { 400 back(1, position(TOP), 1, 0); 401 pos = position(BOTTOM_PLUS_ONE); 402 } while (pos == NULL_POSITION); 403 } 404 } else 405 { 406 eof_bell(); 407 return; 408 } 409 } 410 forw(n, pos, force, only_last, 0); 411 } 412 413 /* 414 * Display n more lines, backward. 415 * Start just before the line currently displayed at the top of the screen. 416 */ 417 public void 418 backward(n, force, only_last) 419 int n; 420 int force; 421 int only_last; 422 { 423 POSITION pos; 424 425 pos = position(TOP); 426 if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0)) 427 { 428 eof_bell(); 429 return; 430 } 431 back(n, pos, force, only_last); 432 } 433 434 /* 435 * Get the backwards scroll limit. 436 * Must call this function instead of just using the value of 437 * back_scroll, because the default case depends on sc_height and 438 * top_scroll, as well as back_scroll. 439 */ 440 public int 441 get_back_scroll(VOID_PARAM) 442 { 443 if (no_back_scroll) 444 return (0); 445 if (back_scroll >= 0) 446 return (back_scroll); 447 if (top_scroll) 448 return (sc_height - 2); 449 return (10000); /* infinity */ 450 } 451 452 /* 453 * Will the entire file fit on one screen? 454 */ 455 public int 456 get_one_screen(VOID_PARAM) 457 { 458 int nlines; 459 POSITION pos = ch_zero(); 460 461 for (nlines = 0; nlines < sc_height; nlines++) 462 { 463 pos = forw_line(pos); 464 if (pos == NULL_POSITION) break; 465 } 466 return (nlines < sc_height); 467 } 468