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