1 /* $NetBSD: termcap.c,v 1.25 2023/01/31 21:11:24 andvar Exp $ */
2 
3 /*
4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5  *
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Roy Marples.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: termcap.c,v 1.25 2023/01/31 21:11:24 andvar Exp $");
32 
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <string.h>
39 #include <term_private.h>
40 #include <term.h>
41 #include <termcap.h>
42 #include <unistd.h>
43 #include <stdio.h>
44 
45 #include "termcap_map.c"
46 #include "termcap_hash.c"
47 
48 char *UP;
49 char *BC;
50 
51 /* ARGSUSED */
52 int
tgetent(__unused char * bp,const char * name)53 tgetent(__unused char *bp, const char *name)
54 {
55           int errret;
56           static TERMINAL *last = NULL;
57 
58           _DIAGASSERT(name != NULL);
59 
60           /* Free the old term */
61           if (cur_term != NULL) {
62                     if (last != NULL && cur_term != last)
63                               del_curterm(last);
64                     last = cur_term;
65           }
66           errret = -1;
67           if (setupterm(name, STDOUT_FILENO, &errret) != 0)
68                     return errret;
69 
70           if (last == NULL)
71                     last = cur_term;
72 
73           if (pad_char != NULL)
74                     PC = pad_char[0];
75           UP = __UNCONST(cursor_up);
76           BC = __UNCONST(cursor_left);
77           return 1;
78 }
79 
80 int
tgetflag(const char * id2)81 tgetflag(const char *id2)
82 {
83           uint32_t ind;
84           size_t i;
85           TERMUSERDEF *ud;
86           const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
87 
88           if (cur_term == NULL)
89                     return 0;
90 
91           ind = _t_flaghash((const unsigned char *)id, strlen(id));
92           if (ind < __arraycount(_ti_cap_flagids)) {
93                     if (strcmp(id, _ti_cap_flagids[ind].id) == 0)
94                               return cur_term->flags[_ti_cap_flagids[ind].ti];
95           }
96           for (i = 0; i < cur_term->_nuserdefs; i++) {
97                     ud = &cur_term->_userdefs[i];
98                     if (ud->type == 'f' && strcmp(ud->id, id) == 0)
99                               return ud->flag;
100           }
101           return 0;
102 }
103 
104 int
tgetnum(const char * id2)105 tgetnum(const char *id2)
106 {
107           uint32_t ind;
108           size_t i;
109           TERMUSERDEF *ud;
110           const TENTRY *te;
111           const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
112 
113           if (cur_term == NULL)
114                     return -1;
115 
116           ind = _t_numhash((const unsigned char *)id, strlen(id));
117           if (ind < __arraycount(_ti_cap_numids)) {
118                     te = &_ti_cap_numids[ind];
119                     if (strcmp(id, te->id) == 0) {
120                               if (!VALID_NUMERIC(cur_term->nums[te->ti]))
121                                         return ABSENT_NUMERIC;
122                               return cur_term->nums[te->ti];
123                     }
124           }
125           for (i = 0; i < cur_term->_nuserdefs; i++) {
126                     ud = &cur_term->_userdefs[i];
127                     if (ud->type == 'n' && strcmp(ud->id, id) == 0) {
128                               if (!VALID_NUMERIC(ud->num))
129                                         return ABSENT_NUMERIC;
130                               return ud->num;
131                     }
132           }
133           return -1;
134 }
135 
136 char *
tgetstr(const char * id2,char ** area)137 tgetstr(const char *id2, char **area)
138 {
139           uint32_t ind;
140           size_t i;
141           TERMUSERDEF *ud;
142           const char *str;
143           const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
144 
145           if (cur_term == NULL)
146                     return NULL;
147 
148           str = NULL;
149           ind = _t_strhash((const unsigned char *)id, strlen(id));
150           if (ind < __arraycount(_ti_cap_strids)) {
151                     if (strcmp(id, _ti_cap_strids[ind].id) == 0) {
152                               str = cur_term->strs[_ti_cap_strids[ind].ti];
153                               if (str == NULL)
154                                         return NULL;
155                     }
156           }
157           if (str != NULL)
158                     for (i = 0; i < cur_term->_nuserdefs; i++) {
159                               ud = &cur_term->_userdefs[i];
160                               if (ud->type == 's' && strcmp(ud->id, id) == 0)
161                                         str = ud->str;
162                     }
163 
164           /* XXX: FIXME
165            * We should fix sgr0(me) as it has a slightly different meaning
166            * for termcap. */
167 
168           if (str != NULL && area != NULL && *area != NULL) {
169                     char *s;
170                     s = *area;
171                     strcpy(*area, str);
172                     *area += strlen(*area) + 1;
173                     return s;
174           }
175 
176           return __UNCONST(str);
177 }
178 
179 char *
tgoto(const char * cm,int destcol,int destline)180 tgoto(const char *cm, int destcol, int destline)
181 {
182           _DIAGASSERT(cm != NULL);
183           return tiparm(cm, destline, destcol);
184 }
185 
186 #ifdef TERMINFO_COMPILE
187 static const char *
flagname(const char * key)188 flagname(const char *key)
189 {
190           uint32_t idx;
191 
192           idx = _t_flaghash((const unsigned char *)key, strlen(key));
193           if (idx < __arraycount(_ti_cap_flagids) &&
194               strcmp(key, _ti_cap_flagids[idx].id) == 0)
195                     return _ti_flagid(_ti_cap_flagids[idx].ti);
196           return key;
197 }
198 
199 static const char *
numname(const char * key)200 numname(const char *key)
201 {
202           uint32_t idx;
203 
204           idx = _t_numhash((const unsigned char *)key, strlen(key));
205           if (idx < __arraycount(_ti_cap_numids) &&
206               strcmp(key, _ti_cap_numids[idx].id) == 0)
207                     return _ti_numid(_ti_cap_numids[idx].ti);
208           return key;
209 }
210 
211 static const char *
strname(const char * key)212 strname(const char *key)
213 {
214           uint32_t idx;
215 
216           idx = _t_strhash((const unsigned char *)key, strlen(key));
217           if (idx < __arraycount(_ti_cap_strids) &&
218               strcmp(key, _ti_cap_strids[idx].id) == 0)
219                     return _ti_strid(_ti_cap_strids[idx].ti);
220 
221           if (strcmp(key, "tc") == 0)
222                     return "use";
223 
224           return key;
225 }
226 
227 /* Print a parameter if needed */
228 static size_t
printparam(char ** dst,char p,bool * nop)229 printparam(char **dst, char p, bool *nop)
230 {
231           if (*nop) {
232                     *nop = false;
233                     return 0;
234           }
235 
236           *(*dst)++ = '%';
237           *(*dst)++ = 'p';
238           *(*dst)++ = '0' + p;
239           return 3;
240 }
241 
242 /* Convert a termcap character into terminfo equivalents */
243 static size_t
printchar(char ** dst,const char ** src)244 printchar(char **dst, const char **src)
245 {
246           char v;
247           size_t l;
248 
249           l = 4;
250           v = *++(*src);
251           if (v == '\\') {
252                     v = *++(*src);
253                     switch (v) {
254                     case '0':
255                     case '1':
256                     case '2':
257                     case '3':
258                               v = 0;
259                               while (isdigit((unsigned char) **src))
260                                         v = 8 * v + (*(*src)++ - '0');
261                               (*src)--;
262                               break;
263                     case '\0':
264                               v = '\\';
265                               break;
266                     }
267           } else if (v == '^')
268                     v = *++(*src) & 0x1f;
269           *(*dst)++ = '%';
270           if (isgraph((unsigned char )v) &&
271               v != ',' && v != '\'' && v != '\\' && v != ':')
272           {
273                     *(*dst)++ = '\'';
274                     *(*dst)++ = v;
275                     *(*dst)++ = '\'';
276           } else {
277                     *(*dst)++ = '{';
278                     if (v > 99) {
279                               *(*dst)++ = '0'+ v / 100;
280                               l++;
281                     }
282                     if (v > 9) {
283                               *(*dst)++ = '0' + ((int) (v / 10)) % 10;
284                               l++;
285                     }
286                     *(*dst)++ = '0' + v % 10;
287                     *(*dst)++ = '}';
288           }
289           return l;
290 }
291 
292 /* Convert termcap commands into terminfo commands */
293 static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+";
294 static const char fmtD[] = "%p0%p0%{2}%*%-";
295 static const char fmtIf[] = "%p0%p0%?";
296 static const char fmtThen[] = "%>%t";
297 static const char fmtElse[] = "%+%;";
298 
299 static char *
strval(const char * val)300 strval(const char *val)
301 {
302           char *info, *ip, c, p;
303           const char *ps, *pe;
304           bool nop;
305           size_t len, l;
306 
307           len = 1024; /* no single string should be bigger */
308           info = ip = malloc(len);
309           if (info == NULL)
310                     return 0;
311 
312           /* Move the = */
313           *ip++ = *val++;
314 
315           /* Set ps and pe to point to the start and end of the padding */
316           if (isdigit((unsigned char)*val)) {
317                     for (ps = pe = val;
318                          isdigit((unsigned char)*val) || *val == '.';
319                          val++)
320                               pe++;
321                     if (*val == '*') {
322                               val++;
323                               pe++;
324                     }
325           } else
326                     ps = pe  = NULL;
327 
328           nop = false;
329           l = 0;
330           p = 1;
331           for (; *val != '\0'; val++) {
332                     if (l + 2 > len)
333                               goto elen;
334                     if (*val != '%') {
335                               if (*val == ',') {
336                                         if (l + 3 > len)
337                                                   goto elen;
338                                         *ip++ = '\\';
339                                         l++;
340                               }
341                               *ip++ = *val;
342                               l++;
343                               continue;
344                     }
345                     switch (c = *++(val)) {
346                     case 'B':
347                               if (l + sizeof(fmtB) > len)
348                                         goto elen;
349                               memcpy(ip, fmtB, sizeof(fmtB) - 1);
350                               /* Replace the embedded parameters with real ones */
351                               ip[2] += p;
352                               ip[19] += p;
353                               ip += sizeof(fmtB) - 1;
354                               l += sizeof(fmtB) - 1;
355                               nop = true;
356                               continue;
357                     case 'D':
358                               if (l + sizeof(fmtD) > len)
359                                         goto elen;
360                               memcpy(ip, fmtD, sizeof(fmtD) - 1);
361                               /* Replace the embedded parameters with real ones */
362                               ip[2] += p;
363                               ip[5] += p;
364                               ip += sizeof(fmtD) - 1;
365                               l += sizeof(fmtD) - 1;
366                               nop = true;
367                               continue;
368                     case 'r':
369                               /* non op as switched below */
370                               break;
371                     case '2': /* FALLTHROUGH */
372                     case '3': /* FALLTHROUGH */
373                     case 'd':
374                               if (l + 7 > len)
375                                         goto elen;
376                               l += printparam(&ip, p, &nop);
377                               *ip++ = '%';
378                               if (c != 'd') {
379                                         *ip++ = c;
380                                         l++;
381                               }
382                               *ip++ = 'd';
383                               l += 2;
384                               break;
385                     case '+':
386                               if (l + 13 > len)
387                                         goto elen;
388                               l += printparam(&ip, p, &nop);
389                               l += printchar(&ip, &val);
390                               *ip++ = '%';
391                               *ip++ = c;
392                               *ip++ = '%';
393                               *ip++ = 'c';
394                               l += 7;
395                               break;
396                     case '>':
397                               if (l + sizeof(fmtIf) + sizeof(fmtThen) +
398                                   sizeof(fmtElse) + (6 * 2) > len)
399                                         goto elen;
400 
401                               memcpy(ip, fmtIf, sizeof(fmtIf) - 1);
402                               /* Replace the embedded parameters with real ones */
403                               ip[2] += p;
404                               ip[5] += p;
405                               ip += sizeof(fmtIf) - 1;
406                               l += sizeof(fmtIf) - 1;
407                               l += printchar(&ip, &val);
408                               memcpy(ip, fmtThen, sizeof(fmtThen) - 1);
409                               ip += sizeof(fmtThen) - 1;
410                               l += sizeof(fmtThen) - 1;
411                               l += printchar(&ip, &val);
412                               memcpy(ip, fmtElse, sizeof(fmtElse) - 1);
413                               ip += sizeof(fmtElse) - 1;
414                               l += sizeof(fmtElse) - 1;
415                               l += 16;
416                               nop = true;
417                               continue;
418                     case '.':
419                               if (l + 6 > len)
420                                         goto elen;
421                               l += printparam(&ip, p, &nop);
422                               *ip++ = '%';
423                               *ip++ = 'c';
424                               l += 2;
425                               break;
426                     default:
427                               /* Hope it matches a terminfo command. */
428                               *ip++ = '%';
429                               *ip++ = c;
430                               l += 2;
431                               if (c == 'i')
432                                         continue;
433                               break;
434                     }
435                     /* Swap p1 and p2 */
436                     p = 3 - p;
437           }
438 
439           /* \E\ is valid termcap.
440            * We need to escape the final \ for terminfo. */
441           if (l > 2 && info[l - 1] == '\\' &&
442               (info[l - 2] != '\\' && info[l - 2] != '^'))
443           {
444                     if (l + 1 > len)
445                               goto elen;
446                     *ip++ = '\\';
447           }
448 
449           /* Add our padding at the end. */
450           if (ps != NULL) {
451                     size_t n = (size_t)(pe - ps);
452                     if (l + n + 4 > len)
453                               goto elen;
454                     *ip++ = '$';
455                     *ip++ = '<';
456                     strncpy(ip, ps, n);
457                     ip += n;
458                     *ip++ = '/';
459                     *ip++ = '>';
460           }
461 
462           *ip = '\0';
463           return info;
464 
465 elen:
466           free(info);
467           errno = ENOMEM;
468           return NULL;
469 }
470 
471 typedef struct {
472           const char *name;
473           const char *cap;
474 } DEF_INFO;
475 
476 static DEF_INFO def_infos[] = {
477           { "bel",  "^G" },
478           { "cr",             "^M" },
479           { "cud1", "^J" },
480           { "ht",             "^I" },
481           { "ind",  "^J" },
482           { "kbs",  "^H" },
483           { "kcub1",          "^H" },
484           { "kcud1",          "^J" },
485           { "nel",  "^M^J" }
486 };
487 
488 char *
captoinfo(char * cap)489 captoinfo(char *cap)
490 {
491           char *info, *ip, *token, *val, *p, tok[3];
492           const char *name;
493           size_t len, lp, nl, vl, rl;
494           int defs[__arraycount(def_infos)], fv;
495 
496           _DIAGASSERT(cap != NULL);
497 
498           len = strlen(cap) * 2;
499           len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */
500           info = ip = malloc(len);
501           if (info == NULL)
502                     return NULL;
503 
504           memset(defs, 0, sizeof(defs));
505           lp = 0;
506           tok[2] = '\0';
507           for (token = _ti_get_token(&cap, ':');
508                token != NULL;
509                token = _ti_get_token(&cap, ':'))
510           {
511                     if (token[0] == '\0')
512                               continue;
513                     name = token;
514                     val = p = NULL;
515                     fv = 0;
516                     nl = 0;
517                     if (token[1] != '\0') {
518                               tok[0] = token[0];
519                               tok[1] = token[1];
520                               nl = 1;
521                               if (token[2] == '\0') {
522                                         name = flagname(tok);
523                                         val = NULL;
524                               } else if (token[2] == '#') {
525                                         name = numname(tok);
526                                         val = token + 2;
527                               } else if (token[2] == '=') {
528                                         name = strname(tok);
529                                         val = strval(token + 2);
530                                         fv = 1;
531                               } else
532                                         nl = 0;
533                     }
534                     /* If not matched we may need to convert padding still. */
535                     if (nl == 0) {
536                               p = strchr(name, '=');
537                               if (p != NULL) {
538                                         val = strval(p);
539                                         *p = '\0';
540                                         fv = 1;
541                               }
542                     }
543 
544                     /* See if this sets a default. */
545                     for (nl = 0; nl < __arraycount(def_infos); nl++) {
546                               if (strcmp(name, def_infos[nl].name) == 0) {
547                                         defs[nl] = 1;
548                                         break;
549                               }
550                     }
551 
552                     nl = strlen(name);
553                     if (val == NULL)
554                               vl = 0;
555                     else
556                               vl = strlen(val);
557                     rl = nl + vl + 3; /* , \0 */
558 
559                     if (lp + rl > len) {
560                               if (rl < 256)
561                                         len += 256;
562                               else
563                                         len += rl;
564                               p = realloc(info, len);
565                               if (p == NULL) {
566                                         if (fv == 1)
567                                                   free(val);
568                                         return NULL;
569                               }
570                               info = p;
571                     }
572 
573                     if (ip != info) {
574                               *ip++ = ',';
575                               *ip++ = ' ';
576                     }
577 
578                     strcpy(ip, name);
579                     ip += nl;
580                     if (val != NULL) {
581                               strcpy(ip, val);
582                               ip += vl;
583                               if (fv == 1)
584                                         free(val);
585                     }
586           }
587 
588           /* Add any defaults not set above. */
589           for (nl = 0; nl < __arraycount(def_infos); nl++) {
590                     if (defs[nl] == 0) {
591                               *ip++ = ',';
592                               *ip++ = ' ';
593                               strcpy(ip, def_infos[nl].name);
594                               ip += strlen(def_infos[nl].name);
595                               *ip++ = '=';
596                               strcpy(ip, def_infos[nl].cap);
597                               ip += strlen(def_infos[nl].cap);
598                     }
599           }
600 
601           *ip = '\0';
602           return info;
603 }
604 #endif
605