0,0 → 1,705 |
/* |
Copyright (C) 1996-1997 Id Software, Inc. |
|
This program is free software; you can redistribute it and/or |
modify it under the terms of the GNU General Public License |
as published by the Free Software Foundation; either version 2 |
of the License, or (at your option) any later version. |
|
This program is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
See the GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with this program; if not, write to the Free Software |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
*/ |
// cmd.c -- Quake script command processing module |
|
#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; |
|
cmdalias_t *cmd_alias; |
|
int trashtest; |
int *trashspot; |
|
qboolean 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" |
============ |
*/ |
void Cmd_Wait_f (void) |
{ |
cmd_wait = true; |
} |
|
/* |
============================================================================= |
|
COMMAND BUFFER |
|
============================================================================= |
*/ |
|
sizebuf_t cmd_text; |
|
/* |
============ |
Cbuf_Init |
============ |
*/ |
void Cbuf_Init (void) |
{ |
SZ_Alloc (&cmd_text, 8192); // 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 = Q_strlen (text); |
|
if (cmd_text.cursize + l >= cmd_text.maxsize) |
{ |
Con_Printf ("Cbuf_AddText: overflow\n"); |
return; |
} |
|
SZ_Write (&cmd_text, text, Q_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; |
int templen; |
|
// copy off any commands still remaining in the exec buffer |
templen = cmd_text.cursize; |
if (templen) |
{ |
temp = Z_Malloc (templen); |
Q_memcpy (temp, cmd_text.data, templen); |
SZ_Clear (&cmd_text); |
} |
else |
temp = NULL; // shut up compiler |
|
// 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; |
Q_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_StuffCmds_f |
|
Adds command line parameters as script statements |
Commands lead with a +, and continue until a - or another + |
quake +prog jctest.qp +cmd amlev1 |
quake -nosound +cmd amlev1 |
=============== |
*/ |
void Cmd_StuffCmds_f (void) |
{ |
int i, j; |
int s; |
char *text, *build, c; |
|
if (Cmd_Argc () != 1) |
{ |
Con_Printf ("stuffcmds : execute command line parameters\n"); |
return; |
} |
|
// build the combined string to parse from |
s = 0; |
for (i=1 ; i<com_argc ; i++) |
{ |
if (!com_argv[i]) |
continue; // NEXTSTEP nulls out -NXHost |
s += Q_strlen (com_argv[i]) + 1; |
} |
if (!s) |
return; |
|
text = Z_Malloc (s+1); |
text[0] = 0; |
for (i=1 ; i<com_argc ; i++) |
{ |
if (!com_argv[i]) |
continue; // NEXTSTEP nulls out -NXHost |
Q_strcat (text,com_argv[i]); |
if (i != com_argc-1) |
Q_strcat (text, " "); |
} |
|
// pull out the commands |
build = Z_Malloc (s+1); |
build[0] = 0; |
|
for (i=0 ; i<s-1 ; i++) |
{ |
if (text[i] == '+') |
{ |
i++; |
|
for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++) |
; |
|
c = text[j]; |
text[j] = 0; |
|
Q_strcat (build, text+i); |
Q_strcat (build, "\n"); |
text[j] = c; |
i = j-1; |
} |
} |
|
if (build[0]) |
Cbuf_InsertText (build); |
|
Z_Free (text); |
Z_Free (build); |
} |
|
|
/* |
=============== |
Cmd_Exec_f |
=============== |
*/ |
void Cmd_Exec_f (void) |
{ |
char *f; |
int mark; |
|
if (Cmd_Argc () != 2) |
{ |
Con_Printf ("exec <filename> : execute a script file\n"); |
return; |
} |
|
mark = Hunk_LowMark (); |
f = (char *)COM_LoadHunkFile (Cmd_Argv(1)); |
if (!f) |
{ |
Con_Printf ("couldn't exec %s\n",Cmd_Argv(1)); |
return; |
} |
Con_Printf ("execing %s\n",Cmd_Argv(1)); |
|
Cbuf_InsertText (f); |
Hunk_FreeToLowMark (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 allready exists, reuse it |
for (a = cmd_alias ; a ; a=a->next) |
{ |
if (!strcmp(s, a->name)) |
{ |
Z_Free (a->value); |
break; |
} |
} |
|
if (!a) |
{ |
a = Z_Malloc (sizeof(cmdalias_t)); |
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 = NULL; |
|
cmd_source_t cmd_source; |
|
|
static cmd_function_t *cmd_functions; // possible commands to execute |
|
/* |
============ |
Cmd_Init |
============ |
*/ |
void Cmd_Init (void) |
{ |
// |
// register our commands |
// |
Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f); |
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 ( (unsigned)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 = NULL; |
|
while (1) |
{ |
// skip whitespace up to a /n |
while (*text && *text <= ' ' && *text != '\n') |
{ |
text++; |
} |
|
if (*text == '\n') |
{ // a newline seperates commands in the buffer |
text++; |
break; |
} |
|
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 (Q_strlen(com_token)+1); |
Q_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 |
Sys_Error ("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 (!Q_strcmp (cmd_name, cmd->name)) |
{ |
Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); |
return; |
} |
} |
|
cmd = Hunk_Alloc (sizeof(cmd_function_t)); |
cmd->name = cmd_name; |
cmd->function = function; |
cmd->next = cmd_functions; |
cmd_functions = cmd; |
} |
|
/* |
============ |
Cmd_Exists |
============ |
*/ |
qboolean Cmd_Exists (char *cmd_name) |
{ |
cmd_function_t *cmd; |
|
for (cmd=cmd_functions ; cmd ; cmd=cmd->next) |
{ |
if (!Q_strcmp (cmd_name,cmd->name)) |
return true; |
} |
|
return false; |
} |
|
|
|
/* |
============ |
Cmd_CompleteCommand |
============ |
*/ |
char *Cmd_CompleteCommand (char *partial) |
{ |
cmd_function_t *cmd; |
int len; |
|
len = Q_strlen(partial); |
|
if (!len) |
return NULL; |
|
// check functions |
for (cmd=cmd_functions ; cmd ; cmd=cmd->next) |
if (!Q_strncmp (partial,cmd->name, len)) |
return cmd->name; |
|
return NULL; |
} |
|
/* |
============ |
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 (!Q_strcasecmp (cmd_argv[0],cmd->name)) |
{ |
cmd->function (); |
return; |
} |
} |
|
// check alias |
for (a=cmd_alias ; a ; a=a->next) |
{ |
if (!Q_strcasecmp (cmd_argv[0], a->name)) |
{ |
Cbuf_InsertText (a->value); |
return; |
} |
} |
|
// check cvars |
if (!Cvar_Command ()) |
Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0)); |
|
} |
|
|
/* |
=================== |
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 (Q_strcasecmp(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"); |
} |
|
|
/* |
================ |
Cmd_CheckParm |
|
Returns the position (1 to argc-1) in the command's argument list |
where the given parameter apears, or 0 if not present |
================ |
*/ |
|
int Cmd_CheckParm (char *parm) |
{ |
int i; |
|
if (!parm) |
Sys_Error ("Cmd_CheckParm: NULL"); |
|
for (i = 1; i < Cmd_Argc (); i++) |
if (! Q_strcasecmp (parm, Cmd_Argv (i))) |
return i; |
|
return 0; |
} |