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