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