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