X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=cvar.c;h=1c347193787b09119f06f782ce3d880b53c78d3d;hb=c1bc0951402274b2d01e3b6e3edb5e32ac70f174;hp=a0d0cb979acdb939898a616cff501bed9c90b268;hpb=27aa321754fe8ae393a2335adb6e9130364a1268;p=xonotic%2Fdarkplaces.git diff --git a/cvar.c b/cvar.c index a0d0cb97..1c347193 100644 --- a/cvar.c +++ b/cvar.c @@ -1,5 +1,6 @@ /* Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2000-2021 DarkPlaces contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -32,9 +33,9 @@ cvar_state_t cvars_null; Cvar_FindVar ============ */ -cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, int neededflags) +cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, unsigned neededflags) { - int hashindex; + unsigned hashindex; cvar_hash_t *hash; // use hash lookup to minimize search time @@ -43,13 +44,13 @@ cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, int neededflags) if (!strcmp (var_name, hash->cvar->name) && (hash->cvar->flags & neededflags)) return hash->cvar; else - for (int i = 0; i < hash->cvar->aliasindex; i++) - if (!strcmp (var_name, hash->cvar->aliases[i]) && (hash->cvar->flags & neededflags)) + for (char **alias = hash->cvar->aliases; alias && *alias; alias++) + if (!strcmp (var_name, *alias) && (hash->cvar->flags & neededflags)) return hash->cvar; return NULL; } -cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, int neededflags) +cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, unsigned neededflags) { cvar_t *var; @@ -73,37 +74,41 @@ cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, int ne return var; } -static cvar_hash_t *Cvar_FindVarLink(cvar_state_t *cvars, const char *var_name, cvar_hash_t **parent, cvar_hash_t ***link, cvar_t **prev_alpha, int neededflags) +/** + * Returns a pointer to the pointer stored in hashtable[] (or the one it links to) + * because we'll need to update that when deleting a cvar as other cvar(s) may share its hashindex. + */ +static cvar_hash_t **Cvar_FindVarLink(cvar_state_t *cvars, const char *var_name, cvar_t **prev_alpha, unsigned neededflags) { - int hashindex; - cvar_hash_t *hash; + unsigned hashindex; + cvar_t *cvar; + cvar_hash_t **hashlinkptr; // use hash lookup to minimize search time - hashindex = CRC_Block((const unsigned char *)var_name, strlen(var_name)); - if(parent) *parent = NULL; + hashindex = CRC_Block((const unsigned char *)var_name, strlen(var_name)) % CVAR_HASHSIZE; if(prev_alpha) *prev_alpha = NULL; - if(link) *link = &cvars->hashtable[hashindex]; - for (hash = cvars->hashtable[hashindex];hash;hash = hash->next) + hashlinkptr = &cvars->hashtable[hashindex]; + for (hashlinkptr = &cvars->hashtable[hashindex]; *hashlinkptr; hashlinkptr = &(*hashlinkptr)->next) { - if (!strcmp (var_name, hash->cvar->name) && (hash->cvar->flags & neededflags)) + cvar = (*hashlinkptr)->cvar; + if (!strcmp (var_name, cvar->name) && (cvar->flags & neededflags)) goto match; else - for (int i = 0; i < hash->cvar->aliasindex; i++) - if (!strcmp (var_name, hash->cvar->aliases[i]) && (hash->cvar->flags & neededflags)) + for (char **alias = cvar->aliases; alias && *alias; alias++) + if (!strcmp (var_name, *alias) && (cvar->flags & neededflags)) goto match; - if(parent) *parent = hash; } - return NULL; -match: - if(!prev_alpha || hash->cvar == cvars->vars) - return hash; + return NULL; +match: + if(!prev_alpha || cvar == cvars->vars) + return hashlinkptr; *prev_alpha = cvars->vars; // if prev_alpha happens to become NULL then there has been some inconsistency elsewhere // already - should I still insert '*prev_alpha &&' in the loop? - while((*prev_alpha)->next != hash->cvar) + while((*prev_alpha)->next != cvar) *prev_alpha = (*prev_alpha)->next; - return hash; + return hashlinkptr; } /* @@ -111,7 +116,7 @@ match: Cvar_VariableValue ============ */ -float Cvar_VariableValueOr(cvar_state_t *cvars, const char *var_name, float def, int neededflags) +float Cvar_VariableValueOr(cvar_state_t *cvars, const char *var_name, float def, unsigned neededflags) { cvar_t *var; @@ -121,7 +126,7 @@ float Cvar_VariableValueOr(cvar_state_t *cvars, const char *var_name, float def, return atof (var->string); } -float Cvar_VariableValue(cvar_state_t *cvars, const char *var_name, int neededflags) +float Cvar_VariableValue(cvar_state_t *cvars, const char *var_name, unsigned neededflags) { return Cvar_VariableValueOr(cvars, var_name, 0, neededflags); } @@ -131,7 +136,7 @@ float Cvar_VariableValue(cvar_state_t *cvars, const char *var_name, int neededfl Cvar_VariableString ============ */ -const char *Cvar_VariableStringOr(cvar_state_t *cvars, const char *var_name, const char *def, int neededflags) +const char *Cvar_VariableStringOr(cvar_state_t *cvars, const char *var_name, const char *def, unsigned neededflags) { cvar_t *var; @@ -141,7 +146,7 @@ const char *Cvar_VariableStringOr(cvar_state_t *cvars, const char *var_name, con return var->string; } -const char *Cvar_VariableString(cvar_state_t *cvars, const char *var_name, int neededflags) +const char *Cvar_VariableString(cvar_state_t *cvars, const char *var_name, unsigned neededflags) { return Cvar_VariableStringOr(cvars, var_name, cvar_null_string, neededflags); } @@ -151,7 +156,7 @@ const char *Cvar_VariableString(cvar_state_t *cvars, const char *var_name, int n Cvar_VariableDefString ============ */ -const char *Cvar_VariableDefString(cvar_state_t *cvars, const char *var_name, int neededflags) +const char *Cvar_VariableDefString(cvar_state_t *cvars, const char *var_name, unsigned neededflags) { cvar_t *var; @@ -166,7 +171,7 @@ const char *Cvar_VariableDefString(cvar_state_t *cvars, const char *var_name, in Cvar_VariableDescription ============ */ -const char *Cvar_VariableDescription(cvar_state_t *cvars, const char *var_name, int neededflags) +const char *Cvar_VariableDescription(cvar_state_t *cvars, const char *var_name, unsigned neededflags) { cvar_t *var; @@ -182,7 +187,7 @@ const char *Cvar_VariableDescription(cvar_state_t *cvars, const char *var_name, Cvar_CompleteVariable ============ */ -const char *Cvar_CompleteVariable(cvar_state_t *cvars, const char *partial, int neededflags) +const char *Cvar_CompleteVariable(cvar_state_t *cvars, const char *partial, unsigned neededflags) { cvar_t *cvar; size_t len; @@ -209,7 +214,7 @@ const char *Cvar_CompleteVariable(cvar_state_t *cvars, const char *partial, int Thanks to Fett erich@heintz.com */ -int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, int neededflags) +int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, unsigned neededflags) { cvar_t *cvar; size_t len; @@ -226,8 +231,8 @@ int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, int nee if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags)) h++; else - for(int i = 0; i < cvar->aliasindex; i++) - if (!strncasecmp(partial, cvar->aliases[i], len) && (cvar->flags & neededflags)) + for (char **alias = cvar->aliases; alias && *alias; alias++) + if (!strncasecmp(partial, *alias, len) && (cvar->flags & neededflags)) h++; return h; @@ -242,7 +247,7 @@ int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, int nee Thanks to taniwha */ -const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, int neededflags) +const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, unsigned neededflags) { const cvar_t *cvar; size_t len = 0; @@ -257,29 +262,30 @@ const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, in if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags)) buf[bpos++] = cvar->name; else - for(int i = 0; i < cvar->aliasindex; i++) - if (!strncasecmp(partial, cvar->aliases[i], len) && (cvar->flags & neededflags)) - buf[bpos++] = cvar->aliases[i]; + for (char **alias = cvar->aliases; alias && *alias; alias++) + if (!strncasecmp(partial, *alias, len) && (cvar->flags & neededflags)) + buf[bpos++] = *alias; buf[bpos] = NULL; return buf; } -void Cvar_PrintHelp(cvar_t *cvar, const char *name, qboolean full) +void Cvar_PrintHelp(cvar_t *cvar, const char *name, qbool full) { - Con_Printf("^3%s^7", name); + // Aliases are purple, cvars are yellow if (strcmp(cvar->name, name)) - Con_Printf(" (now ^3%s^7)", cvar->name); - Con_Printf(" is \"%s\" [\"%s\"] ", ((cvar->flags & CVAR_PRIVATE) ? "********"/*hunter2*/ : cvar->string), cvar->defstring); - + Con_Printf("^6%s^7 (alias of ^3%s^7)", name, cvar->name); + else + Con_Printf("^3%s^7", name); + Con_Printf(" is \"%s^7\" [\"%s^7\"]", ((cvar->flags & CF_PRIVATE) ? "********"/*hunter2*/ : cvar->string), cvar->defstring); if (full) - Con_Printf("%s", cvar->description); - Con_Printf("\n"); + Con_Printf(" %s", cvar->description); + Con_Print("\n"); } // written by LadyHavoc -void Cvar_CompleteCvarPrint(cvar_state_t *cvars, const char *partial, int neededflags) +void Cvar_CompleteCvarPrint(cvar_state_t *cvars, const char *partial, unsigned neededflags) { cvar_t *cvar; size_t len = strlen(partial); @@ -288,15 +294,16 @@ void Cvar_CompleteCvarPrint(cvar_state_t *cvars, const char *partial, int needed if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags)) Cvar_PrintHelp(cvar, cvar->name, true); else - for (int i = 0; i < cvar->aliasindex; i++) - if (!strncasecmp (partial, cvar->aliases[i], len) && (cvar->flags & neededflags)) - Cvar_PrintHelp(cvar, cvar->aliases[i], true); + for (char **alias = cvar->aliases; alias && *alias; alias++) + if (!strncasecmp (partial, *alias, len) && (cvar->flags & neededflags)) + Cvar_PrintHelp(cvar, *alias, true); } -// check if a cvar is held by some progs -static qboolean Cvar_IsAutoCvar(cvar_t *var) +/// Check if a cvar is held by some progs +/// in which case its name is returned, otherwise NULL. +static const char *Cvar_IsAutoCvar(cvar_t *var) { int i; prvm_prog_t *prog; @@ -304,12 +311,12 @@ static qboolean Cvar_IsAutoCvar(cvar_t *var) { prog = &prvm_prog_list[i]; if (prog->loaded && var->globaldefindex[i] >= 0) - return true; + return prog->name; } - return false; + return NULL; } -// we assume that prog is already set to the target progs +/// we assume that prog is already set to the target progs static void Cvar_UpdateAutoCvar(cvar_t *var) { int i; @@ -322,7 +329,7 @@ static void Cvar_UpdateAutoCvar(cvar_t *var) prog = &prvm_prog_list[i]; if (prog->loaded && var->globaldefindex[i] >= 0) { - // MUST BE SYNCED WITH prvm_edict.c PRVM_LoadProgs + // MUST BE SYNCED WITH prvm_edict.c PRVM_Prog_Load switch(prog->globaldefs[var->globaldefindex[i]].type & ~DEF_SAVEGLOBAL) { case ev_float: @@ -354,7 +361,7 @@ static void Cvar_UpdateAutoCvar(cvar_t *var) } } -// called after loading a savegame +/// called after loading a savegame void Cvar_UpdateAllAutoCvars(cvar_state_t *cvars) { cvar_t *var; @@ -362,6 +369,18 @@ void Cvar_UpdateAllAutoCvars(cvar_state_t *cvars) Cvar_UpdateAutoCvar(var); } +void Cvar_Callback(cvar_t *var) +{ + if (var == NULL) + { + Con_Print(CON_WARN "Cvar_Callback: var == NULL\n"); + return; + } + + if(var->callback) + var->callback(var); +} + /* ============ Cvar_Set @@ -370,39 +389,30 @@ Cvar_Set extern cvar_t sv_disablenotify; static void Cvar_SetQuick_Internal (cvar_t *var, const char *value) { - cvar_state_t *cvars = &cvars_all; - qboolean changed; + qbool changed; size_t valuelen; - char vabuf[1024]; - char new_value[MAX_INPUTLINE]; changed = strcmp(var->string, value) != 0; // LadyHavoc: don't reallocate when there is no change if (!changed) - return; - - memcpy(new_value,value,MAX_INPUTLINE); - - // Call the function stored in the cvar for bounds checking, cleanup, etc - if (var->callback) - var->callback(new_value); + goto cvar_callback; // LadyHavoc: don't reallocate when the buffer is the same size - valuelen = strlen(new_value); + valuelen = strlen(value); if (!var->string || strlen(var->string) != valuelen) { Z_Free ((char *)var->string); // free the old value string var->string = (char *)Z_Malloc (valuelen + 1); } - memcpy ((char *)var->string, new_value, valuelen + 1); + memcpy ((char *)var->string, value, valuelen + 1); var->value = atof (var->string); var->integer = (int) var->value; - if ((var->flags & CVAR_NOTIFY) && changed && sv.active && !sv_disablenotify.integer) - SV_BroadcastPrintf("\001^3Server cvar \"%s\" changed to \"%s\"\n", var->name, var->string); + if ((var->flags & CF_NOTIFY) && sv.active && !sv_disablenotify.integer) + SV_BroadcastPrintf("\003^3Server cvar \"%s\" changed to \"%s\"\n", var->name, var->string); #if 0 // TODO: add infostring support to the server? - if ((var->flags & CVAR_SERVERINFO) && changed && sv.active) + if ((var->flags & CF_SERVERINFO) && changed && sv.active) { InfoString_SetValue(svs.serverinfo, sizeof(svs.serverinfo), var->name, var->string); if (sv.active) @@ -413,62 +423,30 @@ static void Cvar_SetQuick_Internal (cvar_t *var, const char *value) } } #endif - if ((var->flags & CVAR_USERINFO) && cls.state != ca_dedicated) + if (var->flags & CF_USERINFO) CL_SetInfo(var->name, var->string, true, false, false, false); - else if ((var->flags & CVAR_NQUSERINFOHACK) && cls.state != ca_dedicated) - { - // update the cls.userinfo to have proper values for the - // silly nq config variables. - // - // this is done when these variables are changed rather than at - // connect time because if the user or code checks the userinfo and it - // holds weird values it may cause confusion... - if (!strcmp(var->name, "_cl_color")) - { - int top = (var->integer >> 4) & 15, bottom = var->integer & 15; - CL_SetInfo("topcolor", va(vabuf, sizeof(vabuf), "%i", top), true, false, false, false); - CL_SetInfo("bottomcolor", va(vabuf, sizeof(vabuf), "%i", bottom), true, false, false, false); - if (cls.protocol != PROTOCOL_QUAKEWORLD && cls.netcon) - { - MSG_WriteByte(&cls.netcon->message, clc_stringcmd); - MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "color %i %i", top, bottom)); - } - } - else if (!strcmp(var->name, "_cl_rate")) - CL_SetInfo("rate", va(vabuf, sizeof(vabuf), "%i", var->integer), true, false, false, false); - else if (!strcmp(var->name, "_cl_rate_burstsize")) - CL_SetInfo("rate_burstsize", va(vabuf, sizeof(vabuf), "%i", var->integer), true, false, false, false); - else if (!strcmp(var->name, "_cl_playerskin")) - CL_SetInfo("playerskin", var->string, true, false, false, false); - else if (!strcmp(var->name, "_cl_playermodel")) - CL_SetInfo("playermodel", var->string, true, false, false, false); - else if (!strcmp(var->name, "_cl_name")) - CL_SetInfo("name", var->string, true, false, false, false); - else if (!strcmp(var->name, "rcon_secure")) - { - // whenever rcon_secure is changed to 0, clear rcon_password for - // security reasons (prevents a send-rcon-password-as-plaintext - // attack based on NQ protocol session takeover and svc_stufftext) - if(var->integer <= 0) - Cvar_Set(cvars, "rcon_password", ""); - } -#ifdef CONFIG_MENU - else if (!strcmp(var->name, "net_slist_favorites")) - NetConn_UpdateFavorites(); -#endif - } Cvar_UpdateAutoCvar(var); + +cvar_callback: + // Call the function stored in the cvar for bounds checking, cleanup, etc + Cvar_Callback(var); } void Cvar_SetQuick (cvar_t *var, const char *value) { if (var == NULL) { - Con_Print("Cvar_SetQuick: var == NULL\n"); + Con_Print(CON_WARN "Cvar_SetQuick: var == NULL\n"); return; } + if (!(var->flags & CF_REGISTERED) && !(var->flags & CF_ALLOCATED)) + { + Con_Printf(CON_WARN "Warning: Cvar_SetQuick() cannot set unregistered cvar \"%s\"\n", var->name); + return; // setting an unregistered engine cvar crashes + } + if (developer_extra.integer) Con_DPrintf("Cvar_SetQuick({\"%s\", \"%s\", %i, \"%s\"}, \"%s\");\n", var->name, var->string, var->flags, var->defstring, value); @@ -481,7 +459,7 @@ void Cvar_Set(cvar_state_t *cvars, const char *var_name, const char *value) var = Cvar_FindVar(cvars, var_name, ~0); if (var == NULL) { - Con_Printf("Cvar_Set: variable %s not found\n", var_name); + Con_Printf(CON_WARN "Cvar_Set: variable %s not found\n", var_name); return; } Cvar_SetQuick(var, value); @@ -514,31 +492,103 @@ void Cvar_SetValue(cvar_state_t *cvars, const char *var_name, float value) Cvar_Set(cvars, var_name, val); } -void Cvar_RegisterCallback(cvar_t *variable, void (*callback)(char *)) +void Cvar_RegisterCallback(cvar_t *variable, void (*callback)(cvar_t *)) { + if (variable == NULL) + { + Con_Print(CON_WARN "Cvar_RegisterCallback: var == NULL\n"); + return; + } + + if (!(variable->flags & cmd_local->cvars_flagsmask)) + { + if (developer_extra.integer) + Con_DPrintf("^6Cvar_RegisterCallback: rejecting cvar \"%s\"\n", variable->name); + return; + } + variable->callback = callback; } -void Cvar_RegisterAlias(cvar_t *variable, const char *alias ) +void Cvar_RegisterVirtual(cvar_t *variable, const char *name ) { cvar_state_t *cvars = &cvars_all; cvar_hash_t *hash; - int hashindex; + unsigned hashindex; + + if (!(variable->flags & cmd_local->cvars_flagsmask)) + { + if (developer_extra.integer) + Con_DPrintf("^6Cvar_RegisterVirtual: rejecting cvar \"%s\" alias \"%s\"\n", variable->name, name); + return; + } - variable->aliases = (char **)Mem_Realloc(zonemempool, variable->aliases, sizeof(char *) * (variable->aliasindex + 1)); - // Add to it - variable->aliases[variable->aliasindex] = (char *)Z_Malloc(strlen(alias) + 1); - memcpy(variable->aliases[variable->aliasindex], alias, strlen(alias) + 1); - variable->aliasindex++; + if(!*name) + { + Con_Printf(CON_WARN "Cvar_RegisterVirtual: invalid virtual cvar name\n"); + return; + } + + // check for overlap with a command + if (Cmd_Exists(cmd_local, name)) + { + Con_Printf(CON_WARN "Cvar_RegisterVirtual: %s is a command\n", name); + return; + } + + if(Cvar_FindVar(&cvars_all, name, 0)) + { + Con_Printf(CON_WARN "Cvar_RegisterVirtual: %s is a cvar\n", name); + return; + } + + // Resize the variable->aliases list to have room for another entry and a null terminator. + // This zero-pads when resizing, so we don't need to write the NULL terminator manually here. + // Also if aliases is NULL this allocates fresh for the correct size, so it's fine to just do this. + variable->aliases = (char **)Z_Realloc(variable->aliases, sizeof(char *) * (variable->aliases_size + 2)); + // Add the new alias, and increment the number of aliases in the list + variable->aliases[variable->aliases_size++] = Z_strdup(name); // link to head of list in this hash table index hash = (cvar_hash_t *)Z_Malloc(sizeof(cvar_hash_t)); - hashindex = CRC_Block((const unsigned char *)alias, strlen(alias)) % CVAR_HASHSIZE; + hashindex = CRC_Block((const unsigned char *)name, strlen(name)) % CVAR_HASHSIZE; hash->next = cvars->hashtable[hashindex]; cvars->hashtable[hashindex] = hash; hash->cvar = variable; } +/* +============ +Cvar_Link + +Links a variable to the variable list and hashtable +============ +*/ +static void Cvar_Link(cvar_t *variable, cvar_state_t *cvars) +{ + cvar_t *current, *next; + cvar_hash_t *hash; + unsigned hashindex; + /* + * Link the variable in + * alphanumerical order + */ + for( current = NULL, next = cvars->vars ; next && strcmp( next->name, variable->name ) < 0 ; current = next, next = next->next ) + ; + if(current) + current->next = variable; + else + cvars->vars = variable; + variable->next = next; + + // link to head of list in this hash table index + hash = (cvar_hash_t *)Z_Malloc(sizeof(cvar_hash_t)); + hashindex = CRC_Block((const unsigned char *)variable->name, strlen(variable->name)) % CVAR_HASHSIZE; + hash->next = cvars->hashtable[hashindex]; + hash->cvar = variable; + cvars->hashtable[hashindex] = hash; +} + /* ============ Cvar_RegisterVariable @@ -548,37 +598,25 @@ Adds a freestanding variable to the variable list. */ void Cvar_RegisterVariable (cvar_t *variable) { - cvar_state_t *cvars = NULL; - int hashindex; - cvar_hash_t *hash; - cvar_t *current, *next, *cvar; - char *oldstr; - size_t alloclen; + cvar_state_t *cvars = &cvars_all; + cvar_t *current, *cvar; int i; - switch (variable->flags & (CVAR_CLIENT | CVAR_SERVER)) + if (!(variable->flags & cmd_local->cvars_flagsmask)) { - case CVAR_CLIENT: - case CVAR_SERVER: - case CVAR_CLIENT | CVAR_SERVER: - cvars = &cvars_all; - break; - case 0: - Sys_Error("Cvar_RegisterVariable({\"%s\", \"%s\", %i}) with no CVAR_CLIENT | CVAR_SERVER flags\n", variable->name, variable->string, variable->flags); - break; - default: - Sys_Error("Cvar_RegisterVariable({\"%s\", \"%s\", %i}) with weird CVAR_CLIENT | CVAR_SERVER flags\n", variable->name, variable->string, variable->flags); - break; + if (developer_extra.integer) + Con_DPrintf("^2Cvar_RegisterVariable: rejecting cvar \"%s\"\n", variable->name); + return; } if (developer_extra.integer) Con_DPrintf("Cvar_RegisterVariable({\"%s\", \"%s\", %i});\n", variable->name, variable->string, variable->flags); -// first check to see if it has already been defined + // first check to see if it has already been defined cvar = Cvar_FindVar(cvars, variable->name, ~0); if (cvar) { - if (cvar->flags & CVAR_ALLOCATED) + if (cvar->flags & CF_ALLOCATED) { if (developer_extra.integer) Con_DPrintf("... replacing existing allocated cvar {\"%s\", \"%s\", %i}\n", cvar->name, cvar->string, cvar->flags); @@ -586,7 +624,7 @@ void Cvar_RegisterVariable (cvar_t *variable) // (because the engine directly accesses fixed variables) // NOTE: this isn't actually used currently // (all cvars are registered before config parsing) - variable->flags |= (cvar->flags & ~CVAR_ALLOCATED); + variable->flags &= ~CF_ALLOCATED; // cvar->string is now owned by variable instead variable->string = cvar->string; variable->defstring = cvar->defstring; @@ -620,45 +658,31 @@ void Cvar_RegisterVariable (cvar_t *variable) return; } -// check for overlap with a command - if (Cmd_Exists(&cmd_client, variable->name) || Cmd_Exists(&cmd_server, variable->name)) + // check for overlap with a command + if (Cmd_Exists(cmd_local, variable->name)) { - Con_Printf("Cvar_RegisterVariable: %s is a command\n", variable->name); + Con_Printf(CON_WARN "Cvar_RegisterVariable: %s is a command\n", variable->name); return; } -// copy the value off, because future sets will Z_Free it - oldstr = (char *)variable->string; - alloclen = strlen(variable->string) + 1; - variable->string = (char *)Z_Malloc (alloclen); - memcpy ((char *)variable->string, oldstr, alloclen); - variable->defstring = (char *)Z_Malloc (alloclen); - memcpy ((char *)variable->defstring, oldstr, alloclen); + // copy the value off, because future sets will Z_Free it + variable->name = Z_strdup(variable->name); + variable->string = Z_strdup(variable->string); + variable->defstring = Z_strdup(variable->string); variable->value = atof (variable->string); variable->integer = (int) variable->value; - variable->aliasindex = 0; + variable->aliases = NULL; + variable->aliases_size = 0; + variable->initstring = NULL; // Mark it as not an autocvar. for (i = 0;i < PRVM_PROG_MAX;i++) variable->globaldefindex[i] = -1; -// link the variable in -// alphanumerical order - for( current = NULL, next = cvars->vars ; next && strcmp( next->name, variable->name ) < 0 ; current = next, next = next->next ) - ; - if( current ) { - current->next = variable; - } else { - cvars->vars = variable; - } - variable->next = next; + // Safe for Cvar_SetQuick() + variable->flags |= CF_REGISTERED; - // link to head of list in this hash table index - hash = (cvar_hash_t *)Z_Malloc(sizeof(cvar_hash_t)); - hashindex = CRC_Block((const unsigned char *)variable->name, strlen(variable->name)) % CVAR_HASHSIZE; - hash->next = cvars->hashtable[hashindex]; - hash->cvar = variable; - cvars->hashtable[hashindex] = hash; + Cvar_Link(variable, cvars); } /* @@ -668,64 +692,63 @@ Cvar_Get Adds a newly allocated variable to the variable list or sets its value. ============ */ -cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int flags, const char *newdescription) +cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, unsigned flags, const char *newdescription) { - int hashindex; - cvar_hash_t *hash; - cvar_t *current, *next, *cvar; + cvar_t *cvar; int i; if (developer_extra.integer) Con_DPrintf("Cvar_Get(\"%s\", \"%s\", %i);\n", name, value, flags); -// first check to see if it has already been defined + // first check to see if it has already been defined cvar = Cvar_FindVar(cvars, name, ~0); if (cvar) { cvar->flags |= flags; Cvar_SetQuick_Internal (cvar, value); - if(newdescription && (cvar->flags & CVAR_ALLOCATED)) + if(newdescription && (cvar->flags & CF_ALLOCATED)) { if(cvar->description != cvar_dummy_description) Z_Free((char *)cvar->description); if(*newdescription) - cvar->description = (char *)Mem_strdup(zonemempool, newdescription); + cvar->description = Z_strdup(newdescription); else cvar->description = cvar_dummy_description; } return cvar; } -// check for pure evil + // check for pure evil if (!*name) { - Con_Printf("Cvar_Get: invalid variable name\n"); + Con_Printf(CON_WARN "Cvar_Get: invalid variable name\n"); return NULL; } -// check for overlap with a command - if (Cmd_Exists(&cmd_client, name) || Cmd_Exists(&cmd_server, name)) + // check for overlap with a command + if (Cmd_Exists(cmd_local, name)) { - Con_Printf("Cvar_Get: %s is a command\n", name); + Con_Printf(CON_WARN "Cvar_Get: %s is a command\n", name); return NULL; } -// allocate a new cvar, cvar name, and cvar string -// TODO: factorize the following code with the one at the end of Cvar_RegisterVariable() -// FIXME: these never get Z_Free'd + // allocate a new cvar, cvar name, and cvar string + // TODO: factorize the following code with the one at the end of Cvar_RegisterVariable() + // FIXME: these never get Z_Free'd cvar = (cvar_t *)Z_Malloc(sizeof(cvar_t)); - cvar->flags = flags | CVAR_ALLOCATED; - cvar->name = (char *)Mem_strdup(zonemempool, name); - cvar->string = (char *)Mem_strdup(zonemempool, value); - cvar->defstring = (char *)Mem_strdup(zonemempool, value); + cvar->flags = flags | CF_ALLOCATED; + cvar->name = Z_strdup(name); + cvar->string = Z_strdup(value); + cvar->defstring = Z_strdup(value); cvar->value = atof (cvar->string); cvar->integer = (int) cvar->value; - cvar->aliases = (char **)Z_Malloc(sizeof(char **)); - memset(cvar->aliases, 0, sizeof(char *)); + cvar->aliases = NULL; + cvar->aliases_size = 0; + cvar->initstring = NULL; if(newdescription && *newdescription) - cvar->description = (char *)Mem_strdup(zonemempool, newdescription); + cvar->description = Z_strdup(newdescription); else cvar->description = cvar_dummy_description; // actually checked by VM_cvar_type @@ -733,34 +756,68 @@ cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int f for (i = 0;i < PRVM_PROG_MAX;i++) cvar->globaldefindex[i] = -1; -// link the variable in -// alphanumerical order - for( current = NULL, next = cvars->vars ; next && strcmp( next->name, cvar->name ) < 0 ; current = next, next = next->next ) - ; - if( current ) - current->next = cvar; + Cvar_Link(cvar, cvars); + + return cvar; +} + +/// For "quiet" mode pass NULL as the callername. +/// Returns true if the cvar was deleted. +static qbool Cvar_Delete(cvar_state_t *cvars, const char *name, const char *callername) +{ + cvar_t *cvar, *prev; + cvar_hash_t **hashlinkptr, *oldhashlink; + const char *progname; + + hashlinkptr = Cvar_FindVarLink(cvars, name, &prev, ~0); + if(!hashlinkptr) + { + if (callername) + Con_Printf("%s: cvar \"%s\" is not defined.\n", callername, name); + return false; + } + cvar = (*hashlinkptr)->cvar; + + if(!(cvar->flags & CF_ALLOCATED)) + { + if (callername) + Con_Printf(CON_WARN "%s: engine cvar \"%s\" cannot be deleted!\n", callername, cvar->name); + return false; + } + if ((progname = Cvar_IsAutoCvar(cvar))) + { + if (callername) + Con_Printf(CON_WARN "%s: unable to delete cvar \"%s\", it is an autocvar used by running %s progs!\n", callername, cvar->name, progname); + return false; + } + + if(cvar == cvars->vars) + cvars->vars = cvar->next; else - cvars->vars = cvar; - cvar->next = next; + prev->next = cvar->next; - // link to head of list in this hash table index - hash = (cvar_hash_t *)Z_Malloc(sizeof(cvar_hash_t)); - hashindex = CRC_Block((const unsigned char *)cvar->name, strlen(cvar->name)) % CVAR_HASHSIZE; - hash->next = cvars->hashtable[hashindex]; - cvars->hashtable[hashindex] = hash; - hash->cvar = cvar; + if(cvar->description != cvar_dummy_description) + Z_Free((char *)cvar->description); + Z_Free((char *)cvar->name); + Z_Free((char *)cvar->string); + Z_Free((char *)cvar->defstring); + Z_Free(cvar); - return cvar; + oldhashlink = *hashlinkptr; + *hashlinkptr = (*hashlinkptr)->next; + Z_Free(oldhashlink); + + return true; } -qboolean Cvar_Readonly (cvar_t *var, const char *cmd_name) +qbool Cvar_Readonly (cvar_t *var, const char *cmd_name) { - if (var->flags & CVAR_READONLY) + if (var->flags & CF_READONLY) { + Con_Print(CON_WARN); if(cmd_name) Con_Printf("%s: ",cmd_name); - Con_Printf("%s", var->name); - Con_Printf(" is read-only\n"); + Con_Printf("%s is read-only\n", var->name); return true; } return false; @@ -773,17 +830,17 @@ Cvar_Command Handles variable inspection and changing from the console ============ */ -qboolean Cvar_Command (cmd_state_t *cmd) +qbool Cvar_Command (cmd_state_t *cmd) { cvar_state_t *cvars = cmd->cvars; cvar_t *v; -// check variables + // check variables v = Cvar_FindVar(cvars, Cmd_Argv(cmd, 0), (cmd->cvars_flagsmask)); if (!v) return false; -// perform a variable print or set + // perform a variable print or set if (Cmd_Argc(cmd) == 1) { Cvar_PrintHelp(v, Cmd_Argv(cmd, 0), true); @@ -792,27 +849,25 @@ qboolean Cvar_Command (cmd_state_t *cmd) if (developer_extra.integer) Con_DPrint("Cvar_Command: "); - + if(Cvar_Readonly(v, NULL)) return true; - + Cvar_SetQuick(v, Cmd_Argv(cmd, 1)); if (developer_extra.integer) Con_DPrint("\n"); return true; } - void Cvar_UnlockDefaults(cmd_state_t *cmd) { cvar_state_t *cvars = cmd->cvars; cvar_t *var; // unlock the default values of all cvars for (var = cvars->vars ; var ; var = var->next) - var->flags &= ~CVAR_DEFAULTSET; + var->flags &= ~CF_DEFAULTSET; } - void Cvar_LockDefaults_f(cmd_state_t *cmd) { cvar_state_t *cvars = cmd->cvars; @@ -820,12 +875,12 @@ void Cvar_LockDefaults_f(cmd_state_t *cmd) // lock in the default values of all cvars for (var = cvars->vars ; var ; var = var->next) { - if (!(var->flags & CVAR_DEFAULTSET)) + if (!(var->flags & CF_DEFAULTSET)) { size_t alloclen; //Con_Printf("locking cvar %s (%s -> %s)\n", var->name, var->string, var->defstring); - var->flags |= CVAR_DEFAULTSET; + var->flags |= CF_DEFAULTSET; Z_Free((char *)var->defstring); alloclen = strlen(var->string) + 1; var->defstring = (char *)Z_Malloc(alloclen); @@ -837,91 +892,47 @@ void Cvar_LockDefaults_f(cmd_state_t *cmd) void Cvar_SaveInitState(cvar_state_t *cvars) { cvar_t *c; + for (c = cvars->vars;c;c = c->next) - { - c->initstate = true; - c->initflags = c->flags; - c->initdefstring = Mem_strdup(zonemempool, c->defstring); - c->initstring = Mem_strdup(zonemempool, c->string); - c->initvalue = c->value; - c->initinteger = c->integer; - VectorCopy(c->vector, c->initvector); - } + if ((c->flags & (CF_PERSISTENT | CF_READONLY)) == 0) + c->initstring = Z_strdup(c->string); } void Cvar_RestoreInitState(cvar_state_t *cvars) { - int hashindex; - cvar_t *c, **cp; - cvar_t *c2, **cp2; - for (cp = &cvars->vars;(c = *cp);) + cvar_t *c, *cp; + + for (cp = cvars->vars; (c = cp);) { - if (c->initstate) + cp = c->next; // get next cvar now in case we delete this cvar + + if (c->initstring) { // restore this cvar, it existed at init - if (((c->flags ^ c->initflags) & CVAR_MAXFLAGSVAL) - || strcmp(c->defstring ? c->defstring : "", c->initdefstring ? c->initdefstring : "") - || strcmp(c->string ? c->string : "", c->initstring ? c->initstring : "")) - { - Con_DPrintf("Cvar_RestoreInitState: Restoring cvar \"%s\"\n", c->name); - if (c->defstring) - Z_Free((char *)c->defstring); - c->defstring = Mem_strdup(zonemempool, c->initdefstring); - if (c->string) - Z_Free((char *)c->string); - c->string = Mem_strdup(zonemempool, c->initstring); - } - c->flags = c->initflags; - c->value = c->initvalue; - c->integer = c->initinteger; - VectorCopy(c->initvector, c->vector); - cp = &c->next; + Con_DPrintf("Cvar_RestoreInitState: Restoring cvar \"%s\"\n", c->name); + + /* bones_was_here: intentionally NOT restoring defstring in this function. + * The only callsite, Host_LoadConfig_f, will re-exec quake.rc, default.cfg, etc. + * Defaults are unlocked here, so Cvar_LockDefaults_f will reset the defstring. + * This is more correct than restoring the defstring here + * because a gamedir change could load configs that should change defaults. + */ + + Cvar_SetQuick(c, c->initstring); + c->flags &= ~CF_DEFAULTSET; } - else + else if (!(c->flags & (CF_PERSISTENT | CF_READONLY))) // cvars with those flags have no initstring AND may not be deleted or reset { - if (!(c->flags & CVAR_ALLOCATED)) - { - Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it was registered after init!\n", c->name); - // In this case, at least reset it to the default. - if((c->flags & CVAR_NORESETTODEFAULTS) == 0) - Cvar_SetQuick(c, c->defstring); - cp = &c->next; - continue; - } - if (Cvar_IsAutoCvar(c)) - { - Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it is an autocvar used by running progs!\n", c->name); - // In this case, at least reset it to the default. - if((c->flags & CVAR_NORESETTODEFAULTS) == 0) - Cvar_SetQuick(c, c->defstring); - cp = &c->next; - continue; - } // remove this cvar, it did not exist at init - Con_DPrintf("Cvar_RestoreInitState: Destroying cvar \"%s\"\n", c->name); - // unlink struct from hash - hashindex = CRC_Block((const unsigned char *)c->name, strlen(c->name)) % CVAR_HASHSIZE; - for (cp2 = &cvars->hashtable[hashindex]->cvar;(c2 = *cp2);) + Con_DPrintf("Cvar_RestoreInitState: cvar \"%s\"", c->name); + if (Cvar_Delete(cvars, c->name, NULL)) + Con_DPrint("deleted.\n"); + else { - if (c2 == c) - { - *cp2 = cvars->hashtable[hashindex]->next->cvar; - break; - } - else - cp2 = &cvars->hashtable[hashindex]->next->cvar; + // In this case, at least reset it to the default. + Con_DPrint("reset.\n"); + Cvar_SetQuick(c, c->defstring); } - // unlink struct from main list - *cp = c->next; - // free strings - if (c->defstring) - Z_Free((char *)c->defstring); - if (c->string) - Z_Free((char *)c->string); - if (c->description && c->description != cvar_dummy_description) - Z_Free((char *)c->description); - // free struct - Z_Free(c); } } } @@ -933,12 +944,11 @@ void Cvar_ResetToDefaults_All_f(cmd_state_t *cmd) // restore the default values of all cvars for (var = cvars->vars ; var ; var = var->next) { - if((var->flags & CVAR_NORESETTODEFAULTS) == 0) + if((var->flags & (CF_PERSISTENT | CF_READONLY)) == 0) Cvar_SetQuick(var, var->defstring); } } - void Cvar_ResetToDefaults_NoSaveOnly_f(cmd_state_t *cmd) { cvar_state_t *cvars = cmd->cvars; @@ -946,7 +956,7 @@ void Cvar_ResetToDefaults_NoSaveOnly_f(cmd_state_t *cmd) // restore the default values of all cvars for (var = cvars->vars ; var ; var = var->next) { - if ((var->flags & (CVAR_NORESETTODEFAULTS | CVAR_SAVE)) == 0) + if ((var->flags & (CF_PERSISTENT | CF_READONLY | CF_ARCHIVE)) == 0) Cvar_SetQuick(var, var->defstring); } } @@ -959,12 +969,11 @@ void Cvar_ResetToDefaults_SaveOnly_f(cmd_state_t *cmd) // restore the default values of all cvars for (var = cvars->vars ; var ; var = var->next) { - if ((var->flags & (CVAR_NORESETTODEFAULTS | CVAR_SAVE)) == CVAR_SAVE) + if ((var->flags & (CF_PERSISTENT | CF_READONLY | CF_ARCHIVE)) == CF_ARCHIVE) Cvar_SetQuick(var, var->defstring); } } - /* ============ Cvar_WriteVariables @@ -980,16 +989,15 @@ void Cvar_WriteVariables (cvar_state_t *cvars, qfile_t *f) // don't save cvars that match their default value for (var = cvars->vars ; var ; var = var->next) { - if ((var->flags & CVAR_SAVE) && (strcmp(var->string, var->defstring) || ((var->flags & CVAR_ALLOCATED) && !(var->flags & CVAR_DEFAULTSET)))) + if ((var->flags & CF_ARCHIVE) && (strcmp(var->string, var->defstring) || ((var->flags & CF_ALLOCATED) && !(var->flags & CF_DEFAULTSET)))) { Cmd_QuoteString(buf1, sizeof(buf1), var->name, "\"\\$", false); Cmd_QuoteString(buf2, sizeof(buf2), var->string, "\"\\$", false); - FS_Printf(f, "%s\"%s\" \"%s\"\n", var->flags & CVAR_ALLOCATED ? "seta " : "", buf1, buf2); + FS_Printf(f, "%s\"%s\" \"%s\"\n", var->flags & CF_ALLOCATED ? "seta " : "", buf1, buf2); } } } - // Added by EvilTypeGuy eviltypeguy@qeradiant.com // 2000-01-09 CvarList command By Matthias "Maddes" Buecher, http://www.inside3d.com/qip/ /* @@ -1002,39 +1010,47 @@ void Cvar_List_f(cmd_state_t *cmd) cvar_state_t *cvars = cmd->cvars; cvar_t *cvar; const char *partial; - size_t len; int count; - qboolean ispattern; + qbool ispattern; + char vabuf[1024]; if (Cmd_Argc(cmd) > 1) { partial = Cmd_Argv(cmd, 1); - len = strlen(partial); ispattern = (strchr(partial, '*') || strchr(partial, '?')); + if(!ispattern) + partial = va(vabuf, sizeof(vabuf), "%s*", partial); } else { - partial = NULL; - len = 0; + partial = va(vabuf, sizeof(vabuf), "*"); ispattern = false; } count = 0; for (cvar = cvars->vars; cvar; cvar = cvar->next) { - if (len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp (partial,cvar->name,len))) - continue; - - Cvar_PrintHelp(cvar, cvar->name, true); - count++; + if (matchpattern_with_separator(cvar->name, partial, false, "", false)) + { + Cvar_PrintHelp(cvar, cvar->name, true); + count++; + } + for (char **alias = cvar->aliases; alias && *alias; alias++) + { + if (matchpattern_with_separator(*alias, partial, false, "", false)) + { + Cvar_PrintHelp(cvar, *alias, true); + count++; + } + } } - if (len) + if (Cmd_Argc(cmd) > 1) { if(ispattern) Con_Printf("%i cvar%s matching \"%s\"\n", count, (count > 1) ? "s" : "", partial); else - Con_Printf("%i cvar%s beginning with \"%s\"\n", count, (count > 1) ? "s" : "", partial); + Con_Printf("%i cvar%s beginning with \"%s\"\n", count, (count > 1) ? "s" : "", Cmd_Argv(cmd,1)); } else Con_Printf("%i cvar(s)\n", count); @@ -1088,16 +1104,13 @@ void Cvar_SetA_f(cmd_state_t *cmd) Con_DPrint("SetA: "); // all looks ok, create/modify the cvar - Cvar_Get(cvars, Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), cmd->cvars_flagsmask | CVAR_SAVE, Cmd_Argc(cmd) > 3 ? Cmd_Argv(cmd, 3) : NULL); + Cvar_Get(cvars, Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), cmd->cvars_flagsmask | CF_ARCHIVE, Cmd_Argc(cmd) > 3 ? Cmd_Argv(cmd, 3) : NULL); } void Cvar_Del_f(cmd_state_t *cmd) { cvar_state_t *cvars = cmd->cvars; - int neededflags = ~0; int i; - cvar_hash_t *hash, *parent, **link; - cvar_t *cvar, *prev; if(Cmd_Argc(cmd) < 2) { @@ -1105,45 +1118,7 @@ void Cvar_Del_f(cmd_state_t *cmd) return; } for(i = 1; i < Cmd_Argc(cmd); ++i) - { - hash = Cvar_FindVarLink(cvars, Cmd_Argv(cmd, i), &parent, &link, &prev, neededflags); - cvar = hash->cvar; - - if(!cvar) - { - Con_Printf("%s: %s is not defined\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, i)); - continue; - } - if(Cvar_Readonly(cvar, Cmd_Argv(cmd, 0))) - continue; - if(!(cvar->flags & CVAR_ALLOCATED)) - { - Con_Printf("%s: %s is static and cannot be deleted\n", Cmd_Argv(cmd, 0), cvar->name); - continue; - } - if(cvar == cvars->vars) - { - cvars->vars = cvar->next; - } - else - { - // in this case, prev must be set, otherwise there has been some inconsistensy - // elsewhere already... should I still check for prev != NULL? - prev->next = cvar->next; - } - - if(parent) - parent->next = hash->next; - else if(link) - *link = hash->next; - if(cvar->description != cvar_dummy_description) - Z_Free((char *)cvar->description); - - Z_Free((char *)cvar->name); - Z_Free((char *)cvar->string); - Z_Free((char *)cvar->defstring); - Z_Free(cvar); - } + Cvar_Delete(cvars, Cmd_Argv(cmd, i), Cmd_Argv(cmd, 0)); } #ifdef FILLALLCVARSWITHRUBBISH @@ -1152,7 +1127,7 @@ void Cvar_FillAll_f(cmd_state_t *cmd) char *buf, *p, *q; int n, i; cvar_t *var; - qboolean verify; + qbool verify; if(Cmd_Argc(cmd) != 2) { Con_Printf("Usage: %s length to plant rubbish\n", Cmd_Argv(cmd, 0));