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