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