#include "os.h"
#include <setjmp.h>
#include "config.h"
#include "stdio.h"
#include "lnode.h"
#include "y.tab.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "wiz_list.h"
#include "security.h"
/* main.c */
extern int d_flag;
extern void *xalloc(int size);
extern char *string_copy(char *str);
extern void debug_message(char *a, ...);
extern void debug_message_value(struct value *v);
extern char *escape_path (char *str);
/* lexical.l */
extern int current_line;
extern char *current_file;
extern void start_new_file(FILE *f);
/* backend.c */
extern struct object *current_reset;
extern int error_recovery_context_exists;
extern jmp_buf error_recovery_context;
extern struct object *current_heart_beat;
extern void parse_command(char *str, struct object *ob);
/* comm1.c */
extern void set_snoop(struct object *me, struct object *you);
extern void remove_all_players(void);
extern void show_info_about(char *str, char *room, struct interactive *i);
extern int set_call(struct object *ob, struct sentence *sent);
extern void remove_interactive(struct object *ob);
extern void add_message(char *fmt, ...);
extern void ipc_remove(void);
/* lang.y */
extern struct lnode *prog, *heart_beat;
extern int variable_count;
extern int yyparse(void);
extern void yyerror(char *str);
/* ed.c */
extern void ed_start (char *file_arg);
/* interpret.c */
extern struct value *alloc_value(void);
extern int tot_alloc_value;
extern struct value *apply(char *fun, struct object *ob, struct value *arg);
#ifdef TRACE
extern char *dump_trace (void);
#endif
/* object.c */
extern void save_object(struct object *ob, char *file);
extern int restore_object(struct object *ob, char *file);
extern struct object *find_living_object(char *name);
extern struct object *find_player(char *name);
extern void tell_npc(struct object *ob, char *str);
extern void tell_object(struct object *ob, char *str);
extern int free_object(struct object *ob, char *from);
extern void add_ref(struct object *ob, char *from);
extern struct object *get_empty_object(void);
extern void dump_all_objects(void);
extern int tot_alloc_object;
/* lnode.c */
extern int tot_alloc_strings;
extern int tot_alloc_lnode;
/* swap.c */
extern int num_swapped;
extern int total_bytes_swapped;
/* string_space.c */
extern int tot_string_space;
#ifdef TRACE
extern int trace_depth;
#endif
struct object *obj_list;
struct object *obj_list_destruct;
struct object *current_object; /* The object interpreting a function. */
struct object *command_giver; /* Where the current command came from. */
int num_parse_error; /* Number of errors in the parser. */
int num_error = 0;
static struct sentence *sent_free = 0;
static int tot_alloc_sentence;
/* simulate.c */
struct value *find_value(struct lnode_variable *p);
struct lnode_var_def *find_status(char *str, int must_find);
struct object *load_object(char *name);
struct object *previous_ob;
struct value *clone_object(char *str1);
struct value *environment(struct value *arg);
void destruct2(struct object *ob);
struct value *call_indirect(int fun, ...);
void free_sentence(struct sentence *p);
void player_parser(char *buff);
void print_local_commands(void);
void fatal(char *fmt, ...);
void error(char *fmt, ...);
void smart_log(char *error_file, int line, char *what);
char *check_file_name(char *file, int writeflg);
void shutdowngame(int sig);
int transfer_object(struct object *ob, struct object *to);
struct object *find_object(char *str);
static void remove_file(char *path);
static struct value *call_other(struct value *obj, char *name, struct value *arg);
static struct value *this_player(void);
static struct value *this_object(void);
static struct value *call_local_function(char *name, struct value *arg);
static char *make_new_name(char *str);
static void command_for_object(char *str);
static struct value *object_present(struct value *v, struct object *ob);
static struct value *object_present2 (struct value *v, struct object *ob);
static void destruct_object(struct value *v);
static struct value *create_wizard(char *owner);
static void say(struct value *v, struct object *avoid_ob);
static void tell_room(struct object *room, struct value *v);
static void shout_string(char *str);
static struct value *first_inventory(struct value *arg);
static struct value *next_inventory(struct value *arg);
static void enable_commands(void);
static struct value *input_to(char *fun);
static void list_files(char *path);
static void print_file(char *path);
static void people(void);
static void log_file(char *file, char *str);
static void do_write(struct value *arg);
static void apply_command(char *com);
static void set_current_room(struct object *ob, struct object *dest);
static void move_object(struct object *item, struct object *dest);
static void add_light(struct object *p, int n);
static struct sentence *alloc_sentence(void);
static void add_action(char *str, struct value *id);
static void add_verb(char *str);
static void remove_sent(struct object *ob, struct object *player);
static void display_all_players(void);
static int special_parse(char *buff);
static void pre_compile(char *str);
static int legal_path(char *path);
static void move_or_destruct(struct object *what, struct object *to);
static struct object *find_object2 (char *str);
struct value *find_value (struct lnode_variable *p)
{
return ¤t_object->variables[p->number];
}
struct lnode_var_def *find_status (char *str, int must_find)
{
struct lnode_var_def *p;
for (p = current_object->status; p; p = p->next) {
if (strcmp (p->name, str) == 0)
return p;
}
if (!must_find)
return 0;
error ("--Status %s not found in prog for %s\n", str, current_object->name);
return 0;
}
static void remove_file (char *path)
{
path = check_file_name (path, 1);
if (path == 0)
return;
if (unlink (path) == -1)
add_message ("No such file: %s\n", path);
return;
}
struct object *load_object (char *name)
{
FILE *f;
/* Why is command giver saved and restored ? */
struct object *ob, *save_command_giver = command_giver;
struct stat i_st, c_st;
int i, name_length;
char *real_name;
/* Truncate possible .c in the object name. */
name_length = strlen (name);
name = string_copy (name);
if (name[name_length - 2] == '.' && name[name_length - 1] == 'c') {
name[name_length - 2] = '\0';
name_length -= 2;
}
/*
* First check that the c-file exists.
*/
real_name = (char *) xalloc (name_length + 3);
strcpy (real_name, name);
strcat (real_name, ".c");
escape_path (real_name);
if (stat (real_name, &c_st) == -1) {
free (name);
free (real_name);
error ("Could not load descr for %s\n", real_name);
return 0;
}
/*
* Now check if the i-file is newer or nonexisting.
*/
real_name[name_length + 1] = 'i';
if (stat (real_name, &i_st) == -1 || i_st.st_mtime < c_st.st_mtime) {
real_name[name_length + 1] = 'c';
pre_compile (name);
real_name[name_length + 1] = 'i';
}
if (stat (real_name, &i_st) == -1 || i_st.st_mtime < c_st.st_mtime) {
if (command_giver)
add_message ("Failed to compile the new file.\n");
}
f = fopen (real_name, "r");
if (f == 0) {
perror (real_name);
free (name);
free (real_name);
error ("Could not read the file.\n");
}
start_new_file (f);
current_file = string_copy (real_name);
current_line = 0;
prog = heart_beat = 0;
prog_status = 0;
variable_count = 0;
num_parse_error = 0;
(void) yyparse ();
(void) fclose (f);
if (num_parse_error > 0 || prog == 0) {
free (name);
free (real_name);
free (current_file);
if (prog) {
/*
* Set the reference count to one.
* We don't want to confuse free_prog().
*/
add_prog_ref ((struct lnode_def *) prog);
free_prog ((struct lnode_def *) prog);
}
/* The following unlinks are security patches - Drax */
if (num_parse_error == 0 && prog == 0) {
unlink (real_name);
error ("No program in object !\n");
}
unlink (real_name);
error ("Error in loading %s\n", real_name);
}
ob = get_empty_object ();
ob->name = string_copy (name);
ob->name_length = (int) strlen (name);
/* Is this object wizard defined ? */
{
char wiz_name[100];
if (sscanf (real_name, "players/%s", wiz_name) == 1) {
char *np;
np = strchr (wiz_name, '/');
if (np)
*np = '\0';
ob->wl = add_name (wiz_name);
} else {
ob->wl = 0;
}
}
/* Allocate space for local variables. */
if (variable_count > 0)
ob->variables =
(struct value *) xalloc (variable_count * sizeof (struct value));
ob->num_variables = variable_count;
/* Initialize variables... */
for (i = 0; i < variable_count; i++) {
ob->variables[i].u.number = 0;
ob->variables[i].type = T_NUMBER;
}
ob->status = prog_status;
if (prog) {
ob->prog = (struct lnode_def *) prog;
add_prog_ref ((struct lnode_def *) prog);
} else {
ob->prog = 0;
}
ob->next_all = obj_list;
obj_list = ob;
ob->heart_beat = heart_beat;
ob->reset = 1;
(void) apply ("reset", ob, 0);
command_giver = save_command_giver;
if (d_flag)
debug_message ("--%s loaded\n", ob->name);
free (real_name);
free (name);
return ob;
}
/*
* Call function in another object.
* Save the caller object in previous_ob, so some functions can
* see who called them.
*/
static struct value *call_other (struct value *obj, char *name, struct value *arg)
{
struct object *ob, *save_command_giver = command_giver,
*save_previous_ob = previous_ob;
struct value *ret;
if (obj->type == T_OBJECT)
ob = obj->u.ob;
else
ob = find_object (obj->u.string);
if (ob == 0)
error ("Could not find object %s\n", obj);
/*
* Test if this really is an existing object.
*/
if (ob->reset == 0)
return 0;
ob->not_touched = 0;
previous_ob = current_object;
ret = apply (name, ob, arg);
previous_ob = save_previous_ob;
command_giver = save_command_giver;
if (ret == 0)
return &const0;
return ret;
}
static struct value *this_player (void)
{
struct value *p = alloc_value ();
if (command_giver == 0)
return &const0;
p->type = T_OBJECT;
p->u.ob = command_giver;
add_ref (command_giver, "this_player()");
return p;
}
static struct value *this_object (void)
{
struct value *p = alloc_value ();
p->type = T_OBJECT;
p->u.ob = current_object;
add_ref (current_object, "this_object()");
return p;
}
static struct value *call_local_function (char *name, struct value *arg)
{
struct value *ret;
ret = apply (name, current_object, arg);
if (ret == 0)
error ("Local function %s not found.\n", name);
return ret;
}
static char *make_new_name (char *str)
{
static int i;
char *p = (char *) xalloc (strlen (str) + 10);
sprintf (p, "%s%d/", str, i);
i++;
return p;
}
struct value *clone_object (char *str1)
{
struct object *ob, *new_ob;
struct value *ret;
int i;
ob = find_object (str1);
if (!ob) {
if (strcmp (str1, "obj/sing") == 0)
fatal ("No singularity.\n");
ret = clone_object ("obj/sing");
if (!ret)
fatal ("No singularity.\n");
return ret;
}
if (ob->super || ob->cloned)
error ("Cloning a used object !\n");
ob->reset = 0;
if (ob == 0)
error ("Object %s not found\n", str1);
new_ob = get_empty_object ();
new_ob->name = make_new_name (ob->name);
new_ob->cloned = 1;
new_ob->prog = ob->prog;
if (new_ob->prog)
add_prog_ref ((struct lnode_def *) (new_ob->prog));
if (current_object && current_object->wl && !ob->wl)
new_ob->wl = current_object->wl;
else
new_ob->wl = ob->wl; /* Possibly a null pointer */
new_ob->status = ob->status;
new_ob->heart_beat = ob->heart_beat;
new_ob->next_all = obj_list;
obj_list = new_ob;
new_ob->num_variables = ob->num_variables;
/* Allocate space for local variables. */
if (ob->num_variables > 0)
new_ob->variables =
(struct value *) xalloc (ob->num_variables * sizeof (struct value));
/* Initialize variables... */
for (i = 0; i < ob->num_variables; i++) {
new_ob->variables[i].u.number = 0;
new_ob->variables[i].type = T_NUMBER;
}
new_ob->reset = 1;
(void) apply ("reset", new_ob, 0);
if (d_flag)
debug_message ("--%s cloned to %s\n", ob->name, new_ob->name);
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = new_ob;
add_ref (new_ob, "clone_object");
return ret;
}
struct value *environment (struct value *arg)
{
struct value *ret;
struct object *ob;
if (arg && arg->type == T_OBJECT)
ob = arg->u.ob;
else if (arg && arg->type == T_STRING)
ob = find_object2 (arg->u.string);
else
ob = current_object;
if (ob == 0 || ob->super == 0)
return &const0;
if (ob->destructed)
error ("environment() off destructed object.\n");
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob->super;
add_ref (ret->u.ob, "environment");
return ret;
}
/*
* Execute a command for an object. Copy the command into a
* new buffer, because 'parse_command()' can modify the command.
*/
static void command_for_object (char *str)
{
char buff[1000];
struct object *ob;
ob = current_object;
strncpy (buff, str, sizeof buff);
parse_command (buff, ob);
}
/*
* To find if an object is present, we have to look in two inventory
* lists. The first list is the inventory of the current object.
* The second list is all things that have the same ->super as
* current_object.
* Also test the environment.
* If the second argument 'ob' is non zero, only search in the
* inventory of 'ob'. The argument 'ob' will be mandatory, later.
*/
static struct value *object_present (struct value *v, struct object *ob)
{
struct value *ret;
int specific = 0;
if (ob == 0)
ob = current_object;
else
specific = 1;
if (v->type == T_OBJECT) {
if (specific) {
if (v->u.ob->super == ob)
return v;
else
return &const0;
}
if (v->u.ob->super == ob ||
(v->u.ob->super == ob->super && ob->super != 0))
return v;
return &const0;
}
ret = object_present2 (v, ob->contains);
if (ret)
return ret;
if (specific)
return &const0;
if (ob->super) {
ret = apply ("id", ob->super, v);
if (ret && !(ret->type == T_NUMBER && ret->u.number == 0)) {
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob->super;
add_ref (ret->u.ob, "present()");
return ret;
}
return object_present2 (v, ob->super->contains);
}
return &const0;
}
static struct value *object_present2 (struct value *v, struct object *ob)
{
struct value *ret;
char *p, *tmp;
int count = 0;
struct value item;
item.type = T_STRING;
item.u.string = string_copy (v->u.string);
p = item.u.string + strlen (item.u.string) - 1;
if (*p >= '0' && *p <= '9') {
while (p > item.u.string && *p >= '0' && *p <= '9')
p--;
if (p > item.u.string && *p == ' ') {
count = atoi (p + 1) - 1;
*p = '\0';
}
}
/* We need to put the cropped string into v->u.string to pass it to apply */
tmp = v->u.string;
v->u.string = item.u.string;
for (; ob; ob = ob->next_inv) {
ret = apply ("id", ob, v);
if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0))
continue;
if (count-- > 0)
continue;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob;
add_ref (ret->u.ob, "object_present2");
v->u.string = tmp;
free (item.u.string);
return ret;
}
v->u.string = tmp;
free (item.u.string);
return 0;
}
/*
* Remove an object. It is first moved into the destruct list, and
* not really destructed until later. (destruct2()).
*/
static void destruct_object (struct value *v)
{
struct object *ob, *super;
struct object **pp;
int removed;
struct value *weight, neg_weight;
if (v->type == T_OBJECT)
ob = v->u.ob;
else {
ob = find_object2 (v->u.string);
if (ob == 0)
error ("destruct_object: Could not find %s\n", v->u.string);
}
if (ob->destructed)
error ("Destruct destructed object.\n");
if (d_flag)
debug_message ("Destruct object %s (ref %d)\n", ob->name, ob->ref);
/*
* Now remove us out of the list of all objects.
*/
removed = 0;
for (pp = &obj_list; *pp; pp = &(*pp)->next_all) {
if (*pp != ob)
continue;
*pp = (*pp)->next_all;
removed = 1;
break;
}
if (!removed && command_giver)
add_message ("Failed to delete object.\n");
if (ob == current_reset)
current_reset = current_reset->next_all;
super = ob->super;
if (super == 0) {
super = find_object ("room/void"); /* Take any existing void. */
} else {
/* Call exit in current room, if player or npc */
if (ob->enable_commands) {
struct value room;
room.type = T_OBJECT;
room.u.ob = ob;
apply ("exit", super, &room);
}
weight = apply ("query_weight", ob, 0);
if (weight && weight->type == T_NUMBER) {
neg_weight.type = T_NUMBER;
neg_weight.u.number = -weight->u.number;
(void) apply ("add_weight", super, &neg_weight);
}
}
if (super == 0)
fatal ("Could not find the void.\n");
while (ob->contains)
move_or_destruct (ob->contains, super);
/*
* Remove us out of this current room (if any).
* Remove all sentences defined by this object from all objects here.
*/
if (ob->super) {
if (ob->super->enable_commands)
remove_sent (ob, ob->super);
add_light (ob->super, -ob->total_light);
for (pp = &ob->super->contains; *pp;) {
if ((*pp)->enable_commands)
remove_sent (ob, *pp);
if (*pp != ob)
pp = &(*pp)->next_inv;
else
*pp = (*pp)->next_inv;
}
}
ob->super = 0;
ob->next_inv = 0;
ob->heart_beat = 0;
ob->contains = 0;
ob->enable_commands = 0;
ob->next_all = obj_list_destruct;
obj_list_destruct = ob;
ob->destructed = 1;
}
/*
* This one is called when no program is execuiting.
* The super pointer is still maintained.
*/
void destruct2 (struct object *ob)
{
if (ob->interactive)
remove_interactive (ob);
/*
* We must deallocate variables here, not in 'free_object()'.
* That is because one of the local variables may point to this object,
* and deallocation of this pointer will also decrease the reference
* count of this object. Otherwise, an object with a variable pointing
* to itself, would never be freed.
* Just in case the program in this object would continue to
* execute, change string and object variables into the number 0.
*/
if (ob->variables) {
/*
* Deallocate variables in this object.
* The space of the variables are not deallocated until
* the object structure is freed in free_object().
*/
int i;
for (i = 0; i < ob->num_variables; i++) {
if (ob->variables[i].type == T_STRING) {
free (ob->variables[i].u.string);
} else if (ob->variables[i].type == T_OBJECT) {
free_object (ob->variables[i].u.ob, "destruct_object var");
}
ob->variables[i].type = T_NUMBER;
ob->variables[i].u.number = 0;
}
}
free_object (ob, "destruct_object");
}
static struct value *create_wizard (char *owner)
{
struct stat st;
char name[200], cmd[200];
FILE *f;
struct value *ret;
struct object *owner_obj;
owner_obj = find_player (owner);
if (owner_obj == 0)
fatal ("create_wizard: Could not find the player %s.\n", owner);
if (owner_obj->super == 0 || owner_obj->super->cloned) {
struct value v;
v.type = T_STRING;
v.u.string =
"There is a crash, the room collapses and the castle disappears.\n";
say (&v, 0);
return 0;
}
if (stat (PLAYER_DIR, &st) == -1)
#ifdef WIN32
if (mkdir (PLAYER_DIR) == -1)
#else
if (mkdir (PLAYER_DIR, 0777) == -1)
#endif
error ("Could not mkdir %s\n", PLAYER_DIR);
sprintf (name, "%s/%s", PLAYER_DIR, owner);
escape_path (name);
if (stat (name, &st) == 0)
error ("Player %s already has a castle!\n", owner);
#ifdef WIN32
if (mkdir (name) == -1) {
#else
if (mkdir (name, 0777) == -1) {
#endif
perror (name);
error ("Could not mkdir %s\n", name);
}
sprintf (name, "%s/%s/%s", PLAYER_DIR, owner, "castle.c");
escape_path (name);
f = fopen (name, "w");
if (f == NULL)
error ("Could not create a castle file %s!\n", name);
(void) fprintf (f, "#define NAME \"%s\"\n", owner);
(void) fprintf (f, "#define DEST \"%s\"\n", owner_obj->super->name);
(void) fclose (f);
(void) sprintf (cmd, "cat %s >> %s", DEFAULT_CASTLE, name);
(void) system (cmd);
f = fopen (INIT_FILE, "a");
if (f == NULL)
error ("Could not add the new castle to the %s\n", INIT_FILE);
(void) fprintf (f, "%s\n", name);
(void) fclose (f);
ret = alloc_value ();
ret->type = T_STRING;
ret->u.string = string_copy (name);
return ret;
}
/*
* A message from an object will reach
* all objects in the inventory,
* all objects in the same environment and
* the surrounding object.
* Non interactive objects gets no messages.
*
* There are two cases to take care of. If this routine is called from
* a player (indirectly), then the message goes to all in his
* environment. Otherwise, the message goes to all in the current_object's
* environment (as the case when called from a heart_beat()).
*
* If there is a second argument 'avoid_ob', then do not send the message
* to that object.
*/
static void say (struct value *v, struct object *avoid_ob)
{
struct object *ob, *save_command_giver = command_giver;
struct object *origin;
char buff[256];
if (current_object->enable_commands)
command_giver = current_object;
if (command_giver)
origin = command_giver;
else
origin = current_object;
switch (v->type) {
case T_STRING:
strncpy (buff, v->u.string, sizeof buff);
break;
case T_OBJECT:
strncpy (buff, v->u.ob->name, sizeof buff);
break;
case T_NUMBER:
sprintf (buff, "%d", v->u.number);
break;
default:
error ("Invalid argument %d to say()\n", v->type);
}
for (ob = origin->contains; ob; ob = ob->next_inv) {
struct object *save_again;
if (ob->interactive == 0) {
if (ob->enable_commands && ob != command_giver && ob != avoid_ob)
tell_npc (ob, buff);
continue;
}
if (ob == save_command_giver || ob == avoid_ob)
continue;
save_again = command_giver;
command_giver = ob;
add_message ("%s", buff);
command_giver = save_again;
}
if (origin->super) {
if (origin->super->interactive && origin != command_giver &&
origin->super != avoid_ob) {
command_giver = origin->super;
add_message ("%s", buff);
} else if (origin->super->interactive == 0 &&
origin->super != avoid_ob &&
origin->super->enable_commands && ob != command_giver) {
tell_npc (origin->super, buff);
}
for (ob = origin->super->contains; ob; ob = ob->next_inv) {
struct object *save_again;
if (ob == avoid_ob)
continue;
if (ob->interactive == 0) {
if (ob->enable_commands && ob != command_giver)
tell_npc (ob, buff);
continue;
}
if (ob == command_giver)
continue;
save_again = command_giver;
command_giver = ob;
add_message ("%s", buff);
command_giver = save_again;
}
}
command_giver = save_command_giver;
}
/*
* Send a message to all objects inside an object.
* Non interactive objects gets no messages.
* Compare with say().
*/
static void tell_room (struct object *room, struct value *v)
{
struct object *ob, *save_command_giver = command_giver;
char buff[256];
switch (v->type) {
case T_STRING:
strncpy (buff, v->u.string, sizeof buff);
break;
case T_OBJECT:
strncpy (buff, v->u.ob->name, sizeof buff);
break;
case T_NUMBER:
sprintf (buff, "%d", v->u.number);
break;
default:
error ("Invalid argument %d to tell_room()\n", v->type);
}
for (ob = room->contains; ob; ob = ob->next_inv) {
if (ob->interactive == 0) {
if (ob->enable_commands)
tell_npc (ob, buff);
continue;
}
command_giver = ob;
add_message ("%s", buff);
}
command_giver = save_command_giver;
}
static void shout_string (char *str)
{
struct object *ob, *save_command_giver = command_giver;
int emergency;
struct value *muffled;
emergency = (str[0] == '!');
if (emergency)
str++;
for (ob = obj_list; ob; ob = ob->next_all) {
if (ob->interactive == 0 || ob == save_command_giver)
continue;
muffled = apply ("query_muffled", ob, &const0);
if (!emergency && muffled->type == T_NUMBER && muffled->u.number)
continue;
command_giver = ob;
add_message ("%s", str);
}
command_giver = save_command_giver;
}
static struct value *first_inventory (struct value *arg)
{
struct object *ob;
struct value *ret;
if (arg->type == T_STRING)
ob = find_object (arg->u.string);
else
ob = arg->u.ob;
if (ob == 0)
error ("No object to first_inventory()");
if (ob->contains == 0)
return 0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob->contains;
add_ref (ret->u.ob, "first_inventory");
return ret;
}
static struct value *next_inventory (struct value *arg)
{
struct object *ob;
struct value *ret;
if (arg->type == T_STRING)
ob = find_object (arg->u.string);
else
ob = arg->u.ob;
if (ob == 0)
error ("No object to next_inventory()");
if (ob->next_inv == 0)
return 0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob->next_inv;
add_ref (ret->u.ob, "next_inventory");
return ret;
}
/*
* This will enable an object to use commands normally only
* accessible by interactive players.
*/
static void enable_commands (void)
{
current_object->enable_commands++;
command_giver = current_object;
}
/*
* Set up a function in this object to be called with the next
* user input string.
*/
static struct value *input_to (char *fun)
{
struct sentence *s = alloc_sentence ();
if (set_call (command_giver, s)) {
s->function = string_copy (fun);
s->ob = current_object;
return &const1;
}
free_sentence (s);
return &const0;
}
static void list_files (char *path)
{
char buff[1000];
FILE *f;
if (!path)
path = ".";
path = check_file_name (path, 0);
if (path == 0)
return;
escape_path (path);
sprintf (buff, "%s %s", LIST_FILES, path);
// f = vpopen(buff, "r");
#ifdef WIN32
f = _popen (buff, "r");
#else
f = popen (buff, "r");
#endif
if (f == 0)
return;
while (1) {
if (fgets (buff, sizeof buff, f) == 0)
break;
add_message ("%s", buff);
}
// vpclose(f);
#ifdef WIN32
_pclose (f);
#else
pclose (f);
#endif
}
static void print_file (char *path)
{
char buff[1000];
FILE *f;
path = check_file_name (path, 0);
if (path == 0)
return;
escape_path (path);
f = fopen (path, "r");
if (f == 0)
return;
while (1) {
if (fgets (buff, sizeof buff, f) == 0)
break;
add_message ("%s", buff);
}
fclose (f);
}
/* 5/10/90 - version to deal with invisibility */
static void people (void)
{
struct object *ob;
struct value *player, *invis, *level;
int level_of_user;
char name[30];
name[0] = 0;
level = apply ("query_level", command_giver, 0);
if (level->type == T_NUMBER)
level_of_user = level->u.number;
else
level_of_user = 0;
for (ob = obj_list; ob; ob = ob->next_all) {
if (!ob->interactive)
continue;
if (!ob->super)
continue;
player = apply ("query_real_name", ob, 0);
if (player == 0 || player->type != T_STRING)
continue;
player->u.string[0] -= 32;
invis = apply ("query_invis", ob, level);
if (invis->type != T_NUMBER)
continue;
if (!(invis->u.number))
strcpy (name, player->u.string);
if ((invis->u.number) < 0)
sprintf (name, "(%s) %d", player->u.string, -invis->u.number);
if ((invis->u.number) > 0)
strcpy (name, "Someone");
if ((invis->u.number) < 100)
show_info_about (name, ob->super->name, ob->interactive);
}
}
static void log_file (char *file, char *str)
{
FILE *f;
char file_name[100];
if (strchr (file, '/') || strchr (file, '\\') || file[0] == '.' || strlen (file) > 30)
error ("Illegal file name to log_file(%s)\n", file);
sprintf (file_name, "log/%s", file);
escape_path (file_name);
f = fopen (file_name, "a");
if (f == 0)
return;
fwrite (str, strlen (str), 1, f);
fclose (f);
}
/*VARARGS1*/
struct value *call_indirect (int fun, ...)
{
struct value *ret;
struct object *o1, *o2;
va_list args;
char *arg, *arg2, *arg3;
va_start (args, fun);
arg = va_arg (args, void *);
arg2 = va_arg (args, void *);
arg3 = va_arg (args, void *);
va_end (args);
switch (fun) {
case F_WIZLIST:
wizlist ();
break;
case F_FIND_OBJECT:
o1 = find_object2 ((char *) arg);
if (!o1)
return &const0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = o1;
add_ref (o1, "F_FIND_OBJECT");
return ret;
case F_SNOOP:
if (arg && ((struct object *) arg)->destructed)
error ("snoop() on destructed object.\n");
set_snoop (current_object, (struct object *) arg);
return 0;
case F_SET_HEART_BEAT:
current_object->enable_heart_beat = (int) arg;
return 0;
case F_LOG_FILE:
log_file ((char *) arg, (char *) arg2);
return 0;
case F_SHUTDOWN:
shutdowngame (0);
return 0;
case F_LIVING:
if (((struct object *) arg)->destructed)
return &const0;
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = ((struct object *) arg)->enable_commands;
return ret;
case F_ED:
if (arg && !legal_path ((char *) arg)) {
add_message ("Illegal path\n");
return 0;
}
ed_start ((char *) arg);
return 0;
case F_PEOPLE:
people ();
return 0;
case F_TELL_OBJECT:
if (((struct object *) arg)->destructed)
error ("Tell_object to destructed object./\n");
tell_object ((struct object *) arg, (char *) arg2);
return 0;
case F_FIND_LIVING:
o1 = find_living_object ((char *) arg);
if (o1 == 0)
return &const0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = o1;
add_ref (ret->u.ob, "find_living_object");
return ret;
case F_FIND_PLAYER:
o1 = find_player ((char *) arg);
if (o1 == 0)
return &const0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = o1;
add_ref (ret->u.ob, "find_player");
return ret;
case F_RM:
remove_file ((char *) arg);
return 0;
case F_LS:
list_files ((char *) arg);
return 0;
case F_CAT:
print_file ((char *) arg);
return 0;
case F_INPUT_TO:
if (current_object->destructed)
error ("input_to() on destructed object.\n");
return input_to ((char *) arg);
case F_ENABLE_COMMANDS:
enable_commands ();
return 0;
case F_FIRST_INVENTORY:
return first_inventory ((struct value *) arg);
case F_NEXT_INVENTORY:
return next_inventory ((struct value *) arg);
case F_SHOUT:
shout_string ((char *) arg);
return 0;
case F_SAY:
say ((struct value *) arg, (struct object *) arg2);
return 0;
case F_TELL_ROOM:
if (((struct object *) arg)->destructed)
error ("tell_room to destructed object.\n");
tell_room ((struct object *) arg, (struct value *) arg2);
return 0;
case F_CREATE_WIZARD:
return create_wizard ((char *) arg);
case F_DESTRUCT:
destruct_object ((struct value *) arg);
return 0;
case F_SET_LIGHT:
add_light (current_object, (int) arg);
ret = alloc_value ();
ret->type = T_NUMBER;
o1 = current_object;
while (o1->super)
o1 = o1->super;
ret->u.number = o1->total_light;
return ret;
case F_COMMAND:
command_for_object ((char *) arg);
return 0;
case F_PRESENT:
if (arg2 && ((struct object *) arg2)->destructed)
error ("present() on destructed object.\n");
return object_present ((struct value *) arg, (struct object *) arg2);
case F_ENVIRONMENT:
return environment ((struct value *) arg);
case F_SAVE_OBJECT:
save_object (current_object, (char *) arg);
return 0;
case F_RESTORE_OBJECT:
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = restore_object (current_object, (char *) arg);
return ret;
case F_CLONE_OBJECT:
return clone_object ((char *) arg);
case F_FUNCTION:
return call_local_function ((char *) arg, (struct value *) arg2);
case F_CALL_OTHER:
return call_other ((struct value *) arg, (char *) arg2,
(struct value *) arg3);
case F_WRITE:
do_write ((struct value *) arg);
break;
case F_MOVE_OBJECT:
if (((struct value *) arg)->type == T_OBJECT)
o1 = ((struct value *) arg)->u.ob;
else {
o1 = find_object (((struct value *) arg)->u.string);
if (o1 == 0)
error ("Object %s not found.\n", ((struct value *) arg)->u.string);
}
if (((struct value *) arg2)->type == T_OBJECT)
o2 = ((struct value *) arg2)->u.ob;
else {
o2 = find_object (((struct value *) arg2)->u.string);
if (o2 == 0)
error ("Object %s not found.\n", ((struct value *) arg2)->u.string);
}
if (((struct object *) o1)->destructed)
error ("move_object() of destructed object.\n");
if (((struct object *) o2)->destructed)
error ("move_object() to destructed object.\n");
move_object (o1, o2);
break;
case F_ADD_ACTION:
add_action ((char *) arg, (struct value *) arg2);
break;
case F_ADD_VERB:
add_verb ((char *) arg);
break;
case F_THIS_PLAYER:
return this_player ();
case F_THIS_OBJECT:
return this_object ();
default:
fatal ("Unimplemented hard linked function %d\n", fun);
WIN32CLEANUP
abort ();
}
return 0;
}
static void do_write (struct value *arg)
{
if (arg == 0)
add_message ("<NULL>");
else if (arg->type == T_STRING)
add_message ("%s", arg->u.string);
else if (arg->type == T_OBJECT)
add_message ("OBJ(%s)", arg->u.ob->name);
else if (arg->type == T_NUMBER)
add_message ("%d", arg->u.number);
else
add_message ("<UNKNOWN>");
}
/* Find an object. If not loaded, load it ! */
struct object *find_object (char *str)
{
struct object *ob;
ob = find_object2 (str);
if (ob)
return ob;
ob = load_object (str);
return ob;
}
/* Look for an loaded object. */
static struct object *find_object2 (char *str)
{
register struct object *ob;
register int length;
char *name;
/* Truncate possible .c in the object name. */
length = strlen (str);
name = string_copy (str);
if (name[length - 2] == '.' && name[length - 1] == 'c') {
name[length - 2] = '\0';
length -= 2;
}
for (ob = obj_list; ob; ob = ob->next_all) {
if (length == ob->name_length && strcmp (ob->name, name) == 0) {
free (name);
return ob;
}
}
free (name);
return 0;
}
static void apply_command (char *com)
{
struct value *ret;
if (command_giver == 0)
error ("command_giver == 0 !\n");
ret = apply (com, command_giver->super, 0);
if (ret != 0) {
add_message ("Result:");
if (ret->type == T_STRING)
add_message ("%s\n", ret->u.string);
if (ret->type == T_NUMBER)
add_message ("%d\n", ret->u.number);
} else {
add_message ("Error apply_command: function %s not found.\n", com);
}
}
static void set_current_room (struct object *ob, struct object *dest)
{
struct object **pp, *p;
struct object *save_command_giver = command_giver;
if (dest == 0)
dest = find_object ("room/void.c"); /* Get any existing void. */
if (dest == 0)
fatal ("Not even a void !\n");
if (ob->enable_commands)
command_giver = ob;
if (ob->super) {
if (!ob->super->destructed) {
struct value v;
v.type = T_OBJECT;
v.u.ob = ob; /* No need to increment ref count */
(void) apply ("exit", ob->super, &v);
add_light (ob->super, -ob->total_light);
}
remove_sent (ob->super, ob);
/*
* Now we link the ob out of its list.
* Remove sentences tied to objects that stays in this room.
*/
for (pp = &ob->super->contains; *pp;) {
if (*pp == ob)
*pp = (*pp)->next_inv;
else {
remove_sent (*pp, ob);
pp = &(*pp)->next_inv;
}
}
}
ob->next_inv = dest->contains;
dest->contains = ob;
add_light (dest, ob->total_light);
ob->super = dest;
if (d_flag)
debug_message ("--Current room: %s\n", dest->name);
(void) apply ("init", dest, 0);
for (p = dest->contains; p; p = p->next_inv) {
if (p == ob)
continue;
(void) apply ("init", p, 0);
}
command_giver = save_command_giver;
}
/*
* Transfer an object.
*/
static void move_object (struct object *item, struct object *dest)
{
struct object **pp, *ob;
struct object *save_cmd = command_giver;
/* Recursive moves are not allowed. */
for (ob = dest; ob; ob = ob->super)
if (ob == item)
return;
item->not_touched = 0;
dest->not_touched = 0;
if (item->enable_commands) {
set_current_room (item, dest);
return;
}
if (item->super) {
int okey = 0;
if (item->super->enable_commands)
remove_sent (item, item->super);
add_light (item->super, -item->total_light);
for (pp = &item->super->contains; *pp;) {
if (*pp != item) {
if ((*pp)->enable_commands)
remove_sent (item, *pp);
pp = &(*pp)->next_inv;
continue;
}
*pp = item->next_inv;
okey = 1;
}
if (!okey)
error ("Failed to find object %s in super list of %s.\n",
item->name, item->super->name);
}
item->next_inv = dest->contains;
dest->contains = item;
item->super = dest;
/*
* Run init of the item once for every present player, and
* for the environment (which can be a player).
*/
for (ob = dest->contains; ob; ob = ob->next_inv) {
if (ob->enable_commands) {
command_giver = ob;
(void) apply ("init", item, 0);
}
}
if (dest->enable_commands) {
command_giver = dest;
(void) apply ("init", item, 0);
}
command_giver = save_cmd;
add_light (dest, item->total_light);
if (d_flag)
debug_message ("--move_object: %s to %s\n", item->name, dest->name);
}
/*
* Every object as a count of number of light sources it contains.
* Update this.
*/
static void add_light (struct object *p, int n)
{
if (n == 0)
return;
p->total_light += n;
if (p->super)
add_light (p->super, n);
}
static struct sentence *alloc_sentence (void)
{
struct sentence *p;
if (sent_free == 0) {
p = (struct sentence *) xalloc (sizeof *p);
tot_alloc_sentence++;
} else {
p = sent_free;
sent_free = sent_free->next;
}
p->verb = 0;
p->function = 0;
p->next = 0;
return p;
}
void free_sentence (struct sentence *p)
{
if (p->function)
free (p->function);
p->function = 0;
if (p->verb)
free (p->verb);
p->verb = 0;
p->next = sent_free;
sent_free = p;
}
void player_parser (char *buff)
{
struct sentence *s;
char *p;
unsigned int length;
if (d_flag)
debug_message ("cmd [%s]: %s\n", command_giver->name, buff);
/* strip trailing spaces. */
for (p = buff + strlen (buff) - 1; p > buff; p--) {
if (*p != ' ')
break;
*p = '\0';
}
if (buff[0] == '\0')
return;
if (special_parse (buff))
return;
p = strchr (buff, ' ');
if (p == 0)
length = strlen (buff);
else
length = p - buff;
for (s = command_giver->sent; s; s = s->next) {
struct value *ret;
struct value arg;
if (s->verb == 0)
continue;
if (strlen (s->verb) != length)
continue;
if (strncmp (buff, s->verb, length) != 0)
continue;
/*
* Now we have found a special sentence !
*/
if (d_flag)
debug_message ("Local command %s on %s\n", s->function, s->ob->name);
/*
* If we have a second argument which was not used for id()
* verification, then we send it to the function.
*/
if (buff[length] == ' ') {
arg.type = T_STRING;
arg.u.string = &buff[length + 1];
ret = apply (s->function, s->ob, &arg);
} else {
ret = apply (s->function, s->ob, 0);
}
/* If we get fail from the call, it was wrong second argument. */
if (ret && ret->type == T_NUMBER && ret->u.number == 0)
continue;
if (s->ob->wl && command_giver->interactive)
s->ob->wl->score++;
if (ret == 0)
add_message ("Error: function %s not found.\n", s->function);
break;
}
if (s == 0)
add_message ("What?\n");
}
static void add_action (char *str, struct value *id)
{
struct sentence *p;
if (d_flag)
debug_message ("--Add action %s\n", str);
if (command_giver) {
p = alloc_sentence ();
p->function = string_copy (str);
p->ob = current_object;
p->next = command_giver->sent;
p->verb = 0;
command_giver->sent = p;
} else
yyerror ("Add_action called with no command_giver");
}
static void add_verb (char *str)
{
if (d_flag)
debug_message ("--Adding verb %s to action %s\n", str,
command_giver->sent->function);
if (command_giver) {
if (command_giver->sent == 0)
error ("No add_action().\n");
if (command_giver->sent->verb != 0)
error ("Tried to set verb again.\n");
command_giver->sent->verb = string_copy (str);
} else
yyerror ("Add_verb called with no command_giver");
}
static void remove_sent (struct object *ob, struct object *player)
{
struct sentence **s;
for (s = &player->sent; *s;) {
struct sentence *tmp;
if ((*s)->ob == ob) {
if (d_flag)
debug_message ("--Unlinking sentence %s\n", (*s)->function);
tmp = *s;
*s = tmp->next;
free_sentence (tmp);
} else
s = &((*s)->next);
}
}
static void display_all_players (void)
{
struct object *ob;
struct value *ret;
for (ob = obj_list; ob; ob = ob->next_all) {
if (ob->interactive == 0)
continue;
ret = apply ("who", ob, &const0);
if (ret && ret->type == T_STRING)
add_message ("%s.\n", ret->u.string);
}
}
static int special_parse (char *buff)
{
char *tmpbuff;
struct value *player;
if (buff[0] == '#')
return 0;
if (strcmp (buff, "who") == 0) {
display_all_players ();
return 1;
}
if (strcmp (buff, "dumpallobj") == 0) {
if (!command_giver)
return 0;
player = apply ("query_level", command_giver, 0);
if (player->type != T_NUMBER || player->u.number < ALL_POWER)
return 0;
dump_all_objects ();
return 1;
}
if (strcmp (buff, "status") == 0) {
add_message ("Sentences: %5d %6d\n", tot_alloc_sentence,
tot_alloc_sentence * sizeof (struct sentence));
add_message ("Objects: %5d %6d (%d swapped, %d Kbyte)\n",
tot_alloc_object,
tot_alloc_object * sizeof (struct object), num_swapped,
total_bytes_swapped / 1024);
add_message ("Values: %5d %6d\n\n", tot_alloc_value,
tot_alloc_value * sizeof (struct value));
add_message ("Strings: %6d\n", tot_alloc_strings);
print_lnode_status (tot_alloc_sentence * sizeof (struct sentence) +
tot_alloc_object * sizeof (struct object) +
tot_alloc_value * sizeof (struct value) + tot_alloc_strings);
add_message ("String space saved: %d bytes.\n", tot_string_space);
return 1;
}
/* This probably shouldn't be done this way, since buff is only 2000
bytes long, and will therefore crash if the input line is over
1996 characters long. */
if ((*buff == '\'') || (*buff == '"')) {
tmpbuff = string_copy (buff);
(void) strcpy (buff, "say ");
(void) strcpy (buff + 4, tmpbuff + 1);
free (tmpbuff);
return 0;
}
if (*buff == ':') {
tmpbuff = string_copy (buff);
(void) strcpy (buff, "emote ");
(void) strcpy (buff + 6, tmpbuff + 1);
free (tmpbuff);
return 0;
}
if (strcmp (buff, "e") == 0) {
(void) strcpy (buff, "east");
return 0;
}
if (strcmp (buff, "w") == 0) {
(void) strcpy (buff, "west");
return 0;
}
if (strcmp (buff, "s") == 0) {
(void) strcpy (buff, "south");
return 0;
}
if (strcmp (buff, "n") == 0) {
(void) strcpy (buff, "north");
return 0;
}
if (strcmp (buff, "ne") == 0) {
(void) strcpy (buff, "northeast");
return 0;
}
if (strcmp (buff, "nw") == 0) {
(void) strcpy (buff, "northwest");
return 0;
}
if (strcmp (buff, "se") == 0) {
(void) strcpy (buff, "southeast");
return 0;
}
if (strcmp (buff, "sw") == 0) {
(void) strcpy (buff, "southwest");
return 0;
}
if (strcmp (buff, "d") == 0) {
(void) strcpy (buff, "down");
return 0;
}
if (strcmp (buff, "u") == 0) {
(void) strcpy (buff, "up");
return 0;
}
if (strcmp (buff, "nw") == 0) {
(void) strcpy (buff, "northwest");
return 0;
}
if (strcmp (buff, "ne") == 0) {
(void) strcpy (buff, "northeast");
return 0;
}
if (strcmp (buff, "sw") == 0) {
(void) strcpy (buff, "southwest");
return 0;
}
if (strcmp (buff, "se") == 0) {
(void) strcpy (buff, "southeast");
return 0;
}
return 0;
}
void print_local_commands (void)
{
struct sentence *s;
add_message ("Current local commands:\n");
for (s = command_giver->sent; s; s = s->next)
add_message ("%s ", s->verb);
add_message ("\n");
}
/*VARARGS1*/
void fatal (char *fmt, ...)
{
va_list args;
va_start (args, fmt);
vfprintf (stderr, fmt, args);
va_end (args);
(void) fprintf (stderr, "Current object was %s\n", current_object->name);
va_start (args, fmt);
debug_message (fmt, args);
va_end (args);
debug_message ("Current object was %s\n", current_object->name);
debug_message ("Dump of variables:\n");
if (current_object) {
struct lnode_var_def *p;
for (p = current_object->status; p; p = p->next) {
debug_message ("%20s: ", p->name);
debug_message_value (¤t_object->variables[p->num_var]);
debug_message ("\n");
}
}
#ifdef TRACE
(void) dump_trace ();
#endif
ipc_remove (); /* Shut down the ipc communication. */
WIN32CLEANUP
abort ();
}
/*VARARGS1*/
void error (char *fmt, ...)
{
va_list args;
struct object *dest;
#ifdef TRACE
char *object_name;
#endif
num_error++;
if (num_error > 2)
fatal ("Too many simultaneous errors.\n");
va_start (args, fmt);
debug_message (fmt, args);
va_end (args);
if (current_object)
debug_message ("Current object was %s, line %d\n", current_object->name, current_line);
#ifdef TRACE
object_name = dump_trace ();
if (object_name) {
struct object *ob;
ob = find_object2 (object_name);
if (!ob) {
if (command_giver)
add_message ("error when executing program in destroyed object %s\n",
object_name);
debug_message ("error when executing program in destroyed object %s\n",
object_name);
}
}
trace_depth = 0;
#endif
if (command_giver) {
va_start (args, fmt);
add_message (fmt, args);
va_end (args);
if (current_object)
add_message ("Current object was %s, line %d\n",
current_object->name, current_line);
/*
* If this is a player, send him to the church. Otherwise, send it to
* the void. Also make sure we have a church!
*/
if (!(command_giver->enable_commands &&
apply ("is_player", command_giver, &const0) &&
(dest = find_object ("room/church.c"))))
dest = find_object ("room/void.c");
if (dest == 0)
fatal ("Could not find the void room.\n");
move_object (command_giver, dest);
}
if (current_heart_beat) {
current_heart_beat->heart_beat = 0;
debug_message ("Heart beat in %s turned off.\n",
current_heart_beat->name);
current_heart_beat = 0;
}
debug_message ("Dump of variables:\n");
if (current_object) {
struct lnode_var_def *p;
for (p = current_object->status; p; p = p->next) {
debug_message ("%20s: ", p->name);
debug_message_value (¤t_object->variables[p->num_var]);
debug_message ("\n");
}
}
num_error--;
if (error_recovery_context_exists)
longjmp (error_recovery_context, 1);
WIN32CLEANUP
abort ();
}
#ifdef TRACE
char *get_current_object_name (void)
{
if (current_object == 0)
return "NONE";
return current_object->name;
}
char *get_command_giver_name (void)
{
if (command_giver == 0)
return "NONE";
return command_giver->name;
}
#endif
static void pre_compile (char *str)
{
char *c_name, *i_name, buff[1000];
FILE *f;
if (!legal_path (str))
error ("Illegal attempt to access %s\n", str);
i_name = (char *) xalloc (strlen (str) + 3);
strcpy (i_name, str);
strcat (i_name, ".i");
c_name = (char *) xalloc (strlen (str) + 3);
strcpy (c_name, str);
strcat (c_name, ".c");
escape_path (i_name);
escape_path (c_name);
#if defined _MSC_VER
sprintf (buff, "%s %s > %s", PRE_COMPILE, c_name, i_name);
#elif defined __BORLANDC__
sprintf (buff, "%s -o%s %s", PRE_COMPILE, i_name, c_name);
#else
sprintf (buff, "%s %s -o %s", PRE_COMPILE, c_name, i_name);
#endif
// f = (FILE *)vpopen(buff, "r");
#ifdef WIN32
f = _popen (buff, "r");
#else
f = (FILE *) popen (buff, "r");
#endif
if (f == 0) {
error ("Unable to invoke precompiler!\n");
WIN32CLEANUP
abort ();
}
while (1) {
if (fgets (buff, sizeof buff, f) == 0)
break;
add_message ("%s", buff);
}
// vpclose(f);
#ifdef WIN32
_pclose (f);
#else
pclose (f);
#endif
}
/*
* Check that it is an legal path. It must not contain a space
* or '..' or begin with '/' or '\'.
*/
static int legal_path (char *path)
{
char *p;
if (path == NULL || strchr (path, ' '))
return 0;
if (path[0] == '\\' || path[0] == '/')
return 0;
for (p = strchr (path, '.'); p; p = strchr (p + 1, '.')) {
if (p[1] == '.')
return 0;
}
return 1;
}
void smart_log (char *error_file, int line, char *what)
{
char buff2[100], buff[100], *p;
int n;
if (error_file == 0)
return;
#ifdef WIN32
n = sscanf (error_file, "players\\%s", buff2);
#else
n = sscanf (error_file, "players/%s", buff2);
#endif
if (n != 1)
return;
#ifdef WIN32
p = strchr (buff2, '\\');
#else
p = strchr (buff2, '/');
#endif
if (p)
*p = '\0';
sprintf (buff, "%s line %d:%s\n", error_file, line, what);
log_file (buff2, buff);
}
/*
* Check that a file name is valid for read or write.
* Also change the name as if the current directory was at the players
* own directory.
* This is done by functions in the player object.
*/
char *check_file_name (char *file, int writeflg)
{
struct value v, *ret;
if (!command_giver) {
yyerror ("Check_file_name called with no command_giver");
return 0;
}
v.type = T_STRING;
v.u.string = file;
/*
* We don't have to free the string in ret. This is done
* by the garbage collection.
*/
if (writeflg)
ret = apply ("valid_write", command_giver, &v);
else
ret = apply ("valid_read", command_giver, &v);
if (!ret || ret->type != T_STRING) {
add_message ("Bad file name.\n");
return 0;
}
if (!legal_path (ret->u.string)) {
add_message ("Illegal path\n");
return 0;
}
return ret->u.string;
}
/*
* This one is called from HUP, and from the command "shutdown".
*/
void shutdowngame (int sig)
{
struct value *player;
if (command_giver) {
player = apply ("query_level", command_giver, 0);
if (player->type != T_NUMBER || player->u.number < SHUTDOWN)
return;
}
shout_string ("LPmud shutting down immediately.\n");
fprintf (stderr, "simulate.c: shutdowngame: LP-mud shut down.\n");
remove_all_players ();
ipc_remove ();
save_wiz_file ();
WIN32CLEANUP
exit (0);
}
/*
* Transfer an object from an object to an object.
* Call add_weight(), drop(), get(), prevent_insert(), add_weight(),
* and can_put_and_get() where needed.
* Return 0 on success, and special code on failure:
*
* 1: Too heavy for destination.
* 2: Can't be dropped.
* 3: Can't take it out of it's container.
* 4: The object can't be inserted into bags etc.
* 5: The destination doesn't allow insertions of objects.
* 6: The object can't be picked up.
*/
int transfer_object (struct object *ob, struct object *to)
{
struct value *weight, neg_weight, *ret;
struct object *from = ob->super;
/*
* Get the weight of the object
*/
weight = apply ("query_weight", ob, 0);
if (weight && weight->type != T_NUMBER)
error ("Bad type of weight of object in transfer()\n");
/*
* If the original place of the object is a living object,
* then we must call drop() to check that the object can be dropped.
*/
if (from && from->enable_commands) {
ret = apply ("drop", ob, 0);
if (ret && (ret->type != T_NUMBER || ret->u.number != 0))
return 2;
}
/*
* If 'from' is not a room and not a player, check that we may
* remove things out of it.
*/
if (from && from->super && !from->enable_commands) {
ret = apply ("can_put_and_get", from, 0);
if (!ret || (ret->type != T_NUMBER && ret->u.number != 1))
return 3;
}
/*
* If the destination is not a room, and not a player,
* Then we must test 'prevent_insert', and 'can_put_and_get'.
*/
if (to->super && to->enable_commands == 0) {
ret = apply ("prevent_insert", ob, 0);
if (ret && (ret->type != T_NUMBER || ret->u.number != 0))
return 4;
ret = apply ("can_put_and_get", to, 0);
if (!ret || (ret->type != T_NUMBER && ret->type != 0))
return 5;
}
/*
* If the destination is a player, check that he can pick it up.
*/
if (to->enable_commands) {
ret = apply ("get", ob, 0);
if (!ret || (ret->type == T_NUMBER && ret->u.number == 0))
return 6;
}
/*
* If it is not a room, correct the total weight in the destination.
*/
if (to->super && weight) {
/*
* Check if the destination can carry that much.
*/
ret = apply ("add_weight", to, weight);
if (ret && ret->type == T_NUMBER && ret->u.number == 0)
return 1;
}
/*
* If it is not a room, correct the weight in the 'from' object.
*/
if (from && from->super && weight) {
neg_weight.type = T_NUMBER;
neg_weight.u.number = -weight->u.number;
(void) apply ("add_weight", from, &neg_weight);
}
move_object (ob, to);
return 0;
}
/*
* Move or destruct one object.
*/
static void move_or_destruct (struct object *what, struct object *to)
{
int res;
struct value v;
res = transfer_object (what, to);
if (res == 0)
return;
if (res == 1 || res == 4 || res == 5) {
move_or_destruct (what, to->super);
return;
}
/*
* No need to add the reference count of 'what', as this
* local 'v' is not deallocated by 'free_all_value()'
*/
v.type = T_OBJECT;
v.u.ob = what;
destruct_object (&v);
}