"SfR Fresh" - the SfR Freeware/Shareware Archive 
Member "less-424/line.c" of archive less-424.tar.gz:
As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers.
Alternatively you can here view or download the uninterpreted source code file.
That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
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 * Routines to manipulate the "line buffer".
14 * The line buffer holds a line of output as it is being built
15 * in preparation for output to the screen.
16 */
17
18 #include "less.h"
19 #include "charset.h"
20
21 static char *linebuf = NULL; /* Buffer which holds the current output line */
22 static char *attr = NULL; /* Extension of linebuf to hold attributes */
23 public int size_linebuf = 0; /* Size of line buffer (and attr buffer) */
24
25 static int cshift; /* Current left-shift of output line buffer */
26 public int hshift; /* Desired left-shift of output line buffer */
27 public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
28 public int ntabstops = 1; /* Number of tabstops */
29 public int tabdefault = 8; /* Default repeated tabstops */
30
31 static int curr; /* Index into linebuf */
32 static int column; /* Printable length, accounting for
33 backspaces, etc. */
34 static int overstrike; /* Next char should overstrike previous char */
35 static int last_overstrike = AT_NORMAL;
36 static int is_null_line; /* There is no current line */
37 static int lmargin; /* Left margin */
38 static char pendc;
39 static POSITION pendpos;
40 static char *end_ansi_chars;
41 static char *mid_ansi_chars;
42
43 static int attr_swidth();
44 static int attr_ewidth();
45 static int do_append();
46
47 extern int sigs;
48 extern int bs_mode;
49 extern int linenums;
50 extern int ctldisp;
51 extern int twiddle;
52 extern int binattr;
53 extern int status_col;
54 extern int auto_wrap, ignaw;
55 extern int bo_s_width, bo_e_width;
56 extern int ul_s_width, ul_e_width;
57 extern int bl_s_width, bl_e_width;
58 extern int so_s_width, so_e_width;
59 extern int sc_width, sc_height;
60 extern int utf_mode;
61 extern int oldbot;
62 extern POSITION start_attnpos;
63 extern POSITION end_attnpos;
64
65 static char mbc_buf[MAX_UTF_CHAR_LEN];
66 static int mbc_buf_len = 0;
67 static int mbc_buf_index = 0;
68 static POSITION mbc_pos;
69
70 /*
71 * Initialize from environment variables.
72 */
73 public void
74 init_line()
75 {
76 end_ansi_chars = lgetenv("LESSANSIENDCHARS");
77 if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
78 end_ansi_chars = "m";
79
80 mid_ansi_chars = lgetenv("LESSANSIMIDCHARS");
81 if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0')
82 mid_ansi_chars = "0123456789;[?!\"'#%()*+ ";
83
84 linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
85 attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
86 size_linebuf = LINEBUF_SIZE;
87 }
88
89 /*
90 * Expand the line buffer.
91 */
92 static int
93 expand_linebuf()
94 {
95 /* Double the size of the line buffer. */
96 int new_size = size_linebuf * 2;
97
98 /* Just realloc to expand the buffer, if we can. */
99 #if HAVE_REALLOC
100 char *new_buf = (char *) realloc(linebuf, new_size);
101 char *new_attr = (char *) realloc(attr, new_size);
102 #else
103 char *new_buf = (char *) calloc(new_size, sizeof(char));
104 char *new_attr = (char *) calloc(new_size, sizeof(char));
105 #endif
106 if (new_buf == NULL || new_attr == NULL)
107 {
108 if (new_attr != NULL)
109 free(new_attr);
110 if (new_buf != NULL)
111 free(new_buf);
112 return 1;
113 }
114 #if HAVE_REALLOC
115 /*
116 * We realloc'd the buffers; they already have the old contents.
117 */
118 #if 0
119 memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
120 memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
121 #endif
122 #else
123 /*
124 * We just calloc'd the buffers; copy the old contents.
125 */
126 memcpy(new_buf, linebuf, size_linebuf * sizeof(char));
127 memcpy(new_attr, attr, size_linebuf * sizeof(char));
128 free(attr);
129 free(linebuf);
130 #endif
131 linebuf = new_buf;
132 attr = new_attr;
133 size_linebuf = new_size;
134 return 0;
135 }
136
137 /*
138 * Is a character ASCII?
139 */
140 public int
141 is_ascii_char(ch)
142 LWCHAR ch;
143 {
144 return (ch <= 0x7F);
145 }
146
147 /*
148 * Rewind the line buffer.
149 */
150 public void
151 prewind()
152 {
153 curr = 0;
154 column = 0;
155 cshift = 0;
156 overstrike = 0;
157 last_overstrike = AT_NORMAL;
158 mbc_buf_len = 0;
159 is_null_line = 0;
160 pendc = '\0';
161 lmargin = 0;
162 if (status_col)
163 lmargin += 1;
164 }
165
166 /*
167 * Insert the line number (of the given position) into the line buffer.
168 */
169 public void
170 plinenum(pos)
171 POSITION pos;
172 {
173 register LINENUM linenum = 0;
174 register int i;
175
176 if (linenums == OPT_ONPLUS)
177 {
178 /*
179 * Get the line number and put it in the current line.
180 * {{ Note: since find_linenum calls forw_raw_line,
181 * it may seek in the input file, requiring the caller
182 * of plinenum to re-seek if necessary. }}
183 * {{ Since forw_raw_line modifies linebuf, we must
184 * do this first, before storing anything in linebuf. }}
185 */
186 linenum = find_linenum(pos);
187 }
188
189 /*
190 * Display a status column if the -J option is set.
191 */
192 if (status_col)
193 {
194 linebuf[curr] = ' ';
195 if (start_attnpos != NULL_POSITION &&
196 pos >= start_attnpos && pos < end_attnpos)
197 attr[curr] = AT_NORMAL|AT_HILITE;
198 else
199 attr[curr] = AT_NORMAL;
200 curr++;
201 column++;
202 }
203 /*
204 * Display the line number at the start of each line
205 * if the -N option is set.
206 */
207 if (linenums == OPT_ONPLUS)
208 {
209 char buf[INT_STRLEN_BOUND(pos) + 2];
210 int n;
211
212 linenumtoa(linenum, buf);
213 n = strlen(buf);
214 if (n < MIN_LINENUM_WIDTH)
215 n = MIN_LINENUM_WIDTH;
216 sprintf(linebuf+curr, "%*s ", n, buf);
217 n++; /* One space after the line number. */
218 for (i = 0; i < n; i++)
219 attr[curr+i] = AT_NORMAL;
220 curr += n;
221 column += n;
222 lmargin += n;
223 }
224
225 /*
226 * Append enough spaces to bring us to the lmargin.
227 */
228 while (column < lmargin)
229 {
230 linebuf[curr] = ' ';
231 attr[curr++] = AT_NORMAL;
232 column++;
233 }
234 }
235
236 /*
237 * Shift the input line left.
238 * This means discarding N printable chars at the start of the buffer.
239 */
240 static void
241 pshift(shift)
242 int shift;
243 {
244 LWCHAR prev_ch = 0;
245 unsigned char c;
246 int shifted = 0;
247 int to;
248 int from;
249 int len;
250 int width;
251 int prev_attr;
252 int next_attr;
253
254 if (shift > column - lmargin)
255 shift = column - lmargin;
256 if (shift > curr - lmargin)
257 shift = curr - lmargin;
258
259 to = from = lmargin;
260 /*
261 * We keep on going when shifted == shift
262 * to get all combining chars.
263 */
264 while (shifted <= shift && from < curr)
265 {
266 c = linebuf[from];
267 if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
268 {
269 /* Keep cumulative effect. */
270 linebuf[to] = c;
271 attr[to++] = attr[from++];
272 while (from < curr && linebuf[from])
273 {
274 linebuf[to] = linebuf[from];
275 attr[to++] = attr[from];
276 if (!is_ansi_middle(linebuf[from++]))
277 break;
278 }
279 continue;
280 }
281
282 width = 0;
283
284 if (!IS_ASCII_OCTET(c) && utf_mode)
285 {
286 /* Assumes well-formedness validation already done. */
287 LWCHAR ch;
288
289 len = utf_len(c);
290 if (from + len > curr)
291 break;
292 ch = get_wchar(linebuf + from);
293 if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch))
294 width = is_wide_char(ch) ? 2 : 1;
295 prev_ch = ch;
296 } else
297 {
298 len = 1;
299 if (c == '\b')
300 /* XXX - Incorrect if several '\b' in a row. */
301 width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
302 else if (!control_char(c))
303 width = 1;
304 prev_ch = 0;
305 }
306
307 if (width == 2 && shift - shifted == 1) {
308 /* Should never happen when called by pshift_all(). */
309 attr[to] = attr[from];
310 /*
311 * Assume a wide_char will never be the first half of a
312 * combining_char pair, so reset prev_ch in case we're
313 * followed by a '\b'.
314 */
315 prev_ch = linebuf[to++] = ' ';
316 from += len;
317 shifted++;
318 continue;
319 }
320
321 /* Adjust width for magic cookies. */
322 prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL;
323 next_attr = (from + len < curr) ? attr[from + len] : prev_attr;
324 if (!is_at_equiv(attr[from], prev_attr) &&
325 !is_at_equiv(attr[from], next_attr))
326 {
327 width += attr_swidth(attr[from]);
328 if (from + len < curr)
329 width += attr_ewidth(attr[from]);
330 if (is_at_equiv(prev_attr, next_attr))
331 {
332 width += attr_ewidth(prev_attr);
333 if (from + len < curr)
334 width += attr_swidth(next_attr);
335 }
336 }
337
338 if (shift - shifted < width)
339 break;
340 from += len;
341 shifted += width;
342 if (shifted < 0)
343 shifted = 0;
344 }
345 while (from < curr)
346 {
347 linebuf[to] = linebuf[from];
348 attr[to++] = attr[from++];
349 }
350 curr = to;
351 column -= shifted;
352 cshift += shifted;
353 }
354
355 /*
356 *
357 */
358 public void
359 pshift_all()
360 {
361 pshift(column);
362 }
363
364 /*
365 * Return the printing width of the start (enter) sequence
366 * for a given character attribute.
367 */
368 static int
369 attr_swidth(a)
370 int a;
371 {
372 int w = 0;
373
374 a = apply_at_specials(a);
375
376 if (a & AT_UNDERLINE)
377 w += ul_s_width;
378 if (a & AT_BOLD)
379 w += bo_s_width;
380 if (a & AT_BLINK)
381 w += bl_s_width;
382 if (a & AT_STANDOUT)
383 w += so_s_width;
384
385 return w;
386 }
387
388 /*
389 * Return the printing width of the end (exit) sequence
390 * for a given character attribute.
391 */
392 static int
393 attr_ewidth(a)
394 int a;
395 {
396 int w = 0;
397
398 a = apply_at_specials(a);
399
400 if (a & AT_UNDERLINE)
401 w += ul_e_width;
402 if (a & AT_BOLD)
403 w += bo_e_width;
404 if (a & AT_BLINK)
405 w += bl_e_width;
406 if (a & AT_STANDOUT)
407 w += so_e_width;
408
409 return w;
410 }
411
412 /*
413 * Return the printing width of a given character and attribute,
414 * if the character were added to the current position in the line buffer.
415 * Adding a character with a given attribute may cause an enter or exit
416 * attribute sequence to be inserted, so this must be taken into account.
417 */
418 static int
419 pwidth(ch, a, prev_ch)
420 LWCHAR ch;
421 int a;
422 LWCHAR prev_ch;
423 {
424 int w;
425
426 if (ch == '\b')
427 /*
428 * Backspace moves backwards one or two positions.
429 * XXX - Incorrect if several '\b' in a row.
430 */
431 return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
432
433 if (!utf_mode || is_ascii_char(ch))
434 {
435 if (control_char((char)ch))
436 {
437 /*
438 * Control characters do unpredictable things,
439 * so we don't even try to guess; say it doesn't move.
440 * This can only happen if the -r flag is in effect.
441 */
442 return (0);
443 }
444 } else
445 {
446 if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
447 {
448 /*
449 * Composing and combining chars take up no space.
450 *
451 * Some terminals, upon failure to compose a
452 * composing character with the character(s) that
453 * precede(s) it will actually take up one column
454 * for the composing character; there isn't much
455 * we could do short of testing the (complex)
456 * composition process ourselves and printing
457 * a binary representation when it fails.
458 */
459 return (0);
460 }
461 }
462
463 /*
464 * Other characters take one or two columns,
465 * plus the width of any attribute enter/exit sequence.
466 */
467 w = 1;
468 if (is_wide_char(ch))
469 w++;
470 if (curr > 0 && !is_at_equiv(attr[curr-1], a))
471 w += attr_ewidth(attr[curr-1]);
472 if ((apply_at_specials(a) != AT_NORMAL) &&
473 (curr == 0 || !is_at_equiv(attr[curr-1], a)))
474 w += attr_swidth(a);
475 return (w);
476 }
477
478 /*
479 * Delete to the previous base character in the line buffer.
480 * Return 1 if one is found.
481 */
482 static int
483 backc()
484 {
485 LWCHAR prev_ch;
486 char *p = linebuf + curr;
487 LWCHAR ch = step_char(&p, -1, linebuf + lmargin);
488 int width;
489
490 /* This assumes that there is no '\b' in linebuf. */
491 while ( curr > lmargin
492 && column > lmargin
493 && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY))))
494 {
495 curr = p - linebuf;
496 prev_ch = step_char(&p, -1, linebuf + lmargin);
497 width = pwidth(ch, attr[curr], prev_ch);
498 column -= width;
499 if (width > 0)
500 return 1;
501 ch = prev_ch;
502 }
503
504 return 0;
505 }
506
507 /*
508 * Are we currently within a recognized ANSI escape sequence?
509 */
510 static int
511 in_ansi_esc_seq()
512 {
513 char *p;
514
515 /*
516 * Search backwards for either an ESC (which means we ARE in a seq);
517 * or an end char (which means we're NOT in a seq).
518 */
519 for (p = &linebuf[curr]; p > linebuf; )
520 {
521 LWCHAR ch = step_char(&p, -1, linebuf);
522 if (IS_CSI_START(ch))
523 return (1);
524 if (!is_ansi_middle(ch))
525 return (0);
526 }
527 return (0);
528 }
529
530 /*
531 * Is a character the end of an ANSI escape sequence?
532 */
533 public int
534 is_ansi_end(ch)
535 LWCHAR ch;
536 {
537 if (!is_ascii_char(ch))
538 return (0);
539 return (strchr(end_ansi_chars, (char) ch) != NULL);
540 }
541
542 /*
543 *
544 */
545 public int
546 is_ansi_middle(ch)
547 LWCHAR ch;
548 {
549 if (!is_ascii_char(ch))
550 return (0);
551 if (is_ansi_end(ch))
552 return (0);
553 return (strchr(mid_ansi_chars, (char) ch) != NULL);
554 }
555
556 /*
557 * Append a character and attribute to the line buffer.
558 */
559 #define STORE_CHAR(ch,a,rep,pos) \
560 do { \
561 if (store_char((ch),(a),(rep),(pos))) return (1); \
562 } while (0)
563
564 static int
565 store_char(ch, a, rep, pos)
566 LWCHAR ch;
567 int a;
568 char *rep;
569 POSITION pos;
570 {
571 int w;
572 int replen;
573 char cs;
574
575 w = (a & (AT_UNDERLINE|AT_BOLD)); /* Pre-use w. */
576 if (w != AT_NORMAL)
577 last_overstrike = w;
578
579 #if HILITE_SEARCH
580 {
581 int matches;
582 if (is_hilited(pos, pos+1, 0, &matches))
583 {
584 /*
585 * This character should be highlighted.
586 * Override the attribute passed in.
587 */
588 if (a != AT_ANSI)
589 a |= AT_HILITE;
590 }
591 }
592 #endif
593
594 if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
595 {
596 if (!is_ansi_end(ch) && !is_ansi_middle(ch)) {
597 /* Remove whole unrecognized sequence. */
598 char *p = &linebuf[curr];
599 LWCHAR bch;
600 do {
601 bch = step_char(&p, -1, linebuf);
602 } while (p > linebuf && !IS_CSI_START(bch));
603 curr = p - linebuf;
604 return 0;
605 }
606 a = AT_ANSI; /* Will force re-AT_'ing around it. */
607 w = 0;
608 }
609 else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))
610 {
611 a = AT_ANSI; /* Will force re-AT_'ing around it. */
612 w = 0;
613 }
614 else
615 {
616 char *p = &linebuf[curr];
617 LWCHAR prev_ch = step_char(&p, -1, linebuf);
618 w = pwidth(ch, a, prev_ch);
619 }
620
621 if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
622 /*
623 * Won't fit on screen.
624 */
625 return (1);
626
627 if (rep == NULL)
628 {
629 cs = (char) ch;
630 rep = &cs;
631 replen = 1;
632 } else
633 {
634 replen = utf_len(rep[0]);
635 }
636 if (curr + replen >= size_linebuf-6)
637 {
638 /*
639 * Won't fit in line buffer.
640 * Try to expand it.
641 */
642 if (expand_linebuf())
643 return (1);
644 }
645
646 while (replen-- > 0)
647 {
648 linebuf[curr] = *rep++;
649 attr[curr] = a;
650 curr++;
651 }
652 column += w;
653 return (0);
654 }
655
656 /*
657 * Append a tab to the line buffer.
658 * Store spaces to represent the tab.
659 */
660 #define STORE_TAB(a,pos) \
661 do { if (store_tab((a),(pos))) return (1); } while (0)
662
663 static int
664 store_tab(attr, pos)
665 int attr;
666 POSITION pos;
667 {
668 int to_tab = column + cshift - lmargin;
669 int i;
670
671 if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
672 to_tab = tabdefault -
673 ((to_tab - tabstops[ntabstops-1]) % tabdefault);
674 else
675 {
676 for (i = ntabstops - 2; i >= 0; i--)
677 if (to_tab >= tabstops[i])
678 break;
679 to_tab = tabstops[i+1] - to_tab;
680 }
681
682 if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width)
683 return 1;
684
685 do {
686 STORE_CHAR(' ', attr, " ", pos);
687 } while (--to_tab > 0);
688 return 0;
689 }
690
691 #define STORE_PRCHAR(c, pos) \
692 do { if (store_prchar((c), (pos))) return 1; } while (0)
693
694 static int
695 store_prchar(c, pos)
696 char c;
697 POSITION pos;
698 {
699 char *s;
700
701 /*
702 * Convert to printable representation.
703 */
704 s = prchar(c);
705
706 /*
707 * Make sure we can get the entire representation
708 * of the character on this line.
709 */
710 if (column + (int) strlen(s) - 1 +
711 pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
712 return 1;
713
714 for ( ; *s != 0; s++)
715 STORE_CHAR(*s, AT_BINARY, NULL, pos);
716
717 return 0;
718 }
719
720 static int
721 flush_mbc_buf(pos)
722 POSITION pos;
723 {
724 int i;
725
726 for (i = 0; i < mbc_buf_index; i++)
727 if (store_prchar(mbc_buf[i], pos))
728 return mbc_buf_index - i;
729
730 return 0;
731 }
732
733 /*
734 * Append a character to the line buffer.
735 * Expand tabs into spaces, handle underlining, boldfacing, etc.
736 * Returns 0 if ok, 1 if couldn't fit in buffer.
737 */
738 public int
739 pappend(c, pos)
740 char c;
741 POSITION pos;
742 {
743 int r;
744
745 if (pendc)
746 {
747 if (do_append(pendc, NULL, pendpos))
748 /*
749 * Oops. We've probably lost the char which
750 * was in pendc, since caller won't back up.
751 */
752 return (1);
753 pendc = '\0';
754 }
755
756 if (c == '\r' && bs_mode == BS_SPECIAL)
757 {
758 if (mbc_buf_len > 0) /* utf_mode must be on. */
759 {
760 /* Flush incomplete (truncated) sequence. */
761 r = flush_mbc_buf(mbc_pos);
762 mbc_buf_index = r + 1;
763 mbc_buf_len = 0;
764 if (r)
765 return (mbc_buf_index);
766 }
767
768 /*
769 * Don't put the CR into the buffer until we see
770 * the next char. If the next char is a newline,
771 * discard the CR.
772 */
773 pendc = c;
774 pendpos = pos;
775 return (0);
776 }
777
778 if (!utf_mode)
779 {
780 r = do_append((LWCHAR) c, NULL, pos);
781 } else
782 {
783 /* Perform strict validation in all possible cases. */
784 if (mbc_buf_len == 0)
785 {
786 retry:
787 mbc_buf_index = 1;
788 *mbc_buf = c;
789 if (IS_ASCII_OCTET(c))
790 r = do_append((LWCHAR) c, NULL, pos);
791 else if (IS_UTF8_LEAD(c))
792 {
793 mbc_buf_len = utf_len(c);
794 mbc_pos = pos;
795 return (0);
796 } else
797 /* UTF8_INVALID or stray UTF8_TRAIL */
798 r = flush_mbc_buf(pos);
799 } else if (IS_UTF8_TRAIL(c))
800 {
801 mbc_buf[mbc_buf_index++] = c;
802 if (mbc_buf_index < mbc_buf_len)
803 return (0);
804 if (is_utf8_well_formed(mbc_buf))
805 r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos);
806 else
807 /* Complete, but not shortest form, sequence. */
808 mbc_buf_index = r = flush_mbc_buf(mbc_pos);
809 mbc_buf_len = 0;
810 } else
811 {
812 /* Flush incomplete (truncated) sequence. */
813 r = flush_mbc_buf(mbc_pos);
814 mbc_buf_index = r + 1;
815 mbc_buf_len = 0;
816 /* Handle new char. */
817 if (!r)
818 goto retry;
819 }
820 }
821
822 /*
823 * If we need to shift the line, do it.
824 * But wait until we get to at least the middle of the screen,
825 * so shifting it doesn't affect the chars we're currently
826 * pappending. (Bold & underline can get messed up otherwise.)
827 */
828 if (cshift < hshift && column > sc_width / 2)
829 {
830 linebuf[curr] = '\0';
831 pshift(hshift - cshift);
832 }
833 if (r)
834 {
835 /* How many chars should caller back up? */
836 r = (!utf_mode) ? 1 : mbc_buf_index;
837 }
838 return (r);
839 }
840
841 static int
842 do_append(ch, rep, pos)
843 LWCHAR ch;
844 char *rep;
845 POSITION pos;
846 {
847 register int a;
848 LWCHAR prev_ch;
849
850 a = AT_NORMAL;
851
852 if (ch == '\b')
853 {
854 if (bs_mode == BS_CONTROL)
855 goto do_control_char;
856
857 /*
858 * A better test is needed here so we don't
859 * backspace over part of the printed
860 * representation of a binary character.
861 */
862 if ( curr <= lmargin
863 || column <= lmargin
864 || (attr[curr - 1] & (AT_ANSI|AT_BINARY)))
865 STORE_PRCHAR('\b', pos);
866 else if (bs_mode == BS_NORMAL)
867 STORE_CHAR(ch, AT_NORMAL, NULL, pos);
868 else if (bs_mode == BS_SPECIAL)
869 overstrike = backc();
870
871 return 0;
872 }
873
874 if (overstrike > 0)
875 {
876 /*
877 * Overstrike the character at the current position
878 * in the line buffer. This will cause either
879 * underline (if a "_" is overstruck),
880 * bold (if an identical character is overstruck),
881 * or just deletion of the character in the buffer.
882 */
883 overstrike = utf_mode ? -1 : 0;
884 /* To be correct, this must be a base character. */
885 prev_ch = get_wchar(linebuf + curr);
886 a = attr[curr];
887 if (ch == prev_ch)
888 {
889 /*
890 * Overstriking a char with itself means make it bold.
891 * But overstriking an underscore with itself is
892 * ambiguous. It could mean make it bold, or
893 * it could mean make it underlined.
894 * Use the previous overstrike to resolve it.
895 */
896 if (ch == '_')
897 {
898 if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL)
899 a |= (AT_BOLD|AT_UNDERLINE);
900 else if (last_overstrike != AT_NORMAL)
901 a |= last_overstrike;
902 else
903 a |= AT_BOLD;
904 } else
905 a |= AT_BOLD;
906 } else if (ch == '_')
907 {
908 a |= AT_UNDERLINE;
909 ch = prev_ch;
910 rep = linebuf + curr;
911 } else if (prev_ch == '_')
912 {
913 a |= AT_UNDERLINE;
914 }
915 /* Else we replace prev_ch, but we keep its attributes. */
916 } else if (overstrike < 0)
917 {
918 if ( is_composing_char(ch)
919 || is_combining_char(get_wchar(linebuf + curr), ch))
920 /* Continuation of the same overstrike. */
921 a = last_overstrike;
922 else
923 overstrike = 0;
924 }
925
926 if (ch == '\t')
927 {
928 /*
929 * Expand a tab into spaces.
930 */
931 switch (bs_mode)
932 {
933 case BS_CONTROL:
934 goto do_control_char;
935 case BS_NORMAL:
936 case BS_SPECIAL:
937 STORE_TAB(a, pos);
938 break;
939 }
940 } else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
941 {
942 do_control_char:
943 if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)))
944 {
945 /*
946 * Output as a normal character.
947 */
948 STORE_CHAR(ch, AT_NORMAL, rep, pos);
949 } else
950 {
951 STORE_PRCHAR((char) ch, pos);
952 }
953 } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch))
954 {
955 char *s;
956
957 s = prutfchar(ch);
958
959 if (column + (int) strlen(s) - 1 +
960 pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
961 return (1);
962
963 for ( ; *s != 0; s++)
964 STORE_CHAR(*s, AT_BINARY, NULL, pos);
965 } else
966 {
967 STORE_CHAR(ch, a, rep, pos);
968 }
969 return (0);
970 }
971
972 /*
973 *
974 */
975 public int
976 pflushmbc()
977 {
978 int r = 0;
979
980 if (mbc_buf_len > 0)
981 {
982 /* Flush incomplete (truncated) sequence. */
983 r = flush_mbc_buf(mbc_pos);
984 mbc_buf_len = 0;
985 }
986 return r;
987 }
988
989 /*
990 * Terminate the line in the line buffer.
991 */
992 public void
993 pdone(endline)
994 int endline;
995 {
996 int nl;
997
998 (void) pflushmbc();
999
1000 if (pendc && (pendc != '\r' || !endline))
1001 /*
1002 * If we had a pending character, put it in the buffer.
1003 * But discard a pending CR if we are at end of line
1004 * (that is, discard the CR in a CR/LF sequence).
1005 */
1006 (void) do_append(pendc, NULL, pendpos);
1007
1008 /*
1009 * Make sure we've shifted the line, if we need to.
1010 */
1011 if (cshift < hshift)
1012 pshift(hshift - cshift);
1013
1014 if (ctldisp == OPT_ONPLUS && is_ansi_end('m'))
1015 {
1016 /* Switch to normal attribute at end of line. */
1017 char *p = "\033[m";
1018 for ( ; *p != '\0'; p++)
1019 {
1020 linebuf[curr] = *p;
1021 attr[curr++] = AT_ANSI;
1022 }
1023 }
1024
1025 /*
1026 * Add a newline if necessary,
1027 * and append a '\0' to the end of the line.
1028 * We output a newline if we're not at the right edge of the screen,
1029 * or if the terminal doesn't auto wrap,
1030 * or if this is really the end of the line AND the terminal ignores
1031 * a newline at the right edge.
1032 * (In the last case we don't want to output a newline if the terminal
1033 * doesn't ignore it since that would produce an extra blank line.
1034 * But we do want to output a newline if the terminal ignores it in case
1035 * the next line is blank. In that case the single newline output for
1036 * that blank line would be ignored!)
1037 */
1038 if (!oldbot)
1039 nl = (column < sc_width || !