From 59fbd48a81edef3d683d0e86ea6e5935d3ba1f5e Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Tue, 28 Jan 2003 21:16:21 +0000 Subject: [PATCH] Overhauled GIB to parse scripts in advance, among other design and language changes. There still remains some bugs to be squashed, a feature or two to add, and some polishing to be done. However, it seems to be in a workable state. --- include/QF/Makefile.am | 2 +- include/QF/cbuf.h | 17 +- include/QF/gib_buffer.h | 47 ++- include/QF/gib_builtin.h | 14 +- include/QF/gib_execute.h | 33 ++ include/QF/gib_function.h | 10 +- include/QF/gib_parse.h | 10 +- include/QF/gib_process.h | 6 +- include/QF/gib_tree.h | 57 +++ include/QF/gib_vars.h | 24 +- libs/console/client.c | 2 +- libs/gib/Makefile.am | 4 +- libs/gib/gib_buffer.c | 115 +++++- libs/gib/gib_builtin.c | 430 ++++++++------------ libs/gib/gib_execute.c | 264 +++++++++++++ libs/gib/gib_function.c | 63 ++- libs/gib/gib_init.c | 8 +- libs/gib/gib_parse.c | 814 ++++++++++++++++++-------------------- libs/gib/gib_process.c | 389 +++++++----------- libs/gib/gib_thread.c | 5 +- libs/gib/gib_tree.c | 88 +++++ libs/gib/gib_vars.c | 236 +++++------ libs/util/cbuf.c | 59 +-- libs/util/idparse.c | 95 ++++- tools/carne/main.c | 17 +- 25 files changed, 1554 insertions(+), 1255 deletions(-) create mode 100644 include/QF/gib_execute.h create mode 100644 include/QF/gib_tree.h create mode 100644 libs/gib/gib_execute.c create mode 100644 libs/gib/gib_tree.c diff --git a/include/QF/Makefile.am b/include/QF/Makefile.am index d08df584b..a50cdaa1f 100644 --- a/include/QF/Makefile.am +++ b/include/QF/Makefile.am @@ -3,7 +3,7 @@ SUBDIRS = GL plugin includedir = $(prefix)/include/QF include_HEADERS = bspfile.h cbuf.h cdaudio.h checksum.h clip_hull.h cmd.h \ console.h crc.h csqc.h cvar.h dstring.h draw.h gib_buffer.h \ - gib_builtin.h gib_function.h gib_init.h gib_parse.h gib_process.h \ + gib_builtin.h gib_execute.h gib_function.h gib_init.h gib_parse.h gib_process.h \ gib_regex.h gib_thread.h gib_vars.h hash.h hl.h idparse.h in_event.h \ info.h input.h joystick.h keys.h link.h locs.h mathlib.h mdfour.h \ model.h modelgen.h msg.h pak.h pakfile.h pcx.h plugin.h pr_comp.h \ diff --git a/include/QF/cbuf.h b/include/QF/cbuf.h index c4a60cc07..e9bd4e039 100644 --- a/include/QF/cbuf.h +++ b/include/QF/cbuf.h @@ -39,14 +39,13 @@ typedef struct cbuf_args_s { int argc; struct dstring_s **argv; + void **argm; // Metadata (optional) const char **args; int argv_size; } cbuf_args_t; typedef struct cbuf_s { - struct dstring_s *buf; - struct dstring_s *line; cbuf_args_t *args; struct cbuf_interpreter_s *interpreter; @@ -57,21 +56,22 @@ typedef struct cbuf_s { CBUF_STATE_WAIT, // Buffer is stalled until next frame CBUF_STATE_ERROR, // An unrecoverable error occured CBUF_STATE_STACK, // A buffer has been added to the stack + CBUF_STATE_DONE, // This buffer has completed execution } state; - qboolean strict; // Should we tolerate unknown commands? - + qboolean strict; // Should we tolerate unknown commands double resumetime; // Time when stack can be executed again - void *data; // Pointer to a custom structure if needed + void *data; // Pointer to interpreter data } cbuf_t; typedef struct cbuf_interpreter_s { - void (*extract_line) (struct cbuf_s *cbuf); - void (*parse_line) (struct cbuf_s *cbuf); - void (*execute_line) (struct cbuf_s *cbuf); void (*construct) (struct cbuf_s *cbuf); void (*destruct) (struct cbuf_s *cbuf); + void (*add) (struct cbuf_s *cbuf, const char *str); + void (*insert) (struct cbuf_s *cbuf, const char *str); + void (*execute) (struct cbuf_s *cbuf); + void (*execute_sets) (struct cbuf_s *cbuf); void (*reset) (struct cbuf_s *cbuf); } cbuf_interpreter_t; @@ -85,6 +85,7 @@ cbuf_t * Cbuf_New (cbuf_interpreter_t *interp); void Cbuf_Delete (cbuf_t *cbuf); void Cbuf_DeleteStack (cbuf_t *stack); +void Cbuf_PushStack (cbuf_t *new); void Cbuf_AddText (cbuf_t *cbuf, const char *text); void Cbuf_InsertText (cbuf_t *cbuf, const char *text); void Cbuf_Execute (cbuf_t *cbuf); diff --git a/include/QF/gib_buffer.h b/include/QF/gib_buffer.h index 5de7e9dda..4abf42c17 100644 --- a/include/QF/gib_buffer.h +++ b/include/QF/gib_buffer.h @@ -29,38 +29,35 @@ $Id$ */ +#include "QF/cbuf.h" +#include "QF/gib_tree.h" +#include "QF/dstring.h" + #define GIB_DATA(buffer) ((gib_buffer_data_t *)(buffer->data)) + typedef struct gib_buffer_data_s { + struct gib_tree_s *program, *ip; struct dstring_s *arg_composite; - struct dstring_s *current_token; - struct dstring_s *loop_program; - struct dstring_s *loop_data; - - char *loop_var_p, *loop_list_p, *loop_ifs_p; - - // 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 - } ret; - + qboolean done, waitret, haveret; + struct gib_sstack_s { + struct gib_dsarray_s { + struct dstring_s **dstrs; + unsigned int realsize, size; + } *values; + unsigned int size, p; + } stack; struct hashtab_s *locals; // Local variables - - enum { - GIB_BUFFER_NORMAL, // Normal buffer - GIB_BUFFER_LOOP, // Looping buffer - GIB_BUFFER_PROXY // Responsible for embedded command - } type; + struct hashtab_s *globals; // Current domain } gib_buffer_data_t; void GIB_Buffer_Construct (struct cbuf_s *cbuf); void GIB_Buffer_Destruct (struct cbuf_s *cbuf); void GIB_Buffer_Reset (struct cbuf_s *cbuf); +void GIB_Buffer_Add (cbuf_t *cbuf, const char *str); +void GIB_Buffer_Insert (cbuf_t *cbuf, const char *str); +void GIB_Buffer_Push_Sstack (struct cbuf_s *cbuf); +void GIB_Buffer_Pop_Sstack (struct cbuf_s *cbuf); +dstring_t *GIB_Buffer_Dsarray_Get (struct cbuf_s *cbuf); + +extern struct cbuf_interpreter_s gib_interp; diff --git a/include/QF/gib_builtin.h b/include/QF/gib_builtin.h index 3ad96de29..4bd5a6390 100644 --- a/include/QF/gib_builtin.h +++ b/include/QF/gib_builtin.h @@ -35,22 +35,20 @@ typedef struct gib_builtin_s { struct dstring_s *name; void (*func) (void); - 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; +extern char gib_null_string[]; + #define GIB_Argc() (cbuf_active->args->argc) -#define GIB_Argv(x) ((x) < cbuf_active->args->argc ? cbuf_active->args->argv[(x)]->str : "") -#define GIB_Args(x) ((x) < cbuf_active->args->argc ? cbuf_active->args->args[(x)] : "") +#define GIB_Argv(x) ((x) < cbuf_active->args->argc ? cbuf_active->args->argv[(x)]->str : gib_null_string) +#define GIB_Args(x) ((x) < cbuf_active->args->argc ? cbuf_active->args->args[(x)] : gib_null_string) #define GIB_Argd(x) ((x) < cbuf_active->args->argc ? cbuf_active->args->argv[(x)] : NULL) +#define GIB_Argm(x) ((x) < cbuf_active->args->argc ? (gib_tree_t *)cbuf_active->args->argm[(x)] : NULL) #define GIB_USAGE(x) (Cbuf_Error ("syntax", "%s: invalid syntax\nusage: %s %s", GIB_Argv(0), GIB_Argv(0), (x))) void GIB_Arg_Strip_Delim (unsigned int arg); dstring_t *GIB_Return (const char *str); -void GIB_Builtin_Add (const char *name, void (*func) (void), enum gib_builtin_type_e type); +void GIB_Builtin_Add (const char *name, void (*func) (void)); gib_builtin_t *GIB_Builtin_Find (const char *name); void GIB_Builtin_Init (qboolean sandbox); diff --git a/include/QF/gib_execute.h b/include/QF/gib_execute.h new file mode 100644 index 000000000..194819ddc --- /dev/null +++ b/include/QF/gib_execute.h @@ -0,0 +1,33 @@ +/* + #FILENAME# + + #DESCRIPTION# + + Copyright (C) 2002 #AUTHOR# + + Author: #AUTHOR# + Date: #DATE# + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +int GIB_Execute_For_Next (cbuf_t *cbuf); +void GIB_Execute (cbuf_t *cbuf); diff --git a/include/QF/gib_function.h b/include/QF/gib_function.h index 43e78ef0d..38450ab18 100644 --- a/include/QF/gib_function.h +++ b/include/QF/gib_function.h @@ -32,12 +32,18 @@ #ifndef __gib_function_h #define __gib_function_h +#include "QF/hash.h" +#include "QF/gib_tree.h" + typedef struct gib_function_s { - struct dstring_s *name, *program; + const char *name; + struct dstring_s *text; + struct gib_tree_s *program; + struct hashtab_s *globals; qboolean exported; } gib_function_t; -void GIB_Function_Define (const char *name, const char *program); +void GIB_Function_Define (const char *name, const char *text, gib_tree_t *program, hashtab_t *globals); gib_function_t *GIB_Function_Find (const char *name); void GIB_Function_Prepare_Args (cbuf_t *cbuf, cbuf_args_t *args); void GIB_Function_Execute (cbuf_t *cbuf, gib_function_t *func, cbuf_args_t *args); diff --git a/include/QF/gib_parse.h b/include/QF/gib_parse.h index b4a9b56e2..6b513811b 100644 --- a/include/QF/gib_parse.h +++ b/include/QF/gib_parse.h @@ -29,15 +29,17 @@ $Id$ */ +#include "QF/gib_tree.h" + inline qboolean GIB_Escaped (const char *str, int i); char GIB_Parse_Match_Brace (const char *str, unsigned int *i); char GIB_Parse_Match_Backtick (const char *str, unsigned int *i); char GIB_Parse_Match_Index (const char *str, unsigned int *i); char GIB_Parse_Match_Paren (const char *str, unsigned int *i); +char GIB_Parse_Match_Var (const char *str, unsigned int *i); -void GIB_Parse_Strip_Comments (struct cbuf_s *cbuf); -void GIB_Parse_Extract_Line (struct cbuf_s *cbuf); -void GIB_Parse_Tokenize_Line (struct cbuf_s *cbuf); +gib_tree_t *GIB_Parse_Lines (const char *program, unsigned int flags); +gib_tree_t *GIB_Parse_Embedded (const char *program, unsigned int flags, gib_tree_t **embedded); -extern struct cbuf_interpreter_s gib_interp; +extern qboolean gib_parse_error; diff --git a/include/QF/gib_process.h b/include/QF/gib_process.h index 0beea9708..3c9835538 100644 --- a/include/QF/gib_process.h +++ b/include/QF/gib_process.h @@ -29,5 +29,7 @@ $Id$ */ -int GIB_Process_Math (struct dstring_s *token); -int GIB_Process_Token (struct dstring_s *token, char delim); + +int GIB_Process_Embedded (gib_tree_t *node, cbuf_args_t *args); +int GIB_Process_Math (struct dstring_s *token, unsigned int i); +void GIB_Process_Escapes (char *str); diff --git a/include/QF/gib_tree.h b/include/QF/gib_tree.h new file mode 100644 index 000000000..c94f88c23 --- /dev/null +++ b/include/QF/gib_tree.h @@ -0,0 +1,57 @@ +/* + #FILENAME# + + #DESCRIPTION# + + Copyright (C) 2003 #AUTHOR# + + Author: #AUTHOR# + Date: #DATE# + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __GIB_TREE_H +#define __GIB_TREE_H + +#define TREE_NORMAL 0 // Normal node +#define TREE_PERM 1 // Permanent (part of a function) +#define TREE_CONCAT 2 // Concatenate to previous +#define TREE_EMBED 4 // Embedded command (expect return value) +#define TREE_P_EMBED 8 // Embedded stuff needs to be processed +#define TREE_P_MATH 16 // Math needs be evaluated +#define TREE_ASPLIT 32 // Token is the name of an array that should be split +#define TREE_FUNC 64 // Node is the first in a function +#define TREE_COND 128 // Conditional jump (if or while command) +#define TREE_END 256 // Node ends a loop or conditional +#define TREE_FORNEXT 512 // For loop is starting again + +typedef struct gib_tree_s { + const char *str; + char delim; + struct gib_tree_s *children, *next, *parent, *jump; + unsigned int flags, start, end; +} gib_tree_t; + +gib_tree_t *GIB_Tree_New (unsigned int flags); +void GIB_Tree_Free_Recursive (gib_tree_t *tree, qboolean force); +void GIB_Tree_Add_Flag_Recursive (gib_tree_t *tree, unsigned int flag); +#endif /* __GIB_TREE_H */ diff --git a/include/QF/gib_vars.h b/include/QF/gib_vars.h index f696d32ae..59c9efb08 100644 --- a/include/QF/gib_vars.h +++ b/include/QF/gib_vars.h @@ -35,16 +35,18 @@ extern hashtab_t *gib_globals; typedef struct gib_var_s { - struct dstring_s *key, *value; - struct hashtab_s *subvars; + const char *key; + struct dstring_s **array; + unsigned int size; } gib_var_t; -void GIB_Var_Set_Local (cbuf_t *cbuf, const char *key, const char *value); -void GIB_Var_Set_Global (const char *key, const char *value); -void GIB_Var_Set (cbuf_t *cbuf, char *key, const char *value); -const char *GIB_Var_Get_Local (cbuf_t *cbuf, const char *key); -const char *GIB_Var_Get_Global (const char *key); -const char *GIB_Var_Get (cbuf_t *cbuf, char *key); -const char *GIB_Var_Get_Key (void *ele, void *ptr); -void GIB_Var_Free (void *ele, void *ptr); -void GIB_Var_Free_Global (const char *key); +typedef struct gib_domain_s { + const char *name; + hashtab_t *vars; +} gib_domain_t; + +gib_var_t *GIB_Var_Get (hashtab_t *first, hashtab_t *second, const char *key); +gib_var_t *GIB_Var_Get_Complex (hashtab_t **first, hashtab_t **second, char *key, unsigned int *ind, qboolean create); +hashtab_t *GIB_Domain_Get (const char *name); + +void GIB_Var_Init (void); diff --git a/libs/console/client.c b/libs/console/client.c index 552f4cfbd..14e9b8042 100644 --- a/libs/console/client.c +++ b/libs/console/client.c @@ -410,7 +410,7 @@ C_Init (void) "file"); // register GIB builtins - GIB_Builtin_Add ("print::center", C_GIB_Print_Center_f, GIB_BUILTIN_NORMAL); + GIB_Builtin_Add ("print::center", C_GIB_Print_Center_f); con_initialized = true; } diff --git a/libs/gib/Makefile.am b/libs/gib/Makefile.am index 80ef3b79d..f4bfa2511 100644 --- a/libs/gib/Makefile.am +++ b/libs/gib/Makefile.am @@ -6,5 +6,5 @@ lib_LTLIBRARIES= libQFgib.la libQFgib_la_LDFLAGS= -version-info 1:0:0 libQFgib_la_SOURCES= \ - gib_buffer.c gib_builtin.c gib_function.c gib_parse.c gib_process.c \ - gib_regex.c gib_thread.c gib_vars.c gib_init.c ops.c exp.c regex.c + gib_buffer.c gib_builtin.c gib_execute.c gib_function.c gib_parse.c gib_process.c \ + gib_regex.c gib_thread.c gib_vars.c gib_init.c gib_tree.c ops.c exp.c regex.c diff --git a/libs/gib/gib_buffer.c b/libs/gib/gib_buffer.c index f4f243d59..a2927ba00 100644 --- a/libs/gib/gib_buffer.c +++ b/libs/gib/gib_buffer.c @@ -41,35 +41,124 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/dstring.h" #include "QF/cbuf.h" #include "QF/hash.h" +#include "QF/gib_parse.h" #include "QF/gib_buffer.h" +#include "QF/gib_tree.h" +#include "QF/gib_vars.h" +#include "QF/gib_execute.h" void GIB_Buffer_Construct (struct cbuf_s *cbuf) { cbuf->data = calloc (1, sizeof (gib_buffer_data_t)); GIB_DATA (cbuf)->arg_composite = dstring_newstr (); - GIB_DATA (cbuf)->current_token = dstring_newstr (); - GIB_DATA (cbuf)->ret.retval = dstring_newstr (); + GIB_DATA (cbuf)->globals = gib_globals; cbuf->strict = true; } 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); - if (GIB_DATA (cbuf)->loop_data) - dstring_delete (GIB_DATA(cbuf)->loop_data); - 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); + gib_buffer_data_t *g = GIB_DATA(cbuf); + unsigned int i, j; + + dstring_delete (g->arg_composite); + if (g->locals) + Hash_DelTable (g->locals); + if (g->program) + GIB_Tree_Free_Recursive (g->program, false); + for (i = 0; i < g->stack.size; i++) { + for (j = 0; j < g->stack.values[i].realsize; j++) + dstring_delete (g->stack.values[i].dstrs[j]); + if (g->stack.values[i].dstrs) + free (g->stack.values[i].dstrs); + } + if (g->stack.values) + free (g->stack.values); free (cbuf->data); } void -GIB_Buffer_Reset (struct cbuf_s *cbuf) +GIB_Buffer_Add (cbuf_t *cbuf, const char *str) { - GIB_DATA (cbuf)->ret.waiting = GIB_DATA (cbuf)->ret.available = false; + gib_buffer_data_t *g = GIB_DATA (cbuf); + gib_tree_t **save, *cur; + + cbuf_active = cbuf; + + if (g->program) { + for (cur = g->program; cur->next; cur = cur->next); + save = &cur->next; + } else + save = &g->program; + *save = GIB_Parse_Lines (str, TREE_NORMAL); + if (gib_parse_error) + Cbuf_Error ("parse", "Parse error in program!"); } + +void +GIB_Buffer_Insert (cbuf_t *cbuf, const char *str) +{ + gib_buffer_data_t *g = GIB_DATA (cbuf); + gib_tree_t *lines, *cur; + + if ((lines = GIB_Parse_Lines (str, TREE_NORMAL))) { + for (cur = lines; cur; cur = cur->next); + cur->next = g->program; + g->program = lines; + } + if (gib_parse_error) + Cbuf_Error ("parse", "Parse error in program!"); +} + +void +GIB_Buffer_Reset (cbuf_t *cbuf) +{ + GIB_DATA(cbuf)->done = GIB_DATA(cbuf)->waitret = GIB_DATA(cbuf)->haveret = false; + if (GIB_DATA(cbuf)->program) + GIB_Tree_Free_Recursive (GIB_DATA(cbuf)->program, false); + GIB_DATA(cbuf)->ip = 0; + GIB_DATA(cbuf)->program = 0; +} + +void +GIB_Buffer_Push_Sstack (struct cbuf_s *cbuf) +{ + gib_buffer_data_t *g = GIB_DATA(cbuf); + if (++g->stack.p > g->stack.size) { + g->stack.values = realloc(g->stack.values, sizeof (struct gib_dsarray_s) * g->stack.p); + g->stack.values[g->stack.p-1].dstrs = 0; + g->stack.values[g->stack.p-1].size = g->stack.values[g->stack.p-1].realsize = 0; + g->stack.size = g->stack.p; + } + g->stack.values[g->stack.p-1].size = 0; +} + +void +GIB_Buffer_Pop_Sstack (struct cbuf_s *cbuf) +{ + GIB_DATA(cbuf)->stack.p--; +} + +dstring_t * +GIB_Buffer_Dsarray_Get (struct cbuf_s *cbuf) +{ + struct gib_dsarray_s *vals = GIB_DATA(cbuf)->stack.values+GIB_DATA(cbuf)->stack.p-1; + if (++vals->size > vals->realsize) { + vals->dstrs = realloc (vals->dstrs, sizeof (dstring_t *) * vals->size); + vals->dstrs[vals->size-1] = dstring_newstr (); + vals->realsize = vals->size; + } else + dstring_clearstr (vals->dstrs[vals->size-1]); + return vals->dstrs[vals->size-1]; +} + +cbuf_interpreter_t gib_interp = { + GIB_Buffer_Construct, + GIB_Buffer_Destruct, + GIB_Buffer_Add, + GIB_Buffer_Insert, + GIB_Execute, + GIB_Execute, + GIB_Buffer_Reset +}; diff --git a/libs/gib/gib_builtin.c b/libs/gib/gib_builtin.c index 6bb491216..ca3f54e0a 100644 --- a/libs/gib/gib_builtin.c +++ b/libs/gib/gib_builtin.c @@ -54,6 +54,7 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/hash.h" #include "QF/dstring.h" #include "QF/gib_parse.h" +#include "QF/gib_execute.h" #include "QF/gib_builtin.h" #include "QF/gib_buffer.h" #include "QF/gib_function.h" @@ -62,6 +63,8 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/gib_thread.h" #include "regex.h" +char gib_null_string[] = ""; + hashtab_t *gib_builtins; /* @@ -87,7 +90,7 @@ GIB_Builtin_Free (void *ele, void *ptr) Registers a new builtin GIB command. */ void -GIB_Builtin_Add (const char *name, void (*func) (void), enum gib_builtin_type_e type) +GIB_Builtin_Add (const char *name, void (*func) (void)) { gib_builtin_t *new; @@ -97,7 +100,6 @@ GIB_Builtin_Add (const char *name, void (*func) (void), enum gib_builtin_type_e 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); } @@ -117,34 +119,19 @@ GIB_Builtin_Find (const char *name) return (gib_builtin_t *) Hash_Find (gib_builtins, name); } -/* - GIB_Arg_Strip_Delim - - Strips any wrapping characters off of the - specified argument. Useful for GIB_BUILTIN_NOPROCESS - or GIB_BUILTIN_FIRSTONLY builtins. -*/ -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; - } -} - dstring_t * GIB_Return (const char *str) { - if (GIB_DATA(cbuf_active)->type != GIB_BUFFER_PROXY) - return 0; - dstring_clearstr (GIB_DATA(cbuf_active->up)->ret.retval); - GIB_DATA(cbuf_active->up)->ret.available = true; - if (!str) - return GIB_DATA(cbuf_active->up)->ret.retval; - else - dstring_appendstr (GIB_DATA(cbuf_active->up)->ret.retval, str); + dstring_t *dstr; + if (GIB_DATA(cbuf_active)->waitret) { + GIB_DATA(cbuf_active)->haveret = true; + dstr = GIB_Buffer_Dsarray_Get (cbuf_active); + dstring_clearstr (dstr); + if (!str) + return dstr; + else + dstring_appendstr (dstr, str); + } return 0; } @@ -156,10 +143,23 @@ GIB_Return (const char *str) static void GIB_Function_f (void) { + gib_tree_t *program; + if (GIB_Argc () != 3) GIB_USAGE ("name program"); - else - GIB_Function_Define (GIB_Argv(1), GIB_Argv(2)); + else { + // Is the function program already tokenized? + if (GIB_Argm (2)->delim != '{') { + // Parse on the fly + if (!(program = GIB_Parse_Lines (GIB_Argv(2), TREE_NORMAL))) { + // Error! + Cbuf_Error ("parse", "Parse error while defining function '%s'.", GIB_Argv(1)); + return; + } + } else + program = GIB_Argm (2)->children; + GIB_Function_Define (GIB_Argv(1), GIB_Argv(2), program, GIB_DATA(cbuf_active)->globals); + } } static void @@ -170,7 +170,7 @@ GIB_Function_Get_f (void) else { gib_function_t *f; if ((f = GIB_Function_Find (GIB_Argv (1)))) - GIB_Return (f->program->str); + GIB_Return (f->text->str); else GIB_Return (""); } @@ -179,118 +179,72 @@ GIB_Function_Get_f (void) static void GIB_Local_f (void) { - int i; + unsigned int i, index; + hashtab_t *zero = 0; if (GIB_Argc () < 2) GIB_USAGE ("var1 [var2 var3 ...]"); else for (i = 1; i < GIB_Argc(); i++) - GIB_Var_Set_Local (cbuf_active, GIB_Argv(i), ""); + GIB_Var_Get_Complex (&GIB_DATA(cbuf_active)->locals, &zero, GIB_Argv(i), &index, true); } + static void GIB_Global_f (void) { - int i; + unsigned int i, index; + hashtab_t *zero = 0; if (GIB_Argc () < 2) GIB_USAGE ("var1 [var2 var3 ...]"); else for (i = 1; i < GIB_Argc(); i++) - GIB_Var_Set_Global (GIB_Argv(i), ""); + GIB_Var_Get_Complex (&GIB_DATA(cbuf_active)->globals, &zero, GIB_Argv(i), &index, true); } static void -GIB_Global_Delete_f (void) +GIB_Global_Domain_f (void) { - if (GIB_Argc () != 2) - GIB_USAGE ("var"); - if (GIB_Var_Get_Global (GIB_Argv(1))) - GIB_Var_Free_Global (GIB_Argv(1)); + if (GIB_Argc() != 2) + GIB_USAGE ("domain"); + else + GIB_DATA(cbuf_active)->globals = GIB_Domain_Get (GIB_Argv(1)); } static void GIB_Return_f (void) { - cbuf_t *sp; + cbuf_t *sp = cbuf_active->up; - if (GIB_Argc () > 2) - GIB_USAGE ("[value]"); + GIB_DATA(cbuf_active)->done = true; + if (!GIB_Argm(0)->next) // No return values + return; + else if (GIB_Argc() == 1) // Empty return array + GIB_DATA(sp)->waitret = false; + else if (!sp || // Nothing above us on the stack + sp->interpreter != &gib_interp || // Not a GIB buffer + !GIB_DATA(sp)->waitret) // Doesn't want a return value + Sys_DPrintf("GIB warning: unwanted return value(s) discarded.\n"); // Not a serious error else { - 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 (!sp->up || // Nothing above us on the stack - sp->up->interpreter != &gib_interp || // Not a GIB buffer - GIB_DATA(sp->up)->type != GIB_BUFFER_PROXY || // Not a proxy buffer - !sp->up->up || // Nothing above proxy buffer on the stack - sp->up->up->interpreter != &gib_interp || // Not a GIB buffer to return to - !GIB_DATA(sp->up->up)->ret.waiting) // Buffer doesn't want a return value - Sys_Printf("GIB warning: unwanted return value discarded.\n"); // Not a serious error - else { - 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; + unsigned int i; + dstring_t *dstr; + for (i = 1; i < GIB_Argc(); i++) { + dstr = GIB_Buffer_Dsarray_Get (sp); + dstring_clearstr (dstr); + dstring_appendstr (dstr, GIB_Argv(i)); } + GIB_DATA(sp)->waitret = false; } } -static 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 (GIB_Argv(0)[2]) - 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 - GIB_USAGE ("condition program [else ...]"); -} - -static void -GIB_While_f (void) -{ - if (GIB_Argc() != 3) { - GIB_USAGE ("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; - } -} - +/* static void GIB_Field_Get_f (void) { unsigned int field; - char *end; - const char *list, *ifs; + char *list, *end; + const char *ifs; if (GIB_Argc() < 3 || GIB_Argc() > 4) { GIB_USAGE ("list element [fs]"); return; @@ -303,7 +257,7 @@ GIB_Field_Get_f (void) ifs = " \t\n\r"; if (!*ifs) { if (field < strlen(GIB_Argv(1))) { - ((char *)GIB_Argv(1))[field+1] = 0; + GIB_Argv(1)[field+1] = 0; GIB_Return (GIB_Argv(1)+field); } else GIB_Return (""); @@ -321,103 +275,65 @@ GIB_Field_Get_f (void) } field--; } - for (end = (char *)list; !strchr(ifs, *end); end++); + for (end = list; !strchr(ifs, *end); end++); *end = 0; GIB_Return (list); } - - -static void -GIB___For_f (void) -{ - char *end = 0, old = 0; - if (!GIB_DATA(cbuf_active)->loop_list_p[0]) { - Cbuf_InsertText (cbuf_active, "break;"); - return; - } - if (!GIB_DATA(cbuf_active)->loop_ifs_p[0]) { - end = GIB_DATA(cbuf_active)->loop_list_p; - old = end[1]; - end[1] = 0; - GIB_Var_Set_Local (cbuf_active, GIB_DATA(cbuf_active)->loop_var_p, GIB_DATA(cbuf_active)->loop_list_p++); - end[1] = old; - return; - } - for (end = GIB_DATA(cbuf_active)->loop_list_p; !strchr(GIB_DATA(cbuf_active)->loop_ifs_p, *end); end++); - if (*end) { - old = *end; - *end = 0; - } - GIB_Var_Set_Local (cbuf_active, GIB_DATA(cbuf_active)->loop_var_p, GIB_DATA(cbuf_active)->loop_list_p); - if (old) - *end = old; - while (*end && strchr(GIB_DATA(cbuf_active)->loop_ifs_p, *end)) - end++; - GIB_DATA(cbuf_active)->loop_list_p = end; -} - +*/ + static void GIB_For_f (void) { - if (strcmp ("in", GIB_Argv (2)) || - (GIB_Argc() == 7 && strcmp ("by", GIB_Argv(4))) || - (GIB_Argc() != 5 && GIB_Argc() != 7)) - GIB_USAGE ("variable in list [by fs] program"); - else if (GIB_Argv (3)[0]) { - char *ll; - const char *ifs; - cbuf_t *sub = Cbuf_New (&gib_interp); - // Create loop buffer - GIB_DATA(sub)->type = GIB_BUFFER_LOOP; - GIB_DATA(sub)->locals = GIB_DATA(cbuf_active)->locals; - GIB_DATA(sub)->loop_program = dstring_newstr (); - GIB_DATA(sub)->loop_data = dstring_newstr (); - if (cbuf_active->down) - Cbuf_DeleteStack (cbuf_active->down); - cbuf_active->down = sub; - sub->up = cbuf_active; - // Store all for-loop data in one big dstring (easy to clean up) - dstring_appendstr (GIB_DATA(sub)->loop_data, GIB_Argv(3)); - dstring_append (GIB_DATA(sub)->loop_data, GIB_Argv(1), strlen(GIB_Argv(1))+1); - if (GIB_Argc() == 7) - ifs = GIB_Argv (5); - else if (!(ifs = GIB_Var_Get_Local (cbuf_active, "ifs"))) - ifs = " \n\r\t"; - dstring_append (GIB_DATA(sub)->loop_data, ifs, strlen(ifs)+1); - // Store pointers to data - for (ll = GIB_DATA(sub)->loop_data->str; *ll && strchr (ifs, *ll); ll++); - GIB_DATA(sub)->loop_list_p = ll; // List to iterate through - GIB_DATA(sub)->loop_var_p = GIB_DATA(sub)->loop_data->str + strlen(GIB_Argv(3))+1; // Var to use - GIB_DATA(sub)->loop_ifs_p = GIB_DATA(sub)->loop_var_p + strlen(GIB_Argv(1))+1; // Internal field separator - dstring_appendstr (GIB_DATA(sub)->loop_program, "__for;"); - dstring_appendstr (GIB_DATA(sub)->loop_program, GIB_Argc() == 7 ? GIB_Argv (6) : GIB_Argv(4)); - Cbuf_AddText (sub, GIB_DATA(sub)->loop_program->str); - cbuf_active->state = CBUF_STATE_STACK; + dstring_t *dstr; + unsigned int i; + if (GIB_Argc() < 5) { + GIB_DATA(cbuf_active)->ip = GIB_DATA(cbuf_active)->ip->jump; + return; } + GIB_Buffer_Push_Sstack (cbuf_active); + dstr = GIB_Buffer_Dsarray_Get (cbuf_active); + dstring_clearstr (dstr); + dstring_appendstr (dstr, GIB_Argv(1)); + for (i = GIB_Argc()-2; i > 2; i--) { + dstr = GIB_Buffer_Dsarray_Get (cbuf_active); + dstring_appendstr (dstr, GIB_Argv(i)); + } + GIB_Execute_For_Next (cbuf_active); } static 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 double freed - dstring_clearstr (cbuf_active->buf); - } + if (!GIB_DATA(cbuf_active)->ip->jump) { + Cbuf_Error ("loop", "Break command attempted outside of a loop."); + return; + } + if (!GIB_DATA(cbuf_active)->ip->jump->flags & TREE_COND) // In a for loop? + GIB_Buffer_Pop_Sstack (cbuf_active); // Kill it + GIB_DATA(cbuf_active)->ip = GIB_DATA(cbuf_active)->ip->jump->jump; } +static gib_tree_t gib_cont = { + "", + ' ', + 0, 0, 0, 0, + TREE_NORMAL +}; + static void GIB_Continue_f (void) { - if (GIB_DATA(cbuf_active)->type != GIB_BUFFER_LOOP) - Cbuf_Error ("syntax", - "continue attempted outside of a loop" - ); - else - dstring_clearstr (cbuf_active->buf); + if (!GIB_DATA(cbuf_active)->ip->jump) { + Cbuf_Error ("loop", "Continue command attempted outside of a loop."); + return; + } + if (GIB_DATA(cbuf_active)->ip->jump->flags & TREE_COND) { + gib_cont.next = GIB_DATA(cbuf_active)->ip->jump; + GIB_DATA(cbuf_active)->ip = &gib_cont; + } else { + GIB_Execute_For_Next (cbuf_active); + GIB_DATA(cbuf_active)->ip = GIB_DATA(cbuf_active)->ip->jump; + } } // Note: this is a standard console command, not a GIB builtin @@ -451,7 +367,7 @@ GIB_Function_Export_f (void) if (!(f = GIB_Function_Find (GIB_Argv (i)))) Cbuf_Error ("function", "function::export: function '%s' not found", GIB_Argv (i)); else if (!f->exported) { - Cmd_AddCommand (f->name->str, GIB_Runexported_f, "Exported GIB function."); + Cmd_AddCommand (f->name, GIB_Runexported_f, "Exported GIB function."); f->exported = true; } } @@ -482,7 +398,7 @@ static void GIB_String_Findsub_f (void) { dstring_t *ret; - const char *haystack, *res; + char *haystack, *res; if (GIB_Argc() != 3) { GIB_USAGE ("string substr"); return; @@ -547,25 +463,26 @@ GIB_Regex_Extract_f (void) int i; char o; - if (GIB_Argc() < 4) { - GIB_USAGE ("string regex options [var1 var2 var3 ...]"); + if (GIB_Argc() != 4) { + GIB_USAGE ("string regex options"); return; } - match = calloc (GIB_Argc() - 4, sizeof(regmatch_t)); + match = calloc (32, sizeof(regmatch_t)); if (!(reg = GIB_Regex_Compile (GIB_Argv(2), REG_EXTENDED | GIB_Regex_Translate_Options (GIB_Argv(3))))) Cbuf_Error ("regex", "%s: %s", GIB_Argv(0), GIB_Regex_Error ()); - else if (!regexec(reg, GIB_Argv(1), GIB_Argc() - 4, match, 0) && match[0].rm_eo) { - for (i = 0; i < GIB_Argc() - 4; i++) { - if (match[i].rm_so != -1 && *GIB_Argv(i+4)) { + else if (!regexec(reg, GIB_Argv(1), 32, match, 0) && match[0].rm_eo) { + if (!(ret = GIB_Return(0))) + return; + dsprintf (ret, "%lu", (unsigned long) match[0].rm_eo); + for (i = 0; i < 32; i++) { + if (match[i].rm_so != -1) { o = GIB_Argv(1)[match[i].rm_eo]; - ((char *)GIB_Argv(1))[match[i].rm_eo] = 0; - GIB_Var_Set_Local (cbuf_active, GIB_Argv(i+4), GIB_Argv(1)+match[i].rm_so); - ((char *)GIB_Argv(1))[match[i].rm_eo] = o; + GIB_Argv(1)[match[i].rm_eo] = 0; + GIB_Return (GIB_Argv(1)+match[i].rm_so); + GIB_Argv(1)[match[i].rm_eo] = o; } } - if ((ret = GIB_Return (0))) - dsprintf (ret, "%lu", (unsigned long) match[0].rm_eo); } else GIB_Return ("-1"); free (match); @@ -593,20 +510,12 @@ GIB_Thread_Kill_f (void) GIB_USAGE ("id"); else { gib_thread_t *thread; - cbuf_t *cur; unsigned long int id = strtoul (GIB_Argv(1), 0, 10); thread = GIB_Thread_Find (id); if (!thread) { Cbuf_Error ("thread", "thread.kill: thread %lu does not exist.", id); return; } - for (cur = thread->cbuf; cur; cur = cur->down) { - // Kill all loops - if (GIB_DATA(cur)->type == GIB_BUFFER_LOOP) - GIB_DATA(cur)->type = GIB_BUFFER_PROXY; // Proxy to prevent shared locals being freed - dstring_clearstr (cur->line); - dstring_clearstr (cur->buf); - } } } @@ -656,7 +565,7 @@ static void GIB_File_Read_f (void) { QFile *file; - const char *path; + char *path; int len; dstring_t *ret; @@ -697,7 +606,7 @@ static void GIB_File_Write_f (void) { QFile *file; - const char *path; + char *path; if (GIB_Argc () != 3) { GIB_USAGE ("file data"); @@ -728,10 +637,8 @@ GIB_File_Find_f (void) { DIR *directory; struct dirent *entry; - const char *path, *glob = 0; - char *s; - const char *ifs; - dstring_t *list; + const char *path, *glob = 0; + char *s; if (GIB_Argc () != 2) { GIB_USAGE ("glob"); @@ -759,25 +666,16 @@ GIB_File_Find_f (void) "file.find: could not open directory %s: %s", path, strerror (errno)); return; } - if ((list = GIB_Return (0))) { - if (!(ifs = GIB_Var_Get_Local (cbuf_active, "ifs"))) - ifs = "\n"; // Newlines don't appear in filenames and are part of the default ifs - while ((entry = readdir (directory))) { - if (strcmp (entry->d_name, ".") && - strcmp (entry->d_name, "..") && - !fnmatch (glob, entry->d_name, 0)) { - dstring_appendsubstr (list, ifs, 1); - dstring_appendstr (list, entry->d_name); - } - } - } + while ((entry = readdir (directory))) + if (strcmp (entry->d_name, ".") && strcmp (entry->d_name, "..") && !fnmatch (glob, entry->d_name, 0)) + GIB_Return (entry->d_name); closedir (directory); } static void GIB_File_Move_f (void) { - const char *path1, *path2; + char *path1, *path2; if (GIB_Argc () != 3) { GIB_USAGE ("from_file to_file"); @@ -804,7 +702,7 @@ GIB_File_Move_f (void) static void GIB_File_Delete_f (void) { - const char *path; + char *path; if (GIB_Argc () != 2) { GIB_USAGE ("file"); @@ -827,24 +725,21 @@ GIB_Range_f (void) { double i, inc, start, limit; dstring_t *dstr; - const char *ifs; if (GIB_Argc () < 3 || GIB_Argc () > 4) { GIB_USAGE ("lower upper [step]"); return; } - if (!(dstr = GIB_Return (0))) - return; // Why bother? limit = atof(GIB_Argv(2)); start = atof(GIB_Argv(1)); if (GIB_Argc () == 4 && (inc = atof(GIB_Argv(3))) == 0.0) return; else inc = limit < start ? -1.0 : 1.0; - if (!(ifs = GIB_Var_Get_Local (cbuf_active, "ifs"))) - ifs = " "; - for (i = atof(GIB_Argv(1)); inc < 0 ? i >= limit : i <= limit; i += inc) - dasprintf(dstr, "%.10g%.1s", i, ifs); - dstr->str[dstr->size-2] = 0; + for (i = atof(GIB_Argv(1)); inc < 0 ? i >= limit : i <= limit; i += inc) { + if (!(dstr = GIB_Return(0))) + return; + dsprintf(dstr, "%.10g", i); + } } static void @@ -860,42 +755,37 @@ GIB_Print_f (void) void GIB_Builtin_Init (qboolean sandbox) { - gib_globals = Hash_NewTable (512, GIB_Var_Get_Key, GIB_Var_Free, 0); if (sandbox) GIB_File_Transform_Path = GIB_File_Transform_Path_Secure; else GIB_File_Transform_Path = GIB_File_Transform_Path_Null; - GIB_Builtin_Add ("function", GIB_Function_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("function::get", GIB_Function_Get_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("function::export", GIB_Function_Export_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("local", GIB_Local_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("global", GIB_Global_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("global::delete", GIB_Global_Delete_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 ("field::get", GIB_Field_Get_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("for", GIB_For_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("__for", GIB___For_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("break", GIB_Break_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("continue", GIB_Continue_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("string::length", GIB_String_Length_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("string::equal", GIB_String_Equal_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("string::findsub", GIB_String_Findsub_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("regex::match", GIB_Regex_Match_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("regex::replace", GIB_Regex_Replace_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("regex::extract", GIB_Regex_Extract_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("thread::create", GIB_Thread_Create_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("thread::kill", GIB_Thread_Kill_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("event::register", GIB_Event_Register_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("file::read", GIB_File_Read_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("file::write", GIB_File_Write_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("file::find", GIB_File_Find_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("file::move", GIB_File_Move_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("file::delete", GIB_File_Delete_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("range", GIB_Range_f, GIB_BUILTIN_NORMAL); - GIB_Builtin_Add ("print", GIB_Print_f, GIB_BUILTIN_NORMAL); + GIB_Builtin_Add ("function", GIB_Function_f); + GIB_Builtin_Add ("function::get", GIB_Function_Get_f); + GIB_Builtin_Add ("function::export", GIB_Function_Export_f); + GIB_Builtin_Add ("local", GIB_Local_f); + GIB_Builtin_Add ("global", GIB_Global_f); + GIB_Builtin_Add ("global::domain", GIB_Global_Domain_f); + GIB_Builtin_Add ("return", GIB_Return_f); +// GIB_Builtin_Add ("dieifnot", GIB_Dieifnot_f); + GIB_Builtin_Add ("for", GIB_For_f); + GIB_Builtin_Add ("break", GIB_Break_f); + GIB_Builtin_Add ("continue", GIB_Continue_f); + GIB_Builtin_Add ("string::length", GIB_String_Length_f); + GIB_Builtin_Add ("string::equal", GIB_String_Equal_f); + GIB_Builtin_Add ("string::findsub", GIB_String_Findsub_f); + GIB_Builtin_Add ("regex::match", GIB_Regex_Match_f); + GIB_Builtin_Add ("regex::replace", GIB_Regex_Replace_f); + GIB_Builtin_Add ("regex::extract", GIB_Regex_Extract_f); + GIB_Builtin_Add ("thread::create", GIB_Thread_Create_f); + GIB_Builtin_Add ("thread::kill", GIB_Thread_Kill_f); + GIB_Builtin_Add ("event::register", GIB_Event_Register_f); + GIB_Builtin_Add ("file::read", GIB_File_Read_f); + GIB_Builtin_Add ("file::write", GIB_File_Write_f); + GIB_Builtin_Add ("file::find", GIB_File_Find_f); + GIB_Builtin_Add ("file::move", GIB_File_Move_f); + GIB_Builtin_Add ("file::delete", GIB_File_Delete_f); + GIB_Builtin_Add ("range", GIB_Range_f); + GIB_Builtin_Add ("print", GIB_Print_f); } diff --git a/libs/gib/gib_execute.c b/libs/gib/gib_execute.c new file mode 100644 index 000000000..387dc2f1a --- /dev/null +++ b/libs/gib/gib_execute.c @@ -0,0 +1,264 @@ +/* + gib_execute.c + + GIB runtime execution functions + + Copyright (C) 2002 Brian Koropoff + + Author: Brian Koropoff + Date: #DATE# + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static __attribute__ ((unused)) const char rcsid[] = + "$Id$"; + +#include +#include + +#include "QF/cbuf.h" +#include "QF/cmd.h" +#include "QF/gib_buffer.h" +#include "QF/gib_vars.h" +#include "QF/gib_process.h" +#include "QF/gib_builtin.h" +#include "QF/gib_function.h" +#include "QF/gib_execute.h" + +static void +GIB_Execute_Generate_Composite (struct cbuf_s *cbuf) +{ + cbuf_args_t *args = cbuf->args; + int i; + + dstring_clearstr (GIB_DATA (cbuf)->arg_composite); + for (i = 0; i < args->argc; i++) { + // ->str could be moved by realloc when a dstring is resized + // so save the offset instead of the pointer + args->args[i] = (const char *) strlen (GIB_DATA (cbuf)->arg_composite->str); + dstring_appendstr (GIB_DATA (cbuf)->arg_composite, args->argv[i]->str); + dstring_appendstr (GIB_DATA (cbuf)->arg_composite, " "); + } + + // Get rid of trailing space + GIB_DATA (cbuf)->arg_composite->str[strlen(GIB_DATA (cbuf)->arg_composite->str)-1] = 0; + + for (i = 0; i < args->argc; i++) + // now that arg_composite is done we can add the pointer to the stored + // offsets and get valid pointers. This *should* be portable. + args->args[i] += (unsigned long int) GIB_DATA (cbuf)->arg_composite->str; +} + +static void +GIB_Execute_Split_Array (cbuf_t *cbuf) +{ + gib_var_t *var; + unsigned int i; + int start = 0, end = 0; + char *c, *str = cbuf->args->argv[cbuf->args->argc-1]->str+1; + void *m = cbuf->args->argm[cbuf->args->argc-1]; + + i = strlen(str)-1; + if (str[i] == ']') + for (; i; i--) + if (str[i] == '[') { + str[i] = 0; + start = atoi (str+i+1); + if ((c = strchr (str+i+1, ':'))) + end = atoi (c+1); + else + end = start; + break; + } + cbuf->args->argc--; + if (!(var = GIB_Var_Get_Complex (&GIB_DATA(cbuf)->locals, &gib_globals, str, &i, false))) + return; + while (start < 0) + start += var->size-1; + while (end < 0) + end += var->size; + if (start >= var->size) + return; + if (end >= var->size || !end) + end = var->size; + for (i = start; i < end; i++) { + if (var->array[i]) + Cbuf_ArgsAdd (cbuf->args, var->array[i]->str); + else + Cbuf_ArgsAdd (cbuf->args, ""); + cbuf->args->argm[cbuf->args->argc-1] = m; + } +} + +static int +GIB_Execute_Prepare_Line (cbuf_t *cbuf, gib_tree_t *line) +{ + gib_tree_t *cur; + cbuf_args_t *args = cbuf->args; + unsigned int pos; + + args->argc = 0; + + for (cur = line->children; cur; cur = cur->next) { + if (cur->flags & TREE_CONCAT) { + pos = args->argv[args->argc-1]->size-1; + if (cur->flags & TREE_P_EMBED) { + GIB_Process_Embedded (cur, cbuf->args); + } else + dstring_appendstr (args->argv[args->argc-1], cur->str); + } else { + pos = 0; + if (cur->flags & TREE_P_EMBED) { + Cbuf_ArgsAdd (args, ""); + GIB_Process_Embedded (cur, cbuf->args); + } else + Cbuf_ArgsAdd (args, cur->str); + args->argm[args->argc-1] = cur; + } + if (cur->flags & TREE_P_MATH && GIB_Process_Math (args->argv[args->argc-1], pos)) + return -1; + if (cur->flags & TREE_ASPLIT) + GIB_Execute_Split_Array (cbuf); + } + return 0; +} + +static void +GIB_Execute_Assign (cbuf_t *cbuf) +{ + cbuf_args_t *args = cbuf->args; + gib_var_t *var; + unsigned int index, i, len, el; + + // First, grab our variable + var = GIB_Var_Get_Complex (&GIB_DATA(cbuf)->locals, &GIB_DATA(cbuf)->globals, args->argv[0]->str, &index, true); + // Now, expand the array to the correct size + len = args->argc-2 + index; + if (len >= var->size) { + var->array = realloc (var->array, len * sizeof (dstring_t *)); + memset (var->array+var->size, 0, (len-var->size) * sizeof (dstring_t *)); + var->size = len; + } else if (len < var->size) { + for (i = len; i < var->size; i++) + if (var->array[i]) + dstring_delete (var->array[i]); + var->array = realloc (var->array, len * sizeof (dstring_t *)); + } + var->size = len; + for (i = 2; i < args->argc; i++) { + el = i-2+index; + if (var->array[el]) + dstring_clearstr (var->array[el]); + else + var->array[el] = dstring_newstr (); + dstring_appendstr (var->array[el], args->argv[i]->str); + } +} + +int +GIB_Execute_For_Next (cbuf_t *cbuf) +{ + unsigned int index; + gib_var_t *var; + struct gib_dsarray_s *array = GIB_DATA(cbuf)->stack.values+GIB_DATA(cbuf)->stack.p-1; + if (array->size == 1) { + GIB_Buffer_Pop_Sstack (cbuf); + return 0; + } + array->size--; + var = GIB_Var_Get_Complex (&GIB_DATA(cbuf)->locals, &GIB_DATA(cbuf)->globals, array->dstrs[0]->str, &index, true); + dstring_clearstr (var->array[index]); + dstring_appendstr (var->array[index], array->dstrs[array->size]->str); + return 1; +} + +void +GIB_Execute (cbuf_t *cbuf) +{ + gib_buffer_data_t *g = GIB_DATA (cbuf); + gib_builtin_t *b; + gib_function_t *f; + + if (g->waitret) { + Cbuf_Error ("return", "Embedded function '%s' did not return a value.", cbuf->args->argv[0]->str); + return; + } + if (!g->program) { + return; + } + if (!g->ip) + g->ip = g->program; + while (!g->done) { + if (GIB_Execute_Prepare_Line (cbuf, g->ip)) + return; + if (g->ip->flags & TREE_COND) { + if (!atoi(cbuf->args->argv[1]->str)) + g->ip = g->ip->jump; + } else if (g->ip->flags & TREE_FORNEXT) { + if (GIB_Execute_For_Next (cbuf)) + g->ip = g->ip->jump; + } else if (g->ip->flags & TREE_END) { + g->ip = g->ip->jump; + if (g->ip->flags & TREE_COND) + continue; + } else if (cbuf->args->argc) { + if (g->ip->flags & TREE_EMBED) { + g->waitret = true; + GIB_Buffer_Push_Sstack (cbuf); // Make room for return values + } + if (!strcmp (cbuf->args->argv[1]->str, "=") && ((gib_tree_t *)cbuf->args->argm[1])->delim == ' ') + GIB_Execute_Assign (cbuf); + else if ((b = GIB_Builtin_Find (cbuf->args->argv[0]->str))) { + b->func (); + // If there already was an error, don't override it + if (g->ip->flags & TREE_EMBED && !cbuf->state) { + if (!g->haveret) { + Cbuf_Error ("return", "Embedded builtin '%s' did not return a value.", cbuf->args->argv[0]->str); + return; + } + g->haveret = g->waitret = 0; + } + } else if ((f = GIB_Function_Find (cbuf->args->argv[0]->str))) { + cbuf_t *new = Cbuf_New (&gib_interp); + cbuf->down = new; + new->up = cbuf; + cbuf->state = CBUF_STATE_STACK; + GIB_Function_Execute (new, f, cbuf->args); + if (g->ip->flags & TREE_EMBED) + g->waitret = true; + } else { + GIB_Execute_Generate_Composite (cbuf); + Cmd_Command (cbuf->args); + } + } + if (!(g->ip = g->ip->next)) // No more commands + g->done = true; + if (cbuf->state) // Let the stack walker figure out what to do + return; + } + g->done = false; + GIB_Tree_Free_Recursive (g->program, false); + g->program = g->ip = 0; +} diff --git a/libs/gib/gib_function.c b/libs/gib/gib_function.c index 8e9dc46c6..095279359 100644 --- a/libs/gib/gib_function.c +++ b/libs/gib/gib_function.c @@ -36,6 +36,7 @@ static __attribute__ ((unused)) const char rcsid[] = "$Id$"; #include +#include #include "QF/sys.h" #include "QF/dstring.h" @@ -45,6 +46,7 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/gib_buffer.h" #include "QF/gib_function.h" #include "QF/gib_vars.h" +#include "QF/gib_tree.h" #include "QF/va.h" hashtab_t *gib_functions = 0; @@ -56,12 +58,11 @@ hashtab_t *gib_functions = 0; a pointer to it. */ static gib_function_t * -GIB_Function_New (void) +GIB_Function_New (const char *name) { gib_function_t *new = calloc (1, sizeof (gib_function_t)); - new->name = dstring_newstr(); - new->program = dstring_newstr(); - + new->text = dstring_newstr(); + new->name = strdup (name); return new; } @@ -71,40 +72,46 @@ GIB_Function_New (void) static const char * GIB_Function_Get_Key (void *ele, void *ptr) { - return ((gib_function_t *)ele)->name->str; + return ((gib_function_t *)ele)->name; } static void GIB_Function_Free (void *ele, void *ptr) { gib_function_t *func = (gib_function_t *)ele; - dstring_delete (func->name); - dstring_delete (func->program); + dstring_delete (func->text); + free ((void *)func->name); + if (func->program) + GIB_Tree_Free_Recursive (func->program, true); free (func); } /* GIB_Function_Define - Sets the program text of a GIB function, + Sets the program and text of a GIB function, allocating one and adding it to the functions hash if needed. */ void -GIB_Function_Define (const char *name, const char *program) +GIB_Function_Define (const char *name, const char *text, gib_tree_t *program, hashtab_t *globals) { gib_function_t *func; if (!gib_functions) gib_functions = Hash_NewTable (1024, GIB_Function_Get_Key, GIB_Function_Free, 0); func = Hash_Find(gib_functions, name); - if (func) - dstring_clearstr (func->program); - else { - func = GIB_Function_New (); - dstring_appendstr (func->name, name); + if (func) { + dstring_clearstr (func->text); + GIB_Tree_Free_Recursive (func->program, true); + } else { + func = GIB_Function_New (name); Hash_Add (gib_functions, func); } - dstring_appendstr (func->program, program); + dstring_appendstr (func->text, text); + func->program = program; + func->globals = globals; + GIB_Tree_Add_Flag_Recursive (program, TREE_PERM); // Don't free this, we are using it now + program->flags |= TREE_FUNC; // Even when forcing a recursive free, don't free this } /* @@ -125,11 +132,24 @@ GIB_Function_Find (const char *name) void GIB_Function_Prepare_Args (cbuf_t *cbuf, cbuf_args_t *args) { - int i; - - for (i = 0; i < args->argc; i++) - GIB_Var_Set_Local (cbuf, va("%i", i), args->argv[i]->str); - GIB_Var_Set_Local (cbuf, "argc", va("%i", args->argc)); + static hashtab_t *zero = 0; + unsigned int i; + gib_var_t *var; + static char argss[] = "args", argcs[] = "argc"; + + var = GIB_Var_Get_Complex (&GIB_DATA(cbuf)->locals, &zero, argss, &i, true); + var->array = realloc (var->array, sizeof (dstring_t *) * args->argc); + memset (var->array+1, 0, (args->argc-1) * sizeof (dstring_t *)); + var->size = args->argc; + for (i = 0; i < args->argc; i++) { + if (var->array[i]) + dstring_clearstr(var->array[i]); + else + var->array[i] = dstring_newstr(); + dstring_appendstr (var->array[i], args->argv[i]->str); + } + var = GIB_Var_Get_Complex (&GIB_DATA(cbuf)->locals, &zero, argcs, &i, true); + dsprintf(var->array[0], "%u", args->argc); } /* @@ -142,6 +162,7 @@ GIB_Function_Prepare_Args (cbuf_t *cbuf, cbuf_args_t *args) void GIB_Function_Execute (cbuf_t *cbuf, gib_function_t *func, cbuf_args_t *args) { - Cbuf_AddText (cbuf, func->program->str); + GIB_DATA(cbuf)->program = func->program; + GIB_DATA(cbuf)->globals = func->globals; GIB_Function_Prepare_Args (cbuf, args); } diff --git a/libs/gib/gib_init.c b/libs/gib/gib_init.c index 2edc583ef..1e243e269 100644 --- a/libs/gib/gib_init.c +++ b/libs/gib/gib_init.c @@ -39,11 +39,13 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/qtypes.h" #include "QF/cbuf.h" #include "QF/quakefs.h" -#include "QF/gib_init.h" #include "QF/gib_parse.h" #include "QF/gib_builtin.h" #include "QF/gib_regex.h" #include "QF/gib_thread.h" +#include "QF/gib_vars.h" +#include "QF/gib_buffer.h" +#include "QF/gib_init.h" #include "QF/cmd.h" #include "QF/sys.h" #include "QF/zone.h" @@ -76,7 +78,7 @@ GIB_Exec_Override_f (void) { sub->up = cbuf_active; cbuf_active->state = CBUF_STATE_STACK; Cbuf_AddText (sub, f); - GIB_Parse_Strip_Comments (sub); + //GIB_Parse_Strip_Comments (sub); } else Cbuf_InsertText (cbuf_active, f); Hunk_FreeToLowMark (mark); @@ -90,6 +92,8 @@ GIB_Init (qboolean sandbox) Cmd_RemoveCommand ("exec"); Cmd_AddCommand ("exec", GIB_Exec_Override_f, "Execute a script file."); } + // Initialize variables + GIB_Var_Init (); // Initialize regex cache GIB_Regex_Init (); // Initialize builtins diff --git a/libs/gib/gib_parse.c b/libs/gib/gib_parse.c index 0b1be1da9..46f0ef2fe 100644 --- a/libs/gib/gib_parse.c +++ b/libs/gib/gib_parse.c @@ -51,18 +51,6 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/gib_vars.h" #include "QF/gib_parse.h" -// Interpreter structure and prototypes - -static void GIB_Parse_Execute_Line (cbuf_t *cbuf); - -cbuf_interpreter_t gib_interp = { - GIB_Parse_Extract_Line, - GIB_Parse_Tokenize_Line, - GIB_Parse_Execute_Line, - GIB_Buffer_Construct, - GIB_Buffer_Destruct, - GIB_Buffer_Reset -}; /* GIB_Escaped @@ -82,15 +70,19 @@ GIB_Escaped (const char *str, int i) return c & 1; } - /* - GIB_Parse_Match_Dquote + GIB_Parse_Match_* - Progresses an index variable through a string - until a double quote is reached. Returns - 0 on success, or the character it could not - match otherwise + These are the workhorses of the GIB parser. They iterate + an index variable through a string until an appropriate + matching character is found, calling themselves and their + neighbors recursively to handle sections of string that they + are uninterested in. + + FIXME: Make sure everything is calling everything else it might + need to. Make appropriate functions intolerant of newlines. */ + static char GIB_Parse_Match_Dquote (const char *str, unsigned int *i) { @@ -105,16 +97,6 @@ GIB_Parse_Match_Dquote (const char *str, unsigned int *i) return '\"'; } -/* - GIB_Parse_Match_Brace - - Progresses an index variable through a string - until a closing brace (}) is reached. Calls - other matching functions to skip areas of a - string it does not want to handle. Returns - 0 on success, or the character it could not - match otherwise. -*/ char GIB_Parse_Match_Brace (const char *str, unsigned int *i) { @@ -134,16 +116,6 @@ GIB_Parse_Match_Brace (const char *str, unsigned int *i) return '{'; } -/* - GIB_Parse_Match_Paren - - Progresses an index variable through a string - until a closing parenthesis is reached. Calls - other matching functions to skip areas of a - string it does not want to handle. Returns - 0 on success, or the character it could not - match otherwise. -*/ char GIB_Parse_Match_Paren (const char *str, unsigned int *i) { @@ -163,16 +135,6 @@ GIB_Parse_Match_Paren (const char *str, unsigned int *i) return '('; } -/* - GIB_Parse_Match_Backtick - - Progresses an index variable through a string - until a backtick (`) is reached. Calls - other matching functions to skip areas of a - string it does not want to handle. Returns - 0 on success, or the character it could not - match otherwise. -*/ char GIB_Parse_Match_Backtick (const char *str, unsigned int *i) { @@ -190,410 +152,420 @@ GIB_Parse_Match_Backtick (const char *str, unsigned int *i) return '`'; } -/* - GIB_Parse_Match_Index - - Progresses an index variable through a string - until a normal brace (]) is reached. Calls - other matching functions to skip areas of a - string it does not want to handle. Returns - 0 on success, or the character it could not - match otherwise. -*/ char GIB_Parse_Match_Index (const char *str, unsigned int *i) { + char c; unsigned int n = *i; for ((*i)++; str[*i]; (*i)++) { - if (str[*i] == ']') + if (str[*i] == '[' && (c = GIB_Parse_Match_Index (str, i))) + return c; + else if (str[*i] == ']') return 0; } *i = n; return '['; } -/* - GIB_Parse_Strip_Comments - - Finds instances of // comments in a cbuf - outside of double quotes and snips between - the // and the next newline or the end of - the string. -*/ -void -GIB_Parse_Strip_Comments (struct cbuf_s *cbuf) +char +GIB_Parse_Match_Var (const char *str, unsigned int *i) { - unsigned int i; - dstring_t *dstr = cbuf->buf; - char c, *n; - - for (i = 0; dstr->str[i]; i++) { - if (dstr->str[i] == '\"') { - if ((c = GIB_Parse_Match_Dquote (dstr->str, &i))) - // We don't care about parse errors here. - // Let the parser sort it out later. - return; - } else if (dstr->str[i] == '/' && dstr->str[i+1] == '/') { - if ((n = strchr (dstr->str+i, '\n'))) { - dstring_snip (dstr, i, n-dstr->str-i); - i--; - } else { - dstring_snip (dstr, i, strlen(dstr->str+i)); - break; - } - } - } -} - -/* - GIB_Parse_Extract_Line - - Extracts the next command from a cbuf and - deposits it in cbuf->line, removing the - portion used from the buffer -*/ -void -GIB_Parse_Extract_Line (struct cbuf_s *cbuf) -{ - unsigned int i; char c; - dstring_t *dstr = cbuf->buf; - - if (GIB_DATA(cbuf)->ret.waiting ) // Not ready for the next line - return; - - dstring_clearstr (cbuf->line); - - for (i = 0; dstr->str[i]; i++) { - if (dstr->str[i] == '{') { - if ((c = GIB_Parse_Match_Brace (dstr->str, &i))) - goto PARSE_ERROR; - } else if (dstr->str[i] == '\"') { - if ((c = GIB_Parse_Match_Dquote (dstr->str, &i))) - goto PARSE_ERROR; - } else if (dstr->str[i] == '\n' || dstr->str[i] == ';') - break; - } - - if (dstr->str[0]) { // If something is left in the buffer - if (i)// If we used any of it - dstring_insert (cbuf->line, 0, dstr->str, i); - // Clip out what we used - while (isspace ((byte) dstr->str[i]) || dstr->str[i] == ';') // Eliminate white space/null statements - i++; - dstring_snip (dstr, 0, i); - } - return; - -PARSE_ERROR: // Extract out the line where the parse error occurred - - for (; i && dstr->str[i] != '\n'; i--); - if (dstr->str[i] == '\n') - i++; - dstring_clearstr (cbuf->line); - dstring_appendstr (cbuf->line, dstr->str+i); - Cbuf_Error ("parse", "Could not find match for %c.", c); -} - -/* - GIB_Parse_Get_Token - - Progresses an index variable through a string - until the end of the next token is found. - Stores the token in dstr with or without - wrapping characters as specified by include_delim. - Returns 0 on error or the wrapping character of - the token otherwise -*/ -inline static char -GIB_Parse_Get_Token (const char *str, unsigned int *i, dstring_t *dstr, qboolean include_delim) -{ - int n; - char c; - - n = *i; // Save start position - if (str[*i] == '\"') { - if ((c = GIB_Parse_Match_Dquote (str, i))) { - Cbuf_Error ("parse", "Could not find matching %c", c); - return 0; // Parse error - } else { - dstring_insert (dstr, 0, str+n+!include_delim, *i-n+1-!include_delim-!include_delim); - return '\"'; - } - } else if (str[*i] == '{') { - if ((c = GIB_Parse_Match_Brace (str, i))) { - Cbuf_Error ("parse", "Could not find matching %c", c); - return 0; // Parse error - } else { - dstring_insert (dstr, 0, str+n+!include_delim, *i-n+1-!include_delim-!include_delim); - return '{'; - } - } else if (str[*i] == '(') { - if ((c = GIB_Parse_Match_Paren (str, i))) { - Cbuf_Error ("parse", "Could not find matching %c", c); - return 0; // Parse error - } else { - dstring_insert (dstr, 0, str+n+!include_delim, *i-n+1-!include_delim-!include_delim); - return '('; - } - } else { - while (str[*i] && !isspace((byte)str[*i]) && str[*i] != ',') { // find end of token - if (str[*i] == '`') { - if ((c = GIB_Parse_Match_Backtick (str, i))) { - Cbuf_Error ("parse", "Could not find matching %c", c); - return 0; // Parse error - } - } else if (str[*i] == '(') { - if ((c = GIB_Parse_Match_Paren (str, i))) { - Cbuf_Error ("parse", "Could not find matching %c", c); - return 0; // Parse error - } - } else if (str[*i] == '{') { - if ((c = GIB_Parse_Match_Brace (str, i))) { - Cbuf_Error ("parse", "Could not find matching %c", c); - return 0; // Parse error - } - } + (*i)++; + if (str[*i] == '{' && (c = GIB_Parse_Match_Brace (str, i))) + return c; + else { + for (; isalnum((byte) str[*i]) || str[*i] == '_'; (*i)++); + if (str[*i] == '[') { + if ((c = GIB_Parse_Match_Index (str, i))) + return c; (*i)++; } - dstring_insert (dstr, 0, str+n, *i-n); - return ' '; } - return 0; // We should never get here + return 0; } -/* - GIB_Parse_Generate_Composite - - Concatenates all parsed arguments in cbuf - into a single string and creates an array - of pointers to the beginnings of tokens - within it. -*/ -inline static void -GIB_Parse_Generate_Composite (struct cbuf_s *cbuf) -{ - cbuf_args_t *args = cbuf->args; - int i; - - dstring_clearstr (GIB_DATA (cbuf)->arg_composite); - for (i = 0; i < args->argc; i++) { - // ->str could be moved by realloc when a dstring is resized - // so save the offset instead of the pointer - args->args[i] = (const char *) strlen (GIB_DATA (cbuf)->arg_composite->str); - dstring_appendstr (GIB_DATA (cbuf)->arg_composite, args->argv[i]->str); - dstring_appendstr (GIB_DATA (cbuf)->arg_composite, " "); - } - - // Get rid of trailing space - GIB_DATA (cbuf)->arg_composite->str[strlen(GIB_DATA (cbuf)->arg_composite->str)-1] = 0; - - for (i = 0; i < args->argc; i++) - // now that arg_composite is done we can add the pointer to the stored - // offsets and get valid pointers. This *should* be portable. - args->args[i] += (unsigned long int) GIB_DATA (cbuf)->arg_composite->str; -} +qboolean gib_parse_error; -/* - GIB_Parse_Add_Token - - Adds a new token to the argument list args - or concatenates it to the end of the last - according to cat. -*/ -inline static void -GIB_Parse_Add_Token (struct cbuf_args_s *args, qboolean cat, dstring_t *token) -{ - if (cat) { - dstring_appendstr (args->argv[args->argc-1], token->str); - } else - Cbuf_ArgsAdd (args, token->str); -} +// FIXME: Concatenation in stupid circumstances should generate errors -/* - GIB_Parse_Tokenize_Line - - Tokenizes and processes an extracted command, - producing an argument list suitable for executing - the command. This function can be interrupted - to call a GIB subroutine, in which case it will - save important variables and start where it left - off when called again. -*/ -void -GIB_Parse_Tokenize_Line (struct cbuf_s *cbuf) +static gib_tree_t * +GIB_Parse_Tokens (const char *program, unsigned int *i, unsigned int flags, gib_tree_t **embedded) { - dstring_t *arg = GIB_DATA(cbuf)->current_token; - const char *str = cbuf->line->str; - cbuf_args_t *args = cbuf->args; + char c, delim, *str; + unsigned int tstart; + gib_tree_t *nodes = 0, *cur, *new, *embs = 0, *tmp; + gib_tree_t **node = &nodes; qboolean cat = false; - int noprocess; - char delim; - int i; - - // This function can be interrupted to call a GIB - // subroutine. First we need to clean up anything - // we left unfinished - - // Do we have a left-over token that needs processing? - - if (GIB_DATA(cbuf)->ret.waiting) { - if (GIB_Process_Token (arg, GIB_DATA(cbuf)->ret.delim)) - 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]) { - while (isspace((byte)str[i])) // Eliminate whitespace - i++; - if (!str[i]) // Blank token - break; - if (str[i] == ',') { // Concatenation + gib_parse_error = false; + + while (1) { + // Skip whitespace + while (program[*i] != '\n' && isspace((byte)program[*i])) + (*i)++; + // Check for concatenation, skip comma and any more whitespace + if (program[*i] == ',') { cat = true; - i++; + (*i)++; continue; } - dstring_clearstr (arg); - delim = GIB_Parse_Get_Token (str, &i, arg, noprocess == 1); - if (!delim) + // New line/command? + if (!program[*i] || program[*i] == '\n' || program[*i] == ';') break; - 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 + // Save our start position + tstart = *i + 1; + delim = program[*i]; + switch (delim) { + case '{': + if ((c = GIB_Parse_Match_Brace (program, i))) + goto ERROR; + break; + case '\"': + if ((c = GIB_Parse_Match_Dquote (program, i))) + goto ERROR; + break; + case '(': + if ((c = GIB_Parse_Match_Paren (program, i))) + goto ERROR; + break; + default: + // Find the end of a "normal" token + delim = ' '; + tstart = *i; + for (;program[*i] && !isspace((byte)program[*i]) && program[*i] != ',' && program[*i] != ';'; (*i)++) { + if (program[*i] == '{') { + if ((c = GIB_Parse_Match_Brace (program, i))) + goto ERROR; + } else if (program[*i] == '(') { + if ((c = GIB_Parse_Match_Paren (program, i))) + goto ERROR; + } else if (program[*i] == '`') { + if ((c = GIB_Parse_Match_Backtick (program, i))) + goto ERROR; + // Handle comments + } else if (program[*i] == '/' && program[*i+1] == '/') { + for((*i) += 2; program[*i] && program[*i] != '\n'; (*i)++); + return nodes; + } + } + } - if (noprocess > 1) - noprocess--; + cur = *node = GIB_Tree_New (flags); + cur->delim = delim; + str = calloc (*i - tstart + 1, sizeof(char)); + memcpy (str, program+tstart, *i - tstart); - GIB_Parse_Add_Token (cbuf->args, cat, arg); - if (cat) + // Don't bother parsing further if we are concatenating, as the resulting + // string would differ from the parsed program. + if (cur->delim == '{' && !cat) { + // Try to parse sub-program + if (!(new = GIB_Parse_Lines (str, flags))) + goto ERROR; + new->parent = cur; + cur->children = new; + // Check for embedded commands/variables + } else if (cur->delim == ' ' || cur->delim == '(') { + if (!(cur->children = GIB_Parse_Embedded (str, flags, &new))) { + // There could be no embedded elements, so check for a real error + if (gib_parse_error) + goto ERROR; + } else { + // Link/set flags + cur->children->parent = cur; + cur->flags |= TREE_P_EMBED; + // Add any embedded commands to top of chain + if (new) { + for (tmp = new; tmp->next; tmp = tmp->next); // Get to end of embedded list + tmp->next = embs; + embs = new; + } + } + // Check for array splitting + // Concatenating this onto something else is non-sensical + if (cur->delim == ' ' && str[0] == '@' && !cat) { + cur->flags |= TREE_ASPLIT; + } + // We can handle escape characters now + } else if (cur->delim == '\"') + GIB_Process_Escapes (str); + + cur->str = str; + // Set proper flags for GIB_Execute_Prepare_Line + if (cur->delim == '(') + cur->flags |= TREE_P_MATH; + if (cat) { + cur->flags |= TREE_CONCAT; cat = false; - + } + // Nothing left to parse? + if (!program[*i]) + break; + // On non-normal tokens, move past the delimeter + if (cur->delim != ' ') + (*i)++; + node = &cur->next; + } + *embedded = embs; + return nodes; +ERROR: + GIB_Tree_Free_Recursive (nodes, true); + gib_parse_error = true; + return 0; +} - if (cbuf->args->argc == 1) { - gib_builtin_t *b; - if ((b = GIB_Builtin_Find (cbuf->args->argv[0]->str))) - noprocess = b->type; +static gib_tree_t * +GIB_Parse_Semantic_Preprocess (gib_tree_t *line) +{ + gib_tree_t *p, *start = line; + unsigned int flags = line->flags; + while (!strcmp (line->children->str, "if")) { + // Sanity checking + if (!line->children->next || !line->children->next->next || !line->children->next->next->children || line->flags & TREE_EMBED) { + gib_parse_error = true; + return line; + } + // Set conditional flag + line->flags |= TREE_COND; + // Save our spot + p = line; + // Move subprogram inline + line->next = line->children->next->next->children; + line->next->parent = 0; + line->children->next->next->children = 0; + // Find end of subprogram + while (line->next) line = line->next; + line->next = GIB_Tree_New (flags | TREE_END); + line = line->next; + // Mark jump point + p->jump = line; + line->flags |= TREE_END; // Last instruction of subprogram + // Handle "else" + if (p->children->next->next->next && !strcmp (p->children->next->next->next->str, "else")) { + // Sanity checking + if (!p->children->next->next->next->next) { + gib_parse_error = true; + return line; + } + // Is "else" followed by a subprogram? + if (p->children->next->next->next->next->delim == '{') { + // Move subprogram inline + line->next = p->children->next->next->next->next->children; + line->next->parent = 0; + p->children->next->next->next->next->children = 0; + while (line->next) line = line->next; + } else { + // Push rest of tokens into a new line + line->next = GIB_Tree_New (flags); + line->next->children = p->children->next->next->next->next; + p->children->next->next->next->next = 0; + line = line->next; + } + } else break; // Don't touch if statements in the sub program + } + // Now we know our exit point, set it on all our ending instructions + while (start) { + if (start->flags & TREE_END && !start->jump) + start->jump = line; + start = start->next; + } + // Nothing expanded from a line remains, exit now + if (!line->children) + return line; + // If we have a while loop, handle that + if (!strcmp (line->children->str, "while")) { + // Sanity checks + if (!line->children->next || !line->children->next->next || !line->children->next->next->children || line->flags & TREE_EMBED) { + gib_parse_error = true; + return line; + } + // Set conditional flag + line->flags |= TREE_COND; + // Save our spot + p = line; + // Move subprogram inline + line->next = line->children->next->next->children; + line->next->parent = 0; + line->children->next->next->children = 0; + // Find end of subprogram, set jump point back to top of loop as we go + for (; line->next; line = line->next) + if (!line->jump) + line->jump = p; + line->next = GIB_Tree_New (flags | TREE_END); + line->next->jump = p; + line = line->next; + // Mark jump point out of loop + p->jump = line; + } else if (!strcmp (line->children->str, "for")) { + gib_tree_t *tmp; + // Sanity checks + if (!line->children->next || !line->children->next->next || strcmp (line->children->next->next->str, "in") || !line->children->next->next->next || !line->children->next->next->next->next) { + gib_parse_error = true; + return line; + } + // Find last token in line (contains program block) + for (tmp = line->children->next->next->next->next; tmp->next; tmp = tmp->next); + // More sanity + if (!tmp->children) { + gib_parse_error = true; + return line; + } + p = line; + // Move subprogram inline + line->next = tmp->children; + line->next->parent = 0; + tmp->children = 0; + // Find end of subprogram, set jump point back to top of loop as we go + for (; line->next; line = line->next) + if (!line->jump) + line->jump = p; + line->next = GIB_Tree_New (flags | TREE_FORNEXT); + line->next->jump = p; + line = line->next; + // Mark jump point out of loop + p->jump = line; + } + return line; +} + +gib_tree_t * +GIB_Parse_Lines (const char *program, unsigned int flags) +{ + unsigned int i = 0, lstart; + gib_tree_t *lines = 0, *cur, *tokens, **line = &lines, *embs; + char *str; + + while (1) { + while (isspace((byte)program[i]) || program[i] == ';') + i++; + if (!program[i]) + break; + lstart = i; + // If we parse something useful... + if ((tokens = GIB_Parse_Tokens (program, &i, flags, &embs))) { + // Link it in + cur = GIB_Tree_New (flags); + cur->delim = '\n'; + str = calloc (i - lstart + 1, sizeof(char)); + memcpy (str, program+lstart, i - lstart); + cur->str = str; + cur->children = tokens; + tokens->parent = cur; + // Line contains embedded commands? + if (embs) { + // Add them to chain before actual line + *line = embs; + for (; embs->next; embs = embs->next); + embs->next = cur; + } else + *line = cur; + // Do preprocessing + line = &(GIB_Parse_Semantic_Preprocess (cur))->next; + } + if (gib_parse_error) + goto ERROR; + } + return lines; +ERROR: + GIB_Tree_Free_Recursive (lines, true); + return 0; +} + +gib_tree_t * +GIB_Parse_Embedded (const char *program, unsigned int flags, gib_tree_t **embedded) +{ + unsigned int i, n, t; + char c, d, *str; + gib_parse_error = false; + + gib_tree_t *lines = 0, **line = &lines, *cur, *tokens, *emb, *tmp; + unsigned int start, end; + + *embedded = 0; + + for (i = 0; program[i]; i++) { + if (program[i] == '`' || (program[i] == '$' && program[i+1] == '(')) { + // Extract the embedded command + start = i; + if (program[i] == '`') { + n = i+1; + if ((c = GIB_Parse_Match_Backtick (program, &i))) + goto ERROR; + } else { + n = ++i+1; + if ((c = GIB_Parse_Match_Paren (program, &i))) + goto ERROR; + } + end = i+1; + // Construct the actual line to be executed + cur = GIB_Tree_New (flags | TREE_EMBED); + cur->delim = '`'; + str = calloc (i - n + 1, sizeof (char)); + memcpy (str, program+n, i - n); + cur->str = str; + + t = 0; + if (!(tokens = GIB_Parse_Tokens (cur->str, &t, flags, &emb))) { + c = 0; + goto ERROR; + } + cur->children = tokens; + tokens->parent = cur; + GIB_Parse_Semantic_Preprocess (cur)->next = *embedded; + if (gib_parse_error) + goto ERROR; + // Did this have embedded commands of it's own? + if (emb) { + // Link them in first + for (tmp = emb; tmp->next; tmp = tmp->next); + tmp->next = cur; + *embedded = emb; + } else + *embedded = cur; + // Create a representative child node for GIB_Process_Embedded to use + cur = GIB_Tree_New (flags | TREE_EMBED); + cur->delim = '`'; + // Save start/end indices + cur->start = start; + cur->end = end; + *line = cur; + line = &cur->next; + // Check for variable substitution + } else if (program[i] == '$' || program[i] == '#') { + // Extract variable name + start = i; + end = 0; + d = program[i]; + if (program[i+1] == '{') { + n = i+2; + end++; + } else + n = i+1; + if ((c = GIB_Parse_Match_Var (program, &i))) + goto ERROR; + end += i; + + cur = GIB_Tree_New (flags | TREE_EMBED); + cur->delim = d; + str = calloc (i - n + 1, sizeof(char)); + memcpy (str, program+n, i - n); + cur->str = str; + // Can we use the name as is, or must processing be done at runtime? + if (strchr (str, '$') || strchr (str, '#')) + cur->flags |= TREE_P_EMBED; + // Save start/end indices + cur->start = start; + cur->end = end; + *line = cur; + line = &cur->next; + // Don't skip anything important + if (program[n-1] != '{') + i--; } } - GIB_Parse_Generate_Composite (cbuf); - 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; + return lines; +ERROR: + gib_parse_error = true; + GIB_Tree_Free_Recursive (lines, true); + return 0; } - -static inline qboolean -GIB_Parse_Execute_Varexp (cbuf_t *cbuf) -{ - char *s; - const char *v; - double newval, other; - cbuf_args_t *args = cbuf->args; - if (args->argc == 1) { - s = args->argv[0]->str + strlen(args->argv[0]->str) - 2; - if (!strcmp (s, "++") || !strcmp (s, "--")) { - *s = 0; - if ((v = GIB_Var_Get (cbuf, args->argv[0]->str))) - newval = atof (v); - else - newval = 0.0; - switch (s[1]) { - case '+': - newval++; - break; - case '-': - newval--; - } - s = va("%.10g", newval); - GIB_Var_Set (cbuf, args->argv[0]->str, s); - GIB_Return (s); - return true; - } else - return false; - } else if (args->argc == 3 && strlen(args->argv[1]->str) <= 2 && strchr(args->argv[1]->str, '=')) { - s = args->argv[1]->str; - if (*s == '=') { - GIB_Return (args->argv[2]->str); - GIB_Var_Set (cbuf, args->argv[0]->str, args->argv[2]->str); - return true; - } else if (s[1] == '=') { - if ((v = GIB_Var_Get (cbuf, args->argv[0]->str))) - newval = atof (v); - else - newval = 0.0; - other = atof (args->argv[2]->str); - switch (*s) { - case '+': - newval += other; - break; - case '-': - newval -= other; - break; - case '*': - newval *= other; - break; - case '/': - newval /= other; - break; - default: - return false; - } - s = va("%.10g", newval); - GIB_Return (s); - GIB_Var_Set (cbuf, args->argv[0]->str, s); - return true; - } else - return false; - } else - return false; -} - - -/* - GIB_Parse_Execute_Line - - After an argument list has been created, - this function executes the appropriate command, - searching in this order: - - GIB builtins - GIB functions - Assignment to a local/global variable - Normal quake console commands -*/ -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 (args->argv[0]->str))) - b->func (); - else if ((f = GIB_Function_Find (args->argv[0]->str))) { - cbuf_t *sub = Cbuf_New (&gib_interp); - GIB_Function_Execute (sub, f, cbuf_active->args); - cbuf_active->down = sub; - sub->up = cbuf_active; - cbuf_active->state = CBUF_STATE_STACK; - } else if (!GIB_Parse_Execute_Varexp(cbuf)) - Cmd_Command (cbuf->args); - dstring_clearstr (cbuf->line); - // If this is a looping buffer and it is now empty, - // copy the loop program back in - if (GIB_DATA(cbuf)->type == GIB_BUFFER_LOOP && !cbuf->buf->str[0]) - Cbuf_AddText (cbuf, GIB_DATA(cbuf)->loop_program->str); -} - diff --git a/libs/gib/gib_process.c b/libs/gib/gib_process.c index 0962cd910..f27e0edda 100644 --- a/libs/gib/gib_process.c +++ b/libs/gib/gib_process.c @@ -39,282 +39,181 @@ static __attribute__ ((unused)) const char rcsid[] = #include #include +#include "QF/va.h" #include "QF/dstring.h" #include "QF/cbuf.h" #include "QF/cvar.h" #include "QF/gib_buffer.h" #include "QF/gib_parse.h" -#include "QF/gib_process.h" #include "QF/gib_vars.h" +#include "QF/gib_process.h" #include "exp.h" static int -GIB_Process_Index (dstring_t *index, unsigned int pos, int *i1, int *i2) +GIB_Process_Variable (dstring_t *token, unsigned int *i) { - int i, v1, v2; - char *p; - - - for (i = pos; index->str[i] != ']'; i++) - if (!index->str[i]) { - Cbuf_Error ("parse", "Could not find matching ["); + hashtab_t *one = GIB_DATA(cbuf_active)->locals, *two = GIB_DATA(cbuf_active)->globals; + unsigned int n = *i, j, k, start = *i, index = 0, len, len2; + gib_var_t *var = 0; + char c; + const char *str; + + (*i)++; + if (token->str[*i] == '{') { + if ((c = GIB_Parse_Match_Brace (token->str, i))) { + Cbuf_Error ("Parse", "Could not find match for %c.", c); return -1; } - v1 = atoi (index->str+pos+1); - if ((p = strchr (index->str+pos, ':'))) { - if (*(p+1) == ']') - v2 = -1; - else { - v2 = atoi (p+1); - if (v2 < 0) - v2--; + n += 2; + len = 1; + } else { + for (; isalnum((byte) token->str[*i]) || token->str[*i] == '_'; (*i)++); + if (token->str[*i] == '[') { + if ((c = GIB_Parse_Match_Index (token->str, i))) + return -1; + else + (*i)++; } + n++; + len = 0; + } + c = token->str[*i]; + token->str[*i] = 0; + + for (k = n; token->str[n]; n++) + if (token->str[n] == '$' || token->str[n] == '#') + if (GIB_Process_Variable (token, &n)) + return -1; + index = 0; + if (n && token->str[n-1] == ']') + for (j = n-1; j; j--) + if (token->str[j] == '[') { + index = atoi (token->str+j+1); + token->str[j] = 0; + } + if ((var = GIB_Var_Get (one, two, token->str+k)) && index < var->size && var->array[index]) { + if (token->str[start] == '#') + str = va("%u", var->size - index); + else + str = var->array[index]->str; } else - v2 = v1; - dstring_snip (index, pos, i - pos + 1); - *i1 = v1; - *i2 = v2; + str = ""; + token->str[n] = c; + len += n-start; + len2 = strlen (str); + dstring_replace (token, start, len, str, len2); + *i = start + len2 - 1; return 0; } -static unsigned int -GIB_Process_Variable (struct dstring_s *dstr, unsigned int pos, qboolean tolerant) -{ - cvar_t *cvar; - const char *str; - char *p, c; - - for (p = dstr->str+pos+1; tolerant ? *p : isalnum ((byte)*p) || *p == '_'; p++); - c = *p; - *p = 0; - if ((str = GIB_Var_Get_Local (cbuf_active, dstr->str+pos+1))) - ; // yay for us - else if ((str = GIB_Var_Get_Global (dstr->str+pos+1))) - ; // yay again - else if ((cvar = Cvar_FindVar (dstr->str+pos+1))) - str = cvar->string; - else - str = 0; - *p = c; - if (str) - dstring_replace (dstr, pos, p - dstr->str - pos, str, strlen(str)); - else - dstring_snip (dstr, pos, p - dstr->str - pos); - return str ? strlen (str) : 0; -} - int -GIB_Process_Math (struct dstring_s *token) +GIB_Process_Math (struct dstring_s *token, unsigned int i) { double value; - value = EXP_Evaluate (token->str); + value = EXP_Evaluate (token->str+i); if (EXP_ERROR) { Cbuf_Error ("math", "Expression \"%s\" caused an error:\n%s", token->str, EXP_GetErrorMsg()); return -1; } else { - dstring_clearstr (token); - dsprintf (token, "%.10g", value); + token->str[i] = 0; + token->size = i+1; + dasprintf (token, "%.10g", value); } return 0; } -static int -GIB_Process_Embedded (struct dstring_s *token) -{ - cbuf_t *sub; - int i, n, m, i1, i2, ofs = 0; - char c = 0, *p; - dstring_t *var = dstring_newstr (); - qboolean index; - - if (GIB_DATA(cbuf_active)->ret.waiting) { - if (!GIB_DATA(cbuf_active)->ret.available) { - GIB_DATA(cbuf_active)->ret.waiting = false; - Cbuf_Error ("return", "Embedded command did not return a value."); - return -1; - } - i = GIB_DATA(cbuf_active)->ret.token_pos; // Jump to the right place - } else - i = 0; - - for (; token->str[i]; i++) { - if (token->str[i] == '`' || (token->str[i] == '$' && token->str[i+1] == '(')) { - n = i; - switch (token->str[i]) { - case '`': - if ((c = GIB_Parse_Match_Backtick (token->str, &i))) { - Cbuf_Error ("parse", "Could not find matching %c", c); - return -1; - } - break; - case '$': - i++; - ofs = 1; - if ((c = GIB_Parse_Match_Paren (token->str, &i))) { - Cbuf_Error ("parse", "Could not find matching %c", c); - return -1; - } - } - if (GIB_DATA(cbuf_active)->ret.available) { - dstring_replace (token, n, i-n+1, GIB_DATA(cbuf_active)->ret.retval->str, - strlen(GIB_DATA(cbuf_active)->ret.retval->str)); - i = n + strlen(GIB_DATA(cbuf_active)->ret.retval->str) - 1; - GIB_DATA(cbuf_active)->ret.waiting = false; - GIB_DATA(cbuf_active)->ret.available = false; - } else { - sub = Cbuf_New (&gib_interp); - GIB_DATA(sub)->type = GIB_BUFFER_PROXY; - GIB_DATA(sub)->locals = GIB_DATA(cbuf_active)->locals; - dstring_insert (sub->buf, 0, token->str+n+ofs+1, i-n-ofs-1); - if (cbuf_active->down) - Cbuf_DeleteStack (cbuf_active->down); - cbuf_active->down = sub; - sub->up = cbuf_active; - cbuf_active->state = CBUF_STATE_STACK; - GIB_DATA(cbuf_active)->ret.waiting = true; - GIB_DATA(cbuf_active)->ret.token_pos = n; - return -1; - } - } else if (token->str[i] == '$') { - index = false; - if (token->str[i+1] == '{') { - n = i+1; - if ((c = GIB_Parse_Match_Brace (token->str, &n))) { - Cbuf_Error ("parse", "Could not find match for %c", c); - goto ERROR; - } - if (token->str[n+1] == '[') { - // Cut index out and put it with the variable - m = n+1; - index = true; - if ((c = GIB_Parse_Match_Index (token->str, &m))) { - Cbuf_Error ("parse", "Could not find match for %c", c); - goto ERROR; - } - dstring_insert (var, 0, token->str+n+1, m-n); - dstring_snip (token, n+1, m-n); - } - n -= i; - dstring_insert (var, 0, token->str+i+2, n-2); - dstring_insertstr (var, 0, "$"); - n++; - } else { - for (n = 1; isalnum((byte)token->str[i+n]) || - token->str[i+n] == '$' || - token->str[i+n] == '_' || - token->str[i+n] == '[' || - token->str[i+n] == ':'; n++) { - if (token->str[i+n] == '[') { - while (token->str[i+n] && token->str[i+n] != ']') - n++; - if (!token->str[i+n]) { - Cbuf_Error ("parse", "Could not find match for ["); - c = '['; - goto ERROR; - } - } - } - dstring_insert (var, 0, token->str+i, n); // extract it - } - for (m = 1; var->str[m]; m++) { - if (var->str[m] == '$') - m += GIB_Process_Variable (var, m, false) - 1; - } - i1 = -1; - if (var->str[strlen(var->str)-1] == ']' && (p = strrchr (var->str, '['))) { - index = true; - if (GIB_Process_Index(var, p-var->str, &i1, &i2)) { - c = '['; - goto ERROR; - } - } - GIB_Process_Variable (var, 0, true); - if (index) { - if (i1 < 0) { - i1 += strlen(var->str); - if (i1 < 0) - i1 = 0; - } else if (i1 >= strlen (var->str)) - i1 = strlen(var->str)-1; - if (i2 < 0) { - i2 += strlen(var->str); - if (i2 < 0) - i2 = 0; - } else if (i2 >= strlen (var->str)) - i2 = strlen(var->str)-1; - if (i2 < i1) - dstring_clearstr (var); - else { - if (i2 < strlen(var->str)-1) // Snip everthing after index 2 - dstring_snip (var, i2+1, strlen(var->str)-i2-1); - if (i1 > 0) // Snip everything before index 1 - dstring_snip (var, 0, i1); - } - } - dstring_replace (token, i, n, var->str, strlen(var->str)); - i += strlen (var->str) - 1; - dstring_clearstr (var); - } - } - return 0; - dstring_delete (var); -ERROR: - dstring_delete (var); - return c ? -1 : 0; -} - -static void -GIB_Process_Escapes (dstring_t *token) -{ - int i; - for (i = 0; token->str[i]; i++) { - if (token->str[i] == '\\') { - if (strlen(token->str+i+1) > 2 && - isdigit ((byte) token->str[i+1]) && - isdigit ((byte) token->str[i+2]) && - isdigit ((byte) token->str[i+3])) { - unsigned int num; - num = 100 * (token->str[i+1] - '0') + 10 * (token->str[i+2] - '0') + (token->str[i+3] - '0'); - if (num > 255) - dstring_snip (token, i, 4); - else { - dstring_snip (token, i, 3); - token->str[i] = (char) num; - } - } else switch (token->str[i+1]) { - case 'n': - token->str[i+1] = '\n'; - goto snip; - case 't': - token->str[i+1] = '\t'; - goto snip; - case 'r': - token->str[i+1] = '\r'; - goto snip; - case '\"': - case '\\': - goto snip; - default: - break; - snip: - dstring_snip (token, i, 1); - } - } - } -} - int -GIB_Process_Token (dstring_t *token, char delim) +GIB_Process_Embedded (gib_tree_t *node, cbuf_args_t *args) { - if (delim != '{' && delim != '\"') - if (GIB_Process_Embedded (token)) - return -1; - if (delim == '(') - if (GIB_Process_Math (token)) - return -1; - if (delim == '\"') - GIB_Process_Escapes (token); + unsigned int n, j; + gib_var_t *var; + gib_tree_t *cur; + unsigned int index, prev = 0; + const char *str = node->str; + + for (cur = node->children; cur; cur = cur->next) { + if (cur->start > prev) + dstring_appendsubstr (args->argv[args->argc-1], str+prev, cur->start - prev); + prev = cur->end; + if (!cur->str) { + struct gib_dsarray_s *retvals = GIB_DATA(cbuf_active)->stack.values+GIB_DATA(cbuf_active)->stack.p-1; + if (retvals->size != 1) { + if (!*args->argv[args->argc-1]->str) + args->argc--; + for (j = 0; j < retvals->size; j++) { + Cbuf_ArgsAdd (args, retvals->dstrs[j]->str); + args->argm[args->argc-1] = node; + } + if (str[prev] && retvals->size) // Still more stuff left? + Cbuf_ArgsAdd (args, ""); + } else + dstring_appendstr (args->argv[args->argc-1], retvals->dstrs[0]->str); + GIB_Buffer_Pop_Sstack (cbuf_active); + } else if (cur->flags & TREE_P_EMBED) { + n = args->argv[args->argc-1]->size-1; + if (cur->delim == '$') + dstring_appendstr (args->argv[args->argc-1], "${"); + else + dstring_appendstr (args->argv[args->argc-1], "#{"); + dstring_appendstr (args->argv[args->argc-1], cur->str); + dstring_appendstr (args->argv[args->argc-1], "}"); + if (GIB_Process_Variable (args->argv[args->argc-1], &n)) + return -1; + } else if ((var = GIB_Var_Get_Complex (&GIB_DATA(cbuf_active)->locals, &GIB_DATA(cbuf_active)->globals, (char *)cur->str, &index, false))) { + if (cur->delim == '$') + dstring_appendstr (args->argv[args->argc-1], var->array[index]->str); + else + dasprintf (args->argv[args->argc-1], "%u", var->size - index); + } + } + if (str[prev]) + dstring_appendstr (args->argv[args->argc-1], str+prev); return 0; } + +void +GIB_Process_Escapes (char *str) +{ + int i, j; + char c; + for (i = 0, j = 0; str[i]; j++) { + if (str[i] == '\\') { + i++; + if (isdigit ((byte) str[i]) && + isdigit ((byte) str[i+1]) && + isdigit ((byte) str[i+2])) { + unsigned int num; + if ((num = 100 * (str[i] - '0') + 10 * (str[i+1] - '0') + (str[i+2] - '0')) <= 255) { + c = (char) num; + i += 3; + } else + c = '\\'; + } else switch (str[i++]) { + case 'n': + c = '\n'; + break; + case 't': + c = '\t'; + break; + case 'r': + c = '\r'; + break; + case '\"': + c = '"'; + break; + case '\\': + c = '\\'; + break; + default: + c = '\\'; + i--; + break; + } + str[j] = c; + } else + str[j] = str[i++]; + } + str[j] = 0; +} diff --git a/libs/gib/gib_thread.c b/libs/gib/gib_thread.c index 74dda52b8..b2b46959f 100644 --- a/libs/gib/gib_thread.c +++ b/libs/gib/gib_thread.c @@ -44,6 +44,7 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/gib_parse.h" #include "QF/gib_thread.h" #include "QF/gib_function.h" +#include "QF/gib_buffer.h" #include "QF/dstring.h" #include "QF/hash.h" @@ -113,7 +114,7 @@ GIB_Thread_Execute (void) for (cur = gib_threads; cur->next; cur = cur->next); for (; cur; cur = tmp) { tmp = cur->prev; - if (!cur->cbuf->buf->str[0] && !cur->cbuf->down) { + if (!cur->cbuf->down) { GIB_Thread_Remove (cur); GIB_Thread_Delete (cur); } else @@ -174,7 +175,7 @@ GIB_Event_Callback (gib_event_t *event, unsigned int argc, ...) va_start (ap, argc); - Cbuf_ArgsAdd (args, f->name->str); + Cbuf_ArgsAdd (args, f->name); for (i = 0; i < argc; i++) Cbuf_ArgsAdd (args, va_arg (ap, const char *)); diff --git a/libs/gib/gib_tree.c b/libs/gib/gib_tree.c new file mode 100644 index 000000000..7575b5cf7 --- /dev/null +++ b/libs/gib/gib_tree.c @@ -0,0 +1,88 @@ +/* + gib_tree.c + + GIB tree handling functions + + Copyright (C) 2003 Brian Koropoff + + Author: Brian Koropoff + Date: #DATE# + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static __attribute__ ((unused)) const char rcsid[] = + "$Id$"; + +#include +#include + +#include "QF/qtypes.h" +#include "QF/gib_tree.h" + +gib_tree_t * +GIB_Tree_New (unsigned int flags) +{ + gib_tree_t *new = calloc (1, sizeof (gib_tree_t)); + new->flags = flags; + return new; +} + +static void +GIB_Tree_Free (gib_tree_t *tree) +{ + if (tree->str) + free((void *) tree->str); + if (tree->parent) + tree->parent->children = 0; + free(tree); +} + +void +GIB_Tree_Free_Recursive (gib_tree_t *tree, qboolean force) +{ + gib_tree_t *n; + + for (; tree; tree = n) { + n = tree->next; + // Leave perm nodes and their children alone + // (creating a temp child of a perm node is illegal) + if (tree->flags & TREE_PERM && !force) + return; + /* Free the children (but not on virtuals, and don't free nested functions EVER) */ + if (tree->children && !(tree->children->flags & TREE_FUNC)) + GIB_Tree_Free_Recursive (tree->children, force); + GIB_Tree_Free (tree); + } +} + +void +GIB_Tree_Add_Flag_Recursive (gib_tree_t *tree, unsigned int flag) +{ + for (; tree; tree = tree->next) { + tree->flags |= flag; + if (tree->children) + GIB_Tree_Add_Flag_Recursive (tree->children, flag); + } +} diff --git a/libs/gib/gib_vars.c b/libs/gib/gib_vars.c index db294888e..ef61e9258 100644 --- a/libs/gib/gib_vars.c +++ b/libs/gib/gib_vars.c @@ -42,179 +42,121 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/hash.h" #include "QF/gib_vars.h" #include "QF/gib_buffer.h" +#include "QF/gib_parse.h" hashtab_t *gib_globals = 0; +hashtab_t *gib_domains = 0; static gib_var_t * -GIB_Var_New (void) +GIB_Var_New (const char *key) { gib_var_t *new = calloc (1, sizeof (gib_var_t)); - new->key = dstring_newstr(); - new->value = dstring_newstr(); - + new->array = calloc (1, sizeof (dstring_t *)); + new->key = strdup (key); return new; } -const char * +static const char * GIB_Var_Get_Key (void *ele, void *ptr) { - return ((gib_var_t *)ele)->key->str; -} - -void -GIB_Var_Free (void *ele, void *ptr) -{ - gib_var_t *l = (gib_var_t *)ele; - dstring_delete (l->key); - dstring_delete (l->value); - if (l->subvars) - Hash_DelTable (l->subvars); -} - -static gib_var_t * -GIB_Var_Get_R (hashtab_t *vars, char *name) -{ - char *p; - gib_var_t *l; - - if (!vars) - return 0; - if ((p = strchr (name, '.'))) { - *p = 0; - l = Hash_Find (vars, name); - *p = '.'; - if (!l || !l->subvars) - return 0; - return GIB_Var_Get_R (l->subvars, p+1); - } else - return Hash_Find (vars, name); + return ((gib_var_t *)ele)->key; } static void -GIB_Var_Set_R (hashtab_t *vars, char *name, const char *value) +GIB_Var_Free (void *ele, void *ptr) { - char *p; - gib_var_t *l; + unsigned int i; + gib_var_t *l = (gib_var_t *)ele; + for (i = 0; i < l->size; i++) + if (l->array[i]) + dstring_delete (l->array[i]); + free((void *)l->key); + free(l); +} + +gib_var_t * +GIB_Var_Get (hashtab_t *first, hashtab_t *second, const char *key) +{ + gib_var_t *var; + if (first && (var = Hash_Find (first, key))) + return var; + else if (second && (var = Hash_Find (second, key))) + return var; + else + return 0; +} + +/* Modifies key but restores it before returning */ +gib_var_t * +GIB_Var_Get_Complex (hashtab_t **first, hashtab_t **second, char *key, unsigned int *ind, qboolean create) +{ + unsigned int i, index; + qboolean fix = false; + gib_var_t *var; - if ((p = strchr (name, '.'))) { - *p = 0; - if (!(l = Hash_Find (vars, name))) { - l = GIB_Var_New (); - dstring_appendstr (l->key, name); - Hash_Add (vars, l); - } - *p = '.'; - if (!l->subvars) - l->subvars = Hash_NewTable (256, GIB_Var_Get_Key, GIB_Var_Free, 0); - GIB_Var_Set_R (l->subvars, p+1, value); - } else { - if ((l = Hash_Find (vars, name))) - dstring_clearstr (l->value); - else { - l = GIB_Var_New (); - dstring_appendstr (l->key, name); - Hash_Add (vars, l); - } - dstring_appendstr (l->value, value); + i = strlen(key); + index = 0; + if (i && key[i-1] == ']') + for (i--; i; i--) + if (key[i] == '[') { + index = atoi (key+i+1); + key[i] = 0; + fix = true; + break; + } + if (!(var = GIB_Var_Get (*first, *second, key))) { + if (create) { + var = GIB_Var_New (key); + if (!*first) + *first = Hash_NewTable (256, GIB_Var_Get_Key, GIB_Var_Free, 0); + Hash_Add (*first, var); + } else return 0; } -} - -void -GIB_Var_Set_Local (cbuf_t *cbuf, const char *key, const char *value) -{ - char *k = strdup (key); - if (!GIB_DATA(cbuf)->locals) { - GIB_DATA(cbuf)->locals = Hash_NewTable (256, GIB_Var_Get_Key, GIB_Var_Free, 0); - if (GIB_DATA(cbuf)->type != GIB_BUFFER_NORMAL) - GIB_DATA(cbuf->up)->locals = GIB_DATA(cbuf)->locals; + if (fix) + key[i] = '['; + if (index >= var->size) { + if (create) { + var->array = realloc (var->array, (index+1) * sizeof (dstring_t *)); + memset (var->array+var->size, 0, (index+1 - var->size) * sizeof (dstring_t *)); + var->size = index+1; + } else return 0; } - GIB_Var_Set_R (GIB_DATA(cbuf)->locals, k, value); - free(k); + if (!var->array[index]) + var->array[index] = dstring_newstr (); + *ind = index; + return var; } -void -GIB_Var_Set_Global (const char *key, const char *value) +static const char * +GIB_Domain_Get_Key (void *ele, void *ptr) { - char *k = strdup (key); - GIB_Var_Set_R (gib_globals, k, value); - free (k); + return ((gib_domain_t *)ele)->name; } -const char * -GIB_Var_Get_Local (cbuf_t *cbuf, const char *key) +static void +GIB_Domain_Free (void *ele, void *ptr) { - gib_var_t *l; - char *k; - if (!GIB_DATA(cbuf)->locals) - return 0; - k = strdup(key); - l = GIB_Var_Get_R (GIB_DATA(cbuf)->locals, k); - free(k); - if (l) - return l->value->str; - else - return 0; + gib_domain_t *l = (gib_domain_t *)ele; + Hash_DelTable (l->vars); + free ((void *)l->name); + free (l); } -const char * -GIB_Var_Get_Global (const char *key) +hashtab_t * +GIB_Domain_Get (const char *name) { - gib_var_t *l; - char *k = strdup (key); - l = GIB_Var_Get_R (gib_globals, k); - free (k); - if (l) - return l->value->str; - else - return 0; -} - -const char * -GIB_Var_Get (cbuf_t *cbuf, char *key) -{ - const char *v; - - if ((v = GIB_Var_Get_Local (cbuf, key)) || (v = GIB_Var_Get_Global (key))) - return v; - else - return 0; -} - -void -GIB_Var_Set (cbuf_t *cbuf, char *key, const char *value) -{ - int glob = 0; - char *c = 0; - if ((c = strchr (key, '.'))) // Only check stem - *c = 0; - glob = (!GIB_Var_Get_Local (cbuf, key) && GIB_Var_Get_Global (key)); - if (c) - *c = '.'; - if (glob) - GIB_Var_Set_Global (key, value); // Set the global - else - GIB_Var_Set_Local (cbuf, key, value); // Set the local -} - -void -GIB_Var_Free_Global (const char *key) -{ - char *p, *k; - gib_var_t *root; - void *del; - k = strdup (key); - if ((p = strrchr (k, '.'))) { - *p = 0; - if ((root = GIB_Var_Get_R (gib_globals, k))) { - del = Hash_Del (root->subvars, p+1); - if (del) - GIB_Var_Free (del, 0); - } - } else { - del = Hash_Del (gib_globals, k); - if (del) - GIB_Var_Free (del, 0); + gib_domain_t *d = Hash_Find (gib_domains, name); + if (!d) { + d = calloc (1, sizeof (gib_domain_t)); + d->name = strdup(name); + d->vars = Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0); } - free (k); + return d->vars; +} + +void +GIB_Var_Init (void) +{ + gib_globals = Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0); + gib_domains = Hash_NewTable (1024, GIB_Domain_Get_Key, GIB_Domain_Free, 0); } - diff --git a/libs/util/cbuf.c b/libs/util/cbuf.c index c7d12f212..f812d5b08 100644 --- a/libs/util/cbuf.c +++ b/libs/util/cbuf.c @@ -81,6 +81,7 @@ Cbuf_ArgsAdd (cbuf_args_t *args, const char *arg) args->argv = realloc (args->argv, args->argv_size * sizeof (dstring_t *)); args->args = realloc (args->args, args->argv_size * sizeof (char *)); + args->argm = realloc (args->argm, args->argv_size * sizeof (void *)); for (i = args->argv_size - 4; i < args->argv_size; i++) { args->argv[i] = dstring_newstr (); args->args[i] = 0; @@ -96,8 +97,6 @@ Cbuf_New (cbuf_interpreter_t *interp) { cbuf_t *cbuf = calloc (1, sizeof (cbuf_t)); - cbuf->buf = dstring_newstr (); - cbuf->line = dstring_newstr (); cbuf->args = Cbuf_ArgsNew (); cbuf->interpreter = interp; if (interp->construct) @@ -110,8 +109,6 @@ Cbuf_Delete (cbuf_t *cbuf) { if (!cbuf) return; - dstring_delete (cbuf->buf); - dstring_delete (cbuf->line); Cbuf_ArgsDelete (cbuf->args); if (cbuf->interpreter->destruct) cbuf->interpreter->destruct (cbuf); @@ -129,17 +126,26 @@ Cbuf_DeleteStack (cbuf_t *stack) } } +void +Cbuf_PushStack (cbuf_t *new) +{ + if (cbuf_active->down) + Cbuf_DeleteStack (cbuf_active->down); + cbuf_active->down = new; + new->up = cbuf_active; + cbuf_active->state = CBUF_STATE_STACK; +} + void Cbuf_AddText (cbuf_t *cbuf, const char *text) { - dstring_appendstr (cbuf->buf, text); + cbuf->interpreter->add (cbuf, text); } void Cbuf_InsertText (cbuf_t *cbuf, const char *text) { - dstring_insertstr (cbuf->buf, 0, "\n"); - dstring_insertstr (cbuf->buf, 0, text); + cbuf->interpreter->insert (cbuf, text); } void @@ -147,19 +153,7 @@ Cbuf_Execute (cbuf_t *cbuf) { cbuf_active = cbuf; cbuf->state = CBUF_STATE_NORMAL; - while (cbuf->buf->str[0] || cbuf->line->str[0]) { - cbuf->interpreter->extract_line (cbuf); - if (cbuf->state) - break; - cbuf->interpreter->parse_line (cbuf); - if (cbuf->state) // Merging extract and parse - break; // will get rid of extra checks - if (!cbuf->args->argc) - continue; - cbuf->interpreter->execute_line (cbuf); - if (cbuf->state) - break; - } + cbuf->interpreter->execute (cbuf); } void @@ -193,8 +187,6 @@ Cbuf_Execute_Stack (cbuf_t *cbuf) } return; ERROR: - dstring_clearstr (cbuf->buf); - dstring_clearstr (cbuf->line); if (cbuf->down) { Cbuf_DeleteStack (cbuf->down); cbuf->down = 0; @@ -206,44 +198,27 @@ ERROR: void Cbuf_Execute_Sets (cbuf_t *cbuf) { - cbuf_args_t *args = cbuf->args; - - cbuf_active = cbuf; - while (cbuf->buf->str[0] || cbuf->line->str[0]) { - cbuf->interpreter->extract_line (cbuf); - cbuf->interpreter->parse_line (cbuf); - if (!args->argc) - continue; - if (strequal (args->argv[0]->str, "set") - || strequal (args->argv[0]->str, "setrom")) - Cmd_Command (args); - } + cbuf->interpreter->execute_sets (cbuf); } void Cbuf_Error (const char *class, const char *fmt, ...) { dstring_t *message = dstring_newstr(); - char *n; va_list args; va_start (args, fmt); dvsprintf (message, fmt, args); va_end (args); - if ((n = strchr (cbuf_active->line->str, '\n'))) - *n = 0; Sys_Printf ( "-----------------------------------\n" "|Error in command buffer execution|\n" "-----------------------------------\n" "Type: %s\n\n" - "%s\n\n" - "Near/on line: %s\n", + "%s\n\n", class, - message->str, - cbuf_active->line->str + message->str ); cbuf_active->state = CBUF_STATE_ERROR; - dstring_clearstr (cbuf_active->buf); dstring_delete (message); } diff --git a/libs/util/idparse.c b/libs/util/idparse.c index bf5c411fa..0e7033217 100644 --- a/libs/util/idparse.c +++ b/libs/util/idparse.c @@ -36,12 +36,51 @@ static __attribute__ ((unused)) const char rcsid[] = "$Id$"; #include +#include +#include #include "QF/dstring.h" #include "QF/cbuf.h" #include "QF/cmd.h" #include "QF/idparse.h" +typedef struct idbuf_s { + dstring_t *buf, *line; +} idbuf_t; + +#define DATA(x) ((idbuf_t *)(x)->data) + +static void +COM_construct (cbuf_t *cbuf) +{ + idbuf_t *new = calloc (1, sizeof (idbuf_t)); + + new->buf = dstring_newstr(); + new->line = dstring_newstr(); + cbuf->data = new; +} + +static void +COM_destruct (cbuf_t *cbuf) +{ + dstring_delete(DATA(cbuf)->buf); + dstring_delete(DATA(cbuf)->line); + free(cbuf->data); +} + +static void +COM_add (cbuf_t *cbuf, const char *str) +{ + dstring_appendstr (DATA(cbuf)->buf, str); +} + +static void +COM_insert (cbuf_t *cbuf, const char *str) +{ + dstring_insertstr (DATA(cbuf)->buf, 0, "\n"); + dstring_insertstr (DATA(cbuf)->buf, 0, str); +} + static dstring_t *_com_token; const char *com_token; @@ -124,12 +163,14 @@ COM_TokenizeString (const char *str, cbuf_args_t *args) static void COM_extract_line (cbuf_t *cbuf) { - int i; - int len = cbuf->buf->size - 1; - char *text = cbuf->buf->str; - int quotes = 0; + int i; + dstring_t *buf = DATA(cbuf)->buf; + dstring_t *line = DATA(cbuf)->line; + int len = buf->size - 1; + char *text = buf->str; + int quotes = 0; - dstring_clearstr (cbuf->line); + dstring_clearstr (line); for (i = 0; i < len; i++) { if (text[i] == '"') quotes++; @@ -142,7 +183,7 @@ COM_extract_line (cbuf_t *cbuf) && (text[j] != '\r' || (j < len - 1 && text[j + 1] != '\n'))) j++; - dstring_snip (cbuf->buf, i, j - i); + dstring_snip (buf, i, j - i); break; } } @@ -151,33 +192,49 @@ COM_extract_line (cbuf_t *cbuf) break; } if (i) - dstring_insert (cbuf->line, 0, text, i); + dstring_insert (line, 0, text, i); if (text[i]) { - dstring_snip (cbuf->buf, 0, i + 1); + dstring_snip (buf, 0, i + 1); } else { // We've hit the end of the buffer, just clear it - dstring_clearstr (cbuf->buf); + dstring_clearstr (buf); } } static void -COM_parse_line (cbuf_t *cbuf) +COM_execute (cbuf_t *cbuf) { - COM_TokenizeString (cbuf->line->str, cbuf->args); - dstring_clearstr (cbuf->line); + dstring_t *buf = DATA(cbuf)->buf; + dstring_t *line = DATA(cbuf)->line; + while (*buf->str) { + COM_extract_line (cbuf); + COM_TokenizeString (line->str, cbuf->args); + if (cbuf->args->argc) + Cmd_Command (cbuf->args); + } } static void -COM_execute_line (cbuf_t *cbuf) +COM_execute_sets (cbuf_t *cbuf) { - Cmd_Command (cbuf->args); + dstring_t *buf = DATA(cbuf)->buf; + dstring_t *line = DATA(cbuf)->buf; + while (*buf->str) { + COM_extract_line (cbuf); + COM_TokenizeString (line->str, cbuf->args); + if (cbuf->args->argc && + (!strcmp (cbuf->args->argv[0]->str, "set") || + !strcmp (cbuf->args->argv[0]->str, "setrom"))) + Cmd_Command (cbuf->args); + } } cbuf_interpreter_t id_interp = { - COM_extract_line, - COM_parse_line, - COM_execute_line, - NULL, - NULL, + COM_construct, + COM_destruct, + COM_add, + COM_insert, + COM_execute, + COM_execute_sets, NULL }; diff --git a/tools/carne/main.c b/tools/carne/main.c index a8cf76422..8019421d6 100644 --- a/tools/carne/main.c +++ b/tools/carne/main.c @@ -50,7 +50,11 @@ Carne_Execute_Script (const char *path, cbuf_args_t *args) if (f) { f[len] = 0; Qread (file, f, len); - Cbuf_InsertText (mbuf, f); + // If there is a hash-bang, strip it out + i = 0; + if (f[0] == '#') + for (; f[i] != '\n' && f[i+1]; i++); + Cbuf_AddText (mbuf, f+i); free (f); } Qclose (file); @@ -59,12 +63,7 @@ Carne_Execute_Script (const char *path, cbuf_args_t *args) return 1; } - // If there is a hash-bang, strip it out - if (mbuf->buf->str[0] == '#') { - for (i = 0; mbuf->buf->str[i] != '\n' && mbuf->buf->str[i+1]; i++); - dstring_snip (mbuf->buf, 0, i+1); - } - GIB_Parse_Strip_Comments (mbuf); + //GIB_Parse_Strip_Comments (mbuf); GIB_Function_Prepare_Args (mbuf, args); @@ -73,7 +72,7 @@ Carne_Execute_Script (const char *path, cbuf_args_t *args) GIB_Thread_Execute (); Cbuf_Execute_Stack (mbuf); // Check if there is anything left to do - if (carne_done || (!gib_threads && !mbuf->down && !mbuf->buf->str[0])) + if (carne_done || !GIB_DATA(mbuf)->program) break; } Cbuf_DeleteStack (mbuf); @@ -111,7 +110,7 @@ main (int argc, char **argv) Cmd_Init (); GIB_Init (false); // No sandbox - GIB_Builtin_Add ("exit", Carne_GIB_Exit_f, GIB_BUILTIN_NORMAL); + GIB_Builtin_Add ("exit", Carne_GIB_Exit_f); if (argc > 1) { // Prepare arguments