mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 06:10:56 +00:00
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:
parent
26c06c09a2
commit
59fbd48a81
25 changed files with 1554 additions and 1255 deletions
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
33
include/QF/gib_execute.h
Normal 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);
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
57
include/QF/gib_tree.h
Normal 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 */
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
264
libs/gib/gib_execute.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
88
libs/gib/gib_tree.c
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue