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