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