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 |
} |