From 5c60be3a492517e09eda2f8cbe2d61075ab08fd4 Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Wed, 7 Aug 2002 06:17:50 +0000 Subject: [PATCH] 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). --- include/QF/gib_buffer.h | 7 +- include/QF/gib_builtin.h | 10 +-- include/QF/gib_function.h | 1 + include/QF/gib_parse.h | 2 +- libs/util/cmd.c | 15 +++- libs/util/gib_buffer.c | 2 + libs/util/gib_builtin.c | 165 ++++++++++++++++++++++++++++++++++---- libs/util/gib_parse.c | 71 +++++++++------- libs/util/gib_process.c | 8 +- nq/source/host.c | 2 +- qw/source/cl_main.c | 2 +- qw/source/sv_main.c | 2 +- 12 files changed, 224 insertions(+), 63 deletions(-) diff --git a/include/QF/gib_buffer.h b/include/QF/gib_buffer.h index 88a740f56..f7764f2c8 100644 --- a/include/QF/gib_buffer.h +++ b/include/QF/gib_buffer.h @@ -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 diff --git a/include/QF/gib_builtin.h b/include/QF/gib_builtin.h index dae562d5e..c6170000a 100644 --- a/include/QF/gib_builtin.h +++ b/include/QF/gib_builtin.h @@ -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); diff --git a/include/QF/gib_function.h b/include/QF/gib_function.h index 066154ee0..17348a596 100644 --- a/include/QF/gib_function.h +++ b/include/QF/gib_function.h @@ -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); diff --git a/include/QF/gib_parse.h b/include/QF/gib_parse.h index 26aeff1c8..b8f4d7824 100644 --- a/include/QF/gib_parse.h +++ b/include/QF/gib_parse.h @@ -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); diff --git a/libs/util/cmd.c b/libs/util/cmd.c index d4e5e266b..d07503566 100644 --- a/libs/util/cmd.c +++ b/libs/util/cmd.c @@ -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)); - Cbuf_InsertText (cbuf_active, f); + + 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); } /* diff --git a/libs/util/gib_buffer.c b/libs/util/gib_buffer.c index 56ce2f6f0..67d98daee 100644 --- a/libs/util/gib_buffer.c +++ b/libs/util/gib_buffer.c @@ -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); diff --git a/libs/util/gib_builtin.c b/libs/util/gib_builtin.c index 9a1c46f93..09c1bd3b7 100644 --- a/libs/util/gib_builtin.c +++ b/libs/util/gib_builtin.c @@ -29,12 +29,15 @@ */ #include +#include +#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 "); 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); } diff --git a/libs/util/gib_parse.c b/libs/util/gib_parse.c index 7058e5b13..089af35a6 100644 --- a/libs/util/gib_parse.c +++ b/libs/util/gib_parse.c @@ -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 (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); } diff --git a/libs/util/gib_process.c b/libs/util/gib_process.c index 6eb74842a..0de961259 100644 --- a/libs/util/gib_process.c +++ b/libs/util/gib_process.c @@ -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; } diff --git a/nq/source/host.c b/nq/source/host.c index d42b51a47..cecb9d636 100644 --- a/nq/source/host.c +++ b/nq/source/host.c @@ -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 (); diff --git a/qw/source/cl_main.c b/qw/source/cl_main.c index 4e4f9443c..0ac898df5 100644 --- a/qw/source/cl_main.c +++ b/qw/source/cl_main.c @@ -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 (); diff --git a/qw/source/sv_main.c b/qw/source/sv_main.c index e014c6ac6..477b24ef0 100644 --- a/qw/source/sv_main.c +++ b/qw/source/sv_main.c @@ -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);