1 /* $NetBSD: termcap.c,v 1.10 2010/10/12 12:49:27 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.10 2010/10/12 12:49:27 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 *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; 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 /* FALLTHROUGH */ 284 case 'r': 285 p = 3 - p; 286 break; 287 default: 288 /* Hope it matches a terminfo command. */ 289 *ip++ = '%'; 290 *ip++ = c; 291 l += 2; 292 break; 293 } 294 } 295 296 /* \E\ is valid termcap. 297 * We need to escape the final \ for terminfo. */ 298 if (l > 2 && info[l - 1] == '\\' && 299 (info[l - 2] != '\\' && info[l - 2] != '^')) 300 { 301 if (l + 1 > len) 302 goto elen; 303 *ip++ = '\\'; 304 } 305 306 /* Add our padding at the end. */ 307 if (ps != NULL) { 308 size_t n = pe - ps; 309 if (l + n + 4 > len) 310 goto elen; 311 *ip++ = '$'; 312 *ip++ = '<'; 313 strncpy(ip, ps, n); 314 ip += n; 315 *ip++ = '/'; 316 *ip++ = '>'; 317 } 318 319 *ip = '\0'; 320 return info; 321 322 elen: 323 free(info); 324 errno = ENOMEM; 325 return NULL; 326 } 327 328 typedef struct { 329 const char *name; 330 const char *cap; 331 } DEF_INFO; 332 333 static DEF_INFO def_infos[] = { 334 { "bel", "^G" }, 335 { "cr", "^M" }, 336 { "cud1", "^J" }, 337 { "ht", "^I" }, 338 { "ind", "^J" }, 339 { "kbs", "^H" }, 340 { "kcub1", "^H" }, 341 { "kcud1", "^J" }, 342 { "nel", "^M^J" } 343 }; 344 345 char * 346 captoinfo(char *cap) 347 { 348 char *info, *ip, *token, *val, *p, tok[3]; 349 const char *name; 350 size_t len, lp, nl, vl, rl; 351 int defs[__arraycount(def_infos)], fv; 352 353 _DIAGASSERT(cap != NULL); 354 355 len = strlen(cap) * 2; 356 len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */ 357 info = ip = malloc(len); 358 if (info == NULL) 359 return NULL; 360 361 memset(defs, 0, sizeof(defs)); 362 lp = 0; 363 tok[2] = '\0'; 364 for (token = _ti_get_token(&cap, ':'); 365 token != NULL; 366 token = _ti_get_token(&cap, ':')) 367 { 368 if (token[0] == '\0') 369 continue; 370 name = token; 371 val = p = NULL; 372 fv = nl = 0; 373 if (token[1] != '\0') { 374 tok[0] = token[0]; 375 tok[1] = token[1]; 376 nl = 1; 377 if (token[2] == '\0') { 378 name = flagname(tok); 379 val = NULL; 380 } else if (token[2] == '#') { 381 name = numname(tok); 382 val = token + 2; 383 } else if (token[2] == '=') { 384 name = strname(tok); 385 val = strval(token + 2); 386 fv = 1; 387 } else 388 nl = 0; 389 } 390 /* If not matched we may need to convert padding still. */ 391 if (nl == 0) { 392 p = strchr(name, '='); 393 if (p != NULL) { 394 val = strval(p); 395 *p = '\0'; 396 fv = 1; 397 } 398 } 399 400 /* See if this sets a default. */ 401 for (nl = 0; nl < __arraycount(def_infos); nl++) { 402 if (strcmp(name, def_infos[nl].name) == 0) { 403 defs[nl] = 1; 404 break; 405 } 406 } 407 408 nl = strlen(name); 409 if (val == NULL) 410 vl = 0; 411 else 412 vl = strlen(val); 413 rl = nl + vl + 3; /* , \0 */ 414 415 if (lp + rl > len) { 416 if (rl < 256) 417 len += 256; 418 else 419 len += rl; 420 p = realloc(info, len); 421 if (p == NULL) 422 return NULL; 423 info = p; 424 } 425 426 if (ip != info) { 427 *ip++ = ','; 428 *ip++ = ' '; 429 } 430 431 strcpy(ip, name); 432 ip += nl; 433 if (val != NULL) { 434 strcpy(ip, val); 435 ip += vl; 436 if (fv == 1) 437 free(val); 438 } 439 } 440 441 /* Add any defaults not set above. */ 442 for (nl = 0; nl < __arraycount(def_infos); nl++) { 443 if (defs[nl] == 0) { 444 *ip++ = ','; 445 *ip++ = ' '; 446 strcpy(ip, def_infos[nl].name); 447 ip += strlen(def_infos[nl].name); 448 *ip++ = '='; 449 strcpy(ip, def_infos[nl].cap); 450 ip += strlen(def_infos[nl].cap); 451 } 452 } 453 454 *ip = '\0'; 455 return info; 456 } 457 458