version 1.4, 2010/02/05 19:21:02 |
version 1.25, 2017/05/16 10:25:40 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
|
/* |
/* |
* Copyright (c) 2009 The NetBSD Foundation, Inc. |
* Copyright (c) 2009, 2010, 2011 The NetBSD Foundation, Inc. |
* |
* |
* This code is derived from software contributed to The NetBSD Foundation |
* This code is derived from software contributed to The NetBSD Foundation |
* by Roy Marples. |
* by Roy Marples. |
Line 33 __RCSID("$NetBSD$"); |
|
Line 33 __RCSID("$NetBSD$"); |
|
#include <sys/stat.h> |
#include <sys/stat.h> |
|
|
#include <assert.h> |
#include <assert.h> |
|
#include <cdbr.h> |
#include <ctype.h> |
#include <ctype.h> |
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
#include <fcntl.h> |
#include <limits.h> |
#include <limits.h> |
#include <ndbm.h> |
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <term_private.h> |
#include <term_private.h> |
#include <term.h> |
#include <term.h> |
|
|
#define TERMINFO_DIRS "/usr/share/misc/terminfo" |
#define _PATH_TERMINFO "/usr/share/misc/terminfo" |
#define TERMINFO_RESCUE "/rescue/terminfo" |
|
|
|
static char database[PATH_MAX]; |
static char __ti_database[PATH_MAX]; |
static char pathbuf[PATH_MAX]; |
|
const char *_ti_database; |
const char *_ti_database; |
|
|
|
/* Include a generated list of pre-compiled terminfo descriptions. */ |
|
#include "compiled_terms.c" |
|
|
static int |
static int |
_ti_readterm(TERMINAL *term, char *cap, size_t caplen, int flags) |
allocset(void *pp, int init, size_t nelem, size_t elemsize) |
{ |
{ |
uint8_t ver; |
void **p = pp; |
|
if (*p) { |
|
memset(*p, init, nelem * elemsize); |
|
return 0; |
|
} |
|
|
|
if ((*p = calloc(nelem, elemsize)) == NULL) |
|
return -1; |
|
|
|
if (init != 0) |
|
memset(*p, init, nelem * elemsize); |
|
return 0; |
|
} |
|
|
|
static int |
|
_ti_readterm(TERMINAL *term, const char *cap, size_t caplen, int flags) |
|
{ |
|
char ver; |
uint16_t ind, num; |
uint16_t ind, num; |
size_t len; |
size_t len; |
TERMUSERDEF *ud; |
TERMUSERDEF *ud; |
|
|
|
if (caplen == 0) |
|
goto out; |
ver = *cap++; |
ver = *cap++; |
/* Only read version 1 and 2 structures */ |
caplen--; |
if (ver != 1 && ver != 2) { |
/* Only read version 1 structures */ |
errno = EINVAL; |
if (ver != 1) |
|
goto out; |
|
|
|
if (allocset(&term->flags, 0, TIFLAGMAX+1, sizeof(*term->flags)) == -1) |
return -1; |
return -1; |
} |
|
|
|
term->flags = calloc(TIFLAGMAX + 1, sizeof(char)); |
if (allocset(&term->nums, -1, TINUMMAX+1, sizeof(*term->nums)) == -1) |
if (term->flags == NULL) |
return -1; |
goto err; |
|
term->nums = malloc((TINUMMAX + 1) * sizeof(short)); |
if (allocset(&term->strs, 0, TISTRMAX+1, sizeof(*term->strs)) == -1) |
if (term->nums == NULL) |
return -1; |
goto err; |
|
memset(term->nums, (short)-1, (TINUMMAX + 1) * sizeof(short)); |
if (term->_arealen != caplen) { |
term->strs = calloc(TISTRMAX + 1, sizeof(char *)); |
term->_arealen = caplen; |
if (term->strs == NULL) |
term->_area = realloc(term->_area, term->_arealen); |
goto err; |
if (term->_area == NULL) |
term->_area = malloc(caplen); |
return -1; |
if (term->_area == NULL) |
} |
goto err; |
memcpy(term->_area, cap, term->_arealen); |
memcpy(term->_area, cap, caplen); |
|
|
|
cap = term->_area; |
cap = term->_area; |
len = le16dec(cap); |
len = le16dec(cap); |
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
term->name = cap; |
term->name = cap; |
cap += len; |
cap += len; |
if (ver == 1) |
len = le16dec(cap); |
|
cap += sizeof(uint16_t); |
|
if (len == 0) |
term->_alias = NULL; |
term->_alias = NULL; |
else { |
else { |
len = le16dec(cap); |
term->_alias = cap; |
cap += sizeof(uint16_t); |
cap += len; |
if (len == 0) |
|
term->_alias = NULL; |
|
else { |
|
term->_alias = cap; |
|
cap += len; |
|
} |
|
} |
} |
len = le16dec(cap); |
len = le16dec(cap); |
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
term->desc = cap; |
if (len == 0) |
cap += len; |
term->desc = NULL; |
|
else { |
|
term->desc = cap; |
|
cap += len; |
|
} |
|
|
num = le16dec(cap); |
num = le16dec(cap); |
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
Line 116 _ti_readterm(TERMINAL *term, char *cap, |
|
Line 137 _ti_readterm(TERMINAL *term, char *cap, |
|
term->flags[ind] = 0; |
term->flags[ind] = 0; |
} |
} |
} |
} |
|
|
num = le16dec(cap); |
num = le16dec(cap); |
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
if (num != 0) { |
if (num != 0) { |
Line 125 _ti_readterm(TERMINAL *term, char *cap, |
|
Line 146 _ti_readterm(TERMINAL *term, char *cap, |
|
for (; num != 0; num--) { |
for (; num != 0; num--) { |
ind = le16dec(cap); |
ind = le16dec(cap); |
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
term->nums[ind] = le16dec(cap); |
term->nums[ind] = (short)le16dec(cap); |
if (flags == 0 && !VALID_NUMERIC(term->nums[ind])) |
if (flags == 0 && !VALID_NUMERIC(term->nums[ind])) |
term->nums[ind] = ABSENT_NUMERIC; |
term->nums[ind] = ABSENT_NUMERIC; |
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
} |
} |
} |
} |
|
|
num = le16dec(cap); |
num = le16dec(cap); |
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
if (num != 0) { |
if (num != 0) { |
Line 151 _ti_readterm(TERMINAL *term, char *cap, |
|
Line 172 _ti_readterm(TERMINAL *term, char *cap, |
|
cap += len; |
cap += len; |
} |
} |
} |
} |
|
|
num = le16dec(cap); |
num = le16dec(cap); |
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
if (num != 0) { |
if (num != 0) { |
term->_nuserdefs = le16dec(cap); |
num = le16dec(cap); |
term->_userdefs = malloc(sizeof(*term->_userdefs) * num); |
|
cap += sizeof(uint16_t); |
cap += sizeof(uint16_t); |
|
if (num != term->_nuserdefs) { |
|
free(term->_userdefs); |
|
term->_userdefs = NULL; |
|
term->_nuserdefs = num; |
|
} |
|
if (allocset(&term->_userdefs, 0, term->_nuserdefs, |
|
sizeof(*term->_userdefs)) == -1) |
|
return -1; |
for (num = 0; num < term->_nuserdefs; num++) { |
for (num = 0; num < term->_nuserdefs; num++) { |
ud = &term->_userdefs[num]; |
ud = &term->_userdefs[num]; |
len = le16dec(cap); |
len = le16dec(cap); |
Line 176 _ti_readterm(TERMINAL *term, char *cap, |
|
Line 204 _ti_readterm(TERMINAL *term, char *cap, |
|
break; |
break; |
case 'n': |
case 'n': |
ud->flag = ABSENT_BOOLEAN; |
ud->flag = ABSENT_BOOLEAN; |
ud->num = le16dec(cap); |
ud->num = (short)le16dec(cap); |
if (flags == 0 && |
if (flags == 0 && |
!VALID_NUMERIC(ud->num)) |
!VALID_NUMERIC(ud->num)) |
ud->num = ABSENT_NUMERIC; |
ud->num = ABSENT_NUMERIC; |
Line 197 _ti_readterm(TERMINAL *term, char *cap, |
|
Line 225 _ti_readterm(TERMINAL *term, char *cap, |
|
cap += len; |
cap += len; |
break; |
break; |
default: |
default: |
errno = EINVAL; |
goto out; |
goto err; |
|
} |
} |
} |
} |
|
} else { |
|
term->_nuserdefs = 0; |
|
if (term->_userdefs) { |
|
free(term->_userdefs); |
|
term->_userdefs = NULL; |
|
} |
} |
} |
|
|
return 1; |
return 1; |
|
out: |
err: |
errno = EINVAL; |
_ti_freeterm(term); |
|
return -1; |
return -1; |
} |
} |
|
|
static int |
static int |
|
_ti_checkname(const TERMINAL *term, const char *name) |
|
{ |
|
const char *a, *p; |
|
size_t name_len; |
|
|
|
/* Check terminal name matches. */ |
|
if (strcmp(name, term->name) == 0) |
|
return 1; |
|
|
|
/* Check terminal aliases match. */ |
|
name_len = strlen(name); |
|
for (a = term->_alias; a != NULL && *a != '\0'; a = p) { |
|
for (p = a; *p != '\0'; p++) { |
|
if (*p == '|') |
|
break; |
|
} |
|
if ((size_t)(p - a) == name_len && |
|
memcmp(name, a, name_len) == 0) |
|
return 1; |
|
} |
|
|
|
/* No match. */ |
|
return 0; |
|
} |
|
|
|
static int |
_ti_dbgetterm(TERMINAL *term, const char *path, const char *name, int flags) |
_ti_dbgetterm(TERMINAL *term, const char *path, const char *name, int flags) |
{ |
{ |
DBM *db; |
struct cdbr *db; |
datum dt; |
const void *data; |
char *p; |
const uint8_t *data8; |
|
size_t len, klen; |
int r; |
int r; |
|
|
db = dbm_open(path, O_RDONLY, 0644); |
if (snprintf(__ti_database, sizeof(__ti_database), "%s.cdb", path) < 0) |
|
return -1; |
|
db = cdbr_open(__ti_database, CDBR_DEFAULT); |
if (db == NULL) |
if (db == NULL) |
return -1; |
return -1; |
strlcpy(database, path, sizeof(database)); |
|
_ti_database = database; |
|
dt.dptr = (void *)__UNCONST(name); |
|
dt.dsize = strlen(name); |
|
dt = dbm_fetch(db, dt); |
|
if (dt.dptr == NULL) { |
|
dbm_close(db); |
|
return 0; |
|
} |
|
|
|
for (;;) { |
r = 0; |
p = (char *)dt.dptr; |
klen = strlen(name) + 1; |
if (*p++ != 0) /* not alias */ |
if (cdbr_find(db, name, klen, &data, &len) == -1) |
break; |
goto out; |
dt.dsize = le16dec(p) - 1; |
data8 = data; |
p += sizeof(uint16_t); |
if (len == 0) |
dt.dptr = p; |
goto out; |
dt = dbm_fetch(db, dt); |
|
if (dt.dptr == NULL) { |
/* If the entry is an alias, load the indexed terminfo description. */ |
dbm_close(db); |
if (data8[0] == 2) { |
return 0; |
if (cdbr_get(db, le32dec(data8 + 1), &data, &len)) |
} |
goto out; |
|
data8 = data; |
} |
} |
|
|
r = _ti_readterm(term, (char *)dt.dptr, dt.dsize, flags); |
r = _ti_readterm(term, data, len, flags); |
dbm_close(db); |
/* Ensure that this is the right terminfo description. */ |
|
if (r == 1) |
|
r = _ti_checkname(term, name); |
|
/* Remember the database we read. */ |
|
if (r == 1) |
|
_ti_database = __ti_database; |
|
|
|
out: |
|
cdbr_close(db); |
return r; |
return r; |
} |
} |
|
|
|
|
_ti_dbgettermp(TERMINAL *term, const char *path, const char *name, int flags) |
_ti_dbgettermp(TERMINAL *term, const char *path, const char *name, int flags) |
{ |
{ |
const char *p; |
const char *p; |
|
char pathbuf[PATH_MAX]; |
size_t l; |
size_t l; |
int r, e; |
int r, e; |
|
|
Line 261 _ti_dbgettermp(TERMINAL *term, const cha |
|
Line 324 _ti_dbgettermp(TERMINAL *term, const cha |
|
do { |
do { |
for (p = path; *path != '\0' && *path != ':'; path++) |
for (p = path; *path != '\0' && *path != ':'; path++) |
continue; |
continue; |
l = path - p; |
l = (size_t)(path - p); |
if (l != 0 && l + 1 < sizeof(pathbuf)) { |
if (l != 0 && l + 1 < sizeof(pathbuf)) { |
memcpy(pathbuf, p, l); |
memcpy(pathbuf, p, l); |
pathbuf[l] = '\0'; |
pathbuf[l] = '\0'; |
Line 275 _ti_dbgettermp(TERMINAL *term, const cha |
|
Line 338 _ti_dbgettermp(TERMINAL *term, const cha |
|
return e; |
return e; |
} |
} |
|
|
int |
static int |
_ti_getterm(TERMINAL *term, const char *name, int flags) |
ticcmp(const TIC *tic, const char *name) |
|
{ |
|
char *alias, *s; |
|
size_t len, l; |
|
|
|
if (strcmp(tic->name, name) == 0) |
|
return 0; |
|
if (tic->alias == NULL) |
|
return -1; |
|
|
|
len = strlen(name); |
|
alias = tic->alias; |
|
while (*alias != '\0') { |
|
s = strchr(alias, '|'); |
|
if (s == NULL) |
|
l = strlen(alias); |
|
else |
|
l = (size_t)(s - alias); |
|
if (len == l && memcmp(alias, name, l) == 0) |
|
return 0; |
|
if (s == NULL) |
|
break; |
|
alias = s + 1; |
|
} |
|
return 1; |
|
} |
|
|
|
static int |
|
_ti_findterm(TERMINAL *term, const char *name, int flags) |
{ |
{ |
int r; |
int r; |
char *e, h[PATH_MAX]; |
char *c, *e, h[PATH_MAX]; |
|
TIC *tic; |
|
uint8_t *f; |
|
ssize_t len; |
|
|
_DIAGASSERT(term != NULL); |
_DIAGASSERT(term != NULL); |
_DIAGASSERT(name != NULL); |
_DIAGASSERT(name != NULL); |
|
|
database[0] = '\0'; |
__ti_database[0] = '\0'; |
_ti_database = NULL; |
_ti_database = NULL; |
e = getenv("TERMINFO"); |
r = 0; |
if (e != NULL) |
|
return _ti_dbgetterm(term, e, name, flags); |
if ((e = getenv("TERMINFO")) != NULL && *e != '\0') { |
|
if (e[0] == '/') |
|
return _ti_dbgetterm(term, e, name, flags); |
|
} |
|
|
|
c = NULL; |
|
if (e == NULL && (c = getenv("TERMCAP")) != NULL) { |
|
if (*c != '\0' && *c != '/') { |
|
c = strdup(c); |
|
if (c != NULL) { |
|
e = captoinfo(c); |
|
free(c); |
|
} |
|
} |
|
} |
|
|
e = getenv("HOME"); |
|
if (e != NULL) { |
if (e != NULL) { |
|
if (c == NULL) |
|
e = strdup(e); /* So we don't destroy env */ |
|
if (e == NULL) |
|
tic = NULL; |
|
else { |
|
tic = _ti_compile(e, TIC_WARNING | |
|
TIC_ALIAS | TIC_DESCRIPTION | TIC_EXTRA); |
|
free(e); |
|
} |
|
if (tic != NULL && ticcmp(tic, name) == 0) { |
|
len = _ti_flatten(&f, tic); |
|
if (len != -1) { |
|
r = _ti_readterm(term, (char *)f, (size_t)len, |
|
flags); |
|
free(f); |
|
} |
|
} |
|
_ti_freetic(tic); |
|
if (r == 1) { |
|
if (c == NULL) |
|
_ti_database = "$TERMINFO"; |
|
else |
|
_ti_database = "$TERMCAP"; |
|
return r; |
|
} |
|
} |
|
|
|
if ((e = getenv("TERMINFO_DIRS")) != NULL) |
|
return _ti_dbgettermp(term, e, name, flags); |
|
|
|
if ((e = getenv("HOME")) != NULL) { |
snprintf(h, sizeof(h), "%s/.terminfo", e); |
snprintf(h, sizeof(h), "%s/.terminfo", e); |
r = _ti_dbgetterm(term, h, name, flags); |
r = _ti_dbgetterm(term, h, name, flags); |
if (r == 1) |
|
return 1; |
|
} |
} |
|
if (r != 1) |
|
r = _ti_dbgettermp(term, _PATH_TERMINFO, name, flags); |
|
|
r = _ti_dbgettermp(term, TERMINFO_DIRS, name, flags); |
|
if (r == 1) |
|
return 1; |
|
|
|
/* If we don't find the term in the rescue db and there is |
|
* no error, then report the last database accessed. */ |
|
strlcpy(h, database, sizeof(h)); |
|
r = _ti_dbgetterm(term, TERMINFO_RESCUE, name, flags); |
|
if (r == 0 && h[0] != '\0') |
|
strlcpy(database, h, sizeof(h)); |
|
return r; |
return r; |
} |
} |
|
|
void |
int |
_ti_freeterm(TERMINAL *term) |
_ti_getterm(TERMINAL *term, const char *name, int flags) |
{ |
{ |
|
int r; |
|
size_t i; |
|
const struct compiled_term *t; |
|
|
_DIAGASSERT(term != NULL); |
r = _ti_findterm(term, name, flags); |
|
if (r == 1) |
free(term->_area); |
return r; |
term->_area = NULL; |
|
free(term->strs); |
for (i = 0; i < __arraycount(compiled_terms); i++) { |
term->strs = NULL; |
t = &compiled_terms[i]; |
free(term->nums); |
if (strcmp(name, t->name) == 0) { |
term->nums = NULL; |
r = _ti_readterm(term, t->cap, t->caplen, flags); |
free(term->flags); |
break; |
term->flags = NULL; |
} |
free(term->_userdefs); |
} |
term->_userdefs = NULL; |
|
|
return r; |
} |
} |