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