spingle/source/cmd.c

865 lines
16 KiB
C
Raw Normal View History

2019-11-24 20:45:15 -08:00
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
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
2019-12-02 07:07:37 -08:00
#include "q_defs.h"
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
void Cmd_ForwardToServer(void);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
#define MAX_ALIAS_NAME 32
2019-11-24 20:45:15 -08:00
#define CMDLINE_LENGTH 256 //johnfitz -- mirrored in common.c
typedef struct cmdalias_s
{
2019-11-25 17:40:18 -08:00
struct cmdalias_s *next;
char name[MAX_ALIAS_NAME];
char *value;
2019-11-24 20:45:15 -08:00
} cmdalias_t;
2019-11-25 17:40:18 -08:00
cmdalias_t *cmd_alias;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
bool cmd_wait;
2019-11-24 20:45:15 -08:00
//=============================================================================
/*
============
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"
============
*/
2019-11-25 17:40:18 -08:00
void Cmd_Wait_f(void)
2019-11-24 20:45:15 -08:00
{
cmd_wait = true;
}
/*
=============================================================================
2019-11-25 17:40:18 -08:00
COMMAND BUFFER
2019-11-24 20:45:15 -08:00
=============================================================================
*/
2019-11-25 17:40:18 -08:00
sizebuf_t cmd_text;
2019-11-24 20:45:15 -08:00
/*
============
Cbuf_Init
============
*/
2019-11-25 17:40:18 -08:00
void Cbuf_Init(void)
2019-11-24 20:45:15 -08:00
{
2019-12-07 09:27:26 -08:00
SZ_Alloc(&cmd_text, 1 << 18); // space for commands and script files. spike -- was 8192, but modern configs can be *HUGE*, at least if they contain lots of comments/docs for things.
2019-11-24 20:45:15 -08:00
}
/*
============
Cbuf_AddText
Adds command text at the end of the buffer
============
*/
2019-11-25 17:40:18 -08:00
void Cbuf_AddText(const char *text)
2019-11-24 20:45:15 -08:00
{
2019-12-07 09:27:26 -08:00
int32_t l;
2019-11-24 20:45:15 -08:00
2019-12-07 09:27:26 -08:00
l = strlen(text);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(cmd_text.cursize + l >= cmd_text.maxsize)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_Printf("Cbuf_AddText: overflow\n");
2019-11-24 20:45:15 -08:00
return;
}
2019-12-07 09:27:26 -08:00
SZ_Write(&cmd_text, text, strlen(text));
2019-11-24 20:45:15 -08:00
}
/*
============
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
============
*/
2019-11-25 17:40:18 -08:00
void Cbuf_InsertText(const char *text)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
char *temp;
int32_t templen;
2019-11-24 20:45:15 -08:00
// copy off any commands still remaining in the exec buffer
templen = cmd_text.cursize;
2019-11-25 17:40:18 -08:00
if(templen)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
temp = (char *) Z_Malloc(templen);
2019-12-07 09:27:26 -08:00
memcpy(temp, cmd_text.data, templen);
2019-11-25 17:40:18 -08:00
SZ_Clear(&cmd_text);
2019-11-24 20:45:15 -08:00
}
else
2019-11-25 17:40:18 -08:00
temp = NULL; // shut up compiler
2019-11-24 20:45:15 -08:00
// add the entire text of the file
2019-11-25 17:40:18 -08:00
Cbuf_AddText(text);
SZ_Write(&cmd_text, "\n", 1);
2019-11-24 20:45:15 -08:00
// add the copied off data
2019-11-25 17:40:18 -08:00
if(templen)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
SZ_Write(&cmd_text, temp, templen);
Z_Free(temp);
2019-11-24 20:45:15 -08:00
}
}
/*
============
Cbuf_Execute
============
*/
2019-11-25 17:40:18 -08:00
void Cbuf_Execute(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
char *text;
char line[1024];
int32_t quotes;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
while(cmd_text.cursize)
2019-11-24 20:45:15 -08:00
{
// find a \n or ; line break
text = (char *)cmd_text.data;
quotes = 0;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < cmd_text.cursize ; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(text[i] == '"')
2019-11-24 20:45:15 -08:00
quotes++;
2019-11-25 17:40:18 -08:00
if(!(quotes & 1) && text[i] == ';')
break; // don't break if inside a quoted string
if(text[i] == '\n')
2019-11-24 20:45:15 -08:00
break;
}
if(i > (int32_t)strsizeof(line))
2019-11-24 20:45:15 -08:00
{
memcpy(line, text, strsizeof(line));
line[strsizeof(line)] = 0;
2019-11-24 20:45:15 -08:00
}
else
{
2019-11-25 17:40:18 -08:00
memcpy(line, text, i);
2019-11-24 20:45:15 -08:00
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
2019-11-25 17:40:18 -08:00
if(i == cmd_text.cursize)
2019-11-24 20:45:15 -08:00
cmd_text.cursize = 0;
else
{
i++;
cmd_text.cursize -= i;
2019-11-25 17:40:18 -08:00
memmove(text, text + i, cmd_text.cursize);
2019-11-24 20:45:15 -08:00
}
// execute the command line
2019-11-25 17:40:18 -08:00
Cmd_ExecuteString(line, src_command);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(cmd_wait)
{
// skip out while text still remains in buffer, leaving it
2019-11-24 20:45:15 -08:00
// for next frame
cmd_wait = false;
break;
}
}
}
/*
==============================================================================
2019-11-25 17:40:18 -08:00
SCRIPT COMMANDS
2019-11-24 20:45:15 -08:00
==============================================================================
*/
/*
===============
Cmd_StuffCmds_f -- johnfitz -- rewritten to read the "cmdline" cvar, for use with dynamic mod loading
Adds command line parameters as script statements
Commands lead with a +, and continue until a - or another +
<program name> +prog jctest.qp +cmd amlev1
<program name> -nosound +cmd amlev1
2019-11-24 20:45:15 -08:00
===============
*/
2019-11-25 17:40:18 -08:00
void Cmd_StuffCmds_f(void)
2019-11-24 20:45:15 -08:00
{
extern cvar_t cmdline;
2019-11-25 17:40:18 -08:00
char cmds[CMDLINE_LENGTH];
int32_t i, j, plus;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
plus = false; // On Unix, argv[0] is command name
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0, j = 0; cmdline.string[i]; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(cmdline.string[i] == '+')
2019-11-24 20:45:15 -08:00
{
plus = true;
2019-11-25 17:40:18 -08:00
if(j > 0)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmds[j - 1] = ';';
2019-11-24 20:45:15 -08:00
cmds[j++] = ' ';
}
}
2019-11-25 17:40:18 -08:00
else if(cmdline.string[i] == '-' &&
(i == 0 || cmdline.string[i - 1] == ' ')) //johnfitz -- allow hypenated map names with +map
plus = false;
else if(plus)
2019-11-24 20:45:15 -08:00
cmds[j++] = cmdline.string[i];
}
cmds[j] = 0;
2019-11-25 17:40:18 -08:00
Cbuf_InsertText(cmds);
2019-11-24 20:45:15 -08:00
}
/*
===============
Cmd_Exec_f
===============
*/
2019-11-25 17:40:18 -08:00
void Cmd_Exec_f(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
char *f;
int32_t mark;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(Cmd_Argc() != 2)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_Printf("exec <filename> : execute a script file\n");
2019-11-24 20:45:15 -08:00
return;
}
2019-11-25 17:40:18 -08:00
mark = Hunk_LowMark();
f = (char *)COM_LoadHunkFile(Cmd_Argv(1), NULL);
if(!f)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_Printf("couldn't exec %s\n", Cmd_Argv(1));
2019-11-24 20:45:15 -08:00
return;
}
2019-11-25 17:40:18 -08:00
Con_Printf("execing %s\n", Cmd_Argv(1));
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
Cbuf_InsertText(f);
Hunk_FreeToLowMark(mark);
2019-11-24 20:45:15 -08:00
}
/*
===============
Cmd_Echo_f
Just prints the rest of the line to the console
===============
*/
2019-11-25 17:40:18 -08:00
void Cmd_Echo_f(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 1 ; i < Cmd_Argc() ; i++)
Con_Printf("%s ", Cmd_Argv(i));
Con_Printf("\n");
2019-11-24 20:45:15 -08:00
}
/*
===============
Cmd_Alias_f -- johnfitz -- rewritten
Creates a new command that executes a command string (possibly ; seperated)
===============
*/
2019-11-25 17:40:18 -08:00
void Cmd_Alias_f(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmdalias_t *a;
char cmd[1024];
int32_t i, c;
const char *s;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
switch(Cmd_Argc())
2019-11-24 20:45:15 -08:00
{
case 1: //list all aliases
2019-11-25 17:40:18 -08:00
for(a = cmd_alias, i = 0; a; a = a->next, i++)
Con_SafePrintf(" %s: %s", a->name, a->value);
if(i)
Con_SafePrintf("%" PRIi32 " alias command(s)\n", i);
2019-11-24 20:45:15 -08:00
else
2019-11-25 17:40:18 -08:00
Con_SafePrintf("no alias commands found\n");
2019-11-24 20:45:15 -08:00
break;
case 2: //output current alias string
2019-11-25 17:40:18 -08:00
for(a = cmd_alias ; a ; a = a->next)
if(!strcmp(Cmd_Argv(1), a->name))
Con_Printf(" %s: %s", a->name, a->value);
2019-11-24 20:45:15 -08:00
break;
default: //set alias string
s = Cmd_Argv(1);
2019-11-25 17:40:18 -08:00
if(strlen(s) >= MAX_ALIAS_NAME)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_Printf("Alias name is too long\n");
2019-11-24 20:45:15 -08:00
return;
}
// if the alias allready exists, reuse it
2019-11-25 17:40:18 -08:00
for(a = cmd_alias ; a ; a = a->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!strcmp(s, a->name))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Z_Free(a->value);
2019-11-24 20:45:15 -08:00
break;
}
}
2019-11-25 17:40:18 -08:00
if(!a)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
a = (cmdalias_t *) Z_Malloc(sizeof(cmdalias_t));
2019-11-24 20:45:15 -08:00
a->next = cmd_alias;
cmd_alias = a;
}
2019-11-25 17:40:18 -08:00
strcpy(a->name, s);
2019-11-24 20:45:15 -08:00
// copy the rest of the command line
2019-11-25 17:40:18 -08:00
cmd[0] = 0; // start out with a null string
2019-11-24 20:45:15 -08:00
c = Cmd_Argc();
2019-11-25 17:40:18 -08:00
for(i = 2; i < c; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
q_strlcat(cmd, Cmd_Argv(i), sizeof(cmd));
if(i != c - 1)
q_strlcat(cmd, " ", sizeof(cmd));
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
if(q_strlcat(cmd, "\n", sizeof(cmd)) >= sizeof(cmd))
2019-11-24 20:45:15 -08:00
{
Con_Printf("alias value too long!\n");
2019-11-25 17:40:18 -08:00
cmd[0] = '\n'; // nullify the string
2019-11-24 20:45:15 -08:00
cmd[1] = 0;
}
2019-11-25 17:40:18 -08:00
a->value = Z_Strdup(cmd);
2019-11-24 20:45:15 -08:00
break;
}
}
/*
===============
Cmd_Unalias_f -- johnfitz
===============
*/
2019-11-25 17:40:18 -08:00
void Cmd_Unalias_f(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmdalias_t *a, *prev;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
switch(Cmd_Argc())
2019-11-24 20:45:15 -08:00
{
default:
case 1:
Con_Printf("unalias <name> : delete alias\n");
break;
case 2:
prev = NULL;
2019-11-25 17:40:18 -08:00
for(a = cmd_alias; a; a = a->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!strcmp(Cmd_Argv(1), a->name))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(prev)
2019-11-24 20:45:15 -08:00
prev->next = a->next;
else
cmd_alias = a->next;
2019-11-25 17:40:18 -08:00
Z_Free(a->value);
Z_Free(a);
2019-11-24 20:45:15 -08:00
return;
}
prev = a;
}
2019-11-25 17:40:18 -08:00
Con_Printf("No alias named %s\n", Cmd_Argv(1));
2019-11-24 20:45:15 -08:00
break;
}
}
/*
===============
Cmd_Unaliasall_f -- johnfitz
===============
*/
2019-11-25 17:40:18 -08:00
void Cmd_Unaliasall_f(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmdalias_t *blah;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
while(cmd_alias)
2019-11-24 20:45:15 -08:00
{
blah = cmd_alias->next;
Z_Free(cmd_alias->value);
Z_Free(cmd_alias);
cmd_alias = blah;
}
}
/*
=============================================================================
2019-11-25 17:40:18 -08:00
COMMAND EXECUTION
2019-11-24 20:45:15 -08:00
=============================================================================
*/
typedef struct cmd_function_s
{
2019-11-25 17:40:18 -08:00
struct cmd_function_s *next;
const char *name;
xcommand_t function;
2019-11-24 20:45:15 -08:00
} cmd_function_t;
2019-11-25 17:40:18 -08:00
#define MAX_ARGS 80
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
static int32_t cmd_argc;
static char *cmd_argv[MAX_ARGS];
static char cmd_null_string[] = "";
static const char *cmd_args = NULL;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
cmd_source_t cmd_source;
2019-11-24 20:45:15 -08:00
//johnfitz -- better tab completion
2019-11-25 17:40:18 -08:00
//static cmd_function_t *cmd_functions; // possible commands to execute
cmd_function_t *cmd_functions; // possible commands to execute
2019-11-24 20:45:15 -08:00
//johnfitz
/*
============
Cmd_List_f -- johnfitz
============
*/
2019-11-25 17:40:18 -08:00
void Cmd_List_f(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmd_function_t *cmd;
const char *partial;
2019-12-07 09:27:26 -08:00
size_t len, count;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(Cmd_Argc() > 1)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
partial = Cmd_Argv(1);
2019-12-07 09:27:26 -08:00
len = strlen(partial);
2019-11-24 20:45:15 -08:00
}
else
{
partial = NULL;
len = 0;
}
2019-11-25 17:40:18 -08:00
count = 0;
for(cmd = cmd_functions ; cmd ; cmd = cmd->next)
2019-11-24 20:45:15 -08:00
{
2019-12-07 09:27:26 -08:00
if(partial && strncmp(partial, cmd->name, len))
2019-11-24 20:45:15 -08:00
{
continue;
}
2019-11-25 17:40:18 -08:00
Con_SafePrintf(" %s\n", cmd->name);
2019-11-24 20:45:15 -08:00
count++;
}
2019-12-07 09:27:26 -08:00
Con_SafePrintf("%zu commands", count);
2019-11-25 17:40:18 -08:00
if(partial)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_SafePrintf(" beginning with \"%s\"", partial);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
Con_SafePrintf("\n");
2019-11-24 20:45:15 -08:00
}
static char *Cmd_TintSubstring(const char *in, const char *substr, char *out, size_t outsize)
{
2019-12-07 09:27:26 -08:00
size_t l;
2019-11-24 20:45:15 -08:00
char *m;
q_strlcpy(out, in, outsize);
2019-11-25 17:40:18 -08:00
while((m = q_strcasestr(out, substr)))
2019-11-24 20:45:15 -08:00
{
l = strlen(substr);
2019-11-25 17:40:18 -08:00
while(l-- > 0)
if(*m >= ' ' && *m < 127)
2019-11-24 20:45:15 -08:00
*m++ |= 0x80;
}
return out;
}
/*
============
Cmd_Apropos_f
scans through each command and cvar names+descriptions for the given substring
we don't support descriptions, so this isn't really all that useful, but even without the sake of consistency it still combines cvars+commands under a single command.
============
*/
void Cmd_Apropos_f(void)
{
char tmpbuf[256];
2019-11-25 16:49:58 -08:00
int32_t hits = 0;
2019-11-25 17:40:18 -08:00
cmd_function_t *cmd;
2019-11-24 20:45:15 -08:00
cvar_t *var;
2019-11-25 17:40:18 -08:00
const char *substr = Cmd_Argv(1);
if(!*substr)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_SafePrintf("%s <substring> : search through commands and cvars for the given substring\n", Cmd_Argv(0));
2019-11-24 20:45:15 -08:00
return;
}
2019-11-25 17:40:18 -08:00
for(cmd = cmd_functions ; cmd ; cmd = cmd->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(q_strcasestr(cmd->name, substr))
2019-11-24 20:45:15 -08:00
{
hits++;
2019-11-25 17:40:18 -08:00
Con_SafePrintf("%s\n", Cmd_TintSubstring(cmd->name, substr, tmpbuf, sizeof(tmpbuf)));
2019-11-24 20:45:15 -08:00
}
}
2019-11-25 17:40:18 -08:00
for(var = Cvar_FindVarAfter("", 0) ; var ; var = var->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(q_strcasestr(var->name, substr))
2019-11-24 20:45:15 -08:00
{
hits++;
2019-11-25 17:40:18 -08:00
Con_SafePrintf("%s (current value: \"%s\")\n", Cmd_TintSubstring(var->name, substr, tmpbuf, sizeof(tmpbuf)), var->string);
2019-11-24 20:45:15 -08:00
}
}
2019-11-25 17:40:18 -08:00
if(!hits)
Con_SafePrintf("no cvars nor commands contain that substring\n");
2019-11-24 20:45:15 -08:00
}
/*
============
Cmd_Init
============
*/
2019-11-25 17:40:18 -08:00
void Cmd_Init(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Cmd_AddCommand("cmdlist", Cmd_List_f); //johnfitz
Cmd_AddCommand("unalias", Cmd_Unalias_f); //johnfitz
Cmd_AddCommand("unaliasall", Cmd_Unaliasall_f); //johnfitz
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_AddCommand("apropos", Cmd_Apropos_f);
Cmd_AddCommand("find", Cmd_Apropos_f);
2019-11-24 20:45:15 -08:00
}
/*
============
Cmd_Argc
============
*/
2019-11-25 17:40:18 -08:00
int32_t Cmd_Argc(void)
2019-11-24 20:45:15 -08:00
{
return cmd_argc;
}
/*
============
Cmd_Argv
============
*/
2019-11-25 17:40:18 -08:00
const char *Cmd_Argv(int32_t arg)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(arg < 0 || arg >= cmd_argc)
2019-11-24 20:45:15 -08:00
return cmd_null_string;
return cmd_argv[arg];
}
/*
============
Cmd_Args
============
*/
2019-11-25 17:40:18 -08:00
const char *Cmd_Args(void)
2019-11-24 20:45:15 -08:00
{
return cmd_args;
}
/*
============
Cmd_TokenizeString
Parses the given string into command line tokens.
============
*/
2019-11-25 17:40:18 -08:00
void Cmd_TokenizeString(const char *text)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
// clear the args from the last string
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < cmd_argc ; i++)
Z_Free(cmd_argv[i]);
2019-11-24 20:45:15 -08:00
cmd_argc = 0;
cmd_args = NULL;
2019-11-25 17:40:18 -08:00
while(1)
2019-11-24 20:45:15 -08:00
{
// skip whitespace up to a /n
2019-11-25 17:40:18 -08:00
while(*text && *text <= ' ' && *text != '\n')
2019-11-24 20:45:15 -08:00
{
text++;
}
2019-11-25 17:40:18 -08:00
if(*text == '\n')
{
// a newline seperates commands in the buffer
2019-11-24 20:45:15 -08:00
text++;
break;
}
2019-11-25 17:40:18 -08:00
if(!*text)
2019-11-24 20:45:15 -08:00
return;
2019-11-25 17:40:18 -08:00
if(cmd_argc == 1)
cmd_args = text;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
text = COM_Parse(text);
if(!text)
2019-11-24 20:45:15 -08:00
return;
2019-11-25 17:40:18 -08:00
if(cmd_argc < MAX_ARGS)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmd_argv[cmd_argc] = Z_Strdup(com_token);
2019-11-24 20:45:15 -08:00
cmd_argc++;
}
}
}
/*
============
Cmd_AddCommand
============
*/
2019-11-25 17:40:18 -08:00
void Cmd_AddCommand(const char *cmd_name, xcommand_t function)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmd_function_t *cmd;
cmd_function_t *cursor, *prev; //johnfitz -- sorted list insert
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(host_initialized) // because hunk allocation would get stomped
Sys_Error("Cmd_AddCommand after host_initialized");
2019-11-24 20:45:15 -08:00
// fail if the command is a variable name
2019-11-25 17:40:18 -08:00
if(Cvar_VariableString(cmd_name)[0])
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
2019-11-24 20:45:15 -08:00
return;
}
// fail if the command already exists
2019-11-25 17:40:18 -08:00
for(cmd = cmd_functions ; cmd ; cmd = cmd->next)
2019-11-24 20:45:15 -08:00
{
2019-12-07 09:27:26 -08:00
if(!strcmp(cmd_name, cmd->name))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
2019-11-24 20:45:15 -08:00
return;
}
}
cmd = Hunk_AllocName(sizeof(cmd_function_t), "cmd_functions");
2019-11-24 20:45:15 -08:00
cmd->name = cmd_name;
cmd->function = function;
//johnfitz -- insert each entry in alphabetical order
2019-11-25 17:40:18 -08:00
if(cmd_functions == NULL || strcmp(cmd->name, cmd_functions->name) < 0) //insert at front
2019-11-24 20:45:15 -08:00
{
cmd->next = cmd_functions;
cmd_functions = cmd;
}
else //insert later
{
prev = cmd_functions;
cursor = cmd_functions->next;
2019-11-25 17:40:18 -08:00
while((cursor != NULL) && (strcmp(cmd->name, cursor->name) > 0))
2019-11-24 20:45:15 -08:00
{
prev = cursor;
cursor = cursor->next;
}
cmd->next = prev->next;
prev->next = cmd;
}
//johnfitz
}
/*
============
Cmd_Exists
============
*/
2019-11-25 17:40:18 -08:00
bool Cmd_Exists(const char *cmd_name)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmd_function_t *cmd;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(cmd = cmd_functions ; cmd ; cmd = cmd->next)
2019-11-24 20:45:15 -08:00
{
2019-12-07 09:27:26 -08:00
if(!strcmp(cmd_name, cmd->name))
2019-11-24 20:45:15 -08:00
return true;
}
return false;
}
/*
============
Cmd_CompleteCommand
============
*/
2019-11-25 17:40:18 -08:00
const char *Cmd_CompleteCommand(const char *partial)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmd_function_t *cmd;
2019-12-07 09:27:26 -08:00
size_t len;
2019-11-24 20:45:15 -08:00
2019-12-07 09:27:26 -08:00
len = strlen(partial);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(!len)
2019-11-24 20:45:15 -08:00
return NULL;
// check functions
2019-11-25 17:40:18 -08:00
for(cmd = cmd_functions ; cmd ; cmd = cmd->next)
2019-12-07 09:27:26 -08:00
if(!strncmp(partial, cmd->name, len))
2019-11-24 20:45:15 -08:00
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?
============
*/
2019-11-25 17:40:18 -08:00
void Cmd_ExecuteString(const char *text, cmd_source_t src)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmd_function_t *cmd;
cmdalias_t *a;
2019-11-24 20:45:15 -08:00
cmd_source = src;
2019-11-25 17:40:18 -08:00
Cmd_TokenizeString(text);
2019-11-24 20:45:15 -08:00
// execute the command line
2019-11-25 17:40:18 -08:00
if(!Cmd_Argc())
return; // no tokens
2019-11-24 20:45:15 -08:00
// check functions
2019-11-25 17:40:18 -08:00
for(cmd = cmd_functions ; cmd ; cmd = cmd->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!q_strcasecmp(cmd_argv[0], cmd->name))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cmd->function();
2019-11-24 20:45:15 -08:00
return;
}
}
// check alias
2019-11-25 17:40:18 -08:00
for(a = cmd_alias ; a ; a = a->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!q_strcasecmp(cmd_argv[0], a->name))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Cbuf_InsertText(a->value);
2019-11-24 20:45:15 -08:00
return;
}
}
// check cvars
2019-11-25 17:40:18 -08:00
if(!Cvar_Command())
Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(0));
2019-11-24 20:45:15 -08:00
}
/*
===================
Cmd_ForwardToServer
Sends the entire command line over to the server
===================
*/
2019-11-25 17:40:18 -08:00
void Cmd_ForwardToServer(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(cls.state != ca_connected)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0));
2019-11-24 20:45:15 -08:00
return;
}
2019-11-25 17:40:18 -08:00
if(cls.demoplayback)
return; // not really connected
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
MSG_WriteByte(&cls.message, clc_stringcmd);
if(q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
SZ_Print(&cls.message, Cmd_Argv(0));
SZ_Print(&cls.message, " ");
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
if(Cmd_Argc() > 1)
SZ_Print(&cls.message, Cmd_Args());
2019-11-24 20:45:15 -08:00
else
2019-11-25 17:40:18 -08:00
SZ_Print(&cls.message, "\n");
2019-11-24 20:45:15 -08:00
}
/*
================
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
================
*/
2019-11-25 17:40:18 -08:00
int32_t Cmd_CheckParm(const char *parm)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(!parm)
Sys_Error("Cmd_CheckParm: NULL");
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 1; i < Cmd_Argc(); i++)
if(! q_strcasecmp(parm, Cmd_Argv(i)))
2019-11-24 20:45:15 -08:00
return i;
return 0;
}