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