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 48char *UP; 49char *BC; 50 51/* ARGSUSED */ 52int 53tgetent(__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 80int 81tgetflag(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 104int 105tgetnum(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 136char * 137tgetstr(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 179char * 180tgoto(const char *cm, int destcol, int destline) 181{ 182 _DIAGASSERT(cm != NULL); 183 return tiparm(cm, destline, destcol); 184} 185 186#ifdef TERMINFO_COMPILE 187static const char * 188flagname(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 199static const char * 200numname(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 211static const char * 212strname(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 */ 228static size_t 229printparam(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 */ 243static size_t 244printchar(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 */ 293static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+"; 294static const char fmtD[] = "%p0%p0%{2}%*%-"; 295static const char fmtIf[] = "%p0%p0%?"; 296static const char fmtThen[] = "%>%t"; 297static const char fmtElse[] = "%+%;"; 298 299static char * 300strval(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 465elen: 466 free(info); 467 errno = ENOMEM; 468 return NULL; 469} 470 471typedef struct { 472 const char *name; 473 const char *cap; 474} DEF_INFO; 475 476static 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 488char * 489captoinfo(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