ref: 3d9a179d3de101cf405df256918a7769d02698ee
dir: /cmd.c/
#include "quakedef.h"
void Cmd_ForwardToServer (void);
#define	MAX_ALIAS_NAME	32
typedef struct cmdalias_s
{
	struct cmdalias_s	*next;
	char	name[MAX_ALIAS_NAME];
	char	*value;
} cmdalias_t;
static cmdalias_t	*cmd_alias;
static bool	cmd_wait;
//=============================================================================
/*
============
Cmd_Wait_f
Causes execution of the remainder of the command buffer to be delayed until
next frame.  This allows commands like:
bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
============
*/
static void Cmd_Wait_f (void)
{
	cmd_wait = true;
}
/*
=============================================================================
						COMMAND BUFFER
=============================================================================
*/
static sizebuf_t	cmd_text;
/*
============
Cbuf_Init
============
*/
void Cbuf_Init (void)
{
	SZ_Alloc (&cmd_text, 65536);		// space for commands and script files
}
/*
============
Cbuf_AddText
Adds command text at the end of the buffer
============
*/
void Cbuf_AddText (char *text)
{
	int		l;
	l = strlen(text);
	if (cmd_text.cursize + l >= cmd_text.maxsize)
	{
		Con_Printf ("Cbuf_AddText: overflow\n");
		return;
	}
	SZ_Write (&cmd_text, text, strlen(text));
}
/*
============
Cbuf_InsertText
Adds command text immediately after the current command
Adds a \n to the text
FIXME: actually change the command buffer to do less copying
============
*/
void Cbuf_InsertText (char *text)
{
	char	*temp = nil;
	int		templen;
	// copy off any commands still remaining in the exec buffer
	templen = cmd_text.cursize;
	if (templen)
	{
		temp = Z_Malloc (templen);
		memcpy(temp, cmd_text.data, templen);
		SZ_Clear (&cmd_text);
	}
	// add the entire text of the file
	Cbuf_AddText (text);
	// add the copied off data
	if (templen)
	{
		SZ_Write (&cmd_text, temp, templen);
		Z_Free (temp);
	}
}
/*
============
Cbuf_Execute
============
*/
void Cbuf_Execute (void)
{
	int		i;
	char	*text;
	char	line[1024];
	int		quotes;
	while (cmd_text.cursize)
	{
		// find a \n or ; line break
		text = (char *)cmd_text.data;
		quotes = 0;
		for (i=0 ; i< cmd_text.cursize ; i++)
		{
			if (text[i] == '"')
				quotes++;
			if ( !(quotes&1) &&  text[i] == ';')
				break;	// don't break if inside a quoted string
			if (text[i] == '\n')
				break;
		}
		memcpy (line, text, i);
		line[i] = 0;
		// delete the text from the command buffer and move remaining commands down
		// this is necessary because commands (exec, alias) can insert data at the
		// beginning of the text buffer
		if (i == cmd_text.cursize)
			cmd_text.cursize = 0;
		else
		{
			i++;
			cmd_text.cursize -= i;
			memcpy(text, text+i, cmd_text.cursize);
		}
		// execute the command line
		Cmd_ExecuteString (line, src_command);
		if (cmd_wait)
		{	// skip out while text still remains in buffer, leaving it
			// for next frame
			cmd_wait = false;
			break;
		}
	}
}
/*
==============================================================================
						SCRIPT COMMANDS
==============================================================================
*/
/*
===============
Cmd_Exec_f
===============
*/
void Cmd_Exec_f (void)
{
	char	*f;
	void	*mark;
	if (Cmd_Argc () != 2)
	{
		Con_Printf ("exec <filename> : execute a script file\n");
		return;
	}
	mark = Hunk_Mark ();
	f = loadhunklmp(Cmd_Argv(1), nil);
	if(f == nil){
		Con_Printf(va("exec: %s\n", lerr()));
		return;
	}
	Con_Printf ("execing %s\n",Cmd_Argv(1));
	Cbuf_InsertText (f);
	Hunk_FreeToMark (mark);
}
/*
===============
Cmd_Echo_f
Just prints the rest of the line to the console
===============
*/
void Cmd_Echo_f (void)
{
	int		i;
	for (i=1 ; i<Cmd_Argc() ; i++)
		Con_Printf ("%s ",Cmd_Argv(i));
	Con_Printf ("\n");
}
/*
===============
Cmd_Alias_f
Creates a new command that executes a command string (possibly ; seperated)
===============
*/
char *CopyString (char *in)
{
	char	*out;
	out = Z_Malloc (strlen(in)+1);
	strcpy (out, in);
	return out;
}
void Cmd_Alias_f (void)
{
	cmdalias_t	*a;
	char		cmd[1024];
	int			i, c;
	char		*s;
	if (Cmd_Argc() == 1)
	{
		Con_Printf ("Current alias commands:\n");
		for (a = cmd_alias ; a ; a=a->next)
			Con_Printf ("%s : %s\n", a->name, a->value);
		return;
	}
	s = Cmd_Argv(1);
	if (strlen(s) >= MAX_ALIAS_NAME)
	{
		Con_Printf ("Alias name is too long\n");
		return;
	}
	// if the alias already exists, reuse it
	for (a = cmd_alias ; a ; a=a->next)
	{
		if(strcmp(s, a->name) == 0)
		{
			Z_Free (a->value);
			break;
		}
	}
	if (a == nil)
	{
		a = Z_Malloc(sizeof *a);
		a->next = cmd_alias;
		cmd_alias = a;
	}
	strcpy (a->name, s);
	// copy the rest of the command line
	cmd[0] = 0;		// start out with a null string
	c = Cmd_Argc();
	for (i=2 ; i< c ; i++)
	{
		strcat (cmd, Cmd_Argv(i));
		if (i != c)
			strcat (cmd, " ");
	}
	strcat (cmd, "\n");
	a->value = CopyString (cmd);
}
/*
=============================================================================
					COMMAND EXECUTION
=============================================================================
*/
typedef struct cmd_function_s
{
	struct cmd_function_s	*next;
	char					*name;
	xcommand_t				function;
} cmd_function_t;
#define	MAX_ARGS		80
static	int			cmd_argc;
static	char		*cmd_argv[MAX_ARGS];
static	char		*cmd_null_string = "";
static	char		*cmd_args = nil;
cmd_source_t	cmd_source;
static	cmd_function_t	*cmd_functions;		// possible commands to execute
/*
============
Cmd_Init
============
*/
void Cmd_Init (void)
{
	Cmd_AddCommand ("exec",Cmd_Exec_f);
	Cmd_AddCommand ("echo",Cmd_Echo_f);
	Cmd_AddCommand ("alias",Cmd_Alias_f);
	Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
	Cmd_AddCommand ("wait", Cmd_Wait_f);
}
/*
============
Cmd_Argc
============
*/
int		Cmd_Argc (void)
{
	return cmd_argc;
}
/*
============
Cmd_Argv
============
*/
char	*Cmd_Argv (int arg)
{
	if (arg >= cmd_argc)
		return cmd_null_string;
	return cmd_argv[arg];
}
/*
============
Cmd_Args
============
*/
char		*Cmd_Args (void)
{
	return cmd_args;
}
/*
============
Cmd_TokenizeString
Parses the given string into command line tokens.
============
*/
void Cmd_TokenizeString (char *text)
{
	int		i;
	// clear the args from the last string
	for (i=0 ; i<cmd_argc ; i++)
		Z_Free (cmd_argv[i]);
	cmd_argc = 0;
	cmd_args = nil;
	while (1)
	{
		// skip whitespace up to a /n
		while (*text && *text <= ' ' && *text != '\n')
		{
			text++;
		}
		if (*text == '\n')	// a newline seperates commands in the buffer
			return;
		if (!*text)
			return;
		if (cmd_argc == 1)
			 cmd_args = text;
		text = COM_Parse (text);
		if (!text)
			return;
		if (cmd_argc < MAX_ARGS)
		{
			cmd_argv[cmd_argc] = Z_Malloc(strlen(com_token)+1);
			strcpy(cmd_argv[cmd_argc], com_token);
			cmd_argc++;
		}
	}
}
/*
============
Cmd_AddCommand
============
*/
void	Cmd_AddCommand (char *cmd_name, xcommand_t function)
{
	cmd_function_t	*cmd;
	if (host_initialized)	// because hunk allocation would get stomped
		fatal ("Cmd_AddCommand after host_initialized");
	// fail if the command is a variable name
	if (Cvar_VariableString(cmd_name)[0])
	{
		Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
		return;
	}
	// fail if the command already exists
	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
	{
		if(strcmp(cmd_name, cmd->name) == 0)
		{
			Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
			return;
		}
	}
	cmd = Hunk_Alloc(sizeof *cmd);
	cmd->name = cmd_name;
	cmd->function = function;
	cmd->next = cmd_functions;
	cmd_functions = cmd;
}
/*
============
Cmd_Exists
============
*/
bool	Cmd_Exists (char *cmd_name)
{
	cmd_function_t	*cmd;
	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
	{
		if(strcmp(cmd_name, cmd->name) == 0)
			return true;
	}
	return false;
}
/*
============
Cmd_CompleteCommand
============
*/
char *Cmd_CompleteCommand (char *partial)
{
	cmd_function_t	*cmd;
	int				len;
	len = strlen(partial);
	if (!len)
		return nil;
	// check functions
	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
		if(strncmp(partial, cmd->name, len) == 0)
			return cmd->name;
	return nil;
}
/*
============
Cmd_ExecuteString
A complete command line has been parsed, so try to execute it
FIXME: lookupnoadd the token to speed search?
============
*/
void	Cmd_ExecuteString (char *text, cmd_source_t src)
{
	cmd_function_t	*cmd;
	cmdalias_t		*a;
	cmd_source = src;
	Cmd_TokenizeString (text);
	// execute the command line
	if (!Cmd_Argc())
		return;		// no tokens
	// check functions
	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
	{
		if(cistrcmp(cmd_argv[0],cmd->name) == 0)
		{
			cmd->function ();
			return;
		}
	}
	// check alias
	for (a=cmd_alias ; a ; a=a->next)
	{
		if(cistrcmp(cmd_argv[0], a->name) == 0)
		{
			Cbuf_InsertText (a->value);
			return;
		}
	}
	// check cvars
	if (!Cvar_Command ())
		Con_Printf ("Unknown command \"%s\": %s\n", Cmd_Argv(0), text);
}
/*
===================
Cmd_ForwardToServer
Sends the entire command line over to the server
===================
*/
void Cmd_ForwardToServer (void)
{
	if (cls.state != ca_connected)
	{
		Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
		return;
	}
	if (cls.demoplayback)
		return;		// not really connected
	MSG_WriteByte (&cls.message, clc_stringcmd);
	if(cistrcmp(Cmd_Argv(0), "cmd") != 0)
	{
		SZ_Print (&cls.message, Cmd_Argv(0));
		SZ_Print (&cls.message, " ");
	}
	if (Cmd_Argc() > 1)
		SZ_Print (&cls.message, Cmd_Args());
	else
		SZ_Print (&cls.message, "\n");
}