1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8
9 This file is part of the OpenJK source code.
10
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24
25 // cmd.c -- Quake script command processing module
26
27 #include "qcommon/qcommon.h"
28
29 #include <vector>
30 #include <algorithm>
31
32 #define MAX_CMD_BUFFER 128*1024
33 #define MAX_CMD_LINE 1024
34
35 typedef struct cmd_s {
36 byte *data;
37 int maxsize;
38 int cursize;
39 } cmd_t;
40
41 int cmd_wait;
42 cmd_t cmd_text;
43 byte cmd_text_buf[MAX_CMD_BUFFER];
44
45 //=============================================================================
46
47 /*
48 ============
49 Cmd_Wait_f
50
51 Causes execution of the remainder of the command buffer to be delayed until
52 next frame. This allows commands like:
53 bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster"
54 ============
55 */
Cmd_Wait_f(void)56 static void Cmd_Wait_f( void ) {
57 if ( Cmd_Argc() == 2 ) {
58 cmd_wait = atoi( Cmd_Argv( 1 ) );
59 if ( cmd_wait < 0 )
60 cmd_wait = 1; // ignore the argument
61 } else {
62 cmd_wait = 1;
63 }
64 }
65
66
67 /*
68 =============================================================================
69
70 COMMAND BUFFER
71
72 =============================================================================
73 */
74
75 /*
76 ============
77 Cbuf_Init
78 ============
79 */
Cbuf_Init(void)80 void Cbuf_Init (void)
81 {
82 cmd_text.data = cmd_text_buf;
83 cmd_text.maxsize = MAX_CMD_BUFFER;
84 cmd_text.cursize = 0;
85 }
86
87 /*
88 ============
89 Cbuf_AddText
90
91 Adds command text at the end of the buffer, does NOT add a final \n
92 ============
93 */
Cbuf_AddText(const char * text)94 void Cbuf_AddText( const char *text ) {
95 int l;
96
97 l = strlen (text);
98
99 if (cmd_text.cursize + l >= cmd_text.maxsize)
100 {
101 Com_Printf ("Cbuf_AddText: overflow\n");
102 return;
103 }
104 Com_Memcpy(&cmd_text.data[cmd_text.cursize], text, l);
105 cmd_text.cursize += l;
106 }
107
108
109 /*
110 ============
111 Cbuf_InsertText
112
113 Adds command text immediately after the current command
114 Adds a \n to the text
115 ============
116 */
Cbuf_InsertText(const char * text)117 void Cbuf_InsertText( const char *text ) {
118 int len;
119 int i;
120
121 len = strlen( text ) + 1;
122 if ( len + cmd_text.cursize > cmd_text.maxsize ) {
123 Com_Printf( "Cbuf_InsertText overflowed\n" );
124 return;
125 }
126
127 // move the existing command text
128 for ( i = cmd_text.cursize - 1 ; i >= 0 ; i-- ) {
129 cmd_text.data[ i + len ] = cmd_text.data[ i ];
130 }
131
132 // copy the new text in
133 Com_Memcpy( cmd_text.data, text, len - 1 );
134
135 // add a \n
136 cmd_text.data[ len - 1 ] = '\n';
137
138 cmd_text.cursize += len;
139 }
140
141
142 /*
143 ============
144 Cbuf_ExecuteText
145 ============
146 */
Cbuf_ExecuteText(int exec_when,const char * text)147 void Cbuf_ExecuteText (int exec_when, const char *text)
148 {
149 switch (exec_when)
150 {
151 case EXEC_NOW:
152 if (text && strlen(text) > 0) {
153 Com_DPrintf(S_COLOR_YELLOW "EXEC_NOW %s\n", text);
154 Cmd_ExecuteString (text);
155 } else {
156 Cbuf_Execute();
157 Com_DPrintf(S_COLOR_YELLOW "EXEC_NOW %s\n", cmd_text.data);
158 }
159 break;
160 case EXEC_INSERT:
161 Cbuf_InsertText (text);
162 break;
163 case EXEC_APPEND:
164 Cbuf_AddText (text);
165 break;
166 default:
167 Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
168 }
169 }
170
171 /*
172 ============
173 Cbuf_Execute
174 ============
175 */
Cbuf_Execute(void)176 void Cbuf_Execute (void)
177 {
178 int i;
179 char *text;
180 char line[MAX_CMD_LINE];
181 int quotes;
182
183 // This will keep // style comments all on one line by not breaking on
184 // a semicolon. It will keep /* ... */ style comments all on one line by not
185 // breaking it for semicolon or newline.
186 qboolean in_star_comment = qfalse;
187 qboolean in_slash_comment = qfalse;
188
189 while (cmd_text.cursize)
190 {
191 if ( cmd_wait > 0 ) {
192 // skip out while text still remains in buffer, leaving it
193 // for next frame
194 cmd_wait--;
195 break;
196 }
197
198 // find a \n or ; line break or comment: // or /* */
199 text = (char *)cmd_text.data;
200
201 quotes = 0;
202 for (i=0 ; i< cmd_text.cursize ; i++)
203 {
204 if (text[i] == '"')
205 quotes++;
206
207 if ( !(quotes&1)) {
208 if (i < cmd_text.cursize - 1) {
209 if (! in_star_comment && text[i] == '/' && text[i+1] == '/')
210 in_slash_comment = qtrue;
211 else if (! in_slash_comment && text[i] == '/' && text[i+1] == '*')
212 in_star_comment = qtrue;
213 else if (in_star_comment && text[i] == '*' && text[i+1] == '/') {
214 in_star_comment = qfalse;
215 // If we are in a star comment, then the part after it is valid
216 // Note: This will cause it to NUL out the terminating '/'
217 // but ExecuteString doesn't require it anyway.
218 i++;
219 break;
220 }
221 }
222 if (! in_slash_comment && ! in_star_comment && text[i] == ';')
223 break;
224 }
225 if (! in_star_comment && (text[i] == '\n' || text[i] == '\r')) {
226 in_slash_comment = qfalse;
227 break;
228 }
229 }
230
231 if( i >= (MAX_CMD_LINE - 1)) {
232 i = MAX_CMD_LINE - 1;
233 }
234
235 Com_Memcpy (line, text, i);
236 line[i] = 0;
237
238 // delete the text from the command buffer and move remaining commands down
239 // this is necessary because commands (exec) can insert data at the
240 // beginning of the text buffer
241
242 if (i == cmd_text.cursize)
243 cmd_text.cursize = 0;
244 else
245 {
246 i++;
247 cmd_text.cursize -= i;
248 memmove (text, text+i, cmd_text.cursize);
249 }
250
251 // execute the command line
252
253 Cmd_ExecuteString (line);
254 }
255 }
256
257
258 /*
259 ==============================================================================
260
261 SCRIPT COMMANDS
262
263 ==============================================================================
264 */
265
266
267 /*
268 ===============
269 Cmd_Exec_f
270 ===============
271 */
Cmd_Exec_f(void)272 static void Cmd_Exec_f( void ) {
273 bool quiet;
274 fileBuffer_t f;
275 char filename[MAX_QPATH];
276
277 quiet = !Q_stricmp(Cmd_Argv(0), "execq");
278
279 if (Cmd_Argc () != 2) {
280 Com_Printf ("exec%s <filename> : execute a script file%s\n",
281 quiet ? "q" : "", quiet ? " without notification" : "");
282 return;
283 }
284
285 Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
286 COM_DefaultExtension( filename, sizeof( filename ), ".cfg" );
287 FS_ReadFile( filename, &f.v);
288 if (!f.c) {
289 Com_Printf ("couldn't exec %s\n", filename);
290 return;
291 }
292 if (!quiet)
293 Com_Printf ("execing %s\n", filename);
294
295 Cbuf_InsertText (f.c);
296
297 FS_FreeFile (f.v);
298 }
299
300
301 /*
302 ===============
303 Cmd_Vstr_f
304
305 Inserts the current value of a variable as command text
306 ===============
307 */
Cmd_Vstr_f(void)308 static void Cmd_Vstr_f( void ) {
309 char *v;
310
311 if (Cmd_Argc () != 2) {
312 Com_Printf ("vstr <variablename> : execute a variable command\n");
313 return;
314 }
315
316 v = Cvar_VariableString( Cmd_Argv( 1 ) );
317 Cbuf_InsertText( va("%s\n", v ) );
318 }
319
320
321 /*
322 ===============
323 Cmd_Echo_f
324
325 Just prints the rest of the line to the console
326 ===============
327 */
Cmd_Echo_f(void)328 static void Cmd_Echo_f (void)
329 {
330 Com_Printf ("%s\n", Cmd_Args());
331 }
332
333
334 /*
335 =============================================================================
336
337 COMMAND EXECUTION
338
339 =============================================================================
340 */
341
342 typedef struct cmd_function_s
343 {
344 struct cmd_function_s *next;
345 char *name;
346 char *description;
347 xcommand_t function;
348 completionFunc_t complete;
349 } cmd_function_t;
350
351
352
353 static int cmd_argc;
354 static char *cmd_argv[MAX_STRING_TOKENS]; // points into cmd_tokenized
355 static char cmd_tokenized[BIG_INFO_STRING+MAX_STRING_TOKENS]; // will have 0 bytes inserted
356 static char cmd_cmd[BIG_INFO_STRING]; // the original command we received (no token processing)
357
358 static cmd_function_t *cmd_functions; // possible commands to execute
359
360
361 /*
362 ============
363 Cmd_Argc
364 ============
365 */
Cmd_Argc(void)366 int Cmd_Argc( void ) {
367 return cmd_argc;
368 }
369
370 /*
371 ============
372 Cmd_Argv
373 ============
374 */
Cmd_Argv(int arg)375 char *Cmd_Argv( int arg ) {
376 if ( (unsigned)arg >= (unsigned)cmd_argc )
377 return "";
378
379 return cmd_argv[arg];
380 }
381
382 /*
383 ============
384 Cmd_ArgvBuffer
385
386 The interpreted versions use this because
387 they can't have pointers returned to them
388 ============
389 */
Cmd_ArgvBuffer(int arg,char * buffer,int bufferLength)390 void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) {
391 Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength );
392 }
393
394 /*
395 ============
396 Cmd_ArgsFrom
397
398 Returns a single string containing argv(arg) to argv(argc()-1)
399 ============
400 */
Cmd_ArgsFrom(int arg)401 char *Cmd_ArgsFrom( int arg ) {
402 static char cmd_args[BIG_INFO_STRING];
403 int i;
404
405 cmd_args[0] = '\0';
406 if (arg < 0)
407 arg = 0;
408 for ( i = arg ; i < cmd_argc ; i++ ) {
409 Q_strcat( cmd_args, sizeof( cmd_args ), cmd_argv[i] );
410 if ( i != cmd_argc-1 ) {
411 Q_strcat( cmd_args, sizeof( cmd_args ), " " );
412 }
413 }
414
415 return cmd_args;
416 }
417
418 /*
419 ============
420 Cmd_Args
421
422 Returns a single string containing argv(1) to argv(argc()-1)
423 ============
424 */
Cmd_Args(void)425 char *Cmd_Args( void ) {
426 return Cmd_ArgsFrom( 1 );
427 }
428
429 /*
430 ============
431 Cmd_ArgsBuffer
432
433 The interpreted versions use this because
434 they can't have pointers returned to them
435 ============
436 */
Cmd_ArgsBuffer(char * buffer,int bufferLength)437 void Cmd_ArgsBuffer( char *buffer, int bufferLength ) {
438 Q_strncpyz( buffer, Cmd_ArgsFrom( 1 ), bufferLength );
439 }
440
441 /*
442 ============
443 Cmd_ArgsFromBuffer
444
445 The interpreted versions use this because
446 they can't have pointers returned to them
447 ============
448 */
Cmd_ArgsFromBuffer(int arg,char * buffer,int bufferLength)449 void Cmd_ArgsFromBuffer( int arg, char *buffer, int bufferLength ) {
450 Q_strncpyz( buffer, Cmd_ArgsFrom( arg ), bufferLength );
451 }
452
453 /*
454 ============
455 Cmd_Cmd
456
457 Retrieve the unmodified command string
458 For rcon use when you want to transmit without altering quoting
459 https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
460 ============
461 */
Cmd_Cmd(void)462 char *Cmd_Cmd(void)
463 {
464 return cmd_cmd;
465 }
466
467 /*
468 Replace command separators with space to prevent interpretation
469 This is a hack to protect buggy qvms
470 https://bugzilla.icculus.org/show_bug.cgi?id=3593
471 https://bugzilla.icculus.org/show_bug.cgi?id=4769
472 */
473
474 /*void Cmd_Args_Sanitize( void ) {
475 for ( int i=1; i<cmd_argc; i++ )
476 {
477 char *c = cmd_argv[i];
478
479 if ( strlen( c ) >= MAX_CVAR_VALUE_STRING )
480 c[MAX_CVAR_VALUE_STRING-1] = '\0';
481
482 while ( (c=strpbrk( c, "\n\r;" )) ) {
483 *c = ' ';
484 ++c;
485 }
486 }
487 }*/
488
Cmd_Args_Sanitize(size_t length,const char * strip,const char * repl)489 void Cmd_Args_Sanitize( size_t length, const char *strip, const char *repl )
490 {
491 for ( int i = 1; i < cmd_argc; i++ )
492 {
493 char *c = cmd_argv[i];
494
495 if ( length > 0 && strlen( c ) >= length )
496 c[length - 1] = '\0';
497
498 if ( VALIDSTRING( strip ) && VALIDSTRING( repl ) )
499 Q_strstrip( c, strip, repl );
500 }
501 }
502
503 /*
504 ============
505 Cmd_TokenizeString
506
507 Parses the given string into command line tokens.
508 The text is copied to a seperate buffer and 0 characters
509 are inserted in the appropriate place, The argv array
510 will point into this temporary buffer.
511 ============
512 */
513 // NOTE TTimo define that to track tokenization issues
514 //#define TKN_DBG
Cmd_TokenizeString2(const char * text_in,qboolean ignoreQuotes)515 static void Cmd_TokenizeString2( const char *text_in, qboolean ignoreQuotes ) {
516 const char *text;
517 char *textOut;
518
519 #ifdef TKN_DBG
520 // FIXME TTimo blunt hook to try to find the tokenization of userinfo
521 Com_DPrintf("Cmd_TokenizeString: %s\n", text_in);
522 #endif
523
524 // clear previous args
525 cmd_argc = 0;
526
527 if ( !text_in ) {
528 return;
529 }
530
531 Q_strncpyz( cmd_cmd, text_in, sizeof(cmd_cmd) );
532
533 text = text_in;
534 textOut = cmd_tokenized;
535
536 while ( 1 ) {
537 if ( cmd_argc == MAX_STRING_TOKENS ) {
538 return; // this is usually something malicious
539 }
540
541 while ( 1 ) {
542 // skip whitespace
543 while ( *text && *(const unsigned char* /*eurofix*/)text <= ' ' ) {
544 text++;
545 }
546 if ( !*text ) {
547 return; // all tokens parsed
548 }
549
550 // skip // comments
551 if ( text[0] == '/' && text[1] == '/' ) {
552 return; // all tokens parsed
553 }
554
555 // skip /* */ comments
556 if ( text[0] == '/' && text[1] =='*' ) {
557 while ( *text && ( text[0] != '*' || text[1] != '/' ) ) {
558 text++;
559 }
560 if ( !*text ) {
561 return; // all tokens parsed
562 }
563 text += 2;
564 } else {
565 break; // we are ready to parse a token
566 }
567 }
568
569 // handle quoted strings
570 // NOTE TTimo this doesn't handle \" escaping
571 if ( !ignoreQuotes && *text == '"' ) {
572 cmd_argv[cmd_argc] = textOut;
573 cmd_argc++;
574 text++;
575 while ( *text && *text != '"' ) {
576 *textOut++ = *text++;
577 }
578 *textOut++ = 0;
579 if ( !*text ) {
580 return; // all tokens parsed
581 }
582 text++;
583 continue;
584 }
585
586 // regular token
587 cmd_argv[cmd_argc] = textOut;
588 cmd_argc++;
589
590 // skip until whitespace, quote, or command
591 while ( *(const unsigned char* /*eurofix*/)text > ' ' ) {
592 if ( !ignoreQuotes && text[0] == '"' ) {
593 break;
594 }
595
596 if ( text[0] == '/' && text[1] == '/' ) {
597 break;
598 }
599
600 // skip /* */ comments
601 if ( text[0] == '/' && text[1] =='*' ) {
602 break;
603 }
604
605 *textOut++ = *text++;
606 }
607
608 *textOut++ = 0;
609
610 if ( !*text ) {
611 return; // all tokens parsed
612 }
613 }
614
615 }
616
617 /*
618 ============
619 Cmd_TokenizeString
620 ============
621 */
Cmd_TokenizeString(const char * text_in)622 void Cmd_TokenizeString( const char *text_in ) {
623 Cmd_TokenizeString2( text_in, qfalse );
624 }
625
626 /*
627 ============
628 Cmd_TokenizeStringIgnoreQuotes
629 ============
630 */
Cmd_TokenizeStringIgnoreQuotes(const char * text_in)631 void Cmd_TokenizeStringIgnoreQuotes( const char *text_in ) {
632 Cmd_TokenizeString2( text_in, qtrue );
633 }
634
635 /*
636 ============
637 Cmd_FindCommand
638 ============
639 */
Cmd_FindCommand(const char * cmd_name)640 cmd_function_t *Cmd_FindCommand( const char *cmd_name )
641 {
642 cmd_function_t *cmd;
643 for( cmd = cmd_functions; cmd; cmd = cmd->next )
644 if( !Q_stricmp( cmd_name, cmd->name ) )
645 return cmd;
646 return NULL;
647 }
648
649 /*
650 ============
651 Cmd_AddCommand
652 ============
653 */
Cmd_AddCommand(const char * cmd_name,xcommand_t function,const char * cmd_desc)654 void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc ) {
655 cmd_function_t *cmd;
656
657 // fail if the command already exists
658 if( Cmd_FindCommand( cmd_name ) )
659 {
660 // allow completion-only commands to be silently doubled
661 if ( function != NULL ) {
662 Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
663 }
664 return;
665 }
666
667 // use a small malloc to avoid zone fragmentation
668 cmd = (struct cmd_function_s *)S_Malloc (sizeof(cmd_function_t));
669 cmd->name = CopyString( cmd_name );
670 if ( VALIDSTRING( cmd_desc ) )
671 cmd->description = CopyString( cmd_desc );
672 else
673 cmd->description = NULL;
674 cmd->function = function;
675 cmd->complete = NULL;
676 cmd->next = cmd_functions;
677 cmd_functions = cmd;
678 }
679
Cmd_AddCommandList(const cmdList_t * cmdList)680 void Cmd_AddCommandList( const cmdList_t *cmdList )
681 {
682 for ( const cmdList_t *cmd = cmdList; cmd && cmd->name; cmd++ )
683 {
684 Cmd_AddCommand( cmd->name, cmd->func, cmd->description );
685 if ( cmd->complete )
686 Cmd_SetCommandCompletionFunc( cmd->name, cmd->complete );
687 }
688 }
689
Cmd_RemoveCommandList(const cmdList_t * cmdList)690 void Cmd_RemoveCommandList( const cmdList_t *cmdList )
691 {
692 for ( const cmdList_t *cmd = cmdList; cmd && cmd->name; cmd++ )
693 {
694 Cmd_RemoveCommand( cmd->name );
695 }
696 }
697
698 /*
699 ============
700 Cmd_SetCommandCompletionFunc
701 ============
702 */
Cmd_SetCommandCompletionFunc(const char * command,completionFunc_t complete)703 void Cmd_SetCommandCompletionFunc( const char *command, completionFunc_t complete ) {
704 for ( cmd_function_t *cmd=cmd_functions; cmd; cmd=cmd->next ) {
705 if ( !Q_stricmp( command, cmd->name ) )
706 cmd->complete = complete;
707 }
708 }
709
710 /*
711 ============
712 Cmd_RemoveCommand
713 ============
714 */
Cmd_RemoveCommand(const char * cmd_name)715 void Cmd_RemoveCommand( const char *cmd_name ) {
716 cmd_function_t *cmd, **back;
717
718 back = &cmd_functions;
719 while( 1 ) {
720 cmd = *back;
721 if ( !cmd ) {
722 // command wasn't active
723 return;
724 }
725 if ( !strcmp( cmd_name, cmd->name ) ) {
726 *back = cmd->next;
727 Z_Free(cmd->name);
728 Z_Free(cmd->description);
729 Z_Free (cmd);
730 return;
731 }
732 back = &cmd->next;
733 }
734 }
735
736 /*
737 ============
738 Cmd_VM_RemoveCommand
739
740 Only remove commands with no associated function
741 ============
742 */
Cmd_VM_RemoveCommand(const char * cmd_name,vmSlots_t vmslot)743 void Cmd_VM_RemoveCommand( const char *cmd_name, vmSlots_t vmslot ) {
744 cmd_function_t *cmd = Cmd_FindCommand( cmd_name );
745
746 if( !cmd )
747 return;
748
749 if( cmd->function )
750 {
751 Com_Printf( "%s tried to remove system command \"%s\"", vmStrs[vmslot], cmd_name );
752 return;
753 }
754
755 Cmd_RemoveCommand( cmd_name );
756 }
757
758 /*
759 ============
760 Cmd_DescriptionString
761 ============
762 */
Cmd_DescriptionString(const char * cmd_name)763 char *Cmd_DescriptionString( const char *cmd_name )
764 {
765 const cmd_function_t *cmd = Cmd_FindCommand( cmd_name );
766
767 if ( !cmd || !VALIDSTRING( cmd->description ) )
768 return "";
769 return cmd->description;
770 }
771
772 /*
773 ============
774 Cmd_Print
775
776 ============
777 */
Cmd_Print(const cmd_function_t * cmd)778 void Cmd_Print( const cmd_function_t *cmd )
779 {
780 Com_Printf( S_COLOR_GREY "Cmd " S_COLOR_WHITE "%s", cmd->name );
781
782 if ( VALIDSTRING( cmd->description ) )
783 {
784 Com_Printf( S_COLOR_GREEN " - %s" S_COLOR_WHITE, cmd->description );
785 }
786
787 Com_Printf( "\n" );
788 }
789
790 /*
791 ============
792 Cmd_CommandCompletion
793 ============
794 */
Cmd_CommandCompletion(callbackFunc_t callback)795 void Cmd_CommandCompletion( callbackFunc_t callback ) {
796 cmd_function_t *cmd;
797
798 for (cmd=cmd_functions ; cmd ; cmd=cmd->next) {
799 callback( cmd->name );
800 }
801 }
802
803 /*
804 ============
805 Cmd_CompleteArgument
806 ============
807 */
Cmd_CompleteArgument(const char * command,char * args,int argNum)808 void Cmd_CompleteArgument( const char *command, char *args, int argNum ) {
809 for ( cmd_function_t *cmd=cmd_functions; cmd; cmd=cmd->next ) {
810 if ( !Q_stricmp( command, cmd->name ) && cmd->complete )
811 cmd->complete( args, argNum );
812 }
813 }
814
815 /*
816 ============
817 Cmd_ExecuteString
818
819 A complete command line has been parsed, so try to execute it
820 ============
821 */
Cmd_ExecuteString(const char * text)822 void Cmd_ExecuteString( const char *text ) {
823 cmd_function_t *cmd, **prev;
824
825 // execute the command line
826 Cmd_TokenizeString( text );
827 if ( !Cmd_Argc() ) {
828 return; // no tokens
829 }
830
831 // check registered command functions
832 for ( prev = &cmd_functions ; *prev ; prev = &cmd->next ) {
833 cmd = *prev;
834 if ( !Q_stricmp( Cmd_Argv(0), cmd->name ) ) {
835 // rearrange the links so that the command will be
836 // near the head of the list next time it is used
837 *prev = cmd->next;
838 cmd->next = cmd_functions;
839 cmd_functions = cmd;
840
841 // perform the action
842 if ( !cmd->function ) {
843 // let the cgame or game handle it
844 break;
845 } else {
846 cmd->function ();
847 }
848 return;
849 }
850 }
851
852 // check cvars
853 if ( Cvar_Command() ) {
854 return;
855 }
856
857 // check client game commands
858 if ( com_cl_running && com_cl_running->integer && CL_GameCommand() ) {
859 return;
860 }
861
862 // check server game commands
863 if ( com_sv_running && com_sv_running->integer && SV_GameCommand() ) {
864 return;
865 }
866
867 // check ui commands
868 if ( com_cl_running && com_cl_running->integer && UI_GameCommand() ) {
869 return;
870 }
871
872 // send it as a server command if we are connected
873 // this will usually result in a chat message
874 //CL_ForwardCommandToServer ( text );
875 CL_ForwardCommandToServer ( text );
876 }
877
878 typedef std::vector<const cmd_function_t *> CmdFuncVector;
879
CmdSort(const cmd_function_t * cmd1,const cmd_function_t * cmd2)880 bool CmdSort( const cmd_function_t *cmd1, const cmd_function_t *cmd2 )
881 {
882 return Q_stricmp( cmd1->name, cmd2->name ) < 0;
883 }
884
885 /*
886 ============
887 Cmd_List_f
888 ============
889 */
Cmd_List_f(void)890 static void Cmd_List_f (void)
891 {
892 const cmd_function_t *cmd = NULL;
893 int i, j;
894 char *match = NULL;
895 CmdFuncVector cmds;
896
897 if ( Cmd_Argc() > 1 ) {
898 match = Cmd_Argv( 1 );
899 }
900
901 for ( cmd=cmd_functions, i=0, j=0;
902 cmd;
903 cmd=cmd->next, i++ )
904 {
905 if ( !cmd->name || (match && !Com_Filter( match, cmd->name, qfalse )) )
906 continue;
907
908 cmds.push_back( cmd );
909 j++;
910 }
911
912 // sort list alphabetically
913 std::sort( cmds.begin(), cmds.end(), CmdSort );
914
915 CmdFuncVector::const_iterator itr;
916 for ( itr = cmds.begin();
917 itr != cmds.end();
918 ++itr )
919 {
920 cmd = (*itr);
921 if ( VALIDSTRING( cmd->description ) )
922 Com_Printf( " %s" S_COLOR_GREEN " - %s" S_COLOR_WHITE "\n", cmd->name, cmd->description );
923 else
924 Com_Printf( " %s\n", cmd->name );
925 }
926
927 Com_Printf ("\n%i total commands\n", i);
928 if ( i != j )
929 Com_Printf( "%i matching commands\n", j );
930 }
931
Cmd_PrintHelp_f(void)932 static void Cmd_PrintHelp_f( void )
933 {
934 if ( Cmd_Argc() != 2 )
935 {
936 Com_Printf( "usage: help <command or alias>\n" );
937 return;
938 }
939
940 const char *name = Cmd_Argv( 1 );
941 const cmd_function_t *cmd = Cmd_FindCommand( name );
942
943 if ( cmd )
944 Cmd_Print( cmd );
945 else
946 Com_Printf( "Command %s does not exist.\n", name );
947 }
948
949 /*
950 ==================
951 Cmd_CompleteCmdName
952 ==================
953 */
Cmd_CompleteCmdName(char * args,int argNum)954 void Cmd_CompleteCmdName( char *args, int argNum )
955 {
956 if ( argNum == 2 )
957 {
958 // Skip "<cmd> "
959 char *p = Com_SkipTokens( args, 1, " " );
960
961 if ( p > args )
962 Field_CompleteCommand( p, qtrue, qfalse );
963 }
964 }
965
966 /*
967 ==================
968 Cmd_CompleteCfgName
969 ==================
970 */
Cmd_CompleteCfgName(char * args,int argNum)971 void Cmd_CompleteCfgName( char *args, int argNum ) {
972 if( argNum == 2 ) {
973 Field_CompleteFilename( "", "cfg", qfalse, qtrue );
974 }
975 }
976
977 /*
978 ============
979 Cmd_Init
980 ============
981 */
Cmd_Init(void)982 void Cmd_Init (void) {
983 Cmd_AddCommand( "cmdlist", Cmd_List_f, "List all commands to console" );
984 Cmd_AddCommand( "help", Cmd_PrintHelp_f, "Print command help" );
985 Cmd_SetCommandCompletionFunc( "help", Cmd_CompleteCmdName );
986 Cmd_AddCommand( "echo", Cmd_Echo_f, "Print message to console" );
987 Cmd_AddCommand( "exec", Cmd_Exec_f, "Execute a script file" );
988 Cmd_AddCommand( "execq", Cmd_Exec_f, "Execute a script file without displaying a message" );
989 Cmd_SetCommandCompletionFunc( "exec", Cmd_CompleteCfgName );
990 Cmd_SetCommandCompletionFunc( "execq", Cmd_CompleteCfgName );
991 Cmd_AddCommand( "vstr", Cmd_Vstr_f, "Execute the value of a cvar" );
992 Cmd_SetCommandCompletionFunc( "vstr", Cvar_CompleteCvarName );
993 Cmd_AddCommand( "wait", Cmd_Wait_f, "Pause command buffer execution" );
994 }
995
996