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