#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#include "lint.h"
#include "y-tab.h"
#include "lnode.h"
#include "interpret.h"
#include "config.h"
#include "object.h"
#include "wiz_list.h"
static struct value *print_function PROT ((struct lnode_def *)),
*print_expr PROT ((struct lnode *)),
*print_expr_list PROT ((struct lnode *)),
*inter_sscanf PROT ((struct lnode *)),
*explode_string PROT ((char *, char *));
struct value *get_one_expr PROT ((struct lnode **, char *)),
*reallocate_array (), *make_vector (), *concatenate ();
struct vector *create_vector ();
struct svalue *copy_in_svalue ();
extern int transfer_object PROT ((struct object *, struct object *)),
swap PROT ((struct object *)),
remove_call_out PROT ((struct object *, char *)), resort_free_list ();
char *string_copy PROT ((char *)), *xalloc PROT ((int)),
*check_file_name ();
extern struct value *call_indirect PROT ((int,...)), *users ();
extern struct object *previous_ob;
struct object *frame_ob;
extern char *last_verb;
extern int remote_command, call_depth;
struct object *current_inherit_ob;
/*
* Inheritance:
* An object X can inherit from another object Y. This is done with
* the statement 'inherit "file";'
* The inherit statement will clone a copy of that file, call reset
* in it, and set a pointer to Y from X.
* Y has to be removed from the linked list of all objects.
* All variables declared by Y will be copied to X, so that X has access
* to them.
*
* If Y isn't loaded when it is needed, X will be discarded, and Y will be
* loaded separetly. X will then be reloaded again.
*/
struct lnode_2 *next_arg_list_to_use;
struct lnode_var_def *find_status PROT ((char *, int));
extern int tail PROT ((char *));
extern void debug_message_value PROT ((struct value *)),
print_local_commands (),
new_call_out PROT ((struct object *, char *, int, struct value *)),
add_action PROT ((char *, char *, int)),
list_files PROT ((char *, char *)),
enable_commands PROT ((int)),
load_ob_from_swap PROT ((struct object *));
extern int d_flag;
extern int current_line, eval_cost;
struct value **current_local_names;
/*
* This variable is set to true when return is called inside a function,
* or when the current object is destructed.
*/
static int stop_function;
/*
* The only reason that the long jump data is in a struct, is that
* it will be easy to copy. The copy is done so we can stack up
* several return contexts, and pop back them.
*/
/* struct context {
jmp_buf a;
int valid;
};
*/
/*
* This is used for 'break' in while-statements.
*/
/* struct context break_context; */
int break_flag, break_level;
#ifdef TRACE
struct trace
{
char *function_name;
char *object_name;
char *command_name;
int line;
};
#define MAX_TRACE 30
struct trace trace_back[MAX_TRACE];
int trace_depth = 0;
#endif /* TRACE */
struct value *return_value; /* Here is a value when "return". */
struct value *current_argument; /* Arguments to current function */
/*
* Information about assignments of values:
*
* There are three types of l-values: Local variables, global variables
* and vector elements.
*
* The local variables are stored in a vector, with pointers to values
* of the 'struct value type'. Because they are local, they can all be
* freed into the free list afterwards. When a local variable is used,
* the pointer can be directly returned, as these values will never be
* changed.
*
* The global variables must keep their values between executions, and
* can't use the normal 'struct value', but will use a 'struct svalue' which
* is smaller also. Because of this, no automatic garbage collection will
* collect these values when they are overwritten with new values, and
* thus their values will have to be freed immediately. When a global
* variable is used, it must be copied into a new 'struct value'.
*
* Elements in vectors are similar to global variables. There is a reference
* count to the whole vector, that states when to deallocate the vector.
* The elements consists of 'struct svalue's, and will thus have to be freed
* immediately when over written. When an item in vector is used,
* it must be copied into a 'struct value'.
*/
struct value *
make_string (str)
char *str;
{
struct value *ret = alloc_value ();
ret->type = T_STRING;
ret->u.string = string_copy (str);
return ret;
}
struct value *
make_number (n)
int n;
{
struct value *ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = n;
return ret;
}
struct value *
copy_svalue (arg)
struct svalue *arg;
{
struct value *ret;
switch (arg->type)
{
case T_STRING :
return make_string (arg->u.string);
case T_OBJECT :
if (arg->u.ob->destructed)
return &const0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = arg->u.ob;
add_ref (ret->u.ob, "Copy identifier");
break;
case T_NUMBER :
return make_number (arg->u.number);
case T_POINTER :
ret = alloc_value ();
ret->type = T_POINTER;
ret->u.vec = arg->u.vec;
ret->u.vec->ref++;
break;
}
return ret;
}
/*
* Allocate an array.
*/
struct value *
allocate_array (n)
int n;
{
struct value *v;
int i;
struct vector *p;
if (n < 0 || n > MAX_ARRAY_SIZE)
error ("Illegal array size.\n");
p = ALLOC_VECTOR (n);
v = alloc_value ();
v->type = T_POINTER;
v->u.vec = p;
p->ref = 1;
p->size = n;
p->wl = current_object->wl;
if (p->wl)
p->wl->size_array += n;
for (i = 0; i < n; i++)
{
p->item[i].type = T_NUMBER;
p->item[i].u.number = 0;
}
return v;
}
void
free_vector (p)
struct vector *p;
{
int i;
p->ref--;
if (p->ref > 0)
return;
for (i = 0; i < p->size; i++)
free_svalue (&p->item[i]);
if (p->wl)
p->wl->size_array -= p->size;
free ((char *) p);
}
/*
* Free the data that an svalue is pointing to. Not the svalue
* itself.
*/
void
free_svalue (v)
struct svalue *v;
{
switch (v->type)
{
case T_STRING :
free_string (v->u.string);
break;
case T_OBJECT :
free_object (v->u.ob, "free_svalue");
break;
case T_POINTER :
free_vector (v->u.vec);
break;
}
}
/*
* Assign a value to an svalue.
* This is done either when element in vector, or when to an identifier
* (as all identifiers are kept in a vector pointed to by the object).
*/
void
assign_svalue (dest, v)
struct svalue *dest;
struct value *v;
{
/* First deallocate the previous value. */
free_svalue (dest);
if (v == 0)
{
dest->type = T_NUMBER;
dest->u.number = 0;
return;
}
dest->type = v->type;
if (v->type == T_STRING)
dest->u.string = make_shared_string (v->u.string);
else if (v->type == T_OBJECT)
{
dest->u.ob = v->u.ob;
add_ref (dest->u.ob, "ass to var");
}
else if (v->type == T_POINTER)
{
dest->u.vec = v->u.vec;
dest->u.vec->ref++;
}
else
dest->u.number = v->u.number;
}
/*
* Check that an assignment to an array item is not cyclic.
*/
static void
check_for_recursion (vec, v)
struct vector *vec, *v;
{
register int i;
if (vec->wl)
vec->wl->cost++;
eval_cost++;
if (v == vec)
error ("Recursive asignment of vectors.\n");
for (i = 0; i < v->size; i++)
{
if (v->item[i].type == T_POINTER)
check_for_recursion (vec, v->item[i].u.vec);
}
}
/*
* Assign a value to a vector element.
*/
void
assign_vec (p, v)
struct lnode *p;
struct value *v;
{
struct value *i, *vec;
i = print_expr (p->a2);
vec = print_expr (p->a1);
if (vec == 0 || vec->type != T_POINTER)
error ("Indexing on illegal type.\n");
if (v->type == T_POINTER)
check_for_recursion (vec->u.vec, v->u.vec);
if (i->type != T_NUMBER || i->u.number < 0 ||
i->u.number >= vec->u.vec->size)
error ("Illegal index\n");
assign_svalue (&vec->u.vec->item[i->u.number], v);
}
/*
* Assign a value to a local variable or global variable.
*/
void
assign (where, v)
struct lnode *where;
struct value *v;
{
struct lnode_number *p;
if (where->type == F_SUBSCRIPT)
{
assign_vec (where, v);
return;
}
p = (struct lnode_number *) where;
if (p->type == F_LOCAL_NAME)
{
if (v == 0)
{
current_local_names[p->number] = &const0;
return;
}
current_local_names[p->number] = v;
return;
}
else if (p->type == F_ARGUMENT)
{
fatal ("No argument types should exist any longer.\n");
}
else if (p->type == F_IDENTIFIER)
{
struct svalue *dest = find_value ((struct lnode_variable *) p);
assign_svalue (dest, v);
}
else
error ("Assignment to illegal type.\n");
}
static struct value *free_value_list;
static struct value *alloced_value_list;
static int num_alloc;
int tot_alloc_value;
/*
* Free all values previously allocated.
*/
void
free_all_values ()
{
for (num_alloc = 0; alloced_value_list; num_alloc++)
{
struct value *tmp;
tmp = alloced_value_list;
alloced_value_list = alloced_value_list->next;
if (tmp->type == T_STRING)
{
if (!tmp->constant)
free (tmp->u.string);
tmp->u.string = 0;
}
else if (tmp->type == T_OBJECT)
{
free_object (tmp->u.ob, "free_all_values");
tmp->u.ob = 0;
}
else if (tmp->type == T_POINTER)
{
free_vector (tmp->u.vec);
tmp->u.vec = 0;
}
tmp->next = free_value_list;
free_value_list = tmp;
}
if (d_flag)
debug_message ("%d alloced values.\n", num_alloc);
}
int
count_value_ref (ob)
struct object *ob;
{
int tot = 0;
struct value *v;
for (v = alloced_value_list; v; v = v->next)
{
if (v->type == T_OBJECT && v->u.ob == ob)
tot++;
}
return tot;
}
#define ALLOC_CHUNK 50
struct value *
alloc_value ()
{
register struct value *tmp;
if (free_value_list == 0)
{
int i;
tmp = (struct value *) xalloc (ALLOC_CHUNK * sizeof (struct value));
tot_alloc_value += ALLOC_CHUNK;
for (i = 0; i < ALLOC_CHUNK - 1; i++)
tmp[i].next = &tmp[i + 1];
tmp[ALLOC_CHUNK - 1].next = 0;
free_value_list = tmp;
}
tmp = free_value_list;
free_value_list = free_value_list->next;
tmp->next = alloced_value_list;
alloced_value_list = tmp;
/*
* We preset the type to number, to ensure that no one forgets to
* use the value, as it would have an old, undefined type when arriving
* to free_all_values().
*/
tmp->type = T_NUMBER;
tmp->constant = 0;
return tmp;
}
/*
* Print or interpret a function.
* Set up the long jump context, to enable the 'return' statment.
* The arguments are passed in one of two ways:
* Either there is one argument in the variable current_argument. Or there
* can be a list of (unevaluated) arguments pointed to by next_arg_list_to_use.
*
* The reason of this is historical. In the beginning, only one argument
* could be used, thus only one single pointer to the argument. When
* the possibility of several arguments was added, this was implemented
* with an extra info pointer.
*
* If the arguments are sent in next_arg_list_to_use, we have to be
* careful about evaluating them, because current_object points to the
* new object (if call_other() was used). And the global variables
* must be fetched from the previous object, and not current_object.
* This is accomplished by temporarily seting current_object to
* the previous object, which is stored in frame_ob. frame_ob is set
* by apply(), even if it is a local function call.
*/
static struct value *
print_function (p)
struct lnode_def *p;
{
struct value **save_current_local_names = current_local_names;
struct value *new_local_names[MAX_LOCAL];
struct value *tmp_value;
int i;
#ifdef TRACE
int save_trace_depth = trace_depth;
char *get_current_object_name (), *get_command_giver_name ();
#endif
#ifdef TRACE
if (trace_depth < MAX_TRACE)
{
trace_back[trace_depth].function_name = p->name;
trace_back[trace_depth].object_name = get_current_object_name ();
trace_back[trace_depth].command_name = get_command_giver_name ();
trace_back[trace_depth].line = -1;
if (trace_depth > 0)
trace_back[trace_depth - 1].line = current_line;
trace_depth++;
}
else
{
error ("Too deep recursion.\n");
}
#endif
if (p->type == F_IDENTIFIER)
{
struct lnode_2 *tmp_arg_list, *tmp_arg_list_save;
struct object *save_current_object = current_object;
if (d_flag)
{
#ifdef TRACE
debug_message ("%s: ", get_current_object_name ());
#endif
debug_message ("%s(", p->name);
debug_message_value (current_argument);
debug_message (")\n");
}
i = 0;
if (current_argument)
{
new_local_names[0] = current_argument;
i = 1;
}
/* Initialize all arguments. */
tmp_arg_list = next_arg_list_to_use;
tmp_arg_list_save = tmp_arg_list;
current_object = frame_ob;
for (next_arg_list_to_use = 0; i < p->num_arg && tmp_arg_list; i++)
{
new_local_names[i] = print_expr (tmp_arg_list->expr1);
tmp_arg_list = (struct lnode_2 *) tmp_arg_list->expr2;
}
current_object = save_current_object;
/* The remaining arguments and local variables are now initialized. */
for (; i < p->num_var; i++)
new_local_names[i] = &const0;
current_local_names = new_local_names;
return_value = &const0;
(void) print_expr (p->block);
stop_function = 0;
#ifdef TRACE
trace_depth = save_trace_depth;
#endif
current_local_names = save_current_local_names;
/*
* Now clear the return value pointer, so that we won't return
* it again somewhere else.
*/
tmp_value = return_value;
return_value = &const0;
return tmp_value;
}
fatal ("Illegal type %d\n", p->type);
return &const0;
}
struct value *catch_value; /* Used to throw an error to a catch */
/*
* Evaluate an expression. It is guaranteed that print_expr() never
* returns a null pointer.
*/
static struct value *
print_expr (p)
struct lnode *p;
{
struct value *ret = &const0;
struct value *arg, *arg1, *arg2;
struct lnode_block *lb;
char *block;
int i;
if (const1.u.number != 1)
fatal ("Change in constant 1\n");
if (const0.u.number != 0)
fatal ("Change in constant 0\n");
if (current_object && current_object->wl)
current_object->wl->cost++;
if (break_flag)
return &const0;
eval_cost++;
if (eval_cost > MAX_COST)
{
printf ("eval_cost to big %d\n", eval_cost);
eval_cost = 0;
error ("To long evaluation. Execution aborted.\n");
}
if (p == 0)
return &const0;
current_line = p->line & ~L_MASK;
switch (p->type)
{
default :
error ("Unimplemented feature (%d)", p->type);
break;
case F_CLONE_OBJECT :
arg1 = print_expr_list (p->a1);
if (arg1 == 0 || arg1->type != T_STRING)
{
error ("Illegal type argument to clone_object()\n");
exit (1);
}
ret = call_indirect (F_CLONE_OBJECT, arg1->u.string);
break;
case F_AGGREGATE :
{
struct lnode *l;
int num;
for (num = 0, l = p->a1; l; l = l->a2)
num++;
ret = allocate_array (num);
for (num = 0, l = p->a1; l; num++, l = l->a2)
assign_svalue (&ret->u.vec->item[num], print_expr (l->a1));
break;
}
case F_TAIL :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to tail()\n");
if (tail (arg->u.string))
return &const1;
return &const0;
case F_FUNCTION :
{
struct lnode_funcall *f = (struct lnode_funcall *) p;
/*
* Set the linked list of arguments in a global pointer.
* prinf_function() will find it there.
* Not a pretty solution, but a simple way of send more than
* one argument to functions.
*/
next_arg_list_to_use = (struct lnode_2 *) p->a1;
ret = call_indirect (F_FUNCTION, f->name, 0);
break;
}
case F_SAVE_OBJECT :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to save_object().\n");
ret = call_indirect (F_SAVE_OBJECT, arg->u.string);
break;
case F_FIND_OBJECT :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to find_object().\n");
ret = call_indirect (F_FIND_OBJECT, arg->u.string);
break;
case F_FIND_PLAYER :
{
struct object *ob;
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to find_player\n");
ob = find_living_object (arg->u.string, 1);
if (!ob)
break;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob;
add_ref (ob, "find_player");
break;
}
case F_FIND_CALL_OUT :
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "find_call_out");
if (arg->type != T_STRING)
{
error ("Wrong type arg 1 to find_call_out()\n");
exit (1);
}
ret = make_number (find_call_out (current_object, arg->u.string));
break;
}
case F_READ_FILE :
{
struct lnode *l = p->a1;
struct value *file, *place = 0;
file = get_one_expr (&l, "read_file()");
if (file == 0 || file->type != T_STRING)
error ("Bad first argument to read_file().\n");
place = get_one_expr (&l, "read_file()");
if (place == 0 || place->type != T_NUMBER)
error ("Bad second argument to read_file().\n");
ret = call_indirect (F_READ_FILE, file->u.string, place->u.number);
}
break;
case F_WRITE_FILE : /* should return a value - I HATE void */
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "write_file");
if (arg->type != T_STRING)
error ("Bad first argument to write_file()\n");
arg1 = get_one_expr (&l, "write_file");
if (arg1->type != T_STRING)
error ("Bad second argument to write_file()\n");
write_file (arg->u.string, arg1->u.string);
ret = &const0;
}
break;
case F_FILE_SIZE :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to file_size()\n");
ret = make_number (file_size (arg->u.string));
break;
case F_FIND_LIVING :
{
struct object *ob;
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to find_living\n");
ob = find_living_object (arg->u.string, 0);
if (!ob)
break;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob;
add_ref (ob, "find_living");
break;
}
case F_TELL_OBJECT :
{
struct value *who, *what;
struct lnode *l = p->a1;
who = get_one_expr (&l, "tell_object()");
what = get_one_expr (&l, "tell_object()");
if (who == 0 || who->type != T_OBJECT)
error ("tell_object() first argument not an object.\n");
if (what == 0 || what->type != T_STRING)
error ("tell_object() second argument not a string.\n");
(void) call_indirect (F_TELL_OBJECT, who->u.ob, what->u.string);
}
break;
case F_RESTORE_OBJECT :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to restore_object().\n");
ret = call_indirect (F_RESTORE_OBJECT, arg->u.string);
break;
case F_THIS_PLAYER :
ret = call_indirect (F_THIS_PLAYER);
break;
case F_INTERACTIVE :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_OBJECT)
error ("Bad type argument to interactive()\n");
ret = call_indirect (F_INTERACTIVE, arg->u.ob);
break;
case F_FIRST_INVENTORY :
arg = print_expr_list (p->a1);
if (arg == 0 || (arg->type != T_STRING && arg->type != T_OBJECT))
error ("Bad type argument to first_inventory()\n");
ret = call_indirect (F_FIRST_INVENTORY, arg);
break;
case F_INDEX :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "add_action");
if (!arg1 || (arg1->type != T_STRING && arg1->type != T_POINTER))
{
error ("Bad first arg to index()\n");
exit (1);
}
arg2 = get_one_expr (&l, 0);
if (!arg2 || (arg1->type == T_STRING && arg2->type != T_STRING))
{
error ("Bad second arg to index()\n");
exit (1);
}
return make_number (index_array (arg1, arg2));
}
case F_SEARCHA :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "add_action");
if (arg1 == 0 || arg1->type != T_POINTER)
{
error ("Bad type argument 1 to searcha()\n");
exit (1);
}
arg2 = get_one_expr (&l, 0);
if (arg2 == 0)
{
error ("Missing argument 2 to searcha()\n");
exit (1);
}
if (arg2)
{
arg = get_one_expr (&l, 0);
if (arg && arg->type != T_NUMBER)
error ("Bad argument number 3 to searcha.\n");
}
else
arg = 0;
ret = call_indirect (F_SEARCHA, arg1->u.vec, arg2, arg ? arg->u.number : 0);
}
break;
case F_REALLOCATE :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "reallocate");
if (arg1 == 0 || arg1->type != T_POINTER)
{
error ("Bad type argument 1 to reallocate()\n");
exit (1);
}
arg2 = get_one_expr (&l, 0);
if (arg2 != 0 && arg2->type != T_NUMBER)
{
error ("Bad type argument 2 to reallocate()\n");
exit (1);
}
ret = reallocate_array (arg1, arg2->u.number);
}
break;
case F_CONTENTS :
arg = print_expr_list (p->a1);
if (arg == 0 || (arg->type != T_STRING && arg->type != T_OBJECT))
error ("Bad type argument to contents()\n");
ret = call_indirect (F_CONTENTS, arg);
break;
case F_IN_EDITOR :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_OBJECT)
error ("Bad type argument to in_editor()\n");
ret = call_indirect (F_IN_EDITOR, arg->u.ob);
break;
case F_LIVING :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_OBJECT)
error ("Bad type argument to living()\n");
ret = call_indirect (F_LIVING, arg->u.ob);
break;
case F_CREATOR :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_OBJECT)
error ("Bad type argument to creator()\n");
if (arg->u.ob->wl == 0)
ret = &const0;
else
ret = make_string (arg->u.ob->wl->name);
break;
case F_SHUTDOWN :
(void) call_indirect (F_SHUTDOWN);
return &const0;
case F_EXPLODE :
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "explode");
if (arg->type != T_STRING)
error ("bad type of first argument to explode.\n");
arg1 = get_one_expr (&l, "explode");
if (arg1->type != T_STRING)
error ("bad type of second argument to explode.\n");
ret = explode_string (arg->u.string, arg1->u.string);
break;
}
case F_FILTER_OBJECTS :
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "filter_obects");
if (arg->type != T_POINTER)
error ("Bad first argument to filter_obects()\n");
arg1 = get_one_expr (&l, "filter_obects");
if (arg1->type != T_STRING)
error ("Bad second argument to filter_obects()\n");
arg2 = get_one_expr (&l, "filter_obects");
if (arg2->type != T_OBJECT)
error ("Bad third argument to filter_obects()\n");
ret = filter (arg, arg1->u.string, arg2->u.ob);
break;
}
case F_SET_BIT :
{
struct lnode *l = p->a1;
char *str;
int len, old_len, ind;
arg = get_one_expr (&l, "set_bit");
if (arg->type != T_STRING)
error ("Bad first argument to set_bit\n");
arg1 = get_one_expr (&l, "set_bit");
if (arg1->type != T_NUMBER)
error ("Bad second argument to set_bit\n");
if (arg1->u.number > MAX_BITS)
error ("set_bit: too big bit number: %d\n", arg1->u.number);
len = strlen (arg->u.string);
old_len = len;
ind = arg1->u.number / 6;
if (ind >= len)
len = ind + 1;
str = xalloc (len + 1);
str[len] = '\0';
if (old_len)
memcpy (str, arg->u.string, old_len);
if (len > old_len)
memset (str + old_len, ' ', len - old_len);
if (str[ind] > 0x3f + ' ' || str[ind] < ' ')
error ("Illegal bit pattern in set_bit character %d\n", ind);
str[ind] = (str[ind] - ' ' | 1 << arg1->u.number % 6) + ' ';
ret = alloc_value ();
ret->type = T_STRING;
ret->u.string = str;
break;
}
case F_CLEAR_BIT :
{
struct lnode *l = p->a1;
char *str;
int len, ind;
arg = get_one_expr (&l, "clear_bit");
if (arg->type != T_STRING)
error ("Bad first argument to clear_bit\n");
arg1 = get_one_expr (&l, "clear_bit");
if (arg1->type != T_NUMBER)
error ("Bad second argument to clear_bit\n");
if (arg1->u.number > MAX_BITS)
error ("clear_bit: too big bit number: %d\n", arg1->u.number);
len = strlen (arg->u.string);
ind = arg1->u.number / 6;
if (ind >= len)
{
ret = arg;
break;
}
str = xalloc (len + 1);
memcpy (str, arg->u.string, len + 1); /* Including null byte */
if (str[ind] > 0x3f + ' ' || str[ind] < ' ')
error ("Illegal bit pattern in clear_bit character %d\n", ind);
str[ind] = (str[ind] - ' ' & ~(1 << arg1->u.number % 6)) + ' ';
ret = alloc_value ();
ret->type = T_STRING;
ret->u.string = str;
break;
}
case F_TEST_BIT :
{
struct lnode *l = p->a1;
int len;
arg = get_one_expr (&l, "test_bit");
if (arg->type != T_STRING)
error ("Bad first argument to test_bit\n");
arg1 = get_one_expr (&l, "test_bit");
if (arg1->type != T_NUMBER)
error ("Bad second argument to test_bit\n");
len = strlen (arg->u.string);
if (arg1->u.number / 6 >= len)
break;
if (arg->u.string[arg1->u.number / 6] - ' ' & 1 << arg1->u.number % 6)
ret = &const1;
break;
}
case F_QUERY_LOAD_AVERAGE :
{
ret = query_load_av ();
break;
}
case F_NOTIFY_FAIL :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to notify_fail\n");
set_notify_fail_message (arg->u.string);
return &const0;
case F_QUERY_IDLE :
{
extern int query_idle PROT ((struct object *));
arg = print_expr_list (p->a1);
if (!arg || arg->type != T_OBJECT)
error ("Bad argument to query_idle().\n");
ret = make_number (query_idle (arg->u.ob));
break;
}
case F_IMPLODE :
{
struct value *implode_string PROT ((struct vector *, char *));
struct lnode *l = p->a1;
arg = get_one_expr (&l, "implode");
if (arg->type != T_POINTER)
error ("bad type of first argument to implode.\n");
arg1 = get_one_expr (&l, "implode");
if (arg1->type != T_STRING)
error ("bad type of second argument to implode.\n");
ret = implode_string (arg->u.vec, arg1->u.string);
break;
}
case F_QUERY_SNOOP :
if (command_giver == 0)
return &const0;
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_OBJECT)
error ("Bad argument to query_snoop\n");
if (arg->u.ob->interactive == 0)
return &const0;
arg1 = apply ("query_level", command_giver, 0);
if (arg1->type != T_NUMBER || arg1->u.number < 22)
return &const0;
ret = query_snoop (arg->u.ob);
break;
case F_QUERY_IP_NUMBER :
{
extern char *query_ip_number PROT ((struct object *));
char *tmp;
if (p->a1)
arg = print_expr_list (p->a1);
else
arg = 0;
if (arg && arg->type != T_OBJECT)
error ("Bad optional argument to query_ip_number()\n");
tmp = query_ip_number (arg ? arg->u.ob : 0);
if (tmp == 0)
ret = &const0;
else
ret = make_string (tmp);
break;
}
case F_QUERY_HOST_NAME :
{
extern char *query_host_name ();
char *tmp;
tmp = query_host_name ();
ret = alloc_value ();
ret->type = T_STRING;
ret->constant = 1;
ret->u.string = tmp;
break;
}
case F_NEXT_INVENTORY :
arg = print_expr_list (p->a1);
if (arg == 0 || (arg->type != T_STRING && arg->type != T_OBJECT))
error ("Bad type argument to next_inventory()\n");
ret = call_indirect (F_NEXT_INVENTORY, arg);
break;
case F_ENVIRONMENT :
arg = print_expr_list (p->a1);
if (arg && arg->type != T_STRING && arg->type != T_OBJECT)
error ("Wrong type to optional arg to environment()\n");
ret = call_indirect (F_ENVIRONMENT, arg);
break;
case F_GREP :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "grep");
if (arg1 == 0 || arg1->type != T_STRING)
error ("Bad type argument 1 to grep()\n");
arg2 = get_one_expr (&l, 0);
if (arg2 == 0 || arg2->type != T_STRING)
error ("Bad type argument 2 to grep()\n");
arg = get_one_expr (&l, 0);
if (arg == 0 || (arg->type != T_STRING && arg->type != T_NUMBER))
error ("Bad type argument 3 to grep()\n");
ret = call_indirect (F_GREP, arg1->u.string, arg2->u.string,
(arg->type == T_STRING) ? arg->u.string : 0);
}
break;
case F_CALLER :
ret = call_indirect (F_CALLER);
case F_THIS_OBJECT :
ret = call_indirect (F_THIS_OBJECT);
break;
case F_REMOTE_COMMAND :
if (remote_command)
ret = &const1;
else
ret = &const0;
break;
case F_PREVIOUS_OBJECT :
if (previous_ob == 0)
{
ret = &const0;
break;
}
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = previous_ob;
add_ref (previous_ob, "previous_object()");
break;
case F_PEOPLE :
(void) call_indirect (F_PEOPLE);
return &const0;
case F_LOCALCMD :
print_local_commands ();
return &const0;
case F_SWAP :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_OBJECT)
error ("bad argument to swap()\n");
(void) swap (arg->u.ob);
return &const0;
case F_TIME :
ret = make_number (time (0l));
break;
case F_TRANSFER :
{
struct object *dest;
struct lnode *l = p->a1;
int i;
arg = get_one_expr (&l, "transfer");
if (arg->type != T_OBJECT)
error ("Bad type of artgument 1 to transfer()\n");
arg1 = get_one_expr (&l, "transfer");
if (arg1->type != T_OBJECT && arg1->type != T_STRING)
error ("Bad type of artgument 2 to transfer()\n");
if (arg1->type == T_STRING)
{
dest = find_object (arg1->u.string);
if (dest == 0)
error ("Object not found.\n");
}
else
{
dest = arg1->u.ob;
}
i = transfer_object (arg->u.ob, dest);
if (i == 0)
ret = &const0; /* Usually this */
else if (i == 1)
ret = &const1;
else
ret = make_number (i);
}
break;
case F_ADD_WORTH :
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "add_worth");
if (arg->type != T_NUMBER)
error ("Bad type of argument 1 to add_worth()\n");
arg1 = get_one_expr (&l, 0);
if (arg1 != 0 && arg1->type != T_OBJECT)
error ("Bad type of artgument 2 to add_worth()\n");
if (strncmp (current_object->name, "obj/", 4) != 0 &&
strncmp (current_object->name, "room/", 5) != 0)
error ("Illegal call of add_worth.\n");
if (arg1)
{
if (arg1->u.ob->wl)
arg1->u.ob->wl->total_worth += arg->u.number;
return &const0;
}
if (previous_ob == 0)
return &const0;
if (previous_ob->wl)
previous_ob->wl->total_worth += arg->u.number;
}
return &const0;
case F_ADD :
ret = alloc_value ();
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type == T_POINTER && arg2->type == T_POINTER)
return (struct value *) concatenate (arg1, arg2);
if (arg1->type == T_STRING && arg2->type == T_STRING)
{
ret->u.string = xalloc (strlen (arg1->u.string) +
strlen (arg2->u.string) + 1);
(void) strcpy (ret->u.string, arg1->u.string);
(void) strcat (ret->u.string, arg2->u.string);
ret->type = T_STRING;
break;
}
if (arg1->type == T_NUMBER && arg2->type == T_STRING)
{
char buff[20];
sprintf (buff, "%d", arg1->u.number);
ret->type = T_STRING;
ret->u.string = xalloc (strlen (arg2->u.string) +
strlen (buff) + 1);
strcpy (ret->u.string, buff);
strcat (ret->u.string, arg2->u.string);
break;
}
if (arg2->type == T_NUMBER && arg1->type == T_STRING)
{
char buff[20];
sprintf (buff, "%d", arg2->u.number);
ret->type = T_STRING;
ret->u.string = xalloc (strlen (arg1->u.string) +
strlen (buff) + 1);
strcpy (ret->u.string, arg1->u.string);
strcat (ret->u.string, buff);
break;
}
if (arg1->type == T_NUMBER && arg2->type == T_NUMBER)
{
ret->u.number = arg1->u.number + arg2->u.number;
ret->type = T_NUMBER;
break;
}
error ("Bad type of arg to '+'\n");
exit (1);
case F_SUBTRACT :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '-'\n");
exit (1);
}
ret = make_number (arg1->u.number - arg2->u.number);
break;
case F_AND :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '&'\n");
exit (1);
}
ret = make_number (arg1->u.number & arg2->u.number);
break;
case F_OR :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '|'\n");
exit (1);
}
ret = make_number (arg1->u.number | arg2->u.number);
break;
case F_XOR :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '^'\n");
exit (1);
}
ret = make_number (arg1->u.number ^ arg2->u.number);
break;
case F_LSH :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '<<'\n");
exit (1);
}
ret = make_number (arg1->u.number << arg2->u.number);
break;
case F_RSH :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '>>'\n");
exit (1);
}
ret = make_number (arg1->u.number >> arg2->u.number);
break;
case F_LAND :
arg1 = print_expr (p->a1);
if (arg1->type == T_NUMBER && arg1->u.number == 0)
break;
arg2 = print_expr (p->a2);
if (arg2->type == T_NUMBER && arg2->u.number == 0)
break;
ret = &const1;
break;
case F_LOR :
ret = print_expr (p->a1);
if ((ret->type == T_NUMBER && ret->u.number != 0) ||
ret->type != T_NUMBER)
break;
ret = print_expr (p->a2);
if ((ret->type == T_NUMBER && ret->u.number != 0) ||
ret->type != T_NUMBER)
break;
ret = &const0;
break;
case F_MULTIPLY :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '*'\n");
exit (1);
}
ret = make_number (arg1->u.number * arg2->u.number);
break;
case F_DIVIDE :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '/'\n");
exit (1);
}
if (arg2->u.number == 0)
error ("Division by zero.\n");
ret = make_number (arg1->u.number / arg2->u.number);
break;
case F_MOD :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '%'\n");
exit (1);
}
if (arg2->u.number == 0)
error ("Modulus by zero.\n");
ret = make_number (arg1->u.number % arg2->u.number);
break;
case F_GT :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type == T_STRING && arg2->type == T_STRING)
{
if (strcmp (arg1->u.string, arg2->u.string) > 0)
ret = &const1;
else
ret = &const0;
break;
}
else if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '>'\n");
exit (1);
}
if (arg1->u.number > arg2->u.number)
ret = &const1;
else
ret = &const0;
break;
case F_GE :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type == T_STRING && arg2->type == T_STRING)
{
if (strcmp (arg1->u.string, arg2->u.string) >= 0)
ret = &const1;
else
ret = &const0;
break;
}
else if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '>='\n");
exit (1);
}
if (arg1->u.number >= arg2->u.number)
ret = &const1;
else
ret = &const0;
break;
case F_LT :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type == T_STRING && arg2->type == T_STRING)
{
if (strcmp (arg1->u.string, arg2->u.string) < 0)
ret = &const1;
else
ret = &const0;
break;
}
else if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '<'\n");
exit (1);
}
if (arg1->u.number < arg2->u.number)
ret = &const1;
else
ret = &const0;
break;
case F_LE :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type == T_STRING && arg2->type == T_STRING)
{
if (strcmp (arg1->u.string, arg2->u.string) <= 0)
ret = &const1;
else
ret = &const0;
break;
}
else if (arg1->type != T_NUMBER || arg2->type != T_NUMBER)
{
error ("Bad type on arg to '<='\n");
exit (1);
}
if (arg1->u.number <= arg2->u.number)
ret = &const1;
else
ret = &const0;
break;
case F_EQ :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
ret = alloc_value ();
ret->type = T_NUMBER;
if (arg1->type == T_NUMBER && arg2->type == T_NUMBER)
{
ret->u.number = arg1->u.number == arg2->u.number;
}
else if (arg1->type == T_POINTER && arg2->type == T_POINTER)
{
ret->u.number = arg1->u.vec == arg2->u.vec;
}
else if (arg1->type == T_STRING && arg2->type == T_STRING)
{
ret->u.number = !strcmp (arg1->u.string, arg2->u.string);
}
else if (arg1->type == T_OBJECT && arg2->type == T_OBJECT)
{
ret->u.number = arg1->u.ob == arg2->u.ob;
}
else
ret->u.number = 0;
break;
case F_NE :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if (arg1->type != arg2->type)
{
ret = &const1;
break;
}
ret = alloc_value ();
ret->type = T_NUMBER;
if (arg1->type == T_NUMBER)
{
ret->u.number = arg1->u.number != arg2->u.number;
}
else if (arg1->type == T_STRING)
{
ret->u.number = strcmp (arg1->u.string, arg2->u.string);
}
else if (arg1->type == T_POINTER)
{
ret->u.number = arg1->u.vec != arg2->u.vec;
}
else if (arg1->type == T_OBJECT)
{
ret->u.number = arg1->u.ob != arg2->u.ob;
}
else
ret->u.number = 0;
break;
case F_BLOCK : /* Sequential list of statements. */
{
lb = (struct lnode_block *) p;
block = lb->block;
for (i = 0; i < lb->num_nodes && !stop_function && !break_flag; i++)
{
if (current_object->destructed &&
((struct lnode *) block)->type != F_RETURN)
{
stop_function = 1;
return_value = &const0;
}
print_expr ((struct lnode *) block);
block += lnode_size[((struct lnode *) block)->line >> L_SHIFT];
}
return &const0;
}
case F_CONS : /* Linked list of statements. */
while (p && !stop_function && !break_flag)
{
if (current_object->destructed && p->a1->type != F_RETURN)
{
return_value = &const0;
stop_function = 1;
}
(void) print_expr (p->a1);
p = p->a2;
}
return &const0;
case F_COMMA :
while (p && !stop_function && !break_flag)
{
if (current_object->destructed && p->a1->type != F_RETURN)
{
return_value = &const0;
stop_function = 1;
}
ret = print_expr (p->a1);
p = p->a2;
}
break;
case F_IF :
ret = print_expr (p->a1);
if ((ret->type == T_NUMBER && ret->u.number) || ret->type != T_NUMBER)
(void) print_expr (p->a2);
else if (p->a3)
(void) print_expr (p->a3);
return &const0;
case F_ARGUMENT :
if (current_argument == 0)
{
ret = &const0;
break;
}
ret = current_argument;
if (ret->type == T_OBJECT && ret->u.ob->destructed)
ret = &const0;
break;
case F_IDENTIFIER :
ret = copy_svalue (find_value ((struct lnode_variable *) p));
break;
case F_RETURN :
if (p->a1)
return_value = print_expr (p->a1);
else
return_value = &const0;
stop_function = 1;
return &const0;
case F_CATCH :
{
extern jmp_buf error_recovery_context;
extern int error_recovery_context_exists;
extern struct value *catch_value;
jmp_buf old_error_recovery_context;
struct object *save_object = current_object, *save_command = command_giver;
int old_exists_flag;
memcpy ((char *) old_error_recovery_context,
(char *) error_recovery_context, sizeof error_recovery_context);
old_exists_flag = error_recovery_context_exists;
catch_value = &const0; /* system errors throw a string */
error_recovery_context_exists = 2; /* signal catch OK - print no err msg */
if (setjmp (error_recovery_context))
{ /* they did a throw() or error */
ret = catch_value; /* get whatever they threw */
current_object = save_object;
command_giver = save_command;
}
else
{
#ifdef REAL_CATCH
print_expr (p->a1); /* do it */
#else
print_expr_list (p->a1);
#endif
ret = &const0; /* no error - catch returns zero */
}
memcpy ((char *) error_recovery_context,
(char *) old_error_recovery_context,
sizeof error_recovery_context);
error_recovery_context_exists = old_exists_flag;
catch_value = &const1; /* next error will still return 1 by default */
}
break;
case F_THROW :
#ifdef REAL_CATCH
catch_value = print_expr (p->a1);
#else
catch_value = print_expr_list (p->a1);
#endif
if (catch_value != 0 && catch_value->type == T_NUMBER &&
catch_value->u.number == 0)
catch_value = &const1;
throw_error (); /* do the longjump */
break;
case F_BREAK :
if (!break_level)
error ("Illegal break statement!\n");
/* longjmp(break_context.a, 1);
fatal("Return from longjmp\n"); */
break_flag = 1;
return &const0;
case F_CONTINUE :
if (!break_level)
error ("Illegal continue statement!\n");
/* longjmp(break_context.a, 2);
fatal("Return from longjmp\n"); */
break_flag = 2;
return &const0;
case F_LOG_FILE :
{
struct lnode *l;
l = p->a1;
arg1 = get_one_expr (&l, "log_file");
arg2 = get_one_expr (&l, "log_file");
if (arg1 == 0 || arg1->type != T_STRING ||
arg2 == 0 || arg2->type != T_STRING)
error ("Bad type argument to log_file().\n");
(void) call_indirect (F_LOG_FILE, arg1->u.string, arg2->u.string);
}
return &const0;
case F_NOT :
arg = print_expr (p->a1);
if (arg->type == T_NUMBER && arg->u.number == 0)
ret = &const1;
else
ret = &const0;
break;
case F_COMPL :
arg = print_expr (p->a1);
if (arg->type != T_NUMBER)
error ("Bad argument to '~'\n");
ret = make_number (~arg->u.number);
break;
case F_NEGATE :
arg = print_expr (p->a1);
if (arg->type != T_NUMBER)
error ("Bad argument to unary '-'\n");
ret = make_number (-arg->u.number);
break;
case F_INC :
arg = print_expr (p->a1);
if (arg->type != T_NUMBER)
error ("Bad argument to '++' (pre)\n");
ret = make_number (arg->u.number + 1);
assign (p->a1, ret);
break;
case F_DEC :
arg = print_expr (p->a1);
if (arg->type != T_NUMBER)
error ("Bad argument to '--' (pre)\n");
ret = make_number (arg->u.number - 1);
assign (p->a1, ret);
break;
case F_POST_INC :
ret = print_expr (p->a1);
if (ret->type != T_NUMBER)
error ("Bad argument to '++' (post)\n");
arg = make_number (ret->u.number + 1);
assign (p->a1, arg);
break;
case F_POST_DEC :
ret = print_expr (p->a1);
if (ret->type != T_NUMBER)
error ("Bad argument to '--' (post)\n");
arg = make_number (ret->u.number - 1);
assign (p->a1, arg);
break;
case F_CALL_OTHER :
{
struct value *a1, *a2;
struct lnode *l = p->a1;
a1 = get_one_expr (&l, "call_other");
a2 = get_one_expr (&l, "call_other");
/*
* Send the rest of the arguments in the linked list,
* unevaluated. It is thus possible to send more than one
* argument.
*/
/* a3 = get_one_expr(&l, (char *)0); */
next_arg_list_to_use = (struct lnode_2 *) l;
if (a1 == 0 || (a1->type != T_STRING &&
a1->type != T_OBJECT))
{
error ("Wrong type arg 1 to call_other()\n");
exit (1);
}
if (a2->type != T_STRING)
{
error ("Wrong type arg 2 to call_other()\n");
exit (1);
}
ret = call_indirect (F_CALL_OTHER, a1, a2->u.string, 0);
}
break;
case F_INTP :
arg = print_expr_list (p->a1);
if (arg && arg->type == T_NUMBER)
ret = &const1;
else
ret = &const0;
break;
case F_STRINGP :
arg = print_expr_list (p->a1);
if (arg && arg->type == T_STRING)
ret = &const1;
else
ret = &const0;
break;
case F_OBJECTP :
arg = print_expr_list (p->a1);
if (arg && arg->type == T_OBJECT)
ret = &const1;
else
ret = &const0;
break;
case F_POINTERP :
arg = print_expr_list (p->a1);
if (arg && arg->type == T_POINTER)
ret = &const1;
else
ret = &const0;
break;
case F_EXTRACT :
{
struct lnode *l;
struct vector *pv;
int len, start, finish;
char *newS;
l = p->a1;
arg = get_one_expr (&l, "extract");
arg1 = get_one_expr (&l, (char *) 0);
arg2 = get_one_expr (&l, (char *) 0);
if (!(arg && (arg->type == T_STRING || arg->type == T_POINTER)))
error ("Bad first argument to extract()\n");
if (!arg1)
{
ret = make_string ("");
break;
}
if (arg1->type != T_NUMBER)
error ("Bad second argument to extract()\n");
if (arg1->u.number < 0)
error ("Bad second argument to extract()\n");
start = arg1->u.number;
if (!arg2)
{ /* no arg? -- set it to the end. */
if (arg->type == T_STRING)
finish = strlen (arg->u.string) - 1;
else
finish = arg->u.vec->size - 1;
}
else if (arg2->type != T_NUMBER)
error ("Bad third argument to extract()\n");
else
finish = arg2->u.number;
if (arg->type == T_STRING)
len = strlen (arg->u.string);
else
len = arg->u.vec->size;
if (finish >= len)
finish = len - 1;
if (finish < start || start >= len)
{ /* urk! */
if (arg->type == T_STRING)
ret = make_string ("");
else
ret = allocate_array (0);
break;
}
if (arg->type == T_STRING)
{
newS = xalloc (finish - start + 2);
strncpy (newS, arg->u.string + start, finish - start + 1);
newS[finish - start + 1] = '\0';
ret = make_string (newS);
free (newS);
}
else
{
pv = (struct vector *) create_vector (finish - start + 1);
copy_in_vector (pv, arg->u.vec, 0, start, len);
return (struct value *) make_vector (pv);
}
break;
}
case F_QUERY_VERB :
if (last_verb == 0)
{
ret = &const0;
break;
}
ret = alloc_value ();
ret->type = T_STRING;
ret->constant = 1;
ret->u.string = last_verb;
break;
case F_FILE_NAME :
arg = print_expr_list (p->a1);
if (arg->type != T_OBJECT)
error ("Bad argument to file_name()\n");
ret = alloc_value ();
ret->type = T_STRING;
ret->u.string = arg->u.ob->name;
ret->constant = 1;
break;
case F_USERS :
ret = users ();
break;
case F_CALL_OUT :
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "call_out");
arg1 = get_one_expr (&l, "call_out");
arg2 = get_one_expr (&l, 0);
if (arg->type != T_STRING)
{
error ("Wrong type arg 1 to call_out()\n");
exit (1);
}
if (arg1->type != T_NUMBER)
{
error ("Wrong type arg 2 to call_out()\n");
exit (1);
}
new_call_out (current_object, arg->u.string, arg1->u.number, arg2);
return &const0;
}
case F_REMOVE_CALL_OUT :
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "call_out");
if (arg->type != T_STRING)
{
error ("Wrong type arg 1 to remove_call_out()\n");
exit (1);
}
ret = make_number (remove_call_out (current_object, arg->u.string));
break;
}
case F_TYPEOF :
arg = print_expr_list (p->a1);
if (!arg)
error ("Bad argument to typeof()\n");
ret = alloc_value ();
ret->type = T_STRING;
switch (arg->type)
{
case T_NUMBER :
ret->u.string = string_copy ("int");
break;
case T_OBJECT :
ret->u.string = string_copy ("object");
break;
case T_STRING :
ret->u.string = string_copy ("string");
break;
case T_POINTER :
ret->u.string = string_copy ("vector");
break;
default :
ret->u.string = string_copy ("");
}
break;
case F_WRITE :
arg = print_expr_list (p->a1);
if (arg == 0)
error ("Bad argument to write()");
(void) call_indirect (F_WRITE, arg);
return &const0;
#if 0
case F_REGCOMP :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to regcomp()");
ret = alloc_value ();
ret->type = T_POINTER;
ret->u.vec = (struct vector *) xalloc (sizeof (struct vector_regexp));
ret->u.vec =
(char *) regcomp (arg->u.string);
break;
case F_REGEXEC :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "regexec");
arg2 = get_one_expr (&l, "regexec");
if (arg1 == 0 || arg2->type != T_STRING)
error ("Bad arg 1 to regexec.\n");
if (arg2 == 0 || arg2->type != T_STRING)
error ("Bad arg 2 to regexec.\n");
debug_message ("regexec: arg1 u.string: %x\n", arg1->u.string);
debug_message ("regexec: arg2 u.string: %x\n", arg2->u.string);
if (regexec (arg1->u.string, arg2->u.string))
ret = &const1;
else
ret = &const0;
}
#endif
case F_MOVE_OBJECT :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "move_object");
arg2 = get_one_expr (&l, "move_object");
if (arg1 == 0 || (arg1->type != T_STRING &&
arg1->type != T_OBJECT))
{
error ("Bad type argument to move_object()\n");
return &const0;
}
if (arg2 == 0 || (arg2->type != T_STRING &&
arg2->type != T_OBJECT))
{
error ("Bad type argument to move_object()\n");
return &const0;
}
(void) call_indirect (F_MOVE_OBJECT, arg1, arg2);
return &const0;
}
case F_SNOOP :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, (char *) 0);
if (arg1 && arg1->type != T_OBJECT)
error ("Bad type arg to snoop.\n");
(void) call_indirect (F_SNOOP, arg1 ? arg1->u.ob : 0);
return &const0;
}
case F_ADD_ACTION :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "add_action");
if (arg1 == 0 || arg1->type != T_STRING)
{
error ("Bad type argument 1 to add_action()\n");
exit (1);
}
arg2 = get_one_expr (&l, 0);
if (arg2 != 0 && arg2->type != T_STRING)
{
error ("Bad type argument 2 to add_action()\n");
exit (1);
}
if (arg2)
{
arg = get_one_expr (&l, 0);
if (arg && arg->type != T_NUMBER)
error ("Bad argument number 3 to add_action.\n");
}
else
arg = 0;
add_action (arg1->u.string, arg2 ? arg2->u.string : 0,
arg ? arg->u.number : 0);
}
return &const0;
case F_ADD_VERB :
ret = print_expr_list (p->a1);
if (ret == 0 || ret->type != T_STRING)
{
error ("Bad type argument to add_verb()\n");
exit (1);
}
(void) call_indirect (F_ADD_VERB, ret->u.string);
return &const0;
case F_ADD_XVERB :
ret = print_expr_list (p->a1);
if (ret == 0 || ret->type != T_STRING ||
strlen (ret->u.string) < 1)
{
error ("Bad type argument to add_xverb()\n");
exit (1);
}
(void) call_indirect (F_ADD_XVERB, ret->u.string);
return &const0;
case F_ALLOCATE :
ret = print_expr_list (p->a1);
if (ret == 0 || ret->type != T_NUMBER)
{
error ("Bad type argument to allocate()\n");
exit (1);
}
ret = allocate_array (ret->u.number);
break;
case F_ED :
arg = print_expr_list (p->a1);
if (arg == 0)
{
char *p;
if (command_giver == 0 || command_giver->interactive == 0)
return &const0;
arg = apply ("query_real_name", command_giver, 0);
if (arg == 0 || arg->type != T_STRING)
return &const0;
p = get_error_file (arg->u.string);
if (p == 0)
return &const0;
(void) call_indirect (F_ED, p);
return &const0;
}
if (arg->type != T_STRING)
error ("Bad argument to ed().\n");
(void) call_indirect (F_ED, arg ? arg->u.string : 0);
return &const0;
case F_CRYPT :
{
char salt[2];
char *choise =
"abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789./";
struct lnode *l = p->a1;
arg = get_one_expr (&l, "crypt");
if (arg->type != T_STRING)
error ("Bad type first arg to crypt.\n");
arg1 = get_one_expr (&l, "crypt");
if (arg1->type == T_STRING && strlen (arg->u.string) >= 2)
{
salt[0] = arg1->u.string[0];
salt[1] = arg1->u.string[1];
}
else
{
#ifdef RANDOM
salt[0] = choise[random () % strlen (choise)];
salt[1] = choise[random () % strlen (choise)];
#else /* RANDOM */
#ifdef DRAND48
salt[0] = choise[(int) (drand48 () * strlen (choise))];
salt[1] = choise[(int) (drand48 () * strlen (choise))];
#else /* DRAND48 */
salt[0] = choise[time (0l) % strlen (choise)];
salt[1] = choise[time (0l) % strlen (choise)];
#endif /* DRAND48 */
#endif /* RANDOM */
}
ret = alloc_value ();
ret->type = T_STRING;
/*#ifdef sun*/
ret->u.string = string_copy (_crypt (arg->u.string, salt));
#ifdef _bub_
/*#else*/
ret->u.string = string_copy (crypt (arg->u.string, salt));
#endif
break;
}
case F_CREATE_WIZARD :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to create_wizard().\n");
ret = call_indirect (F_CREATE_WIZARD, arg->u.string);
break;
case F_DESTRUCT :
ret = print_expr_list (p->a1);
if (ret == 0 || ret->type != T_STRING && ret->type != T_OBJECT)
{
error ("Bad type argument to destruct()\n");
exit (1);
}
(void) call_indirect (F_DESTRUCT, ret);
return &const0;
case F_RANDOM :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_NUMBER)
error ("Bad type arg to random()\n");
if (arg->u.number <= 0)
{
ret = &const0;
break;
}
#ifdef DRAND48
ret = make_number ((int) (drand48 () * arg->u.number));
#else
#ifdef RANDOM
ret = make_number (random () % arg->u.number);
#else
/* BUB I'm gonna just kludge up something because it is late.
ret = make_number ((((int) arg + (int) ret) >> 2 + time (0l)) %
arg->u.number);
*/
ret = make_number (rand () % arg->u.number);
#endif /* RANDOM */
#endif /* DRAND48 */
break;
case F_SAY :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "say()");
if (arg1 == 0)
{
error ("Bad type argument to say()\n");
exit (1);
}
arg2 = get_one_expr (&l, (char *) 0);
if (arg2 && arg2->type != T_OBJECT)
error ("Bad type argument to say()\n");
(void) call_indirect (F_SAY, arg1, arg2 ? arg2->u.ob : 0);
}
return &const0;
case F_TELL_ROOM :
{
struct lnode *l = p->a1;
struct object *ob;
arg1 = get_one_expr (&l, "tell_room");
if (arg1->type == T_OBJECT)
ob = arg1->u.ob;
else if (arg1->type != T_STRING)
error ("Bad type argument to tell_room()\n");
else
{
ob = find_object (arg1->u.string);
if (ob == 0)
error ("Object not found.\n");
}
arg2 = get_one_expr (&l, "tell_room");
(void) call_indirect (F_TELL_ROOM, ob, arg2);
}
return &const0;
case F_SHOUT :
ret = print_expr_list (p->a1);
if (ret == 0 || ret->type != T_STRING)
{
error ("Bad type argument to shout()\n");
exit (1);
}
(void) call_indirect (F_SHOUT, ret->u.string);
return &const0;
case F_WHILE :
{
/* struct context old_context;
old_context = break_context; */
/*
* We come to the next statement in three ways:
* 0: Set up of long jump.
* 1: break statement.
* 2: continue statement.
*/
/* if (setjmp(break_context.a) == 1) {
break_context.valid = 0;
break;
} */
break_level++;
while (!stop_function && !break_flag)
{
arg = print_expr (p->a1);
if (arg->type == T_NUMBER && arg->u.number == 0)
break;
(void) print_expr (p->a2);
if (break_flag == 2)
break_flag = 0;
if (current_object->destructed)
{
return_value = &const0;
stop_function = 1;
}
}
break_flag = 0;
break_level--;
return &const0;
}
case F_DO :
{
/* struct context old_context;
old_context = break_context; */
/*
* We come to the next statement in three ways:
* 0: Set up of long jump.
* 1: break statement.
* 2: continue statement.
*/
/* if (setjmp(break_context.a) == 1) {
break_context.valid = 0;
break;
} */
break_level++;
while (!stop_function && !break_flag)
{
(void) print_expr (p->a1);
if (break_flag == 2)
break_flag = 0;
arg = print_expr (p->a2);
if (arg->type == T_NUMBER && arg->u.number == 0)
break;
if (current_object->destructed)
{
return_value = &const0;
stop_function = 1;
}
}
break_flag = 0;
break_level--;
return &const0;
}
case F_FOR :
{
/* struct context old_context;
old_context = break_context; */
/*
* We come to the next statement in three ways:
* 0: Set up of long jump.
* 1: break statement.
* 2: continue statement.
*/
/* if (setjmp(break_context.a) == 1) {
break_context.valid = 0;
break;
} */
break_level++;
while (!stop_function && !break_flag)
{
arg = print_expr (p->a1);
if (arg->type == T_NUMBER && arg->u.number == 0)
break;
(void) print_expr (p->a3);
if (break_flag == 2)
break_flag = 0;
if (!break_flag)
(void) print_expr (p->a2);
if (current_object->destructed)
{
return_value = &const0;
stop_function = 1;
}
}
break_flag = 0;
break_level--;
return &const0;
}
case F_SUBSCRIPT :
arg1 = print_expr (p->a1);
arg2 = print_expr (p->a2);
if ((arg1->type != T_STRING && arg1->type != T_POINTER) ||
arg2->type != T_NUMBER)
error ("Bad type argument to subscripts.\n");
if (arg1->type == T_POINTER)
{
i = arg2->u.number;
if (i < 0 || i >= arg1->u.vec->size)
error ("Illegal index size: %d\n", i);
ret = copy_svalue (&arg1->u.vec->item[i]);
break;
}
if (arg2->u.number >= strlen (arg1->u.string))
break;
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = arg1->u.string[arg2->u.number];
break;
case F_STRLEN :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Illegal argument to strlen()\n");
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = strlen (arg->u.string);
break;
case F_SIZEOF :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_POINTER)
error ("Illegal argument to sizeof()\n");
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = arg->u.vec->size;
break;
case F_LOWER_CASE :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad type argument to lowercase\n");
{
int i;
ret = make_string (arg->u.string);
for (i = strlen (ret->u.string) - 1; i >= 0; i--)
if (isalpha (ret->u.string[i]))
ret->u.string[i] |= 'a' - 'A';
}
break;
case F_SET_HEART_BEAT :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_NUMBER)
error ("Bad type argument to set_heart_beat()\n");
ret = call_indirect (F_SET_HEART_BEAT, arg->u.number);
break;
case F_CAPITALIZE :
ret = print_expr_list (p->a1);
if (ret == 0 || ret->type != T_STRING)
error ("Bad type arg to capitalize\n");
if (ret->u.string[0] == '\0')
break;
if (islower (ret->u.string[0]))
{
ret = make_string (ret->u.string);
ret->u.string[0] += 'A' - 'a';
}
break;
case F_COMMAND :
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "command");
if (arg->type != T_STRING)
{
error ("Bad first argument to command()\n");
exit (1);
}
arg1 = get_one_expr (&l, 0);
if (arg1 && arg1->type != T_OBJECT)
error ("Bad second argument to command()\n");
ret = call_indirect (F_COMMAND, arg->u.string, arg1 ? arg1->u.ob : 0);
break;
}
case F_LS :
{
struct lnode *l = p->a1;
arg = get_one_expr (&l, "ls");
if (arg->type != T_STRING && arg->type != T_NUMBER)
error ("Bad type argument to command()\n");
arg1 = get_one_expr (&l, 0);
if (arg1 && arg1->type != T_STRING)
error ("Bad second argument to ls()\n");
list_files (arg->type == T_STRING ? arg->u.string : 0,
arg1 ? arg1->u.string : 0);
return &const0;
}
case F_RM :
ret = print_expr_list (p->a1);
if (ret == 0 || ret->type != T_STRING)
{
error ("Bad type argument to rm()\n");
exit (1);
}
(void) call_indirect (F_RM, ret->u.string);
return &const0;
case F_CAT :
{
struct lnode *l = p->a1;
int start = 0, len = 0;
ret = get_one_expr (&l, "cat");
if (ret == 0 || ret->type != T_STRING)
{
error ("Bad type argument to cat()\n");
exit (1);
}
arg1 = get_one_expr (&l, 0);
if (arg1)
{
if (arg1->type != T_NUMBER)
error ("Bad second argument to cat\n");
start = arg1->u.number;
arg2 = get_one_expr (&l, "cat third arg");
if (arg2->type != T_NUMBER)
error ("Bad third argument to cat\n");
len = arg2->u.number;
}
ret = call_indirect (F_CAT, ret->u.string, start, len);
}
break;
case F_MKDIR :
{
struct lnode *l = p->a1;
char *path;
ret = get_one_expr (&l, "mkdir");
if (ret->type != T_STRING)
error ("Bad type argument to mkdir()\n");
path = check_file_name (ret->u.string, 1);
if (path == 0 || mkdir (path, 0770) == -1)
ret = &const0;
else
ret = &const1;
}
break;
case F_RMDIR :
{
struct lnode *l = p->a1;
char *path;
ret = get_one_expr (&l, "mkdir");
if (ret->type != T_STRING)
error ("Bad type argument to rmdir()\n");
path = check_file_name (ret->u.string, 1);
if (path == 0 || rmdir (path) == -1)
ret = &const0;
else
ret = &const1;
}
break;
case F_CP :
{
struct value *src, *dst;
struct lnode *l = p->a1;
src = get_one_expr (&l, "cp()");
dst = get_one_expr (&l, "cp()");
if (src == 0 || src->type != T_STRING)
{
error ("Bad type first argument to cp()\n");
exit (1);
}
if (dst == 0 || dst->type != T_STRING)
{
error ("Bad type second argument to cpr()\n");
exit (1);
}
(void) call_indirect (F_CP, src->u.string, dst->u.string);
return &const0;
}
break;
case F_RENAME :
{
struct value *src, *dst;
struct lnode *l = p->a1;
src = get_one_expr (&l, "rename()");
dst = get_one_expr (&l, "rename()");
if (src == 0 || src->type != T_STRING)
{
error ("Bad type first argument to rmdir()\n");
exit (1);
}
if (dst == 0 || dst->type != T_STRING)
{
error ("Bad type second argument to rmdir()\n");
exit (1);
}
(void) call_indirect (F_RENAME, src->u.string, dst->u.string);
return &const0;
}
break;
case F_INPUT_TO :
{
struct lnode *l = p->a1;
int flag = 1;
ret = get_one_expr (&l, "input_to");
if (ret == 0 || ret->type != T_STRING)
{
error ("Bad type argument to input_to()\n");
exit (1);
}
arg1 = get_one_expr (&l, 0);
if (!arg1 || (arg1->type == T_NUMBER && arg1->u.number == 0))
flag = 0;
ret = call_indirect (F_INPUT_TO, ret->u.string, flag);
}
break;
case F_SET_LIVING_NAME :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_STRING)
error ("Bad argument to set_living_name()\n");
set_living_name (current_object, arg->u.string);
return &const0;
case F_PARSE_COMMAND :
ret = parse (p->a1);
break;
case F_SSCANF :
ret = inter_sscanf (p->a1);
break;
case F_ENABLE_COMMANDS :
(void) call_indirect (F_ENABLE_COMMANDS);
return &const0;
case F_DISABLE_COMMANDS :
enable_commands (0);
return &const0;
case F_PRESENT :
{
struct lnode *l = p->a1;
arg1 = get_one_expr (&l, "present()");
if (arg1->type != T_STRING && arg1->type != T_OBJECT)
error ("Bad type argument to present()\n");
arg2 = get_one_expr (&l, NULL);
if (arg2 && arg2->type != T_OBJECT)
error ("Bad second argument to present()\n");
ret = call_indirect (F_PRESENT, arg1, arg2 ? arg2->u.ob : 0);
}
break;
case F_SET_LIGHT :
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_NUMBER)
error ("Bad type argument to set_light()\n");
ret = call_indirect (F_SET_LIGHT, arg->u.number);
break;
case F_CONST0 :
ret = &const0;
break;
case F_CONST1 :
ret = &const1;
break;
case F_NUMBER :
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = (int) p->a1;
break;
case F_ASSIGN :
ret = print_expr (p->a2);
assign (p->a1, ret);
break;
case F_CTIME :
{
long tmp;
char *cp;
arg = print_expr_list (p->a1);
if (arg == 0 || arg->type != T_NUMBER)
error ("Bad argument to ctime()\n");
tmp = arg->u.number;
ret = make_string (ctime (&tmp));
/* Now strip the newline. */
cp = strchr (ret->u.string, '\n');
if (cp)
*cp = '\0';
}
break;
case F_ADD_EQ :
{
struct value *v;
arg = print_expr (p->a2);
v = print_expr (p->a1);
if (v->type == T_POINTER && arg->type == T_POINTER)
return (struct value *) concatenate (v, arg);
if (v->type == T_STRING)
{
char *new_str;
if (arg->type == T_STRING)
{
new_str = xalloc (strlen (arg->u.string) +
strlen (v->u.string) + 1);
strcpy (new_str, v->u.string);
strcat (new_str, arg->u.string);
}
else if (arg->type == T_NUMBER)
{
char buff[20];
sprintf (buff, "%d", arg->u.number);
new_str = xalloc (strlen (v->u.string) + strlen (buff) + 1);
strcpy (new_str, v->u.string);
strcat (new_str, buff);
}
else
error ("Illegal rhs to +=\n");
ret = alloc_value ();
ret->type = T_STRING;
ret->u.string = new_str;
assign (p->a1, ret);
break;
}
if (arg->type != T_NUMBER)
error ("Bad type to rhs +=.\n");
if (v->type != T_NUMBER)
error ("Bad type to lhs +=");
assign (p->a1, ret = make_number (v->u.number + arg->u.number));
}
break;
case F_SUB_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type to -=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type to -=");
assign (p->a1, ret = make_number (v->u.number - arg->u.number));
}
break;
case F_MULT_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type rhs to *=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type lhs to *=");
assign (p->a1, ret = make_number (v->u.number * arg->u.number));
}
break;
case F_AND_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type to &=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type to &=");
assign (p->a1, ret = make_number (v->u.number & arg->u.number));
}
break;
case F_OR_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type to |=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type to |=");
assign (p->a1, ret = make_number (v->u.number | arg->u.number));
}
break;
case F_XOR_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type to ^=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type to ^=");
assign (p->a1, ret = make_number (v->u.number ^ arg->u.number));
}
break;
case F_LSH_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type to <<=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type to <<=");
assign (p->a1, ret = make_number (v->u.number << arg->u.number));
}
break;
case F_RSH_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type to >>=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type to >>=");
assign (p->a1, ret = make_number (v->u.number >> arg->u.number));
}
break;
case F_COMBINE_FREE_LIST :
#ifdef MALLOC_malloc
ret = make_number (resort_free_list ());
#else
ret = &const0;
#endif
break;
case F_LOCAL_NAME :
ret = current_local_names[(int) p->a1];
if (ret->type == T_OBJECT && ret->u.ob->destructed)
ret = &const0;
break;
case F_DIV_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type rhs to /=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type lhs to /=");
if (arg->u.number == 0)
error ("Division by zero.");
assign (p->a1, ret = make_number (v->u.number / arg->u.number));
}
break;
case F_MOD_EQ :
{
struct value *v;
arg = print_expr (p->a2);
if (arg->type != T_NUMBER)
error ("Bad type rhs to %=.\n");
v = print_expr (p->a1);
if (v->type != T_NUMBER)
error ("Bad type lhs to %=");
if (arg->u.number == 0)
error ("Division by zero.");
assign (p->a1, ret = make_number (v->u.number % arg->u.number));
}
break;
case F_STRING :
{
struct lnode_name *n = (struct lnode_name *) p;
ret = alloc_value ();
ret->u.string = n->name;
ret->constant = 1;
ret->type = T_STRING;
break;
}
case F_COND :
ret = print_expr (p->a1);
if (ret && ((ret->type == T_NUMBER && ret->u.number) ||
ret->type == T_STRING || ret->type == T_OBJECT))
ret = print_expr (p->a2);
else
ret = print_expr (p->a3);
break;
}
if (ret == 0)
ret = &const0;
return ret;
}
static struct value *
print_expr_list (p)
struct lnode *p;
{
if (p == 0)
return 0;
if (p->type != F_CONS)
fatal ("Bad type to print_expr_list()\n");
return print_expr (p->a1);
}
/*
* Apply a fun 'fun' to the program in object 'ob', with the
* optional argument 'arg'.
* If the function is not found, search in the object pointed to by the
* inherit pointer.
* If the function name starts with '::', search in the object pointed out
* through the inherit pointer by the current object. The 'current_object'
* stores the base object, not the object that has the current function being
* evaluated. Thus, the variable current_inherit_ob is used to save this
* information.
*
* There is a special case when called from the heart beat, as
* current_inherit_ob will be 0. So when it is 0, we set current_inherit_ob
* to the 'ob' sent as argument.
*
* If the function is not found, return a null pointer which indicates
* this. print_function() will never return a null pointer.
*
* Note that the object 'ob' can be destructed. This must be handled by
* the caller of apply().
*/
char debug_apply_fun[30]; /* For debugging */
struct value *
apply (fun, ob, arg)
char *fun;
struct object *ob;
struct value *arg;
{
struct lnode_def *pr;
struct value *old_argument = current_argument;
struct object *save_current_object = current_object;
struct object *prog_ob;
extern int num_error;
struct object *save_current_inherit_ob;
int length;
strncpy (debug_apply_fun, fun, sizeof debug_apply_fun);
if (num_error > 0)
return &const0;
call_depth++;
if (call_depth > MAX_RECURSION)
{
call_depth = 0;
error ("Maximum recursion exceeded. Execution aborted.\n");
}
prog_ob = ob;
if (fun[0] == ':' && fun[1] == ':')
{
if (current_object != ob)
error ("Illegal to call functions with '::' from the outside.\n");
/* current_inherit_ob can only be 0 when called from heart beat. */
if (current_inherit_ob != 0)
prog_ob = current_inherit_ob;
}
length = strlen (fun);
for (; prog_ob; prog_ob = prog_ob->inherit)
{
if (fun[0] == ':' && fun[1] == ':')
{
fun += 2;
length -= 2; /* Must recompute the length without '::' */
continue;
}
if (prog_ob->swapped)
load_ob_from_swap (prog_ob);
pr = prog_ob->prog;
if (pr == 0)
continue;
if (prog_ob->destructed)
{
current_object = prog_ob;
error ("Executing '%s' in destructed object.\n", fun);
}
for (; pr; pr = pr->next)
{
if (pr->type == F_IDENTIFIER && pr->length == length &&
strcmp (pr->name, fun) == 0)
{
struct value *ret;
struct object *save_previous_ob;
/* Static functions may not be called from outside. */
if (pr->is_static && current_object != ob)
continue;
current_argument = arg;
frame_ob = current_object;
save_previous_ob = previous_ob;
if (current_object != ob)
previous_ob = current_object;
current_object = ob;
save_current_inherit_ob = current_inherit_ob;
current_inherit_ob = prog_ob;
ret = print_function (pr);
current_inherit_ob = save_current_inherit_ob;
/*
* print_function() will clear the value of
* next_arg_list_to_use
*/
current_object = save_current_object;
previous_ob = save_previous_ob;
current_argument = old_argument;
call_depth--;
return ret;
}
}
}
if (d_flag)
debug_message ("--Could not find the function %s\n", fun);
/*
* We must clear next_arg_list_to_use, so that those arguments won't be
* sent to another function.
*/
next_arg_list_to_use = 0;
call_depth--;
return 0;
}
/*
* Call a specific function in an object.
* Make sure that current_object is set up, and the the program
* is not swapped.
*/
void
call_function (pr)
struct lnode_def *pr;
{
(void) print_function (pr);
}
#ifdef TRACE
/*
* Write out a trace. If there is an heart_beat(), then return the
* object that had that heart beat.
*/
char *
dump_trace ()
{
int i;
char *ret = 0;
if (trace_depth == 0)
{
(void) printf ("No trace.\n");
debug_message ("No trace.\n");
return 0;
}
for (i = 0; i < trace_depth; i++)
{
(void) printf ("%-3d: '%20s' in '%20s' line %d\n", i,
trace_back[i].function_name, trace_back[i].object_name,
trace_back[i].line);
debug_message ("%-3d: '%20s' in '%20s' line %d\n", i,
trace_back[i].function_name, trace_back[i].object_name,
trace_back[i].line);
if (strcmp (trace_back[i].function_name, "heart_beat") == 0)
ret = trace_back[i].object_name;
}
return ret;
}
#else
void
dump_trace ()
{
}
#endif /* TRACE */
struct value *
get_one_expr (pp, from)
struct lnode **pp;
char *from;
{
struct value *v;
if (*pp == 0)
{
if (from == 0)
return 0;
error ("Wrong number of arguments to %s", from);
}
v = print_expr ((*pp)->a1);
*pp = (*pp)->a2;
return v;
}
static char *
find_percent (str)
char *str;
{
while (1)
{
str = strchr (str, '%');
if (str == 0)
return 0;
if (str[1] != '%')
return str;
str++;
}
}
static struct value *
inter_sscanf (l)
struct lnode *l;
{
struct value *p;
char *fmt; /* Format description */
char *in_string; /* The string o be parsed. */
int number_of_matches;
char *cp;
/*
* First get the string to be parsed.
*/
p = get_one_expr (&l, "sscanf");
if (p == 0 || p->type != T_STRING)
error ("Bad first argument to sscanf.");
in_string = p->u.string;
if (in_string == 0)
return &const0;
/*
* Now get the format description.
*/
p = get_one_expr (&l, "sscanf");
if (p == 0 || p->type != T_STRING)
error ("Bad second argument to sscanf.");
fmt = p->u.string;
/*
* First, skip and match leading text.
*/
for (cp = find_percent (fmt); fmt != cp; fmt++, in_string++)
{
if (in_string[0] == '\0' || fmt[0] != in_string[0])
return &const0;
}
/*
* For every % or substring in the format.
*/
for (number_of_matches = 0; l && l->a1; number_of_matches++, l = l->a2)
{
int i, type;
if (fmt[0] == '\0')
{
/*
* We have reached end of the format string.
* If there are any chars left in the in_string,
* then we put them in the last variable (if any).
*/
if (in_string[0])
{
assign (l->a1, make_string (in_string));
number_of_matches++;
}
break;
}
if (fmt[0] != '%')
fatal ("Should be a %% now !\n");
type = T_STRING;
if (fmt[1] == 'd')
type = T_NUMBER;
else if (fmt[1] != 's')
error ("Bad type : '%%%c' in sscanf fmt string.", fmt[1]);
fmt += 2;
/*
* Parsing a number is the easy case. Just use strtol() to
* find the end of the number.
*/
if (type == T_NUMBER)
{
char *tmp = in_string;
int tmp_num;
tmp_num = (int) strtol (in_string, &in_string, 0);
if (tmp == in_string)
{
/* No match */
break;
}
assign (l->a1, make_number (tmp_num));
while (fmt[0] && fmt[0] == in_string[0])
fmt++, in_string++;
if (fmt[0] != '%')
{
number_of_matches++;
break;
}
continue;
}
/*
* Now we have the string case.
*/
cp = find_percent (fmt);
if (cp == fmt)
error ("Illegal to have 2 adjacent %'s in fmt string in sscanf.");
if (cp == 0)
cp = fmt + strlen (fmt);
/*
* First case: There was no extra characters to match.
* Then this is the last match.
*/
if (cp == fmt)
{
assign (l->a1, make_string (in_string));
number_of_matches++;
break;
}
for (i = 0; in_string[i]; i++)
{
if (strncmp (in_string + i, fmt, cp - fmt) == 0)
{
char *match;
/*
* Found a match !
*/
match = xalloc (i + 1);
(void) strncpy (match, in_string, i);
in_string += i + cp - fmt;
match[i] = '\0';
assign (l->a1, make_string (match));
free (match);
fmt = cp; /* Advance fmt to next % */
break;
}
}
if (fmt == cp) /* If match, then do continue. */
continue;
/*
* No match was found. Then we stop here, and return
* the result so far !
*/
break;
}
return make_number (number_of_matches);
}
struct value *
users ()
{
struct object *ob;
extern int num_player; /* set by comm1.c */
int i;
struct value *ret;
ret = allocate_array (num_player);
for (i = 0; i < num_player; i++)
{
ret->u.vec->item[i].type = T_OBJECT;
ret->u.vec->item[i].u.ob = ob = get_interactive_object (i);
add_ref (ob, "users");
}
return ret;
}
static struct value *
explode_string (str, del)
char *str, *del;
{
char *p, *beg;
int num, len;
struct value *ret;
char *buff;
len = strlen (del);
if ((len == 0) || (strlen (str) == 0))
return &const0;
/*
* Skip leading 'del' strings, if any.
*/
while (strncmp (str, del, len) == 0)
{
str += len;
if (str[0] == '\0')
return &const0;
}
/*
* Find number of occurences of the delimiter 'del'.
*/
for (p = str, num = 0; *p;)
{
if (strncmp (p, del, len) == 0)
{
num++;
p += len;
}
else
p += 1;
}
/*
* Compute number of array items. It is either number of delimiters,
* or, one more.
*/
if (strlen (str) < len || strcmp (str + strlen (str) - len, del) != 0)
num++;
buff = xalloc (strlen (str) + 1);
ret = allocate_array (num);
for (p = str, beg = str, num = 0; *p;)
{
if (strncmp (p, del, len) == 0)
{
strncpy (buff, beg, p - beg);
buff[p - beg] = '\0';
if (num >= ret->u.vec->size)
fatal ("Too big index in explode !\n");
assign_svalue (&ret->u.vec->item[num], make_string (buff));
num++;
beg = p + len;
p = beg;
}
else
{
p += 1;
}
}
/* Copy last occurence, if there was not a 'del' at the end. */
if (*beg != '\0')
assign_svalue (&ret->u.vec->item[num], make_string (beg));
free (buff);
return ret;
}
struct value *
implode_string (arr, del)
struct vector *arr;
char *del;
{
int size, i, num;
char *p, *q;
struct value *ret;
for (i = 0, size = 0, num = 0; i < arr->size; i++)
{
if (arr->item[i].type == T_STRING)
{
size += strlen (arr->item[i].u.string);
num++;
}
}
if (num == 0)
return &const0;
p = xalloc (size + (num - 1) * strlen (del) + 1);
q = p;
p[0] = '\0';
for (i = 0, size = 0, num = 0; i < arr->size; i++)
{
if (arr->item[i].type == T_STRING)
{
if (num > 0)
{
strcpy (p, del);
p += strlen (del);
}
strcpy (p, arr->item[i].u.string);
p += strlen (arr->item[i].u.string);
num++;
}
}
ret = alloc_value ();
ret->type = T_STRING;
ret->u.string = q;
return ret;
}
struct value *
make_vector (p)
struct vector *p;
{
struct value *ret = alloc_value ();
ret->type = T_POINTER;
ret->u.vec = p;
ret->u.vec->ref++;
return ret;
}
struct value *
make_object (n)
struct object *n;
{
struct value *ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = n;
add_ref (ret->u.ob, "make_object()");
return ret;
}
struct vector *
create_vector (n)
int n;
{
struct vector *p;
int i;
if (n < 0 || n > MAX_ARRAY_SIZE)
error ("Illegal array size.\n");
p = ALLOC_VECTOR (n);
p->size = n;
p->ref = 1;
for (i = 0; i < n; i++)
{
p->item[i].type = T_NUMBER;
p->item[i].u.number = 0;
}
p->wl = current_object->wl;
if (p->wl)
p->wl->size_array += n;
return p;
}
int
index_array (a, s)
struct value *a;
struct value *s;
{
struct vector *arr;
char *str;
int i;
if (a->type == T_POINTER)
{
arr = a->u.vec;
for (i = 0; i < arr->size; i++)
{
if (arr->item[i].type == T_STRING && s->type == T_STRING)
if (!strcmp (arr->item[i].u.string, s->u.string))
return i;
/* should be ok with shared strings */
/*if (!strcmp(arr->item[i].u.string,s->u.string)) return i;*/
if (arr->item[i].type == T_NUMBER && s->type == T_NUMBER)
if (arr->item[i].u.number == s->u.number)
return i;
if (arr->item[i].type == T_POINTER && s->type == T_POINTER)
if (arr->item[i].u.vec == s->u.vec)
return i;
if (arr->item[i].type == T_OBJECT && s->type == T_OBJECT)
if (arr->item[i].u.ob == s->u.ob)
return i;
}
return -1;
}
else if (a->type == T_STRING)
{
if (s->type != T_NUMBER)
return -1;
str = a->u.string;
while (*str)
{
if (*str == s->u.number)
return (str - a->u.string);
str++;
}
return -1;
}
else
return -1;
}
struct value *
concatenate (a, b)
struct value *a;
struct value *b;
{
int az = 0;
struct vector *p;
struct value *ret;
if (a->type == T_POINTER)
az = a->u.vec->size;
else
az = 1;
if (b->type == T_POINTER)
az += b->u.vec->size;
else
az += 1;
ret = allocate_array (az);
p = ret->u.vec;
if (a->type != T_POINTER)
{
assign_svalue (&p->item[0], a);
az = 1;
}
else
{
copy_in_vector (p, a->u.vec, 0, 0, a->u.vec->size);
az = a->u.vec->size;
}
if (b->type != T_POINTER)
assign_svalue (&p->item[az], b);
else
copy_in_vector (p, b->u.vec, az, 0, b->u.vec->size);
return ret;
}
int
copy_in_vector (a, b, startto, startfrom, len)
struct vector *a, *b;
int startto, startfrom;
int len;
{
int i, s, x;
s = b->size - startfrom;
x = a->size - startto;
if (len > x)
len = x;
if (len > s)
len = s;
for (i = 0; i < len; i++)
{
(struct svalue *) copy_in_svalue (&a->item[i + startto],
&b->item[i + startfrom]);
}
}
struct svalue *
copy_in_svalue (a, b)
struct svalue *a, *b;
{
a->type = b->type;
switch (a->type)
{
case T_STRING :
a->u.string =
make_shared_string (b->u.string);
break;
case T_OBJECT :
if (a->u.ob->destructed)
{
a->type = T_NUMBER;
a->u.number = 0;
}
else
{
a->u.ob = b->u.ob;
add_ref (a->u.ob, "copy_in_svalue");
}
break;
case T_POINTER :
a->u.vec = b->u.vec;
a->u.vec->ref++;
break;
case T_NUMBER :
a->u.number = b->u.number;
break;
default :
a->type = T_NUMBER;
a->u.number = 0;
break;
}
return a;
}
struct value *
reallocate_array (a, n)
struct value *a;
int n;
{
int i, j;
struct value *v;
struct vector *p, *ap;
if (a->type != T_POINTER)
error ("Not an array pointer.\n");
p = create_vector (n);
j = ((a->u.vec->size) < n) ? (a->u.vec->size) : n;
ap = a->u.vec;
for (i = 0; i < n; i++)
{
if (i < j)
{
copy_in_svalue (&p->item[i], &ap->item[i]);
}
else
{
p->item[i].type = T_NUMBER;
p->item[i].u.number = 0;
}
}
return make_vector (p);
}