Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

cmd.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // cmd.c -- Quake script command processing module
00023 
00024 #include "../game/q_shared.h"
00025 #include "qcommon.h"
00026 
00027 #define MAX_CMD_BUFFER  16384
00028 #define MAX_CMD_LINE    1024
00029 
00030 typedef struct {
00031     byte    *data;
00032     int     maxsize;
00033     int     cursize;
00034 } cmd_t;
00035 
00036 int         cmd_wait;
00037 cmd_t       cmd_text;
00038 byte        cmd_text_buf[MAX_CMD_BUFFER];
00039 
00040 
00041 //=============================================================================
00042 
00043 /*
00044 ============
00045 Cmd_Wait_f
00046 
00047 Causes execution of the remainder of the command buffer to be delayed until
00048 next frame.  This allows commands like:
00049 bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster"
00050 ============
00051 */
00052 void Cmd_Wait_f( void ) {
00053     if ( Cmd_Argc() == 2 ) {
00054         cmd_wait = atoi( Cmd_Argv( 1 ) );
00055     } else {
00056         cmd_wait = 1;
00057     }
00058 }
00059 
00060 
00061 /*
00062 =============================================================================
00063 
00064                         COMMAND BUFFER
00065 
00066 =============================================================================
00067 */
00068 
00069 /*
00070 ============
00071 Cbuf_Init
00072 ============
00073 */
00074 void Cbuf_Init (void)
00075 {
00076     cmd_text.data = cmd_text_buf;
00077     cmd_text.maxsize = MAX_CMD_BUFFER;
00078     cmd_text.cursize = 0;
00079 }
00080 
00081 /*
00082 ============
00083 Cbuf_AddText
00084 
00085 Adds command text at the end of the buffer, does NOT add a final \n
00086 ============
00087 */
00088 void Cbuf_AddText( const char *text ) {
00089     int     l;
00090     
00091     l = strlen (text);
00092 
00093     if (cmd_text.cursize + l >= cmd_text.maxsize)
00094     {
00095         Com_Printf ("Cbuf_AddText: overflow\n");
00096         return;
00097     }
00098     Com_Memcpy(&cmd_text.data[cmd_text.cursize], text, l);
00099     cmd_text.cursize += l;
00100 }
00101 
00102 
00103 /*
00104 ============
00105 Cbuf_InsertText
00106 
00107 Adds command text immediately after the current command
00108 Adds a \n to the text
00109 ============
00110 */
00111 void Cbuf_InsertText( const char *text ) {
00112     int     len;
00113     int     i;
00114 
00115     len = strlen( text ) + 1;
00116     if ( len + cmd_text.cursize > cmd_text.maxsize ) {
00117         Com_Printf( "Cbuf_InsertText overflowed\n" );
00118         return;
00119     }
00120 
00121     // move the existing command text
00122     for ( i = cmd_text.cursize - 1 ; i >= 0 ; i-- ) {
00123         cmd_text.data[ i + len ] = cmd_text.data[ i ];
00124     }
00125 
00126     // copy the new text in
00127     Com_Memcpy( cmd_text.data, text, len - 1 );
00128 
00129     // add a \n
00130     cmd_text.data[ len - 1 ] = '\n';
00131 
00132     cmd_text.cursize += len;
00133 }
00134 
00135 
00136 /*
00137 ============
00138 Cbuf_ExecuteText
00139 ============
00140 */
00141 void Cbuf_ExecuteText (int exec_when, const char *text)
00142 {
00143     switch (exec_when)
00144     {
00145     case EXEC_NOW:
00146         if (text && strlen(text) > 0) {
00147             Cmd_ExecuteString (text);
00148         } else {
00149             Cbuf_Execute();
00150         }
00151         break;
00152     case EXEC_INSERT:
00153         Cbuf_InsertText (text);
00154         break;
00155     case EXEC_APPEND:
00156         Cbuf_AddText (text);
00157         break;
00158     default:
00159         Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
00160     }
00161 }
00162 
00163 /*
00164 ============
00165 Cbuf_Execute
00166 ============
00167 */
00168 void Cbuf_Execute (void)
00169 {
00170     int     i;
00171     char    *text;
00172     char    line[MAX_CMD_LINE];
00173     int     quotes;
00174 
00175     while (cmd_text.cursize)
00176     {
00177         if ( cmd_wait ) {
00178             // skip out while text still remains in buffer, leaving it
00179             // for next frame
00180             cmd_wait--;
00181             break;
00182         }
00183 
00184         // find a \n or ; line break
00185         text = (char *)cmd_text.data;
00186 
00187         quotes = 0;
00188         for (i=0 ; i< cmd_text.cursize ; i++)
00189         {
00190             if (text[i] == '"')
00191                 quotes++;
00192             if ( !(quotes&1) &&  text[i] == ';')
00193                 break;  // don't break if inside a quoted string
00194             if (text[i] == '\n' || text[i] == '\r' )
00195                 break;
00196         }
00197 
00198         if( i >= (MAX_CMD_LINE - 1)) {
00199             i = MAX_CMD_LINE - 1;
00200         }
00201                 
00202         Com_Memcpy (line, text, i);
00203         line[i] = 0;
00204         
00205 // delete the text from the command buffer and move remaining commands down
00206 // this is necessary because commands (exec) can insert data at the
00207 // beginning of the text buffer
00208 
00209         if (i == cmd_text.cursize)
00210             cmd_text.cursize = 0;
00211         else
00212         {
00213             i++;
00214             cmd_text.cursize -= i;
00215             memmove (text, text+i, cmd_text.cursize);
00216         }
00217 
00218 // execute the command line
00219 
00220         Cmd_ExecuteString (line);       
00221     }
00222 }
00223 
00224 
00225 /*
00226 ==============================================================================
00227 
00228                         SCRIPT COMMANDS
00229 
00230 ==============================================================================
00231 */
00232 
00233 
00234 /*
00235 ===============
00236 Cmd_Exec_f
00237 ===============
00238 */
00239 void Cmd_Exec_f( void ) {
00240     char    *f;
00241     int     len;
00242     char    filename[MAX_QPATH];
00243 
00244     if (Cmd_Argc () != 2) {
00245         Com_Printf ("exec <filename> : execute a script file\n");
00246         return;
00247     }
00248 
00249     Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
00250     COM_DefaultExtension( filename, sizeof( filename ), ".cfg" ); 
00251     len = FS_ReadFile( filename, (void **)&f);
00252     if (!f) {
00253         Com_Printf ("couldn't exec %s\n",Cmd_Argv(1));
00254         return;
00255     }
00256     Com_Printf ("execing %s\n",Cmd_Argv(1));
00257     
00258     Cbuf_InsertText (f);
00259 
00260     FS_FreeFile (f);
00261 }
00262 
00263 
00264 /*
00265 ===============
00266 Cmd_Vstr_f
00267 
00268 Inserts the current value of a variable as command text
00269 ===============
00270 */
00271 void Cmd_Vstr_f( void ) {
00272     char    *v;
00273 
00274     if (Cmd_Argc () != 2) {
00275         Com_Printf ("vstr <variablename> : execute a variable command\n");
00276         return;
00277     }
00278 
00279     v = Cvar_VariableString( Cmd_Argv( 1 ) );
00280     Cbuf_InsertText( va("%s\n", v ) );
00281 }
00282 
00283 
00284 /*
00285 ===============
00286 Cmd_Echo_f
00287 
00288 Just prints the rest of the line to the console
00289 ===============
00290 */
00291 void Cmd_Echo_f (void)
00292 {
00293     int     i;
00294     
00295     for (i=1 ; i<Cmd_Argc() ; i++)
00296         Com_Printf ("%s ",Cmd_Argv(i));
00297     Com_Printf ("\n");
00298 }
00299 
00300 
00301 /*
00302 =============================================================================
00303 
00304                     COMMAND EXECUTION
00305 
00306 =============================================================================
00307 */
00308 
00309 typedef struct cmd_function_s
00310 {
00311     struct cmd_function_s   *next;
00312     char                    *name;
00313     xcommand_t              function;
00314 } cmd_function_t;
00315 
00316 
00317 static  int         cmd_argc;
00318 static  char        *cmd_argv[MAX_STRING_TOKENS];       // points into cmd_tokenized
00319 static  char        cmd_tokenized[BIG_INFO_STRING+MAX_STRING_TOKENS];   // will have 0 bytes inserted
00320 static  char        cmd_cmd[BIG_INFO_STRING]; // the original command we received (no token processing)
00321 
00322 static  cmd_function_t  *cmd_functions;     // possible commands to execute
00323 
00324 /*
00325 ============
00326 Cmd_Argc
00327 ============
00328 */
00329 int     Cmd_Argc( void ) {
00330     return cmd_argc;
00331 }
00332 
00333 /*
00334 ============
00335 Cmd_Argv
00336 ============
00337 */
00338 char    *Cmd_Argv( int arg ) {
00339     if ( (unsigned)arg >= cmd_argc ) {
00340         return "";
00341     }
00342     return cmd_argv[arg];   
00343 }
00344 
00345 /*
00346 ============
00347 Cmd_ArgvBuffer
00348 
00349 The interpreted versions use this because
00350 they can't have pointers returned to them
00351 ============
00352 */
00353 void    Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) {
00354     Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength );
00355 }
00356 
00357 
00358 /*
00359 ============
00360 Cmd_Args
00361 
00362 Returns a single string containing argv(1) to argv(argc()-1)
00363 ============
00364 */
00365 char    *Cmd_Args( void ) {
00366     static  char        cmd_args[MAX_STRING_CHARS];
00367     int     i;
00368 
00369     cmd_args[0] = 0;
00370     for ( i = 1 ; i < cmd_argc ; i++ ) {
00371         strcat( cmd_args, cmd_argv[i] );
00372         if ( i != cmd_argc-1 ) {
00373             strcat( cmd_args, " " );
00374         }
00375     }
00376 
00377     return cmd_args;
00378 }
00379 
00380 /*
00381 ============
00382 Cmd_Args
00383 
00384 Returns a single string containing argv(arg) to argv(argc()-1)
00385 ============
00386 */
00387 char *Cmd_ArgsFrom( int arg ) {
00388     static  char        cmd_args[BIG_INFO_STRING];
00389     int     i;
00390 
00391     cmd_args[0] = 0;
00392     if (arg < 0)
00393         arg = 0;
00394     for ( i = arg ; i < cmd_argc ; i++ ) {
00395         strcat( cmd_args, cmd_argv[i] );
00396         if ( i != cmd_argc-1 ) {
00397             strcat( cmd_args, " " );
00398         }
00399     }
00400 
00401     return cmd_args;
00402 }
00403 
00404 /*
00405 ============
00406 Cmd_ArgsBuffer
00407 
00408 The interpreted versions use this because
00409 they can't have pointers returned to them
00410 ============
00411 */
00412 void    Cmd_ArgsBuffer( char *buffer, int bufferLength ) {
00413     Q_strncpyz( buffer, Cmd_Args(), bufferLength );
00414 }
00415 
00416 /*
00417 ============
00418 Cmd_Cmd
00419 
00420 Retrieve the unmodified command string
00421 For rcon use when you want to transmit without altering quoting
00422 https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
00423 ============
00424 */
00425 char *Cmd_Cmd()
00426 {
00427     return cmd_cmd;
00428 }
00429 
00430 /*
00431 ============
00432 Cmd_TokenizeString
00433 
00434 Parses the given string into command line tokens.
00435 The text is copied to a seperate buffer and 0 characters
00436 are inserted in the apropriate place, The argv array
00437 will point into this temporary buffer.
00438 ============
00439 */
00440 // NOTE TTimo define that to track tokenization issues
00441 //#define TKN_DBG
00442 void Cmd_TokenizeString( const char *text_in ) {
00443     const char  *text;
00444     char    *textOut;
00445 
00446 #ifdef TKN_DBG
00447   // FIXME TTimo blunt hook to try to find the tokenization of userinfo
00448   Com_DPrintf("Cmd_TokenizeString: %s\n", text_in);
00449 #endif
00450 
00451     // clear previous args
00452     cmd_argc = 0;
00453 
00454     if ( !text_in ) {
00455         return;
00456     }
00457     
00458     Q_strncpyz( cmd_cmd, text_in, sizeof(cmd_cmd) );
00459 
00460     text = text_in;
00461     textOut = cmd_tokenized;
00462 
00463     while ( 1 ) {
00464         if ( cmd_argc == MAX_STRING_TOKENS ) {
00465             return;         // this is usually something malicious
00466         }
00467 
00468         while ( 1 ) {
00469             // skip whitespace
00470             while ( *text && *text <= ' ' ) {
00471                 text++;
00472             }
00473             if ( !*text ) {
00474                 return;         // all tokens parsed
00475             }
00476 
00477             // skip // comments
00478             if ( text[0] == '/' && text[1] == '/' ) {
00479                 return;         // all tokens parsed
00480             }
00481 
00482             // skip /* */ comments
00483             if ( text[0] == '/' && text[1] =='*' ) {
00484                 while ( *text && ( text[0] != '*' || text[1] != '/' ) ) {
00485                     text++;
00486                 }
00487                 if ( !*text ) {
00488                     return;     // all tokens parsed
00489                 }
00490                 text += 2;
00491             } else {
00492                 break;          // we are ready to parse a token
00493             }
00494         }
00495 
00496         // handle quoted strings
00497     // NOTE TTimo this doesn't handle \" escaping
00498         if ( *text == '"' ) {
00499             cmd_argv[cmd_argc] = textOut;
00500             cmd_argc++;
00501             text++;
00502             while ( *text && *text != '"' ) {
00503                 *textOut++ = *text++;
00504             }
00505             *textOut++ = 0;
00506             if ( !*text ) {
00507                 return;     // all tokens parsed
00508             }
00509             text++;
00510             continue;
00511         }
00512 
00513         // regular token
00514         cmd_argv[cmd_argc] = textOut;
00515         cmd_argc++;
00516 
00517         // skip until whitespace, quote, or command
00518         while ( *text > ' ' ) {
00519             if ( text[0] == '"' ) {
00520                 break;
00521             }
00522 
00523             if ( text[0] == '/' && text[1] == '/' ) {
00524                 break;
00525             }
00526 
00527             // skip /* */ comments
00528             if ( text[0] == '/' && text[1] =='*' ) {
00529                 break;
00530             }
00531 
00532             *textOut++ = *text++;
00533         }
00534 
00535         *textOut++ = 0;
00536 
00537         if ( !*text ) {
00538             return;     // all tokens parsed
00539         }
00540     }
00541     
00542 }
00543 
00544 
00545 /*
00546 ============
00547 Cmd_AddCommand
00548 ============
00549 */
00550 void    Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
00551     cmd_function_t  *cmd;
00552     
00553     // fail if the command already exists
00554     for ( cmd = cmd_functions ; cmd ; cmd=cmd->next ) {
00555         if ( !strcmp( cmd_name, cmd->name ) ) {
00556             // allow completion-only commands to be silently doubled
00557             if ( function != NULL ) {
00558                 Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
00559             }
00560             return;
00561         }
00562     }
00563 
00564     // use a small malloc to avoid zone fragmentation
00565     cmd = S_Malloc (sizeof(cmd_function_t));
00566     cmd->name = CopyString( cmd_name );
00567     cmd->function = function;
00568     cmd->next = cmd_functions;
00569     cmd_functions = cmd;
00570 }
00571 
00572 /*
00573 ============
00574 Cmd_RemoveCommand
00575 ============
00576 */
00577 void    Cmd_RemoveCommand( const char *cmd_name ) {
00578     cmd_function_t  *cmd, **back;
00579 
00580     back = &cmd_functions;
00581     while( 1 ) {
00582         cmd = *back;
00583         if ( !cmd ) {
00584             // command wasn't active
00585             return;
00586         }
00587         if ( !strcmp( cmd_name, cmd->name ) ) {
00588             *back = cmd->next;
00589             if (cmd->name) {
00590                 Z_Free(cmd->name);
00591             }
00592             Z_Free (cmd);
00593             return;
00594         }
00595         back = &cmd->next;
00596     }
00597 }
00598 
00599 
00600 /*
00601 ============
00602 Cmd_CommandCompletion
00603 ============
00604 */
00605 void    Cmd_CommandCompletion( void(*callback)(const char *s) ) {
00606     cmd_function_t  *cmd;
00607     
00608     for (cmd=cmd_functions ; cmd ; cmd=cmd->next) {
00609         callback( cmd->name );
00610     }
00611 }
00612 
00613 
00614 /*
00615 ============
00616 Cmd_ExecuteString
00617 
00618 A complete command line has been parsed, so try to execute it
00619 ============
00620 */
00621 void    Cmd_ExecuteString( const char *text ) { 
00622     cmd_function_t  *cmd, **prev;
00623 
00624     // execute the command line
00625     Cmd_TokenizeString( text );     
00626     if ( !Cmd_Argc() ) {
00627         return;     // no tokens
00628     }
00629 
00630     // check registered command functions   
00631     for ( prev = &cmd_functions ; *prev ; prev = &cmd->next ) {
00632         cmd = *prev;
00633         if ( !Q_stricmp( cmd_argv[0],cmd->name ) ) {
00634             // rearrange the links so that the command will be
00635             // near the head of the list next time it is used
00636             *prev = cmd->next;
00637             cmd->next = cmd_functions;
00638             cmd_functions = cmd;
00639 
00640             // perform the action
00641             if ( !cmd->function ) {
00642                 // let the cgame or game handle it
00643                 break;
00644             } else {
00645                 cmd->function ();
00646             }
00647             return;
00648         }
00649     }
00650     
00651     // check cvars
00652     if ( Cvar_Command() ) {
00653         return;
00654     }
00655 
00656     // check client game commands
00657     if ( com_cl_running && com_cl_running->integer && CL_GameCommand() ) {
00658         return;
00659     }
00660 
00661     // check server game commands
00662     if ( com_sv_running && com_sv_running->integer && SV_GameCommand() ) {
00663         return;
00664     }
00665 
00666     // check ui commands
00667     if ( com_cl_running && com_cl_running->integer && UI_GameCommand() ) {
00668         return;
00669     }
00670 
00671     // send it as a server command if we are connected
00672     // this will usually result in a chat message
00673     CL_ForwardCommandToServer ( text );
00674 }
00675 
00676 /*
00677 ============
00678 Cmd_List_f
00679 ============
00680 */
00681 void Cmd_List_f (void)
00682 {
00683     cmd_function_t  *cmd;
00684     int             i;
00685     char            *match;
00686 
00687     if ( Cmd_Argc() > 1 ) {
00688         match = Cmd_Argv( 1 );
00689     } else {
00690         match = NULL;
00691     }
00692 
00693     i = 0;
00694     for (cmd=cmd_functions ; cmd ; cmd=cmd->next) {
00695         if (match && !Com_Filter(match, cmd->name, qfalse)) continue;
00696 
00697         Com_Printf ("%s\n", cmd->name);
00698         i++;
00699     }
00700     Com_Printf ("%i commands\n", i);
00701 }
00702 
00703 /*
00704 ============
00705 Cmd_Init
00706 ============
00707 */
00708 void Cmd_Init (void) {
00709     Cmd_AddCommand ("cmdlist",Cmd_List_f);
00710     Cmd_AddCommand ("exec",Cmd_Exec_f);
00711     Cmd_AddCommand ("vstr",Cmd_Vstr_f);
00712     Cmd_AddCommand ("echo",Cmd_Echo_f);
00713     Cmd_AddCommand ("wait", Cmd_Wait_f);
00714 }
00715 

Generated on Thu Aug 25 12:37:46 2005 for Quake III Arena by  doxygen 1.3.9.1