Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

cmd.c (13732B)


      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 Foobar; 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 "../game/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 */
     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 */
     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 */
     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 */
    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 */
    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 			Cmd_ExecuteString (text);
    148 		} else {
    149 			Cbuf_Execute();
    150 		}
    151 		break;
    152 	case EXEC_INSERT:
    153 		Cbuf_InsertText (text);
    154 		break;
    155 	case EXEC_APPEND:
    156 		Cbuf_AddText (text);
    157 		break;
    158 	default:
    159 		Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
    160 	}
    161 }
    162 
    163 /*
    164 ============
    165 Cbuf_Execute
    166 ============
    167 */
    168 void Cbuf_Execute (void)
    169 {
    170 	int		i;
    171 	char	*text;
    172 	char	line[MAX_CMD_LINE];
    173 	int		quotes;
    174 
    175 	while (cmd_text.cursize)
    176 	{
    177 		if ( cmd_wait )	{
    178 			// skip out while text still remains in buffer, leaving it
    179 			// for next frame
    180 			cmd_wait--;
    181 			break;
    182 		}
    183 
    184 		// find a \n or ; line break
    185 		text = (char *)cmd_text.data;
    186 
    187 		quotes = 0;
    188 		for (i=0 ; i< cmd_text.cursize ; i++)
    189 		{
    190 			if (text[i] == '"')
    191 				quotes++;
    192 			if ( !(quotes&1) &&  text[i] == ';')
    193 				break;	// don't break if inside a quoted string
    194 			if (text[i] == '\n' || text[i] == '\r' )
    195 				break;
    196 		}
    197 
    198 		if( i >= (MAX_CMD_LINE - 1)) {
    199 			i = MAX_CMD_LINE - 1;
    200 		}
    201 				
    202 		Com_Memcpy (line, text, i);
    203 		line[i] = 0;
    204 		
    205 // delete the text from the command buffer and move remaining commands down
    206 // this is necessary because commands (exec) can insert data at the
    207 // beginning of the text buffer
    208 
    209 		if (i == cmd_text.cursize)
    210 			cmd_text.cursize = 0;
    211 		else
    212 		{
    213 			i++;
    214 			cmd_text.cursize -= i;
    215 			memmove (text, text+i, cmd_text.cursize);
    216 		}
    217 
    218 // execute the command line
    219 
    220 		Cmd_ExecuteString (line);		
    221 	}
    222 }
    223 
    224 
    225 /*
    226 ==============================================================================
    227 
    228 						SCRIPT COMMANDS
    229 
    230 ==============================================================================
    231 */
    232 
    233 
    234 /*
    235 ===============
    236 Cmd_Exec_f
    237 ===============
    238 */
    239 void Cmd_Exec_f( void ) {
    240 	char	*f;
    241 	int		len;
    242 	char	filename[MAX_QPATH];
    243 
    244 	if (Cmd_Argc () != 2) {
    245 		Com_Printf ("exec <filename> : execute a script file\n");
    246 		return;
    247 	}
    248 
    249 	Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
    250 	COM_DefaultExtension( filename, sizeof( filename ), ".cfg" ); 
    251 	len = FS_ReadFile( filename, (void **)&f);
    252 	if (!f) {
    253 		Com_Printf ("couldn't exec %s\n",Cmd_Argv(1));
    254 		return;
    255 	}
    256 	Com_Printf ("execing %s\n",Cmd_Argv(1));
    257 	
    258 	Cbuf_InsertText (f);
    259 
    260 	FS_FreeFile (f);
    261 }
    262 
    263 
    264 /*
    265 ===============
    266 Cmd_Vstr_f
    267 
    268 Inserts the current value of a variable as command text
    269 ===============
    270 */
    271 void Cmd_Vstr_f( void ) {
    272 	char	*v;
    273 
    274 	if (Cmd_Argc () != 2) {
    275 		Com_Printf ("vstr <variablename> : execute a variable command\n");
    276 		return;
    277 	}
    278 
    279 	v = Cvar_VariableString( Cmd_Argv( 1 ) );
    280 	Cbuf_InsertText( va("%s\n", v ) );
    281 }
    282 
    283 
    284 /*
    285 ===============
    286 Cmd_Echo_f
    287 
    288 Just prints the rest of the line to the console
    289 ===============
    290 */
    291 void Cmd_Echo_f (void)
    292 {
    293 	int		i;
    294 	
    295 	for (i=1 ; i<Cmd_Argc() ; i++)
    296 		Com_Printf ("%s ",Cmd_Argv(i));
    297 	Com_Printf ("\n");
    298 }
    299 
    300 
    301 /*
    302 =============================================================================
    303 
    304 					COMMAND EXECUTION
    305 
    306 =============================================================================
    307 */
    308 
    309 typedef struct cmd_function_s
    310 {
    311 	struct cmd_function_s	*next;
    312 	char					*name;
    313 	xcommand_t				function;
    314 } cmd_function_t;
    315 
    316 
    317 static	int			cmd_argc;
    318 static	char		*cmd_argv[MAX_STRING_TOKENS];		// points into cmd_tokenized
    319 static	char		cmd_tokenized[BIG_INFO_STRING+MAX_STRING_TOKENS];	// will have 0 bytes inserted
    320 static	char		cmd_cmd[BIG_INFO_STRING]; // the original command we received (no token processing)
    321 
    322 static	cmd_function_t	*cmd_functions;		// possible commands to execute
    323 
    324 /*
    325 ============
    326 Cmd_Argc
    327 ============
    328 */
    329 int		Cmd_Argc( void ) {
    330 	return cmd_argc;
    331 }
    332 
    333 /*
    334 ============
    335 Cmd_Argv
    336 ============
    337 */
    338 char	*Cmd_Argv( int arg ) {
    339 	if ( (unsigned)arg >= cmd_argc ) {
    340 		return "";
    341 	}
    342 	return cmd_argv[arg];	
    343 }
    344 
    345 /*
    346 ============
    347 Cmd_ArgvBuffer
    348 
    349 The interpreted versions use this because
    350 they can't have pointers returned to them
    351 ============
    352 */
    353 void	Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) {
    354 	Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength );
    355 }
    356 
    357 
    358 /*
    359 ============
    360 Cmd_Args
    361 
    362 Returns a single string containing argv(1) to argv(argc()-1)
    363 ============
    364 */
    365 char	*Cmd_Args( void ) {
    366 	static	char		cmd_args[MAX_STRING_CHARS];
    367 	int		i;
    368 
    369 	cmd_args[0] = 0;
    370 	for ( i = 1 ; i < cmd_argc ; i++ ) {
    371 		strcat( cmd_args, cmd_argv[i] );
    372 		if ( i != cmd_argc-1 ) {
    373 			strcat( cmd_args, " " );
    374 		}
    375 	}
    376 
    377 	return cmd_args;
    378 }
    379 
    380 /*
    381 ============
    382 Cmd_Args
    383 
    384 Returns a single string containing argv(arg) to argv(argc()-1)
    385 ============
    386 */
    387 char *Cmd_ArgsFrom( int arg ) {
    388 	static	char		cmd_args[BIG_INFO_STRING];
    389 	int		i;
    390 
    391 	cmd_args[0] = 0;
    392 	if (arg < 0)
    393 		arg = 0;
    394 	for ( i = arg ; i < cmd_argc ; i++ ) {
    395 		strcat( cmd_args, cmd_argv[i] );
    396 		if ( i != cmd_argc-1 ) {
    397 			strcat( cmd_args, " " );
    398 		}
    399 	}
    400 
    401 	return cmd_args;
    402 }
    403 
    404 /*
    405 ============
    406 Cmd_ArgsBuffer
    407 
    408 The interpreted versions use this because
    409 they can't have pointers returned to them
    410 ============
    411 */
    412 void	Cmd_ArgsBuffer( char *buffer, int bufferLength ) {
    413 	Q_strncpyz( buffer, Cmd_Args(), bufferLength );
    414 }
    415 
    416 /*
    417 ============
    418 Cmd_Cmd
    419 
    420 Retrieve the unmodified command string
    421 For rcon use when you want to transmit without altering quoting
    422 https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
    423 ============
    424 */
    425 char *Cmd_Cmd()
    426 {
    427 	return cmd_cmd;
    428 }
    429 
    430 /*
    431 ============
    432 Cmd_TokenizeString
    433 
    434 Parses the given string into command line tokens.
    435 The text is copied to a seperate buffer and 0 characters
    436 are inserted in the apropriate place, The argv array
    437 will point into this temporary buffer.
    438 ============
    439 */
    440 // NOTE TTimo define that to track tokenization issues
    441 //#define TKN_DBG
    442 void Cmd_TokenizeString( const char *text_in ) {
    443 	const char	*text;
    444 	char	*textOut;
    445 
    446 #ifdef TKN_DBG
    447   // FIXME TTimo blunt hook to try to find the tokenization of userinfo
    448   Com_DPrintf("Cmd_TokenizeString: %s\n", text_in);
    449 #endif
    450 
    451 	// clear previous args
    452 	cmd_argc = 0;
    453 
    454 	if ( !text_in ) {
    455 		return;
    456 	}
    457 	
    458 	Q_strncpyz( cmd_cmd, text_in, sizeof(cmd_cmd) );
    459 
    460 	text = text_in;
    461 	textOut = cmd_tokenized;
    462 
    463 	while ( 1 ) {
    464 		if ( cmd_argc == MAX_STRING_TOKENS ) {
    465 			return;			// this is usually something malicious
    466 		}
    467 
    468 		while ( 1 ) {
    469 			// skip whitespace
    470 			while ( *text && *text <= ' ' ) {
    471 				text++;
    472 			}
    473 			if ( !*text ) {
    474 				return;			// all tokens parsed
    475 			}
    476 
    477 			// skip // comments
    478 			if ( text[0] == '/' && text[1] == '/' ) {
    479 				return;			// all tokens parsed
    480 			}
    481 
    482 			// skip /* */ comments
    483 			if ( text[0] == '/' && text[1] =='*' ) {
    484 				while ( *text && ( text[0] != '*' || text[1] != '/' ) ) {
    485 					text++;
    486 				}
    487 				if ( !*text ) {
    488 					return;		// all tokens parsed
    489 				}
    490 				text += 2;
    491 			} else {
    492 				break;			// we are ready to parse a token
    493 			}
    494 		}
    495 
    496 		// handle quoted strings
    497     // NOTE TTimo this doesn't handle \" escaping
    498 		if ( *text == '"' ) {
    499 			cmd_argv[cmd_argc] = textOut;
    500 			cmd_argc++;
    501 			text++;
    502 			while ( *text && *text != '"' ) {
    503 				*textOut++ = *text++;
    504 			}
    505 			*textOut++ = 0;
    506 			if ( !*text ) {
    507 				return;		// all tokens parsed
    508 			}
    509 			text++;
    510 			continue;
    511 		}
    512 
    513 		// regular token
    514 		cmd_argv[cmd_argc] = textOut;
    515 		cmd_argc++;
    516 
    517 		// skip until whitespace, quote, or command
    518 		while ( *text > ' ' ) {
    519 			if ( text[0] == '"' ) {
    520 				break;
    521 			}
    522 
    523 			if ( text[0] == '/' && text[1] == '/' ) {
    524 				break;
    525 			}
    526 
    527 			// skip /* */ comments
    528 			if ( text[0] == '/' && text[1] =='*' ) {
    529 				break;
    530 			}
    531 
    532 			*textOut++ = *text++;
    533 		}
    534 
    535 		*textOut++ = 0;
    536 
    537 		if ( !*text ) {
    538 			return;		// all tokens parsed
    539 		}
    540 	}
    541 	
    542 }
    543 
    544 
    545 /*
    546 ============
    547 Cmd_AddCommand
    548 ============
    549 */
    550 void	Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
    551 	cmd_function_t	*cmd;
    552 	
    553 	// fail if the command already exists
    554 	for ( cmd = cmd_functions ; cmd ; cmd=cmd->next ) {
    555 		if ( !strcmp( cmd_name, cmd->name ) ) {
    556 			// allow completion-only commands to be silently doubled
    557 			if ( function != NULL ) {
    558 				Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
    559 			}
    560 			return;
    561 		}
    562 	}
    563 
    564 	// use a small malloc to avoid zone fragmentation
    565 	cmd = S_Malloc (sizeof(cmd_function_t));
    566 	cmd->name = CopyString( cmd_name );
    567 	cmd->function = function;
    568 	cmd->next = cmd_functions;
    569 	cmd_functions = cmd;
    570 }
    571 
    572 /*
    573 ============
    574 Cmd_RemoveCommand
    575 ============
    576 */
    577 void	Cmd_RemoveCommand( const char *cmd_name ) {
    578 	cmd_function_t	*cmd, **back;
    579 
    580 	back = &cmd_functions;
    581 	while( 1 ) {
    582 		cmd = *back;
    583 		if ( !cmd ) {
    584 			// command wasn't active
    585 			return;
    586 		}
    587 		if ( !strcmp( cmd_name, cmd->name ) ) {
    588 			*back = cmd->next;
    589 			if (cmd->name) {
    590 				Z_Free(cmd->name);
    591 			}
    592 			Z_Free (cmd);
    593 			return;
    594 		}
    595 		back = &cmd->next;
    596 	}
    597 }
    598 
    599 
    600 /*
    601 ============
    602 Cmd_CommandCompletion
    603 ============
    604 */
    605 void	Cmd_CommandCompletion( void(*callback)(const char *s) ) {
    606 	cmd_function_t	*cmd;
    607 	
    608 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next) {
    609 		callback( cmd->name );
    610 	}
    611 }
    612 
    613 
    614 /*
    615 ============
    616 Cmd_ExecuteString
    617 
    618 A complete command line has been parsed, so try to execute it
    619 ============
    620 */
    621 void	Cmd_ExecuteString( const char *text ) {	
    622 	cmd_function_t	*cmd, **prev;
    623 
    624 	// execute the command line
    625 	Cmd_TokenizeString( text );		
    626 	if ( !Cmd_Argc() ) {
    627 		return;		// no tokens
    628 	}
    629 
    630 	// check registered command functions	
    631 	for ( prev = &cmd_functions ; *prev ; prev = &cmd->next ) {
    632 		cmd = *prev;
    633 		if ( !Q_stricmp( cmd_argv[0],cmd->name ) ) {
    634 			// rearrange the links so that the command will be
    635 			// near the head of the list next time it is used
    636 			*prev = cmd->next;
    637 			cmd->next = cmd_functions;
    638 			cmd_functions = cmd;
    639 
    640 			// perform the action
    641 			if ( !cmd->function ) {
    642 				// let the cgame or game handle it
    643 				break;
    644 			} else {
    645 				cmd->function ();
    646 			}
    647 			return;
    648 		}
    649 	}
    650 	
    651 	// check cvars
    652 	if ( Cvar_Command() ) {
    653 		return;
    654 	}
    655 
    656 	// check client game commands
    657 	if ( com_cl_running && com_cl_running->integer && CL_GameCommand() ) {
    658 		return;
    659 	}
    660 
    661 	// check server game commands
    662 	if ( com_sv_running && com_sv_running->integer && SV_GameCommand() ) {
    663 		return;
    664 	}
    665 
    666 	// check ui commands
    667 	if ( com_cl_running && com_cl_running->integer && UI_GameCommand() ) {
    668 		return;
    669 	}
    670 
    671 	// send it as a server command if we are connected
    672 	// this will usually result in a chat message
    673 	CL_ForwardCommandToServer ( text );
    674 }
    675 
    676 /*
    677 ============
    678 Cmd_List_f
    679 ============
    680 */
    681 void Cmd_List_f (void)
    682 {
    683 	cmd_function_t	*cmd;
    684 	int				i;
    685 	char			*match;
    686 
    687 	if ( Cmd_Argc() > 1 ) {
    688 		match = Cmd_Argv( 1 );
    689 	} else {
    690 		match = NULL;
    691 	}
    692 
    693 	i = 0;
    694 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next) {
    695 		if (match && !Com_Filter(match, cmd->name, qfalse)) continue;
    696 
    697 		Com_Printf ("%s\n", cmd->name);
    698 		i++;
    699 	}
    700 	Com_Printf ("%i commands\n", i);
    701 }
    702 
    703 /*
    704 ============
    705 Cmd_Init
    706 ============
    707 */
    708 void Cmd_Init (void) {
    709 	Cmd_AddCommand ("cmdlist",Cmd_List_f);
    710 	Cmd_AddCommand ("exec",Cmd_Exec_f);
    711 	Cmd_AddCommand ("vstr",Cmd_Vstr_f);
    712 	Cmd_AddCommand ("echo",Cmd_Echo_f);
    713 	Cmd_AddCommand ("wait", Cmd_Wait_f);
    714 }
    715