1 /* 2 * Copyright (C) 1984-2017 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 /* 12 * High level routines dealing with getting lines of input 13 * from the file being viewed. 14 * 15 * When we speak of "lines" here, we mean PRINTABLE lines; 16 * lines processed with respect to the screen width. 17 * We use the term "raw line" to refer to lines simply 18 * delimited by newlines; not processed with respect to screen width. 19 */ 20 21 #include "less.h" 22 23 extern int squeeze; 24 extern int chopline; 25 extern int hshift; 26 extern int quit_if_one_screen; 27 extern int sigs; 28 extern int ignore_eoi; 29 extern int status_col; 30 extern POSITION start_attnpos; 31 extern POSITION end_attnpos; 32 #if HILITE_SEARCH 33 extern int hilite_search; 34 extern int size_linebuf; 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(curr_pos) 46 POSITION curr_pos; 47 { 48 POSITION base_pos; 49 POSITION new_pos; 50 int c; 51 int blankline; 52 int endline; 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 for (;;) 147 { 148 if (ABORT_SIGS()) 149 { 150 null_line(); 151 return (NULL_POSITION); 152 } 153 if (c == '\n' || c == EOI) 154 { 155 /* 156 * End of the line. 157 */ 158 backchars = pflushmbc(); 159 new_pos = ch_tell(); 160 if (backchars > 0 && !chopline && hshift == 0) 161 { 162 new_pos -= backchars + 1; 163 endline = FALSE; 164 } else 165 endline = TRUE; 166 break; 167 } 168 if (c != '\r') 169 blankline = 0; 170 171 /* 172 * Append the char to the line and get the next char. 173 */ 174 backchars = pappend(c, ch_tell()-1); 175 if (backchars > 0) 176 { 177 /* 178 * The char won't fit in the line; the line 179 * is too long to print in the screen width. 180 * End the line here. 181 */ 182 if (chopline || hshift > 0) 183 { 184 do 185 { 186 if (ABORT_SIGS()) 187 { 188 null_line(); 189 return (NULL_POSITION); 190 } 191 c = ch_forw_get(); 192 } while (c != '\n' && c != EOI); 193 new_pos = ch_tell(); 194 endline = TRUE; 195 quit_if_one_screen = FALSE; 196 } else 197 { 198 new_pos = ch_tell() - backchars; 199 endline = FALSE; 200 } 201 break; 202 } 203 c = ch_forw_get(); 204 } 205 206 pdone(endline, 1); 207 208 #if HILITE_SEARCH 209 if (is_filtered(base_pos)) 210 { 211 /* 212 * We don't want to display this line. 213 * Get the next line. 214 */ 215 curr_pos = new_pos; 216 goto get_forw_line; 217 } 218 219 if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL)) 220 set_status_col('*'); 221 #endif 222 223 if (squeeze && blankline) 224 { 225 /* 226 * This line is blank. 227 * Skip down to the last contiguous blank line 228 * and pretend it is the one which we are returning. 229 */ 230 while ((c = ch_forw_get()) == '\n' || c == '\r') 231 if (ABORT_SIGS()) 232 { 233 null_line(); 234 return (NULL_POSITION); 235 } 236 if (c != EOI) 237 (void) ch_back_get(); 238 new_pos = ch_tell(); 239 } 240 241 return (new_pos); 242 } 243 244 /* 245 * Get the previous line. 246 * A "current" position is passed and a "new" position is returned. 247 * The current position is the position of the first character of 248 * a line. The new position is the position of the first character 249 * of the PREVIOUS line. The line obtained is the one starting at new_pos. 250 */ 251 public POSITION 252 back_line(curr_pos) 253 POSITION curr_pos; 254 { 255 POSITION new_pos, begin_new_pos, base_pos; 256 int c; 257 int endline; 258 int backchars; 259 260 get_back_line: 261 if (curr_pos == NULL_POSITION || curr_pos <= ch_zero()) 262 { 263 null_line(); 264 return (NULL_POSITION); 265 } 266 #if HILITE_SEARCH 267 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) 268 prep_hilite((curr_pos < 3*size_linebuf) ? 269 0 : curr_pos - 3*size_linebuf, curr_pos, -1); 270 #endif 271 if (ch_seek(curr_pos-1)) 272 { 273 null_line(); 274 return (NULL_POSITION); 275 } 276 277 if (squeeze) 278 { 279 /* 280 * Find out if the "current" line was blank. 281 */ 282 (void) ch_forw_get(); /* Skip the newline */ 283 c = ch_forw_get(); /* First char of "current" line */ 284 (void) ch_back_get(); /* Restore our position */ 285 (void) ch_back_get(); 286 287 if (c == '\n' || c == '\r') 288 { 289 /* 290 * The "current" line was blank. 291 * Skip over any preceding blank lines, 292 * since we skipped them in forw_line(). 293 */ 294 while ((c = ch_back_get()) == '\n' || c == '\r') 295 if (ABORT_SIGS()) 296 { 297 null_line(); 298 return (NULL_POSITION); 299 } 300 if (c == EOI) 301 { 302 null_line(); 303 return (NULL_POSITION); 304 } 305 (void) ch_forw_get(); 306 } 307 } 308 309 /* 310 * Scan backwards until we hit the beginning of the line. 311 */ 312 for (;;) 313 { 314 if (ABORT_SIGS()) 315 { 316 null_line(); 317 return (NULL_POSITION); 318 } 319 c = ch_back_get(); 320 if (c == '\n') 321 { 322 /* 323 * This is the newline ending the previous line. 324 * We have hit the beginning of the line. 325 */ 326 base_pos = ch_tell() + 1; 327 break; 328 } 329 if (c == EOI) 330 { 331 /* 332 * We have hit the beginning of the file. 333 * This must be the first line in the file. 334 * This must, of course, be the beginning of the line. 335 */ 336 base_pos = ch_tell(); 337 break; 338 } 339 } 340 341 /* 342 * Now scan forwards from the beginning of this line. 343 * We keep discarding "printable lines" (based on screen width) 344 * until we reach the curr_pos. 345 * 346 * {{ This algorithm is pretty inefficient if the lines 347 * are much longer than the screen width, 348 * but I don't know of any better way. }} 349 */ 350 new_pos = base_pos; 351 if (ch_seek(new_pos)) 352 { 353 null_line(); 354 return (NULL_POSITION); 355 } 356 endline = FALSE; 357 prewind(); 358 plinenum(new_pos); 359 loop: 360 begin_new_pos = new_pos; 361 (void) ch_seek(new_pos); 362 363 do 364 { 365 c = ch_forw_get(); 366 if (c == EOI || ABORT_SIGS()) 367 { 368 null_line(); 369 return (NULL_POSITION); 370 } 371 new_pos++; 372 if (c == '\n') 373 { 374 backchars = pflushmbc(); 375 if (backchars > 0 && !chopline && hshift == 0) 376 { 377 backchars++; 378 goto shift; 379 } 380 endline = TRUE; 381 break; 382 } 383 backchars = pappend(c, ch_tell()-1); 384 if (backchars > 0) 385 { 386 /* 387 * Got a full printable line, but we haven't 388 * reached our curr_pos yet. Discard the line 389 * and start a new one. 390 */ 391 if (chopline || hshift > 0) 392 { 393 endline = TRUE; 394 quit_if_one_screen = FALSE; 395 break; 396 } 397 shift: 398 pshift_all(); 399 while (backchars-- > 0) 400 { 401 (void) ch_back_get(); 402 new_pos--; 403 } 404 goto loop; 405 } 406 } while (new_pos < curr_pos); 407 408 pdone(endline, 0); 409 410 #if HILITE_SEARCH 411 if (is_filtered(base_pos)) 412 { 413 /* 414 * We don't want to display this line. 415 * Get the previous line. 416 */ 417 curr_pos = begin_new_pos; 418 goto get_back_line; 419 } 420 421 if (status_col && curr_pos > 0 && is_hilited(base_pos, curr_pos-1, 1, NULL)) 422 set_status_col('*'); 423 #endif 424 425 return (begin_new_pos); 426 } 427 428 /* 429 * Set attnpos. 430 */ 431 public void 432 set_attnpos(pos) 433 POSITION pos; 434 { 435 int c; 436 437 if (pos != NULL_POSITION) 438 { 439 if (ch_seek(pos)) 440 return; 441 for (;;) 442 { 443 c = ch_forw_get(); 444 if (c == EOI) 445 break; 446 if (c == '\n' || c == '\r') 447 { 448 (void) ch_back_get(); 449 break; 450 } 451 pos++; 452 } 453 end_attnpos = pos; 454 for (;;) 455 { 456 c = ch_back_get(); 457 if (c == EOI || c == '\n' || c == '\r') 458 break; 459 pos--; 460 } 461 } 462 start_attnpos = pos; 463 } 464