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