1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 // cmd.c -- Quake script command processing module
30 
31 #include "../qcommon/q_shared.h"
32 #include "qcommon.h"
33 
34 #define MAX_CMD_BUFFER  128*1024
35 #define MAX_CMD_LINE    1024
36 
37 typedef struct {
38 	byte    *data;
39 	int maxsize;
40 	int cmdsize;        //DAJ renamed from cursize
41 } cmd_t;
42 
43 int cmd_wait;
44 cmd_t cmd_text;
45 byte cmd_text_buf[MAX_CMD_BUFFER];
46 
47 
48 //=============================================================================
49 
50 /*
51 ============
52 Cmd_Wait_f
53 
54 Causes execution of the remainder of the command buffer to be delayed until
55 next frame.  This allows commands like:
56 bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster"
57 ============
58 */
Cmd_Wait_f(void)59 void Cmd_Wait_f( void ) {
60 	if ( Cmd_Argc() == 2 ) {
61 		cmd_wait = atoi( Cmd_Argv( 1 ) );
62 		if ( cmd_wait < 0 )
63 			cmd_wait = 1; // ignore the argument
64 	} else {
65 		cmd_wait = 1;
66 	}
67 }
68 
69 
70 /*
71 =============================================================================
72 
73 						COMMAND BUFFER
74 
75 =============================================================================
76 */
77 
78 /*
79 ============
80 Cbuf_Init
81 ============
82 */
Cbuf_Init(void)83 void Cbuf_Init( void ) {
84 	cmd_text.data = cmd_text_buf;
85 	cmd_text.maxsize = MAX_CMD_BUFFER;
86 	cmd_text.cmdsize = 0;
87 }
88 
89 /*
90 ============
91 Cbuf_AddText
92 
93 Adds command text at the end of the buffer, does NOT add a final \n
94 ============
95 */
Cbuf_AddText(const char * text)96 void Cbuf_AddText( const char *text ) {
97 	int l;
98 
99 	l = strlen( text );
100 
101 	if ( cmd_text.cmdsize + l >= cmd_text.maxsize ) {
102 		Com_Printf( "Cbuf_AddText: overflow\n" );
103 		return;
104 	}
105 	memcpy( &cmd_text.data[cmd_text.cmdsize], text, l );
106 	cmd_text.cmdsize += l;
107 }
108 
109 
110 /*
111 ============
112 Cbuf_InsertText
113 
114 Adds command text immediately after the current command
115 Adds a \n to the text
116 ============
117 */
Cbuf_InsertText(const char * text)118 void Cbuf_InsertText( const char *text ) {
119 	int len;
120 	int i;
121 
122 	len = strlen( text ) + 1;
123 	if ( len + cmd_text.cmdsize > cmd_text.maxsize ) {
124 		Com_Printf( "Cbuf_InsertText overflowed\n" );
125 		return;
126 	}
127 
128 	// move the existing command text
129 	for ( i = cmd_text.cmdsize - 1 ; i >= 0 ; i-- ) {
130 		cmd_text.data[ i + len ] = cmd_text.data[ i ];
131 	}
132 
133 	// copy the new text in
134 	memcpy( cmd_text.data, text, len - 1 );
135 
136 	// add a \n
137 	cmd_text.data[ len - 1 ] = '\n';
138 
139 	cmd_text.cmdsize += len;
140 }
141 
142 
143 /*
144 ============
145 Cbuf_ExecuteText
146 ============
147 */
Cbuf_ExecuteText(int exec_when,const char * text)148 void Cbuf_ExecuteText( int exec_when, const char *text ) {
149 	switch ( exec_when )
150 	{
151 	case EXEC_NOW:
152 		if ( text && strlen( text ) > 0 ) {
153 			Com_DPrintf(S_COLOR_YELLOW "EXEC_NOW %s\n", text);
154 			Cmd_ExecuteString( text );
155 		} else {
156 			Cbuf_Execute();
157 			Com_DPrintf(S_COLOR_YELLOW "EXEC_NOW %s\n", cmd_text.data);
158 		}
159 		break;
160 	case EXEC_INSERT:
161 		Cbuf_InsertText( text );
162 		break;
163 	case EXEC_APPEND:
164 		Cbuf_AddText( text );
165 		break;
166 	default:
167 		Com_Error( ERR_FATAL, "Cbuf_ExecuteText: bad exec_when" );
168 	}
169 }
170 
171 /*
172 ============
173 Cbuf_Execute
174 ============
175 */
Cbuf_Execute(void)176 void Cbuf_Execute( void ) {
177 	int i;
178 	char    *text;
179 	char line[MAX_CMD_LINE];
180 	int quotes;
181 
182 	// This will keep // style comments all on one line by not breaking on
183 	// a semicolon.  It will keep /* ... */ style comments all on one line by not
184 	// breaking it for semicolon or newline.
185 	qboolean in_star_comment = qfalse;
186 	qboolean in_slash_comment = qfalse;
187 	while ( cmd_text.cmdsize )
188 	{
189 		if ( cmd_wait > 0 ) {
190 			// skip out while text still remains in buffer, leaving it
191 			// for next frame
192 			cmd_wait--;
193 			break;
194 		}
195 
196 		// find a \n or ; line break or comment: // or /* */
197 		text = (char *)cmd_text.data;
198 
199 		quotes = 0;
200 		for ( i = 0 ; i < cmd_text.cmdsize ; i++ )
201 		{
202 			if ( text[i] == '"' ) {
203 				quotes++;
204 			}
205 			if ( !(quotes&1)) {
206 				if (i < cmd_text.cmdsize - 1) {
207 					if (! in_star_comment && text[i] == '/' && text[i+1] == '/')
208 						in_slash_comment = qtrue;
209 					else if (! in_slash_comment && text[i] == '/' && text[i+1] == '*')
210 						in_star_comment = qtrue;
211 					else if (in_star_comment && text[i] == '*' && text[i+1] == '/') {
212 						in_star_comment = qfalse;
213 						// If we are in a star comment, then the part after it is valid
214 						// Note: This will cause it to NUL out the terminating '/'
215 						// but ExecuteString doesn't require it anyway.
216 						i++;
217 						break;
218 					}
219 				}
220 				if (! in_slash_comment && ! in_star_comment && text[i] == ';')
221 					break;
222 			}
223 			if (! in_star_comment && (text[i] == '\n' || text[i] == '\r')) {
224 				in_slash_comment = qfalse;
225 				break;
226 			}
227 		}
228 
229 		if ( i >= ( MAX_CMD_LINE - 1 ) ) {
230 			i = MAX_CMD_LINE - 1;
231 		}
232 
233 		memcpy( line, text, i );
234 		line[i] = 0;
235 
236 // delete the text from the command buffer and move remaining commands down
237 // this is necessary because commands (exec) can insert data at the
238 // beginning of the text buffer
239 
240 		if ( i == cmd_text.cmdsize ) {
241 			cmd_text.cmdsize = 0;
242 		} else
243 		{
244 			i++;
245 			cmd_text.cmdsize -= i;
246 			memmove( text, text + i, cmd_text.cmdsize );
247 		}
248 
249 // execute the command line
250 
251 		Cmd_ExecuteString( line );
252 	}
253 }
254 
255 
256 /*
257 ==============================================================================
258 
259 						SCRIPT COMMANDS
260 
261 ==============================================================================
262 */
263 
264 
265 /*
266 ===============
267 Cmd_Exec_f
268 ===============
269 */
Cmd_Exec_f(void)270 void Cmd_Exec_f( void ) {
271 	qboolean quiet;
272 	union {
273 		char	*c;
274 		void	*v;
275 	} f;
276 	char filename[MAX_QPATH];
277 
278 	quiet = !Q_stricmp(Cmd_Argv(0), "execq");
279 
280 	if ( Cmd_Argc() != 2 ) {
281 		Com_Printf ("exec%s <filename> : execute a script file%s\n",
282 		            quiet ? "q" : "", quiet ? " without notification" : "");
283 		return;
284 	}
285 
286 	Q_strncpyz( filename, Cmd_Argv( 1 ), sizeof( filename ) );
287 	COM_DefaultExtension( filename, sizeof( filename ), ".cfg" );
288 	FS_ReadFile( filename, &f.v);
289 	if (!f.c) {
290 		Com_Printf ("couldn't exec %s\n", filename);
291 		return;
292 	}
293 
294 	if (!quiet)
295 		Com_Printf ("execing %s\n", filename);
296 
297 	Cbuf_InsertText (f.c);
298 
299 	FS_FreeFile (f.v);
300 }
301 
302 
303 /*
304 ===============
305 Cmd_Vstr_f
306 
307 Inserts the current value of a variable as command text
308 ===============
309 */
Cmd_Vstr_f(void)310 void Cmd_Vstr_f( void ) {
311 	char    *v;
312 
313 	if ( Cmd_Argc() != 2 ) {
314 		Com_Printf( "vstr <variablename> : execute a variable command\n" );
315 		return;
316 	}
317 
318 	v = Cvar_VariableString( Cmd_Argv( 1 ) );
319 	Cbuf_InsertText( va( "%s\n", v ) );
320 }
321 
322 
323 /*
324 ===============
325 Cmd_Echo_f
326 
327 Just prints the rest of the line to the console
328 ===============
329 */
Cmd_Echo_f(void)330 void Cmd_Echo_f( void ) {
331 	Com_Printf ("%s\n", Cmd_Args());
332 }
333 
334 
335 /*
336 =============================================================================
337 
338 					COMMAND EXECUTION
339 
340 =============================================================================
341 */
342 
343 typedef struct cmd_function_s
344 {
345 	struct cmd_function_s   *next;
346 	char                    *name;
347 	xcommand_t function;
348 	completionFunc_t	complete;
349 } cmd_function_t;
350 
351 
352 static int cmd_argc;
353 static char *cmd_argv[MAX_STRING_TOKENS];				// points into cmd_tokenized
354 static char cmd_tokenized[BIG_INFO_STRING + MAX_STRING_TOKENS];		// will have 0 bytes inserted
355 static char cmd_cmd[BIG_INFO_STRING];					// the original command we received (no token processing)
356 
357 static cmd_function_t  *cmd_functions;					// possible commands to execute
358 
359 /*
360 ============
361 Cmd_Argc
362 ============
363 */
Cmd_Argc(void)364 int     Cmd_Argc( void ) {
365 	return cmd_argc;
366 }
367 
368 /*
369 ============
370 Cmd_Argv
371 ============
372 */
Cmd_Argv(int arg)373 char    *Cmd_Argv( int arg ) {
374 	if ( (unsigned)arg >= cmd_argc ) {
375 		return "";
376 	}
377 	return cmd_argv[arg];
378 }
379 
380 /*
381 ============
382 Cmd_ArgvBuffer
383 
384 The interpreted versions use this because
385 they can't have pointers returned to them
386 ============
387 */
Cmd_ArgvBuffer(int arg,char * buffer,int bufferLength)388 void    Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) {
389 	Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength );
390 }
391 
392 
393 /*
394 ============
395 Cmd_Args
396 
397 Returns a single string containing argv(1) to argv(argc()-1)
398 ============
399 */
Cmd_Args(void)400 char    *Cmd_Args( void ) {
401 	static char cmd_args[MAX_STRING_CHARS];
402 	int i;
403 
404 	cmd_args[0] = 0;
405 	for ( i = 1 ; i < cmd_argc ; i++ ) {
406 		strcat( cmd_args, cmd_argv[i] );
407 		if ( i != cmd_argc - 1 ) {
408 			strcat( cmd_args, " " );
409 		}
410 	}
411 
412 	return cmd_args;
413 }
414 
415 /*
416 ============
417 Cmd_Args
418 
419 Returns a single string containing argv(arg) to argv(argc()-1)
420 ============
421 */
Cmd_ArgsFrom(int arg)422 char *Cmd_ArgsFrom( int arg ) {
423 	static char cmd_args[BIG_INFO_STRING];
424 	int i;
425 
426 	cmd_args[0] = 0;
427 	if ( arg < 0 ) {
428 		arg = 0;
429 	}
430 	for ( i = arg ; i < cmd_argc ; i++ ) {
431 		strcat( cmd_args, cmd_argv[i] );
432 		if ( i != cmd_argc - 1 ) {
433 			strcat( cmd_args, " " );
434 		}
435 	}
436 
437 	return cmd_args;
438 }
439 
440 /*
441 ============
442 Cmd_ArgsBuffer
443 
444 The interpreted versions use this because
445 they can't have pointers returned to them
446 ============
447 */
Cmd_ArgsBuffer(char * buffer,int bufferLength)448 void    Cmd_ArgsBuffer( char *buffer, int bufferLength ) {
449 	Q_strncpyz( buffer, Cmd_Args(), bufferLength );
450 }
451 
452 /*
453 ============
454 Cmd_Cmd
455 
456 Retrieve the unmodified command string
457 For rcon use when you want to transmit without altering quoting
458 https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
459 ============
460 */
Cmd_Cmd(void)461 char *Cmd_Cmd(void)
462 {
463 	return cmd_cmd;
464 }
465 
466 /*
467    Replace command separators with space to prevent interpretation
468    This is a hack to protect buggy qvms
469    https://bugzilla.icculus.org/show_bug.cgi?id=3593
470    https://bugzilla.icculus.org/show_bug.cgi?id=4769
471 */
472 
Cmd_Args_Sanitize(void)473 void Cmd_Args_Sanitize(void)
474 {
475 	int i;
476 
477 	for(i = 1; i < cmd_argc; i++)
478 	{
479 		char *c = cmd_argv[i];
480 
481 		if(strlen(c) > MAX_CVAR_VALUE_STRING - 1)
482 			c[MAX_CVAR_VALUE_STRING - 1] = '\0';
483 
484 		while ((c = strpbrk(c, "\n\r;"))) {
485 			*c = ' ';
486 			++c;
487 		}
488 	}
489 }
490 
491 /*
492 ============
493 Cmd_TokenizeString
494 
495 Parses the given string into command line tokens.
496 The text is copied to a seperate buffer and 0 characters
497 are inserted in the apropriate place, The argv array
498 will point into this temporary buffer.
499 ============
500 */
Cmd_TokenizeString2(const char * text_in,qboolean ignoreQuotes)501 static void Cmd_TokenizeString2( const char *text_in, qboolean ignoreQuotes ) {
502 	const char  *text;
503 	char    *textOut;
504 
505 	// clear previous args
506 	cmd_argc = 0;
507 
508 	if ( !text_in ) {
509 		return;
510 	}
511 
512 	Q_strncpyz( cmd_cmd, text_in, sizeof( cmd_cmd ) );
513 
514 	text = text_in;
515 	textOut = cmd_tokenized;
516 
517 	while ( 1 ) {
518 		if ( cmd_argc == MAX_STRING_TOKENS ) {
519 			return;         // this is usually something malicious
520 		}
521 
522 		while ( 1 ) {
523 			// skip whitespace
524 			while ( *text && *text <= ' ' ) {
525 				text++;
526 			}
527 			if ( !*text ) {
528 				return;         // all tokens parsed
529 			}
530 
531 			// skip // comments
532 			if ( text[0] == '/' && text[1] == '/' ) {
533 				return;         // all tokens parsed
534 			}
535 
536 			// skip /* */ comments
537 			if ( text[0] == '/' && text[1] == '*' ) {
538 				while ( *text && ( text[0] != '*' || text[1] != '/' ) ) {
539 					text++;
540 				}
541 				if ( !*text ) {
542 					return;     // all tokens parsed
543 				}
544 				text += 2;
545 			} else {
546 				break;          // we are ready to parse a token
547 			}
548 		}
549 
550 		// handle quoted strings
551 		if ( !ignoreQuotes && *text == '"' ) {
552 			cmd_argv[cmd_argc] = textOut;
553 			cmd_argc++;
554 			text++;
555 			while ( *text && *text != '"' ) {
556 				*textOut++ = *text++;
557 			}
558 			*textOut++ = 0;
559 			if ( !*text ) {
560 				return;     // all tokens parsed
561 			}
562 			text++;
563 			continue;
564 		}
565 
566 		// regular token
567 		cmd_argv[cmd_argc] = textOut;
568 		cmd_argc++;
569 
570 		// skip until whitespace, quote, or command
571 		while ( *text > ' ' ) {
572 			if ( !ignoreQuotes && text[0] == '"' ) {
573 				break;
574 			}
575 
576 			if ( text[0] == '/' && text[1] == '/' ) {
577 				break;
578 			}
579 
580 			// skip /* */ comments
581 			if ( text[0] == '/' && text[1] == '*' ) {
582 				break;
583 			}
584 
585 			*textOut++ = *text++;
586 		}
587 
588 		*textOut++ = 0;
589 
590 		if ( !*text ) {
591 			return;     // all tokens parsed
592 		}
593 	}
594 
595 }
596 
597 /*
598 ============
599 Cmd_TokenizeString
600 ============
601 */
Cmd_TokenizeString(const char * text_in)602 void Cmd_TokenizeString( const char *text_in ) {
603 	Cmd_TokenizeString2( text_in, qfalse );
604 }
605 
606 /*
607 ============
608 Cmd_TokenizeStringIgnoreQuotes
609 ============
610 */
Cmd_TokenizeStringIgnoreQuotes(const char * text_in)611 void Cmd_TokenizeStringIgnoreQuotes( const char *text_in ) {
612 	Cmd_TokenizeString2( text_in, qtrue );
613 }
614 
615 /*
616 ============
617 Cmd_FindCommand
618 ============
619 */
Cmd_FindCommand(const char * cmd_name)620 cmd_function_t *Cmd_FindCommand( const char *cmd_name )
621 {
622 	cmd_function_t *cmd;
623 	for( cmd = cmd_functions; cmd; cmd = cmd->next )
624 		if( !Q_stricmp( cmd_name, cmd->name ) )
625 			return cmd;
626 	return NULL;
627 }
628 
629 /*
630 ============
631 Cmd_AddCommand
632 ============
633 */
Cmd_AddCommand(const char * cmd_name,xcommand_t function)634 void    Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
635 	cmd_function_t  *cmd;
636 
637 	// fail if the command already exists
638 	if( Cmd_FindCommand( cmd_name ) )
639 	{
640 		// allow completion-only commands to be silently doubled
641 		if( function != NULL )
642 			Com_Printf( "Cmd_AddCommand: %s already defined\n", cmd_name );
643 		return;
644 	}
645 
646 	// use a small malloc to avoid zone fragmentation
647 	cmd = Z_Malloc( sizeof( cmd_function_t ) );
648 	cmd->name = CopyString( cmd_name );
649 	cmd->function = function;
650 	cmd->complete = NULL;
651 	cmd->next = cmd_functions;
652 	cmd_functions = cmd;
653 }
654 
655 /*
656 ============
657 Cmd_SetCommandCompletionFunc
658 ============
659 */
Cmd_SetCommandCompletionFunc(const char * command,completionFunc_t complete)660 void Cmd_SetCommandCompletionFunc( const char *command, completionFunc_t complete ) {
661 	cmd_function_t	*cmd;
662 
663 	for( cmd = cmd_functions; cmd; cmd = cmd->next ) {
664 		if( !Q_stricmp( command, cmd->name ) ) {
665 			cmd->complete = complete;
666 			return;
667 		}
668 	}
669 }
670 
671 /*
672 ============
673 Cmd_RemoveCommand
674 ============
675 */
Cmd_RemoveCommand(const char * cmd_name)676 void    Cmd_RemoveCommand( const char *cmd_name ) {
677 	cmd_function_t  *cmd, **back;
678 
679 	back = &cmd_functions;
680 	while ( 1 ) {
681 		cmd = *back;
682 		if ( !cmd ) {
683 			// command wasn't active
684 			return;
685 		}
686 		if ( !strcmp( cmd_name, cmd->name ) ) {
687 			*back = cmd->next;
688 			Z_Free( cmd->name );
689 			Z_Free( cmd );
690 			return;
691 		}
692 		back = &cmd->next;
693 	}
694 }
695 
696 /*
697 ============
698 Cmd_RemoveCommandSafe
699 
700 Only remove commands with no associated function
701 ============
702 */
Cmd_RemoveCommandSafe(const char * cmd_name)703 void Cmd_RemoveCommandSafe( const char *cmd_name )
704 {
705 	cmd_function_t *cmd = Cmd_FindCommand( cmd_name );
706 
707 	if( !cmd )
708 		return;
709 	if( cmd->function )
710 	{
711 		Com_Error( ERR_DROP, "Restricted source tried to remove "
712 			"system command \"%s\"", cmd_name );
713 		return;
714 	}
715 
716 	Cmd_RemoveCommand( cmd_name );
717 }
718 
719 /*
720 ============
721 Cmd_CommandCompletion
722 ============
723 */
Cmd_CommandCompletion(void (* callback)(const char * s))724 void    Cmd_CommandCompletion( void ( *callback )(const char *s) ) {
725 	cmd_function_t  *cmd;
726 
727 	for ( cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
728 		callback( cmd->name );
729 	}
730 }
731 
732 /*
733 ============
734 Cmd_CompleteArgument
735 ============
736 */
Cmd_CompleteArgument(const char * command,char * args,int argNum)737 void Cmd_CompleteArgument( const char *command, char *args, int argNum ) {
738 	cmd_function_t	*cmd;
739 
740 	for( cmd = cmd_functions; cmd; cmd = cmd->next ) {
741 		if( !Q_stricmp( command, cmd->name ) ) {
742 			if ( cmd->complete ) {
743 				cmd->complete( args, argNum );
744 			}
745 			return;
746 		}
747 	}
748 }
749 
750 /*
751 ============
752 Cmd_ExecuteString
753 
754 A complete command line has been parsed, so try to execute it
755 ============
756 */
Cmd_ExecuteString(const char * text)757 void    Cmd_ExecuteString( const char *text ) {
758 	cmd_function_t  *cmd, **prev;
759 
760 	// execute the command line
761 	Cmd_TokenizeString( text );
762 	if ( !Cmd_Argc() ) {
763 		return;     // no tokens
764 	}
765 
766 	// check registered command functions
767 	for ( prev = &cmd_functions ; *prev ; prev = &cmd->next ) {
768 		cmd = *prev;
769 		if ( !Q_stricmp( cmd_argv[0],cmd->name ) ) {
770 			// rearrange the links so that the command will be
771 			// near the head of the list next time it is used
772 			*prev = cmd->next;
773 			cmd->next = cmd_functions;
774 			cmd_functions = cmd;
775 
776 			// perform the action
777 			if ( !cmd->function ) {
778 				// let the cgame or game handle it
779 				break;
780 			} else {
781 				cmd->function();
782 			}
783 			return;
784 		}
785 	}
786 
787 	// check cvars
788 	if ( Cvar_Command() ) {
789 		return;
790 	}
791 
792 	// check client game commands
793 	if ( com_cl_running && com_cl_running->integer && CL_GameCommand() ) {
794 		return;
795 	}
796 
797 	// check server game commands
798 	if ( com_sv_running && com_sv_running->integer && SV_GameCommand() ) {
799 		return;
800 	}
801 
802 	// check ui commands
803 	if ( com_cl_running && com_cl_running->integer && UI_GameCommand() ) {
804 		return;
805 	}
806 
807 	// send it as a server command if we are connected
808 	// this will usually result in a chat message
809 	CL_ForwardCommandToServer( text );
810 }
811 
812 /*
813 ============
814 Cmd_List_f
815 ============
816 */
Cmd_List_f(void)817 void Cmd_List_f( void ) {
818 	cmd_function_t  *cmd;
819 	int i;
820 	char            *match;
821 
822 	if ( Cmd_Argc() > 1 ) {
823 		match = Cmd_Argv( 1 );
824 	} else {
825 		match = NULL;
826 	}
827 
828 	i = 0;
829 	for ( cmd = cmd_functions ; cmd ; cmd = cmd->next ) {
830 		if ( match && !Com_Filter( match, cmd->name, qfalse ) ) {
831 			continue;
832 		}
833 
834 		Com_Printf( "%s\n", cmd->name );
835 		i++;
836 	}
837 	Com_Printf( "%i commands\n", i );
838 }
839 
840 /*
841 ==================
842 Cmd_CompleteCfgName
843 ==================
844 */
Cmd_CompleteCfgName(char * args,int argNum)845 void Cmd_CompleteCfgName( char *args, int argNum ) {
846 	if( argNum == 2 ) {
847 		Field_CompleteFilename( "", "cfg", qfalse, qtrue );
848 	}
849 }
850 
851 /*
852 ============
853 Cmd_Init
854 ============
855 */
Cmd_Init(void)856 void Cmd_Init( void ) {
857 	Cmd_AddCommand( "cmdlist",Cmd_List_f );
858 	Cmd_AddCommand( "exec",Cmd_Exec_f );
859 	Cmd_AddCommand ("execq",Cmd_Exec_f);
860 	Cmd_SetCommandCompletionFunc( "exec", Cmd_CompleteCfgName );
861 	Cmd_SetCommandCompletionFunc( "execq", Cmd_CompleteCfgName );
862 	Cmd_AddCommand( "vstr",Cmd_Vstr_f );
863 	Cmd_SetCommandCompletionFunc( "vstr", Cvar_CompleteCvarName );
864 	Cmd_AddCommand( "echo",Cmd_Echo_f );
865 	Cmd_AddCommand( "wait", Cmd_Wait_f );
866 }
867 
868