mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-27 06:48:31 +00:00
Add ncurses 6.4
This commit is contained in:
parent
6ee5580adc
commit
0ed7309fdb
3071 changed files with 69379 additions and 1 deletions
457
third_party/ncurses/db_iterator.c
vendored
Normal file
457
third_party/ncurses/db_iterator.c
vendored
Normal file
|
@ -0,0 +1,457 @@
|
|||
/****************************************************************************
|
||||
* Copyright 2018-2020,2022 Thomas E. Dickey *
|
||||
* Copyright 2006-2016,2017 Free Software Foundation, Inc. *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the *
|
||||
* "Software"), to deal in the Software without restriction, including *
|
||||
* without limitation the rights to use, copy, modify, merge, publish, *
|
||||
* distribute, distribute with modifications, sublicense, and/or sell *
|
||||
* copies of the Software, and to permit persons to whom the Software is *
|
||||
* furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included *
|
||||
* in all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
|
||||
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* *
|
||||
* Except as contained in this notice, the name(s) of the above copyright *
|
||||
* holders shall not be used in advertising or otherwise to promote the *
|
||||
* sale, use or other dealings in this Software without prior written *
|
||||
* authorization. *
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Author: Thomas E. Dickey *
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* Iterators for terminal databases.
|
||||
*/
|
||||
|
||||
#include "curses.priv.h"
|
||||
|
||||
#include <time.h>
|
||||
#include "tic.h"
|
||||
|
||||
#if USE_HASHED_DB
|
||||
#include "hashed_db.h"
|
||||
#endif
|
||||
|
||||
MODULE_ID("$Id: db_iterator.c,v 1.49 2022/04/23 20:03:15 tom Exp $")
|
||||
|
||||
#define HaveTicDirectory _nc_globals.have_tic_directory
|
||||
#define KeepTicDirectory _nc_globals.keep_tic_directory
|
||||
#define TicDirectory _nc_globals.tic_directory
|
||||
#define my_blob _nc_globals.dbd_blob
|
||||
#define my_list _nc_globals.dbd_list
|
||||
#define my_size _nc_globals.dbd_size
|
||||
#define my_time _nc_globals.dbd_time
|
||||
#define my_vars _nc_globals.dbd_vars
|
||||
|
||||
static void
|
||||
add_to_blob(const char *text, size_t limit)
|
||||
{
|
||||
(void) limit;
|
||||
|
||||
if (*text != '\0') {
|
||||
char *last = my_blob + strlen(my_blob);
|
||||
if (last != my_blob)
|
||||
*last++ = NCURSES_PATHSEP;
|
||||
_nc_STRCPY(last, text, limit);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
check_existence(const char *name, struct stat *sb)
|
||||
{
|
||||
bool result = FALSE;
|
||||
|
||||
if (quick_prefix(name)) {
|
||||
result = TRUE;
|
||||
} else if (stat(name, sb) == 0
|
||||
&& (S_ISDIR(sb->st_mode)
|
||||
|| (S_ISREG(sb->st_mode) && sb->st_size))) {
|
||||
result = TRUE;
|
||||
}
|
||||
#if USE_HASHED_DB
|
||||
else if (strlen(name) < PATH_MAX - sizeof(DBM_SUFFIX)) {
|
||||
char temp[PATH_MAX];
|
||||
_nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) "%s%s", name, DBM_SUFFIX);
|
||||
if (stat(temp, sb) == 0 && S_ISREG(sb->st_mode) && sb->st_size) {
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trim newlines (and backslashes preceding those) and tab characters to
|
||||
* help simplify scripting of the quick-dump feature. Leave spaces and
|
||||
* other backslashes alone.
|
||||
*/
|
||||
static void
|
||||
trim_formatting(char *source)
|
||||
{
|
||||
char *target = source;
|
||||
char ch;
|
||||
|
||||
while ((ch = *source++) != '\0') {
|
||||
if (ch == '\\' && *source == '\n')
|
||||
continue;
|
||||
if (ch == '\n' || ch == '\t')
|
||||
continue;
|
||||
*target++ = ch;
|
||||
}
|
||||
*target = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the latest value of an environment variable in my_vars[] so we can
|
||||
* detect if one changes, invalidating the cached search-list.
|
||||
*/
|
||||
static bool
|
||||
update_getenv(const char *name, DBDIRS which)
|
||||
{
|
||||
bool result = FALSE;
|
||||
|
||||
if (which < dbdLAST) {
|
||||
char *value;
|
||||
char *cached_value = my_vars[which].value;
|
||||
bool same_value;
|
||||
|
||||
if ((value = getenv(name)) != 0) {
|
||||
value = strdup(value);
|
||||
}
|
||||
same_value = ((value == 0 && cached_value == 0) ||
|
||||
(value != 0 &&
|
||||
cached_value != 0 &&
|
||||
strcmp(value, cached_value) == 0));
|
||||
|
||||
/* Set variable name to enable checks in cache_expired(). */
|
||||
my_vars[which].name = name;
|
||||
|
||||
if (!same_value) {
|
||||
FreeIfNeeded(my_vars[which].value);
|
||||
my_vars[which].value = value;
|
||||
result = TRUE;
|
||||
} else {
|
||||
free(value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP
|
||||
static char *
|
||||
cache_getenv(const char *name, DBDIRS which)
|
||||
{
|
||||
char *result = 0;
|
||||
|
||||
(void) update_getenv(name, which);
|
||||
if (which < dbdLAST) {
|
||||
result = my_vars[which].value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The cache expires if at least a second has passed since the initial lookup,
|
||||
* or if one of the environment variables changed.
|
||||
*
|
||||
* Only a few applications use multiple lookups of terminal entries, seems that
|
||||
* aside from bulk I/O such as tic and toe, that leaves interactive programs
|
||||
* which should not be modifying the terminal databases in a way that would
|
||||
* invalidate the search-list.
|
||||
*
|
||||
* The "1-second" is to allow for user-directed changes outside the program.
|
||||
*/
|
||||
static bool
|
||||
cache_expired(void)
|
||||
{
|
||||
bool result = FALSE;
|
||||
time_t now = time((time_t *) 0);
|
||||
|
||||
if (now > my_time) {
|
||||
result = TRUE;
|
||||
} else {
|
||||
DBDIRS n;
|
||||
for (n = (DBDIRS) 0; n < dbdLAST; ++n) {
|
||||
if (my_vars[n].name != 0
|
||||
&& update_getenv(my_vars[n].name, n)) {
|
||||
result = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
free_cache(void)
|
||||
{
|
||||
FreeAndNull(my_blob);
|
||||
FreeAndNull(my_list);
|
||||
}
|
||||
|
||||
static void
|
||||
update_tic_dir(const char *update)
|
||||
{
|
||||
free((char *) TicDirectory);
|
||||
TicDirectory = update;
|
||||
}
|
||||
|
||||
/*
|
||||
* Record the "official" location of the terminfo directory, according to
|
||||
* the place where we're writing to, or the normal default, if not.
|
||||
*/
|
||||
NCURSES_EXPORT(const char *)
|
||||
_nc_tic_dir(const char *path)
|
||||
{
|
||||
T(("_nc_tic_dir %s", NonNull(path)));
|
||||
if (!KeepTicDirectory) {
|
||||
if (path != NULL) {
|
||||
if (path != TicDirectory)
|
||||
update_tic_dir(strdup(path));
|
||||
HaveTicDirectory = TRUE;
|
||||
} else if (HaveTicDirectory == 0) {
|
||||
if (use_terminfo_vars()) {
|
||||
const char *envp;
|
||||
if ((envp = getenv("TERMINFO")) != 0)
|
||||
return _nc_tic_dir(envp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TicDirectory ? TicDirectory : TERMINFO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special fix to prevent the terminfo directory from being moved after tic
|
||||
* has chdir'd to it. If we let it be changed, then if $TERMINFO has a
|
||||
* relative path, we'll lose track of the actual directory.
|
||||
*/
|
||||
NCURSES_EXPORT(void)
|
||||
_nc_keep_tic_dir(const char *path)
|
||||
{
|
||||
_nc_tic_dir(path);
|
||||
KeepTicDirectory = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup.
|
||||
*/
|
||||
NCURSES_EXPORT(void)
|
||||
_nc_last_db(void)
|
||||
{
|
||||
if (my_blob != 0 && cache_expired()) {
|
||||
free_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a simple iterator which allows the caller to step through the
|
||||
* possible locations for a terminfo directory. ncurses uses this to find
|
||||
* terminfo files to read.
|
||||
*/
|
||||
NCURSES_EXPORT(const char *)
|
||||
_nc_next_db(DBDIRS * state, int *offset)
|
||||
{
|
||||
const char *result;
|
||||
|
||||
(void) offset;
|
||||
if ((int) *state < my_size
|
||||
&& my_list != 0
|
||||
&& my_list[*state] != 0) {
|
||||
result = my_list[*state];
|
||||
(*state)++;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
if (result != 0) {
|
||||
T(("_nc_next_db %d %s", *state, result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
NCURSES_EXPORT(void)
|
||||
_nc_first_db(DBDIRS * state, int *offset)
|
||||
{
|
||||
bool cache_has_expired = FALSE;
|
||||
*state = dbdTIC;
|
||||
*offset = 0;
|
||||
|
||||
T((T_CALLED("_nc_first_db")));
|
||||
|
||||
/* build a blob containing all of the strings we will use for a lookup
|
||||
* table.
|
||||
*/
|
||||
if (my_blob == 0 || (cache_has_expired = cache_expired())) {
|
||||
size_t blobsize = 0;
|
||||
const char *values[dbdLAST];
|
||||
struct stat *my_stat;
|
||||
int j;
|
||||
|
||||
if (cache_has_expired)
|
||||
free_cache();
|
||||
|
||||
for (j = 0; j < dbdLAST; ++j)
|
||||
values[j] = 0;
|
||||
|
||||
/*
|
||||
* This is the first item in the list, and is used only when tic is
|
||||
* writing to the database, as a performance improvement.
|
||||
*/
|
||||
values[dbdTIC] = TicDirectory;
|
||||
|
||||
#if NCURSES_USE_DATABASE
|
||||
#ifdef TERMINFO_DIRS
|
||||
values[dbdCfgList] = TERMINFO_DIRS;
|
||||
#endif
|
||||
#ifdef TERMINFO
|
||||
values[dbdCfgOnce] = TERMINFO;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if NCURSES_USE_TERMCAP
|
||||
values[dbdCfgList2] = TERMPATH;
|
||||
#endif
|
||||
|
||||
if (use_terminfo_vars()) {
|
||||
#if NCURSES_USE_DATABASE
|
||||
values[dbdEnvOnce] = cache_getenv("TERMINFO", dbdEnvOnce);
|
||||
values[dbdHome] = _nc_home_terminfo();
|
||||
(void) cache_getenv("HOME", dbdHome);
|
||||
values[dbdEnvList] = cache_getenv("TERMINFO_DIRS", dbdEnvList);
|
||||
|
||||
#endif
|
||||
#if NCURSES_USE_TERMCAP
|
||||
values[dbdEnvOnce2] = cache_getenv("TERMCAP", dbdEnvOnce2);
|
||||
/* only use $TERMCAP if it is an absolute path */
|
||||
if (values[dbdEnvOnce2] != 0
|
||||
&& *values[dbdEnvOnce2] != '/') {
|
||||
values[dbdEnvOnce2] = 0;
|
||||
}
|
||||
values[dbdEnvList2] = cache_getenv("TERMPATH", dbdEnvList2);
|
||||
#endif /* NCURSES_USE_TERMCAP */
|
||||
}
|
||||
|
||||
for (j = 0; j < dbdLAST; ++j) {
|
||||
if (values[j] == 0)
|
||||
values[j] = "";
|
||||
blobsize += 2 + strlen(values[j]);
|
||||
}
|
||||
|
||||
my_blob = malloc(blobsize);
|
||||
if (my_blob != 0) {
|
||||
*my_blob = '\0';
|
||||
for (j = 0; j < dbdLAST; ++j) {
|
||||
add_to_blob(values[j], blobsize);
|
||||
}
|
||||
|
||||
/* Now, build an array which will be pointers to the distinct
|
||||
* strings in the blob.
|
||||
*/
|
||||
blobsize = 2;
|
||||
for (j = 0; my_blob[j] != '\0'; ++j) {
|
||||
if (my_blob[j] == NCURSES_PATHSEP)
|
||||
++blobsize;
|
||||
}
|
||||
my_list = typeCalloc(char *, blobsize);
|
||||
my_stat = typeCalloc(struct stat, blobsize);
|
||||
if (my_list != 0 && my_stat != 0) {
|
||||
int k = 0;
|
||||
my_list[k++] = my_blob;
|
||||
for (j = 0; my_blob[j] != '\0'; ++j) {
|
||||
if (my_blob[j] == NCURSES_PATHSEP
|
||||
&& ((&my_blob[j] - my_list[k - 1]) != 3
|
||||
|| !quick_prefix(my_list[k - 1]))) {
|
||||
my_blob[j] = '\0';
|
||||
my_list[k++] = &my_blob[j + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Eliminate duplicates from the list.
|
||||
*/
|
||||
for (j = 0; my_list[j] != 0; ++j) {
|
||||
#ifdef TERMINFO
|
||||
if (*my_list[j] == '\0')
|
||||
my_list[j] = strdup(TERMINFO);
|
||||
#endif
|
||||
trim_formatting(my_list[j]);
|
||||
for (k = 0; k < j; ++k) {
|
||||
if (!strcmp(my_list[j], my_list[k])) {
|
||||
T(("duplicate %s", my_list[j]));
|
||||
k = j - 1;
|
||||
while ((my_list[j] = my_list[j + 1]) != 0) {
|
||||
++j;
|
||||
}
|
||||
j = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Eliminate non-existent databases, and those that happen to
|
||||
* be symlinked to another location.
|
||||
*/
|
||||
for (j = 0; my_list[j] != 0; ++j) {
|
||||
bool found = check_existence(my_list[j], &my_stat[j]);
|
||||
#if HAVE_LINK
|
||||
if (found) {
|
||||
for (k = 0; k < j; ++k) {
|
||||
if (my_stat[j].st_dev == my_stat[k].st_dev
|
||||
&& my_stat[j].st_ino == my_stat[k].st_ino) {
|
||||
found = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!found) {
|
||||
T(("not found %s", my_list[j]));
|
||||
k = j;
|
||||
while ((my_list[k] = my_list[k + 1]) != 0) {
|
||||
++k;
|
||||
}
|
||||
--j;
|
||||
}
|
||||
}
|
||||
my_size = j;
|
||||
my_time = time((time_t *) 0);
|
||||
} else {
|
||||
FreeAndNull(my_blob);
|
||||
}
|
||||
free(my_stat);
|
||||
}
|
||||
}
|
||||
returnVoid;
|
||||
}
|
||||
|
||||
#if NO_LEAKS
|
||||
void
|
||||
_nc_db_iterator_leaks(void)
|
||||
{
|
||||
DBDIRS which;
|
||||
|
||||
if (my_blob != 0)
|
||||
FreeAndNull(my_blob);
|
||||
if (my_list != 0)
|
||||
FreeAndNull(my_list);
|
||||
for (which = 0; (int) which < dbdLAST; ++which) {
|
||||
my_vars[which].name = 0;
|
||||
FreeIfNeeded(my_vars[which].value);
|
||||
my_vars[which].value = 0;
|
||||
}
|
||||
update_tic_dir(NULL);
|
||||
}
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue