• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 Copyright (C) 1996-1997 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 "quakedef.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 cvar_t cl_warncmd = CVAR2("cl_warncmd", "0");
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 "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
50 ============
51 */
Cmd_Wait_f(void)52 void Cmd_Wait_f (void)
53 {
54 	cmd_wait = true;
55 }
56 
57 /*
58 =============================================================================
59 
60 						COMMAND BUFFER
61 
62 =============================================================================
63 */
64 
65 sizebuf_t	cmd_text;
66 byte		cmd_text_buf[8192];
67 
68 /*
69 ============
70 Cbuf_Init
71 ============
72 */
Cbuf_Init(void)73 void Cbuf_Init (void)
74 {
75 	cmd_text.data = cmd_text_buf;
76 	cmd_text.maxsize = sizeof(cmd_text_buf);
77 }
78 
79 /*
80 ============
81 Cbuf_AddText
82 
83 Adds command text at the end of the buffer
84 ============
85 */
Cbuf_AddText(char * text)86 void Cbuf_AddText (char *text)
87 {
88 	int		l;
89 
90 	l = Q_strlen (text);
91 
92 	if (cmd_text.cursize + l >= cmd_text.maxsize)
93 	{
94 		Con_Printf ("Cbuf_AddText: overflow\n");
95 		return;
96 	}
97 	SZ_Write (&cmd_text, text, Q_strlen (text));
98 }
99 
100 
101 /*
102 ============
103 Cbuf_InsertText
104 
105 Adds command text immediately after the current command
106 Adds a \n to the text
107 FIXME: actually change the command buffer to do less copying
108 ============
109 */
Cbuf_InsertText(char * text)110 void Cbuf_InsertText (char *text)
111 {
112 	char	*temp;
113 	int		templen;
114 
115 // copy off any commands still remaining in the exec buffer
116 	templen = cmd_text.cursize;
117 	if (templen)
118 	{
119 		temp = Z_Malloc (templen);
120 		Q_memcpy (temp, cmd_text.data, templen);
121 		SZ_Clear (&cmd_text);
122 	}
123 	else
124 		temp = NULL;	// shut up compiler
125 
126 // add the entire text of the file
127 	Cbuf_AddText (text);
128 	SZ_Write (&cmd_text, "\n", 1);
129 // add the copied off data
130 	if (templen)
131 	{
132 		SZ_Write (&cmd_text, temp, templen);
133 		Z_Free (temp);
134 	}
135 }
136 
137 /*
138 ============
139 Cbuf_Execute
140 ============
141 */
Cbuf_Execute(void)142 void Cbuf_Execute (void)
143 {
144 	int		i;
145 	char	*text;
146 	char	line[1024];
147 	int		quotes;
148 
149 	while (cmd_text.cursize)
150 	{
151 // find a \n or ; line break
152 		text = (char *)cmd_text.data;
153 
154 		quotes = 0;
155 		for (i=0 ; i< cmd_text.cursize ; i++)
156 		{
157 			if (text[i] == '"')
158 				quotes++;
159 			if ( !(quotes&1) &&  text[i] == ';')
160 				break;	// don't break if inside a quoted string
161 			if (text[i] == '\n' || text[i] == '\r')
162 				break;
163 		}
164 
165 
166 		memcpy (line, text, i);
167 		line[i] = 0;
168 
169 // delete the text from the command buffer and move remaining commands down
170 // this is necessary because commands (exec, alias) can insert data at the
171 // beginning of the text buffer
172 
173 		if (i == cmd_text.cursize)
174 			cmd_text.cursize = 0;
175 		else
176 		{
177 			i++;
178 			cmd_text.cursize -= i;
179 			Q_memcpy (text, text+i, cmd_text.cursize);
180 		}
181 
182 // execute the command line
183 		Cmd_ExecuteString (line);
184 
185 		if (cmd_wait)
186 		{	// skip out while text still remains in buffer, leaving it
187 			// for next frame
188 			cmd_wait = false;
189 			break;
190 		}
191 	}
192 }
193 
194 /*
195 ==============================================================================
196 
197 						SCRIPT COMMANDS
198 
199 ==============================================================================
200 */
201 
202 /*
203 ===============
204 Cmd_StuffCmds_f
205 
206 Adds command line parameters as script statements
207 Commands lead with a +, and continue until a - or another +
208 quake +prog jctest.qp +cmd amlev1
209 quake -nosound +cmd amlev1
210 ===============
211 */
Cmd_StuffCmds_f(void)212 void Cmd_StuffCmds_f (void)
213 {
214 	int		i, j;
215 	int		s;
216 	char	*text, *build, c;
217 
218 // build the combined string to parse from
219 	s = 0;
220 	for (i=1 ; i<com_argc ; i++)
221 	{
222 		if (!com_argv[i])
223 			continue;		// NEXTSTEP nulls out -NXHost
224 		s += Q_strlen (com_argv[i]) + 1;
225 	}
226 	if (!s)
227 		return;
228 
229 	text = Z_Malloc (s+1);
230 	text[0] = 0;
231 	for (i=1 ; i<com_argc ; i++)
232 	{
233 		if (!com_argv[i])
234 			continue;		// NEXTSTEP nulls out -NXHost
235 		Q_strcat (text,com_argv[i]);
236 		if (i != com_argc-1)
237 			Q_strcat (text, " ");
238 	}
239 
240 // pull out the commands
241 	build = Z_Malloc (s+1);
242 	build[0] = 0;
243 
244 	for (i=0 ; i<s-1 ; i++)
245 	{
246 		if (text[i] == '+')
247 		{
248 			i++;
249 
250 			for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
251 				;
252 
253 			c = text[j];
254 			text[j] = 0;
255 
256 			Q_strcat (build, text+i);
257 			Q_strcat (build, "\n");
258 			text[j] = c;
259 			i = j-1;
260 		}
261 	}
262 
263 	if (build[0])
264 		Cbuf_InsertText (build);
265 
266 	Z_Free (text);
267 	Z_Free (build);
268 }
269 
270 
271 /*
272 ===============
273 Cmd_Exec_f
274 ===============
275 */
Cmd_Exec_f(void)276 void Cmd_Exec_f (void)
277 {
278 	char	*f;
279 	int		mark;
280 
281 	if (Cmd_Argc () != 2)
282 	{
283 		Con_Printf ("exec <filename> : execute a script file\n");
284 		return;
285 	}
286 
287 	// FIXME: is this safe freeing the hunk here???
288 	mark = Hunk_LowMark ();
289 	f = (char *)COM_LoadHunkFile (Cmd_Argv(1));
290 	if (!f)
291 	{
292 		Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
293 		return;
294 	}
295 	if (!Cvar_Command () && (cl_warncmd.value || developer.value))
296 		Con_Printf ("execing %s\n",Cmd_Argv(1));
297 
298 	Cbuf_InsertText (f);
299 	Hunk_FreeToLowMark (mark);
300 }
301 
302 
303 /*
304 ===============
305 Cmd_Echo_f
306 
307 Just prints the rest of the line to the console
308 ===============
309 */
Cmd_Echo_f(void)310 void Cmd_Echo_f (void)
311 {
312 	int		i;
313 
314 	for (i=1 ; i<Cmd_Argc() ; i++)
315 		Con_Printf ("%s ",Cmd_Argv(i));
316 	Con_Printf ("\n");
317 }
318 
319 /*
320 ===============
321 Cmd_Alias_f
322 
323 Creates a new command that executes a command string (possibly ; seperated)
324 ===============
325 */
326 
CopyString(char * in)327 char *CopyString (char *in)
328 {
329 	char	*out;
330 
331 	out = Z_Malloc (strlen(in)+1);
332 	strcpy (out, in);
333 	return out;
334 }
335 
Cmd_Alias_f(void)336 void Cmd_Alias_f (void)
337 {
338 	cmdalias_t	*a;
339 	char		cmd[1024];
340 	int			i, c;
341 	char		*s;
342 
343 	if (Cmd_Argc() == 1)
344 	{
345 		Con_Printf ("Current alias commands:\n");
346 		for (a = cmd_alias ; a ; a=a->next)
347 			Con_Printf ("%s : %s\n", a->name, a->value);
348 		return;
349 	}
350 
351 	s = Cmd_Argv(1);
352 	if (strlen(s) >= MAX_ALIAS_NAME)
353 	{
354 		Con_Printf ("Alias name is too long\n");
355 		return;
356 	}
357 
358 	// if the alias allready exists, reuse it
359 	for (a = cmd_alias ; a ; a=a->next)
360 	{
361 		if (!strcmp(s, a->name))
362 		{
363 			Z_Free (a->value);
364 			break;
365 		}
366 	}
367 
368 	if (!a)
369 	{
370 		a = Z_Malloc (sizeof(cmdalias_t));
371 		a->next = cmd_alias;
372 		cmd_alias = a;
373 	}
374 	strcpy (a->name, s);
375 
376 // copy the rest of the command line
377 	cmd[0] = 0;		// start out with a null string
378 	c = Cmd_Argc();
379 	for (i=2 ; i< c ; i++)
380 	{
381 		strcat (cmd, Cmd_Argv(i));
382 		if (i != c)
383 			strcat (cmd, " ");
384 	}
385 	strcat (cmd, "\n");
386 
387 	a->value = CopyString (cmd);
388 }
389 
390 /*
391 =============================================================================
392 
393 					COMMAND EXECUTION
394 
395 =============================================================================
396 */
397 
398 typedef struct cmd_function_s
399 {
400 	struct cmd_function_s	*next;
401 	char					*name;
402 	xcommand_t				function;
403 } cmd_function_t;
404 
405 
406 #define	MAX_ARGS		80
407 
408 static	int			cmd_argc;
409 static	char		*cmd_argv[MAX_ARGS];
410 static	char		*cmd_null_string = "";
411 static	char		*cmd_args = NULL;
412 
413 
414 
415 static	cmd_function_t	*cmd_functions;		// possible commands to execute
416 
417 /*
418 ============
419 Cmd_Argc
420 ============
421 */
Cmd_Argc(void)422 int		Cmd_Argc (void)
423 {
424 	return cmd_argc;
425 }
426 
427 /*
428 ============
429 Cmd_Argv
430 ============
431 */
Cmd_Argv(int arg)432 char	*Cmd_Argv (int arg)
433 {
434 	if ( arg >= cmd_argc )
435 		return cmd_null_string;
436 	return cmd_argv[arg];
437 }
438 
439 /*
440 ============
441 Cmd_Args
442 
443 Returns a single string containing argv(1) to argv(argc()-1)
444 ============
445 */
Cmd_Args(void)446 char		*Cmd_Args (void)
447 {
448 	if (!cmd_args)
449 		return "";
450 	return cmd_args;
451 }
452 
453 
454 /*
455 ============
456 Cmd_TokenizeString
457 
458 Parses the given string into command line tokens.
459 ============
460 */
Cmd_TokenizeString(char * text)461 void Cmd_TokenizeString (char *text)
462 {
463 	int		i;
464 
465 // clear the args from the last string
466 	for (i=0 ; i<cmd_argc ; i++)
467 		Z_Free (cmd_argv[i]);
468 
469 	cmd_argc = 0;
470 	cmd_args = NULL;
471 
472 	while (1)
473 	{
474 // skip whitespace up to a /n
475 		while (*text && *text <= ' ' && *text != '\n')
476 		{
477 			text++;
478 		}
479 
480 		if (*text == '\n')
481 		{	// a newline seperates commands in the buffer
482 			text++;
483 			break;
484 		}
485 
486 		if (!*text)
487 			return;
488 
489 		if (cmd_argc == 1)
490 			 cmd_args = text;
491 
492 		text = COM_Parse (text);
493 		if (!text)
494 			return;
495 
496 		if (cmd_argc < MAX_ARGS)
497 		{
498 			cmd_argv[cmd_argc] = Z_Malloc (Q_strlen(com_token)+1);
499 			Q_strcpy (cmd_argv[cmd_argc], com_token);
500 			cmd_argc++;
501 		}
502 	}
503 
504 }
505 
506 
507 /*
508 ============
509 Cmd_AddCommand
510 ============
511 */
Cmd_AddCommand(char * cmd_name,xcommand_t function)512 void	Cmd_AddCommand (char *cmd_name, xcommand_t function)
513 {
514 	cmd_function_t	*cmd;
515 
516 	if (host_initialized)	// because hunk allocation would get stomped
517 		Sys_Error ("Cmd_AddCommand after host_initialized");
518 
519 // fail if the command is a variable name
520 	if (Cvar_VariableString(cmd_name)[0])
521 	{
522 		Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
523 		return;
524 	}
525 
526 // fail if the command already exists
527 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
528 	{
529 		if (!Q_strcmp (cmd_name, cmd->name))
530 		{
531 			Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
532 			return;
533 		}
534 	}
535 
536 	cmd = Hunk_Alloc (sizeof(cmd_function_t));
537 	cmd->name = cmd_name;
538 	cmd->function = function;
539 	cmd->next = cmd_functions;
540 	cmd_functions = cmd;
541 }
542 
543 /*
544 ============
545 Cmd_Exists
546 ============
547 */
Cmd_Exists(char * cmd_name)548 qboolean	Cmd_Exists (char *cmd_name)
549 {
550 	cmd_function_t	*cmd;
551 
552 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
553 	{
554 		if (!Q_strcmp (cmd_name,cmd->name))
555 			return true;
556 	}
557 
558 	return false;
559 }
560 
561 
562 
563 /*
564 ============
565 Cmd_CompleteCommand
566 ============
567 */
Cmd_CompleteCommand(char * partial)568 char *Cmd_CompleteCommand (char *partial)
569 {
570 	cmd_function_t	*cmd;
571 	int				len;
572 	cmdalias_t		*a;
573 
574 	len = Q_strlen(partial);
575 
576 	if (!len)
577 		return NULL;
578 
579 // check for exact match
580 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
581 		if (!strcmp (partial,cmd->name))
582 			return cmd->name;
583 	for (a=cmd_alias ; a ; a=a->next)
584 		if (!strcmp (partial, a->name))
585 			return a->name;
586 
587 // check for partial match
588 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
589 		if (!strncmp (partial,cmd->name, len))
590 			return cmd->name;
591 	for (a=cmd_alias ; a ; a=a->next)
592 		if (!strncmp (partial, a->name, len))
593 			return a->name;
594 
595 	return NULL;
596 }
597 
598 #ifndef SERVERONLY		// FIXME
599 /*
600 ===================
601 Cmd_ForwardToServer
602 
603 adds the current command line as a clc_stringcmd to the client message.
604 things like godmode, noclip, etc, are commands directed to the server,
605 so when they are typed in at the console, they will need to be forwarded.
606 ===================
607 */
Cmd_ForwardToServer(void)608 void Cmd_ForwardToServer (void)
609 {
610 	if (cls.state == ca_disconnected)
611 	{
612 		Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
613 		return;
614 	}
615 
616 	if (cls.demoplayback)
617 		return;		// not really connected
618 
619 	MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
620 	SZ_Print (&cls.netchan.message, Cmd_Argv(0));
621 	if (Cmd_Argc() > 1)
622 	{
623 		SZ_Print (&cls.netchan.message, " ");
624 		SZ_Print (&cls.netchan.message, Cmd_Args());
625 	}
626 }
627 
628 // don't forward the first argument
Cmd_ForwardToServer_f(void)629 void Cmd_ForwardToServer_f (void)
630 {
631 	if (cls.state == ca_disconnected)
632 	{
633 		Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
634 		return;
635 	}
636 
637 	if (Q_strcasecmp(Cmd_Argv(1), "snap") == 0) {
638 		Cbuf_InsertText ("snap\n");
639 		return;
640 	}
641 
642 	if (cls.demoplayback)
643 		return;		// not really connected
644 
645 	if (Cmd_Argc() > 1)
646 	{
647 		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
648 		SZ_Print (&cls.netchan.message, Cmd_Args());
649 	}
650 }
651 #else
Cmd_ForwardToServer(void)652 void Cmd_ForwardToServer (void)
653 {
654 }
655 #endif
656 
657 /*
658 ============
659 Cmd_ExecuteString
660 
661 A complete command line has been parsed, so try to execute it
662 FIXME: lookupnoadd the token to speed search?
663 ============
664 */
Cmd_ExecuteString(char * text)665 void	Cmd_ExecuteString (char *text)
666 {
667 	cmd_function_t	*cmd;
668 	cmdalias_t		*a;
669 
670 	Cmd_TokenizeString (text);
671 
672 // execute the command line
673 	if (!Cmd_Argc())
674 		return;		// no tokens
675 
676 // check functions
677 	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
678 	{
679 		if (!Q_strcasecmp (cmd_argv[0],cmd->name))
680 		{
681 			if (!cmd->function)
682 				Cmd_ForwardToServer ();
683 			else
684 				cmd->function ();
685 			return;
686 		}
687 	}
688 
689 // check alias
690 	for (a=cmd_alias ; a ; a=a->next)
691 	{
692 		if (!Q_strcasecmp (cmd_argv[0], a->name))
693 		{
694 			Cbuf_InsertText (a->value);
695 			return;
696 		}
697 	}
698 
699 // check cvars
700 	if (!Cvar_Command () && (cl_warncmd.value || developer.value))
701 		Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
702 
703 }
704 
705 
706 
707 /*
708 ================
709 Cmd_CheckParm
710 
711 Returns the position (1 to argc-1) in the command's argument list
712 where the given parameter apears, or 0 if not present
713 ================
714 */
Cmd_CheckParm(char * parm)715 int Cmd_CheckParm (char *parm)
716 {
717 	int i;
718 
719 	if (!parm)
720 		Sys_Error ("Cmd_CheckParm: NULL");
721 
722 	for (i = 1; i < Cmd_Argc (); i++)
723 		if (! Q_strcasecmp (parm, Cmd_Argv (i)))
724 			return i;
725 
726 	return 0;
727 }
728 
729 /*
730 ============
731 Cmd_Init
732 ============
733 */
Cmd_Init(void)734 void Cmd_Init (void)
735 {
736 //
737 // register our commands
738 //
739 	Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
740 	Cmd_AddCommand ("exec",Cmd_Exec_f);
741 	Cmd_AddCommand ("echo",Cmd_Echo_f);
742 	Cmd_AddCommand ("alias",Cmd_Alias_f);
743 	Cmd_AddCommand ("wait", Cmd_Wait_f);
744 #ifndef SERVERONLY
745 	Cmd_AddCommand ("cmd", Cmd_ForwardToServer_f);
746 #endif
747 }
748 
749