1 /* 2 * Copyright (C) 1984-2022 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information, see the README file. 8 */ 9 10 /* 11 * High level routines dealing with getting lines of input 12 * from the file being viewed. 13 * 14 * When we speak of "lines" here, we mean PRINTABLE lines; 15 * lines processed with respect to the screen width. 16 * We use the term "raw line" to refer to lines simply 17 * delimited by newlines; not processed with respect to screen width. 18 */ 19 20 #include "less.h" 21 22 extern int squeeze; 23 extern int hshift; 24 extern int quit_if_one_screen; 25 extern int sigs; 26 extern int ignore_eoi; 27 extern int status_col; 28 extern POSITION start_attnpos; 29 extern POSITION end_attnpos; 30 #if HILITE_SEARCH 31 extern int hilite_search; 32 extern int size_linebuf; 33 extern int show_attn; 34 #endif 35 36 /* 37 * Get the next line. 38 * A "current" position is passed and a "new" position is returned. 39 * The current position is the position of the first character of 40 * a line. The new position is the position of the first character 41 * of the NEXT line. The line obtained is the line starting at curr_pos. 42 */ 43 public POSITION 44 forw_line_seg(curr_pos, skipeol, rscroll, nochop) 45 POSITION curr_pos; 46 int skipeol; 47 int rscroll; 48 int nochop; 49 { 50 POSITION base_pos; 51 POSITION new_pos; 52 int c; 53 int blankline; 54 int endline; 55 int chopped; 56 int backchars; 57 58 get_forw_line: 59 if (curr_pos == NULL_POSITION) 60 { 61 null_line(); 62 return (NULL_POSITION); 63 } 64 #if HILITE_SEARCH 65 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) 66 { 67 /* 68 * If we are ignoring EOI (command F), only prepare 69 * one line ahead, to avoid getting stuck waiting for 70 * slow data without displaying the data we already have. 71 * If we're not ignoring EOI, we *could* do the same, but 72 * for efficiency we prepare several lines ahead at once. 73 */ 74 prep_hilite(curr_pos, curr_pos + 3*size_linebuf, 75 ignore_eoi ? 1 : -1); 76 curr_pos = next_unfiltered(curr_pos); 77 } 78 #endif 79 if (ch_seek(curr_pos)) 80 { 81 null_line(); 82 return (NULL_POSITION); 83 } 84 85 /* 86 * Step back to the beginning of the line. 87 */ 88 base_pos = curr_pos; 89 for (;;) 90 { 91 if (ABORT_SIGS()) 92 { 93 null_line(); 94 return (NULL_POSITION); 95 } 96 c = ch_back_get(); 97 if (c == EOI) 98 break; 99 if (c == '\n') 100 { 101 (void) ch_forw_get(); 102 break; 103 } 104 --base_pos; 105 } 106 107 /* 108 * Read forward again to the position we should start at. 109 */ 110 prewind(); 111 plinestart(base_pos); 112 (void) ch_seek(base_pos); 113 new_pos = base_pos; 114 while (new_pos < curr_pos) 115 { 116 if (ABORT_SIGS()) 117 { 118 null_line(); 119 return (NULL_POSITION); 120 } 121 c = ch_forw_get(); 122 backchars = pappend(c, new_pos); 123 new_pos++; 124 if (backchars > 0) 125 { 126 pshift_all(); 127 new_pos -= backchars; 128 while (--backchars >= 0) 129 (void) ch_back_get(); 130 } 131 } 132 (void) pflushmbc(); 133 pshift_all(); 134 135 /* 136 * Read the first character to display. 137 */ 138 c = ch_forw_get(); 139 if (c == EOI) 140 { 141 null_line(); 142 return (NULL_POSITION); 143 } 144 blankline = (c == '\n' || c == '\r'); 145 146 /* 147 * Read each character in the line and append to the line buffer. 148 */ 149 chopped = FALSE; 150 for (;;) 151 { 152 if (ABORT_SIGS()) 153 { 154 null_line(); 155 return (NULL_POSITION); 156 } 157 if (c == '\n' || c == EOI) 158 { 159 /* 160 * End of the line. 161 */ 162 backchars = pflushmbc(); 163 new_pos = ch_tell(); 164 if (backchars > 0 && (nochop || !chop_line()) && hshift == 0) 165 { 166 new_pos -= backchars + 1; 167 endline = FALSE; 168 } else 169 endline = TRUE; 170 break; 171 } 172 if (c != '\r') 173 blankline = 0; 174 175 /* 176 * Append the char to the line and get the next char. 177 */ 178 backchars = pappend(c, ch_tell()-1); 179 if (backchars > 0) 180 { 181 /* 182 * The char won't fit in the line; the line 183 * is too long to print in the screen width. 184 * End the line here. 185 */ 186 if (skipeol) 187 { 188 /* Read to end of line. */ 189 do 190 { 191 if (ABORT_SIGS()) 192 { 193 null_line(); 194 return (NULL_POSITION); 195 } 196 c = ch_forw_get(); 197 } while (c != '\n' && c != EOI); 198 new_pos = ch_tell(); 199 endline = TRUE; 200 quit_if_one_screen = FALSE; 201 chopped = TRUE; 202 } else 203 { 204 new_pos = ch_tell() - backchars; 205 endline = FALSE; 206 } 207 break; 208 } 209 c = ch_forw_get(); 210 } 211 212 #if HILITE_SEARCH 213 if (blankline && show_attn) 214 { 215 /* Add spurious space to carry possible attn hilite. */ 216 pappend(' ', ch_tell()-1); 217 } 218 #endif 219 pdone(endline, rscroll && chopped, 1); 220 221 #if HILITE_SEARCH 222 if (is_filtered(base_pos)) 223 { 224 /* 225 * We don't want to display this line. 226 * Get the next line. 227 */ 228 curr_pos = new_pos; 229 goto get_forw_line; 230 } 231 232 if (status_col) 233 { 234 int attr = is_hilited_attr(base_pos, ch_tell()-1, 1, NULL); 235 if (attr) 236 set_status_col('*', attr); 237 } 238 #endif 239 240 if (squeeze && blankline) 241 { 242 /* 243 * This line is blank. 244 * Skip down to the last contiguous blank line 245 * and pretend it is the one which we are returning. 246 */ 247 while ((c = ch_forw_get()) == '\n' || c == '\r') 248 if (ABORT_SIGS()) 249 { 250 null_line(); 251 return (NULL_POSITION); 252 } 253 if (c != EOI) 254 (void) ch_back_get(); 255 new_pos = ch_tell(); 256 } 257 258 return (new_pos); 259 } 260 261 public POSITION 262 forw_line(curr_pos) 263 POSITION curr_pos; 264 { 265 266 return forw_line_seg(curr_pos, (chop_line() || hshift > 0), TRUE, FALSE); 267 } 268 269 /* 270 * Get the previous line. 271 * A "current" position is passed and a "new" position is returned. 272 * The current position is the position of the first character of 273 * a line. The new position is the position of the first character 274 * of the PREVIOUS line. The line obtained is the one starting at new_pos. 275 */ 276 public POSITION 277 back_line(curr_pos) 278 POSITION curr_pos; 279 { 280 POSITION new_pos, begin_new_pos, base_pos; 281 int c; 282 int endline; 283 int chopped; 284 int backchars; 285 286 get_back_line: 287 if (curr_pos == NULL_POSITION || curr_pos <= ch_zero()) 288 { 289 null_line(); 290 return (NULL_POSITION); 291 } 292 #if HILITE_SEARCH 293 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) 294 prep_hilite((curr_pos < 3*size_linebuf) ? 295 0 : curr_pos - 3*size_linebuf, curr_pos, -1); 296 #endif 297 if (ch_seek(curr_pos-1)) 298 { 299 null_line(); 300 return (NULL_POSITION); 301 } 302 303 if (squeeze) 304 { 305 /* 306 * Find out if the "current" line was blank. 307 */ 308 (void) ch_forw_get(); /* Skip the newline */ 309 c = ch_forw_get(); /* First char of "current" line */ 310 (void) ch_back_get(); /* Restore our position */ 311 (void) ch_back_get(); 312 313 if (c == '\n' || c == '\r') 314 { 315 /* 316 * The "current" line was blank. 317 * Skip over any preceding blank lines, 318 * since we skipped them in forw_line(). 319 */ 320 while ((c = ch_back_get()) == '\n' || c == '\r') 321 if (ABORT_SIGS()) 322 { 323 null_line(); 324 return (NULL_POSITION); 325 } 326 if (c == EOI) 327 { 328 null_line(); 329 return (NULL_POSITION); 330 } 331 (void) ch_forw_get(); 332 } 333 } 334 335 /* 336 * Scan backwards until we hit the beginning of the line. 337 */ 338 for (;;) 339 { 340 if (ABORT_SIGS()) 341 { 342 null_line(); 343 return (NULL_POSITION); 344 } 345 c = ch_back_get(); 346 if (c == '\n') 347 { 348 /* 349 * This is the newline ending the previous line. 350 * We have hit the beginning of the line. 351 */ 352 base_pos = ch_tell() + 1; 353 break; 354 } 355 if (c == EOI) 356 { 357 /* 358 * We have hit the beginning of the file. 359 * This must be the first line in the file. 360 * This must, of course, be the beginning of the line. 361 */ 362 base_pos = ch_tell(); 363 break; 364 } 365 } 366 367 /* 368 * Now scan forwards from the beginning of this line. 369 * We keep discarding "printable lines" (based on screen width) 370 * until we reach the curr_pos. 371 * 372 * {{ This algorithm is pretty inefficient if the lines 373 * are much longer than the screen width, 374 * but I don't know of any better way. }} 375 */ 376 new_pos = base_pos; 377 if (ch_seek(new_pos)) 378 { 379 null_line(); 380 return (NULL_POSITION); 381 } 382 endline = FALSE; 383 prewind(); 384 plinestart(new_pos); 385 loop: 386 begin_new_pos = new_pos; 387 (void) ch_seek(new_pos); 388 chopped = FALSE; 389 390 do 391 { 392 c = ch_forw_get(); 393 if (c == EOI || ABORT_SIGS()) 394 { 395 null_line(); 396 return (NULL_POSITION); 397 } 398 new_pos++; 399 if (c == '\n') 400 { 401 backchars = pflushmbc(); 402 if (backchars > 0 && !chop_line() && hshift == 0) 403 { 404 backchars++; 405 goto shift; 406 } 407 endline = TRUE; 408 break; 409 } 410 backchars = pappend(c, ch_tell()-1); 411 if (backchars > 0) 412 { 413 /* 414 * Got a full printable line, but we haven't 415 * reached our curr_pos yet. Discard the line 416 * and start a new one. 417 */ 418 if (chop_line() || hshift > 0) 419 { 420 endline = TRUE; 421 chopped = TRUE; 422 quit_if_one_screen = FALSE; 423 break; 424 } 425 shift: 426 pshift_all(); 427 while (backchars-- > 0) 428 { 429 (void) ch_back_get(); 430 new_pos--; 431 } 432 goto loop; 433 } 434 } while (new_pos < curr_pos); 435 436 pdone(endline, chopped, 0); 437 438 #if HILITE_SEARCH 439 if (is_filtered(base_pos)) 440 { 441 /* 442 * We don't want to display this line. 443 * Get the previous line. 444 */ 445 curr_pos = begin_new_pos; 446 goto get_back_line; 447 } 448 449 if (status_col && curr_pos > 0) 450 { 451 int attr = is_hilited_attr(base_pos, curr_pos-1, 1, NULL); 452 if (attr) 453 set_status_col('*', attr); 454 } 455 #endif 456 457 return (begin_new_pos); 458 } 459 460 /* 461 * Set attnpos. 462 */ 463 public void 464 set_attnpos(pos) 465 POSITION pos; 466 { 467 int c; 468 469 if (pos != NULL_POSITION) 470 { 471 if (ch_seek(pos)) 472 return; 473 for (;;) 474 { 475 c = ch_forw_get(); 476 if (c == EOI) 477 break; 478 if (c == '\n' || c == '\r') 479 { 480 (void) ch_back_get(); 481 break; 482 } 483 pos++; 484 } 485 end_attnpos = pos; 486 for (;;) 487 { 488 c = ch_back_get(); 489 if (c == EOI || c == '\n' || c == '\r') 490 break; 491 pos--; 492 } 493 } 494 start_attnpos = pos; 495 } 496