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