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