dhcpcd: update README.DRAGONFLY
[dragonfly.git] / contrib / less / input.c
blob0e021e1d05e154db0ea10799142c251b8ecc2c7f
1 /*
2 * Copyright (C) 1984-2023 Mark Nudelman
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.
7 * For more information, see the README file.
8 */
11 * High level routines dealing with getting lines of input
12 * from the file being viewed.
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.
20 #include "less.h"
22 extern int squeeze;
23 extern int hshift;
24 extern int quit_if_one_screen;
25 extern int sigs;
26 extern int ignore_eoi;
27 extern int status_col;
28 extern int wordwrap;
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
38 * Set the status column.
39 * base Position of first char in line.
40 * disp First visible char.
41 * Different than base_pos if line is shifted.
42 * edisp Last visible char.
43 * eol End of line. Normally the newline.
44 * Different than edisp if line is chopped.
46 static void init_status_col(POSITION base_pos, POSITION disp_pos, POSITION edisp_pos, POSITION eol_pos)
48 int hl_before = (chop_line() && disp_pos != NULL_POSITION) ?
49 is_hilited_attr(base_pos, disp_pos, TRUE, NULL) : 0;
50 int hl_after = (chop_line()) ?
51 is_hilited_attr(edisp_pos, eol_pos, TRUE, NULL) : 0;
52 int attr;
53 char ch;
55 if (hl_before && hl_after)
57 attr = hl_after;
58 ch = '=';
59 } else if (hl_before)
61 attr = hl_before;
62 ch = '<';
63 } else if (hl_after)
65 attr = hl_after;
66 ch = '>';
67 } else
69 attr = is_hilited_attr(base_pos, eol_pos, TRUE, NULL);
70 ch = '*';
72 if (attr)
73 set_status_col(ch, attr);
77 * Get the next line.
78 * A "current" position is passed and a "new" position is returned.
79 * The current position is the position of the first character of
80 * a line. The new position is the position of the first character
81 * of the NEXT line. The line obtained is the line starting at curr_pos.
83 public POSITION forw_line_seg(POSITION curr_pos, int skipeol, int rscroll, int nochop)
85 POSITION base_pos;
86 POSITION new_pos;
87 POSITION edisp_pos;
88 int c;
89 int blankline;
90 int endline;
91 int chopped;
92 int backchars;
93 POSITION wrap_pos;
94 int skipped_leading;
96 get_forw_line:
97 if (curr_pos == NULL_POSITION)
99 null_line();
100 return (NULL_POSITION);
102 #if HILITE_SEARCH
103 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
106 * If we are ignoring EOI (command F), only prepare
107 * one line ahead, to avoid getting stuck waiting for
108 * slow data without displaying the data we already have.
109 * If we're not ignoring EOI, we *could* do the same, but
110 * for efficiency we prepare several lines ahead at once.
112 prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
113 ignore_eoi ? 1 : -1);
114 curr_pos = next_unfiltered(curr_pos);
116 #endif
117 if (ch_seek(curr_pos))
119 null_line();
120 return (NULL_POSITION);
124 * Step back to the beginning of the line.
126 base_pos = curr_pos;
127 for (;;)
129 if (ABORT_SIGS())
131 null_line();
132 return (NULL_POSITION);
134 c = ch_back_get();
135 if (c == EOI)
136 break;
137 if (c == '\n')
139 (void) ch_forw_get();
140 break;
142 --base_pos;
146 * Read forward again to the position we should start at.
148 prewind();
149 plinestart(base_pos);
150 (void) ch_seek(base_pos);
151 new_pos = base_pos;
152 while (new_pos < curr_pos)
154 if (ABORT_SIGS())
156 null_line();
157 return (NULL_POSITION);
159 c = ch_forw_get();
160 backchars = pappend(c, new_pos);
161 new_pos++;
162 if (backchars > 0)
164 pshift_all();
165 if (wordwrap && (c == ' ' || c == '\t'))
169 new_pos++;
170 c = ch_forw_get();
171 } while (c == ' ' || c == '\t');
172 backchars = 1;
174 new_pos -= backchars;
175 while (--backchars >= 0)
176 (void) ch_back_get();
179 (void) pflushmbc();
180 pshift_all();
183 * Read the first character to display.
185 c = ch_forw_get();
186 if (c == EOI)
188 null_line();
189 return (NULL_POSITION);
191 blankline = (c == '\n' || c == '\r');
192 wrap_pos = NULL_POSITION;
193 skipped_leading = FALSE;
196 * Read each character in the line and append to the line buffer.
198 chopped = FALSE;
199 for (;;)
201 if (ABORT_SIGS())
203 null_line();
204 return (NULL_POSITION);
206 if (c == '\n' || c == EOI)
209 * End of the line.
211 backchars = pflushmbc();
212 new_pos = ch_tell();
213 if (backchars > 0 && (nochop || !chop_line()) && hshift == 0)
215 new_pos -= backchars + 1;
216 endline = FALSE;
217 } else
218 endline = TRUE;
219 edisp_pos = new_pos;
220 break;
222 if (c != '\r')
223 blankline = 0;
226 * Append the char to the line and get the next char.
228 backchars = pappend(c, ch_tell()-1);
229 if (backchars > 0)
232 * The char won't fit in the line; the line
233 * is too long to print in the screen width.
234 * End the line here.
236 if (skipeol)
238 /* Read to end of line. */
239 edisp_pos = ch_tell();
242 if (ABORT_SIGS())
244 null_line();
245 return (NULL_POSITION);
247 c = ch_forw_get();
248 } while (c != '\n' && c != EOI);
249 new_pos = ch_tell();
250 endline = TRUE;
251 quit_if_one_screen = FALSE;
252 chopped = TRUE;
253 } else
255 if (!wordwrap)
256 new_pos = ch_tell() - backchars;
257 else
260 * We're word-wrapping, so go back to the last space.
261 * However, if it's the space itself that couldn't fit,
262 * simply ignore it and any subsequent spaces.
264 if (c == ' ' || c == '\t')
268 new_pos = ch_tell();
269 c = ch_forw_get();
270 } while (c == ' ' || c == '\t');
271 if (c == '\r')
272 c = ch_forw_get();
273 if (c == '\n')
274 new_pos = ch_tell();
275 } else if (wrap_pos == NULL_POSITION)
276 new_pos = ch_tell() - backchars;
277 else
279 new_pos = wrap_pos;
280 loadc();
283 endline = FALSE;
285 break;
287 if (wordwrap)
289 if (c == ' ' || c == '\t')
291 if (skipped_leading)
293 wrap_pos = ch_tell();
294 savec();
296 } else
297 skipped_leading = TRUE;
299 c = ch_forw_get();
302 #if HILITE_SEARCH
303 if (blankline && show_attn)
305 /* Add spurious space to carry possible attn hilite. */
306 pappend(' ', ch_tell()-1);
308 #endif
309 pdone(endline, rscroll && chopped, 1);
311 #if HILITE_SEARCH
312 if (is_filtered(base_pos))
315 * We don't want to display this line.
316 * Get the next line.
318 curr_pos = new_pos;
319 goto get_forw_line;
321 if (status_col)
322 init_status_col(base_pos, line_position(), edisp_pos, new_pos);
323 #endif
325 if (squeeze && blankline)
328 * This line is blank.
329 * Skip down to the last contiguous blank line
330 * and pretend it is the one which we are returning.
332 while ((c = ch_forw_get()) == '\n' || c == '\r')
333 if (ABORT_SIGS())
335 null_line();
336 return (NULL_POSITION);
338 if (c != EOI)
339 (void) ch_back_get();
340 new_pos = ch_tell();
343 return (new_pos);
346 public POSITION forw_line(POSITION curr_pos)
349 return forw_line_seg(curr_pos, (chop_line() || hshift > 0), TRUE, FALSE);
353 * Get the previous line.
354 * A "current" position is passed and a "new" position is returned.
355 * The current position is the position of the first character of
356 * a line. The new position is the position of the first character
357 * of the PREVIOUS line. The line obtained is the one starting at new_pos.
359 public POSITION back_line(POSITION curr_pos)
361 POSITION base_pos;
362 POSITION new_pos;
363 POSITION edisp_pos;
364 POSITION begin_new_pos;
365 int c;
366 int endline;
367 int chopped;
368 int backchars;
369 POSITION wrap_pos;
370 int skipped_leading;
372 get_back_line:
373 if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
375 null_line();
376 return (NULL_POSITION);
378 #if HILITE_SEARCH
379 if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
380 prep_hilite((curr_pos < 3*size_linebuf) ?
381 0 : curr_pos - 3*size_linebuf, curr_pos, -1);
382 #endif
383 if (ch_seek(curr_pos-1))
385 null_line();
386 return (NULL_POSITION);
389 if (squeeze)
392 * Find out if the "current" line was blank.
394 (void) ch_forw_get(); /* Skip the newline */
395 c = ch_forw_get(); /* First char of "current" line */
396 (void) ch_back_get(); /* Restore our position */
397 (void) ch_back_get();
399 if (c == '\n' || c == '\r')
402 * The "current" line was blank.
403 * Skip over any preceding blank lines,
404 * since we skipped them in forw_line().
406 while ((c = ch_back_get()) == '\n' || c == '\r')
407 if (ABORT_SIGS())
409 null_line();
410 return (NULL_POSITION);
412 if (c == EOI)
414 null_line();
415 return (NULL_POSITION);
417 (void) ch_forw_get();
422 * Scan backwards until we hit the beginning of the line.
424 for (;;)
426 if (ABORT_SIGS())
428 null_line();
429 return (NULL_POSITION);
431 c = ch_back_get();
432 if (c == '\n')
435 * This is the newline ending the previous line.
436 * We have hit the beginning of the line.
438 base_pos = ch_tell() + 1;
439 break;
441 if (c == EOI)
444 * We have hit the beginning of the file.
445 * This must be the first line in the file.
446 * This must, of course, be the beginning of the line.
448 base_pos = ch_tell();
449 break;
454 * Now scan forwards from the beginning of this line.
455 * We keep discarding "printable lines" (based on screen width)
456 * until we reach the curr_pos.
458 * {{ This algorithm is pretty inefficient if the lines
459 * are much longer than the screen width,
460 * but I don't know of any better way. }}
462 new_pos = base_pos;
463 if (ch_seek(new_pos))
465 null_line();
466 return (NULL_POSITION);
468 endline = FALSE;
469 prewind();
470 plinestart(new_pos);
471 loop:
472 wrap_pos = NULL_POSITION;
473 skipped_leading = FALSE;
474 begin_new_pos = new_pos;
475 (void) ch_seek(new_pos);
476 chopped = FALSE;
478 for (;;)
480 c = ch_forw_get();
481 if (c == EOI || ABORT_SIGS())
483 null_line();
484 return (NULL_POSITION);
486 new_pos++;
487 if (c == '\n')
489 backchars = pflushmbc();
490 if (backchars > 0 && !chop_line() && hshift == 0)
492 backchars++;
493 goto shift;
495 endline = TRUE;
496 edisp_pos = new_pos;
497 break;
499 backchars = pappend(c, ch_tell()-1);
500 if (backchars > 0)
503 * Got a full printable line, but we haven't
504 * reached our curr_pos yet. Discard the line
505 * and start a new one.
507 if (chop_line() || hshift > 0)
509 endline = TRUE;
510 chopped = TRUE;
511 quit_if_one_screen = FALSE;
512 edisp_pos = new_pos;
513 break;
515 shift:
516 if (!wordwrap)
518 pshift_all();
519 new_pos -= backchars;
520 } else
522 if (c == ' ' || c == '\t')
524 for (;;)
526 c = ch_forw_get();
527 if (c == ' ' || c == '\t')
528 new_pos++;
529 else
531 if (c == '\r')
533 c = ch_forw_get();
534 if (c == '\n')
535 new_pos++;
537 if (c == '\n')
538 new_pos++;
539 break;
542 if (new_pos >= curr_pos)
543 break;
544 pshift_all();
545 } else
547 pshift_all();
548 if (wrap_pos == NULL_POSITION)
549 new_pos -= backchars;
550 else
551 new_pos = wrap_pos;
554 goto loop;
556 if (wordwrap)
558 if (c == ' ' || c == '\t')
560 if (skipped_leading)
561 wrap_pos = new_pos;
562 } else
563 skipped_leading = TRUE;
565 if (new_pos >= curr_pos)
567 edisp_pos = new_pos;
568 break;
572 pdone(endline, chopped, 0);
574 #if HILITE_SEARCH
575 if (is_filtered(base_pos))
578 * We don't want to display this line.
579 * Get the previous line.
581 curr_pos = begin_new_pos;
582 goto get_back_line;
584 if (status_col)
585 init_status_col(base_pos, line_position(), edisp_pos, new_pos);
586 #endif
588 return (begin_new_pos);
592 * Set attnpos.
594 public void set_attnpos(POSITION pos)
596 int c;
598 if (pos != NULL_POSITION)
600 if (ch_seek(pos))
601 return;
602 for (;;)
604 c = ch_forw_get();
605 if (c == EOI)
606 break;
607 if (c == '\n' || c == '\r')
609 (void) ch_back_get();
610 break;
612 pos++;
614 end_attnpos = pos;
615 for (;;)
617 c = ch_back_get();
618 if (c == EOI || c == '\n' || c == '\r')
619 break;
620 pos--;
623 start_attnpos = pos;