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