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