]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cmd.c
cmd: Re-re-re-re-implement the command parser. Allocate text only as needed.
[xonotic/darkplaces.git] / cmd.c
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 // cmd.c -- Quake script command processing module
21
22 #include "quakedef.h"
23 #include "thread.h"
24
25 cmd_state_t cmd_client;
26 cmd_state_t cmd_server;
27 cmd_state_t cmd_serverfromclient;
28
29 cmd_userdefined_t cmd_userdefined_all;
30 cmd_userdefined_t cmd_userdefined_null;
31
32 typedef struct cmd_iter_s {
33         cmd_state_t *cmd;
34 }
35 cmd_iter_t;
36
37 static cmd_iter_t cmd_iter_all[] = {
38         {&cmd_client},
39         {&cmd_server},
40         {&cmd_serverfromclient},
41         {NULL},
42 };
43
44
45 // we only run the +whatever commandline arguments once
46 qbool host_stuffcmdsrun = false;
47
48 //=============================================================================
49
50 void Cbuf_Lock(cbuf_t *cbuf)
51 {
52         Thread_LockMutex(cbuf->lock);
53 }
54
55 void Cbuf_Unlock(cbuf_t *cbuf)
56 {
57         Thread_UnlockMutex(cbuf->lock);
58 }
59
60
61 /*
62 ============
63 Cmd_Wait_f
64
65 Causes execution of the remainder of the command buffer to be delayed until
66 next frame.  This allows commands like:
67 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
68 ============
69 */
70 static void Cmd_Wait_f (cmd_state_t *cmd)
71 {
72         cmd->cbuf->wait = true;
73 }
74
75 /*
76 ============
77 Cmd_Defer_f
78
79 Cause a command to be executed after a delay.
80 ============
81 */
82 static cmd_input_t *Cbuf_LinkGet(cbuf_t *cbuf, cmd_input_t *existing);
83 static void Cmd_Defer_f (cmd_state_t *cmd)
84 {
85         cmd_input_t *current;
86         cbuf_t *cbuf = cmd->cbuf;
87
88         if(Cmd_Argc(cmd) == 1)
89         {
90                 if(List_IsEmpty(&cbuf->deferred))
91                         Con_Printf("No commands are pending.\n");
92                 else
93                 {
94                         llist_t *pos;
95                 List_ForEach(pos, &cbuf->deferred)
96                 {
97                                 current = List_Container(*pos, cmd_input_t, list);
98                                 Con_Printf("-> In %9.2f: %s\n", current->delay, current->text);
99                         }
100                 }
101         }
102         else if(Cmd_Argc(cmd) == 2 && !strcasecmp("clear", Cmd_Argv(cmd, 1)))
103         {
104                 while(!List_IsEmpty(&cbuf->deferred))
105                         List_Move_Tail(cbuf->deferred.next, &cbuf->free);
106         }
107         else if(Cmd_Argc(cmd) == 3)
108         {
109                 const char *text = Cmd_Argv(cmd, 2);
110                 current = Cbuf_LinkGet(cbuf, NULL);
111                 current->size = strlen(text);
112                 current->source = cmd;
113                 current->delay = atof(Cmd_Argv(cmd, 1));
114
115                 memcpy(current->text, text, current->size + 1);
116
117                 List_Move_Tail(&current->list, &cbuf->deferred);
118
119         }
120         else
121         {
122                 Con_Printf("usage: defer <seconds> <command>\n"
123                            "       defer clear\n");
124                 return;
125         }
126 }
127
128 /*
129 ============
130 Cmd_Centerprint_f
131
132 Print something to the center of the screen using SCR_Centerprint
133 ============
134 */
135 static void Cmd_Centerprint_f (cmd_state_t *cmd)
136 {
137         char msg[MAX_INPUTLINE];
138         unsigned int i, c, p;
139         c = Cmd_Argc(cmd);
140         if(c >= 2)
141         {
142                 strlcpy(msg, Cmd_Argv(cmd,1), sizeof(msg));
143                 for(i = 2; i < c; ++i)
144                 {
145                         strlcat(msg, " ", sizeof(msg));
146                         strlcat(msg, Cmd_Argv(cmd, i), sizeof(msg));
147                 }
148                 c = (unsigned int)strlen(msg);
149                 for(p = 0, i = 0; i < c; ++i)
150                 {
151                         if(msg[i] == '\\')
152                         {
153                                 if(msg[i+1] == 'n')
154                                         msg[p++] = '\n';
155                                 else if(msg[i+1] == '\\')
156                                         msg[p++] = '\\';
157                                 else {
158                                         msg[p++] = '\\';
159                                         msg[p++] = msg[i+1];
160                                 }
161                                 ++i;
162                         } else {
163                                 msg[p++] = msg[i];
164                         }
165                 }
166                 msg[p] = '\0';
167                 SCR_CenterPrint(msg);
168         }
169 }
170
171 /*
172 =============================================================================
173
174                                                 COMMAND BUFFER
175
176 =============================================================================
177 */
178
179 static cmd_input_t *Cbuf_LinkGet(cbuf_t *cbuf, cmd_input_t *existing)
180 {
181         cmd_input_t *ret = NULL;
182         if(existing && existing->pending)
183                 ret = existing;
184         else if(!List_IsEmpty(&cbuf->free))
185         {
186                 ret = List_Container(*cbuf->free.next, cmd_input_t, list);
187                 ret->length = 0;
188                 ret->pending = false;
189         }
190         return ret;
191 }
192
193 static cmd_input_t *Cmd_AllocInputNode(void)
194 {
195         cmd_input_t *node = (cmd_input_t *)Z_Malloc(sizeof(cmd_input_t));
196         node->list.prev = node->list.next = &node->list;
197         node->size = node->length = node->pending = 0;
198         return node;
199 }
200
201 static size_t Cmd_ParseInput (cmd_input_t **output, char **input)
202 {
203         size_t pos, cmdsize = 0, start = 0;
204         qbool command = false, lookahead = false;
205         qbool quotes = false, comment = false;
206         qbool escaped = false;
207
208         /*
209          * The Quake command-line is super basic. It can be entered in the console
210          * or in config files. A semicolon is used to terminate a command and chain
211          * them together. Otherwise, a newline delineates command input.
212          * 
213          * In most engines, the Quake command-line is a simple linear text buffer that
214          * is parsed when it executes. In Darkplaces, we use a linked list of command
215          * input and parse the input on the spot.
216          * 
217          * This was done because Darkplaces allows multiple command interpreters on the
218          * same thread. Previously, each interpreter maintained its own buffer and this
219          * caused problems related to execution order, and maintaining a single simple
220          * buffer for all interpreters makes it non-trivial to keep track of which
221          * command should execute on which interpreter.
222          */
223
224         // Run until command and lookahead are both true, or until we run out of input.
225         for (pos = 0; (*input)[pos]; pos++)
226         {
227                 // Look for newlines and semicolons. Ignore semicolons in quotes.
228                 switch((*input)[pos])
229                 {
230                 case '\r':
231                 case '\n':
232                         command = false;
233                         comment = false;
234                         break;
235                 default: 
236                         if(!comment) // Not a newline so far. Still not a valid command yet.
237                         {
238                                 if(!quotes && (*input)[pos] == ';') // Ignore semicolons in quotes.
239                                         command = false;
240                                 else if (ISCOMMENT((*input), pos)) // Comments
241                                 {
242                                         comment = true;
243                                         command = false;
244                                 }
245                                 else
246                                 {
247                                         command = true;
248                                         if(!lookahead)
249                                         {
250                                                 if(!cmdsize)
251                                                         start = pos;
252                                                 cmdsize++;
253                                         }
254
255                                         switch((*input)[pos])
256                                         {
257                                         case '"':
258                                                 if (!escaped)
259                                                         quotes = !quotes;
260                                                 else
261                                                         escaped = false;
262                                                 break;
263                                         case '\\':
264                                                 if (!escaped && quotes)
265                                                         escaped = true;
266                                                 else if (escaped)
267                                                         escaped = false;
268                                                 break;
269                                         }
270                                 }
271                         }
272                 }
273                 if(cmdsize && !command)
274                         lookahead = true;
275
276                 if(command && lookahead)
277                         break;
278         }
279
280         if(cmdsize)
281         {
282                 size_t offset = 0;
283
284                 if(!*output)
285                         *output = Cmd_AllocInputNode();
286
287                 if((*output)->pending)
288                         offset = (*output)->length;
289
290                 (*output)->length += cmdsize;
291
292                 if((*output)->size < (*output)->length + 1)
293                 {
294                         (*output)->text = (char *)Mem_Realloc(tempmempool, (*output)->text, (*output)->size + cmdsize + 1);
295                         (*output)->size = (*output)->length + 1;
296                 }
297
298                 strlcpy(&(*output)->text[offset], &(*input)[start], cmdsize + 1);
299                 (*output)->pending = !lookahead;
300         }
301
302         // Set input to its new position. Can be NULL.
303         *input = &(*input)[pos];
304
305         return cmdsize;
306 }
307
308 // Cloudwalk: Not happy with this, but it works.
309 static void Cbuf_LinkCreate(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text)
310 {
311         char *in = (char *)&text[0];
312         cbuf_t *cbuf = cmd->cbuf;
313         size_t totalsize = 0, newsize = 0;
314         cmd_input_t *current = NULL;
315
316         // Slide the pointer down until we reach the end
317         while(*in)
318         {
319                 current = Cbuf_LinkGet(cbuf, existing);
320                 newsize = Cmd_ParseInput(&current, &in);
321
322                 // Valid command
323                 if(newsize)
324                 {
325                         if(current != existing)
326                         {
327                                 current->source = cmd;
328                                 List_Move_Tail(&current->list, head);
329                         }
330
331                         totalsize += newsize;
332                 }
333                 else if (current == existing && !totalsize)
334                         current->pending = false;
335                 current = NULL;
336         }
337
338         cbuf->size += totalsize;
339 }
340
341 /*
342 ============
343 Cbuf_AddText
344
345 Adds command text at the end of the buffer
346 ============
347 */
348 void Cbuf_AddText (cmd_state_t *cmd, const char *text)
349 {
350         size_t l = strlen(text);
351         cbuf_t *cbuf = cmd->cbuf;
352         llist_t llist = {&llist, &llist};
353
354         Cbuf_Lock(cbuf);
355
356         if (cbuf->maxsize - cbuf->size <= l)
357                 Con_Print("Cbuf_AddText: overflow\n");
358         else
359         {
360                 Cbuf_LinkCreate(cmd, &llist, (List_IsEmpty(&cbuf->start) ? NULL : List_Container(*cbuf->start.prev, cmd_input_t, list)), text);
361                 if(!List_IsEmpty(&llist))
362                         List_Splice_Tail(&llist, &cbuf->start);
363         }
364         Cbuf_Unlock(cbuf);
365 }
366
367 /*
368 ============
369 Cbuf_InsertText
370
371 Adds command text immediately after the current command
372 FIXME: actually change the command buffer to do less copying
373 ============
374 */
375 void Cbuf_InsertText (cmd_state_t *cmd, const char *text)
376 {
377         cbuf_t *cbuf = cmd->cbuf;
378         llist_t llist = {&llist, &llist};
379         size_t l = strlen(text);
380
381         Cbuf_Lock(cbuf);
382
383         // we need to memmove the existing text and stuff this in before it...
384         if (cbuf->size + l >= (size_t)cbuf->maxsize)
385                 Con_Print("Cbuf_InsertText: overflow\n");
386         else
387         {
388                 Cbuf_LinkCreate(cmd, &llist, List_Container(*cbuf->start.next, cmd_input_t, list), text);
389                 List_Splice(&llist, &cbuf->start);
390         }
391
392         Cbuf_Unlock(cbuf);
393 }
394
395 /*
396 ============
397 Cbuf_Execute_Deferred --blub
398 ============
399 */
400 static void Cbuf_Execute_Deferred (cbuf_t *cbuf)
401 {
402         llist_t *pos;
403         cmd_input_t *current;
404         double eat;
405
406         if (host.realtime - cbuf->deferred_oldtime < 0 || host.realtime - cbuf->deferred_oldtime > 1800)
407                 cbuf->deferred_oldtime = host.realtime;
408         eat = host.realtime - cbuf->deferred_oldtime;
409         if (eat < (1.0 / 120.0))
410                 return;
411         cbuf->deferred_oldtime = host.realtime;
412
413     List_ForEach(pos, &cbuf->deferred)
414         {
415                 current = List_Container(*pos, cmd_input_t, list);
416                 current->delay -= eat;
417                 if(current->delay <= 0)
418                 {
419                         cbuf->size += current->size;
420                         List_Move(pos, &cbuf->start);
421                         // We must return and come back next frame or the engine will freeze. Fragile... like glass :3
422                         return;
423                 }
424         }
425 }
426
427 /*
428 ============
429 Cbuf_Execute
430 ============
431 */
432 static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias );
433 void Cbuf_Execute (cbuf_t *cbuf)
434 {
435         cmd_input_t *current;
436         char preprocessed[MAX_INPUTLINE];
437         char *firstchar;
438
439         // LadyHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes
440         cbuf->tokenizebufferpos = 0;
441
442         while (!List_IsEmpty(&cbuf->start))
443         {
444                 /*
445                  * Delete the text from the command buffer and move remaining
446                  * commands down. This is necessary because commands (exec, alias)
447                  * can insert data at the beginning of the text buffer
448                  */
449                 current = List_Container(*cbuf->start.next, cmd_input_t, list);
450
451                 /*
452                  * Assume we're rolling with the current command-line and
453                  * always set this false because alias expansion or cbuf insertion
454                  * without a newline may set this true, and cause weirdness.
455                  */
456                 current->pending = false;
457
458                 cbuf->size -= current->length;
459
460                 firstchar = current->text;
461                 while(*firstchar && ISWHITESPACE(*firstchar))
462                         ++firstchar;
463                 if((strncmp(firstchar, "alias", 5)   || !ISWHITESPACE(firstchar[5])) &&
464                    (strncmp(firstchar, "bind", 4)    || !ISWHITESPACE(firstchar[4])) &&
465                    (strncmp(firstchar, "in_bind", 7) || !ISWHITESPACE(firstchar[7])))
466                 {
467                         if(Cmd_PreprocessString(current->source, current->text, preprocessed, sizeof(preprocessed), NULL ))
468                                 Cmd_ExecuteString(current->source, preprocessed, src_local, false);
469                 }
470                 else
471                 {
472                         Cmd_ExecuteString (current->source, current->text, src_local, false);
473                 }
474
475                 // Recycle memory so using WASD doesn't cause a malloc and free
476                 List_Move_Tail(&current->list, &cbuf->free);
477
478                 current = NULL;
479
480                 if (cbuf->wait)
481                 {
482                         /*
483                          * Skip out while text still remains in
484                          * buffer, leaving it for next frame
485                          */
486                         cbuf->wait = false;
487                         break;
488                 }
489         }
490 }
491
492 void Cbuf_Frame(cbuf_t *cbuf)
493 {
494         Cbuf_Execute_Deferred(cbuf);
495         if (cbuf->size)
496         {
497                 SV_LockThreadMutex();
498                 Cbuf_Execute(cbuf);
499                 SV_UnlockThreadMutex();
500         }
501 }
502
503 /*
504 ==============================================================================
505
506                                                 SCRIPT COMMANDS
507
508 ==============================================================================
509 */
510
511 /*
512 ===============
513 Cmd_StuffCmds_f
514
515 Adds command line parameters as script statements
516 Commands lead with a +, and continue until a - or another +
517 quake +prog jctest.qp +cmd amlev1
518 quake -nosound +cmd amlev1
519 ===============
520 */
521 static void Cmd_StuffCmds_f (cmd_state_t *cmd)
522 {
523         int             i, j, l;
524         // this is for all commandline options combined (and is bounds checked)
525         char    build[MAX_INPUTLINE];
526         
527         // come back later so we don't crash
528         if(host.state == host_init)
529                 return;
530
531         if (Cmd_Argc (cmd) != 1)
532         {
533                 Con_Print("stuffcmds : execute command line parameters\n");
534                 return;
535         }
536
537         // no reason to run the commandline arguments twice
538         if (host_stuffcmdsrun)
539                 return;
540
541         host_stuffcmdsrun = true;
542         build[0] = 0;
543         l = 0;
544         for (i = 0;i < sys.argc;i++)
545         {
546                 if (sys.argv[i] && sys.argv[i][0] == '+' && (sys.argv[i][1] < '0' || sys.argv[i][1] > '9') && l + strlen(sys.argv[i]) - 1 <= sizeof(build) - 1)
547                 {
548                         j = 1;
549                         while (sys.argv[i][j])
550                                 build[l++] = sys.argv[i][j++];
551                         i++;
552                         for (;i < sys.argc;i++)
553                         {
554                                 if (!sys.argv[i])
555                                         continue;
556                                 if ((sys.argv[i][0] == '+' || sys.argv[i][0] == '-') && (sys.argv[i][1] < '0' || sys.argv[i][1] > '9'))
557                                         break;
558                                 if (l + strlen(sys.argv[i]) + 4 > sizeof(build) - 1)
559                                         break;
560                                 build[l++] = ' ';
561                                 if (strchr(sys.argv[i], ' '))
562                                         build[l++] = '\"';
563                                 for (j = 0;sys.argv[i][j];j++)
564                                         build[l++] = sys.argv[i][j];
565                                 if (strchr(sys.argv[i], ' '))
566                                         build[l++] = '\"';
567                         }
568                         build[l++] = '\n';
569                         i--;
570                 }
571         }
572         // now terminate the combined string and prepend it to the command buffer
573         // we already reserved space for the terminator
574         build[l++] = 0;
575         Cbuf_InsertText (cmd, build);
576 }
577
578 static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
579 {
580         char *f;
581         size_t filenameLen = strlen(filename);
582         qbool isdefaultcfg =
583                 !strcmp(filename, "default.cfg") ||
584                 (filenameLen >= 12 && !strcmp(filename + filenameLen - 12, "/default.cfg"));
585
586         if (!strcmp(filename, "config.cfg"))
587         {
588                 filename = CONFIGFILENAME;
589                 if (Sys_CheckParm("-noconfig"))
590                         return; // don't execute config.cfg
591         }
592
593         f = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
594         if (!f)
595         {
596                 Con_Printf("couldn't exec %s\n",filename);
597                 return;
598         }
599         Con_Printf("execing %s\n",filename);
600
601         // if executing default.cfg for the first time, lock the cvar defaults
602         // it may seem backwards to insert this text BEFORE the default.cfg
603         // but Cbuf_InsertText inserts before, so this actually ends up after it.
604         if (isdefaultcfg)
605                 Cbuf_InsertText(cmd, "\ncvar_lockdefaults\n");
606
607         Cbuf_InsertText (cmd, f);
608         Mem_Free(f);
609
610         if (isdefaultcfg)
611         {
612                 // special defaults for specific games go here, these execute before default.cfg
613                 // Nehahra pushable crates malfunction in some levels if this is on
614                 // Nehahra NPC AI is confused by blowupfallenzombies
615                 switch(gamemode)
616                 {
617                 case GAME_NORMAL:
618                         Cbuf_InsertText(cmd, "\n"
619 "sv_gameplayfix_blowupfallenzombies 0\n"
620 "sv_gameplayfix_findradiusdistancetobox 0\n"
621 "sv_gameplayfix_grenadebouncedownslopes 0\n"
622 "sv_gameplayfix_slidemoveprojectiles 0\n"
623 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
624 "sv_gameplayfix_setmodelrealbox 0\n"
625 "sv_gameplayfix_droptofloorstartsolid 0\n"
626 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
627 "sv_gameplayfix_noairborncorpse 0\n"
628 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
629 "sv_gameplayfix_easierwaterjump 0\n"
630 "sv_gameplayfix_delayprojectiles 0\n"
631 "sv_gameplayfix_multiplethinksperframe 0\n"
632 "sv_gameplayfix_fixedcheckwatertransition 0\n"
633 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
634 "sv_gameplayfix_swiminbmodels 0\n"
635 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
636 "sys_ticrate 0.01388889\n"
637 "r_shadow_gloss 1\n"
638 "r_shadow_bumpscale_basetexture 0\n"
639 "csqc_polygons_defaultmaterial_nocullface 0\n"
640                                 );
641                         break;
642                 case GAME_NEHAHRA:
643                         Cbuf_InsertText(cmd, "\n"
644 "sv_gameplayfix_blowupfallenzombies 0\n"
645 "sv_gameplayfix_findradiusdistancetobox 0\n"
646 "sv_gameplayfix_grenadebouncedownslopes 0\n"
647 "sv_gameplayfix_slidemoveprojectiles 0\n"
648 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
649 "sv_gameplayfix_setmodelrealbox 0\n"
650 "sv_gameplayfix_droptofloorstartsolid 0\n"
651 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
652 "sv_gameplayfix_noairborncorpse 0\n"
653 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
654 "sv_gameplayfix_easierwaterjump 0\n"
655 "sv_gameplayfix_delayprojectiles 0\n"
656 "sv_gameplayfix_multiplethinksperframe 0\n"
657 "sv_gameplayfix_fixedcheckwatertransition 0\n"
658 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
659 "sv_gameplayfix_swiminbmodels 0\n"
660 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
661 "sys_ticrate 0.01388889\n"
662 "r_shadow_gloss 1\n"
663 "r_shadow_bumpscale_basetexture 0\n"
664 "csqc_polygons_defaultmaterial_nocullface 0\n"
665                                 );
666                         break;
667                 // hipnotic mission pack has issues in their 'friendly monster' ai, which seem to attempt to attack themselves for some reason when findradius() returns non-solid entities.
668                 // hipnotic mission pack has issues with bobbing water entities 'jittering' between different heights on alternate frames at the default 0.0138889 ticrate, 0.02 avoids this issue
669                 // hipnotic mission pack has issues in their proximity mine sticking code, which causes them to bounce off.
670                 case GAME_HIPNOTIC:
671                 case GAME_QUOTH:
672                         Cbuf_InsertText(cmd, "\n"
673 "sv_gameplayfix_blowupfallenzombies 0\n"
674 "sv_gameplayfix_findradiusdistancetobox 0\n"
675 "sv_gameplayfix_grenadebouncedownslopes 0\n"
676 "sv_gameplayfix_slidemoveprojectiles 0\n"
677 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
678 "sv_gameplayfix_setmodelrealbox 0\n"
679 "sv_gameplayfix_droptofloorstartsolid 0\n"
680 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
681 "sv_gameplayfix_noairborncorpse 0\n"
682 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
683 "sv_gameplayfix_easierwaterjump 0\n"
684 "sv_gameplayfix_delayprojectiles 0\n"
685 "sv_gameplayfix_multiplethinksperframe 0\n"
686 "sv_gameplayfix_fixedcheckwatertransition 0\n"
687 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
688 "sv_gameplayfix_swiminbmodels 0\n"
689 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
690 "sys_ticrate 0.02\n"
691 "r_shadow_gloss 1\n"
692 "r_shadow_bumpscale_basetexture 0\n"
693 "csqc_polygons_defaultmaterial_nocullface 0\n"
694                                 );
695                         break;
696                 // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area
697                 case GAME_ROGUE:
698                         Cbuf_InsertText(cmd, "\n"
699 "sv_gameplayfix_blowupfallenzombies 0\n"
700 "sv_gameplayfix_findradiusdistancetobox 0\n"
701 "sv_gameplayfix_grenadebouncedownslopes 0\n"
702 "sv_gameplayfix_slidemoveprojectiles 0\n"
703 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
704 "sv_gameplayfix_setmodelrealbox 0\n"
705 "sv_gameplayfix_droptofloorstartsolid 0\n"
706 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
707 "sv_gameplayfix_noairborncorpse 0\n"
708 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
709 "sv_gameplayfix_easierwaterjump 0\n"
710 "sv_gameplayfix_delayprojectiles 0\n"
711 "sv_gameplayfix_multiplethinksperframe 0\n"
712 "sv_gameplayfix_fixedcheckwatertransition 0\n"
713 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
714 "sv_gameplayfix_swiminbmodels 0\n"
715 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
716 "sys_ticrate 0.01388889\n"
717 "r_shadow_gloss 1\n"
718 "r_shadow_bumpscale_basetexture 0\n"
719 "csqc_polygons_defaultmaterial_nocullface 0\n"
720                                 );
721                         break;
722                 case GAME_TENEBRAE:
723                         Cbuf_InsertText(cmd, "\n"
724 "sv_gameplayfix_blowupfallenzombies 0\n"
725 "sv_gameplayfix_findradiusdistancetobox 0\n"
726 "sv_gameplayfix_grenadebouncedownslopes 0\n"
727 "sv_gameplayfix_slidemoveprojectiles 0\n"
728 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
729 "sv_gameplayfix_setmodelrealbox 0\n"
730 "sv_gameplayfix_droptofloorstartsolid 0\n"
731 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
732 "sv_gameplayfix_noairborncorpse 0\n"
733 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
734 "sv_gameplayfix_easierwaterjump 0\n"
735 "sv_gameplayfix_delayprojectiles 0\n"
736 "sv_gameplayfix_multiplethinksperframe 0\n"
737 "sv_gameplayfix_fixedcheckwatertransition 0\n"
738 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
739 "sv_gameplayfix_swiminbmodels 0\n"
740 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
741 "sys_ticrate 0.01388889\n"
742 "r_shadow_gloss 2\n"
743 "r_shadow_bumpscale_basetexture 4\n"
744 "csqc_polygons_defaultmaterial_nocullface 0\n"
745                                 );
746                         break;
747                 case GAME_NEXUIZ:
748                         Cbuf_InsertText(cmd, "\n"
749 "sv_gameplayfix_blowupfallenzombies 1\n"
750 "sv_gameplayfix_findradiusdistancetobox 1\n"
751 "sv_gameplayfix_grenadebouncedownslopes 1\n"
752 "sv_gameplayfix_slidemoveprojectiles 1\n"
753 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
754 "sv_gameplayfix_setmodelrealbox 1\n"
755 "sv_gameplayfix_droptofloorstartsolid 1\n"
756 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
757 "sv_gameplayfix_noairborncorpse 1\n"
758 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
759 "sv_gameplayfix_easierwaterjump 1\n"
760 "sv_gameplayfix_delayprojectiles 1\n"
761 "sv_gameplayfix_multiplethinksperframe 1\n"
762 "sv_gameplayfix_fixedcheckwatertransition 1\n"
763 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
764 "sv_gameplayfix_swiminbmodels 1\n"
765 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
766 "sys_ticrate 0.01388889\n"
767 "sv_gameplayfix_q2airaccelerate 1\n"
768 "sv_gameplayfix_stepmultipletimes 1\n"
769 "csqc_polygons_defaultmaterial_nocullface 1\n"
770 "con_chatsound_team_mask 13\n"
771                                 );
772                         break;
773                 case GAME_XONOTIC:
774                 case GAME_VORETOURNAMENT:
775                         // compatibility for versions prior to 2020-05-25, this can be overridden in newer versions to get the default behavior and be consistent with FTEQW engine
776                         Cbuf_InsertText(cmd, "\n"
777 "csqc_polygons_defaultmaterial_nocullface 1\n"
778 "con_chatsound_team_mask 13\n"
779 "sv_gameplayfix_customstats 1\n"
780                                 );
781                         break;
782                 // Steel Storm: Burning Retribution csqc misinterprets CSQC_InputEvent if type is a value other than 0 or 1
783                 case GAME_STEELSTORM:
784                         Cbuf_InsertText(cmd, "\n"
785 "sv_gameplayfix_blowupfallenzombies 1\n"
786 "sv_gameplayfix_findradiusdistancetobox 1\n"
787 "sv_gameplayfix_grenadebouncedownslopes 1\n"
788 "sv_gameplayfix_slidemoveprojectiles 1\n"
789 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
790 "sv_gameplayfix_setmodelrealbox 1\n"
791 "sv_gameplayfix_droptofloorstartsolid 1\n"
792 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
793 "sv_gameplayfix_noairborncorpse 1\n"
794 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
795 "sv_gameplayfix_easierwaterjump 1\n"
796 "sv_gameplayfix_delayprojectiles 1\n"
797 "sv_gameplayfix_multiplethinksperframe 1\n"
798 "sv_gameplayfix_fixedcheckwatertransition 1\n"
799 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
800 "sv_gameplayfix_swiminbmodels 1\n"
801 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
802 "sys_ticrate 0.01388889\n"
803 "cl_csqc_generatemousemoveevents 0\n"
804 "csqc_polygons_defaultmaterial_nocullface 1\n"
805                                 );
806                         break;
807                 default:
808                         Cbuf_InsertText(cmd, "\n"
809 "sv_gameplayfix_blowupfallenzombies 1\n"
810 "sv_gameplayfix_findradiusdistancetobox 1\n"
811 "sv_gameplayfix_grenadebouncedownslopes 1\n"
812 "sv_gameplayfix_slidemoveprojectiles 1\n"
813 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
814 "sv_gameplayfix_setmodelrealbox 1\n"
815 "sv_gameplayfix_droptofloorstartsolid 1\n"
816 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
817 "sv_gameplayfix_noairborncorpse 1\n"
818 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
819 "sv_gameplayfix_easierwaterjump 1\n"
820 "sv_gameplayfix_delayprojectiles 1\n"
821 "sv_gameplayfix_multiplethinksperframe 1\n"
822 "sv_gameplayfix_fixedcheckwatertransition 1\n"
823 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
824 "sv_gameplayfix_swiminbmodels 1\n"
825 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
826 "sys_ticrate 0.01388889\n"
827 "csqc_polygons_defaultmaterial_nocullface 0\n"
828                                 );
829                         break;
830                 }
831         }
832 }
833
834 /*
835 ===============
836 Cmd_Exec_f
837 ===============
838 */
839 static void Cmd_Exec_f (cmd_state_t *cmd)
840 {
841         fssearch_t *s;
842         int i;
843
844         if (Cmd_Argc(cmd) != 2)
845         {
846                 Con_Print("exec <filename> : execute a script file\n");
847                 return;
848         }
849
850         s = FS_Search(Cmd_Argv(cmd, 1), true, true, NULL);
851         if(!s || !s->numfilenames)
852         {
853                 Con_Printf("couldn't exec %s\n",Cmd_Argv(cmd, 1));
854                 return;
855         }
856
857         for(i = 0; i < s->numfilenames; ++i)
858                 Cmd_Exec(cmd, s->filenames[i]);
859
860         FS_FreeSearch(s);
861 }
862
863
864 /*
865 ===============
866 Cmd_Echo_f
867
868 Just prints the rest of the line to the console
869 ===============
870 */
871 static void Cmd_Echo_f (cmd_state_t *cmd)
872 {
873         int             i;
874
875         for (i=1 ; i<Cmd_Argc(cmd) ; i++)
876                 Con_Printf("%s ",Cmd_Argv(cmd, i));
877         Con_Print("\n");
878 }
879
880 // DRESK - 5/14/06
881 // Support Doom3-style Toggle Console Command
882 /*
883 ===============
884 Cmd_Toggle_f
885
886 Toggles a specified console variable amongst the values specified (default is 0 and 1)
887 ===============
888 */
889 static void Cmd_Toggle_f(cmd_state_t *cmd)
890 {
891         // Acquire Number of Arguments
892         int nNumArgs = Cmd_Argc(cmd);
893
894         if(nNumArgs == 1)
895                 // No Arguments Specified; Print Usage
896                 Con_Print("Toggle Console Variable - Usage\n  toggle <variable> - toggles between 0 and 1\n  toggle <variable> <value> - toggles between 0 and <value>\n  toggle <variable> [string 1] [string 2]...[string n] - cycles through all strings\n");
897         else
898         { // Correct Arguments Specified
899                 // Acquire Potential CVar
900                 cvar_t* cvCVar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 1), cmd->cvars_flagsmask);
901
902                 if(cvCVar != NULL)
903                 { // Valid CVar
904                         if(nNumArgs == 2)
905                         { // Default Usage
906                                 if(cvCVar->integer)
907                                         Cvar_SetValueQuick(cvCVar, 0);
908                                 else
909                                         Cvar_SetValueQuick(cvCVar, 1);
910                         }
911                         else
912                         if(nNumArgs == 3)
913                         { // 0 and Specified Usage
914                                 if(cvCVar->integer == atoi(Cmd_Argv(cmd, 2) ) )
915                                         // CVar is Specified Value; // Reset to 0
916                                         Cvar_SetValueQuick(cvCVar, 0);
917                                 else
918                                 if(cvCVar->integer == 0)
919                                         // CVar is 0; Specify Value
920                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
921                                 else
922                                         // CVar does not match; Reset to 0
923                                         Cvar_SetValueQuick(cvCVar, 0);
924                         }
925                         else
926                         { // Variable Values Specified
927                                 int nCnt;
928                                 int bFound = 0;
929
930                                 for(nCnt = 2; nCnt < nNumArgs; nCnt++)
931                                 { // Cycle through Values
932                                         if( strcmp(cvCVar->string, Cmd_Argv(cmd, nCnt) ) == 0)
933                                         { // Current Value Located; Increment to Next
934                                                 if( (nCnt + 1) == nNumArgs)
935                                                         // Max Value Reached; Reset
936                                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
937                                                 else
938                                                         // Next Value
939                                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, nCnt + 1) );
940
941                                                 // End Loop
942                                                 nCnt = nNumArgs;
943                                                 // Assign Found
944                                                 bFound = 1;
945                                         }
946                                 }
947                                 if(!bFound)
948                                         // Value not Found; Reset to Original
949                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
950                         }
951
952                 }
953                 else
954                 { // Invalid CVar
955                         Con_Printf("ERROR : CVar '%s' not found\n", Cmd_Argv(cmd, 1) );
956                 }
957         }
958 }
959
960 /*
961 ===============
962 Cmd_Alias_f
963
964 Creates a new command that executes a command string (possibly ; seperated)
965 ===============
966 */
967 static void Cmd_Alias_f (cmd_state_t *cmd)
968 {
969         cmd_alias_t     *a;
970         char            line[MAX_INPUTLINE];
971         int                     i, c;
972         const char              *s;
973         size_t          alloclen;
974
975         if (Cmd_Argc(cmd) == 1)
976         {
977                 Con_Print("Current alias commands:\n");
978                 for (a = cmd->userdefined->alias ; a ; a=a->next)
979                         Con_Printf("%s : %s", a->name, a->value);
980                 return;
981         }
982
983         s = Cmd_Argv(cmd, 1);
984         if (strlen(s) >= MAX_ALIAS_NAME)
985         {
986                 Con_Print("Alias name is too long\n");
987                 return;
988         }
989
990         // if the alias already exists, reuse it
991         for (a = cmd->userdefined->alias ; a ; a=a->next)
992         {
993                 if (!strcmp(s, a->name))
994                 {
995                         Z_Free (a->value);
996                         break;
997                 }
998         }
999
1000         if (!a)
1001         {
1002                 cmd_alias_t *prev, *current;
1003
1004                 a = (cmd_alias_t *)Z_Malloc (sizeof(cmd_alias_t));
1005                 strlcpy (a->name, s, sizeof (a->name));
1006                 // insert it at the right alphanumeric position
1007                 for( prev = NULL, current = cmd->userdefined->alias ; current && strcmp( current->name, a->name ) < 0 ; prev = current, current = current->next )
1008                         ;
1009                 if( prev ) {
1010                         prev->next = a;
1011                 } else {
1012                         cmd->userdefined->alias = a;
1013                 }
1014                 a->next = current;
1015         }
1016
1017
1018 // copy the rest of the command line
1019         line[0] = 0;            // start out with a null string
1020         c = Cmd_Argc(cmd);
1021         for (i=2 ; i < c ; i++)
1022         {
1023                 if (i != 2)
1024                         strlcat (line, " ", sizeof (line));
1025                 strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
1026         }
1027         strlcat (line, "\n", sizeof (line));
1028
1029         alloclen = strlen (line) + 1;
1030         if(alloclen >= 2)
1031                 line[alloclen - 2] = '\n'; // to make sure a newline is appended even if too long
1032         a->value = (char *)Z_Malloc (alloclen);
1033         memcpy (a->value, line, alloclen);
1034 }
1035
1036 /*
1037 ===============
1038 Cmd_UnAlias_f
1039
1040 Remove existing aliases.
1041 ===============
1042 */
1043 static void Cmd_UnAlias_f (cmd_state_t *cmd)
1044 {
1045         cmd_alias_t     *a, *p;
1046         int i;
1047         const char *s;
1048
1049         if(Cmd_Argc(cmd) == 1)
1050         {
1051                 Con_Print("unalias: Usage: unalias alias1 [alias2 ...]\n");
1052                 return;
1053         }
1054
1055         for(i = 1; i < Cmd_Argc(cmd); ++i)
1056         {
1057                 s = Cmd_Argv(cmd, i);
1058                 p = NULL;
1059                 for(a = cmd->userdefined->alias; a; p = a, a = a->next)
1060                 {
1061                         if(!strcmp(s, a->name))
1062                         {
1063                                 if (a->initstate) // we can not remove init aliases
1064                                         continue;
1065                                 if(a == cmd->userdefined->alias)
1066                                         cmd->userdefined->alias = a->next;
1067                                 if(p)
1068                                         p->next = a->next;
1069                                 Z_Free(a->value);
1070                                 Z_Free(a);
1071                                 break;
1072                         }
1073                 }
1074                 if(!a)
1075                         Con_Printf("unalias: %s alias not found\n", s);
1076         }
1077 }
1078
1079 /*
1080 =============================================================================
1081
1082                                         COMMAND EXECUTION
1083
1084 =============================================================================
1085 */
1086
1087 static const char *Cmd_GetDirectCvarValue(cmd_state_t *cmd, const char *varname, cmd_alias_t *alias, qbool *is_multiple)
1088 {
1089         cvar_t *cvar;
1090         long argno;
1091         char *endptr;
1092         static char vabuf[1024]; // cmd_mutex
1093
1094         if(is_multiple)
1095                 *is_multiple = false;
1096
1097         if(!varname || !*varname)
1098                 return NULL;
1099
1100         if(alias)
1101         {
1102                 if(!strcmp(varname, "*"))
1103                 {
1104                         if(is_multiple)
1105                                 *is_multiple = true;
1106                         return Cmd_Args(cmd);
1107                 }
1108                 else if(!strcmp(varname, "#"))
1109                 {
1110                         return va(vabuf, sizeof(vabuf), "%d", Cmd_Argc(cmd));
1111                 }
1112                 else if(varname[strlen(varname) - 1] == '-')
1113                 {
1114                         argno = strtol(varname, &endptr, 10);
1115                         if(endptr == varname + strlen(varname) - 1)
1116                         {
1117                                 // whole string is a number, apart from the -
1118                                 const char *p = Cmd_Args(cmd);
1119                                 for(; argno > 1; --argno)
1120                                         if(!COM_ParseToken_Console(&p))
1121                                                 break;
1122                                 if(p)
1123                                 {
1124                                         if(is_multiple)
1125                                                 *is_multiple = true;
1126
1127                                         // kill pre-argument whitespace
1128                                         for (;*p && ISWHITESPACE(*p);p++)
1129                                                 ;
1130
1131                                         return p;
1132                                 }
1133                         }
1134                 }
1135                 else
1136                 {
1137                         argno = strtol(varname, &endptr, 10);
1138                         if(*endptr == 0)
1139                         {
1140                                 // whole string is a number
1141                                 // NOTE: we already made sure we don't have an empty cvar name!
1142                                 if(argno >= 0 && argno < Cmd_Argc(cmd))
1143                                         return Cmd_Argv(cmd, argno);
1144                         }
1145                 }
1146         }
1147
1148         if((cvar = Cvar_FindVar(cmd->cvars, varname, cmd->cvars_flagsmask)) && !(cvar->flags & CVAR_PRIVATE))
1149                 return cvar->string;
1150
1151         return NULL;
1152 }
1153
1154 qbool Cmd_QuoteString(char *out, size_t outlen, const char *in, const char *quoteset, qbool putquotes)
1155 {
1156         qbool quote_quot = !!strchr(quoteset, '"');
1157         qbool quote_backslash = !!strchr(quoteset, '\\');
1158         qbool quote_dollar = !!strchr(quoteset, '$');
1159
1160         if(putquotes)
1161         {
1162                 if(outlen <= 2)
1163                 {
1164                         *out++ = 0;
1165                         return false;
1166                 }
1167                 *out++ = '"'; --outlen;
1168                 --outlen;
1169         }
1170
1171         while(*in)
1172         {
1173                 if(*in == '"' && quote_quot)
1174                 {
1175                         if(outlen <= 2)
1176                                 goto fail;
1177                         *out++ = '\\'; --outlen;
1178                         *out++ = '"'; --outlen;
1179                 }
1180                 else if(*in == '\\' && quote_backslash)
1181                 {
1182                         if(outlen <= 2)
1183                                 goto fail;
1184                         *out++ = '\\'; --outlen;
1185                         *out++ = '\\'; --outlen;
1186                 }
1187                 else if(*in == '$' && quote_dollar)
1188                 {
1189                         if(outlen <= 2)
1190                                 goto fail;
1191                         *out++ = '$'; --outlen;
1192                         *out++ = '$'; --outlen;
1193                 }
1194                 else
1195                 {
1196                         if(outlen <= 1)
1197                                 goto fail;
1198                         *out++ = *in; --outlen;
1199                 }
1200                 ++in;
1201         }
1202         if(putquotes)
1203                 *out++ = '"';
1204         *out++ = 0;
1205         return true;
1206 fail:
1207         if(putquotes)
1208                 *out++ = '"';
1209         *out++ = 0;
1210         return false;
1211 }
1212
1213 static const char *Cmd_GetCvarValue(cmd_state_t *cmd, const char *var, size_t varlen, cmd_alias_t *alias)
1214 {
1215         static char varname[MAX_INPUTLINE]; // cmd_mutex
1216         static char varval[MAX_INPUTLINE]; // cmd_mutex
1217         const char *varstr = NULL;
1218         char *varfunc;
1219         qbool required = false;
1220         qbool optional = false;
1221         static char asis[] = "asis"; // just to suppress const char warnings
1222
1223         if(varlen >= MAX_INPUTLINE)
1224                 varlen = MAX_INPUTLINE - 1;
1225         memcpy(varname, var, varlen);
1226         varname[varlen] = 0;
1227         varfunc = strchr(varname, ' ');
1228
1229         if(varfunc)
1230         {
1231                 *varfunc = 0;
1232                 ++varfunc;
1233         }
1234
1235         if(*var == 0)
1236         {
1237                 // empty cvar name?
1238                 if(alias)
1239                         Con_Printf(CON_WARN "Warning: Could not expand $ in alias %s\n", alias->name);
1240                 else
1241                         Con_Printf(CON_WARN "Warning: Could not expand $\n");
1242                 return "$";
1243         }
1244
1245         if(varfunc)
1246         {
1247                 char *p;
1248                 // ? means optional
1249                 while((p = strchr(varfunc, '?')))
1250                 {
1251                         optional = true;
1252                         memmove(p, p+1, strlen(p)); // with final NUL
1253                 }
1254                 // ! means required
1255                 while((p = strchr(varfunc, '!')))
1256                 {
1257                         required = true;
1258                         memmove(p, p+1, strlen(p)); // with final NUL
1259                 }
1260                 // kill spaces
1261                 while((p = strchr(varfunc, ' ')))
1262                 {
1263                         memmove(p, p+1, strlen(p)); // with final NUL
1264                 }
1265                 // if no function is left, NULL it
1266                 if(!*varfunc)
1267                         varfunc = NULL;
1268         }
1269
1270         if(varname[0] == '$')
1271                 varstr = Cmd_GetDirectCvarValue(cmd, Cmd_GetDirectCvarValue(cmd, varname + 1, alias, NULL), alias, NULL);
1272         else
1273         {
1274                 qbool is_multiple = false;
1275                 // Exception: $* and $n- don't use the quoted form by default
1276                 varstr = Cmd_GetDirectCvarValue(cmd, varname, alias, &is_multiple);
1277                 if(is_multiple)
1278                         if(!varfunc)
1279                                 varfunc = asis;
1280         }
1281
1282         if(!varstr)
1283         {
1284                 if(required)
1285                 {
1286                         if(alias)
1287                                 Con_Printf(CON_ERROR "Error: Could not expand $%s in alias %s\n", varname, alias->name);
1288                         else
1289                                 Con_Printf(CON_ERROR "Error: Could not expand $%s\n", varname);
1290                         return NULL;
1291                 }
1292                 else if(optional)
1293                 {
1294                         return "";
1295                 }
1296                 else
1297                 {
1298                         if(alias)
1299                                 Con_Printf(CON_WARN "Warning: Could not expand $%s in alias %s\n", varname, alias->name);
1300                         else
1301                                 Con_Printf(CON_WARN "Warning: Could not expand $%s\n", varname);
1302                         dpsnprintf(varval, sizeof(varval), "$%s", varname);
1303                         return varval;
1304                 }
1305         }
1306
1307         if(!varfunc || !strcmp(varfunc, "q")) // note: quoted form is default, use "asis" to override!
1308         {
1309                 // quote it so it can be used inside double quotes
1310                 // we just need to replace " by \", and of course, double backslashes
1311                 Cmd_QuoteString(varval, sizeof(varval), varstr, "\"\\", false);
1312                 return varval;
1313         }
1314         else if(!strcmp(varfunc, "asis"))
1315         {
1316                 return varstr;
1317         }
1318         else
1319                 Con_Printf("Unknown variable function %s\n", varfunc);
1320
1321         return varstr;
1322 }
1323
1324 /*
1325 Cmd_PreprocessString
1326
1327 Preprocesses strings and replaces $*, $param#, $cvar accordingly. Also strips comments.
1328 */
1329 static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias ) {
1330         const char *in;
1331         size_t eat, varlen;
1332         unsigned outlen;
1333         const char *val;
1334
1335         // don't crash if there's no room in the outtext buffer
1336         if( maxoutlen == 0 ) {
1337                 return false;
1338         }
1339         maxoutlen--; // because of \0
1340
1341         in = intext;
1342         outlen = 0;
1343
1344         while( *in && outlen < maxoutlen ) {
1345                 if( *in == '$' ) {
1346                         // this is some kind of expansion, see what comes after the $
1347                         in++;
1348
1349                         // The console does the following preprocessing:
1350                         //
1351                         // - $$ is transformed to a single dollar sign.
1352                         // - $var or ${var} are expanded to the contents of the named cvar,
1353                         //   with quotation marks and backslashes quoted so it can safely
1354                         //   be used inside quotation marks (and it should always be used
1355                         //   that way)
1356                         // - ${var asis} inserts the cvar value as is, without doing this
1357                         //   quoting
1358                         // - ${var ?} silently expands to the empty string if
1359                         //   $var does not exist
1360                         // - ${var !} fails expansion and executes nothing if
1361                         //   $var does not exist
1362                         // - prefix the cvar name with a dollar sign to do indirection;
1363                         //   for example, if $x has the value timelimit, ${$x} will return
1364                         //   the value of $timelimit
1365                         // - when expanding an alias, the special variable name $* refers
1366                         //   to all alias parameters, and a number refers to that numbered
1367                         //   alias parameter, where the name of the alias is $0, the first
1368                         //   parameter is $1 and so on; as a special case, $* inserts all
1369                         //   parameters, without extra quoting, so one can use $* to just
1370                         //   pass all parameters around. All parameters starting from $n
1371                         //   can be referred to as $n- (so $* is equivalent to $1-).
1372                         // - ${* q} and ${n- q} force quoting anyway
1373                         //
1374                         // Note: when expanding an alias, cvar expansion is done in the SAME step
1375                         // as alias expansion so that alias parameters or cvar values containing
1376                         // dollar signs have no unwanted bad side effects. However, this needs to
1377                         // be accounted for when writing complex aliases. For example,
1378                         //   alias foo "set x NEW; echo $x"
1379                         // actually expands to
1380                         //   "set x NEW; echo OLD"
1381                         // and will print OLD! To work around this, use a second alias:
1382                         //   alias foo "set x NEW; foo2"
1383                         //   alias foo2 "echo $x"
1384                         //
1385                         // Also note: lines starting with alias are exempt from cvar expansion.
1386                         // If you want cvar expansion, write "alias" instead:
1387                         //
1388                         //   set x 1
1389                         //   alias foo "echo $x"
1390                         //   "alias" bar "echo $x"
1391                         //   set x 2
1392                         //
1393                         // foo will print 2, because the variable $x will be expanded when the alias
1394                         // gets expanded. bar will print 1, because the variable $x was expanded
1395                         // at definition time. foo can be equivalently defined as
1396                         //
1397                         //   "alias" foo "echo $$x"
1398                         //
1399                         // because at definition time, $$ will get replaced to a single $.
1400
1401                         if( *in == '$' ) {
1402                                 val = "$";
1403                                 eat = 1;
1404                         } else if(*in == '{') {
1405                                 varlen = strcspn(in + 1, "}");
1406                                 if(in[varlen + 1] == '}')
1407                                 {
1408                                         val = Cmd_GetCvarValue(cmd, in + 1, varlen, alias);
1409                                         if(!val)
1410                                                 return false;
1411                                         eat = varlen + 2;
1412                                 }
1413                                 else
1414                                 {
1415                                         // ran out of data?
1416                                         val = NULL;
1417                                         eat = varlen + 1;
1418                                 }
1419                         } else {
1420                                 varlen = strspn(in, "#*0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-");
1421                                 val = Cmd_GetCvarValue(cmd, in, varlen, alias);
1422                                 if(!val)
1423                                         return false;
1424                                 eat = varlen;
1425                         }
1426                         if(val)
1427                         {
1428                                 // insert the cvar value
1429                                 while(*val && outlen < maxoutlen)
1430                                         outtext[outlen++] = *val++;
1431                                 in += eat;
1432                         }
1433                         else
1434                         {
1435                                 // copy the unexpanded text
1436                                 outtext[outlen++] = '$';
1437                                 while(eat && outlen < maxoutlen)
1438                                 {
1439                                         outtext[outlen++] = *in++;
1440                                         --eat;
1441                                 }
1442                         }
1443                 }
1444                 else 
1445                         outtext[outlen++] = *in++;
1446         }
1447         outtext[outlen] = 0;
1448         return true;
1449 }
1450
1451 /*
1452 ============
1453 Cmd_ExecuteAlias
1454
1455 Called for aliases and fills in the alias into the cbuffer
1456 ============
1457 */
1458 static void Cmd_ExecuteAlias (cmd_state_t *cmd, cmd_alias_t *alias)
1459 {
1460         static char buffer[ MAX_INPUTLINE ]; // cmd_mutex
1461         static char buffer2[ MAX_INPUTLINE ]; // cmd_mutex
1462         qbool ret = Cmd_PreprocessString( cmd, alias->value, buffer, sizeof(buffer) - 2, alias );
1463         if(!ret)
1464                 return;
1465         // insert at start of command buffer, so that aliases execute in order
1466         // (fixes bug introduced by Black on 20050705)
1467
1468         // Note: Cbuf_PreprocessString will be called on this string AGAIN! So we
1469         // have to make sure that no second variable expansion takes place, otherwise
1470         // alias parameters containing dollar signs can have bad effects.
1471         Cmd_QuoteString(buffer2, sizeof(buffer2), buffer, "$", false);
1472         Cbuf_InsertText(cmd, buffer2);
1473 }
1474
1475 /*
1476 ========
1477 Cmd_List
1478
1479         CmdList Added by EvilTypeGuy eviltypeguy@qeradiant.com
1480         Thanks to Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
1481
1482 ========
1483 */
1484 static void Cmd_List_f (cmd_state_t *cmd)
1485 {
1486         cmd_function_t *func;
1487         const char *partial;
1488         size_t len;
1489         int count;
1490         qbool ispattern;
1491
1492         if (Cmd_Argc(cmd) > 1)
1493         {
1494                 partial = Cmd_Argv(cmd, 1);
1495                 len = strlen(partial);
1496                 ispattern = (strchr(partial, '*') || strchr(partial, '?'));
1497         }
1498         else
1499         {
1500                 partial = NULL;
1501                 len = 0;
1502                 ispattern = false;
1503         }
1504
1505         count = 0;
1506         for (func = cmd->userdefined->csqc_functions; func; func = func->next)
1507         {
1508                 if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len)))
1509                         continue;
1510                 Con_Printf("%s : %s\n", func->name, func->description);
1511                 count++;
1512         }
1513         for (func = cmd->engine_functions; func; func = func->next)
1514         {
1515                 if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len)))
1516                         continue;
1517                 Con_Printf("%s : %s\n", func->name, func->description);
1518                 count++;
1519         }
1520
1521         if (len)
1522         {
1523                 if(ispattern)
1524                         Con_Printf("%i Command%s matching \"%s\"\n\n", count, (count > 1) ? "s" : "", partial);
1525                 else
1526                         Con_Printf("%i Command%s beginning with \"%s\"\n\n", count, (count > 1) ? "s" : "", partial);
1527         }
1528         else
1529                 Con_Printf("%i Command%s\n\n", count, (count > 1) ? "s" : "");
1530 }
1531
1532 static void Cmd_Apropos_f(cmd_state_t *cmd)
1533 {
1534         cmd_function_t *func;
1535         cvar_t *cvar;
1536         cmd_alias_t *alias;
1537         const char *partial;
1538         int count;
1539         qbool ispattern;
1540         char vabuf[1024];
1541
1542         if (Cmd_Argc(cmd) > 1)
1543                 partial = Cmd_Args(cmd);
1544         else
1545         {
1546                 Con_Printf("usage: %s <string>\n",Cmd_Argv(cmd, 0));
1547                 return;
1548         }
1549
1550         ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
1551         if(!ispattern)
1552                 partial = va(vabuf, sizeof(vabuf), "*%s*", partial);
1553
1554         count = 0;
1555         for (cvar = cmd->cvars->vars; cvar; cvar = cvar->next)
1556         {
1557                 if (matchpattern_with_separator(cvar->name, partial, true, "", false) ||
1558                     matchpattern_with_separator(cvar->description, partial, true, "", false))
1559                 {
1560                         Con_Printf ("cvar ");
1561                         Cvar_PrintHelp(cvar, cvar->name, true);
1562                         count++;
1563                 }
1564                 for (int i = 0; i < cvar->aliasindex; i++)
1565                 {
1566                         if (matchpattern_with_separator(cvar->aliases[i], partial, true, "", false))
1567                         {
1568                                 Con_Printf ("cvar ");
1569                                 Cvar_PrintHelp(cvar, cvar->aliases[i], true);
1570                                 count++;
1571                         }
1572                 }
1573         }
1574         for (func = cmd->userdefined->csqc_functions; func; func = func->next)
1575         {
1576                 if (!matchpattern_with_separator(func->name, partial, true, "", false))
1577                         if (!matchpattern_with_separator(func->description, partial, true, "", false))
1578                                 continue;
1579                 Con_Printf("command ^2%s^7: %s\n", func->name, func->description);
1580                 count++;
1581         }
1582         for (func = cmd->engine_functions; func; func = func->next)
1583         {
1584                 if (!matchpattern_with_separator(func->name, partial, true, "", false))
1585                 if (!matchpattern_with_separator(func->description, partial, true, "", false))
1586                         continue;
1587                 Con_Printf("command ^2%s^7: %s\n", func->name, func->description);
1588                 count++;
1589         }
1590         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
1591         {
1592                 // procede here a bit differently as an alias value always got a final \n
1593                 if (!matchpattern_with_separator(alias->name, partial, true, "", false))
1594                 if (!matchpattern_with_separator(alias->value, partial, true, "\n", false)) // when \n is as separator wildcards don't match it
1595                         continue;
1596                 Con_Printf("alias ^5%s^7: %s", alias->name, alias->value); // do not print an extra \n
1597                 count++;
1598         }
1599         Con_Printf("%i result%s\n\n", count, (count > 1) ? "s" : "");
1600 }
1601
1602 /*
1603 ============
1604 Cmd_Init
1605 ============
1606 */
1607 void Cmd_Init(void)
1608 {
1609         cmd_iter_t *cmd_iter;
1610         cbuf_t *cbuf = (cbuf_t *)Z_Malloc(sizeof(cbuf_t));
1611         cbuf->maxsize = 655360;
1612         cbuf->lock = Thread_CreateMutex();
1613         cbuf->wait = false;
1614         host.cbuf = cbuf;
1615
1616         cbuf->start.prev = cbuf->start.next = &(cbuf->start);
1617         cbuf->deferred.prev = cbuf->deferred.next = &(cbuf->deferred);
1618         cbuf->free.prev = cbuf->free.next = &(cbuf->free);
1619
1620         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
1621         {
1622                 cmd_state_t *cmd = cmd_iter->cmd;
1623                 cmd->mempool = Mem_AllocPool("commands", 0, NULL);
1624                 // space for commands and script files
1625                 cmd->cbuf = cbuf;
1626                 cmd->null_string = "";
1627         }
1628         // client console can see server cvars because the user may start a server
1629         cmd_client.cvars = &cvars_all;
1630         cmd_client.cvars_flagsmask = CVAR_CLIENT | CVAR_SERVER;
1631         cmd_client.cmd_flags = CMD_CLIENT | CMD_CLIENT_FROM_SERVER;
1632         cmd_client.auto_flags = CMD_SERVER_FROM_CLIENT;
1633         cmd_client.auto_function = CL_ForwardToServer_f; // FIXME: Move this to the client.
1634         cmd_client.userdefined = &cmd_userdefined_all;
1635         // dedicated server console can only see server cvars, there is no client
1636         cmd_server.cvars = &cvars_all;
1637         cmd_server.cvars_flagsmask = CVAR_SERVER;
1638         cmd_server.cmd_flags = CMD_SERVER;
1639         cmd_server.auto_flags = 0;
1640         cmd_server.auto_function = NULL;
1641         cmd_server.userdefined = &cmd_userdefined_all;
1642         // server commands received from clients have no reason to access cvars, cvar expansion seems perilous.
1643         cmd_serverfromclient.cvars = &cvars_null;
1644         cmd_serverfromclient.cvars_flagsmask = 0;
1645         cmd_serverfromclient.cmd_flags = CMD_SERVER_FROM_CLIENT | CMD_USERINFO;
1646         cmd_serverfromclient.auto_flags = 0;
1647         cmd_serverfromclient.auto_function = NULL;
1648         cmd_serverfromclient.userdefined = &cmd_userdefined_null;
1649
1650 //
1651 // register our commands
1652 //
1653         // client-only commands
1654         Cmd_AddCommand(CMD_SHARED, "wait", Cmd_Wait_f, "make script execution wait for next rendered frame");
1655         Cmd_AddCommand(CMD_CLIENT, "cprint", Cmd_Centerprint_f, "print something at the screen center");
1656
1657         // maintenance commands used for upkeep of cvars and saved configs
1658         Cmd_AddCommand(CMD_SHARED, "stuffcmds", Cmd_StuffCmds_f, "execute commandline parameters (must be present in quake.rc script)");
1659         Cmd_AddCommand(CMD_SHARED, "cvar_lockdefaults", Cvar_LockDefaults_f, "stores the current values of all cvars into their default values, only used once during startup after parsing default.cfg");
1660         Cmd_AddCommand(CMD_SHARED, "cvar_resettodefaults_all", Cvar_ResetToDefaults_All_f, "sets all cvars to their locked default values");
1661         Cmd_AddCommand(CMD_SHARED, "cvar_resettodefaults_nosaveonly", Cvar_ResetToDefaults_NoSaveOnly_f, "sets all non-saved cvars to their locked default values (variables that will not be saved to config.cfg)");
1662         Cmd_AddCommand(CMD_SHARED, "cvar_resettodefaults_saveonly", Cvar_ResetToDefaults_SaveOnly_f, "sets all saved cvars to their locked default values (variables that will be saved to config.cfg)");
1663
1664         // general console commands used in multiple environments
1665         Cmd_AddCommand(CMD_SHARED, "exec", Cmd_Exec_f, "execute a script file");
1666         Cmd_AddCommand(CMD_SHARED, "echo",Cmd_Echo_f, "print a message to the console (useful in scripts)");
1667         Cmd_AddCommand(CMD_SHARED, "alias",Cmd_Alias_f, "create a script function (parameters are passed in as $X (being X a number), $* for all parameters, $X- for all parameters starting from $X). Without arguments show the list of all alias");
1668         Cmd_AddCommand(CMD_SHARED, "unalias",Cmd_UnAlias_f, "remove an alias");
1669         Cmd_AddCommand(CMD_SHARED, "set", Cvar_Set_f, "create or change the value of a console variable");
1670         Cmd_AddCommand(CMD_SHARED, "seta", Cvar_SetA_f, "create or change the value of a console variable that will be saved to config.cfg");
1671         Cmd_AddCommand(CMD_SHARED, "unset", Cvar_Del_f, "delete a cvar (does not work for static ones like _cl_name, or read-only ones)");
1672
1673 #ifdef FILLALLCVARSWITHRUBBISH
1674         Cmd_AddCommand(CMD_SHARED, "fillallcvarswithrubbish", Cvar_FillAll_f, "fill all cvars with a specified number of characters to provoke buffer overruns");
1675 #endif /* FILLALLCVARSWITHRUBBISH */
1676
1677         // 2000-01-09 CmdList, CvarList commands By Matthias "Maddes" Buecher
1678         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
1679         Cmd_AddCommand(CMD_SHARED, "cmdlist", Cmd_List_f, "lists all console commands beginning with the specified prefix or matching the specified wildcard pattern");
1680         Cmd_AddCommand(CMD_SHARED, "cvarlist", Cvar_List_f, "lists all console variables beginning with the specified prefix or matching the specified wildcard pattern");
1681         Cmd_AddCommand(CMD_SHARED, "apropos", Cmd_Apropos_f, "lists all console variables/commands/aliases containing the specified string in the name or description");
1682         Cmd_AddCommand(CMD_SHARED, "find", Cmd_Apropos_f, "lists all console variables/commands/aliases containing the specified string in the name or description");
1683
1684         Cmd_AddCommand(CMD_SHARED, "defer", Cmd_Defer_f, "execute a command in the future");
1685
1686         // DRESK - 5/14/06
1687         // Support Doom3-style Toggle Command
1688         Cmd_AddCommand(CMD_SHARED | CMD_CLIENT_FROM_SERVER, "toggle", Cmd_Toggle_f, "toggles a console variable's values (use for more info)");
1689 }
1690
1691 /*
1692 ============
1693 Cmd_Shutdown
1694 ============
1695 */
1696 void Cmd_Shutdown(void)
1697 {
1698         cmd_iter_t *cmd_iter;
1699         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
1700         {
1701                 cmd_state_t *cmd = cmd_iter->cmd;
1702
1703                 if (cmd->cbuf->lock)
1704                 {
1705                         // we usually have this locked when we get here from Host_Quit_f
1706                         Cbuf_Unlock(cmd->cbuf);
1707                 }
1708
1709                 Mem_FreePool(&cmd->mempool);
1710         }
1711 }
1712
1713 /*
1714 ============
1715 Cmd_Argc
1716 ============
1717 */
1718 int             Cmd_Argc (cmd_state_t *cmd)
1719 {
1720         return cmd->argc;
1721 }
1722
1723 /*
1724 ============
1725 Cmd_Argv
1726 ============
1727 */
1728 const char *Cmd_Argv(cmd_state_t *cmd, int arg)
1729 {
1730         if (arg >= cmd->argc )
1731                 return cmd->null_string;
1732         return cmd->argv[arg];
1733 }
1734
1735 /*
1736 ============
1737 Cmd_Args
1738 ============
1739 */
1740 const char *Cmd_Args (cmd_state_t *cmd)
1741 {
1742         return cmd->args;
1743 }
1744
1745 /*
1746 ============
1747 Cmd_TokenizeString
1748
1749 Parses the given string into command line tokens.
1750 ============
1751 */
1752 // AK: This function should only be called from ExcuteString because the current design is a bit of an hack
1753 static void Cmd_TokenizeString (cmd_state_t *cmd, const char *text)
1754 {
1755         int l;
1756
1757         cmd->argc = 0;
1758         cmd->args = NULL;
1759
1760         while (1)
1761         {
1762                 // skip whitespace up to a /n
1763                 while (*text && ISWHITESPACE(*text) && *text != '\r' && *text != '\n')
1764                         text++;
1765
1766                 // line endings:
1767                 // UNIX: \n
1768                 // Mac: \r
1769                 // Windows: \r\n
1770                 if (*text == '\n' || *text == '\r')
1771                 {
1772                         // a newline separates commands in the buffer
1773                         if (*text == '\r' && text[1] == '\n')
1774                                 text++;
1775                         text++;
1776                         break;
1777                 }
1778
1779                 if (!*text)
1780                         return;
1781
1782                 if (cmd->argc == 1)
1783                         cmd->args = text;
1784
1785                 if (!COM_ParseToken_Console(&text))
1786                         return;
1787
1788                 if (cmd->argc < MAX_ARGS)
1789                 {
1790                         l = (int)strlen(com_token) + 1;
1791                         if (cmd->cbuf->tokenizebufferpos + l > CMD_TOKENIZELENGTH)
1792                         {
1793                                 Con_Printf("Cmd_TokenizeString: ran out of %i character buffer space for command arguments\n", CMD_TOKENIZELENGTH);
1794                                 break;
1795                         }
1796                         memcpy (cmd->cbuf->tokenizebuffer + cmd->cbuf->tokenizebufferpos, com_token, l);
1797                         cmd->argv[cmd->argc] = cmd->cbuf->tokenizebuffer + cmd->cbuf->tokenizebufferpos;
1798                         cmd->cbuf->tokenizebufferpos += l;
1799                         cmd->argc++;
1800                 }
1801         }
1802 }
1803
1804
1805 /*
1806 ============
1807 Cmd_AddCommand
1808 ============
1809 */
1810 void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const char *description)
1811 {
1812         cmd_function_t *func;
1813         cmd_function_t *prev, *current;
1814         cmd_state_t *cmd;
1815         xcommand_t save = NULL;
1816         qbool auto_add = false;
1817         int i;
1818
1819         for (i = 0; i < 3; i++)
1820         {
1821                 cmd = cmd_iter_all[i].cmd;
1822                 if ((flags & cmd->cmd_flags) || (flags & cmd->auto_flags))
1823                 {
1824                         if((flags & cmd->auto_flags) && cmd->auto_function)
1825                         {
1826                                 save = function;
1827                                 function = cmd->auto_function;
1828                                 auto_add = true;
1829                         }
1830
1831                         // fail if the command is a variable name
1832                         if (Cvar_FindVar(cmd->cvars, cmd_name, ~0))
1833                         {
1834                                 Con_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
1835                                 return;
1836                         }
1837
1838                         if (function)
1839                         {
1840                                 // fail if the command already exists in this interpreter
1841                                 for (func = cmd->engine_functions; func; func = func->next)
1842                                 {
1843                                         if (!strcmp(cmd_name, func->name))
1844                                         {
1845                                                 if(func->autofunc && !auto_add)
1846                                                         break;
1847                                                 Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
1848                                                 goto next;
1849                                         }
1850                                 }
1851
1852                                 func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
1853                                 func->flags = flags;
1854                                 func->name = cmd_name;
1855                                 func->function = function;
1856                                 func->description = description;
1857                                 func->next = cmd->engine_functions;
1858                                 func->autofunc = auto_add;
1859
1860                                 // insert it at the right alphanumeric position
1861                                 for (prev = NULL, current = cmd->engine_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
1862                                         ;
1863                                 if (prev) {
1864                                         prev->next = func;
1865                                 }
1866                                 else {
1867                                         cmd->engine_functions = func;
1868                                 }
1869                                 func->next = current;
1870                         }
1871                         else
1872                         {
1873                                 // mark csqcfunc if the function already exists in the csqc_functions list
1874                                 for (func = cmd->userdefined->csqc_functions; func; func = func->next)
1875                                 {
1876                                         if (!strcmp(cmd_name, func->name))
1877                                         {
1878                                                 func->csqcfunc = true; //[515]: csqc
1879                                                 continue;
1880                                         }
1881                                 }
1882
1883
1884                                 func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
1885                                 func->name = cmd_name;
1886                                 func->function = function;
1887                                 func->description = description;
1888                                 func->csqcfunc = true; //[515]: csqc
1889                                 func->next = cmd->userdefined->csqc_functions;
1890                                 func->autofunc = false;
1891
1892                                 // insert it at the right alphanumeric position
1893                                 for (prev = NULL, current = cmd->userdefined->csqc_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
1894                                         ;
1895                                 if (prev) {
1896                                         prev->next = func;
1897                                 }
1898                                 else {
1899                                         cmd->userdefined->csqc_functions = func;
1900                                 }
1901                                 func->next = current;
1902                         }
1903                         if (save)
1904                                 function = save;
1905                 }
1906 next:
1907                 auto_add = false;
1908                 continue;
1909         }
1910 }
1911
1912 static int Cmd_Compare(const char *s1, const char *s2, size_t len, qbool casesensitive)
1913 {
1914         if(len)
1915                 return (casesensitive ? strncmp(s1, s2, len) : strncasecmp(s1, s2, len));
1916         else
1917                 return (casesensitive ? strcmp(s1, s2) : strcasecmp(s1, s2));
1918 }
1919
1920 cmd_function_t *Cmd_GetCommand(cmd_state_t *cmd, const char *partial, size_t len, qbool casesensitive)
1921 {
1922         cmd_function_t *func = NULL;
1923
1924         // check functions
1925         for (func = cmd->userdefined->csqc_functions; func; func = func->next)
1926                 if (!Cmd_Compare(partial, func->name, len, casesensitive))
1927                         break;
1928
1929         for (func=cmd->engine_functions ; func ; func=func->next)
1930                 if (!Cmd_Compare(partial, func->name, len, casesensitive))
1931                         break;
1932
1933         return func;
1934 }
1935
1936 /*
1937 ============
1938 Cmd_Exists
1939 ============
1940 */
1941 qbool Cmd_Exists (cmd_state_t *cmd, const char *cmd_name)
1942 {
1943         if(Cmd_GetCommand(cmd, cmd_name, 0, true))
1944                 return true;
1945         return false;
1946 }
1947
1948 /*
1949 ============
1950 Cmd_CompleteCommand
1951 ============
1952 */
1953 const char *Cmd_CompleteCommand (cmd_state_t *cmd, const char *partial)
1954 {
1955         cmd_function_t *func;
1956
1957         func = Cmd_GetCommand(cmd, partial, strlen(partial), false);
1958         if(func)
1959                 return func->name;
1960         return NULL;
1961 }
1962
1963 /*
1964         Cmd_CompleteCountPossible
1965
1966         New function for tab-completion system
1967         Added by EvilTypeGuy
1968         Thanks to Fett erich@heintz.com
1969         Thanks to taniwha
1970
1971 */
1972 int Cmd_CompleteCountPossible (cmd_state_t *cmd, const char *partial)
1973 {
1974         cmd_function_t *func;
1975         size_t len;
1976         int h;
1977
1978         h = 0;
1979         len = strlen(partial);
1980
1981         if (!len)
1982                 return 0;
1983
1984         // Loop through the command list and count all partial matches
1985         for (func = cmd->userdefined->csqc_functions; func; func = func->next)
1986                 if (!strncasecmp(partial, func->name, len))
1987                         h++;
1988
1989         for (func = cmd->engine_functions; func; func = func->next)
1990                 if (!strncasecmp(partial, func->name, len))
1991                         h++;
1992
1993         return h;
1994 }
1995
1996 /*
1997         Cmd_CompleteBuildList
1998
1999         New function for tab-completion system
2000         Added by EvilTypeGuy
2001         Thanks to Fett erich@heintz.com
2002         Thanks to taniwha
2003
2004 */
2005 const char **Cmd_CompleteBuildList (cmd_state_t *cmd, const char *partial)
2006 {
2007         cmd_function_t *func;
2008         size_t len = 0;
2009         size_t bpos = 0;
2010         size_t sizeofbuf = (Cmd_CompleteCountPossible (cmd, partial) + 1) * sizeof (const char *);
2011         const char **buf;
2012
2013         len = strlen(partial);
2014         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
2015         // Loop through the functions lists and print all matches
2016         for (func = cmd->userdefined->csqc_functions; func; func = func->next)
2017                 if (!strncasecmp(partial, func->name, len))
2018                         buf[bpos++] = func->name;
2019         for (func = cmd->engine_functions; func; func = func->next)
2020                 if (!strncasecmp(partial, func->name, len))
2021                         buf[bpos++] = func->name;
2022
2023         buf[bpos] = NULL;
2024         return buf;
2025 }
2026
2027 // written by LadyHavoc
2028 void Cmd_CompleteCommandPrint (cmd_state_t *cmd, const char *partial)
2029 {
2030         cmd_function_t *func;
2031         size_t len = strlen(partial);
2032         // Loop through the command list and print all matches
2033         for (func = cmd->userdefined->csqc_functions; func; func = func->next)
2034                 if (!strncasecmp(partial, func->name, len))
2035                         Con_Printf("^2%s^7: %s\n", func->name, func->description);
2036         for (func = cmd->engine_functions; func; func = func->next)
2037                 if (!strncasecmp(partial, func->name, len))
2038                         Con_Printf("^2%s^7: %s\n", func->name, func->description);
2039 }
2040
2041 /*
2042         Cmd_CompleteAlias
2043
2044         New function for tab-completion system
2045         Added by EvilTypeGuy
2046         Thanks to Fett erich@heintz.com
2047         Thanks to taniwha
2048
2049 */
2050 const char *Cmd_CompleteAlias (cmd_state_t *cmd, const char *partial)
2051 {
2052         cmd_alias_t *alias;
2053         size_t len;
2054
2055         len = strlen(partial);
2056
2057         if (!len)
2058                 return NULL;
2059
2060         // Check functions
2061         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2062                 if (!strncasecmp(partial, alias->name, len))
2063                         return alias->name;
2064
2065         return NULL;
2066 }
2067
2068 // written by LadyHavoc
2069 void Cmd_CompleteAliasPrint (cmd_state_t *cmd, const char *partial)
2070 {
2071         cmd_alias_t *alias;
2072         size_t len = strlen(partial);
2073         // Loop through the alias list and print all matches
2074         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2075                 if (!strncasecmp(partial, alias->name, len))
2076                         Con_Printf("^5%s^7: %s", alias->name, alias->value);
2077 }
2078
2079
2080 /*
2081         Cmd_CompleteAliasCountPossible
2082
2083         New function for tab-completion system
2084         Added by EvilTypeGuy
2085         Thanks to Fett erich@heintz.com
2086         Thanks to taniwha
2087
2088 */
2089 int Cmd_CompleteAliasCountPossible (cmd_state_t *cmd, const char *partial)
2090 {
2091         cmd_alias_t     *alias;
2092         size_t          len;
2093         int                     h;
2094
2095         h = 0;
2096
2097         len = strlen(partial);
2098
2099         if (!len)
2100                 return 0;
2101
2102         // Loop through the command list and count all partial matches
2103         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2104                 if (!strncasecmp(partial, alias->name, len))
2105                         h++;
2106
2107         return h;
2108 }
2109
2110 /*
2111         Cmd_CompleteAliasBuildList
2112
2113         New function for tab-completion system
2114         Added by EvilTypeGuy
2115         Thanks to Fett erich@heintz.com
2116         Thanks to taniwha
2117
2118 */
2119 const char **Cmd_CompleteAliasBuildList (cmd_state_t *cmd, const char *partial)
2120 {
2121         cmd_alias_t *alias;
2122         size_t len = 0;
2123         size_t bpos = 0;
2124         size_t sizeofbuf = (Cmd_CompleteAliasCountPossible (cmd, partial) + 1) * sizeof (const char *);
2125         const char **buf;
2126
2127         len = strlen(partial);
2128         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
2129         // Loop through the alias list and print all matches
2130         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2131                 if (!strncasecmp(partial, alias->name, len))
2132                         buf[bpos++] = alias->name;
2133
2134         buf[bpos] = NULL;
2135         return buf;
2136 }
2137
2138 // TODO: Make this more generic?
2139 void Cmd_ClearCSQCCommands (cmd_state_t *cmd)
2140 {
2141         cmd_function_t *func;
2142         cmd_function_t **next = &cmd->userdefined->csqc_functions;
2143         
2144         while(*next)
2145         {
2146                 func = *next;
2147                 *next = func->next;
2148                 Z_Free(func);
2149         }
2150 }
2151
2152 extern cvar_t sv_cheats;
2153
2154 /*
2155 ============
2156 Cmd_ExecuteString
2157
2158 A complete command line has been parsed, so try to execute it
2159 FIXME: lookupnoadd the token to speed search?
2160 ============
2161 */
2162 void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qbool lockmutex)
2163 {
2164         int oldpos;
2165         cmd_function_t *func;
2166         cmd_alias_t *a;
2167         if (lockmutex)
2168                 Cbuf_Lock(cmd->cbuf);
2169         oldpos = cmd->cbuf->tokenizebufferpos;
2170         cmd->source = src;
2171
2172         Cmd_TokenizeString (cmd, text);
2173
2174 // execute the command line
2175         if (!Cmd_Argc(cmd))
2176                 goto done; // no tokens
2177
2178 // check functions
2179         func = Cmd_GetCommand(cmd, cmd->argv[0], 0, false);
2180         if(func)
2181         {
2182                 if (func->csqcfunc && CL_VM_ConsoleCommand(text))       //[515]: csqc
2183                         goto done;
2184                 else
2185                 {
2186                         switch (src)
2187                         {
2188                         case src_local:
2189                                 if (func->function)
2190                                         func->function(cmd);
2191                                 else
2192                                         Con_Printf("Command \"%s\" can not be executed\n", Cmd_Argv(cmd, 0));
2193                                 goto done;
2194                         case src_client:
2195                                 if (func->function)
2196                                 {
2197                                         if((func->flags & CMD_CHEAT) && !sv_cheats.integer)
2198                                                 SV_ClientPrintf("No cheats allowed. The server must have sv_cheats set to 1\n");
2199                                         else
2200                                                 func->function(cmd);
2201                                         goto done;
2202                                 }
2203                         }
2204                 }
2205         }
2206
2207         // if it's a client command and no command was found, say so.
2208         if (cmd->source == src_client)
2209         {
2210                 Con_Printf("Client \"%s\" tried to execute \"%s\"\n", host_client->name, text);
2211                 goto done;
2212         }
2213
2214 // check alias
2215         for (a=cmd->userdefined->alias ; a ; a=a->next)
2216         {
2217                 if (!strcasecmp (cmd->argv[0], a->name))
2218                 {
2219                         Cmd_ExecuteAlias(cmd, a);
2220                         goto done;
2221                 }
2222         }
2223
2224 // check cvars
2225         if (!Cvar_Command(cmd) && host.framecount > 0)
2226                 Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(cmd, 0));
2227 done:
2228         cmd->cbuf->tokenizebufferpos = oldpos;
2229         if (lockmutex)
2230                 Cbuf_Unlock(cmd->cbuf);
2231 }
2232
2233 /*
2234 ================
2235 Cmd_CheckParm
2236
2237 Returns the position (1 to argc-1) in the command's argument list
2238 where the given parameter apears, or 0 if not present
2239 ================
2240 */
2241
2242 int Cmd_CheckParm (cmd_state_t *cmd, const char *parm)
2243 {
2244         int i;
2245
2246         if (!parm)
2247         {
2248                 Con_Printf ("Cmd_CheckParm: NULL");
2249                 return 0;
2250         }
2251
2252         for (i = 1; i < Cmd_Argc (cmd); i++)
2253                 if (!strcasecmp (parm, Cmd_Argv(cmd, i)))
2254                         return i;
2255
2256         return 0;
2257 }
2258
2259
2260
2261 void Cmd_SaveInitState(void)
2262 {
2263         cmd_iter_t *cmd_iter;
2264         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
2265         {
2266                 cmd_state_t *cmd = cmd_iter->cmd;
2267                 cmd_function_t *f;
2268                 cmd_alias_t *a;
2269                 for (f = cmd->userdefined->csqc_functions; f; f = f->next)
2270                         f->initstate = true;
2271                 for (f = cmd->engine_functions; f; f = f->next)
2272                         f->initstate = true;
2273                 for (a = cmd->userdefined->alias; a; a = a->next)
2274                 {
2275                         a->initstate = true;
2276                         a->initialvalue = Mem_strdup(zonemempool, a->value);
2277                 }
2278         }
2279         Cvar_SaveInitState(&cvars_all);
2280 }
2281
2282 void Cmd_RestoreInitState(void)
2283 {
2284         cmd_iter_t *cmd_iter;
2285         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
2286         {
2287                 cmd_state_t *cmd = cmd_iter->cmd;
2288                 cmd_function_t *f, **fp;
2289                 cmd_alias_t *a, **ap;
2290                 for (fp = &cmd->userdefined->csqc_functions; (f = *fp);)
2291                 {
2292                         if (f->initstate)
2293                                 fp = &f->next;
2294                         else
2295                         {
2296                                 // destroy this command, it didn't exist at init
2297                                 Con_DPrintf("Cmd_RestoreInitState: Destroying command %s\n", f->name);
2298                                 *fp = f->next;
2299                                 Z_Free(f);
2300                         }
2301                 }
2302                 for (fp = &cmd->engine_functions; (f = *fp);)
2303                 {
2304                         if (f->initstate)
2305                                 fp = &f->next;
2306                         else
2307                         {
2308                                 // destroy this command, it didn't exist at init
2309                                 Con_DPrintf("Cmd_RestoreInitState: Destroying command %s\n", f->name);
2310                                 *fp = f->next;
2311                                 Z_Free(f);
2312                         }
2313                 }
2314                 for (ap = &cmd->userdefined->alias; (a = *ap);)
2315                 {
2316                         if (a->initstate)
2317                         {
2318                                 // restore this alias, it existed at init
2319                                 if (strcmp(a->value ? a->value : "", a->initialvalue ? a->initialvalue : ""))
2320                                 {
2321                                         Con_DPrintf("Cmd_RestoreInitState: Restoring alias %s\n", a->name);
2322                                         if (a->value)
2323                                                 Z_Free(a->value);
2324                                         a->value = Mem_strdup(zonemempool, a->initialvalue);
2325                                 }
2326                                 ap = &a->next;
2327                         }
2328                         else
2329                         {
2330                                 // free this alias, it didn't exist at init...
2331                                 Con_DPrintf("Cmd_RestoreInitState: Destroying alias %s\n", a->name);
2332                                 *ap = a->next;
2333                                 if (a->value)
2334                                         Z_Free(a->value);
2335                                 Z_Free(a);
2336                         }
2337                 }
2338         }
2339         Cvar_RestoreInitState(&cvars_all);
2340 }