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