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