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