mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-17 22:50:51 +00:00
You can now assign to local variables with var = value. You can also
index into variables in either substitution (i.e. $bob[25]) or assignment (i.e. bob[25] = 'q') When using assignment into an index of a var, the current character is removed and replace with the new value. That means that if the new value is longer than one character, the string will grow. A feature that may be done in the future is indexing between two values to get a substring, ie bob[25..30] = newstr.
This commit is contained in:
parent
599e0f4b0a
commit
c3da90fce0
1 changed files with 144 additions and 89 deletions
233
libs/util/cmd.c
233
libs/util/cmd.c
|
@ -107,7 +107,7 @@ Cmd_NewLocal (const char *key, const char *value)
|
|||
}
|
||||
|
||||
void
|
||||
Cmd_SetLocal (cmd_buffer_t *buffer, const char *key, const char *value)
|
||||
Cmd_SetLocal (cmd_buffer_t *buffer, const char *key, const char *value, int index)
|
||||
{
|
||||
cmd_localvar_t *var;
|
||||
|
||||
|
@ -116,8 +116,13 @@ Cmd_SetLocal (cmd_buffer_t *buffer, const char *key, const char *value)
|
|||
var = Cmd_NewLocal (key, value);
|
||||
Hash_Add (buffer->locals, (void *) var);
|
||||
} else {
|
||||
dstring_clearstr (var->value);
|
||||
dstring_appendstr (var->value, value);
|
||||
if (index < 0) {
|
||||
dstring_clearstr (var->value);
|
||||
dstring_appendstr (var->value, value);
|
||||
} else if (index <= strlen(var->value->str)) {
|
||||
dstring_snip (var->value, index, 1);
|
||||
dstring_insertstr (var->value, value, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -933,6 +938,9 @@ Cmd_GetToken (const char *str, qboolean legacy)
|
|||
return i;
|
||||
}
|
||||
|
||||
int Cmd_ProcessVariables (dstring_t * dstr);
|
||||
int Cmd_ProcessEmbedded (dstring_t * dstr);
|
||||
|
||||
int tag_shift = 0;
|
||||
int tag_special = 0;
|
||||
|
||||
|
@ -1038,6 +1046,31 @@ Cmd_ProcessTags (dstring_t * dstr)
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
Cmd_ProcessIndex (dstring_t * dstr, int start)
|
||||
{
|
||||
dstring_t *value;
|
||||
int val;
|
||||
int i,n;
|
||||
|
||||
for (i = start + 1;; i++) {
|
||||
if (dstr->str[i] == ']' && !escaped (dstr->str, i)) {
|
||||
value = dstring_newstr ();
|
||||
dstring_insert (value, dstr->str+start+1, i-start, 0);
|
||||
n = Cmd_ProcessVariables(value);
|
||||
if (n < 0)
|
||||
return n;
|
||||
dstring_snip (dstr, start, i-start+1);
|
||||
val = atoi (value->str);
|
||||
dstring_delete (value);
|
||||
return val;
|
||||
}
|
||||
if (!dstr->str[i])
|
||||
return -1;
|
||||
}
|
||||
return -1; // Should never get here
|
||||
}
|
||||
|
||||
/*
|
||||
Cmd_ProcessVariablesRecursive
|
||||
|
||||
|
@ -1050,17 +1083,22 @@ Cmd_ProcessTags (dstring_t * dstr)
|
|||
int
|
||||
Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start)
|
||||
{
|
||||
dstring_t *varname;
|
||||
cmd_localvar_t *lvar;
|
||||
cvar_t *cvar;
|
||||
int i, n, braces = 0;
|
||||
|
||||
varname = dstring_newstr ();
|
||||
static int level = 0; // Keep track of our level of recursion for debugging
|
||||
|
||||
level++;
|
||||
if (level > 100) {
|
||||
Sys_Printf("Warning! Variable substitution caught in loop. Text was: %s\n", dstr->str+start);
|
||||
level = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dstr->str[start+1] == '{')
|
||||
braces = 1;
|
||||
for (i = start + 1 + braces;; i++) {
|
||||
if (dstr->str[i] == '$' && (braces || dstr->str[i+1] == '{') && !escaped (dstr->str, i)) {
|
||||
if (dstr->str[i] == '$' && (braces || dstr->str[i-1] == '$') && !escaped (dstr->str, i)) {
|
||||
n = Cmd_ProcessVariablesRecursive (dstr, i);
|
||||
if (n < 0) {
|
||||
break;
|
||||
|
@ -1068,107 +1106,104 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start)
|
|||
i += n - 1;
|
||||
continue;
|
||||
}
|
||||
} else if (braces && dstr->str[i] == '}' && !escaped (dstr->str, i)) {
|
||||
dstring_clearstr (varname);
|
||||
dstring_insert (varname, dstr->str + start + 2, i - start - 2, 0);
|
||||
// Nuke it, even if no match is found
|
||||
dstring_snip (dstr, start, i - start + 1);
|
||||
lvar = (cmd_localvar_t *) Hash_Find (cmd_activebuffer->locals,
|
||||
varname->str);
|
||||
if (lvar) {
|
||||
// Local variables get precedence
|
||||
dstring_insertstr (dstr, lvar->value->str, start);
|
||||
n = strlen (lvar->value->str);
|
||||
} else if ((cvar = Cvar_FindVar (varname->str))) {
|
||||
// Then cvars
|
||||
// Stick in the value of variable
|
||||
dstring_insertstr (dstr, cvar->string, start);
|
||||
n = strlen (cvar->string);
|
||||
} else
|
||||
n = 0;
|
||||
break;
|
||||
} else if (!dstr->str[i] && braces) { // No closing brace
|
||||
n = -1;
|
||||
break;
|
||||
} else if (!braces && !isalnum((byte)dstr->str[i]) && dstr->str[i] != '_') {
|
||||
dstring_clearstr (varname);
|
||||
dstring_insert (varname, dstr->str + start + 1, i - start - 1, 0);
|
||||
} else if ((braces && dstr->str[i] == '}' && !escaped (dstr->str, i)) || (!braces && !isalnum((byte)dstr->str[i]) && dstr->str[i] != '_')) {
|
||||
int index;
|
||||
dstring_t *varname = dstring_newstr ();
|
||||
dstring_t *copy = dstring_newstr ();
|
||||
dstring_insert (varname, dstr->str + start + 1 + braces, i - start - 1 - braces, 0);
|
||||
// Nuke it, even if no match is found
|
||||
dstring_snip (dstr, start, i - start);
|
||||
dstring_snip (dstr, start, i - start + braces);
|
||||
lvar = (cmd_localvar_t *) Hash_Find (cmd_activebuffer->locals,
|
||||
varname->str);
|
||||
if (lvar) {
|
||||
// Local variables get precedence
|
||||
dstring_insertstr (dstr, lvar->value->str, start);
|
||||
n = strlen (lvar->value->str);
|
||||
dstring_appendstr (copy, lvar->value->str);
|
||||
} else if ((cvar = Cvar_FindVar (varname->str))) {
|
||||
// Then cvars
|
||||
// Stick in the value of variable
|
||||
dstring_insertstr (dstr, cvar->string, start);
|
||||
n = strlen (cvar->string);
|
||||
} else
|
||||
n = 0;
|
||||
dstring_appendstr (copy, cvar->string);
|
||||
}
|
||||
if (dstr->str[start] == '[') {
|
||||
index = Cmd_ProcessIndex (dstr, start);
|
||||
if (index < 0)
|
||||
n = -1;
|
||||
else if (index < strlen(copy->str)) {
|
||||
dstring_insert (dstr, copy->str+index, 1, start);
|
||||
n = 1;
|
||||
}
|
||||
else
|
||||
n = 0;
|
||||
} else {
|
||||
dstring_insertstr (dstr, copy->str, start);
|
||||
n = strlen(copy->str);
|
||||
}
|
||||
dstring_delete (copy);
|
||||
dstring_delete (varname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dstring_delete (varname);
|
||||
level--;
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
Cmd_ProcessEmbeddedSingle (dstring_t * dstr, int start)
|
||||
{
|
||||
int n;
|
||||
cmd_buffer_t *temp;
|
||||
dstring_t *command, *retval;
|
||||
|
||||
|
||||
n = Cmd_EndBrace (dstr->str+start+1)+1;
|
||||
if (n < 0) {
|
||||
Cmd_Error ("GIB: Unmatched brace in embedded command expression.\n");
|
||||
return n;
|
||||
}
|
||||
command = dstring_newstr ();
|
||||
dstring_insert (command, dstr->str + start + 2, n - 2, 0);
|
||||
temp = Cmd_NewBuffer (false);
|
||||
temp->imperative = true;
|
||||
temp->locals = cmd_activebuffer->locals;
|
||||
Cbuf_InsertTextTo (temp, command->str);
|
||||
Cbuf_ExecuteBuffer (temp);
|
||||
retval = 0;
|
||||
if (!cmd_error) {
|
||||
dstring_snip(dstr, start, n+1);
|
||||
if (temp->returning) // Top buffer returned a value (likely a builtin function)
|
||||
retval = temp->retval;
|
||||
else if (temp->next && temp->next->returning) // Next buffer returned a value (an alias or config file)
|
||||
retval = temp->next->retval;
|
||||
else {
|
||||
Cmd_Error ("GIB: Embedded command expression resulted in no return value.\n");
|
||||
n = -1;
|
||||
}
|
||||
if (retval) {
|
||||
dstring_insertstr (dstr, retval->str, start);
|
||||
n = strlen(retval->str);
|
||||
}
|
||||
}
|
||||
Cmd_FreeStack (temp);
|
||||
dstring_delete (command);
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
Cmd_ProcessEmbedded (dstring_t * dstr)
|
||||
{
|
||||
int i, n, braces = 0, ret = 0;
|
||||
cmd_buffer_t *temp;
|
||||
dstring_t *command, *retval;
|
||||
int i, n;
|
||||
|
||||
command = dstring_newstr ();
|
||||
for (i = 0; i < strlen(dstr->str); i++) {
|
||||
if (dstr->str[i] == '~' && dstr->str[i+1] == '{' && !escaped (dstr->str,i)) {
|
||||
braces++;
|
||||
for (n = 2; dstr->str[i+n]; n++) {
|
||||
if (dstr->str[i+n] == '{')
|
||||
braces++;
|
||||
if (dstr->str[i+n] == '}') {
|
||||
braces--;
|
||||
if (!braces)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (braces) {
|
||||
Cmd_Error ("Parse error: Unmatched brace in embedded command expression.\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
dstring_clearstr (command);
|
||||
dstring_insert (command, dstr->str + i + 2, n - 2, 0);
|
||||
temp = Cmd_NewBuffer (false);
|
||||
temp->imperative = true;
|
||||
temp->locals = cmd_activebuffer->locals;
|
||||
Cbuf_InsertTextTo (temp, command->str);
|
||||
Cbuf_ExecuteBuffer (temp);
|
||||
retval = 0;
|
||||
if (!cmd_error) {
|
||||
dstring_snip(dstr, i, n +1);
|
||||
if (temp->returning) // Top buffer returned a value (likely a builtin function)
|
||||
retval = temp->retval;
|
||||
else if (temp->next && temp->next->returning) // Next buffer returned a value (an alias or config file)
|
||||
retval = temp->next->retval;
|
||||
else {
|
||||
Cmd_Error ("GIB: Embedded command expression resulted in no return value.\n");
|
||||
ret = -1;
|
||||
Cmd_FreeStack (temp);
|
||||
break;
|
||||
}
|
||||
dstring_insertstr (dstr, retval->str, i);
|
||||
i += strlen(retval->str) - 1;
|
||||
|
||||
}
|
||||
Cmd_FreeStack (temp);
|
||||
if (dstr->str[i] == '~' && dstr->str[i+1] == '{') {
|
||||
n = Cmd_ProcessEmbeddedSingle (dstr, i);
|
||||
if (n < 0)
|
||||
return n;
|
||||
else
|
||||
i += n-1;
|
||||
}
|
||||
}
|
||||
dstring_delete (command);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1673,6 +1708,26 @@ Cmd_ExecuteString (const char *text, cmd_source_t src)
|
|||
// Tonik: check cvars
|
||||
if (Cvar_Command ())
|
||||
return;
|
||||
|
||||
// Check for assignment
|
||||
if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "=")) {
|
||||
int i, n = 0;
|
||||
dstring_t *var = dstring_newstr ();
|
||||
dstring_appendstr (var, Cmd_Argv(0));
|
||||
if (var->str[strlen(var->str)-1] == ']') {
|
||||
for (i = 0; i < strlen(var->str); i++)
|
||||
if (var->str[i] == '[') {
|
||||
n = Cmd_ProcessIndex (var, i);
|
||||
if (n >= 0)
|
||||
Cmd_SetLocal (cmd_activebuffer, var->str, Cmd_Argv(2), n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
Cmd_SetLocal (cmd_activebuffer, Cmd_Argv(0), Cmd_Argv(2), -1);
|
||||
dstring_delete (var);
|
||||
return;
|
||||
}
|
||||
|
||||
// check alias
|
||||
a = (cmdalias_t *) Hash_Find (cmd_alias_hash, Cmd_Argv (0));
|
||||
|
@ -1682,8 +1737,8 @@ Cmd_ExecuteString (const char *text, cmd_source_t src)
|
|||
sub = Cmd_NewBuffer (true);
|
||||
Cbuf_InsertTextTo (sub, a->value);
|
||||
for (i = 0; i < Cmd_Argc (); i++)
|
||||
Cmd_SetLocal (sub, va ("%i", i), Cmd_Argv (i));
|
||||
Cmd_SetLocal (sub, "argn", va ("%i", Cmd_Argc ()));
|
||||
Cmd_SetLocal (sub, va ("%i", i), Cmd_Argv (i), -1);
|
||||
Cmd_SetLocal (sub, "argn", va ("%i", Cmd_Argc ()), -1);
|
||||
// This will handle freeing the buffer for us, leave it alone
|
||||
Cmd_ExecuteSubroutine (sub);
|
||||
return;
|
||||
|
@ -1840,7 +1895,7 @@ Cmd_Break_f (void) {
|
|||
return;
|
||||
}
|
||||
else {
|
||||
Cmd_Error("break command used outside of loop!\n");
|
||||
Cmd_Error("Break command used outside of loop!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1876,7 +1931,7 @@ Cmd_Lset_f (void)
|
|||
Sys_Printf ("Usage: lset [local variable] [value]\n");
|
||||
return;
|
||||
}
|
||||
Cmd_SetLocal (cmd_activebuffer, Cmd_Argv (1), Cmd_Argv (2));
|
||||
Cmd_SetLocal (cmd_activebuffer, Cmd_Argv (1), Cmd_Argv (2), -1);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in a new issue