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