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 #include <stdio.h>
22 
23 #include "cmd.h"
24 #include "common.h"
25 #include "console.h"
26 #include "cvar.h"
27 #include "shell.h"
28 #include "zone.h"
29 
30 #ifdef NQ_HACK
31 #include "server.h"
32 #include "quakedef.h"
33 #include "host.h"
34 #endif
35 
36 #ifdef SERVERONLY
37 #include "qwsvdef.h"
38 #include "server.h"
39 #else
40 #ifdef QW_HACK
41 #include "quakedef.h"
42 #include "client.h"
43 #endif
44 #endif
45 
46 #define cvar_entry(ptr) container_of(ptr, struct cvar_s, stree)
47 DECLARE_STREE_ROOT(cvar_tree);
48 
49 /*
50 ============
51 Cvar_FindVar
52 ============
53 */
54 cvar_t *
Cvar_FindVar(const char * var_name)55 Cvar_FindVar(const char *var_name)
56 {
57     struct cvar_s *ret = NULL;
58     struct stree_node *n;
59 
60     n = STree_Find(&cvar_tree, var_name);
61     if (n)
62 	ret = cvar_entry(n);
63 
64     return ret;
65 }
66 
67 /*
68  * Return a string tree with all possible argument completions of the given
69  * buffer for the given cvar.
70  */
71 struct stree_root *
Cvar_ArgCompletions(const char * name,const char * buf)72 Cvar_ArgCompletions(const char *name, const char *buf)
73 {
74     cvar_t *cvar;
75     struct stree_root *root = NULL;
76 
77     cvar = Cvar_FindVar(name);
78     if (cvar && cvar->completion)
79 	root = cvar->completion(buf);
80 
81     return root;
82 }
83 
84 /*
85  * Call the argument completion function for cvar "name".
86  * Returned result should be Z_Free'd after use.
87  */
88 char *
Cvar_ArgComplete(const char * name,const char * buf)89 Cvar_ArgComplete(const char *name, const char *buf)
90 {
91     char *result = NULL;
92     struct stree_root *root;
93 
94     root = Cvar_ArgCompletions(name, buf);
95     if (root) {
96 	result = STree_MaxMatch(root, buf);
97 	Z_Free(root);
98     }
99 
100     return result;
101 }
102 
103 
104 #ifdef NQ_HACK
105 /*
106  * For NQ/net_dgrm.c, command == CCREQ_RULE_INFO case
107  */
108 cvar_t *
Cvar_NextServerVar(const char * var_name)109 Cvar_NextServerVar(const char *var_name)
110 {
111    cvar_t *ret = NULL;
112    cvar_t *var;
113    struct stree_node *n;
114 
115    if (var_name[0] == '\0')
116       var_name = NULL;
117 
118    if (var_name)
119    {
120       STree_ForEach_Init__(&cvar_tree, &n);
121       STree_ForEach_After__(&cvar_tree, &n, var_name);
122       for (; STree_WalkLeft__(&cvar_tree, &n) ; STree_WalkRight__(&n)) {
123          var = cvar_entry(n);
124          if (var->server) {
125             ret = var;
126             STree_ForEach_Cleanup__(&cvar_tree);
127             return ret;
128          }
129       }
130    }
131    else
132    {
133       STree_ForEach_After_NullStr(&cvar_tree, n) {
134          var = cvar_entry(n);
135          if (var->server) {
136             ret = var;
137             STree_ForEach_Cleanup__(&cvar_tree);
138             return ret;
139          }
140       }
141    }
142 
143    return ret;
144 }
145 #endif
146 
147 /*
148 ============
149 Cvar_VariableValue
150 ============
151 */
152 float
Cvar_VariableValue(const char * var_name)153 Cvar_VariableValue(const char *var_name)
154 {
155     cvar_t *var;
156 
157     var = Cvar_FindVar(var_name);
158     if (!var)
159 	return 0;
160     return Q_atof(var->string);
161 }
162 
163 
164 /*
165 ============
166 Cvar_VariableString
167 ============
168 */
169 const char *
Cvar_VariableString(const char * var_name)170 Cvar_VariableString(const char *var_name)
171 {
172     cvar_t *var;
173 
174     var = Cvar_FindVar(var_name);
175 
176     return var ? var->string : "";
177 }
178 
179 
180 /*
181 ============
182 Cvar_Set
183 ============
184 */
185 void
Cvar_Set(const char * var_name,const char * value)186 Cvar_Set(const char *var_name, const char *value)
187 {
188     cvar_t *var;
189     char *newstring;
190     qboolean changed;
191 
192     var = Cvar_FindVar(var_name);
193     if (!var) {
194 	/* there is an error in C code if this happens */
195 	Con_Printf("Cvar_Set: variable %s not found\n", var_name);
196 	return;
197     }
198 
199     if (var->flags & CVAR_OBSOLETE) {
200 	Con_Printf("%s is obsolete.\n", var_name);
201 	return;
202     }
203 
204     changed = strcmp(var->string, value);
205 
206     /* Check for developer-only cvar */
207     if (changed && (var->flags & CVAR_DEVELOPER) && !developer.value) {
208 	Con_Printf("%s is settable only in developer mode.\n", var_name);
209 	return;
210     }
211 
212 #ifdef SERVERONLY
213     if (var->info) {
214 	Info_SetValueForKey(svs.info, var_name, value, MAX_SERVERINFO_STRING);
215 	SV_SendServerInfoChange(var_name, value);
216 //              SV_BroadcastCommand ("fullserverinfo \"%s\"\n", svs.info);
217     }
218 #else
219 #ifdef QW_HACK
220     if (var->info) {
221 	Info_SetValueForKey(cls.userinfo, var_name, value, MAX_INFO_STRING);
222 	if (cls.state >= ca_connected) {
223 	    MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
224 	    MSG_WriteStringf(&cls.netchan.message, "setinfo \"%s\" \"%s\"\n",
225 			     var_name, value);
226 	}
227     }
228 #endif
229 #endif
230 
231     Z_Free(var->string);	// free the old value string
232 
233     newstring = (char*)Z_Malloc(strlen(value) + 1);
234     strcpy(newstring, value);
235     var->string = newstring;
236     var->value = Q_atof(var->string);
237 
238 #ifdef NQ_HACK
239     if (var->server && changed) {
240 	if (sv.active)
241 	    SV_BroadcastPrintf("\"%s\" changed to \"%s\"\n", var->name,
242 			       var->string);
243     }
244 #endif
245 
246     if (changed && var->callback)
247 	var->callback(var);
248 
249 #ifdef NQ_HACK
250     // Don't allow deathmatch and coop at the same time...
251     if ((var->value != 0) && (!strcmp(var->name, deathmatch.name)))
252 	Cvar_Set("coop", "0");
253     if ((var->value != 0) && (!strcmp(var->name, coop.name)))
254 	Cvar_Set("deathmatch", "0");
255 #endif
256 }
257 
258 /*
259 ============
260 Cvar_SetValue
261 ============
262 */
263 void
Cvar_SetValue(const char * var_name,float value)264 Cvar_SetValue(const char *var_name, float value)
265 {
266     char val[32];
267 
268     snprintf(val, sizeof(val), "%f", value);
269     Cvar_Set(var_name, val);
270 }
271 
272 
273 /*
274 ============
275 Cvar_RegisterVariable
276 
277 Adds a freestanding variable to the variable list.
278 ============
279 */
Cvar_RegisterVariable(cvar_t * variable)280 void Cvar_RegisterVariable(cvar_t *variable)
281 {
282    char value[512];		// FIXME - magic numbers...
283    float old_developer;
284 
285    /* first check to see if it has allready been defined */
286    if (Cvar_FindVar(variable->name))
287    {
288       Con_Printf("Can't register variable %s, allready defined\n",
289             variable->name);
290       return;
291    }
292    /* check for overlap with a command */
293    if (Cmd_Exists(variable->name)) {
294       Con_Printf("Cvar_RegisterVariable: %s is a command\n",
295             variable->name);
296       return;
297    }
298 
299    variable->stree.string = variable->name;
300    STree_Insert(&cvar_tree, &variable->stree);
301 
302    // copy the value off, because future sets will Z_Free it
303    strncpy(value, variable->string, 511);
304    value[511] = '\0';
305    variable->string = (const char*)Z_Malloc(1);
306 
307    if (!(variable->flags & CVAR_CALLBACK))
308      variable->callback = NULL;
309 
310    /*
311     * FIXME (BARF) - readonly cvars need to be initialised
312     *                developer 1 allows set
313     */
314    /* set it through the function to be consistant */
315    old_developer = developer.value;
316    developer.value = 1;
317    Cvar_Set(variable->name, value);
318    developer.value = old_developer;
319 }
320 
321 /*
322 ============
323 Cvar_SetCallback
324 
325 Set a callback function to the var
326 ============
327 */
Cvar_SetCallback(cvar_t * var,cvar_callback func)328 void Cvar_SetCallback (cvar_t *var, cvar_callback func)
329 {
330 	var->callback = func;
331 	if (func)
332 		var->flags |= CVAR_CALLBACK;
333 	else	var->flags &= ~CVAR_CALLBACK;
334 }
335 
336 /*
337 ============
338 Cvar_Command
339 
340 Handles variable inspection and changing from the console
341 ============
342 */
Cvar_Command(void)343 qboolean Cvar_Command(void)
344 {
345    /* check variables */
346    cvar_t *v = Cvar_FindVar(Cmd_Argv(0));
347    if (!v)
348       return false;
349 
350    /* perform a variable print or set */
351    if (Cmd_Argc() == 1)
352    {
353       if (v->flags & CVAR_OBSOLETE)
354          Con_Printf("%s is obsolete.\n", v->name);
355       else
356          Con_Printf("\"%s\" is \"%s\"\n", v->name, v->string);
357       return true;
358    }
359 
360    Cvar_Set(v->name, Cmd_Argv(1));
361    return true;
362 }
363 
364 
365 /*
366 ============
367 Cvar_WriteVariables
368 
369 Writes lines containing "set variable value" for all variables
370 with the archive flag set to true.
371 ============
372 */
Cvar_WriteVariables(FILE * f)373 void Cvar_WriteVariables(FILE *f)
374 {
375    struct stree_node *n;
376 
377    STree_ForEach_After_NullStr(&cvar_tree, n)
378    {
379       cvar_t *var = cvar_entry(n);
380       if (var->archive)
381          fprintf(f, "%s \"%s\"\n", var->name, var->string);
382    }
383 }
384