0
0
Fork 0
mirror of https://git.code.sf.net/p/quake/quakeforge synced 2025-03-21 18:01:15 +00:00

[cvar] Remove the cvar lists

They're really redundant, and removing the next pointer makes for a
slightly smaller cvar struct. Cvar_Select was added to allow finding
lists of matching cvars.

The tab-completion and config saving code was reworked to use the hash
table DO functions. Comments removed since the code was completely
rewritten, but still many thanks to EvilTypeGuy and Fett.
This commit is contained in:
Bill Currie 2022-04-25 00:23:28 +09:00
parent 8bd626a3e4
commit f6baa16084
3 changed files with 200 additions and 128 deletions
include/QF
libs

View file

@ -46,17 +46,17 @@ typedef struct cvar_s {
exprval_t value;
int (*validator) (const struct cvar_s *var);
struct cvar_listener_set_s *listeners;
struct cvar_s *next;
} cvar_t;
typedef struct cvar_listener_set_s LISTENER_SET_TYPE (cvar_t)
cvar_listener_set_t;
typedef void (*cvar_listener_t) (void *data, const cvar_t *cvar);
typedef int (*cvar_select_t) (const cvar_t *cvar, void *data);
typedef struct cvar_alias_s {
char *name; ///< The name of the alias.
cvar_t *cvar; ///< The cvar to which this alias refers
struct cvar_alias_s *next; ///< \internal LInked list of aliases.
} cvar_alias_t;
/** \name cvar_flags
@ -133,11 +133,11 @@ const char **Cvar_CompleteBuildList (const char *partial);
// Returns a pointer to the Cvar, NULL if not found
cvar_t *Cvar_FindVar (const char *var_name);
const cvar_t **Cvar_Select (cvar_select_t select, void *data);
void Cvar_Init_Hash (void);
void Cvar_Init (void);
extern cvar_t *cvar_vars;
///@}
#endif//__QF_cvar_h

View file

@ -38,6 +38,7 @@
#include "QF/cmd.h"
#include "QF/cvar.h"
#include "QF/sys.h"
#include "QF/heapsort.h"
#include "QF/keys.h"
#include "QF/qendian.h"
#include "QF/msg.h"
@ -580,6 +581,26 @@ Datagram_Listen (qboolean state)
}
}
static const cvar_t **cvar_list;
static int num_cvars;
static int
dg_cvar_cmp (const void *_a, const void *_b)
{
const cvar_t * const *a = _a;
const cvar_t * const *b = _b;
return strcmp ((*a)->name, (*b)->name);
}
static int
dg_cvar_select (const cvar_t *cvar, void *data)
{
if (cvar->flags & CVAR_SERVERINFO) {
*(int *)data += 1;
return 1;
}
return 0;
}
static qsocket_t *
_Datagram_CheckNewConnections (void)
@ -690,25 +711,25 @@ _Datagram_CheckNewConnections (void)
if (command == CCREQ_RULE_INFO) {
const char *prevCvarName;
cvar_t *var;
const cvar_t *var;
// find the search start location
prevCvarName = MSG_ReadString (net_message);
if (*prevCvarName) {
var = Cvar_FindVar (prevCvarName);
cvar_t key = { .name = prevCvarName };
var = bsearch (&key, cvar_list, num_cvars, sizeof (cvar_t *),
dg_cvar_cmp);
if (!var)
return NULL;
var = var->next;
return 0;
var++;
} else {
var = cvar_vars;
}
// search for the next server cvar
while (var) {
if (var->flags & CVAR_SERVERINFO) {
break;
if (cvar_list) {
free (cvar_list);
}
var = var->next;
num_cvars = 0;
cvar_list = Cvar_Select (dg_cvar_select, &num_cvars);
heapsort (cvar_list, num_cvars, sizeof (cvar_t *), dg_cvar_cmp);
var = cvar_list[0];
}
// send the response

View file

@ -45,6 +45,7 @@
#include "QF/cvar.h"
#include "QF/cmem.h"
#include "QF/hash.h"
#include "QF/heapsort.h"
#include "QF/mathlib.h"
#include "QF/plist.h"
#include "QF/qargs.h"
@ -97,11 +98,9 @@ static cvar_t developer_cvar = {
.flags = CVAR_NONE,
.value = { .type = &developer_type, .value = &developer },
};
VISIBLE cvar_t *cvar_vars;
static cvar_t *user_cvars;
static const char *cvar_null_string = "";
static cvar_alias_t *calias_vars;
static hashtab_t *cvar_hash;
static hashtab_t *user_cvar_hash;
static hashtab_t *calias_hash;
static cvar_t *
@ -115,10 +114,7 @@ cvar_create (const char *name, const char *value)
var->value.value = var + 1;
*(char **)var->value.value = strdup (value);
var->next = user_cvars;
user_cvars = var;
Hash_Add (cvar_hash, var);
Hash_Add (user_cvar_hash, var);
return var;
}
@ -128,7 +124,7 @@ cvar_destroy (cvar_t *var)
if (!(var->flags & CVAR_USER_CREATED)) {
Sys_Error ("Attempt to destroy non-user cvar");
}
Hash_Del (cvar_hash, var->name);
Hash_Del (user_cvar_hash, var->name);
free (*(char **) var->value.value);
free ((char *) var->name);
free (var);
@ -175,8 +171,6 @@ Cvar_MakeAlias (const char *name, cvar_t *cvar)
if (!var) {
alias = (cvar_alias_t *) calloc (1, sizeof (cvar_alias_t));
alias->next = calias_vars;
calias_vars = alias;
alias->name = strdup (name);
alias->cvar = cvar;
Hash_Add (calias_hash, alias);
@ -256,97 +250,90 @@ Cvar_VarString (const cvar_t *var)
return cvar_string (var);
}
VISIBLE const char *
Cvar_CompleteVariable (const char *partial)
typedef struct {
const char *match;
size_t match_len;
int num_matches;
} cvar_count_ctx_t;
static void
cvar_match_count (void *ele, void *data)
{
cvar_t *cvar;
cvar_alias_t *alias;
int len;
cvar_count_ctx_t *ctx = data;
const cvar_t *cvar = ele;
len = strlen (partial);
if (!len)
return NULL;
// check exact match
for (cvar = cvar_vars; cvar; cvar = cvar->next)
if (!strcmp (partial, cvar->name))
return cvar->name;
// check aliases too :)
for (alias = calias_vars; alias; alias = alias->next)
if (!strcmp (partial, alias->name))
return alias->name;
// check partial match
for (cvar = cvar_vars; cvar; cvar = cvar->next)
if (!strncmp (partial, cvar->name, len))
return cvar->name;
// check aliases too :)
for (alias = calias_vars; alias; alias = alias->next)
if (!strncmp (partial, alias->name, len))
return alias->name;
return NULL;
if (strncmp (cvar->name, ctx->match, ctx->match_len) == 0) {
ctx->num_matches++;
}
}
/*
CVar_CompleteCountPossible
New function for tab-completion system
Added by EvilTypeGuy
Thanks to Fett erich@heintz.com
*/
VISIBLE int
Cvar_CompleteCountPossible (const char *partial)
{
cvar_t *cvar;
int len;
int h;
cvar_count_ctx_t ctx = {
.match = partial,
.match_len = strlen (partial),
.num_matches = 0,
};
h = 0;
len = strlen(partial);
Hash_ForEach (cvar_hash, cvar_match_count, &ctx);
Hash_ForEach (user_cvar_hash, cvar_match_count, &ctx);
// this is a bit of a hack, but both cvar_alias_t and cvar_t have
// name in the first file, so it will work out as that's the only
// criteron for a match
Hash_ForEach (calias_hash, cvar_match_count, &ctx);
if (!len)
return 0;
// Loop through the cvars and count all possible matches
for (cvar = cvar_vars; cvar; cvar = cvar->next)
if (!strncmp(partial, cvar->name, len))
h++;
return h;
return ctx.num_matches;
}
/*
CVar_CompleteBuildList
typedef struct {
const char *match;
size_t match_len;
const char **list;
int index;
} cvar_copy_ctx_t;
New function for tab-completion system
Added by EvilTypeGuy
Thanks to Fett erich@heintz.com
Thanks to taniwha
*/
VISIBLE const char **
static void
cvar_match_copy (void *ele, void *data)
{
cvar_copy_ctx_t *ctx = data;
const cvar_t *cvar = ele;
if (strncmp (cvar->name, ctx->match, ctx->match_len) == 0) {
ctx->list[ctx->index++] = cvar->name;
}
}
static int
cvar_cmp_name (const void *_a, const void *_b)
{
const char * const *a = _a;
const char * const *b = _b;
return strcmp (*a, *b);
}
VISIBLE const char **
Cvar_CompleteBuildList (const char *partial)
{
cvar_t *cvar;
int len = 0;
int bpos = 0;
int sizeofbuf = (Cvar_CompleteCountPossible (partial) + 1) *
sizeof (char *);
const char **buf;
int num_matches = Cvar_CompleteCountPossible (partial);
len = strlen(partial);
buf = malloc(sizeofbuf + sizeof (char *));
SYS_CHECKMEM (buf);
// Loop through the alias list and print all matches
for (cvar = cvar_vars; cvar; cvar = cvar->next)
if (!strncmp(partial, cvar->name, len))
buf[bpos++] = cvar->name;
cvar_copy_ctx_t ctx = {
.match = partial,
.match_len = strlen (partial),
.list = malloc((num_matches + 1) * sizeof (char *)),
.index = 0,
};
buf[bpos] = NULL;
return buf;
Hash_ForEach (cvar_hash, cvar_match_copy, &ctx);
Hash_ForEach (user_cvar_hash, cvar_match_copy, &ctx);
// this is a bit of a hack, but both cvar_alias_t and cvar_t have
// name in the first file, so it will work out as that's the only
// criteron for a match
Hash_ForEach (calias_hash, cvar_match_copy, &ctx);
ctx.list[ctx.index] = 0;
fprintf (stderr, "%d %d\n", num_matches, ctx.index);
heapsort (ctx.list, ctx.index, sizeof (char *), cvar_cmp_name);
return ctx.list;
}
VISIBLE void
@ -464,6 +451,17 @@ Cvar_Command (void)
return true;
}
static void
cvar_write_variable (void *ele, void *data)
{
cvar_t *cvar = ele;
QFile *f = data;
if (cvar->flags & CVAR_ARCHIVE) {
Qprintf (f, "seta %s \"%s\"\n", cvar->name, cvar_string (cvar));
}
}
/*
Cvar_WriteVariables
@ -473,11 +471,19 @@ Cvar_Command (void)
VISIBLE void
Cvar_WriteVariables (QFile *f)
{
cvar_t *var;
Hash_ForEach (cvar_hash, cvar_write_variable, f);
Hash_ForEach (user_cvar_hash, cvar_write_variable, f);
}
for (var = cvar_vars; var; var = var->next)
if (var->flags & CVAR_ARCHIVE)
Qprintf (f, "seta %s \"%s\"\n", var->name, cvar_string (var));
static void
cvar_write_config (void *ele, void *data)
{
cvar_t *cvar = ele;
plitem_t *cfg = data;
if (cvar->flags & CVAR_ARCHIVE) {
PL_D_AddObject (cfg, cvar->name, PL_NewString (cvar_string (cvar)));
}
}
VISIBLE void
@ -485,11 +491,8 @@ Cvar_SaveConfig (plitem_t *config)
{
plitem_t *cvars = PL_NewDictionary (0); //FIXME hashlinks
PL_D_AddObject (config, "cvars", cvars);
for (cvar_t *var = cvar_vars; var; var = var->next) {
if (var->flags & CVAR_ARCHIVE) {
PL_D_AddObject (cvars, var->name, PL_NewString (cvar_string (var)));
}
}
Hash_ForEach (cvar_hash, cvar_write_config, cvars);
Hash_ForEach (user_cvar_hash, cvar_write_config, cvars);
}
VISIBLE void
@ -704,35 +707,49 @@ Cvar_Reset_f (void)
}
}
static void
cvar_reset_var (void *ele, void *data)
{
cvar_t *var = ele;
if (!(var->flags & CVAR_ROM))
Cvar_Reset (var);
}
static void
Cvar_ResetAll_f (void)
{
cvar_t *var;
Hash_ForEach (cvar_hash, cvar_reset_var, 0);
}
for (var = cvar_vars; var; var = var->next)
if (!(var->flags & CVAR_ROM))
Cvar_Reset (var);
static int
cvar_cmp (const void *_a, const void *_b)
{
const cvar_t * const *a = _a;
const cvar_t * const *b = _b;
return strcmp ((*a)->name, (*b)->name);
}
static void
Cvar_CvarList_f (void)
{
cvar_t *var;
int i;
int showhelp = 0;
const char *flags;
if (Cmd_Argc () > 1) {
showhelp = 1;
if (strequal (Cmd_Argv (1), "cfg"))
showhelp++;
}
for (var = cvar_vars, i = 0; var; var = var->next, i++) {
flags = va (0, "%c%c%c%c",
var->flags & CVAR_ROM ? 'r' : ' ',
var->flags & CVAR_ARCHIVE ? '*' : ' ',
var->flags & CVAR_USERINFO ? 'u' : ' ',
var->flags & CVAR_SERVERINFO ? 's' : ' ');
void **cvar_list = Hash_GetList (cvar_hash);
int num_vars = Hash_NumElements (cvar_hash);
heapsort (cvar_list, num_vars, sizeof (void *), cvar_cmp);
for (cvar_t **cvar = (cvar_t **) cvar_list; *cvar; cvar++) {
cvar_t *var = *cvar;
const char *flags = va (0, "%c%c%c%c",
var->flags & CVAR_ROM ? 'r' : ' ',
var->flags & CVAR_ARCHIVE ? '*' : ' ',
var->flags & CVAR_USERINFO ? 'u' : ' ',
var->flags & CVAR_SERVERINFO ? 's' : ' ');
if (showhelp == 2)
Sys_Printf ("//%s %s\n%s \"%s\"\n\n", flags, var->description,
var->name, cvar_string (var));
@ -742,7 +759,7 @@ Cvar_CvarList_f (void)
Sys_Printf ("%s %s\n", flags, var->name);
}
Sys_Printf ("------------\n%d variables\n", i);
Sys_Printf ("------------\n%d variables\n", num_vars);
}
static const char *
@ -771,6 +788,7 @@ VISIBLE void
Cvar_Init_Hash (void)
{
cvar_hash = Hash_NewTable (1021, cvar_get_key, 0, 0, 0);
user_cvar_hash = Hash_NewTable (1021, cvar_get_key, 0, 0, 0);
calias_hash = Hash_NewTable (1021, calias_get_key, calias_free, 0, 0);
}
@ -809,15 +827,13 @@ Cvar_Register (cvar_t *var, cvar_listener_t listener, void *data)
Sys_Error ("Cvar %s already registered", var->name);
}
if ((user_var = Hash_Find (cvar_hash, var->name))) {
if ((user_var = Hash_Find (user_cvar_hash, var->name))) {
cvar_setvar (var, cvar_string (user_var));
cvar_destroy (user_var);
} else {
cvar_setvar (var, var->default_value);
}
var->flags |= CVAR_REGISTERED;
var->next = cvar_vars;
cvar_vars = var;
if (listener) {
Cvar_AddListener (var, listener, data);
@ -843,3 +859,38 @@ Cvar_SetFlags (cvar_t *var, int cvarflags)
var->flags = cvarflags;
}
typedef struct {
cvar_select_t select;
void *data;
const cvar_t **list;
int index;
} cvar_select_ctx_t;
static void
cvar_select (void *ele, void *data)
{
const cvar_t *cvar = ele;
cvar_select_ctx_t *ctx = data;
if (ctx->select (cvar, ctx->data)) {
ctx->list[ctx->index++] = cvar;
}
}
VISIBLE const cvar_t **
Cvar_Select (cvar_select_t select, void *data)
{
int num_cvars = Hash_NumElements (cvar_hash)
+ Hash_NumElements (user_cvar_hash);
cvar_select_ctx_t ctx = {
.select = select,
.data = data,
.list = malloc ((num_cvars + 1) * sizeof (cvar_t *)),
.index = 0,
};
Hash_ForEach (cvar_hash, cvar_select, &ctx);
Hash_ForEach (user_cvar_hash, cvar_select, &ctx);
ctx.list[num_cvars] = 0;
return ctx.list;
}