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