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