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