1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: c_cvars.cpp 4521 2014-01-22 17:55:01Z dr_sean $
5 //
6 // Copyright (C) 1998-2006 by Randy Heit (ZDoom).
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 //	Command-line variables
21 //
22 //-----------------------------------------------------------------------------
23 
24 
25 #include <cstring>
26 #include <cmath>
27 #include <stdio.h>
28 
29 #include "cmdlib.h"
30 #include "c_console.h"
31 #include "c_dispatch.h"
32 #include "m_alloc.h"
33 
34 #include "doomstat.h"
35 #include "c_cvars.h"
36 #include "d_player.h"
37 
38 #include "d_netinf.h"
39 #include "gstrings.h"
40 
41 #include "i_system.h"
42 
43 bool cvar_t::m_DoNoSet = false;
44 bool cvar_t::m_UseCallback = false;
45 
46 // denis - all this class does is delete the cvars during its static destruction
47 class ad_t {
48 public:
GetCVars()49 	cvar_t *&GetCVars() { static cvar_t *CVars; return CVars; }
ad_t()50 	ad_t() {}
~ad_t()51 	~ad_t()
52 	{
53 		cvar_t *cvar = GetCVars();
54 		while (cvar)
55 		{
56 			cvar_t *next = cvar->GetNext();
57 			if(cvar->m_Flags & CVAR_AUTO)
58 				delete cvar;
59 			cvar = next;
60 		}
61 	}
62 } ad;
63 
GetFirstCvar(void)64 cvar_t* GetFirstCvar(void)
65 {
66 	return ad.GetCVars();
67 }
68 
69 int cvar_defflags;
70 
cvar_t(const char * var_name,const char * def,const char * help,cvartype_t type,DWORD flags,float minval,float maxval)71 cvar_t::cvar_t(const char* var_name, const char* def, const char* help, cvartype_t type,
72 		DWORD flags, float minval, float maxval)
73 {
74 	InitSelf(var_name, def, help, type, flags, NULL, minval, maxval);
75 }
76 
cvar_t(const char * var_name,const char * def,const char * help,cvartype_t type,DWORD flags,void (* callback)(cvar_t &),float minval,float maxval)77 cvar_t::cvar_t(const char* var_name, const char* def, const char* help, cvartype_t type,
78 		DWORD flags, void (*callback)(cvar_t &), float minval, float maxval)
79 {
80 	InitSelf(var_name, def, help, type, flags, callback, minval, maxval);
81 }
82 
InitSelf(const char * var_name,const char * def,const char * help,cvartype_t type,DWORD var_flags,void (* callback)(cvar_t &),float minval,float maxval)83 void cvar_t::InitSelf(const char* var_name, const char* def, const char* help, cvartype_t type,
84 		DWORD var_flags, void (*callback)(cvar_t &), float minval, float maxval)
85 {
86 	cvar_t* dummy;
87 	cvar_t* var = FindCVar(var_name, &dummy);
88 
89 	m_Callback = callback;
90 	m_String = "";
91 	m_Value = 0.0f;
92 	m_Flags = 0;
93 	m_LatchedString = "";
94     m_HelpText = help;
95     m_Type = type;
96 
97 	if (var_flags & CVAR_NOENABLEDISABLE)
98 	{
99 		m_MinValue = minval;
100 		m_MaxValue = maxval;
101 	}
102 	else
103 	{
104 		m_MinValue = 0.0f;
105 		m_MaxValue = 1.0f;
106 	}
107 
108 	if (def)
109 		m_Default = def;
110 	else
111 		m_Default = "";
112 
113 	if (var_name)
114 	{
115 		C_AddTabCommand(var_name);
116 		m_Name = var_name;
117 		m_Next = ad.GetCVars();
118 		ad.GetCVars() = this;
119 	}
120 	else
121 		m_Name = "";
122 
123 	if (var)
124 	{
125 		ForceSet(var->m_String.c_str());
126 		if (var->m_Flags & CVAR_AUTO)
127 			delete var;
128 		else
129 			var->~cvar_t();
130 	}
131 	else if (def)
132 		ForceSet(def);
133 
134 	m_Flags = var_flags | CVAR_ISDEFAULT;
135 }
136 
~cvar_t()137 cvar_t::~cvar_t ()
138 {
139 	if (m_Name.length())
140 	{
141 		cvar_t *var, *dummy = NULL;
142 
143 		var = FindCVar (m_Name.c_str(), &dummy);
144 
145 		if (var == this)
146 		{
147 			if (dummy)
148 				dummy->m_Next = m_Next;
149 			else
150 				ad.GetCVars() = m_Next;
151 		}
152 	}
153 }
154 
ForceSet(const char * valstr)155 void cvar_t::ForceSet(const char* valstr)
156 {
157 	// [SL] 2013-04-16 - Latched CVARs do not change values until the next map.
158 	// Servers and single-player games should abide by this behavior but
159 	// multiplayer clients should just do what the server tells them.
160 	if (m_Flags & CVAR_LATCH && serverside &&
161 		(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
162 	{
163 		m_Flags |= CVAR_MODIFIED;
164 		if (valstr)
165 			m_LatchedString = valstr;
166 		else
167 			m_LatchedString.clear();
168 	}
169 	else
170 	{
171 		m_Flags |= CVAR_MODIFIED;
172 
173 		bool numerical_value = IsRealNum(valstr);
174 		bool integral_type = m_Type == CVARTYPE_BOOL || m_Type == CVARTYPE_BYTE ||
175 					m_Type == CVARTYPE_WORD || m_Type == CVARTYPE_INT;
176 		bool floating_type = m_Type == CVARTYPE_FLOAT;
177 		float valf = numerical_value ? atof(valstr) : 0.0f;
178 
179 		// perform rounding to nearest integer for integral types
180 		if (integral_type)
181 			valf = floor(valf + 0.5f);
182 
183 		valf = clamp(valf, m_MinValue, m_MaxValue);
184 
185 		if (numerical_value || integral_type || floating_type)
186 		{
187 			// generate m_String based on the clamped valf value
188 			char tmp[32];
189 			sprintf(tmp, "%g", valf);
190 			m_String = tmp;
191 		}
192 		else
193 		{
194 			// just set m_String to valstr
195 			if (valstr)
196 				m_String = valstr;
197 			else
198 				m_String.clear();
199 		}
200 
201 		m_Value = valf;
202 
203 		if (m_Flags & CVAR_USERINFO)
204 			D_UserInfoChanged(this);
205 		if (m_Flags & CVAR_SERVERINFO)
206 			D_SendServerInfoChange(this, m_String.c_str());
207 
208 		if (m_UseCallback)
209 			Callback();
210 	}
211 
212 	m_Flags &= ~CVAR_ISDEFAULT;
213 }
214 
215 
ForceSet(float val)216 void cvar_t::ForceSet(float val)
217 {
218 	char string[32];
219 	sprintf(string, "%g", val);
220 	ForceSet(string);
221 }
222 
Set(const char * val)223 void cvar_t::Set (const char *val)
224 {
225 	if (!(m_Flags & CVAR_NOSET) || !m_DoNoSet)
226 		ForceSet (val);
227 }
228 
Set(float val)229 void cvar_t::Set (float val)
230 {
231 	if (!(m_Flags & CVAR_NOSET) || !m_DoNoSet)
232 		ForceSet (val);
233 }
234 
SetDefault(const char * val)235 void cvar_t::SetDefault (const char *val)
236 {
237 	if(val)
238 		m_Default = val;
239 	else
240 		m_Default = "";
241 
242 	if (m_Flags & CVAR_ISDEFAULT)
243 	{
244 		Set (val);
245 		m_Flags |= CVAR_ISDEFAULT;
246 	}
247 }
248 
RestoreDefault()249 void cvar_t::RestoreDefault ()
250 {
251 	Set(m_Default.c_str());
252 	m_Flags |= CVAR_ISDEFAULT;
253 }
254 
255 //
256 // cvar_t::Transfer
257 //
258 // Copies the value from one cvar to another and then removes the source cvar
259 //
Transfer(const char * fromname,const char * toname)260 void cvar_t::Transfer(const char *fromname, const char *toname)
261 {
262 	cvar_t *from, *to, *dummy;
263 
264 	from = FindCVar(fromname, &dummy);
265 	to = FindCVar(toname, &dummy);
266 
267 	if (from && to)
268 	{
269 		to->ForceSet(from->m_Value);
270 		to->ForceSet(from->m_String.c_str());
271 
272 		// remove the old cvar
273 		cvar_t *cur = ad.GetCVars();
274 		while (cur->m_Next != from)
275 			cur = cur->m_Next;
276 
277 		cur->m_Next = from->m_Next;
278 	}
279 }
280 
cvar_set(const char * var_name,const char * val)281 cvar_t *cvar_t::cvar_set (const char *var_name, const char *val)
282 {
283 	cvar_t *var, *dummy;
284 
285 	if ( (var = FindCVar (var_name, &dummy)) )
286 		var->Set (val);
287 
288 	return var;
289 }
290 
cvar_forceset(const char * var_name,const char * val)291 cvar_t *cvar_t::cvar_forceset (const char *var_name, const char *val)
292 {
293 	cvar_t *var, *dummy;
294 
295 	if ( (var = FindCVar (var_name, &dummy)) )
296 		var->ForceSet (val);
297 
298 	return var;
299 }
300 
EnableNoSet()301 void cvar_t::EnableNoSet ()
302 {
303 	m_DoNoSet = true;
304 }
305 
EnableCallbacks()306 void cvar_t::EnableCallbacks ()
307 {
308 	m_UseCallback = true;
309 	cvar_t *cvar = ad.GetCVars();
310 
311 	while (cvar)
312 	{
313 		cvar->Callback ();
314 		cvar = cvar->m_Next;
315 	}
316 }
317 
sortcvars(const void * a,const void * b)318 static int STACK_ARGS sortcvars (const void *a, const void *b)
319 {
320 	return strcmp (((*(cvar_t **)a))->name(), ((*(cvar_t **)b))->name());
321 }
322 
FilterCompactCVars(TArray<cvar_t * > & cvars,DWORD filter)323 void cvar_t::FilterCompactCVars (TArray<cvar_t *> &cvars, DWORD filter)
324 {
325 	cvar_t *cvar = ad.GetCVars();
326 	while (cvar)
327 	{
328 		if (cvar->m_Flags & filter)
329 			cvars.Push (cvar);
330 		cvar = cvar->m_Next;
331 	}
332 	if (cvars.Size () > 0)
333 	{
334 		qsort (&cvars[0], cvars.Size (), sizeof(cvar_t *), sortcvars);
335 	}
336 }
337 
C_WriteCVars(byte ** demo_p,DWORD filter,bool compact)338 void cvar_t::C_WriteCVars (byte **demo_p, DWORD filter, bool compact)
339 {
340 	cvar_t *cvar = ad.GetCVars();
341 	byte *ptr = *demo_p;
342 
343 	if (compact)
344 	{
345 		TArray<cvar_t *> cvars;
346 		ptr += sprintf ((char *)ptr, "\\\\%ux", (unsigned int)filter);
347 		FilterCompactCVars (cvars, filter);
348 		while (cvars.Pop (cvar))
349 		{
350 			ptr += sprintf ((char *)ptr, "\\%s", cvar->cstring());
351 		}
352 	}
353 	else
354 	{
355 		cvar = ad.GetCVars();
356 		while (cvar)
357 		{
358 			if (cvar->m_Flags & filter)
359 			{
360 				ptr += sprintf ((char *)ptr, "\\%s\\%s",
361 								cvar->name(), cvar->cstring());
362 			}
363 			cvar = cvar->m_Next;
364 		}
365 	}
366 
367 	*demo_p = ptr + 1;
368 }
369 
C_ReadCVars(byte ** demo_p)370 void cvar_t::C_ReadCVars (byte **demo_p)
371 {
372 	char *ptr = *((char **)demo_p);
373 	char *breakpt;
374 
375 	if (*ptr++ != '\\')
376 		return;
377 
378 	if (*ptr == '\\')
379 	{	// compact mode
380 		TArray<cvar_t *> cvars;
381 		cvar_t *cvar;
382 		DWORD filter;
383 
384 		ptr++;
385 		breakpt = strchr (ptr, '\\');
386 		*breakpt = 0;
387 		filter = strtoul (ptr, NULL, 16);
388 		*breakpt = '\\';
389 		ptr = breakpt + 1;
390 
391 		FilterCompactCVars (cvars, filter);
392 
393 		while (cvars.Pop (cvar))
394 		{
395 			breakpt = strchr (ptr, '\\');
396 			if (breakpt)
397 				*breakpt = 0;
398 			cvar->Set (ptr);
399 			if (breakpt)
400 			{
401 				*breakpt = '\\';
402 				ptr = breakpt + 1;
403 			}
404 			else
405 				break;
406 		}
407 	}
408 	else
409 	{
410 		char *value;
411 
412 		while ( (breakpt = strchr (ptr, '\\')) )
413 		{
414 			*breakpt = 0;
415 			value = breakpt + 1;
416 			if ( (breakpt = strchr (value, '\\')) )
417 				*breakpt = 0;
418 
419 			cvar_set (ptr, value);
420 
421 			*(value - 1) = '\\';
422 			if (breakpt)
423 			{
424 				*breakpt = '\\';
425 				ptr = breakpt + 1;
426 			}
427 			else
428 			{
429 				break;
430 			}
431 		}
432 	}
433 	*demo_p += strlen (*((char **)demo_p)) + 1;
434 }
435 
436 static struct backup_s
437 {
438 	std::string name, string;
439 } CVarBackups[MAX_BACKUPCVARS];
440 
441 static int numbackedup = 0;
442 
443 //
444 // C_BackupCVars
445 //
446 // Backup cvars for restoration later. Called before connecting to a server
447 // or a demo starts playing to save all cvars which could be changed while
448 // by the server or by playing a demo.
449 // [SL] bitflag can be used to filter which cvars are set to default.
450 // The default value for bitflag is 0xFFFFFFFF, which effectively disables
451 // the filtering.
452 //
C_BackupCVars(unsigned int bitflag)453 void cvar_t::C_BackupCVars (unsigned int bitflag)
454 {
455 	struct backup_s *backup = CVarBackups;
456 	cvar_t *cvar = ad.GetCVars();
457 
458 	while (cvar)
459 	{
460 		if (cvar->m_Flags & bitflag)
461 		{
462 			if (backup == &CVarBackups[MAX_BACKUPCVARS])
463 				I_Error ("C_BackupDemoCVars: Too many cvars to save (%d)", MAX_BACKUPCVARS);
464 			backup->name = cvar->m_Name;
465 			backup->string = cvar->m_String;
466 			backup++;
467 		}
468 		cvar = cvar->m_Next;
469 	}
470 	numbackedup = backup - CVarBackups;
471 }
472 
C_RestoreCVars(void)473 void cvar_t::C_RestoreCVars (void)
474 {
475 	struct backup_s *backup = CVarBackups;
476 	int i;
477 
478 	for (i = numbackedup; i; i--, backup++)
479 	{
480 		cvar_set (backup->name.c_str(), backup->string.c_str());
481 		backup->name = backup->string = "";
482 	}
483 	numbackedup = 0;
484 	UnlatchCVars();
485 }
486 
FindCVar(const char * var_name,cvar_t ** prev)487 cvar_t *cvar_t::FindCVar (const char *var_name, cvar_t **prev)
488 {
489 	cvar_t *var;
490 
491 	if (var_name == NULL)
492 		return NULL;
493 
494 	var = ad.GetCVars();
495 	*prev = NULL;
496 	while (var)
497 	{
498 		if (iequals(var->m_Name, var_name))
499 			break;
500 		*prev = var;
501 		var = var->m_Next;
502 	}
503 	return var;
504 }
505 
UnlatchCVars(void)506 void cvar_t::UnlatchCVars (void)
507 {
508 	cvar_t *var;
509 
510 	var = ad.GetCVars();
511 	while (var)
512 	{
513 		if (var->m_Flags & (CVAR_MODIFIED | CVAR_LATCH))
514 		{
515 			unsigned oldflags = var->m_Flags & ~CVAR_MODIFIED;
516 			var->m_Flags &= ~(CVAR_LATCH);
517 			if (var->m_LatchedString.length())
518 			{
519 				var->Set (var->m_LatchedString.c_str());
520 				var->m_LatchedString = "";
521 			}
522 			var->m_Flags = oldflags;
523 		}
524 		var = var->m_Next;
525 	}
526 }
527 
528 //
529 // C_SetCvarsToDefault
530 //
531 // Initialize cvars to default values after they are created.
532 // [SL] bitflag can be used to filter which cvars are set to default.
533 // The default value for bitflag is 0xFFFFFFFF, which effectively disables
534 // the filtering.
535 //
C_SetCVarsToDefaults(unsigned int bitflag)536 void cvar_t::C_SetCVarsToDefaults (unsigned int bitflag)
537 {
538 	cvar_t *cvar = ad.GetCVars();
539 
540 	while (cvar)
541 	{
542 		if (cvar->m_Flags & bitflag)
543 		{
544 			if (cvar->m_Default.length())
545 				cvar->Set (cvar->m_Default.c_str());
546 		}
547 
548 		cvar = cvar->m_Next;
549 	}
550 }
551 
C_ArchiveCVars(void * f)552 void cvar_t::C_ArchiveCVars (void *f)
553 {
554 	cvar_t *cvar = ad.GetCVars();
555 
556 	while (cvar)
557 	{
558 		if ((baseapp == client && (cvar->m_Flags & CVAR_CLIENTARCHIVE))
559 			|| (baseapp == server && (cvar->m_Flags & CVAR_SERVERARCHIVE)))
560 		{
561 			fprintf ((FILE *)f, "// %s\n", cvar->helptext());
562 			fprintf ((FILE *)f, "set %s %s\n\n", C_QuoteString(cvar->name()).c_str(), C_QuoteString(cvar->cstring()).c_str());
563 		}
564 		cvar = cvar->m_Next;
565 	}
566 }
567 
cvarlist()568 void cvar_t::cvarlist()
569 {
570 	cvar_t *var = ad.GetCVars();
571 	int count = 0;
572 
573 	while (var)
574 	{
575 		unsigned flags = var->m_Flags;
576 
577 		count++;
578 		Printf (PRINT_HIGH, "%c%c%c%c %s \"%s\"\n",
579 				flags & CVAR_ARCHIVE ? 'A' :
580 					flags & CVAR_CLIENTARCHIVE ? 'C' :
581 					flags & CVAR_SERVERARCHIVE ? 'S' : ' ',
582 				flags & CVAR_USERINFO ? 'U' : ' ',
583 				flags & CVAR_SERVERINFO ? 'S' : ' ',
584 				flags & CVAR_NOSET ? '-' :
585 					flags & CVAR_LATCH ? 'L' :
586 					flags & CVAR_UNSETTABLE ? '*' : ' ',
587 				var->name(),
588 				var->cstring());
589 		var = var->m_Next;
590 	}
591 	Printf (PRINT_HIGH, "%d cvars\n", count);
592 }
593 
594 
C_GetValueString(const cvar_t * var)595 static std::string C_GetValueString(const cvar_t* var)
596 {
597 	if (!var)
598 		return "unset";
599 
600 	if (var->flags() & CVAR_NOENABLEDISABLE)
601 		return '"' + var->str() + '"';
602 
603 	if (atof(var->cstring()) == 0.0f)
604 		return "disabled";
605 	else
606 		return "enabled";
607 }
608 
C_GetLatchedValueString(const cvar_t * var)609 static std::string C_GetLatchedValueString(const cvar_t* var)
610 {
611 	if (!var)
612 		return "unset";
613 
614 	if (!(var->flags() & CVAR_LATCH))
615 		return C_GetValueString(var);
616 
617 	if (var->flags() & CVAR_NOENABLEDISABLE)
618 		return '"' + var->latched() + '"';
619 
620 	if (atof(var->latched()) == 0.0f)
621 		return "disabled";
622 	else
623 		return "enabled";
624 }
625 
BEGIN_COMMAND(set)626 BEGIN_COMMAND (set)
627 {
628 	if (argc != 3)
629 	{
630 		Printf (PRINT_HIGH, "usage: set <variable> <value>\n");
631 	}
632 	else
633 	{
634 		cvar_t *var, *prev;
635 
636 		var = cvar_t::FindCVar (argv[1], &prev);
637 		if (!var)
638 			var = new cvar_t(argv[1], NULL, "", CVARTYPE_NONE,  CVAR_AUTO | CVAR_UNSETTABLE | cvar_defflags);
639 
640 		if (var->flags() & CVAR_NOSET)
641 		{
642 			Printf(PRINT_HIGH, "%s is write protected.\n", argv[1]);
643 			return;
644 		}
645 		else if (multiplayer && baseapp == client && (var->flags() & CVAR_SERVERINFO))
646 		{
647 			Printf (PRINT_HIGH, "%s is under server control.\n", argv[1]);
648 			return;
649 		}
650 
651 		// [Russell] - Allow the user to specify either 'enable' and 'disable',
652 		// this will get converted to either 1 or 0
653 		// [AM] Introduce zdoom-standard "true" and "false"
654 		if (!(var->flags() & CVAR_NOENABLEDISABLE))
655 		{
656 			if (strcmp("enabled", argv[2]) == 0 ||
657 			    strcmp("true", argv[2]) == 0)
658 			{
659 				argv[2] = (char *)"1";
660 			}
661 			else if (strcmp("disabled", argv[2]) == 0 ||
662 			         strcmp("false", argv[2]) == 0)
663 			{
664 				argv[2] = (char *)"0";
665 			}
666 		}
667 
668 		if (var->flags() & CVAR_LATCH)
669 		{
670 			// if new value is different from current value and latched value
671 			if (strcmp(var->cstring(), argv[2]) && strcmp(var->latched(), argv[2]) && gamestate == GS_LEVEL)
672 				Printf(PRINT_HIGH, "%s will be changed for next game.\n", argv[1]);
673 		}
674 
675 		var->Set(argv[2]);
676 	}
677 }
678 END_COMMAND (set)
679 
BEGIN_COMMAND(get)680 BEGIN_COMMAND (get)
681 {
682     cvar_t *prev;
683 	cvar_t *var;
684 
685     if (argc < 2)
686 	{
687 		Printf (PRINT_HIGH, "usage: get <variable>\n");
688         return;
689 	}
690 
691     var = cvar_t::FindCVar (argv[1], &prev);
692 
693 	if (var)
694 	{
695 		// [AM] Determine whose control the cvar is under
696 		std::string control;
697 		if (multiplayer && baseapp == client && (var->flags() & CVAR_SERVERINFO))
698 			control = " (server)";
699 
700 		// [Russell] - Don't make the user feel inadequate, tell
701 		// them its either enabled, disabled or its other value
702 		Printf(PRINT_HIGH, "\"%s\" is %s%s.\n",
703 				var->name(), C_GetValueString(var).c_str(), control.c_str());
704 
705 		if (var->flags() & CVAR_LATCH && var->flags() & CVAR_MODIFIED)
706 			Printf(PRINT_HIGH, "\"%s\" will be changed to %s.\n",
707 					var->name(), C_GetLatchedValueString(var).c_str());
708 	}
709 	else
710 	{
711 		Printf(PRINT_HIGH, "\"%s\" is unset.\n", argv[1]);
712 	}
713 }
714 END_COMMAND (get)
715 
BEGIN_COMMAND(toggle)716 BEGIN_COMMAND (toggle)
717 {
718     cvar_t *prev;
719 	cvar_t *var;
720 
721     if (argc < 2)
722 	{
723 		Printf (PRINT_HIGH, "usage: toggle <variable>\n");
724         return;
725 	}
726 
727     var = cvar_t::FindCVar (argv[1], &prev);
728 
729 	if (!var)
730 	{
731 		Printf(PRINT_HIGH, "\"%s\" is unset.\n", argv[1]);
732 	}
733 	else if (var->flags() & CVAR_NOENABLEDISABLE)
734 	{
735 		Printf(PRINT_HIGH, "\"%s\" cannot be toggled.\n", argv[1]);
736 	}
737 	else
738 	{
739 		if (var->flags() & CVAR_LATCH && var->flags() & CVAR_MODIFIED)
740 			var->Set(!atof(var->latched()));
741 		else
742 			var->Set(!var->value());
743 
744 		// [Russell] - Don't make the user feel inadequate, tell
745 		// them its either enabled, disabled or its other value
746 		Printf(PRINT_HIGH, "\"%s\" is %s.\n",
747 				var->name(), C_GetValueString(var).c_str());
748 
749 		if (var->flags() & CVAR_LATCH && var->flags() & CVAR_MODIFIED)
750 			Printf(PRINT_HIGH, "\"%s\" will be changed to %s.\n",
751 					var->name(), C_GetLatchedValueString(var).c_str());
752 	}
753 }
754 END_COMMAND (toggle)
755 
BEGIN_COMMAND(cvarlist)756 BEGIN_COMMAND (cvarlist)
757 {
758 	cvar_t::cvarlist();
759 }
760 END_COMMAND (cvarlist)
761 
BEGIN_COMMAND(help)762 BEGIN_COMMAND (help)
763 {
764     cvar_t *prev;
765     cvar_t *var;
766 
767     if (argc < 2)
768     {
769 		Printf (PRINT_HIGH, "usage: help <variable>\n");
770         return;
771     }
772 
773     var = cvar_t::FindCVar (argv[1], &prev);
774 
775     if (!var)
776     {
777         Printf (PRINT_HIGH, "\"%s\" is unset.\n", argv[1]);
778         return;
779     }
780 
781     Printf(PRINT_HIGH, "Help: %s - %s\n", var->name(), var->helptext());
782 }
783 END_COMMAND (help)
784 
785 VERSION_CONTROL (c_cvars_cpp, "$Id: c_cvars.cpp 4521 2014-01-22 17:55:01Z dr_sean $")
786