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