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 * High level routines dealing with getting lines of input 14 * from the file being viewed. 15 * 16 * When we speak of "lines" here, we mean PRINTABLE lines; 17 * lines processed with respect to the screen width. 18 * We use the term "raw line" to refer to lines simply 19 * delimited by newlines; not processed with respect to screen width. 20 */ 21 22 #include "less.h" 23 24 extern int squeeze; 25 extern int chopline; 26 extern int hshift; 27 extern int quit_if_one_screen; 28 extern int ignore_eoi; 29 extern int status_col; 30 extern off_t start_attnpos; 31 extern off_t end_attnpos; 32 extern int hilite_search; 33 extern int size_linebuf; 34 35 /* 36 * Get the next line. 37 * A "current" position is passed and a "new" position is returned. 38 * The current position is the position of the first character of 39 * a line. The new position is the position of the first character 40 * of the NEXT line. The line obtained is the line starting at curr_pos. 41 */ 42 off_t 43 forw_line(off_t curr_pos) 44 { 45 off_t base_pos; 46 off_t new_pos; 47 int c; 48 int blankline; 49 int endline; 50 int backchars; 51 52 get_forw_line: 53 if (curr_pos == -1) { 54 null_line(); 55 return (-1); 56 } 57 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) 58 /* 59 * If we are ignoring EOI (command F), only prepare 60 * one line ahead, to avoid getting stuck waiting for 61 * slow data without displaying the data we already have. 62 * If we're not ignoring EOI, we *could* do the same, but 63 * for efficiency we prepare several lines ahead at once. 64 */ 65 prep_hilite(curr_pos, curr_pos + 3*size_linebuf, 66 ignore_eoi ? 1 : -1); 67 if (ch_seek(curr_pos)) { 68 null_line(); 69 return (-1); 70 } 71 72 /* 73 * Step back to the beginning of the line. 74 */ 75 base_pos = curr_pos; 76 for (;;) { 77 if (abort_sigs()) { 78 null_line(); 79 return (-1); 80 } 81 c = ch_back_get(); 82 if (c == EOI) 83 break; 84 if (c == '\n') { 85 (void) ch_forw_get(); 86 break; 87 } 88 --base_pos; 89 } 90 91 /* 92 * Read forward again to the position we should start at. 93 */ 94 prewind(); 95 plinenum(base_pos); 96 (void) ch_seek(base_pos); 97 new_pos = base_pos; 98 while (new_pos < curr_pos) { 99 if (abort_sigs()) { 100 null_line(); 101 return (-1); 102 } 103 c = ch_forw_get(); 104 backchars = pappend(c, new_pos); 105 new_pos++; 106 if (backchars > 0) { 107 pshift_all(); 108 new_pos -= backchars; 109 while (--backchars >= 0) 110 (void) ch_back_get(); 111 } 112 } 113 (void) pflushmbc(); 114 pshift_all(); 115 116 /* 117 * Read the first character to display. 118 */ 119 c = ch_forw_get(); 120 if (c == EOI) { 121 null_line(); 122 return (-1); 123 } 124 blankline = (c == '\n' || c == '\r'); 125 126 /* 127 * Read each character in the line and append to the line buffer. 128 */ 129 for (;;) { 130 if (abort_sigs()) { 131 null_line(); 132 return (-1); 133 } 134 if (c == '\n' || c == EOI) { 135 /* 136 * End of the line. 137 */ 138 backchars = pflushmbc(); 139 new_pos = ch_tell(); 140 if (backchars > 0 && !chopline && hshift == 0) { 141 new_pos -= backchars + 1; 142 endline = FALSE; 143 } else 144 endline = TRUE; 145 break; 146 } 147 if (c != '\r') 148 blankline = 0; 149 150 /* 151 * Append the char to the line and get the next char. 152 */ 153 backchars = pappend(c, ch_tell()-1); 154 if (backchars > 0) { 155 /* 156 * The char won't fit in the line; the line 157 * is too long to print in the screen width. 158 * End the line here. 159 */ 160 if (chopline || hshift > 0) { 161 do { 162 if (abort_sigs()) { 163 null_line(); 164 return (-1); 165 } 166 c = ch_forw_get(); 167 } while (c != '\n' && c != EOI); 168 new_pos = ch_tell(); 169 endline = TRUE; 170 quit_if_one_screen = FALSE; 171 } else { 172 new_pos = ch_tell() - backchars; 173 endline = FALSE; 174 } 175 break; 176 } 177 c = ch_forw_get(); 178 } 179 180 pdone(endline, 1); 181 182 if (is_filtered(base_pos)) { 183 /* 184 * We don't want to display this line. 185 * Get the next line. 186 */ 187 curr_pos = new_pos; 188 goto get_forw_line; 189 } 190 191 if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL)) 192 set_status_col('*'); 193 194 if (squeeze && blankline) { 195 /* 196 * This line is blank. 197 * Skip down to the last contiguous blank line 198 * and pretend it is the one which we are returning. 199 */ 200 while ((c = ch_forw_get()) == '\n' || c == '\r') 201 if (abort_sigs()) { 202 null_line(); 203 return (-1); 204 } 205 if (c != EOI) 206 (void) ch_back_get(); 207 new_pos = ch_tell(); 208 } 209 210 return (new_pos); 211 } 212 213 /* 214 * Get the previous line. 215 * A "current" position is passed and a "new" position is returned. 216 * The current position is the position of the first character of 217 * a line. The new position is the position of the first character 218 * of the PREVIOUS line. The line obtained is the one starting at new_pos. 219 */ 220 off_t 221 back_line(off_t curr_pos) 222 { 223 off_t new_pos, begin_new_pos, base_pos; 224 int c; 225 int endline; 226 int backchars; 227 228 get_back_line: 229 if (curr_pos == -1 || curr_pos <= ch_zero()) { 230 null_line(); 231 return (-1); 232 } 233 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) 234 prep_hilite((curr_pos < 3*size_linebuf) ? 235 0 : curr_pos - 3*size_linebuf, curr_pos, -1); 236 if (ch_seek(curr_pos-1)) { 237 null_line(); 238 return (-1); 239 } 240 241 if (squeeze) { 242 /* 243 * Find out if the "current" line was blank. 244 */ 245 (void) ch_forw_get(); /* Skip the newline */ 246 c = ch_forw_get(); /* First char of "current" line */ 247 (void) ch_back_get(); /* Restore our position */ 248 (void) ch_back_get(); 249 250 if (c == '\n' || c == '\r') { 251 /* 252 * The "current" line was blank. 253 * Skip over any preceding blank lines, 254 * since we skipped them in forw_line(). 255 */ 256 while ((c = ch_back_get()) == '\n' || c == '\r') 257 if (abort_sigs()) { 258 null_line(); 259 return (-1); 260 } 261 if (c == EOI) { 262 null_line(); 263 return (-1); 264 } 265 (void) ch_forw_get(); 266 } 267 } 268 269 /* 270 * Scan backwards until we hit the beginning of the line. 271 */ 272 for (;;) { 273 if (abort_sigs()) { 274 null_line(); 275 return (-1); 276 } 277 c = ch_back_get(); 278 if (c == '\n') { 279 /* 280 * This is the newline ending the previous line. 281 * We have hit the beginning of the line. 282 */ 283 base_pos = ch_tell() + 1; 284 break; 285 } 286 if (c == EOI) { 287 /* 288 * We have hit the beginning of the file. 289 * This must be the first line in the file. 290 * This must, of course, be the beginning of the line. 291 */ 292 base_pos = ch_tell(); 293 break; 294 } 295 } 296 297 /* 298 * Now scan forwards from the beginning of this line. 299 * We keep discarding "printable lines" (based on screen width) 300 * until we reach the curr_pos. 301 * 302 * {{ This algorithm is pretty inefficient if the lines 303 * are much longer than the screen width, 304 * but I don't know of any better way. }} 305 */ 306 new_pos = base_pos; 307 if (ch_seek(new_pos)) { 308 null_line(); 309 return (-1); 310 } 311 endline = FALSE; 312 prewind(); 313 plinenum(new_pos); 314 loop: 315 begin_new_pos = new_pos; 316 (void) ch_seek(new_pos); 317 318 do { 319 c = ch_forw_get(); 320 if (c == EOI || abort_sigs()) { 321 null_line(); 322 return (-1); 323 } 324 new_pos++; 325 if (c == '\n') { 326 backchars = pflushmbc(); 327 if (backchars > 0 && !chopline && hshift == 0) { 328 backchars++; 329 goto shift; 330 } 331 endline = TRUE; 332 break; 333 } 334 backchars = pappend(c, ch_tell()-1); 335 if (backchars > 0) { 336 /* 337 * Got a full printable line, but we haven't 338 * reached our curr_pos yet. Discard the line 339 * and start a new one. 340 */ 341 if (chopline || hshift > 0) { 342 endline = TRUE; 343 quit_if_one_screen = FALSE; 344 break; 345 } 346 shift: 347 pshift_all(); 348 while (backchars-- > 0) { 349 (void) ch_back_get(); 350 new_pos--; 351 } 352 goto loop; 353 } 354 } while (new_pos < curr_pos); 355 356 pdone(endline, 0); 357 358 if (is_filtered(base_pos)) { 359 /* 360 * We don't want to display this line. 361 * Get the previous line. 362 */ 363 curr_pos = begin_new_pos; 364 goto get_back_line; 365 } 366 367 if (status_col && curr_pos > 0 && 368 is_hilited(base_pos, curr_pos-1, 1, NULL)) 369 set_status_col('*'); 370 371 return (begin_new_pos); 372 } 373 374 /* 375 * Set attnpos. 376 */ 377 void 378 set_attnpos(off_t pos) 379 { 380 int c; 381 382 if (pos != -1) { 383 if (ch_seek(pos)) 384 return; 385 for (;;) { 386 c = ch_forw_get(); 387 if (c == EOI) 388 return; 389 if (c != '\n' && c != '\r') 390 break; 391 pos++; 392 } 393 } 394 start_attnpos = pos; 395 for (;;) { 396 c = ch_forw_get(); 397 pos++; 398 if (c == EOI || c == '\n' || c == '\r') 399 break; 400 } 401 end_attnpos = pos; 402 } 403