1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "sys/platform.h"
30 #include "idlib/containers/StrList.h"
31 #include "framework/Common.h"
32 #include "framework/FileSystem.h"
33 #include "framework/DeclManager.h"
34 #include "framework/Session.h"
35 
36 #include "framework/CmdSystem.h"
37 
38 /*
39 ===============================================================================
40 
41 	idCmdSystemLocal
42 
43 ===============================================================================
44 */
45 
46 typedef struct commandDef_s {
47 	struct commandDef_s *	next;
48 	char *					name;
49 	cmdFunction_t			function;
50 	argCompletion_t			argCompletion;
51 	int						flags;
52 	char *					description;
53 } commandDef_t;
54 
55 
56 class idCmdSystemLocal : public idCmdSystem {
57 public:
58 	virtual void			Init( void );
59 	virtual void			Shutdown( void );
60 
61 	virtual void			AddCommand( const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion = NULL );
62 	virtual void			RemoveCommand( const char *cmdName );
63 	virtual void			RemoveFlaggedCommands( int flags );
64 
65 	virtual void			CommandCompletion( void(*callback)( const char *s ) );
66 	virtual void			ArgCompletion( const char *cmdString, void(*callback)( const char *s ) );
67 
68 	virtual void			BufferCommandText( cmdExecution_t exec, const char *text );
69 	virtual void			ExecuteCommandBuffer( void );
70 
71 	virtual void			ArgCompletion_FolderExtension( const idCmdArgs &args, void(*callback)( const char *s ), const char *folder, bool stripFolder, ... );
72 	virtual void			ArgCompletion_DeclName( const idCmdArgs &args, void(*callback)( const char *s ), int type );
73 
74 	virtual void			BufferCommandArgs( cmdExecution_t exec, const idCmdArgs &args );
75 
76 	virtual void			SetupReloadEngine( const idCmdArgs &args );
77 	virtual bool			PostReloadEngine( void );
78 
SetWait(int numFrames)79 	void					SetWait( int numFrames ) { wait = numFrames; }
GetCommands(void) const80 	commandDef_t *			GetCommands( void ) const { return commands; }
81 
82 private:
83 	static const int		MAX_CMD_BUFFER = 0x10000;
84 
85 	commandDef_t *			commands;
86 
87 	int						wait;
88 	int						textLength;
89 	byte					textBuf[MAX_CMD_BUFFER];
90 
91 	idStr					completionString;
92 	idStrList				completionParms;
93 
94 	// piggybacks on the text buffer, avoids tokenize again and screwing it up
95 	idList<idCmdArgs>		tokenizedCmds;
96 
97 	// a command stored to be executed after a reloadEngine and all associated commands have been processed
98 	idCmdArgs				postReload;
99 
100 private:
101 	void					ExecuteTokenizedString( const idCmdArgs &args );
102 	void					ExecuteCommandText( const char *text );
103 	void					InsertCommandText( const char *text );
104 	void					AppendCommandText( const char *text );
105 
106 	static void				ListByFlags( const idCmdArgs &args, cmdFlags_t flags );
107 	static void				List_f( const idCmdArgs &args );
108 	static void				SystemList_f( const idCmdArgs &args );
109 	static void				RendererList_f( const idCmdArgs &args );
110 	static void				SoundList_f( const idCmdArgs &args );
111 	static void				GameList_f( const idCmdArgs &args );
112 	static void				ToolList_f( const idCmdArgs &args );
113 	static void				Exec_f( const idCmdArgs &args );
114 	static void				Vstr_f( const idCmdArgs &args );
115 	static void				Echo_f( const idCmdArgs &args );
116 	static void				Parse_f( const idCmdArgs &args );
117 	static void				Wait_f( const idCmdArgs &args );
118 	static void				PrintMemInfo_f( const idCmdArgs &args );
119 };
120 
121 idCmdSystemLocal			cmdSystemLocal;
122 idCmdSystem *				cmdSystem = &cmdSystemLocal;
123 
124 
125 /*
126 ============
127 idCmdSystemLocal::ListByFlags
128 ============
129 */
130 // NOTE: the const wonkyness is required to make msvc happy
131 template<>
idListSortCompare(const commandDef_t * const * a,const commandDef_t * const * b)132 ID_INLINE int idListSortCompare( const commandDef_t * const *a, const commandDef_t * const *b ) {
133 	return idStr::Icmp( (*a)->name, (*b)->name );
134 }
135 
ListByFlags(const idCmdArgs & args,cmdFlags_t flags)136 void idCmdSystemLocal::ListByFlags( const idCmdArgs &args, cmdFlags_t flags ) {
137 	int i;
138 	idStr match;
139 	const commandDef_t *cmd;
140 	idList<const commandDef_t *> cmdList;
141 
142 	if ( args.Argc() > 1 ) {
143 		match = args.Args( 1, -1 );
144 		match.Replace( " ", "" );
145 	} else {
146 		match = "";
147 	}
148 
149 	for ( cmd = cmdSystemLocal.GetCommands(); cmd; cmd = cmd->next ) {
150 		if ( !( cmd->flags & flags ) ) {
151 			continue;
152 		}
153 		if ( match.Length() && idStr( cmd->name ).Filter( match, false ) == 0 ) {
154 			continue;
155 		}
156 
157 		cmdList.Append( cmd );
158 	}
159 
160 	cmdList.Sort();
161 
162 	for ( i = 0; i < cmdList.Num(); i++ ) {
163 		cmd = cmdList[i];
164 
165 		common->Printf( "  %-21s %s\n", cmd->name, cmd->description );
166 	}
167 
168 	common->Printf( "%i commands\n", cmdList.Num() );
169 }
170 
171 /*
172 ============
173 idCmdSystemLocal::List_f
174 ============
175 */
List_f(const idCmdArgs & args)176 void idCmdSystemLocal::List_f( const idCmdArgs &args ) {
177 	idCmdSystemLocal::ListByFlags( args, CMD_FL_ALL );
178 }
179 
180 /*
181 ============
182 idCmdSystemLocal::SystemList_f
183 ============
184 */
SystemList_f(const idCmdArgs & args)185 void idCmdSystemLocal::SystemList_f( const idCmdArgs &args ) {
186 	idCmdSystemLocal::ListByFlags( args, CMD_FL_SYSTEM );
187 }
188 
189 /*
190 ============
191 idCmdSystemLocal::RendererList_f
192 ============
193 */
RendererList_f(const idCmdArgs & args)194 void idCmdSystemLocal::RendererList_f( const idCmdArgs &args ) {
195 	idCmdSystemLocal::ListByFlags( args, CMD_FL_RENDERER );
196 }
197 
198 /*
199 ============
200 idCmdSystemLocal::SoundList_f
201 ============
202 */
SoundList_f(const idCmdArgs & args)203 void idCmdSystemLocal::SoundList_f( const idCmdArgs &args ) {
204 	idCmdSystemLocal::ListByFlags( args, CMD_FL_SOUND );
205 }
206 
207 /*
208 ============
209 idCmdSystemLocal::GameList_f
210 ============
211 */
GameList_f(const idCmdArgs & args)212 void idCmdSystemLocal::GameList_f( const idCmdArgs &args ) {
213 	idCmdSystemLocal::ListByFlags( args, CMD_FL_GAME );
214 }
215 
216 /*
217 ============
218 idCmdSystemLocal::ToolList_f
219 ============
220 */
ToolList_f(const idCmdArgs & args)221 void idCmdSystemLocal::ToolList_f( const idCmdArgs &args ) {
222 	idCmdSystemLocal::ListByFlags( args, CMD_FL_TOOL );
223 }
224 
225 /*
226 ===============
227 idCmdSystemLocal::Exec_f
228 ===============
229 */
Exec_f(const idCmdArgs & args)230 void idCmdSystemLocal::Exec_f( const idCmdArgs &args ) {
231 	char *	f;
232 	idStr	filename;
233 
234 	if ( args.Argc () != 2 ) {
235 		common->Printf( "exec <filename> : execute a script file\n" );
236 		return;
237 	}
238 
239 	filename = args.Argv(1);
240 	filename.DefaultFileExtension( ".cfg" );
241 	fileSystem->ReadFile( filename, reinterpret_cast<void **>(&f), NULL );
242 	if ( !f ) {
243 		common->Printf( "couldn't exec %s\n", args.Argv(1) );
244 		return;
245 	}
246 	common->Printf( "execing %s\n", args.Argv(1) );
247 
248 	cmdSystemLocal.BufferCommandText( CMD_EXEC_INSERT, f );
249 
250 	fileSystem->FreeFile( f );
251 }
252 
253 /*
254 ===============
255 idCmdSystemLocal::Vstr_f
256 
257 Inserts the current value of a cvar as command text
258 ===============
259 */
Vstr_f(const idCmdArgs & args)260 void idCmdSystemLocal::Vstr_f( const idCmdArgs &args ) {
261 	const char *v;
262 
263 	if ( args.Argc () != 2 ) {
264 		common->Printf( "vstr <variablename> : execute a variable command\n" );
265 		return;
266 	}
267 
268 	v = cvarSystem->GetCVarString( args.Argv( 1 ) );
269 
270 	cmdSystemLocal.BufferCommandText( CMD_EXEC_APPEND, va( "%s\n", v ) );
271 }
272 
273 /*
274 ===============
275 idCmdSystemLocal::Echo_f
276 
277 Just prints the rest of the line to the console
278 ===============
279 */
Echo_f(const idCmdArgs & args)280 void idCmdSystemLocal::Echo_f( const idCmdArgs &args ) {
281 	int		i;
282 
283 	for ( i = 1; i < args.Argc(); i++ ) {
284 		common->Printf( "%s ", args.Argv( i ) );
285 	}
286 	common->Printf( "\n" );
287 }
288 
289 /*
290 ============
291 idCmdSystemLocal::Wait_f
292 
293 Causes execution of the remainder of the command buffer to be delayed until next frame.
294 ============
295 */
Wait_f(const idCmdArgs & args)296 void idCmdSystemLocal::Wait_f( const idCmdArgs &args ) {
297 	if ( args.Argc() == 2 ) {
298 		cmdSystemLocal.SetWait( atoi( args.Argv( 1 ) ) );
299 	} else {
300 		cmdSystemLocal.SetWait( 1 );
301 	}
302 }
303 
304 /*
305 ============
306 idCmdSystemLocal::Parse_f
307 
308 This just prints out how the rest of the line was parsed, as a debugging tool.
309 ============
310 */
Parse_f(const idCmdArgs & args)311 void idCmdSystemLocal::Parse_f( const idCmdArgs &args ) {
312 	int		i;
313 
314 	for ( i = 0; i < args.Argc(); i++ ) {
315 		common->Printf( "%i: %s\n", i, args.Argv(i) );
316 	}
317 }
318 
319 /*
320 ============
321 idCmdSystemLocal::Init
322 ============
323 */
Init(void)324 void idCmdSystemLocal::Init( void ) {
325 
326 	AddCommand( "listCmds", List_f, CMD_FL_SYSTEM, "lists commands" );
327 	AddCommand( "listSystemCmds", SystemList_f, CMD_FL_SYSTEM, "lists system commands" );
328 	AddCommand( "listRendererCmds", RendererList_f, CMD_FL_SYSTEM, "lists renderer commands" );
329 	AddCommand( "listSoundCmds", SoundList_f, CMD_FL_SYSTEM, "lists sound commands" );
330 	AddCommand( "listGameCmds", GameList_f, CMD_FL_SYSTEM, "lists game commands" );
331 	AddCommand( "listToolCmds", ToolList_f, CMD_FL_SYSTEM, "lists tool commands" );
332 	AddCommand( "exec", Exec_f, CMD_FL_SYSTEM, "executes a config file", ArgCompletion_ConfigName );
333 	AddCommand( "vstr", Vstr_f, CMD_FL_SYSTEM, "inserts the current value of a cvar as command text" );
334 	AddCommand( "echo", Echo_f, CMD_FL_SYSTEM, "prints text" );
335 	AddCommand( "parse", Parse_f, CMD_FL_SYSTEM, "prints tokenized string" );
336 	AddCommand( "wait", Wait_f, CMD_FL_SYSTEM, "delays remaining buffered commands one or more frames" );
337 
338 	completionString = "*";
339 
340 	textLength = 0;
341 }
342 
343 /*
344 ============
345 idCmdSystemLocal::Shutdown
346 ============
347 */
Shutdown(void)348 void idCmdSystemLocal::Shutdown( void ) {
349 	commandDef_t *cmd;
350 
351 	for ( cmd = commands; cmd; cmd = commands ) {
352 		commands = commands->next;
353 		Mem_Free( cmd->name );
354 		Mem_Free( cmd->description );
355 		delete cmd;
356 	}
357 
358 	completionString.Clear();
359 	completionParms.Clear();
360 	tokenizedCmds.Clear();
361 	postReload.Clear();
362 }
363 
364 /*
365 ============
366 idCmdSystemLocal::AddCommand
367 ============
368 */
AddCommand(const char * cmdName,cmdFunction_t function,int flags,const char * description,argCompletion_t argCompletion)369 void idCmdSystemLocal::AddCommand( const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion ) {
370 	commandDef_t *cmd;
371 
372 	// fail if the command already exists
373 	for ( cmd = commands; cmd; cmd = cmd->next ) {
374 		if ( idStr::Cmp( cmdName, cmd->name ) == 0 ) {
375 			if ( function != cmd->function ) {
376 				common->Printf( "idCmdSystemLocal::AddCommand: %s already defined\n", cmdName );
377 			}
378 			return;
379 		}
380 	}
381 
382 	cmd = new commandDef_t;
383 	cmd->name = Mem_CopyString( cmdName );
384 	cmd->function = function;
385 	cmd->argCompletion = argCompletion;
386 	cmd->flags = flags;
387 	cmd->description = Mem_CopyString( description );
388 	cmd->next = commands;
389 	commands = cmd;
390 }
391 
392 /*
393 ============
394 idCmdSystemLocal::RemoveCommand
395 ============
396 */
RemoveCommand(const char * cmdName)397 void idCmdSystemLocal::RemoveCommand( const char *cmdName ) {
398 	commandDef_t *cmd, **last;
399 
400 	for ( last = &commands, cmd = *last; cmd; cmd = *last ) {
401 		if ( idStr::Cmp( cmdName, cmd->name ) == 0 ) {
402 			*last = cmd->next;
403 			Mem_Free( cmd->name );
404 			Mem_Free( cmd->description );
405 			delete cmd;
406 			return;
407 		}
408 		last = &cmd->next;
409 	}
410 }
411 
412 /*
413 ============
414 idCmdSystemLocal::RemoveFlaggedCommands
415 ============
416 */
RemoveFlaggedCommands(int flags)417 void idCmdSystemLocal::RemoveFlaggedCommands( int flags ) {
418 	commandDef_t *cmd, **last;
419 
420 	for ( last = &commands, cmd = *last; cmd; cmd = *last ) {
421 		if ( cmd->flags & flags ) {
422 			*last = cmd->next;
423 			Mem_Free( cmd->name );
424 			Mem_Free( cmd->description );
425 			delete cmd;
426 			continue;
427 		}
428 		last = &cmd->next;
429 	}
430 }
431 
432 /*
433 ============
434 idCmdSystemLocal::CommandCompletion
435 ============
436 */
CommandCompletion(void (* callback)(const char * s))437 void idCmdSystemLocal::CommandCompletion( void(*callback)( const char *s ) ) {
438 	commandDef_t *cmd;
439 
440 	for ( cmd = commands; cmd; cmd = cmd->next ) {
441 		callback( cmd->name );
442 	}
443 }
444 
445 /*
446 ============
447 idCmdSystemLocal::ArgCompletion
448 ============
449 */
ArgCompletion(const char * cmdString,void (* callback)(const char * s))450 void idCmdSystemLocal::ArgCompletion( const char *cmdString, void(*callback)( const char *s ) ) {
451 	commandDef_t *cmd;
452 	idCmdArgs args;
453 
454 	args.TokenizeString( cmdString, false );
455 
456 	for ( cmd = commands; cmd; cmd = cmd->next ) {
457 		if ( !cmd->argCompletion ) {
458 			continue;
459 		}
460 		if ( idStr::Icmp( args.Argv( 0 ), cmd->name ) == 0 ) {
461 			cmd->argCompletion( args, callback );
462 			break;
463 		}
464 	}
465 }
466 
467 /*
468 ============
469 idCmdSystemLocal::ExecuteTokenizedString
470 ============
471 */
ExecuteTokenizedString(const idCmdArgs & args)472 void idCmdSystemLocal::ExecuteTokenizedString( const idCmdArgs &args ) {
473 	commandDef_t *cmd, **prev;
474 
475 	// execute the command line
476 	if ( !args.Argc() ) {
477 		return;		// no tokens
478 	}
479 
480 	// check registered command functions
481 	for ( prev = &commands; *prev; prev = &cmd->next ) {
482 		cmd = *prev;
483 		if ( idStr::Icmp( args.Argv( 0 ), cmd->name ) == 0 ) {
484 			// rearrange the links so that the command will be
485 			// near the head of the list next time it is used
486 			*prev = cmd->next;
487 			cmd->next = commands;
488 			commands = cmd;
489 
490 			if ( ( cmd->flags & (CMD_FL_CHEAT|CMD_FL_TOOL) ) && session && session->IsMultiplayer() && !cvarSystem->GetCVarBool( "net_allowCheats" ) ) {
491 				common->Printf( "Command '%s' not valid in multiplayer mode.\n", cmd->name );
492 				return;
493 			}
494 			// perform the action
495 			if ( !cmd->function ) {
496 				break;
497 			} else {
498 				cmd->function( args );
499 			}
500 			return;
501 		}
502 	}
503 
504 	// check cvars
505 	if ( cvarSystem->Command( args ) ) {
506 		return;
507 	}
508 
509 	common->Printf( "Unknown command '%s'\n", args.Argv( 0 ) );
510 }
511 
512 /*
513 ============
514 idCmdSystemLocal::ExecuteCommandText
515 
516 Tokenizes, then executes.
517 ============
518 */
ExecuteCommandText(const char * text)519 void idCmdSystemLocal::ExecuteCommandText( const char *text ) {
520 	ExecuteTokenizedString( idCmdArgs( text, false ) );
521 }
522 
523 /*
524 ============
525 idCmdSystemLocal::InsertCommandText
526 
527 Adds command text immediately after the current command
528 Adds a \n to the text
529 ============
530 */
InsertCommandText(const char * text)531 void idCmdSystemLocal::InsertCommandText( const char *text ) {
532 	int		len;
533 	int		i;
534 
535 	len = strlen( text ) + 1;
536 	if ( len + textLength > (int)sizeof( textBuf ) ) {
537 		common->Printf( "idCmdSystemLocal::InsertText: buffer overflow\n" );
538 		return;
539 	}
540 
541 	// move the existing command text
542 	for ( i = textLength - 1; i >= 0; i-- ) {
543 		textBuf[ i + len ] = textBuf[ i ];
544 	}
545 
546 	// copy the new text in
547 	memcpy( textBuf, text, len - 1 );
548 
549 	// add a \n
550 	textBuf[ len - 1 ] = '\n';
551 
552 	textLength += len;
553 }
554 
555 /*
556 ============
557 idCmdSystemLocal::AppendCommandText
558 
559 Adds command text at the end of the buffer, does NOT add a final \n
560 ============
561 */
AppendCommandText(const char * text)562 void idCmdSystemLocal::AppendCommandText( const char *text ) {
563 	int l;
564 
565 	l = strlen( text );
566 
567 	if ( textLength + l >= (int)sizeof( textBuf ) ) {
568 		common->Printf( "idCmdSystemLocal::AppendText: buffer overflow\n" );
569 		return;
570 	}
571 	memcpy( textBuf + textLength, text, l );
572 	textLength += l;
573 }
574 
575 /*
576 ============
577 idCmdSystemLocal::BufferCommandText
578 ============
579 */
BufferCommandText(cmdExecution_t exec,const char * text)580 void idCmdSystemLocal::BufferCommandText( cmdExecution_t exec, const char *text ) {
581 	switch( exec ) {
582 		case CMD_EXEC_NOW: {
583 			ExecuteCommandText( text );
584 			break;
585 		}
586 		case CMD_EXEC_INSERT: {
587 			InsertCommandText( text );
588 			break;
589 		}
590 		case CMD_EXEC_APPEND: {
591 			AppendCommandText( text );
592 			break;
593 		}
594 		default: {
595 			common->FatalError( "idCmdSystemLocal::BufferCommandText: bad exec type" );
596 		}
597 	}
598 }
599 
600 /*
601 ============
602 idCmdSystemLocal::BufferCommandArgs
603 ============
604 */
BufferCommandArgs(cmdExecution_t exec,const idCmdArgs & args)605 void idCmdSystemLocal::BufferCommandArgs( cmdExecution_t exec, const idCmdArgs &args ) {
606 	switch ( exec ) {
607 		case CMD_EXEC_NOW: {
608 			ExecuteTokenizedString( args );
609 			break;
610 		}
611 		case CMD_EXEC_APPEND: {
612 			AppendCommandText( "_execTokenized\n" );
613 			tokenizedCmds.Append( args );
614 			break;
615 		}
616 		default: {
617 			common->FatalError( "idCmdSystemLocal::BufferCommandArgs: bad exec type" );
618 		}
619 	}
620 }
621 
622 /*
623 ============
624 idCmdSystemLocal::ExecuteCommandBuffer
625 ============
626 */
ExecuteCommandBuffer(void)627 void idCmdSystemLocal::ExecuteCommandBuffer( void ) {
628 	int			i;
629 	char *		text;
630 	int			quotes;
631 	idCmdArgs	args;
632 
633 	while( textLength ) {
634 
635 		if ( wait )	{
636 			// skip out while text still remains in buffer, leaving it for next frame
637 			wait--;
638 			break;
639 		}
640 
641 		// find a \n or ; line break
642 		text = (char *)textBuf;
643 
644 		quotes = 0;
645 		for ( i = 0; i < textLength; i++ ) {
646 			if ( text[i] == '"' ) {
647 				quotes++;
648 			}
649 			if ( !( quotes & 1 ) &&  text[i] == ';' ) {
650 				break;	// don't break if inside a quoted string
651 			}
652 			if ( text[i] == '\n' || text[i] == '\r' ) {
653 				break;
654 			}
655 		}
656 
657 		text[i] = 0;
658 
659 		if ( !idStr::Cmp( text, "_execTokenized" ) ) {
660 			args = tokenizedCmds[ 0 ];
661 			tokenizedCmds.RemoveIndex( 0 );
662 		} else {
663 			args.TokenizeString( text, false );
664 		}
665 
666 		// delete the text from the command buffer and move remaining commands down
667 		// this is necessary because commands (exec) can insert data at the
668 		// beginning of the text buffer
669 
670 		if ( i == textLength ) {
671 			textLength = 0;
672 		} else {
673 			i++;
674 			textLength -= i;
675 			memmove( text, text+i, textLength );
676 		}
677 
678 		// execute the command line that we have already tokenized
679 		ExecuteTokenizedString( args );
680 	}
681 }
682 
683 /*
684 ============
685 idCmdSystemLocal::ArgCompletion_FolderExtension
686 ============
687 */
ArgCompletion_FolderExtension(const idCmdArgs & args,void (* callback)(const char * s),const char * folder,bool stripFolder,...)688 void idCmdSystemLocal::ArgCompletion_FolderExtension( const idCmdArgs &args, void(*callback)( const char *s ), const char *folder, bool stripFolder, ... ) {
689 	int i;
690 	idStr string;
691 	const char *extension;
692 	va_list argPtr;
693 
694 	string = args.Argv( 0 );
695 	string += " ";
696 	string += args.Argv( 1 );
697 
698 	if ( string.Icmp( completionString ) != 0 ) {
699 		idStr parm, path;
700 		idFileList *names;
701 
702 		completionString = string;
703 		completionParms.Clear();
704 
705 		parm = args.Argv( 1 );
706 		parm.ExtractFilePath( path );
707 		if ( stripFolder || path.Length() == 0 ) {
708 			path = folder + path;
709 		}
710 		path.StripTrailing( '/' );
711 
712 		// list folders
713 		names = fileSystem->ListFiles( path, "/", true, true );
714 		for ( i = 0; i < names->GetNumFiles(); i++ ) {
715 			idStr name = names->GetFile( i );
716 			if ( stripFolder ) {
717 				name.Strip( folder );
718 			} else {
719 				name.Strip( "/" );
720 			}
721 			name = args.Argv( 0 ) + ( " " + name ) + "/";
722 			completionParms.Append( name );
723 		}
724 		fileSystem->FreeFileList( names );
725 
726 		// list files
727 		va_start( argPtr, stripFolder );
728 		for ( extension = va_arg( argPtr, const char * ); extension; extension = va_arg( argPtr, const char * ) ) {
729 			names = fileSystem->ListFiles( path, extension, true, true );
730 			for ( i = 0; i < names->GetNumFiles(); i++ ) {
731 				idStr name = names->GetFile( i );
732 				if ( stripFolder ) {
733 					name.Strip( folder );
734 				} else {
735 					name.Strip( "/" );
736 				}
737 				name = args.Argv( 0 ) + ( " " + name );
738 				completionParms.Append( name );
739 			}
740 			fileSystem->FreeFileList( names );
741 		}
742 		va_end( argPtr );
743 	}
744 	for ( i = 0; i < completionParms.Num(); i++ ) {
745 		callback( completionParms[i] );
746 	}
747 }
748 
749 /*
750 ============
751 idCmdSystemLocal::ArgCompletion_DeclName
752 ============
753 */
ArgCompletion_DeclName(const idCmdArgs & args,void (* callback)(const char * s),int type)754 void idCmdSystemLocal::ArgCompletion_DeclName( const idCmdArgs &args, void(*callback)( const char *s ), int type ) {
755 	int i, num;
756 
757 	if ( declManager == NULL ) {
758 		return;
759 	}
760 	num = declManager->GetNumDecls( (declType_t)type );
761 	for ( i = 0; i < num; i++ ) {
762 		callback( idStr( args.Argv( 0 ) ) + " " + declManager->DeclByIndex( (declType_t)type, i , false )->GetName() );
763 	}
764 }
765 
766 /*
767 ============
768 idCmdSystemLocal::SetupReloadEngine
769 ============
770 */
SetupReloadEngine(const idCmdArgs & args)771 void idCmdSystemLocal::SetupReloadEngine( const idCmdArgs &args ) {
772 	BufferCommandText( CMD_EXEC_APPEND, "reloadEngine\n" );
773 	postReload = args;
774 }
775 
776 /*
777 ============
778 idCmdSystemLocal::PostReloadEngine
779 ============
780 */
PostReloadEngine(void)781 bool idCmdSystemLocal::PostReloadEngine( void ) {
782 	if ( !postReload.Argc() ) {
783 		return false;
784 	}
785 	BufferCommandArgs( CMD_EXEC_APPEND, postReload );
786 	postReload.Clear();
787 	return true;
788 }
789