Added thread support to GIB and cleaned up escape characters yet again.

With any luck it should actually work this time.  Added the getimpulse
command so that GIB scripts can check if an impulse command is pending
before sending their own.  Fixed all the memory leaks I could find.
QuakeC and GIB seem to be clean except for maybe one or two sneaky leaks
I can't track down.
This commit is contained in:
Brian Koropoff 2002-05-11 00:36:12 +00:00
parent 73e6cf062c
commit 3fffa26fb4
6 changed files with 226 additions and 109 deletions

View file

@ -89,6 +89,12 @@ typedef struct cmd_buffer_s {
struct cmd_buffer_s *prev, *next; // Neighboring buffers in stack struct cmd_buffer_s *prev, *next; // Neighboring buffers in stack
} cmd_buffer_t; } cmd_buffer_t;
typedef struct cmd_thread_s {
struct cmd_buffer_s *cbuf;
long int id;
struct cmd_thread_s *prev, *next;
} cmd_thread_t;
//=========================================================================== //===========================================================================
void escape (dstring_t * dstr, const char *clist); void escape (dstring_t * dstr, const char *clist);

View file

@ -122,6 +122,7 @@ menu_free (void *_m, void *unused)
free ((char*)p->name); free ((char*)p->name);
free (p); free (p);
} }
free (m);
} }
static void static void

View file

@ -402,12 +402,16 @@ static void
bi_strh_clear (progs_t *pr, void *data) bi_strh_clear (progs_t *pr, void *data)
{ {
strh_resources_t *res = (strh_resources_t *)data; strh_resources_t *res = (strh_resources_t *)data;
int i,d; int i,d,n;
for (i = 0; i < res->cnt_hashes; i++) { for (i = 0; i < res->cnt_hashes; i++) {
if (res->hashes[i]) { if (res->hashes[i]) {
for(d = 0; d < res->hashes[i]->cnt_elements; d++) { for(d = 0; d < res->hashes[i]->cnt_elements; d++) {
free(res->hashes[i]->elements[d]); free(res->hashes[i]->elements[d]->key); // Free the key
for (n = 0; n < MAX_SH_VALUES; n++) // Free all values
if (res->hashes[i]->elements[d]->values[n])
free(res->hashes[i]->elements[d]->values[n]);
free(res->hashes[i]->elements[d]); // Free the element itself
} }
free(res->hashes[i]->elements); free(res->hashes[i]->elements);
free(res->hashes[i]); free(res->hashes[i]);

View file

@ -63,19 +63,18 @@ typedef struct cmdalias_s {
cmdalias_t *cmd_alias; cmdalias_t *cmd_alias;
cmd_source_t cmd_source; cmd_source_t cmd_source;
/* FIXME: All these separate buffers are sort of hacky cmd_buffer_t *cmd_consolebuffer; // Console buffer
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.
*/
cmd_buffer_t *cmd_consolebuffer; // Console buffer
cmd_buffer_t *cmd_legacybuffer; // Server stuffcmd buffer with cmd_buffer_t *cmd_legacybuffer; // Server stuffcmd buffer with
// absolute backwards-compatibility // absolute backwards-compatibility
cmd_buffer_t *cmd_privatebuffer; // Buffer for internal command execution cmd_buffer_t *cmd_privatebuffer; // Buffer for internal command execution
cmd_buffer_t *cmd_keybindbuffer; // Buffer for commands from bound keys cmd_buffer_t *cmd_keybindbuffer; // Buffer for commands from bound keys
cmd_buffer_t *cmd_activebuffer; // Buffer currently being executed cmd_buffer_t *cmd_activebuffer; // Buffer currently being executed
cmd_buffer_t *cmd_recycled; // Recycled buffers cmd_buffer_t *cmd_recycled; // Recycled buffers
cmd_thread_t *cmd_threads; // Detached buffers running by themselves
long int cmd_threadid; // The id of the last thread + 1
dstring_t *cmd_backtrace; dstring_t *cmd_backtrace;
@ -89,7 +88,7 @@ hashtab_t *cmd_hash;
//============================================================================= //=============================================================================
/* Local variable stuff */ /* Structure management */
cmd_localvar_t * cmd_localvar_t *
Cmd_NewLocal (const char *key, const char *value) Cmd_NewLocal (const char *key, const char *value)
@ -221,6 +220,45 @@ Cmd_FreeStack (cmd_buffer_t *stack) {
} }
cmd_thread_t *
Cmd_NewThread (long int id) {
cmd_thread_t *new;
new = calloc (1, sizeof(cmd_thread_t));
SYS_CHECKMEM (new);
new->id = id;
new->cbuf = Cmd_NewBuffer (true);
return new;
}
void
Cmd_FreeThread (cmd_thread_t *thread) {
Cmd_FreeBuffer (thread->cbuf);
free (thread);
return;
}
void
Cmd_AddThread (cmd_thread_t **list, cmd_thread_t *thread) {
thread->next = *list;
thread->prev = 0;
if (*list)
(*list)->prev = thread;
*list = thread;
}
void
Cmd_RemoveThread (cmd_thread_t **list, cmd_thread_t *thread) {
if (thread == *list)
*list = thread->next;
if (thread->next)
thread->next->prev = thread->prev;
if (thread->prev)
thread->prev->next = thread->next;
Cmd_FreeThread (thread);
}
/*void /*void
Cmd_FreeBuffer (cmd_buffer_t *del) Cmd_FreeBuffer (cmd_buffer_t *del)
{ {
@ -345,6 +383,8 @@ Cbuf_Init (void)
cmd_activebuffer = cmd_consolebuffer; cmd_activebuffer = cmd_consolebuffer;
cmd_threads = 0;
cmd_threadid = 0;
cmd_recycled = 0; cmd_recycled = 0;
cmd_backtrace = dstring_newstr (); cmd_backtrace = dstring_newstr ();
@ -480,7 +520,6 @@ Cbuf_ExecuteBuffer (cmd_buffer_t *buffer)
Cbuf_ExtractLine (buffer->buffer, buf, buffer->legacy); Cbuf_ExtractLine (buffer->buffer, buf, buffer->legacy);
if (!buf->str[0]) if (!buf->str[0])
continue; continue;
Sys_DPrintf("Cbuf_ExecuteBuffer: Executing line %s\n", buf->str);
Cmd_TokenizeString (buf->str, cmd_activebuffer->legacy); Cmd_TokenizeString (buf->str, cmd_activebuffer->legacy);
if (cmd_error) if (cmd_error)
break; break;
@ -547,6 +586,16 @@ Cbuf_ExecuteStack (cmd_buffer_t *buffer)
void void
Cbuf_Execute (void) Cbuf_Execute (void)
{ {
cmd_thread_t *t, *temp;
for (t = cmd_threads; t; t = temp) {
temp = t->next;
if (!t->cbuf->next && !t->cbuf->buffer->str[0]) {
Cmd_RemoveThread (&cmd_threads, t);
continue;
}
Cbuf_ExecuteStack (t->cbuf);
}
Cbuf_ExecuteStack (cmd_keybindbuffer); Cbuf_ExecuteStack (cmd_keybindbuffer);
Cbuf_ExecuteStack (cmd_consolebuffer); Cbuf_ExecuteStack (cmd_consolebuffer);
Cbuf_ExecuteStack (cmd_legacybuffer); Cbuf_ExecuteStack (cmd_legacybuffer);
@ -1093,31 +1142,27 @@ Cmd_ProcessMath (dstring_t * dstr)
statement = dstring_newstr (); statement = dstring_newstr ();
for (i = 0; i < strlen (dstr->str); i++) { for (i = 0; i < strlen (dstr->str); i++) {
if (dstr->str[i] == '#' && dstr->str[i + 1] == '{') { if (dstr->str[i] == '#' && dstr->str[i + 1] == '{' && !escaped (dstr->str, i)) {
if (!escaped (dstr->str, i)) { n = Cmd_EndBrace (dstr->str+i+1)+1;
i -= unescape (dstr, i); if (n < 0) {
n = Cmd_EndBrace (dstr->str+i+1)+1; Cmd_Error ("Unmatched brace in math expression.\n");
if (n < 0) { ret = -1;
Cmd_Error ("Unmatched brace in math expression.\n"); break;
ret = -1; }
break; /* Copy text between parentheses into a buffer */
} dstring_clearstr (statement);
/* Copy text between parentheses into a buffer */ dstring_insert (statement, dstr->str + i + 2, n - 2, 0);
dstring_clearstr (statement); value = EXP_Evaluate (statement->str);
dstring_insert (statement, dstr->str + i + 2, n - 2, 0); if (EXP_ERROR == EXP_E_NORMAL) {
value = EXP_Evaluate (statement->str); temp = va ("%.10g", value);
if (EXP_ERROR == EXP_E_NORMAL) { dstring_snip (dstr, i, n + 1); // Nuke the statement
temp = va ("%.10g", value); dstring_insertstr (dstr, temp, i); // Stick in the value
dstring_snip (dstr, i, n + 1); // Nuke the statement i += strlen (temp) - 1;
dstring_insertstr (dstr, temp, i); // Stick in the value } else {
i += strlen (temp) - 1; ret = -2;
} else { Cmd_Error (va("Math error: invalid expression %s\n", statement->str));
ret = -2; break; // Math evaluation error
Cmd_Error (va("Math error: invalid expression %s\n", statement->str)); }
break; // Math evaluation error
}
} else
i -= unescape (dstr, i);
} }
} }
dstring_delete (statement); dstring_delete (statement);
@ -1183,18 +1228,14 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start)
if (dstr->str[start+1] == '{') if (dstr->str[start+1] == '{')
braces = 1; braces = 1;
for (i = start + 1 + braces;; i++) { for (i = start + 1 + braces;; i++) {
if (dstr->str[i] == '$' && (braces || dstr->str[i-1] == '$')) { if (dstr->str[i] == '$' && (braces || dstr->str[i-1] == '$') && !escaped (dstr->str, i)) {
if (!escaped (dstr->str, i)) { n = Cmd_ProcessVariablesRecursive (dstr, i);
i -= unescape (dstr, i); if (n < 0) {
n = Cmd_ProcessVariablesRecursive (dstr, i); break;
if (n < 0) { } else {
break; i += n - 1;
} else { continue;
i += n - 1; }
continue;
}
} else
i -= unescape (dstr, i);
} else if (!dstr->str[i] && braces) { // No closing brace } else if (!dstr->str[i] && braces) { // No closing brace
Cmd_Error ("Unmatched brace in variable substitution expression.\n"); Cmd_Error ("Unmatched brace in variable substitution expression.\n");
n = -1; n = -1;
@ -1214,6 +1255,8 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start)
// Then cvars // Then cvars
dstring_appendstr (copy, cvar->string); dstring_appendstr (copy, cvar->string);
} }
n = 0;
dstring_clearstr (varname); // Reuse variable
if (dstr->str[start] == '[') { if (dstr->str[start] == '[') {
int val1, val2, res; int val1, val2, res;
res = Cmd_ProcessIndex (dstr, start, &val1, &val2); res = Cmd_ProcessIndex (dstr, start, &val1, &val2);
@ -1221,16 +1264,16 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start)
n = -1; n = -1;
else if (val1 >= strlen(copy->str) || val1 < 0) else if (val1 >= strlen(copy->str) || val1 < 0)
n = 0; n = 0;
else if (val2 < strlen(copy->str)) { else if (val2 < strlen(copy->str))
dstring_insert (dstr, copy->str+val1, val2-val1+1, start); dstring_insert (varname, copy->str+val1, val2-val1+1, 0);
n = val2 - val1 + 1; else
} else { dstring_insert (varname, copy->str+val1, strlen(copy->str)-val1, 0);
dstring_insert (dstr, copy->str+val1, strlen(copy->str)-val1, start); } else
n = strlen(copy->str)-val1; dstring_appendstr (varname, copy->str);
} if (n >= 0) {
} else { escape (varname, "\\");
dstring_insertstr (dstr, copy->str, start); dstring_insertstr (dstr, varname->str, start);
n = strlen(copy->str); n = strlen(varname->str);
} }
dstring_delete (copy); dstring_delete (copy);
dstring_delete (varname); dstring_delete (varname);
@ -1258,6 +1301,7 @@ Cmd_ProcessEmbeddedSingle (dstring_t * dstr, int start)
return -1; return -1;
} }
if (cmd_activebuffer->returned == cmd_returned) { if (cmd_activebuffer->returned == cmd_returned) {
escape (cmd_activebuffer->retval, "\\");
dstring_snip(dstr, start, n+1); dstring_snip(dstr, start, n+1);
dstring_insertstr (dstr, cmd_activebuffer->retval->str, start); dstring_insertstr (dstr, cmd_activebuffer->retval->str, start);
n = strlen(cmd_activebuffer->retval->str); n = strlen(cmd_activebuffer->retval->str);
@ -1283,20 +1327,16 @@ Cmd_ProcessEmbedded (cmd_token_t *tok, dstring_t * dstr)
int i, n; int i, n;
for (i = tok->pos; i < strlen(dstr->str); i++) { for (i = tok->pos; i < strlen(dstr->str); i++) {
if (dstr->str[i] == '~' && dstr->str[i+1] == '{') { if (dstr->str[i] == '~' && dstr->str[i+1] == '{' && !escaped (dstr->str, i)) {
if (!escaped (dstr->str, i)) { n = Cmd_ProcessEmbeddedSingle (dstr, i);
i -= unescape (dstr, i); if (n == -2) {
n = Cmd_ProcessEmbeddedSingle (dstr, i); tok->pos = i;
if (n == -2) { return n;
tok->pos = i; }
return n; if (n < 0)
} return n;
if (n < 0) else
return n; i += n-1;
else
i += n-1;
} else
i -= unescape (dstr, i);
} }
} }
return 0; return 0;
@ -1308,16 +1348,12 @@ Cmd_ProcessVariables (dstring_t * dstr)
int i, n; int i, n;
for (i = 0; i < strlen (dstr->str); i++) { for (i = 0; i < strlen (dstr->str); i++) {
if (dstr->str[i] == '$') { if (dstr->str[i] == '$' && !escaped (dstr->str, i)) {
if (!escaped (dstr->str, i)) { n = Cmd_ProcessVariablesRecursive (dstr, i);
i -= unescape (dstr, i); if (n < 0)
n = Cmd_ProcessVariablesRecursive (dstr, i); return n;
if (n < 0) else
return n; i += n - 1;
else
i += n - 1;
} else
i -= unescape (dstr, i);
} }
} }
return 0; return 0;
@ -1335,27 +1371,24 @@ Cmd_ProcessVariables (dstring_t * dstr)
tags, etc. can be escaped tags, etc. can be escaped
*/ */
void int
Cmd_ProcessEscapes (dstring_t * dstr, const char *noprocess) Cmd_ProcessEscapes (dstring_t * dstr, const char *noprocess)
{ {
int i; int i;
if (strlen(dstr->str) == 1) if (strlen(dstr->str) == 1)
return; return 0;
for (i = 1; i <= strlen (dstr->str); i++) { for (i = 0; i < strlen (dstr->str); i++)
if (!(dstr->str[i] && strchr (noprocess, dstr->str[i])) && dstr->str[i-1] == '\\' && dstr->str[i] != '\\') { if (dstr->str[i] == '\\') {
if (dstr->str[i] == 'n' && escaped (dstr->str, i)) dstring_snip (dstr, i, 1);
dstr->str[i] = '\n';
i -= unescape (dstr, i);
} }
} return 0;
} }
int int
Cmd_ProcessToken (cmd_token_t *token) Cmd_ProcessToken (cmd_token_t *token)
{ {
int res; int res;
Cmd_ProcessEscapes (token->processed, "$#~");
res = Cmd_ProcessEmbedded (token, token->processed); res = Cmd_ProcessEmbedded (token, token->processed);
if (res < 0) if (res < 0)
return res; return res;
@ -1366,6 +1399,9 @@ Cmd_ProcessToken (cmd_token_t *token)
if (res < 0) if (res < 0)
return res; return res;
Cmd_ProcessTags (token->processed); Cmd_ProcessTags (token->processed);
res = Cmd_ProcessEscapes (token->processed, "$#~");
if (res < 0)
return res;
token->state = cmd_done; token->state = cmd_done;
return 0; return 0;
} }
@ -2004,6 +2040,10 @@ Cmd_For_f (void) {
Cmd_ExecuteSubroutine (sub); Cmd_ExecuteSubroutine (sub);
} else } else
Cmd_Error("Malformed for statement.\n"); Cmd_Error("Malformed for statement.\n");
dstring_delete (arg1);
dstring_delete (init);
dstring_delete (cond);
dstring_delete (inc);
return; return;
} }
@ -2096,6 +2136,53 @@ Cmd_Strlen_f (void)
Cmd_Return (va("%ld", (long) strlen (Cmd_Argv(1)))); Cmd_Return (va("%ld", (long) strlen (Cmd_Argv(1))));
} }
void
Cmd_Detach_f (void)
{
cmd_thread_t *thread;
if (Cmd_Argc () != 2) {
Cmd_Error ("detach: invalid number of arguments.\n");
return;
}
thread = Cmd_NewThread (cmd_threadid++);
Cmd_AddThread (&cmd_threads, thread);
Cbuf_AddTextTo (thread->cbuf, Cmd_Argv(1));
Cbuf_ExecuteStack (thread->cbuf); // Execute it now
cmd_error = false; // Don't let errors cross into this stack
Cmd_Return (va("%li", thread->id));
}
void
Cmd_Killthread_f (void)
{
long int id;
cmd_thread_t *t;
if (Cmd_Argc() != 2)
Cmd_Error ("killthread: invalid number of arguments.\n");
id = atol (Cmd_Argv(1));
for (t = cmd_threads; t; t = t->next)
if (id == t->id) {
if (t->cbuf->next)
Cmd_FreeStack (t->cbuf->next);
t->cbuf->next = 0;
dstring_clearstr (t->cbuf->buffer);
return;
}
Cmd_Error ("kill: invalid thread id\n");
}
void
Cmd_Threadstats_f (void)
{
cmd_thread_t *t;
Sys_Printf ("Currently running threads:\n");
for (t = cmd_threads; t; t = t->next)
Sys_Printf("%li\n", t->id);
}
void void
Cmd_Hash_Stats_f (void) Cmd_Hash_Stats_f (void)
{ {
@ -2179,6 +2266,9 @@ Cmd_Init (void)
Cmd_AddCommand ("randint", Cmd_Randint_f, "Returns a random integer between $1 and $2"); Cmd_AddCommand ("randint", Cmd_Randint_f, "Returns a random integer between $1 and $2");
Cmd_AddCommand ("streq", Cmd_Streq_f, "Returns 1 if $1 and $2 are the same string, 0 otherwise"); Cmd_AddCommand ("streq", Cmd_Streq_f, "Returns 1 if $1 and $2 are the same string, 0 otherwise");
Cmd_AddCommand ("strlen", Cmd_Strlen_f, "Returns the length of $1"); Cmd_AddCommand ("strlen", Cmd_Strlen_f, "Returns the length of $1");
Cmd_AddCommand ("detach", Cmd_Detach_f, "Starts a thread with an initial program of $1");
Cmd_AddCommand ("killthread", Cmd_Killthread_f, "Kills thread with id $1");
Cmd_AddCommand ("threadstats", Cmd_Threadstats_f, "Shows statistics about threads");
//Cmd_AddCommand ("cmd_hash_stats", Cmd_Hash_Stats_f, "Display statistics " //Cmd_AddCommand ("cmd_hash_stats", Cmd_Hash_Stats_f, "Display statistics "
// "alias and command hash tables"); // "alias and command hash tables");

View file

@ -45,6 +45,7 @@ static const char rcsid[] =
#include "QF/keys.h" #include "QF/keys.h"
#include "QF/msg.h" #include "QF/msg.h"
#include "QF/teamplay.h" #include "QF/teamplay.h"
#include "QF/va.h"
#include "cl_cam.h" #include "cl_cam.h"
#include "cl_demo.h" #include "cl_demo.h"
@ -365,6 +366,12 @@ IN_Impulse (void)
Team_BestWeaponImpulse (); // HACK HACK HACK Team_BestWeaponImpulse (); // HACK HACK HACK
} }
void
IN_Getimpulse_f (void)
{
Cmd_Return(va("%i", in_impulse));
}
/* /*
CL_KeyState CL_KeyState
@ -795,6 +802,8 @@ CL_Input_Init (void)
"jumping"); "jumping");
Cmd_AddCommand ("impulse", IN_Impulse, "Call a game function or QuakeC " Cmd_AddCommand ("impulse", IN_Impulse, "Call a game function or QuakeC "
"function."); "function.");
Cmd_AddCommand ("getimpulse", IN_Getimpulse_f, "Returns the impulse that "
"will be sent to the server this frame");
Cmd_AddCommand ("+klook", IN_KLookDown, "When active, +forward and +back " Cmd_AddCommand ("+klook", IN_KLookDown, "When active, +forward and +back "
"perform +lookup and +lookdown"); "perform +lookup and +lookdown");
Cmd_AddCommand ("-klook", IN_KLookUp, "When active, +forward and +back " Cmd_AddCommand ("-klook", IN_KLookUp, "When active, +forward and +back "

View file

@ -453,6 +453,7 @@ void
CL_Disconnect (void) CL_Disconnect (void)
{ {
byte final[10]; byte final[10];
int i;
connect_time = -1; connect_time = -1;
@ -494,6 +495,12 @@ CL_Disconnect (void)
Team_ResetTimers (); Team_ResetTimers ();
// remove player info strings
for (i = 0; i < MAX_CLIENTS; i++)
if (cl.players[i].userinfo) {
Info_Destroy (cl.players[i].userinfo);
cl.players[i].userinfo = 0;
}
} }
void void