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