1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 #include "jove.h"
9 #include "fp.h"
10 #include "chars.h"
11 #include "jctype.h"
12 #include "disp.h"
13 #include "extend.h"
14 #include "fmt.h"
15 #include "term.h"
16 #include "mac.h"
17 #include "screen.h"
18 #include "wind.h"
19 
20 int	AbortCnt,
21 	tabstop = 8;	/* VAR: expand tabs to this number of spaces */
22 
23 struct scrimage
24 	*DesiredScreen = NULL,
25 	*PhysScreen = NULL;
26 
27 private struct screenline   *Savelines = NULL;	/* scratch entries (LI of them) */
28 
29 private void LEclear proto((struct screenline *));	/* free s_effects component */
30 
31 private char	*cursor;			/* offset into current Line */
32 
33 char	*cursend;
34 
35 int	CapCol,
36 	CapLine;
37 
38 private int
39 	i_line,
40 	i_col;
41 
42 void
make_scr()43 make_scr()
44 {
45 	register int	i;
46 	register struct screenline	*ns;
47 	register char	*nsp;
48 	static char	*screenchars = NULL;
49 	static volatile int	oldLI = 0;
50 
51 	/* In case we are RESHAPING the window! */
52 	if (DesiredScreen != NULL)
53 		free((UnivPtr) DesiredScreen);
54 	if (PhysScreen != NULL)
55 		free((UnivPtr) PhysScreen);
56 	i = oldLI;
57 	oldLI = 0;
58 	if (Savelines != NULL) {
59 		/* Note: each screenline in Savelines has a null s_effects
60 		 * (or is uninitialized).  LEclear must not be applied.
61 		 */
62 		free((UnivPtr) Savelines);
63 	}
64 	if (Screen != NULL) {
65 #ifdef HIGHLIGHTING
66 		for (ns = Screen; ns != &Screen[i]; ns++)
67 			LEclear(ns);
68 #endif
69 		free((UnivPtr) Screen);
70 	}
71 	if (screenchars != NULL)
72 		free((UnivPtr) screenchars);	/* free all the screen data */
73 
74 	DesiredScreen = (struct scrimage *) malloc((unsigned) LI * sizeof (struct scrimage));
75 	PhysScreen = (struct scrimage *) malloc((unsigned) LI * sizeof (struct scrimage));
76 
77 	Savelines = (struct screenline *)
78 			malloc((unsigned) LI * sizeof(struct screenline));
79 	ns = Screen = (struct screenline *)
80 			malloc((unsigned) LI * sizeof(struct screenline));
81 
82 	nsp = screenchars = (char *) malloc((unsigned)CO * LI);
83 
84 	if (DesiredScreen == NULL
85 	|| PhysScreen == NULL
86 	|| Savelines == NULL
87 	|| ns == NULL
88 	|| nsp == NULL)
89 	{
90 		writef("\n\rCannot malloc screen!\n");
91 		finish(-1);	/* die! */
92 	}
93 
94 	for (i = 0; i < LI; i++) {
95 		ns->s_line = nsp;
96 		/* End of Line (nsp[CO-1] is never used) */
97 		ns->s_roof = nsp + CO - 1;
98 		ns->s_effects = NOEFFECT;
99 		nsp += CO;
100 		ns += 1;
101 
102 		/* ??? The following is a fudge to placate Purify.
103 		 * There is a real bug here, so we squash it with
104 		 * a sledge hammer.  What is the correct fix?
105 		 */
106 		{
107 			register struct scrimage	*p;
108 
109 			p = &PhysScreen[i];
110 			p->s_offset = 0;
111 			p->s_flags = 0;
112 			p->s_vln = 0;
113 			p->s_id = NULL_DADDR;
114 			p->s_lp = NULL;
115 			p->s_window = NULL;
116 
117 			p = &DesiredScreen[i];
118 			p->s_offset = 0;
119 			p->s_flags = 0;
120 			p->s_vln = 0;
121 			p->s_id = NULL_DADDR;
122 			p->s_lp = NULL;
123 			p->s_window = NULL;
124 		}
125 	}
126 	oldLI = LI;
127 	SO_off();
128 #ifdef HIGHLIGHTING
129 	US_effect(NO);
130 #endif
131 	cl_scr(NO);
132 }
133 
134 void
clrline(cp1,cp2)135 clrline(cp1, cp2)
136 register char	*cp1,
137 		*cp2;
138 {
139 	while (cp1 < cp2)
140 		*cp1++ = ' ';
141 }
142 
143 
144 /* Output one character (if necessary) at the current position */
145 
146 #ifdef MAC
147 
148 /* Character output to bit-mapped screen is very expensive. It makes
149    much more sense to write the entire line at once. So, we print all
150    the characters, whether already there or not, once the line is
151    complete. */
152 
153 private unsigned char sput_buf[255];
154 private size_t sput_len = 0;
155 
156 private void
sput_start()157 sput_start()
158 {
159 /*	if (i_line != CapLine || i_col != CapCol) */
160 		NPlacur(i_line, i_col);
161 	sput_len = 0;
162 }
163 
164 private void
sput_end()165 sput_end()
166 {
167 	if (sput_len != 0) {
168 		writetext(sput_buf, sput_len);
169 		sput_len = 0;
170 	}
171 }
172 
173 private void
sputc(c)174 sputc(c)
175 register char c;
176 {
177 	/* if line gets too long for sput_buf, ignore subsequent chars */
178 	if (sput_len < sizeof(sput_buf)) {
179 		*cursor++ = c;
180 		sput_buf[sput_len++] = (c == '0')? 0xAF /* slashed zero */ : c;
181 		CapCol++;
182 		i_col++;
183 	}
184 }
185 
186 #else /* !MAC */
187 
188 # ifdef HIGHLIGHTING
189 #  define	CharChanged(c)	(*cursor != (char) (c))
190 # else /* !HIGHLIGHTING */
191 private bool	ChangeEffect = NO;
192 #  define	CharChanged(c)	(ChangeEffect || *cursor != (char) (c))
193 # endif /* !HIGHLIGHTING */
194 
195 # ifdef IBMPCDOS
196 /* On PC, we think that trying to avoid painting the character
197  * is slower than just doing it.  I wonder if this is true.
198  */
199 #  define	sputc(c)	do_sputc(c)
200 # else /* !IBMPCDOS */
201 #  define sputc(c)	{ \
202 	if (CharChanged(c)) { \
203 		do_sputc(c); \
204 	} else { \
205 		cursor += 1; \
206 		i_col += 1; \
207 	} \
208 }
209 # endif /* !IBMPCDOS */
210 
211 private void
do_sputc(c)212 do_sputc(c)
213 register char	c;
214 {
215 	if (CharChanged(c)) {
216 # ifdef ID_CHAR
217 		INSmode(NO);
218 # endif
219 		if (i_line != CapLine || i_col != CapCol)
220 			Placur(i_line, i_col);
221 		*cursor++ = c;
222 # ifdef TERMCAP
223 		if (UL && c == '_' && *cursor != ' ')
224 			putstr(" \b");		/* Erase so '_' looks right. */
225 # endif
226 		scr_putchar(c);
227 		AbortCnt -= 1;
228 		CapCol += 1;
229 	} else {
230 		cursor += 1;
231 	}
232 	i_col += 1;
233 }
234 #endif /* !MAC */
235 
236 #ifdef HIGHLIGHTING
237 
238 private void	(*real_effect) ptrproto((bool));
239 
240 private void
do_hlsputc(hl,oldhl,c)241 do_hlsputc(hl, oldhl, c)
242 register const struct LErange *hl;	/* desired highlighting */
243 register const struct LErange *oldhl;	/* previous highlighting */
244 char c;
245 {
246 	/* assert: hl != NULL && oldhl != NULL
247 	 * In other words, hl and oldhl must point to real LErange structs.
248 	 */
249 
250 	/* The following two initializing expressions use the peculiar
251 	 * properties of unsigneds to make an efficient range test.
252 	 */
253 	void
254 		(*virtual_effect) ptrproto((bool)) =
255 			(unsigned)i_col - hl->start < hl->width? hl->high : hl->norm,
256 		(*underlying_effect) ptrproto((bool)) =
257 			(unsigned)i_col - oldhl->start < oldhl->width? oldhl->high : oldhl->norm;
258 
259 	if (*cursor != c || virtual_effect != underlying_effect) {
260 # ifdef ID_CHAR
261 		INSmode(NO);
262 # endif
263 		if (i_line != CapLine || i_col != CapCol)
264 			Placur(i_line, i_col);
265 		if (virtual_effect != real_effect) {
266 			if (real_effect != NULL)
267 				real_effect(NO);
268 			/* instantaneously in neutral state */
269 			if (virtual_effect != NULL)
270 				virtual_effect(YES);
271 			real_effect = virtual_effect;
272 		}
273 # ifdef TERMCAP
274 		if (UL && c == '_' && *cursor != ' ')
275 			putstr(" \b");		/* Erase so '_' looks right. */
276 # endif
277 		*cursor++ = c;
278 		scr_putchar(c);
279 		AbortCnt -= 1;
280 		CapCol += 1;
281 	} else {
282 		cursor += 1;
283 	}
284 	i_col += 1;
285 }
286 
287 #endif /* HIGHLIGHTING */
288 
289 void
cl_eol()290 cl_eol()
291 {
292 	if (cursor == Curline->s_line)
293 		LEclear(Curline);	/* in case swrite was not called (hack!) */
294 
295 	if (cursor < Curline->s_roof) {
296 #ifdef TERMCAP
297 		if (CE) {
298 			Placur(i_line, i_col);
299 			putpad(CE, 1);
300 			clrline(cursor, Curline->s_roof);
301 		} else {
302 			/* Ugh.  The slow way for dumb terminals. */
303 			register char *savecp = cursor;
304 
305 			while (cursor < Curline->s_roof)
306 				sputc(' ');
307 			cursor = savecp;
308 		}
309 #else /* !TERMCAP */
310 		Placur(i_line, i_col);
311 		clr_eoln();	/* MAC and PCSCR define this */
312 		clrline(cursor, Curline->s_roof);
313 #endif /* !TERMCAP */
314 		Curline->s_roof = cursor;
315 	}
316 }
317 
318 void
cl_scr(doit)319 cl_scr(doit)
320 bool doit;
321 {
322 	register int	i;
323 	register struct screenline	*sp = Screen;
324 
325 	for (i = 0; i < LI; i++, sp++) {
326 		LEclear(sp);
327 		clrline(sp->s_line, sp->s_roof);
328 		sp->s_roof = sp->s_line;
329 		PhysScreen[i].s_id = NULL_DADDR;
330 	}
331 	if (doit) {
332 		clr_page();
333 		CapCol = CapLine = 0;
334 		UpdMesg = YES;
335 	}
336 }
337 
338 /* routines to manage a pool of LErange structs */
339 
340 #ifdef HIGHLIGHTING
341 
342 union LEspace {
343 	struct LErange	le;
344 	union LEspace	*next;
345 };
346 
347 private union LEspace *LEfreeHead = NULL;
348 
349 private struct LErange *
LEnew()350 LEnew()
351 {
352 	struct LErange	*ret;
353 
354 	if (LEfreeHead == NULL) {
355 		LEfreeHead = (union LEspace *) emalloc(sizeof(union LEspace));
356 		LEfreeHead->next = NULL;
357 	}
358 	ret = &LEfreeHead->le;
359 	LEfreeHead = LEfreeHead->next;
360 	return ret;
361 }
362 
363 #endif /* HIGHLIGHTING */
364 
365 private void
LEclear(sl)366 LEclear(sl)
367 struct screenline *sl;
368 {
369 #ifdef HIGHLIGHTING
370 	if (sl->s_effects != NOEFFECT) {
371 		((union LEspace *) sl->s_effects)->next = LEfreeHead;
372 		LEfreeHead = (union LEspace *) sl->s_effects;
373 	}
374 #endif /* HIGHLIGHTING */
375 	sl->s_effects = NOEFFECT;
376 }
377 
378 /* Write `line' at the current position of `cursor'.  Stop when we
379  * reach the end of the screen.  Aborts if there is a character
380  * waiting.
381  *
382  * Note: All callers must have "DeTabed" "line", or processed
383  * it equivalently -- it is presumed that line contains only
384  * displayable characters.
385  */
386 
387 bool
swrite(line,hl,abortable)388 swrite(line, hl, abortable)
389 register char	*line;
390 LineEffects	hl;
391 bool	abortable;
392 {
393 	register int	n = cursend - cursor;
394 	bool	aborted = NO;
395 	/* Unfortunately, neither of our LineEffects representation
396 	 * is suitable for representing the state of a partially
397 	 * updated line.  Consequently, this routine unconditionally
398 	 * replaces the old hl with the new.  To ensure that the new
399 	 * hl is correct, we compute MinCol to indicate how far in
400 	 * the line we must get, and will not abort until we have
401 	 * reached at least that column.
402 	 *
403 	 * This is unacceptably ugly.  We really must switch to a better
404 	 * representation
405 	 */
406 	int	MinCol = 0;
407 #ifdef HIGHLIGHTING
408 	/* If either the old line or the new line has effects,
409 	 * we know that some effects processing is necessary.
410 	 * If so, we ensure that the old line has an effect
411 	 * by adding a no-op effect if necessary: this is needed
412 	 * to ensure Placur does not get into trouble.  (UGLY!)
413 	 */
414 	struct LErange	*oldhl = Curline->s_effects;
415 	static const struct LErange	nohl = { 0, 0, NULL, NULL };
416 
417 	if (oldhl != NOEFFECT) {
418 		int	w = Curline->s_roof - Curline->s_line;
419 
420 		if (oldhl->norm != NULL)
421 			MinCol = w;
422 		if (oldhl->high != NULL && w > (int)oldhl->start)
423 			MinCol = max(MinCol, min(w, (int) (oldhl->start + oldhl->width)));
424 	}
425 	/* If either the old line or the new line has effects,
426 	 * we know that some effects processing is necessary.
427 	 * If so, we ensure that the old line has an effect
428 	 * by adding a no-op effect if necessary: this is needed
429 	 * to ensure Placur does not get into trouble.  (UGLY!)
430 	 */
431 	if (hl != NOEFFECT) {
432 		if (hl->high != NULL)
433 			MinCol = max(MinCol, (int) (hl->start + hl->width));
434 		if (oldhl == NOEFFECT) {
435 			oldhl = Curline->s_effects = LEnew();	/* keep Placur on-track */
436 			*oldhl = nohl;
437 		}
438 	}
439 	real_effect = NULL;
440 #else /* !HIGHLIGHTING */
441 	if (Curline->s_effects != hl)
442 		MinCol = Curline->s_roof - Curline->s_line;	/* must obliterate old */
443 #endif /* !HIGHLIGHTING */
444 
445 	if (n > 0) {
446 		register ZXchar	c;
447 		int	col = i_col;
448 
449 #ifdef HIGHLIGHTING
450 		/* nnhl: non-NULL version of hl (possibly
451 		 * a no-op) to reduce the cases handled.
452 		 */
453 		const struct LErange	*nnhl = hl == NOEFFECT? &nohl : hl;
454 #		define	spit(c)	{ if (oldhl != NULL) do_hlsputc(nnhl,oldhl,c); else sputc(c); }
455 
456 #else /* !HIGHLIGHTING */
457 
458 #		define	spit(c)	sputc(c)
459 
460 # ifdef MAC
461 		sput_start();	/* Okay, because no interruption possible */
462 # else /* !MAC */
463 		if (hl != Curline->s_effects)
464 			ChangeEffect = YES;
465 # endif /* !MAC */
466 
467 		if (hl != NOEFFECT)
468 			SO_effect(YES);
469 #endif /* !HIGHLIGHTING */
470 
471 		while ((c = ZXC(*line++)) != '\0') {
472 			if (abortable && i_col >= MinCol && AbortCnt < 0) {
473 				AbortCnt = ScrBufSize;
474 				if (PreEmptOutput()) {
475 					aborted = YES;
476 					break;
477 				}
478 			}
479 #ifdef TERMCAP
480 			if (Hazeltine && c == '~')
481 				c = '`';
482 #endif
483 #ifdef CODEPAGE437
484 			/* ??? Some archane mapping of IBM PC characters.
485 			 * According to the appendix of the Microsoft MSDOS
486 			 * Operating System 5.0 User's Guide and Reference,
487 			 * in Code Page 437 (USA English) ' ', 0x00, and 0xFF are
488 			 * blank and 0x01 is a face.
489 			 */
490 			if (c == 0xFF)
491 				c = 1;
492 			else if (c == ' ' && hl != NOEFFECT)
493 				c = 0xFF;
494 #endif /* CODEPAGE437 */
495 			if (--n <= 0) {
496 				/* We've got one more column -- how will we spend it?
497 				 * ??? This is probably redundant -- callers do truncation.
498 				 */
499 				if (*line != '\0')
500 					c = '!';
501 				spit(c);
502 				break;
503 			}
504 			spit(c);
505 			col += 1;
506 		}
507 #ifdef HIGHLIGHTING
508 		if (real_effect != NULL)
509 			real_effect(NO);
510 #else /* !HIGHLIGHTING */
511 # ifdef MAC
512 		sput_end();	/* flush before reverting SO */
513 # else /* !MAC */
514 		ChangeEffect = NO;
515 # endif /* !MAC */
516 		if (hl != NOEFFECT)
517 			SO_off();
518 #endif /* !HIGHLIGHTING */
519 		if (cursor > Curline->s_roof)
520 			Curline->s_roof = cursor;
521 #		undef	spit
522 	}
523 #ifdef HIGHLIGHTING
524 	if (hl == NOEFFECT)
525 		LEclear(Curline);
526 	else
527 		*(Curline->s_effects) = *hl;
528 #else /* !HIGHLIGHTING */
529 	Curline->s_effects = hl;
530 #endif /* !HIGHLIGHTING */
531 	return !aborted;
532 }
533 
534 void
i_set(nline,ncol)535 i_set(nline, ncol)
536 register int	nline,
537 		ncol;
538 {
539 	Curline = &Screen[nline];
540 	cursor = Curline->s_line + ncol;
541 	cursend = &Curline->s_line[CO - 1];
542 	i_line = nline;
543 	i_col = ncol;
544 }
545 
546 void
SO_off()547 SO_off()
548 {
549 	SO_effect(NO);
550 }
551 
552 #ifdef TERMCAP
553 
554 void
SO_effect(on)555 SO_effect(on)
556 bool	on;
557 {
558 	/* If there are magic cookies, then WHERE the SO string is
559 	   printed decides where the SO actually starts on the screen.
560 	   So it's important to make sure the cursor is positioned there
561 	   anyway.  I think this is right. */
562 	if (SG != 0) {
563 		Placur(i_line, i_col);
564 		i_col += SG;
565 		CapCol += SG;
566 		cursor += SG;
567 	}
568 	putpad(on? SO : SE, 1);
569 }
570 
571 # ifdef HIGHLIGHTING
572 void
US_effect(on)573 US_effect(on)
574 bool	on;
575 {
576 	if (UG == 0)	/* not used if magic cookies */
577 		putpad(on? US : UE, 1);
578 }
579 # endif /* HIGHLIGHTING */
580 
581 #endif /* TERMCAP */
582 
583 /* Insert `num' lines at top, but leave all the lines BELOW `bottom'
584    alone (at least they won't look any different when we are done).
585    This changes the screen array AND does the physical changes. */
586 
587 void
v_ins_line(num,top,bottom)588 v_ins_line(num, top, bottom)
589 int num,
590     top,
591     bottom;
592 {
593 	register int	i;
594 
595 	/* assert(num <= bottom-top+1) */
596 
597 	/* Blank and save the screen pointers that will fall off the end. */
598 
599 	for(i = 0; i < num; i++) {
600 		struct screenline	*sp = &Screen[bottom - i];
601 
602 		clrline(sp->s_line, sp->s_roof);
603 		sp->s_roof = sp->s_line;
604 		LEclear(sp);
605 		Savelines[i] = *sp;
606 	}
607 
608 	/* Num number of bottom lines will be lost.
609 	   Copy everything down num number of times. */
610 
611 	for (i = bottom-num; i >= top; i--)
612 		Screen[i + num] = Screen[i];
613 
614 	/* Insert the now-blank saved ones at the top. */
615 
616 	for (i = 0; i < num; i++)
617 		Screen[top + i] = Savelines[i];
618 	i_lines(top, bottom, num);
619 }
620 
621 /* Delete `num' lines starting at `top' leaving the lines below `bottom'
622    alone.  This updates the internal image as well as the physical image.  */
623 
624 void
v_del_line(num,top,bottom)625 v_del_line(num, top, bottom)
626 int num,
627     top,
628     bottom;
629 {
630 	register int	i;
631 
632 	/* assert(num <= bottom-top+1) */
633 
634 	/* Blank and save the lines to be deleted from the top. */
635 
636 	for (i = 0; i < num; i++) {
637 		struct screenline	*sp = &Screen[top + i];
638 
639 		clrline(sp->s_line, sp->s_roof);
640 		sp->s_roof = sp->s_line;
641 		LEclear(sp);
642 		Savelines[i] = *sp;
643 	}
644 
645 	/* Copy everything up num number of lines. */
646 
647 	for (i = top; i + num <= bottom; i++)
648 		Screen[i] = Screen[i + num];
649 
650 	/* Restore the now-blank lost lines */
651 
652 	for (i = 0; i < num; i++)
653 		Screen[bottom - i] = Savelines[i];
654 	d_lines(top, bottom, num);
655 }
656 
657 #ifdef TERMCAP	/* remainder of this file */
658 
659 /* The cursor optimization happens here.  You may decide that this
660    is going too far with cursor optimization, or perhaps it should
661    limit the amount of checking to when the output speed is slow.
662    What ever turns you on ...   */
663 
664 struct cursaddr {
665 	int	cm_numchars;
666 	void	(*cm_proc) ();
667 };
668 
669 private char	*Cmstr;
670 private struct cursaddr	*HorMin,
671 			*VertMin,
672 			*DirectMin;
673 
674 private void
675 	ForTab proto((int)),
676 	RetTab proto((int)),
677 	DownMotion proto((int)),
678 	UpMotion proto((int)),
679 	GoDirect proto((int, int)),
680 	HomeGo proto((int, int)),
681 	BottomUp proto((int, int));
682 
683 
684 private struct cursaddr	WarpHor[] = {
685 	{ 0,	ForTab },
686 	{ 0,	RetTab }
687 };
688 
689 private struct cursaddr	WarpVert[] = {
690 	{ 0,	DownMotion },
691 	{ 0,	UpMotion }
692 };
693 
694 private struct cursaddr	WarpDirect[] = {
695 	{ 0,	GoDirect },
696 	{ 0,	HomeGo },
697 	{ 0,	BottomUp }
698 };
699 
700 # define FORTAB		0	/* Forward using tabs */
701 # define RETFORTAB	1	/* Beginning of line and then tabs */
702 # define NUMHOR		2
703 
704 # define DOWN		0	/* Move down */
705 # define UPMOVE		1	/* Move up */
706 # define NUMVERT		2
707 
708 # define DIRECT		0	/* Using CM */
709 # define HOME		1	/* HOME	*/
710 # define LOWER		2	/* Lower Line */
711 # define NUMDIRECT	3
712 
713 # define	home()		Placur(0, 0)
714 # define LowLine()	{ putpad(LL, 1); CapLine = ILI; CapCol = 0; }
715 # define PrintHo()	{ putpad(HO, 1); CapLine = CapCol = 0; }
716 
717 private void
GoDirect(line,col)718 GoDirect(line, col)
719 register int	line,
720 		col;
721 {
722 	putpad(Cmstr, 1);
723 	CapLine = line;
724 	CapCol = col;
725 }
726 
727 private void
RetTab(col)728 RetTab(col)
729 register int	col;
730 {
731 	scr_putchar('\r');
732 	CapCol = 0;
733 	ForTab(col);
734 }
735 
736 private void
HomeGo(line,col)737 HomeGo(line, col)
738 int line,
739     col;
740 {
741 	PrintHo();
742 	DownMotion(line);
743 	ForTab(col);
744 }
745 
746 private void
BottomUp(line,col)747 BottomUp(line, col)
748 register int	line,
749 		col;
750 {
751 	LowLine();
752 	UpMotion(line);
753 	ForTab(col);
754 }
755 
756 /* Tries to move forward using tabs (if possible).  It tabs to the
757    closest tabstop which means it may go past 'destcol' and backspace
758    to it.
759    Note: changes to this routine must be matched by changes in ForNum. */
760 
761 private void
ForTab(to)762 ForTab(to)
763 int	to;
764 {
765 	if ((to > CapCol+1) && TABS && (phystab > 0)) {
766 		register int	tabgoal,
767 				ntabs,
768 				pts = phystab;
769 
770 		tabgoal = to + (pts / 2);
771 		tabgoal -= (tabgoal % pts);
772 
773 		/* Don't tab to last place or else it is likely to screw up. */
774 		if (tabgoal >= CO)
775 			tabgoal -= pts;
776 
777 		ntabs = (tabgoal / pts) - (CapCol / pts);
778 		/* If tabbing moves past goal, and goal is more cols back
779 		 * than we would have had to move forward from our original
780 		 * position, tab is counterproductive.  Notice that if our
781 		 * original motion would have been backwards, tab loses too,
782 		 * so we need not write abs(to-CapCol).
783 		 */
784 		if (tabgoal > to && tabgoal-to >= to-CapCol)
785 			ntabs = 0;
786 		while (--ntabs >= 0) {
787 			scr_putchar('\t');
788 			CapCol = tabgoal;	/* idempotent */
789 		}
790 	}
791 
792 	if (to > CapCol) {
793 		register char	*cp = &Screen[CapLine].s_line[CapCol];
794 
795 # ifdef ID_CHAR
796 		INSmode(NO);	/* we're not just a motion */
797 # endif
798 		while (to > CapCol) {
799 			scr_putchar(*cp++);
800 			CapCol++;
801 		}
802 	}
803 
804 	while (to < CapCol) {
805 		putpad(BC, 1);
806 		CapCol--;
807 	}
808 }
809 
810 private void
DownMotion(destline)811 DownMotion(destline)
812 register int	destline;
813 {
814 	register int	nlines = destline - CapLine;
815 
816 	while (--nlines >= 0) {
817 		putpad(DO, 1);
818 		CapLine = destline;	/* idempotent */
819 	}
820 }
821 
822 private void
UpMotion(destline)823 UpMotion(destline)
824 register int	destline;
825 {
826 	register int	nchars = CapLine - destline;
827 
828 	while (--nchars >= 0) {
829 		putpad(UP, 1);
830 		CapLine = destline;	/* idempotent */
831 	}
832 }
833 
834 private int ForNum proto((int from, int to));
835 
836 void
Placur(line,col)837 Placur(line, col)
838 int line,
839     col;
840 {
841 	int	dline,		/* Number of lines to move */
842 		dcol;		/* Number of columns to move */
843 	register int	best,
844 			i;
845 	register struct cursaddr	*cp;
846 	int	xtracost = 0;	/* Misc addition to cost. */
847 
848 # define CursMin(which,addrs,max)	{ \
849 	for (best = 0, cp = &(addrs)[1], i = 1; i < (max); i++, cp++) \
850 		if (cp->cm_numchars < (addrs)[best].cm_numchars) \
851 			best = i; \
852 	(which) = &(addrs)[best]; \
853 }
854 
855 	if (line == CapLine && col == CapCol)
856 		return;		/* We are already there. */
857 
858 	dline = line - CapLine;
859 	dcol = col - CapCol;
860 # ifdef ID_CHAR
861 	if (IN_INSmode && MI)
862 		xtracost = EIlen + IMlen;
863 	/* If we're already in insert mode, it is likely that we will
864 	   want to be in insert mode again, after the insert. */
865 # endif
866 
867 	/* Number of characters to move horizontally for each case.
868 	   1: Try tabbing to the correct place.
869 	   2: Try going to the beginning of the line, and then tab. */
870 
871 	if (dcol == 1 || dcol == 0) {		/* Most common case. */
872 		HorMin = &WarpHor[FORTAB];
873 		HorMin->cm_numchars = dcol + xtracost;
874 	} else {
875 		WarpHor[FORTAB].cm_numchars = xtracost + ForNum(CapCol, col);
876 		WarpHor[RETFORTAB].cm_numchars = xtracost + 1 + ForNum(0, col);
877 
878 		/* Which is the shortest of the bunch */
879 
880 		CursMin(HorMin, WarpHor, NUMHOR);
881 	}
882 
883 	/* Moving vertically is more simple. */
884 
885 	WarpVert[DOWN].cm_numchars = dline >= 0 ? dline : INFINITY;
886 	WarpVert[UPMOVE].cm_numchars = dline < 0 ? ((-dline) * UPlen) : INFINITY;
887 
888 	/* Which of these is simpler */
889 	CursMin(VertMin, WarpVert, NUMVERT);
890 
891 	/* Homing first and lowering first are considered
892 	   direct motions.
893 	   Homing first's total is the sum of the cost of homing
894 	   and the sum of tabbing (if possible) to the right. */
895 
896 	if (Screen[line].s_effects != NOEFFECT && CM != NULL) {
897 		/* We are going to a line with inversion or underlining;
898 		   Don't try any clever stuff */
899 		DirectMin = &WarpDirect[DIRECT];
900 		DirectMin->cm_numchars = 0;
901 		Cmstr = targ2(CM, col, line);
902 	} else if (VertMin->cm_numchars + HorMin->cm_numchars <= 3) {
903 		/* Since no direct method is ever shorter than 3 chars, don't try it. */
904 		DirectMin = &WarpDirect[DIRECT];	/* A dummy ... */
905 		DirectMin->cm_numchars = INFINITY;
906 	} else {
907 		WarpDirect[DIRECT].cm_numchars = CM != NULL ?
908 				strlen(Cmstr = targ2(CM, col, line)) : INFINITY;
909 		WarpDirect[HOME].cm_numchars = HOlen + line +
910 				WarpHor[RETFORTAB].cm_numchars;
911 		WarpDirect[LOWER].cm_numchars = LLlen + ((ILI - line) * UPlen) +
912 				WarpHor[RETFORTAB].cm_numchars;
913 		CursMin(DirectMin, WarpDirect, NUMDIRECT);
914 	}
915 
916 	if (HorMin->cm_numchars + VertMin->cm_numchars < DirectMin->cm_numchars) {
917 		if (line != CapLine)
918 			(*(void (*)ptrproto((int)))VertMin->cm_proc)(line);
919 		if (col != CapCol) {
920 # ifdef ID_CHAR
921 			INSmode(NO);	/* We may use real characters ... */
922 # endif
923 			(*(void (*)ptrproto((int)))HorMin->cm_proc)(col);
924 		}
925 	} else {
926 # ifdef ID_CHAR
927 		if (IN_INSmode && !MI)
928 			INSmode(NO);
929 # endif
930 		(*(void (*)ptrproto((int, int)))DirectMin->cm_proc)(line, col);
931 	}
932 }
933 
934 
935 /* Figures out how many characters ForTab() would use to move forward
936    using tabs (if possible).
937    Note: changes to this routine must be matched by changes in ForTab.
938    An exception is that any cost for leaving insert mode has been
939    accounted for by our caller. */
940 
941 private int
ForNum(from,to)942 ForNum(from, to)
943 register int	from;
944 int to;
945 {
946 	register int	tabgoal,
947 			pts = phystab;
948 	int		ntabs = 0;
949 
950 	if ((to > from+1) && TABS && (pts > 0)) {
951 		tabgoal = to + (pts / 2);
952 		tabgoal -= (tabgoal % pts);
953 		if (tabgoal >= CO)
954 			tabgoal -= pts;
955 		ntabs = (tabgoal / pts) - (from / pts);
956 		/* If tabbing moves past goal, and goal is more cols back
957 		 * than we would have had to move forward from our original
958 		 * position, tab is counterproductive.  Notice that if our
959 		 * original motion would have been backwards, tab loses too,
960 		 * so we need not write abs(to-from).
961 		 */
962 		if (tabgoal > to && tabgoal-to >= to-from)
963 			ntabs = 0;
964 		if (ntabs != 0)
965 			from = tabgoal;
966 	}
967 	return ntabs + (from>to? from-to : to-from);
968 }
969 
970 void
i_lines(top,bottom,num)971 i_lines(top, bottom, num)
972 int top,
973     bottom,
974     num;
975 {
976 	if (CS) {
977 		putpad(targ2(CS, bottom, top), 1);
978 		CapCol = CapLine = 0;
979 		Placur(top, 0);
980 		putmulti(SR, M_SR, num, bottom - top);
981 		putpad(targ2(CS, ILI, 0), 1);
982 		CapCol = CapLine = 0;
983 	} else {
984 		Placur(bottom - num + 1, 0);
985 		putmulti(DL, M_DL, num, ILI - CapLine);
986 		Placur(top, 0);
987 		putmulti(AL, M_AL, num, ILI - CapLine);
988 	}
989 }
990 
991 void
d_lines(top,bottom,num)992 d_lines(top, bottom, num)
993 int top,
994     bottom,
995     num;
996 {
997 	if (CS) {
998 		putpad(targ2(CS, bottom, top), 1);
999 		CapCol = CapLine = 0;
1000 		Placur(bottom, 0);
1001 		putmulti(SF, M_SF, num, bottom - top);
1002 		putpad(targ2(CS, ILI, 0), 1);
1003 		CapCol = CapLine = 0;
1004 	} else {
1005 		Placur(top, 0);
1006 		putmulti(DL, M_DL, num, ILI - top);
1007 		Placur(bottom + 1 - num, 0);
1008 		putmulti(AL, M_AL, num, ILI - CapLine);
1009 	}
1010 }
1011 
1012 #endif /* TERMCAP */
1013