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