GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libedit/refresh.c Lines: 6 324 1.9 %
Date: 2017-11-07 Branches: 2 264 0.8 %

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