another update from CVS HEAD, for QA
[alioth/jupp.git] / termcap.c
1 /*
2  *      TERMCAP/TERMINFO database interface
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/termcap.c,v 1.23 2017/12/08 02:57:18 tg Exp $");
12
13 #include <sys/stat.h>
14 #include <stdlib.h>
15
16 #include "blocks.h"
17 #include "path.h"
18 #include "termcap.h"
19 #include "utils.h"
20 #include "va.h"
21 #include "vs.h"
22
23 int dopadding = 0;
24 unsigned char *joeterm = NULL;
25
26 /* Default termcap entry */
27
28 static const unsigned char defentry[] = "\
29 :co#80:li#25:am:\
30 :ho=\\E[H:cm=\\E[%i%d;%dH:cV=\\E[%i%dH:\
31 :up=\\E[A:UP=\\E[%dA:DO=\\E[%dB:nd=\\E[C:RI=\\E[%dC:LE=\\E[%dD:\
32 :cd=\\E[J:ce=\\E[K:cl=\\E[H\\E[J:\
33 :so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\
34 :mb=\\E[5m:md=\\E[1m:mh=\\E[2m:me=\\E[m:\
35 :ku=\\E[A:kd=\\E[B:kl=\\E[D:kr=\\E[C:\
36 :al=\\E[L:AL=\\E[%dL:dl=\\E[M:DL=\\E[%dM:\
37 :ic=\\E[@:IC=\\E[%d@:dc=\\E[P:DC=\\E[%dP:\
38 ";
39
40 /* Return true if termcap line matches name */
41
42 static int
43 match(const unsigned char *s, const unsigned char *name)
44 {
45         if (s[0] == 0 || s[0] == '#')
46                 return 0;
47         do {
48                 int x;
49
50                 for (x = 0; s[x] == name[x] && name[x] && s[x]; ++x) ;
51                 if (name[x] == 0 && (s[x] == ':' || s[x] == '|'))
52                         return 1;
53                 while (s[x] != ':' && s[x] != '|' && s[x])
54                         ++x;
55                 s += x + 1;
56         } while (s[-1] == '|');
57         return 0;
58 }
59
60 /* Find termcap entry in a file */
61
62 static unsigned char *
63 lfind(unsigned char *s, int pos, FILE *fd, const unsigned char *name)
64 {
65         int c, x;
66
67         if (!s)
68                 s = vsmk(1024);
69  loop:
70         while (c = getc(fd), c == ' ' || c == '\t' || c == '#')
71                 do {
72                         c = getc(fd);
73                 } while (c != -1 && c != '\n');
74         if (c == -1)
75                 return s = vstrunc(s, pos);
76         ungetc(c, fd);
77         s = vstrunc(s, x = pos);
78         while (1) {
79                 c = getc(fd);
80                 if (c == -1 || c == '\n')
81                         if (x != pos && s[x - 1] == '\\') {
82                                 --x;
83                                 if (!match(s + pos, name))
84                                         goto loop;
85                                 else
86                                         break;
87                         } else if (!match(s + pos, name))
88                                 goto loop;
89                         else
90                                 return vstrunc(s, x);
91                 else if (c == '\r')
92                         /* nothing */;
93                 else {
94                         s = vsset(s, x, c);
95                         ++x;
96                 }
97         }
98         while (c = getc(fd), c != -1)
99                 if (c == '\n')
100                         if (s[x - 1] == '\\')
101                                 --x;
102                         else
103                                 break;
104                 else if (c == '\r')
105                         /* nothing */;
106                 else {
107                         s = vsset(s, x, c);
108                         ++x;
109                 }
110         s = vstrunc(s, x);
111         return s;
112 }
113
114 /* Lookup termcap entry in index */
115
116 static off_t
117 findidx(FILE *file, const unsigned char *name)
118 {
119         unsigned char buf[80];
120         off_t addr = 0;
121
122         while (fgets((char *)buf, 80, file)) {
123                 int x = 0, flg = 0, c, y;
124
125                 do {
126                         for (y = x; buf[y] && buf[y] != ' ' && buf[y] != '\n'; ++y) ;
127                         c = buf[y];
128                         buf[y] = 0;
129                         if (c == '\n' || !c)
130                                 addr += ustol(buf + x, NULL, USTOL_HEX);
131                         else if (!strcmp(buf + x, name))
132                                 flg = 1;
133                         x = y + 1;
134                 } while (c && c != '\n');
135                 if (flg)
136                         return addr;
137         }
138         return 0;
139 }
140
141 /* Load termcap entry */
142
143 CAP *getcap(unsigned char *name, unsigned int baud, void (*out) (unsigned char *, unsigned char), void *outptr)
144 {
145         CAP *cap;
146         FILE *f, *f1;
147         off_t idx;
148         int x, y, c, z, ti;
149         unsigned char *tp, *pp, *qq, *namebuf, **npbuf, *idxname;
150         int sortsiz;
151
152         if (!name && !(name = joeterm) && !(name = (unsigned char *)getenv("TERM")))
153                 return NULL;
154         cap = malloc(sizeof(CAP));
155         cap->tbuf = vsmk(4096);
156         cap->abuf = NULL;
157         cap->sort = NULL;
158         cap->paste_on = NULL;
159         cap->paste_off = NULL;
160
161         if (!strcmp(name, "xterm-xfree86")) {
162                 cap->paste_on = "\033[?2004h";
163                 cap->paste_off = "\033[?2004l";
164         }
165
166 #ifdef TERMINFO
167         cap->abuf = malloc(4096);
168         cap->abufp = cap->abuf;
169         if (tgetent((char *)cap->tbuf, (char *)name) == 1)
170                 return setcap(cap, baud, out, outptr);
171         else {
172                 free(cap->abuf);
173                 cap->abuf = NULL;
174         }
175 #endif
176
177         name = vsncpy(NULL, 0, sz(name));
178         cap->sort = malloc(sizeof(struct sortentry) * (sortsiz = 64));
179
180         cap->sortlen = 0;
181
182         tp = (unsigned char *)getenv("TERMCAP");
183
184         if (tp && tp[0] == '/')
185                 namebuf = vsncpy(NULL, 0, sz(tp));
186         else {
187                 if (tp)
188                         cap->tbuf = vsncpy(sv(cap->tbuf), sz(tp));
189                 if ((tp = (unsigned char *)getenv("TERMPATH")))
190                         namebuf = vsncpy(NULL, 0, sz(tp));
191                 else {
192                         if ((tp = (unsigned char *)getenv("HOME"))) {
193                                 namebuf = vsncpy(NULL, 0, sz(tp));
194                                 namebuf = vsadd(namebuf, '/');
195                         } else
196                                 namebuf = NULL;
197                         namebuf = vsncpy(sv(namebuf), sc(".termcap "));
198                         if (has_JOERC &&
199                             vsscan(sz(get_JOERC), sc("\t :")) == ~0) {
200                                 namebuf = vsncpy(sv(namebuf), sz(get_JOERC));
201                                 namebuf = vsncpy(sv(namebuf), sc("termcap "));
202                         }
203                         namebuf = vsncpy(sv(namebuf), sc("/etc/termcap"));
204                 }
205         }
206
207         npbuf = vawords(NULL, sv(namebuf), sc("\t :"));
208         vsrm(namebuf);
209
210         y = 0;
211         ti = 0;
212
213         if (match(cap->tbuf, name))
214                 goto checktc;
215
216         cap->tbuf = vstrunc(cap->tbuf, 0);
217
218  nextfile:
219         if (!npbuf[y]) {
220                 fprintf(stderr, "Couldn't load termcap entry.  Using ansi default\n");
221                 ti = 0;
222                 cap->tbuf = vsncpy(cap->tbuf, 0, sc(defentry));
223                 goto checktc;
224         }
225         idx = 0;
226         idxname = vsncpy(NULL, 0, sz(npbuf[y]));
227         idxname = vsncpy(idxname, sLEN(idxname), sc(".idx"));
228         f1 = fopen((char *)(npbuf[y]), "r");
229         ++y;
230         if (!f1)
231                 goto nextfile;
232         f = fopen((char *)idxname, "r");
233         if (f) {
234                 struct stat buf, buf1;
235
236                 fstat(fileno(f), &buf);
237                 fstat(fileno(f1), &buf1);
238                 if (buf.st_mtime > buf1.st_mtime)
239                         idx = findidx(f, name);
240                 else
241                         fprintf(stderr, "%s is out of date\n", idxname);
242                 fclose(f);
243         }
244         vsrm(idxname);
245 #ifdef HAVE_FSEEKO
246         fseeko(f1, idx, 0);
247 #else
248         /* ugh. SOL! */
249         fseek(f1, (long)idx, 0);
250 #endif
251         cap->tbuf = lfind(cap->tbuf, ti, f1, name);
252         fclose(f1);
253         if (sLEN(cap->tbuf) == ti)
254                 goto nextfile;
255
256  checktc:
257         x = sLEN(cap->tbuf);
258         do {
259                 cap->tbuf[x] = 0;
260                 while (x && cap->tbuf[--x] != ':')
261                         /* nothing */;
262         } while (x && (!cap->tbuf[x + 1] || cap->tbuf[x + 1] == ':'));
263
264         if (cap->tbuf[x + 1] == 't' && cap->tbuf[x + 2] == 'c' && cap->tbuf[x + 3] == '=') {
265                 name = vsncpy(NULL, 0, sz(cap->tbuf + x + 4));
266                 cap->tbuf[x] = 0;
267                 cap->tbuf[x + 1] = 0;
268                 ti = x + 1;
269                 sLen(cap->tbuf) = x + 1;
270                 if (y)
271                         --y;
272                 goto nextfile;
273         }
274
275  doline:
276         pp = cap->tbuf + ti;
277
278         /* Process line at pp */
279
280  loop:
281         while (*pp && *pp != ':')
282                 ++pp;
283         if (*pp) {
284                 int q;
285
286                 *pp++ = 0;
287  loop1:
288                 if (pp[0] == ' ' || pp[0] == '\t')
289                         goto loop;
290                 for (q = 0; pp[q] && pp[q] != '#' && pp[q] != '=' && pp[q] != '@' && pp[q] != ':'; ++q) ;
291                 qq = pp;
292                 c = pp[q];
293                 pp[q] = 0;
294                 if (c)
295                         pp += q + 1;
296                 else
297                         pp += q;
298
299                 x = 0;
300                 y = cap->sortlen;
301                 z = -1;
302                 if (!y)
303                         goto in;
304                 while (z != (x + y) / 2) {
305                         int found;
306
307                         z = (x + y) / 2;
308                         found = strcmp(qq, cap->sort[z].name);
309                         if(found > 0) {
310                                 x = z;
311                         } else if(found < 0) {
312                                 y = z;
313                         } else {
314                                 if (c == '@')
315                                         mmove(cap->sort + z, cap->sort + z + 1, (cap->sortlen-- - (z + 1)) * sizeof(struct sortentry));
316
317                                 else if (c && c != ':')
318                                         cap->sort[z].value = qq + q + 1;
319                                 else
320                                         cap->sort[z].value = NULL;
321                                 if (c == ':')
322                                         goto loop1;
323                                 else
324                                         goto loop;
325                         }
326                 }
327  in:
328                 if (cap->sortlen == sortsiz)
329                         cap->sort = realloc(cap->sort, (sortsiz += 32) * sizeof(struct sortentry));
330                 mmove(cap->sort + y + 1, cap->sort + y, (cap->sortlen++ - y) * sizeof(struct sortentry));
331
332                 cap->sort[y].name = qq;
333                 if (c && c != ':')
334                         cap->sort[y].value = qq + q + 1;
335                 else
336                         cap->sort[y].value = NULL;
337                 if (c == ':')
338                         goto loop1;
339                 else
340                         goto loop;
341         }
342
343         if (ti) {
344                 for (--ti; ti; --ti)
345                         if (!cap->tbuf[ti - 1])
346                                 break;
347                 goto doline;
348         }
349
350         varm(npbuf);
351         vsrm(name);
352
353         cap->pad = jgetstr(cap, UC "pc");
354         if (dopadding)
355                 cap->dopadding = 1;
356         else
357                 cap->dopadding = 0;
358
359 /* show sorted entries
360         for(x=0;x!=cap->sortlen;++x)
361                 printf("%s = %s\n",cap->sort[x].name,cap->sort[x].value);
362 */
363         return setcap(cap, baud, out, outptr);
364 }
365
366 static struct sortentry *
367 findcap(CAP *cap, const unsigned char *name)
368 {
369         int x, y, z;
370         int found;
371
372         x = 0;
373         y = cap->sortlen;
374         z = -1;
375         while (z != (x + y) / 2) {
376                 z = (x + y) / 2;
377                 found = strcmp(name, cap->sort[z].name);
378                 if (found > 0)
379                         x = z;
380                 else if (found < 0)
381                         y = z;
382                 else
383                         return cap->sort + z;
384         }
385         return NULL;
386 }
387
388 CAP *setcap(CAP *cap, unsigned int baud, void (*out) (unsigned char *, unsigned char), void *outptr)
389 {
390         cap->baud = baud;
391         cap->div = 100000 / baud;
392         cap->out = out;
393         cap->outptr = outptr;
394         return cap;
395 }
396
397 int
398 getflag(CAP *cap, const unsigned char *name)
399 {
400 #ifdef TERMINFO
401         if (cap->abuf)
402                 return tgetflag((char *)name);
403 #endif
404         return findcap(cap, name) != NULL;
405 }
406
407 const unsigned char *
408 jgetstr(CAP *cap, const unsigned char *name)
409 {
410         struct sortentry *s;
411
412 #ifdef TERMINFO
413         if (cap->abuf)
414                 return (const unsigned char *)tgetstr((char *)name,
415                     (char **)&cap->abufp);
416 #endif
417         s = findcap(cap, name);
418         if (s)
419                 return s->value;
420         else
421                 return NULL;
422 }
423
424 int
425 getnum(CAP *cap, const unsigned char *name)
426 {
427         struct sortentry *s;
428
429 #ifdef TERMINFO
430         if (cap->abuf)
431                 return tgetnum((char *)name);
432 #endif
433         s = findcap(cap, name);
434         if (s && s->value)
435                 return atoi((const char *)(s->value));
436         return -1;
437 }
438
439 void rmcap(CAP *cap)
440 {
441         vsrm(cap->tbuf);
442         if (cap->abuf)
443                 free(cap->abuf);
444         if (cap->sort)
445                 free(cap->sort);
446         free(cap);
447 }
448
449 static unsigned char
450 escape(const unsigned char **s)
451 {
452         unsigned char c = *(*s)++;
453         int i;
454
455         if (c == '^' && **s)
456                 if (**s != '?')
457                         return 037 & *(*s)++;
458                 else {
459                         (*s)++;
460                         return 127;
461                 }
462         else if (c == '\\' && **s)
463                 switch (c = *((*s)++)) {
464                 case '0':
465                 case '1':
466                 case '2':
467                 case '3':
468                 case '4':
469                 case '5':
470                 case '6':
471                 case '7':
472                         (*s)--;
473                         *s += ustoc_oct(*s, &i, USTOC_MAX);
474                         return i;
475                 case 'e':
476                 case 'E':
477                         return 27;
478                 case 'n':
479                 case 'l':
480                         return 10;
481                 case 'r':
482                         return 13;
483                 case 't':
484                         return 9;
485                 case 'b':
486                         return 8;
487                 case 'f':
488                         return 12;
489                 case 's':
490                         return 32;
491                 default:
492                         return c;
493         } else
494                 return c;
495 }
496
497 #ifdef TERMINFO
498 static CAP *outcap;
499 static int outout(int c)
500 {
501         outcap->out(outcap->outptr, c);
502         return(c);      /* act like putchar() - return written char */
503 }
504 #endif
505
506 void
507 texec(CAP *cap, const unsigned char *s, int l, int a0, int a1, int a2, int a3)
508 {
509         int c, tenth = 0, x;
510         int args[4];
511         int vars[128];
512         int *a = args;
513
514         /* Do nothing if there is no string */
515         if (!s)
516                 return;
517
518 #ifdef TERMINFO
519         if (cap->abuf) {
520                 unsigned char *aa;
521
522                 outcap = cap;
523                 aa = (unsigned char *)tgoto((const char *)s, a1, a0);
524                 tputs((char *)aa, l, outout);
525                 return;
526         }
527 #endif
528
529         /* Copy args into array (yuk) */
530         args[0] = a0;
531         args[1] = a1;
532         args[2] = a2;
533         args[3] = a3;
534
535         /* Get tenths of MS of padding needed */
536         while (*s >= '0' && *s <= '9')
537                 tenth = tenth * 10 + *s++ - '0';
538         tenth *= 10;
539         if (*s == '.') {
540                 ++s;
541                 tenth += *s++ - '0';
542         }
543
544         /* Check if we have to multiply by number of lines */
545         if (*s == '*') {
546                 ++s;
547                 tenth *= l;
548         }
549
550         /* Output string */
551         while ((c = *s++) != '\0')
552                 if (c == '%' && *s) {
553                         switch (x = a[0], c = escape(&s)) {
554                         case 'C':
555                                 if (x >= 96) {
556                                         cap->out(cap->outptr, x / 96);
557                                         x %= 96;
558                                 }
559                                 /* FALLTHROUGH */
560                         case '+':
561                                 if (*s)
562                                         x += escape(&s);
563                                 /* FALLTHROUGH */
564                         case '.':
565                                 cap->out(cap->outptr, x);
566                                 ++a;
567                                 break;
568                         case 'd':
569                                 if (x < 10)
570                                         goto one;
571                                 /* FALLTHROUGH */
572                         case '2':
573                                 if (x < 100)
574                                         goto two;
575                                 /* FALLTHROUGH */
576                         case '3':
577                                 c = '0';
578                                 while (x >= 100) {
579                                         ++c;
580                                         x -= 100;
581                                 }
582                                 cap->out(cap->outptr, c);
583  two:
584                                 c = '0';
585                                 while (x >= 10) {
586                                         ++c;
587                                         x -= 10;
588                                 }
589                                 cap->out(cap->outptr, c);
590                               one:cap->out(cap->outptr, '0' + x);
591                                 ++a;
592                                 break;
593                         case 'r':
594                                 a[0] = a[1];
595                                 a[1] = x;
596                                 break;
597                         case 'i':
598                                 ++a[0];
599                                 ++a[1];
600                                 break;
601                         case 'n':
602                                 a[0] ^= 0140;
603                                 a[1] ^= 0140;
604                                 break;
605                         case 'm':
606                                 a[0] ^= 0177;
607                                 a[1] ^= 0177;
608                                 break;
609                         case 'f':
610                                 ++a;
611                                 break;
612                         case 'b':
613                                 --a;
614                                 break;
615                         case 'a':
616                                 x = s[2];
617                                 if (s[1] == 'p')
618                                         x = a[x - 0100];
619                                 switch (*s) {
620                                 case '+':
621                                         a[0] += x;
622                                         break;
623                                 case '-':
624                                         a[0] -= x;
625                                         break;
626                                 case '*':
627                                         a[0] *= x;
628                                         break;
629                                 case '/':
630                                         a[0] /= x;
631                                         break;
632                                 case '%':
633                                         a[0] %= x;
634                                         break;
635                                 case 'l':
636                                         a[0] = vars[x];
637                                         break;
638                                 case 's':
639                                         vars[x] = a[0];
640                                         break;
641                                 default:
642                                         a[0] = x;
643                                 }
644                                 s += 3;
645                                 break;
646                         case 'D':
647                                 a[0] = a[0] - 2 * (a[0] & 15);
648                                 break;
649                         case 'B':
650                                 a[0] = 16 * (a[0] / 10) + a[0] % 10;
651                                 break;
652                         case '>':
653                                 if (a[0] > escape(&s))
654                                         a[0] += escape(&s);
655                                 else
656                                         escape(&s);
657                                 /* FALLTHROUGH */
658                         default:
659                                 cap->out(cap->outptr, '%');
660                                 cap->out(cap->outptr, c);
661                         }
662                 } else {
663                         --s;
664                         cap->out(cap->outptr, escape(&s));
665                 }
666
667 /* Output padding characters */
668         if (cap->dopadding) {
669                 if (cap->pad)
670                         while (tenth >= cap->div)
671                                 for (s = cap->pad; *s; ++s) {
672                                         cap->out(cap->outptr, *s);
673                                         tenth -= cap->div;
674                                 }
675                 else
676                         while (tenth >= cap->div) {
677                                 cap->out(cap->outptr, 0);
678                                 tenth -= cap->div;
679                         }
680         }
681 }
682
683 static int total;
684
685 static void cst(unsigned char *ptr, unsigned char c)
686 {
687         ++total;
688 }
689
690 int tcost(CAP *cap, const unsigned char *s, int l, int a0, int a1, int a2, int a3)
691 {
692         void (*out)(unsigned char *, unsigned char) = cap->out;
693
694         if (!s)
695                 return 10000;
696         total = 0;
697         cap->out = cst;
698         texec(cap, s, l, a0, a1, a2, a3);
699         cap->out = out;
700         return total;
701 }
702
703 static unsigned char *ssp;
704 static void cpl(unsigned char *ptr, unsigned char c)
705 {
706         ssp = vsadd(ssp, c);
707 }
708
709 unsigned char *
710 tcompile(CAP *cap, const unsigned char *s, int a0, int a1, int a2, int a3)
711 {
712         void (*out) (unsigned char *, unsigned char) = cap->out;
713         int divider = cap->div;
714
715         if (!s)
716                 return NULL;
717         cap->out = cpl;
718         cap->div = 10000;
719         ssp = vsmk(10);
720         texec(cap, s, 0, a0, a1, a2, a3);
721         cap->out = out;
722         cap->div = divider;
723         return ssp;
724 }