GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/less/less/../line.c Lines: 200 440 45.5 %
Date: 2017-11-13 Branches: 116 400 29.0 %

Line Branch Exec Source
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
 * 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 "charset.h"
19
#include "less.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
int size_linebuf = 0;		/* Size of line buffer (and attr buffer) */
24
25
static int cshift;		/* Current left-shift of output line buffer */
26
int hshift;			/* Desired left-shift of output line buffer */
27
int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
28
int ntabstops = 1;		/* Number of tabstops */
29
int tabdefault = 8;		/* Default repeated tabstops */
30
off_t highest_hilite;		/* Pos of last hilite in file found so far */
31
32
static int curr;		/* Index into linebuf */
33
static int column;	/* Printable length, accounting for backspaces, etc. */
34
static int overstrike;		/* Next char should overstrike previous char */
35
static int is_null_line;	/* There is no current line */
36
static int lmargin;		/* Left margin */
37
static char pendc;
38
static off_t pendpos;
39
static char *end_ansi_chars;
40
static char *mid_ansi_chars;
41
42
static int attr_swidth(int);
43
static int attr_ewidth(int);
44
static int do_append(LWCHAR, char *, off_t);
45
46
extern volatile sig_atomic_t sigs;
47
extern int bs_mode;
48
extern int linenums;
49
extern int ctldisp;
50
extern int twiddle;
51
extern int binattr;
52
extern int status_col;
53
extern int auto_wrap, ignaw;
54
extern int bo_s_width, bo_e_width;
55
extern int ul_s_width, ul_e_width;
56
extern int bl_s_width, bl_e_width;
57
extern int so_s_width, so_e_width;
58
extern int sc_width, sc_height;
59
extern int utf_mode;
60
extern off_t start_attnpos;
61
extern off_t end_attnpos;
62
63
static char mbc_buf[MAX_UTF_CHAR_LEN];
64
static int mbc_buf_len = 0;
65
static int mbc_buf_index = 0;
66
static off_t mbc_pos;
67
68
/*
69
 * Initialize from environment variables.
70
 */
71
void
72
init_line(void)
73
{
74
16
	end_ansi_chars = lgetenv("LESSANSIENDCHARS");
75

8
	if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
76
8
		end_ansi_chars = "m";
77
78
8
	mid_ansi_chars = lgetenv("LESSANSIMIDCHARS");
79

8
	if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0')
80
8
		mid_ansi_chars = "0123456789;[?!\"'#%()*+ ";
81
82
8
	linebuf = ecalloc(LINEBUF_SIZE, sizeof (char));
83
8
	attr = ecalloc(LINEBUF_SIZE, sizeof (char));
84
8
	size_linebuf = LINEBUF_SIZE;
85
8
}
86
87
/*
88
 * Expand the line buffer.
89
 */
90
static int
91
expand_linebuf(void)
92
{
93
	/* Double the size of the line buffer. */
94
8
	int new_size = size_linebuf * 2;
95
96
	/* Just realloc to expand the buffer, if we can. */
97
4
	char *new_buf = recallocarray(linebuf, size_linebuf, new_size, 1);
98
4
	char *new_attr = recallocarray(attr, size_linebuf, new_size, 1);
99
4
	if (new_buf == NULL || new_attr == NULL) {
100
		free(new_attr);
101
		free(new_buf);
102
		return (1);
103
	}
104
4
	linebuf = new_buf;
105
4
	attr = new_attr;
106
4
	size_linebuf = new_size;
107
4
	return (0);
108
4
}
109
110
/*
111
 * Is a character ASCII?
112
 */
113
int
114
is_ascii_char(LWCHAR ch)
115
{
116
	return (ch <= 0x7F);
117
}
118
119
/*
120
 * Rewind the line buffer.
121
 */
122
void
123
prewind(void)
124
{
125
2324
	curr = 0;
126
1162
	column = 0;
127
1162
	cshift = 0;
128
1162
	overstrike = 0;
129
1162
	mbc_buf_len = 0;
130
1162
	is_null_line = 0;
131
1162
	pendc = '\0';
132
1162
	lmargin = 0;
133
1162
	if (status_col)
134
		lmargin += 1;
135
1162
}
136
137
/*
138
 * Insert the line number (of the given position) into the line buffer.
139
 */
140
void
141
plinenum(off_t pos)
142
{
143
	off_t linenum = 0;
144
	int i;
145
146
2324
	if (linenums == OPT_ONPLUS) {
147
		/*
148
		 * Get the line number and put it in the current line.
149
		 * {{ Note: since find_linenum calls forw_raw_line,
150
		 *    it may seek in the input file, requiring the caller
151
		 *    of plinenum to re-seek if necessary. }}
152
		 * {{ Since forw_raw_line modifies linebuf, we must
153
		 *    do this first, before storing anything in linebuf. }}
154
		 */
155
		linenum = find_linenum(pos);
156
	}
157
158
	/*
159
	 * Display a status column if the -J option is set.
160
	 */
161
1162
	if (status_col) {
162
		linebuf[curr] = ' ';
163
		if (start_attnpos != -1 &&
164
		    pos >= start_attnpos && pos < end_attnpos)
165
			attr[curr] = AT_NORMAL|AT_HILITE;
166
		else
167
			attr[curr] = AT_NORMAL;
168
		curr++;
169
		column++;
170
	}
171
	/*
172
	 * Display the line number at the start of each line
173
	 * if the -N option is set.
174
	 */
175
1162
	if (linenums == OPT_ONPLUS) {
176
		char buf[23];
177
		int n;
178
179
		postoa(linenum, buf, sizeof(buf));
180
		n = strlen(buf);
181
		if (n < MIN_LINENUM_WIDTH)
182
			n = MIN_LINENUM_WIDTH;
183
		snprintf(linebuf+curr, size_linebuf-curr, "%*s ", n, buf);
184
		n++;	/* One space after the line number. */
185
		for (i = 0; i < n; i++)
186
			attr[curr+i] = AT_NORMAL;
187
		curr += n;
188
		column += n;
189
		lmargin += n;
190
	}
191
192
	/*
193
	 * Append enough spaces to bring us to the lmargin.
194
	 */
195
2324
	while (column < lmargin) {
196
		linebuf[curr] = ' ';
197
		attr[curr++] = AT_NORMAL;
198
		column++;
199
	}
200
1162
}
201
202
/*
203
 * Shift the input line left.
204
 * This means discarding N printable chars at the start of the buffer.
205
 */
206
static void
207
pshift(int shift)
208
{
209
	LWCHAR prev_ch = 0;
210
	unsigned char c;
211
	int shifted = 0;
212
	int to;
213
	int from;
214
	int len;
215
	int width;
216
	int prev_attr;
217
	int next_attr;
218
219
1888
	if (shift > column - lmargin)
220
		shift = column - lmargin;
221
944
	if (shift > curr - lmargin)
222
		shift = curr - lmargin;
223
224
944
	to = from = lmargin;
225
	/*
226
	 * We keep on going when shifted == shift
227
	 * to get all combining chars.
228
	 */
229

10584
	while (shifted <= shift && from < curr) {
230
2584
		c = linebuf[from];
231

2584
		if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) {
232
			/* Keep cumulative effect.  */
233
			linebuf[to] = c;
234
			attr[to++] = attr[from++];
235
			while (from < curr && linebuf[from]) {
236
				linebuf[to] = linebuf[from];
237
				attr[to++] = attr[from];
238
				if (!is_ansi_middle(linebuf[from++]))
239
					break;
240
			}
241
			continue;
242
		}
243
244
		width = 0;
245
246
2584
		if (!IS_ASCII_OCTET(c) && utf_mode) {
247
			/* Assumes well-formedness validation already done.  */
248
			LWCHAR ch;
249
250
			len = utf_len(c);
251
			if (from + len > curr)
252
				break;
253
			ch = get_wchar(linebuf + from);
254
			if (!is_composing_char(ch) &&
255
			    !is_combining_char(prev_ch, ch))
256
				width = is_wide_char(ch) ? 2 : 1;
257
			prev_ch = ch;
258
		} else {
259
			len = 1;
260
2584
			if (c == '\b')
261
				/* XXX - Incorrect if several '\b' in a row.  */
262
				width = (utf_mode && is_wide_char(prev_ch)) ?
263
				    -2 : -1;
264
2584
			else if (!control_char(c))
265
2584
				width = 1;
266
			prev_ch = 0;
267
		}
268
269

2584
		if (width == 2 && shift - shifted == 1) {
270
			/* Should never happen when called by pshift_all().  */
271
			attr[to] = attr[from];
272
			/*
273
			 * Assume a wide_char will never be the first half of a
274
			 * combining_char pair, so reset prev_ch in case we're
275
			 * followed by a '\b'.
276
			 */
277
			prev_ch = linebuf[to++] = ' ';
278
			from += len;
279
			shifted++;
280
			continue;
281
		}
282
283
		/* Adjust width for magic cookies. */
284
5168
		prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL;
285
7752
		next_attr = (from + len < curr) ? attr[from + len] : prev_attr;
286

2584
		if (!is_at_equiv(attr[from], prev_attr) &&
287
		    !is_at_equiv(attr[from], next_attr)) {
288
			width += attr_swidth(attr[from]);
289
			if (from + len < curr)
290
				width += attr_ewidth(attr[from]);
291
			if (is_at_equiv(prev_attr, next_attr)) {
292
				width += attr_ewidth(prev_attr);
293
				if (from + len < curr)
294
					width += attr_swidth(next_attr);
295
			}
296
		}
297
298
2584
		if (shift - shifted < width)
299
			break;
300
		from += len;
301
2584
		shifted += width;
302
2584
		if (shifted < 0)
303
			shifted = 0;
304
	}
305
1888
	while (from < curr) {
306
		linebuf[to] = linebuf[from];
307
		attr[to++] = attr[from++];
308
	}
309
944
	curr = to;
310
944
	column -= shifted;
311
944
	cshift += shifted;
312
944
}
313
314
/*
315
 *
316
 */
317
void
318
pshift_all(void)
319
{
320
1888
	pshift(column);
321
944
}
322
323
/*
324
 * Return the printing width of the start (enter) sequence
325
 * for a given character attribute.
326
 */
327
static int
328
attr_swidth(int a)
329
{
330
	int w = 0;
331
332
48
	a = apply_at_specials(a);
333
334
24
	if (a & AT_UNDERLINE)
335
		w += ul_s_width;
336
24
	if (a & AT_BOLD)
337
		w += bo_s_width;
338
24
	if (a & AT_BLINK)
339
		w += bl_s_width;
340
24
	if (a & AT_STANDOUT)
341
24
		w += so_s_width;
342
343
24
	return (w);
344
}
345
346
/*
347
 * Return the printing width of the end (exit) sequence
348
 * for a given character attribute.
349
 */
350
static int
351
attr_ewidth(int a)
352
{
353
	int w = 0;
354
355
77366
	a = apply_at_specials(a);
356
357
38683
	if (a & AT_UNDERLINE)
358
		w += ul_e_width;
359
38683
	if (a & AT_BOLD)
360
		w += bo_e_width;
361
38683
	if (a & AT_BLINK)
362
		w += bl_e_width;
363
38683
	if (a & AT_STANDOUT)
364
120
		w += so_e_width;
365
366
38683
	return (w);
367
}
368
369
/*
370
 * Return the printing width of a given character and attribute,
371
 * if the character were added to the current position in the line buffer.
372
 * Adding a character with a given attribute may cause an enter or exit
373
 * attribute sequence to be inserted, so this must be taken into account.
374
 */
375
static int
376
pwidth(LWCHAR ch, int a, LWCHAR prev_ch)
377
{
378
	int w;
379
380
77270
	if (ch == '\b')
381
		/*
382
		 * Backspace moves backwards one or two positions.
383
		 * XXX - Incorrect if several '\b' in a row.
384
		 */
385
		return ((utf_mode && is_wide_char(prev_ch)) ? -2 : -1);
386
387

38635
	if (!utf_mode || is_ascii_char(ch)) {
388
38635
		if (control_char((char)ch)) {
389
			/*
390
			 * Control characters do unpredictable things,
391
			 * so we don't even try to guess; say it doesn't move.
392
			 * This can only happen if the -r flag is in effect.
393
			 */
394
			return (0);
395
		}
396
	} else {
397
		if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) {
398
			/*
399
			 * Composing and combining chars take up no space.
400
			 *
401
			 * Some terminals, upon failure to compose a
402
			 * composing character with the character(s) that
403
			 * precede(s) it will actually take up one column
404
			 * for the composing character; there isn't much
405
			 * we could do short of testing the (complex)
406
			 * composition process ourselves and printing
407
			 * a binary representation when it fails.
408
			 */
409
			return (0);
410
		}
411
	}
412
413
	/*
414
	 * Other characters take one or two columns,
415
	 * plus the width of any attribute enter/exit sequence.
416
	 */
417
	w = 1;
418
38635
	if (is_wide_char(ch))
419
		w++;
420

76119
	if (curr > 0 && !is_at_equiv(attr[curr-1], a))
421
48
		w += attr_ewidth(attr[curr-1]);
422

38731
	if ((apply_at_specials(a) != AT_NORMAL) &&
423
192
	    (curr == 0 || !is_at_equiv(attr[curr-1], a)))
424
24
		w += attr_swidth(a);
425
38635
	return (w);
426
38635
}
427
428
/*
429
 * Delete to the previous base character in the line buffer.
430
 * Return 1 if one is found.
431
 */
432
static int
433
backc(void)
434
{
435
	LWCHAR prev_ch;
436
	char *p = linebuf + curr;
437
	LWCHAR ch = step_char(&p, -1, linebuf + lmargin);
438
	int width;
439
440
	/* This assumes that there is no '\b' in linebuf.  */
441
	while (curr > lmargin && column > lmargin &&
442
	    (!(attr[curr - 1] & (AT_ANSI|AT_BINARY)))) {
443
		curr = p - linebuf;
444
		prev_ch = step_char(&p, -1, linebuf + lmargin);
445
		width = pwidth(ch, attr[curr], prev_ch);
446
		column -= width;
447
		if (width > 0)
448
			return (1);
449
		ch = prev_ch;
450
	}
451
452
	return (0);
453
}
454
455
/*
456
 * Are we currently within a recognized ANSI escape sequence?
457
 */
458
static int
459
in_ansi_esc_seq(void)
460
{
461
	char *p;
462
463
	/*
464
	 * Search backwards for either an ESC (which means we ARE in a seq);
465
	 * or an end char (which means we're NOT in a seq).
466
	 */
467
	for (p = &linebuf[curr]; p > linebuf; ) {
468
		LWCHAR ch = step_char(&p, -1, linebuf);
469
		if (IS_CSI_START(ch))
470
			return (1);
471
		if (!is_ansi_middle(ch))
472
			return (0);
473
	}
474
	return (0);
475
}
476
477
/*
478
 * Is a character the end of an ANSI escape sequence?
479
 */
480
int
481
is_ansi_end(LWCHAR ch)
482
{
483
	if (!is_ascii_char(ch))
484
		return (0);
485
	return (strchr(end_ansi_chars, (char)ch) != NULL);
486
}
487
488
/*
489
 *
490
 */
491
int
492
is_ansi_middle(LWCHAR ch)
493
{
494
	if (!is_ascii_char(ch))
495
		return (0);
496
	if (is_ansi_end(ch))
497
		return (0);
498
	return (strchr(mid_ansi_chars, (char)ch) != NULL);
499
}
500
501
/*
502
 * Append a character and attribute to the line buffer.
503
 */
504
#define	STORE_CHAR(ch, a, rep, pos)				\
505
		if (store_char((ch), (a), (rep), (pos)))	\
506
			return (1)
507
508
static int
509
store_char(LWCHAR ch, char a, char *rep, off_t pos)
510
{
511
	int w;
512
	int replen;
513
77270
	char cs;
514
38635
	int matches;
515
516
38635
	if (is_hilited(pos, pos+1, 0, &matches)) {
517
		/*
518
		 * This character should be highlighted.
519
		 * Override the attribute passed in.
520
		 */
521
96
		if (a != AT_ANSI) {
522

192
			if (highest_hilite != -1 && pos > highest_hilite)
523
96
				highest_hilite = pos;
524
96
			a |= AT_HILITE;
525
96
		}
526
	}
527
528

38635
	if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq()) {
529
		if (!is_ansi_end(ch) && !is_ansi_middle(ch)) {
530
			/* Remove whole unrecognized sequence.  */
531
			char *p = &linebuf[curr];
532
			LWCHAR bch;
533
			do {
534
				bch = step_char(&p, -1, linebuf);
535
			} while (p > linebuf && !IS_CSI_START(bch));
536
			curr = p - linebuf;
537
			return (0);
538
		}
539
		a = AT_ANSI;	/* Will force re-AT_'ing around it.  */
540
		w = 0;
541

38635
	} else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)) {
542
		a = AT_ANSI;	/* Will force re-AT_'ing around it.  */
543
		w = 0;
544
	} else {
545
38635
		char *p = &linebuf[curr];
546
38635
		LWCHAR prev_ch = step_char(&p, -1, linebuf);
547
38635
		w = pwidth(ch, a, prev_ch);
548
38635
	}
549
550

77270
	if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
551
		/*
552
		 * Won't fit on screen.
553
		 */
554
19
		return (1);
555
556
38616
	if (rep == NULL) {
557
38616
		cs = (char)ch;
558
		rep = &cs;
559
		replen = 1;
560
38616
	} else {
561
		replen = utf_len(rep[0]);
562
	}
563
38616
	if (curr + replen >= size_linebuf-6) {
564
		/*
565
		 * Won't fit in line buffer.
566
		 * Try to expand it.
567
		 */
568
		if (expand_linebuf())
569
			return (1);
570
	}
571
572
154464
	while (replen-- > 0) {
573
38616
		linebuf[curr] = *rep++;
574
38616
		attr[curr] = a;
575
38616
		curr++;
576
	}
577
38616
	column += w;
578
38616
	return (0);
579
38635
}
580
581
/*
582
 * Append a tab to the line buffer.
583
 * Store spaces to represent the tab.
584
 */
585
#define	STORE_TAB(a, pos)		\
586
	if (store_tab((a), (pos)))	\
587
		return (1)
588
589
static int
590
store_tab(int attr, off_t pos)
591
{
592
	int to_tab = column + cshift - lmargin;
593
	int i;
594
595
	if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
596
		to_tab = tabdefault -
597
		    ((to_tab - tabstops[ntabstops-1]) % tabdefault);
598
	else {
599
		for (i = ntabstops - 2; i >= 0; i--)
600
			if (to_tab >= tabstops[i])
601
				break;
602
		to_tab = tabstops[i+1] - to_tab;
603
	}
604
605
	if (column + to_tab - 1 + pwidth(' ', attr, 0) +
606
	    attr_ewidth(attr) > sc_width)
607
		return (1);
608
609
	do {
610
		STORE_CHAR(' ', attr, " ", pos);
611
	} while (--to_tab > 0);
612
	return (0);
613
}
614
615
#define	STORE_PRCHAR(c, pos)		\
616
	if (store_prchar((c), (pos)))	\
617
		return (1)
618
619
static int
620
store_prchar(char c, off_t pos)
621
{
622
	char *s;
623
624
	/*
625
	 * Convert to printable representation.
626
	 */
627
	s = prchar(c);
628
629
	/*
630
	 * Make sure we can get the entire representation
631
	 * of the character on this line.
632
	 */
633
	if (column + (int)strlen(s) - 1 +
634
	    pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
635
		return (1);
636
637
	for (; *s != 0; s++) {
638
		STORE_CHAR(*s, AT_BINARY, NULL, pos);
639
	}
640
	return (0);
641
}
642
643
static int
644
flush_mbc_buf(off_t pos)
645
{
646
	int i;
647
648
	for (i = 0; i < mbc_buf_index; i++)
649
		if (store_prchar(mbc_buf[i], pos))
650
			return (mbc_buf_index - i);
651
652
	return (0);
653
}
654
655
/*
656
 * Append a character to the line buffer.
657
 * Expand tabs into spaces, handle underlining, boldfacing, etc.
658
 * Returns 0 if ok, 1 if couldn't fit in buffer.
659
 */
660
int
661
pappend(char c, off_t pos)
662
{
663
	int r;
664
665
77270
	if (pendc) {
666
		if (do_append(pendc, NULL, pendpos))
667
			/*
668
			 * Oops.  We've probably lost the char which
669
			 * was in pendc, since caller won't back up.
670
			 */
671
			return (1);
672
		pendc = '\0';
673
	}
674
675
38635
	if (c == '\r' && bs_mode == BS_SPECIAL) {
676
		if (mbc_buf_len > 0)  /* utf_mode must be on. */ {
677
			/* Flush incomplete (truncated) sequence. */
678
			r = flush_mbc_buf(mbc_pos);
679
			mbc_buf_index = r + 1;
680
			mbc_buf_len = 0;
681
			if (r)
682
				return (mbc_buf_index);
683
		}
684
685
		/*
686
		 * Don't put the CR into the buffer until we see
687
		 * the next char.  If the next char is a newline,
688
		 * discard the CR.
689
		 */
690
		pendc = c;
691
		pendpos = pos;
692
		return (0);
693
	}
694
695
38635
	if (!utf_mode) {
696
38635
		r = do_append((LWCHAR) c, NULL, pos);
697
38635
	} else {
698
		/* Perform strict validation in all possible cases. */
699
		if (mbc_buf_len == 0) {
700
retry:
701
			mbc_buf_index = 1;
702
			*mbc_buf = c;
703
			if (IS_ASCII_OCTET(c)) {
704
				r = do_append((LWCHAR) c, NULL, pos);
705
			} else if (IS_UTF8_LEAD(c)) {
706
				mbc_buf_len = utf_len(c);
707
				mbc_pos = pos;
708
				return (0);
709
			} else {
710
				/* UTF8_INVALID or stray UTF8_TRAIL */
711
				r = flush_mbc_buf(pos);
712
			}
713
		} else if (IS_UTF8_TRAIL(c)) {
714
			mbc_buf[mbc_buf_index++] = c;
715
			if (mbc_buf_index < mbc_buf_len)
716
				return (0);
717
			if (is_utf8_well_formed(mbc_buf))
718
				r = do_append(get_wchar(mbc_buf), mbc_buf,
719
				    mbc_pos);
720
			else
721
				/* Complete, but not shortest form, sequence. */
722
				mbc_buf_index = r = flush_mbc_buf(mbc_pos);
723
			mbc_buf_len = 0;
724
		} else {
725
			/* Flush incomplete (truncated) sequence.  */
726
			r = flush_mbc_buf(mbc_pos);
727
			mbc_buf_index = r + 1;
728
			mbc_buf_len = 0;
729
			/* Handle new char.  */
730
			if (!r)
731
				goto retry;
732
		}
733
	}
734
735
	/*
736
	 * If we need to shift the line, do it.
737
	 * But wait until we get to at least the middle of the screen,
738
	 * so shifting it doesn't affect the chars we're currently
739
	 * pappending.  (Bold & underline can get messed up otherwise.)
740
	 */
741

38635
	if (cshift < hshift && column > sc_width / 2) {
742
		linebuf[curr] = '\0';
743
		pshift(hshift - cshift);
744
	}
745
38635
	if (r) {
746
		/* How many chars should caller back up? */
747
19
		r = (!utf_mode) ? 1 : mbc_buf_index;
748
19
	}
749
38635
	return (r);
750
38635
}
751
752
static int
753
do_append(LWCHAR ch, char *rep, off_t pos)
754
{
755
	int a;
756
	LWCHAR prev_ch;
757
758
	a = AT_NORMAL;
759
760
77270
	if (ch == '\b') {
761
		if (bs_mode == BS_CONTROL)
762
			goto do_control_char;
763
764
		/*
765
		 * A better test is needed here so we don't
766
		 * backspace over part of the printed
767
		 * representation of a binary character.
768
		 */
769
		if (curr <= lmargin ||
770
		    column <= lmargin ||
771
		    (attr[curr - 1] & (AT_ANSI|AT_BINARY))) {
772
			STORE_PRCHAR('\b', pos);
773
		} else if (bs_mode == BS_NORMAL) {
774
			STORE_CHAR(ch, AT_NORMAL, NULL, pos);
775
		} else if (bs_mode == BS_SPECIAL) {
776
			overstrike = backc();
777
		}
778
779
		return (0);
780
	}
781
782
38635
	if (overstrike > 0) {
783
		/*
784
		 * Overstrike the character at the current position
785
		 * in the line buffer.  This will cause either
786
		 * underline (if a "_" is overstruck),
787
		 * bold (if an identical character is overstruck),
788
		 * or just deletion of the character in the buffer.
789
		 */
790
		overstrike = utf_mode ? -1 : 0;
791
		/* To be correct, this must be a base character.  */
792
		prev_ch = get_wchar(linebuf + curr);
793
		a = attr[curr];
794
		if (ch == prev_ch) {
795
			/*
796
			 * Overstriking a char with itself means make it bold.
797
			 * But overstriking an underscore with itself is
798
			 * ambiguous.  It could mean make it bold, or
799
			 * it could mean make it underlined.
800
			 * Use the previous overstrike to resolve it.
801
			 */
802
			if (ch == '_') {
803
				if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL)
804
					a |= (AT_BOLD|AT_UNDERLINE);
805
				else if (curr > 0 && attr[curr - 1] & AT_UNDERLINE)
806
					a |= AT_UNDERLINE;
807
				else if (curr > 0 && attr[curr - 1] & AT_BOLD)
808
					a |= AT_BOLD;
809
				else
810
					a |= AT_INDET;
811
			} else {
812
				a |= AT_BOLD;
813
			}
814
		} else if (ch == '_') {
815
			a |= AT_UNDERLINE;
816
			ch = prev_ch;
817
			rep = linebuf + curr;
818
		} else if (prev_ch == '_') {
819
			a |= AT_UNDERLINE;
820
		}
821
		/* Else we replace prev_ch, but we keep its attributes.  */
822
38635
	} else if (overstrike < 0) {
823
		if (is_composing_char(ch) ||
824
		    is_combining_char(get_wchar(linebuf + curr), ch)) {
825
			/* Continuation of the same overstrike.  */
826
			if (curr > 0)
827
				a = attr[curr - 1] & (AT_UNDERLINE | AT_BOLD);
828
			else
829
				a = AT_NORMAL;
830
		} else
831
			overstrike = 0;
832
	}
833
834
38635
	if (ch == '\t') {
835
		/*
836
		 * Expand a tab into spaces.
837
		 */
838
		switch (bs_mode) {
839
		case BS_CONTROL:
840
			goto do_control_char;
841
		case BS_NORMAL:
842
		case BS_SPECIAL:
843
			STORE_TAB(a, pos);
844
			break;
845
		}
846

77270
	} else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch)) {
847
do_control_char:
848
		if (ctldisp == OPT_ON ||
849
		    (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))) {
850
			/*
851
			 * Output as a normal character.
852
			 */
853
			STORE_CHAR(ch, AT_NORMAL, rep, pos);
854
		} else {
855
			STORE_PRCHAR((char)ch, pos);
856
		}
857

38635
	} else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch)) {
858
		char *s;
859
860
		s = prutfchar(ch);
861
862
		if (column + (int)strlen(s) - 1 +
863
		    pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
864
			return (1);
865
866
		for (; *s != 0; s++)
867
			STORE_CHAR(*s, AT_BINARY, NULL, pos);
868
	} else {
869
38654
		STORE_CHAR(ch, a, rep, pos);
870
	}
871
38616
	return (0);
872
38635
}
873
874
/*
875
 *
876
 */
877
int
878
pflushmbc(void)
879
{
880
	int r = 0;
881
882
6478
	if (mbc_buf_len > 0) {
883
		/* Flush incomplete (truncated) sequence.  */
884
		r = flush_mbc_buf(mbc_pos);
885
		mbc_buf_len = 0;
886
	}
887
3239
	return (r);
888
}
889
890
/*
891
 * Terminate the line in the line buffer.
892
 */
893
void
894
pdone(int endline, int forw)
895
{
896
	int i;
897
898
2320
	(void) pflushmbc();
899
900

1160
	if (pendc && (pendc != '\r' || !endline))
901
		/*
902
		 * If we had a pending character, put it in the buffer.
903
		 * But discard a pending CR if we are at end of line
904
		 * (that is, discard the CR in a CR/LF sequence).
905
		 */
906
		(void) do_append(pendc, NULL, pendpos);
907
908
74384
	for (i = curr - 1; i >= 0; i--) {
909
36032
		if (attr[i] & AT_INDET) {
910
			attr[i] &= ~AT_INDET;
911
			if (i < curr - 1 && attr[i + 1] & AT_BOLD)
912
				attr[i] |= AT_BOLD;
913
			else
914
				attr[i] |= AT_UNDERLINE;
915
		}
916
	}
917
918
	/*
919
	 * Make sure we've shifted the line, if we need to.
920
	 */
921
1160
	if (cshift < hshift)
922
		pshift(hshift - cshift);
923
924

1160
	if (ctldisp == OPT_ONPLUS && is_ansi_end('m')) {
925
		/* Switch to normal attribute at end of line. */
926
		char *p = "\033[m";
927
		for (; *p != '\0'; p++) {
928
			linebuf[curr] = *p;
929
			attr[curr++] = AT_ANSI;
930
		}
931
	}
932
933
	/*
934
	 * Add a newline if necessary,
935
	 * and append a '\0' to the end of the line.
936
	 * We output a newline if we're not at the right edge of the screen,
937
	 * or if the terminal doesn't auto wrap,
938
	 * or if this is really the end of the line AND the terminal ignores
939
	 * a newline at the right edge.
940
	 * (In the last case we don't want to output a newline if the terminal
941
	 * doesn't ignore it since that would produce an extra blank line.
942
	 * But we do want to output a newline if the terminal ignores it in case
943
	 * the next line is blank.  In that case the single newline output for
944
	 * that blank line would be ignored!)
945
	 */
946

1198
	if (column < sc_width || !auto_wrap || (endline && ignaw) ||
947
19
	    ctldisp == OPT_ON) {
948
1141
		linebuf[curr] = '\n';
949
1141
		attr[curr] = AT_NORMAL;
950
1141
		curr++;
951

1179
	} else if (ignaw && column >= sc_width && forw) {
952
		/*
953
		 * Terminals with "ignaw" don't wrap until they *really* need
954
		 * to, i.e. when the character *after* the last one to fit on a
955
		 * line is output. But they are too hard to deal with when they
956
		 * get in the state where a full screen width of characters
957
		 * have been output but the cursor is sitting on the right edge
958
		 * instead of at the start of the next line.
959
		 * So we nudge them into wrapping by outputting a space
960
		 * character plus a backspace.  But do this only if moving
961
		 * forward; if we're moving backward and drawing this line at
962
		 * the top of the screen, the space would overwrite the first
963
		 * char on the next line.  We don't need to do this "nudge"
964
		 * at the top of the screen anyway.
965
		 */
966
13
		linebuf[curr] = ' ';
967
13
		attr[curr++] = AT_NORMAL;
968
13
		linebuf[curr] = '\b';
969
13
		attr[curr++] = AT_NORMAL;
970
13
	}
971
1160
	linebuf[curr] = '\0';
972
1160
	attr[curr] = AT_NORMAL;
973
1160
}
974
975
/*
976
 *
977
 */
978
void
979
set_status_col(char c)
980
{
981
	linebuf[0] = c;
982
	attr[0] = AT_NORMAL|AT_HILITE;
983
}
984
985
/*
986
 * Get a character from the current line.
987
 * Return the character as the function return value,
988
 * and the character attribute in *ap.
989
 */
990
int
991
gline(int i, int *ap)
992
{
993
62964
	if (is_null_line) {
994
		/*
995
		 * If there is no current line, we pretend the line is
996
		 * either "~" or "", depending on the "twiddle" flag.
997
		 */
998
84
		if (twiddle) {
999
84
			if (i == 0) {
1000
28
				*ap = AT_BOLD;
1001
28
				return ('~');
1002
			}
1003
56
			--i;
1004
56
		}
1005
		/* Make sure we're back to AT_NORMAL before the '\n'.  */
1006
56
		*ap = AT_NORMAL;
1007
56
		return (i ? '\0' : '\n');
1008
	}
1009
1010
31398
	*ap = attr[i];
1011
31398
	return (linebuf[i] & 0xFF);
1012
31482
}
1013
1014
/*
1015
 * Indicate that there is no current line.
1016
 */
1017
void
1018
null_line(void)
1019
{
1020
62
	is_null_line = 1;
1021
31
	cshift = 0;
1022
31
}
1023
1024
/*
1025
 * Analogous to forw_line(), but deals with "raw lines":
1026
 * lines which are not split for screen width.
1027
 * {{ This is supposed to be more efficient than forw_line(). }}
1028
 */
1029
off_t
1030
forw_raw_line(off_t curr_pos, char **linep, int *line_lenp)
1031
{
1032
	int n;
1033
	int c;
1034
	off_t new_pos;
1035
1036

43904
	if (curr_pos == -1 || ch_seek(curr_pos) ||
1037
10976
	    (c = ch_forw_get()) == EOI)
1038
290
		return (-1);
1039
1040
	n = 0;
1041
479248
	for (;;) {
1042

947810
		if (c == '\n' || c == EOI || ABORT_SIGS()) {
1043
10686
			new_pos = ch_tell();
1044
10686
			break;
1045
		}
1046
468562
		if (n >= size_linebuf-1) {
1047
4
			if (expand_linebuf()) {
1048
				/*
1049
				 * Overflowed the input buffer.
1050
				 * Pretend the line ended here.
1051
				 */
1052
				new_pos = ch_tell() - 1;
1053
				break;
1054
			}
1055
		}
1056
468562
		linebuf[n++] = (char)c;
1057
468562
		c = ch_forw_get();
1058
	}
1059
10686
	linebuf[n] = '\0';
1060
10686
	if (linep != NULL)
1061
9990
		*linep = linebuf;
1062
10686
	if (line_lenp != NULL)
1063
9990
		*line_lenp = n;
1064
10686
	return (new_pos);
1065
10976
}
1066
1067
/*
1068
 * Analogous to back_line(), but deals with "raw lines".
1069
 * {{ This is supposed to be more efficient than back_line(). }}
1070
 */
1071
off_t
1072
back_raw_line(off_t curr_pos, char **linep, int *line_lenp)
1073
{
1074
	int n;
1075
	int c;
1076
	off_t new_pos;
1077
1078

336
	if (curr_pos == -1 || curr_pos <= ch_zero() || ch_seek(curr_pos - 1))
1079
		return (-1);
1080
1081
112
	n = size_linebuf;
1082
112
	linebuf[--n] = '\0';
1083
2151
	for (;;) {
1084
2151
		c = ch_back_get();
1085

4190
		if (c == '\n' || ABORT_SIGS()) {
1086
			/*
1087
			 * This is the newline ending the previous line.
1088
			 * We have hit the beginning of the line.
1089
			 */
1090
112
			new_pos = ch_tell() + 1;
1091
112
			break;
1092
		}
1093
2039
		if (c == EOI) {
1094
			/*
1095
			 * We have hit the beginning of the file.
1096
			 * This must be the first line in the file.
1097
			 * This must, of course, be the beginning of the line.
1098
			 */
1099
			new_pos = ch_zero();
1100
			break;
1101
		}
1102
2039
		if (n <= 0) {
1103
			int old_size_linebuf = size_linebuf;
1104
			if (expand_linebuf()) {
1105
				/*
1106
				 * Overflowed the input buffer.
1107
				 * Pretend the line ended here.
1108
				 */
1109
				new_pos = ch_tell() + 1;
1110
				break;
1111
			}
1112
			/*
1113
			 * Shift the data to the end of the new linebuf.
1114
			 */
1115
			n = size_linebuf - old_size_linebuf;
1116
			memmove(linebuf + n, linebuf, old_size_linebuf);
1117
		}
1118
2039
		linebuf[--n] = c;
1119
	}
1120
112
	if (linep != NULL)
1121
		*linep = &linebuf[n];
1122
112
	if (line_lenp != NULL)
1123
		*line_lenp = size_linebuf - 1 - n;
1124
112
	return (new_pos);
1125
112
}