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