cmd.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. // cmd.c -- Quake script command processing module
  2. #include "../game/q_shared.h"
  3. #include "qcommon.h"
  4. #define MAX_CMD_BUFFER 8192
  5. int cmd_wait;
  6. msg_t cmd_text;
  7. byte cmd_text_buf[MAX_CMD_BUFFER];
  8. char cmd_defer_text_buf[MAX_CMD_BUFFER];
  9. //=============================================================================
  10. /*
  11. ============
  12. Cmd_Wait_f
  13. Causes execution of the remainder of the command buffer to be delayed until
  14. next frame. This allows commands like:
  15. bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster"
  16. ============
  17. */
  18. void Cmd_Wait_f( void ) {
  19. if ( Cmd_Argc() == 2 ) {
  20. cmd_wait = atoi( Cmd_Argv( 1 ) );
  21. } else {
  22. cmd_wait = 1;
  23. }
  24. }
  25. /*
  26. =============================================================================
  27. COMMAND BUFFER
  28. =============================================================================
  29. */
  30. /*
  31. ============
  32. Cbuf_Init
  33. ============
  34. */
  35. void Cbuf_Init (void)
  36. {
  37. MSG_Init (&cmd_text, cmd_text_buf, sizeof(cmd_text_buf));
  38. }
  39. /*
  40. ============
  41. Cbuf_AddText
  42. Adds command text at the end of the buffer, does NOT add a final \n
  43. ============
  44. */
  45. void Cbuf_AddText( const char *text ) {
  46. int l;
  47. l = strlen (text);
  48. if (cmd_text.cursize + l >= cmd_text.maxsize)
  49. {
  50. Com_Printf ("Cbuf_AddText: overflow\n");
  51. return;
  52. }
  53. MSG_WriteData (&cmd_text, text, strlen (text));
  54. }
  55. /*
  56. ============
  57. Cbuf_InsertText
  58. Adds command text immediately after the current command
  59. Adds a \n to the text
  60. ============
  61. */
  62. void Cbuf_InsertText( const char *text ) {
  63. int len;
  64. int i;
  65. len = strlen( text ) + 1;
  66. if ( len + cmd_text.cursize > cmd_text.maxsize ) {
  67. Com_Printf( "Cbuf_InsertText overflowed\n" );
  68. return;
  69. }
  70. // move the existing command text
  71. for ( i = cmd_text.cursize - 1 ; i >= 0 ; i-- ) {
  72. cmd_text.data[ i + len ] = cmd_text.data[ i ];
  73. }
  74. // copy the new text in
  75. memcpy( cmd_text.data, text, len - 1 );
  76. // add a \n
  77. cmd_text.data[ len - 1 ] = '\n';
  78. cmd_text.cursize += len;
  79. }
  80. /*
  81. ============
  82. Cbuf_ExecuteText
  83. ============
  84. */
  85. void Cbuf_ExecuteText (int exec_when, const char *text)
  86. {
  87. switch (exec_when)
  88. {
  89. case EXEC_NOW:
  90. Cmd_ExecuteString (text);
  91. break;
  92. case EXEC_INSERT:
  93. Cbuf_InsertText (text);
  94. break;
  95. case EXEC_APPEND:
  96. Cbuf_AddText (text);
  97. break;
  98. default:
  99. Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
  100. }
  101. }
  102. /*
  103. ============
  104. Cbuf_Execute
  105. ============
  106. */
  107. void Cbuf_Execute (void)
  108. {
  109. int i;
  110. char *text;
  111. char line[MAX_CMD_BUFFER];
  112. int quotes;
  113. while (cmd_text.cursize)
  114. {
  115. if ( cmd_wait ) {
  116. // skip out while text still remains in buffer, leaving it
  117. // for next frame
  118. cmd_wait--;
  119. break;
  120. }
  121. // find a \n or ; line break
  122. text = (char *)cmd_text.data;
  123. quotes = 0;
  124. for (i=0 ; i< cmd_text.cursize ; i++)
  125. {
  126. if (text[i] == '"')
  127. quotes++;
  128. if ( !(quotes&1) && text[i] == ';')
  129. break; // don't break if inside a quoted string
  130. if (text[i] == '\n' || text[i] == '\r' )
  131. break;
  132. }
  133. memcpy (line, text, i);
  134. line[i] = 0;
  135. // delete the text from the command buffer and move remaining commands down
  136. // this is necessary because commands (exec) can insert data at the
  137. // beginning of the text buffer
  138. if (i == cmd_text.cursize)
  139. cmd_text.cursize = 0;
  140. else
  141. {
  142. i++;
  143. cmd_text.cursize -= i;
  144. memmove (text, text+i, cmd_text.cursize);
  145. }
  146. // execute the command line
  147. Cmd_ExecuteString (line);
  148. }
  149. }
  150. /*
  151. ==============================================================================
  152. SCRIPT COMMANDS
  153. ==============================================================================
  154. */
  155. /*
  156. ===============
  157. Cmd_Exec_f
  158. ===============
  159. */
  160. void Cmd_Exec_f( void ) {
  161. char *f;
  162. int len;
  163. char filename[MAX_QPATH];
  164. if (Cmd_Argc () != 2) {
  165. Com_Printf ("exec <filename> : execute a script file\n");
  166. return;
  167. }
  168. Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
  169. COM_DefaultExtension( filename, sizeof( filename ), ".cfg" );
  170. len = FS_ReadFile( filename, (void **)&f);
  171. if (!f) {
  172. Com_Printf ("couldn't exec %s\n",Cmd_Argv(1));
  173. return;
  174. }
  175. Com_Printf ("execing %s\n",Cmd_Argv(1));
  176. Cbuf_InsertText (f);
  177. FS_FreeFile (f);
  178. }
  179. /*
  180. ===============
  181. Cmd_Vstr_f
  182. Inserts the current value of a variable as command text
  183. ===============
  184. */
  185. void Cmd_Vstr_f( void ) {
  186. char *v;
  187. if (Cmd_Argc () != 2) {
  188. Com_Printf ("vstr <variablename> : execute a variable command\n");
  189. return;
  190. }
  191. v = Cvar_VariableString( Cmd_Argv( 1 ) );
  192. Cbuf_InsertText( va("%s\n", v ) );
  193. }
  194. /*
  195. ===============
  196. Cmd_Echo_f
  197. Just prints the rest of the line to the console
  198. ===============
  199. */
  200. void Cmd_Echo_f (void)
  201. {
  202. int i;
  203. for (i=1 ; i<Cmd_Argc() ; i++)
  204. Com_Printf ("%s ",Cmd_Argv(i));
  205. Com_Printf ("\n");
  206. }
  207. /*
  208. =============================================================================
  209. COMMAND EXECUTION
  210. =============================================================================
  211. */
  212. #define CMD_MAX_NUM 256
  213. #define CMD_MAX_NAME 32
  214. typedef struct cmd_function_s
  215. {
  216. char name[CMD_MAX_NAME];
  217. xcommand_t function;
  218. } cmd_function_t;
  219. static int cmd_argc;
  220. static char *cmd_argv[MAX_STRING_TOKENS]; // points into cmd_tokenized
  221. static char cmd_tokenized[MAX_STRING_CHARS+MAX_STRING_TOKENS]; // will have 0 bytes inserted
  222. static cmd_function_t cmd_functions[CMD_MAX_NUM] = {0}; // possible commands to execute
  223. /*
  224. ============
  225. Cmd_Argc
  226. ============
  227. */
  228. int Cmd_Argc( void ) {
  229. return cmd_argc;
  230. }
  231. /*
  232. ============
  233. Cmd_Argv
  234. ============
  235. */
  236. char *Cmd_Argv( int arg ) {
  237. if ( (unsigned)arg >= cmd_argc ) {
  238. return "";
  239. }
  240. return cmd_argv[arg];
  241. }
  242. /*
  243. ============
  244. Cmd_ArgvBuffer
  245. The interpreted versions use this because
  246. they can't have pointers returned to them
  247. ============
  248. */
  249. void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) {
  250. Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength );
  251. }
  252. /*
  253. ============
  254. Cmd_Args
  255. Returns a single string containing argv(1) to argv(argc()-1)
  256. ============
  257. */
  258. char *Cmd_Args( void ) {
  259. static char cmd_args[MAX_STRING_CHARS];
  260. int i;
  261. cmd_args[0] = 0;
  262. for ( i = 1 ; i < cmd_argc ; i++ ) {
  263. strcat( cmd_args, cmd_argv[i] );
  264. if ( i != cmd_argc ) {
  265. strcat( cmd_args, " " );
  266. }
  267. }
  268. return cmd_args;
  269. }
  270. /*
  271. ============
  272. Cmd_ArgsBuffer
  273. The interpreted versions use this because
  274. they can't have pointers returned to them
  275. ============
  276. */
  277. void Cmd_ArgsBuffer( char *buffer, int bufferLength ) {
  278. Q_strncpyz( buffer, Cmd_Args(), bufferLength );
  279. }
  280. /*
  281. ============
  282. Cmd_TokenizeString
  283. Parses the given string into command line tokens.
  284. The text is copied to a seperate buffer and 0 characters
  285. are inserted in the apropriate place, The argv array
  286. will point into this temporary buffer.
  287. ============
  288. */
  289. void Cmd_TokenizeString( const char *text_in ) {
  290. const unsigned char *text;
  291. char *textOut;
  292. // clear previous args
  293. cmd_argc = 0;
  294. if ( !text_in ) {
  295. return;
  296. }
  297. text = (const unsigned char *)text_in;
  298. textOut = cmd_tokenized;
  299. while ( 1 ) {
  300. if ( cmd_argc == MAX_STRING_TOKENS ) {
  301. return; // this is usually something malicious
  302. }
  303. while ( 1 ) {
  304. // skip whitespace
  305. while ( *text && *text <= ' ' ) {
  306. text++;
  307. }
  308. if ( !*text ) {
  309. return; // all tokens parsed
  310. }
  311. // skip // comments
  312. if ( text[0] == '/' && text[1] == '/' ) {
  313. return; // all tokens parsed
  314. }
  315. // skip /* */ comments
  316. if ( text[0] == '/' && text[1] =='*' ) {
  317. while ( *text && ( text[0] != '*' || text[1] != '/' ) ) {
  318. text++;
  319. }
  320. if ( !*text ) {
  321. return; // all tokens parsed
  322. }
  323. text += 2;
  324. } else {
  325. break; // we are ready to parse a token
  326. }
  327. }
  328. // handle quoted strings
  329. if ( *text == '"' ) {
  330. cmd_argv[cmd_argc] = textOut;
  331. cmd_argc++;
  332. text++;
  333. while ( *text && *text != '"' ) {
  334. *textOut++ = *text++;
  335. }
  336. *textOut++ = 0;
  337. if ( !*text ) {
  338. return; // all tokens parsed
  339. }
  340. text++;
  341. continue;
  342. }
  343. // regular token
  344. cmd_argv[cmd_argc] = textOut;
  345. cmd_argc++;
  346. // skip until whitespace, quote, or command
  347. while ( *text > ' ' ) {
  348. if ( text[0] == '"' ) {
  349. break;
  350. }
  351. if ( text[0] == '/' && text[1] == '/' ) {
  352. break;
  353. }
  354. // skip /* */ comments
  355. if ( text[0] == '/' && text[1] =='*' ) {
  356. break;
  357. }
  358. *textOut++ = *text++;
  359. }
  360. *textOut++ = 0;
  361. if ( !*text ) {
  362. return; // all tokens parsed
  363. }
  364. }
  365. }
  366. /*
  367. ============
  368. Cmd_AddCommand
  369. ============
  370. */
  371. void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) {
  372. cmd_function_t *cmd;
  373. cmd_function_t *add = NULL;
  374. int c;
  375. // fail if the command already exists
  376. for ( c = 0; c < CMD_MAX_NUM; ++c )
  377. {
  378. cmd = cmd_functions + c;
  379. if ( !strcmp( cmd_name, cmd->name ) ) {
  380. // allow completion-only commands to be silently doubled
  381. if ( function != NULL ) {
  382. Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
  383. }
  384. return;
  385. }
  386. if ( add == NULL && cmd->name[0] == '\0')
  387. {
  388. add = cmd;
  389. }
  390. }
  391. if ( add == NULL )
  392. {
  393. Com_Printf ("Cmd_AddCommand: Too many commands registered\n", cmd_name);
  394. return;
  395. }
  396. Q_strncpyz(add->name, cmd_name, CMD_MAX_NAME, qtrue);
  397. add->function = function;
  398. }
  399. /*
  400. ============
  401. Cmd_RemoveCommand
  402. ============
  403. */
  404. void Cmd_RemoveCommand( const char *cmd_name ) {
  405. cmd_function_t *cmd;
  406. for ( int c = 0; c < CMD_MAX_NUM; ++c )
  407. {
  408. cmd = cmd_functions + c;
  409. if ( !strcmp( cmd_name, cmd->name ) ) {
  410. cmd->name[0] = '\0';
  411. return;
  412. }
  413. }
  414. }
  415. char *Cmd_CompleteCommandNext (char *partial, char *last)
  416. {
  417. cmd_function_t *cmd, *base;
  418. int len, c;
  419. len = strlen(partial);
  420. if (!len)
  421. return NULL;
  422. // start past last match
  423. base = NULL;
  424. if(last)
  425. {
  426. for (c = 0; c < CMD_MAX_NUM; ++c)
  427. {
  428. cmd = cmd_functions + c;
  429. if(!strcmp(last, cmd->name))
  430. {
  431. base = cmd + 1;
  432. break;
  433. }
  434. }
  435. if(base == NULL)
  436. { //not found, either error or at end of list
  437. return NULL;
  438. }
  439. }
  440. else
  441. {
  442. base = cmd_functions;
  443. }
  444. for (c = base - cmd_functions; c < CMD_MAX_NUM; ++c)
  445. {
  446. cmd = cmd_functions + c;
  447. if (!strcmp (partial,cmd->name))
  448. return cmd->name;
  449. }
  450. // check for partial match
  451. for (c = base - cmd_functions; c < CMD_MAX_NUM; ++c)
  452. {
  453. cmd = cmd_functions + c;
  454. if (!strncmp (partial,cmd->name, len))
  455. return cmd->name;
  456. }
  457. return NULL;
  458. }
  459. /*
  460. ============
  461. Cmd_CompleteCommand
  462. ============
  463. */
  464. char *Cmd_CompleteCommand( const char *partial ) {
  465. cmd_function_t *cmd;
  466. int len;
  467. len = strlen(partial);
  468. if (!len)
  469. return NULL;
  470. // check for exact match
  471. for (int c = 0; c < CMD_MAX_NUM; ++c)
  472. {
  473. cmd = cmd_functions + c;
  474. if (!Q_stricmp( partial, cmd->name))
  475. return cmd->name;
  476. }
  477. // check for partial match
  478. for (int c = 0; c < CMD_MAX_NUM; ++c)
  479. {
  480. cmd = cmd_functions + c;
  481. if (!Q_stricmpn (partial,cmd->name, len))
  482. return cmd->name;
  483. }
  484. return NULL;
  485. }
  486. /*
  487. ============
  488. Cmd_ExecuteString
  489. A complete command line has been parsed, so try to execute it
  490. ============
  491. */
  492. extern void Key_SetCatcher( int catcher );
  493. extern void Menus_CloseAll(void);
  494. void Cmd_ExecuteString( const char *text ) {
  495. // execute the command line
  496. Cmd_TokenizeString( text );
  497. if ( !Cmd_Argc() ) {
  498. return; // no tokens
  499. }
  500. cvar_t* levelSelectCheat = Cvar_Get("levelSelectCheat", "-1", CVAR_SAVEGAME);
  501. if( (!strcmp(text,"use end_level") || strstr(text, "maptransition") || !strcmp(text, "uimenu ingameMissionSelect") || !strcmp(text, "uimenu ingameGotoTier")) && // level end
  502. levelSelectCheat->integer != -1 ) // was cheating
  503. {
  504. Cbuf_ExecuteText( EXEC_APPEND, "disconnect\n" ); // disconnect the player
  505. Key_SetCatcher( KEYCATCH_UI ); // set them in the ui
  506. Menus_CloseAll(); // close all the menus
  507. Cvar_Set("levelSelectCheat", "-1");
  508. return;
  509. }
  510. // check registered command functions
  511. for ( int c = 0; c < CMD_MAX_NUM; ++c )
  512. {
  513. if ( !Q_stricmp( cmd_argv[0],cmd_functions[c].name ) ) {
  514. // rearrange the links so that the command will be
  515. // near the head of the list next time it is used
  516. cmd_function_t temp = cmd_functions[c];
  517. cmd_functions[c] = cmd_functions[0];
  518. cmd_functions[0] = temp;
  519. // perform the action
  520. if ( !temp.function ) {
  521. // let the cgame or game handle it
  522. break;
  523. } else {
  524. temp.function ();
  525. }
  526. return;
  527. }
  528. }
  529. // check cvars
  530. if ( Cvar_Command() ) {
  531. return;
  532. }
  533. // check client game commands
  534. if ( com_cl_running && com_cl_running->integer && CL_GameCommand() ) {
  535. return;
  536. }
  537. // check server game commands
  538. if ( com_sv_running && com_sv_running->integer && SV_GameCommand() ) {
  539. return;
  540. }
  541. // check ui commands
  542. if ( com_cl_running && com_cl_running->integer && UI_GameCommand() ) {
  543. return;
  544. }
  545. // send it as a server command if we are connected
  546. // this will usually result in a chat message
  547. CL_ForwardCommandToServer ();
  548. }
  549. /*
  550. ============
  551. Cmd_List_f
  552. ============
  553. */
  554. void Cmd_List_f (void)
  555. {
  556. cmd_function_t *cmd;
  557. int i;
  558. char *match;
  559. if ( Cmd_Argc() > 1 ) {
  560. match = Cmd_Argv( 1 );
  561. } else {
  562. match = NULL;
  563. }
  564. i = 0;
  565. for ( int c = 0; c < CMD_MAX_NUM; ++c )
  566. {
  567. cmd = cmd_functions + c;
  568. if (match && !Com_Filter(match, cmd->name, qfalse)) continue;
  569. Com_Printf ("%s\n", cmd->name);
  570. i++;
  571. }
  572. Com_Printf ("%i commands\n", i);
  573. }
  574. /*
  575. ============
  576. Cmd_Init
  577. ============
  578. */
  579. void Cmd_Init (void)
  580. {
  581. //
  582. // register our commands
  583. //
  584. Cmd_AddCommand ("cmdlist",Cmd_List_f);
  585. Cmd_AddCommand ("exec",Cmd_Exec_f);
  586. Cmd_AddCommand ("vstr",Cmd_Vstr_f);
  587. Cmd_AddCommand ("echo",Cmd_Echo_f);
  588. Cmd_AddCommand ("wait", Cmd_Wait_f);
  589. }