mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 14:20:59 +00:00
The ultimate GIB bugfix commit, including reworked semantic processing,
a fix for arguments passed to a GIB function run via rcon, and various other fixes.
This commit is contained in:
parent
645b7ca3d6
commit
d8c0f50c11
12 changed files with 200 additions and 340 deletions
|
@ -4,10 +4,10 @@ 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_execute.h gib_function.h gib_init.h gib_parse.h \
|
||||
gib_process.h gib_regex.h gib_thread.h gib_tree.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 pr_debug.h pr_obj.h progs.h qargs.h qdefs.h qendian.h \
|
||||
qfplist.h qtypes.h quakefs.h quakeio.h render.h riff.h screen.h sizebuf.h \
|
||||
skin.h sound.h spritegn.h sys.h teamplay.h texture.h tga.h uint32.h va.h \
|
||||
ver_check.h vid.h wad.h zone.h
|
||||
gib_process.h gib_regex.h gib_semantics.h gib_thread.h gib_tree.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 pr_debug.h pr_obj.h progs.h qargs.h \
|
||||
qdefs.h qendian.h qfplist.h qtypes.h quakefs.h quakeio.h render.h riff.h \
|
||||
screen.h sizebuf.h skin.h sound.h spritegn.h sys.h teamplay.h texture.h \
|
||||
tga.h uint32.h va.h ver_check.h vid.h wad.h zone.h
|
||||
|
|
|
@ -48,7 +48,9 @@ typedef struct gib_function_s {
|
|||
|
||||
void GIB_Function_Define (const char *name, const char *text, gib_tree_t *program, gib_script_t *script, hashtab_t *globals);
|
||||
gib_function_t *GIB_Function_Find (const char *name);
|
||||
void GIB_Function_Prepare_Args (cbuf_t *cbuf, dstring_t **args, unsigned int argc);
|
||||
void GIB_Function_Execute (cbuf_t *cbuf, gib_function_t *func, dstring_t **args, unsigned int argc);
|
||||
void GIB_Function_Prepare_Args (cbuf_t *cbuf, const char **args, unsigned int argc);
|
||||
void GIB_Function_Prepare_Args_D (cbuf_t *cbuf, dstring_t **args, unsigned int argc);
|
||||
void GIB_Function_Execute (cbuf_t *cbuf, gib_function_t *func, const char **args, unsigned int argc);
|
||||
void GIB_Function_Execute_D (cbuf_t *cbuf, gib_function_t *func, dstring_t **args, unsigned int argc);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,8 +40,9 @@ char GIB_Parse_Match_Paren (const char *str, unsigned int *i);
|
|||
char GIB_Parse_Match_Var (const char *str, unsigned int *i);
|
||||
|
||||
gib_tree_t *GIB_Parse_Lines (const char *program, unsigned int pofs);
|
||||
gib_tree_t *GIB_Parse_Embedded (const char *program, unsigned int pofs, gib_tree_t **embedded);
|
||||
gib_tree_t *GIB_Parse_Embedded (gib_tree_t *token);
|
||||
|
||||
extern qboolean gib_parse_error;
|
||||
void GIB_Parse_Error (const char *msg, unsigned int pos);
|
||||
const char *GIB_Parse_ErrorMsg (void);
|
||||
unsigned int GIB_Parse_ErrorPos (void);
|
||||
|
|
|
@ -54,10 +54,10 @@ typedef struct gib_tree_s {
|
|||
TREE_T_COND, // Conditional jump
|
||||
TREE_T_ASSIGN, // Assignment
|
||||
TREE_T_JUMP, // Jump
|
||||
TREE_T_JUMPPLUS, // Jump, go to next instruction
|
||||
TREE_T_ARG, // Argument (not a line)
|
||||
TREE_T_FORNEXT, // Fetch next arg in for loop
|
||||
TREE_T_META // Info node
|
||||
TREE_T_META, // Info node
|
||||
TREE_T_NOP // Do nothing (label, etc)
|
||||
} type;
|
||||
struct gib_tree_s *children, *next, *jump;
|
||||
} gib_tree_t;
|
||||
|
@ -66,5 +66,5 @@ gib_tree_t *GIB_Tree_New (enum gib_tree_type_e type);
|
|||
void GIB_Tree_Free_Recursive (gib_tree_t *tree);
|
||||
void GIB_Tree_Ref (gib_tree_t **tp);
|
||||
void GIB_Tree_Unref (gib_tree_t **tp);
|
||||
|
||||
|
||||
#endif /* __GIB_TREE_H */
|
||||
|
|
|
@ -7,4 +7,5 @@ lib_LTLIBRARIES= libQFgib.la
|
|||
libQFgib_la_LDFLAGS= -version-info 1:0:0
|
||||
libQFgib_la_SOURCES= \
|
||||
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
|
||||
gib_regex.c gib_thread.c gib_vars.c gib_init.c gib_tree.c gib_semantics.c ops.c \
|
||||
exp.c regex.c
|
||||
|
|
|
@ -208,6 +208,9 @@ GIB_Local_f (void)
|
|||
if (GIB_Argc () >= 3)
|
||||
GIB_Var_Assign (var, index, cbuf_active->args->argv + 3,
|
||||
GIB_Argc () - 3);
|
||||
if (GIB_CanReturn ())
|
||||
for (i = 3; i < GIB_Argc(); i++)
|
||||
GIB_Return (GIB_Argv(i));
|
||||
} else for (i = 1; i < GIB_Argc(); i++)
|
||||
var = GIB_Var_Get_Complex (&GIB_DATA (cbuf_active)->locals, &zero,
|
||||
GIB_Argv (i), &index, true);
|
||||
|
@ -229,6 +232,9 @@ GIB_Global_f (void)
|
|||
if (GIB_Argc () >= 3)
|
||||
GIB_Var_Assign (var, index, cbuf_active->args->argv + 3,
|
||||
GIB_Argc () - 3);
|
||||
if (GIB_CanReturn ())
|
||||
for (i = 3; i < GIB_Argc(); i++)
|
||||
GIB_Return (GIB_Argv(i));
|
||||
} else for (i = 1; i < GIB_Argc(); i++)
|
||||
var = GIB_Var_Get_Complex (&GIB_DATA (cbuf_active)->globals, &zero,
|
||||
GIB_Argv (i), &index, true);
|
||||
|
@ -321,19 +327,21 @@ static void
|
|||
GIB_Runexported_f (void)
|
||||
{
|
||||
gib_function_t *f;
|
||||
const char **args;
|
||||
|
||||
if (!(f = GIB_Function_Find (Cmd_Argv (0))))
|
||||
Sys_Printf ("Error: No function found for exported command \"%s\".\n"
|
||||
"This is most likely a bug, please report it to"
|
||||
"The QuakeForge developers.", Cmd_Argv (0));
|
||||
else {
|
||||
cbuf_t *sub = Cbuf_New (&gib_interp);
|
||||
cbuf_t *sub = Cbuf_PushStack (&gib_interp);
|
||||
unsigned int i;
|
||||
|
||||
GIB_Function_Execute (sub, f, cbuf_active->args->argv,
|
||||
cbuf_active->args->argc);
|
||||
cbuf_active->down = sub;
|
||||
sub->up = cbuf_active;
|
||||
cbuf_active->state = CBUF_STATE_STACK;
|
||||
args = malloc (sizeof (char *) * Cmd_Argc());
|
||||
for (i = 0; i < Cmd_Argc(); i++)
|
||||
args[i] = Cmd_Argv(i);
|
||||
GIB_Function_Execute (sub, f, args, Cmd_Argc());
|
||||
free (args);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,7 +594,7 @@ GIB_Thread_Create_f (void)
|
|||
else {
|
||||
gib_thread_t *thread = GIB_Thread_New ();
|
||||
|
||||
GIB_Function_Execute (thread->cbuf, f, cbuf_active->args->argv + 1,
|
||||
GIB_Function_Execute_D (thread->cbuf, f, cbuf_active->args->argv + 1,
|
||||
cbuf_active->args->argc - 1);
|
||||
GIB_Thread_Add (thread);
|
||||
if (GIB_CanReturn ())
|
||||
|
|
|
@ -58,8 +58,7 @@ GIB_Execute_Generate_Composite (struct cbuf_s *cbuf)
|
|||
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);
|
||||
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, " ");
|
||||
}
|
||||
|
@ -71,8 +70,7 @@ GIB_Execute_Generate_Composite (struct cbuf_s *cbuf)
|
|||
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;
|
||||
args->args[i] += (unsigned long int) GIB_DATA (cbuf)->arg_composite->str;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -101,9 +99,11 @@ GIB_Execute_Split_Var (cbuf_t * cbuf)
|
|||
break;
|
||||
}
|
||||
cbuf->args->argc--;
|
||||
if (!(var = GIB_Var_Get_Complex (&GIB_DATA (cbuf)->locals,
|
||||
&GIB_DATA (cbuf)->globals, str, &i, false)))
|
||||
return;
|
||||
if (!(var = GIB_Var_Get_Complex (
|
||||
&GIB_DATA (cbuf)->locals,
|
||||
&GIB_DATA (cbuf)->globals,
|
||||
str, &i, false)))
|
||||
return;
|
||||
if (end < 0)
|
||||
end += var->size;
|
||||
else if (end > var->size)
|
||||
|
@ -123,17 +123,19 @@ GIB_Execute_Split_Var (cbuf_t * cbuf)
|
|||
}
|
||||
} else {
|
||||
gib_var_t **vlist, **v;
|
||||
|
||||
|
||||
cbuf->args->argc--;
|
||||
if (!(var = GIB_Var_Get_Complex (&GIB_DATA (cbuf)->locals,
|
||||
&GIB_DATA (cbuf)->globals, str, &i, false)))
|
||||
return;
|
||||
if (!(var = GIB_Var_Get_Complex (
|
||||
&GIB_DATA (cbuf)->locals,
|
||||
&GIB_DATA (cbuf)->globals,
|
||||
str, &i, false)))
|
||||
return;
|
||||
if (!var->array[i].leaves)
|
||||
return;
|
||||
vlist = (gib_var_t **) Hash_GetList (var->array[i].leaves);
|
||||
for (v = vlist; *v; v++)
|
||||
Cbuf_ArgsAdd (cbuf->args, (*v)->key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -175,17 +177,17 @@ 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;
|
||||
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 -1;
|
||||
}
|
||||
array->size--;
|
||||
var =
|
||||
GIB_Var_Get_Complex (&GIB_DATA (cbuf)->locals,
|
||||
&GIB_DATA (cbuf)->globals, array->dstrs[0]->str,
|
||||
&index, true);
|
||||
var = GIB_Var_Get_Complex (
|
||||
&GIB_DATA (cbuf)->locals,
|
||||
&GIB_DATA (cbuf)->globals, array->dstrs[0]->str,
|
||||
&index, true
|
||||
);
|
||||
dstring_clearstr (var->array[index].value);
|
||||
dstring_appendstr (var->array[index].value, array->dstrs[array->size]->str);
|
||||
return 0;
|
||||
|
@ -199,40 +201,53 @@ GIB_Execute (cbuf_t * cbuf)
|
|||
gib_function_t *f;
|
||||
unsigned int index;
|
||||
gib_var_t *var;
|
||||
unsigned int i;
|
||||
|
||||
if (!g->program)
|
||||
return;
|
||||
g->ip = g->ip ? g->ip->next : g->program;
|
||||
while (g->ip) {
|
||||
switch (g->ip->type) {
|
||||
case TREE_T_JUMP:
|
||||
case TREE_T_NOP: // Move to next instruction
|
||||
g->ip = g->ip->next;
|
||||
continue;
|
||||
case TREE_T_JUMP: // Absolute jump
|
||||
g->ip = g->ip->jump;
|
||||
continue;
|
||||
case TREE_T_JUMPPLUS:
|
||||
g->ip = g->ip->jump->next;
|
||||
continue;
|
||||
case TREE_T_FORNEXT:
|
||||
case TREE_T_FORNEXT: // Fetch next value in a for loop
|
||||
if (GIB_Execute_For_Next (cbuf))
|
||||
g->ip = g->ip->jump->next;
|
||||
else
|
||||
g->ip = g->ip->next;
|
||||
continue;
|
||||
case TREE_T_COND:
|
||||
case TREE_T_COND: // Conditional jump, move to next instruction
|
||||
if (GIB_Execute_Prepare_Line (cbuf, g->ip))
|
||||
return;
|
||||
if (g->ip->flags & TREE_L_NOT ? atof (cbuf->args->argv[1]->str) : !atof (cbuf->args->argv[1]->str))
|
||||
g->ip = g->ip->jump->next;
|
||||
if (g->ip->flags & TREE_L_NOT ?
|
||||
atof (cbuf->args->argv[1]->str) :
|
||||
!atof (cbuf->args->argv[1]->str))
|
||||
g->ip = g->ip->jump->next;
|
||||
else
|
||||
g->ip = g->ip->next;
|
||||
continue;
|
||||
case TREE_T_ASSIGN:
|
||||
case TREE_T_ASSIGN: // Assignment
|
||||
if (GIB_Execute_Prepare_Line (cbuf, g->ip))
|
||||
return;
|
||||
var = GIB_Var_Get_Complex (&g->locals, &g->globals, cbuf->args->argv[0]->str, &index, true);
|
||||
var = GIB_Var_Get_Complex (
|
||||
&g->locals,
|
||||
&g->globals,
|
||||
cbuf->args->argv[0]->str, &index, true);
|
||||
GIB_Var_Assign (var, index, cbuf->args->argv + 2, cbuf->args->argc - 2);
|
||||
if (g->ip->flags & TREE_L_EMBED) {
|
||||
GIB_Buffer_Push_Sstack (cbuf);
|
||||
g->waitret = true;
|
||||
for (i = 2; i < cbuf->args->argc; i++)
|
||||
GIB_Return (cbuf->args->argv[i]->str);
|
||||
} else
|
||||
g->waitret = false;
|
||||
g->ip = g->ip->next;
|
||||
continue;
|
||||
case TREE_T_CMD:
|
||||
case TREE_T_CMD: // Normal command
|
||||
if (GIB_Execute_Prepare_Line (cbuf, g->ip))
|
||||
return;
|
||||
if (g->ip->flags & TREE_L_EMBED) {
|
||||
|
@ -246,13 +261,17 @@ GIB_Execute (cbuf_t * cbuf)
|
|||
b->func ();
|
||||
else if ((f = GIB_Function_Find (cbuf->args->argv[0]->str))) {
|
||||
cbuf_t *new = Cbuf_PushStack (&gib_interp);
|
||||
GIB_Function_Execute (new, f, cbuf->args->argv, cbuf->args->argc);
|
||||
GIB_Function_Execute_D (
|
||||
new, f,
|
||||
cbuf->args->argv, cbuf->args->argc
|
||||
);
|
||||
} else {
|
||||
GIB_Execute_Generate_Composite (cbuf);
|
||||
if (Cmd_Command (cbuf->args))
|
||||
GIB_Error (
|
||||
"command",
|
||||
"No builtin, function, or console command named '%s' was found.",
|
||||
"No builtin, function, or console command "
|
||||
"named '%s' was found.",
|
||||
cbuf->args->argv[0]->str
|
||||
);
|
||||
}
|
||||
|
@ -261,8 +280,11 @@ GIB_Execute (cbuf_t * cbuf)
|
|||
}
|
||||
g->ip = g->ip->next;
|
||||
continue;
|
||||
default:
|
||||
GIB_Error ("QUAKEFORGE-BUG-PLEASE-REPORT", "Unknown instruction type; tastes like chicken.");
|
||||
default: // We should never get here
|
||||
GIB_Error (
|
||||
"QUAKEFORGE-BUG-PLEASE-REPORT",
|
||||
"Unknown instruction type; tastes like chicken."
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,7 +149,29 @@ GIB_Function_Find (const char *name)
|
|||
}
|
||||
|
||||
void
|
||||
GIB_Function_Prepare_Args (cbuf_t * cbuf, dstring_t ** args, unsigned int argc)
|
||||
GIB_Function_Prepare_Args (cbuf_t * cbuf, const char **args, unsigned int argc)
|
||||
{
|
||||
static hashtab_t *zero = 0;
|
||||
unsigned int i;
|
||||
gib_var_t *var;
|
||||
static char argss[] = "args";
|
||||
|
||||
var =
|
||||
GIB_Var_Get_Complex (&GIB_DATA (cbuf)->locals, &zero, argss, &i, true);
|
||||
var->array = realloc (var->array, sizeof (struct gib_varray_s) * argc);
|
||||
memset (var->array + 1, 0, (argc - 1) * sizeof (struct gib_varray_s));
|
||||
var->size = argc;
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (var->array[i].value)
|
||||
dstring_clearstr (var->array[i].value);
|
||||
else
|
||||
var->array[i].value = dstring_newstr ();
|
||||
dstring_appendstr (var->array[i].value, args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GIB_Function_Prepare_Args_D (cbuf_t * cbuf, dstring_t **args, unsigned int argc)
|
||||
{
|
||||
static hashtab_t *zero = 0;
|
||||
unsigned int i;
|
||||
|
@ -178,7 +200,7 @@ GIB_Function_Prepare_Args (cbuf_t * cbuf, dstring_t ** args, unsigned int argc)
|
|||
*/
|
||||
|
||||
void
|
||||
GIB_Function_Execute (cbuf_t * cbuf, gib_function_t * func, dstring_t ** args,
|
||||
GIB_Function_Execute (cbuf_t * cbuf, gib_function_t * func, const char ** args,
|
||||
unsigned int argc)
|
||||
{
|
||||
GIB_Tree_Ref (&func->program);
|
||||
|
@ -189,3 +211,16 @@ GIB_Function_Execute (cbuf_t * cbuf, gib_function_t * func, dstring_t ** args,
|
|||
GIB_DATA (cbuf)->globals = func->globals;
|
||||
GIB_Function_Prepare_Args (cbuf, args, argc);
|
||||
}
|
||||
|
||||
void
|
||||
GIB_Function_Execute_D (cbuf_t * cbuf, gib_function_t * func, dstring_t ** args,
|
||||
unsigned int argc)
|
||||
{
|
||||
GIB_Tree_Ref (&func->program);
|
||||
if (func->script)
|
||||
func->script->refs++;
|
||||
GIB_Buffer_Set_Program (cbuf, func->program);
|
||||
GIB_DATA (cbuf)->script = func->script;
|
||||
GIB_DATA (cbuf)->globals = func->globals;
|
||||
GIB_Function_Prepare_Args_D (cbuf, args, argc);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ const char rcsid[] = "$Id$";
|
|||
#include "QF/gib_function.h"
|
||||
#include "QF/gib_vars.h"
|
||||
#include "QF/gib_parse.h"
|
||||
#include "QF/gib_semantics.h"
|
||||
|
||||
|
||||
/*
|
||||
|
@ -78,7 +79,7 @@ GIB_Escaped (const char *str, int i)
|
|||
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.
|
||||
*/
|
||||
|
@ -195,7 +196,7 @@ qboolean gib_parse_error;
|
|||
unsigned int gib_parse_error_pos;
|
||||
const char *gib_parse_error_msg;
|
||||
|
||||
static void
|
||||
void
|
||||
GIB_Parse_Error (const char *msg, unsigned int pos)
|
||||
{
|
||||
gib_parse_error = true;
|
||||
|
@ -215,14 +216,12 @@ GIB_Parse_ErrorPos (void)
|
|||
return gib_parse_error_pos;
|
||||
}
|
||||
|
||||
// FIXME: Concatenation in stupid circumstances should generate errors
|
||||
|
||||
static gib_tree_t *
|
||||
GIB_Parse_Tokens (const char *program, unsigned int *i, unsigned int pofs, gib_tree_t ** embedded)
|
||||
GIB_Parse_Tokens (const char *program, unsigned int *i, unsigned int pofs)
|
||||
{
|
||||
char c = 0, delim, *str;
|
||||
unsigned int tstart, start;
|
||||
gib_tree_t *nodes = 0, *cur, *new, *embs = 0, *tmp;
|
||||
gib_tree_t *nodes = 0, *cur, *new;
|
||||
gib_tree_t **node = &nodes;
|
||||
enum {CAT_NORMAL = 0, CAT_DISALLOW, CAT_CONCAT} cat = CAT_DISALLOW;
|
||||
const char *catestr = "Comma found before first argument, nothing to concatenate to.";
|
||||
|
@ -314,25 +313,6 @@ GIB_Parse_Tokens (const char *program, unsigned int *i, unsigned int pofs, gib_t
|
|||
cur->children = new;
|
||||
// Check for embedded commands/variables
|
||||
} else if (cur->delim == ' ' || cur->delim == '(') {
|
||||
if (!
|
||||
(cur->children =
|
||||
GIB_Parse_Embedded (str, tstart + pofs, &new))) {
|
||||
// There could be no embedded elements, so check for a real
|
||||
// error
|
||||
if (gib_parse_error)
|
||||
goto ERROR;
|
||||
} else {
|
||||
// Link/set flags
|
||||
cur->flags |= TREE_A_EMBED;
|
||||
// Add any embedded commands to top of chain
|
||||
if (new) {
|
||||
for (tmp = new; tmp->next; tmp = tmp->next);
|
||||
tmp->next = embs;
|
||||
embs = new;
|
||||
}
|
||||
}
|
||||
// Check for array splitting
|
||||
// Concatenating this onto something else is non-sensical
|
||||
if (cur->delim == ' ' && (str[0] == '@' || str[0] == '%')) {
|
||||
if (cat == CAT_CONCAT) {
|
||||
GIB_Parse_Error ("Variable expansions may not be concatenated with other arguments.", start + pofs);
|
||||
|
@ -342,10 +322,9 @@ GIB_Parse_Tokens (const char *program, unsigned int *i, unsigned int pofs, gib_t
|
|||
cat = CAT_DISALLOW;
|
||||
cur->flags |= TREE_A_EXPAND;
|
||||
}
|
||||
// We can handle escape characters now
|
||||
// We can handle escape characters now
|
||||
} else if (cur->delim == '\"')
|
||||
GIB_Process_Escapes (str);
|
||||
|
||||
if (cat == CAT_CONCAT)
|
||||
cur->flags |= TREE_A_CONCAT;
|
||||
// Nothing left to parse?
|
||||
|
@ -357,217 +336,20 @@ GIB_Parse_Tokens (const char *program, unsigned int *i, unsigned int pofs, gib_t
|
|||
node = &cur->next;
|
||||
}
|
||||
DONE:
|
||||
*embedded = embs;
|
||||
return nodes;
|
||||
ERROR:
|
||||
if (c)
|
||||
GIB_Parse_Error (va ("Could not find match for '%c'.", c), *i + pofs);
|
||||
if (nodes)
|
||||
GIB_Tree_Unref (&nodes);
|
||||
if (embs)
|
||||
GIB_Tree_Unref (&embs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gib_tree_t *
|
||||
GIB_Parse_Semantic_Preprocess (gib_tree_t * line)
|
||||
{
|
||||
gib_tree_t *p, *start = line;
|
||||
|
||||
// If second token is concatenated, than the first can't possibly mean anything
|
||||
if (line->children->next && line->children->next->flags & TREE_A_CONCAT)
|
||||
return line;
|
||||
while (!strcmp (line->children->str, "if")
|
||||
|| !strcmp (line->children->str, "ifnot")) {
|
||||
// Sanity checking
|
||||
if (!line->children->next || !line->children->next->next) {
|
||||
GIB_Parse_Error ("Not enough arguments to 'if' statement.",
|
||||
line->start);
|
||||
return line;
|
||||
} else if (!line->children->next->next->children
|
||||
|| line->children->next->next->delim != '{') {
|
||||
GIB_Parse_Error
|
||||
("First program block in 'if' statement not enclosed in braces or invalid.",
|
||||
line->start);
|
||||
return line;
|
||||
} else if (line->flags & TREE_L_EMBED) {
|
||||
GIB_Parse_Error
|
||||
("'if' statements may not be used in embedded commands.",
|
||||
line->start);
|
||||
return line;
|
||||
}
|
||||
// Set as conditional
|
||||
line->type = TREE_T_COND;
|
||||
if (line->children->str[2])
|
||||
line->flags |= TREE_L_NOT;
|
||||
// Save our spot
|
||||
p = line;
|
||||
// Move subprogram inline
|
||||
line->next = line->children->next->next->children;
|
||||
line->children->next->next->children = 0;
|
||||
// Find end of subprogram
|
||||
while (line->next)
|
||||
line = line->next;
|
||||
// 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
|
||||
("'if' statement contains 'else' but no secondary program block or command.",
|
||||
line->start);
|
||||
return line;
|
||||
}
|
||||
// On 'true' first block must jump past this
|
||||
// We will figure out jump target later
|
||||
line->next = GIB_Tree_New (TREE_T_JUMPPLUS);
|
||||
line = line->next;
|
||||
// Jump to else block on 'false'
|
||||
p->jump = 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;
|
||||
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 (TREE_T_CMD);
|
||||
line->next->children = p->children->next->next->next->next;
|
||||
p->children->next->next->next->next = 0;
|
||||
line = line->next;
|
||||
}
|
||||
} else {
|
||||
// Jump past block on 'false'
|
||||
p->jump = line;
|
||||
break; // Don't touch if statements in the sub program
|
||||
}
|
||||
}
|
||||
// Now we know exit point from if-else if chain, set our jumps
|
||||
while (start) {
|
||||
if (start->type == TREE_T_JUMPPLUS && !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) {
|
||||
GIB_Parse_Error ("Not enough arguments to 'while' statement.",
|
||||
line->start);
|
||||
return line;
|
||||
} else if (!line->children->next->next->children
|
||||
|| line->children->next->next->delim != '{') {
|
||||
GIB_Parse_Error
|
||||
("Program block in 'while' statement not enclosed in braces or invalid.",
|
||||
line->start);
|
||||
return line;
|
||||
} else if (line->flags & TREE_L_EMBED) {
|
||||
GIB_Parse_Error
|
||||
("'while' statements may not be used in embedded commands.",
|
||||
line->start);
|
||||
return line;
|
||||
}
|
||||
// Set conditional flag
|
||||
line->type = TREE_T_COND;
|
||||
// Save our spot
|
||||
p = line;
|
||||
// Move subprogram inline
|
||||
line->next = line->children->next->next->children;
|
||||
line->children->next->next->children = 0;
|
||||
// Find end of subprogram
|
||||
for (; line->next; line = line->next)
|
||||
if (!line->jump && line->children) {
|
||||
if (!strcmp (line->children->str, "continue")) {
|
||||
line->type = TREE_T_JUMP;
|
||||
line->jump = p;
|
||||
} else if (!strcmp (line->children->str, "break"))
|
||||
line->type = TREE_T_JUMPPLUS;
|
||||
}
|
||||
line->next = GIB_Tree_New (TREE_T_JUMP);
|
||||
line->next->jump = p;
|
||||
line = line->next;
|
||||
// Mark jump point out of loop
|
||||
p->jump = line;
|
||||
// Set jumps out of loop for "break" commands;
|
||||
while (p) {
|
||||
if (p->type == TREE_T_JUMPPLUS && !p->jump)
|
||||
p->jump = line;
|
||||
p = p->next;
|
||||
}
|
||||
} 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 ("Malformed 'for' statement.", line->start);
|
||||
return line;
|
||||
} else if (line->flags & TREE_L_EMBED) {
|
||||
GIB_Parse_Error ("'for' statements may not be used in embedded commands.", line->start);
|
||||
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->delim != '{' || !tmp->children) {
|
||||
GIB_Parse_Error
|
||||
("Program block in 'for' statement not enclosed in braces or invalid.",
|
||||
line->start);
|
||||
return line;
|
||||
}
|
||||
// Add instruction to fetch next argument (this is the true loop start)
|
||||
line->next = GIB_Tree_New (TREE_T_FORNEXT);
|
||||
line = line->next;
|
||||
p = line;
|
||||
// Move subprogram inline
|
||||
line->next = tmp->children;
|
||||
tmp->children = 0;
|
||||
// Find end of subprogram
|
||||
for (; line->next; line = line->next)
|
||||
if (!line->jump && line->children) {
|
||||
if (!strcmp (line->children->str, "continue")) {
|
||||
line->type = TREE_T_JUMP;
|
||||
line->jump = p;
|
||||
} else if (!strcmp (line->children->str, "break"))
|
||||
line->type = TREE_T_JUMPPLUS;
|
||||
}
|
||||
line->next = GIB_Tree_New (TREE_T_JUMP);
|
||||
line->next->jump = p;
|
||||
line = line->next;
|
||||
// Mark jump point out of loop
|
||||
p->jump = line;
|
||||
// Mark jump point out of loop for break command
|
||||
while (p) {
|
||||
if (p->type == TREE_T_JUMPPLUS && !p->jump)
|
||||
p->jump = line;
|
||||
p = p->next;
|
||||
}
|
||||
} else if (
|
||||
line->children->next &&
|
||||
!(line->children->next->flags & TREE_A_CONCAT) &&
|
||||
line->children->next->delim == ' ' &&
|
||||
!strcmp (line->children->next->str, "=")
|
||||
) {
|
||||
if (line->flags & TREE_L_EMBED)
|
||||
GIB_Parse_Error ("Assignment may not be used as an embedded command.", line->start);
|
||||
line->type = TREE_T_ASSIGN;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
gib_tree_t *
|
||||
GIB_Parse_Lines (const char *program, unsigned int pofs)
|
||||
{
|
||||
unsigned int i = 0, lstart;
|
||||
gib_tree_t *lines = 0, *cur, *tokens, **line = &lines, *embs;
|
||||
gib_tree_t *lines = 0, *cur, *tokens, **line = &lines, *temp;
|
||||
char *str;
|
||||
|
||||
while (1) {
|
||||
|
@ -577,26 +359,26 @@ GIB_Parse_Lines (const char *program, unsigned int pofs)
|
|||
break;
|
||||
lstart = i;
|
||||
// If we parse something useful...
|
||||
if ((tokens = GIB_Parse_Tokens (program, &i, pofs, &embs))) {
|
||||
// Link it in
|
||||
cur = GIB_Tree_New (TREE_T_CMD);
|
||||
cur->delim = '\n';
|
||||
str = calloc (i - lstart + 1, sizeof (char));
|
||||
if ((tokens = GIB_Parse_Tokens (program, &i, pofs))) {
|
||||
str = calloc (i - lstart + 1, sizeof(char));
|
||||
memcpy (str, program + lstart, i - lstart);
|
||||
cur->str = str;
|
||||
cur->start = lstart + pofs;
|
||||
cur->end = i + pofs;
|
||||
cur->children = tokens;
|
||||
// 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;
|
||||
|
||||
// All settings for the line must be passed here
|
||||
cur = GIB_Semantic_Tokens_To_Lines (
|
||||
tokens, str, 0,
|
||||
lstart+pofs,
|
||||
i+pofs
|
||||
);
|
||||
if (gib_parse_error) {
|
||||
free (str);
|
||||
goto ERROR;
|
||||
}
|
||||
// Find last line
|
||||
for (temp = cur; temp->next; temp = temp->next);
|
||||
|
||||
// Link it to lines we already have
|
||||
*line = cur;
|
||||
line = &temp->next;
|
||||
}
|
||||
if (gib_parse_error)
|
||||
goto ERROR;
|
||||
|
@ -609,16 +391,16 @@ GIB_Parse_Lines (const char *program, unsigned int pofs)
|
|||
}
|
||||
|
||||
gib_tree_t *
|
||||
GIB_Parse_Embedded (const char *program, unsigned int pofs, gib_tree_t ** embedded)
|
||||
GIB_Parse_Embedded (gib_tree_t *token)
|
||||
{
|
||||
unsigned int i, n, t;
|
||||
unsigned int i, n, t, start, end;
|
||||
char c, d, *str;
|
||||
gib_tree_t *lines = 0, **line = &lines, *cur, *tokens, *emb, *tmp, **embfirst;
|
||||
unsigned int start, end;
|
||||
const char *program = token->str;
|
||||
gib_tree_t *lines = 0, *cur, *tokens, **metanext, *temp;
|
||||
|
||||
gib_parse_error = false;
|
||||
embfirst = embedded;
|
||||
*embedded = 0;
|
||||
|
||||
metanext = &token->children;
|
||||
|
||||
for (i = 0; program[i]; i++) {
|
||||
if (program[i] == '`' || (program[i] == '$' && program[i + 1] == '(')) {
|
||||
|
@ -634,45 +416,56 @@ GIB_Parse_Embedded (const char *program, unsigned int pofs, gib_tree_t ** embedd
|
|||
goto ERROR;
|
||||
}
|
||||
end = i + 1;
|
||||
|
||||
// Construct the actual line to be executed
|
||||
cur = GIB_Tree_New (TREE_T_CMD);
|
||||
cur->flags |= TREE_L_EMBED;
|
||||
cur->delim = '`';
|
||||
str = calloc (i - n + 1, sizeof (char));
|
||||
memcpy (str, program + n, i - n);
|
||||
cur->str = str;
|
||||
cur->start = start + pofs;
|
||||
cur->end = end + pofs;
|
||||
|
||||
// Reset error condition
|
||||
c = 0;
|
||||
// Location to begin parsing from
|
||||
t = 0;
|
||||
if (!(tokens = GIB_Parse_Tokens (cur->str, &t, start + pofs, &emb))) {
|
||||
GIB_Tree_Unref (&cur);
|
||||
|
||||
// Extract embedded command into a new string
|
||||
str = calloc (end - start + 1, sizeof(char));
|
||||
memcpy (str, program + n, i - n);
|
||||
|
||||
// Attempt to parse tokens from it
|
||||
tokens = GIB_Parse_Tokens (str, &t, start + token->start);
|
||||
if (!tokens) {
|
||||
free (str);
|
||||
goto ERROR;
|
||||
}
|
||||
cur->children = tokens;
|
||||
GIB_Parse_Semantic_Preprocess (cur)->next = *embedded;
|
||||
|
||||
// All settings for the line must be passed here
|
||||
cur = GIB_Semantic_Tokens_To_Lines (
|
||||
tokens, str, TREE_L_EMBED,
|
||||
start + token->start,
|
||||
end + token->start
|
||||
);
|
||||
if (gib_parse_error) {
|
||||
GIB_Tree_Unref (&cur);
|
||||
free (str);
|
||||
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
|
||||
// Find last line
|
||||
for (temp = cur; temp->next; temp = temp->next);
|
||||
|
||||
// Link it to lines we already have
|
||||
// (new lines are added to the start)
|
||||
temp->next = lines;
|
||||
lines = cur;
|
||||
|
||||
// Create a representative child node for
|
||||
// GIB_Process_Embedded to use
|
||||
cur = GIB_Tree_New (TREE_T_META);
|
||||
cur->delim = '`';
|
||||
|
||||
// Save start/end indices
|
||||
cur->start = start;
|
||||
cur->end = end;
|
||||
*line = cur;
|
||||
line = &cur->next;
|
||||
// Check for variable substitution
|
||||
|
||||
// Link it to end of children list for token we are processing
|
||||
*metanext = cur;
|
||||
metanext = &cur->next;
|
||||
// Check for variable substitution
|
||||
} else if (program[i] == '$' || program[i] == '#') {
|
||||
// Extract variable name
|
||||
start = i;
|
||||
|
@ -699,8 +492,8 @@ GIB_Parse_Embedded (const char *program, unsigned int pofs, gib_tree_t ** embedd
|
|||
// Save start/end indices
|
||||
cur->start = start;
|
||||
cur->end = end;
|
||||
*line = cur;
|
||||
line = &cur->next;
|
||||
*metanext = cur;
|
||||
metanext = &cur->next;
|
||||
// Don't skip anything important
|
||||
if (program[n - 1] != '{')
|
||||
i--;
|
||||
|
@ -709,10 +502,8 @@ GIB_Parse_Embedded (const char *program, unsigned int pofs, gib_tree_t ** embedd
|
|||
return lines;
|
||||
ERROR:
|
||||
if (c)
|
||||
GIB_Parse_Error (va ("Could not find match for '%c'.", c), i + pofs);
|
||||
GIB_Parse_Error (va ("Could not find match for '%c'.", c), i + token->start);
|
||||
if (lines)
|
||||
GIB_Tree_Unref (&lines);
|
||||
if (*embfirst)
|
||||
GIB_Tree_Unref (embfirst);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ GIB_Process_Embedded (gib_tree_t * node, cbuf_args_t * args)
|
|||
Cbuf_ArgsAdd (args, retvals->dstrs[j]->str);
|
||||
args->argm[args->argc - 1] = node;
|
||||
}
|
||||
if (str[prev] || retvals->size) // Still more stuff left?
|
||||
if (str[prev] && retvals->size) // Still more stuff left?
|
||||
Cbuf_ArgsAdd (args, "");
|
||||
} else
|
||||
dstring_appendstr (args->argv[args->argc - 1],
|
||||
|
|
|
@ -186,7 +186,7 @@ GIB_Event_Callback (gib_event_t * event, unsigned int argc, ...)
|
|||
|
||||
va_end (ap);
|
||||
|
||||
GIB_Function_Execute (thread->cbuf, f, args->argv, args->argc);
|
||||
GIB_Function_Execute_D (thread->cbuf, f, args->argv, args->argc);
|
||||
GIB_Thread_Add (thread);
|
||||
Cbuf_ArgsDelete (args);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ Carne_Execute_Script (const char *path, cbuf_args_t *args)
|
|||
goto ERROR;
|
||||
}
|
||||
|
||||
GIB_Function_Prepare_Args (mbuf, args->argv, args->argc);
|
||||
GIB_Function_Prepare_Args_D (mbuf, args->argv, args->argc);
|
||||
|
||||
// Main loop
|
||||
while (1) {
|
||||
|
|
Loading…
Reference in a new issue