Fixed some bugs and generalized the command buffer interface. Allowed for

buffers to be linked into execution stacks and changed aliases and script
files to be run in a new stack frame.  Cbuf_Execute executes the stack from
the top down, so wait commands are handled properly.
This commit is contained in:
Brian Koropoff 2002-03-18 04:12:09 +00:00
parent f4180e7ad8
commit 46cc4ac9dd
3 changed files with 152 additions and 85 deletions

View file

@ -33,6 +33,19 @@
#include "QF/qtypes.h"
#include "QF/dstring.h"
typedef struct cmd_buffer_s {
dstring_t *buffer; // Actual text
qboolean wait; // Execution stopped until next frame
qboolean legacy; // Backwards compatible with old command buffer style
unsigned int argc, maxargc; // Number of args, number of args allocated
dstring_t **argv; // Array of arguments
dstring_t *realline; // Actual command being processed
dstring_t *line; // Tokenized and reassembled command
int *args; // Array of positions of each token in above string
int *argspace; // Amount of space before each token
struct cmd_buffer_s *prev, *next; // Next buffer in the stack
} cmd_buffer_t;
//===========================================================================
/*
@ -51,7 +64,7 @@ The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute ();
void Cbuf_Init (void);
// allocates an initial text buffer that will grow as needed
void Cbuf_AddTextTo (dstring_t *buffer, const char *text);
void Cbuf_AddTextTo (cmd_buffer_t *buffer, const char *text);
void Cbuf_AddText (const char *text);
// as new commands are generated from the console or keybindings,
// the text is added to the end of the command buffer.
@ -153,6 +166,6 @@ const char *COM_Parse (const char *data);
extern struct cvar_s *cmd_warncmd;
extern dstring_t *cmd_legacybuffer; // Allow access to the legacy buffer as an alternate console buffer
extern cmd_buffer_t *cmd_legacybuffer; // Allow access to the legacy buffer as an alternate console buffer
#endif // __cmd_h

View file

@ -67,19 +67,9 @@ The command buffer interface should be generalized and
each part of QF (console, stufftext, config files and scripts)
that needs one should allocate and maintain its own.
*/
dstring_t *cmd_buffer; // Console buffer
dstring_t *cmd_legacybuffer; // Server stuffcmd buffer with absolute backwards-compatibility
dstring_t *cmd_activebuffer; // Buffer currently being executed
dstring_t *cmd_trueline; // The true, unadulterated line last tokenized
dstring_t *cmd_argbuf; // Buffer for reconstructing tokens into single string
qboolean cmd_wait = false;
qboolean cmd_legacy = false; // Are we executing current buffer in legacy mode?
int cmd_argc;
int cmd_maxargc = 0;
dstring_t **cmd_argv = 0;
int *cmd_args = 0;
int *cmd_argspace = 0;
cmd_buffer_t *cmd_consolebuffer; // Console buffer
cmd_buffer_t *cmd_legacybuffer; // Server stuffcmd buffer with absolute backwards-compatibility
cmd_buffer_t *cmd_activebuffer; // Buffer currently being executed
cvar_t *cmd_warncmd;
cvar_t *cmd_highchars;
@ -89,6 +79,35 @@ hashtab_t *cmd_hash;
//=============================================================================
/* Functions to manage command buffers */
cmd_buffer_t *
Cmd_NewBuffer (void)
{
cmd_buffer_t *new;
new = calloc(1, sizeof(cmd_buffer_t));
new->buffer = dstring_newstr ();
new->line = dstring_newstr ();
new->realline = dstring_newstr ();
return new;
}
void
Cmd_FreeBuffer (cmd_buffer_t *del)
{
int i;
dstring_delete (del->buffer);
dstring_delete (del->line);
dstring_delete (del->realline);
for (i = 0; i < del->maxargc; i++)
if (del->argv[i])
dstring_delete(del->argv[i]);
free(del);
}
/* Quick function to determine if a character is escaped */
qboolean
@ -134,7 +153,9 @@ escape (dstring_t *dstr)
void
Cmd_Wait_f (void)
{
cmd_wait = true;
cmd_buffer_t *cur;
for (cur = cmd_activebuffer; cur; cur = cur->prev)
cur->wait = true;
}
/*
@ -146,19 +167,19 @@ byte cmd_text_buf[8192];
void
Cbuf_Init (void)
{
cmd_consolebuffer = Cmd_NewBuffer ();
cmd_buffer = dstring_newstr ();
cmd_legacybuffer = dstring_newstr ();
cmd_activebuffer = cmd_buffer;
cmd_argbuf = dstring_newstr ();
cmd_trueline = dstring_newstr ();
cmd_legacybuffer = Cmd_NewBuffer ();
cmd_legacybuffer->legacy = true;
cmd_activebuffer = cmd_consolebuffer;
}
void
Cbuf_AddTextTo (dstring_t *buffer, const char *text)
Cbuf_AddTextTo (cmd_buffer_t *buffer, const char *text)
{
dstring_appendstr (buffer, text);
dstring_appendstr (buffer->buffer, text);
}
/*
@ -174,10 +195,10 @@ Cbuf_AddText (const char *text)
}
void
Cbuf_InsertTextTo (dstring_t *buffer, const char *text)
Cbuf_InsertTextTo (cmd_buffer_t *buffer, const char *text)
{
dstring_insertstr (buffer, "\n", 0);
dstring_insertstr (buffer, text, 0);
dstring_insertstr (buffer->buffer, "\n", 0);
dstring_insertstr (buffer->buffer, text, 0);
}
/* Cbuf_InsertText
@ -222,8 +243,14 @@ extract_line (dstring_t *buffer, dstring_t *line)
dstring_snip(buffer, i, n);
}
if (buffer->str[i] == '\n' || buffer->str[i] == '\r') {
if (braces)
if (braces) {
if (i && buffer->str[i-1] != ';')
buffer->str[i] = ';';
else {
dstring_snip(buffer, i, 1);
i--;
}
}
else
break;
}
@ -245,16 +272,16 @@ extract_line (dstring_t *buffer, dstring_t *line)
*/
void
Cbuf_ExecuteBuffer (dstring_t *buffer)
Cbuf_ExecuteBuffer (cmd_buffer_t *buffer)
{
dstring_t *buf = dstring_newstr ();
dstring_t *temp = cmd_activebuffer; // save old context
cmd_buffer_t *temp = cmd_activebuffer; // save old context
cmd_activebuffer = buffer;
while (strlen(buffer->str)) {
extract_line (buffer, buf);
buffer->wait = false;
while (strlen(buffer->buffer->str)) {
extract_line (buffer->buffer, buf);
Cmd_ExecuteString(buf->str, src_command);
if (cmd_wait) {
cmd_wait = false;
if (buffer->wait) {
break;
}
dstring_clearstr(buf);
@ -266,10 +293,40 @@ Cbuf_ExecuteBuffer (dstring_t *buffer)
void
Cbuf_Execute (void)
{
Cbuf_ExecuteBuffer (cmd_buffer); // Console buffer gets precedence
cmd_legacy = true;
qboolean wait = false;
cmd_buffer_t *cur;
// Execute the buffer stack first
while (cmd_consolebuffer->next) {
for (cur = cmd_consolebuffer; cur->next; cur=cur->next);
Cbuf_ExecuteBuffer (cur);
if (cur->wait) {
wait = true;
break;
}
else {
cur->prev->next = 0;
Cmd_FreeBuffer (cur);
}
}
if (!wait)
Cbuf_ExecuteBuffer (cmd_consolebuffer); // Then console buffer
Cbuf_ExecuteBuffer (cmd_legacybuffer); // Then legacy/stufftext buffer
cmd_legacy = false;
}
void Cmd_ExecuteSubroutine (const char *text)
{
cmd_buffer_t *buffer = Cmd_NewBuffer ();
cmd_activebuffer->next = buffer;
buffer->prev = cmd_activebuffer;
Cbuf_InsertTextTo (buffer, text);
Cbuf_ExecuteBuffer (buffer);
if (!buffer->wait) {
Cmd_FreeBuffer(buffer);
cmd_activebuffer->next = 0;
}
return;
}
/*
Cbuf_Execute_Sets
@ -285,8 +342,8 @@ Cbuf_Execute_Sets (void)
{
dstring_t *buf = dstring_newstr ();
while (strlen(cmd_buffer->str)) {
extract_line (cmd_buffer, buf);
while (strlen(cmd_consolebuffer->buffer->str)) {
extract_line (cmd_consolebuffer->buffer, buf);
if (!strncmp (buf->str, "set", 3) && isspace ((int) buf->str[3])) {
Cmd_ExecuteString (buf->str, src_command);
} else if (!strncmp (buf->str, "setrom", 6) && isspace ((int) buf->str[6])) {
@ -370,7 +427,7 @@ Cmd_Exec_File (const char *path)
f[len] = 0;
Qread (file, f, len);
Qclose (file);
Cbuf_InsertTextTo (cmd_buffer, f); // Always insert into console
Cbuf_InsertTextTo (cmd_consolebuffer, f); // Always insert into console
free (f);
}
}
@ -393,12 +450,10 @@ Cmd_Exec_f (void)
Sys_Printf ("couldn't exec %s\n", Cmd_Argv (1));
return;
}
Cbuf_InsertTextTo (cmd_buffer, f); // Always insert into console
Hunk_FreeToLowMark (mark);
if (!Cvar_Command () && (cmd_warncmd->int_val
|| (developer && developer->int_val)))
if (!Cvar_Command () && (cmd_warncmd->int_val || (developer && developer->int_val)))
Sys_Printf ("execing %s\n", Cmd_Argv (1));
Cmd_ExecuteSubroutine (f); // Execute file in it's own buffer
Hunk_FreeToLowMark (mark);
}
/*
@ -512,15 +567,15 @@ static cmd_function_t *cmd_functions; // possible commands to execute
int
Cmd_Argc (void)
{
return cmd_argc;
return cmd_activebuffer->argc;
}
const char *
Cmd_Argv (int arg)
{
if (arg >= cmd_argc)
if (arg >= cmd_activebuffer->argc)
return "";
return cmd_argv[arg]->str;
return cmd_activebuffer->argv[arg]->str;
}
/*
@ -531,9 +586,9 @@ Cmd_Argv (int arg)
const char *
Cmd_Args (int start)
{
if (start >= cmd_argc)
if (start >= cmd_activebuffer->argc)
return "";
return cmd_argbuf->str + cmd_args[start];
return cmd_activebuffer->line->str + cmd_activebuffer->args[start];
}
@ -776,12 +831,12 @@ Cmd_ProcessMath (dstring_t *dstr)
else if (!dstr->str[i+n])
return -1; // Open parentheses, give up
}
/* Copy text between braces into a buffer */
/* Copy text between parentheses into a buffer */
dstring_clearstr (statement);
dstring_insert (statement, dstr->str+i+2, n-2, 0);
value = EXP_Evaluate (statement->str);
if (EXP_ERROR == EXP_E_NORMAL) {
temp = va("%f", value);
temp = va("%g", value);
dstring_snip (dstr, i, n+1); // Nuke the statement
dstring_insertstr (dstr, temp, i); // Stick in the value
i += strlen(temp) - 1;
@ -874,8 +929,7 @@ Cmd_TokenizeString (const char *text, qboolean filter)
{
int i = 0, n, len = 0, quotes, braces, space, res;
const char *str = text;
cmd_argc = 0;
unsigned int cmd_argc = 0;
/* Turn off tags at the beginning of a command.
This causes tags to continue past token boundaries. */
@ -898,21 +952,21 @@ Cmd_TokenizeString (const char *text, qboolean filter)
else if (len == 0)
break;
cmd_argc++;
if (cmd_argc > cmd_maxargc) {
cmd_argv = realloc(cmd_argv, sizeof(dstring_t *)*cmd_argc);
if (!cmd_argv)
if (cmd_argc > cmd_activebuffer->maxargc) {
cmd_activebuffer->argv = realloc(cmd_activebuffer->argv, sizeof(dstring_t *)*cmd_argc);
if (!cmd_activebuffer->argv)
Sys_Error ("Cmd_TokenizeString: Failed to reallocate memory.\n");
cmd_args = realloc(cmd_args, sizeof(int)*cmd_argc);
if (!cmd_args)
cmd_activebuffer->args = realloc(cmd_activebuffer->args, sizeof(int)*cmd_argc);
if (!cmd_activebuffer->args)
Sys_Error ("Cmd_TokenizeString: Failed to reallocate memory.\n");
cmd_argspace = realloc(cmd_argspace, sizeof(int)*cmd_argc);
if (!cmd_argspace)
cmd_activebuffer->argspace = realloc(cmd_activebuffer->argspace, sizeof(int)*cmd_argc);
if (!cmd_activebuffer->argspace)
Sys_Error ("Cmd_TokenizeString: Failed to reallocate memory.\n");
cmd_argv[cmd_argc-1] = dstring_newstr ();
cmd_maxargc++;
cmd_activebuffer->argv[cmd_argc-1] = dstring_newstr ();
cmd_activebuffer->maxargc++;
}
dstring_clearstr(cmd_argv[cmd_argc-1]);
cmd_argspace[cmd_argc-1] = space;
dstring_clearstr(cmd_activebuffer->argv[cmd_argc-1]);
cmd_activebuffer->argspace[cmd_argc-1] = space;
/* Remove surrounding quotes or double quotes or braces*/
quotes = 0;
braces = 0;
@ -926,12 +980,12 @@ Cmd_TokenizeString (const char *text, qboolean filter)
len-=1;
braces = 1;
}
dstring_insert(cmd_argv[cmd_argc-1], str + i, len, 0);
dstring_insert(cmd_activebuffer->argv[cmd_argc-1], str + i, len, 0);
if (filter && text[0] != '|') {
if (!braces) {
Cmd_ProcessTags(cmd_argv[cmd_argc-1]);
Cmd_ProcessVariables(cmd_argv[cmd_argc-1]);
res = Cmd_ProcessMath(cmd_argv[cmd_argc-1]);
Cmd_ProcessTags(cmd_activebuffer->argv[cmd_argc-1]);
Cmd_ProcessVariables(cmd_activebuffer->argv[cmd_argc-1]);
res = Cmd_ProcessMath(cmd_activebuffer->argv[cmd_argc-1]);
if (res == -1) {
Sys_Printf("Parse error: Unmatched parenthesis in following line:\n--> %s\n", text);
cmd_argc = 0;
@ -943,21 +997,22 @@ Cmd_TokenizeString (const char *text, qboolean filter)
return;
}
}
Cmd_ProcessEscapes(cmd_argv[cmd_argc-1]);
Cmd_ProcessEscapes(cmd_activebuffer->argv[cmd_argc-1]);
}
i += len + quotes + braces; /* If we ended on a quote or brace, skip it */
}
/* Now we must reconstruct cmd_args */
dstring_clearstr (cmd_argbuf);
dstring_clearstr (cmd_activebuffer->line);
for (i = 0; i < cmd_argc; i++) {
for (n = 0; n < cmd_argspace[i]; n++)
dstring_appendstr (cmd_argbuf," ");
cmd_args[i] = strlen(cmd_argbuf->str);
dstring_appendstr (cmd_argbuf, cmd_argv[i]->str);
for (n = 0; n < cmd_activebuffer->argspace[i]; n++)
dstring_appendstr (cmd_activebuffer->line," ");
cmd_activebuffer->args[i] = strlen(cmd_activebuffer->line->str);
dstring_appendstr (cmd_activebuffer->line, cmd_activebuffer->argv[i]->str);
}
dstring_clearstr (cmd_trueline);
dstring_appendstr (cmd_trueline, text);
dstring_clearstr (cmd_activebuffer->realline);
dstring_appendstr (cmd_activebuffer->realline, text);
cmd_activebuffer->argc = cmd_argc;
}
void
@ -1195,14 +1250,14 @@ Cmd_ExecuteString (const char *text, cmd_source_t src)
cmdalias_t *a;
cmd_source = src;
Cmd_TokenizeString (text, !cmd_legacy);
Cmd_TokenizeString (text, !cmd_activebuffer->legacy);
// execute the command line
if (!Cmd_Argc ())
return; // no tokens
// check functions
cmd = (cmd_function_t*)Hash_Find (cmd_hash, cmd_argv[0]->str);
cmd = (cmd_function_t*)Hash_Find (cmd_hash, Cmd_Argv(0));
if (cmd) {
if (cmd->function)
cmd->function ();
@ -1214,19 +1269,18 @@ Cmd_ExecuteString (const char *text, cmd_source_t src)
return;
// check alias
a = (cmdalias_t*)Hash_Find (cmd_alias_hash, cmd_argv[0]->str);
a = (cmdalias_t*)Hash_Find (cmd_alias_hash, Cmd_Argv(0));
if (a) {
dstring_t *temp = dstring_newstr ();
dstring_appendstr (temp, a->value);
Cmd_ProcessPercents (temp);
Cbuf_InsertText (temp->str);
Cmd_ExecuteSubroutine (temp->str);
dstring_delete (temp);
return;
}
if (cmd_warncmd->int_val || developer->int_val)
Sys_Printf ("Unknown command \"%s\"\n", Cmd_Argv (0));
cmd_argc = 0;
}
/*
@ -1338,7 +1392,7 @@ Cmd_While_f (void) {
num = strtol (Cmd_Argv(1), 0, 10);
if (num) {
Cbuf_InsertText (cmd_trueline->str); // Run while loop again
Cbuf_InsertText (cmd_activebuffer->realline->str); // Run while loop again
Cbuf_InsertText (Cmd_Argv(2)); // But not before executing body
}
return;

View file

@ -75,5 +75,5 @@ float OP_GreaterThanEqual (float op1, float op2)
}
float OP_LessThanEqual (float op1, float op2)
{
return op2 <= op1;
return op1 <= op2;
}