]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cvar.c
10eda39d5783ebbe5a85300c28aaeece42f0801b
[xonotic/darkplaces.git] / cvar.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2000-2021 DarkPlaces contributors
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 */
21 // cvar.c -- dynamic variable tracking
22
23 #include "quakedef.h"
24
25 const char *cvar_dummy_description = "custom cvar";
26 static const char *cvar_null_string = "";
27
28 cvar_state_t cvars_all;
29 cvar_state_t cvars_null;
30
31 /*
32 ============
33 Cvar_FindVar
34 ============
35 */
36 cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, int neededflags)
37 {
38         int hashindex;
39         cvar_hash_t *hash;
40
41         // use hash lookup to minimize search time
42         hashindex = CRC_Block((const unsigned char *)var_name, strlen(var_name)) % CVAR_HASHSIZE;
43         for (hash = cvars->hashtable[hashindex];hash;hash = hash->next)
44                 if (!strcmp (var_name, hash->cvar->name) && (hash->cvar->flags & neededflags))
45                         return hash->cvar;
46                 else
47                         for (char **alias = hash->cvar->aliases; alias && *alias; alias++)
48                                 if (!strcmp (var_name, *alias) && (hash->cvar->flags & neededflags))
49                                         return hash->cvar;
50         return NULL;
51 }
52
53 cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, int neededflags)
54 {
55         cvar_t *var;
56
57         if (*prev_var_name)
58         {
59                 var = Cvar_FindVar(cvars, prev_var_name, neededflags);
60                 if (!var)
61                         return NULL;
62                 var = var->next;
63         }
64         else
65                 var = cvars->vars;
66
67         // search for the next cvar matching the needed flags
68         while (var)
69         {
70                 if (var->flags & neededflags)
71                         break;
72                 var = var->next;
73         }
74         return var;
75 }
76
77 /**
78  * Returns a pointer to the pointer stored in hashtable[] (or the one it links to)
79  * because we'll need to update that when deleting a cvar as other cvar(s) may share its hashindex.
80  */
81 static cvar_hash_t **Cvar_FindVarLink(cvar_state_t *cvars, const char *var_name, cvar_t **parent, cvar_t ***link, cvar_t **prev_alpha, int neededflags)
82 {
83         int hashindex;
84         cvar_t *cvar;
85         cvar_hash_t **hashlinkptr;
86
87         // use hash lookup to minimize search time
88         hashindex = CRC_Block((const unsigned char *)var_name, strlen(var_name)) % CVAR_HASHSIZE;
89         if(parent) *parent = NULL;
90         if(prev_alpha) *prev_alpha = NULL;
91         if(link) *link = &cvars->hashtable[hashindex]->cvar;
92         hashlinkptr = &cvars->hashtable[hashindex];
93         for (hashlinkptr = &cvars->hashtable[hashindex]; *hashlinkptr; hashlinkptr = &(*hashlinkptr)->next)
94         {
95                 cvar = (*hashlinkptr)->cvar;
96                 if (!strcmp (var_name, cvar->name) && (cvar->flags & neededflags))
97                         goto match;
98                 else
99                         for (char **alias = cvar->aliases; alias && *alias; alias++)
100                                 if (!strcmp (var_name, *alias) && (cvar->flags & neededflags))
101                                         goto match;
102                 if(parent) *parent = cvar;
103         }
104         return NULL;
105 match:
106         if(!prev_alpha || cvar == cvars->vars)
107                 return hashlinkptr;
108
109         *prev_alpha = cvars->vars;
110         // if prev_alpha happens to become NULL then there has been some inconsistency elsewhere
111         // already - should I still insert '*prev_alpha &&' in the loop?
112         while((*prev_alpha)->next != cvar)
113                 *prev_alpha = (*prev_alpha)->next;
114         return hashlinkptr;
115 }
116
117 /*
118 ============
119 Cvar_VariableValue
120 ============
121 */
122 float Cvar_VariableValueOr(cvar_state_t *cvars, const char *var_name, float def, int neededflags)
123 {
124         cvar_t *var;
125
126         var = Cvar_FindVar(cvars, var_name, neededflags);
127         if (!var)
128                 return def;
129         return atof (var->string);
130 }
131
132 float Cvar_VariableValue(cvar_state_t *cvars, const char *var_name, int neededflags)
133 {
134         return Cvar_VariableValueOr(cvars, var_name, 0, neededflags);
135 }
136
137 /*
138 ============
139 Cvar_VariableString
140 ============
141 */
142 const char *Cvar_VariableStringOr(cvar_state_t *cvars, const char *var_name, const char *def, int neededflags)
143 {
144         cvar_t *var;
145
146         var = Cvar_FindVar(cvars, var_name, neededflags);
147         if (!var)
148                 return def;
149         return var->string;
150 }
151
152 const char *Cvar_VariableString(cvar_state_t *cvars, const char *var_name, int neededflags)
153 {
154         return Cvar_VariableStringOr(cvars, var_name, cvar_null_string, neededflags);
155 }
156
157 /*
158 ============
159 Cvar_VariableDefString
160 ============
161 */
162 const char *Cvar_VariableDefString(cvar_state_t *cvars, const char *var_name, int neededflags)
163 {
164         cvar_t *var;
165
166         var = Cvar_FindVar(cvars, var_name, neededflags);
167         if (!var)
168                 return cvar_null_string;
169         return var->defstring;
170 }
171
172 /*
173 ============
174 Cvar_VariableDescription
175 ============
176 */
177 const char *Cvar_VariableDescription(cvar_state_t *cvars, const char *var_name, int neededflags)
178 {
179         cvar_t *var;
180
181         var = Cvar_FindVar(cvars, var_name, neededflags);
182         if (!var)
183                 return cvar_null_string;
184         return var->description;
185 }
186
187
188 /*
189 ============
190 Cvar_CompleteVariable
191 ============
192 */
193 const char *Cvar_CompleteVariable(cvar_state_t *cvars, const char *partial, int neededflags)
194 {
195         cvar_t          *cvar;
196         size_t          len;
197
198         len = strlen(partial);
199
200         if (!len)
201                 return NULL;
202
203 // check functions
204         for (cvar=cvars->vars ; cvar ; cvar=cvar->next)
205                 if (!strncasecmp (partial,cvar->name, len) && (cvar->flags & neededflags))
206                         return cvar->name;
207
208         return NULL;
209 }
210
211
212 /*
213         CVar_CompleteCountPossible
214
215         New function for tab-completion system
216         Added by EvilTypeGuy
217         Thanks to Fett erich@heintz.com
218
219 */
220 int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, int neededflags)
221 {
222         cvar_t  *cvar;
223         size_t  len;
224         int             h;
225
226         h = 0;
227         len = strlen(partial);
228
229         if (!len)
230                 return  0;
231
232         // Loop through the cvars and count all possible matches
233         for (cvar = cvars->vars; cvar; cvar = cvar->next)
234                 if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags))
235                         h++;
236                 else
237                         for (char **alias = cvar->aliases; alias && *alias; alias++)
238                                 if (!strncasecmp(partial, *alias, len) && (cvar->flags & neededflags))
239                                         h++;
240                 
241         return h;
242 }
243
244 /*
245         CVar_CompleteBuildList
246
247         New function for tab-completion system
248         Added by EvilTypeGuy
249         Thanks to Fett erich@heintz.com
250         Thanks to taniwha
251
252 */
253 const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, int neededflags)
254 {
255         const cvar_t *cvar;
256         size_t len = 0;
257         size_t bpos = 0;
258         size_t sizeofbuf = (Cvar_CompleteCountPossible(cvars, partial, neededflags) + 1) * sizeof(const char *);
259         const char **buf;
260
261         len = strlen(partial);
262         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof(const char *));
263         // Loop through the alias list and print all matches
264         for (cvar = cvars->vars; cvar; cvar = cvar->next)
265                 if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags))
266                         buf[bpos++] = cvar->name;
267                 else
268                         for (char **alias = cvar->aliases; alias && *alias; alias++)
269                                 if (!strncasecmp(partial, *alias, len) && (cvar->flags & neededflags))
270                                         buf[bpos++] = *alias;
271                 
272
273         buf[bpos] = NULL;
274         return buf;
275 }
276
277 void Cvar_PrintHelp(cvar_t *cvar, const char *name, qbool full)
278 {
279         // Aliases are purple, cvars are yellow
280         if (strcmp(cvar->name, name))
281                 Con_Printf("^6%s^7 (alias of ^3%s^7)", name, cvar->name);
282         else
283                 Con_Printf("^3%s^7", name);
284         Con_Printf(" is \"%s^7\" [\"%s^7\"]", ((cvar->flags & CF_PRIVATE) ? "********"/*hunter2*/ : cvar->string), cvar->defstring);
285         if (full)
286                 Con_Printf(" %s", cvar->description);
287         Con_Print("\n");
288 }
289
290 // written by LadyHavoc
291 void Cvar_CompleteCvarPrint(cvar_state_t *cvars, const char *partial, int neededflags)
292 {
293         cvar_t *cvar;
294         size_t len = strlen(partial);
295         // Loop through the command list and print all matches
296         for (cvar = cvars->vars; cvar; cvar = cvar->next)
297                 if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags))
298                         Cvar_PrintHelp(cvar, cvar->name, true);
299                 else
300                         for (char **alias = cvar->aliases; alias && *alias; alias++)
301                                 if (!strncasecmp (partial, *alias, len) && (cvar->flags & neededflags))
302                                         Cvar_PrintHelp(cvar, *alias, true);
303
304                 
305 }
306
307 // check if a cvar is held by some progs
308 static qbool Cvar_IsAutoCvar(cvar_t *var)
309 {
310         int i;
311         prvm_prog_t *prog;
312         for (i = 0;i < PRVM_PROG_MAX;i++)
313         {
314                 prog = &prvm_prog_list[i];
315                 if (prog->loaded && var->globaldefindex[i] >= 0)
316                         return true;
317         }
318         return false;
319 }
320
321 // we assume that prog is already set to the target progs
322 static void Cvar_UpdateAutoCvar(cvar_t *var)
323 {
324         int i;
325         int j;
326         const char *s;
327         vec3_t v;
328         prvm_prog_t *prog;
329         for (i = 0;i < PRVM_PROG_MAX;i++)
330         {
331                 prog = &prvm_prog_list[i];
332                 if (prog->loaded && var->globaldefindex[i] >= 0)
333                 {
334                         // MUST BE SYNCED WITH prvm_edict.c PRVM_LoadProgs
335                         switch(prog->globaldefs[var->globaldefindex[i]].type & ~DEF_SAVEGLOBAL)
336                         {
337                         case ev_float:
338                                 PRVM_GLOBALFIELDFLOAT(prog->globaldefs[var->globaldefindex[i]].ofs) = var->value;
339                                 break;
340                         case ev_vector:
341                                 s = var->string;
342                                 VectorClear(v);
343                                 for (j = 0;j < 3;j++)
344                                 {
345                                         while (*s && ISWHITESPACE(*s))
346                                                 s++;
347                                         if (!*s)
348                                                 break;
349                                         v[j] = atof(s);
350                                         while (!ISWHITESPACE(*s))
351                                                 s++;
352                                         if (!*s)
353                                                 break;
354                                 }
355                                 VectorCopy(v, PRVM_GLOBALFIELDVECTOR(prog->globaldefs[var->globaldefindex[i]].ofs));
356                                 break;
357                         case ev_string:
358                                 PRVM_ChangeEngineString(prog, var->globaldefindex_stringno[i], var->string);
359                                 PRVM_GLOBALFIELDSTRING(prog->globaldefs[var->globaldefindex[i]].ofs) = var->globaldefindex_stringno[i];
360                                 break;
361                         }
362                 }
363         }
364 }
365
366 // called after loading a savegame
367 void Cvar_UpdateAllAutoCvars(cvar_state_t *cvars)
368 {
369         cvar_t *var;
370         for (var = cvars->vars ; var ; var = var->next)
371                 Cvar_UpdateAutoCvar(var);
372 }
373
374 void Cvar_Callback(cvar_t *var)
375 {
376         if (var == NULL)
377         {
378                 Con_Print(CON_WARN "Cvar_Callback: var == NULL\n");
379                 return;
380         }
381
382         if(var->callback)
383                 var->callback(var);
384 }
385
386 /*
387 ============
388 Cvar_Set
389 ============
390 */
391 extern cvar_t sv_disablenotify;
392 static void Cvar_SetQuick_Internal (cvar_t *var, const char *value)
393 {
394         qbool changed;
395         size_t valuelen;
396
397         changed = strcmp(var->string, value) != 0;
398         // LadyHavoc: don't reallocate when there is no change
399         if (!changed)
400                 goto cvar_callback;
401
402         // LadyHavoc: don't reallocate when the buffer is the same size
403         valuelen = strlen(value);
404         if (!var->string || strlen(var->string) != valuelen)
405         {
406                 Z_Free ((char *)var->string);   // free the old value string
407
408                 var->string = (char *)Z_Malloc (valuelen + 1);
409         }
410         memcpy ((char *)var->string, value, valuelen + 1);
411         var->value = atof (var->string);
412         var->integer = (int) var->value;
413         if ((var->flags & CF_NOTIFY) && sv.active && !sv_disablenotify.integer)
414                 SV_BroadcastPrintf("\003^3Server cvar \"%s\" changed to \"%s\"\n", var->name, var->string);
415 #if 0
416         // TODO: add infostring support to the server?
417         if ((var->flags & CF_SERVERINFO) && changed && sv.active)
418         {
419                 InfoString_SetValue(svs.serverinfo, sizeof(svs.serverinfo), var->name, var->string);
420                 if (sv.active)
421                 {
422                         MSG_WriteByte (&sv.reliable_datagram, svc_serverinfostring);
423                         MSG_WriteString (&sv.reliable_datagram, var->name);
424                         MSG_WriteString (&sv.reliable_datagram, var->string);
425                 }
426         }
427 #endif
428         if (var->flags & CF_USERINFO)
429                 CL_SetInfo(var->name, var->string, true, false, false, false);
430
431         Cvar_UpdateAutoCvar(var);
432
433 cvar_callback:
434         // Call the function stored in the cvar for bounds checking, cleanup, etc
435         Cvar_Callback(var);
436 }
437
438 void Cvar_SetQuick (cvar_t *var, const char *value)
439 {
440         if (var == NULL)
441         {
442                 Con_Print(CON_WARN "Cvar_SetQuick: var == NULL\n");
443                 return;
444         }
445
446         if (!(var->flags & CF_REGISTERED) && !(var->flags & CF_ALLOCATED))
447         {
448                 Con_Printf(CON_WARN "Warning: Cvar_SetQuick() cannot set unregistered cvar \"%s\"\n", var->name);
449                 return; // setting an unregistered engine cvar crashes
450         }
451
452         if (developer_extra.integer)
453                 Con_DPrintf("Cvar_SetQuick({\"%s\", \"%s\", %i, \"%s\"}, \"%s\");\n", var->name, var->string, var->flags, var->defstring, value);
454
455         Cvar_SetQuick_Internal(var, value);
456 }
457
458 void Cvar_Set(cvar_state_t *cvars, const char *var_name, const char *value)
459 {
460         cvar_t *var;
461         var = Cvar_FindVar(cvars, var_name, ~0);
462         if (var == NULL)
463         {
464                 Con_Printf(CON_WARN "Cvar_Set: variable %s not found\n", var_name);
465                 return;
466         }
467         Cvar_SetQuick(var, value);
468 }
469
470 /*
471 ============
472 Cvar_SetValue
473 ============
474 */
475 void Cvar_SetValueQuick(cvar_t *var, float value)
476 {
477         char val[MAX_INPUTLINE];
478
479         if ((float)((int)value) == value)
480                 dpsnprintf(val, sizeof(val), "%i", (int)value);
481         else
482                 dpsnprintf(val, sizeof(val), "%f", value);
483         Cvar_SetQuick(var, val);
484 }
485
486 void Cvar_SetValue(cvar_state_t *cvars, const char *var_name, float value)
487 {
488         char val[MAX_INPUTLINE];
489
490         if ((float)((int)value) == value)
491                 dpsnprintf(val, sizeof(val), "%i", (int)value);
492         else
493                 dpsnprintf(val, sizeof(val), "%f", value);
494         Cvar_Set(cvars, var_name, val);
495 }
496
497 void Cvar_RegisterCallback(cvar_t *variable, void (*callback)(cvar_t *))
498 {
499         if (variable == NULL)
500         {
501                 Con_Print(CON_WARN "Cvar_RegisterCallback: var == NULL\n");
502                 return;
503         }
504
505         if (!(variable->flags & cmd_local->cvars_flagsmask))
506         {
507                 if (developer_extra.integer)
508                         Con_DPrintf("^6Cvar_RegisterCallback: rejecting cvar \"%s\"\n", variable->name);
509                 return;
510         }
511
512         variable->callback = callback;
513 }
514
515 void Cvar_RegisterVirtual(cvar_t *variable, const char *name )
516 {
517         cvar_state_t *cvars = &cvars_all;
518         cvar_hash_t *hash;
519         int hashindex;
520
521         if (!(variable->flags & cmd_local->cvars_flagsmask))
522         {
523                 if (developer_extra.integer)
524                         Con_DPrintf("^6Cvar_RegisterVirtual: rejecting cvar \"%s\" alias \"%s\"\n", variable->name, name);
525                 return;
526         }
527
528         if(!*name)
529         {
530                 Con_Printf(CON_WARN "Cvar_RegisterVirtual: invalid virtual cvar name\n");
531                 return;
532         }
533
534         // check for overlap with a command
535         if (Cmd_Exists(cmd_local, name))
536         {
537                 Con_Printf(CON_WARN "Cvar_RegisterVirtual: %s is a command\n", name);
538                 return;
539         }
540
541         if(Cvar_FindVar(&cvars_all, name, 0))
542         {
543                 Con_Printf(CON_WARN "Cvar_RegisterVirtual: %s is a cvar\n", name);
544                 return;
545         }
546
547         // Resize the variable->aliases list to have room for another entry and a null terminator.
548         // This zero-pads when resizing, so we don't need to write the NULL terminator manually here.
549         // Also if aliases is NULL this allocates fresh for the correct size, so it's fine to just do this.
550         variable->aliases = (char **)Z_Realloc(variable->aliases, sizeof(char *) * (variable->aliases_size + 2));
551         // Add the new alias, and increment the number of aliases in the list
552         variable->aliases[variable->aliases_size++] = (char *)Z_strdup(name);
553
554         // link to head of list in this hash table index
555         hash = (cvar_hash_t *)Z_Malloc(sizeof(cvar_hash_t));
556         hashindex = CRC_Block((const unsigned char *)name, strlen(name)) % CVAR_HASHSIZE;
557         hash->next = cvars->hashtable[hashindex];
558         cvars->hashtable[hashindex] = hash;
559         hash->cvar = variable;
560 }
561
562 /*
563 ============
564 Cvar_Link
565
566 Links a variable to the variable list and hashtable
567 ============
568 */
569 static void Cvar_Link(cvar_t *variable, cvar_state_t *cvars)
570 {
571         cvar_t *current, *next;
572         cvar_hash_t *hash;
573         int hashindex;
574         /*
575          * Link the variable in
576          * alphanumerical order
577          */
578         for( current = NULL, next = cvars->vars ; next && strcmp( next->name, variable->name ) < 0 ; current = next, next = next->next )
579                 ;
580         if(current)
581                 current->next = variable;
582         else
583                 cvars->vars = variable;
584         variable->next = next;
585
586         // link to head of list in this hash table index
587         hash = (cvar_hash_t *)Z_Malloc(sizeof(cvar_hash_t));
588         hashindex = CRC_Block((const unsigned char *)variable->name, strlen(variable->name)) % CVAR_HASHSIZE;
589         hash->next = cvars->hashtable[hashindex];
590         hash->cvar = variable;
591         cvars->hashtable[hashindex] = hash;
592 }
593
594 /*
595 ============
596 Cvar_RegisterVariable
597
598 Adds a freestanding variable to the variable list.
599 ============
600 */
601 void Cvar_RegisterVariable (cvar_t *variable)
602 {
603         cvar_state_t *cvars = &cvars_all;
604         cvar_t *current, *cvar;
605         int i;
606
607         if (!(variable->flags & cmd_local->cvars_flagsmask))
608         {
609                 if (developer_extra.integer)
610                         Con_DPrintf("^2Cvar_RegisterVariable: rejecting cvar \"%s\"\n", variable->name);
611                 return;
612         }
613
614         if (developer_extra.integer)
615                 Con_DPrintf("Cvar_RegisterVariable({\"%s\", \"%s\", %i});\n", variable->name, variable->string, variable->flags);
616
617         // first check to see if it has already been defined
618         cvar = Cvar_FindVar(cvars, variable->name, ~0);
619         if (cvar)
620         {
621                 if (cvar->flags & CF_ALLOCATED)
622                 {
623                         if (developer_extra.integer)
624                                 Con_DPrintf("...  replacing existing allocated cvar {\"%s\", \"%s\", %i}\n", cvar->name, cvar->string, cvar->flags);
625                         // fixed variables replace allocated ones
626                         // (because the engine directly accesses fixed variables)
627                         // NOTE: this isn't actually used currently
628                         // (all cvars are registered before config parsing)
629                         variable->flags &= ~CF_ALLOCATED;
630                         // cvar->string is now owned by variable instead
631                         variable->string = cvar->string;
632                         variable->defstring = cvar->defstring;
633                         variable->value = atof (variable->string);
634                         variable->integer = (int) variable->value;
635                         // Preserve autocvar status.
636                         memcpy(variable->globaldefindex, cvar->globaldefindex, sizeof(variable->globaldefindex));
637                         memcpy(variable->globaldefindex_stringno, cvar->globaldefindex_stringno, sizeof(variable->globaldefindex_stringno));
638                         // replace cvar with this one...
639                         variable->next = cvar->next;
640                         if (cvars->vars == cvar)
641                         {
642                                 // head of the list is easy to change
643                                 cvars->vars = variable;
644                         }
645                         else
646                         {
647                                 // otherwise find it somewhere in the list
648                                 for (current = cvars->vars;current->next != cvar;current = current->next)
649                                         ;
650                                 current->next = variable;
651                         }
652
653                         // get rid of old allocated cvar
654                         // (but not cvar->string and cvar->defstring, because we kept those)
655                         Z_Free((char *)cvar->name);
656                         Z_Free(cvar);
657                 }
658                 else
659                         Con_DPrintf("Can't register variable %s, already defined\n", variable->name);
660                 return;
661         }
662
663         // check for overlap with a command
664         if (Cmd_Exists(cmd_local, variable->name))
665         {
666                 Con_Printf(CON_WARN "Cvar_RegisterVariable: %s is a command\n", variable->name);
667                 return;
668         }
669
670         // copy the value off, because future sets will Z_Free it
671         variable->name = (char *)Mem_strdup(zonemempool, variable->name);
672         variable->string = (char *)Mem_strdup(zonemempool, variable->string);
673         variable->defstring = (char *)Mem_strdup(zonemempool, variable->string);
674         variable->value = atof (variable->string);
675         variable->integer = (int) variable->value;
676         variable->aliases = NULL;
677         variable->aliases_size = 0;
678         variable->initstate = NULL;
679
680         // Mark it as not an autocvar.
681         for (i = 0;i < PRVM_PROG_MAX;i++)
682                 variable->globaldefindex[i] = -1;
683
684         // Safe for Cvar_SetQuick()
685         variable->flags |= CF_REGISTERED;
686
687         Cvar_Link(variable, cvars);
688 }
689
690 /*
691 ============
692 Cvar_Get
693
694 Adds a newly allocated variable to the variable list or sets its value.
695 ============
696 */
697 cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int flags, const char *newdescription)
698 {
699         cvar_t *cvar;
700         int i;
701
702         if (developer_extra.integer)
703                 Con_DPrintf("Cvar_Get(\"%s\", \"%s\", %i);\n", name, value, flags);
704
705         // first check to see if it has already been defined
706         cvar = Cvar_FindVar(cvars, name, ~0);
707         if (cvar)
708         {
709                 cvar->flags |= flags;
710                 Cvar_SetQuick_Internal (cvar, value);
711                 if(newdescription && (cvar->flags & CF_ALLOCATED))
712                 {
713                         if(cvar->description != cvar_dummy_description)
714                                 Z_Free((char *)cvar->description);
715
716                         if(*newdescription)
717                                 cvar->description = (char *)Mem_strdup(zonemempool, newdescription);
718                         else
719                                 cvar->description = cvar_dummy_description;
720                 }
721                 return cvar;
722         }
723
724         // check for pure evil
725         if (!*name)
726         {
727                 Con_Printf(CON_WARN "Cvar_Get: invalid variable name\n");
728                 return NULL;
729         }
730
731         // check for overlap with a command
732         if (Cmd_Exists(cmd_local, name))
733         {
734                 Con_Printf(CON_WARN "Cvar_Get: %s is a command\n", name);
735                 return NULL;
736         }
737
738         // allocate a new cvar, cvar name, and cvar string
739         // TODO: factorize the following code with the one at the end of Cvar_RegisterVariable()
740         // FIXME: these never get Z_Free'd
741         cvar = (cvar_t *)Z_Malloc(sizeof(cvar_t));
742         cvar->flags = flags | CF_ALLOCATED;
743         cvar->name = (char *)Mem_strdup(zonemempool, name);
744         cvar->string = (char *)Mem_strdup(zonemempool, value);
745         cvar->defstring = (char *)Mem_strdup(zonemempool, value);
746         cvar->value = atof (cvar->string);
747         cvar->integer = (int) cvar->value;
748         cvar->aliases = NULL;
749         cvar->aliases_size = 0;
750         cvar->initstate = NULL;
751
752         if(newdescription && *newdescription)
753                 cvar->description = (char *)Mem_strdup(zonemempool, newdescription);
754         else
755                 cvar->description = cvar_dummy_description; // actually checked by VM_cvar_type
756
757         // Mark it as not an autocvar.
758         for (i = 0;i < PRVM_PROG_MAX;i++)
759                 cvar->globaldefindex[i] = -1;
760
761         Cvar_Link(cvar, cvars);
762
763         return cvar;
764 }
765
766 qbool Cvar_Readonly (cvar_t *var, const char *cmd_name)
767 {
768         if (var->flags & CF_READONLY)
769         {
770                 Con_Print(CON_WARN);
771                 if(cmd_name)
772                         Con_Printf("%s: ",cmd_name);
773                 Con_Printf("%s is read-only\n", var->name);
774                 return true;
775         }
776         return false;
777 }
778
779 /*
780 ============
781 Cvar_Command
782
783 Handles variable inspection and changing from the console
784 ============
785 */
786 qbool   Cvar_Command (cmd_state_t *cmd)
787 {
788         cvar_state_t    *cvars = cmd->cvars;
789         cvar_t                  *v;
790
791         // check variables
792         v = Cvar_FindVar(cvars, Cmd_Argv(cmd, 0), (cmd->cvars_flagsmask));
793         if (!v)
794                 return false;
795
796         // perform a variable print or set
797         if (Cmd_Argc(cmd) == 1)
798         {
799                 Cvar_PrintHelp(v, Cmd_Argv(cmd, 0), true);
800                 return true;
801         }
802
803         if (developer_extra.integer)
804                 Con_DPrint("Cvar_Command: ");
805
806         if(Cvar_Readonly(v, NULL))
807                 return true;
808
809         Cvar_SetQuick(v, Cmd_Argv(cmd, 1));
810         if (developer_extra.integer)
811                 Con_DPrint("\n");
812         return true;
813 }
814
815 void Cvar_UnlockDefaults(cmd_state_t *cmd)
816 {
817         cvar_state_t *cvars = cmd->cvars;
818         cvar_t *var;
819         // unlock the default values of all cvars
820         for (var = cvars->vars ; var ; var = var->next)
821                 var->flags &= ~CF_DEFAULTSET;
822 }
823
824 void Cvar_LockDefaults_f(cmd_state_t *cmd)
825 {
826         cvar_state_t *cvars = cmd->cvars;
827         cvar_t *var;
828         // lock in the default values of all cvars
829         for (var = cvars->vars ; var ; var = var->next)
830         {
831                 if (!(var->flags & CF_DEFAULTSET))
832                 {
833                         size_t alloclen;
834
835                         //Con_Printf("locking cvar %s (%s -> %s)\n", var->name, var->string, var->defstring);
836                         var->flags |= CF_DEFAULTSET;
837                         Z_Free((char *)var->defstring);
838                         alloclen = strlen(var->string) + 1;
839                         var->defstring = (char *)Z_Malloc(alloclen);
840                         memcpy((char *)var->defstring, var->string, alloclen);
841                 }
842         }
843 }
844
845 void Cvar_SaveInitState(cvar_state_t *cvars)
846 {
847         cvar_t *c;
848         for (c = cvars->vars;c;c = c->next)
849         {
850                 c->initstate = (cvar_t *)Z_Malloc(sizeof(cvar_t));
851                 memcpy(c->initstate, c, sizeof(cvar_t));
852         }
853 }
854
855 void Cvar_RestoreInitState(cvar_state_t *cvars)
856 {
857         int hashindex;
858         cvar_t *c, **cp;
859         cvar_t *c2, **cp2;
860         for (cp = &cvars->vars;(c = *cp);)
861         {
862                 if (c->initstate)
863                 {
864                         // restore this cvar, it existed at init
865                         if (((c->flags ^ c->initstate->flags) & CF_MAXFLAGSVAL)
866                          || strcmp(c->defstring ? c->defstring : "", c->initstate->defstring ? c->initstate->defstring : "")
867                          || strcmp(c->string ? c->string : "", c->initstate->string ? c->initstate->string : ""))
868                         {
869                                 Con_DPrintf("Cvar_RestoreInitState: Restoring cvar \"%s\"\n", c->name);
870                                 if (c->defstring)
871                                         Z_Free((char *)c->defstring);
872                                 c->defstring = Mem_strdup(zonemempool, c->initstate->defstring);
873                                 if (c->string)
874                                         Z_Free((char *)c->string);
875                                 c->string = Mem_strdup(zonemempool, c->initstate->string);
876                         }
877                         c->flags = c->initstate->flags;
878                         c->value = c->initstate->value;
879                         c->integer = c->initstate->integer;
880                         VectorCopy(c->initstate->vector, c->vector);
881                         cp = &c->next;
882                 }
883                 else
884                 {
885                         if (!(c->flags & CF_ALLOCATED))
886                         {
887                                 Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it was registered after init!\n", c->name);
888                                 // In this case, at least reset it to the default.
889                                 if((c->flags & CF_PERSISTENT) == 0)
890                                         Cvar_SetQuick(c, c->defstring);
891                                 cp = &c->next;
892                                 continue;
893                         }
894                         if (Cvar_IsAutoCvar(c))
895                         {
896                                 Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it is an autocvar used by running progs!\n", c->name);
897                                 // In this case, at least reset it to the default.
898                                 if((c->flags & CF_PERSISTENT) == 0)
899                                         Cvar_SetQuick(c, c->defstring);
900                                 cp = &c->next;
901                                 continue;
902                         }
903                         // remove this cvar, it did not exist at init
904                         Con_DPrintf("Cvar_RestoreInitState: Destroying cvar \"%s\"\n", c->name);
905                         // unlink struct from hash
906                         hashindex = CRC_Block((const unsigned char *)c->name, strlen(c->name)) % CVAR_HASHSIZE;
907                         for (cp2 = &cvars->hashtable[hashindex]->cvar;(c2 = *cp2);)
908                         {
909                                 if (c2 == c)
910                                 {
911                                         *cp2 = cvars->hashtable[hashindex]->next->cvar;
912                                         break;
913                                 }
914                                 else
915                                         cp2 = &cvars->hashtable[hashindex]->next->cvar;
916                         }
917                         // unlink struct from main list
918                         *cp = c->next;
919                         // free strings
920                         if (c->defstring)
921                                 Z_Free((char *)c->defstring);
922                         if (c->string)
923                                 Z_Free((char *)c->string);
924                         if (c->description && c->description != cvar_dummy_description)
925                                 Z_Free((char *)c->description);
926                         // free struct
927                         Z_Free(c);
928                 }
929         }
930 }
931
932 void Cvar_ResetToDefaults_All_f(cmd_state_t *cmd)
933 {
934         cvar_state_t *cvars = cmd->cvars;
935         cvar_t *var;
936         // restore the default values of all cvars
937         for (var = cvars->vars ; var ; var = var->next)
938         {
939                 if((var->flags & CF_PERSISTENT) == 0)
940                         Cvar_SetQuick(var, var->defstring);
941         }
942 }
943
944 void Cvar_ResetToDefaults_NoSaveOnly_f(cmd_state_t *cmd)
945 {
946         cvar_state_t *cvars = cmd->cvars;
947         cvar_t *var;
948         // restore the default values of all cvars
949         for (var = cvars->vars ; var ; var = var->next)
950         {
951                 if ((var->flags & (CF_PERSISTENT | CF_ARCHIVE)) == 0)
952                         Cvar_SetQuick(var, var->defstring);
953         }
954 }
955
956
957 void Cvar_ResetToDefaults_SaveOnly_f(cmd_state_t *cmd)
958 {
959         cvar_state_t *cvars = cmd->cvars;
960         cvar_t *var;
961         // restore the default values of all cvars
962         for (var = cvars->vars ; var ; var = var->next)
963         {
964                 if ((var->flags & (CF_PERSISTENT | CF_ARCHIVE)) == CF_ARCHIVE)
965                         Cvar_SetQuick(var, var->defstring);
966         }
967 }
968
969 /*
970 ============
971 Cvar_WriteVariables
972
973 Writes lines containing "set variable value" for all variables
974 with the archive flag set to true.
975 ============
976 */
977 void Cvar_WriteVariables (cvar_state_t *cvars, qfile_t *f)
978 {
979         cvar_t  *var;
980         char buf1[MAX_INPUTLINE], buf2[MAX_INPUTLINE];
981
982         // don't save cvars that match their default value
983         for (var = cvars->vars ; var ; var = var->next) {
984                 if ((var->flags & CF_ARCHIVE) && (strcmp(var->string, var->defstring) || ((var->flags & CF_ALLOCATED) && !(var->flags & CF_DEFAULTSET))))
985                 {
986                         Cmd_QuoteString(buf1, sizeof(buf1), var->name, "\"\\$", false);
987                         Cmd_QuoteString(buf2, sizeof(buf2), var->string, "\"\\$", false);
988                         FS_Printf(f, "%s\"%s\" \"%s\"\n", var->flags & CF_ALLOCATED ? "seta " : "", buf1, buf2);
989                 }
990         }
991 }
992
993 // Added by EvilTypeGuy eviltypeguy@qeradiant.com
994 // 2000-01-09 CvarList command By Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
995 /*
996 =========
997 Cvar_List
998 =========
999 */
1000 void Cvar_List_f(cmd_state_t *cmd)
1001 {
1002         cvar_state_t *cvars = cmd->cvars;
1003         cvar_t *cvar;
1004         const char *partial;
1005         int count;
1006         qbool ispattern;
1007         char vabuf[1024];
1008
1009         if (Cmd_Argc(cmd) > 1)
1010         {
1011                 partial = Cmd_Argv(cmd, 1);
1012                 ispattern = (strchr(partial, '*') || strchr(partial, '?'));
1013                 if(!ispattern)
1014                         partial = va(vabuf, sizeof(vabuf), "%s*", partial);
1015         }
1016         else
1017         {
1018                 partial = va(vabuf, sizeof(vabuf), "*");
1019                 ispattern = false;
1020         }
1021
1022         count = 0;
1023         for (cvar = cvars->vars; cvar; cvar = cvar->next)
1024         {
1025                 if (matchpattern_with_separator(cvar->name, partial, false, "", false))
1026                 {
1027                         Cvar_PrintHelp(cvar, cvar->name, true);
1028                         count++;
1029                 }
1030                 for (char **alias = cvar->aliases; alias && *alias; alias++)
1031                 {
1032                         if (matchpattern_with_separator(*alias, partial, false, "", false))
1033                         {
1034                                 Cvar_PrintHelp(cvar, *alias, true);
1035                                 count++;
1036                         }
1037                 }
1038         }
1039
1040         if (Cmd_Argc(cmd) > 1)
1041         {
1042                 if(ispattern)
1043                         Con_Printf("%i cvar%s matching \"%s\"\n", count, (count > 1) ? "s" : "", partial);
1044                 else
1045                         Con_Printf("%i cvar%s beginning with \"%s\"\n", count, (count > 1) ? "s" : "", Cmd_Argv(cmd,1));
1046         }
1047         else
1048                 Con_Printf("%i cvar(s)\n", count);
1049 }
1050 // 2000-01-09 CvarList command by Maddes
1051
1052 void Cvar_Set_f(cmd_state_t *cmd)
1053 {
1054         cvar_state_t *cvars = cmd->cvars;
1055         cvar_t *cvar;
1056
1057         // make sure it's the right number of parameters
1058         if (Cmd_Argc(cmd) < 3)
1059         {
1060                 Con_Printf("Set: wrong number of parameters, usage: set <variablename> <value> [<description>]\n");
1061                 return;
1062         }
1063
1064         // check if it's read-only
1065         cvar = Cvar_FindVar(cvars, Cmd_Argv(cmd, 1), ~0);
1066         if (cvar)
1067                 if(Cvar_Readonly(cvar,"Set"))
1068                         return;
1069
1070         if (developer_extra.integer)
1071                 Con_DPrint("Set: ");
1072
1073         // all looks ok, create/modify the cvar
1074         Cvar_Get(cvars, Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), cmd->cvars_flagsmask, Cmd_Argc(cmd) > 3 ? Cmd_Argv(cmd, 3) : NULL);
1075 }
1076
1077 void Cvar_SetA_f(cmd_state_t *cmd)
1078 {
1079         cvar_state_t *cvars = cmd->cvars;
1080         cvar_t *cvar;
1081
1082         // make sure it's the right number of parameters
1083         if (Cmd_Argc(cmd) < 3)
1084         {
1085                 Con_Printf("SetA: wrong number of parameters, usage: seta <variablename> <value> [<description>]\n");
1086                 return;
1087         }
1088
1089         // check if it's read-only
1090         cvar = Cvar_FindVar(cvars, Cmd_Argv(cmd, 1), ~0);
1091         if (cvar)
1092                 if(Cvar_Readonly(cvar,"SetA"))
1093                         return;
1094
1095         if (developer_extra.integer)
1096                 Con_DPrint("SetA: ");
1097
1098         // all looks ok, create/modify the cvar
1099         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);
1100 }
1101
1102 void Cvar_Del_f(cmd_state_t *cmd)
1103 {
1104         cvar_state_t *cvars = cmd->cvars;
1105         int i;
1106         cvar_t *parent, **link;
1107         cvar_t *cvar, *prev;
1108         cvar_hash_t **hashlinkptr, *oldhashlink;
1109
1110         if(Cmd_Argc(cmd) < 2)
1111         {
1112                 Con_Printf("%s: wrong number of parameters, usage: unset <variablename1> [<variablename2> ...]\n", Cmd_Argv(cmd, 0));
1113                 return;
1114         }
1115         for(i = 1; i < Cmd_Argc(cmd); ++i)
1116         {
1117                 hashlinkptr = Cvar_FindVarLink(cvars, Cmd_Argv(cmd, i), &parent, &link, &prev, ~0);
1118
1119                 if(!hashlinkptr)
1120                 {
1121                         Con_Printf("%s: %s is not defined\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, i));
1122                         continue;
1123                 }
1124                 cvar = (*hashlinkptr)->cvar;
1125
1126                 if(Cvar_Readonly(cvar, Cmd_Argv(cmd, 0)))
1127                         continue;
1128                 if(!(cvar->flags & CF_ALLOCATED))
1129                 {
1130                         Con_Printf(CON_WARN "%s: %s is static and cannot be deleted\n", Cmd_Argv(cmd, 0), cvar->name);
1131                         continue;
1132                 }
1133
1134                 if(cvar == cvars->vars)
1135                 {
1136                         cvars->vars = cvar->next;
1137                 }
1138                 else
1139                 {
1140                         // in this case, prev must be set, otherwise there has been some inconsistensy
1141                         // elsewhere already... should I still check for prev != NULL?
1142                         prev->next = cvar->next;
1143                 }
1144
1145                 if(parent)
1146                         parent->next = cvar->next;
1147                 else if(link)
1148                         *link = cvar->next;
1149                 if(cvar->description != cvar_dummy_description)
1150                         Z_Free((char *)cvar->description);
1151
1152                 Z_Free((char *)cvar->name);
1153                 Z_Free((char *)cvar->string);
1154                 Z_Free((char *)cvar->defstring);
1155                 Z_Free(cvar);
1156
1157                 oldhashlink = *hashlinkptr;
1158                 *hashlinkptr = (*hashlinkptr)->next;
1159                 Z_Free(oldhashlink);
1160         }
1161 }
1162
1163 #ifdef FILLALLCVARSWITHRUBBISH
1164 void Cvar_FillAll_f(cmd_state_t *cmd)
1165 {
1166         char *buf, *p, *q;
1167         int n, i;
1168         cvar_t *var;
1169         qbool verify;
1170         if(Cmd_Argc(cmd) != 2)
1171         {
1172                 Con_Printf("Usage: %s length to plant rubbish\n", Cmd_Argv(cmd, 0));
1173                 Con_Printf("Usage: %s -length to verify that the rubbish is still there\n", Cmd_Argv(cmd, 0));
1174                 return;
1175         }
1176         n = atoi(Cmd_Argv(cmd, 1));
1177         verify = (n < 0);
1178         if(verify)
1179                 n = -n;
1180         buf = Z_Malloc(n + 1);
1181         buf[n] = 0;
1182         for(var = cvars->vars; var; var = var->next)
1183         {
1184                 for(i = 0, p = buf, q = var->name; i < n; ++i)
1185                 {
1186                         *p++ = *q++;
1187                         if(!*q)
1188                                 q = var->name;
1189                 }
1190                 if(verify && strcmp(var->string, buf))
1191                 {
1192                         Con_Printf("\n%s does not contain the right rubbish, either this is the first run or a possible overrun was detected, or something changed it intentionally; it DOES contain: %s\n", var->name, var->string);
1193                 }
1194                 Cvar_SetQuick(var, buf);
1195         }
1196         Z_Free(buf);
1197 }
1198 #endif /* FILLALLCVARSWITHRUBBISH */