1 /* 2 * Copyright (C) 1984-2012 Mark Nudelman 3 * Modified for use with illumos by Garrett D'Amore. 4 * Copyright 2014 Garrett D'Amore <garrett@damore.org> 5 * 6 * You may distribute under the terms of either the GNU General Public 7 * License or the Less License, as specified in the README file. 8 * 9 * For more information, see the README file. 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 int screen_trashed; 21 int squished; 22 int no_back_scroll = 0; 23 int forw_prompt; 24 25 extern int top_scroll; 26 extern int quiet; 27 extern int sc_width, sc_height; 28 extern int plusoption; 29 extern int forw_scroll; 30 extern int back_scroll; 31 extern int ignore_eoi; 32 extern int clear_bg; 33 extern int final_attr; 34 extern int oldbot; 35 extern char *tagoption; 36 37 /* 38 * Sound the bell to indicate user is trying to move past end of file. 39 */ 40 static void 41 eof_bell(void) 42 { 43 if (quiet == NOT_QUIET) 44 ring_bell(); 45 else 46 vbell(); 47 } 48 49 /* 50 * Check to see if the end of file is currently displayed. 51 */ 52 int 53 eof_displayed(void) 54 { 55 off_t pos; 56 57 if (ignore_eoi) 58 return (0); 59 60 if (ch_length() == -1) 61 /* 62 * If the file length is not known, 63 * we can't possibly be displaying EOF. 64 */ 65 return (0); 66 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 return (pos == -1 || pos == ch_length()); 74 } 75 76 /* 77 * Check to see if the entire file is currently displayed. 78 */ 79 int 80 entire_file_displayed(void) 81 { 82 off_t pos; 83 84 /* Make sure last line of file is displayed. */ 85 if (!eof_displayed()) 86 return (0); 87 88 /* Make sure first line of file is displayed. */ 89 pos = position(0); 90 return (pos == -1 || pos == 0); 91 } 92 93 /* 94 * If the screen is "squished", repaint it. 95 * "Squished" means the first displayed line is not at the top 96 * of the screen; this can happen when we display a short file 97 * for the first time. 98 */ 99 void 100 squish_check(void) 101 { 102 if (!squished) 103 return; 104 squished = 0; 105 repaint(); 106 } 107 108 /* 109 * Display n lines, scrolling forward, 110 * starting at position pos in the input file. 111 * "force" means display the n lines even if we hit end of file. 112 * "only_last" means display only the last screenful if n > screen size. 113 * "nblank" is the number of blank lines to draw before the first 114 * real line. If nblank > 0, the pos must be -1. 115 * The first real line after the blanks will start at ch_zero(). 116 */ 117 void 118 forw(int n, off_t pos, int force, int only_last, int nblank) 119 { 120 int nlines = 0; 121 int do_repaint; 122 static int first_time = 1; 123 124 squish_check(); 125 126 /* 127 * do_repaint tells us not to display anything till the end, 128 * then just repaint the entire screen. 129 * We repaint if we are supposed to display only the last 130 * screenful and the request is for more than a screenful. 131 * Also if the request exceeds the forward scroll limit 132 * (but not if the request is for exactly a screenful, since 133 * repainting itself involves scrolling forward a screenful). 134 */ 135 do_repaint = (only_last && n > sc_height-1) || 136 (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1); 137 138 if (!do_repaint) { 139 if (top_scroll && n >= sc_height - 1 && pos != ch_length()) { 140 /* 141 * Start a new screen. 142 * {{ This is not really desirable if we happen 143 * to hit eof in the middle of this screen, 144 * but we don't yet know if that will happen. }} 145 */ 146 pos_clear(); 147 add_forw_pos(pos); 148 force = 1; 149 do_clear(); 150 home(); 151 } 152 153 if (pos != position(BOTTOM_PLUS_ONE) || empty_screen()) { 154 /* 155 * This is not contiguous with what is 156 * currently displayed. Clear the screen image 157 * (position table) and start a new screen. 158 */ 159 pos_clear(); 160 add_forw_pos(pos); 161 force = 1; 162 if (top_scroll) { 163 do_clear(); 164 home(); 165 } else if (!first_time) { 166 putstr("...skipping...\n"); 167 } 168 } 169 } 170 171 while (--n >= 0) { 172 /* 173 * Read the next line of input. 174 */ 175 if (nblank > 0) { 176 /* 177 * Still drawing blanks; don't get a line 178 * from the file yet. 179 * If this is the last blank line, get ready to 180 * read a line starting at ch_zero() next time. 181 */ 182 if (--nblank == 0) 183 pos = ch_zero(); 184 } else { 185 /* 186 * Get the next line from the file. 187 */ 188 pos = forw_line(pos); 189 if (pos == -1) { 190 /* 191 * End of file: stop here unless the top line 192 * is still empty, or "force" is true. 193 * Even if force is true, stop when the last 194 * line in the file reaches the top of screen. 195 */ 196 if (!force && position(TOP) != -1) 197 break; 198 if (!empty_lines(0, 0) && 199 !empty_lines(1, 1) && 200 empty_lines(2, sc_height-1)) 201 break; 202 } 203 } 204 /* 205 * Add the position of the next line to the position table. 206 * Display the current line on the screen. 207 */ 208 add_forw_pos(pos); 209 nlines++; 210 if (do_repaint) 211 continue; 212 /* 213 * If this is the first screen displayed and 214 * we hit an early EOF (i.e. before the requested 215 * number of lines), we "squish" the display down 216 * at the bottom of the screen. 217 * But don't do this if a + option or a -t option 218 * was given. These options can cause us to 219 * start the display after the beginning of the file, 220 * and it is not appropriate to squish in that case. 221 */ 222 if (first_time && pos == -1 && !top_scroll && 223 tagoption == NULL && !plusoption) { 224 squished = 1; 225 continue; 226 } 227 put_line(); 228 forw_prompt = 1; 229 } 230 231 if (nlines == 0) 232 eof_bell(); 233 else if (do_repaint) 234 repaint(); 235 first_time = 0; 236 (void) currline(BOTTOM); 237 } 238 239 /* 240 * Display n lines, scrolling backward. 241 */ 242 void 243 back(int n, off_t pos, int force, int only_last) 244 { 245 int nlines = 0; 246 int do_repaint; 247 248 squish_check(); 249 do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); 250 while (--n >= 0) { 251 /* 252 * Get the previous line of input. 253 */ 254 pos = back_line(pos); 255 if (pos == -1) { 256 /* 257 * Beginning of file: stop here unless "force" is true. 258 */ 259 if (!force) 260 break; 261 } 262 /* 263 * Add the position of the previous line to the position table. 264 * Display the line on the screen. 265 */ 266 add_back_pos(pos); 267 nlines++; 268 if (!do_repaint) { 269 home(); 270 add_line(); 271 put_line(); 272 } 273 } 274 275 if (nlines == 0) 276 eof_bell(); 277 else if (do_repaint) 278 repaint(); 279 else if (!oldbot) 280 lower_left(); 281 (void) currline(BOTTOM); 282 } 283 284 /* 285 * Display n more lines, forward. 286 * Start just after the line currently displayed at the bottom of the screen. 287 */ 288 void 289 forward(int n, int force, int only_last) 290 { 291 off_t pos; 292 293 if (get_quit_at_eof() && eof_displayed() && 294 !(ch_getflags() & CH_HELPFILE)) { 295 /* 296 * If the -e flag is set and we're trying to go 297 * forward from end-of-file, go on to the next file. 298 */ 299 if (edit_next(1)) 300 quit(QUIT_OK); 301 return; 302 } 303 304 pos = position(BOTTOM_PLUS_ONE); 305 if (pos == -1 && (!force || empty_lines(2, sc_height-1))) { 306 if (ignore_eoi) { 307 /* 308 * ignore_eoi is to support A_F_FOREVER. 309 * Back up until there is a line at the bottom 310 * of the screen. 311 */ 312 if (empty_screen()) { 313 pos = ch_zero(); 314 } else { 315 do { 316 back(1, position(TOP), 1, 0); 317 pos = position(BOTTOM_PLUS_ONE); 318 } while (pos == -1); 319 } 320 } else { 321 eof_bell(); 322 return; 323 } 324 } 325 forw(n, pos, force, only_last, 0); 326 } 327 328 /* 329 * Display n more lines, backward. 330 * Start just before the line currently displayed at the top of the screen. 331 */ 332 void 333 backward(int n, int force, int only_last) 334 { 335 off_t pos; 336 337 pos = position(TOP); 338 if (pos == -1 && (!force || position(BOTTOM) == 0)) { 339 eof_bell(); 340 return; 341 } 342 back(n, pos, force, only_last); 343 } 344 345 /* 346 * Get the backwards scroll limit. 347 * Must call this function instead of just using the value of 348 * back_scroll, because the default case depends on sc_height and 349 * top_scroll, as well as back_scroll. 350 */ 351 int 352 get_back_scroll(void) 353 { 354 if (no_back_scroll) 355 return (0); 356 if (back_scroll >= 0) 357 return (back_scroll); 358 if (top_scroll) 359 return (sc_height - 2); 360 return (10000); /* infinity */ 361 } 362