#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#if defined(__386BSD__) || defined(SunOS_5)
#include <unistd.h>
#endif
#if defined(__STDC__) && !defined(LATTICE)
#include <memory.h>
#endif
#include "lint.h"
#include "interpret.h"
#include "mapping.h"
#include "buffer.h"
#include "object.h"
#include "sent.h"
#include "exec.h"
#include "debug.h"
extern int d_flag;
extern int total_num_prog_blocks, total_prog_block_size;
extern char *xalloc PROT((int)), *string_copy PROT((char *));
void remove_swap_file PROT((struct object *));
extern int atoi();
void remove_all_call_out PROT((struct object *obj));
struct object *previous_ob;
extern struct svalue const0, const0n;
int tot_alloc_object, tot_alloc_object_size;
int save_svalue_depth = 0;
static char *save_array PROT((struct vector *v));
char *save_mapping PROT ((struct mapping *m));
static int vector_save_size PROT((struct vector *));
static struct mapping *restore_mapping PROT((char **));
INLINE int
valid_hide(obj)
struct object *obj;
{
struct svalue *ret;
if (!obj) {
return 0;
}
push_object(obj);
ret = apply_master_ob("valid_hide",1);
return (!IS_ZERO(ret));
}
/*
* Replace newlines in a string with a carriage return, to make the string
* writeable on one line.
*/
static void replace_newline(str)
char *str;
{
for (; *str; str++) {
if (str[0] == '\n')
#ifndef MSDOS
str[0] = '\r';
#else
str[0] = 30;
#endif
}
}
/*
* Replace carriage return in a string with newlines.
*/
static void restore_newline(str)
char *str;
{
for (; *str; str++) {
#ifndef MSDOS
if (str[0] == '\r')
#else
if (str[0] == 30)
#endif
str[0] = '\n';
}
}
static int my_strlen(str)
char *str;
{
int sz = 0;
while (*str) {
sz++;
if (*str == '\"' || *str == '\\') sz++;
str++;
}
return sz;
}
/*
* Does a strcpy, but also advances the buffer to point to the
* end of the string so extra searches are unnecessary for later
* catenation.
*/
void bufcat(buf, str)
char **buf;
char *str;
{
while (*str)
{
**buf = *str++;
(*buf)++;
}
**buf = 0;
}
/*
* Similar to strcat(), but escapes all funny characters.
* Used by save_object().
* src is modified temporarily, but restored again :-(
*/
static void my_strcat(dest,src)
char **dest,*src;
{
char *pt,*pt2,ch[2];
pt = strchr(src,'\\'); ch[1] = 0;
pt2 = strchr(src,'\"');
if ((pt2 && pt2 < pt) || (pt == 0))
pt = pt2;
while (pt) {
ch[0] = *pt; *pt = 0;
bufcat(dest,src); bufcat(dest,"\\"); bufcat(dest,ch);
src = pt+1; *pt = ch[0];
pt = strchr(src,'\\');
pt2 = strchr(src,'\"');
if ((pt2 && pt2 < pt) || (pt == 0))
pt = pt2;
}
bufcat(dest,src);
}
int svalue_save_size(v)
struct svalue *v;
{
char numbuf[SMALL_STRING_SIZE];
int mapping_save_size PROT((struct mapping *));
int size;
if (v->type == T_STRING)
return my_strlen(v->u.string) + 3; /* my_ */
if (v->type == T_POINTER) {
save_svalue_depth++;
if (save_svalue_depth > MAX_SAVE_SVALUE_DEPTH) {
return 0;
}
size = vector_save_size(v->u.vec);
save_svalue_depth--;
return 2 + size + 2 + 1;
}
if (v->type == T_MAPPING) {
save_svalue_depth++;
if (save_svalue_depth > MAX_SAVE_SVALUE_DEPTH) {
return 0;
}
size = mapping_save_size(v->u.map);
save_svalue_depth--;
return size + 5;
}
if (v->type == T_NUMBER) {
sprintf(numbuf, "%d", v->u.number);
return strlen(numbuf) + 1;
}
if (v->type == T_REAL) {
sprintf(numbuf, "%f", v->u.real);
return strlen(numbuf) + 1;
}
return 2;
}
static int vector_save_size(v)
struct vector *v;
{
int i, siz;
for (i=0, siz = 0; i < v->size; i++)
siz += svalue_save_size(&v->item[i]);
return siz;
}
void save_svalue(v, buf)
struct svalue *v;
char **buf;
{
char *tbuf;
if (v->type == T_STRING) {
bufcat(buf, "\""); my_strcat(buf, v->u.string); /* my_ */
bufcat(buf, "\"");
} else if (v->type == T_POINTER) {
tbuf = save_array(v->u.vec);
bufcat(buf, tbuf); FREE(tbuf);
} else if (v->type == T_NUMBER) {
sprintf(*buf, "%d", v->u.number);
*buf += strlen(*buf);
} else if (v->type == T_REAL) {
sprintf(*buf, "%f", v->u.real);
*buf += strlen(*buf);
} else if (v->type == T_MAPPING) {
tbuf = save_mapping(v->u.map);
bufcat(buf,tbuf); FREE(tbuf);
}
}
/*
* Encode an array of elements into a contiguous string.
*/
static char *
save_array(v)
struct vector *v;
{
char *buf, *p;
int i;
buf = DXALLOC(2+vector_save_size(v)+2+1, 79, "save_array");
p = buf;
bufcat(&p, "({");
for (i=0; i < v->size; i++) {
save_svalue(&v->item[i], &p);
bufcat(&p, ",");
}
bufcat(&p,"})");
return buf;
}
static char *
my_string_copy(str)
char *str;
{
char *apa, *cp;
cp = apa = DXALLOC(strlen(str)+1, 82, "my_string_copy");
while (*str) {
if (*str == '\\') {
*cp = str[1];
if (str[1]) {
str+=2;
} else {
str++; /* String ends with a \\ buggy probably */
}
cp++;
} else {
*cp = *str; cp++; str++;
}
}
*cp = 0;
cp = string_copy(apa);
FREE(apa);
return cp;
}
/*
* Find the size of an array or mapping. Return -1 for failure.
*/
static int restore_size(str, is_mapping)
char **str;
int is_mapping;
{
char *pt, *pt2;
int siz,tsiz;
pt = *str;
if (strncmp(pt, is_mapping ? "([" : "({", 2)) return -1;
else pt += 2;
siz = 0;
while ((pt) && (*pt)) {
if (pt[0] == '}') {
if (pt[1] != ')') return -1;
if (is_mapping) return -1;
*str = &pt[2];
return siz;
}
if (pt[0] == ']') {
if (pt[1] != ')') return -1;
if (!is_mapping) return -1;
*str = &pt[2];
return siz;
}
if (pt[0] == '\"') {
for (pt2 = &pt[1]; *pt2 && (*pt2 != '\"'); pt2++) {
if (*pt2 == '\\') {
pt2++;
}
}
if (!*pt2) {
return -1;
}
pt2--;
if (pt2[2] != ',' && pt2[2] != ':') return -1;
siz++;
pt = &pt2[3];
}
else if (pt[0] == '(') {
tsiz = restore_size(&pt, pt[1] == '[');
/* Lazy way of doing it, a bit inefficient */
if (tsiz < 0)
return -1;
pt++;
siz++;
}
else {
if (is_mapping && !(siz % 2))
pt2 = strchr(pt, ':');
else
pt2 = strchr(pt, ',');
if (!pt2)
return -1;
siz++;
pt = &pt2[1];
}
}
return -1;
}
static struct vector *
restore_array(str, is_mapping)
char **str;
int is_mapping;
{
struct vector *v,*t;
char *pt,*pt2;
char delim;
int i,siz;
pt = *str;
if (is_mapping && strncmp(pt, "([", 2)) return 0;
else if (!is_mapping && strncmp(pt, "({", 2)) return 0;
pt2 = pt;
siz = restore_size(&pt2,is_mapping);
if (siz < 0) return 0;
v = allocate_array(siz);
pt+=2;
for (i=0;i<siz;i++) {
if (!*pt) return v;
if (is_mapping && !(i % 2)) delim = ':';
else delim = ',';
if (pt[0] == '\"') {
for (pt2 = &pt[1]; *pt2 && (*pt2 != '\"'); pt2++) {
if (*pt2 == '\\') {
pt2++;
}
}
if (!*pt2) {
return v;
}
pt2--;
if (pt2[2] != delim) return v;
pt2[1] = 0;
v->item[i].type = T_STRING;
v->item[i].u.string = my_string_copy(pt+1); /* my_ */
v->item[i].subtype = STRING_MALLOC;
pt = &pt2[3];
}
else if (pt[0] == '(' && pt[1] == '{') {
t = restore_array(&pt, 0);
if (!t) return v;
v->item[i].type = T_POINTER;
v->item[i].u.vec = t;
/* v->item[i].u.vec->ref++; marion - ref is already 1 (allocate_array) */
pt++;
} else if (pt[0] == '(' && pt[1] == '[') {
struct mapping *m;
m = restore_mapping(&pt);
if (!m) return v;
v->item[i].type = T_MAPPING;
v->item[i].u.map = m;
pt++;
}
else {
pt2 = strchr(pt, delim);
if (!pt2) return v;
pt2[0] = 0;
if (strchr(pt, '.'))
{
v->item[i].type = T_REAL;
sscanf(pt, "%f", &(v->item[i].u.real));
} else
{
v->item[i].type = T_NUMBER;
sscanf(pt,"%d",&(v->item[i].u.number));
}
pt = &pt2[1];
}
}
if (is_mapping && strncmp(pt, "])", 2)) return v;
if (!is_mapping && strncmp(pt, "})", 2)) return v;
*str = &pt[2];
return v;
}
static struct mapping
*restore_mapping(str)
char **str;
{
struct vector *v;
struct mapping *newmap;
/* We take the easy way out. */
v = restore_array(str, 1);
if (!v) return (struct mapping *) 0;
newmap = load_mapping_from_aggregate(&v->item[0], v->size);
free_vector(v);
return newmap;
}
int
restore_svalue(val, v)
char *val;
struct svalue *v;
{
if (val[0] == '\"') {
restore_newline(val+1);
free_svalue(v);
v->type = T_STRING;
val[strlen(val) - 1] = '\0'; /* don't copy the ending quote (") */
v->u.string = my_string_copy(val+1);
v->subtype = STRING_MALLOC;
return 0;
}
/* Restore array: JnA 910520
*/
if (val[0] == '(' && val[1] == '{') {
char *pt = val;
restore_newline(val+1);
free_svalue(v);
v->type = T_POINTER;
v->u.vec = restore_array(&pt, 0);
if (!v->u.vec) {
*v = const0n;
return -1;
}
return 0;
}
/* Restore mapping: JTR 01/24/92
*/
if (val[0] == '(' && val[1] == '[') {
char *pt = val;
restore_newline(val + 1);
free_svalue(v);
v->type = T_MAPPING;
v->u.map = restore_mapping(&pt);
if (!v->u.map) {
*v = const0n;
return -2;
}
return 0;
}
/* Restore real: blackthorn 2/24/93
*/
if (strchr(val, '.'))
{
free_svalue(v);
v->type = T_REAL;
if (sscanf(val, "%f", &(v->u.real)) == 1)
return 0;
}
free_svalue(v);
v->type = T_NUMBER;
v->u.number = atoi(val);
return 0;
}
void
restore_object_from_buff(ob, theBuff, name, val)
struct object *ob;
char *theBuff, *name, *val;
{
char *buff, *nextBuff, *tmp, *theEnd, *space;
char var[100];
struct variable *p;
int rc;
theEnd = theBuff + strlen(theBuff);
nextBuff = theBuff;
while (1) {
struct svalue *v;
buff = nextBuff;
if (!buff || (buff == theEnd))
break;
if ((tmp = strchr(buff, '\n'))) {
*tmp = '\0';
nextBuff = tmp + 1;
} else {
nextBuff = 0;
}
if (buff[0] == '#') /* ignore 'comments' in savefiles */
continue;
space = strchr(buff, ' ');
if ((space == 0) || ((space - buff) >= sizeof(var))) {
FREE(val);
FREE(name);
FREE(theBuff);
error("restore_object(): Illegal file format.\n");
}
(void)strncpy(var, buff, space - buff);
var[space - buff] = '\0';
(void)strcpy(val, space+1);
p = find_status(var, 0);
if (p == 0 || (p->type & TYPE_MOD_STATIC))
continue;
v = &ob->variables[p - ob->prog->p.i.variable_names];
rc = restore_svalue(val, v);
if (rc < 0) {
FREE(val);
FREE(name);
FREE(theBuff);
if (rc == -1)
error("restore_object(): Illegal array format.\n");
else if (rc == -2)
error("restore_object(): Illegal mapping format.\n");
}
}
}
/*
* Save an object to a file.
* The routine checks with the function "valid_write()" in /obj/master.c
* to assertain that the write is legal.
* If 'save_zeros' is set, 0 valued variables will be saved
*/
int
save_object(ob, file, save_zeros)
struct object *ob;
char *file;
int save_zeros;
{
char *name;
static char tmp_name[80];
int len, i;
FILE *f;
int failed = 0;
char *use_name;
int free_use_name = 0;
/* struct svalue *v; */
if (ob->flags & O_DESTRUCTED)
return 0;
if (file[0] != '/')
{
use_name = DXALLOC(strlen(file) + 2, 80, "save_object: 1");
strcpy(use_name, "/");
strcat(use_name, file);
free_use_name = 1;
} else
use_name = file;
file = check_valid_path(use_name, ob, "save_object", 1);
if (file == 0)
error("Denied write permission in save_object().\n");
len = strlen(file);
name = DXALLOC(len + strlen(SAVE_EXTENSION) + 1, 80, "save_object: 1");
(void)strcpy(name, file);
if (free_use_name)
FREE(use_name);
#ifndef MSDOS
(void)strcat(name, SAVE_EXTENSION);
#endif
/*
* Write the save-files to different directories, just in case
* they are on different file systems.
*/
sprintf(tmp_name, "%s.tmp", name);
#ifdef MSDOS
(void)strcat(name, SAVE_EXTENSION);
#endif
f = fopen(tmp_name, "w");
if (f == 0) {
FREE(name);
error("Could not open %s for a save.\n", tmp_name);
}
fprintf(f, "#%s\n", ob->prog->name);
for (i=0; i < (int)ob->prog->p.i.num_variables; i++) {
struct svalue *v = &ob->variables[i];
char *new_string, *p;
int theSize;
if (ob->prog->p.i.variable_names[i].type & TYPE_MOD_STATIC)
continue;
save_svalue_depth = 0;
theSize = svalue_save_size(v);
if (save_svalue_depth > MAX_SAVE_SVALUE_DEPTH) {
error("Mappings and/or arrays nested too deep (%d) for save_object\n",
MAX_SAVE_SVALUE_DEPTH);
}
new_string = (char *)DXALLOC(theSize, 81, "save_object: 2");
*new_string = '\0';
p = new_string;
save_svalue(v, &p);
replace_newline(new_string);
if (save_zeros || strcmp(new_string,"0")) /* Armidale */
if (fprintf(f, "%s %s\n", ob->prog->p.i.variable_names[i].name,
new_string) == EOF) {
failed = 1;
}
FREE(new_string);
}
if (failed) {
add_message("Failed to completely save file. Disk could be full.\n");
} else {
(void) fclose(f);
if (rename(tmp_name, name) < 0)
{
perror(name);
printf("Failed to rename %s to %s\n", tmp_name, name);
add_message("Failed to save object!\n");
}
}
FREE(name);
if (failed) {
add_message("Failed to save to file. Disk could be full.\n");
return 0;
}
return 1;
}
int restore_object(ob, file, noclear)
struct object *ob;
char *file;
int noclear;
{
char *name, *val, *theBuff;
int len, num_var, i;
FILE *f;
struct object *save = current_object;
struct stat st;
if (ob->flags & O_DESTRUCTED)
return 0;
file = check_valid_path(file, ob, "restore_object: 1", 0);
if (file == 0)
error("Denied read permission in restore_object().\n");
len = strlen(file);
name = DXALLOC(len + strlen(SAVE_EXTENSION) + 1, 83, "restore_object: 2");
(void)strcpy(name, file);
if (name[len-2] == '.' && name[len-1] == 'c')
name[len-2] = 0;
(void)strcat(name, SAVE_EXTENSION);
f = fopen(name, "r");
if (!f || fstat(fileno(f), &st) == -1) {
FREE(name);
if (f)
(void)fclose(f);
return 0;
}
if (st.st_size == 0) {
(void)fclose(f);
FREE(name);
return 0;
}
val = DXALLOC(st.st_size + 1, 84, "restore_object: 3");
theBuff = DXALLOC(st.st_size + 1, 85, "restore_object: 4");
fread(theBuff, 1, st.st_size, f);
fclose(f);
theBuff[st.st_size] = '\0';
current_object = ob;
/* This next bit added by Armidale@Cyberworld 1/1/93
* If 'noclear' flag is not set, all non-static variables will be
* initialized to 0 when restored.
*/
if (!noclear) {
num_var = ob->prog->p.i.num_variables;
for (i=0; i<num_var; i++) {
if (!(ob->prog->p.i.variable_names[i].type & TYPE_MOD_STATIC))
assign_svalue(&ob->variables[i], &const0n);
}
}
restore_object_from_buff(ob, theBuff, name, val);
current_object = save;
if (d_flag > 1)
debug_message("Object %s restored from %s.\n", ob->name, name);
FREE(name);
FREE(theBuff);
FREE(val);
return 1;
}
void tell_npc(ob, str)
struct object *ob;
char *str;
{
push_constant_string(str);
(void)apply("catch_tell", ob, 1);
}
/*
* tell_object: Send a message to an object.
* If it is an interactive object, it will go to his
* screen. Otherwise, it will go to a local function
* catch_tell() in that object. This enables communications
* between users and NPC's, and between other NPC's.
* If INTERACTIVE_CATCH_TELL is defined then the message always
* goes to catch_tell unless the target of tell_object is interactive
* and is the current_object in which case it is written via add_message().
*/
void tell_object(ob, str)
struct object *ob;
char *str;
{
struct object *save_command_giver;
save_command_giver = command_giver;
if (!ob || (ob->flags & O_DESTRUCTED)) {
command_giver = 0;
add_message("%s", str);
command_giver = save_command_giver;
return;
}
#ifdef INTERACTIVE_CATCH_TELL
tell_npc(ob, str);
return;
#else
if (ob->interactive) {
command_giver = ob;
add_message("%s", str);
command_giver = save_command_giver;
return;
}
tell_npc(ob, str);
#endif
}
void free_object(ob, from)
struct object *ob;
char *from;
{
struct sentence *s;
ob->ref--;
if (d_flag > 1)
printf("Subtr ref to ob %s: %d (%s)\n", ob->name,
ob->ref, from);
debug(16384, ("subtr ref to ob %s: %d (%s)\n", ob->name, ob->ref, from));
if (ob->ref > 0)
return;
if (d_flag)
printf("free_object: %s.\n", ob->name);
if (!(ob->flags & O_DESTRUCTED)) {
/* This is fatal, and should never happen. */
fatal("FATAL: Object 0x%x %s ref count 0, but not destructed (from %s).\n",
ob, ob->name, from);
}
if (ob->interactive)
fatal("Tried to free an interactive object.\n");
/*
* If the program is freed, then we can also free the variable
* declarations.
*/
if (ob->swap_num != -1)
remove_swap_file(ob); /* do this before prog is freed */
if (ob->prog) {
tot_alloc_object_size -=
(ob->prog->p.i.num_variables - 1) * sizeof (struct svalue) +
sizeof (struct object);
free_prog(ob->prog, 1);
ob->prog = 0;
}
for (s = ob->sent; s;) {
struct sentence *next;
next = s->next;
free_sentence(s);
s = next;
}
if (ob->name) {
if (d_flag > 1)
debug_message("Free object %s\n", ob->name);
if (lookup_object_hash(ob->name) == ob)
fatal("Freeing object %s but name still in name table", ob->name);
FREE(ob->name);
ob->name = 0;
}
tot_alloc_object--;
FREE((char *)ob);
}
/*
* Allocate an empty object, and set all variables to 0. Note that a
* 'struct object' already has space for one variable. So, if no variables
* are needed, we allocate a space that is smaller than 'struct object'. This
* unused (last) part must of course (and will not) be referenced.
*/
struct object *get_empty_object(num_var)
int num_var;
{
static struct object NULL_object;
struct object *ob;
int size = sizeof (struct object) +
(num_var - !!num_var) * sizeof (struct svalue);
int i;
tot_alloc_object++;
tot_alloc_object_size += size;
ob = (struct object *)DXALLOC(size, 86, "get_empty_object");
/* marion
* Don't initialize via memset, this is incorrect. E.g. the bull machines
* have a (char *)0 which is not zero. We have structure assignment, so
* use it.
*/
*ob = NULL_object;
ob->ref = 1;
ob->swap_num = -1;
for (i=0; i<num_var; i++)
ob->variables[i] = const0n;
return ob;
}
void remove_all_objects() {
struct object *ob;
struct svalue v;
v.type = T_OBJECT;
while(1) {
if (obj_list == 0)
break;
ob = obj_list;
v.u.ob = ob;
destruct_object(&v);
if (ob == obj_list)
break;
}
remove_destructed_objects();
}
#ifdef CHECK_OB_REF
/*
* For debugging purposes.
*/
void check_ob_ref(ob, from)
struct object *ob;
char *from;
{
struct object *o;
int i;
for (o = obj_list, i=0; o; o = o->next_all) {
if (o->inherit == ob)
i++;
}
if (i+1 > ob->ref) {
fatal("FATAL too many references to inherited object %s (%d) from %s.\n",
ob->name, ob->ref, from);
if (current_object)
fprintf(stderr, "current_object: %s\n", current_object->name);
for (o = obj_list; o; o = o->next_all) {
if (o->inherit != ob)
continue;
fprintf(stderr, " %s\n", ob->name);
}
}
}
#endif /* CHECK_OB_REF */
static struct object *hashed_living[LIVING_HASH_SIZE];
static int num_living_names, num_searches = 1, search_length = 1;
static int hash_living_name(str)
char *str;
{
return hashstr(str, 100, LIVING_HASH_SIZE);
}
struct object *find_living_object(str, user)
char *str;
int user;
{
struct object **obp, *tmp;
struct object **hl;
if (!str) return 0;
num_searches++;
hl = &hashed_living[hash_living_name(str)];
for (obp = hl; *obp; obp = &(*obp)->next_hashed_living) {
search_length++;
if ((*obp)->flags & O_HIDDEN)
{
if (!valid_hide(current_object))
continue;
}
if (user && !((*obp)->flags & O_ONCE_INTERACTIVE))
continue;
if (!((*obp)->flags & O_ENABLE_COMMANDS))
continue;
if (strcmp((*obp)->living_name, str) == 0)
break;
}
if (*obp == 0)
return 0;
/* Move the found ob first. */
if (obp == hl)
return *obp;
tmp = *obp;
*obp = tmp->next_hashed_living;
tmp->next_hashed_living = *hl;
*hl = tmp;
return tmp;
}
void set_living_name(ob, str)
struct object *ob;
char *str;
{
struct object **hl;
if (ob->flags & O_DESTRUCTED)
return;
if (ob->living_name) {
remove_living_name(ob);
}
num_living_names++;
hl = &hashed_living[hash_living_name(str)];
ob->next_hashed_living = *hl;
*hl = ob;
ob->living_name = make_shared_string(str);
return;
}
void remove_living_name(ob)
struct object *ob;
{
struct object **hl;
num_living_names--;
if (!ob->living_name)
fatal("remove_living_name: no living name set.\n");
hl = &hashed_living[hash_living_name(ob->living_name)];
while(*hl) {
if (*hl == ob)
break;
hl = &(*hl)->next_hashed_living;
}
if (*hl == 0)
fatal("remove_living_name: Object named %s no in hash list.\n",
ob->living_name);
*hl = ob->next_hashed_living;
free_string(ob->living_name);
ob->next_hashed_living = 0;
ob->living_name = 0;
}
void stat_living_objects() {
add_message("Hash table of living objects:\n");
add_message("-----------------------------\n");
add_message("%d living named objects, average search length: %4.2f\n",
num_living_names, (double)search_length / num_searches);
}
void reference_prog (progp, from)
struct program *progp;
char *from;
{
progp->p.i.ref++;
if (d_flag)
printf("reference_prog: %s ref %d (%s)\n",
progp->name, progp->p.i.ref, from);
}
/*
* Decrement reference count for a program. If it is 0, then free the prgram.
* The flag free_sub_strings tells if the propgram plus all used strings
* should be freed. They normally are, except when objects are swapped,
* as we want to be able to read the program in again from the swap area.
* That means that strings are not swapped.
*/
void free_prog(progp, free_sub_strings)
struct program *progp;
int free_sub_strings;
{
progp->p.i.ref--;
if (progp->p.i.ref > 0)
return;
if (d_flag)
printf("free_prog: %s\n", progp->name);
if (progp->p.i.ref < 0)
fatal("Negative ref count for prog ref.\n");
total_prog_block_size -= progp->p.i.total_size;
total_num_prog_blocks -= 1;
if (free_sub_strings) {
int i;
/* Free all function names. */
for (i=0; i < (int)progp->p.i.num_functions; i++)
if (progp->p.i.functions[i].name)
free_string(progp->p.i.functions[i].name);
/* Free all strings */
for (i=0; i < (int)progp->p.i.num_strings; i++)
free_string(progp->p.i.strings[i]);
/* Free all variable names */
for (i=0; i < (int)progp->p.i.num_variables; i++)
free_string(progp->p.i.variable_names[i].name);
/* Free all inherited objects */
for (i=0; i < (int)progp->p.i.num_inherited; i++)
free_prog(progp->p.i.inherit[i].prog, 1);
FREE(progp->name);
/*
* We're going away for good, not just being swapped,
* so free up line_number stuff.
*/
if (progp->p.i.line_swap_index != -1)
remove_line_swap(progp);
if (progp->p.i.line_numbers)
FREE(progp->p.i.line_numbers);
}
FREE((char *)progp);
}
void reset_object(ob, arg)
struct object *ob;
int arg;
{
extern int current_time;
/* Be sure to update time first ! */
ob->next_reset = current_time + TIME_TO_RESET/2 +
random_number(TIME_TO_RESET/2);
if (arg == 0) {
apply("__INIT", ob, 0);
apply("create", ob, 0);
} else { /* check for O_WILL_RESET in backend */
struct object *save_command_giver;
save_command_giver = command_giver;
command_giver = (struct object *)0;
if (!apply("reset", ob, 0)) { /* no reset() in the object */
ob->flags &= ~O_WILL_RESET; /* don't call it next time */
}
command_giver = save_command_giver;
}
ob->flags |= O_RESET_STATE;
}
/*
* If there is a shadow for this object, then the message should be
* sent to it. But only if catch_tell() is defined. Beware that one of the
* shadows may be the originator of the message, which means that we must
* not send the message to that shadow, or any shadows in the linked list
* before that shadow.
*/
#ifndef NO_SHADOWS
int shadow_catch_message(ob, str)
struct object *ob;
char *str;
{
if (!ob->shadowed)
return 0;
while(ob->shadowed != 0 && ob->shadowed != current_object)
ob = ob->shadowed;
while(ob->shadowing) {
if (function_exists("catch_tell", ob))
{
push_constant_string(str);
if (apply("catch_tell", ob, 1)) /* this will work, since we know the */
/* function is defined */
return 1;
}
ob = ob->shadowing;
}
return 0;
}
#endif
INLINE int
object_visible(ob)
struct object *ob;
{
if (ob->flags & O_HIDDEN) {
if (current_object->flags & O_HIDDEN) {
return 1;
}
return valid_hide(current_object);
} else {
return 1;
}
}
void
reload_object(obj)
struct object *obj;
{
int i;
if (!obj->prog) return;
for (i = 0; i < (int)obj->prog->p.i.num_variables; i++)
{
free_svalue(&obj->variables[i]);
obj->variables[i] = const0n;
}
#ifdef SOCKET_EFUNS
if (obj->flags & O_EFUN_SOCKET) {
close_referencing_sockets(obj);
}
#endif
/*
* If this is the first object being shadowed by another object, then
* destruct the whole list of shadows.
*/
#ifndef NO_SHADOWS
if (obj->shadowed && !obj->shadowing) {
struct svalue svp;
struct object *ob2;
svp.type = T_OBJECT;
for (ob2 = obj->shadowed; ob2; ) {
svp.u.ob = ob2;
ob2 = ob2->shadowed;
svp.u.ob->shadowed = 0;
svp.u.ob->shadowing = 0;
destruct_object(&svp);
}
}
/*
* The chain of shadows is a double linked list. Take care to update
* it correctly.
*/
if (obj->shadowing)
obj->shadowing->shadowed = obj->shadowed;
if (obj->shadowed)
obj->shadowed->shadowing = obj->shadowing;
obj->shadowing = 0;
obj->shadowed = 0;
#endif
if (obj->living_name)
remove_living_name(obj);
obj->flags &= ~O_ENABLE_COMMANDS;
set_heart_beat(obj, 0);
remove_all_call_out(obj);
add_light(obj, -(obj->total_light));
#ifdef AUTO_SETEUID
obj->euid = obj->uid;
#else
obj->euid = NULL;
#endif
reset_object(obj, 0);
}