1 /* 2 * Copyright (C) 1984-2008 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 c = ch_forw_get(); 185 } while (c != '\n' && c != EOI); 186 new_pos = ch_tell(); 187 endline = TRUE; 188 quit_if_one_screen = FALSE; 189 } else 190 { 191 new_pos = ch_tell() - backchars; 192 endline = FALSE; 193 } 194 break; 195 } 196 c = ch_forw_get(); 197 } 198 199 pdone(endline, c); 200 201 #if HILITE_SEARCH 202 if (is_filtered(base_pos)) 203 { 204 /* 205 * We don't want to display this line. 206 * Get the next line. 207 */ 208 curr_pos = new_pos; 209 goto get_forw_line; 210 } 211 212 if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL)) 213 set_status_col('*'); 214 #endif 215 216 if (squeeze && blankline) 217 { 218 /* 219 * This line is blank. 220 * Skip down to the last contiguous blank line 221 * and pretend it is the one which we are returning. 222 */ 223 while ((c = ch_forw_get()) == '\n' || c == '\r') 224 if (ABORT_SIGS()) 225 { 226 null_line(); 227 return (NULL_POSITION); 228 } 229 if (c != EOI) 230 (void) ch_back_get(); 231 new_pos = ch_tell(); 232 } 233 234 return (new_pos); 235 } 236 237 /* 238 * Get the previous line. 239 * A "current" position is passed and a "new" position is returned. 240 * The current position is the position of the first character of 241 * a line. The new position is the position of the first character 242 * of the PREVIOUS line. The line obtained is the one starting at new_pos. 243 */ 244 public POSITION 245 back_line(curr_pos) 246 POSITION curr_pos; 247 { 248 POSITION new_pos, begin_new_pos, base_pos; 249 int c; 250 int endline; 251 int backchars; 252 253 get_back_line: 254 if (curr_pos == NULL_POSITION || curr_pos <= ch_zero()) 255 { 256 null_line(); 257 return (NULL_POSITION); 258 } 259 #if HILITE_SEARCH 260 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) 261 prep_hilite((curr_pos < 3*size_linebuf) ? 262 0 : curr_pos - 3*size_linebuf, curr_pos, -1); 263 #endif 264 if (ch_seek(curr_pos-1)) 265 { 266 null_line(); 267 return (NULL_POSITION); 268 } 269 270 if (squeeze) 271 { 272 /* 273 * Find out if the "current" line was blank. 274 */ 275 (void) ch_forw_get(); /* Skip the newline */ 276 c = ch_forw_get(); /* First char of "current" line */ 277 (void) ch_back_get(); /* Restore our position */ 278 (void) ch_back_get(); 279 280 if (c == '\n' || c == '\r') 281 { 282 /* 283 * The "current" line was blank. 284 * Skip over any preceding blank lines, 285 * since we skipped them in forw_line(). 286 */ 287 while ((c = ch_back_get()) == '\n' || c == '\r') 288 if (ABORT_SIGS()) 289 { 290 null_line(); 291 return (NULL_POSITION); 292 } 293 if (c == EOI) 294 { 295 null_line(); 296 return (NULL_POSITION); 297 } 298 (void) ch_forw_get(); 299 } 300 } 301 302 /* 303 * Scan backwards until we hit the beginning of the line. 304 */ 305 for (;;) 306 { 307 if (ABORT_SIGS()) 308 { 309 null_line(); 310 return (NULL_POSITION); 311 } 312 c = ch_back_get(); 313 if (c == '\n') 314 { 315 /* 316 * This is the newline ending the previous line. 317 * We have hit the beginning of the line. 318 */ 319 base_pos = ch_tell() + 1; 320 break; 321 } 322 if (c == EOI) 323 { 324 /* 325 * We have hit the beginning of the file. 326 * This must be the first line in the file. 327 * This must, of course, be the beginning of the line. 328 */ 329 base_pos = ch_tell(); 330 break; 331 } 332 } 333 334 /* 335 * Now scan forwards from the beginning of this line. 336 * We keep discarding "printable lines" (based on screen width) 337 * until we reach the curr_pos. 338 * 339 * {{ This algorithm is pretty inefficient if the lines 340 * are much longer than the screen width, 341 * but I don't know of any better way. }} 342 */ 343 new_pos = base_pos; 344 if (ch_seek(new_pos)) 345 { 346 null_line(); 347 return (NULL_POSITION); 348 } 349 endline = FALSE; 350 prewind(); 351 plinenum(new_pos); 352 loop: 353 begin_new_pos = new_pos; 354 (void) ch_seek(new_pos); 355 356 do 357 { 358 c = ch_forw_get(); 359 if (c == EOI || ABORT_SIGS()) 360 { 361 null_line(); 362 return (NULL_POSITION); 363 } 364 new_pos++; 365 if (c == '\n') 366 { 367 backchars = pflushmbc(); 368 if (backchars > 0 && !chopline && hshift == 0) 369 { 370 backchars++; 371 goto shift; 372 } 373 endline = TRUE; 374 break; 375 } 376 backchars = pappend(c, ch_tell()-1); 377 if (backchars > 0) 378 { 379 /* 380 * Got a full printable line, but we haven't 381 * reached our curr_pos yet. Discard the line 382 * and start a new one. 383 */ 384 if (chopline || hshift > 0) 385 { 386 endline = TRUE; 387 quit_if_one_screen = FALSE; 388 break; 389 } 390 shift: 391 pshift_all(); 392 while (backchars-- > 0) 393 { 394 (void) ch_back_get(); 395 new_pos--; 396 } 397 goto loop; 398 } 399 } while (new_pos < curr_pos); 400 401 pdone(endline, ch_forw_get()); 402 403 #if HILITE_SEARCH 404 if (is_filtered(base_pos)) 405 { 406 /* 407 * We don't want to display this line. 408 * Get the previous line. 409 */ 410 curr_pos = begin_new_pos; 411 goto get_back_line; 412 } 413 414 if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL)) 415 set_status_col('*'); 416 #endif 417 418 return (begin_new_pos); 419 } 420 421 /* 422 * Set attnpos. 423 */ 424 public void 425 set_attnpos(pos) 426 POSITION pos; 427 { 428 int c; 429 430 if (pos != NULL_POSITION) 431 { 432 if (ch_seek(pos)) 433 return; 434 for (;;) 435 { 436 c = ch_forw_get(); 437 if (c == EOI) 438 return; 439 if (c != '\n' && c != '\r') 440 break; 441 pos++; 442 } 443 } 444 start_attnpos = pos; 445 for (;;) 446 { 447 c = ch_forw_get(); 448 pos++; 449 if (c == EOI || c == '\n' || c == '\r') 450 break; 451 } 452 end_attnpos = pos; 453 } 454