1 /* 2 * Copyright (C) 1984-2022 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 * Primitives for displaying the file on the screen, 13 * scrolling either forward or backward. 14 */ 15 16 #include "less.h" 17 #include "position.h" 18 19 public int screen_trashed; 20 public int squished; 21 public int no_back_scroll = 0; 22 public int forw_prompt; 23 public int first_time = 1; 24 25 extern int sigs; 26 extern int top_scroll; 27 extern int quiet; 28 extern int sc_width, sc_height; 29 extern int hshift; 30 extern int auto_wrap; 31 extern int plusoption; 32 extern int forw_scroll; 33 extern int back_scroll; 34 extern int ignore_eoi; 35 extern int clear_bg; 36 extern int final_attr; 37 extern int header_lines; 38 extern int header_cols; 39 #if HILITE_SEARCH 40 extern int size_linebuf; 41 extern int hilite_search; 42 extern int status_col; 43 #endif 44 #if TAGS 45 extern char *tagoption; 46 #endif 47 48 /* 49 * Sound the bell to indicate user is trying to move past end of file. 50 */ 51 static void 52 eof_bell(VOID_PARAM) 53 { 54 #if HAVE_TIME 55 static time_type last_eof_bell = 0; 56 time_type now = get_time(); 57 if (now == last_eof_bell) /* max once per second */ 58 return; 59 last_eof_bell = now; 60 #endif 61 if (quiet == NOT_QUIET) 62 bell(); 63 else 64 vbell(); 65 } 66 67 /* 68 * Check to see if the end of file is currently displayed. 69 */ 70 public int 71 eof_displayed(VOID_PARAM) 72 { 73 POSITION pos; 74 75 if (ignore_eoi) 76 return (0); 77 78 if (ch_length() == NULL_POSITION) 79 /* 80 * If the file length is not known, 81 * we can't possibly be displaying EOF. 82 */ 83 return (0); 84 85 /* 86 * If the bottom line is empty, we are at EOF. 87 * If the bottom line ends at the file length, 88 * we must be just at EOF. 89 */ 90 pos = position(BOTTOM_PLUS_ONE); 91 return (pos == NULL_POSITION || pos == ch_length()); 92 } 93 94 /* 95 * Check to see if the entire file is currently displayed. 96 */ 97 public int 98 entire_file_displayed(VOID_PARAM) 99 { 100 POSITION pos; 101 102 /* Make sure last line of file is displayed. */ 103 if (!eof_displayed()) 104 return (0); 105 106 /* Make sure first line of file is displayed. */ 107 pos = position(0); 108 return (pos == NULL_POSITION || pos == 0); 109 } 110 111 /* 112 * If the screen is "squished", repaint it. 113 * "Squished" means the first displayed line is not at the top 114 * of the screen; this can happen when we display a short file 115 * for the first time. 116 */ 117 public void 118 squish_check(VOID_PARAM) 119 { 120 if (!squished) 121 return; 122 squished = 0; 123 repaint(); 124 } 125 126 /* 127 * Read the first pfx columns of the next line. 128 * If skipeol==0 stop there, otherwise read and discard chars to end of line. 129 */ 130 static POSITION 131 forw_line_pfx(pos, pfx, skipeol) 132 POSITION pos; 133 int pfx; 134 int skipeol; 135 { 136 int save_sc_width = sc_width; 137 int save_auto_wrap = auto_wrap; 138 int save_hshift = hshift; 139 /* Set fake sc_width to force only pfx chars to be read. */ 140 sc_width = pfx + line_pfx_width(); 141 auto_wrap = 0; 142 hshift = 0; 143 pos = forw_line_seg(pos, skipeol, FALSE, FALSE); 144 sc_width = save_sc_width; 145 auto_wrap = save_auto_wrap; 146 hshift = save_hshift; 147 return pos; 148 } 149 150 /* 151 * Set header text color. 152 * Underline last line of headers, but not at beginning of file 153 * (where there is no gap between the last header line and the next line). 154 */ 155 static void 156 set_attr_header(ln) 157 int ln; 158 { 159 set_attr_line(AT_COLOR_HEADER); 160 if (ln+1 == header_lines && position(0) != ch_zero()) 161 set_attr_line(AT_UNDERLINE); 162 } 163 164 /* 165 * Display file headers, overlaying text already drawn 166 * at top and left of screen. 167 */ 168 public int 169 overlay_header(VOID_PARAM) 170 { 171 POSITION pos = ch_zero(); /* header lines are at beginning of file */ 172 int ln; 173 int moved = FALSE; 174 175 if (header_lines > 0) 176 { 177 /* Draw header_lines lines from start of file at top of screen. */ 178 home(); 179 for (ln = 0; ln < header_lines; ++ln) 180 { 181 pos = forw_line(pos); 182 set_attr_header(ln); 183 clear_eol(); 184 put_line(); 185 } 186 moved = TRUE; 187 } 188 if (header_cols > 0) 189 { 190 /* Draw header_cols columns at left of each line. */ 191 home(); 192 pos = ch_zero(); 193 for (ln = 0; ln < sc_height-1; ++ln) 194 { 195 if (ln >= header_lines) /* switch from header lines to normal lines */ 196 pos = position(ln); 197 if (pos == NULL_POSITION) 198 putchr('\n'); 199 else 200 { 201 /* Need skipeol for all header lines except the last one. */ 202 pos = forw_line_pfx(pos, header_cols, ln+1 < header_lines); 203 set_attr_header(ln); 204 put_line(); 205 } 206 } 207 moved = TRUE; 208 } 209 if (moved) 210 lower_left(); 211 return moved; 212 } 213 214 /* 215 * Display n lines, scrolling forward, 216 * starting at position pos in the input file. 217 * "force" means display the n lines even if we hit end of file. 218 * "only_last" means display only the last screenful if n > screen size. 219 * "nblank" is the number of blank lines to draw before the first 220 * real line. If nblank > 0, the pos must be NULL_POSITION. 221 * The first real line after the blanks will start at ch_zero(). 222 */ 223 public void 224 forw(n, pos, force, only_last, nblank) 225 int n; 226 POSITION pos; 227 int force; 228 int only_last; 229 int nblank; 230 { 231 int nlines = 0; 232 int do_repaint; 233 234 squish_check(); 235 236 /* 237 * do_repaint tells us not to display anything till the end, 238 * then just repaint the entire screen. 239 * We repaint if we are supposed to display only the last 240 * screenful and the request is for more than a screenful. 241 * Also if the request exceeds the forward scroll limit 242 * (but not if the request is for exactly a screenful, since 243 * repainting itself involves scrolling forward a screenful). 244 */ 245 do_repaint = (only_last && n > sc_height-1) || 246 (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1); 247 248 #if HILITE_SEARCH 249 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) { 250 prep_hilite(pos, pos + 4*size_linebuf, ignore_eoi ? 1 : -1); 251 pos = next_unfiltered(pos); 252 } 253 #endif 254 255 if (!do_repaint) 256 { 257 if (top_scroll && n >= sc_height - 1 && pos != ch_length()) 258 { 259 /* 260 * Start a new screen. 261 * {{ This is not really desirable if we happen 262 * to hit eof in the middle of this screen, 263 * but we don't yet know if that will happen. }} 264 */ 265 pos_clear(); 266 add_forw_pos(pos); 267 force = 1; 268 clear(); 269 home(); 270 } 271 272 if (pos != position(BOTTOM_PLUS_ONE) || empty_screen()) 273 { 274 /* 275 * This is not contiguous with what is 276 * currently displayed. Clear the screen image 277 * (position table) and start a new screen. 278 */ 279 pos_clear(); 280 add_forw_pos(pos); 281 force = 1; 282 if (top_scroll) 283 { 284 clear(); 285 home(); 286 } else if (!first_time && !is_filtering()) 287 { 288 putstr("...skipping...\n"); 289 } 290 } 291 } 292 293 while (--n >= 0) 294 { 295 /* 296 * Read the next line of input. 297 */ 298 if (nblank > 0) 299 { 300 /* 301 * Still drawing blanks; don't get a line 302 * from the file yet. 303 * If this is the last blank line, get ready to 304 * read a line starting at ch_zero() next time. 305 */ 306 if (--nblank == 0) 307 pos = ch_zero(); 308 } else 309 { 310 /* 311 * Get the next line from the file. 312 */ 313 pos = forw_line(pos); 314 #if HILITE_SEARCH 315 pos = next_unfiltered(pos); 316 #endif 317 if (pos == NULL_POSITION) 318 { 319 /* 320 * End of file: stop here unless the top line 321 * is still empty, or "force" is true. 322 * Even if force is true, stop when the last 323 * line in the file reaches the top of screen. 324 */ 325 if (!force && position(TOP) != NULL_POSITION) 326 break; 327 if (!empty_lines(0, 0) && 328 !empty_lines(1, 1) && 329 empty_lines(2, sc_height-1)) 330 break; 331 } 332 } 333 /* 334 * Add the position of the next line to the position table. 335 * Display the current line on the screen. 336 */ 337 add_forw_pos(pos); 338 nlines++; 339 if (do_repaint) 340 continue; 341 /* 342 * If this is the first screen displayed and 343 * we hit an early EOF (i.e. before the requested 344 * number of lines), we "squish" the display down 345 * at the bottom of the screen. 346 * But don't do this if a + option or a -t option 347 * was given. These options can cause us to 348 * start the display after the beginning of the file, 349 * and it is not appropriate to squish in that case. 350 */ 351 if (first_time && pos == NULL_POSITION && !top_scroll && 352 #if TAGS 353 tagoption == NULL && 354 #endif 355 !plusoption) 356 { 357 squished = 1; 358 continue; 359 } 360 put_line(); 361 #if 0 362 /* {{ 363 * Can't call clear_eol here. The cursor might be at end of line 364 * on an ignaw terminal, so clear_eol would clear the last char 365 * of the current line instead of all of the next line. 366 * If we really need to do this on clear_bg terminals, we need 367 * to find a better way. 368 * }} 369 */ 370 if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL) 371 { 372 /* 373 * Writing the last character on the last line 374 * of the display may have scrolled the screen. 375 * If we were in standout mode, clear_bg terminals 376 * will fill the new line with the standout color. 377 * Now we're in normal mode again, so clear the line. 378 */ 379 clear_eol(); 380 } 381 #endif 382 forw_prompt = 1; 383 } 384 385 if (header_lines > 0) 386 { 387 /* 388 * Don't allow ch_zero to appear on screen except at top of screen. 389 * Otherwise duplicate header lines may be displayed. 390 */ 391 if (onscreen(ch_zero()) > 0) 392 { 393 jump_loc(ch_zero(), 0); /* {{ yuck }} */ 394 return; 395 } 396 } 397 if (nlines == 0 && !ignore_eoi) 398 eof_bell(); 399 else if (do_repaint) 400 repaint(); 401 else 402 { 403 overlay_header(); 404 /* lower_left(); {{ considered harmful? }} */ 405 } 406 first_time = 0; 407 (void) currline(BOTTOM); 408 } 409 410 /* 411 * Display n lines, scrolling backward. 412 */ 413 public void 414 back(n, pos, force, only_last) 415 int n; 416 POSITION pos; 417 int force; 418 int only_last; 419 { 420 int nlines = 0; 421 int do_repaint; 422 423 squish_check(); 424 do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1) || header_lines > 0); 425 #if HILITE_SEARCH 426 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) { 427 prep_hilite((pos < 3*size_linebuf) ? 0 : pos - 3*size_linebuf, pos, -1); 428 } 429 #endif 430 while (--n >= 0) 431 { 432 /* 433 * Get the previous line of input. 434 */ 435 #if HILITE_SEARCH 436 pos = prev_unfiltered(pos); 437 #endif 438 439 pos = back_line(pos); 440 if (pos == NULL_POSITION) 441 { 442 /* 443 * Beginning of file: stop here unless "force" is true. 444 */ 445 if (!force) 446 break; 447 } 448 /* 449 * Add the position of the previous line to the position table. 450 * Display the line on the screen. 451 */ 452 add_back_pos(pos); 453 nlines++; 454 if (!do_repaint) 455 { 456 home(); 457 add_line(); 458 put_line(); 459 } 460 } 461 if (nlines == 0) 462 eof_bell(); 463 else if (do_repaint) 464 repaint(); 465 else 466 { 467 overlay_header(); 468 lower_left(); 469 } 470 (void) currline(BOTTOM); 471 } 472 473 /* 474 * Display n more lines, forward. 475 * Start just after the line currently displayed at the bottom of the screen. 476 */ 477 public void 478 forward(n, force, only_last) 479 int n; 480 int force; 481 int only_last; 482 { 483 POSITION pos; 484 485 if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE)) 486 { 487 /* 488 * If the -e flag is set and we're trying to go 489 * forward from end-of-file, go on to the next file. 490 */ 491 if (edit_next(1)) 492 quit(QUIT_OK); 493 return; 494 } 495 496 pos = position(BOTTOM_PLUS_ONE); 497 if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1))) 498 { 499 if (ignore_eoi) 500 { 501 /* 502 * ignore_eoi is to support A_F_FOREVER. 503 * Back up until there is a line at the bottom 504 * of the screen. 505 */ 506 if (empty_screen()) 507 pos = ch_zero(); 508 else 509 { 510 do 511 { 512 back(1, position(TOP), 1, 0); 513 pos = position(BOTTOM_PLUS_ONE); 514 } while (pos == NULL_POSITION); 515 } 516 } else 517 { 518 eof_bell(); 519 return; 520 } 521 } 522 forw(n, pos, force, only_last, 0); 523 } 524 525 /* 526 * Display n more lines, backward. 527 * Start just before the line currently displayed at the top of the screen. 528 */ 529 public void 530 backward(n, force, only_last) 531 int n; 532 int force; 533 int only_last; 534 { 535 POSITION pos; 536 537 pos = position(TOP); 538 if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0)) 539 { 540 eof_bell(); 541 return; 542 } 543 back(n, pos, force, only_last); 544 } 545 546 /* 547 * Get the backwards scroll limit. 548 * Must call this function instead of just using the value of 549 * back_scroll, because the default case depends on sc_height and 550 * top_scroll, as well as back_scroll. 551 */ 552 public int 553 get_back_scroll(VOID_PARAM) 554 { 555 if (no_back_scroll) 556 return (0); 557 if (back_scroll >= 0) 558 return (back_scroll); 559 if (top_scroll) 560 return (sc_height - 2); 561 return (10000); /* infinity */ 562 } 563 564 /* 565 * Will the entire file fit on one screen? 566 */ 567 public int 568 get_one_screen(VOID_PARAM) 569 { 570 int nlines; 571 POSITION pos = ch_zero(); 572 573 for (nlines = 0; nlines < sc_height; nlines++) 574 { 575 pos = forw_line(pos); 576 if (pos == NULL_POSITION) break; 577 } 578 return (nlines < sc_height); 579 } 580