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.
This commit is contained in:
Brian Koropoff 2003-01-28 21:16:21 +00:00
parent 26c06c09a2
commit 59fbd48a81
25 changed files with 1554 additions and 1255 deletions

View file

@ -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 \

View file

@ -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);

View file

@ -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;

View file

@ -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);

33
include/QF/gib_execute.h Normal file
View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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);

57
include/QF/gib_tree.h Normal file
View file

@ -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 */

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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
};

View file

@ -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);
}

264
libs/gib/gib_execute.c Normal file
View file

@ -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 <string.h>
#include <stdlib.h>
#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;
}

View file

@ -36,6 +36,7 @@ static __attribute__ ((unused)) const char rcsid[] =
"$Id$";
#include <stdlib.h>
#include <string.h>
#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);
}

View file

@ -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

View file

@ -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);
}

View file

@ -39,282 +39,181 @@ static __attribute__ ((unused)) const char rcsid[] =
#include <ctype.h>
#include <stdlib.h>
#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;
}

View file

@ -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 *));

88
libs/gib/gib_tree.c Normal file
View file

@ -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 <stdlib.h>
#include <string.h>
#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);
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -36,12 +36,51 @@ static __attribute__ ((unused)) const char rcsid[] =
"$Id$";
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#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
};

View file

@ -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