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