1 /*
2  *	device-independent TTY interface for JOE
3  *	Copyright
4  *		(C) 1992 Joseph H. Allen
5  *
6  *	This file is part of JOE (Joe's Own Editor)
7  */
8 #include "config.h"
9 #include "types.h"
10 
11 __RCSID("$MirOS: contrib/code/jupp/scrn.c,v 1.45 2018/06/28 03:11:13 tg Exp $");
12 
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include "bw.h"
17 #include "blocks.h"
18 #include "scrn.h"
19 #include "termcap.h"
20 #include "charmap.h"
21 #include "utils.h"
22 
23 int skiptop = 0;
24 int lines = 0;
25 int columns = 0;
26 int notite = 0;
27 int pastetite = 0;
28 int usetabs = 0;
29 int assume_color = 0;
30 
31 static int
xlat(int chkasis,int c,int * ap,struct charmap * map)32 xlat(int chkasis, int c, int *ap, struct charmap *map)
33 {
34 	if (joe_isprint(map, c))
35 		return (c);
36 	if (chkasis && dspasis && c >= 128)
37 		return (c);
38 
39 	if (c & 0x80) {
40 		c &= 0x7F;
41 		*ap ^= INVERSE;
42 	}
43 	if (c < 0x20 || c == 0x7F) {
44 		c ^= 0x40;
45 		*ap ^= UNDERLINE;
46 	}
47 	return (c);
48 }
49 
50 /* Set attributes */
51 
set_attr(SCRN * t,int c)52 int set_attr(SCRN *t, int c)
53 {
54 	int e;
55 
56 	c &= ~255;
57 
58 	/* Attributes which have gone off */
59 	e = ((AT_MASK|FG_NOT_DEFAULT|BG_NOT_DEFAULT)&t->attrib & ~c);
60 
61 	if (e) {	/* If any attribute go off, switch them all off: fixes bug on PCs */
62 		if (t->me)
63 			texec(t->cap, t->me, 1, 0, 0, 0, 0);
64 		else {
65 			if (t->ue)
66 				texec(t->cap, t->ue, 1, 0, 0, 0, 0);
67 			if (t->se)
68 				texec(t->cap, t->se, 1, 0, 0, 0, 0);
69 		}
70 		t->attrib = 0;
71 	}
72 
73 	/* Attributes which have turned on */
74 	e = (c & ~t->attrib);
75 
76 	if (e & INVERSE) {
77 		if (t->mr)
78 			texec(t->cap, t->mr, 1, 0, 0, 0, 0);
79 		else if (t->so)
80 			texec(t->cap, t->so, 1, 0, 0, 0, 0);
81 	}
82 
83 	if (e & UNDERLINE)
84 		if (t->us)
85 			texec(t->cap, t->us, 1, 0, 0, 0, 0);
86 	if (e & BLINK)
87 		if (t->mb)
88 			texec(t->cap, t->mb, 1, 0, 0, 0, 0);
89 	if (e & BOLD)
90 		if (t->md)
91 			texec(t->cap, t->md, 1, 0, 0, 0, 0);
92 	if (e & DIM)
93 		if (t->mh)
94 			texec(t->cap, t->mh, 1, 0, 0, 0, 0);
95 
96 	if ((t->attrib&FG_MASK)!=(c&FG_MASK))
97 		if (t->Sf) texec(t->cap,t->Sf,1,7-(((c&FG_VALUE)>>FG_SHIFT)),0,0,0);
98 
99 	if ((t->attrib&BG_MASK)!=(c&BG_MASK))
100 		if (t->Sb) texec(t->cap,t->Sb,1,((c&BG_VALUE)>>BG_SHIFT),0,0,0);
101 
102 	t->attrib = c;
103 
104 	return 0;
105 }
106 
107 /* Output character with attributes */
108 
109 void
outatr_help(SCRN * t,int * scrn,int * attrf,int xx,int yy,int c,int a)110 outatr_help(SCRN *t, int *scrn, int *attrf, int xx, int yy, int c, int a)
111 {
112 	/* kludge for help_display() only */
113 	if (locale_map->type)
114 		c = xlat(0, c, &a, locale_map);
115 	outatr(locale_map, t, scrn, attrf, xx, yy, c, a);
116 }
117 
118 void
outatr(struct charmap * map,SCRN * t,int * scrn,int * attrf,int xx,int yy,int c,int a)119 outatr(struct charmap *map, SCRN *t, int *scrn, int *attrf, int xx, int yy, int c, int a)
120 {
121 	int wid = 1;
122 
123 	if (locale_map->type) {
124 		/* to UTF-8 terminal */
125 		if (map->type) {
126 			/* from UTF-8 file */
127 			switch ((wid = unictrl(c))) {
128 			case 0:
129 				/* not a control character */
130 				wid = joe_wcwidth(c);
131 				break;
132 			case 1:
133 				c ^= 0x40;
134 				/* FALLTHROUGH */
135 			default:
136 				a ^= UNDERLINE;
137 				break;
138 			}
139 		} else {
140 			/* from SBCS file */
141 			c = to_uni(map, xlat(1, c, &a, map));
142 			if (c < 32 || (c >= 0x7F && c < 0xA0)) {
143 				c = 0xFFFD;
144 				a = (a | UNDERLINE) ^ INVERSE;
145 			}
146 		}
147 	} else {
148 		/* to SBCS terminal */
149 		if (map->type) {
150 			/* from UTF-8 file */
151 
152 			/* don't convert control chars below 256 */
153 			if ((c >= 0x20 && c < 0x7F) || c >= 0xA0) {
154 				if (unictrl(c))
155 					a ^= UNDERLINE;
156 				if ((c = from_uni(locale_map, c)) == -1) {
157 					c = '!';
158 					a |= UNDERLINE;
159 				}
160 			}
161 		}
162 		c = xlat(1, c, &a, locale_map);
163 	}
164 
165 	if (*scrn == c && *attrf == a)
166 		return;
167 
168 	*scrn = c;
169 	*attrf = a;
170 	if (t->ins)
171 		clrins(t);
172 	if (t->x != xx || t->y != yy)
173 		cpos(t, xx, yy);
174 	if (t->attrib != a)
175 		set_attr(t, a);
176 	if (!locale_map->type) {
177 		/* SBCS terminal */
178 		ttputc(c);
179 	} else if (map->type && *unictrlbuf) {
180 		/* UTF-8 control char, masked */
181 		ttputs(unictrlbuf);
182 	} else {
183 		unsigned char buf[7];
184 
185 		utf8_encode(buf, c);
186 		ttputs(buf);
187 		if (wid == 0 && xx > 0)
188 			attrf[-1] |= HAS_COMBINING;
189 	}
190 	t->x += wid;
191 	while (wid-- > 1) {
192 		*++scrn = -1;
193 		*++attrf = 0;
194 	}
195 }
196 
197 /* Set scrolling region */
198 
setregn(SCRN * t,int top,int bot)199 static void setregn(SCRN *t, int top, int bot)
200 {
201 	if (!t->cs) {
202 		t->top = top;
203 		t->bot = bot;
204 		return;
205 	}
206 	if (t->top != top || t->bot != bot) {
207 		t->top = top;
208 		t->bot = bot;
209 		texec(t->cap, t->cs, 1, top, bot - 1, 0, 0);
210 		t->x = -1;
211 		t->y = -1;
212 	}
213 }
214 
215 /* Enter insert mode */
216 
setins(SCRN * t,int x)217 static void setins(SCRN *t, int x)
218 {
219 	if (t->ins != 1 && t->im) {
220 		t->ins = 1;
221 		texec(t->cap, t->im, 1, x, 0, 0, 0);
222 	}
223 }
224 
225 /* Exit insert mode */
226 
clrins(SCRN * t)227 int clrins(SCRN *t)
228 {
229 	if (t->ins != 0) {
230 		texec(t->cap, t->ei, 1, 0, 0, 0, 0);
231 		t->ins = 0;
232 	}
233 	return 0;
234 }
235 
236 /* Erase from given screen coordinate to end of line */
237 
eraeol(SCRN * t,int x,int y)238 int eraeol(SCRN *t, int x, int y)
239 {
240 	int *s, *ss, *a, *aa;
241 	int w = t->co - x - 1;	/* Don't worry about last column */
242 
243 	if (w <= 0)
244 		return 0;
245 	s = t->scrn + y * t->co + x;
246 	a = t->attr + y * t->co + x;
247 	ss = s + w;
248 	aa = a + w;
249 	do {
250 		if (*--ss != ' ') {
251 			++ss;
252 			break;
253 		} else if (*--aa != 0) {
254 			++ss;
255 			++aa;
256 			break;
257 		}
258 	} while (ss != s);
259 	if ((ss - s > 3 || s[w] != ' ' || a[w] != 0) && t->ce) {
260 		cpos(t, x, y);
261 		set_attr(t, 0);
262 		texec(t->cap, t->ce, 1, 0, 0, 0, 0);
263 		msetI(s, ' ', w);
264 		msetI(a, 0, w);
265 	} else if (s != ss) {
266 		if (t->ins)
267 			clrins(t);
268 		if (t->x != x || t->y != y)
269 			cpos(t, x, y);
270 		if (t->attrib)
271 			set_attr(t, 0);
272 		while (s != ss) {
273 			*s = ' ';
274 			*a = 0;
275 			ttputc(' ');
276 			++t->x;
277 			++s;
278 			++a;
279 		}
280 	}
281 	return 0;
282 }
283 
out(unsigned char * t,unsigned char c)284 static void out(unsigned char *t, unsigned char c)
285 {
286 	ttputc(c);
287 }
288 
nopen(CAP * cap)289 SCRN *nopen(CAP *cap)
290 {
291 	SCRN *t = calloc(1, sizeof(SCRN));
292 	int x, y;
293 
294 	ttopen();
295 
296 	t->cap = cap;
297 	setcap(cap, baud, out, NULL);
298 
299 	t->li = getnum(t->cap, UC "li");
300 	if (t->li < 1)
301 		t->li = 24;
302 	t->co = getnum(t->cap, UC "co");
303 	if (t->co < 2)
304 		t->co = 80;
305 	x = y = 0;
306 	ttgtsz(&x, &y);
307 	if (x > 7 && y > 3) {
308 		t->li = y;
309 		t->co = x;
310 	}
311 
312 	t->haz = getflag(t->cap, UC "hz");
313 	t->os = getflag(t->cap, UC "os");
314 	t->eo = getflag(t->cap, UC "eo");
315 	if (getflag(t->cap, UC "hc"))
316 		t->os = 1;
317 	if (t->os || getflag(t->cap, UC "ul"))
318 		t->ul = 1;
319 
320 	t->xn = getflag(t->cap, UC "xn");
321 	t->am = getflag(t->cap, UC "am");
322 
323 	t->cl = jgetstr(t->cap, UC "cl");
324 	t->cd = jgetstr(t->cap, UC "cd");
325 
326 	if (!notite) {
327 		t->ti = jgetstr(t->cap, UC "ti");
328 		t->te = jgetstr(t->cap, UC "te");
329 	}
330 	if (pastetite && t->cap->paste_on && t->cap->paste_off) {
331 		if (notite) {
332 			t->ti = t->cap->paste_on;
333 			t->te = t->cap->paste_off;
334 		} else {
335 			size_t n1, n2;
336 			char *cp;
337 
338 			n1 = t->ti ? strlen(t->ti) : 0;
339 			n2 = strlen(t->cap->paste_on);
340 			cp = malloc(n1 + n2 + 1);
341 			if (t->ti)
342 				memcpy(cp, t->ti, n1);
343 			memcpy(cp + n1, t->cap->paste_on, n2 + 1);
344 			t->ti = cp;
345 
346 			n1 = t->te ? strlen(t->te) : 0;
347 			n2 = strlen(t->cap->paste_off);
348 			cp = malloc(n1 + n2 + 1);
349 			memcpy(cp, t->cap->paste_off, n2 + 1);
350 			if (t->te)
351 				memcpy(cp + n2, t->te, n1 + 1);
352 			t->te = cp;
353 		}
354 	}
355 
356 	t->ut = getflag(t->cap, UC "ut");
357 	t->Sb = jgetstr(t->cap, UC "AB");
358 	if (!t->Sb) t->Sb = jgetstr(t->cap, UC "Sb");
359 	t->Sf = jgetstr(t->cap, UC "AF");
360 	if (!t->Sf) t->Sf = jgetstr(t->cap, UC "Sf");
361 
362 	if (!(t->me = jgetstr(t->cap, UC "me")))
363 		goto oops;
364 	if ((t->mb = jgetstr(t->cap, UC "mb")))
365 		t->avattr |= BLINK;
366 	if ((t->md = jgetstr(t->cap, UC "md")))
367 		t->avattr |= BOLD;
368 	if ((t->mh = jgetstr(t->cap, UC "mh")))
369 		t->avattr |= DIM;
370 	if ((t->mr = jgetstr(t->cap, UC "mr")))
371 		t->avattr |= INVERSE;
372  oops:
373 
374 	if (assume_color && !t->Sf && t->md) {
375 		/*
376 		 * Install colour support if this looks like an ANSI
377 		 * terminal — that is, it’s got bold with ESC ‘[’…
378 		 */
379 		if (t->md[0] == '\\' && t->md[1] == 'E' && t->md[2] == '[') {
380 			t->ut = 1;
381 			t->Sf = UC "\\E[3%dm";
382 			t->Sb = UC "\\E[4%dm";
383 		} else if (t->md[0] == '\033' && t->md[1] == '[') {
384 			t->ut = 1;
385 			t->Sf = UC "\033[3%p1%dm";
386 			t->Sb = UC "\033[4%p1%dm";
387 		}
388 	}
389 
390 	if (getnum(t->cap, UC "sg") <= 0 && !t->mr && jgetstr(t->cap, UC "se")) {
391 		if ((t->so = jgetstr(t->cap, UC "so")) != NULL)
392 			t->avattr |= INVERSE;
393 		t->se = jgetstr(t->cap, UC "se");
394 	}
395 	if (getflag(t->cap, UC "xs") || getflag(t->cap, UC "xt"))
396 		t->so = NULL;
397 
398 	if (getnum(t->cap, UC "ug") <= 0 && jgetstr(t->cap, UC "ue")) {
399 		if ((t->us = jgetstr(t->cap, UC "us")) != NULL)
400 			t->avattr |= UNDERLINE;
401 		t->ue = jgetstr(t->cap, UC "ue");
402 	}
403 
404 	if (!(t->uc = jgetstr(t->cap, UC "uc")))
405 		if (t->ul)
406 			t->uc = UC "_";
407 	if (t->uc)
408 		t->avattr |= UNDERLINE;
409 
410 	t->ms = getflag(t->cap, UC "ms");
411 
412 	t->da = getflag(t->cap, UC "da");
413 	t->db = getflag(t->cap, UC "db");
414 	t->cs = jgetstr(t->cap, UC "cs");
415 	t->rr = getflag(t->cap, UC "rr");
416 	t->sf = jgetstr(t->cap, UC "sf");
417 	t->sr = jgetstr(t->cap, UC "sr");
418 	t->SF = jgetstr(t->cap, UC "SF");
419 	t->SR = jgetstr(t->cap, UC "SR");
420 	t->al = jgetstr(t->cap, UC "al");
421 	t->dl = jgetstr(t->cap, UC "dl");
422 	t->AL = jgetstr(t->cap, UC "AL");
423 	t->DL = jgetstr(t->cap, UC "DL");
424 	if (!getflag(t->cap, UC "ns") && !t->sf)
425 		t->sf = UC "\12";
426 
427 	if (!getflag(t->cap, UC "in") && baud < 38400) {
428 		t->dc = jgetstr(t->cap, UC "dc");
429 		t->DC = jgetstr(t->cap, UC "DC");
430 		t->dm = jgetstr(t->cap, UC "dm");
431 		t->ed = jgetstr(t->cap, UC "ed");
432 
433 		t->im = jgetstr(t->cap, UC "im");
434 		t->ei = jgetstr(t->cap, UC "ei");
435 		t->ic = jgetstr(t->cap, UC "ic");
436 		t->IC = jgetstr(t->cap, UC "IC");
437 		t->ip = jgetstr(t->cap, UC "ip");
438 		t->mi = getflag(t->cap, UC "mi");
439 	}
440 
441 	if (jgetstr(t->cap, UC "bc"))
442 		t->bs = jgetstr(t->cap, UC "bc");
443 	else if (jgetstr(t->cap, UC "le"))
444 		t->bs = jgetstr(t->cap, UC "le");
445 	if (getflag(t->cap, UC "bs"))
446 		t->bs = UC "\10";
447 
448 	t->cbs = tcost(t->cap, t->bs, 1, 2, 2, 0, 0);
449 
450 	t->lf = UC "\12";
451 	if (jgetstr(t->cap, UC "do"))
452 		t->lf = jgetstr(t->cap, UC "do");
453 	t->clf = tcost(t->cap, t->lf, 1, 2, 2, 0, 0);
454 
455 	t->up = jgetstr(t->cap, UC "up");
456 	t->cup = tcost(t->cap, t->up, 1, 2, 2, 0, 0);
457 
458 	t->nd = jgetstr(t->cap, UC "nd");
459 
460 	t->tw = 8;
461 	if (getnum(t->cap, UC "it") > 0)
462 		t->tw = getnum(t->cap, UC "it");
463 	else if (getnum(t->cap, UC "tw") > 0)
464 		t->tw = getnum(t->cap, UC "tw");
465 
466 	if (!(t->ta = jgetstr(t->cap, UC "ta")))
467 		if (getflag(t->cap, UC "pt"))
468 			t->ta = UC "\11";
469 	t->bt = jgetstr(t->cap, UC "bt");
470 	if (getflag(t->cap, UC "xt") || !usetabs) {
471 		t->ta = NULL;
472 		t->bt = NULL;
473 	}
474 
475 	t->cta = tcost(t->cap, t->ta, 1, 2, 2, 0, 0);
476 	t->cbt = tcost(t->cap, t->bt, 1, 2, 2, 0, 0);
477 
478 	t->ho = jgetstr(t->cap, UC "ho");
479 	t->cho = tcost(t->cap, t->ho, 1, 2, 2, 0, 0);
480 	t->ll = jgetstr(t->cap, UC "ll");
481 	t->cll = tcost(t->cap, t->ll, 1, 2, 2, 0, 0);
482 
483 	t->cr = UC "\15";
484 	if (jgetstr(t->cap, UC "cr"))
485 		t->cr = jgetstr(t->cap, UC "cr");
486 	if (getflag(t->cap, UC "nc") || getflag(t->cap, UC "xr"))
487 		t->cr = NULL;
488 	t->ccr = tcost(t->cap, t->cr, 1, 2, 2, 0, 0);
489 
490 	t->cRI = tcost(t->cap, t->RI = jgetstr(t->cap, UC "RI"), 1, 2, 2, 0, 0);
491 	t->cLE = tcost(t->cap, t->LE = jgetstr(t->cap, UC "LE"), 1, 2, 2, 0, 0);
492 	t->cUP = tcost(t->cap, t->UP = jgetstr(t->cap, UC "UP"), 1, 2, 2, 0, 0);
493 	t->cDO = tcost(t->cap, t->DO = jgetstr(t->cap, UC "DO"), 1, 2, 2, 0, 0);
494 	t->cch = tcost(t->cap, t->ch = jgetstr(t->cap, UC "ch"), 1, 2, 2, 0, 0);
495 	t->ccv = tcost(t->cap, t->cv = jgetstr(t->cap, UC "cv"), 1, 2, 2, 0, 0);
496 	t->ccV = tcost(t->cap, t->cV = jgetstr(t->cap, UC "cV"), 1, 2, 2, 0, 0);
497 	t->ccm = tcost(t->cap, t->cm = jgetstr(t->cap, UC "cm"), 1, 2, 2, 0, 0);
498 
499 	t->cce = tcost(t->cap, t->ce = jgetstr(t->cap, UC "ce"), 1, 2, 2, 0, 0);
500 
501 	/* Make sure terminal can do absolute positioning */
502 	if (t->cm)
503 		goto ok;
504 	if (t->ch && t->cv)
505 		goto ok;
506 	if (t->ho && (t->lf || t->DO || t->cv))
507 		goto ok;
508 	if (t->ll && (t->up || t->UP || t->cv))
509 		goto ok;
510 	if (t->cr && t->cv)
511 		goto ok;
512 	leave = 1;
513 	ttclose();
514 	signrm(0);
515 	free(t);
516 	fprintf(stderr,"Sorry, your terminal can't do absolute cursor positioning.\nIt's broken\n");
517 	return NULL;
518  ok:
519 
520 	/* Determine if we can scroll */
521 	if (((t->sr || t->SR) && (t->sf || t->SF) && t->cs) || ((t->al || t->AL) && (t->dl || t->DL)))
522 		t->scroll = 1;
523 	else if (baud < 38400)
524 		mid = 1;
525 
526 	/* Determine if we can ins/del within lines */
527 	if ((t->im || t->ic || t->IC) && (t->dc || t->DC))
528 		t->insdel = 1;
529 
530 	/* Adjust for high baud rates */
531 	if (baud >= 38400) {
532 		t->scroll = 0;
533 		t->insdel = 0;
534 	}
535 
536 	/* Send out terminal initialisation string */
537 	if (t->ti)
538 		texec(t->cap, t->ti, 1, 0, 0, 0, 0);
539 	if (!skiptop && t->cl)
540 		texec(t->cap, t->cl, 1, 0, 0, 0, 0);
541 
542 	/* Initialise variable screen size-dependent vars */
543 	t->htab = ralloc(256, sizeof(struct s_hentry));
544 
545 	nresize(t, t->co, t->li);
546 
547 	return t;
548 }
549 
550 /* Change size of screen */
551 
nresize(SCRN * t,int w,int h)552 void nresize(SCRN *t, int w, int h)
553 {
554 	if (h < 4)
555 		h = 4;
556 	if (w < 8)
557 		w = 8;
558 	t->li = h;
559 	t->co = w;
560 	if (notoktomul((size_t)t->li, (size_t)t->co)) {
561 		/* who has THAT large screens? */
562 		ttabrt(0, "screen too large");
563 		exit(255);
564 	}
565 	if (t->sary)
566 		free(t->sary);
567 	if (t->updtab)
568 		free(t->updtab);
569 	if (t->syntab)
570 		free(t->syntab);
571 	if (t->scrn)
572 		free(t->scrn);
573 	if (t->attr)
574 		free(t->attr);
575 	if (t->compose)
576 		free(t->compose);
577 	if (t->ofst)
578 		free(t->ofst);
579 	if (t->ary)
580 		free(t->ary);
581 	t->scrn = ralloc((size_t)t->li * (size_t)t->co, sizeof(int));
582 	t->attr = ralloc((size_t)t->li * (size_t)t->co, sizeof(int));
583 	t->sary = calloc(t->li, sizeof(int));
584 	t->updtab = ralloc((size_t)t->li, sizeof(int));
585 	t->syntab = ralloc((size_t)t->li, sizeof(int));
586 	t->compose = ralloc((size_t)t->co, sizeof(int));
587 	t->ofst = ralloc((size_t)t->co, sizeof(int));
588 	t->ary = ralloc((size_t)t->co, sizeof(struct s_hentry));
589 
590 	if (!t->htab || !t->scrn || !t->attr || !t->sary || !t->updtab ||
591 	    !t->syntab || !t->compose || !t->ofst || !t->ary) {
592 		ttabrt(0, "screen allocation failed");
593 		exit(255);
594 	}
595 
596 	nredraw(t);
597 }
598 
599 /*
600  * Calculate cost of positioning the cursor using only relative cursor
601  * positioning functions: t->(lf, DO, up, UP, bs, LE, RI, ta, bt) and
602  * rewriting characters (to move right)
603  *
604  * This doesn't use the am and bw capabilities although it probably could.
605  */
606 
relcost(register SCRN * t,register int x,register int y,register int ox,register int oy)607 static int relcost(register SCRN *t, register int x, register int y, register int ox, register int oy)
608 {
609 	int cost = 0;
610 
611 	/* If we don't know the cursor position, force use of absolute positioning */
612 	if (oy == -1 || ox == -1)
613 		return 10000;
614 
615 	/* First adjust row */
616 	if (y > oy) {
617 		int dist = y - oy;
618 
619 		/* Have to go down */
620 		if (t->lf) {
621 			int mult = dist * t->clf;
622 
623 			if (dist < 10 && t->cDO < mult)
624 				cost += t->cDO;
625 			else if (dist >= 10 && t->cDO + 1 < mult)
626 				cost += t->cDO + 1;
627 			else
628 				cost += mult;
629 		} else if (t->DO)
630 			if (dist < 10)
631 				cost += t->cDO;
632 			else
633 				cost += t->cDO + 1;
634 		else
635 			return 10000;
636 	} else if (y < oy) {
637 		int dist = oy - y;
638 
639 		/* Have to go up */
640 		if (t->up) {
641 			int mult = dist * t->cup;
642 
643 			if (dist < 10 && t->cUP < mult)
644 				cost += t->cUP;
645 			else if (dist >= 10 && t->cUP < mult)
646 				cost += t->cUP + 1;
647 			else
648 				cost += mult;
649 		} else if (t->UP)
650 			if (dist < 10)
651 				cost += t->cUP;
652 			else
653 				cost += t->cUP + 1;
654 		else
655 			return 10000;
656 	}
657 
658 	/* Now adjust column */
659 
660 	/* Use tabs */
661 	if (x > ox && t->ta) {
662 		int dist = x - ox;
663 		int ntabs = (dist + ox % t->tw) / t->tw;
664 		int cstunder = x % t->tw + t->cta * ntabs;
665 		int cstover;
666 
667 		if (x + t->tw < t->co && t->bs)
668 			cstover = t->cbs * (t->tw - x % t->tw) + t->cta * (ntabs + 1);
669 		else
670 			cstover = 10000;
671 		if (dist < 10 && cstunder < t->cRI && cstunder < x - ox && cstover > cstunder)
672 			return cost + cstunder;
673 		else if (cstunder < t->cRI + 1 && cstunder < x - ox && cstover > cstunder)
674 			return cost + cstunder;
675 		else if (dist < 10 && cstover < t->cRI && cstover < x - ox)
676 			return cost + cstover;
677 		else if (cstover < t->cRI + 1 && cstover < x - ox)
678 			return cost + cstover;
679 	} else if (x < ox && t->bt) {
680 		int dist = ox - x;
681 		int ntabs = (dist + t->tw - ox % t->tw) / t->tw;
682 		int cstunder, cstover;
683 
684 		if (t->bs)
685 			cstunder = t->cbt * ntabs + t->cbs * (t->tw - x % t->tw);
686 		else
687 			cstunder = 10000;
688 		if (x - t->tw >= 0)
689 			cstover = t->cbt * (ntabs + 1) + x % t->tw;
690 		else
691 			cstover = 10000;
692 		if (dist < 10 && cstunder < t->cLE && (t->bs ? cstunder < (ox - x) * t->cbs : 1)
693 		    && cstover > cstunder)
694 			return cost + cstunder;
695 		if (cstunder < t->cLE + 1 && (t->bs ? cstunder < (ox - x) * t->cbs : 1)
696 		    && cstover > cstunder)
697 			return cost + cstunder;
698 		else if (dist < 10 && cstover < t->cRI && (t->bs ? cstover < (ox - x) * t->cbs : 1))
699 			return cost + cstover;
700 		else if (cstover < t->cRI + 1 && (t->bs ? cstover < (ox - x) * t->cbs : 1))
701 			return cost + cstover;
702 	}
703 
704 	/* Use simple motions */
705 	if (x < ox) {
706 		int dist = ox - x;
707 
708 		/* Have to go left */
709 		if (t->bs) {
710 			int mult = dist * t->cbs;
711 
712 			if (t->cLE < mult && dist < 10)
713 				cost += t->cLE;
714 			else if (t->cLE + 1 < mult)
715 				cost += t->cLE + 1;
716 			else
717 				cost += mult;
718 		} else if (t->LE)
719 			cost += t->cLE;
720 		else
721 			return 10000;
722 	} else if (x > ox) {
723 		int dist = x - ox;
724 
725 		/* Have to go right */
726 		/* Hmm.. this should take into account possible attribute changes */
727 		if (t->cRI < dist && dist < 10)
728 			cost += t->cRI;
729 		else if (t->cRI + 1 < dist)
730 			cost += t->cRI + 1;
731 		else
732 			cost += dist;
733 	}
734 
735 	return cost;
736 }
737 
738 /* Find optimal set of cursor positioning commands to move from the current
739  * cursor row and column (either or both of which might be unknown) to the
740  * given new row and column and execute them.
741  */
742 
cposs(register SCRN * t,register int x,register int y)743 static void cposs(register SCRN *t, register int x, register int y)
744 {
745 	register int bestcost, cost;
746 	int bestway;
747 	int hy;
748 	int hl;
749 
750 	/*
751 	 * Home y position is usually 0, but it is 'top' if we have
752 	 * scrolling region relative addressing
753 	 */
754 	if (t->rr) {
755 		hy = t->top;
756 		hl = t->bot - 1;
757 	} else {
758 		hy = 0;
759 		hl = t->li - 1;
760 	}
761 
762 	/* Assume best way is with only using relative cursor positioning */
763 
764 	bestcost = relcost(t, x, y, t->x, t->y);
765 	bestway = 0;
766 
767 	/*
768 	 * Now check if combinations of absolute cursor positioning
769 	 * functions are better (or necessary in case one or both cursor
770 	 * positions are unknown)
771 	 */
772 
773 	if (t->ccm < bestcost) {
774 		cost = tcost(t->cap, t->cm, 1, y, x, 0, 0);
775 		if (cost < bestcost) {
776 			bestcost = cost;
777 			bestway = 6;
778 		}
779 	}
780 	if (t->ccr < bestcost) {
781 		cost = relcost(t, x, y, 0, t->y) + t->ccr;
782 		if (cost < bestcost) {
783 			bestcost = cost;
784 			bestway = 1;
785 		}
786 	}
787 	if (t->cho < bestcost) {
788 		cost = relcost(t, x, y, 0, hy) + t->cho;
789 		if (cost < bestcost) {
790 			bestcost = cost;
791 			bestway = 2;
792 		}
793 	}
794 	if (t->cll < bestcost) {
795 		cost = relcost(t, x, y, 0, hl) + t->cll;
796 		if (cost < bestcost) {
797 			bestcost = cost;
798 			bestway = 3;
799 		}
800 	}
801 	if (t->cch < bestcost && x != t->x) {
802 		cost = relcost(t, x, y, x, t->y) + tcost(t->cap, t->ch, 1, x, 0, 0, 0);
803 		if (cost < bestcost) {
804 			bestcost = cost;
805 			bestway = 4;
806 		}
807 	}
808 	if (t->ccv < bestcost && y != t->y) {
809 		cost = relcost(t, x, y, t->x, y) + tcost(t->cap, t->cv, 1, y, 0, 0, 0);
810 		if (cost < bestcost) {
811 			bestcost = cost;
812 			bestway = 5;
813 		}
814 	}
815 	if (t->ccV < bestcost) {
816 		cost = relcost(t, x, y, 0, y) + tcost(t->cap, t->cV, 1, y, 0, 0, 0);
817 		if (cost < bestcost) {
818 			bestcost = cost;
819 			bestway = 13;
820 		}
821 	}
822 	if (t->cch + t->ccv < bestcost && x != t->x && y != t->y) {
823 		cost = tcost(t->cap, t->cv, 1, y - hy, 0, 0, 0) + tcost(t->cap, t->ch, 1, x, 0, 0, 0);
824 		if (cost < bestcost) {
825 			bestcost = cost;
826 			bestway = 7;
827 		}
828 	}
829 	if (t->ccv + t->ccr < bestcost && y != t->y) {
830 		cost = tcost(t->cap, t->cv, 1, y, 0, 0, 0) + tcost(t->cap, t->cr, 1, 0, 0, 0, 0) + relcost(t, x, y, 0, y);
831 		if (cost < bestcost) {
832 			bestcost = cost;
833 			bestway = 8;
834 		}
835 	}
836 	if (t->cll + t->cch < bestcost) {
837 		cost = tcost(t->cap, t->ll, 1, 0, 0, 0, 0) + tcost(t->cap, t->ch, 1, x, 0, 0, 0) + relcost(t, x, y, x, hl);
838 		if (cost < bestcost) {
839 			bestcost = cost;
840 			bestway = 9;
841 		}
842 	}
843 	if (t->cll + t->ccv < bestcost) {
844 		cost = tcost(t->cap, t->ll, 1, 0, 0, 0, 0) + tcost(t->cap, t->cv, 1, y, 0, 0, 0) + relcost(t, x, y, 0, y);
845 		if (cost < bestcost) {
846 			bestcost = cost;
847 			bestway = 10;
848 		}
849 	}
850 	if (t->cho + t->cch < bestcost) {
851 		cost = tcost(t->cap, t->ho, 1, 0, 0, 0, 0) + tcost(t->cap, t->ch, 1, x, 0, 0, 0) + relcost(t, x, y, x, hy);
852 		if (cost < bestcost) {
853 			bestcost = cost;
854 			bestway = 11;
855 		}
856 	}
857 	if (t->cho + t->ccv < bestcost) {
858 		cost = tcost(t->cap, t->ho, 1, 0, 0, 0, 0) + tcost(t->cap, t->cv, 1, y, 0, 0, 0) + relcost(t, x, y, 0, y);
859 		if (cost < bestcost) {
860 			/* dead store: bestcost = cost; */
861 			bestway = 12;
862 		}
863 	}
864 
865 	/*
866 	 * Do absolute cursor positioning if we don't know the cursor
867 	 * position or if it is faster than doing only relative cursor
868 	 * positioning
869 	 */
870 
871 	switch (bestway) {
872 	case 1:
873 		texec(t->cap, t->cr, 1, 0, 0, 0, 0);
874 		t->x = 0;
875 		break;
876 	case 2:
877 		texec(t->cap, t->ho, 1, 0, 0, 0, 0);
878 		t->x = 0;
879 		t->y = hy;
880 		break;
881 	case 3:
882 		texec(t->cap, t->ll, 1, 0, 0, 0, 0);
883 		t->x = 0;
884 		t->y = hl;
885 		break;
886 	case 9:
887 		texec(t->cap, t->ll, 1, 0, 0, 0, 0);
888 		t->x = 0;
889 		t->y = hl;
890 		goto doch;
891 	case 11:
892 		texec(t->cap, t->ho, 1, 0, 0, 0, 0);
893 		t->x = 0;
894 		t->y = hy;
895 		/* FALLTHROUGH */
896  doch:
897 		/* FALLTHROUGH */
898 	case 4:
899 		texec(t->cap, t->ch, 1, x, 0, 0, 0);
900 		t->x = x;
901 		break;
902 	case 10:
903 		texec(t->cap, t->ll, 1, 0, 0, 0, 0);
904 		t->x = 0;
905 		t->y = hl;
906 		goto docv;
907 	case 12:
908 		texec(t->cap, t->ho, 1, 0, 0, 0, 0);
909 		t->x = 0;
910 		t->y = hy;
911 		goto docv;
912 	case 8:
913 		texec(t->cap, t->cr, 1, 0, 0, 0, 0);
914 		t->x = 0;
915 		/* FALLTHROUGH */
916  docv:
917 		/* FALLTHROUGH */
918 	case 5:
919 		texec(t->cap, t->cv, 1, y, 0, 0, 0);
920 		t->y = y;
921 		break;
922 	case 6:
923 		texec(t->cap, t->cm, 1, y, x, 0, 0);
924 		t->y = y;
925 		t->x = x;
926 		break;
927 	case 7:
928 		texec(t->cap, t->cv, 1, y, 0, 0, 0);
929 		t->y = y;
930 		texec(t->cap, t->ch, 1, x, 0, 0, 0);
931 		t->x = x;
932 		break;
933 	case 13:
934 		texec(t->cap, t->cV, 1, y, 0, 0, 0);
935 		t->y = y;
936 		t->x = 0;
937 		break;
938 	}
939 
940 	/* Use relative cursor position functions if we're not there yet */
941 
942 	/* First adjust row */
943 	if (y > t->y) {
944 		/* Have to go down */
945 		if (!t->lf || t->cDO < (y - t->y) * t->clf) {
946 			texec(t->cap, t->DO, 1, y - t->y, 0, 0, 0);
947 			t->y = y;
948 		} else
949 			while (y > t->y) {
950 				texec(t->cap, t->lf, 1, 0, 0, 0, 0);
951 				++t->y;
952 			}
953 	} else if (y < t->y) {
954 		/* Have to go up */
955 		if (!t->up || t->cUP < (t->y - y) * t->cup) {
956 			texec(t->cap, t->UP, 1, t->y - y, 0, 0, 0);
957 			t->y = y;
958 		} else
959 			while (y < t->y) {
960 				texec(t->cap, t->up, 1, 0, 0, 0, 0);
961 				--t->y;
962 			}
963 	}
964 
965 	/* Use tabs */
966 	if (x > t->x && t->ta) {
967 		int ntabs = (x - t->x + t->x % t->tw) / t->tw;
968 		int cstunder = x % t->tw + t->cta * ntabs;
969 		int cstover;
970 
971 		if (x + t->tw < t->co && t->bs)
972 			cstover = t->cbs * (t->tw - x % t->tw) + t->cta * (ntabs + 1);
973 		else
974 			cstover = 10000;
975 		if (cstunder < t->cRI && cstunder < x - t->x && cstover > cstunder) {
976 			if (ntabs) {
977 				t->x = x - x % t->tw;
978 				do {
979 					texec(t->cap, t->ta, 1, 0, 0, 0, 0);
980 				} while (--ntabs);
981 			}
982 		} else if (cstover < t->cRI && cstover < x - t->x) {
983 			t->x = t->tw + x - x % t->tw;
984 			++ntabs;
985 			do {
986 				texec(t->cap, t->ta, 1, 0, 0, 0, 0);
987 			} while (--ntabs);
988 		}
989 	} else if (x < t->x && t->bt) {
990 		int ntabs = ((t->x + t->tw - 1) - (t->x + t->tw - 1) % t->tw - ((x + t->tw - 1) - (x + t->tw - 1) % t->tw)) / t->tw;
991 		int cstunder, cstover;
992 
993 		if (t->bs)
994 			cstunder = t->cbt * ntabs + t->cbs * (t->tw - x % t->tw);
995 		else
996 			cstunder = 10000;
997 		if (x - t->tw >= 0)
998 			cstover = t->cbt * (ntabs + 1) + x % t->tw;
999 		else
1000 			cstover = 10000;
1001 		if (cstunder < t->cLE && (t->bs ? cstunder < (t->x - x) * t->cbs : 1)
1002 		    && cstover > cstunder) {
1003 			if (ntabs) {
1004 				do {
1005 					texec(t->cap, t->bt, 1, 0, 0, 0, 0);
1006 				} while (--ntabs);
1007 				t->x = x + t->tw - x % t->tw;
1008 			}
1009 		} else if (cstover < t->cRI && (t->bs ? cstover < (t->x - x) * t->cbs : 1)) {
1010 			t->x = x - x % t->tw;
1011 			++ntabs;
1012 			do {
1013 				texec(t->cap, t->bt, 1, 0, 0, 0, 0);
1014 			} while (--ntabs);
1015 		}
1016 	}
1017 
1018 	/* Now adjust column */
1019 	if (x < t->x) {
1020 		/* Have to go left */
1021 		if (!t->bs || t->cLE < (t->x - x) * t->cbs) {
1022 			texec(t->cap, t->LE, 1, t->x - x, 0, 0, 0);
1023 			t->x = x;
1024 		} else
1025 			while (x < t->x) {
1026 				texec(t->cap, t->bs, 1, 0, 0, 0, 0);
1027 				--t->x;
1028 			}
1029 	} else if (x > t->x) {
1030 		/* Have to go right */
1031 		/* Hmm.. this should take into account possible attribute changes */
1032 		if (x-t->x>1 && t->RI) {
1033 			texec(t->cap, t->RI, 1, x - t->x, 0, 0, 0);
1034 			t->x = x;
1035 		} else {
1036 			while(x>t->x) {
1037 				texec(t->cap, t->nd, 1, 0, 0, 0, 0);
1038 				++t->x;
1039 			}
1040 		}
1041 	}
1042 }
1043 
cpos(register SCRN * t,register int x,register int y)1044 int cpos(register SCRN *t, register int x, register int y)
1045 {
1046 	/* Move cursor quickly if we can */
1047 	if (y == t->y) {
1048 		if (x > t->x && x - t->x < 4 && !t->ins) {
1049 			int *cs = t->scrn + t->x + t->co * t->y;
1050 			int *as = t->attr + t->x + t->co * t->y;
1051 			do {
1052 				/* We used to space over unknown chars, but they now could be
1053 				   the right half of a UTF-8 two column character, so we can't.
1054 				   Also do not try to emit utf-8 sequences here. */
1055 				if(*cs<32 || *cs>=127)
1056 					break;
1057 
1058 				/* has a combining character attached? */
1059 				if (*as & HAS_COMBINING)
1060 					break;
1061 
1062 				if (*as != t->attrib)
1063 					set_attr(t, *as);
1064 
1065 				ttputc(*cs);
1066 
1067 				++cs;
1068 				++as;
1069 				++t->x;
1070 
1071 			} while (x != t->x);
1072 		}
1073 		if (x == t->x)
1074 			return 0;
1075 	}
1076 	if ((!t->ms && t->attrib & (INVERSE | UNDERLINE | BG_NOT_DEFAULT)) ||
1077 	    (t->ut && (t->attrib & BG_NOT_DEFAULT)))
1078 		set_attr(t, t->attrib & ~(INVERSE | UNDERLINE | BG_MASK));
1079 
1080 	/* Should be in cposs */
1081 	if (y < t->top || y >= t->bot)
1082 		setregn(t, 0, t->li);
1083 
1084 	cposs(t, x, y);
1085 	return 0;
1086 }
1087 
doinschr(SCRN * t,int x,int y,int * s,int * as,int n)1088 static void doinschr(SCRN *t, int x, int y, int *s, int *as, int n)
1089 {
1090 	int a;
1091 
1092 	if (x < 0) {
1093 		s -= x;
1094 		as -= x;
1095 		x = 0;
1096 	}
1097 	if (x >= t->co - 1 || n <= 0)
1098 		return;
1099 	if (t->im || t->ic || t->IC) {
1100 		cpos(t, x, y);
1101 		if ((n == 1 && t->ic) || !t->IC) {
1102 			if (!t->ic)
1103 				setins(t, x);
1104 			for (a = 0; a != n; ++a) {
1105 				texec(t->cap, t->ic, 1, x, 0, 0, 0);
1106 				texec(t->cap, t->ip, 1, x, 0, 0, 0);
1107 			}
1108 			if (!t->mi)
1109 				clrins(t);
1110 		} else {
1111 			texec(t->cap, t->IC, 1, n, 0, 0, 0);
1112 		}
1113 	}
1114 	mmove(t->scrn + x + t->co * y + n, t->scrn + x + t->co * y, (t->co - (x + n)) * sizeof(int));
1115 	mmove(t->attr + x + t->co * y + n, t->attr + x + t->co * y, (t->co - (x + n)) * sizeof(int));
1116 	mmove(t->scrn + x + t->co * y, s, n * sizeof(int));
1117 	mmove(t->attr + x + t->co * y, s, n * sizeof(int));
1118 }
1119 
dodelchr(SCRN * t,int x,int y,int n)1120 static void dodelchr(SCRN *t, int x, int y, int n)
1121 {
1122 	int a;
1123 
1124 	if (x < 0)
1125 		x = 0;
1126 	if (!n || x >= t->co - 1)
1127 		return;
1128 	if (t->dc || t->DC) {
1129 		cpos(t, x, y);
1130 		texec(t->cap, t->dm, 1, x, 0, 0, 0);	/* Enter delete mode */
1131 		if ((n == 1 && t->dc) || !t->DC)
1132 			for (a = n; a; --a)
1133 				texec(t->cap, t->dc, 1, x, 0, 0, 0);
1134 		else
1135 			texec(t->cap, t->DC, 1, n, 0, 0, 0);
1136 		texec(t->cap, t->ed, 1, x, 0, 0, 0);	/* Exit delete mode */
1137 	}
1138 	mmove(t->scrn + t->co * y + x, t->scrn + t->co * y + x + n, (t->co - (x + n)) * sizeof(int));
1139 	mmove(t->attr + t->co * y + x, t->attr + t->co * y + x + n, (t->co - (x + n)) * sizeof(int));
1140 	msetI(t->scrn + t->co * y + t->co - n, ' ', n);
1141 	msetI(t->attr + t->co * y + t->co - n, 0, n);
1142 }
1143 
1144 /* Insert/Delete within line */
1145 /* FIXME: doesn't know about attr */
1146 
magic(SCRN * t,int y,int * cs,int * ca,int * s,int * a,int placex)1147 void magic(SCRN *t, int y, int *cs, int *ca,int *s, int *a, int placex)
1148 {
1149 	struct s_hentry *htab = t->htab;
1150 	int *ofst = t->ofst;
1151 	int aryx = 1;
1152 	int x;
1153 
1154 	if (!(t->im || t->ic || t->IC) || !(t->dc || t->DC))
1155 		return;
1156 	mset(htab, 0, 256 * sizeof(struct s_hentry));
1157 
1158 	msetI(ofst, 0, t->co);
1159 
1160 	/* Build hash table */
1161 	for (x = 0; x != t->co - 1; ++x) {
1162 		t->ary[aryx].next = htab[cs[x] & 255].next;
1163 		t->ary[aryx].loc = x;
1164 		++htab[cs[x] & 255].loc;
1165 		htab[cs[x] & 255].next = aryx++;
1166 	}
1167 
1168 	/* Build offset table */
1169 	for (x = 0; x < t->co - 1;)
1170 		if (htab[s[x] & 255].loc >= 15)
1171 			ofst[x++] = t->co - 1;
1172 		else {
1173 			int aryy;
1174 			int maxaryy = 0;
1175 			int maxlen = 0;
1176 			int best = 0;
1177 			int bestback = 0;
1178 			int z;
1179 
1180 			for (aryy = htab[s[x] & 255].next; aryy; aryy = t->ary[aryy].next) {
1181 				int amnt, back;
1182 				int tsfo = t->ary[aryy].loc - x;
1183 				int cst = -abs(tsfo);
1184 				int pre = 32;
1185 
1186 				for (amnt = 0; x + amnt < t->co - 1 && x + tsfo + amnt < t->co - 1; ++amnt) {
1187 					if (cs[x + tsfo + amnt] != s[x + amnt])
1188 						break;
1189 					else if ((s[x + amnt] & 255) != 32 || pre != 32)
1190 						++cst;
1191 					pre = s[x + amnt] & 255;
1192 				}
1193 				pre = 32;
1194 				for (back = 0; back + x > 0 && back + tsfo + x > 0; --back) {
1195 					if (cs[x + tsfo + back - 1] != s[x + back - 1])
1196 						break;
1197 					else if ((s[x + back - 1] & 255) != 32 || pre != 32)
1198 						++cst;
1199 					pre = s[x + back - 1] & 255;
1200 				}
1201 				if (cst > best) {
1202 					maxaryy = aryy;
1203 					maxlen = amnt;
1204 					best = cst;
1205 					bestback = back;
1206 				}
1207 			}
1208 			if (!maxlen) {
1209 				ofst[x] = t->co - 1;
1210 				maxlen = 1;
1211 			} else if (best < 2)
1212 				for (z = 0; z != maxlen; ++z)
1213 					ofst[x + z] = t->co - 1;
1214 			else
1215 				for (z = 0; z != maxlen - bestback; ++z)
1216 					ofst[x + z + bestback] = t->ary[maxaryy].loc - x;
1217 			x += maxlen;
1218 		}
1219 
1220 	/* Apply scrolling commands */
1221 
1222 	for (x = 0; x != t->co - 1; ++x) {
1223 		int q = ofst[x];
1224 
1225 		if (q && q != t->co - 1) {
1226 			if (q > 0) {
1227 				int z, fu;
1228 
1229 				for (z = x; z != t->co - 1 && ofst[z] == q; ++z)
1230 					;
1231 				while (s[x] == cs[x] && x < placex)
1232 					++x;
1233 				dodelchr(t, x, y, q);
1234 				for (fu = x; fu != t->co - 1; ++fu)
1235 					if (ofst[fu] != t->co - 1)
1236 						ofst[fu] -= q;
1237 				x = z - 1;
1238 			} else {
1239 				int z, fu;
1240 
1241 				for (z = x; z != t->co - 1 && ofst[z] == q; ++z)
1242 					;
1243 				while (s[x + q] == cs[x + q] && x - q < placex)
1244 					++x;
1245 				doinschr(t, x + q, y, s + x + q, a + x + q, -q);
1246 				for (fu = x; fu != t->co - 1; ++fu)
1247 					if (ofst[fu] != t->co - 1)
1248 						ofst[fu] -= q;
1249 				x = z - 1;
1250 			}
1251 		}
1252 	}
1253 }
1254 
doupscrl(SCRN * t,int top,int bot,int amnt)1255 static void doupscrl(SCRN *t, int top, int bot, int amnt)
1256 {
1257 	int a = amnt;
1258 
1259 	if (!amnt)
1260 		return;
1261 	set_attr(t, 0);
1262 	if (top == 0 && bot == t->li && (t->sf || t->SF)) {
1263 		setregn(t, 0, t->li);
1264 		cpos(t, 0, t->li - 1);
1265 		if ((amnt == 1 && t->sf) || !t->SF)
1266 			while (a--)
1267 				texec(t->cap, t->sf, 1, t->li - 1, 0, 0, 0);
1268 		else
1269 			texec(t->cap, t->SF, a, a, 0, 0, 0);
1270 		goto done;
1271 	}
1272 	if (bot == t->li && (t->dl || t->DL)) {
1273 		setregn(t, 0, t->li);
1274 		cpos(t, 0, top);
1275 		if ((amnt == 1 && t->dl) || !t->DL)
1276 			while (a--)
1277 				texec(t->cap, t->dl, 1, top, 0, 0, 0);
1278 		else
1279 			texec(t->cap, t->DL, a, a, 0, 0, 0);
1280 		goto done;
1281 	}
1282 	if (t->cs && (t->sf || t->SF)) {
1283 		setregn(t, top, bot);
1284 		cpos(t, 0, bot - 1);
1285 		if ((amnt == 1 && t->sf) || !t->SF)
1286 			while (a--)
1287 				texec(t->cap, t->sf, 1, bot - 1, 0, 0, 0);
1288 		else
1289 			texec(t->cap, t->SF, a, a, 0, 0, 0);
1290 		goto done;
1291 	}
1292 	if ((t->dl || t->DL) && (t->al || t->AL)) {
1293 		cpos(t, 0, top);
1294 		if ((amnt == 1 && t->dl) || !t->DL)
1295 			while (a--)
1296 				texec(t->cap, t->dl, 1, top, 0, 0, 0);
1297 		else
1298 			texec(t->cap, t->DL, a, a, 0, 0, 0);
1299 		a = amnt;
1300 		cpos(t, 0, bot - amnt);
1301 		if ((amnt == 1 && t->al) || !t->AL)
1302 			while (a--)
1303 				texec(t->cap, t->al, 1, bot - amnt, 0, 0, 0);
1304 		else
1305 			texec(t->cap, t->AL, a, a, 0, 0, 0);
1306 		goto done;
1307 	}
1308 	msetI(t->updtab + top, 1, bot - top);
1309 	msetI(t->syntab + top, -1, bot - top);
1310 	return;
1311 
1312  done:
1313 	mmove(t->scrn + top * t->co, t->scrn + (top + amnt) * t->co, (bot - top - amnt) * t->co * sizeof(int));
1314 	mmove(t->attr + top * t->co, t->attr + (top + amnt) * t->co, (bot - top - amnt) * t->co * sizeof(int));
1315 
1316 	if (bot == t->li && t->db) {
1317 		msetI(t->scrn + (t->li - amnt) * t->co, -1, amnt * t->co);
1318 		msetI(t->attr + (t->li - amnt) * t->co, 0, amnt * t->co);
1319 		msetI(t->updtab + t->li - amnt, 1, amnt);
1320 		msetI(t->syntab + t->li - amnt, -1, amnt);
1321 	} else {
1322 		msetI(t->scrn + (bot - amnt) * t->co, ' ', amnt * t->co);
1323 		msetI(t->attr + (bot - amnt) * t->co, 0, amnt * t->co);
1324 	}
1325 }
1326 
dodnscrl(SCRN * t,int top,int bot,int amnt)1327 static void dodnscrl(SCRN *t, int top, int bot, int amnt)
1328 {
1329 	int a = amnt;
1330 
1331 	if (!amnt)
1332 		return;
1333 	set_attr(t, 0);
1334 	if (top == 0 && bot == t->li && (t->sr || t->SR)) {
1335 		setregn(t, 0, t->li);
1336 		cpos(t, 0, 0);
1337 		if ((amnt == 1 && t->sr) || !t->SR)
1338 			while (a--)
1339 				texec(t->cap, t->sr, 1, 0, 0, 0, 0);
1340 		else
1341 			texec(t->cap, t->SR, a, a, 0, 0, 0);
1342 		goto done;
1343 	}
1344 	if (bot == t->li && (t->al || t->AL)) {
1345 		setregn(t, 0, t->li);
1346 		cpos(t, 0, top);
1347 		if ((amnt == 1 && t->al) || !t->AL)
1348 			while (a--)
1349 				texec(t->cap, t->al, 1, top, 0, 0, 0);
1350 		else
1351 			texec(t->cap, t->AL, a, a, 0, 0, 0);
1352 		goto done;
1353 	}
1354 	if (t->cs && (t->sr || t->SR)) {
1355 		setregn(t, top, bot);
1356 		cpos(t, 0, top);
1357 		if ((amnt == 1 && t->sr) || !t->SR)
1358 			while (a--)
1359 				texec(t->cap, t->sr, 1, top, 0, 0, 0);
1360 		else
1361 			texec(t->cap, t->SR, a, a, 0, 0, 0);
1362 		goto done;
1363 	}
1364 	if ((t->dl || t->DL) && (t->al || t->AL)) {
1365 		cpos(t, 0, bot - amnt);
1366 		if ((amnt == 1 && t->dl) || !t->DL)
1367 			while (a--)
1368 				texec(t->cap, t->dl, 1, bot - amnt, 0, 0, 0);
1369 		else
1370 			texec(t->cap, t->DL, a, a, 0, 0, 0);
1371 		a = amnt;
1372 		cpos(t, 0, top);
1373 		if ((amnt == 1 && t->al) || !t->AL)
1374 			while (a--)
1375 				texec(t->cap, t->al, 1, top, 0, 0, 0);
1376 		else
1377 			texec(t->cap, t->AL, a, a, 0, 0, 0);
1378 		goto done;
1379 	}
1380 	msetI(t->updtab + top, 1, bot - top);
1381 	msetI(t->syntab + top, -1, bot - top);
1382 	return;
1383  done:
1384 	mmove(t->scrn + (top + amnt) * t->co, t->scrn + top * t->co, (bot - top - amnt) * t->co * sizeof(int));
1385 	mmove(t->attr + (top + amnt) * t->co, t->attr + top * t->co, (bot - top - amnt) * t->co * sizeof(int));
1386 
1387 	if (!top && t->da) {
1388 		msetI(t->scrn, -1, amnt * t->co);
1389 		msetI(t->attr, 0, amnt * t->co);
1390 		msetI(t->updtab, 1, amnt);
1391 		msetI(t->syntab, -1, amnt);
1392 	} else {
1393 		msetI(t->scrn + t->co * top, ' ', amnt * t->co);
1394 		msetI(t->attr + t->co * top, 0, amnt * t->co);
1395 	}
1396 }
1397 
nscroll(SCRN * t)1398 void nscroll(SCRN *t)
1399 {
1400 	int y, z, q, r, p;
1401 
1402 	for (y = 0; y != t->li; ++y) {
1403 		q = t->sary[y];
1404 		if (have)
1405 			return;
1406 		if (q && q != t->li) {
1407 			if (q > 0) {
1408 				for (z = y; z != t->li && t->sary[z] == q; ++z)
1409 					t->sary[z] = 0;
1410 				doupscrl(t, y, z + q, q);
1411 				y = z - 1;
1412 			} else {
1413 				for (r = y; r != t->li && (t->sary[r] < 0 || t->sary[r] == t->li); ++r)
1414 					;
1415 				p = r - 1;
1416 				do {
1417 					q = t->sary[p];
1418 					if (q && q != t->li) {
1419 						for (z = p; t->sary[z] = 0, (z && t->sary[z - 1] == q); --z)
1420 							;
1421 						dodnscrl(t, z + q, p + 1, -q);
1422 						p = z + 1;
1423 					}
1424 				} while (p-- != y);
1425 				y = r - 1;
1426 			}
1427 		}
1428 	}
1429 	msetI(t->sary, 0, t->li);
1430 }
1431 
npartial(SCRN * t)1432 void npartial(SCRN *t)
1433 {
1434 	set_attr(t, 0);
1435 	clrins(t);
1436 	setregn(t, 0, t->li);
1437 }
1438 
nescape(SCRN * t)1439 void nescape(SCRN *t)
1440 {
1441 	npartial(t);
1442 	cpos(t, 0, t->li - 1);
1443 	eraeol(t, 0, t->li - 1);
1444 	if (t->te)
1445 		texec(t->cap, t->te, 1, 0, 0, 0, 0);
1446 }
1447 
nreturn(SCRN * t)1448 void nreturn(SCRN *t)
1449 {
1450 	if (t->ti)
1451 		texec(t->cap, t->ti, 1, 0, 0, 0, 0);
1452 	if (!skiptop && t->cl)
1453 		texec(t->cap, t->cl, 1, 0, 0, 0, 0);
1454 	nredraw(t);
1455 }
1456 
nclose(SCRN * t)1457 void nclose(SCRN *t)
1458 {
1459 	leave = 1;
1460 	set_attr(t, 0);
1461 	clrins(t);
1462 	setregn(t, 0, t->li);
1463 	cpos(t, 0, t->li - 1);
1464 	if (t->te)
1465 		texec(t->cap, t->te, 1, 0, 0, 0, 0);
1466 	ttclose();
1467 	rmcap(t->cap);
1468 	free(t->scrn);
1469 	free(t->attr);
1470 	free(t->sary);
1471 	free(t->ofst);
1472 	free(t->htab);
1473 	free(t->ary);
1474 	free(t);
1475 }
1476 
nscrldn(SCRN * t,int top,int bot,int amnt)1477 void nscrldn(SCRN *t, int top, int bot, int amnt)
1478 {
1479 	int x;
1480 
1481 	if (!amnt || top >= bot || bot > t->li)
1482 		return;
1483 	if ((amnt < bot - top && bot - top - amnt < amnt / 2) || !t->scroll)
1484 		amnt = bot - top;
1485 	if (amnt < bot - top) {
1486 		for (x = bot; x != top + amnt; --x) {
1487 			t->sary[x - 1] = (t->sary[x - amnt - 1] == t->li ? t->li : t->sary[x - amnt - 1] - amnt);
1488 			t->updtab[x - 1] = t->updtab[x - amnt - 1];
1489 			t->syntab[x - 1] = t->syntab[x - amnt - 1];
1490 		}
1491 		for (x = top; x != top + amnt; ++x) {
1492 			t->updtab[x] = 1;
1493 			t->syntab[x] = -1;
1494 		}
1495 	}
1496 	if (amnt > bot - top)
1497 		amnt = bot - top;
1498 	msetI(t->sary + top, t->li, amnt);
1499 	if (amnt == bot - top) {
1500 		msetI(t->updtab + top, 1, amnt);
1501 		msetI(t->syntab + top, -1, amnt);
1502 	}
1503 }
1504 
nscrlup(SCRN * t,int top,int bot,int amnt)1505 void nscrlup(SCRN *t, int top, int bot, int amnt)
1506 {
1507 	int x;
1508 
1509 	if (!amnt || top >= bot || bot > t->li)
1510 		return;
1511 	if ((amnt < bot - top && bot - top - amnt < amnt / 2) || !t->scroll)
1512 		amnt = bot - top;
1513 	if (amnt < bot - top) {
1514 		for (x = top + amnt; x != bot; ++x) {
1515 			t->sary[x - amnt] = (t->sary[x] == t->li ? t->li : t->sary[x] + amnt);
1516 			t->updtab[x - amnt] = t->updtab[x];
1517 			t->syntab[x - amnt] = t->syntab[x];
1518 		}
1519 		for (x = bot - amnt; x != bot; ++x) {
1520 			t->updtab[x] = 1;
1521 			t->syntab[x] = -1;
1522 		}
1523 	}
1524 	if (amnt > bot - top)
1525 		amnt = bot - top;
1526 	msetI(t->sary + bot - amnt, t->li, amnt);
1527 	if (amnt == bot - top) {
1528 		msetI(t->updtab + bot - amnt, 1, amnt);
1529 		msetI(t->syntab + bot - amnt, -1, amnt);
1530 	}
1531 }
1532 
1533 extern volatile int dostaupd;
1534 
nredraw(SCRN * t)1535 void nredraw(SCRN *t)
1536 {
1537 	dostaupd = 1;
1538 	msetI(t->scrn, ' ', t->co * skiptop);
1539 	msetI(t->attr, 0, t->co * skiptop);
1540 	msetI(t->scrn + skiptop * t->co, -1, (t->li - skiptop) * t->co);
1541 	msetI(t->attr + skiptop * t->co, 0, (t->li - skiptop) * t->co);
1542 	msetI(t->sary, 0, t->li);
1543 	msetI(t->updtab + skiptop, -1, t->li - skiptop);
1544 	msetI(t->syntab + skiptop, -1, t->li - skiptop);
1545 	t->x = -1;
1546 	t->y = -1;
1547 	t->top = t->li;
1548 	t->bot = 0;
1549 	t->attrib = -1;
1550 	t->ins = -1;
1551 	set_attr(t, 0);
1552 	clrins(t);
1553 	setregn(t, 0, t->li);
1554 
1555 	if (!skiptop) {
1556 		if (t->cl) {
1557 			texec(t->cap, t->cl, 1, 0, 0, 0, 0);
1558 			t->x = 0;
1559 			t->y = 0;
1560 			msetI(t->scrn, ' ', t->li * t->co);
1561 			msetI(t->attr, 0, t->li * t->co);
1562 		} else if (t->cd) {
1563 			cpos(t, 0, 0);
1564 			texec(t->cap, t->cd, 1, 0, 0, 0, 0);
1565 			msetI(t->scrn, ' ', t->li * t->co);
1566 			msetI(t->attr, 0, t->li * t->co);
1567 		}
1568 	}
1569 }
1570 
1571 /* Convert color/attribute name into internal code */
1572 
meta_color(unsigned char * s)1573 int meta_color(unsigned char *s)
1574 {
1575 	if(!strcmp((char *)s,"inverse"))
1576 		return INVERSE;
1577 	else if(!strcmp((char *)s,"underline"))
1578 		return UNDERLINE;
1579 	else if(!strcmp((char *)s,"bold"))
1580 		return BOLD;
1581 	else if(!strcmp((char *)s,"blink"))
1582 		return BLINK;
1583 	else if(!strcmp((char *)s,"dim"))
1584 		return DIM;
1585 	else if(!strcmp((char *)s,"white"))
1586 		return FG_WHITE;
1587 	else if(!strcmp((char *)s,"cyan"))
1588 		return FG_CYAN;
1589 	else if(!strcmp((char *)s,"magenta"))
1590 		return FG_MAGENTA;
1591 	else if(!strcmp((char *)s,"blue"))
1592 		return FG_BLUE;
1593 	else if(!strcmp((char *)s,"yellow"))
1594 		return FG_YELLOW;
1595 	else if(!strcmp((char *)s,"green"))
1596 		return FG_GREEN;
1597 	else if(!strcmp((char *)s,"red"))
1598 		return FG_RED;
1599 	else if(!strcmp((char *)s,"black"))
1600 		return FG_BLACK;
1601 	else if(!strcmp((char *)s,"bg_white"))
1602 		return BG_WHITE;
1603 	else if(!strcmp((char *)s,"bg_cyan"))
1604 		return BG_CYAN;
1605 	else if(!strcmp((char *)s,"bg_magenta"))
1606 		return BG_MAGENTA;
1607 	else if(!strcmp((char *)s,"bg_blue"))
1608 		return BG_BLUE;
1609 	else if(!strcmp((char *)s,"bg_yellow"))
1610 		return BG_YELLOW;
1611 	else if(!strcmp((char *)s,"bg_green"))
1612 		return BG_GREEN;
1613 	else if(!strcmp((char *)s,"bg_red"))
1614 		return BG_RED;
1615 	else if(!strcmp((char *)s,"bg_black"))
1616 		return BG_BLACK;
1617 	else
1618 		return 0;
1619 }
1620 
1621 /* Generate a field
1622  *
1623  * 't' is SCRN to write to.
1624  * 'scrn' is address of field in character buffer
1625  * 'attr' is address of field in attribute buffer
1626  * 'x', 'y' are starting column and line numbers of field
1627  * 'ofst' is first column within string to display
1628  * 's', 'len' is string to generate in field
1629  * 'atr' is screeen attributes (and color) which should be used
1630  * 'width' is column width of field
1631  * 'flg' if set, erases to end of line
1632  */
1633 
genfield(SCRN * t,int * scrn,int * attr,int x,int y,int ofst,unsigned char * s,int len,int atr,int width,int flg,int * fmt)1634 void genfield(SCRN *t,int *scrn,int *attr,int x,int y,int ofst,unsigned char *s,int len,int atr,int width,int flg,int *fmt)
1635 {
1636 	int col;
1637 	struct utf8_sm sm;
1638 	int last_col = x + width;
1639 
1640 	utf8_init(&sm);
1641 
1642 	for (col = 0;len != 0 && x < last_col; len--) {
1643 		int c = *s++;
1644 		int wid = -1;
1645 		int my_atr = atr;
1646 		if (fmt) my_atr |= *fmt++;
1647 		if (locale_map->type) {
1648 			/* UTF-8 mode: decode character and determine its width */
1649 			c = utf8_decode(&sm,c);
1650 			if (c >= 0)
1651 				wid = joe_wcwidth(c);
1652 		} else {
1653 			/* Byte mode: character is one column wide */
1654 			wid = 1;
1655 		}
1656 		if (wid >= 0) {
1657 			if (col >= ofst) {
1658 				if (x + wid > last_col) {
1659 					/* Character crosses end of field, so fill balance of field with '>' characters instead */
1660 					while (x < last_col) {
1661 						outatr(utf8_map, t, scrn, attr, x, y, '>', my_atr);
1662 						++scrn;
1663 						++attr;
1664 						++x;
1665 					}
1666 				} else /* if (wid >(=) 0) */ {
1667 					/* Emit character */
1668 					outatr(locale_map, t, scrn, attr, x, y, c, my_atr);
1669 					x += wid;
1670 					scrn += wid;
1671 					attr += wid;
1672 				}
1673 			} else if ((col + wid) > ofst) {
1674 				/* Wide character crosses left side of field */
1675 				wid -= ofst - col;
1676 				col = ofst;
1677 				while (wid) {
1678 					outatr(utf8_map, t, scrn, attr, x, y, '<', my_atr);
1679 					++scrn;
1680 					++attr;
1681 					++x;
1682 					++col;
1683 					--wid;
1684 				}
1685 			} else
1686 				col += wid;
1687 		}
1688 	}
1689 	/* Fill balance of field with spaces */
1690 	while (x < last_col) {
1691 		outatr(utf8_map, t, scrn, attr, x, y, ' ', 0);
1692 		++x;
1693 		++scrn;
1694 		++attr;
1695 	}
1696 	/* Erase to end of line */
1697 	if (flg)
1698 		eraeol(t, x, y);
1699 }
1700 
1701 /* Width function for above */
1702 
txtwidth(unsigned char * s,int len)1703 int txtwidth(unsigned char *s,int len)
1704 {
1705 	if (locale_map->type) {
1706 		int col=0;
1707 		struct utf8_sm sm;
1708 		utf8_init(&sm);
1709 
1710 		while(len--) {
1711 			int d = utf8_decode(&sm,*s++);
1712 			if (d >= 0)
1713 				col += joe_wcwidth(d);
1714 		}
1715 
1716 		return col;
1717 	} else
1718 		return len;
1719 }
1720 
1721 /* Generate text with formatting escape sequences */
1722 
genfmt(SCRN * t,int x,int y,int ofst,const unsigned char * s,int flg)1723 void genfmt(SCRN *t, int x, int y, int ofst, const unsigned char *s, int flg)
1724 {
1725 	int *scrn = t->scrn + y * t->co + x;
1726 	int *attr = t->attr + y * t->co + x;
1727 	int atr = 0;
1728 	int col = 0;
1729 	int c;
1730 	struct utf8_sm sm;
1731 
1732 	utf8_init(&sm);
1733 
1734 	while ((c = *s++) != '\0')
1735 		if (c == '\\') {
1736 			switch ((c = *s++) | 0x20) {
1737 			case 'u':
1738 				atr ^= UNDERLINE;
1739 				break;
1740 			case 'i':
1741 				atr ^= INVERSE;
1742 				break;
1743 			case 'b':
1744 				atr ^= BOLD;
1745 				break;
1746 			case 'd':
1747 				atr ^= DIM;
1748 				break;
1749 			case 'f':
1750 				atr ^= BLINK;
1751 				break;
1752 			default: {
1753 				if (!c)
1754 					--s;
1755 				else if (col++ >= ofst) {
1756 					outatr(locale_map, t, scrn, attr, x, y, (c&0x7F), atr);
1757 					++scrn;
1758 					++attr;
1759 					++x;
1760 					}
1761 				break;
1762 				}
1763 			}
1764 		} else {
1765 			int wid = -1;
1766 			if (locale_map->type) {
1767 				/* UTF-8 mode: decode character and determine its width */
1768 				c = utf8_decode(&sm,c);
1769 				if (c >= 0) {
1770 					wid = joe_wcwidth(c);
1771 				}
1772 			} else {
1773 				/* Byte mode: character is one column wide */
1774 				wid = 1;
1775 			}
1776 
1777 			if (wid >= 0) {
1778 				if (col >= ofst) {
1779 					outatr(locale_map, t, scrn, attr, x, y, c, atr);
1780 					scrn += wid;
1781 					attr += wid;
1782 					x += wid;
1783 					col += wid;
1784 				} else if (col+wid>ofst) {
1785 					while (col<ofst) {
1786 						++col;
1787 						--wid;
1788 					}
1789 					while (wid) {
1790 						outatr(utf8_map, t, scrn, attr, x, y, '<', atr);
1791 						++scrn;
1792 						++attr;
1793 						++x;
1794 						++col;
1795 						--wid;
1796 					}
1797 				} else
1798 					col += wid;
1799 		}
1800 		}
1801 	if (flg)
1802 		eraeol(t, x, y);
1803 }
1804 
1805 /* Determine column width of string with format codes */
1806 
fmtlen(const unsigned char * s)1807 int fmtlen(const unsigned char *s)
1808 {
1809 	int col = 0;
1810 	struct utf8_sm sm;
1811 	int c;
1812 
1813 	utf8_init(&sm);
1814 
1815 	while ((c = (*s++))) {
1816 		if (c == '\\') {
1817 			switch (*s++) {
1818 			case 'u':
1819 			case 'i':
1820 			case 'd':
1821 			case 'f':
1822 			case 'b':
1823 			case 'U':
1824 			case 'I':
1825 			case 'D':
1826 			case 'F':
1827 			case 'B':
1828 				continue;
1829 			case 0:
1830 				return col;
1831 			default:
1832 				++col;
1833 				continue;
1834 			}
1835 		} else {
1836 			int wid = 0;
1837 			if(locale_map->type) {
1838 				c = utf8_decode(&sm,c);
1839 				if (c>=0)
1840 					wid = joe_wcwidth(c);
1841 			} else {
1842 				wid = 1;
1843 			}
1844 			col += wid;
1845 		}
1846 	}
1847 	return col;
1848 }
1849 
1850 /* Return offset within format string which corresponds to a particular
1851    column */
1852 
1853 /* FIXME: this is not valid if we land in the middle of a double-wide character */
1854 
fmtpos(unsigned char * s,int goal)1855 int fmtpos(unsigned char *s, int goal)
1856 {
1857 	unsigned char *org = s;
1858 	int col = 0;
1859 	int c;
1860 	struct utf8_sm sm;
1861 
1862 	utf8_init(&sm);
1863 
1864 	while ((c= *s) && col<goal) {
1865 		s++;
1866 		if (c == '\\') {
1867 			switch (*s++) {
1868 			case 'u':
1869 			case 'i':
1870 			case 'd':
1871 			case 'f':
1872 			case 'b':
1873 			case 'U':
1874 			case 'I':
1875 			case 'D':
1876 			case 'F':
1877 			case 'B':
1878 				continue;
1879 			case 0:
1880 				--s;
1881 				break;
1882 			default:
1883 				++col;
1884 				continue;
1885 			}
1886 		} else {
1887 			int wid = 0;
1888 			if(locale_map->type) {
1889 				c = utf8_decode(&sm,c);
1890 				if (c>=0)
1891 					wid = joe_wcwidth(c);
1892 			} else {
1893 				wid = 1;
1894 			}
1895 			col += wid;
1896 		}
1897 	}
1898 
1899 	return s - org + goal - col;
1900 }
1901