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