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