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