1 /* $NetBSD: termcap.c,v 1.8 2010/07/04 07:30:33 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.8 2010/07/04 07:30:33 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 182 _DIAGASSERT(cm != NULL); 183 return vtparm(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 /* We don't currently map %> %B %D 227 * That means no conversion for regent100, hz1500, act4, act5, mime terms. */ 228 static char * 229 strval(const char *val) 230 { 231 char *info, *ip, c; 232 const char *ps, *pe; 233 int p; 234 size_t len, l, n; 235 236 len = 1024; /* no single string should be bigger */ 237 info = ip = malloc(len); 238 if (info == NULL) 239 return 0; 240 241 /* Move the = */ 242 *ip++ = *val++; 243 244 /* Set ps and pe to point to the start and end of the padding */ 245 if (isdigit((unsigned char)*val)) { 246 for (ps = pe = val; 247 isdigit((unsigned char)*val) || *val == '.'; 248 val++) 249 pe++; 250 if (*val == '*') { 251 val++; 252 pe++; 253 } 254 } else 255 ps = pe = NULL; 256 257 l = 0; 258 p = 1; 259 for (; *val != '\0'; val++) { 260 if (l + 2 > len) 261 goto elen; 262 if (*val != '%') { 263 if (*val == ',') { 264 if (l + 3 > len) 265 goto elen; 266 *ip++ = '\\'; 267 l++; 268 } 269 *ip++ = *val; 270 l++; 271 continue; 272 } 273 switch (c = *(++val)) { 274 case 'd': 275 if (l + 6 > len) 276 goto elen; 277 *ip++ = '%'; 278 *ip++ = 'p'; 279 *ip++ = '0' + p; 280 *ip++ = '%'; 281 *ip++ = 'd'; 282 l += 5; 283 n += 5; 284 /* FALLTHROUGH */ 285 case 'r': 286 p = 3 - p; 287 break; 288 default: 289 /* Hope it matches a terminfo command. */ 290 *ip++ = '%'; 291 *ip++ = c; 292 l += 2; 293 break; 294 } 295 } 296 297 /* \E\ is valid termcap. 298 * We need to escape the final \ for terminfo. */ 299 if (l > 2 && info[l - 1] == '\\' && 300 (info[l - 2] != '\\' && info[l - 2] != '^')) 301 { 302 if (l + 1 > len) 303 goto elen; 304 *ip++ = '\\'; 305 } 306 307 /* Add our padding at the end. */ 308 if (ps != NULL) { 309 n = pe - ps; 310 if (l + n + 4 > len) 311 goto elen; 312 *ip++ = '$'; 313 *ip++ = '<'; 314 strncpy(ip, ps, n); 315 ip += n; 316 *ip++ = '/'; 317 *ip++ = '>'; 318 } 319 320 *ip = '\0'; 321 return info; 322 323 elen: 324 free(info); 325 errno = ENOMEM; 326 return NULL; 327 } 328 329 static struct def_info { 330 const char *name; 331 const char *cap; 332 } def_infos[] = { 333 { "bel", "^G" }, 334 { "cr", "^M" }, 335 { "cud1", "^J" }, 336 { "ht", "^I" }, 337 { "ind", "^J" }, 338 { "kbs", "^H" }, 339 { "kcub1", "^H" }, 340 { "kcud1", "^J" }, 341 { "nel", "^M^J" } 342 }; 343 344 char * 345 captoinfo(char *cap) 346 { 347 char *info, *ip, *token, *val, *p, tok[3]; 348 const char *name; 349 size_t len, lp, nl, vl, rl; 350 int defs[__arraycount(def_infos)], fv; 351 352 _DIAGASSERT(cap != NULL); 353 354 len = strlen(cap) * 2; 355 len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */ 356 info = ip = malloc(len); 357 if (info == NULL) 358 return NULL; 359 360 memset(defs, 0, sizeof(defs)); 361 lp = 0; 362 tok[2] = '\0'; 363 for (token = _ti_get_token(&cap, ':'); 364 token != NULL; 365 token = _ti_get_token(&cap, ':')) 366 { 367 if (token[0] == '\0') 368 continue; 369 name = token; 370 val = p = NULL; 371 fv = nl = 0; 372 if (token[1] != '\0') { 373 tok[0] = token[0]; 374 tok[1] = token[1]; 375 nl = 1; 376 if (token[2] == '\0') { 377 name = flagname(tok); 378 val = NULL; 379 } else if (token[2] == '#') { 380 name = numname(tok); 381 val = token + 2; 382 } else if (token[2] == '=') { 383 name = strname(tok); 384 val = strval(token + 2); 385 fv = 1; 386 } else 387 nl = 0; 388 } 389 /* If not matched we may need to convert padding still. */ 390 if (nl == 0) { 391 p = strchr(name, '='); 392 if (p != NULL) { 393 val = strval(p); 394 *p = '\0'; 395 fv = 1; 396 } 397 } 398 399 /* See if this sets a default. */ 400 for (nl = 0; nl < __arraycount(def_infos); nl++) { 401 if (strcmp(name, def_infos[nl].name) == 0) { 402 defs[nl] = 1; 403 break; 404 } 405 } 406 407 nl = strlen(name); 408 if (val == NULL) 409 vl = 0; 410 else 411 vl = strlen(val); 412 rl = nl + vl + 3; /* , \0 */ 413 414 if (lp + rl > len) { 415 if (rl < 256) 416 len += 256; 417 else 418 len += rl; 419 p = realloc(info, len); 420 if (p == NULL) 421 return NULL; 422 info = p; 423 } 424 425 if (ip != info) { 426 *ip++ = ','; 427 *ip++ = ' '; 428 } 429 430 strcpy(ip, name); 431 ip += nl; 432 if (val != NULL) { 433 strcpy(ip, val); 434 ip += vl; 435 if (fv == 1) 436 free(val); 437 } 438 } 439 440 /* Add any defaults not set above. */ 441 for (nl = 0; nl < __arraycount(def_infos); nl++) { 442 if (defs[nl] == 0) { 443 *ip++ = ','; 444 *ip++ = ' '; 445 strcpy(ip, def_infos[nl].name); 446 ip += strlen(def_infos[nl].name); 447 *ip++ = '='; 448 strcpy(ip, def_infos[nl].cap); 449 ip += strlen(def_infos[nl].cap); 450 } 451 } 452 453 *ip = '\0'; 454 return info; 455 } 456 457