1 /* $NetBSD: term.c,v 1.19 2015/11/26 01:03:22 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2009, 2010, 2011 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: term.c,v 1.19 2015/11/26 01:03:22 christos Exp $"); 32 33 #include <sys/stat.h> 34 35 #include <assert.h> 36 #include <cdbr.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <limits.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <term_private.h> 45 #include <term.h> 46 47 #define _PATH_TERMINFO "/usr/share/misc/terminfo" 48 49 static char database[PATH_MAX]; 50 static char pathbuf[PATH_MAX]; 51 const char *_ti_database; 52 53 /* Include a generated list of pre-compiled terminfo descriptions. */ 54 #include "compiled_terms.c" 55 56 static int 57 allocset(void *pp, int init, size_t nelem, size_t elemsize) 58 { 59 void **p = pp; 60 if (*p) { 61 memset(*p, init, nelem * elemsize); 62 return 0; 63 } 64 65 if ((*p = calloc(nelem, elemsize)) == NULL) 66 return -1; 67 68 if (init != 0) 69 memset(*p, init, nelem * elemsize); 70 return 0; 71 } 72 73 static int 74 _ti_readterm(TERMINAL *term, const char *cap, size_t caplen, int flags) 75 { 76 uint8_t ver; 77 uint16_t ind, num; 78 size_t len; 79 TERMUSERDEF *ud; 80 81 ver = *cap++; 82 /* Only read version 1 structures */ 83 if (ver != 1) { 84 errno = EINVAL; 85 return -1; 86 } 87 88 89 if (allocset(&term->flags, 0, TIFLAGMAX + 1, sizeof(*term->flags)) == -1) 90 return -1; 91 92 if (allocset(&term->nums, -1, TINUMMAX + 1, sizeof(*term->nums)) == -1) 93 return -1; 94 95 if (allocset(&term->strs, 0, TISTRMAX + 1, sizeof(*term->strs)) == -1) 96 return -1; 97 98 if (term->_arealen != caplen) { 99 term->_arealen = caplen; 100 term->_area = realloc(term->_area, term->_arealen); 101 if (term->_area == NULL) 102 return -1; 103 } 104 memcpy(term->_area, cap, term->_arealen); 105 106 cap = term->_area; 107 len = le16dec(cap); 108 cap += sizeof(uint16_t); 109 term->name = cap; 110 cap += len; 111 len = le16dec(cap); 112 cap += sizeof(uint16_t); 113 if (len == 0) 114 term->_alias = NULL; 115 else { 116 term->_alias = cap; 117 cap += len; 118 } 119 len = le16dec(cap); 120 cap += sizeof(uint16_t); 121 if (len == 0) 122 term->desc = NULL; 123 else { 124 term->desc = cap; 125 cap += len; 126 } 127 128 num = le16dec(cap); 129 cap += sizeof(uint16_t); 130 if (num != 0) { 131 num = le16dec(cap); 132 cap += sizeof(uint16_t); 133 for (; num != 0; num--) { 134 ind = le16dec(cap); 135 cap += sizeof(uint16_t); 136 term->flags[ind] = *cap++; 137 if (flags == 0 && !VALID_BOOLEAN(term->flags[ind])) 138 term->flags[ind] = 0; 139 } 140 } 141 142 num = le16dec(cap); 143 cap += sizeof(uint16_t); 144 if (num != 0) { 145 num = le16dec(cap); 146 cap += sizeof(uint16_t); 147 for (; num != 0; num--) { 148 ind = le16dec(cap); 149 cap += sizeof(uint16_t); 150 term->nums[ind] = le16dec(cap); 151 if (flags == 0 && !VALID_NUMERIC(term->nums[ind])) 152 term->nums[ind] = ABSENT_NUMERIC; 153 cap += sizeof(uint16_t); 154 } 155 } 156 157 num = le16dec(cap); 158 cap += sizeof(uint16_t); 159 if (num != 0) { 160 num = le16dec(cap); 161 cap += sizeof(uint16_t); 162 for (; num != 0; num--) { 163 ind = le16dec(cap); 164 cap += sizeof(uint16_t); 165 len = le16dec(cap); 166 cap += sizeof(uint16_t); 167 if (len > 0) 168 term->strs[ind] = cap; 169 else if (flags == 0) 170 term->strs[ind] = ABSENT_STRING; 171 else 172 term->strs[ind] = CANCELLED_STRING; 173 cap += len; 174 } 175 } 176 177 num = le16dec(cap); 178 cap += sizeof(uint16_t); 179 if (num != 0) { 180 num = le16dec(cap); 181 cap += sizeof(uint16_t); 182 if (num != term->_nuserdefs) { 183 free(term->_userdefs); 184 term->_userdefs = NULL; 185 term->_nuserdefs = num; 186 } 187 if (allocset(&term->_userdefs, 0, term->_nuserdefs, 188 sizeof(*term->_userdefs)) == -1) 189 return -1; 190 for (num = 0; num < term->_nuserdefs; num++) { 191 ud = &term->_userdefs[num]; 192 len = le16dec(cap); 193 cap += sizeof(uint16_t); 194 ud->id = cap; 195 cap += len; 196 ud->type = *cap++; 197 switch (ud->type) { 198 case 'f': 199 ud->flag = *cap++; 200 if (flags == 0 && 201 !VALID_BOOLEAN(ud->flag)) 202 ud->flag = 0; 203 ud->num = ABSENT_NUMERIC; 204 ud->str = ABSENT_STRING; 205 break; 206 case 'n': 207 ud->flag = ABSENT_BOOLEAN; 208 ud->num = le16dec(cap); 209 if (flags == 0 && 210 !VALID_NUMERIC(ud->num)) 211 ud->num = ABSENT_NUMERIC; 212 ud->str = ABSENT_STRING; 213 cap += sizeof(uint16_t); 214 break; 215 case 's': 216 ud->flag = ABSENT_BOOLEAN; 217 ud->num = ABSENT_NUMERIC; 218 len = le16dec(cap); 219 cap += sizeof(uint16_t); 220 if (len > 0) 221 ud->str = cap; 222 else if (flags == 0) 223 ud->str = ABSENT_STRING; 224 else 225 ud->str = CANCELLED_STRING; 226 cap += len; 227 break; 228 default: 229 errno = EINVAL; 230 return -1; 231 } 232 } 233 } else { 234 term->_nuserdefs = 0; 235 if (term->_userdefs) { 236 free(term->_userdefs); 237 term->_userdefs = NULL; 238 } 239 } 240 241 return 1; 242 } 243 244 static int 245 _ti_dbgetterm(TERMINAL *term, const char *path, const char *name, int flags) 246 { 247 struct cdbr *db; 248 const void *data; 249 char *db_name; 250 const uint8_t *data8; 251 size_t len, klen; 252 int r; 253 254 if (asprintf(&db_name, "%s.cdb", path) < 0) 255 return -1; 256 257 db = cdbr_open(db_name, CDBR_DEFAULT); 258 free(db_name); 259 if (db == NULL) 260 return -1; 261 262 klen = strlen(name) + 1; 263 if (cdbr_find(db, name, klen, &data, &len) == -1) 264 goto fail; 265 data8 = data; 266 if (len == 0) 267 goto fail; 268 /* Check for alias first, fall through to processing normal entries. */ 269 if (data8[0] == 2) { 270 if (klen + 7 > len || le16dec(data8 + 5) != klen) 271 goto fail; 272 if (memcmp(data8 + 7, name, klen)) 273 goto fail; 274 if (cdbr_get(db, le32dec(data8 + 1), &data, &len)) 275 goto fail; 276 data8 = data; 277 if (data8[0] != 1) 278 goto fail; 279 } else if (data8[0] != 1) 280 goto fail; 281 else if (klen + 3 >= len || le16dec(data8 + 1) != klen) 282 goto fail; 283 else if (memcmp(data8 + 3, name, klen)) 284 goto fail; 285 286 strlcpy(database, path, sizeof(database)); 287 _ti_database = database; 288 289 r = _ti_readterm(term, data, len, flags); 290 cdbr_close(db); 291 return r; 292 293 fail: 294 cdbr_close(db); 295 return 0; 296 } 297 298 static int 299 _ti_dbgettermp(TERMINAL *term, const char *path, const char *name, int flags) 300 { 301 const char *p; 302 size_t l; 303 int r, e; 304 305 e = -1; 306 r = 0; 307 do { 308 for (p = path; *path != '\0' && *path != ':'; path++) 309 continue; 310 l = path - p; 311 if (l != 0 && l + 1 < sizeof(pathbuf)) { 312 memcpy(pathbuf, p, l); 313 pathbuf[l] = '\0'; 314 r = _ti_dbgetterm(term, pathbuf, name, flags); 315 if (r == 1) 316 return 1; 317 if (r == 0) 318 e = 0; 319 } 320 } while (*path++ == ':'); 321 return e; 322 } 323 324 static int 325 ticcmp(const TIC *tic, const char *name) 326 { 327 char *alias, *s; 328 size_t len, l; 329 330 if (strcmp(tic->name, name) == 0) 331 return 0; 332 if (tic->alias == NULL) 333 return -1; 334 335 len = strlen(name); 336 alias = tic->alias; 337 while (*alias != '\0') { 338 s = strchr(alias, '|'); 339 if (s == NULL) 340 l = strlen(alias); 341 else 342 l = s - alias; 343 if (len == l && memcmp(alias, name, l) == 0) 344 return 0; 345 if (s == NULL) 346 break; 347 alias = s + 1; 348 } 349 return 1; 350 } 351 352 static int 353 _ti_findterm(TERMINAL *term, const char *name, int flags) 354 { 355 int r; 356 char *c, *e, h[PATH_MAX]; 357 TIC *tic; 358 uint8_t *f; 359 ssize_t len; 360 361 _DIAGASSERT(term != NULL); 362 _DIAGASSERT(name != NULL); 363 364 database[0] = '\0'; 365 _ti_database = NULL; 366 r = 0; 367 368 if ((e = getenv("TERMINFO")) != NULL && *e != '\0') 369 if (e[0] == '/') 370 return _ti_dbgetterm(term, e, name, flags); 371 372 c = NULL; 373 if (e == NULL && (c = getenv("TERMCAP")) != NULL) { 374 if (*c != '\0' && *c != '/') { 375 c = strdup(c); 376 if (c != NULL) { 377 e = captoinfo(c); 378 free(c); 379 } 380 } 381 } 382 383 if (e != NULL) { 384 if (c == NULL) 385 e = strdup(e); /* So we don't destroy env */ 386 if (e == NULL) 387 tic = NULL; 388 else { 389 tic = _ti_compile(e, TIC_WARNING | 390 TIC_ALIAS | TIC_DESCRIPTION | TIC_EXTRA); 391 free(e); 392 } 393 if (tic != NULL && ticcmp(tic, name) == 0) { 394 len = _ti_flatten(&f, tic); 395 if (len != -1) { 396 r = _ti_readterm(term, (char *)f, (size_t)len, 397 flags); 398 free(f); 399 } 400 } 401 _ti_freetic(tic); 402 if (r == 1) { 403 if (c == NULL) 404 _ti_database = "$TERMINFO"; 405 else 406 _ti_database = "$TERMCAP"; 407 return r; 408 } 409 } 410 411 if ((e = getenv("TERMINFO_DIRS")) != NULL) 412 return _ti_dbgettermp(term, e, name, flags); 413 414 if ((e = getenv("HOME")) != NULL) { 415 snprintf(h, sizeof(h), "%s/.terminfo", e); 416 r = _ti_dbgetterm(term, h, name, flags); 417 } 418 if (r != 1) 419 r = _ti_dbgettermp(term, _PATH_TERMINFO, name, flags); 420 421 return r; 422 423 } 424 425 int 426 _ti_getterm(TERMINAL *term, const char *name, int flags) 427 { 428 int r; 429 size_t i; 430 const struct compiled_term *t; 431 432 r = _ti_findterm(term, name, flags); 433 if (r == 1) 434 return r; 435 436 for (i = 0; i < __arraycount(compiled_terms); i++) { 437 t = &compiled_terms[i]; 438 if (strcmp(name, t->name) == 0) { 439 r = _ti_readterm(term, t->cap, t->caplen, flags); 440 break; 441 } 442 } 443 444 return r; 445 } 446