joe-3.1jupp31.tgz (die zweite Klappe…)
[alioth/jupp.git] / scrn.c
1 /* $MirOS: contrib/code/jupp/scrn.c,v 1.26 2017/08/08 16:09:43 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                                 /* FALLTHROUGH */
217                         default:
218                                 a ^= UNDERLINE;
219                                 break;
220                         }
221
222                         if(*scrn==c && *attrf==a)
223                                 return;
224
225                         *scrn = c;
226                         *attrf = a;
227                         if(t->ins)
228                                 clrins(t);
229                         if(t->x != xx || t->y != yy)
230                                 cpos(t, xx, yy);
231                         if(t->attrib != a)
232                                 set_attr(t, a);
233                         if (*unictrlbuf) {
234                                 ttputs(unictrlbuf);
235                         } else {
236                                 unsigned char buf[7];
237
238                                 utf8_encode(buf,c);
239                                 ttputs(buf);
240                                 if (wid == 0 && xx > 0)
241                                         attrf[-1] |= HAS_COMBINING;
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[7];
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                 /* FALLTHROUGH */
1033  doch:
1034                 /* FALLTHROUGH */
1035         case 4:
1036                 texec(t->cap, t->ch, 1, x, 0, 0, 0);
1037                 t->x = x;
1038                 break;
1039         case 10:
1040                 texec(t->cap, t->ll, 1, 0, 0, 0, 0);
1041                 t->x = 0;
1042                 t->y = hl;
1043                 goto docv;
1044         case 12:
1045                 texec(t->cap, t->ho, 1, 0, 0, 0, 0);
1046                 t->x = 0;
1047                 t->y = hy;
1048                 goto docv;
1049         case 8:
1050                 texec(t->cap, t->cr, 1, 0, 0, 0, 0);
1051                 t->x = 0;
1052                 /* FALLTHROUGH */
1053  docv:
1054                 /* FALLTHROUGH */
1055         case 5:
1056                 texec(t->cap, t->cv, 1, y, 0, 0, 0);
1057                 t->y = y;
1058                 break;
1059         case 6:
1060                 texec(t->cap, t->cm, 1, y, x, 0, 0);
1061                 t->y = y;
1062                 t->x = x;
1063                 break;
1064         case 7:
1065                 texec(t->cap, t->cv, 1, y, 0, 0, 0);
1066                 t->y = y;
1067                 texec(t->cap, t->ch, 1, x, 0, 0, 0);
1068                 t->x = x;
1069                 break;
1070         case 13:
1071                 texec(t->cap, t->cV, 1, y, 0, 0, 0);
1072                 t->y = y;
1073                 t->x = 0;
1074                 break;
1075         }
1076
1077 /* Use relative cursor position functions if we're not there yet */
1078
1079 /* First adjust row */
1080         if (y > t->y) {
1081                 /* Have to go down */
1082                 if (!t->lf || t->cDO < (y - t->y) * t->clf) {
1083                         texec(t->cap, t->DO, 1, y - t->y, 0, 0, 0);
1084                         t->y = y;
1085                 } else
1086                         while (y > t->y) {
1087                                 texec(t->cap, t->lf, 1, 0, 0, 0, 0);
1088                                 ++t->y;
1089                         }
1090         } else if (y < t->y) {
1091                 /* Have to go up */
1092                 if (!t->up || t->cUP < (t->y - y) * t->cup) {
1093                         texec(t->cap, t->UP, 1, t->y - y, 0, 0, 0);
1094                         t->y = y;
1095                 } else
1096                         while (y < t->y) {
1097                                 texec(t->cap, t->up, 1, 0, 0, 0, 0);
1098                                 --t->y;
1099                         }
1100         }
1101
1102 /* Use tabs */
1103         if (x > t->x && t->ta) {
1104                 int ntabs = (x - t->x + t->x % t->tw) / t->tw;
1105                 int cstunder = x % t->tw + t->cta * ntabs;
1106                 int cstover;
1107
1108                 if (x + t->tw < t->co && t->bs)
1109                         cstover = t->cbs * (t->tw - x % t->tw) + t->cta * (ntabs + 1);
1110                 else
1111                         cstover = 10000;
1112                 if (cstunder < t->cRI && cstunder < x - t->x && cstover > cstunder) {
1113                         if (ntabs) {
1114                                 t->x = x - x % t->tw;
1115                                 do {
1116                                         texec(t->cap, t->ta, 1, 0, 0, 0, 0);
1117                                 } while (--ntabs);
1118                         }
1119                 } else if (cstover < t->cRI && cstover < x - t->x) {
1120                         t->x = t->tw + x - x % t->tw;
1121                         ++ntabs;
1122                         do {
1123                                 texec(t->cap, t->ta, 1, 0, 0, 0, 0);
1124                         } while (--ntabs);
1125                 }
1126         } else if (x < t->x && t->bt) {
1127                 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;
1128                 int cstunder, cstover;
1129
1130                 if (t->bs)
1131                         cstunder = t->cbt * ntabs + t->cbs * (t->tw - x % t->tw);
1132                 else
1133                         cstunder = 10000;
1134                 if (x - t->tw >= 0)
1135                         cstover = t->cbt * (ntabs + 1) + x % t->tw;
1136                 else
1137                         cstover = 10000;
1138                 if (cstunder < t->cLE && (t->bs ? cstunder < (t->x - x) * t->cbs : 1)
1139                     && cstover > cstunder) {
1140                         if (ntabs) {
1141                                 do {
1142                                         texec(t->cap, t->bt, 1, 0, 0, 0, 0);
1143                                 } while (--ntabs);
1144                                 t->x = x + t->tw - x % t->tw;
1145                         }
1146                 } else if (cstover < t->cRI && (t->bs ? cstover < (t->x - x) * t->cbs : 1)) {
1147                         t->x = x - x % t->tw;
1148                         ++ntabs;
1149                         do {
1150                                 texec(t->cap, t->bt, 1, 0, 0, 0, 0);
1151                         } while (--ntabs);
1152                 }
1153         }
1154
1155 /* Now adjust column */
1156         if (x < t->x) {
1157                 /* Have to go left */
1158                 if (!t->bs || t->cLE < (t->x - x) * t->cbs) {
1159                         texec(t->cap, t->LE, 1, t->x - x, 0, 0, 0);
1160                         t->x = x;
1161                 } else
1162                         while (x < t->x) {
1163                                 texec(t->cap, t->bs, 1, 0, 0, 0, 0);
1164                                 --t->x;
1165                         }
1166         } else if (x > t->x) {
1167                 /* Have to go right */
1168                 /* Hmm.. this should take into account possible attribute changes */
1169                 if (x-t->x>1 && t->RI) {
1170                         texec(t->cap, t->RI, 1, x - t->x, 0, 0, 0);
1171                         t->x = x;
1172                 } else {
1173                         while(x>t->x) {
1174                                 texec(t->cap, t->nd, 1, 0, 0, 0, 0);
1175                                 ++t->x;
1176                         }
1177                 }
1178
1179                 /* if (t->cRI < x - t->x) { */
1180 /*              } else {
1181                         int *s = t->scrn + t->x + t->y * t->co;
1182                         int *a = t->attr + t->x + t->y * t->co;
1183
1184                         if (t->ins)
1185                                 clrins(t);
1186                         while (x > t->x) {
1187                                 int atr, c;
1188                                 if(*s==-1) c=' ', atr=0;
1189                                 else c= *s, atr= *a;
1190
1191                                 if (atr != t->attrib)
1192                                         set_attr(t, atr);
1193                                 utf8_putc(c);
1194                                 ++s;
1195                                 ++a;
1196                                 ++t->x;
1197                         }
1198                 }
1199 */
1200         }
1201 }
1202
1203 int cpos(register SCRN *t, register int x, register int y)
1204 {
1205         /* Move cursor quickly if we can */
1206         if (y == t->y) {
1207                 if (x > t->x && x - t->x < 4 && !t->ins) {
1208                         int *cs = t->scrn + t->x + t->co * t->y;
1209                         int *as = t->attr + t->x + t->co * t->y;
1210                         do {
1211                                 /* We used to space over unknown chars, but they now could be
1212                                    the right half of a UTF-8 two column character, so we can't.
1213                                    Also do not try to emit utf-8 sequences here. */
1214                                 if(*cs<32 || *cs>=127)
1215                                         break;
1216
1217                                 /* has a combining character attached? */
1218                                 if (*as & HAS_COMBINING)
1219                                         break;
1220
1221                                 if (*as != t->attrib)
1222                                         set_attr(t, *as);
1223
1224                                 ttputc(*cs);
1225
1226                                 ++cs;
1227                                 ++as;
1228                                 ++t->x;
1229
1230                         } while (x != t->x);
1231                 }
1232                 if (x == t->x)
1233                         return 0;
1234         }
1235         if ((!t->ms && t->attrib & (INVERSE | UNDERLINE | BG_NOT_DEFAULT)) ||
1236             (t->ut && (t->attrib & BG_NOT_DEFAULT)))
1237                 set_attr(t, t->attrib & ~(INVERSE | UNDERLINE | BG_MASK));
1238
1239         /* Should be in cposs */
1240         if (y < t->top || y >= t->bot)
1241                 setregn(t, 0, t->li);
1242
1243         cposs(t, x, y);
1244         return 0;
1245 }
1246
1247 static void doinschr(SCRN *t, int x, int y, int *s, int *as, int n)
1248 {
1249         int a;
1250
1251         if (x < 0) {
1252                 s -= x;
1253                 as -= x;
1254                 x = 0;
1255         }
1256         if (x >= t->co - 1 || n <= 0)
1257                 return;
1258         if (t->im || t->ic || t->IC) {
1259                 cpos(t, x, y);
1260                 if ((n == 1 && t->ic) || !t->IC) {
1261                         if (!t->ic)
1262                                 setins(t, x);
1263                         for (a = 0; a != n; ++a) {
1264                                 texec(t->cap, t->ic, 1, x, 0, 0, 0);
1265                                 texec(t->cap, t->ip, 1, x, 0, 0, 0);
1266                         }
1267                         if (!t->mi)
1268                                 clrins(t);
1269                 } else {
1270                         texec(t->cap, t->IC, 1, n, 0, 0, 0);
1271                 }
1272         }
1273         mmove(t->scrn + x + t->co * y + n, t->scrn + x + t->co * y, (t->co - (x + n)) * sizeof(int));
1274         mmove(t->attr + x + t->co * y + n, t->attr + x + t->co * y, (t->co - (x + n)) * sizeof(int));
1275         mmove(t->scrn + x + t->co * y, s, n * sizeof(int));
1276         mmove(t->attr + x + t->co * y, s, n * sizeof(int));
1277 }
1278
1279 static void dodelchr(SCRN *t, int x, int y, int n)
1280 {
1281         int a;
1282
1283         if (x < 0)
1284                 x = 0;
1285         if (!n || x >= t->co - 1)
1286                 return;
1287         if (t->dc || t->DC) {
1288                 cpos(t, x, y);
1289                 texec(t->cap, t->dm, 1, x, 0, 0, 0);    /* Enter delete mode */
1290                 if ((n == 1 && t->dc) || !t->DC)
1291                         for (a = n; a; --a)
1292                                 texec(t->cap, t->dc, 1, x, 0, 0, 0);
1293                 else
1294                         texec(t->cap, t->DC, 1, n, 0, 0, 0);
1295                 texec(t->cap, t->ed, 1, x, 0, 0, 0);    /* Exit delete mode */
1296         }
1297         mmove(t->scrn + t->co * y + x, t->scrn + t->co * y + x + n, (t->co - (x + n)) * sizeof(int));
1298         mmove(t->attr + t->co * y + x, t->attr + t->co * y + x + n, (t->co - (x + n)) * sizeof(int));
1299         msetI(t->scrn + t->co * y + t->co - n, ' ', n);
1300         msetI(t->attr + t->co * y + t->co - n, 0, n);
1301 }
1302
1303 /* Insert/Delete within line */
1304 /* FIXME: doesn't know about attr */
1305
1306 void magic(SCRN *t, int y, int *cs, int *ca,int *s, int *a, int placex)
1307 {
1308         struct hentry *htab = t->htab;
1309         int *ofst = t->ofst;
1310         int aryx = 1;
1311         int x;
1312
1313         if (!(t->im || t->ic || t->IC) || !(t->dc || t->DC))
1314                 return;
1315         mset(htab, 0, 256 * sizeof(struct hentry));
1316
1317         msetI(ofst, 0, t->co);
1318
1319 /* Build hash table */
1320         for (x = 0; x != t->co - 1; ++x) {
1321                 t->ary[aryx].next = htab[cs[x] & 255].next;
1322                 t->ary[aryx].loc = x;
1323                 ++htab[cs[x] & 255].loc;
1324                 htab[cs[x] & 255].next = aryx++;
1325         }
1326
1327 /* Build offset table */
1328         for (x = 0; x < t->co - 1;)
1329                 if (htab[s[x] & 255].loc >= 15)
1330                         ofst[x++] = t->co - 1;
1331                 else {
1332                         int aryy;
1333                         int maxaryy = 0;
1334                         int maxlen = 0;
1335                         int best = 0;
1336                         int bestback = 0;
1337                         int z;
1338
1339                         for (aryy = htab[s[x] & 255].next; aryy; aryy = t->ary[aryy].next) {
1340                                 int amnt, back;
1341                                 int tsfo = t->ary[aryy].loc - x;
1342                                 int cst = -abs(tsfo);
1343                                 int pre = 32;
1344
1345                                 for (amnt = 0; x + amnt < t->co - 1 && x + tsfo + amnt < t->co - 1; ++amnt) {
1346                                         if (cs[x + tsfo + amnt] != s[x + amnt])
1347                                                 break;
1348                                         else if ((s[x + amnt] & 255) != 32 || pre != 32)
1349                                                 ++cst;
1350                                         pre = s[x + amnt] & 255;
1351                                 }
1352                                 pre = 32;
1353                                 for (back = 0; back + x > 0 && back + tsfo + x > 0; --back) {
1354                                         if (cs[x + tsfo + back - 1] != s[x + back - 1])
1355                                                 break;
1356                                         else if ((s[x + back - 1] & 255) != 32 || pre != 32)
1357                                                 ++cst;
1358                                         pre = s[x + back - 1] & 255;
1359                                 }
1360                                 if (cst > best) {
1361                                         maxaryy = aryy;
1362                                         maxlen = amnt;
1363                                         best = cst;
1364                                         bestback = back;
1365                                 }
1366                         }
1367                         if (!maxlen) {
1368                                 ofst[x] = t->co - 1;
1369                                 maxlen = 1;
1370                         } else if (best < 2)
1371                                 for (z = 0; z != maxlen; ++z)
1372                                         ofst[x + z] = t->co - 1;
1373                         else
1374                                 for (z = 0; z != maxlen - bestback; ++z)
1375                                         ofst[x + z + bestback] = t->ary[maxaryy].loc - x;
1376                         x += maxlen;
1377                 }
1378
1379 /* Apply scrolling commands */
1380
1381         for (x = 0; x != t->co - 1; ++x) {
1382                 int q = ofst[x];
1383
1384                 if (q && q != t->co - 1) {
1385                         if (q > 0) {
1386                                 int z, fu;
1387
1388                                 for (z = x; z != t->co - 1 && ofst[z] == q; ++z)
1389                                         ;
1390                                 while (s[x] == cs[x] && x < placex)
1391                                         ++x;
1392                                 dodelchr(t, x, y, q);
1393                                 for (fu = x; fu != t->co - 1; ++fu)
1394                                         if (ofst[fu] != t->co - 1)
1395                                                 ofst[fu] -= q;
1396                                 x = z - 1;
1397                         } else {
1398                                 int z, fu;
1399
1400                                 for (z = x; z != t->co - 1 && ofst[z] == q; ++z)
1401                                         ;
1402                                 while (s[x + q] == cs[x + q] && x - q < placex)
1403                                         ++x;
1404                                 doinschr(t, x + q, y, s + x + q, a + x + q, -q);
1405                                 for (fu = x; fu != t->co - 1; ++fu)
1406                                         if (ofst[fu] != t->co - 1)
1407                                                 ofst[fu] -= q;
1408                                 x = z - 1;
1409                         }
1410                 }
1411         }
1412 }
1413
1414 static void doupscrl(SCRN *t, int top, int bot, int amnt)
1415 {
1416         int a = amnt;
1417
1418         if (!amnt)
1419                 return;
1420         set_attr(t, 0);
1421         if (top == 0 && bot == t->li && (t->sf || t->SF)) {
1422                 setregn(t, 0, t->li);
1423                 cpos(t, 0, t->li - 1);
1424                 if ((amnt == 1 && t->sf) || !t->SF)
1425                         while (a--)
1426                                 texec(t->cap, t->sf, 1, t->li - 1, 0, 0, 0);
1427                 else
1428                         texec(t->cap, t->SF, a, a, 0, 0, 0);
1429                 goto done;
1430         }
1431         if (bot == t->li && (t->dl || t->DL)) {
1432                 setregn(t, 0, t->li);
1433                 cpos(t, 0, top);
1434                 if ((amnt == 1 && t->dl) || !t->DL)
1435                         while (a--)
1436                                 texec(t->cap, t->dl, 1, top, 0, 0, 0);
1437                 else
1438                         texec(t->cap, t->DL, a, a, 0, 0, 0);
1439                 goto done;
1440         }
1441         if (t->cs && (t->sf || t->SF)) {
1442                 setregn(t, top, bot);
1443                 cpos(t, 0, bot - 1);
1444                 if ((amnt == 1 && t->sf) || !t->SF)
1445                         while (a--)
1446                                 texec(t->cap, t->sf, 1, bot - 1, 0, 0, 0);
1447                 else
1448                         texec(t->cap, t->SF, a, a, 0, 0, 0);
1449                 goto done;
1450         }
1451         if ((t->dl || t->DL) && (t->al || t->AL)) {
1452                 cpos(t, 0, top);
1453                 if ((amnt == 1 && t->dl) || !t->DL)
1454                         while (a--)
1455                                 texec(t->cap, t->dl, 1, top, 0, 0, 0);
1456                 else
1457                         texec(t->cap, t->DL, a, a, 0, 0, 0);
1458                 a = amnt;
1459                 cpos(t, 0, bot - amnt);
1460                 if ((amnt == 1 && t->al) || !t->AL)
1461                         while (a--)
1462                                 texec(t->cap, t->al, 1, bot - amnt, 0, 0, 0);
1463                 else
1464                         texec(t->cap, t->AL, a, a, 0, 0, 0);
1465                 goto done;
1466         }
1467         msetI(t->updtab + top, 1, bot - top);
1468         msetI(t->syntab + top, -1, bot - top);
1469         return;
1470
1471  done:
1472         mmove(t->scrn + top * t->co, t->scrn + (top + amnt) * t->co, (bot - top - amnt) * t->co * sizeof(int));
1473         mmove(t->attr + top * t->co, t->attr + (top + amnt) * t->co, (bot - top - amnt) * t->co * sizeof(int));
1474
1475         if (bot == t->li && t->db) {
1476                 msetI(t->scrn + (t->li - amnt) * t->co, -1, amnt * t->co);
1477                 msetI(t->attr + (t->li - amnt) * t->co, 0, amnt * t->co);
1478                 msetI(t->updtab + t->li - amnt, 1, amnt);
1479                 msetI(t->syntab + t->li - amnt, -1, amnt);
1480         } else {
1481                 msetI(t->scrn + (bot - amnt) * t->co, ' ', amnt * t->co);
1482                 msetI(t->attr + (bot - amnt) * t->co, 0, amnt * t->co);
1483         }
1484 }
1485
1486 static void dodnscrl(SCRN *t, int top, int bot, int amnt)
1487 {
1488         int a = amnt;
1489
1490         if (!amnt)
1491                 return;
1492         set_attr(t, 0);
1493         if (top == 0 && bot == t->li && (t->sr || t->SR)) {
1494                 setregn(t, 0, t->li);
1495                 cpos(t, 0, 0);
1496                 if ((amnt == 1 && t->sr) || !t->SR)
1497                         while (a--)
1498                                 texec(t->cap, t->sr, 1, 0, 0, 0, 0);
1499                 else
1500                         texec(t->cap, t->SR, a, a, 0, 0, 0);
1501                 goto done;
1502         }
1503         if (bot == t->li && (t->al || t->AL)) {
1504                 setregn(t, 0, t->li);
1505                 cpos(t, 0, top);
1506                 if ((amnt == 1 && t->al) || !t->AL)
1507                         while (a--)
1508                                 texec(t->cap, t->al, 1, top, 0, 0, 0);
1509                 else
1510                         texec(t->cap, t->AL, a, a, 0, 0, 0);
1511                 goto done;
1512         }
1513         if (t->cs && (t->sr || t->SR)) {
1514                 setregn(t, top, bot);
1515                 cpos(t, 0, top);
1516                 if ((amnt == 1 && t->sr) || !t->SR)
1517                         while (a--)
1518                                 texec(t->cap, t->sr, 1, top, 0, 0, 0);
1519                 else
1520                         texec(t->cap, t->SR, a, a, 0, 0, 0);
1521                 goto done;
1522         }
1523         if ((t->dl || t->DL) && (t->al || t->AL)) {
1524                 cpos(t, 0, bot - amnt);
1525                 if ((amnt == 1 && t->dl) || !t->DL)
1526                         while (a--)
1527                                 texec(t->cap, t->dl, 1, bot - amnt, 0, 0, 0);
1528                 else
1529                         texec(t->cap, t->DL, a, a, 0, 0, 0);
1530                 a = amnt;
1531                 cpos(t, 0, top);
1532                 if ((amnt == 1 && t->al) || !t->AL)
1533                         while (a--)
1534                                 texec(t->cap, t->al, 1, top, 0, 0, 0);
1535                 else
1536                         texec(t->cap, t->AL, a, a, 0, 0, 0);
1537                 goto done;
1538         }
1539         msetI(t->updtab + top, 1, bot - top);
1540         msetI(t->syntab + top, -1, bot - top);
1541         return;
1542  done:
1543         mmove(t->scrn + (top + amnt) * t->co, t->scrn + top * t->co, (bot - top - amnt) * t->co * sizeof(int));
1544         mmove(t->attr + (top + amnt) * t->co, t->attr + top * t->co, (bot - top - amnt) * t->co * sizeof(int));
1545
1546         if (!top && t->da) {
1547                 msetI(t->scrn, -1, amnt * t->co);
1548                 msetI(t->attr, 0, amnt * t->co);
1549                 msetI(t->updtab, 1, amnt);
1550                 msetI(t->syntab, -1, amnt);
1551         } else {
1552                 msetI(t->scrn + t->co * top, ' ', amnt * t->co);
1553                 msetI(t->attr + t->co * top, 0, amnt * t->co);
1554         }
1555 }
1556
1557 void nscroll(SCRN *t)
1558 {
1559         int y, z, q, r, p;
1560
1561         for (y = 0; y != t->li; ++y) {
1562                 q = t->sary[y];
1563                 if (ifhave)
1564                         return;
1565                 if (q && q != t->li) {
1566                         if (q > 0) {
1567                                 for (z = y; z != t->li && t->sary[z] == q; ++z)
1568                                         t->sary[z] = 0;
1569                                 doupscrl(t, y, z + q, q);
1570                                 y = z - 1;
1571                         } else {
1572                                 for (r = y; r != t->li && (t->sary[r] < 0 || t->sary[r] == t->li); ++r)
1573                                         ;
1574                                 p = r - 1;
1575                                 do {
1576                                         q = t->sary[p];
1577                                         if (q && q != t->li) {
1578                                                 for (z = p; t->sary[z] = 0, (z && t->sary[z - 1] == q); --z)
1579                                                         ;
1580                                                 dodnscrl(t, z + q, p + 1, -q);
1581                                                 p = z + 1;
1582                                         }
1583                                 } while (p-- != y);
1584                                 y = r - 1;
1585                         }
1586                 }
1587         }
1588         msetI(t->sary, 0, t->li);
1589 }
1590
1591 void npartial(SCRN *t)
1592 {
1593         set_attr(t, 0);
1594         clrins(t);
1595         setregn(t, 0, t->li);
1596 }
1597
1598 void nescape(SCRN *t)
1599 {
1600         npartial(t);
1601         cpos(t, 0, t->li - 1);
1602         eraeol(t, 0, t->li - 1);
1603         if (t->te)
1604                 texec(t->cap, t->te, 1, 0, 0, 0, 0);
1605 }
1606
1607 void nreturn(SCRN *t)
1608 {
1609         if (t->ti)
1610                 texec(t->cap, t->ti, 1, 0, 0, 0, 0);
1611         if (!skiptop && t->cl)
1612                 texec(t->cap, t->cl, 1, 0, 0, 0, 0);
1613         nredraw(t);
1614 }
1615
1616 void nclose(SCRN *t)
1617 {
1618         leave = 1;
1619         set_attr(t, 0);
1620         clrins(t);
1621         setregn(t, 0, t->li);
1622         cpos(t, 0, t->li - 1);
1623         if (t->te)
1624                 texec(t->cap, t->te, 1, 0, 0, 0, 0);
1625         ttclose();
1626         rmcap(t->cap);
1627         joe_free(t->scrn);
1628         joe_free(t->attr);
1629         joe_free(t->sary);
1630         joe_free(t->ofst);
1631         joe_free(t->htab);
1632         joe_free(t->ary);
1633         joe_free(t);
1634 }
1635
1636 void nscrldn(SCRN *t, int top, int bot, int amnt)
1637 {
1638         int x;
1639
1640         if (!amnt || top >= bot || bot > t->li)
1641                 return;
1642         if ((amnt < bot - top && bot - top - amnt < amnt / 2) || !t->scroll)
1643                 amnt = bot - top;
1644         if (amnt < bot - top) {
1645                 for (x = bot; x != top + amnt; --x) {
1646                         t->sary[x - 1] = (t->sary[x - amnt - 1] == t->li ? t->li : t->sary[x - amnt - 1] - amnt);
1647                         t->updtab[x - 1] = t->updtab[x - amnt - 1];
1648                         t->syntab[x - 1] = t->syntab[x - amnt - 1];
1649                 }
1650                 for (x = top; x != top + amnt; ++x) {
1651                         t->updtab[x] = 1;
1652                         t->syntab[x] = -1;
1653                 }
1654         }
1655         if (amnt > bot - top)
1656                 amnt = bot - top;
1657         msetI(t->sary + top, t->li, amnt);
1658         if (amnt == bot - top) {
1659                 msetI(t->updtab + top, 1, amnt);
1660                 msetI(t->syntab + top, -1, amnt);
1661         }
1662 }
1663
1664 void nscrlup(SCRN *t, int top, int bot, int amnt)
1665 {
1666         int x;
1667
1668         if (!amnt || top >= bot || bot > t->li)
1669                 return;
1670         if ((amnt < bot - top && bot - top - amnt < amnt / 2) || !t->scroll)
1671                 amnt = bot - top;
1672         if (amnt < bot - top) {
1673                 for (x = top + amnt; x != bot; ++x) {
1674                         t->sary[x - amnt] = (t->sary[x] == t->li ? t->li : t->sary[x] + amnt);
1675                         t->updtab[x - amnt] = t->updtab[x];
1676                         t->syntab[x - amnt] = t->syntab[x];
1677                 }
1678                 for (x = bot - amnt; x != bot; ++x) {
1679                         t->updtab[x] = 1;
1680                         t->syntab[x] = -1;
1681                 }
1682         }
1683         if (amnt > bot - top)
1684                 amnt = bot - top;
1685         msetI(t->sary + bot - amnt, t->li, amnt);
1686         if (amnt == bot - top) {
1687                 msetI(t->updtab + bot - amnt, 1, amnt);
1688                 msetI(t->syntab + bot - amnt, -1, amnt);
1689         }
1690 }
1691
1692 extern volatile int dostaupd;
1693
1694 void nredraw(SCRN *t)
1695 {
1696         dostaupd = 1;
1697         msetI(t->scrn, ' ', t->co * skiptop);
1698         msetI(t->attr, 0, t->co * skiptop);
1699         msetI(t->scrn + skiptop * t->co, -1, (t->li - skiptop) * t->co);
1700         msetI(t->attr + skiptop * t->co, 0, (t->li - skiptop) * t->co);
1701         msetI(t->sary, 0, t->li);
1702         msetI(t->updtab + skiptop, -1, t->li - skiptop);
1703         msetI(t->syntab + skiptop, -1, t->li - skiptop);
1704         t->x = -1;
1705         t->y = -1;
1706         t->top = t->li;
1707         t->bot = 0;
1708         t->attrib = -1;
1709         t->ins = -1;
1710         set_attr(t, 0);
1711         clrins(t);
1712         setregn(t, 0, t->li);
1713
1714         if (!skiptop) {
1715                 if (t->cl) {
1716                         texec(t->cap, t->cl, 1, 0, 0, 0, 0);
1717                         t->x = 0;
1718                         t->y = 0;
1719                         msetI(t->scrn, ' ', t->li * t->co);
1720                         msetI(t->attr, 0, t->li * t->co);
1721                 } else if (t->cd) {
1722                         cpos(t, 0, 0);
1723                         texec(t->cap, t->cd, 1, 0, 0, 0, 0);
1724                         msetI(t->scrn, ' ', t->li * t->co);
1725                         msetI(t->attr, 0, t->li * t->co);
1726                 }
1727         }
1728 }
1729
1730 /* Convert color/attribute name into internal code */
1731
1732 int meta_color(unsigned char *s)
1733 {
1734         if(!strcmp((char *)s,"inverse"))
1735                 return INVERSE;
1736         else if(!strcmp((char *)s,"underline"))
1737                 return UNDERLINE;
1738         else if(!strcmp((char *)s,"bold"))
1739                 return BOLD;
1740         else if(!strcmp((char *)s,"blink"))
1741                 return BLINK;
1742         else if(!strcmp((char *)s,"dim"))
1743                 return DIM;
1744         else if(!strcmp((char *)s,"white"))
1745                 return FG_WHITE;
1746         else if(!strcmp((char *)s,"cyan"))
1747                 return FG_CYAN;
1748         else if(!strcmp((char *)s,"magenta"))
1749                 return FG_MAGENTA;
1750         else if(!strcmp((char *)s,"blue"))
1751                 return FG_BLUE;
1752         else if(!strcmp((char *)s,"yellow"))
1753                 return FG_YELLOW;
1754         else if(!strcmp((char *)s,"green"))
1755                 return FG_GREEN;
1756         else if(!strcmp((char *)s,"red"))
1757                 return FG_RED;
1758         else if(!strcmp((char *)s,"black"))
1759                 return FG_BLACK;
1760         else if(!strcmp((char *)s,"bg_white"))
1761                 return BG_WHITE;
1762         else if(!strcmp((char *)s,"bg_cyan"))
1763                 return BG_CYAN;
1764         else if(!strcmp((char *)s,"bg_magenta"))
1765                 return BG_MAGENTA;
1766         else if(!strcmp((char *)s,"bg_blue"))
1767                 return BG_BLUE;
1768         else if(!strcmp((char *)s,"bg_yellow"))
1769                 return BG_YELLOW;
1770         else if(!strcmp((char *)s,"bg_green"))
1771                 return BG_GREEN;
1772         else if(!strcmp((char *)s,"bg_red"))
1773                 return BG_RED;
1774         else if(!strcmp((char *)s,"bg_black"))
1775                 return BG_BLACK;
1776         else
1777                 return 0;
1778 }
1779
1780 /* Generate a field
1781  *
1782  * 't' is SCRN to write to.
1783  * 'scrn' is address of field in character buffer
1784  * 'attr' is address of field in attribute buffer
1785  * 'x', 'y' are starting column and line numbers of field
1786  * 'ofst' is first column within string to display
1787  * 's', 'len' is string to generate in field
1788  * 'atr' is screeen attributes (and color) which should be used
1789  * 'width' is column width of field
1790  * 'flg' if set, erases to end of line
1791  */
1792
1793 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)
1794 {
1795         int col;
1796         struct utf8_sm sm;
1797         int last_col = x + width;
1798
1799         utf8_init(&sm);
1800
1801         for (col = 0;len != 0 && x < last_col; len--) {
1802                 int c = *s++;
1803                 int wid = -1;
1804                 int my_atr = atr;
1805                 if (fmt) my_atr |= *fmt++;
1806                 if (locale_map->type) {
1807                         /* UTF-8 mode: decode character and determine its width */
1808                         c = utf8_decode(&sm,c);
1809                         if (c >= 0)
1810                                 wid = joe_wcwidth(1,c);
1811                 } else {
1812                         /* Byte mode: character is one column wide */
1813                         wid = 1;
1814                 }
1815                 if (wid >= 0) {
1816                         if (col >= ofst) {
1817                                 if (x + wid > last_col) {
1818                                         /* Character crosses end of field, so fill balance of field with '>' characters instead */
1819                                         while (x < last_col) {
1820                                                 outatr(utf8_map, t, scrn, attr, x, y, '>', my_atr);
1821                                                 ++scrn;
1822                                                 ++attr;
1823                                                 ++x;
1824                                         }
1825                                 } else /* if (wid >(=) 0) */ {
1826                                         /* Emit character */
1827                                         outatr(locale_map, t, scrn, attr, x, y, c, my_atr);
1828                                         x += wid;
1829                                         scrn += wid;
1830                                         attr += wid;
1831                                 }
1832                         } else if ((col + wid) > ofst) {
1833                                 /* Wide character crosses left side of field */
1834                                 wid -= ofst - col;
1835                                 col = ofst;
1836                                 while (wid) {
1837                                         outatr(utf8_map, t, scrn, attr, x, y, '<', my_atr);
1838                                         ++scrn;
1839                                         ++attr;
1840                                         ++x;
1841                                         ++col;
1842                                         --wid;
1843                                 }
1844                         } else
1845                                 col += wid;
1846                 }
1847         }
1848         /* Fill balance of field with spaces */
1849         while (x < last_col) {
1850                 outatr(utf8_map, t, scrn, attr, x, y, ' ', 0);
1851                 ++x;
1852                 ++scrn;
1853                 ++attr;
1854         }
1855         /* Erase to end of line */
1856         if (flg)
1857                 eraeol(t, x, y);
1858 }
1859
1860 /* Width function for above */
1861
1862 int txtwidth(unsigned char *s,int len)
1863 {
1864         if (locale_map->type) {
1865                 int col=0;
1866                 struct utf8_sm sm;
1867                 utf8_init(&sm);
1868
1869                 while(len--) {
1870                         int d = utf8_decode(&sm,*s++);
1871                         if (d >= 0)
1872                                 col += joe_wcwidth(1,d);
1873                 }
1874
1875                 return col;
1876         } else
1877                 return len;
1878 }
1879
1880 /* Generate text with formatting escape sequences */
1881
1882 void genfmt(SCRN *t, int x, int y, int ofst, const unsigned char *s, int flg)
1883 {
1884         int *scrn = t->scrn + y * t->co + x;
1885         int *attr = t->attr + y * t->co + x;
1886         int atr = 0;
1887         int col = 0;
1888         int c;
1889         struct utf8_sm sm;
1890
1891         utf8_init(&sm);
1892
1893         while ((c = *s++) != '\0')
1894                 if (c == '\\') {
1895                         switch (c = *s++) {
1896                         case 'u':
1897                         case 'U':
1898                                 atr ^= UNDERLINE;
1899                                 break;
1900                         case 'i':
1901                         case 'I':
1902                                 atr ^= INVERSE;
1903                                 break;
1904                         case 'b':
1905                         case 'B':
1906                                 atr ^= BOLD;
1907                                 break;
1908                         case 'd':
1909                         case 'D':
1910                                 atr ^= DIM;
1911                                 break;
1912                         case 'f':
1913                         case 'F':
1914                                 atr ^= BLINK;
1915                                 break;
1916                         case 0:
1917                                 --s;
1918                                 break;
1919                         default: {
1920                                 if (col++ >= ofst) {
1921                                         outatr(locale_map, t, scrn, attr, x, y, (c&0x7F), atr);
1922                                         ++scrn;
1923                                         ++attr;
1924                                         ++x;
1925                                         }
1926                                 break;
1927                                 }
1928                         }
1929                 } else {
1930                         int wid = -1;
1931                         if (locale_map->type) {
1932                                 /* UTF-8 mode: decode character and determine its width */
1933                                 c = utf8_decode(&sm,c);
1934                                 if (c >= 0) {
1935                                         wid = joe_wcwidth(1, c);
1936                                 }
1937                         } else {
1938                                 /* Byte mode: character is one column wide */
1939                                 wid = 1;
1940                         }
1941
1942                         if (wid >= 0) {
1943                                 if (col >= ofst) {
1944                                         outatr(locale_map, t, scrn, attr, x, y, c, atr);
1945                                         scrn += wid;
1946                                         attr += wid;
1947                                         x += wid;
1948                                         col += wid;
1949                                 } else if (col+wid>ofst) {
1950                                         while (col<ofst) {
1951                                                 ++col;
1952                                                 --wid;
1953                                         }
1954                                         while (wid) {
1955                                                 outatr(utf8_map, t, scrn, attr, x, y, '<', atr);
1956                                                 ++scrn;
1957                                                 ++attr;
1958                                                 ++x;
1959                                                 ++col;
1960                                                 --wid;
1961                                         }
1962                                 } else
1963                                         col += wid;
1964                 }
1965                 }
1966         if (flg)
1967                 eraeol(t, x, y);
1968 }
1969
1970 /* Determine column width of string with format codes */
1971
1972 int fmtlen(const unsigned char *s)
1973 {
1974         int col = 0;
1975         struct utf8_sm sm;
1976         int c;
1977
1978         utf8_init(&sm);
1979
1980         while ((c = (*s++))) {
1981                 if (c == '\\') {
1982                         switch (*s++) {
1983                         case 'u':
1984                         case 'i':
1985                         case 'd':
1986                         case 'f':
1987                         case 'b':
1988                         case 'U':
1989                         case 'I':
1990                         case 'D':
1991                         case 'F':
1992                         case 'B':
1993                                 continue;
1994                         case 0:
1995                                 return col;
1996                         default:
1997                                 ++col;
1998                                 continue;
1999                         }
2000                 } else {
2001                         int wid = 0;
2002                         if(locale_map->type) {
2003                                 c = utf8_decode(&sm,c);
2004                                 if (c>=0)
2005                                         wid = joe_wcwidth(1,c);
2006                         } else {
2007                                 wid = 1;
2008                         }
2009                         col += wid;
2010                 }
2011         }
2012         return col;
2013 }
2014
2015 /* Return offset within format string which corresponds to a particular
2016    column */
2017
2018 /* FIXME: this is not valid if we land in the middle of a double-wide character */
2019
2020 int fmtpos(unsigned char *s, int goal)
2021 {
2022         unsigned char *org = s;
2023         int col = 0;
2024         int c;
2025         struct utf8_sm sm;
2026
2027         utf8_init(&sm);
2028
2029         while ((c= *s) && col<goal) {
2030                 s++;
2031                 if (c == '\\') {
2032                         switch (*s++) {
2033                         case 'u':
2034                         case 'i':
2035                         case 'd':
2036                         case 'f':
2037                         case 'b':
2038                         case 'U':
2039                         case 'I':
2040                         case 'D':
2041                         case 'F':
2042                         case 'B':
2043                                 continue;
2044                         case 0:
2045                                 --s;
2046                                 break;
2047                         default:
2048                                 ++col;
2049                                 continue;
2050                         }
2051                 } else {
2052                         int wid = 0;
2053                         if(locale_map->type) {
2054                                 c = utf8_decode(&sm,c);
2055                                 if (c>=0)
2056                                         wid = joe_wcwidth(1,c);
2057                         } else {
2058                                 wid = 1;
2059                         }
2060                         col += wid;
2061                 }
2062         }
2063
2064         return s - org + goal - col;
2065 }