Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/libedit/refresh.c
39478 views
1
/* $NetBSD: refresh.c,v 1.60 2024/12/05 22:21:53 christos Exp $ */
2
3
/*-
4
* Copyright (c) 1992, 1993
5
* The Regents of the University of California. All rights reserved.
6
*
7
* This code is derived from software contributed to Berkeley by
8
* Christos Zoulas of Cornell University.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
* 3. Neither the name of the University nor the names of its contributors
19
* may be used to endorse or promote products derived from this software
20
* without specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
* SUCH DAMAGE.
33
*/
34
35
#include "config.h"
36
#if !defined(lint) && !defined(SCCSID)
37
#if 0
38
static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93";
39
#else
40
__RCSID("$NetBSD: refresh.c,v 1.60 2024/12/05 22:21:53 christos Exp $");
41
#endif
42
#endif /* not lint && not SCCSID */
43
44
/*
45
* refresh.c: Lower level screen refreshing functions
46
*/
47
#include <stdio.h>
48
#include <stdlib.h>
49
#include <string.h>
50
#include <unistd.h>
51
52
#include "el.h"
53
54
static void re_nextline(EditLine *);
55
static void re_addc(EditLine *, wint_t);
56
static void re_update_line(EditLine *, wchar_t *, wchar_t *, int);
57
static void re_insert (EditLine *, wchar_t *, int, int, wchar_t *, int);
58
static void re_delete(EditLine *, wchar_t *, int, int, int);
59
static void re_fastputc(EditLine *, wint_t);
60
static void re_clear_eol(EditLine *, int, int, int);
61
static void re__strncopy(wchar_t *, wchar_t *, size_t);
62
static void re__copy_and_pad(wchar_t *, const wchar_t *, size_t);
63
64
#ifdef DEBUG_REFRESH
65
static void re_printstr(EditLine *, const char *, wchar_t *, wchar_t *);
66
#define __F el->el_errfile
67
#define ELRE_ASSERT(a, b, c) do \
68
if (/*CONSTCOND*/ a) { \
69
(void) fprintf b; \
70
c; \
71
} \
72
while (/*CONSTCOND*/0)
73
#define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;)
74
75
/* re_printstr():
76
* Print a string on the debugging pty
77
*/
78
static void
79
re_printstr(EditLine *el, const char *str, wchar_t *f, wchar_t *t)
80
{
81
82
ELRE_DEBUG(1, (__F, "%s:\"", str));
83
while (f < t)
84
ELRE_DEBUG(1, (__F, "%c", *f++ & 0177));
85
ELRE_DEBUG(1, (__F, "\"\r\n"));
86
}
87
#else
88
#define ELRE_ASSERT(a, b, c)
89
#define ELRE_DEBUG(a, b)
90
#endif
91
92
/* re_nextline():
93
* Move to the next line or scroll
94
*/
95
static void
96
re_nextline(EditLine *el)
97
{
98
el->el_refresh.r_cursor.h = 0; /* reset it. */
99
100
/*
101
* If we would overflow (input is longer than terminal size),
102
* emulate scroll by dropping first line and shuffling the rest.
103
* We do this via pointer shuffling - it's safe in this case
104
* and we avoid memcpy().
105
*/
106
if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v) {
107
int i, lins = el->el_terminal.t_size.v;
108
wint_t *firstline = el->el_vdisplay[0];
109
110
for(i = 1; i < lins; i++)
111
el->el_vdisplay[i - 1] = el->el_vdisplay[i];
112
113
firstline[0] = '\0'; /* empty the string */
114
el->el_vdisplay[i - 1] = firstline;
115
} else
116
el->el_refresh.r_cursor.v++;
117
118
ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_terminal.t_size.v,
119
(__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
120
el->el_refresh.r_cursor.v, el->el_terminal.t_size.v),
121
abort());
122
}
123
124
/* re_addc():
125
* Draw c, expanding tabs, control chars etc.
126
*/
127
static void
128
re_addc(EditLine *el, wint_t c)
129
{
130
switch (ct_chr_class(c)) {
131
case CHTYPE_TAB: /* expand the tab */
132
for (;;) {
133
re_putc(el, ' ', 1);
134
if ((el->el_refresh.r_cursor.h & 07) == 0)
135
break; /* go until tab stop */
136
}
137
break;
138
case CHTYPE_NL: {
139
int oldv = el->el_refresh.r_cursor.v;
140
re_putc(el, '\0', 0); /* assure end of line */
141
if (oldv == el->el_refresh.r_cursor.v) /* XXX */
142
re_nextline(el);
143
break;
144
}
145
case CHTYPE_PRINT:
146
re_putc(el, c, 1);
147
break;
148
default: {
149
wchar_t visbuf[VISUAL_WIDTH_MAX];
150
ssize_t i, n =
151
ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
152
for (i = 0; n-- > 0; ++i)
153
re_putc(el, visbuf[i], 1);
154
break;
155
}
156
}
157
}
158
159
/* re_putliteral():
160
* Place the literal string given
161
*/
162
libedit_private void
163
re_putliteral(EditLine *el, const wchar_t *begin, const wchar_t *end)
164
{
165
coord_t *cur = &el->el_refresh.r_cursor;
166
wint_t c;
167
int sizeh = el->el_terminal.t_size.h;
168
int i, w;
169
170
c = literal_add(el, begin, end, &w);
171
if (c == 0 || w < 0)
172
return;
173
el->el_vdisplay[cur->v][cur->h] = c;
174
175
i = w;
176
if (i > sizeh - cur->h) /* avoid overflow */
177
i = sizeh - cur->h;
178
while (--i > 0)
179
el->el_vdisplay[cur->v][cur->h + i] = MB_FILL_CHAR;
180
181
cur->h += w ? w : 1;
182
if (cur->h >= sizeh) {
183
/* assure end of line */
184
el->el_vdisplay[cur->v][sizeh] = '\0';
185
re_nextline(el);
186
}
187
}
188
189
/* re_putc():
190
* Draw the character given
191
*/
192
libedit_private void
193
re_putc(EditLine *el, wint_t c, int shift)
194
{
195
coord_t *cur = &el->el_refresh.r_cursor;
196
int i, w = wcwidth(c);
197
int sizeh = el->el_terminal.t_size.h;
198
199
ELRE_DEBUG(1, (__F, "printing %5x '%lc'\r\n", c, c));
200
if (w == -1)
201
w = 0;
202
203
while (shift && (cur->h + w > sizeh))
204
re_putc(el, ' ', 1);
205
206
el->el_vdisplay[cur->v][cur->h] = c;
207
/* assumes !shift is only used for single-column chars */
208
i = w;
209
while (--i > 0)
210
el->el_vdisplay[cur->v][cur->h + i] = MB_FILL_CHAR;
211
212
if (!shift)
213
return;
214
215
cur->h += w ? w : 1; /* advance to next place */
216
if (cur->h >= sizeh) {
217
/* assure end of line */
218
el->el_vdisplay[cur->v][sizeh] = '\0';
219
re_nextline(el);
220
}
221
}
222
223
224
/* re_refresh():
225
* draws the new virtual screen image from the current input
226
* line, then goes line-by-line changing the real image to the new
227
* virtual image. The routine to re-draw a line can be replaced
228
* easily in hopes of a smarter one being placed there.
229
*/
230
libedit_private void
231
re_refresh(EditLine *el)
232
{
233
int i, rhdiff;
234
wchar_t *cp, *st;
235
coord_t cur;
236
#ifdef notyet
237
size_t termsz;
238
#endif
239
240
ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%ls:\r\n",
241
el->el_line.buffer));
242
243
literal_clear(el);
244
/* reset the Drawing cursor */
245
el->el_refresh.r_cursor.h = 0;
246
el->el_refresh.r_cursor.v = 0;
247
248
terminal_move_to_char(el, 0);
249
250
/* temporarily draw rprompt to calculate its size */
251
prompt_print(el, EL_RPROMPT);
252
253
/* reset the Drawing cursor */
254
el->el_refresh.r_cursor.h = 0;
255
el->el_refresh.r_cursor.v = 0;
256
257
if (el->el_line.cursor >= el->el_line.lastchar) {
258
if (el->el_map.current == el->el_map.alt
259
&& el->el_line.lastchar != el->el_line.buffer)
260
el->el_line.cursor = el->el_line.lastchar - 1;
261
else
262
el->el_line.cursor = el->el_line.lastchar;
263
}
264
265
cur.h = -1; /* set flag in case I'm not set */
266
cur.v = 0;
267
268
prompt_print(el, EL_PROMPT);
269
270
/* draw the current input buffer */
271
#if notyet
272
termsz = el->el_terminal.t_size.h * el->el_terminal.t_size.v;
273
if (el->el_line.lastchar - el->el_line.buffer > termsz) {
274
/*
275
* If line is longer than terminal, process only part
276
* of line which would influence display.
277
*/
278
size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
279
280
st = el->el_line.lastchar - rem
281
- (termsz - (((rem / el->el_terminal.t_size.v) - 1)
282
* el->el_terminal.t_size.v));
283
} else
284
#endif
285
st = el->el_line.buffer;
286
287
for (cp = st; cp < el->el_line.lastchar; cp++) {
288
if (cp == el->el_line.cursor) {
289
int w = wcwidth(*cp);
290
/* save for later */
291
cur.h = el->el_refresh.r_cursor.h;
292
cur.v = el->el_refresh.r_cursor.v;
293
/* handle being at a linebroken doublewidth char */
294
if (w > 1 && el->el_refresh.r_cursor.h + w >
295
el->el_terminal.t_size.h) {
296
cur.h = 0;
297
cur.v++;
298
}
299
}
300
re_addc(el, *cp);
301
}
302
303
if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */
304
cur.h = el->el_refresh.r_cursor.h;
305
cur.v = el->el_refresh.r_cursor.v;
306
}
307
rhdiff = el->el_terminal.t_size.h - el->el_refresh.r_cursor.h -
308
el->el_rprompt.p_pos.h;
309
if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
310
!el->el_refresh.r_cursor.v && rhdiff > 1) {
311
/*
312
* have a right-hand side prompt that will fit
313
* on the end of the first line with at least
314
* one character gap to the input buffer.
315
*/
316
while (--rhdiff > 0) /* pad out with spaces */
317
re_putc(el, ' ', 1);
318
prompt_print(el, EL_RPROMPT);
319
} else {
320
el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */
321
el->el_rprompt.p_pos.v = 0;
322
}
323
324
re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */
325
326
el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
327
328
ELRE_DEBUG(1, (__F,
329
"term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
330
el->el_terminal.t_size.h, el->el_refresh.r_cursor.h,
331
el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0],
332
&el->el_scratch)));
333
334
ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv));
335
for (i = 0; i <= el->el_refresh.r_newcv; i++) {
336
/* NOTE THAT re_update_line MAY CHANGE el_display[i] */
337
re_update_line(el, (wchar_t *)el->el_display[i],
338
(wchar_t *)el->el_vdisplay[i], i);
339
340
/*
341
* Copy the new line to be the current one, and pad out with
342
* spaces to the full width of the terminal so that if we try
343
* moving the cursor by writing the character that is at the
344
* end of the screen line, it won't be a NUL or some old
345
* leftover stuff.
346
*/
347
re__copy_and_pad((wchar_t *)el->el_display[i],
348
(wchar_t *)el->el_vdisplay[i],
349
(size_t) el->el_terminal.t_size.h);
350
}
351
ELRE_DEBUG(1, (__F,
352
"\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
353
el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i));
354
355
if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
356
for (; i <= el->el_refresh.r_oldcv; i++) {
357
terminal_move_to_line(el, i);
358
terminal_move_to_char(el, 0);
359
/* This wcslen should be safe even with MB_FILL_CHARs */
360
terminal_clear_EOL(el,
361
(int) wcslen((const wchar_t *)el->el_display[i]));
362
#ifdef DEBUG_REFRESH
363
terminal_overwrite(el, L"C\b", 2);
364
#endif /* DEBUG_REFRESH */
365
el->el_display[i][0] = '\0';
366
}
367
368
el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */
369
ELRE_DEBUG(1, (__F,
370
"\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
371
el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
372
cur.h, cur.v));
373
terminal_move_to_line(el, cur.v); /* go to where the cursor is */
374
terminal_move_to_char(el, cur.h);
375
}
376
377
378
/* re_goto_bottom():
379
* used to go to last used screen line
380
*/
381
libedit_private void
382
re_goto_bottom(EditLine *el)
383
{
384
385
terminal_move_to_line(el, el->el_refresh.r_oldcv);
386
terminal__putc(el, '\n');
387
re_clear_display(el);
388
terminal__flush(el);
389
}
390
391
392
/* re_insert():
393
* insert num characters of s into d (in front of the character)
394
* at dat, maximum length of d is dlen
395
*/
396
static void
397
/*ARGSUSED*/
398
re_insert(EditLine *el __attribute__((__unused__)),
399
wchar_t *d, int dat, int dlen, wchar_t *s, int num)
400
{
401
wchar_t *a, *b;
402
403
if (num <= 0)
404
return;
405
if (num > dlen - dat)
406
num = dlen - dat;
407
408
ELRE_DEBUG(1,
409
(__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
410
num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
411
ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
412
&el->el_scratch)));
413
414
/* open up the space for num chars */
415
if (num > 0) {
416
b = d + dlen - 1;
417
a = b - num;
418
while (a >= &d[dat])
419
*b-- = *a--;
420
d[dlen] = '\0'; /* just in case */
421
}
422
423
ELRE_DEBUG(1, (__F,
424
"re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
425
num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
426
ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
427
&el->el_scratch)));
428
429
/* copy the characters */
430
for (a = d + dat; (a < d + dlen) && (num > 0); num--)
431
*a++ = *s++;
432
433
#ifdef notyet
434
/* ct_encode_string() uses a static buffer, so we can't conveniently
435
* encode both d & s here */
436
ELRE_DEBUG(1,
437
(__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
438
num, dat, dlen, d, s));
439
ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s));
440
#endif
441
}
442
443
444
/* re_delete():
445
* delete num characters d at dat, maximum length of d is dlen
446
*/
447
static void
448
/*ARGSUSED*/
449
re_delete(EditLine *el __attribute__((__unused__)),
450
wchar_t *d, int dat, int dlen, int num)
451
{
452
wchar_t *a, *b;
453
454
if (num <= 0)
455
return;
456
if (dat + num >= dlen) {
457
d[dat] = '\0';
458
return;
459
}
460
ELRE_DEBUG(1,
461
(__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
462
num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
463
464
/* open up the space for num chars */
465
if (num > 0) {
466
b = d + dat;
467
a = b + num;
468
while (a < &d[dlen])
469
*b++ = *a++;
470
d[dlen] = '\0'; /* just in case */
471
}
472
ELRE_DEBUG(1,
473
(__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
474
num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
475
}
476
477
478
/* re__strncopy():
479
* Like strncpy without padding.
480
*/
481
static void
482
re__strncopy(wchar_t *a, wchar_t *b, size_t n)
483
{
484
485
while (n-- && *b)
486
*a++ = *b++;
487
}
488
489
/* re_clear_eol():
490
* Find the number of characters we need to clear till the end of line
491
* in order to make sure that we have cleared the previous contents of
492
* the line. fx and sx is the number of characters inserted or deleted
493
* in the first or second diff, diff is the difference between the
494
* number of characters between the new and old line.
495
*/
496
static void
497
re_clear_eol(EditLine *el, int fx, int sx, int diff)
498
{
499
500
ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n",
501
sx, fx, diff));
502
503
if (fx < 0)
504
fx = -fx;
505
if (sx < 0)
506
sx = -sx;
507
if (fx > diff)
508
diff = fx;
509
if (sx > diff)
510
diff = sx;
511
512
ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff));
513
terminal_clear_EOL(el, diff);
514
}
515
516
/*****************************************************************
517
re_update_line() is based on finding the middle difference of each line
518
on the screen; vis:
519
520
/old first difference
521
/beginning of line | /old last same /old EOL
522
v v v v
523
old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
524
new: eddie> Oh, my little buggy says to me, as lurgid as
525
^ ^ ^ ^
526
\beginning of line | \new last same \new end of line
527
\new first difference
528
529
all are character pointers for the sake of speed. Special cases for
530
no differences, as well as for end of line additions must be handled.
531
**************************************************************** */
532
533
/* Minimum at which doing an insert it "worth it". This should be about
534
* half the "cost" of going into insert mode, inserting a character, and
535
* going back out. This should really be calculated from the termcap
536
* data... For the moment, a good number for ANSI terminals.
537
*/
538
#define MIN_END_KEEP 4
539
540
static void
541
re_update_line(EditLine *el, wchar_t *old, wchar_t *new, int i)
542
{
543
wchar_t *o, *n, *p, c;
544
wchar_t *ofd, *ols, *oe, *nfd, *nls, *ne;
545
wchar_t *osb, *ose, *nsb, *nse;
546
int fx, sx;
547
size_t len;
548
549
/*
550
* find first diff
551
*/
552
for (o = old, n = new; *o && (*o == *n); o++, n++)
553
continue;
554
ofd = o;
555
nfd = n;
556
557
/*
558
* Find the end of both old and new
559
*/
560
while (*o)
561
o++;
562
/*
563
* Remove any trailing blanks off of the end, being careful not to
564
* back up past the beginning.
565
*/
566
while (ofd < o) {
567
if (o[-1] != ' ')
568
break;
569
o--;
570
}
571
oe = o;
572
*oe = '\0';
573
574
while (*n)
575
n++;
576
577
/* remove blanks from end of new */
578
while (nfd < n) {
579
if (n[-1] != ' ')
580
break;
581
n--;
582
}
583
ne = n;
584
*ne = '\0';
585
586
/*
587
* if no diff, continue to next line of redraw
588
*/
589
if (*ofd == '\0' && *nfd == '\0') {
590
ELRE_DEBUG(1, (__F, "no difference.\r\n"));
591
return;
592
}
593
/*
594
* find last same pointer
595
*/
596
while ((o > ofd) && (n > nfd) && (*--o == *--n))
597
continue;
598
ols = ++o;
599
nls = ++n;
600
601
/*
602
* find same beginning and same end
603
*/
604
osb = ols;
605
nsb = nls;
606
ose = ols;
607
nse = nls;
608
609
/*
610
* case 1: insert: scan from nfd to nls looking for *ofd
611
*/
612
if (*ofd) {
613
for (c = *ofd, n = nfd; n < nls; n++) {
614
if (c == *n) {
615
for (o = ofd, p = n;
616
p < nls && o < ols && *o == *p;
617
o++, p++)
618
continue;
619
/*
620
* if the new match is longer and it's worth
621
* keeping, then we take it
622
*/
623
if (((nse - nsb) < (p - n)) &&
624
(2 * (p - n) > n - nfd)) {
625
nsb = n;
626
nse = p;
627
osb = ofd;
628
ose = o;
629
}
630
}
631
}
632
}
633
/*
634
* case 2: delete: scan from ofd to ols looking for *nfd
635
*/
636
if (*nfd) {
637
for (c = *nfd, o = ofd; o < ols; o++) {
638
if (c == *o) {
639
for (n = nfd, p = o;
640
p < ols && n < nls && *p == *n;
641
p++, n++)
642
continue;
643
/*
644
* if the new match is longer and it's worth
645
* keeping, then we take it
646
*/
647
if (((ose - osb) < (p - o)) &&
648
(2 * (p - o) > o - ofd)) {
649
nsb = nfd;
650
nse = n;
651
osb = o;
652
ose = p;
653
}
654
}
655
}
656
}
657
/*
658
* Pragmatics I: If old trailing whitespace or not enough characters to
659
* save to be worth it, then don't save the last same info.
660
*/
661
if ((oe - ols) < MIN_END_KEEP) {
662
ols = oe;
663
nls = ne;
664
}
665
/*
666
* Pragmatics II: if the terminal isn't smart enough, make the data
667
* dumber so the smart update doesn't try anything fancy
668
*/
669
670
/*
671
* fx is the number of characters we need to insert/delete: in the
672
* beginning to bring the two same begins together
673
*/
674
fx = (int)((nsb - nfd) - (osb - ofd));
675
/*
676
* sx is the number of characters we need to insert/delete: in the
677
* end to bring the two same last parts together
678
*/
679
sx = (int)((nls - nse) - (ols - ose));
680
681
if (!EL_CAN_INSERT) {
682
if (fx > 0) {
683
osb = ols;
684
ose = ols;
685
nsb = nls;
686
nse = nls;
687
}
688
if (sx > 0) {
689
ols = oe;
690
nls = ne;
691
}
692
if ((ols - ofd) < (nls - nfd)) {
693
ols = oe;
694
nls = ne;
695
}
696
}
697
if (!EL_CAN_DELETE) {
698
if (fx < 0) {
699
osb = ols;
700
ose = ols;
701
nsb = nls;
702
nse = nls;
703
}
704
if (sx < 0) {
705
ols = oe;
706
nls = ne;
707
}
708
if ((ols - ofd) > (nls - nfd)) {
709
ols = oe;
710
nls = ne;
711
}
712
}
713
/*
714
* Pragmatics III: make sure the middle shifted pointers are correct if
715
* they don't point to anything (we may have moved ols or nls).
716
*/
717
/* if the change isn't worth it, don't bother */
718
/* was: if (osb == ose) */
719
if ((ose - osb) < MIN_END_KEEP) {
720
osb = ols;
721
ose = ols;
722
nsb = nls;
723
nse = nls;
724
}
725
/*
726
* Now that we are done with pragmatics we recompute fx, sx
727
*/
728
fx = (int)((nsb - nfd) - (osb - ofd));
729
sx = (int)((nls - nse) - (ols - ose));
730
731
ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
732
ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n",
733
ofd - old, osb - old, ose - old, ols - old, oe - old));
734
ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n",
735
nfd - new, nsb - new, nse - new, nls - new, ne - new));
736
ELRE_DEBUG(1, (__F,
737
"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
738
ELRE_DEBUG(1, (__F,
739
"xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
740
#ifdef DEBUG_REFRESH
741
re_printstr(el, "old- oe", old, oe);
742
re_printstr(el, "new- ne", new, ne);
743
re_printstr(el, "old-ofd", old, ofd);
744
re_printstr(el, "new-nfd", new, nfd);
745
re_printstr(el, "ofd-osb", ofd, osb);
746
re_printstr(el, "nfd-nsb", nfd, nsb);
747
re_printstr(el, "osb-ose", osb, ose);
748
re_printstr(el, "nsb-nse", nsb, nse);
749
re_printstr(el, "ose-ols", ose, ols);
750
re_printstr(el, "nse-nls", nse, nls);
751
re_printstr(el, "ols- oe", ols, oe);
752
re_printstr(el, "nls- ne", nls, ne);
753
#endif /* DEBUG_REFRESH */
754
755
/*
756
* el_cursor.v to this line i MUST be in this routine so that if we
757
* don't have to change the line, we don't move to it. el_cursor.h to
758
* first diff char
759
*/
760
terminal_move_to_line(el, i);
761
762
/*
763
* at this point we have something like this:
764
*
765
* /old /ofd /osb /ose /ols /oe
766
* v.....................v v..................v v........v
767
* eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
768
* eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
769
* ^.....................^ ^..................^ ^........^
770
* \new \nfd \nsb \nse \nls \ne
771
*
772
* fx is the difference in length between the chars between nfd and
773
* nsb, and the chars between ofd and osb, and is thus the number of
774
* characters to delete if < 0 (new is shorter than old, as above),
775
* or insert (new is longer than short).
776
*
777
* sx is the same for the second differences.
778
*/
779
780
/*
781
* if we have a net insert on the first difference, AND inserting the
782
* net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
783
* character (which is ne if nls != ne, otherwise is nse) off the edge
784
* of the screen (el->el_terminal.t_size.h) else we do the deletes first
785
* so that we keep everything we need to.
786
*/
787
788
/*
789
* if the last same is the same like the end, there is no last same
790
* part, otherwise we want to keep the last same part set p to the
791
* last useful old character
792
*/
793
p = (ols != oe) ? oe : ose;
794
795
/*
796
* if (There is a diffence in the beginning) && (we need to insert
797
* characters) && (the number of characters to insert is less than
798
* the term width)
799
* We need to do an insert!
800
* else if (we need to delete characters)
801
* We need to delete characters!
802
* else
803
* No insert or delete
804
*/
805
if ((nsb != nfd) && fx > 0 &&
806
((p - old) + fx <= el->el_terminal.t_size.h)) {
807
ELRE_DEBUG(1,
808
(__F, "first diff insert at %td...\r\n", nfd - new));
809
/*
810
* Move to the first char to insert, where the first diff is.
811
*/
812
terminal_move_to_char(el, (int)(nfd - new));
813
/*
814
* Check if we have stuff to keep at end
815
*/
816
if (nsb != ne) {
817
ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
818
/*
819
* insert fx chars of new starting at nfd
820
*/
821
if (fx > 0) {
822
ELRE_DEBUG(!EL_CAN_INSERT, (__F,
823
"ERROR: cannot insert in early first diff\n"));
824
terminal_insertwrite(el, nfd, fx);
825
re_insert(el, old, (int)(ofd - old),
826
el->el_terminal.t_size.h, nfd, fx);
827
}
828
/*
829
* write (nsb-nfd) - fx chars of new starting at
830
* (nfd + fx)
831
*/
832
len = (size_t) ((nsb - nfd) - fx);
833
terminal_overwrite(el, (nfd + fx), len);
834
re__strncopy(ofd + fx, nfd + fx, len);
835
} else {
836
ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
837
len = (size_t)(nsb - nfd);
838
terminal_overwrite(el, nfd, len);
839
re__strncopy(ofd, nfd, len);
840
/*
841
* Done
842
*/
843
return;
844
}
845
} else if (fx < 0) {
846
ELRE_DEBUG(1,
847
(__F, "first diff delete at %td...\r\n", ofd - old));
848
/*
849
* move to the first char to delete where the first diff is
850
*/
851
terminal_move_to_char(el, (int)(ofd - old));
852
/*
853
* Check if we have stuff to save
854
*/
855
if (osb != oe) {
856
ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
857
/*
858
* fx is less than zero *always* here but we check
859
* for code symmetry
860
*/
861
if (fx < 0) {
862
ELRE_DEBUG(!EL_CAN_DELETE, (__F,
863
"ERROR: cannot delete in first diff\n"));
864
terminal_deletechars(el, -fx);
865
re_delete(el, old, (int)(ofd - old),
866
el->el_terminal.t_size.h, -fx);
867
}
868
/*
869
* write (nsb-nfd) chars of new starting at nfd
870
*/
871
len = (size_t) (nsb - nfd);
872
terminal_overwrite(el, nfd, len);
873
re__strncopy(ofd, nfd, len);
874
875
} else {
876
ELRE_DEBUG(1, (__F,
877
"but with nothing left to save\r\n"));
878
/*
879
* write (nsb-nfd) chars of new starting at nfd
880
*/
881
terminal_overwrite(el, nfd, (size_t)(nsb - nfd));
882
re_clear_eol(el, fx, sx,
883
(int)((oe - old) - (ne - new)));
884
/*
885
* Done
886
*/
887
return;
888
}
889
} else
890
fx = 0;
891
892
if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) {
893
ELRE_DEBUG(1, (__F,
894
"second diff delete at %td...\r\n", (ose - old) + fx));
895
/*
896
* Check if we have stuff to delete
897
*/
898
/*
899
* fx is the number of characters inserted (+) or deleted (-)
900
*/
901
902
terminal_move_to_char(el, (int)((ose - old) + fx));
903
/*
904
* Check if we have stuff to save
905
*/
906
if (ols != oe) {
907
ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
908
/*
909
* Again a duplicate test.
910
*/
911
if (sx < 0) {
912
ELRE_DEBUG(!EL_CAN_DELETE, (__F,
913
"ERROR: cannot delete in second diff\n"));
914
terminal_deletechars(el, -sx);
915
}
916
/*
917
* write (nls-nse) chars of new starting at nse
918
*/
919
terminal_overwrite(el, nse, (size_t)(nls - nse));
920
} else {
921
ELRE_DEBUG(1, (__F,
922
"but with nothing left to save\r\n"));
923
terminal_overwrite(el, nse, (size_t)(nls - nse));
924
re_clear_eol(el, fx, sx,
925
(int)((oe - old) - (ne - new)));
926
}
927
}
928
/*
929
* if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
930
*/
931
if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
932
ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n",
933
nfd - new));
934
935
terminal_move_to_char(el, (int)(nfd - new));
936
/*
937
* Check if we have stuff to keep at the end
938
*/
939
if (nsb != ne) {
940
ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
941
/*
942
* We have to recalculate fx here because we set it
943
* to zero above as a flag saying that we hadn't done
944
* an early first insert.
945
*/
946
fx = (int)((nsb - nfd) - (osb - ofd));
947
if (fx > 0) {
948
/*
949
* insert fx chars of new starting at nfd
950
*/
951
ELRE_DEBUG(!EL_CAN_INSERT, (__F,
952
"ERROR: cannot insert in late first diff\n"));
953
terminal_insertwrite(el, nfd, fx);
954
re_insert(el, old, (int)(ofd - old),
955
el->el_terminal.t_size.h, nfd, fx);
956
}
957
/*
958
* write (nsb-nfd) - fx chars of new starting at
959
* (nfd + fx)
960
*/
961
len = (size_t) ((nsb - nfd) - fx);
962
terminal_overwrite(el, (nfd + fx), len);
963
re__strncopy(ofd + fx, nfd + fx, len);
964
} else {
965
ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
966
len = (size_t) (nsb - nfd);
967
terminal_overwrite(el, nfd, len);
968
re__strncopy(ofd, nfd, len);
969
}
970
}
971
/*
972
* line is now NEW up to nse
973
*/
974
if (sx >= 0) {
975
ELRE_DEBUG(1, (__F,
976
"second diff insert at %d...\r\n", (int)(nse - new)));
977
terminal_move_to_char(el, (int)(nse - new));
978
if (ols != oe) {
979
ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
980
if (sx > 0) {
981
/* insert sx chars of new starting at nse */
982
ELRE_DEBUG(!EL_CAN_INSERT, (__F,
983
"ERROR: cannot insert in second diff\n"));
984
terminal_insertwrite(el, nse, sx);
985
}
986
/*
987
* write (nls-nse) - sx chars of new starting at
988
* (nse + sx)
989
*/
990
terminal_overwrite(el, (nse + sx),
991
(size_t)((nls - nse) - sx));
992
} else {
993
ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
994
terminal_overwrite(el, nse, (size_t)(nls - nse));
995
996
/*
997
* No need to do a clear-to-end here because we were
998
* doing a second insert, so we will have over
999
* written all of the old string.
1000
*/
1001
}
1002
}
1003
ELRE_DEBUG(1, (__F, "done.\r\n"));
1004
}
1005
1006
1007
/* re__copy_and_pad():
1008
* Copy string and pad with spaces
1009
*/
1010
static void
1011
re__copy_and_pad(wchar_t *dst, const wchar_t *src, size_t width)
1012
{
1013
size_t i;
1014
1015
for (i = 0; i < width; i++) {
1016
if (*src == '\0')
1017
break;
1018
*dst++ = *src++;
1019
}
1020
1021
for (; i < width; i++)
1022
*dst++ = ' ';
1023
1024
*dst = '\0';
1025
}
1026
1027
1028
/* re_refresh_cursor():
1029
* Move to the new cursor position
1030
*/
1031
libedit_private void
1032
re_refresh_cursor(EditLine *el)
1033
{
1034
wchar_t *cp;
1035
int h, v, th, w;
1036
1037
if (el->el_line.cursor >= el->el_line.lastchar) {
1038
if (el->el_map.current == el->el_map.alt
1039
&& el->el_line.lastchar != el->el_line.buffer)
1040
el->el_line.cursor = el->el_line.lastchar - 1;
1041
else
1042
el->el_line.cursor = el->el_line.lastchar;
1043
}
1044
1045
/* first we must find where the cursor is... */
1046
h = el->el_prompt.p_pos.h;
1047
v = el->el_prompt.p_pos.v;
1048
th = el->el_terminal.t_size.h; /* optimize for speed */
1049
1050
/* do input buffer to el->el_line.cursor */
1051
for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
1052
switch (ct_chr_class(*cp)) {
1053
case CHTYPE_NL: /* handle newline in data part too */
1054
h = 0;
1055
v++;
1056
break;
1057
case CHTYPE_TAB: /* if a tab, to next tab stop */
1058
while (++h & 07)
1059
continue;
1060
break;
1061
default:
1062
w = wcwidth(*cp);
1063
if (w > 1 && h + w > th) { /* won't fit on line */
1064
h = 0;
1065
v++;
1066
}
1067
h += ct_visual_width(*cp);
1068
break;
1069
}
1070
1071
if (h >= th) { /* check, extra long tabs picked up here also */
1072
h -= th;
1073
v++;
1074
}
1075
}
1076
/* if we have a next character, and it's a doublewidth one, we need to
1077
* check whether we need to linebreak for it to fit */
1078
if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1)
1079
if (h + w > th) {
1080
h = 0;
1081
v++;
1082
}
1083
1084
/* now go there */
1085
terminal_move_to_line(el, v);
1086
terminal_move_to_char(el, h);
1087
terminal__flush(el);
1088
}
1089
1090
1091
/* re_fastputc():
1092
* Add a character fast.
1093
*/
1094
static void
1095
re_fastputc(EditLine *el, wint_t c)
1096
{
1097
wint_t *lastline;
1098
int w;
1099
1100
w = wcwidth(c);
1101
while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h)
1102
re_fastputc(el, ' ');
1103
1104
terminal__putc(el, c);
1105
el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1106
while (--w > 0)
1107
el->el_display[el->el_cursor.v][el->el_cursor.h++]
1108
= MB_FILL_CHAR;
1109
1110
if (el->el_cursor.h >= el->el_terminal.t_size.h) {
1111
/* if we must overflow */
1112
el->el_cursor.h = 0;
1113
1114
/*
1115
* If we would overflow (input is longer than terminal size),
1116
* emulate scroll by dropping first line and shuffling the rest.
1117
* We do this via pointer shuffling - it's safe in this case
1118
* and we avoid memcpy().
1119
*/
1120
if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) {
1121
int i, lins = el->el_terminal.t_size.v;
1122
1123
lastline = el->el_display[0];
1124
for(i = 1; i < lins; i++)
1125
el->el_display[i - 1] = el->el_display[i];
1126
1127
el->el_display[i - 1] = lastline;
1128
} else {
1129
el->el_cursor.v++;
1130
lastline = el->el_display[++el->el_refresh.r_oldcv];
1131
}
1132
re__copy_and_pad((wchar_t *)lastline, L"",
1133
(size_t)el->el_terminal.t_size.h);
1134
1135
if (EL_HAS_AUTO_MARGINS) {
1136
if (EL_HAS_MAGIC_MARGINS) {
1137
terminal__putc(el, ' ');
1138
terminal__putc(el, '\b');
1139
}
1140
} else {
1141
terminal__putc(el, '\r');
1142
terminal__putc(el, '\n');
1143
}
1144
}
1145
}
1146
1147
1148
/* re_fastaddc():
1149
* we added just one char, handle it fast.
1150
* Assumes that screen cursor == real cursor
1151
*/
1152
libedit_private void
1153
re_fastaddc(EditLine *el)
1154
{
1155
wchar_t c;
1156
int rhdiff;
1157
1158
if (el->el_line.cursor == el->el_line.buffer) {
1159
re_refresh(el);
1160
return;
1161
}
1162
c = el->el_line.cursor[-1];
1163
1164
if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1165
re_refresh(el); /* too hard to handle */
1166
return;
1167
}
1168
rhdiff = el->el_terminal.t_size.h - el->el_cursor.h -
1169
el->el_rprompt.p_pos.h;
1170
if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1171
re_refresh(el); /* clear out rprompt if less than 1 char gap */
1172
return;
1173
} /* else (only do at end of line, no TAB) */
1174
switch (ct_chr_class(c)) {
1175
case CHTYPE_TAB: /* already handled, should never happen here */
1176
break;
1177
case CHTYPE_NL:
1178
case CHTYPE_PRINT:
1179
re_fastputc(el, c);
1180
break;
1181
case CHTYPE_ASCIICTL:
1182
case CHTYPE_NONPRINT: {
1183
wchar_t visbuf[VISUAL_WIDTH_MAX];
1184
ssize_t i, n =
1185
ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
1186
for (i = 0; n-- > 0; ++i)
1187
re_fastputc(el, visbuf[i]);
1188
break;
1189
}
1190
}
1191
terminal__flush(el);
1192
}
1193
1194
1195
/* re_clear_display():
1196
* clear the screen buffers so that new new prompt starts fresh.
1197
*/
1198
libedit_private void
1199
re_clear_display(EditLine *el)
1200
{
1201
int i;
1202
1203
el->el_cursor.v = 0;
1204
el->el_cursor.h = 0;
1205
for (i = 0; i < el->el_terminal.t_size.v; i++)
1206
el->el_display[i][0] = '\0';
1207
el->el_refresh.r_oldcv = 0;
1208
}
1209
1210
1211
/* re_clear_lines():
1212
* Make sure all lines are *really* blank
1213
*/
1214
libedit_private void
1215
re_clear_lines(EditLine *el)
1216
{
1217
1218
if (EL_CAN_CEOL) {
1219
int i;
1220
for (i = el->el_refresh.r_oldcv; i >= 0; i--) {
1221
/* for each line on the screen */
1222
terminal_move_to_line(el, i);
1223
terminal_move_to_char(el, 0);
1224
terminal_clear_EOL(el, el->el_terminal.t_size.h);
1225
}
1226
} else {
1227
terminal_move_to_line(el, el->el_refresh.r_oldcv);
1228
/* go to last line */
1229
terminal__putc(el, '\r'); /* go to BOL */
1230
terminal__putc(el, '\n'); /* go to new line */
1231
}
1232
}
1233
1234