1 /* @(#)screen.c	1.42 18/08/27 Copyright 1984-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)screen.c	1.42 18/08/27 Copyright 1984-2018 J. Schilling";
6 #endif
7 /*
8  *	Screen update functions for VED
9  *
10  *	Copyright (c) 1984-2018 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 /*
27  * The functions below are responsible for the fast screen update of VED.
28  * Main implementation goal was to make the screen uptate fast, ergonomic
29  * and free of jerks. The screen update of vi is not aceptable since
30  * inserting line by line makes the screen hard to read while scrolling.
31  * The screen update of emacs shows you jerking status lines and for that
32  * reason is hard to read too.
33  *
34  * If the article from James Gosling (ACM Sig-Plan Notices 1981 vol 16 p123-129
35  * still applies to EMACS, then its screen update method differs from the
36  * update method used by VED. Emacs tries to compute the updates for more
37  * than one change while VED only looks for one changed spot. However, the
38  * visible behaviour on screen looks very similar.
39  *
40  * Newer versions of 'emacs' try to imitate the basic screen update behaviour
41  * of VED by doing block insert lines, but are slower (emacs spends 4 times
42  * the CPU time in its equivalent screen update package than VED needs).
43  * Take extrame care when doing any modifications on this module.
44  * The basic functionality has not been changed since 1986. Although
45  * many minor changes (markwrap and some cursor positioning problems)
46  * have been done to make it behave more correctly.
47  *
48  * The basic idea is to use no retained memory to compare the current and
49  * the new screen but to try to compute the visual efects 'on the fly'.
50  * This allows to resize the screen to any size without any problems.
51  * To be able to compute sizes, a character string table and a corresponding
52  * character width table is used. VED may deal with lines that are even longer
53  * than the screen if folded and handles printing strings of arbitrary
54  * length for each character. To be able to do this, the routine typerest()
55  * will type any un-typed rest of a character string if there is the need to
56  * add charecters to the end of the screen because of a scroll up or
57  * delete operation.
58  * Typescn() is the interface function that may be used to do partial
59  * screen updates, typescreen() will handle to retype the whole screen
60  * unconditionally. Typescn() is only an internal interface for either
61  * dispup() or typescreen(). Dispup() is the external high level interface
62  * that takes care of doing optimizations like insert char, delete char
63  * and similar.
64  *
65  * Exported functions:
66  *
67  *	update		- Update the display for the result of a cursor
68  *			  movement. This may result in the need for an update
69  *			  of the screen content too.
70  *	setwindow	- Adjust the current window for optimal cursor position
71  *	newwindow	- Adjust the current window for optimal cursor position
72  *			  and retype the screen.
73  *	getindent	- Return visible indentation of current line,
74  *			  used by autoindent functionality.
75  *	setpos		- Re-compute the actual cursor position.
76  *	setcursor	- Set HW-cursor to 'cursor' position value.
77  *	countpos	- Count visible position difference for two byte
78  *			  offset positions in buffer.
79  *	findcol		- Find the place on current line that is at a specific
80  *			  visible column.
81  *	dispup		- Update display as a result of an insert or delete
82  *			  operation.
83  *	realvp		- Return 'real' vertial cursor position.
84  *	realhp		- Return 'real' horizontal cursor position.
85  *	typescreen	- unconditionally re-type the whole screen or
86  *			  parts of it
87  *
88  * Edit() calls update() after each action. Only functions that insert or
89  * delete parts of the visible screen need to call dispup() and - or
90  * a setpos() setcursor() sequence.
91  *
92  * To find out how much of the screen needs to be updated, findmatch() is used.
93  */
94 
95 /*
96  * Ein Problem sind Terminals, bei denen b_auto_right_margin true ist.
97  * Sie setzen am Ende einer Zeile Den Cursor automatisch auf die naechste Zeile
98  * oder scrollen gar, wenn es sich um die letzte Bildschirmposition handelt.
99  *
100  * Da wird es schwierig, die letzte Bildschirmposition zu beschreiben.
101  * Ich sehe z.Zt. nur die M�glichkeit durch Verwendung von insert char oder
102  * insert line.
103  *
104  * Will man mit insert char arbeiten, dann wird zuerst das Letze Zeichen auf der
105  * vorletzten Position geschrieben und dann davor das vorletzte Zeichen
106  * eingef�gt.
107  * Vorteil: ruhiges Bild.
108  *
109  * Wenn man mit insert Line arbeitet, denn geht es z.B. mit folgendem
110  * Codefragment, welches aber den Nachteil hat, das das Bild springt und das es
111  * nicht funktioniert, wenn das verwendete Terminal wider Erwarten doch micht
112  * hochscrollt.
113  *
114  *	if (b_auto_right_margin && hpos >= wp->llen) {
115  *		MOVE_CURSOR(0, 0);
116  *		INSERT_LINE(wp, 1);
117  *		refreshmsg();
118  *	}
119  *
120  * Da der ved versucht die Terminalabh�ngigen Dinge m�glichst vorsichtig zu
121  * verwenden, scheidet daher die letzte Methode aus.
122  *
123  * N.B.: Der vi ist auf maximale Performance bei 300 Baud optimiert, der
124  *	 ved auf maximale Performance bei hohen Datenraten.
125  */
126 
127 #include "ved.h"
128 #include "movedot.h"
129 #include "buffer.h"
130 #include "terminal.h"
131 
132 /*#define	DEBUG*/
133 #ifdef	DEBUG
134 #define	DBG(a)		(cdbg a)
135 #define	FDBG(a)		(flush(), cdbg a)
136 #define	SDBG(a)		(cdbg a, sleep(2))
137 #define	FSDBG(a)	(flush(), cdbg a, sleep(2))
138 #else
139 #define	DBG(a)
140 #define	FDBG(a)
141 #define	SDBG(a)
142 #define	FSDBG(a)
143 #endif
144 
145 EXPORT	cpos_t	cursor;		/* position of cursor in window hp not mapped*/
146 EXPORT	cpos_t	cpos;		/* real pos of cursor on screen hp mapped    */
147 
148 #define	MARKWRAP
149 /* XXX markwrap ist noch nicht implementiert */
150 
151 LOCAL	BOOL	nl_ostop;	/* stop output past wrap marker '\\'	    */
152 
153 extern	Uchar	csize[];	/* The character sizes table		    */
154 extern	Uchar	*ctab[];	/* The character string table		    */
155 
156 /*
157  * XXX static beseitigen! -> automatic.
158  */
159 LOCAL	headr_t	*passlink = 0;		/* Used by getnext(). Both should be */
160 LOCAL	int	passoffset = 0;		/* set with findpos() and left alone */
161 
162 /*
163  * Get the next character in buffer.
164  *
165  * Getnext() needs acess to two variables: rpasslink and rpassoffset.
166  * Both should be in registers as a result of a previous call to
167  * findpos(pos, &passlink, &passoffset); and then left alone.
168  * Getnext() then will increment rpasslink and rpassoffset as needed and
169  * return the next character in the buffer on each call.
170  */
171 
172 #define	getnext(wp)	rpasslink->cont[rpassoffset++];		\
173 									\
174 	while (rpassoffset >= rpasslink->size) {			\
175 		rpassoffset -= rpasslink->size;				\
176 		rpasslink = rpasslink->next;				\
177 		readybuffer(wp, rpasslink);				\
178 	}
179 
180 #define	peeknext()	rpasslink->cont[rpassoffset]
181 
182 /*
183  * On a Motorola 68000 there is no divsll and even on an 68020
184  * divu if much faster than divsll.
185  * Try to use divu as long as llen * psize is < 65535 (256 * 256).
186  * Otherwise define LONG_MOD.
187  */
188 
189 #ifndef	JOS
190 #if	defined(mc68000) && !defined(LONG_MOD)
191 #define	modu(a, b)	((unsigned short) \
192 				(((unsigned short)(a)) % ((unsigned short)(b))))
193 #else
194 #define	modu(a, b)	((a) % (b))
195 #endif	/* mc68000 */
196 #endif	/* JOS */
197 
198 #ifndef	modu
199 #define	modu(a, b)	((a) % (b))
200 #endif
201 
202 /*
203  * Check if we are done with matching the screen update.
204  * This is true if there was a linewrap and we are at the start of the line or
205  * if folding the lines would take us to the same virtual horizontal position.
206  */
207 #define	nomatch(hp1, hp2) (linewrap ? (hp1) || (hp2) : \
208 				modu(hp1, wp->llen) != modu(hp2, wp->llen))
209 
210 LOCAL	epos_t	lastdot = -1L;
211 
212 EXPORT	void	update		__PR((ewin_t *wp));
213 EXPORT	void	setwindow	__PR((ewin_t *wp));
214 EXPORT	void	newwindow	__PR((ewin_t *wp));
215 LOCAL	epos_t	getnewwindow	__PR((ewin_t *wp));
216 LOCAL	BOOL	setcurpos	__PR((ewin_t *wp, epos_t ldot));
217 LOCAL	BOOL	onscreen	__PR((ewin_t *wp, cpos_t * cp));
218 EXPORT	int	getindent	__PR((ewin_t *wp));
219 EXPORT	void	setpos		__PR((ewin_t *wp));
220 EXPORT	BOOL	setcursor	__PR((ewin_t *wp));
221 LOCAL	void	mappos		__PR((ewin_t *wp, cpos_t * cp));
222 EXPORT	epos_t	countpos	__PR((ewin_t *wp, epos_t old, epos_t new,
223 					cpos_t * cp));
224 LOCAL	epos_t	getcol		__PR((ewin_t *wp, epos_t begin, int maxcol,
225 					cpos_t * cp));
226 EXPORT	epos_t	findcol		__PR((ewin_t *wp, int col, epos_t begin));
227 LOCAL	epos_t	findmatch	__PR((ewin_t *wp, epos_t begin, cpos_t * a,
228 					cpos_t * b));
229 EXPORT	void	dispup		__PR((ewin_t *wp, epos_t old, epos_t new));
230 LOCAL	void	dispdel		__PR((ewin_t *wp, epos_t old, epos_t new));
231 LOCAL	void	dispins		__PR((ewin_t *wp, epos_t old, epos_t new));
232 EXPORT	int	realvp		__PR((ewin_t *wp, cpos_t * cp));
233 EXPORT	int	realhp		__PR((ewin_t *wp, cpos_t * cp));
234 LOCAL	void	typescn		__PR((ewin_t *wp, epos_t begin, int col,
235 					epos_t end));
236 LOCAL	epos_t	typescx		__PR((ewin_t *wp, epos_t begin, int col,
237 					epos_t end));
238 LOCAL	void	typerest	__PR((ewin_t *wp, epos_t begin, cpos_t * cp));
239 EXPORT	void	typescreen	__PR((ewin_t *wp, epos_t begin, int col,
240 					epos_t end));
241 LOCAL	BOOL	outch		__PR((ewin_t *wp, int c));
242 LOCAL	BOOL	outnl		__PR((ewin_t *wp, BOOL  nlflg));
243 LOCAL	BOOL	outtab		__PR((ewin_t *wp));
244 LOCAL	BOOL	siaddchar	__PR((ewin_t *wp, int c));
245 LOCAL	void	insert_pad	__PR((ewin_t *wp, int size));
246 
247 
248 /*---------------------------------------------------------------------------
249 |
250 | Update window/cursor position to reflect the new position of dot
251 |
252 +---------------------------------------------------------------------------*/
253 
254 EXPORT void
update(wp)255 update(wp)
256 	ewin_t	*wp;
257 {
258 	if (wp->dot >= wp->window && setcurpos(wp, lastdot))
259 		setcursor(wp);
260 	else
261 		setwindow(wp);
262 	lastdot = wp->dot;
263 }
264 
265 
266 /*---------------------------------------------------------------------------
267 |
268 | Adjust window so that the cursor will be in an optimal position on screen
269 |
270 +---------------------------------------------------------------------------*/
271 
272 EXPORT void
setwindow(wp)273 setwindow(wp)
274 	ewin_t	*wp;
275 {
276 	epos_t	save = wp->window;
277 
278 	DBG(("setwindow"));
279 
280 	if (save > wp->eof)
281 		save = wp->eof;
282 	wp->window = getnewwindow(wp);
283 	MOVE_CURSOR(wp, 1, 0);
284 	cursor.vp = 1;
285 	cursor.hp = 0;
286 	dispup(wp, save, wp->window);
287 	setpos(wp);
288 	setcursor(wp);
289 }
290 
291 
292 /*---------------------------------------------------------------------------
293 |
294 | Retype the whole screen after adjusting the window with an optimal curpos
295 |
296 +---------------------------------------------------------------------------*/
297 
298 EXPORT void
newwindow(wp)299 newwindow(wp)
300 	ewin_t	*wp;
301 {
302 	DBG(("newwindow"));
303 
304 	wp->window = getnewwindow(wp);
305 	MOVE_CURSOR_ABS(wp, 1, 0);
306 	cursor.vp = 1;
307 	cursor.hp = 0;
308 	typescreen(wp, wp->window, 0, wp->eof);
309 	setpos(wp);
310 	setcursor(wp);
311 	lastdot = wp->dot;
312 }
313 
314 #ifdef OLD
315 
316 LOCAL epos_t
getnewwindow()317 getnewwindow()
318 {
319 	return (revline(dot, optline));
320 }
321 
322 #else
323 
324 
325 /*---------------------------------------------------------------------------
326 |
327 | Recompute the start of the window to have the cursor in a optimal position
328 |
329 +---------------------------------------------------------------------------*/
330 
331 LOCAL epos_t
getnewwindow(wp)332 getnewwindow(wp)
333 	ewin_t	*wp;
334 {
335 		cpos_t	c;
336 	register epos_t	newwin;
337 	register epos_t	win = wp->dot;
338 	register int	vpos = 1;
339 	register int	vopt = wp->optline;
340 	register int	col;
341 
342 	if (win > wp->eof) {
343 		writeerr(wp, "BAD DOT POS (>EOF)");
344 		wp->dot = wp->eof;
345 		return (getnewwindow(wp));
346 	}
347 	do {
348 		c.vp = 1;
349 		c.hp = 0;
350 		newwin = revline(wp, win, (ecnt_t)2); /* eine Zeile zurueck */
351 		countpos(wp, newwin, win, &c);
352 		win = newwin;
353 		vpos += realvp(wp, &c) - 1;
354 	} while (vpos < vopt && newwin > 0);
355 	if (vpos >= wp->psize) {
356 		/*
357 		 * Should not happen .... but paranoia.
358 		 */
359 		wp->pmargin = 0;
360 		col = wp->llen/2;
361 		newwin = wp->dot - col - 1;
362 		while (findcol(wp, col, ++newwin) < wp->dot);
363 	}
364 	findwpos(wp, newwin);
365 	return (newwin);
366 }
367 #endif
368 
369 
370 /*---------------------------------------------------------------------------
371 |
372 | Compute the position where the cursor should be depending on 'dot' pos.
373 |
374 +---------------------------------------------------------------------------*/
375 
376 LOCAL BOOL
setcurpos(wp,ldot)377 setcurpos(wp, ldot)
378 	ewin_t	*wp;
379 	epos_t	ldot;
380 {
381 	DBG(("setcurpos dot: %lld ldot: %lld", (Llong)wp->dot, (Llong)ldot));
382 
383 	if (ldot >= 0 && wp->dot >= ldot) {
384 
385 		countpos(wp, ldot, wp->dot, &cursor);
386 	} else {
387 		setpos(wp);
388 	}
389 	return (onscreen(wp, &cursor));
390 }
391 
392 
393 /*---------------------------------------------------------------------------
394 |
395 | Check if 'cp' will be visible on the current screen
396 |
397 +---------------------------------------------------------------------------*/
398 
399 LOCAL BOOL
onscreen(wp,cp)400 onscreen(wp, cp)
401 	ewin_t	*wp;
402 	cpos_t	*cp;
403 {
404 	register int	v = realvp(wp, cp);
405 
406 	return (v <= wp->psize-wp->pmargin &&
407 		(v != wp->psize-wp->pmargin || realhp(wp, cp) < wp->llen) &&
408 					(v > wp->pmargin || wp->window == 0));
409 }
410 
411 
412 /*---------------------------------------------------------------------------
413 |
414 | Return the (visible) indentation of the current line
415 |
416 +---------------------------------------------------------------------------*/
417 
418 EXPORT int
getindent(wp)419 getindent(wp)
420 	ewin_t	*wp;
421 {
422 	cpos_t	c;
423 	BOOL		save;
424 	epos_t		lbeg;
425 	epos_t		textbeg;
426 	extern Uchar	notwhitespace[];
427 
428 	c.vp = 1;
429 	c.hp = 0;
430 	lbeg = revline(wp, wp->dot, (ecnt_t)1);	/* an den Anfang der Zeile */
431 	save = wp->magic;
432 	wp->magic = TRUE;
433 	textbeg = search(wp, lbeg, notwhitespace, strlen(C notwhitespace), 0)
434 			- 1;
435 	wp->magic = save;
436 	if (textbeg > wp->dot)
437 		textbeg = wp->dot;
438 /*	writeerr(wp, "%d %d %d", lbeg, textbeg, wp->dot);*/
439 	countpos(wp, lbeg, textbeg, &c);
440 	return (c.hp);
441 }
442 
443 
444 /*---------------------------------------------------------------------------
445 |
446 | Compute the position where the cursor should be, start at top of 'window'
447 |
448 +---------------------------------------------------------------------------*/
449 
450 EXPORT void
setpos(wp)451 setpos(wp)
452 	ewin_t	*wp;
453 {
454 	cursor.vp = 1;
455 	cursor.hp = 0;
456 	countpos(wp, wp->window, wp->dot, &cursor);
457 }
458 
459 
460 /*---------------------------------------------------------------------------
461 |
462 | Set the cursor to the position previously set up in struct curpos by setpos()
463 |
464 +---------------------------------------------------------------------------*/
465 
466 EXPORT BOOL
setcursor(wp)467 setcursor(wp)
468 	ewin_t	*wp;
469 {
470 	int	v;
471 
472 	DBG(("setcursor caller: 0x%lX P: %d.%d C: %d.%d",
473 		getcaller(), cpos.vp, cpos.hp, cursor.vp, cursor.hp));
474 
475 	if ((v = realvp(wp, &cursor)) <= wp->psize) {
476 		MOVE_CURSOR(wp, v, realhp(wp, &cursor));
477 
478 		DBG(("setcursor P: %d.%d C: %d.%d",
479 			cpos.vp, cpos.hp, cursor.vp, cursor.hp));
480 
481 		return (TRUE);
482 	} else {
483 		return (FALSE);
484 	}
485 }
486 
487 
488 /*---------------------------------------------------------------------------
489 |
490 | Map cp->vp/cp->hp position description into description of the form vp/0
491 |
492 +---------------------------------------------------------------------------*/
493 
494 LOCAL void
mappos(wp,cp)495 mappos(wp, cp)
496 		ewin_t	*wp;
497 	register cpos_t	*cp;
498 {
499 	cp->vp = realvp(wp, cp) + 1;
500 	cp->hp = 0;
501 }
502 
503 
504 /*---------------------------------------------------------------------------
505 |
506 | Update 'cp' to the cursor position when moving from old to new.
507 | Return position in file that is actually taken.
508 |
509 +---------------------------------------------------------------------------*/
510 
511 EXPORT epos_t
countpos(wp,old,new,cp)512 countpos(wp, old, new, cp)
513 		ewin_t	*wp;
514 		epos_t	old;
515 		epos_t	new;
516 	register cpos_t	*cp;
517 {
518 	register headr_t *rpasslink;	/* state for getnext - these should */
519 	register int	rpassoffset;	/* set by any caller and left alone */
520 	register Uchar	c;
521 	register Uchar	*rcsize;
522 	register int	rllen;
523 	register int	rpsize;
524 	register epos_t	cnt;
525 
526 	if (old >= new)
527 		return (old);
528 	rcsize = csize;
529 	rllen = wp->llen;
530 	rpsize = wp->psize;
531 	findpos(wp, old, &passlink, &passoffset);
532 	rpasslink = passlink;
533 	rpassoffset = passoffset;
534 
535 	cnt = new - old;
536 	while (cnt > 0) {
537 		c = getnext(wp);
538 		cnt--;
539 		if (c == '\n') {
540 			mappos(wp, cp);
541 			if (cp->vp > rpsize)
542 				break;
543 		} else if (c == TAB) {
544 			cp->hp = (cp->hp / wp->tabstop) * wp->tabstop +
545 					wp->tabstop;
546 		} else {
547 			cp->hp += rcsize[c];
548 		}
549 		if (cp->hp >= rllen && realvp(wp, cp) > rpsize)
550 			break;
551 	}
552 	return (new - cnt);
553 }
554 
555 
556 /*---------------------------------------------------------------------------
557 |
558 | Get 'cp' for maximun column in this line.
559 | Update 'cp' to the cursor position when moving from old to this column.
560 | Return position in file that is actually taken.
561 |
562 +---------------------------------------------------------------------------*/
563 
564 LOCAL epos_t
getcol(wp,begin,maxcol,cp)565 getcol(wp, begin, maxcol, cp)
566 		ewin_t	*wp;
567 		epos_t	begin;
568 	register int	maxcol;
569 	register cpos_t	*cp;
570 {
571 	register headr_t *rpasslink;	/* state for getnext - these should */
572 	register int	rpassoffset;	/* set by any caller and left alone */
573 	register Uchar	c;
574 	register Uchar	*rcsize;
575 	register int	ocol;
576 	register int	col;
577 	register epos_t	cnt;
578 
579 	if (begin >= wp->eof)
580 		return (begin);
581 	rcsize = csize;
582 	findpos(wp, begin, &passlink, &passoffset);
583 	rpasslink = passlink;
584 	rpassoffset = passoffset;
585 
586 	cnt = wp->eof - begin;
587 	col = realhp(wp, cp);
588 	while (cnt > 0 && col <= maxcol) {
589 		c = getnext(wp);
590 		cnt--;
591 		if (c == '\n') {
592 			mappos(wp, cp);
593 			col = realhp(wp, cp);
594 			break;
595 		} else if (c == TAB) {
596 			ocol = cp->hp;
597 			cp->hp = (cp->hp / wp->tabstop) * wp->tabstop +
598 					wp->tabstop;
599 			col += cp->hp - ocol;
600 		} else {
601 			cp->hp += rcsize[c];
602 			col += rcsize[c];
603 		}
604 	}
605 	return (wp->eof - cnt);
606 }
607 
608 
609 /*---------------------------------------------------------------------------
610 |
611 | Find the file offset that belongs to a specific visible screen column.
612 | Start position needs to be at the beginning of a line.
613 |
614 +---------------------------------------------------------------------------*/
615 
616 EXPORT epos_t
findcol(wp,col,begin)617 findcol(wp, col, begin)
618 		ewin_t	*wp;
619 	register int	col;
620 	register epos_t	begin;
621 {
622 	cpos_t a;
623 
624 	a.hp = 0;
625 	a.vp = 1;
626 
627 	while (begin < wp->eof && a.hp < col) {
628 		countpos(wp, begin, begin+1, &a);
629 		/*
630 		 * If this line is too short, give up.
631 		 */
632 		if (a.hp == 0)
633 			break;
634 		begin++;
635 	}
636 	return (begin);
637 }
638 
639 LOCAL	int	tabcnt;		/* # of tabs found by last findmatch call */
640 LOCAL	BOOL	linewrap;	/* The changes caused a diffrent vpos    */
641 
642 /*---------------------------------------------------------------------------
643 |
644 | Find the minimum number of characters needed to print for updating the
645 | screen to a correct new state.
646 |
647 +---------------------------------------------------------------------------*/
648 
649 LOCAL epos_t
findmatch(wp,begin,a,b)650 findmatch(wp, begin, a, b)
651 		ewin_t	*wp;
652 		epos_t	begin;
653 	register cpos_t	*a;
654 	register cpos_t	*b;
655 {
656 	register headr_t *rpasslink;	/* state for getnext - these should */
657 	register int	rpassoffset;	/* set by any caller and left alone */
658 	register Uchar	c;
659 	register Uchar	*rcsize;
660 	register Uchar	size;
661 	register epos_t	cnt;
662 
663 	tabcnt = 0;
664 	linewrap = a->vp != b->vp || a->hp/wp->llen != b->hp/wp->llen;
665 	if (begin >= wp->eof)
666 		return (begin);
667 	rcsize = csize;
668 	findpos(wp, begin, &passlink, &passoffset);
669 	rpasslink = passlink;
670 	rpassoffset = passoffset;
671 
672 	cnt = wp->eof - begin;
673 	while (nomatch(a->hp, b->hp) && cnt > 0) {
674 		c = getnext(wp);
675 		cnt--;
676 		if (c == '\n') {
677 			mappos(wp, a);
678 			mappos(wp, b);
679 			return (wp->eof - cnt);
680 		}
681 		if (c == TAB) {
682 			a->hp = (a->hp / wp->tabstop) * wp->tabstop +
683 					wp->tabstop;
684 			b->hp = (b->hp / wp->tabstop) * wp->tabstop +
685 					wp->tabstop;
686 			tabcnt++;
687 		} else {
688 			a->hp += size = rcsize[c];
689 			b->hp += size;
690 		}
691 	}
692 	return (wp->eof - cnt);
693 }
694 
695 
696 /*---------------------------------------------------------------------------
697 |
698 | Check if we are ready with matching the screen update.
699 | This is true if there was a linewrap and we are at the start of the line or
700 | if folding the lines would take us to the same virtual horizontal position.
701 |
702 +---------------------------------------------------------------------------*/
703 
704 #ifndef	nomatch
nomatch(hp1,hp2)705 nomatch(hp1, hp2)
706 {
707 	if (linewrap)
708 		return (hp1 || hp2);
709 	else
710 		return (modu(hp1, wp->llen) != modu(hp2, wp->llen));
711 }
712 #endif
713 
714 LOCAL	int	gotshorter;	/* There were visible deletions on screen */
715 
716 /*---------------------------------------------------------------------------
717 |
718 | Update the display to reflect the effects of a insert or delete operation.
719 | 'old' is the file offset where the cursor currently is located.
720 | 'new' is the file offset where the cursor should be.
721 |
722 +---------------------------------------------------------------------------*/
723 EXPORT void
dispup(wp,old,new)724 dispup(wp, old, new)
725 	ewin_t	*wp;
726 	epos_t	old;
727 	epos_t	new;
728 {
729 	FDBG(("  dispup (%lld, %lld) P: %d.%d %s", (Llong)old, (Llong)new,
730 					cpos.vp, cpos.hp,
731 					(new > old)? "Delete":"Insert"));
732 
733 	if ((gotshorter = (new > old)) != 0) {
734 		dispdel(wp, old, new);
735 	} else {
736 		dispins(wp, old, new);
737 	}
738 }
739 
740 
741 /*---------------------------------------------------------------------------
742 |
743 | Update the display to reflect the effects of a delete operation.
744 | 'old' is where the deleted text starts now (before deleting)
745 | 'new' is where the remaining characters have been before doing the deletion.
746 |
747 +---------------------------------------------------------------------------*/
748 
749 LOCAL void
dispdel(wp,old,new)750 dispdel(wp, old, new)
751 	ewin_t	*wp;
752 	epos_t	old;
753 	epos_t	new;
754 {
755 	epos_t	save = old;
756 	epos_t	skip;
757 	int	size;
758 	int	bottomv;
759 	int	bottomh;
760 	int	col = cursor.hp;
761 	cpos_t	o;
762 	cpos_t	n;
763 
764 	/*
765 	 * First iniatilize to current cursor position.
766 	 * Later, 'n' is updated to where the cursor should be,
767 	 * 'o' is updated to where the appropriate characters currently are.
768 	 */
769 	o.vp = n.vp = cursor.vp;
770 	o.hp = n.hp = cursor.hp;
771 
772 	/*
773 	 * Compute the visible size of deleted characters and the position
774 	 * on screen where typing these caracters would take us.
775 	 */
776 	countpos(wp, old, new, &o);
777 	size = o.hp - n.hp;
778 
779 	/*
780 	 * Check how many characters we would have to type until the
781 	 * screen remains the same as before deleting the characters.
782 	 */
783 	old = findmatch(wp, new, &n, &o);
784 
785 	linewrap = realvp(wp, &o) != realvp(wp, &n);
786 
787 	/*
788 	 * If we did not find any tab and we only have to retype the rest of
789 	 * the current line use delete char.
790 	 * If there is no charater remaining after the current position
791 	 * don't use delete char.
792 	 */
793 	if (old != 0 && o.hp == 0 && o.vp == realvp(wp, &cursor) + 1)
794 		if (tabcnt == 0 && f_del_char)
795 			if (size > 0 && size < (old-new)/2 && save+1 < old) {
796 				DELETE_CHAR(wp, size);
797 				old = new;
798 
799 				FSDBG(("DCdispdel(%lld, %lld) P: %d.%d",
800 					(Llong)old, (Llong)new,
801 					cpos.vp, cpos.hp));
802 			}
803 
804 	/*
805 	 * Limit the number if deleted lines to 2/3 of the screen and
806 	 * require that at least one old line remains on screen.
807 	 */
808 	if ((size = realvp(wp, &o) - realvp(wp, &n)) > 0 && new < wp->eof) {
809 		if (f_del_line && size>>1 <= wp->psize/3+1 &&
810 		    size+cpos.vp < wp->psize-1 &&
811 		    cpos.vp < wp->psize) {
812 			/*
813 			 * First redraw to end of current screen line.
814 			 */
815 			n.vp = cursor.vp;
816 			n.hp = cursor.hp;
817 			skip = getcol(wp, new, wp->llen, &n);
818 			if (skip < wp->eof) {
819 				nl_ostop = TRUE;
820 				typescn(wp, new, col, skip);
821 				nl_ostop = FALSE;
822 
823 				FSDBG(("TSdispdel(%lld, %lld) P: %d.%d",
824 					(Llong)old, (Llong)new,
825 					cpos.vp, cpos.hp));
826 
827 				/* find end of screen */
828 				new = countpos(wp, old, wp->eof, &o);
829 				col = o.hp;
830 				DELETE_LINE(wp, size);
831 
832 				FSDBG(("DLdispdel(%lld, %lld) P: %d.%d",
833 					(Llong)old, (Llong)new,
834 					cpos.vp, cpos.hp));
835 
836 				if (new == wp->eof) {
837 					/*
838 					 * Das Ende der Datei ist bereits
839 					 * sichtbar, Bildschirmupdate beendet.
840 					 */
841 					goto out;
842 					/* need goto to print dbg */
843 				}
844 
845 				FSDBG(("BVdispdel(%lld, %lld) O: %d.%d",
846 						(Llong)old, (Llong)new,
847 						o.vp, o.hp));
848 				/*
849 				 * Berechnung der realen Cursorposition, ab
850 				 * der der Bildchirm beschrieben werden mu�.
851 				 */
852 				bottomv = realvp(wp, &o);
853 				bottomv = min(bottomv, wp->psize+1);
854 				bottomh = bottomv > wp->psize ? 0 :
855 							realhp(wp, &o);
856 
857 				if (b_auto_right_margin && !f_ins_char) {
858 					/*
859 					 * In diesem Fall ist das letzte
860 					 * Zeichen auf dem Bildschirm nicht
861 					 * geschrieben worden.
862 					 */
863 					if (bottomh == 0) {
864 						bottomv--;
865 						bottomh = wp->llen;
866 					} else {
867 						/*
868 						 * Das sollte nie passieren!
869 						 */
870 						cdbg("bottomh: %d",
871 							bottomh);
872 					}
873 				}
874 				MOVE_CURSOR(wp, bottomv-size, bottomh);
875 
876 				FSDBG(("MCdispdel(%lld, %lld) P: %d.%d",
877 					(Llong)old, (Llong)new,
878 					cpos.vp, cpos.hp));
879 
880 				typerest(wp, new, &o);
881 
882 				FSDBG(("TRdispdel(%lld, %lld) P: %d.%d",
883 					(Llong)old, (Llong)new,
884 					cpos.vp, cpos.hp));
885 			}
886 		}
887 		/*
888 		 * There is no delele-line feature or we don't want
889 		 * to use it, redraw to end.
890 		 */
891 		old = wp->eof;
892 	}
893 	typescn(wp, new, col, old);
894 out:
895 	FSDBG(("TSdispdel(%lld, %lld) P: %d.%d",
896 			(Llong)old, (Llong)new,
897 			cpos.vp, cpos.hp));
898 }
899 
900 
901 /*---------------------------------------------------------------------------
902 |
903 | Update the display to reflect the effects of an insert operation.
904 | 'new' is where the new (inserted characters) start
905 | 'old' is where the old characters will be after doing the insertion.
906 |
907 +---------------------------------------------------------------------------*/
908 
909 LOCAL void
dispins(wp,old,new)910 dispins(wp, old, new)
911 	ewin_t	*wp;
912 	epos_t	old;
913 	epos_t	new;
914 {
915 	epos_t	save = old;
916 	int	size;
917 	int	col = cursor.hp;
918 	cpos_t	o;
919 	cpos_t	n;
920 
921 	/*
922 	 * First iniatilize to current cursor position.
923 	 * Later, 'n' is updated to where the cursor should be,
924 	 * 'o' is updated to where the appropriate characters currently are.
925 	 */
926 	o.vp = n.vp = cursor.vp;
927 	o.hp = n.hp = cursor.hp;
928 
929 	/*
930 	 * Compute the visible size of inserted characters and the position
931 	 * on screen where typing the caracters would take us.
932 	 */
933 	countpos(wp, new, old, &n);
934 	size = n.hp - o.hp;
935 
936 	/*
937 	 * Check how many characters we have to type until the screen
938 	 * remains the same as before inserting the characters.
939 	 */
940 	old = findmatch(wp, old, &n, &o);
941 
942 	linewrap = realvp(wp, &o) != realvp(wp, &n);
943 
944 	/*
945 	 * If we did not find any tab and we only have to retype the rest of
946 	 * the current line use insert char.
947 	 * If there is no charater remaining after the current position
948 	 * don't use insert char.
949 	 */
950 	if (old != wp->eof && n.hp == 0 && n.vp == realvp(wp, &cursor) + 1) {
951 		if (tabcnt == 0 && f_ins_char) {
952 			if (size > 0 && size < (old-new)/3 && save+1 < old) {
953 				/* XXX Besser direkt Inhalt schreiben */
954 				insert_pad(wp, size);
955 
956 				FSDBG(("ICdispins(%lld, %lld) P: %d.%d",
957 					(Llong)old, (Llong)new,
958 					cpos.vp, cpos.hp));
959 
960 				MOVE_CURSOR(wp, cpos.vp, cpos.hp - size);
961 				old = save;
962 
963 				FSDBG(("MCdispins(%lld, %lld) P: %d.%d",
964 					(Llong)old, (Llong)new,
965 					cpos.vp, cpos.hp));
966 			}
967 		}
968 	} else if (o.hp) {
969 		mappos(wp, &o);
970 	}
971 
972 	/*
973 	 * Limit the number if inserted lines to half the screen and
974 	 * require that at least one old line remains on screen.
975 	 */
976 	if ((size = (realvp(wp, &n)-realvp(wp, &o)))*2 > wp->psize ||
977 			size+cpos.vp >= wp->psize-1 ||
978 			old == wp->eof) {
979 		typescn(wp, new, col, wp->eof);		/* Retype rest */
980 
981 		FSDBG(("TS1dispins(%lld, %lld) P: %d.%d",
982 				(Llong)old, (Llong)new,
983 				cpos.vp, cpos.hp));
984 	} else {
985 		/*
986 		 * We decided that is is worth to insert lines.
987 		 */
988 		if (size > 0) {
989 			if (f_ins_line) {
990 				if (col > 0)
991 					MOVE_CURSOR(wp, cpos.vp + 1, 0);
992 				INSERT_LINE(wp, size);
993 
994 				FSDBG(("ILdispins(%lld, %lld) P: %d.%d",
995 					(Llong)old, (Llong)new,
996 					cpos.vp, cpos.hp));
997 				if (col > 0)
998 					setcursor(wp);
999 			} else {
1000 				old = wp->eof;
1001 			}
1002 		}
1003 		typescn(wp, new, col, old);
1004 
1005 		FSDBG(("TS2dispins(%lld, %lld) P: %d.%d",
1006 			(Llong)old, (Llong)new,
1007 			cpos.vp, cpos.hp));
1008 	}
1009 }
1010 
1011 
1012 /*---------------------------------------------------------------------------
1013 |
1014 | Return the 'real' vertical position of 'cp'.
1015 | When computing cursor position, the vertical position is only updated to
1016 | the correct visible value if we at the start of a line. This computes the
1017 | visible vertical position as an effect of possible folding of long lines.
1018 | Take care not to increment if hpos is 0.
1019 |
1020 +---------------------------------------------------------------------------*/
1021 
1022 EXPORT int
realvp(wp,cp)1023 realvp(wp, cp)
1024 		ewin_t	*wp;
1025 	register cpos_t	*cp;
1026 {
1027 #ifdef	MARKWRAP
1028 	return (cp->hp >= (wp->llen+wp->markwrap) ?
1029 			cp->vp + (cp->hp - wp->markwrap) / wp->llen : cp->vp);
1030 #else
1031 	return (cp->hp > wp->llen ? cp->vp + (cp->hp - 1) / wp->llen : cp->vp);
1032 #endif
1033 }
1034 
1035 
1036 /*---------------------------------------------------------------------------
1037 |
1038 | Return the 'real' horizontal position of 'cp'.
1039 | When computing cursor position, the vertical position is only updated to
1040 | the correct visible value if we at the start of a line. This computes the
1041 | visible horizontal position as an effect of possible folding of long lines.
1042 | Take care not to increment if hpos is 0.
1043 |
1044 +---------------------------------------------------------------------------*/
1045 
1046 EXPORT int
realhp(wp,cp)1047 realhp(wp, cp)
1048 	ewin_t	*wp;
1049 	cpos_t	*cp;
1050 {
1051 	register int	h = cp->hp;
1052 
1053 #ifdef	MARKWRAP
1054 	return (h >= (wp->llen+wp->markwrap) ?
1055 			(h - wp->markwrap) % wp->llen + wp->markwrap : h);
1056 #else
1057 	return (h > wp->llen ? (h - 1) % wp->llen + 1 : h);
1058 #endif
1059 }
1060 
1061 
1062 /*---------------------------------------------------------------------------
1063 |
1064 | Type characters from 'x' to 'y' starting on column 'col'.
1065 | Stop if we reached 'eof' or the end of the screen.
1066 | Use typescx() to do the work, then set 'ewindow' to the last character
1067 | position on screen.
1068 |
1069 +---------------------------------------------------------------------------*/
1070 
1071 LOCAL void
typescn(wp,begin,col,end)1072 typescn(wp, begin, col, end)
1073 	ewin_t	*wp;
1074 	epos_t	begin;
1075 	int	col;
1076 	epos_t	end;
1077 {
1078 	epos_t	lpos;
1079 
1080 	if ((lpos = typescx(wp, begin, col, end)) >= 0)
1081 		wp->ewindow = lpos;
1082 	if (wp->ewindow < wp->window)	/* XXX ??? */
1083 		wp->ewindow = wp->eof;
1084 }
1085 
1086 LOCAL	int	lcol;			/* Remembered last typed column. */
1087 
1088 /*---------------------------------------------------------------------------
1089 |
1090 | Type characters from 'x' to 'y' starting on column 'col'.
1091 | Stop if we reached 'eof' or the end of the screen.
1092 | Return the offset of the last character typed if 'y' would not fit on screen
1093 | or -1 if the whole number of characters could be typed.
1094 | Should only be called from typescn().
1095 |
1096 +---------------------------------------------------------------------------*/
1097 
1098 LOCAL epos_t
typescx(wp,begin,col,end)1099 typescx(wp, begin, col, end)
1100 		ewin_t	*wp;
1101 	register epos_t	begin;
1102 		int	col;
1103 		epos_t	end;
1104 {
1105 	register headr_t *rpasslink;	/* state for getnext - these should */
1106 	register int	rpassoffset;	/* set by any caller and left alone */
1107 	register Uchar	c;
1108 	register Uchar	*s;
1109 	register Uchar	**rctab;
1110 	register epos_t	lposition;	/* last pos in buffer to be typed */
1111 
1112 	rctab = ctab;
1113 	findpos(wp, begin, &passlink, &passoffset);
1114 	rpasslink = passlink;
1115 	rpassoffset = passoffset;
1116 
1117 	lcol = col;
1118 	lposition = min(end, wp->eof);
1119 
1120 	while (begin < lposition) {
1121 		if (wp->markvalid && begin == wp->mark)
1122 			onmark();
1123 		c = getnext(wp);
1124 		begin++;
1125 		if (c == '\n') {
1126 			if (!outnl(wp, begin < lposition || gotshorter ||
1127 						linewrap || wp->visible))
1128 				return (begin);
1129 		} else if (c == '\r' && wp->dosmode && peeknext() == '\n') {
1130 			/*EMPTY*/
1131 			;
1132 		} else if (c == TAB) {
1133 			if (!outtab(wp))
1134 				return (begin);
1135 		} else {
1136 			s = rctab[c];
1137 			while (*s)
1138 				if (!outch(wp, *s++))
1139 					return (begin);
1140 		}
1141 		if (markon)
1142 			offmark();
1143 	}
1144 
1145 	/*
1146 	 * If the last position to be typed is 'eof' take care of the EOF
1147 	 * marker.
1148 	 */
1149 	if (lposition == wp->eof) {
1150 		if (wp->mark == wp->eof && wp->markvalid) {
1151 			onmark();
1152 			addchar((Uchar) (wp->visible ? '>' : ' '));
1153 			offmark();
1154 		} else {
1155 			addchar((Uchar) (wp->visible ? '>' : ' '));
1156 		}
1157 
1158 		/*
1159 		 * If the file got shorter, erase the rest of the screen if
1160 		 * the change was visible on more than line orerase the rest
1161 		 * of the line if we only had to type one line.
1162 		 */
1163 		if (gotshorter) {
1164 			if (linewrap)
1165 				CLEAR_TO_EOF_SCREEN(wp);
1166 			else
1167 				CLEAR_TO_EOF_LINE(wp);
1168 		}
1169 	}
1170 	return (-1);
1171 }
1172 
1173 
1174 /*
1175  * Gibt den Rest eines Buchstabens aus,
1176  * wenn durch Hochscrollen der hintere Teil sichtbar wird.
1177  * Das passiert, wenn ein Buchstabe vorher nicht komplett auf dem
1178  * Bildschirm sichtbar war.
1179  *
1180  * Pos ist die Cursorposition hinter dem wickelnden char.
1181  * Wenn pos nicht hinter die letzte Zeile zeigt,
1182  * oder der Rest gr��er als der dort stehende Buchstabe ist,
1183  * wird nichts ausgegeben.
1184  */
1185 LOCAL void
typerest(wp,begin,cp)1186 typerest(wp, begin, cp)
1187 		ewin_t	*wp;
1188 	register epos_t	begin;
1189 	register cpos_t	*cp;
1190 {
1191 	register headr_t *rpasslink;	/* state for getnext - these should */
1192 	register int	rpassoffset;	/* set by any caller and left alone */
1193 	register Uchar	c;		/* wrapping character		    */
1194 	register Uchar	*s;		/* string to output for 'c'	    */
1195 	register int	rest;		/* number of untyped chars in 'c'   */
1196 		int	p;
1197 
1198 	/*
1199 	 * Wenn die Zeile wickelt, dann ist realvp(cp) mindestens auf psize+1
1200 	 */
1201 	if (realvp(wp, cp) - wp->psize <= 0)
1202 		return;
1203 
1204 	p = wp->psize + 1 - cp->vp;
1205 
1206 	rest = cp->hp - p * wp->llen;
1207 
1208 	DBG(("typerest: cp: %d.%d psize: %d p: %d rest: %d",
1209 			cp->vp, cp->hp, wp->psize, p, rest));
1210 
1211 	if (rest <= 0)
1212 		return;
1213 
1214 	lcol = cp->hp - rest;
1215 	findpos(wp, --begin, &passlink, &passoffset);
1216 	rpasslink = passlink;
1217 	rpassoffset = passoffset;
1218 
1219 	if (wp->markvalid && begin == wp->mark)
1220 		onmark();
1221 	c = getnext(wp);
1222 	if (c == TAB) {
1223 		if (!outtab(wp))
1224 			return;
1225 	} else {
1226 		s = ctab[c];
1227 		p = csize[c];
1228 		if (rest > p)
1229 			goto out;
1230 		s += p - rest;
1231 		while (*s)
1232 			if (!outch(wp, *s++))
1233 				return;
1234 	}
1235 out:
1236 	if (markon)
1237 		offmark();
1238 }
1239 
1240 
1241 /*---------------------------------------------------------------------------
1242 |
1243 | The external interface to (re-)type the whole screen or parts of it.
1244 | Typescreen does no optimization like dispup().
1245 | The cursor must be set to the position where typing should start.
1246 | It first sets some variables to force typescn() to do no optimization.
1247 |
1248 +---------------------------------------------------------------------------*/
1249 
1250 EXPORT void
typescreen(wp,begin,col,end)1251 typescreen(wp, begin, col, end)
1252 		ewin_t	*wp;
1253 	register epos_t	begin;
1254 		int	col;
1255 		epos_t	end;
1256 {
1257 	gotshorter	= TRUE;
1258 	linewrap	= FALSE;
1259 	typescn(wp, begin, col, end);
1260 }
1261 
1262 /*
1263  * lastchar & lastmark sind ein Versuch den letzten Buchstaben auf dem Schirm
1264  * zu merken, damit man ihn mit insert char zum Beschreiben der letzten
1265  * Bildschirmposition verwenden kann.
1266  * Das funktioniert aber nur, wenn die letzten Buchstaben auf dem Bildschirm
1267  * nacheinander ausgegeben werden.
1268  */
1269 LOCAL	Uchar	lastchar;
1270 LOCAL	int	lastmark;
1271 
1272 /*---------------------------------------------------------------------------
1273 |
1274 | Output one character to the screen, take care of the screens's linelength
1275 | and pagesize. Mark a wrapping line with a backslash if wanted.
1276 | Return FALSE if the character was the last character that fit on screen.
1277 |
1278 +---------------------------------------------------------------------------*/
1279 
1280 LOCAL BOOL
outch(wp,c)1281 outch(wp, c)
1282 	ewin_t	*wp;
1283 	Uchar	c;
1284 {
1285 /*cdbg("llen: %d xxx: %d\n", wp->llen, (wp->llen+markwrap-1));*/
1286 #ifdef	MARKWRAP
1287 	if (cpos.hp >= (wp->llen+wp->markwrap-1)) {
1288 		Uchar lastc = wp->markwrap ? '\\' : c;
1289 #else
1290 	if (cpos.hp >= wp->llen) {
1291 #endif
1292 
1293 		if (cpos.vp >= wp->psize) {
1294 			if (!b_auto_right_margin) {
1295 
1296 #ifdef	MARKWRAP
1297 				addchar(lastc);
1298 #else
1299 				addchar((Uchar) '\\');
1300 #endif
1301 				/*
1302 				 * nun ist cpos.hp > llen
1303 				 */
1304 			} else {
1305 #ifdef	MARKWRAP
1306 				siaddchar(wp, lastc);
1307 #else
1308 				siaddchar(wp, (Uchar) '\\');
1309 #endif
1310 			}
1311 			if (markon)
1312 				offmark();
1313 			return (FALSE);
1314 		}
1315 /*
1316  * XXX hier ist der Fehler, da� der letzte Buchstabe nochmal auf der neuen
1317  * XXX Zeile ausgegeben wird.
1318  */
1319 #ifdef	MARKWRAP
1320 		addchar(lastc);
1321 #else
1322 		addchar((Uchar) '\\');
1323 #endif
1324 		/*
1325 		 * auch hier ist cpos.hp > llen
1326 		 */
1327 		if (b_auto_right_margin) {
1328 			/*
1329 			 * Flush mark buffer, then move cursor.
1330 			 */
1331 			if (markon) {
1332 				offmark();
1333 				onmark();
1334 			}
1335 			MOVE_CURSOR_ABS(wp, cpos.vp, cpos.hp);
1336 		}
1337 		addchar((Uchar) '\n');
1338 		if (nl_ostop)
1339 			return (TRUE);
1340 #ifdef	MARKWRAP
1341 		if (!wp->markwrap)
1342 			goto out;
1343 #endif
1344 	}
1345 	addchar(c);
1346 lastchar = c;
1347 lastmark = markon;
1348 out:
1349 	lcol++;
1350 	return (TRUE);
1351 }
1352 
1353 
1354 /*---------------------------------------------------------------------------
1355 |
1356 | Output a NL (^J) (depending on current mode [visible])
1357 |
1358 +---------------------------------------------------------------------------*/
1359 
1360 LOCAL BOOL
outnl(wp,nlflg)1361 outnl(wp, nlflg)
1362 	ewin_t	*wp;
1363 	BOOL	nlflg;
1364 {
1365 	if (!b_auto_right_margin || cpos.vp < wp->psize || cpos.hp < wp->llen)
1366 		addchar((Uchar) (wp->visible ? '$' : ' '));
1367 	else
1368 		siaddchar(wp, (Uchar) (wp->visible ? '$' : ' '));
1369 
1370 	if (b_auto_right_margin && cpos.hp >= wp->llen) {
1371 		MOVE_CURSOR_ABS(wp, cpos.vp, cpos.hp);
1372 	}
1373 	if (nlflg && cpos.hp <= wp->llen)
1374 		CLEAR_TO_EOF_LINE(wp);
1375 	if (cpos.vp >= wp->psize) {
1376 		if (markon)
1377 			offmark();
1378 		return (FALSE);
1379 	}
1380 	if (nlflg)
1381 		addchar((Uchar) '\n');
1382 	lcol = 0;
1383 	return (TRUE);
1384 }
1385 
1386 
1387 /*---------------------------------------------------------------------------
1388 |
1389 | Output a TAB (^I) in expanded form (depending on current mode)
1390 |
1391 +---------------------------------------------------------------------------*/
1392 
1393 LOCAL BOOL
outtab(wp)1394 outtab(wp)
1395 	ewin_t	*wp;
1396 {
1397 	register int	rtabstop = wp->tabstop;
1398 
1399 	do {
1400 		if (!outch(wp, (Uchar) (wp->visible ?
1401 					(modu(lcol+1, rtabstop)?'.' : ':') :
1402 					' ')))
1403 			return (FALSE);
1404 	} while (modu(lcol, rtabstop));
1405 	return (TRUE);
1406 }
1407 
1408 
1409 /*---------------------------------------------------------------------------
1410 |
1411 | Simulate addchar() at last screen position by using insert char.
1412 |
1413 +---------------------------------------------------------------------------*/
1414 
1415 LOCAL BOOL
siaddchar(wp,c)1416 siaddchar(wp, c)
1417 	ewin_t	*wp;
1418 	Uchar	c;
1419 {
1420 	char	str[2];
1421 	int	smark = markon;
1422 
1423 	if (!f_ins_char)
1424 		return (FALSE);
1425 
1426 	if (markon)
1427 		offmark();
1428 	CURSOR_LEFT(wp, 1);
1429 	if (smark)
1430 		onmark();
1431 
1432 	addchar(c);
1433 
1434 	if (markon)
1435 		offmark();
1436 
1437 	str[0] = lastchar;
1438 	str[1] = '\0';
1439 	CURSOR_LEFT(wp, 1);
1440 	INSERT_CHAR(wp, str);
1441 
1442 	if (lastmark) {
1443 		CURSOR_LEFT(wp, 1);
1444 		onmark();
1445 		addchar(lastchar);
1446 		offmark();
1447 	}
1448 	if (smark)
1449 		onmark();
1450 	return (TRUE);
1451 }
1452 
1453 
1454 /*---------------------------------------------------------------------------
1455 |
1456 | Insert a string of 'size' spaces
1457 |
1458 +---------------------------------------------------------------------------*/
1459 
1460 LOCAL void
insert_pad(wp,size)1461 insert_pad(wp, size)
1462 	ewin_t	*wp;
1463 	int	size;
1464 {
1465 		char	padding[256];
1466 	register int	idx;
1467 	register int	amt = size;
1468 
1469 	while (size > 0) {
1470 		if (amt >= sizeof (padding))
1471 			amt = sizeof (padding) - 1;
1472 
1473 		for (idx = 0; idx < amt; )
1474 			padding[idx++] = ' ';
1475 		padding[idx] = '\0';
1476 		INSERT_CHAR(wp, padding);
1477 		size -= amt;
1478 	}
1479 }
1480