1 |
|
|
/* $OpenBSD: vs_line.c,v 1.16 2016/05/27 09:18:12 martijn Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1993, 1994 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* Copyright (c) 1992, 1993, 1994, 1995, 1996 |
7 |
|
|
* Keith Bostic. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* See the LICENSE file for redistribution information. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include "config.h" |
13 |
|
|
|
14 |
|
|
#include <sys/types.h> |
15 |
|
|
#include <sys/queue.h> |
16 |
|
|
#include <sys/time.h> |
17 |
|
|
|
18 |
|
|
#include <bitstring.h> |
19 |
|
|
#include <limits.h> |
20 |
|
|
#include <stdio.h> |
21 |
|
|
#include <string.h> |
22 |
|
|
|
23 |
|
|
#include "../common/common.h" |
24 |
|
|
#include "vi.h" |
25 |
|
|
|
26 |
|
|
#ifdef VISIBLE_TAB_CHARS |
27 |
|
|
#define TABCH '-' |
28 |
|
|
#else |
29 |
|
|
#define TABCH ' ' |
30 |
|
|
#endif |
31 |
|
|
|
32 |
|
|
/* |
33 |
|
|
* vs_line -- |
34 |
|
|
* Update one line on the screen. |
35 |
|
|
* |
36 |
|
|
* PUBLIC: int vs_line(SCR *, SMAP *, size_t *, size_t *); |
37 |
|
|
*/ |
38 |
|
|
int |
39 |
|
|
vs_line(SCR *sp, SMAP *smp, size_t *yp, size_t *xp) |
40 |
|
|
{ |
41 |
|
|
CHAR_T *kp; |
42 |
|
|
GS *gp; |
43 |
|
|
SMAP *tsmp; |
44 |
|
|
size_t chlen = 0, cno_cnt, cols_per_screen, len, nlen; |
45 |
|
|
size_t offset_in_char, offset_in_line, oldx, oldy; |
46 |
|
|
size_t scno, skip_cols, skip_screens; |
47 |
|
|
int ch = 0, dne, is_cached, is_partial, is_tab, no_draw; |
48 |
|
|
int list_tab, list_dollar; |
49 |
|
|
char *p, *cbp, *ecbp, cbuf[128]; |
50 |
|
|
|
51 |
|
|
#if defined(DEBUG) && 0 |
52 |
|
|
TRACE(sp, "vs_line: row %u: line: %u off: %u\n", |
53 |
|
|
smp - HMAP, smp->lno, smp->off); |
54 |
|
|
#endif |
55 |
|
|
/* |
56 |
|
|
* If ex modifies the screen after ex output is already on the screen, |
57 |
|
|
* don't touch it -- we'll get scrolling wrong, at best. |
58 |
|
|
*/ |
59 |
|
|
no_draw = 0; |
60 |
|
|
if (!F_ISSET(sp, SC_TINPUT_INFO) && VIP(sp)->totalcount > 1) |
61 |
|
|
no_draw = 1; |
62 |
|
|
if (F_ISSET(sp, SC_SCR_EXWROTE) && smp - HMAP != LASTLINE(sp)) |
63 |
|
|
no_draw = 1; |
64 |
|
|
|
65 |
|
|
/* |
66 |
|
|
* Assume that, if the cache entry for the line is filled in, the |
67 |
|
|
* line is already on the screen, and all we need to do is return |
68 |
|
|
* the cursor position. If the calling routine doesn't need the |
69 |
|
|
* cursor position, we can just return. |
70 |
|
|
*/ |
71 |
|
|
is_cached = SMAP_CACHE(smp); |
72 |
|
|
if (yp == NULL && (is_cached || no_draw)) |
73 |
|
|
return (0); |
74 |
|
|
|
75 |
|
|
/* |
76 |
|
|
* A nasty side effect of this routine is that it returns the screen |
77 |
|
|
* position for the "current" character. Not pretty, but this is the |
78 |
|
|
* only routine that really knows what's out there. |
79 |
|
|
* |
80 |
|
|
* Move to the line. This routine can be called by vs_sm_position(), |
81 |
|
|
* which uses it to fill in the cache entry so it can figure out what |
82 |
|
|
* the real contents of the screen are. Because of this, we have to |
83 |
|
|
* return to whereever we started from. |
84 |
|
|
*/ |
85 |
|
|
gp = sp->gp; |
86 |
|
|
(void)gp->scr_cursor(sp, &oldy, &oldx); |
87 |
|
|
(void)gp->scr_move(sp, smp - HMAP, 0); |
88 |
|
|
|
89 |
|
|
/* Get the line. */ |
90 |
|
|
dne = db_get(sp, smp->lno, 0, &p, &len); |
91 |
|
|
|
92 |
|
|
/* |
93 |
|
|
* Special case if we're printing the info/mode line. Skip printing |
94 |
|
|
* the leading number, as well as other minor setup. The only time |
95 |
|
|
* this code paints the mode line is when the user is entering text |
96 |
|
|
* for a ":" command, so we can put the code here instead of dealing |
97 |
|
|
* with the empty line logic below. This is a kludge, but it's pretty |
98 |
|
|
* much confined to this module. |
99 |
|
|
* |
100 |
|
|
* Set the number of columns for this screen. |
101 |
|
|
* Set the number of chars or screens to skip until a character is to |
102 |
|
|
* be displayed. |
103 |
|
|
*/ |
104 |
|
|
cols_per_screen = sp->cols; |
105 |
|
|
if (O_ISSET(sp, O_LEFTRIGHT)) { |
106 |
|
|
skip_screens = 0; |
107 |
|
|
skip_cols = smp->coff; |
108 |
|
|
} else { |
109 |
|
|
skip_screens = smp->soff - 1; |
110 |
|
|
skip_cols = skip_screens * cols_per_screen; |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
list_tab = O_ISSET(sp, O_LIST); |
114 |
|
|
if (F_ISSET(sp, SC_TINPUT_INFO)) |
115 |
|
|
list_dollar = 0; |
116 |
|
|
else { |
117 |
|
|
list_dollar = list_tab; |
118 |
|
|
|
119 |
|
|
/* |
120 |
|
|
* If O_NUMBER is set, the line doesn't exist and it's line |
121 |
|
|
* number 1, i.e., an empty file, display the line number. |
122 |
|
|
* |
123 |
|
|
* If O_NUMBER is set, the line exists and the first character |
124 |
|
|
* on the screen is the first character in the line, display |
125 |
|
|
* the line number. |
126 |
|
|
* |
127 |
|
|
* !!! |
128 |
|
|
* If O_NUMBER set, decrement the number of columns in the |
129 |
|
|
* first screen. DO NOT CHANGE THIS -- IT'S RIGHT! The |
130 |
|
|
* rest of the code expects this to reflect the number of |
131 |
|
|
* columns in the first screen, regardless of the number of |
132 |
|
|
* columns we're going to skip. |
133 |
|
|
*/ |
134 |
|
|
if (O_ISSET(sp, O_NUMBER)) { |
135 |
|
|
cols_per_screen -= O_NUMBER_LENGTH; |
136 |
|
|
if ((!dne || smp->lno == 1) && skip_cols == 0) { |
137 |
|
|
nlen = snprintf(cbuf, sizeof(cbuf), |
138 |
|
|
O_NUMBER_FMT, (ulong)smp->lno); |
139 |
|
|
if (nlen >= sizeof(cbuf)) |
140 |
|
|
nlen = sizeof(cbuf) - 1; |
141 |
|
|
(void)gp->scr_addstr(sp, cbuf, nlen); |
142 |
|
|
} |
143 |
|
|
} |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
/* |
147 |
|
|
* Special case non-existent lines and the first line of an empty |
148 |
|
|
* file. In both cases, the cursor position is 0, but corrected |
149 |
|
|
* as necessary for the O_NUMBER field, if it was displayed. |
150 |
|
|
*/ |
151 |
|
|
if (dne || len == 0) { |
152 |
|
|
/* Fill in the cursor. */ |
153 |
|
|
if (yp != NULL && smp->lno == sp->lno) { |
154 |
|
|
*yp = smp - HMAP; |
155 |
|
|
*xp = sp->cols - cols_per_screen; |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
/* If the line is on the screen, quit. */ |
159 |
|
|
if (is_cached || no_draw) |
160 |
|
|
goto ret1; |
161 |
|
|
|
162 |
|
|
/* Set line cache information. */ |
163 |
|
|
smp->c_sboff = smp->c_eboff = 0; |
164 |
|
|
smp->c_scoff = smp->c_eclen = 0; |
165 |
|
|
|
166 |
|
|
/* |
167 |
|
|
* Lots of special cases for empty lines, but they only apply |
168 |
|
|
* if we're displaying the first screen of the line. |
169 |
|
|
*/ |
170 |
|
|
if (skip_cols == 0) { |
171 |
|
|
if (dne) { |
172 |
|
|
if (smp->lno == 1) { |
173 |
|
|
if (list_dollar) { |
174 |
|
|
ch = '$'; |
175 |
|
|
goto empty; |
176 |
|
|
} |
177 |
|
|
} else { |
178 |
|
|
ch = '~'; |
179 |
|
|
goto empty; |
180 |
|
|
} |
181 |
|
|
} else |
182 |
|
|
if (list_dollar) { |
183 |
|
|
ch = '$'; |
184 |
|
|
empty: (void)gp->scr_addstr(sp, |
185 |
|
|
KEY_NAME(sp, ch), KEY_LEN(sp, ch)); |
186 |
|
|
} |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
(void)gp->scr_clrtoeol(sp); |
190 |
|
|
(void)gp->scr_move(sp, oldy, oldx); |
191 |
|
|
return (0); |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
/* |
195 |
|
|
* If we just wrote this or a previous line, we cached the starting |
196 |
|
|
* and ending positions of that line. The way it works is we keep |
197 |
|
|
* information about the lines displayed in the SMAP. If we're |
198 |
|
|
* painting the screen in the forward direction, this saves us from |
199 |
|
|
* reformatting the physical line for every line on the screen. This |
200 |
|
|
* wins big on binary files with 10K lines. |
201 |
|
|
* |
202 |
|
|
* Test for the first screen of the line, then the current screen line, |
203 |
|
|
* then the line behind us, then do the hard work. Note, it doesn't |
204 |
|
|
* do us any good to have a line in front of us -- it would be really |
205 |
|
|
* hard to try and figure out tabs in the reverse direction, i.e. how |
206 |
|
|
* many spaces a tab takes up in the reverse direction depends on |
207 |
|
|
* what characters preceded it. |
208 |
|
|
* |
209 |
|
|
* Test for the first screen of the line. |
210 |
|
|
*/ |
211 |
|
|
if (skip_cols == 0) { |
212 |
|
|
smp->c_sboff = offset_in_line = 0; |
213 |
|
|
smp->c_scoff = offset_in_char = 0; |
214 |
|
|
p = &p[offset_in_line]; |
215 |
|
|
goto display; |
216 |
|
|
} |
217 |
|
|
|
218 |
|
|
/* Test to see if we've seen this exact line before. */ |
219 |
|
|
if (is_cached) { |
220 |
|
|
offset_in_line = smp->c_sboff; |
221 |
|
|
offset_in_char = smp->c_scoff; |
222 |
|
|
p = &p[offset_in_line]; |
223 |
|
|
|
224 |
|
|
/* Set cols_per_screen to 2nd and later line length. */ |
225 |
|
|
if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen) |
226 |
|
|
cols_per_screen = sp->cols; |
227 |
|
|
goto display; |
228 |
|
|
} |
229 |
|
|
|
230 |
|
|
/* Test to see if we saw an earlier part of this line before. */ |
231 |
|
|
if (smp != HMAP && |
232 |
|
|
SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) { |
233 |
|
|
if (tsmp->c_eclen != tsmp->c_ecsize) { |
234 |
|
|
offset_in_line = tsmp->c_eboff; |
235 |
|
|
offset_in_char = tsmp->c_eclen; |
236 |
|
|
} else { |
237 |
|
|
offset_in_line = tsmp->c_eboff + 1; |
238 |
|
|
offset_in_char = 0; |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
/* Put starting info for this line in the cache. */ |
242 |
|
|
smp->c_sboff = offset_in_line; |
243 |
|
|
smp->c_scoff = offset_in_char; |
244 |
|
|
p = &p[offset_in_line]; |
245 |
|
|
|
246 |
|
|
/* Set cols_per_screen to 2nd and later line length. */ |
247 |
|
|
if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen) |
248 |
|
|
cols_per_screen = sp->cols; |
249 |
|
|
goto display; |
250 |
|
|
} |
251 |
|
|
|
252 |
|
|
scno = 0; |
253 |
|
|
offset_in_line = 0; |
254 |
|
|
offset_in_char = 0; |
255 |
|
|
|
256 |
|
|
/* Do it the hard way, for leftright scrolling screens. */ |
257 |
|
|
if (O_ISSET(sp, O_LEFTRIGHT)) { |
258 |
|
|
for (; offset_in_line < len; ++offset_in_line) { |
259 |
|
|
chlen = (ch = *(u_char *)p++) == '\t' && !list_tab ? |
260 |
|
|
TAB_OFF(scno) : KEY_LEN(sp, ch); |
261 |
|
|
if ((scno += chlen) >= skip_cols) |
262 |
|
|
break; |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
/* Set cols_per_screen to 2nd and later line length. */ |
266 |
|
|
cols_per_screen = sp->cols; |
267 |
|
|
|
268 |
|
|
/* Put starting info for this line in the cache. */ |
269 |
|
|
if (scno != skip_cols) { |
270 |
|
|
smp->c_sboff = offset_in_line; |
271 |
|
|
smp->c_scoff = |
272 |
|
|
offset_in_char = chlen - (scno - skip_cols); |
273 |
|
|
--p; |
274 |
|
|
} else { |
275 |
|
|
smp->c_sboff = ++offset_in_line; |
276 |
|
|
smp->c_scoff = 0; |
277 |
|
|
} |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
/* Do it the hard way, for historic line-folding screens. */ |
281 |
|
|
else { |
282 |
|
|
for (; offset_in_line < len; ++offset_in_line) { |
283 |
|
|
chlen = (ch = *(u_char *)p++) == '\t' && !list_tab ? |
284 |
|
|
TAB_OFF(scno) : KEY_LEN(sp, ch); |
285 |
|
|
if ((scno += chlen) < cols_per_screen) |
286 |
|
|
continue; |
287 |
|
|
scno -= cols_per_screen; |
288 |
|
|
|
289 |
|
|
/* Set cols_per_screen to 2nd and later line length. */ |
290 |
|
|
cols_per_screen = sp->cols; |
291 |
|
|
|
292 |
|
|
/* |
293 |
|
|
* If crossed the last skipped screen boundary, start |
294 |
|
|
* displaying the characters. |
295 |
|
|
*/ |
296 |
|
|
if (--skip_screens == 0) |
297 |
|
|
break; |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
/* Put starting info for this line in the cache. */ |
301 |
|
|
if (scno != 0) { |
302 |
|
|
smp->c_sboff = offset_in_line; |
303 |
|
|
smp->c_scoff = offset_in_char = chlen - scno; |
304 |
|
|
--p; |
305 |
|
|
} else { |
306 |
|
|
smp->c_sboff = ++offset_in_line; |
307 |
|
|
smp->c_scoff = 0; |
308 |
|
|
} |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
display: |
312 |
|
|
/* |
313 |
|
|
* Set the number of characters to skip before reaching the cursor |
314 |
|
|
* character. Offset by 1 and use 0 as a flag value. Vs_line is |
315 |
|
|
* called repeatedly with a valid pointer to a cursor position. |
316 |
|
|
* Don't fill anything in unless it's the right line and the right |
317 |
|
|
* character, and the right part of the character... |
318 |
|
|
*/ |
319 |
|
|
if (yp == NULL || |
320 |
|
|
smp->lno != sp->lno || sp->cno < offset_in_line || |
321 |
|
|
offset_in_line + cols_per_screen < sp->cno) { |
322 |
|
|
cno_cnt = 0; |
323 |
|
|
/* If the line is on the screen, quit. */ |
324 |
|
|
if (is_cached || no_draw) |
325 |
|
|
goto ret1; |
326 |
|
|
} else |
327 |
|
|
cno_cnt = (sp->cno - offset_in_line) + 1; |
328 |
|
|
|
329 |
|
|
ecbp = (cbp = cbuf) + sizeof(cbuf) - 1; |
330 |
|
|
|
331 |
|
|
/* This is the loop that actually displays characters. */ |
332 |
|
|
for (is_partial = 0, scno = 0; |
333 |
|
|
offset_in_line < len; ++offset_in_line, offset_in_char = 0) { |
334 |
|
|
if ((ch = *(u_char *)p++) == '\t' && !list_tab) { |
335 |
|
|
scno += chlen = TAB_OFF(scno) - offset_in_char; |
336 |
|
|
is_tab = 1; |
337 |
|
|
} else { |
338 |
|
|
scno += chlen = KEY_LEN(sp, ch) - offset_in_char; |
339 |
|
|
is_tab = 0; |
340 |
|
|
} |
341 |
|
|
|
342 |
|
|
/* |
343 |
|
|
* Only display up to the right-hand column. Set a flag if |
344 |
|
|
* the entire character wasn't displayed for use in setting |
345 |
|
|
* the cursor. If reached the end of the line, set the cache |
346 |
|
|
* info for the screen. Don't worry about there not being |
347 |
|
|
* characters to display on the next screen, its lno/off won't |
348 |
|
|
* match up in that case. |
349 |
|
|
*/ |
350 |
|
|
if (scno >= cols_per_screen) { |
351 |
|
|
if (is_tab == 1) { |
352 |
|
|
chlen -= scno - cols_per_screen; |
353 |
|
|
smp->c_ecsize = smp->c_eclen = chlen; |
354 |
|
|
scno = cols_per_screen; |
355 |
|
|
} else { |
356 |
|
|
smp->c_ecsize = chlen; |
357 |
|
|
chlen -= scno - cols_per_screen; |
358 |
|
|
smp->c_eclen = chlen; |
359 |
|
|
|
360 |
|
|
if (scno > cols_per_screen) |
361 |
|
|
is_partial = 1; |
362 |
|
|
} |
363 |
|
|
smp->c_eboff = offset_in_line; |
364 |
|
|
|
365 |
|
|
/* Terminate the loop. */ |
366 |
|
|
offset_in_line = len; |
367 |
|
|
} |
368 |
|
|
|
369 |
|
|
/* |
370 |
|
|
* If the caller wants the cursor value, and this was the |
371 |
|
|
* cursor character, set the value. There are two ways to |
372 |
|
|
* put the cursor on a character -- if it's normal display |
373 |
|
|
* mode, it goes on the last column of the character. If |
374 |
|
|
* it's input mode, it goes on the first. In normal mode, |
375 |
|
|
* set the cursor only if the entire character was displayed. |
376 |
|
|
*/ |
377 |
|
|
if (cno_cnt && |
378 |
|
|
--cno_cnt == 0 && (F_ISSET(sp, SC_TINPUT) || !is_partial)) { |
379 |
|
|
*yp = smp - HMAP; |
380 |
|
|
if (F_ISSET(sp, SC_TINPUT)) { |
381 |
|
|
if (is_partial) |
382 |
|
|
*xp = scno - smp->c_ecsize; |
383 |
|
|
else |
384 |
|
|
*xp = scno - chlen; |
385 |
|
|
} else |
386 |
|
|
*xp = scno - 1; |
387 |
|
|
if (O_ISSET(sp, O_NUMBER) && |
388 |
|
|
!F_ISSET(sp, SC_TINPUT_INFO) && skip_cols == 0) |
389 |
|
|
*xp += O_NUMBER_LENGTH; |
390 |
|
|
|
391 |
|
|
/* If the line is on the screen, quit. */ |
392 |
|
|
if (is_cached) |
393 |
|
|
goto ret1; |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
/* If the line is on the screen, don't display anything. */ |
397 |
|
|
if (is_cached) |
398 |
|
|
continue; |
399 |
|
|
|
400 |
|
|
#define FLUSH(gp, sp, cbp, cbuf) do { \ |
401 |
|
|
*(cbp) = '\0'; \ |
402 |
|
|
(void)(gp)->scr_addstr((sp), (cbuf), (cbp) - (cbuf)); \ |
403 |
|
|
(cbp) = (cbuf); \ |
404 |
|
|
} while (0) |
405 |
|
|
/* |
406 |
|
|
* Display the character. We do tab expansion here because |
407 |
|
|
* the screen interface doesn't have any way to set the tab |
408 |
|
|
* length. Note, it's theoretically possible for chlen to |
409 |
|
|
* be larger than cbuf, if the user set a impossibly large |
410 |
|
|
* tabstop. |
411 |
|
|
*/ |
412 |
|
|
if (is_tab) |
413 |
|
|
while (chlen--) { |
414 |
|
|
if (cbp >= ecbp) |
415 |
|
|
FLUSH(gp, sp, cbp, cbuf); |
416 |
|
|
*cbp++ = TABCH; |
417 |
|
|
} |
418 |
|
|
else { |
419 |
|
|
if (cbp + chlen >= ecbp) |
420 |
|
|
FLUSH(gp, sp, cbp, cbuf); |
421 |
|
|
for (kp = KEY_NAME(sp, ch) + offset_in_char; chlen--;) |
422 |
|
|
*cbp++ = *kp++; |
423 |
|
|
} |
424 |
|
|
} |
425 |
|
|
|
426 |
|
|
if (scno < cols_per_screen) { |
427 |
|
|
/* If we didn't paint the whole line, update the cache. */ |
428 |
|
|
smp->c_ecsize = smp->c_eclen = KEY_LEN(sp, ch); |
429 |
|
|
smp->c_eboff = len - 1; |
430 |
|
|
|
431 |
|
|
/* |
432 |
|
|
* If not the info/mode line, and O_LIST set, and at the |
433 |
|
|
* end of the line, and the line ended on this screen, |
434 |
|
|
* add a trailing $. |
435 |
|
|
*/ |
436 |
|
|
if (list_dollar) { |
437 |
|
|
++scno; |
438 |
|
|
|
439 |
|
|
chlen = KEY_LEN(sp, '$'); |
440 |
|
|
if (cbp + chlen >= ecbp) |
441 |
|
|
FLUSH(gp, sp, cbp, cbuf); |
442 |
|
|
for (kp = KEY_NAME(sp, '$'); chlen--;) |
443 |
|
|
*cbp++ = *kp++; |
444 |
|
|
} |
445 |
|
|
|
446 |
|
|
/* If still didn't paint the whole line, clear the rest. */ |
447 |
|
|
if (scno < cols_per_screen) |
448 |
|
|
(void)gp->scr_clrtoeol(sp); |
449 |
|
|
} |
450 |
|
|
|
451 |
|
|
/* Flush any buffered characters. */ |
452 |
|
|
if (cbp > cbuf) |
453 |
|
|
FLUSH(gp, sp, cbp, cbuf); |
454 |
|
|
|
455 |
|
|
ret1: (void)gp->scr_move(sp, oldy, oldx); |
456 |
|
|
return (0); |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
/* |
460 |
|
|
* vs_number -- |
461 |
|
|
* Repaint the numbers on all the lines. |
462 |
|
|
* |
463 |
|
|
* PUBLIC: int vs_number(SCR *); |
464 |
|
|
*/ |
465 |
|
|
int |
466 |
|
|
vs_number(SCR *sp) |
467 |
|
|
{ |
468 |
|
|
GS *gp; |
469 |
|
|
SMAP *smp; |
470 |
|
|
size_t len, oldy, oldx; |
471 |
|
|
int exist; |
472 |
|
|
char nbuf[10]; |
473 |
|
|
|
474 |
|
|
gp = sp->gp; |
475 |
|
|
|
476 |
|
|
/* No reason to do anything if we're in input mode on the info line. */ |
477 |
|
|
if (F_ISSET(sp, SC_TINPUT_INFO)) |
478 |
|
|
return (0); |
479 |
|
|
|
480 |
|
|
/* |
481 |
|
|
* Try and avoid getting the last line in the file, by getting the |
482 |
|
|
* line after the last line in the screen -- if it exists, we know |
483 |
|
|
* we have to to number all the lines in the screen. Get the one |
484 |
|
|
* after the last instead of the last, so that the info line doesn't |
485 |
|
|
* fool us. (The problem is that file_lline will lie, and tell us |
486 |
|
|
* that the info line is the last line in the file.) If that test |
487 |
|
|
* fails, we have to check each line for existence. |
488 |
|
|
*/ |
489 |
|
|
exist = db_exist(sp, TMAP->lno + 1); |
490 |
|
|
|
491 |
|
|
(void)gp->scr_cursor(sp, &oldy, &oldx); |
492 |
|
|
for (smp = HMAP; smp <= TMAP; ++smp) { |
493 |
|
|
/* Numbers are only displayed for the first screen line. */ |
494 |
|
|
if (O_ISSET(sp, O_LEFTRIGHT)) { |
495 |
|
|
if (smp->coff != 0) |
496 |
|
|
continue; |
497 |
|
|
} else |
498 |
|
|
if (smp->soff != 1) |
499 |
|
|
continue; |
500 |
|
|
|
501 |
|
|
/* |
502 |
|
|
* The first line of an empty file gets numbered, otherwise |
503 |
|
|
* number any existing line. |
504 |
|
|
*/ |
505 |
|
|
if (smp->lno != 1 && !exist && !db_exist(sp, smp->lno)) |
506 |
|
|
break; |
507 |
|
|
|
508 |
|
|
(void)gp->scr_move(sp, smp - HMAP, 0); |
509 |
|
|
len = snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, (ulong)smp->lno); |
510 |
|
|
if (len >= sizeof(nbuf)) |
511 |
|
|
len = sizeof(nbuf) - 1; |
512 |
|
|
(void)gp->scr_addstr(sp, nbuf, len); |
513 |
|
|
} |
514 |
|
|
(void)gp->scr_move(sp, oldy, oldx); |
515 |
|
|
return (0); |
516 |
|
|
} |