1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // cmd.c -- Quake script command processing module
21 
22 #include "qcommon.h"
23 
24 void Cmd_ForwardToServer (void);
25 
26 #define	MAX_ALIAS_NAME	32
27 
28 typedef struct cmdalias_s
29 {
30 	struct cmdalias_s	*next;
31 	char	name[MAX_ALIAS_NAME];
32 	char	*value;
33 } cmdalias_t;
34 
35 cmdalias_t	*cmd_alias;
36 
37 qboolean	cmd_wait;
38 
39 #define	ALIAS_LOOP_COUNT	16
40 int		alias_count;		// for detecting runaway loops
41 
42 
43 //=============================================================================
44 
45 /*
46 ============
47 Cmd_Wait_f
48 
49 Causes execution of the remainder of the command buffer to be delayed until
50 next frame.  This allows commands like:
51 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
52 ============
53 */
Cmd_Wait_f(void)54 void Cmd_Wait_f (void)
55 {
56 	cmd_wait = true;
57 }
58 
59 
60 /*
61 =============================================================================
62 
63 						COMMAND BUFFER
64 
65 =============================================================================
66 */
67 
68 sizebuf_t	cmd_text;
69 
70 byte		cmd_text_buf[32768]; //8192
71 byte		defer_text_buf[32768]; //8192
72 
73 /*
74 ============
75 Cbuf_Init
76 ============
77 */
Cbuf_Init(void)78 void Cbuf_Init (void)
79 {
80 	SZ_Init (&cmd_text, cmd_text_buf, sizeof(cmd_text_buf));
81 }
82 
83 /*
84 ============
85 Cbuf_AddText
86 
87 Adds command text at the end of the buffer
88 ============
89 */
Cbuf_AddText(char * text)90 void Cbuf_AddText (char *text)
91 {
92 	int		l;
93 
94 	l = strlen (text);
95 
96 	if (cmd_text.cursize + l >= cmd_text.maxsize)
97 	{
98 		Com_Printf ("Cbuf_AddText: overflow\n");
99 		return;
100 	}
101 	SZ_Write (&cmd_text, text, strlen (text));
102 }
103 
104 
105 /*
106 ============
107 Cbuf_InsertText
108 
109 Adds command text immediately after the current command
110 Adds a \n to the text
111 FIXME: actually change the command buffer to do less copying
112 ============
113 */
Cbuf_InsertText(char * text)114 void Cbuf_InsertText (char *text)
115 {
116 	char	*temp;
117 	int		templen;
118 
119 // copy off any commands still remaining in the exec buffer
120 	templen = cmd_text.cursize;
121 	if (templen)
122 	{
123 		temp = Z_Malloc (templen);
124 		memcpy (temp, cmd_text.data, templen);
125 		SZ_Clear (&cmd_text);
126 	}
127 	else
128 		temp = NULL;	// shut up compiler
129 
130 // add the entire text of the file
131 	Cbuf_AddText (text);
132 
133 // add the copied off data
134 	if (templen)
135 	{
136 		SZ_Write (&cmd_text, temp, templen);
137 		Z_Free (temp);
138 	}
139 }
140 
141 
142 /*
143 ============
144 Cbuf_CopyToDefer
145 ============
146 */
Cbuf_CopyToDefer(void)147 void Cbuf_CopyToDefer (void)
148 {
149 	memcpy(defer_text_buf, cmd_text_buf, cmd_text.cursize);
150 	defer_text_buf[cmd_text.cursize] = 0;
151 	cmd_text.cursize = 0;
152 }
153 
154 /*
155 ============
156 Cbuf_InsertFromDefer
157 ============
158 */
Cbuf_InsertFromDefer(void)159 void Cbuf_InsertFromDefer (void)
160 {
161 	Cbuf_InsertText (defer_text_buf);
162 	defer_text_buf[0] = 0;
163 }
164 
165 
166 /*
167 ============
168 Cbuf_ExecuteText
169 ============
170 */
Cbuf_ExecuteText(int exec_when,char * text)171 void Cbuf_ExecuteText (int exec_when, char *text)
172 {
173 	switch (exec_when)
174 	{
175 	case EXEC_NOW:
176 		Cmd_ExecuteString (text);
177 		break;
178 	case EXEC_INSERT:
179 		Cbuf_InsertText (text);
180 		break;
181 	case EXEC_APPEND:
182 		Cbuf_AddText (text);
183 		break;
184 	default:
185 		Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
186 	}
187 }
188 
189 /*
190 ============
191 Cbuf_Execute
192 ============
193 */
Cbuf_Execute(void)194 void Cbuf_Execute (void)
195 {
196 	int		i;
197 	char	*text;
198 	char	line[1024];
199 	int		quotes;
200 
201 	alias_count = 0;		// don't allow infinite alias loops
202 
203 	while (cmd_text.cursize)
204 	{
205 // find a \n or ; line break
206 		text = (char *)cmd_text.data;
207 
208 		quotes = 0;
209 		for (i=0 ; i< cmd_text.cursize ; i++)
210 		{
211 			if (text[i] == '"')
212 				quotes++;
213 			if ( !(quotes&1) &&  text[i] == ';')
214 				break;	// don't break if inside a quoted string
215 			if (text[i] == '\n')
216 				break;
217 		}
218 
219 
220 		memcpy (line, text, i);
221 		line[i] = 0;
222 
223 // delete the text from the command buffer and move remaining commands down
224 // this is necessary because commands (exec, alias) can insert data at the
225 // beginning of the text buffer
226 
227 		if (i == cmd_text.cursize)
228 			cmd_text.cursize = 0;
229 		else
230 		{
231 			i++;
232 			cmd_text.cursize -= i;
233 			memmove (text, text+i, cmd_text.cursize);
234 		}
235 
236 // execute the command line
237 		Cmd_ExecuteString (line);
238 
239 		if (cmd_wait)
240 		{
241 			// skip out while text still remains in buffer, leaving it
242 			// for next frame
243 			cmd_wait = false;
244 			break;
245 		}
246 	}
247 }
248 
249 
250 /*
251 ===============
252 Cbuf_AddEarlyCommands
253 
254 Adds command line parameters as script statements
255 Commands lead with a +, and continue until another +
256 
257 Set commands are added early, so they are guaranteed to be set before
258 the client and server initialize for the first time.
259 
260 Other commands are added late, after all initialization is complete.
261 ===============
262 */
Cbuf_AddEarlyCommands(qboolean clear)263 void Cbuf_AddEarlyCommands (qboolean clear)
264 {
265 	int		i;
266 	char	*s;
267 
268 	for (i=0 ; i<COM_Argc() ; i++)
269 	{
270 		s = COM_Argv(i);
271 		if (strcmp (s, "+set"))
272 			continue;
273 		Cbuf_AddText (va("set %s %s\n", COM_Argv(i+1), COM_Argv(i+2)));
274 		if (clear)
275 		{
276 			COM_ClearArgv(i);
277 			COM_ClearArgv(i+1);
278 			COM_ClearArgv(i+2);
279 		}
280 		i+=2;
281 	}
282 }
283 
284 /*
285 =================
286 Cbuf_AddLateCommands
287 
288 Adds command line parameters as script statements
289 Commands lead with a + and continue until another + or -
290 quake +vid_ref gl +map amlev1
291 
292 Returns true if any late commands were added, which
293 will keep the demoloop from immediately starting
294 =================
295 */
Cbuf_AddLateCommands(void)296 qboolean Cbuf_AddLateCommands (void)
297 {
298 	int		i, j;
299 	int		s;
300 	char	*text, *build, c;
301 	int		argc;
302 	qboolean	ret;
303 
304 // build the combined string to parse from
305 	s = 0;
306 	argc = COM_Argc();
307 	for (i=1 ; i<argc ; i++)
308 	{
309 		s += strlen (COM_Argv(i)) + 1;
310 	}
311 	if (!s)
312 		return false;
313 
314 	text = Z_Malloc (s+1);
315 	text[0] = 0;
316 	for (i=1 ; i<argc ; i++)
317 	{
318 		strcat (text,COM_Argv(i));
319 		if (i != argc-1)
320 			strcat (text, " ");
321 	}
322 
323 // pull out the commands
324 	build = Z_Malloc (s+1);
325 	build[0] = 0;
326 
327 	for (i=0 ; i<s-1 ; i++)
328 	{
329 		if (text[i] == '+')
330 		{
331 			i++;
332 
333 			for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
334 				;
335 
336 			c = text[j];
337 			text[j] = 0;
338 
339 			strcat (build, text+i);
340 			strcat (build, "\n");
341 			text[j] = c;
342 			i = j-1;
343 		}
344 	}
345 
346 	ret = (build[0] != 0);
347 	if (ret)
348 		Cbuf_AddText (build);
349 
350 	Z_Free (text);
351 	Z_Free (build);
352 
353 	return ret;
354 }
355 
356 
357 /*
358 ==============================================================================
359 
360 						SCRIPT COMMANDS
361 
362 ==============================================================================
363 */
364 
365 
366 /*
367 ===============
368 Cmd_Exec_f
369 ===============
370 */
Cmd_Exec_f(void)371 void Cmd_Exec_f (void)
372 {
373 	char	*scriptname;
374 	char	*f, *f2;
375 	int		len;
376 
377 	if (Cmd_Argc () != 2)
378 	{
379 		Com_Printf ("exec <filename> : execute a script file\n");
380 		return;
381 	}
382 
383 	scriptname = Cmd_Argv(1);
384 
385 	//AutoFind if now suffix
386 	if (!strstr (scriptname, ".")) //add .cfg for easy use
387 	{
388 		scriptname = va("%s.cfg", Cmd_Argv(1));
389 		if (FS_LoadFile (scriptname, NULL) == -1)
390 		{
391 			scriptname = va("%s.rc", Cmd_Argv(1));
392 			if (FS_LoadFile (scriptname, NULL) == -1)
393 			{
394 				scriptname = va("%s.c", Cmd_Argv(1));
395 				if (FS_LoadFile (scriptname, NULL) == -1)
396 				{
397 					Com_Printf ("couldn't exec %s\n", Cmd_Argv(1));
398 					return;
399 				}
400 			}
401 		}
402 	}
403 
404 	len = FS_LoadFile (scriptname, (void **)&f);
405 	if (!f)
406 	{
407 		Com_Printf ("couldn't exec %s\n",scriptname);
408 		return;
409 	}
410 	Com_Printf ("execing %s\n",scriptname);
411 
412 	// the file doesn't have a trailing 0, so we need to copy it off
413 	f2 = Z_Malloc(len+1);
414 	memcpy (f2, f, len);
415 	f2[len] = 0;
416 
417 	Cbuf_InsertText (f2);
418 
419 	Z_Free (f2);
420 	FS_FreeFile (f);
421 }
422 
423 
424 /*
425 ===============
426 Cmd_Echo_f
427 
428 Just prints the rest of the line to the console
429 ===============
430 */
Cmd_Echo_f(void)431 void Cmd_Echo_f (void)
432 {
433 	int		i;
434 
435 	for (i=1 ; i<Cmd_Argc() ; i++)
436 		Com_Printf ("%s ",Cmd_Argv(i));
437 	Com_Printf ("\n");
438 }
439 
440 /*
441 ===============
442 Cmd_Alias_f
443 
444 Creates a new command that executes a command string (possibly ; seperated)
445 ===============
446 */
Cmd_Alias_f(void)447 void Cmd_Alias_f (void)
448 {
449 	cmdalias_t	*a;
450 	char		cmd[1024];
451 	int			i, c;
452 	char		*s;
453 
454 	if (Cmd_Argc() == 1)
455 	{
456 		Com_Printf ("Current alias commands:\n");
457 		for (a = cmd_alias ; a ; a=a->next)
458 			Com_Printf ("%s : %s\n", a->name, a->value);
459 		return;
460 	}
461 
462 	s = Cmd_Argv(1);
463 	if (strlen(s) >= MAX_ALIAS_NAME)
464 	{
465 		Com_Printf ("Alias name is too long\n");
466 		return;
467 	}
468 
469 	// if the alias already exists, reuse it
470 	for (a = cmd_alias ; a ; a=a->next)
471 	{
472 		if (!strcmp(s, a->name))
473 		{
474 			Z_Free (a->value);
475 			break;
476 		}
477 	}
478 
479 	if (!a)
480 	{
481 		a = Z_Malloc (sizeof(cmdalias_t));
482 		a->next = cmd_alias;
483 		cmd_alias = a;
484 	}
485 	strcpy (a->name, s);
486 
487 // copy the rest of the command line
488 	cmd[0] = 0;		// start out with a null string
489 	c = Cmd_Argc();
490 	for (i=2 ; i< c ; i++)
491 	{
492 		strcat (cmd, Cmd_Argv(i));
493 		if (i != (c - 1))
494 			strcat (cmd, " ");
495 	}
496 	strcat (cmd, "\n");
497 
498 	a->value = CopyString (cmd);
499 }
500 
501 /*
502 =============================================================================
503 
504 					COMMAND EXECUTION
505 
506 =============================================================================
507 */
508 
509 typedef struct cmd_function_s
510 {
511 	struct cmd_function_s	*next;
512 	char					*name;
513 	xcommand_t				function;
514 } cmd_function_t;
515 
516 
517 static	int			cmd_argc;
518 static	char		*cmd_argv[MAX_STRING_TOKENS];
519 static	char		*cmd_null_string = "";
520 static	char		cmd_args[MAX_STRING_CHARS];
521 
522 static	cmd_function_t	*cmd_functions;		// possible commands to execute
523 
524 /*
525 ============
526 Cmd_Argc
527 ============
528 */
Cmd_Argc(void)529 int		Cmd_Argc (void)
530 {
531 	return cmd_argc;
532 }
533 
534 /*
535 ============
536 Cmd_Argv
537 ============
538 */
Cmd_Argv(int arg)539 char	*Cmd_Argv (int arg)
540 {
541 	if ( (unsigned)arg >= cmd_argc )
542 		return cmd_null_string;
543 	return cmd_argv[arg];
544 }
545 
546 /*
547 ============
548 Cmd_Args
549 
550 Returns a single string containing argv(1) to argv(argc()-1)
551 ============
552 */
Cmd_Args(void)553 char		*Cmd_Args (void)
554 {
555 	return cmd_args;
556 }
557 
558 
559 /*
560 ======================
561 Cmd_MacroExpandString
562 ======================
563 */
Cmd_MacroExpandString(char * text)564 char *Cmd_MacroExpandString (char *text)
565 {
566 	int		i, j, count, len;
567 	qboolean	inquote;
568 	char	*scan;
569 	static	char	expanded[MAX_STRING_CHARS];
570 	char	temporary[MAX_STRING_CHARS];
571 	char	*token, *start;
572 
573 	inquote = false;
574 	scan = text;
575 
576 	len = strlen (scan);
577 	if (len >= MAX_STRING_CHARS)
578 	{
579 		Com_Printf ("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
580 		return NULL;
581 	}
582 
583 	count = 0;
584 
585 	for (i=0 ; i<len ; i++)
586 	{
587 		if (scan[i] == '"')
588 			inquote ^= 1;
589 		if (inquote)
590 			continue;	// don't expand inside quotes
591 		if (scan[i] != '$')
592 			continue;
593 		// scan out the complete macro
594 		start = scan+i+1;
595 		token = COM_Parse (&start);
596 		if (!start)
597 			continue;
598 
599 		token = Cvar_VariableString (token);
600 
601 		j = strlen(token);
602 		len += j;
603 		if (len >= MAX_STRING_CHARS)
604 		{
605 			Com_Printf ("Expanded line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
606 			return NULL;
607 		}
608 
609 		strncpy (temporary, scan, i);
610 		strcpy (temporary+i, token);
611 		strcpy (temporary+i+j, start);
612 
613 		strcpy (expanded, temporary);
614 		scan = expanded;
615 		i--;
616 
617 		if (++count == 100)
618 		{
619 			Com_Printf ("Macro expansion loop, discarded.\n");
620 			return NULL;
621 		}
622 	}
623 
624 	if (inquote)
625 	{
626 		Com_Printf ("Line has unmatched quote, discarded.\n");
627 		return NULL;
628 	}
629 
630 	return scan;
631 }
632 
633 
634 /*
635 ============
636 Cmd_TokenizeString
637 
638 Parses the given string into command line tokens.
639 $Cvars will be expanded unless they are in a quoted token
640 ============
641 */
Cmd_TokenizeString(char * text,qboolean macroExpand)642 void Cmd_TokenizeString (char *text, qboolean macroExpand)
643 {
644 	int		i;
645 	char	*com_token;
646 
647 // clear the args from the last string
648 	for (i=0 ; i<cmd_argc ; i++)
649 		Z_Free (cmd_argv[i]);
650 
651 	cmd_argc = 0;
652 	cmd_args[0] = 0;
653 
654 	// macro expand the text
655 	if (macroExpand)
656 		text = Cmd_MacroExpandString (text);
657 	if (!text)
658 		return;
659 
660 	while (1)
661 	{
662 // skip whitespace up to a /n
663 		while (*text && *text <= ' ' && *text != '\n')
664 		{
665 			text++;
666 		}
667 
668 		if (*text == '\n')
669 		{	// a newline seperates commands in the buffer
670 			text++;
671 			break;
672 		}
673 
674 		if (!*text)
675 			return;
676 
677 		// set cmd_args to everything after the first arg
678 		if (cmd_argc == 1)
679 		{
680 			int		l;
681 
682 			strcpy (cmd_args, text);
683 
684 			// strip off any trailing whitespace
685 			l = strlen(cmd_args) - 1;
686 			for ( ; l >= 0 ; l--)
687 				if (cmd_args[l] <= ' ')
688 					cmd_args[l] = 0;
689 				else
690 					break;
691 		}
692 
693 		com_token = COM_Parse (&text);
694 		if (!text)
695 			return;
696 
697 		if (cmd_argc < MAX_STRING_TOKENS)
698 		{
699 			cmd_argv[cmd_argc] = Z_Malloc (strlen(com_token)+1);
700 			strcpy (cmd_argv[cmd_argc], com_token);
701 			cmd_argc++;
702 		}
703 	}
704 
705 }
706 
707 
708 /*
709 ============
710 Cmd_AddCommand
711 ============
712 */
Cmd_AddCommand(char * cmd_name,xcommand_t function)713 void	Cmd_AddCommand (char *cmd_name, xcommand_t function)
714 {
715 	cmd_function_t	*cmd;
716 
717 // fail if the command is a variable name
718 	if (Cvar_VariableString(cmd_name)[0])
719 	{
720 		Com_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
721 		return;
722 	}
723 
724 // fail if the command already exists
725 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
726 	{
727 		if (!strcmp (cmd_name, cmd->name))
728 		{
729 			Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
730 			return;
731 		}
732 	}
733 
734 	cmd = Z_Malloc (sizeof(cmd_function_t));
735 	cmd->name = cmd_name;
736 	cmd->function = function;
737 	cmd->next = cmd_functions;
738 	cmd_functions = cmd;
739 }
740 
741 /*
742 ============
743 Cmd_RemoveCommand
744 ============
745 */
Cmd_RemoveCommand(char * cmd_name)746 void	Cmd_RemoveCommand (char *cmd_name)
747 {
748 	cmd_function_t	*cmd, **back;
749 
750 	back = &cmd_functions;
751 	while (1)
752 	{
753 		cmd = *back;
754 		if (!cmd)
755 		{
756 			Com_Printf ("Cmd_RemoveCommand: %s not added\n", cmd_name);
757 			return;
758 		}
759 		if (!strcmp (cmd_name, cmd->name))
760 		{
761 			*back = cmd->next;
762 			Z_Free (cmd);
763 			return;
764 		}
765 		back = &cmd->next;
766 	}
767 }
768 
769 /*
770 ============
771 Cmd_Exists
772 ============
773 */
Cmd_Exists(char * cmd_name)774 qboolean	Cmd_Exists (char *cmd_name)
775 {
776 	cmd_function_t	*cmd;
777 
778 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
779 	{
780 		if (!strcmp (cmd_name,cmd->name))
781 			return true;
782 	}
783 
784 	return false;
785 }
786 
787 
788 
789 /*
790 ============
791 Cmd_CompleteCommand
792 ============
793 */
794 char			retval[256];
Cmd_CompleteCommand(char * partial)795 char *Cmd_CompleteCommand (char *partial)
796 {
797 	cmd_function_t	*cmd;
798 	int				len,i,o,p;
799 	cmdalias_t		*a;
800 	cvar_t			*cvar;
801 	char			*pmatch[1024];
802 	qboolean		diff = false;
803 
804 	len = strlen(partial);
805 
806 	if (!len)
807 		return NULL;
808 
809 // check for exact match
810 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
811 		if (!_stricmp (partial,cmd->name))
812 			return cmd->name;
813 	for (a=cmd_alias ; a ; a=a->next)
814 		if (!_stricmp (partial, a->name))
815 			return a->name;
816 	for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
817 		if (!_stricmp (partial,cvar->name))
818 			return cvar->name;
819 
820 	for (i=0; i<1024; i++)
821 		pmatch[i]=NULL;
822 	i=0;
823 
824 // check for partial match
825 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
826 		if (!_strnicmp (partial,cmd->name, len)) {
827 			pmatch[i]=cmd->name;
828 			i++;
829 		}
830 	for (a=cmd_alias ; a ; a=a->next)
831 		if (!_strnicmp (partial, a->name, len)) {
832 			pmatch[i]=a->name;
833 			i++;
834 		}
835 	for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
836 		if (!_strnicmp (partial,cvar->name, len)) {
837 			pmatch[i]=cvar->name;
838 			i++;
839 		}
840 
841 	if (i) {
842 		if (i == 1)
843 			return pmatch[0];
844 
845 		Com_Printf("]/%s\n",partial);
846 		for (o=0; o<i; o++)
847 			Com_Printf("   %s\n",pmatch[o]);
848 
849 		strcpy(retval,""); p=0;
850 		while (!diff && p < 256) {
851 			retval[p]=pmatch[0][p];
852 			for (o=0; o<i; o++) {
853 				if (p > strlen(pmatch[o]))
854 					continue;
855 				if (retval[p] != pmatch[o][p]) {
856 					retval[p] = 0;
857 					diff=false;
858 				}
859 			}
860 			p++;
861 		}
862 		return retval;
863 	}
864 
865 	return NULL;
866 }
867 
Cmd_IsComplete(char * command)868 qboolean Cmd_IsComplete (char *command)
869 {
870 	cmd_function_t	*cmd;
871 	cmdalias_t		*a;
872 	cvar_t			*cvar;
873 
874 // check for exact match
875 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
876 		if (!_stricmp (command,cmd->name))
877 			return true;
878 	for (a=cmd_alias ; a ; a=a->next)
879 		if (!_stricmp (command, a->name))
880 			return true;
881 	for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
882 		if (!_stricmp (command,cvar->name))
883 			return true;
884 
885 	return false;
886 }
887 
888 /*
889 ============
890 Cmd_ExecuteString
891 
892 A complete command line has been parsed, so try to execute it
893 FIXME: lookupnoadd the token to speed search?
894 ============
895 */
Cmd_ExecuteString(char * text)896 void	Cmd_ExecuteString (char *text)
897 {
898 	cmd_function_t	*cmd;
899 	cmdalias_t		*a;
900 
901 	Cmd_TokenizeString (text, true);
902 
903 	// execute the command line
904 	if (!Cmd_Argc())
905 		return;		// no tokens
906 
907 	// check functions
908 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
909 	{
910 		if (!Q_strcasecmp (cmd_argv[0],cmd->name))
911 		{
912 			if (!cmd->function)
913 			{	// forward to server command
914 				Cmd_ExecuteString (va("cmd %s", text));
915 			}
916 			else
917 				cmd->function ();
918 			return;
919 		}
920 	}
921 
922 	// check alias
923 	for (a=cmd_alias ; a ; a=a->next)
924 	{
925 		if (!Q_strcasecmp (cmd_argv[0], a->name))
926 		{
927 			if (++alias_count == ALIAS_LOOP_COUNT)
928 			{
929 				Com_Printf ("ALIAS_LOOP_COUNT\n");
930 				return;
931 			}
932 			Cbuf_InsertText (a->value);
933 			return;
934 		}
935 	}
936 
937 	// check cvars
938 	if (Cvar_Command ())
939 		return;
940 
941 	// send it as a server command if we are connected
942 	Cmd_ForwardToServer ();
943 }
944 
945 /*
946 ============
947 Cmd_List_f
948 ============
949 */
Cmd_List_f(void)950 void Cmd_List_f (void)
951 {
952 	cmd_function_t	*cmd;
953 	int				i;
954 
955 	i = 0;
956 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next, i++)
957 		Com_Printf ("%s\n", cmd->name);
958 	Com_Printf ("%i commands\n", i);
959 }
960 
961 /*
962 ============
963 Cmd_Init
964 ============
965 */
Cmd_Init(void)966 void Cmd_Init (void)
967 {
968 //
969 // register our commands
970 //
971 	Cmd_AddCommand ("cmdlist",Cmd_List_f);
972 	Cmd_AddCommand ("exec",Cmd_Exec_f);
973 	Cmd_AddCommand ("echo",Cmd_Echo_f);
974 	Cmd_AddCommand ("alias",Cmd_Alias_f);
975 	Cmd_AddCommand ("wait", Cmd_Wait_f);
976 }
977 
978