#include "qcommon.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;
bool cmd_wait;
#define ALIAS_LOOP_COUNT 16
int alias_count;
void Cmd_Wait_f(void) { cmd_wait = true; }
sizebuf_t cmd_text;
byte cmd_text_buf[8192];
byte defer_text_buf[8192];
void Cbuf_Init(void) { SZ_Init(&cmd_text, cmd_text_buf, sizeof(cmd_text_buf)); }
void Cbuf_AddText(const char *text) {
int l;
l = strlen(text);
if(cmd_text.cursize + l >= cmd_text.maxsize) {
Com_Printf("Cbuf_AddText: overflow\n");
return;
}
SZ_Write(&cmd_text, text, strlen(text));
}
void Cbuf_InsertText(const char *text) {
char *temp;
int templen;
templen = cmd_text.cursize;
if(templen) {
temp = Z_Malloc(templen);
memcpy(temp, cmd_text.data, templen);
SZ_Clear(&cmd_text);
} else
temp = NULL;
Cbuf_AddText(text);
if(templen) {
SZ_Write(&cmd_text, temp, templen);
Z_Free(temp);
}
}
void Cbuf_CopyToDefer(void) {
memcpy(defer_text_buf, cmd_text_buf, cmd_text.cursize);
defer_text_buf[cmd_text.cursize] = 0;
cmd_text.cursize = 0;
}
void Cbuf_InsertFromDefer(void) {
Cbuf_InsertText((const char *)defer_text_buf);
defer_text_buf[0] = 0;
}
void Cbuf_ExecuteText(int exec_when, const char *text) {
switch(exec_when) {
case EXEC_NOW:
Cmd_ExecuteString(text);
break;
case EXEC_INSERT:
Cbuf_InsertText(text);
break;
case EXEC_APPEND:
Cbuf_AddText(text);
break;
default:
Com_Error(ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
}
}
void Cbuf_Execute(void) {
int i;
char *text;
char line[1024];
int quotes;
alias_count = 0;
while(cmd_text.cursize) {
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; if(text[i] == '\n')
break;
}
memcpy(line, text, i);
line[i] = 0;
if(i == cmd_text.cursize)
cmd_text.cursize = 0;
else {
i++;
cmd_text.cursize -= i;
memmove(text, text + i, cmd_text.cursize);
}
Cmd_ExecuteString(line);
if(cmd_wait) {
cmd_wait = false;
break;
}
}
}
void Cbuf_AddEarlyCommands(bool clear) {
int i;
char *s;
for(i = 0; i < COM_Argc(); i++) {
s = COM_Argv(i);
if(strcmp(s, "+set"))
continue;
Cbuf_AddText(va("set %s %s\n", COM_Argv(i + 1), COM_Argv(i + 2)));
if(clear) {
COM_ClearArgv(i);
COM_ClearArgv(i + 1);
COM_ClearArgv(i + 2);
}
i += 2;
}
}
bool Cbuf_AddLateCommands(void) {
int i, j;
int s;
char *text, *build, c;
int argc;
bool ret;
s = 0;
argc = COM_Argc();
for(i = 1; i < argc; i++) {
s += strlen(COM_Argv(i)) + 1;
}
if(!s)
return false;
text = Z_Malloc(s + 1);
text[0] = 0;
for(i = 1; i < argc; i++) {
strcat(text, COM_Argv(i));
if(i != argc - 1)
strcat(text, " ");
}
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;
strcat(build, text + i);
strcat(build, "\n");
text[j] = c;
i = j - 1;
}
}
ret = (build[0] != 0);
if(ret)
Cbuf_AddText(build);
Z_Free(text);
Z_Free(build);
return ret;
}
void Cmd_Exec_f(void) {
char *f, *f2;
int len;
if(Cmd_Argc() != 2) {
Com_Printf("exec <filename> : execute a script file\n");
return;
}
len = FS_LoadFile(Cmd_Argv(1), (void **)&f);
if(!f) {
Com_Printf("couldn't exec %s\n", Cmd_Argv(1));
return;
}
Com_Printf("execing %s\n", Cmd_Argv(1));
f2 = Z_Malloc(len + 1);
memcpy(f2, f, len);
f2[len] = 0;
Cbuf_InsertText(f2);
Z_Free(f2);
FS_FreeFile(f);
}
void Cmd_Echo_f(void) {
int i;
for(i = 1; i < Cmd_Argc(); i++)
Com_Printf("%s ", Cmd_Argv(i));
Com_Printf("\n");
}
void Cmd_Alias_f(void) {
cmdalias_t *a;
char cmd[1024];
int i, c;
char *s;
if(Cmd_Argc() == 1) {
Com_Printf("Current alias commands:\n");
for(a = cmd_alias; a; a = a->next)
Com_Printf("%s : %s\n", a->name, a->value);
return;
}
s = Cmd_Argv(1);
if(strlen(s) >= MAX_ALIAS_NAME) {
Com_Printf("Alias name is too long\n");
return;
}
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);
cmd[0] = 0; c = Cmd_Argc();
for(i = 2; i < c; i++) {
strcat(cmd, Cmd_Argv(i));
if(i != (c - 1))
strcat(cmd, " ");
}
strcat(cmd, "\n");
a->value = CopyString(cmd);
}
typedef struct cmd_function_s {
struct cmd_function_s *next;
const char *name;
xcommand_t function;
} cmd_function_t;
static int cmd_argc;
static char *cmd_argv[MAX_STRING_TOKENS];
static char *cmd_null_string = "";
static char cmd_args[MAX_STRING_CHARS];
static cmd_function_t *cmd_functions;
int Cmd_Argc(void) { return cmd_argc; }
char *Cmd_Argv(int arg) {
if((unsigned)arg >= cmd_argc)
return cmd_null_string;
return cmd_argv[arg];
}
char *Cmd_Args(void) { return cmd_args; }
const char *Cmd_MacroExpandString(const char *text) {
int i, j, count, len;
bool inquote;
const char *scan;
static char expanded[MAX_STRING_CHARS];
char temporary[MAX_STRING_CHARS];
const char *token, *start;
inquote = false;
scan = text;
len = strlen(scan);
if(len >= MAX_STRING_CHARS) {
Com_Printf("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
return NULL;
}
count = 0;
for(i = 0; i < len; i++) {
if(scan[i] == '"')
inquote ^= 1;
if(inquote)
continue; if(scan[i] != '$')
continue;
start = scan + i + 1;
token = COM_Parse(&start);
if(!start)
continue;
token = Cvar_VariableString(token);
j = strlen(token);
len += j;
if(len >= MAX_STRING_CHARS) {
Com_Printf("Expanded line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
return NULL;
}
strncpy(temporary, scan, i);
strcpy(temporary + i, token);
strcpy(temporary + i + j, start);
strcpy(expanded, temporary);
scan = expanded;
i--;
if(++count == 100) {
Com_Printf("Macro expansion loop, discarded.\n");
return NULL;
}
}
if(inquote) {
Com_Printf("Line has unmatched quote, discarded.\n");
return NULL;
}
return scan;
}
void Cmd_TokenizeString(const char *text, bool macroExpand) {
int i;
char *com_token;
for(i = 0; i < cmd_argc; i++)
Z_Free(cmd_argv[i]);
cmd_argc = 0;
cmd_args[0] = 0;
if(macroExpand)
text = Cmd_MacroExpandString(text);
if(!text)
return;
while(1) {
while(*text && *text <= ' ' && *text != '\n') {
text++;
}
if(*text == '\n') { text++;
break;
}
if(!*text)
return;
if(cmd_argc == 1) {
int l;
strcpy(cmd_args, text);
l = strlen(cmd_args) - 1;
for(; l >= 0; l--)
if(cmd_args[l] <= ' ')
cmd_args[l] = 0;
else
break;
}
com_token = COM_Parse(&text);
if(!text)
return;
if(cmd_argc < MAX_STRING_TOKENS) {
cmd_argv[cmd_argc] = Z_Malloc(strlen(com_token) + 1);
strcpy(cmd_argv[cmd_argc], com_token);
cmd_argc++;
}
}
}
void Cmd_AddCommand(const char *cmd_name, xcommand_t function) {
cmd_function_t *cmd;
if(Cvar_VariableString(cmd_name)[0]) {
Com_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
return;
}
for(cmd = cmd_functions; cmd; cmd = cmd->next) {
if(!strcmp(cmd_name, cmd->name)) {
Com_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
return;
}
}
cmd = Z_Malloc(sizeof(cmd_function_t));
cmd->name = cmd_name;
cmd->function = function;
cmd->next = cmd_functions;
cmd_functions = cmd;
}
void Cmd_RemoveCommand(const char *cmd_name) {
cmd_function_t *cmd, **back;
back = &cmd_functions;
while(1) {
cmd = *back;
if(!cmd) {
Com_Printf("Cmd_RemoveCommand: %s not added\n", cmd_name);
return;
}
if(!strcmp(cmd_name, cmd->name)) {
*back = cmd->next;
Z_Free(cmd);
return;
}
back = &cmd->next;
}
}
bool Cmd_Exists(const char *cmd_name) {
cmd_function_t *cmd;
for(cmd = cmd_functions; cmd; cmd = cmd->next) {
if(!strcmp(cmd_name, cmd->name))
return true;
}
return false;
}
const char *Cmd_CompleteCommand(const char *partial) {
cmd_function_t *cmd;
int len;
cmdalias_t *a;
len = strlen(partial);
if(!len)
return NULL;
for(cmd = cmd_functions; cmd; cmd = cmd->next)
if(!strcmp(partial, cmd->name))
return cmd->name;
for(a = cmd_alias; a; a = a->next)
if(!strcmp(partial, a->name))
return a->name;
for(cmd = cmd_functions; cmd; cmd = cmd->next)
if(!strncmp(partial, cmd->name, len))
return cmd->name;
for(a = cmd_alias; a; a = a->next)
if(!strncmp(partial, a->name, len))
return a->name;
return NULL;
}
void Cmd_ExecuteString(const char *text) {
cmd_function_t *cmd;
cmdalias_t *a;
Cmd_TokenizeString(text, true);
if(!Cmd_Argc())
return;
for(cmd = cmd_functions; cmd; cmd = cmd->next) {
if(!Q_strcasecmp(cmd_argv[0], cmd->name)) {
if(!cmd->function) { Cmd_ExecuteString(va("cmd %s", text));
} else
cmd->function();
return;
}
}
for(a = cmd_alias; a; a = a->next) {
if(!Q_strcasecmp(cmd_argv[0], a->name)) {
if(++alias_count == ALIAS_LOOP_COUNT) {
Com_Printf("ALIAS_LOOP_COUNT\n");
return;
}
Cbuf_InsertText(a->value);
return;
}
}
if(Cvar_Command())
return;
Cmd_ForwardToServer();
}
void Cmd_List_f(void) {
cmd_function_t *cmd;
int i;
i = 0;
for(cmd = cmd_functions; cmd; cmd = cmd->next, i++)
Com_Printf("%s\n", cmd->name);
Com_Printf("%i commands\n", i);
}
void Cmd_Init(void) {
Cmd_AddCommand("cmdlist", Cmd_List_f);
Cmd_AddCommand("exec", Cmd_Exec_f);
Cmd_AddCommand("echo", Cmd_Echo_f);
Cmd_AddCommand("alias", Cmd_Alias_f);
Cmd_AddCommand("wait", Cmd_Wait_f);
}