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