diff --git a/include/QF/cmd.h b/include/QF/cmd.h index 8ab906021..c572d9cba 100644 --- a/include/QF/cmd.h +++ b/include/QF/cmd.h @@ -89,6 +89,12 @@ typedef struct cmd_buffer_s { struct cmd_buffer_s *prev, *next; // Neighboring buffers in stack } 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); diff --git a/libs/console/menu.c b/libs/console/menu.c index 339a3abb5..41bdabd69 100644 --- a/libs/console/menu.c +++ b/libs/console/menu.c @@ -122,6 +122,7 @@ menu_free (void *_m, void *unused) free ((char*)p->name); free (p); } + free (m); } static void diff --git a/libs/gamecode/builtins/bi_strhash.c b/libs/gamecode/builtins/bi_strhash.c index 9685855a4..f694a39a6 100644 --- a/libs/gamecode/builtins/bi_strhash.c +++ b/libs/gamecode/builtins/bi_strhash.c @@ -225,7 +225,7 @@ bi_StringHash_Set (progs_t *pr) sh->elements = (str_hash_elem**) malloc(sizeof(str_hash_elem*)); sh->cnt_elements = 0; // 0 because usage as index here } else { - sh->elements = (str_hash_elem**) realloc(sh->elements, + sh->elements = (str_hash_elem**) realloc(sh->elements, sizeof(str_hash_elem*) * (sh->cnt_elements+1)); } sh->elements[sh->cnt_elements] = malloc(sizeof(str_hash_elem)); @@ -260,7 +260,7 @@ bi_StringHash_SetIdx (progs_t *pr) str_hash *sh = NULL; // validate the hash ID - if(res->hashes == NULL || + if(res->hashes == NULL || (hash_id >= res->cnt_hashes || hash_id < 0) || (val_id < 0 || val_id >= MAX_SH_VALUES)) { @@ -270,7 +270,7 @@ bi_StringHash_SetIdx (progs_t *pr) sh = res->hashes[hash_id]; if(idx < 0 || idx >= sh->cnt_elements || sh->elements[idx] == NULL) { - if(sh->elements[idx] == NULL) + if(sh->elements[idx] == NULL) PR_Error(pr, "NULL hash-element found -> not supposed!"); G_INT (pr, OFS_RETURN) = 0; @@ -304,8 +304,8 @@ bi_StringHash_Get (progs_t *pr) const char *retstr = NULL; // validate the hash ID - if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0 || - val_id >= MAX_SH_VALUES) + if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0 || + val_id >= MAX_SH_VALUES) { retstr = ""; RETURN_STRING(pr, retstr); @@ -375,8 +375,8 @@ bi_StringHash_GetIdx (progs_t *pr) } sh = res->hashes[hash_id]; - if(idx < 0 || idx >= sh->cnt_elements || - (val_id < -1 || val_id >= MAX_SH_VALUES)) + if(idx < 0 || idx >= sh->cnt_elements || + (val_id < -1 || val_id >= MAX_SH_VALUES)) { retstr = NULL; } else { @@ -402,12 +402,16 @@ static void bi_strh_clear (progs_t *pr, void *data) { strh_resources_t *res = (strh_resources_t *)data; - int i,d; + int i,d,n; for (i = 0; i < res->cnt_hashes; i++) { if (res->hashes[i]) { 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]); @@ -443,10 +447,10 @@ StringHash_Progs_Init (progs_t *pr) PR_AddBuiltin (pr, "StringHash_GetIdx", bi_StringHash_GetIdx, -1); } -/* - XXX NOTE by elmex: - A file, decripted like this is what - i want to see everywhere in qf-cvs =) +/* + XXX NOTE by elmex: + A file, decripted like this is what + i want to see everywhere in qf-cvs =) No excuse for undocumented code and design without a reason for it. We/I want to know why something was designed how it is. diff --git a/libs/util/cmd.c b/libs/util/cmd.c index a2554b61c..5c37a6c36 100644 --- a/libs/util/cmd.c +++ b/libs/util/cmd.c @@ -63,19 +63,18 @@ typedef struct cmdalias_s { cmdalias_t *cmd_alias; cmd_source_t cmd_source; -/* FIXME: All these separate buffers are sort of hacky -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_consolebuffer; // Console buffer cmd_buffer_t *cmd_legacybuffer; // Server stuffcmd buffer with - // absolute backwards-compatibility -cmd_buffer_t *cmd_privatebuffer; // Buffer for internal command execution + // absolute backwards-compatibility +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_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; @@ -89,7 +88,7 @@ hashtab_t *cmd_hash; //============================================================================= -/* Local variable stuff */ +/* Structure management */ cmd_localvar_t * Cmd_NewLocal (const char *key, const char *value) @@ -152,7 +151,7 @@ Cmd_LocalFree (void *ele, void *ptr) cmd_token_t * Cmd_NewToken (void) { cmd_token_t *new; - + new = calloc(1,sizeof(cmd_token_t)); SYS_CHECKMEM (new); new->original = dstring_newstr(); @@ -190,7 +189,7 @@ Cmd_NewBuffer (qboolean ownvars) /* Cmd_FreeBuffer - + Actually just "recycles" buffer for later use */ @@ -203,7 +202,7 @@ Cmd_FreeBuffer (cmd_buffer_t *free) { dstring_clearstr (free->buffer); dstring_clearstr (free->line); dstring_clearstr (free->realline); - dstring_clearstr (free->looptext); + dstring_clearstr (free->looptext); dstring_clearstr (free->retval); free->locals = 0; free->next = cmd_recycled; @@ -213,13 +212,52 @@ Cmd_FreeBuffer (cmd_buffer_t *free) { void Cmd_FreeStack (cmd_buffer_t *stack) { cmd_buffer_t *temp; - + for (;stack; stack = temp) { temp = stack->next; Cmd_FreeBuffer (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 Cmd_FreeBuffer (cmd_buffer_t *del) @@ -345,6 +383,8 @@ Cbuf_Init (void) cmd_activebuffer = cmd_consolebuffer; + cmd_threads = 0; + cmd_threadid = 0; cmd_recycled = 0; cmd_backtrace = dstring_newstr (); @@ -480,7 +520,6 @@ Cbuf_ExecuteBuffer (cmd_buffer_t *buffer) Cbuf_ExtractLine (buffer->buffer, buf, buffer->legacy); if (!buf->str[0]) continue; - Sys_DPrintf("Cbuf_ExecuteBuffer: Executing line %s\n", buf->str); Cmd_TokenizeString (buf->str, cmd_activebuffer->legacy); if (cmd_error) break; @@ -547,6 +586,16 @@ Cbuf_ExecuteStack (cmd_buffer_t *buffer) 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_consolebuffer); Cbuf_ExecuteStack (cmd_legacybuffer); @@ -1093,31 +1142,27 @@ Cmd_ProcessMath (dstring_t * dstr) statement = dstring_newstr (); for (i = 0; i < strlen (dstr->str); i++) { - if (dstr->str[i] == '#' && dstr->str[i + 1] == '{') { - if (!escaped (dstr->str, i)) { - i -= unescape (dstr, i); - n = Cmd_EndBrace (dstr->str+i+1)+1; - if (n < 0) { - Cmd_Error ("Unmatched brace in math expression.\n"); - ret = -1; - break; - } - /* 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 ("%.10g", value); - dstring_snip (dstr, i, n + 1); // Nuke the statement - dstring_insertstr (dstr, temp, i); // Stick in the value - i += strlen (temp) - 1; - } else { - ret = -2; - Cmd_Error (va("Math error: invalid expression %s\n", statement->str)); - break; // Math evaluation error - } - } else - i -= unescape (dstr, i); + if (dstr->str[i] == '#' && dstr->str[i + 1] == '{' && !escaped (dstr->str, i)) { + n = Cmd_EndBrace (dstr->str+i+1)+1; + if (n < 0) { + Cmd_Error ("Unmatched brace in math expression.\n"); + ret = -1; + break; + } + /* 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 ("%.10g", value); + dstring_snip (dstr, i, n + 1); // Nuke the statement + dstring_insertstr (dstr, temp, i); // Stick in the value + i += strlen (temp) - 1; + } else { + ret = -2; + Cmd_Error (va("Math error: invalid expression %s\n", statement->str)); + break; // Math evaluation error + } } } dstring_delete (statement); @@ -1183,18 +1228,14 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start) if (dstr->str[start+1] == '{') braces = 1; for (i = start + 1 + braces;; i++) { - if (dstr->str[i] == '$' && (braces || dstr->str[i-1] == '$')) { - if (!escaped (dstr->str, i)) { - i -= unescape (dstr, i); - n = Cmd_ProcessVariablesRecursive (dstr, i); - if (n < 0) { - break; - } else { - i += n - 1; - continue; - } - } else - i -= unescape (dstr, i); + if (dstr->str[i] == '$' && (braces || dstr->str[i-1] == '$') && !escaped (dstr->str, i)) { + n = Cmd_ProcessVariablesRecursive (dstr, i); + if (n < 0) { + break; + } else { + i += n - 1; + continue; + } } else if (!dstr->str[i] && braces) { // No closing brace Cmd_Error ("Unmatched brace in variable substitution expression.\n"); n = -1; @@ -1214,6 +1255,8 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start) // Then cvars dstring_appendstr (copy, cvar->string); } + n = 0; + dstring_clearstr (varname); // Reuse variable if (dstr->str[start] == '[') { int val1, val2, res; res = Cmd_ProcessIndex (dstr, start, &val1, &val2); @@ -1221,16 +1264,16 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start) n = -1; else if (val1 >= strlen(copy->str) || val1 < 0) n = 0; - else if (val2 < strlen(copy->str)) { - dstring_insert (dstr, copy->str+val1, val2-val1+1, start); - n = val2 - val1 + 1; - } else { - dstring_insert (dstr, copy->str+val1, strlen(copy->str)-val1, start); - n = strlen(copy->str)-val1; - } - } else { - dstring_insertstr (dstr, copy->str, start); - n = strlen(copy->str); + else if (val2 < strlen(copy->str)) + dstring_insert (varname, copy->str+val1, val2-val1+1, 0); + else + dstring_insert (varname, copy->str+val1, strlen(copy->str)-val1, 0); + } else + dstring_appendstr (varname, copy->str); + if (n >= 0) { + escape (varname, "\\"); + dstring_insertstr (dstr, varname->str, start); + n = strlen(varname->str); } dstring_delete (copy); dstring_delete (varname); @@ -1258,6 +1301,7 @@ Cmd_ProcessEmbeddedSingle (dstring_t * dstr, int start) return -1; } if (cmd_activebuffer->returned == cmd_returned) { + escape (cmd_activebuffer->retval, "\\"); dstring_snip(dstr, start, n+1); dstring_insertstr (dstr, cmd_activebuffer->retval->str, start); n = strlen(cmd_activebuffer->retval->str); @@ -1283,20 +1327,16 @@ Cmd_ProcessEmbedded (cmd_token_t *tok, dstring_t * dstr) int i, n; for (i = tok->pos; i < strlen(dstr->str); i++) { - if (dstr->str[i] == '~' && dstr->str[i+1] == '{') { - if (!escaped (dstr->str, i)) { - i -= unescape (dstr, i); - n = Cmd_ProcessEmbeddedSingle (dstr, i); - if (n == -2) { - tok->pos = i; - return n; - } - if (n < 0) - return n; - else - i += n-1; - } else - i -= unescape (dstr, i); + if (dstr->str[i] == '~' && dstr->str[i+1] == '{' && !escaped (dstr->str, i)) { + n = Cmd_ProcessEmbeddedSingle (dstr, i); + if (n == -2) { + tok->pos = i; + return n; + } + if (n < 0) + return n; + else + i += n-1; } } return 0; @@ -1308,16 +1348,12 @@ Cmd_ProcessVariables (dstring_t * dstr) int i, n; for (i = 0; i < strlen (dstr->str); i++) { - if (dstr->str[i] == '$') { - if (!escaped (dstr->str, i)) { - i -= unescape (dstr, i); - n = Cmd_ProcessVariablesRecursive (dstr, i); - if (n < 0) - return n; - else - i += n - 1; - } else - i -= unescape (dstr, i); + if (dstr->str[i] == '$' && !escaped (dstr->str, i)) { + n = Cmd_ProcessVariablesRecursive (dstr, i); + if (n < 0) + return n; + else + i += n - 1; } } return 0; @@ -1335,27 +1371,24 @@ Cmd_ProcessVariables (dstring_t * dstr) tags, etc. can be escaped */ -void +int Cmd_ProcessEscapes (dstring_t * dstr, const char *noprocess) { int i; if (strlen(dstr->str) == 1) - return; - for (i = 1; 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] == 'n' && escaped (dstr->str, i)) - dstr->str[i] = '\n'; - i -= unescape (dstr, i); + return 0; + for (i = 0; i < strlen (dstr->str); i++) + if (dstr->str[i] == '\\') { + dstring_snip (dstr, i, 1); } - } + return 0; } int Cmd_ProcessToken (cmd_token_t *token) { int res; - Cmd_ProcessEscapes (token->processed, "$#~"); res = Cmd_ProcessEmbedded (token, token->processed); if (res < 0) return res; @@ -1366,6 +1399,9 @@ Cmd_ProcessToken (cmd_token_t *token) if (res < 0) return res; Cmd_ProcessTags (token->processed); + res = Cmd_ProcessEscapes (token->processed, "$#~"); + if (res < 0) + return res; token->state = cmd_done; return 0; } @@ -2004,6 +2040,10 @@ Cmd_For_f (void) { Cmd_ExecuteSubroutine (sub); } else Cmd_Error("Malformed for statement.\n"); + dstring_delete (arg1); + dstring_delete (init); + dstring_delete (cond); + dstring_delete (inc); return; } @@ -2096,6 +2136,53 @@ Cmd_Strlen_f (void) 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 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 ("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 ("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 " // "alias and command hash tables"); diff --git a/qw/source/cl_input.c b/qw/source/cl_input.c index cff317d13..2c2c84343 100644 --- a/qw/source/cl_input.c +++ b/qw/source/cl_input.c @@ -45,6 +45,7 @@ static const char rcsid[] = #include "QF/keys.h" #include "QF/msg.h" #include "QF/teamplay.h" +#include "QF/va.h" #include "cl_cam.h" #include "cl_demo.h" @@ -365,6 +366,12 @@ IN_Impulse (void) Team_BestWeaponImpulse (); // HACK HACK HACK } +void +IN_Getimpulse_f (void) +{ + Cmd_Return(va("%i", in_impulse)); +} + /* CL_KeyState @@ -795,6 +802,8 @@ CL_Input_Init (void) "jumping"); Cmd_AddCommand ("impulse", IN_Impulse, "Call a game function or QuakeC " "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 " "perform +lookup and +lookdown"); Cmd_AddCommand ("-klook", IN_KLookUp, "When active, +forward and +back " diff --git a/qw/source/cl_main.c b/qw/source/cl_main.c index 52ebed332..605b069fa 100644 --- a/qw/source/cl_main.c +++ b/qw/source/cl_main.c @@ -453,6 +453,7 @@ void CL_Disconnect (void) { byte final[10]; + int i; connect_time = -1; @@ -494,6 +495,12 @@ CL_Disconnect (void) 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