#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#ifdef __STDC__
#include <memory.h>
#endif
#include "config.h"
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "exec.h"
#include "mapping.h"
#include "mudstat.h"
extern int d_flag, s_flag;
extern int total_num_prog_blocks, total_prog_block_size;
extern char *add_slash();
extern void print_svalue (struct svalue *, struct object *);
int tot_alloc_dest_object = 0;
int tot_removed_object = 0;
#ifdef USE_TIOCGETP /* Check if BSD */
extern int getpid();
#else
extern pid_t getpid();
#endif
#define align(x) ( ((x) + (sizeof(void*)-1) ) & ~(sizeof(void*)-1) )
extern char *xalloc (int), *string_copy (char *);
void remove_swap_file (struct object *);
extern int atoi();
struct object *previous_ob;
extern struct svalue const0;
int tot_alloc_object, tot_alloc_object_size;
/*
* Replace carriage return in a string with newlines.
*/
static void
restore_newline(char *str)
{
for (; *str; str++)
{
if (str[0] == '\r')
str[0] = '\n';
}
}
struct savebuf {
unsigned int max_size;
unsigned int size;
char *buf;
};
void
add_strbuf(struct savebuf *sbuf, char *str)
{
int len;
len = strlen(str);
if (sbuf->size + len + 1 > sbuf->max_size)
{
char *nbuf;
int nsize;
nbuf = xalloc(nsize = len + sbuf->size + 80);
strcpy(nbuf, sbuf->buf);
sbuf->max_size = nsize;
free(sbuf->buf);
sbuf->buf = nbuf;
}
strcpy(&(sbuf->buf[sbuf->size]), str);
sbuf->size += len;
}
static int failed;
#define Fprintf(s) if (fprintf s == EOF) failed=1
static void save_one (struct savebuf *,struct svalue *);
static void
save_string(struct savebuf *f, char *s)
{
char buf[2];
buf[1] = '\0';
add_strbuf(f, "\"");
while (*s)
{
switch (*s)
{
case '"':
add_strbuf(f, "\\\"");
break;
case '\\':
add_strbuf(f, "\\\\");
break;
case '\n':
add_strbuf(f, "\\n");
break;
default:
/*if (isprint(*s))*/
buf[0] = *s;
add_strbuf(f, buf);
break;
}
s++;
}
add_strbuf(f, "\"");
}
/*
* Encode an array of elements.
*/
static void
save_array(struct savebuf *f, struct vector *v)
{
int i;
add_strbuf(f, "({");
for (i = 0; i < v->size; i++)
{
save_one(f, &v->item[i]);
add_strbuf(f,",");
}
add_strbuf(f,"})");
}
static void
save_mapping(struct savebuf *f, struct mapping *m)
{
int i;
struct apair *p;
add_strbuf(f, "([");
for (i = 0; i < m->size; i++)
{
for(p = m->pairs[i]; p; p = p->next)
{
save_one(f, &p->arg);
add_strbuf(f,":");
save_one(f, &p->val);
add_strbuf(f,",");
}
}
add_strbuf(f,"])");
}
static void
save_one(struct savebuf *f, struct svalue *v)
{
char buf[20];
switch(v->type) {
case T_FLOAT:
sprintf(buf,"#%.8e#", v->u.real);
add_strbuf(f, buf);
break;
case T_NUMBER:
sprintf(buf, "%d", v->u.number);
add_strbuf(f, buf);
break;
case T_STRING:
save_string(f, v->u.string);
break;
case T_POINTER:
save_array(f, v->u.vec);
break;
case T_MAPPING:
save_mapping(f, v->u.map);
break;
case T_OBJECT:
sprintf(buf, "$%d@", v->u.ob->created);
add_strbuf(f, buf);
add_strbuf(f, v->u.ob->name);
add_strbuf(f, "$");
break;
default:
add_strbuf(f, "0");
break;
}
}
/*
* 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.
*/
void save_object(struct object *ob, char *file)
{
char *name, tmp_name[80];
int len, i, j;
FILE *f;
int failed = 0;
struct savebuf sbuf;
/* struct svalue *v; */
if (ob->flags & O_DESTRUCTED)
return;
file = check_valid_path(file, ob, "save_object", 1);
if (file == 0)
error("Illegal use of save_object()\n");
len = strlen(file);
name = xalloc(len + 3);
(void)strcpy(name, file);
(void)strcat(name, ".o");
/*
* Write the save-files to different directories, just in case
* they are on different file systems.
*/
sprintf(tmp_name, "%s.tmp", name);
f = fopen(tmp_name, "w");
if (s_flag)
num_filewrite++;
if (f == 0) {
free(name);
error("Could not open %s for a save.\n", tmp_name);
}
failed = 0;
sbuf.size = 0;
sbuf.max_size = 80;
sbuf.buf = xalloc(80);
for (j = 0; j < (int)ob->prog->num_inherited; j++)
{
struct program *prog = ob->prog->inherit[j].prog;
if (ob->prog->inherit[j].type & TYPE_MOD_SECOND ||
prog->num_variables <= 0)
continue;
access_program(prog);
for (i = 0; i < (int)prog->num_variables; i++) {
struct svalue *v =
&ob->variables[i + ob->prog->inherit[j].variable_index_offset];
if (ob->prog->inherit[j].prog->variable_names[i].type & TYPE_MOD_STATIC)
continue;
if (v->type == T_NUMBER || v->type == T_STRING || v->type == T_POINTER
|| v->type == T_MAPPING || v->type == T_OBJECT || v->type == T_FLOAT) {
sbuf.size = 0;
sbuf.buf[0] = 0;
save_one(&sbuf, v);
Fprintf((f, "%s %s\n", ob->prog->inherit[j].prog->variable_names[i].name, sbuf.buf));
}
}
}
free(sbuf.buf);
if (fclose(f) == EOF)
failed = 1;
if (failed) {
unlink(tmp_name);
error("Failed to save to file. Disk could be full.\n");
}
(void)unlink(name);
if (link(tmp_name, name) == -1)
{
perror(name);
printf("Failed to link %s to %s\n", tmp_name, name);
error("Failed to save object !\n");
}
unlink(tmp_name);
free(name);
}
/*
* Save an object to a mapping.
*/
struct mapping *
m_save_object(struct object *ob)
{
int len, i, j;
FILE *f;
int failed = 0;
struct mapping *ret;
struct svalue s = const0;
if (ob->flags & O_DESTRUCTED)
return;
ret = allocate_map(ob->prog->num_variables +
ob->prog->inherit[ob->prog->num_inherited - 1].
variable_index_offset);
for (j = 0; j < (int)ob->prog->num_inherited; j++)
{
struct program *prog = ob->prog->inherit[j].prog;
if (ob->prog->inherit[j].type & TYPE_MOD_SECOND ||
prog->num_variables <= 0)
continue;
access_program(prog);
for (i = 0; i < (int)prog->num_variables; i++)
{
struct svalue *v =
&ob->variables[i + ob->prog->inherit[j].
variable_index_offset];
if (prog->variable_names[i].type & TYPE_MOD_STATIC)
continue;
free_svalue(&s);
s.type = TYPE_STRING;
s.string_type = STRING_MALLOC;
s.u.string = string_copy(prog->variable_names[i].name);
assign_svalue(get_map_lvalue(ret, &s, 1), v);
}
}
free_svalue(&s);
return ret;
}
void save_map(struct object *ob, struct mapping *map, char *file)
{
char *name, tmp_name[80];
struct apair *i;
int len, j;
FILE *f;
struct savebuf sbuf;
int failed = 0;
/* struct svalue *v; */
file = check_valid_path(file, ob, "save_map", 1);
if (file == 0)
error("Illegal use of save_map()\n");
len = strlen(file);
name = xalloc(len + 3);
(void)strcpy(name, file);
(void)strcat(name, ".o");
/*
* Write the save-files to different directories, just in case
* they are on different file systems.
*/
sprintf(tmp_name, "%s.tmp", name);
f = fopen(tmp_name, "w");
if (s_flag)
num_filewrite++;
if (f == 0) {
free(name);
error("Could not open %s for a save.\n", tmp_name);
}
failed = 0;
sbuf.size = 0;
sbuf.max_size = 80;
sbuf.buf = xalloc(80);
for (j = 0; j < map->size; j++)
{
for (i = map->pairs[j]; i; i = i->next) {
struct svalue *v =
&i->val;
if (i->arg.type != T_STRING)
continue;
if (v->type == T_NUMBER || v->type == T_STRING || v->type == T_POINTER
|| v->type == T_MAPPING || v->type == T_OBJECT || v->type == T_FLOAT) {
sbuf.size = 0;
sbuf.buf[0] = 0;
save_one(&sbuf, v);
Fprintf((f, "%s %s\n", i->arg.u.string, sbuf.buf));
}
}
}
free(sbuf.buf);
if (failed)
error("Failed to save to file. Disk could be full.\n");
(void)unlink(name);
if (link(tmp_name, name) == -1)
{
perror(name);
printf("Failed to link %s to %s\n", tmp_name, name);
error("Failed to save mapping !\n");
}
(void)fclose(f);
unlink(tmp_name);
free(name);
}
char *
valtostr(struct svalue *sval)
{
struct savebuf sbuf;
sbuf.buf = xalloc(80);
sbuf.size = 0;
sbuf.max_size = 80;
sbuf.buf[0] = 0;
save_one(&sbuf, sval);
return sbuf.buf;
}
#define BIG 1000
static FILE *resf;
static char *resname;
int restore_one (struct svalue *, char **);
static struct vector *
restore_array(char **str)
{
struct svalue *tmp;
int nmax = BIG;
int i, k;
tmp = (struct svalue *)xalloc(nmax * sizeof(struct svalue));
for(k = 0; k < nmax; k++)
tmp[k] = const0;
i = 0;
for(;;)
{
if (**str == '}')
{
if (*++*str == ')')
{
struct vector *v;
++*str;
v = allocate_array(i);
memcpy((char *)&v->item[0], (char *)tmp, sizeof(struct svalue) * i);
free((char *)tmp);
return v;
}
else
break;
}
else
{
if (i >= nmax)
{
struct svalue *ntmp;
ntmp = (struct svalue *)xalloc(nmax * 2 * sizeof(struct svalue));
memcpy((char *)ntmp, (char *)tmp, sizeof(struct svalue) * nmax);
free((char *)tmp);
tmp = ntmp;
nmax *= 2;
for(k = i; k < nmax; k++)
tmp[k] = const0;
}
if (!restore_one(&tmp[i++], str))
break;
if (*(*str)++ != ',')
break;
}
}
for (i--; i >= 0; i--)
free_svalue(&(tmp[i]));
return 0;
}
static struct mapping *
restore_mapping(char **str)
{
struct mapping *m;
m = allocate_map(0);
for(;;)
{
if (**str == ']')
{
if (*++*str == ')')
{
++*str;
return m;
}
else
break;
}
else
{
struct svalue arg, *val;
arg = const0;
if (!restore_one(&arg, str))
break;
if (*(*str)++ != ':')
{
free_svalue(&arg);
break;
}
val = get_map_lvalue(m, &arg, 1);
free_svalue(&arg);
if (!restore_one(val, str) ||
*(*str)++ != ',')
break;
}
}
free_mapping(m);
return 0;
}
int
restore_one(struct svalue *v, char **sp)
{
char *q, *p, *s;
s = *sp;
switch(*s) {
case '(':
switch(*++s)
{
case '[':
{
struct mapping *map;
s++;
map = restore_mapping(&s);
if (!map) {
return 0;
}
free_svalue(v);
v->type = T_MAPPING;
v->u.map = map;
}
break;
case '{':
{
struct vector *vec;
s++;
vec = restore_array(&s);
if (!vec) {
return 0;
}
free_svalue(v);
v->type = T_POINTER;
v->u.vec = vec;
}
break;
default:
return 0;
}
break;
case '"':
for(p = s+1, q = s; *p && *p != '"'; p++)
{
if (*p == '\\') {
switch (*++p) {
case 'n':
*q++ = '\n';
break;
default:
*q++ = *p;
break;
}
} else {
/* Have to be able to restore old format... */
if (*p == '\r')
*q++ = '\n';
else
*q++ = *p;
}
}
*q = 0;
if (*p != '"')
return 0;
free_svalue(v);
v->type = T_STRING;
v->u.string = string_copy(s);
v->string_type = STRING_MALLOC;
s = p+1;
break;
case '$':
{
int ct;
struct object *ob;
char name[1024];
char *b = strchr(s + 1,'$');
*name = '\0';
if (b == NULL)
return 0;
if (sscanf(s,"$%d@%[^$ \n\t]$",&ct,name) != 2)
return 0;
ob = find_object2(name);
free_svalue(v);
if (ob && ob->created == ct)
{
v->type = T_OBJECT;
v->u.ob = ob;
add_ref(ob,"restore_one");
}
else
{
v->type = T_NUMBER;
v->u.number = 0;
}
s = b + 1;
}
break;
case '#':
{
float f;
char *b = strchr(s + 1, '#');
if (b == NULL)
return 0;
if (sscanf(s,"#%f#",&f) != 1)
return 0;
free_svalue(v);
v->type = T_FLOAT;
v->u.real = f;
s = b + 1;
}
break;
default:
if (!isdigit(*s) && *s != '-')
return 0;
free_svalue(v);
v->type = T_NUMBER;
v->u.number = atoi(s);
while(isdigit(*s) || *s == '-')
s++;
break;
}
*sp = s;
return 1;
}
int
restore_object(struct object *ob, char *file)
{
char *name, var[100], *buff, *space;
int len;
FILE *f;
struct object *save = current_object;
struct stat st;
int p;
if (current_object != ob)
fatal("Bad argument to restore_object()\n");
if (ob->flags & O_DESTRUCTED)
return 0;
file = check_valid_path(file, ob, "restore_object", 0);
if (file == 0)
error("Illegal use of restore_object()\n");
len = strlen(file);
name = xalloc(len + 3);
(void)strcpy(name, file);
if (name[len-2] == '.' && name[len-1] == 'c')
name[len-1] = 'o';
else
(void)strcat(name, ".o");
f = fopen(name, "r");
if (s_flag)
num_fileread++;
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;
}
buff = xalloc(st.st_size + 1);
current_object = ob;
while(1) {
struct svalue *v;
if (fgets(buff, st.st_size + 1, f) == 0)
break;
/* Remember that we have a newline at end of buff ! */
space = strchr(buff, ' ');
if (space == 0 || space - buff >= sizeof (var)) {
(void)fclose(f);
free(name);
free(buff);
error("Illegal format when restoring %s.\n", file);
}
(void)strncpy(var, buff, space - buff);
var[space - buff] = '\0';
p = find_status(ob->prog, var, TYPE_MOD_STATIC);
if (p == -1)
continue;
v = &ob->variables[p];
resname = name;
resf = f;
space++;
if (!restore_one(v, &space)) {
(void)fclose(f);
free(name);
free(buff);
error("Illegal format when restoring %s from %s.\n", var, file);
}
}
current_object = save;
if (d_flag & DEBUG_RESTORE)
debug_message("Object %s restored from %s.\n", ob->name, file);
free(name);
free(buff);
(void)fclose(f);
return 1;
}
int
m_restore_object(struct object *ob, struct mapping *map)
{
int p;
int i;
struct apair *j;
if (ob->flags & O_DESTRUCTED)
return 0;
for (i = 0; i < map->size; i++)
{
for (j = map->pairs[i]; j ; j = j->next)
{
if (j->arg.type != TYPE_STRING)
continue;
if ((p = find_status(ob->prog, j->arg.u.string, TYPE_MOD_STATIC))
== -1)
continue;
assign_svalue(&ob->variables[p], &j->val);
}
}
return 1;
}
int
restore_map(struct object *ob, struct mapping *map, char *file)
{
char *name, *buff, *space;
int len;
FILE *f;
struct object *save = current_object;
struct stat st;
int p;
if (current_object != ob)
fatal("Bad argument to restore_map()\n");
if (ob->flags & O_DESTRUCTED)
return 0;
file = check_valid_path(file, ob, "restore_map", 0);
if (file == 0)
error("Illegal use of restore_map()\n");
len = strlen(file);
name = xalloc(len + 3);
(void)strcpy(name, file);
if (name[len-2] == '.' && name[len-1] == 'c')
name[len-1] = 'o';
else
(void)strcat(name, ".o");
f = fopen(name, "r");
if (s_flag)
num_fileread++;
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;
}
buff = xalloc(st.st_size + 1);
while(1) {
struct svalue v;
v.type = T_STRING;
v.string_type = STRING_MALLOC;
if (fgets(buff, st.st_size + 1, f) == 0)
break;
/* Remember that we have a newline at end of buff ! */
space = strchr(buff, ' ');
if (space == 0) {
(void)fclose(f);
free(buff);
free(name);
error("Illegal format when restoring %s.\n", file);
}
v.u.string = xalloc(space - buff + 1);
(void)strncpy(v.u.string, buff, space - buff);
v.u.string[space - buff] = '\0';
resname = name;
resf = f;
space++;
if (!restore_one(get_map_lvalue(map,&v,1), &space)) {
(void)fclose(f);
free(buff);
free(name);
error("Illegal format when restoring %s.\n", file);
}
free_svalue(&v);
}
current_object = save;
free(name);
free(buff);
(void)fclose(f);
return 1;
}
void
tell_npc(struct object *ob, char *str)
{
push_string(str, STRING_MALLOC);
(void)apply("catch_tell", ob, 1, 1);
}
/*
* 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 players and NPC's, and between other NPC's.
*/
void
tell_object(struct object *ob, struct svalue *arg)
{
struct object *save_command_giver;
if (ob &&(ob->flags & O_DESTRUCTED))
return;
print_svalue(arg, ob);
return;
}
void
free_object(struct object *ob, char *from)
{
struct sentence *s;
ob->ref--;
if (d_flag & DEBUG_OB_REF)
printf("Subtr ref to ob %s: %d (%s)\n", ob->name,
ob->ref, from);
if (ob->ref > 0)
return;
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->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--;
tot_alloc_dest_object--;
tot_removed_object++;
#ifdef RUSAGE
remove_cpu_stack(ob);
#endif
free((char *)ob);
tot_alloc_object_size -= sizeof (struct object);
}
void
add_ref(struct object *ob, char *from)
{
ob->ref++;
if (d_flag & DEBUG_OB_REF)
printf("Add reference to object %s: %d (%s)\n", ob->name,
ob->ref, from);
}
/*
* 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(int num_var)
{
static struct object NULL_object;
struct object *ob;
int size = sizeof (struct object);
int i;
tot_alloc_object++;
tot_alloc_object_size += size;
ob = (struct object *)xalloc(sizeof(struct 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;
return ob;
}
void remove_all_objects()
{
struct object *ob;
while(1) {
if (obj_list == 0)
break;
ob = obj_list;
destruct_object(ob);
if (ob == obj_list)
break;
}
remove_destructed_objects();
}
#if 0
/*
* For debugging purposes.
*/
void
check_ob_ref(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 /* 0 */
static struct object *hashed_living[LIVING_HASH_SIZE];
static int num_living_names, num_searches = 1, search_length = 1;
#if defined(ultrix) && !defined(__GNUC__)
#define LivHash(s) (hashstr((s), 100, LIVING_HASH_SIZE))
#else
#if BITNUM(LIVING_HASH_SIZE) == 1
/* This one only works for even power-of-2 table size, but is faster */
#define LivHash(s) (hashstr16((s), 100) & ((LIVING_HASH_SIZE)-1))
#else
#define LivHash(s) (hashstr((s), 100, LIVING_HASH_SIZE))
#endif
#endif
struct object *
find_living_object(char *str)
{
struct object **obp, *tmp;
struct object **hl;
num_searches++;
hl = &hashed_living[LivHash(str)];
for (obp = hl; *obp; obp = &(*obp)->next_hashed_living) {
search_length++;
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(struct object *ob, char *str)
{
struct object **hl;
if (ob->flags & O_DESTRUCTED)
return;
if (ob->living_name) {
remove_living_name(ob);
}
if (!*str)
return;
num_living_names++;
hl = &hashed_living[LivHash(str)];
ob->next_hashed_living = *hl;
*hl = ob;
ob->living_name = make_shared_string(str);
return;
}
void
remove_living_name(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[LivHash(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;
}
char *
stat_living_objects()
{
static char tmp[400];
sprintf(tmp,"Hash table of living objects:\n-----------------------------\n%d living named objects, average search length: %4.2f\n",
num_living_names, (double)search_length / num_searches);
return tmp;
}
void
reference_prog (struct program *progp, char *from)
{
progp->ref++;
if (d_flag & DEBUG_PROG_REF)
printf("reference_prog: %s ref %d (%s)\n",
progp->name, progp->ref, from);
}
struct program *prog_list;
/* Add a program to the list of all programs */
void register_program(struct program *prog)
{
extern struct program *swap_prog;
if (prog_list)
{
prog->next_all = prog_list;
prog->prev_all = prog_list->prev_all;
prog_list->prev_all->next_all = prog;
prog_list->prev_all = prog;
prog_list = prog;
}
else
prog_list = swap_prog = prog->next_all = prog->prev_all = prog;
if (current_prog)
access_program(current_prog);
}
/*
* 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(struct program *progp, int free_sub_strings)
{
int psize;
extern int total_program_size;
extern struct program *swap_prog;
progp->ref--;
if (d_flag & DEBUG_PROG_REF)
printf("free_prog: %s\n", progp->name);
if (progp->ref > 0)
return;
if (progp->ref < 0)
fatal("Negative ref count for prog ref.\n");
if (progp == swap_prog)
swap_prog = progp->prev_all;
if (progp == prog_list)
prog_list = progp->next_all;
progp->prev_all->next_all = progp->next_all;
progp->next_all->prev_all = progp->prev_all;
if (progp->next_all == progp)
prog_list = swap_prog = 0;
remove_prog_from_swap(progp);
total_program_size -= progp->exec_size;
total_prog_block_size -= progp->total_size;
total_num_prog_blocks--;
if (free_sub_strings) {
int i;
/* Free all function names. */
for (i=0; i < (int)progp->num_functions; i++)
if (progp->functions[i].name)
free_string(progp->functions[i].name);
/* Free all variable names */
for (i=0; i < (int)progp->num_variables; i++)
free_string(progp->variable_names[i].name);
/* Free all inherited objects */
for (i=0; i < (int)progp->num_inherited - 1; i++)
{
free_prog(progp->inherit[i].prog, 1);
free_string(progp->inherit[i].name);
}
free_string(progp->inherit[i].name);
free(progp->name);
}
free((char *)progp->program);
if (progp->line_numbers != 0)
free((char *)progp->line_numbers);
free((char *)progp);
}
void
create_object(struct object *ob)
{
extern int current_time;
int num_var, i;
extern int tot_alloc_variable_size;
if (!(ob->flags & O_CREATED))
{
ob->flags |= O_CREATED;
ob->created = current_time;
for (i = 0; i < ob->prog->num_inherited; i++)
if (!(ob->prog->inherit[i].type & TYPE_MOD_SECOND))
{
if (ob->prog->inherit[i].prog->ctor_index !=
(unsigned short) -1)
{
call_function(ob, i, ob->prog->inherit[i].prog->
ctor_index, 0);
pop_stack();
}
}
if (search_for_function("create", ob->prog))
{
call_function(ob, function_inherit_found,
function_index_found, 0);
pop_stack();
}
}
}
void
recreate_object(struct object *ob, struct object *old_ob)
{
extern int current_time;
int num_var, i;
extern int tot_alloc_variable_size;
if (!(ob->flags & O_CREATED))
{
ob->flags |= O_CREATED;
if (!ob->created)
ob->created = current_time;
for (i = 0; i < ob->prog->num_inherited; i++)
if (!(ob->prog->inherit[i].type & TYPE_MOD_SECOND))
{
if (ob->prog->inherit[i].prog->ctor_index !=
(unsigned short) -1)
{
call_function(ob, i, ob->prog->inherit[i].prog->
ctor_index, 0);
pop_stack();
}
}
}
if (search_for_function("recreate", ob->prog))
{
push_object(old_ob);
call_function(ob, function_inherit_found,
function_index_found, 1);
pop_stack();
}
}
/*
* 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.
*/
int
shadow_catch_message(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_string(str, STRING_MALLOC);
/* this will work, since we know the function is defined
*/
if (apply("catch_tell", ob, 1, 1))
return 1;
}
ob = ob->shadowing;
}
return 0;
}
/*
* Returns a list of all inherited files.
*
*/
struct vector *
inherit_list(struct object *ob)
{
struct vector *ret;
int inh;
ret = allocate_array(ob->prog->num_inherited);
for (inh = 0; inh < (int)ob->prog->num_inherited; inh++ )
{
ret->item[inh].type = T_STRING;
ret->item[inh].string_type = STRING_MALLOC;
ret->item[inh].u.string = add_slash(ob->prog->inherit[inh].prog->name);
}
return ret;
}
void
change_ref(struct object *to, struct object *from, char *msg)
{
if (from)
add_ref(from, msg);
if (to)
free_object(to, msg);
}