/MirOS/dist/jupp/joe-3.1jupp30.tgz
[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                         case '+':
575                                 if (*s)
576                                         x += escape(&s);
577                         case '.':
578                                 cap->out(cap->outptr, x);
579                                 ++a;
580                                 break;
581                         case 'd':
582                                 if (x < 10)
583                                         goto one;
584                         case '2':
585                                 if (x < 100)
586                                         goto two;
587                         case '3':
588                                 c = '0';
589                                 while (x >= 100) {
590                                         ++c;
591                                         x -= 100;
592                                 }
593                                 cap->out(cap->outptr, c);
594                               two:c = '0';
595                                 while (x >= 10) {
596                                         ++c;
597                                         x -= 10;
598                                 }
599                                 cap->out(cap->outptr, c);
600                               one:cap->out(cap->outptr, '0' + x);
601                                 ++a;
602                                 break;
603                         case 'r':
604                                 a[0] = a[1];
605                                 a[1] = x;
606                                 break;
607                         case 'i':
608                                 ++a[0];
609                                 ++a[1];
610                                 break;
611                         case 'n':
612                                 a[0] ^= 0140;
613                                 a[1] ^= 0140;
614                                 break;
615                         case 'm':
616                                 a[0] ^= 0177;
617                                 a[1] ^= 0177;
618                                 break;
619                         case 'f':
620                                 ++a;
621                                 break;
622                         case 'b':
623                                 --a;
624                                 break;
625                         case 'a':
626                                 x = s[2];
627                                 if (s[1] == 'p')
628                                         x = a[x - 0100];
629                                 switch (*s) {
630                                 case '+':
631                                         a[0] += x;
632                                         break;
633                                 case '-':
634                                         a[0] -= x;
635                                         break;
636                                 case '*':
637                                         a[0] *= x;
638                                         break;
639                                 case '/':
640                                         a[0] /= x;
641                                         break;
642                                 case '%':
643                                         a[0] %= x;
644                                         break;
645                                 case 'l':
646                                         a[0] = vars[x];
647                                         break;
648                                 case 's':
649                                         vars[x] = a[0];
650                                         break;
651                                 default:
652                                         a[0] = x;
653                                 }
654                                 s += 3;
655                                 break;
656                         case 'D':
657                                 a[0] = a[0] - 2 * (a[0] & 15);
658                                 break;
659                         case 'B':
660                                 a[0] = 16 * (a[0] / 10) + a[0] % 10;
661                                 break;
662                         case '>':
663                                 if (a[0] > escape(&s))
664                                         a[0] += escape(&s);
665                                 else
666                                         escape(&s);
667                         default:
668                                 cap->out(cap->outptr, '%');
669                                 cap->out(cap->outptr, c);
670                         }
671                 } else {
672                         --s;
673                         cap->out(cap->outptr, escape(&s));
674                 }
675
676 /* Output padding characters */
677         if (cap->dopadding) {
678                 if (cap->pad)
679                         while (tenth >= cap->div)
680                                 for (s = cap->pad; *s; ++s) {
681                                         cap->out(cap->outptr, *s);
682                                         tenth -= cap->div;
683                                 }
684                 else
685                         while (tenth >= cap->div) {
686                                 cap->out(cap->outptr, 0);
687                                 tenth -= cap->div;
688                         }
689         }
690 }
691
692 static int total;
693
694 static void cst(unsigned char *ptr, unsigned char c)
695 {
696         ++total;
697 }
698
699 int tcost(CAP *cap, unsigned char *s, int l, int a0, int a1, int a2, int a3)
700 {
701         void (*out) (unsigned char *, unsigned char) = cap->out;
702
703         if (!s)
704                 return 10000;
705         total = 0;
706         cap->out = cst;
707         texec(cap, s, l, a0, a1, a2, a3);
708         cap->out = out;
709         return total;
710 }
711
712 static unsigned char *ssp;
713 static void cpl(unsigned char *ptr, unsigned char c)
714 {
715         ssp = vsadd(ssp, c);
716 }
717
718 unsigned char *tcompile(CAP *cap, unsigned char *s, int a0, int a1, int a2, int a3)
719 {
720         void (*out) (unsigned char *, unsigned char) = cap->out;
721         int divider = cap->div;
722
723         if (!s)
724                 return NULL;
725         cap->out = cpl;
726         cap->div = 10000;
727         ssp = vsmk(10);
728         texec(cap, s, 0, a0, a1, a2, a3);
729         cap->out = out;
730         cap->div = divider;
731         return ssp;
732 }