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 "types.h"
9 
10 int bg_text = 0; /* Background color for text */
11 int skiptop = 0;
12 int env_lines = 0;
13 int env_columns = 0;
14 int notite = 0;
15 int brpaste = 0;
16 int nolinefeeds = 0;
17 int opt_usetabs = 0;
18 int assume_color = 0;
19 int assume_256color = 0;
20 
21 /* How to display characters (especially the control ones) */
22 /* here are characters ... */
23 char xlatc[256] = {
24 	 64,  65,  66,  67,  68,  69,  70,  71,			/*   8 */
25 	 72,  73,  74,  75,  76,  77,  78,  79,			/*  16 */
26 	 80,  81,  82,  83,  84,  85,  86,  87,			/*  24 */
27 	 88,  89,  90,  91,  92,  93,  94,  95,			/*  32 */
28 	 32,  33,  34,  35,  36,  37,  38,  39,			/*  40 */
29 	 40,  41,  42,  43,  44,  45,  46,  47,			/*  48 */
30 	 48,  49,  50,  51,  52,  53,  54,  55,			/*  56 */
31 	 56,  57,  58,  59,  60,  61,  62,  63,			/*  64 */
32 
33 	 64,  65,  66,  67,  68,  69,  70,  71,			/*  72 */
34 	 72,  73,  74,  75,  76,  77,  78,  79,			/*  80 */
35 	 80,  81,  82,  83,  84,  85,  86,  87,			/*  88 */
36 	 88,  89,  90,  91,  92,  93,  94,  95,			/*  96 */
37 	 96,  97,  98,  99, 100, 101, 102, 103,			/* 104 */
38 	104, 105, 106, 107, 108, 109, 110, 111,			/* 112 */
39 	112, 113, 114, 115, 116, 117, 118, 119,			/* 120 */
40 	120, 121, 122, 123, 124, 125, 126,  63,			/* 128 */
41 
42 	 64,  65,  66,  67,  68,  69,  70,  71,			/* 136 */
43 	 72,  73,  74,  75,  76,  77,  78,  79,			/* 144 */
44 	 80,  81,  82,  83,  84,  85,  86,  87,			/* 152 */
45 	 88,  89,  90,  91,  92,  93,  94,  95,			/* 160 */
46 	 32,  33,  34,  35,  36,  37,  38,  39,			/* 168 */
47 	 40,  41,  42,  43,  44,  45,  46,  47,			/* 176 */
48 	 48,  49,  50,  51,  52,  53,  54,  55,			/* 184 */
49 	 56,  57,  58,  59,  60,  61,  62,  63,			/* 192 */
50 
51 	 64,  65,  66,  67,  68,  69,  70,  71,			/* 200 */
52 	 72,  73,  74,  75,  76,  77,  78,  79,			/* 208 */
53 	 80,  81,  82,  83,  84,  85,  86,  87,			/* 216 */
54 	 88,  89,  90,  91,  92,  93,  94,  95,			/* 224 */
55 	 96,  97,  98,  99, 100, 101, 102, 103,			/* 232 */
56 	104, 105, 106, 107, 108, 109, 110, 111,			/* 240 */
57 	112, 113, 114, 115, 116, 117, 118, 119,			/* 248 */
58 	120, 121, 122, 123, 124, 125, 126,  63			/* 256 */
59 };
60 /* ... and here their attributes */
61 int xlata[256] = {
62 	UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE,		/*   4 */
63 	UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE,		/*   8 */
64 	UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE,		/*  12 */
65 	UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE,		/*  16 */
66 	UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE,		/*  20 */
67 	UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE,		/*  24 */
68 	UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE,		/*  28 */
69 	UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE,		/*  32 */
70 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  48 */
71 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  64 */
72 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  80 */
73 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  96 */
74 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 112 */
75 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, UNDERLINE,	/* 128 */
76 
77 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 130 */
78 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 132 */
79 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 134 */
80 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 136 */
81 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 138 */
82 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 140 */
83 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 142 */
84 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 144 */
85 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 146 */
86 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 148 */
87 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 150 */
88 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 152 */
89 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 154 */
90 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 156 */
91 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 158 */
92 	INVERSE + UNDERLINE, INVERSE + UNDERLINE,		/* 160 */
93 
94 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 164 */
95 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 168 */
96 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 172 */
97 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 176 */
98 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 180 */
99 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 184 */
100 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 188 */
101 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 192 */
102 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 196 */
103 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 200 */
104 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 204 */
105 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 208 */
106 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 212 */
107 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 216 */
108 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 220 */
109 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 224 */
110 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 228 */
111 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 232 */
112 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 236 */
113 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 240 */
114 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 244 */
115 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 248 */
116 	INVERSE, INVERSE, INVERSE, INVERSE,			/* 252 */
117 	INVERSE, INVERSE, INVERSE, INVERSE + UNDERLINE		/* 256 */
118 };
119 
mfill(int (* dest)[COMPOSE],int val,ptrdiff_t count)120 static void mfill(int (*dest)[COMPOSE], int val, ptrdiff_t count)
121 {
122 	ptrdiff_t x;
123 	while (count--) {
124 		(*dest)[0] = val;
125 		for (x = 1; x != COMPOSE; ++x)
126 			(*dest)[x] = 0;
127 		++dest;
128 	}
129 }
130 
131 /* For terminals with the "xn" capability, which includes all popular
132  * graphical terminal emulators: When a character is printed in the last
133  * (let's say 80th) column, the cursor enters a special state. If a letter is
134  * printed next (which shouldn't happen in joe) then it wraps to the next
135  * line. However, if the cursor is moved, it behaves as if it still stood in
136  * the 80th column, rather than in the invisible 81st as joe thinks. Some
137  * terminal emulators (e.g. xterm) display this cursor in the 80th (rightmost)
138  * column, some others (e.g. gnome-terminal) don't show it at all, yet some
139  * others (e.g. konsole) show it at the beginning of the next line. This is a
140  * state at which we never want to settle in joe, it's just a necessary
141  * transient state while printing in the rightmost column. This method makes
142  * sure that the cursor is not in this strange state, moving it out of there
143  * if necessary.
144  *
145  * For terminal without "xn": In nresize() just revert to not using the
146  * rightmost column. Another possibility would be to do a character insertion
147  * to push an already printed character to the right, as implemented in pts's
148  * fork of joe at http://pts-mini-gpl.googlecode.com/svn/trunk/joe-p37/ but
149  * it's quite complicated (especially with CJK) and probably not worth the
150  * trouble.
151  *
152  * More information:
153  * https://www.gnu.org/software/termutils/manual/termcap-1.3/html_chapter/termcap_4.html#SEC27
154  */
155 
fixupcursor(register SCRN * t)156 static void fixupcursor(register SCRN *t)
157 {
158 	if (t->x == t->co) {
159 		texec(t->cap, t->cr, 1, 0, 0, 0, 0);
160 		t->x = 0;
161 	}
162 }
163 
164 /* Set attributes */
165 
set_attr(SCRN * t,int c)166 int set_attr(SCRN *t, int c)
167 {
168 	int e;
169 
170 	/* Attributes which have gone off */
171 	e = ((AT_MASK|FG_NOT_DEFAULT|BG_NOT_DEFAULT)&t->attrib & ~c);
172 
173 	if (e) {	/* If any attribute go off, switch them all off: fixes bug on PCs */
174 		if (t->me)
175 			texec(t->cap, t->me, 1, 0, 0, 0, 0);
176 		else {
177 			if (t->ue)
178 				texec(t->cap, t->ue, 1, 0, 0, 0, 0);
179 			if (t->se)
180 				texec(t->cap, t->se, 1, 0, 0, 0, 0);
181 			if (t->ZR)
182 				texec(t->cap, t->ZR, 1, 0, 0, 0, 0);
183 		}
184 		t->attrib = 0;
185 	}
186 
187 	/* Attributes which have turned on */
188 	e = (c & ~t->attrib);
189 
190 	if (e & INVERSE) {
191 		if (t->mr)
192 			texec(t->cap, t->mr, 1, 0, 0, 0, 0);
193 		else if (t->so)
194 			texec(t->cap, t->so, 1, 0, 0, 0, 0);
195 	}
196 
197 	if (e & UNDERLINE)
198 		if (t->us)
199 			texec(t->cap, t->us, 1, 0, 0, 0, 0);
200 
201 	if (e & DOUBLE_UNDERLINE)
202 		if (t->dunderline)
203 			texec(t->cap, t->dunderline, 1, 0, 0, 0, 0);
204 
205 	if (e & CROSSED_OUT)
206 		if (t->stricken)
207 			texec(t->cap, t->stricken, 1, 0, 0, 0, 0);
208 
209 	if (e & BLINK)
210 		if (t->mb)
211 			texec(t->cap, t->mb, 1, 0, 0, 0, 0);
212 	if (e & BOLD)
213 		if (t->md)
214 			texec(t->cap, t->md, 1, 0, 0, 0, 0);
215 	if (e & DIM)
216 		if (t->mh)
217 			texec(t->cap, t->mh, 1, 0, 0, 0, 0);
218 	if (e & ITALIC)
219 		if (t->ZH)
220 			texec(t->cap, t->ZH, 1, 0, 0, 0, 0);
221 
222 	if ((t->attrib & FG_MASK) != (c & FG_MASK)) {
223 		if (t->Sf) {
224 			int color = ((c & FG_VALUE) >> FG_SHIFT);
225 			if (c & FG_TRUECOLOR) {
226 				if (t->truecolor && t->palette && t->palette[color] >= 0) {
227 					char bf[32];
228 					int rgb = t->palette[color];
229 					joe_snprintf_3(bf, SIZEOF(bf), "\033[38;2;%d;%d;%dm", (rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
230 					ttputs(bf);
231 				}
232 			} else if (t->assume_256 && color >= t->Co) {
233 				char bf[32];
234 				joe_snprintf_1(bf, SIZEOF(bf), "\033[38;5;%dm", color);
235 				ttputs(bf);
236 			} else {
237 				if (t->Co & (t->Co - 1))
238 					texec(t->cap, t->Sf, 1, color % t->Co, 0, 0, 0);
239 				else
240 					texec(t->cap, t->Sf, 1, color & (t->Co - 1), 0, 0, 0);
241 			}
242 		}
243 	}
244 
245 	if ((t->attrib & BG_MASK) != (c & BG_MASK)) {
246 		if (t->Sb) {
247 			int color = ((c & BG_VALUE) >> BG_SHIFT);
248 			if (c & BG_TRUECOLOR) {
249 				if (t->truecolor && t->palette && t->palette[color] >= 0) {
250 					char bf[32];
251 					int rgb = t->palette[color];
252 					joe_snprintf_3(bf, SIZEOF(bf), "\033[48;2;%d;%d;%dm", (rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
253 					ttputs(bf);
254 				}
255 			} else if (t->assume_256 && color >= t->Co) {
256 				char bf[32];
257 				joe_snprintf_1(bf,SIZEOF(bf),"\033[48;5;%dm",color);
258 				ttputs(bf);
259 			} else {
260 				if (t->Co & (t->Co - 1))
261 					texec(t->cap, t->Sb, 1, color % t->Co, 0, 0, 0);
262 				else
263 					texec(t->cap, t->Sb, 1, color & (t->Co - 1), 0, 0, 0);
264 			}
265 		}
266 	}
267 
268 	t->attrib = c;
269 
270 	return 0;
271 }
272 
273 /* Output character with attributes */
274 
275 int outatr_state; /* 1 = we have a start character waiting to emit.  2 = we have combining chars we are ignoring */
276 int (*outatr_scrn)[COMPOSE];
277 int *outatr_attrf;
278 int outatr_build[COMPOSE];
279 ptrdiff_t outatr_xx;
280 ptrdiff_t outatr_yy;
281 ptrdiff_t outatr_ofst;
282 int outatr_wid;
283 int outatr_uni_ctrl;
284 int outatr_a;
285 
286 /* Emit accumulated character (start character + compose characters) */
287 
outatr_complete(SCRN * t)288 void outatr_complete(SCRN *t)
289 {
290 	if (outatr_state == 1) {
291 		ptrdiff_t x;
292 		for (x = 0; x != COMPOSE; ++x)
293 			if (outatr_scrn[0][x] != outatr_build[x])
294 				break;
295 		if (x != COMPOSE || *outatr_attrf != outatr_a) {
296 			char buf[16];
297 			for (x = 0; x != COMPOSE; ++x)
298 				outatr_scrn[0][x] = outatr_build[x];
299 			*outatr_attrf = outatr_a;
300 			if (t->ins)
301 				clrins(t);
302 			if (t->x != outatr_xx || t->y != outatr_yy)
303 				cpos(t, outatr_xx, outatr_yy);
304 			if (t->attrib != outatr_a)
305 				set_attr(t, outatr_a);
306 			if (outatr_uni_ctrl) {
307 				sprintf(buf, "<%X>", outatr_build[0]);
308 				ttputs(buf);
309 			} else {
310 				for (x = 0; x != COMPOSE && outatr_build[x]; ++x) {
311 					utf8_encode(buf, outatr_build[x]);
312 					ttputs(buf);
313 				}
314 			}
315 			t->x += outatr_wid;
316 			while (outatr_wid > 1) {
317 				(*++outatr_scrn)[0] = -1;
318 				*++outatr_attrf = 0;
319 				--outatr_wid;
320 			}
321 		}
322 	}
323 	outatr_state = 0;
324 }
325 
outatr(struct charmap * map,SCRN * t,int (* scrn)[COMPOSE],int * attrf,ptrdiff_t xx,ptrdiff_t yy,int c,int a)326 void outatr(struct charmap *map,SCRN *t,int (*scrn)[COMPOSE],int *attrf,ptrdiff_t xx,ptrdiff_t yy,int c,int a)
327 {
328 	if (c < 0)
329 		c += 256;
330 	if(map->type)
331 		if(locale_map->type) {
332 			if (cclass_lookup(cclass_combining, c)) { /* It's a combining character */
333 				if (!outatr_state) /* No start character? */
334 					outatr_state = 2; /* Ignore it... */
335 				else if (outatr_state == 1) { /* We have a start character, add it */
336 					if (outatr_ofst != COMPOSE)
337 						outatr_build[outatr_ofst++] = c;
338 					else { /* More combining chars than we buffer */
339 						char buf[16];
340 						outatr_scrn[0][0] = -1; /* Force outatr_complete to emit character */
341 						outatr_complete(t);
342 						utf8_encode(buf, c);
343 						ttputs(buf);
344 						outatr_state = 3;
345 					}
346 				} else if (outatr_state == 3) { /* We have alread emitted the start character, but we have more combining chars */
347 					char buf[16];
348 					utf8_encode(buf, c);
349 					ttputs(buf);
350 				}
351 			} else {
352 				ptrdiff_t x;
353 				if (outatr_state) /* We already have a start character? */
354 					outatr_complete(t);
355 				outatr_uni_ctrl = 0;
356 				/* Deal with control characters */
357 				if (c < 32) {
358 					c = c + '@';
359 					a ^= UNDERLINE;
360 				} else if (c == 127) {
361 					c = '?';
362 					a ^= UNDERLINE;
363 				} else if (unictrl(c)) {
364 					a ^= UNDERLINE;
365 					outatr_uni_ctrl = 1;
366 				}
367 				outatr_wid = joe_wcwidth(1, c);
368 				outatr_state = 1;
369 				outatr_scrn = scrn;
370 				outatr_attrf = attrf;
371 				outatr_xx = xx;
372 				outatr_yy = yy;
373 				outatr_ofst = 1;
374 				outatr_a = a;
375 				outatr_build[0] = c;
376 				for (x = 1; x != COMPOSE; ++x)
377 					outatr_build[x] = 0;
378 			}
379 		} else {
380 			/* UTF-8 char to non-UTF-8 terminal */
381 			/* Don't convert control chars below 256 */
382 			if ((c>=32 && c<=126) || c>=160) {
383 				if (unictrl(c))
384 					a ^= UNDERLINE;
385 				c = from_uni(locale_map,c);
386 				if (c==-1)
387 					c = '?';
388 			}
389 
390 			/* Deal with control characters */
391 			if (!joe_isprint(locale_map,c) && !(dspasis && c>=128)) {
392 				a ^= xlata[c];
393 				c = xlatc[c];
394 			}
395 
396 			if((*scrn)[0] == c && *attrf == a)
397 				return;
398 
399 			(*scrn)[0] = c;
400 			*attrf = a;
401 			if(t->ins)
402 				clrins(t);
403 			if(t->x != xx || t->y != yy)
404 				cpos(t,xx,yy);
405 			if(t->attrib != a)
406 				set_attr(t,a);
407 			ttputc(TO_CHAR_OK(c));
408 			t->x++;
409 		}
410 	else
411 		if (!locale_map->type) {
412 			/* Non UTF-8 char to non UTF-8 terminal */
413 			/* Byte-byte Translate? */
414 
415 			/* Deal with control characters */
416 			if (!joe_isprint(locale_map,c) && !(dspasis && c>=128)) {
417 				a ^= xlata[c];
418 				c = xlatc[c];
419 			}
420 
421 			if ((*scrn)[0] == c && *attrf == a)
422 				return;
423 
424 			(*scrn)[0] = c;
425 			*attrf = a;
426 
427 			if(t->ins)
428 				clrins(t);
429 			if(t->x != xx || t->y != yy)
430 				cpos(t,xx,yy);
431 			if(t->attrib != a)
432 				set_attr(t,a);
433 			ttputc(TO_CHAR_OK(c));
434 			t->x++;
435 		} else {
436 			/* Non UTF-8 char to UTF-8 terminal */
437 			char buf[16];
438 			int wid;
439 
440 			/* Deal with control characters */
441 			if (!(dspasis && c>=128) && !joe_isprint(map,c)) {
442 				a ^= xlata[c];
443 				c = xlatc[c];
444 			}
445 
446 			c = to_uni(map,c);
447 			if (c == -1)
448 				c = '?';
449 			utf8_encode(buf,c);
450 
451 			if ((*scrn)[0] == c && *attrf == a)
452 				return;
453 
454 			wid = joe_wcwidth(0,c);
455 			(*scrn)[0] = c;
456 			*attrf = a;
457 			if(t->ins)
458 				clrins(t);
459 			if(t->x != xx || t->y != yy)
460 				cpos(t, xx, yy);
461 			if(t->attrib != a)
462 				set_attr(t, a);
463 			ttputs(buf);
464 			t->x+=wid;
465 			while(wid>1) {
466 				(*++scrn)[0] = -1;
467 				*++attrf= 0;
468 				--wid;
469 			}
470 		}
471 /* At this point, t->x might equal t->co.  Fixupcursor() will be called by next call
472    to cpos to deal with this. */
473 }
474 
475 /* Set scrolling region */
476 
setregn(SCRN * t,ptrdiff_t top,ptrdiff_t bot)477 static void setregn(SCRN *t, ptrdiff_t top, ptrdiff_t bot)
478 {
479 	if (!t->cs) {
480 		t->top = top;
481 		t->bot = bot;
482 		return;
483 	}
484 	if (t->top != top || t->bot != bot) {
485 		t->top = top;
486 		t->bot = bot;
487 		texec(t->cap, t->cs, 1, top, bot - 1, 0, 0);
488 		t->x = -1;
489 		t->y = -1;
490 	}
491 }
492 
493 /* Exit insert mode */
494 
clrins(SCRN * t)495 int clrins(SCRN *t)
496 {
497 	if (t->ins != 0) {
498 		texec(t->cap, t->ei, 1, 0, 0, 0, 0);
499 		t->ins = 0;
500 	}
501 	return 0;
502 }
503 
504 /* Erase from given screen coordinate to end of line */
505 
506 /* Now we store a '\n' mark in the screen buffer.  The idea is to emit ESC [
507  * K at the \n and remember that we did it.  This way spaces at the end of
508  * the line are recorded in the terminal emulator so that they are preserved
509  * with cut/paste operations.
510  */
511 
eraeol(SCRN * t,ptrdiff_t x,ptrdiff_t y,int atr)512 int eraeol(SCRN *t, ptrdiff_t x, ptrdiff_t y, int atr)
513 {
514 	int (*s)[COMPOSE], (*ss)[COMPOSE], *a, *aa;
515 	ptrdiff_t w = t->co - x;
516 
517 	if (w <= 0)
518 		return 0;
519 	s = t->scrn + y * t->co + x;
520 	a = t->attr + y * t->co + x;
521 	ss = s + w;
522 	aa = a + w;
523 	do {
524 		--ss;
525 		if ((*ss)[0] != (ss == s ? '\n' : ' ')) {
526 			++ss;
527 			break;
528 		} else if (*--aa != atr) {
529 			++ss;
530 			++aa;
531 			break;
532 		}
533 	} while (ss != s);
534 	if (s != ss) {
535 		if (t->ce) {
536 			cpos(t, x, y);
537 			if(t->attrib != atr)
538 				set_attr(t, atr);
539 			texec(t->cap, t->ce, 1, 0, 0, 0, 0);
540 			mfill(s, ' ', w);
541 			msetI(a, atr, w);
542 			(*s)[0] = '\n';
543 		} else {
544 			if (t->ins)
545 				clrins(t);
546 			if (t->x != x || t->y != y)
547 				cpos(t, x, y);
548 			if (t->attrib != atr)
549 				set_attr(t, atr);
550 			(*s)[0] = '\n';
551 			*a = atr;
552 			ttputc(' ');
553 			++t->x;
554 			++s;
555 			++a;
556 			while (s != ss) {
557 				(*s)[0] = ' ';
558 				*a = atr;
559 				ttputc(' ');
560 				++t->x;
561 				++s;
562 				++a;
563 			}
564 		}
565 	}
566 	return 0;
567 }
568 
out(void * t,char c)569 static void out(void *t, char c)
570 {
571 	ttputc(c);
572 }
573 
nopen(CAP * cap)574 SCRN *nopen(CAP *cap)
575 {
576 	SCRN *t = (SCRN *) joe_malloc(SIZEOF(SCRN));
577 	ptrdiff_t x, y, co, li;
578 	int ansiish;
579 
580 	ttopen();
581 
582 	t->cap = cap;
583 	setcap(cap, tty_baud, out, NULL);
584 
585 	li = getnum(t->cap,"li");
586 	if (li < 1)
587 		li = 24;
588 	co = getnum(t->cap,"co");
589 	if (co < 2)
590 		co = 80;
591 	x = y = 0;
592 	ttgtsz(&x, &y);
593 	if (x > 7 && y > 3) {
594 		li = y;
595 		co = x;
596 	}
597 	t->co = t->li = -1;  /* will be set by nresize() below */
598 
599 	t->haz = getflag(t->cap,"hz");
600 	t->os = getflag(t->cap,"os");
601 	t->eo = getflag(t->cap,"eo");
602 	if (getflag(t->cap,"hc"))
603 		t->os = 1;
604 	if (t->os || getflag(t->cap,"ul"))
605 		t->ul = 1;
606 	else
607 		t->ul = 0;
608 
609 	t->xn = getflag(t->cap,"xn");
610 	t->am = getflag(t->cap,"am");
611 
612 	if (notite)
613 		t->ti = 0;
614 	else
615 		t->ti = jgetstr(t->cap,"ti");
616 	t->cl = jgetstr(t->cap,"cl");
617 	t->cd = jgetstr(t->cap,"cd");
618 
619 	if (notite)
620 		t->te = 0;
621 	else
622 		t->te = jgetstr(t->cap,"te");
623 
624 	t->ut = getflag(t->cap,"ut");
625 	t->Sb = jgetstr(t->cap,"AB");
626 	if (!t->Sb) t->Sb = jgetstr(t->cap,"Sb");
627 	t->Sf = jgetstr(t->cap,"AF");
628 	if (!t->Sf) t->Sf = jgetstr(t->cap,"Sf");
629 	t->Co = getnum(t->cap,"Co");
630 	if (t->Co == -1)
631 		t->Co = 8;
632 
633 	t->mb = NULL;
634 	t->md = NULL;
635 	t->mh = NULL;
636 	t->mr = NULL;
637 	t->stricken = NULL;
638 	t->dunderline = NULL;
639 	t->avattr = 0;
640 	if (!(t->me = jgetstr(t->cap,"me")))
641 		goto oops;
642 	if ((t->mb = jgetstr(t->cap,"mb")))
643 		t->avattr |= BLINK;
644 	if ((t->md = jgetstr(t->cap,"md")))
645 		t->avattr |= BOLD;
646 	if ((t->mh = jgetstr(t->cap,"mh")))
647 		t->avattr |= DIM;
648 	if ((t->mr = jgetstr(t->cap,"mr")))
649 		t->avattr |= INVERSE;
650       oops:
651 
652 	/* Does it look like an ansi terminal? (it has bold which begins with ESC [) */
653 #ifndef TERMINFO
654 	ansiish = t->md && t->md[0] == '\\' && t->md[1] == 'E' && t->md[2] == '[';
655 #else
656 	ansiish = t->md && t->md[0] == '\033' && t->md[1] == '[';
657 #endif
658 
659 	/* Cheating here: there is no capability for stricken and double-underline, so we just assume it has it if terminal looks ansi */
660 	if (ansiish) {
661 		t->stricken = "\033[9m";
662 	}
663 
664 	if (ansiish) {
665 		t->dunderline = "\033[21m";
666 	}
667 
668 	/* No termcap for bracketed paste.  ANSI-looking terminals will either support bracketed paste
669 	   or this setting will cause no harm. */
670 	if (ansiish && brpaste) {
671 #ifndef TERMINFO
672 		t->brp = "\\E[?2004h";
673 		t->bre = "\\E[?2004l";
674 #else
675 		t->brp = "\033[?2004h";
676 		t->bre = "\033[?2004l";
677 #endif
678 	} else {
679 		t->brp = t->bre = 0;
680 	}
681 
682 	if (assume_color || assume_256color) {
683 		/* Install 8 color support if it looks like an ansi terminal */
684 		if (ansiish && !t->Sf) {
685 #ifndef TERMINFO
686 			t->ut = 1;
687 			t->Sf = "\\E[3%dm";
688 			t->Sb = "\\E[4%dm";
689 			t->Co = 8;
690 #else
691 			t->ut = 1;
692 			t->Sf = "\033[3%p1%dm";
693 			t->Sb = "\033[4%p1%dm";
694 #endif
695 		}
696 	}
697 
698 	t->assume_256 = 0;
699       	if (assume_256color && t->Co < 256) {
700 		/* Force 256 color support */
701 		if (ansiish) {
702 			t->assume_256 = 1;
703 #ifndef TERMINFO
704 #ifdef junk
705 			t->ut = 1;
706 			t->Sf = "\\E[38;5;%dm";
707 			t->Sb = "\\E[48;5;%dm";
708 #endif
709 #else
710 #ifdef junk
711 			t->ut = 1;
712 			t->Sf = "\033[38;5;%p1%dm";
713 			t->Sb = "\033[48;5;%p1%dm";
714 #endif
715 #endif
716 		}
717 	}
718 
719 	{
720 		char *s = getenv("COLORTERM");
721 		t->truecolor = s && (!zicmp(s, "truecolor") || !zicmp(s, "24bit"));
722 		t->palette = NULL;
723 	}
724 
725 	t->so = NULL;
726 	t->se = NULL;
727 	if (getnum(t->cap,"sg") <= 0 && !t->mr && jgetstr(t->cap,"se")) {
728 		if ((t->so = jgetstr(t->cap,"so")) != NULL)
729 			t->avattr |= INVERSE;
730 		t->se = jgetstr(t->cap,"se");
731 	}
732 	if (getflag(t->cap,"xs") || getflag(t->cap,"xt"))
733 		t->so = NULL;
734 
735 	t->us = NULL;
736 	t->ue = NULL;
737 	if (getnum(t->cap,"ug") <= 0 && jgetstr(t->cap,"ue")) {
738 		if ((t->us = jgetstr(t->cap,"us")) != NULL)
739 			t->avattr |= UNDERLINE;
740 		t->ue = jgetstr(t->cap,"ue");
741 	}
742 
743 	t->ZH = NULL;
744 	t->ZR = NULL;
745 	if ((t->ZH = jgetstr(t->cap,"ZH")) != NULL)
746 			t->avattr |= ITALIC;
747 		t->ZR = jgetstr(t->cap,"ZR");
748 
749 	if (!(t->uc = jgetstr(t->cap,"uc")))
750 		if (t->ul)
751 			t->uc ="_";
752 	if (t->uc)
753 		t->avattr |= UNDERLINE;
754 
755 	t->ms = getflag(t->cap,"ms");
756 
757 	t->da = getflag(t->cap,"da");
758 	t->db = getflag(t->cap,"db");
759 	t->cs = jgetstr(t->cap,"cs");
760 	t->rr = getflag(t->cap,"rr");
761 	t->sf = jgetstr(t->cap,"sf");
762 	t->sr = jgetstr(t->cap,"sr");
763 	t->SF = jgetstr(t->cap,"SF");
764 	t->SR = jgetstr(t->cap,"SR");
765 	t->al = jgetstr(t->cap,"al");
766 	t->dl = jgetstr(t->cap,"dl");
767 	t->AL = jgetstr(t->cap,"AL");
768 	t->DL = jgetstr(t->cap,"DL");
769 	if (!getflag(t->cap,"ns") && !t->sf)
770 		t->sf ="\12";
771 
772 	if (!getflag(t->cap,"in") && tty_baud < 38400) {
773 		t->dc = jgetstr(t->cap,"dc");
774 		t->DC = jgetstr(t->cap,"DC");
775 		t->dm = jgetstr(t->cap,"dm");
776 		t->ed = jgetstr(t->cap,"ed");
777 
778 		t->im = jgetstr(t->cap,"im");
779 		t->ei = jgetstr(t->cap,"ei");
780 		t->ic = jgetstr(t->cap,"ic");
781 		t->IC = jgetstr(t->cap,"IC");
782 		t->ip = jgetstr(t->cap,"ip");
783 		t->mi = getflag(t->cap,"mi");
784 	} else {
785 		t->dm = NULL;
786 		t->dc = NULL;
787 		t->DC = NULL;
788 		t->ed = NULL;
789 		t->im = NULL;
790 		t->ic = NULL;
791 		t->IC = NULL;
792 		t->ip = NULL;
793 		t->ei = NULL;
794 		t->mi = 1;
795 	}
796 
797 	t->bs = NULL;
798 	if (jgetstr(t->cap,"bc"))
799 		t->bs = jgetstr(t->cap,"bc");
800 	else if (jgetstr(t->cap,"le"))
801 		t->bs = jgetstr(t->cap,"le");
802 	if (getflag(t->cap,"bs"))
803 		t->bs ="\10";
804 
805 	t->cbs = tcost(t->cap, t->bs, 1, 2, 2, 0, 0);
806 
807 	t->lf ="\12";
808 	if (jgetstr(t->cap,"do"))
809 		t->lf = jgetstr(t->cap,"do");
810 	t->clf = tcost(t->cap, t->lf, 1, 2, 2, 0, 0);
811 
812 	t->up = jgetstr(t->cap,"up");
813 	t->cup = tcost(t->cap, t->up, 1, 2, 2, 0, 0);
814 
815 	t->nd = jgetstr(t->cap,"nd");
816 
817 	t->tw = 8;
818 	if (getnum(t->cap,"it") > 0)
819 		t->tw = getnum(t->cap,"it");
820 	else if (getnum(t->cap,"tw") > 0)
821 		t->tw = getnum(t->cap,"tw");
822 
823 	if (!(t->ta = jgetstr(t->cap,"ta")))
824 		if (getflag(t->cap,"pt"))
825 			t->ta ="\11";
826 	t->bt = jgetstr(t->cap,"bt");
827 	if (getflag(t->cap,"xt")) {
828 		t->ta = NULL;
829 		t->bt = NULL;
830 	}
831 
832 	if (!opt_usetabs) {
833 		t->ta = NULL;
834 		t->bt = NULL;
835 	}
836 
837 	t->cta = tcost(t->cap, t->ta, 1, 2, 2, 0, 0);
838 	t->cbt = tcost(t->cap, t->bt, 1, 2, 2, 0, 0);
839 
840 	t->ho = jgetstr(t->cap,"ho");
841 	t->cho = tcost(t->cap, t->ho, 1, 2, 2, 0, 0);
842 	t->ll = jgetstr(t->cap,"ll");
843 	t->cll = tcost(t->cap, t->ll, 1, 2, 2, 0, 0);
844 
845 	t->cr ="\15";
846 	if (jgetstr(t->cap,"cr"))
847 		t->cr = jgetstr(t->cap,"cr");
848 	if (getflag(t->cap,"nc") || getflag(t->cap,"xr"))
849 		t->cr = NULL;
850 	t->ccr = tcost(t->cap, t->cr, 1, 2, 2, 0, 0);
851 
852 	t->cRI = tcost(t->cap, t->RI = jgetstr(t->cap,"RI"), 1, 2, 2, 0, 0);
853 	t->cLE = tcost(t->cap, t->LE = jgetstr(t->cap,"LE"), 1, 2, 2, 0, 0);
854 	t->cUP = tcost(t->cap, t->UP = jgetstr(t->cap,"UP"), 1, 2, 2, 0, 0);
855 	t->cDO = tcost(t->cap, t->DO = jgetstr(t->cap,"DO"), 1, 2, 2, 0, 0);
856 	t->cch = tcost(t->cap, t->ch = jgetstr(t->cap,"ch"), 1, 2, 2, 0, 0);
857 	t->ccv = tcost(t->cap, t->cv = jgetstr(t->cap,"cv"), 1, 2, 2, 0, 0);
858 	t->ccV = tcost(t->cap, t->cV = jgetstr(t->cap,"cV"), 1, 2, 2, 0, 0);
859 	t->ccm = tcost(t->cap, t->cm = jgetstr(t->cap,"cm"), 1, 2, 2, 0, 0);
860 
861 	t->cce = tcost(t->cap, t->ce = jgetstr(t->cap,"ce"), 1, 2, 2, 0, 0);
862 
863 /* Make sure terminal can do absolute positioning */
864 	if (t->cm)
865 		goto ok;
866 	if (t->ch && t->cv)
867 		goto ok;
868 	if (t->ho && (t->lf || t->DO || t->cv))
869 		goto ok;
870 	if (t->ll && (t->up || t->UP || t->cv))
871 		goto ok;
872 	if (t->cr && t->cv)
873 		goto ok;
874 	leave = 1;
875 	ttclose();
876 	signrm();
877         fprintf(stderr,"cm=%p ch=%p cv=%p ho=%p lf=%p DO=%p ll=%p up=%p UP=%p cr=%p\n",
878                        t->cm, t->ch, t->cv, t->ho, t->lf, t->DO, t->ll, t->up, t->UP, t->cr);
879 	fputs(joe_gettext(_("Sorry, your terminal can't do absolute cursor positioning.\nIt's broken\n")), stderr);
880 	return NULL;
881       ok:
882 
883 /* Determine if we can scroll */
884 	if (((t->sr || t->SR) && (t->sf || t->SF) && t->cs) || ((t->al || t->AL) && (t->dl || t->DL)))
885 		t->scroll = 1;
886 	else {
887 		t->scroll = 0;
888 		if (tty_baud < 38400)
889 			opt_mid = 1;
890 	}
891 
892 /* Determine if we can ins/del within lines */
893 	if ((t->im || t->ic || t->IC) && (t->dc || t->DC))
894 		t->insdel = 1;
895 	else
896 		t->insdel = 0;
897 
898 /* Adjust for high baud rates */
899 	if (tty_baud >= 38400) {
900 		/* t->scroll = 0; */ /* With all the output from syntax highlighting, it's now better to scroll in terminal emulators */
901 		t->insdel = 0;
902 	}
903 
904 /* Adjust for low baud rates */
905 	if (tty_baud < 38400) {
906 		opt_left = -2;
907 		opt_right = -2;
908 	}
909 
910 /* Send out li linefeeds so that scroll-back history is not lost */
911 	if (notite && !nolinefeeds) {
912 		for (y = 1; y < li; ++y)
913 			ttputc(10);
914 	}
915 
916 /* Send out terminal initialization string */
917 	if (t->ti)
918 		texec(t->cap, t->ti, 1, 0, 0, 0, 0);
919 	if (!skiptop && t->cl)
920 		texec(t->cap, t->cl, 1, 0, 0, 0, 0);
921 	if (t->brp)
922 		texec(t->cap, t->brp, 1, 0, 0, 0, 0);
923 
924 /* Initialize variable screen size dependent vars */
925 	t->scrn = NULL;
926 	t->attr = NULL;
927 	t->sary = NULL;
928 	t->updtab = NULL;
929 	t->compose = NULL;
930 	t->ofst = NULL;
931 	t->ary = NULL;
932 	t->htab = (struct hentry *) joe_malloc(256 * SIZEOF(struct hentry));
933 
934 	nresize(t, co, li);
935 
936 /* Initialize mouse */
937 	mouseopen();
938 
939 	return t;
940 }
941 
942 /* Change size of screen. Decrease width by 1 if printing in the last column is not supported */
943 
nresize(SCRN * t,ptrdiff_t w,ptrdiff_t h)944 int nresize(SCRN *t, ptrdiff_t w, ptrdiff_t h)
945 {
946 	if (h < skiptop + 1)
947 		h = skiptop + 1;
948 	if (w < 8)
949 		w = 8;
950 	if (!t->xn)
951 		w--;  /* Don't write to the last column if terminal lacks xn capability */
952 	if (h == t->li && w == t->co)
953 		return 0;
954 	t->li = h;
955 	t->co = w;
956 	if (t->sary)
957 		joe_free(t->sary);
958 	if (t->updtab)
959 		joe_free(t->updtab);
960 	if (t->scrn)
961 		joe_free(t->scrn);
962 	if (t->attr)
963 		joe_free(t->attr);
964 	if (t->compose)
965 		joe_free(t->compose);
966 	if (t->ofst)
967 		joe_free(t->ofst);
968 	if (t->ary)
969 		joe_free(t->ary);
970 	t->scrn = (int (*)[COMPOSE])joe_malloc(t->li * t->co * SIZEOF(int [COMPOSE]));
971 	t->attr = (int *)joe_malloc(t->li * t->co * SIZEOF(int));
972 	t->sary = (ptrdiff_t *)joe_calloc(t->li, SIZEOF(ptrdiff_t));
973 	t->updtab = (int *)joe_malloc(t->li * SIZEOF(int));
974 	t->compose = (int *)joe_malloc(t->co * SIZEOF(int));
975 	t->ofst = (ptrdiff_t *)joe_malloc(t->co * SIZEOF(ptrdiff_t));
976 	t->ary = (struct hentry *)joe_malloc(t->co * SIZEOF(struct hentry));
977 
978 	nredraw(t);
979 	return 1;
980 }
981 
982 /* Calculate cost of positioning the cursor using only relative cursor
983  * positioning functions: t->(lf, DO, up, UP, bs, LE, RI, ta, bt) and rewriting
984  * characters (to move right)
985  *
986  * This doesn't use the am and bw capabilities although it probably could.
987  */
988 
relcost(register SCRN * t,register ptrdiff_t x,register ptrdiff_t y,register ptrdiff_t ox,register ptrdiff_t oy)989 static ptrdiff_t relcost(register SCRN *t, register ptrdiff_t x, register ptrdiff_t y, register ptrdiff_t ox, register ptrdiff_t oy)
990 {
991 	ptrdiff_t cost = 0;
992 
993 /* If we don't know the cursor position, force use of absolute positioning */
994 	if (oy == -1 || ox == -1)
995 		return 10000;
996 
997 /* First adjust row */
998 	if (y > oy) {
999 		ptrdiff_t dist = y - oy;
1000 
1001 		/* Have to go down */
1002 		if (t->lf) {
1003 			ptrdiff_t mult = dist * t->clf;
1004 
1005 			if (dist < 10 && t->cDO < mult)
1006 				cost += t->cDO;
1007 			else if (dist >= 10 && t->cDO + 1 < mult)
1008 				cost += t->cDO + 1;
1009 			else
1010 				cost += mult;
1011 		} else if (t->DO)
1012 			if (dist < 10)
1013 				cost += t->cDO;
1014 			else
1015 				cost += t->cDO + 1;
1016 		else
1017 			return 10000;
1018 	} else if (y < oy) {
1019 		ptrdiff_t dist = oy - y;
1020 
1021 		/* Have to go up */
1022 		if (t->up) {
1023 			ptrdiff_t mult = dist * t->cup;
1024 
1025 			if (dist < 10 && t->cUP < mult)
1026 				cost += t->cUP;
1027 			else if (dist >= 10 && t->cUP < mult)
1028 				cost += t->cUP + 1;
1029 			else
1030 				cost += mult;
1031 		} else if (t->UP)
1032 			if (dist < 10)
1033 				cost += t->cUP;
1034 			else
1035 				cost += t->cUP + 1;
1036 		else
1037 			return 10000;
1038 	}
1039 
1040 /* Now adjust column */
1041 
1042 /* Use tabs */
1043 	if (x > ox && t->ta) {
1044 		ptrdiff_t dist = x - ox;
1045 		ptrdiff_t ntabs = (dist + ox % t->tw) / t->tw;
1046 		ptrdiff_t cstunder = x % t->tw + t->cta * ntabs;
1047 		ptrdiff_t cstover;
1048 
1049 		if (x + t->tw < t->co && t->bs)
1050 			cstover = t->cbs * (t->tw - x % t->tw) + t->cta * (ntabs + 1);
1051 		else
1052 			cstover = 10000;
1053 		if (dist < 10 && cstunder < t->cRI && cstunder < x - ox && cstover > cstunder)
1054 			return cost + cstunder;
1055 		else if (cstunder < t->cRI + 1 && cstunder < x - ox && cstover > cstunder)
1056 			return cost + cstunder;
1057 		else if (dist < 10 && cstover < t->cRI && cstover < x - ox)
1058 			return cost + cstover;
1059 		else if (cstover < t->cRI + 1 && cstover < x - ox)
1060 			return cost + cstover;
1061 	} else if (x < ox && t->bt) {
1062 		ptrdiff_t dist = ox - x;
1063 		ptrdiff_t ntabs = (dist + t->tw - ox % t->tw) / t->tw;
1064 		ptrdiff_t cstunder, cstover;
1065 
1066 		if (t->bs)
1067 			cstunder = t->cbt * ntabs + t->cbs * (t->tw - x % t->tw);
1068 		else
1069 			cstunder = 10000;
1070 		if (x - t->tw >= 0)
1071 			cstover = t->cbt * (ntabs + 1) + x % t->tw;
1072 		else
1073 			cstover = 10000;
1074 		if (dist < 10 && cstunder < t->cLE && (t->bs ? cstunder < (ox - x) * t->cbs : 1)
1075 		    && cstover > cstunder)
1076 			return cost + cstunder;
1077 		if (cstunder < t->cLE + 1 && (t->bs ? cstunder < (ox - x) * t->cbs : 1)
1078 		    && cstover > cstunder)
1079 			return cost + cstunder;
1080 		else if (dist < 10 && cstover < t->cRI && (t->bs ? cstover < (ox - x) * t->cbs : 1))
1081 			return cost + cstover;
1082 		else if (cstover < t->cRI + 1 && (t->bs ? cstover < (ox - x) * t->cbs : 1))
1083 			return cost + cstover;
1084 	}
1085 
1086 /* Use simple motions */
1087 	if (x < ox) {
1088 		ptrdiff_t dist = ox - x;
1089 
1090 		/* Have to go left */
1091 		if (t->bs) {
1092 			ptrdiff_t mult = dist * t->cbs;
1093 
1094 			if (t->cLE < mult && dist < 10)
1095 				cost += t->cLE;
1096 			else if (t->cLE + 1 < mult)
1097 				cost += t->cLE + 1;
1098 			else
1099 				cost += mult;
1100 		} else if (t->LE)
1101 			cost += t->cLE;
1102 		else
1103 			return 10000;
1104 	} else if (x > ox) {
1105 		ptrdiff_t dist = x - ox;
1106 
1107 		/* Have to go right */
1108 		/* Hmm.. this should take into account possible attribute changes */
1109 		if (t->cRI < dist && dist < 10)
1110 			cost += t->cRI;
1111 		else if (t->cRI + 1 < dist)
1112 			cost += t->cRI + 1;
1113 		else
1114 			cost += dist;
1115 	}
1116 
1117 	return cost;
1118 }
1119 
1120 /* Find optimal set of cursor positioning commands to move from the current
1121  * cursor row and column (either or both of which might be unknown) to the
1122  * given new row and column and execute them.
1123  */
1124 
cposs(register SCRN * t,register ptrdiff_t x,register ptrdiff_t y)1125 static void cposs(register SCRN *t, register ptrdiff_t x, register ptrdiff_t y)
1126 {
1127 	register ptrdiff_t bestcost, cost;
1128 	int bestway;
1129 	ptrdiff_t hy;
1130 	ptrdiff_t hl;
1131 
1132 	fixupcursor(t);
1133 
1134 /* Home y position is usually 0, but it is 'top' if we have scrolling region
1135  * relative addressing
1136  */
1137 	if (t->rr) {
1138 		hy = t->top;
1139 		hl = t->bot - 1;
1140 	} else {
1141 		hy = 0;
1142 		hl = t->li - 1;
1143 	}
1144 
1145 /* Assume best way is with only using relative cursor positioning */
1146 
1147 	bestcost = relcost(t, x, y, t->x, t->y);
1148 	bestway = 0;
1149 
1150 /* Now check if combinations of absolute cursor positioning functions are
1151  * better (or necessary in case one or both cursor positions are unknown)
1152  */
1153 
1154 	if (t->ccm < bestcost) {
1155 		cost = tcost(t->cap, t->cm, 1, y, x, 0, 0);
1156 		if (cost < bestcost) {
1157 			bestcost = cost;
1158 			bestway = 6;
1159 		}
1160 	}
1161 	if (t->ccr < bestcost) {
1162 		cost = relcost(t, x, y, 0, t->y) + t->ccr;
1163 		if (cost < bestcost) {
1164 			bestcost = cost;
1165 			bestway = 1;
1166 		}
1167 	}
1168 	if (t->cho < bestcost) {
1169 		cost = relcost(t, x, y, 0, hy) + t->cho;
1170 		if (cost < bestcost) {
1171 			bestcost = cost;
1172 			bestway = 2;
1173 		}
1174 	}
1175 	if (t->cll < bestcost) {
1176 		cost = relcost(t, x, y, 0, hl) + t->cll;
1177 		if (cost < bestcost) {
1178 			bestcost = cost;
1179 			bestway = 3;
1180 		}
1181 	}
1182 	if (t->cch < bestcost && x != t->x) {
1183 		cost = relcost(t, x, y, x, t->y) + tcost(t->cap, t->ch, 1, x, 0, 0, 0);
1184 		if (cost < bestcost) {
1185 			bestcost = cost;
1186 			bestway = 4;
1187 		}
1188 	}
1189 	if (t->ccv < bestcost && y != t->y) {
1190 		cost = relcost(t, x, y, t->x, y) + tcost(t->cap, t->cv, 1, y, 0, 0, 0);
1191 		if (cost < bestcost) {
1192 			bestcost = cost;
1193 			bestway = 5;
1194 		}
1195 	}
1196 	if (t->ccV < bestcost) {
1197 		cost = relcost(t, x, y, 0, y) + tcost(t->cap, t->cV, 1, y, 0, 0, 0);
1198 		if (cost < bestcost) {
1199 			bestcost = cost;
1200 			bestway = 13;
1201 		}
1202 	}
1203 	if (t->cch + t->ccv < bestcost && x != t->x && y != t->y) {
1204 		cost = tcost(t->cap, t->cv, 1, y - hy, 0, 0, 0) + tcost(t->cap, t->ch, 1, x, 0, 0, 0);
1205 		if (cost < bestcost) {
1206 			bestcost = cost;
1207 			bestway = 7;
1208 		}
1209 	}
1210 	if (t->ccv + t->ccr < bestcost && y != t->y) {
1211 		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);
1212 		if (cost < bestcost) {
1213 			bestcost = cost;
1214 			bestway = 8;
1215 		}
1216 	}
1217 	if (t->cll + t->cch < bestcost) {
1218 		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);
1219 		if (cost < bestcost) {
1220 			bestcost = cost;
1221 			bestway = 9;
1222 		}
1223 	}
1224 	if (t->cll + t->ccv < bestcost) {
1225 		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);
1226 		if (cost < bestcost) {
1227 			bestcost = cost;
1228 			bestway = 10;
1229 		}
1230 	}
1231 	if (t->cho + t->cch < bestcost) {
1232 		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);
1233 		if (cost < bestcost) {
1234 			bestcost = cost;
1235 			bestway = 11;
1236 		}
1237 	}
1238 	if (t->cho + t->ccv < bestcost) {
1239 		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);
1240 		if (cost < bestcost) {
1241 			bestcost = cost;
1242 			bestway = 12;
1243 		}
1244 	}
1245 
1246 /* Do absolute cursor positioning if we don't know the cursor position or
1247  * if it is faster than doing only relative cursor positioning
1248  */
1249 
1250 	switch (bestway) {
1251 	case 1:
1252 		texec(t->cap, t->cr, 1, 0, 0, 0, 0);
1253 		t->x = 0;
1254 		break;
1255 	case 2:
1256 		texec(t->cap, t->ho, 1, 0, 0, 0, 0);
1257 		t->x = 0;
1258 		t->y = hy;
1259 		break;
1260 	case 3:
1261 		texec(t->cap, t->ll, 1, 0, 0, 0, 0);
1262 		t->x = 0;
1263 		t->y = hl;
1264 		break;
1265 	case 9:
1266 		texec(t->cap, t->ll, 1, 0, 0, 0, 0);
1267 		t->x = 0;
1268 		t->y = hl;
1269 		goto doch;
1270 	case 11:
1271 		texec(t->cap, t->ho, 1, 0, 0, 0, 0);
1272 		t->x = 0;
1273 		t->y = hy;
1274 doch:
1275 	case 4:
1276 		texec(t->cap, t->ch, 1, x, 0, 0, 0);
1277 		t->x = x;
1278 		break;
1279 	case 10:
1280 		texec(t->cap, t->ll, 1, 0, 0, 0, 0);
1281 		t->x = 0;
1282 		t->y = hl;
1283 		goto docv;
1284 	case 12:
1285 		texec(t->cap, t->ho, 1, 0, 0, 0, 0);
1286 		t->x = 0;
1287 		t->y = hy;
1288 		goto docv;
1289 	case 8:
1290 		texec(t->cap, t->cr, 1, 0, 0, 0, 0);
1291 		t->x = 0;
1292 docv:
1293 	case 5:
1294 		texec(t->cap, t->cv, 1, y, 0, 0, 0);
1295 		t->y = y;
1296 		break;
1297 	case 6:
1298 		texec(t->cap, t->cm, 1, y, x, 0, 0);
1299 		t->y = y;
1300 		t->x = x;
1301 		break;
1302 	case 7:
1303 		texec(t->cap, t->cv, 1, y, 0, 0, 0);
1304 		t->y = y;
1305 		texec(t->cap, t->ch, 1, x, 0, 0, 0);
1306 		t->x = x;
1307 		break;
1308 	case 13:
1309 		texec(t->cap, t->cV, 1, y, 0, 0, 0);
1310 		t->y = y;
1311 		t->x = 0;
1312 		break;
1313 	}
1314 
1315 /* Use relative cursor position functions if we're not there yet */
1316 
1317 /* First adjust row */
1318 	if (y > t->y) {
1319 		/* Have to go down */
1320 		if (!t->lf || t->cDO < (y - t->y) * t->clf) {
1321 			texec(t->cap, t->DO, 1, y - t->y, 0, 0, 0);
1322 			t->y = y;
1323 		} else
1324 			while (y > t->y) {
1325 				texec(t->cap, t->lf, 1, 0, 0, 0, 0);
1326 				++t->y;
1327 			}
1328 	} else if (y < t->y) {
1329 		/* Have to go up */
1330 		if (!t->up || t->cUP < (t->y - y) * t->cup) {
1331 			texec(t->cap, t->UP, 1, t->y - y, 0, 0, 0);
1332 			t->y = y;
1333 		} else
1334 			while (y < t->y) {
1335 				texec(t->cap, t->up, 1, 0, 0, 0, 0);
1336 				--t->y;
1337 			}
1338 	}
1339 
1340 /* Use tabs */
1341 	if (x > t->x && t->ta) {
1342 		ptrdiff_t ntabs = (x - t->x + t->x % t->tw) / t->tw;
1343 		ptrdiff_t cstunder = x % t->tw + t->cta * ntabs;
1344 		ptrdiff_t cstover;
1345 
1346 		if (x + t->tw < t->co && t->bs)
1347 			cstover = t->cbs * (t->tw - x % t->tw) + t->cta * (ntabs + 1);
1348 		else
1349 			cstover = 10000;
1350 		if (cstunder < t->cRI && cstunder < x - t->x && cstover > cstunder) {
1351 			if (ntabs) {
1352 				t->x = x - x % t->tw;
1353 				do {
1354 					texec(t->cap, t->ta, 1, 0, 0, 0, 0);
1355 				} while (--ntabs);
1356 			}
1357 		} else if (cstover < t->cRI && cstover < x - t->x) {
1358 			t->x = t->tw + x - x % t->tw;
1359 			++ntabs;
1360 			do {
1361 				texec(t->cap, t->ta, 1, 0, 0, 0, 0);
1362 			} while (--ntabs);
1363 		}
1364 	} else if (x < t->x && t->bt) {
1365 		ptrdiff_t 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;
1366 		ptrdiff_t cstunder, cstover;
1367 
1368 		if (t->bs)
1369 			cstunder = t->cbt * ntabs + t->cbs * (t->tw - x % t->tw);
1370 		else
1371 			cstunder = 10000;
1372 		if (x - t->tw >= 0)
1373 			cstover = t->cbt * (ntabs + 1) + x % t->tw;
1374 		else
1375 			cstover = 10000;
1376 		if (cstunder < t->cLE && (t->bs ? cstunder < (t->x - x) * t->cbs : 1)
1377 		    && cstover > cstunder) {
1378 			if (ntabs) {
1379 				do {
1380 					texec(t->cap, t->bt, 1, 0, 0, 0, 0);
1381 				} while (--ntabs);
1382 				t->x = x + t->tw - x % t->tw;
1383 			}
1384 		} else if (cstover < t->cRI && (t->bs ? cstover < (t->x - x) * t->cbs : 1)) {
1385 			t->x = x - x % t->tw;
1386 			++ntabs;
1387 			do {
1388 				texec(t->cap, t->bt, 1, 0, 0, 0, 0);
1389 			} while (--ntabs);
1390 		}
1391 	}
1392 
1393 /* Now adjust column */
1394 	if (x < t->x) {
1395 		/* Have to go left */
1396 		if (!t->bs || t->cLE < (t->x - x) * t->cbs) {
1397 			texec(t->cap, t->LE, 1, t->x - x, 0, 0, 0);
1398 			t->x = x;
1399 		} else
1400 			while (x < t->x) {
1401 				texec(t->cap, t->bs, 1, 0, 0, 0, 0);
1402 				--t->x;
1403 			}
1404 	} else if (x > t->x) {
1405 		/* Have to go right */
1406 		/* Hmm.. this should take into account possible attribute changes */
1407 		if (x-t->x>1 && t->RI) {
1408 			texec(t->cap, t->RI, 1, x - t->x, 0, 0, 0);
1409 			t->x = x;
1410 		} else {
1411 			while(x>t->x) {
1412 				texec(t->cap, t->nd, 1, 0, 0, 0, 0);
1413 				++t->x;
1414 			}
1415 		}
1416 
1417 		/* if (t->cRI < x - t->x) { */
1418 /*		} else {
1419 			int *s = t->scrn + t->x + t->y * t->co;
1420 			int *a = t->attr + t->x + t->y * t->co;
1421 
1422 			if (t->ins)
1423 				clrins(t);
1424 			while (x > t->x) {
1425 				int atr, c;
1426 				if(*s==-1) c=' ', atr=0;
1427 				else c= *s, atr= *a;
1428 
1429 				if (atr != t->attrib)
1430 					set_attr(t, atr);
1431 				utf8_putc(c);
1432 				++s;
1433 				++a;
1434 				++t->x;
1435 			}
1436 		}
1437 */
1438 	}
1439 }
1440 
cpos(register SCRN * t,register ptrdiff_t x,register ptrdiff_t y)1441 int cpos(register SCRN *t, register ptrdiff_t x, register ptrdiff_t y)
1442 {
1443 	/* Move cursor quickly if we can */
1444 	if (y == t->y) {
1445 		if (x > t->x && x - t->x < 4 && !t->ins) {
1446 			int (*cs)[COMPOSE] = t->scrn + t->x + t->co * t->y;
1447 			int *as = t->attr + t->x + t->co * t->y;
1448 			do {
1449 				/* We used to space over unknown chars, but they now could be
1450 				   the right half of a UTF-8 two column character, so we can't.
1451 				   Also do not try to emit utf-8 sequences here. */
1452 				if((*cs)[0]<32 || (*cs)[0]>=127)
1453 					break;
1454 				/* Also ignore if we would have to emit combining characters */
1455 				if ((*cs)[1])
1456 					break;
1457 
1458 				if (*as != t->attrib)
1459 					set_attr(t, *as);
1460 
1461 				ttputc(TO_CHAR_OK((*cs)[0]));
1462 
1463 				++cs;
1464 				++as;
1465 				++t->x;
1466 
1467 			} while (x != t->x);
1468 		}
1469 		if (x == t->x)
1470 			return 0;
1471 	}
1472 
1473 	/* We used to reset background if t->ut was set before a cursor motion.  This
1474 	   should not be necessary and makes screen update slower, especially with syntax
1475 	   highlighting and/or color scheme. */
1476 	if ((!t->ms && t->attrib & (INVERSE | UNDERLINE | BG_NOT_DEFAULT)) /* ||
1477 	    (t->ut && (t->attrib & BG_NOT_DEFAULT)) */)
1478 		set_attr(t, t->attrib & ~(INVERSE | UNDERLINE | BG_MASK));
1479 
1480 	/* Should be in cposs */
1481 	if (y < t->top || y >= t->bot)
1482 		setregn(t, 0, t->li);
1483 
1484 	cposs(t, x, y);
1485 	return 0;
1486 }
1487 
1488 /* Insert/Delete within line */
1489 /* FIXME: doesn't know about attr */
1490 #if 0
1491 
1492 /* Enter insert mode */
1493 
1494 static void setins(SCRN *t, ptrdiff_t x)
1495 {
1496 	if (t->ins != 1 && t->im) {
1497 		t->ins = 1;
1498 		texec(t->cap, t->im, 1, x, 0, 0, 0);
1499 	}
1500 }
1501 
1502 /* As above but usable in insert mode */
1503 /* The cursor position must already be correct */
1504 
1505 static void outatri(SCRN *t, ptrdiff_t x, ptrdiff_t y, int c, int a)
1506 {
1507 /*
1508 	if (c == -1)
1509 		c = ' ';
1510 	if (a != t->attrib)
1511 		set_attr(t, a);
1512 	if (t->haz && c == '~')
1513 		c = '\\';
1514 	utf8_putc(c);
1515 	t->x+=joe_wcwidth(1,c);
1516 */
1517 	/* ++t->x; */
1518 }
1519 
1520 
1521 static void doinschr(SCRN *t, ptrdiff_t x, ptrdiff_t y, int (*s)[COMPOSE], int *as, ptrdiff_t n)
1522 {
1523 	ptrdiff_t a;
1524 
1525 	if (x < 0) {
1526 		s -= x;
1527 		as -= x;
1528 		x = 0;
1529 	}
1530 	if (x >= t->co || n <= 0)
1531 		return;
1532 	if (t->im || t->ic || t->IC) {
1533 		cpos(t, x, y);
1534 		if ((n == 1 && t->ic) || !t->IC) {
1535 			if (!t->ic)
1536 				setins(t, x);
1537 			for (a = 0; a != n; ++a) {
1538 				texec(t->cap, t->ic, 1, x, 0, 0, 0);
1539 				outatri(t, x + a, y, s[a][0], as[a]);
1540 				texec(t->cap, t->ip, 1, x, 0, 0, 0);
1541 			}
1542 			if (!t->mi)
1543 				clrins(t);
1544 		} else {
1545 			texec(t->cap, t->IC, 1, n, 0, 0, 0);
1546 			for (a = 0; a != n; ++a)
1547 				outatri(t, x + a, y, s[a][0], as[a]);
1548 		}
1549 	}
1550 	mmove(t->scrn + x + t->co * y + n, t->scrn + x + t->co * y, (t->co - (x + n)) * SIZEOF(int [COMPOSE]));
1551 	mmove(t->attr + x + t->co * y + n, t->attr + x + t->co * y, (t->co - (x + n)) * SIZEOF(int));
1552 	mmove(t->scrn + x + t->co * y, s, n * SIZEOF(int [COMPOSE]));
1553 	mmove(t->attr + x + t->co * y, s, n * SIZEOF(int));
1554 }
1555 
1556 static void dodelchr(SCRN *t, ptrdiff_t x, ptrdiff_t y, ptrdiff_t n)
1557 {
1558 	ptrdiff_t a;
1559 
1560 	if (x < 0)
1561 		x = 0;
1562 	if (!n || x >= t->co)
1563 		return;
1564 	if (t->dc || t->DC) {
1565 		cpos(t, x, y);
1566 		texec(t->cap, t->dm, 1, x, 0, 0, 0);	/* Enter delete mode */
1567 		if ((n == 1 && t->dc) || !t->DC)
1568 			for (a = n; a; --a)
1569 				texec(t->cap, t->dc, 1, x, 0, 0, 0);
1570 		else
1571 			texec(t->cap, t->DC, 1, n, 0, 0, 0);
1572 		texec(t->cap, t->ed, 1, x, 0, 0, 0);	/* Exit delete mode */
1573 	}
1574 	mmove(t->scrn + t->co * y + x, t->scrn + t->co * y + x + n, (t->co - (x + n)) * SIZEOF(int [COMPOSE]));
1575 	mmove(t->attr + t->co * y + x, t->attr + t->co * y + x + n, (t->co - (x + n)) * SIZEOF(int));
1576 	mfill(t->scrn + t->co * y + t->co - n, ' ', n);
1577 	msetI(t->attr + t->co * y + t->co - n, (t->attrib & FG_MASK), n);
1578 }
1579 
1580 void magic(SCRN *t, ptrdiff_t y, int (*cs)[COMPOSE], int *ca,int *s, int *a, ptrdiff_t placex)
1581 {
1582 	struct hentry *htab = t->htab;
1583 	ptrdiff_t *ofst = t->ofst;
1584 	int aryx = 1;
1585 	ptrdiff_t x;
1586 
1587 	if (!(t->im || t->ic || t->IC) || !(t->dc || t->DC))
1588 		return;
1589 	mset((char *)htab, 0, 256 * SIZEOF(struct hentry));
1590 
1591 	msetD(ofst, 0, t->co);
1592 
1593 /* Build hash table */
1594 	for (x = 0; x != t->co; ++x) {
1595 		t->ary[aryx].next = htab[cs[x] & 255].next;
1596 		t->ary[aryx].loc = x;
1597 		++htab[cs[x] & 255].loc;
1598 		htab[cs[x] & 255].next = aryx++;
1599 	}
1600 
1601 /* Build offset table */
1602 	for (x = 0; x < t->co;)
1603 		if (htab[s[x] & 255].loc >= 15)
1604 			ofst[x++] = t->co;
1605 		else {
1606 			ptrdiff_t aryy;
1607 			ptrdiff_t maxaryy = 0;
1608 			ptrdiff_t maxlen = 0;
1609 			ptrdiff_t best = 0;
1610 			ptrdiff_t bestback = 0;
1611 			ptrdiff_t z;
1612 
1613 			for (aryy = htab[s[x] & 255].next; aryy; aryy = t->ary[aryy].next) {
1614 				ptrdiff_t amnt, back;
1615 				ptrdiff_t tsfo = t->ary[aryy].loc - x;
1616 				ptrdiff_t cst = tsfo < 0 ? -tsfo : tsfo;
1617 				ptrdiff_t pre = 32;
1618 
1619 				for (amnt = 0; x + amnt < t->co && x + tsfo + amnt < t->co; ++amnt) {
1620 					if (cs[x + tsfo + amnt] != s[x + amnt])
1621 						break;
1622 					else if ((s[x + amnt] & 255) != 32 || pre != 32)
1623 						++cst;
1624 					pre = s[x + amnt] & 255;
1625 				}
1626 				pre = 32;
1627 				for (back = 0; back + x > 0 && back + tsfo + x > 0; --back) {
1628 					if (cs[x + tsfo + back - 1] != s[x + back - 1])
1629 						break;
1630 					else if ((s[x + back - 1] & 255) != 32 || pre != 32)
1631 						++cst;
1632 					pre = s[x + back - 1] & 255;
1633 				}
1634 				if (cst > best) {
1635 					maxaryy = aryy;
1636 					maxlen = amnt;
1637 					best = cst;
1638 					bestback = back;
1639 				}
1640 			}
1641 			if (!maxlen) {
1642 				ofst[x] = t->co;
1643 				maxlen = 1;
1644 			} else if (best < 2)
1645 				for (z = 0; z != maxlen; ++z)
1646 					ofst[x + z] = t->co;
1647 			else
1648 				for (z = 0; z != maxlen - bestback; ++z)
1649 					ofst[x + z + bestback] = t->ary[maxaryy].loc - x;
1650 			x += maxlen;
1651 		}
1652 
1653 /* Apply scrolling commands */
1654 
1655 	for (x = 0; x != t->co; ++x) {
1656 		ptrdiff_t q = ofst[x];
1657 
1658 		if (q && q != t->co) {
1659 			if (q > 0) {
1660 				ptrdiff_t z, fu;
1661 
1662 				for (z = x; z != t->co && ofst[z] == q; ++z) ;
1663 				while (s[x] == cs[x] && x < placex)
1664 					++x;
1665 				dodelchr(t, x, y, q);
1666 				for (fu = x; fu != t->co; ++fu)
1667 					if (ofst[fu] != t->co)
1668 						ofst[fu] -= q;
1669 				x = z - 1;
1670 			} else {
1671 				ptrdiff_t z, fu;
1672 
1673 				for (z = x; z != t->co && ofst[z] == q; ++z) ;
1674 				while (s[x + q] == cs[x + q] && x - q < placex)
1675 					++x;
1676 				doinschr(t, x + q, y, s + x + q, a + x + q, -q);
1677 				for (fu = x; fu != t->co; ++fu)
1678 					if (ofst[fu] != t->co)
1679 						ofst[fu] -= q;
1680 				x = z - 1;
1681 			}
1682 		}
1683 	}
1684 }
1685 #endif
1686 
doupscrl(SCRN * t,ptrdiff_t top,ptrdiff_t bot,ptrdiff_t amnt,int atr)1687 static void doupscrl(SCRN *t, ptrdiff_t top, ptrdiff_t bot, ptrdiff_t amnt, int atr)
1688 {
1689 	ptrdiff_t a = amnt;
1690 
1691 	if (!amnt)
1692 		return;
1693 	set_attr(t, atr);
1694 	if (top == 0 && bot == t->li && (t->sf || t->SF)) {
1695 		setregn(t, 0, t->li);
1696 		cpos(t, 0, t->li - 1);
1697 		if ((amnt == 1 && t->sf) || !t->SF)
1698 			while (a--)
1699 				texec(t->cap, t->sf, 1, t->li - 1, 0, 0, 0);
1700 		else
1701 			texec(t->cap, t->SF, a, a, 0, 0, 0);
1702 		goto done;
1703 	}
1704 	if (bot == t->li && (t->dl || t->DL)) {
1705 		setregn(t, 0, t->li);
1706 		cpos(t, 0, top);
1707 		if ((amnt == 1 && t->dl) || !t->DL)
1708 			while (a--)
1709 				texec(t->cap, t->dl, 1, top, 0, 0, 0);
1710 		else
1711 			texec(t->cap, t->DL, a, a, 0, 0, 0);
1712 		goto done;
1713 	}
1714 	if (t->cs && (t->sf || t->SF)) {
1715 		setregn(t, top, bot);
1716 		cpos(t, 0, bot - 1);
1717 		if ((amnt == 1 && t->sf) || !t->SF)
1718 			while (a--)
1719 				texec(t->cap, t->sf, 1, bot - 1, 0, 0, 0);
1720 		else
1721 			texec(t->cap, t->SF, a, a, 0, 0, 0);
1722 		goto done;
1723 	}
1724 	if ((t->dl || t->DL) && (t->al || t->AL)) {
1725 		cpos(t, 0, top);
1726 		if ((amnt == 1 && t->dl) || !t->DL)
1727 			while (a--)
1728 				texec(t->cap, t->dl, 1, top, 0, 0, 0);
1729 		else
1730 			texec(t->cap, t->DL, a, a, 0, 0, 0);
1731 		a = amnt;
1732 		cpos(t, 0, bot - amnt);
1733 		if ((amnt == 1 && t->al) || !t->AL)
1734 			while (a--)
1735 				texec(t->cap, t->al, 1, bot - amnt, 0, 0, 0);
1736 		else
1737 			texec(t->cap, t->AL, a, a, 0, 0, 0);
1738 		goto done;
1739 	}
1740 	msetI(t->updtab + top, 1, bot - top);
1741 	return;
1742 
1743       done:
1744 	mmove(t->scrn + top * t->co, t->scrn + (top + amnt) * t->co, (bot - top - amnt) * t->co * SIZEOF(int [COMPOSE]));
1745 	mmove(t->attr + top * t->co, t->attr + (top + amnt) * t->co, (bot - top - amnt) * t->co * SIZEOF(int));
1746 
1747 	if (bot == t->li && t->db) {
1748 		mfill(t->scrn + (t->li - amnt) * t->co, -1, amnt * t->co);
1749 		msetI(t->attr + (t->li - amnt) * t->co, 0, amnt * t->co);
1750 		msetI(t->updtab + t->li - amnt, 1, amnt);
1751 	} else {
1752 		mfill(t->scrn + (bot - amnt) * t->co, ' ', amnt * t->co);
1753 		msetI(t->attr + (bot - amnt) * t->co, 0, amnt * t->co);
1754 	}
1755 }
1756 
dodnscrl(SCRN * t,ptrdiff_t top,ptrdiff_t bot,ptrdiff_t amnt,int atr)1757 static void dodnscrl(SCRN *t, ptrdiff_t top, ptrdiff_t bot, ptrdiff_t amnt, int atr)
1758 {
1759 	ptrdiff_t a = amnt;
1760 
1761 	if (!amnt)
1762 		return;
1763 	set_attr(t, atr);
1764 	if (top == 0 && bot == t->li && (t->sr || t->SR)) {
1765 		setregn(t, 0, t->li);
1766 		cpos(t, 0, 0);
1767 		if ((amnt == 1 && t->sr) || !t->SR)
1768 			while (a--)
1769 				texec(t->cap, t->sr, 1, 0, 0, 0, 0);
1770 		else
1771 			texec(t->cap, t->SR, a, a, 0, 0, 0);
1772 		goto done;
1773 	}
1774 	if (bot == t->li && (t->al || t->AL)) {
1775 		setregn(t, 0, t->li);
1776 		cpos(t, 0, top);
1777 		if ((amnt == 1 && t->al) || !t->AL)
1778 			while (a--)
1779 				texec(t->cap, t->al, 1, top, 0, 0, 0);
1780 		else
1781 			texec(t->cap, t->AL, a, a, 0, 0, 0);
1782 		goto done;
1783 	}
1784 	if (t->cs && (t->sr || t->SR)) {
1785 		setregn(t, top, bot);
1786 		cpos(t, 0, top);
1787 		if ((amnt == 1 && t->sr) || !t->SR)
1788 			while (a--)
1789 				texec(t->cap, t->sr, 1, top, 0, 0, 0);
1790 		else
1791 			texec(t->cap, t->SR, a, a, 0, 0, 0);
1792 		goto done;
1793 	}
1794 	if ((t->dl || t->DL) && (t->al || t->AL)) {
1795 		cpos(t, 0, bot - amnt);
1796 		if ((amnt == 1 && t->dl) || !t->DL)
1797 			while (a--)
1798 				texec(t->cap, t->dl, 1, bot - amnt, 0, 0, 0);
1799 		else
1800 			texec(t->cap, t->DL, a, a, 0, 0, 0);
1801 		a = amnt;
1802 		cpos(t, 0, top);
1803 		if ((amnt == 1 && t->al) || !t->AL)
1804 			while (a--)
1805 				texec(t->cap, t->al, 1, top, 0, 0, 0);
1806 		else
1807 			texec(t->cap, t->AL, a, a, 0, 0, 0);
1808 		goto done;
1809 	}
1810 	msetI(t->updtab + top, 1, bot - top);
1811 	return;
1812       done:
1813 	mmove(t->scrn + (top + amnt) * t->co, t->scrn + top * t->co, (bot - top - amnt) * t->co * SIZEOF(int [COMPOSE]));
1814 	mmove(t->attr + (top + amnt) * t->co, t->attr + top * t->co, (bot - top - amnt) * t->co * SIZEOF(int));
1815 
1816 	if (!top && t->da) {
1817 		mfill(t->scrn, -1, amnt * t->co);
1818 		msetI(t->attr, 0, amnt * t->co);
1819 		msetI(t->updtab, 1, amnt);
1820 	} else {
1821 		mfill(t->scrn + t->co * top, ' ', amnt * t->co);
1822 		msetI(t->attr + t->co * top, 0, amnt * t->co);
1823 	}
1824 }
1825 
nscroll(SCRN * t,int atr)1826 void nscroll(SCRN *t,int atr)
1827 {
1828 	ptrdiff_t y, z, q, r, p;
1829 
1830 	for (y = 0; y != t->li; ++y) {
1831 		q = t->sary[y];
1832 		if (ifhave)
1833 			return;
1834 		if (q && q != t->li) {
1835 			if (q > 0) {
1836 				for (z = y; z != t->li && t->sary[z] == q; ++z)
1837 					t->sary[z] = 0;
1838 				doupscrl(t, y, z + q, q, atr);
1839 				y = z - 1;
1840 			} else {
1841 				for (r = y; r != t->li && (t->sary[r] < 0 || t->sary[r] == t->li); ++r) ;
1842 				p = r - 1;
1843 				do {
1844 					q = t->sary[p];
1845 					if (q && q != t->li) {
1846 						for (z = p; t->sary[z] = 0, (z && t->sary[z - 1] == q); --z) ;
1847 						dodnscrl(t, z + q, p + 1, -q, atr);
1848 						p = z + 1;
1849 					}
1850 				} while (p-- != y);
1851 				y = r - 1;
1852 			}
1853 		}
1854 	}
1855 	msetD(t->sary, 0, t->li);
1856 }
1857 
npartial(SCRN * t)1858 void npartial(SCRN *t)
1859 {
1860 	set_attr(t, BG_COLOR(bg_text));
1861 	clrins(t);
1862 	setregn(t, 0, t->li);
1863 }
1864 
nescape(SCRN * t)1865 void nescape(SCRN *t)
1866 {
1867 	mouseclose();
1868 	npartial(t);
1869 	cpos(t, 0, t->li - 1);
1870 	eraeol(t, 0, t->li - 1, 0);
1871 	if (t->bre)
1872 		texec(t->cap, t->bre, 1, 0, 0, 0, 0);
1873 	if (t->te)
1874 		texec(t->cap, t->te, 1, 0, 0, 0, 0);
1875 }
1876 
nreturn(SCRN * t)1877 void nreturn(SCRN *t)
1878 {
1879 	mouseopen();
1880 	if (t->ti)
1881 		texec(t->cap, t->ti, 1, 0, 0, 0, 0);
1882 	if (!skiptop && t->cl)
1883 		texec(t->cap, t->cl, 1, 0, 0, 0, 0);
1884 	if (t->brp)
1885 		texec(t->cap, t->brp, 1, 0, 0, 0, 0);
1886 	nredraw(t);
1887 }
1888 
nclose(SCRN * t)1889 void nclose(SCRN *t)
1890 {
1891 	mouseclose();
1892 	leave = 1;
1893 	set_attr(t, 0);
1894 	clrins(t);
1895 	setregn(t, 0, t->li);
1896 	cpos(t, 0, t->li - 1);
1897 	if (t->bre)
1898 		texec(t->cap, t->bre, 1, 0, 0, 0, 0);
1899 	if (t->te)
1900 		texec(t->cap, t->te, 1, 0, 0, 0, 0);
1901 	ttclose();
1902 	rmcap(t->cap);
1903 	joe_free(t->scrn);
1904 	joe_free(t->attr);
1905 	joe_free(t->sary);
1906 	joe_free(t->ofst);
1907 	joe_free(t->htab);
1908 	joe_free(t->ary);
1909 	joe_free(t);
1910 }
1911 
nscrldn(SCRN * t,ptrdiff_t top,ptrdiff_t bot,ptrdiff_t amnt)1912 void nscrldn(SCRN *t, ptrdiff_t top, ptrdiff_t bot, ptrdiff_t amnt)
1913 {
1914 	ptrdiff_t x;
1915 
1916 	if (!amnt || top >= bot || bot > t->li)
1917 		return;
1918 	if ((amnt < bot - top && bot - top - amnt < amnt / 2) || !t->scroll)
1919 		amnt = bot - top;
1920 	if (amnt < bot - top) {
1921 		for (x = bot; x != top + amnt; --x) {
1922 			t->sary[x - 1] = (t->sary[x - amnt - 1] == t->li ? t->li : t->sary[x - amnt - 1] - amnt);
1923 			t->updtab[x - 1] = t->updtab[x - amnt - 1];
1924 		}
1925 		for (x = top; x != top + amnt; ++x) {
1926 			t->updtab[x] = 1;
1927 		}
1928 	}
1929 	if (amnt > bot - top)
1930 		amnt = bot - top;
1931 	msetD(t->sary + top, t->li, amnt);
1932 	if (amnt == bot - top) {
1933 		msetI(t->updtab + top, 1, amnt);
1934 	}
1935 }
1936 
nscrlup(SCRN * t,ptrdiff_t top,ptrdiff_t bot,ptrdiff_t amnt)1937 void nscrlup(SCRN *t, ptrdiff_t top, ptrdiff_t bot, ptrdiff_t amnt)
1938 {
1939 	ptrdiff_t x;
1940 
1941 	if (!amnt || top >= bot || bot > t->li)
1942 		return;
1943 	if ((amnt < bot - top && bot - top - amnt < amnt / 2) || !t->scroll)
1944 		amnt = bot - top;
1945 	if (amnt < bot - top) {
1946 		for (x = top + amnt; x != bot; ++x) {
1947 			t->sary[x - amnt] = (t->sary[x] == t->li ? t->li : t->sary[x] + amnt);
1948 			t->updtab[x - amnt] = t->updtab[x];
1949 		}
1950 		for (x = bot - amnt; x != bot; ++x) {
1951 			t->updtab[x] = 1;
1952 		}
1953 	}
1954 	if (amnt > bot - top)
1955 		amnt = bot - top;
1956 	msetD(t->sary + bot - amnt, t->li, amnt);
1957 	if (amnt == bot - top) {
1958 		msetI(t->updtab + bot - amnt, 1, amnt);
1959 		}
1960 }
1961 
nredraw(SCRN * t)1962 void nredraw(SCRN *t)
1963 {
1964 	dostaupd = 1;
1965 	mfill(t->scrn, ' ', t->co * skiptop);
1966 	msetI(t->attr, BG_COLOR(bg_text), t->co * skiptop);
1967 	mfill(t->scrn + skiptop * t->co, -1, (t->li - skiptop) * t->co);
1968 	msetI(t->attr + skiptop * t->co, BG_COLOR(bg_text), (t->li - skiptop) * t->co);
1969 	msetD(t->sary, 0, t->li);
1970 	msetI(t->updtab + skiptop, -1, t->li - skiptop);
1971 	t->x = -1;
1972 	t->y = -1;
1973 	t->top = t->li;
1974 	t->bot = 0;
1975 	t->attrib = -1;
1976 	t->ins = -1;
1977 	set_attr(t, BG_COLOR(bg_text));
1978 	clrins(t);
1979 	setregn(t, 0, t->li);
1980 
1981 	if (!skiptop) {
1982 #if 0 // Leave screen contents invalid.  This way line update emits explicit spaces.
1983 		if (t->cl) {
1984 			texec(t->cap, t->cl, 1, 0, 0, 0, 0);
1985 			t->x = 0;
1986 			t->y = 0;
1987 			mfill(t->scrn, ' ', t->li * t->co);
1988 			msetI(t->attr, BG_COLOR(bg_text), t->li * t->co);
1989 		} else if (t->cd) {
1990 			cpos(t, 0, 0);
1991 			texec(t->cap, t->cd, 1, 0, 0, 0, 0);
1992 			mfill(t->scrn, ' ', t->li * t->co);
1993 			msetI(t->attr, BG_COLOR(bg_text), t->li * t->co);
1994 		}
1995 #endif
1996 	}
1997 }
1998 
1999 /* Convert color/attribute name into internal code */
2000 
meta_color_single(const char * s)2001 static int meta_color_single(const char *s)
2002 {
2003 	if(!zcmp(s,"inverse"))
2004 		return INVERSE;
2005 	else if(!zcmp(s,"underline"))
2006 		return UNDERLINE;
2007 	else if(!zcmp(s,"bold"))
2008 		return BOLD;
2009 	else if(!zcmp(s,"blink"))
2010 		return BLINK;
2011 	else if(!zcmp(s,"dim"))
2012 		return DIM;
2013 	else if(!zcmp(s,"italic"))
2014 		return ITALIC;
2015 	else if(!zcmp(s,"dunderline"))
2016 		return DOUBLE_UNDERLINE;
2017 	else if(!zcmp(s,"stricken"))
2018 		return CROSSED_OUT;
2019 
2020 	/* ISO colors */
2021 	else if(!zcmp(s,"white"))
2022 		return FG_WHITE;
2023 	else if(!zcmp(s,"cyan"))
2024 		return FG_CYAN;
2025 	else if(!zcmp(s,"magenta"))
2026 		return FG_MAGENTA;
2027 	else if(!zcmp(s,"blue"))
2028 		return FG_BLUE;
2029 	else if(!zcmp(s,"yellow"))
2030 		return FG_YELLOW;
2031 	else if(!zcmp(s,"green"))
2032 		return FG_GREEN;
2033 	else if(!zcmp(s,"red"))
2034 		return FG_RED;
2035 	else if(!zcmp(s,"black"))
2036 		return FG_BLACK;
2037 	else if(!zcmp(s,"bg_white"))
2038 		return BG_WHITE;
2039 	else if(!zcmp(s,"bg_cyan"))
2040 		return BG_CYAN;
2041 	else if(!zcmp(s,"bg_magenta"))
2042 		return BG_MAGENTA;
2043 	else if(!zcmp(s,"bg_blue"))
2044 		return BG_BLUE;
2045 	else if(!zcmp(s,"bg_yellow"))
2046 		return BG_YELLOW;
2047 	else if(!zcmp(s,"bg_green"))
2048 		return BG_GREEN;
2049 	else if(!zcmp(s,"bg_red"))
2050 		return BG_RED;
2051 	else if(!zcmp(s,"bg_black"))
2052 		return BG_BLACK;
2053 
2054 	/* 16 color xterm support: codes 8 - 15 are brighter versions of above */
2055 	else if(!zcmp(s,"WHITE"))
2056 		return FG_BWHITE;
2057 	else if(!zcmp(s,"CYAN"))
2058 		return FG_BCYAN;
2059 	else if(!zcmp(s,"MAGENTA"))
2060 		return FG_BMAGENTA;
2061 	else if(!zcmp(s,"BLUE"))
2062 		return FG_BBLUE;
2063 	else if(!zcmp(s,"YELLOW"))
2064 		return FG_BYELLOW;
2065 	else if(!zcmp(s,"GREEN"))
2066 		return FG_BGREEN;
2067 	else if(!zcmp(s,"RED"))
2068 		return FG_BRED;
2069 	else if(!zcmp(s,"BLACK"))
2070 		return FG_BBLACK;
2071 	else if(!zcmp(s,"bg_WHITE"))
2072 		return BG_BWHITE;
2073 	else if(!zcmp(s,"bg_CYAN"))
2074 		return BG_BCYAN;
2075 	else if(!zcmp(s,"bg_MAGENTA"))
2076 		return BG_BMAGENTA;
2077 	else if(!zcmp(s,"bg_BLUE"))
2078 		return BG_BBLUE;
2079 	else if(!zcmp(s,"bg_YELLOW"))
2080 		return BG_BYELLOW;
2081 	else if(!zcmp(s,"bg_GREEN"))
2082 		return BG_BGREEN;
2083 	else if(!zcmp(s,"bg_RED"))
2084 		return BG_BRED;
2085 	else if(!zcmp(s,"bg_BLACK"))
2086 		return BG_BBLACK;
2087 
2088 	/* Look at the "256colres.pl" PERL script in the xterm source
2089 	   distribution to see how these work. */
2090 
2091 	/* 256 color xterm support: bg_RGB and fg_RGB, where R, G, and B range from 0 - 5 */
2092 	/* Codes 16 - 231 are a 6x6x6 color cube */
2093 	else if(s[0]=='f' && s[1]=='g' && s[2]=='_' &&
2094 		s[3]>='0' && s[3]<='5' &&
2095 		s[4]>='0' && s[4]<='5' &&
2096 		s[5]>='0' && s[5]<='5' && !s[6])
2097 	        return FG_NOT_DEFAULT | ((16 + (s[3]-'0')*6*6 + (s[4]-'0')*6 + (s[5]-'0')) << FG_SHIFT);
2098 
2099 	else if(s[0]=='b' && s[1]=='g' && s[2]=='_' &&
2100 		  s[3]>='0' && s[3]<='5' &&
2101 		  s[4]>='0' && s[4]<='5' &&
2102 		  s[5]>='0' && s[5]<='5' && !s[6])
2103 	        return BG_NOT_DEFAULT | ((16 + (s[3]-'0')*6*6 + (s[4]-'0')*6 + (s[5]-'0')) << BG_SHIFT);
2104 
2105 	/* 256 color xterm support: shades of grey */
2106 	/* Codes 232 - 255 are shades of grey */
2107 	else if(s[0]=='f' && s[1]=='g' && s[2]=='_' && ztoi(s+3) >= 0 && ztoi(s+3) <= 23)
2108 		return FG_NOT_DEFAULT | (232 + (ztoi(s+3) << FG_SHIFT));
2109 
2110 	else if(s[0]=='b' && s[1]=='g' && s[2]=='_' && ztoi(s+3) >= 0 && ztoi(s+3) <= 23)
2111 		return BG_NOT_DEFAULT | (232 + (ztoi(s+3) << BG_SHIFT));
2112 
2113 	else
2114 		return 0;
2115 }
2116 
meta_color(const char * s)2117 int meta_color(const char *s)
2118 {
2119 	int code = 0;
2120 	while (*s) {
2121 		char buf[32];
2122 		int x = 0;
2123 		while (*s)
2124 			if (*s && *s != '+') {
2125 				if (x != SIZEOF(buf) - 1)
2126 					buf[x++] = *s;
2127 				++s;
2128 			} else
2129 				break;
2130 		if (*s == '+')
2131 			++s;
2132 		buf[x] = 0;
2133 		code |= meta_color_single(buf);
2134 	}
2135 	return code;
2136 }
2137 
2138 /* Generate a field
2139  *
2140  * 't' is SCRN to write to.
2141  * 'scrn' is address of field in character buffer
2142  * 'attr' is address of field in attribute buffer
2143  * 'x', 'y' are starting column and line numbers of field
2144  * 'ofst' is first column within string to display
2145  * 's', 'len' is string to generate in field
2146  * 'atr' is screeen attributes (and color) which should be used
2147  * 'width' is column width of field
2148  * 'flg' if set, erases to end of line
2149  * 'fmt' is array of attributes, one for each byte.  OK if NULL.
2150  */
2151 
genfield(SCRN * t,int (* scrn)[COMPOSE],int * attr,ptrdiff_t x,ptrdiff_t y,ptrdiff_t ofst,const char * s,ptrdiff_t len,int atr,ptrdiff_t width,int flg,int * fmt)2152 void genfield(SCRN *t,int (*scrn)[COMPOSE],int *attr,ptrdiff_t x,ptrdiff_t y,ptrdiff_t ofst,const char *s,ptrdiff_t len,int atr,ptrdiff_t width,int flg,int *fmt)
2153 {
2154 	ptrdiff_t col;
2155 	struct utf8_sm sm;
2156 	ptrdiff_t last_col = x + width;
2157 
2158 	utf8_init(&sm);
2159 
2160 	for (col = 0;len != 0 && x < last_col; len--) {
2161 		int c = *(const unsigned char *)s++;
2162 		ptrdiff_t wid = -1;
2163 		int my_atr = atr;
2164 		if (fmt) {
2165 			int fmtatr = *fmt++;
2166 			if (fmtatr & FG_MASK)
2167 				my_atr = (my_atr & ~FG_MASK) | (fmtatr & FG_MASK);
2168 			if (fmtatr & BG_MASK)
2169 				my_atr = (my_atr & ~BG_MASK) | (fmtatr & BG_MASK);
2170 			my_atr |= fmtatr & AT_MASK;
2171 		}
2172 		if (locale_map->type) {
2173 			/* UTF-8 mode: decode character and determine its width */
2174 			c = utf8_decode(&sm,TO_CHAR_OK(c));
2175 			if (c >= 0)
2176 				wid = joe_wcwidth(1,c);
2177 		} else {
2178 			/* Byte mode: character is one column wide */
2179 			wid = 1 ;
2180 		}
2181 		if (wid>=0) {
2182 			if (col >= ofst) {
2183 				if (x + wid > last_col) {
2184 					/* Character crosses end of field, so fill balance of field with '>' characters instead */
2185 					while (x < last_col) {
2186 						outatr(locale_map, t, scrn, attr, x, y, '>', my_atr);
2187 						++scrn;
2188 						++attr;
2189 						++x;
2190 					}
2191 				} else if(wid) {
2192 					/* Emit character */
2193 					outatr(locale_map, t, scrn, attr, x, y, c, my_atr);
2194 					x += wid;
2195 					scrn += wid;
2196 					attr += wid;
2197 				}
2198 			} else if ((col + wid) > ofst) {
2199 				/* Wide character crosses left side of field */
2200 				wid -= ofst - col;
2201 				col = ofst;
2202 				while (wid) {
2203 					outatr(locale_map, t, scrn, attr, x, y, '<', my_atr);
2204 					++scrn;
2205 					++attr;
2206 					++x;
2207 					++col;
2208 					--wid;
2209 				}
2210 			} else
2211 				col += wid;
2212 		}
2213 	}
2214 	/* Fill balance of field with spaces */
2215 	while (x < last_col) {
2216 		outatr(locale_map, t, scrn, attr, x, y, ' ', atr);
2217 		++x;
2218 		++scrn;
2219 		++attr;
2220 	}
2221 	/* Complete any unfinished characters */
2222 	outatr_complete(t);
2223 	/* Erase to end of line */
2224 	if (flg)
2225 		eraeol(t, x, y, atr);
2226 }
2227 
2228 /* Width function for above */
2229 
txtwidth(const char * s,ptrdiff_t len)2230 ptrdiff_t txtwidth(const char *s,ptrdiff_t len)
2231 {
2232 	if (locale_map->type) {
2233 		ptrdiff_t col=0;
2234 		struct utf8_sm sm;
2235 		utf8_init(&sm);
2236 
2237 		while(len--) {
2238 			int d = utf8_decode(&sm,*s++);
2239 			if (d >= 0)
2240 				col += joe_wcwidth(1,d);
2241 		}
2242 
2243 		return col;
2244 	} else
2245 		return len;
2246 }
2247 
txtwidth1(struct charmap * map,off_t tabwidth,const char * s,ptrdiff_t len)2248 off_t txtwidth1(struct charmap *map,off_t tabwidth,const char *s,ptrdiff_t len)
2249 {
2250 	if (map->type) {
2251 		off_t col=0;
2252 		struct utf8_sm sm;
2253 		utf8_init(&sm);
2254 
2255 		while(len--) {
2256 			int d = utf8_decode(&sm,*s++);
2257 			if (d == '\t') {
2258 				++col;
2259 				col += tabwidth - (col % tabwidth);
2260 			} else if (d >= 0)
2261 				col += joe_wcwidth(1,d);
2262 		}
2263 
2264 		return col;
2265 	} else {
2266 		off_t col = 0;
2267 		while (len--) {
2268 			if (*s++ == '\t') {
2269 				++col;
2270 				col += tabwidth - (col % tabwidth);
2271 			} else
2272 				++col;
2273 		}
2274 		return col;
2275 	}
2276 }
2277 
2278 /* Generate text with formatting escape sequences */
2279 
genfmt(SCRN * t,ptrdiff_t x,ptrdiff_t y,ptrdiff_t ofst,const char * s,int atr,int iatr,int flg)2280 void genfmt(SCRN *t, ptrdiff_t x, ptrdiff_t y, ptrdiff_t ofst, const char *s, int atr, int iatr, int flg)
2281 {
2282 	int (*scrn)[COMPOSE] = t->scrn + y * t->co + x;
2283 	int *attr = t->attr + y * t->co + x;
2284 	ptrdiff_t col = 0;
2285 	int c;
2286 	struct utf8_sm sm;
2287 	int inverted = !!(atr & INVERSE);
2288 	int origcolor = atr & ~(FG_MASK | BG_MASK);
2289 
2290 	if (iatr && inverted) {
2291 		atr = iatr | (atr & ~(FG_MASK | BG_MASK | INVERSE));
2292 	}
2293 
2294 	utf8_init(&sm);
2295 
2296 	while ((c = *(const unsigned char *)s++) != '\0')
2297 		if (c == '\\') {
2298 			switch (c = *s++) {
2299 			case 'u':
2300 			case 'U':
2301 				atr ^= UNDERLINE;
2302 				break;
2303 			case 'i':
2304 			case 'I':
2305 				if (iatr) {
2306 					inverted = !inverted;
2307 					atr = (inverted ? iatr : origcolor) | (atr & ~(FG_MASK | BG_MASK));
2308 				} else {
2309 					atr ^= INVERSE;
2310 				}
2311 				break;
2312 			case 'b':
2313 			case 'B':
2314 				atr ^= BOLD;
2315 				break;
2316 			case 'l':
2317 			case 'L':
2318 				atr ^= ITALIC;
2319 				break;
2320 			case 'd':
2321 			case 'D':
2322 				atr ^= DIM;
2323 				break;
2324 			case 'f':
2325 			case 'F':
2326 				atr ^= BLINK;
2327 				break;
2328 			case 's':
2329 			case 'S':
2330 				atr ^= CROSSED_OUT;
2331 				break;
2332 			case 'z':
2333 			case 'Z':
2334 				atr ^= DOUBLE_UNDERLINE;
2335 				break;
2336 			case 0:
2337 				--s;
2338 				break;
2339 			case '@':
2340 				c = 0;
2341 			default: {
2342 				if (col++ >= ofst) {
2343 					outatr(locale_map, t, scrn, attr, x, y, (c&0x7F), atr);
2344 					++scrn;
2345 					++attr;
2346 					++x;
2347 					}
2348 				break;
2349 				}
2350 			}
2351 		} else {
2352 			ptrdiff_t wid = -1;
2353 			if (locale_map->type) {
2354 				/* UTF-8 mode: decode character and determine its width */
2355 				c = utf8_decode(&sm,TO_CHAR_OK(c));
2356 				if (c >= 0) {
2357 						wid = joe_wcwidth(1,c);
2358 				}
2359 			} else {
2360 				/* Byte mode: character is one column wide */
2361 				wid = 1 ;
2362 			}
2363 
2364 			if (wid>=0) {
2365 				if (col >= ofst) {
2366 					outatr(locale_map, t, scrn, attr, x, y, c, atr);
2367 					scrn += wid;
2368 					attr += wid;
2369 					x += wid;
2370 					col += wid;
2371 				} else if (col+wid>ofst) {
2372 					while (col<ofst) {
2373 						++col;
2374 						--wid;
2375 					}
2376 					while (wid) {
2377 						outatr(locale_map, t, scrn, attr, x, y, '<', atr);
2378 						++scrn;
2379 						++attr;
2380 						++x;
2381 						++col;
2382 						--wid;
2383 					}
2384 				} else
2385 					col += wid;
2386 			}
2387 		}
2388 	outatr_complete(t);
2389 	if (flg)
2390 		eraeol(t, x, y, atr);
2391 }
2392 
2393 /* Determine column width of string with format codes */
2394 
fmtlen(const char * s)2395 ptrdiff_t fmtlen(const char *s)
2396 {
2397 	ptrdiff_t col = 0;
2398 	struct utf8_sm sm;
2399 	int c;
2400 
2401 	utf8_init(&sm);
2402 
2403 	while ((c = *(const unsigned char  *)s++)) {
2404 		if (c == '\\') {
2405 			switch (*s++) {
2406 			case 'u':
2407 			case 'i':
2408 			case 'd':
2409 			case 'f':
2410 			case 'b':
2411 			case 'U':
2412 			case 'I':
2413 			case 'D':
2414 			case 'F':
2415 			case 'B':
2416 				continue;
2417 			case 0:
2418 				return col;
2419 			default:
2420 				++col;
2421 				continue;
2422 			}
2423 		} else {
2424 			ptrdiff_t wid = 0;
2425 			if(locale_map->type) {
2426 				c = utf8_decode(&sm,TO_CHAR_OK(c));
2427 				if (c>=0)
2428 					wid = joe_wcwidth(1,c);
2429 			} else {
2430 				wid = 1;
2431 			}
2432 			col += wid;
2433 		}
2434 	}
2435 	return col;
2436 }
2437 
2438 /* Return offset within format string which corresponds to a particular
2439    column */
2440 
2441 /* FIXME: this is not valid if we land in the middle of a double-wide character */
2442 
fmtpos(const char * s,ptrdiff_t goal)2443 ptrdiff_t fmtpos(const char *s, ptrdiff_t goal)
2444 {
2445 	const char *org = s;
2446 	ptrdiff_t col = 0;
2447 	int c;
2448 	struct utf8_sm sm;
2449 
2450 	utf8_init(&sm);
2451 
2452 	while ((c = *(const unsigned char *)s) && col<goal) {
2453 		s++;
2454 		if (c == '\\') {
2455 			switch (*s++) {
2456 			case 'u':
2457 			case 'i':
2458 			case 'd':
2459 			case 'f':
2460 			case 'b':
2461 			case 'U':
2462 			case 'I':
2463 			case 'D':
2464 			case 'F':
2465 			case 'B':
2466 				continue;
2467 			case 0:
2468 				--s;
2469 				break;
2470 			default:
2471 				++col;
2472 				continue;
2473 			}
2474 		} else {
2475 			ptrdiff_t wid = 0;
2476 			if(locale_map->type) {
2477 				c = utf8_decode(&sm,TO_CHAR_OK(c));
2478 				if (c>=0)
2479 					wid = joe_wcwidth(1,c);
2480 			} else {
2481 				wid = 1;
2482 			}
2483 			col += wid;
2484 		}
2485 	}
2486 
2487 	return s - org + goal - col;
2488 }
2489 
2490 /* Set extended colors palette */
setextpal(SCRN * t,int * palette)2491 void setextpal(SCRN *t, int *palette)
2492 {
2493 	t->palette = palette;
2494 }
2495