If and while now work, and embedded commands use backticks instead of <> now

to prevent conflicts with math expressions.  All clients and servers are set
to create buffers with the normal parser.  However, scripts ending in .gib
are now executed in a GIB buffer, and the export command can be used to
make specific GIB functions available to normal command buffers (i.e. the
console).
This commit is contained in:
Brian Koropoff 2002-08-07 06:17:50 +00:00
parent e56404d242
commit 5c60be3a49
12 changed files with 224 additions and 63 deletions

View file

@ -37,17 +37,20 @@ typedef struct gib_local_s {
typedef struct gib_buffer_data_s {
struct dstring_s *arg_composite;
struct dstring_s *current_token;
struct dstring_s *loop_program;
// Data for handling return values
struct {
qboolean waiting, available; // Return value states
struct dstring_s *retval; // Returned value
// Data saved by tokenizer/processor
unsigned int line_pos; // Position within line
unsigned int token_pos; // Position within token
qboolean cat; // Concatenate to previous token?
int noprocess; // Process tokens?
char delim; // delimiter of token
struct dstring_s *retval; // Returned value
} ret;
struct hashtab_s *locals; // Local variables

View file

@ -31,13 +31,13 @@
typedef struct gib_builtin_s {
struct dstring_s *name;
void (*func) (void);
enum {
GIB_BUILTIN_NORMAL, // Normal argument processing
GIB_BUILTIN_NOPROCESS, // Don't process arguments
GIB_BUILTIN_FIRSTONLY, // Process only the first argument
enum gib_builtin_type_e {
GIB_BUILTIN_NORMAL = 0, // Normal argument processing
GIB_BUILTIN_NOPROCESS = 1, // Don't process arguments
GIB_BUILTIN_FIRSTONLY = 2, // Process only the first argument
} type;
} gib_builtin_t;
void GIB_Builtin_Add (const char *name, void (*func) (void));
void GIB_Builtin_Add (const char *name, void (*func) (void), enum gib_builtin_type_e type);
gib_builtin_t *GIB_Builtin_Find (const char *name);
void GIB_Builtin_Init (void);

View file

@ -30,6 +30,7 @@
typedef struct gib_function_s {
struct dstring_s *name, *program;
qboolean exported;
} gib_function_t;
void GIB_Function_Define (const char *name, const char *program);

View file

@ -28,7 +28,7 @@
*/
char GIB_Parse_Match_Angle (const char *str, unsigned int *i);
char GIB_Parse_Match_Backtick (const char *str, unsigned int *i);
void GIB_Parse_Extract_Line (struct cbuf_s *cbuf);
void GIB_Parse_Tokenize_Line (struct cbuf_s *cbuf);

View file

@ -53,6 +53,7 @@ static const char rcsid[] =
#include "QF/vfs.h"
#include "QF/zone.h"
#include "QF/gib_builtin.h"
#include "QF/gib_parse.h"
typedef struct cmdalias_s {
struct cmdalias_s *next;
@ -489,9 +490,19 @@ Cmd_Exec_f (void)
if (!Cvar_Command ()
&& (cmd_warncmd->int_val || (developer && developer->int_val)))
Sys_Printf ("execing %s\n", Cmd_Argv (1));
if (!strcmp (Cmd_Argv (1) + strlen (Cmd_Argv(1)) - 4, ".gib")) {
// GIB script, put it in a new buffer on the stack
cbuf_t *sub = Cbuf_New (&gib_interp);
if (cbuf_active->down)
Cbuf_DeleteStack (cbuf_active->down);
cbuf_active->down = sub;
sub->up = cbuf_active;
cbuf_active->state = CBUF_STATE_STACK;
Cbuf_AddText (sub, f);
} else
Cbuf_InsertText (cbuf_active, f);
Hunk_FreeToLowMark (mark);
Cbuf_Execute (cbuf_active);
}
/*

View file

@ -99,6 +99,8 @@ void GIB_Buffer_Destruct (struct cbuf_s *cbuf)
{
dstring_delete (GIB_DATA (cbuf)->arg_composite);
dstring_delete (GIB_DATA (cbuf)->current_token);
if (GIB_DATA (cbuf)->loop_program)
dstring_delete (GIB_DATA(cbuf)->loop_program);
dstring_delete (GIB_DATA (cbuf)->ret.retval);
if (GIB_DATA(cbuf)->locals && GIB_DATA(cbuf)->type == GIB_BUFFER_NORMAL)
Hash_DelTable (GIB_DATA(cbuf)->locals);

View file

@ -29,12 +29,15 @@
*/
#include <stdlib.h>
#include <string.h>
#include "QF/va.h"
#include "QF/sys.h"
#include "QF/cmd.h"
#include "QF/cbuf.h"
#include "QF/hash.h"
#include "QF/dstring.h"
#include "QF/gib_parse.h"
#include "QF/gib_builtin.h"
#include "QF/gib_buffer.h"
#include "QF/gib_function.h"
@ -57,7 +60,7 @@ GIB_Builtin_Free (void *ele, void *ptr)
}
void
GIB_Builtin_Add (const char *name, void (*func) (void))
GIB_Builtin_Add (const char *name, void (*func) (void), enum gib_builtin_type_e type)
{
gib_builtin_t *new;
@ -67,6 +70,7 @@ GIB_Builtin_Add (const char *name, void (*func) (void))
new = calloc (1, sizeof (gib_builtin_t));
new->func = func;
new->name = dstring_newstr();
new->type = type;
dstring_appendstr (new->name, name);
Hash_Add (gib_builtins, new);
}
@ -94,12 +98,31 @@ GIB_Argv (unsigned int arg)
return "";
}
const char *
GIB_Args (unsigned int arg)
{
if (arg < cbuf_active->args->argc)
return cbuf_active->args->args[arg];
else
return "";
}
void
GIB_Arg_Strip_Delim (unsigned int arg)
{
char *p = cbuf_active->args->argv[arg]->str;
if (*p == '{' || *p == '\"') {
dstring_snip (cbuf_active->args->argv[arg], 0, 1);
p[strlen(p)-1] = 0;
}
}
void
GIB_Function_f (void)
{
if (GIB_Argc () != 3)
Cbuf_Error ("numargs",
"function: invalid number of arguments\n"
Cbuf_Error ("syntax",
"function: invalid syntax\n"
"usage: function function_name {program}");
else
GIB_Function_Define (GIB_Argv(1), GIB_Argv(2));
@ -109,8 +132,8 @@ void
GIB_Lset_f (void)
{
if (GIB_Argc () != 3)
Cbuf_Error ("numargs",
"lset: invalid number of arguments\n"
Cbuf_Error ("syntax",
"lset: invalid syntax\n"
"usage: lset variable value");
else
GIB_Local_Set (cbuf_active, GIB_Argv(1), GIB_Argv(2));
@ -119,28 +142,136 @@ GIB_Lset_f (void)
void
GIB_Return_f (void)
{
cbuf_t *sp;
if (GIB_Argc () > 2)
Cbuf_Error ("numargs",
"return: invalid number of arguments\n"
Cbuf_Error ("syntax",
"return: invalid syntax\n"
"usage: return <value>");
else {
dstring_clearstr (cbuf_active->buf);
sp = cbuf_active;
while (sp->interpreter == &gib_interp && GIB_DATA(sp)->type == GIB_BUFFER_LOOP) { // Get out of loops
GIB_DATA(sp)->type = GIB_BUFFER_PROXY;
dstring_clearstr (sp->buf);
dstring_clearstr (sp->line);
sp = sp->up;
}
dstring_clearstr (sp->buf);
dstring_clearstr (sp->line);
if (GIB_Argc () == 1)
return;
if (!cbuf_active->up || !cbuf_active->up->up)
Cbuf_Error ("return","return attempted at top of stack");
if (GIB_DATA(cbuf_active->up->up)->ret.waiting) {
dstring_clearstr (GIB_DATA(cbuf_active->up->up)->ret.retval);
dstring_appendstr (GIB_DATA(cbuf_active->up->up)->ret.retval, GIB_Argv(1));
GIB_DATA(cbuf_active->up->up)->ret.available = true;
if (!sp->up || !sp->up->up)
Cbuf_Error ("stack","return attempted at top of stack");
else if (sp->up->up->interpreter != &gib_interp)
Cbuf_Error ("stack","return to non-GIB command buffer attempted");
else if (GIB_DATA(sp->up->up)->ret.waiting) {
dstring_clearstr (GIB_DATA(sp->up->up)->ret.retval);
dstring_appendstr (GIB_DATA(sp->up->up)->ret.retval, GIB_Argv(1));
GIB_DATA(sp->up->up)->ret.available = true;
}
}
}
void
GIB_If_f (void)
{
int condition;
if ((!strcmp (GIB_Argv (3), "else") && GIB_Argc() >= 5) || // if condition {program} else ...
(GIB_Argc() == 3)) { // if condition {program}
condition = atoi(GIB_Argv(1));
if (!strcmp (GIB_Argv(0), "ifnot"))
condition = !condition;
if (condition) {
GIB_Arg_Strip_Delim (2);
Cbuf_InsertText (cbuf_active, GIB_Argv(2));
} else if (GIB_Argc() == 5) {
GIB_Arg_Strip_Delim (4);
Cbuf_InsertText (cbuf_active, GIB_Argv(4));
} else if (GIB_Argc() > 5)
Cbuf_InsertText (cbuf_active, GIB_Args (4));
} else
Cbuf_Error ("syntax",
"if: invalid syntax\n"
"usage: if condition {program} [else ...]"
);
}
void
GIB_While_f (void)
{
if (GIB_Argc() != 3) {
Cbuf_Error ("syntax",
"while: invalid syntax\n"
"usage: while condition {program}"
);
} else {
cbuf_t *sub = Cbuf_New (&gib_interp);
GIB_DATA(sub)->type = GIB_BUFFER_LOOP;
GIB_DATA(sub)->locals = GIB_DATA(cbuf_active)->locals;
GIB_DATA(sub)->loop_program = dstring_newstr ();
if (cbuf_active->down)
Cbuf_DeleteStack (cbuf_active->down);
cbuf_active->down = sub;
sub->up = cbuf_active;
GIB_Arg_Strip_Delim (2);
dstring_appendstr (GIB_DATA(sub)->loop_program, va("ifnot %s break\n%s", GIB_Argv (1), GIB_Argv (2)));
Cbuf_AddText (sub, GIB_DATA(sub)->loop_program->str);
cbuf_active->state = CBUF_STATE_STACK;
}
}
void
GIB_Break_f (void)
{
if (GIB_DATA(cbuf_active)->type != GIB_BUFFER_LOOP)
Cbuf_Error ("syntax",
"break attempted outside of a loop"
);
else {
GIB_DATA(cbuf_active)->type = GIB_BUFFER_PROXY; // If we set it to normal locals will get freed
dstring_clearstr (cbuf_active->buf);
}
}
void
GIB_Runexported_f (void)
{
gib_function_t *f;
if (!(f = GIB_Function_Find (Cmd_Argv (0))))
Sys_Printf ("Error: No function found for exported command \"%s\".\n"
"This is most likely a bug, please report it to"
"The QuakeForge developers.", Cmd_Argv(0));
else
GIB_Function_Execute (f);
}
void
GIB_Export_f (void)
{
gib_function_t *f;
if (GIB_Argc() != 2)
Cbuf_Error ("syntax",
"export: invalid syntax\n"
"usage: export function");
else if (!(f = GIB_Function_Find (GIB_Argv (1))))
Cbuf_Error ("existance", "export: function '%s' not found", GIB_Argv (1));
else if (!f->exported) {
Cmd_AddCommand (f->name->str, GIB_Runexported_f, "Exported GIB function.");
f->exported = true;
}
}
void
GIB_Builtin_Init (void)
{
GIB_Builtin_Add ("function", GIB_Function_f);
GIB_Builtin_Add ("lset", GIB_Lset_f);
GIB_Builtin_Add ("return", GIB_Return_f);
GIB_Builtin_Add ("function", GIB_Function_f, GIB_BUILTIN_NORMAL);
GIB_Builtin_Add ("export", GIB_Export_f, GIB_BUILTIN_NORMAL);
GIB_Builtin_Add ("lset", GIB_Lset_f, GIB_BUILTIN_NORMAL);
GIB_Builtin_Add ("return", GIB_Return_f, GIB_BUILTIN_NORMAL);
GIB_Builtin_Add ("if", GIB_If_f, GIB_BUILTIN_FIRSTONLY);
GIB_Builtin_Add ("ifnot", GIB_If_f, GIB_BUILTIN_FIRSTONLY);
GIB_Builtin_Add ("while", GIB_While_f, GIB_BUILTIN_NOPROCESS);
GIB_Builtin_Add ("break", GIB_Break_f, GIB_BUILTIN_NORMAL);
}

View file

@ -96,29 +96,25 @@ GIB_Parse_Match_Paren (const char *str, unsigned int *i)
return c;
} else if (str[*i] == ')')
return 0;
else if (str[*i] == '\n') // Newlines in math == bad
return '(';
}
return '(';
}
char
GIB_Parse_Match_Angle (const char *str, unsigned int *i)
GIB_Parse_Match_Backtick (const char *str, unsigned int *i)
{
char c;
for ((*i)++; str[*i]; (*i)++) {
if (str[*i] == '>')
if (str[*i] == '`')
return 0;
else if (str[*i] == '<') {
if ((c = GIB_Parse_Match_Angle (str, i))) // Embedded inside embedded
return c;
} else if (str[*i] == '(') { // Skip over math expressions to avoid < and >
if ((c = GIB_Parse_Match_Paren (str, i)))
return c;
} else if (str[*i] == '\"') { // Skip over strings
else if (str[*i] == '\"') { // Skip over strings
if ((c = GIB_Parse_Match_Dquote (str, i)))
return c;
}
}
return '<';
return '`';
}
void
@ -145,11 +141,6 @@ GIB_Parse_Extract_Line (struct cbuf_s *cbuf)
Cbuf_Error ("parse", "Could not find matching %c", c);
return;
}
} else if (dstr->str[i] == '<') {
if ((c = GIB_Parse_Match_Angle (dstr->str, &i))) {
Cbuf_Error ("parse", "Could not find matching %c", c);
return;
}
} else if (dstr->str[i] == '\n' || dstr->str[i] == ';')
break;
else if (dstr->str[i] == '/' && dstr->str[i+1] == '/') {
@ -171,12 +162,15 @@ GIB_Parse_Extract_Line (struct cbuf_s *cbuf)
dstring_snip (dstr, 0, i + (dstr->str[i] == '\n' || dstr->str[i] == ';'));
}
if (GIB_DATA(cbuf)->type == GIB_BUFFER_LOOP && !dstr->str[0])
Cbuf_AddText (cbuf, GIB_DATA(cbuf)->loop_program->str);
return;
}
inline static char
GIB_Parse_Get_Token (const char *str, unsigned int *i, dstring_t *dstr)
GIB_Parse_Get_Token (const char *str, unsigned int *i, dstring_t *dstr, qboolean include_delim)
{
int n;
char c;
@ -187,7 +181,7 @@ GIB_Parse_Get_Token (const char *str, unsigned int *i, dstring_t *dstr)
Cbuf_Error ("parse", "Could not find matching %c", c);
return 0; // Parse error
} else {
dstring_insert (dstr, 0, str+n+1, *i-n-1);
dstring_insert (dstr, 0, str+n+!include_delim, *i-n+1-!include_delim-!include_delim);
return '\"';
}
} else if (str[*i] == '{') {
@ -195,7 +189,7 @@ GIB_Parse_Get_Token (const char *str, unsigned int *i, dstring_t *dstr)
Cbuf_Error ("parse", "Could not find matching %c", c);
return 0; // Parse error
} else {
dstring_insert (dstr, 0, str+n+1, *i-n-1);
dstring_insert (dstr, 0, str+n+!include_delim, *i-n+1-!include_delim-!include_delim);
return '{';
}
} else if (str[*i] == '(') {
@ -203,13 +197,13 @@ GIB_Parse_Get_Token (const char *str, unsigned int *i, dstring_t *dstr)
Cbuf_Error ("parse", "Could not find matching %c", c);
return 0; // Parse error
} else {
dstring_insert (dstr, 0, str+n+1, *i-n-1);
dstring_insert (dstr, 0, str+n+!include_delim, *i-n+1-!include_delim-!include_delim);
return '\(';
}
} else {
while (str[*i] && !isspace(str[*i]) && str[*i] != ',') { // find end of token
if (str[*i] == '<') {
if ((c = GIB_Parse_Match_Angle (str, i))) {
if (str[*i] == '`') {
if ((c = GIB_Parse_Match_Backtick (str, i))) {
Cbuf_Error ("parse", "Could not find matching %c", c);
return 0; // Parse error
}
@ -262,6 +256,7 @@ GIB_Parse_Tokenize_Line (struct cbuf_s *cbuf)
const char *str = cbuf->line->str;
cbuf_args_t *args = cbuf->args;
qboolean cat = false;
int noprocess;
char delim;
int i;
@ -276,9 +271,11 @@ GIB_Parse_Tokenize_Line (struct cbuf_s *cbuf)
return; // Still not done, or error
GIB_Parse_Add_Token (cbuf->args, GIB_DATA(cbuf)->ret.cat, arg);
i = GIB_DATA(cbuf)->ret.line_pos; // Start tokenizing where we left off
noprocess = GIB_DATA(cbuf)->ret.noprocess ? 1 : 0; // Past second token, no sense in having it be 2
} else {
args->argc = 0; // Start from scratch
i = 0;
noprocess = 0;
}
while (str[i]) {
@ -292,41 +289,55 @@ GIB_Parse_Tokenize_Line (struct cbuf_s *cbuf)
continue;
}
dstring_clearstr (arg);
delim = GIB_Parse_Get_Token (str, &i, arg);
delim = GIB_Parse_Get_Token (str, &i, arg, noprocess == 1);
if (!delim)
break;
Sys_DPrintf("Got token: %s\n", arg->str);
if (delim != ' ') // Move into whitespace if we haven't already
i++;
if (noprocess != 1)
if (GIB_Process_Token (arg, delim))
goto FILTER_ERROR; // Error or GIB subroutine needs to be called
if (noprocess > 1)
noprocess--;
GIB_Parse_Add_Token (cbuf->args, cat, arg);
if (cat)
cat = false;
if (delim != ' ') // Move into whitespace if we haven't already
i++;
if (cbuf->args->argc == 1) {
gib_builtin_t *b;
if ((b = GIB_Builtin_Find (cbuf->args->argv[0]->str)))
noprocess = b->type;
}
}
GIB_Parse_Generate_Composite (cbuf);
dstring_clearstr (cbuf->line);
return;
FILTER_ERROR:
GIB_DATA(cbuf)->ret.line_pos = i; // save our information in case
GIB_DATA(cbuf)->ret.cat = cat; // error is not fatal
GIB_DATA(cbuf)->ret.delim = delim;
GIB_DATA(cbuf)->ret.noprocess = noprocess;
return;
}
void GIB_Parse_Execute_Line (cbuf_t *cbuf)
{
cbuf_args_t *args = cbuf->args;
gib_builtin_t *b;
gib_function_t *f;
if ((b = GIB_Builtin_Find (cbuf->args->argv[0]->str)))
if ((b = GIB_Builtin_Find (args->argv[0]->str)))
b->func ();
else if ((f = GIB_Function_Find (cbuf->args->argv[0]->str))) {
else if ((f = GIB_Function_Find (args->argv[0]->str)))
GIB_Function_Execute (f);
} else
else if (args->argc == 3 && !strcmp (args->argv[1]->str, "="))
GIB_Local_Set (cbuf, args->argv[0]->str, args->argv[2]->str);
else
Cmd_Command (cbuf->args);
dstring_clearstr (cbuf->line);
}

View file

@ -69,7 +69,9 @@ GIB_Process_Variables_All (struct dstring_s *token)
for (i = 0; token->str[i]; i++) {
if (token->str[i] == '$') {
for (n = 1; token->str[i+n] == '$'; n++); // get past $s
for (; isalnum(token->str[i+n]); n++); // find end of var
for (; isalnum(token->str[i+n]) ||
token->str[i+n] == '.' ||
token->str[i+n] == '_'; n++); // find end of var
dstring_insert (var, 0, token->str+i, n); // extract it
GIB_Process_Variable (var);
dstring_replace (token, i, n, var->str, strlen(var->str));
@ -114,9 +116,9 @@ GIB_Process_Embedded (struct dstring_s *token)
i = 0;
for (; token->str[i]; i++) {
if (token->str[i] == '<') {
if (token->str[i] == '`') {
n = i;
if ((c = GIB_Parse_Match_Angle (token->str, &i))) {
if ((c = GIB_Parse_Match_Backtick (token->str, &i))) {
Cbuf_Error ("parse", "Could not find matching %c", c);
return -1;
}

View file

@ -607,7 +607,7 @@ _Host_Frame (float time)
// process console commands
cmd_source = src_command;
Cbuf_Execute (host_cbuf);
Cbuf_Execute_Stack (host_cbuf);
NET_Poll ();

View file

@ -1535,7 +1535,7 @@ Host_Frame (float time)
IN_Commands ();
// process console commands
Cbuf_Execute (cl_cbuf);
Cbuf_Execute_Stack (cl_cbuf);
// fetch results from server
CL_ReadPackets ();

View file

@ -2408,7 +2408,7 @@ SV_Init (void)
// COM_AddParm ("-game");
// COM_AddParm ("qw");
sv_cbuf = Cbuf_New (&gib_interp);
sv_cbuf = Cbuf_New (&id_interp);
sv_args = Cbuf_ArgsNew ();
Sys_RegisterShutdown (SV_Shutdown);