1 /* $NetBSD: termcap.c,v 1.22 2017/05/04 09:42:23 roy 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.22 2017/05/04 09:42:23 roy 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 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 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 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 * 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: FXIXME 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 * 180 tgoto(const char *cm, int destcol, int destline) 181 { 182 _DIAGASSERT(cm != NULL); 183 return tiparm(cm, destline, destcol); 184 } 185 186 static const char * 187 flagname(const char *key) 188 { 189 uint32_t idx; 190 191 idx = _t_flaghash((const unsigned char *)key, strlen(key)); 192 if (idx < __arraycount(_ti_cap_flagids) && 193 strcmp(key, _ti_cap_flagids[idx].id) == 0) 194 return _ti_flagid(_ti_cap_flagids[idx].ti); 195 return key; 196 } 197 198 static const char * 199 numname(const char *key) 200 { 201 uint32_t idx; 202 203 idx = _t_numhash((const unsigned char *)key, strlen(key)); 204 if (idx < __arraycount(_ti_cap_numids) && 205 strcmp(key, _ti_cap_numids[idx].id) == 0) 206 return _ti_numid(_ti_cap_numids[idx].ti); 207 return key; 208 } 209 210 static const char * 211 strname(const char *key) 212 { 213 uint32_t idx; 214 215 idx = _t_strhash((const unsigned char *)key, strlen(key)); 216 if (idx < __arraycount(_ti_cap_strids) && 217 strcmp(key, _ti_cap_strids[idx].id) == 0) 218 return _ti_strid(_ti_cap_strids[idx].ti); 219 220 if (strcmp(key, "tc") == 0) 221 return "use"; 222 223 return key; 224 } 225 226 /* Print a parameter if needed */ 227 static size_t 228 printparam(char **dst, char p, bool *nop) 229 { 230 if (*nop) { 231 *nop = false; 232 return 0; 233 } 234 235 *(*dst)++ = '%'; 236 *(*dst)++ = 'p'; 237 *(*dst)++ = '0' + p; 238 return 3; 239 } 240 241 /* Convert a termcap character into terminfo equivalents */ 242 static size_t 243 printchar(char **dst, const char **src) 244 { 245 char v; 246 size_t l; 247 248 l = 4; 249 v = *++(*src); 250 if (v == '\\') { 251 v = *++(*src); 252 switch (v) { 253 case '0': 254 case '1': 255 case '2': 256 case '3': 257 v = 0; 258 while (isdigit((unsigned char) **src)) 259 v = 8 * v + (*(*src)++ - '0'); 260 (*src)--; 261 break; 262 case '\0': 263 v = '\\'; 264 break; 265 } 266 } else if (v == '^') 267 v = *++(*src) & 0x1f; 268 *(*dst)++ = '%'; 269 if (isgraph((unsigned char )v) && 270 v != ',' && v != '\'' && v != '\\' && v != ':') 271 { 272 *(*dst)++ = '\''; 273 *(*dst)++ = v; 274 *(*dst)++ = '\''; 275 } else { 276 *(*dst)++ = '{'; 277 if (v > 99) { 278 *(*dst)++ = '0'+ v / 100; 279 l++; 280 } 281 if (v > 9) { 282 *(*dst)++ = '0' + ((int) (v / 10)) % 10; 283 l++; 284 } 285 *(*dst)++ = '0' + v % 10; 286 *(*dst)++ = '}'; 287 } 288 return l; 289 } 290 291 /* Convert termcap commands into terminfo commands */ 292 static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+"; 293 static const char fmtD[] = "%p0%p0%{2}%*%-"; 294 static const char fmtIf[] = "%p0%p0%?"; 295 static const char fmtThen[] = "%>%t"; 296 static const char fmtElse[] = "%+%;"; 297 298 static char * 299 strval(const char *val) 300 { 301 char *info, *ip, c, p; 302 const char *ps, *pe; 303 bool nop; 304 size_t len, l; 305 306 len = 1024; /* no single string should be bigger */ 307 info = ip = malloc(len); 308 if (info == NULL) 309 return 0; 310 311 /* Move the = */ 312 *ip++ = *val++; 313 314 /* Set ps and pe to point to the start and end of the padding */ 315 if (isdigit((unsigned char)*val)) { 316 for (ps = pe = val; 317 isdigit((unsigned char)*val) || *val == '.'; 318 val++) 319 pe++; 320 if (*val == '*') { 321 val++; 322 pe++; 323 } 324 } else 325 ps = pe = NULL; 326 327 nop = false; 328 l = 0; 329 p = 1; 330 for (; *val != '\0'; val++) { 331 if (l + 2 > len) 332 goto elen; 333 if (*val != '%') { 334 if (*val == ',') { 335 if (l + 3 > len) 336 goto elen; 337 *ip++ = '\\'; 338 l++; 339 } 340 *ip++ = *val; 341 l++; 342 continue; 343 } 344 switch (c = *++(val)) { 345 case 'B': 346 if (l + sizeof(fmtB) > len) 347 goto elen; 348 memcpy(ip, fmtB, sizeof(fmtB) - 1); 349 /* Replace the embedded parameters with real ones */ 350 ip[2] += p; 351 ip[19] += p; 352 ip += sizeof(fmtB) - 1; 353 l += sizeof(fmtB) - 1; 354 nop = true; 355 continue; 356 case 'D': 357 if (l + sizeof(fmtD) > len) 358 goto elen; 359 memcpy(ip, fmtD, sizeof(fmtD) - 1); 360 /* Replace the embedded parameters with real ones */ 361 ip[2] += p; 362 ip[5] += p; 363 ip += sizeof(fmtD) - 1; 364 l += sizeof(fmtD) - 1; 365 nop = true; 366 continue; 367 case 'r': 368 /* non op as switched below */ 369 break; 370 case '2': /* FALLTHROUGH */ 371 case '3': /* FALLTHROUGH */ 372 case 'd': 373 if (l + 7 > len) 374 goto elen; 375 l += printparam(&ip, p, &nop); 376 *ip++ = '%'; 377 if (c != 'd') { 378 *ip++ = c; 379 l++; 380 } 381 *ip++ = 'd'; 382 l += 2; 383 break; 384 case '+': 385 if (l + 13 > len) 386 goto elen; 387 l += printparam(&ip, p, &nop); 388 l += printchar(&ip, &val); 389 *ip++ = '%'; 390 *ip++ = c; 391 *ip++ = '%'; 392 *ip++ = 'c'; 393 l += 7; 394 break; 395 case '>': 396 if (l + sizeof(fmtIf) + sizeof(fmtThen) + 397 sizeof(fmtElse) + (6 * 2) > len) 398 goto elen; 399 400 memcpy(ip, fmtIf, sizeof(fmtIf) - 1); 401 /* Replace the embedded parameters with real ones */ 402 ip[2] += p; 403 ip[5] += p; 404 ip += sizeof(fmtIf) - 1; 405 l += sizeof(fmtIf) - 1; 406 l += printchar(&ip, &val); 407 memcpy(ip, fmtThen, sizeof(fmtThen) - 1); 408 ip += sizeof(fmtThen) - 1; 409 l += sizeof(fmtThen) - 1; 410 l += printchar(&ip, &val); 411 memcpy(ip, fmtElse, sizeof(fmtElse) - 1); 412 ip += sizeof(fmtElse) - 1; 413 l += sizeof(fmtElse) - 1; 414 l += 16; 415 nop = true; 416 continue; 417 case '.': 418 if (l + 6 > len) 419 goto elen; 420 l += printparam(&ip, p, &nop); 421 *ip++ = '%'; 422 *ip++ = 'c'; 423 l += 2; 424 break; 425 default: 426 /* Hope it matches a terminfo command. */ 427 *ip++ = '%'; 428 *ip++ = c; 429 l += 2; 430 if (c == 'i') 431 continue; 432 break; 433 } 434 /* Swap p1 and p2 */ 435 p = 3 - p; 436 } 437 438 /* \E\ is valid termcap. 439 * We need to escape the final \ for terminfo. */ 440 if (l > 2 && info[l - 1] == '\\' && 441 (info[l - 2] != '\\' && info[l - 2] != '^')) 442 { 443 if (l + 1 > len) 444 goto elen; 445 *ip++ = '\\'; 446 } 447 448 /* Add our padding at the end. */ 449 if (ps != NULL) { 450 size_t n = (size_t)(pe - ps); 451 if (l + n + 4 > len) 452 goto elen; 453 *ip++ = '$'; 454 *ip++ = '<'; 455 strncpy(ip, ps, n); 456 ip += n; 457 *ip++ = '/'; 458 *ip++ = '>'; 459 } 460 461 *ip = '\0'; 462 return info; 463 464 elen: 465 free(info); 466 errno = ENOMEM; 467 return NULL; 468 } 469 470 typedef struct { 471 const char *name; 472 const char *cap; 473 } DEF_INFO; 474 475 static DEF_INFO def_infos[] = { 476 { "bel", "^G" }, 477 { "cr", "^M" }, 478 { "cud1", "^J" }, 479 { "ht", "^I" }, 480 { "ind", "^J" }, 481 { "kbs", "^H" }, 482 { "kcub1", "^H" }, 483 { "kcud1", "^J" }, 484 { "nel", "^M^J" } 485 }; 486 487 char * 488 captoinfo(char *cap) 489 { 490 char *info, *ip, *token, *val, *p, tok[3]; 491 const char *name; 492 size_t len, lp, nl, vl, rl; 493 int defs[__arraycount(def_infos)], fv; 494 495 _DIAGASSERT(cap != NULL); 496 497 len = strlen(cap) * 2; 498 len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */ 499 info = ip = malloc(len); 500 if (info == NULL) 501 return NULL; 502 503 memset(defs, 0, sizeof(defs)); 504 lp = 0; 505 tok[2] = '\0'; 506 for (token = _ti_get_token(&cap, ':'); 507 token != NULL; 508 token = _ti_get_token(&cap, ':')) 509 { 510 if (token[0] == '\0') 511 continue; 512 name = token; 513 val = p = NULL; 514 fv = nl = 0; 515 if (token[1] != '\0') { 516 tok[0] = token[0]; 517 tok[1] = token[1]; 518 nl = 1; 519 if (token[2] == '\0') { 520 name = flagname(tok); 521 val = NULL; 522 } else if (token[2] == '#') { 523 name = numname(tok); 524 val = token + 2; 525 } else if (token[2] == '=') { 526 name = strname(tok); 527 val = strval(token + 2); 528 fv = 1; 529 } else 530 nl = 0; 531 } 532 /* If not matched we may need to convert padding still. */ 533 if (nl == 0) { 534 p = strchr(name, '='); 535 if (p != NULL) { 536 val = strval(p); 537 *p = '\0'; 538 fv = 1; 539 } 540 } 541 542 /* See if this sets a default. */ 543 for (nl = 0; nl < __arraycount(def_infos); nl++) { 544 if (strcmp(name, def_infos[nl].name) == 0) { 545 defs[nl] = 1; 546 break; 547 } 548 } 549 550 nl = strlen(name); 551 if (val == NULL) 552 vl = 0; 553 else 554 vl = strlen(val); 555 rl = nl + vl + 3; /* , \0 */ 556 557 if (lp + rl > len) { 558 if (rl < 256) 559 len += 256; 560 else 561 len += rl; 562 p = realloc(info, len); 563 if (p == NULL) { 564 if (fv == 1) 565 free(val); 566 return NULL; 567 } 568 info = p; 569 } 570 571 if (ip != info) { 572 *ip++ = ','; 573 *ip++ = ' '; 574 } 575 576 strcpy(ip, name); 577 ip += nl; 578 if (val != NULL) { 579 strcpy(ip, val); 580 ip += vl; 581 if (fv == 1) 582 free(val); 583 } 584 } 585 586 /* Add any defaults not set above. */ 587 for (nl = 0; nl < __arraycount(def_infos); nl++) { 588 if (defs[nl] == 0) { 589 *ip++ = ','; 590 *ip++ = ' '; 591 strcpy(ip, def_infos[nl].name); 592 ip += strlen(def_infos[nl].name); 593 *ip++ = '='; 594 strcpy(ip, def_infos[nl].cap); 595 ip += strlen(def_infos[nl].cap); 596 } 597 } 598 599 *ip = '\0'; 600 return info; 601 } 602 603