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