tmi2/
tmi2/bin/
tmi2/etc/
tmi2/lib/
tmi2/lib/adm/
tmi2/lib/adm/daemons/languages/
tmi2/lib/adm/daemons/network/I3/
tmi2/lib/adm/daemons/virtual/template/
tmi2/lib/adm/obj/master/
tmi2/lib/adm/priv/
tmi2/lib/adm/shell/
tmi2/lib/adm/tmp/
tmi2/lib/cmds/
tmi2/lib/d/
tmi2/lib/d/Conf/
tmi2/lib/d/Conf/adm/
tmi2/lib/d/Conf/boards/
tmi2/lib/d/Conf/cmds/
tmi2/lib/d/Conf/data/
tmi2/lib/d/Conf/logs/
tmi2/lib/d/Conf/obj/
tmi2/lib/d/Conf/text/help/
tmi2/lib/d/Fooland/adm/
tmi2/lib/d/Fooland/data/
tmi2/lib/d/Fooland/data/attic/
tmi2/lib/d/Fooland/items/
tmi2/lib/d/TMI/
tmi2/lib/d/TMI/adm/
tmi2/lib/d/TMI/boards/
tmi2/lib/d/TMI/data/
tmi2/lib/d/TMI/rooms/
tmi2/lib/d/grid/
tmi2/lib/d/grid/adm/
tmi2/lib/d/grid/data/
tmi2/lib/d/std/
tmi2/lib/d/std/adm/
tmi2/lib/data/adm/
tmi2/lib/data/adm/daemons/
tmi2/lib/data/adm/daemons/doc_d/
tmi2/lib/data/adm/daemons/emoted/
tmi2/lib/data/adm/daemons/network/http/
tmi2/lib/data/adm/daemons/network/services/mail_q/
tmi2/lib/data/adm/daemons/network/smtp/
tmi2/lib/data/adm/daemons/news/archives/
tmi2/lib/data/attic/connection/
tmi2/lib/data/attic/user/
tmi2/lib/data/std/connection/b/
tmi2/lib/data/std/connection/l/
tmi2/lib/data/std/user/a/
tmi2/lib/data/std/user/b/
tmi2/lib/data/std/user/d/
tmi2/lib/data/std/user/f/
tmi2/lib/data/std/user/l/
tmi2/lib/data/std/user/x/
tmi2/lib/data/u/d/dm/working/doc_d/
tmi2/lib/data/u/l/leto/doc_d/
tmi2/lib/data/u/l/leto/smtp/
tmi2/lib/doc/
tmi2/lib/doc/driverdoc/applies/
tmi2/lib/doc/driverdoc/concepts/
tmi2/lib/doc/driverdoc/driver/
tmi2/lib/doc/driverdoc/efuns/arrays/
tmi2/lib/doc/driverdoc/efuns/buffers/
tmi2/lib/doc/driverdoc/efuns/compile/
tmi2/lib/doc/driverdoc/efuns/ed/
tmi2/lib/doc/driverdoc/efuns/floats/
tmi2/lib/doc/driverdoc/efuns/functions/
tmi2/lib/doc/driverdoc/efuns/general/
tmi2/lib/doc/driverdoc/efuns/numbers/
tmi2/lib/doc/driverdoc/efuns/parsing/
tmi2/lib/doc/driverdoc/lpc/constructs/
tmi2/lib/doc/driverdoc/lpc/preprocessor/
tmi2/lib/doc/driverdoc/lpc/types/
tmi2/lib/doc/driverdoc/platforms/
tmi2/lib/doc/mudlib/
tmi2/lib/ftp/
tmi2/lib/log/
tmi2/lib/obj/net/
tmi2/lib/obj/shells/
tmi2/lib/std/board/
tmi2/lib/std/body/
tmi2/lib/std/fun/
tmi2/lib/std/living/
tmi2/lib/std/object/
tmi2/lib/std/shop/
tmi2/lib/std/socket/
tmi2/lib/std/virtual/
tmi2/lib/student/
tmi2/lib/student/kalypso/
tmi2/lib/student/kalypso/armor/
tmi2/lib/student/kalypso/rooms/
tmi2/lib/student/kalypso/weapons/
tmi2/lib/u/l/leto/
tmi2/lib/u/l/leto/cmds/
tmi2/lib/www/errors/
tmi2/lib/www/gateways/
tmi2/lib/www/images/
tmi2/old/
tmi2/v21.7a10/
tmi2/v21.7a10/ChangeLog.old/
tmi2/v21.7a10/compat/simuls/
tmi2/v21.7a10/include/
tmi2/v21.7a10/testsuite/
tmi2/v21.7a10/testsuite/clone/
tmi2/v21.7a10/testsuite/command/
tmi2/v21.7a10/testsuite/data/
tmi2/v21.7a10/testsuite/etc/
tmi2/v21.7a10/testsuite/include/
tmi2/v21.7a10/testsuite/inherit/
tmi2/v21.7a10/testsuite/inherit/master/
tmi2/v21.7a10/testsuite/log/
tmi2/v21.7a10/testsuite/u/
tmi2/v21.7a10/tmp/
#include "std.h"
#include "lpc_incl.h"
#include "file_incl.h"
#include "otable.h"
#include "backend.h"
#include "debug.h"
#include "comm.h"
#include "swap.h"
#include "socket_efuns.h"
#include "call_out.h"
#include "port.h"
#include "file.h"
#include "hash.h"

#define too_deep_save_error() \
    error("Mappings and/or arrays nested too deep (%d) for save_object\n",\
	  MAX_SAVE_SVALUE_DEPTH);

object_t *previous_ob;
int tot_alloc_object, tot_alloc_object_size;

char *save_mapping PROT ((mapping_t *m));
INLINE static int restore_array PROT((char **str, svalue_t *));
INLINE static int restore_class PROT((char **str, svalue_t *));
int restore_hash_string PROT((char **str, int *a, svalue_t *));

INLINE int
valid_hide P1(object_t *, obj)
{
    svalue_t *ret;

    if (!obj) {
	return 0;
    }
    push_object(obj);
    ret = apply_master_ob(APPLY_VALID_HIDE, 1);
    return (!IS_ZERO(ret));
}


int save_svalue_depth = 0, max_depth;
int *sizes = 0;

INLINE int svalue_save_size P1(svalue_t *, v)
{
    switch(v->type){
    case T_STRING:
	{
	    register char *cp = v->u.string;
	    char c;
	    int size = 0;

	    while ((c = *cp++)) {
		if (c == '\\' || c == '\"') size++;
		size++;
	    }
	    return 3 + size;
	}

    case T_ARRAY:
	{
	    svalue_t *sv = v->u.arr->item;
	    int i = v->u.arr->size, size = 0;

	    if (++save_svalue_depth > MAX_SAVE_SVALUE_DEPTH){
		too_deep_save_error();
	    }
	    while (i--) size += svalue_save_size(sv++);
	    save_svalue_depth--;
	    return size + 5;
	}

    case T_CLASS:
	{
	    svalue_t *sv = v->u.arr->item;
	    int i = v->u.arr->size, size = 0;

	    if (++save_svalue_depth > MAX_SAVE_SVALUE_DEPTH){
		too_deep_save_error();
	    }
	    while (i--) size += svalue_save_size(sv++);
	    save_svalue_depth--;
	    return size + 5;
	}

    case T_MAPPING:
	{
	    mapping_node_t **a = v->u.map->table, *elt;
	    int j = v->u.map->table_size, size = 0;

	    if (++save_svalue_depth > MAX_SAVE_SVALUE_DEPTH){
                too_deep_save_error();
	    }
	    do {
		for (elt = a[j]; elt; elt = elt->next){
		    size += svalue_save_size(elt->values) +
			    svalue_save_size(elt->values+1);
		}
	    } while (j--);
	    save_svalue_depth--;
	    return size + 5;
	}

    case T_NUMBER:
	{
	    int res = v->u.number, len;
	    len = res < 0 ? (res = (-res) & 0x7fffffff,3) : 2;
	    while (res>9) { res /= 10; len++; }
	    return len;
	}

    case T_REAL:
	{
	    char buf[256];
	    sprintf(buf, "%g", v->u.real);
	    return (int)(strlen(buf)+1);
	}

    default:
	{
	    return 2;
	}
    }
}

INLINE void save_svalue P2(svalue_t *, v, char **, buf)
{
    switch(v->type){
    case T_STRING:
	{
	    register char *cp = *buf, *str = v->u.string;
	    char c;

	    *cp++ = '"';
	    while ((c = *str++)) {
		if (c == '"' || c == '\\'){
		    *cp++ = '\\';
		    *cp++ = c;
		}
		else *cp++ = (c == '\n') ? '\r' : c;
	    }

	    *cp++ = '"';
	    *(*buf = cp) = '\0';
	    return;
	}

    case T_ARRAY:
	{
	    int i = v->u.arr->size;
	    svalue_t *sv = v->u.arr->item;

	    *(*buf)++ = '(';
	    *(*buf)++ = '{';
	    while (i--){
		save_svalue(sv++, buf);
		*(*buf)++ = ',';
	    }
	    *(*buf)++ = '}';
	    *(*buf)++ = ')';
	    *(*buf) = '\0';
	    return;
	}

    case T_CLASS:
	{
	    int i = v->u.arr->size;
	    svalue_t *sv = v->u.arr->item;

	    *(*buf)++ = '(';
	    *(*buf)++ = '/';  /* Why yes, this *is* a kludge! */
	    while (i--){
		save_svalue(sv++, buf);
		*(*buf)++ = ',';
	    }
	    *(*buf)++ = '/';
	    *(*buf)++ = ')';
	    *(*buf) = '\0';
	    return;
	}

    case T_NUMBER:
	{
	    int res = v->u.number, fact, len = 1, neg = 0;
	    register char *cp;

	    if (res < 0) { len++, neg = 1, res = (-res) & 0x7fffffff; }
	    fact = res;
	    while (fact > 9) { fact /= 10; len++; }
	    *(cp = (*buf += len)) = '\0';
	    do {
		*--cp = res % 10 + '0';
		res /= 10;
	    } while (res);
	    if (neg) *(cp-1) = '-';
	    return;
	}

    case T_REAL:
	{
	    sprintf(*buf, "%g", v->u.real);
	    (*buf) += strlen(*buf);
	    return;
	}

    case T_MAPPING:
	{
	    int j = v->u.map->table_size;
	    mapping_node_t **a = v->u.map->table, *elt;

	    *(*buf)++ = '(';
	    *(*buf)++ = '[';
	    do {
		for (elt = a[j]; elt; elt = elt = elt->next){
		    save_svalue(elt->values, buf);
		    *(*buf)++ = ':';
		    save_svalue(elt->values + 1, buf);
		    *(*buf)++ = ',';
		}
	    } while (j--);

	    *(*buf)++ = ']';
	    *(*buf)++ = ')';
	    *(*buf) = '\0';
	    return;
	}
    }
}

INLINE static int
restore_internal_size P3(char **, str, int, is_mapping, int, depth)
{
    register char *cp = *str;
    int size = 0;
    char c, delim, index = 0;

    delim = is_mapping ? ':' : ',';
    while ((c = *cp++)) {
	switch(c){
	case '\"':
	    {
		while ((c = *cp++) != '"')
		    if ((c == '\0') || (c == '\\' && !*cp++)){
			return 0;
		    }
		if (*cp++ != delim) return 0;
		size++;
		break;
	    }

	case '(':
	    {
		if (*cp == '{'){
	            *str = ++cp;
		    if (!restore_internal_size(str, 0, save_svalue_depth++)){
			return 0;
		    }
		}
		else if (*cp == '['){
		    *str = ++cp;
		    if (!restore_internal_size(str, 1, save_svalue_depth++)){ return 0;}
		}
		else if (*cp == '/') {
		    *str = ++cp;
		    if (!restore_internal_size(str, 0, save_svalue_depth++))
			return 0;
		} else { return 0;}
		
		if (*(cp = *str) != delim){ return 0;}
		cp++;
		size++;
		break;
	    }

	case ']':
	    {
		if (*cp++ == ')' && is_mapping){
		    *str = cp;
		    if (!sizes) {
			max_depth = 128;
			while (max_depth <= depth) max_depth <<= 1;
			sizes = CALLOCATE(max_depth, int, TAG_TEMPORARY,
					  "restore_internal_size");
		    }
		    else if (depth >= max_depth){
			while ((max_depth <<= 1) <= depth);
			sizes = RESIZE(sizes, max_depth, int, TAG_TEMPORARY,
				       "restore_internal_size");
		    }
		    sizes[depth] = size;
		    return 1;
		}
		else { return 0; }
	    }

	case '/':
	case '}':
	    {
		if (*cp++ == ')' && !is_mapping){
		    *str = cp;
                    if (!sizes){
                        max_depth = 128;
                        while (max_depth <= depth) max_depth <<= 1;
			sizes = CALLOCATE(max_depth, int, TAG_TEMPORARY,
					  "restore_internal_size");
		    }
                    else if (depth >= max_depth){
                        while ((max_depth <<= 1) <= depth);
			sizes = RESIZE(sizes, max_depth, int, TAG_TEMPORARY,
				       "restore_internal_size");
		    }
                    sizes[depth] = size;
		    return 1;
		}
		else { return 0;}
	    }

	case ':':
	case ',':
	    {
		if (c != delim) return 0;
		size++;
		break;
	    }

	default:
	    {
		if (!(cp = strchr(cp, delim))) return 0;
		cp++;
		size++;
	    }
	}
	if (is_mapping) delim = (index ^= 1) ? ',' : ':';
    }
    return 0;
}



INLINE static int
restore_size P2(char **, str, int, is_mapping)
{
    register char *cp = *str;
    int size = 0;
    char c, delim, index = 0;

    delim = is_mapping ? ':' : ',';

    while ((c = *cp++)) {
	switch(c){
	case '\"':
	    {
		while ((c = *cp++) != '"')
		    if ((c == '\0') || (c == '\\' && !*cp++)) return 0;

		if (*cp++ != delim){ return -1; }
		size++;
		break;
	    }

	case '(':
	    {
		if (*cp == '{'){
	            *str = ++cp;
		    if (!restore_internal_size(str, 0, save_svalue_depth++)) return -1;
		}
		else if (*cp == '['){
		    *str = ++cp;
		    if (!restore_internal_size(str, 1, save_svalue_depth++)) return -1;
		}
		else if (*cp == '/'){
		    *str = ++cp;
		    if (!restore_internal_size(str, 0, save_svalue_depth++)) return -1;
		} else { return -1; }
		
		if (*(cp = *str) != delim) { return -1;}
		cp++;
		size++;
		break;
	    }

	case ']':
	    {
		save_svalue_depth = 0;
		if (*cp++ == ')' && is_mapping){
		    *str = cp;
		    return size;
		}
		else { return -1;}
	    }

	case '/':
	case '}':
	    {
		save_svalue_depth = 0;
		if (*cp++ == ')' && !is_mapping){
		    *str = cp;
		    return size;
		}
		else { return -1;}
	    }

	case ':':
	case ',':
	    {
		if (c != delim) return -1;
		size++;
		break;
	    }

	default:
	    {
		if (!(cp = strchr(cp, delim))) { return -1;}
		cp++;
		size++;
	    }
	}
	if (is_mapping) delim = (index ^= 1) ? ',' : ':';
    }
    return -1;
}

INLINE static int
restore_interior_string P2(char **, val, svalue_t *, sv)
{
    register char *cp = *val;
    char *start = cp;
    char c;
    int len;

    while ((c = *cp++) != '"') {
	switch (c){
	case '\r':
	    {
		*(cp-1) = '\n';
		break;
	    }

	case '\\':
	    {
		char *new = cp - 1;

		if ((*new++ = *cp++)) {
		    while ((c = *cp++) != '"'){
			if (c == '\\'){
			    if (!(*new++ = *cp++)) return ROB_STRING_ERROR;
			}
			else {
			    if (c == '\r')
				*new++ = '\n';
			    else *new++ = c;
			}
		    }
		    if (c == '\0') return ROB_STRING_ERROR;
		    *new = '\0';
		    *val = cp;
		    sv->u.string = new_string(len = (new - start),
					      "restore_string");
		    strcpy(sv->u.string, start);
		    sv->type = T_STRING;
		    sv->subtype = STRING_MALLOC;
		    return 0;
		}
		else return ROB_STRING_ERROR;
	    }

	case '\0':
	    {
		return ROB_STRING_ERROR;
	    }

	}
    }

    *val = cp;
    *--cp = '\0';
    len = cp - start;
    sv->u.string = new_string(len, "restore_string");
    strcpy(sv->u.string, start);
    sv->type = T_STRING;
    sv->subtype = STRING_MALLOC;
    return 0;
}

#define PARSE_NUMERIC(X,Y,Z) \
    { \
                int res = c - '0'; \
	\
                while ((c = *cp++) >= '0' && c <= '9'){ \
                    res *= 10, res += c - '0'; \
		} \
                if (c == '.'){ \
                    float f1 = 0.0, f2 = 10.0; \
                    int hh = 0; \
			\
                    while ((c = *cp++) >= '0' && c <= '9' && !(++hh & 8)){ \
                        f1 += (c - '0') / f2; \
                        f2 *= 10.0; \
		    } \
                    if (!hh)  \
			Z; \
		    X; \
		} \
                else { \
		    Y; \
		} \
                break; \
    } \

INLINE static void add_map_stats P2(mapping_t *, m, int, count)
{
    total_mapping_nodes += count;
    total_mapping_size += count * sizeof(mapping_node_t);
#ifdef PACKAGE_MUDLIB_STATS
    add_array_size(&m->stats, count << 1);
#endif
    m->count = count;
}

int growMap PROT((mapping_t *));

static int
restore_mapping P2(char **,str, svalue_t *, sv)
{
    int size, i, oi, mask, count = 0;
    char c;
    mapping_t *m;
    svalue_t key, value;
    mapping_node_t **a, *elt, *elt2;
    register char *cp = *str;
    int err;

    if (save_svalue_depth) size = sizes[save_svalue_depth-1]; 
    else if ((size = restore_size(str, 1)) < 0) return 0;
    
    if (!size) {
	*str += 2;
	sv->u.map = allocate_mapping(0);
	sv->type = T_MAPPING;
	return 0;
    }
    m = allocate_mapping(size >> 1); /* have to clean up after this or */
    a = m->table;                    /* we'll leak */
    mask = m->table_size;
    
    while (1) {
	switch (c = *cp++) {
	case '"':
	    {
		*str = cp;
		if ((err = restore_hash_string(str, &oi, &key)))
		    goto key_error;
		cp = *str;
		cp++;
		break;
	    }
	    
	case '(':
	    {
		save_svalue_depth++;
		if (*cp == '['){
		    *str = ++cp;
		    if ((err = restore_mapping(str, &key)))
			goto key_error;
		    oi = (POINTER_INT) key.u.map;
		}
		else if (*cp == '{'){
		    *str = ++cp;
		    if ((err = restore_array(str, &key)))
			goto key_error;
		    oi = (POINTER_INT) key.u.arr;
		}
		else if (*cp == '/') {
		    *str = ++cp;
		    if ((err = restore_class(str, &key)))
			goto key_error;
		    oi = (POINTER_INT) key.u.arr;
		}
		else goto generic_key_error;
		cp = *str;
		cp++;
		break;
	    }
	    
	case ':':
	    {
		oi = key.u.number = 0;
		key.type = T_NUMBER;
		break;
	    }
	    
	case ']':
	    *str = ++cp;
	    add_map_stats(m, count);
	    sv->type = T_MAPPING;
	    sv->u.map = m;
	    return 0;

	case '-':
	    if ((c = *cp++) < '0' || c > '9') {
		goto key_numeral_error;
	    }
	    PARSE_NUMERIC(oi = 0;key.u.real = -(f1+res); key.type = T_REAL,
			  key.type = T_NUMBER; key.u.number = -(oi = res),
			  goto key_numeral_error)
		
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	    {
		PARSE_NUMERIC(oi = 0; key.u.real = f1+res;key.type = T_REAL,
			      key.type = T_NUMBER; oi = key.u.number = res,
			      goto key_numeral_error)
	    }
	    
	default:
	    goto generic_key_error;
	}

	/* At this point, key is a valid, referenced svalue and we're
	   responsible for it */
	
	switch (c = *cp++){
	case '"':
	    {
		*str = cp;
		if ((err = restore_interior_string(str, &value)))
		    goto value_error;
		cp = *str;
		cp++;
		break;
	    }
	    
	case '(':
	    {
		save_svalue_depth++;
		if (*cp == '['){
		    *str = ++cp;
		    if ((err = restore_mapping(str, &value)))
			goto value_error;
		}
		else if (*cp == '{'){
		    *str = ++cp;
		    if ((err = restore_array(str, &value)))
			goto value_error;
		} else if (*cp == '/') {
		    *str = ++cp;
		    if ((err = restore_class(str, &value)))
			goto value_error;
		}
		else goto generic_value_error;
		cp = *str;
		cp++;
		break;
	    }
	    
	case '-':
	    {
		if ((c = *cp++) < '0' || c > '9') {
		    goto value_numeral_error;
		}
		PARSE_NUMERIC((value.u.real = -(f1+res), value.type = T_REAL),
			      (value.type = T_NUMBER, value.u.number = -res),
			      goto value_numeral_error)
	    }
	    
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	    {
		PARSE_NUMERIC((value.u.real = (f1+res),value.type = T_REAL),
			      (value.type = T_NUMBER,value.u.number = res),
			      goto value_numeral_error)
	    }
	    
	case ',':
	    {
		value.u.number = 0;
		value.type = T_NUMBER;
		break;
	    }
	    
	default:
	    goto generic_value_error;
	}

	/* both key and value are valid, referenced svalues */

	i = oi & mask;
	if ((elt2 = elt = a[i])) {
	    do {
		/* This should never happen, but don't bail on it */
		if (msameval(&key, elt->values)) {
		    free_svalue(&key, "restore_mapping: duplicate key");
		    free_svalue(elt->values+1, "restore_mapping: replaced value");
		    *(elt->values+1) = value;
		    break;
		}
	    } while ((elt = elt->next));
	    if (elt)
		continue;
	} else if (!(--m->unfilled)){
	    if (growMap(m)){
		a = m->table;
		if (oi & ++mask) elt2 = a[i |= mask];
		mask <<= 1;
		mask--;
	    } else {
		add_map_stats(m, count);
		free_mapping(m);
		free_svalue(&key, "restore_mapping: out of memory");
		free_svalue(&value, "restore_mapping: out of memory");
		error("Out of memory\n");
	    }
	}
	
	if (++count > MAX_MAPPING_SIZE) {
	    add_map_stats(m, count -1);
	    free_mapping(m);
	    free_svalue(&key, "restore_mapping: mapping too large");
	    free_svalue(&value, "restore_mapping: mapping too large");
	    mapping_too_large();
	}
	
	elt = new_map_node();
	*elt->values = key;
	*(elt->values + 1) = value;
	(a[i] = elt)->next = elt2;
    }

    /* something went wrong */
 value_numeral_error:
    free_svalue(&key, "restore_mapping: numeral value error");
 key_numeral_error:
    add_map_stats(m, count);
    free_mapping(m);
    return ROB_NUMERAL_ERROR;
 generic_value_error:
    free_svalue(&key, "restore_mapping: generic value error");
 generic_key_error:
    add_map_stats(m, count);
    free_mapping(m);
    return ROB_MAPPING_ERROR;
 value_error:
    free_svalue(&key, "restore_mapping: value error");
 key_error:
    add_map_stats(m, count);
    free_mapping(m);
    return err;
}


INLINE static int
restore_class P2(char **, str, svalue_t *, ret)
{   
    int size;
    char c;
    array_t *v;
    svalue_t *sv;
    register char *cp = *str;
    int err;

    if (save_svalue_depth) size = sizes[save_svalue_depth-1];
    else if ((size = restore_size(str,0)) < 0) return ROB_CLASS_ERROR; 

    v = allocate_array(size); /* after this point we have to clean up
				 or we'll leak */
    sv = v->item;

    while (size--) {
	switch (c = *cp++) {
	case '"':
	    *str = cp;
	    if ((err = restore_interior_string(str, sv)))
		goto generic_error;
	    cp = *str;
	    cp++;
	    sv++;
	    break;

	case ',':
	    sv++;
	    break;

	case '(':
	    {
		save_svalue_depth++;
		if (*cp == '['){
		    *str = ++cp;
		    if ((err = restore_mapping(str, sv)))
			goto error;
		}
		else if (*cp == '{'){
		    *str = ++cp;
		    if ((err = restore_array(str, sv)))
			goto error;
		}
		else if (*cp == '/') {
		    *str = ++cp;
		    if ((err = restore_class(str, sv)))
			goto error;
		}
		else goto generic_error;
		sv++;
		cp = *str;
		cp++;
		break;
	    }

	case '-':
	    {
		if ((c = *cp++) < '0' || c > '9') {
		    err = ROB_NUMERAL_ERROR;
		    goto error;
		}
		PARSE_NUMERIC((sv->u.real = -(f1+res), (sv++)->type = T_REAL),
			      ((sv++)->u.number = -res),
			      goto numeral_error)
	    }

	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	    {
		PARSE_NUMERIC((sv->u.real = (f1+res), (sv++)->type = T_REAL),
			      ((sv++)->u.number = res),
			      goto numeral_error)
	    }

	default:
	    goto generic_error;
	}
    }

    cp += 2;
    *str = cp;
    ret->u.arr = v;
    ret->type = T_CLASS;
    return 0;
    /* something went wrong */
 numeral_error:
    err = ROB_NUMERAL_ERROR;
    goto error;
 generic_error:
    err = ROB_CLASS_ERROR;
 error:
    free_array(v);
    return err;
}

INLINE static int
restore_array P2(char **, str, svalue_t *, ret)
{   
    int size;
    char c;
    array_t *v;
    svalue_t *sv;
    register char *cp = *str;
    int err;

    if (save_svalue_depth) size = sizes[save_svalue_depth-1];
    else if ((size = restore_size(str,0)) < 0) return ROB_ARRAY_ERROR; 

    v = allocate_array(size); /* after this point we have to clean up
				 or we'll leak */
    sv = v->item;

    while (size--) {
	switch (c = *cp++) {
	case '"':
	    *str = cp;
	    if ((err = restore_interior_string(str, sv)))
		goto generic_error;
	    cp = *str;
	    cp++;
	    sv++;
	    break;

	case ',':
	    sv++;
	    break;

	case '(':
	    {
		save_svalue_depth++;
		if (*cp == '['){
		    *str = ++cp;
		    if ((err = restore_mapping(str, sv)))
			goto error;
		}
		else if (*cp == '{'){
		    *str = ++cp;
		    if ((err = restore_array(str, sv)))
			goto error;
		}
		else if (*cp == '/') {
		    *str = ++cp;
		    if ((err = restore_class(str, sv)))
			goto error;
		}
		else goto generic_error;
		sv++;
		cp = *str;
		cp++;
		break;
	    }

	case '-':
	    {
		if ((c = *cp++) < '0' || c > '9') {
		    err = ROB_NUMERAL_ERROR;
		    goto error;
		}
		PARSE_NUMERIC((sv->u.real = -(f1+res), (sv++)->type = T_REAL),
			      ((sv++)->u.number = -res),
			      goto numeral_error)
	    }

	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	    {
		PARSE_NUMERIC((sv->u.real = (f1+res), (sv++)->type = T_REAL),
			      ((sv++)->u.number = res),
			      goto numeral_error)
	    }

	default:
	    goto generic_error;
	}
    }

    cp += 2;
    *str = cp;
    ret->u.arr = v;
    ret->type = T_ARRAY;
    return 0;
    /* something went wrong */
 numeral_error:
    err = ROB_NUMERAL_ERROR;
    goto error;
 generic_error:
    err = ROB_ARRAY_ERROR;
 error:
    free_array(v);
    return err;
}

INLINE int
restore_string P2(char *, val, svalue_t *, sv)
{
    register char *cp = val;
    char *start = cp;
    char c;
    int len;

    while ((c = *cp++) != '"') {
        switch (c){
	case '\r':
            {
                *(cp-1) = '\n';
                break;
	    }

	case '\\':
            {
                char *new = cp - 1;

                if ((*new++ = *cp++)) {
                    while ((c = *cp++) != '"'){
                        if (c == '\\'){
                            if (!(*new++ = *cp++)) return ROB_STRING_ERROR;
			}
                        else {
                            if (c == '\r')
                                *new++ = '\n';
                            else *new++ = c;
			}
		    }
                    if ((c == '\0') || (*cp != '\0')) return ROB_STRING_ERROR;
                    *new = '\0';
                    sv->u.string = new_string(new - start,
					      "restore_string");
                    strcpy(sv->u.string, start);
		    sv->type = T_STRING;
		    sv->subtype = STRING_MALLOC;
                    return 0;
		}
                else return ROB_STRING_ERROR;
	    }

	case '\0':
            {
                return ROB_STRING_ERROR;
	    }

	}
    }

    if (*cp--) return ROB_STRING_ERROR;
    *cp = '\0';
    len = cp - start;
    sv->u.string = new_string(len, "restore_string");
    strcpy(sv->u.string, start);
    sv->type = T_STRING;
    sv->subtype = STRING_MALLOC;
    return 0;
}

/* for this case, the variable in question has been set to zero already,
   and we don't have to worry about preserving it */
INLINE int
restore_svalue P2(char *, cp, svalue_t *, v)
{
    int ret;
    
    switch(*cp++) {
    case '\"':
	return restore_string(cp, v);
    case '(':
	if (*cp == '{') {
	    cp++;
	    ret = restore_array(&cp, v);
	} else if (*cp == '[') {
	    cp++;
	    ret = restore_mapping(&cp, v);
	} else if (*cp++ == '/') {
	    ret = restore_class(&cp, v);
	}
	else ret = ROB_GENERAL_ERROR;

	if (save_svalue_depth) {
	    save_svalue_depth = max_depth = 0;
	    if (sizes)
		FREE((char *) sizes);
	    sizes = (int *) 0;
	}
	return ret;
	
    case '-':
	{
	    char c;

	    if ((c = *cp++) < '0' || (c > '9')) return ROB_NUMERAL_ERROR;
	    PARSE_NUMERIC((v->type = T_REAL, v->u.real = -f1-res),
			  (v->type = T_NUMBER, v->u.number = -res),
			  return ROB_NUMERAL_ERROR);
	}

    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
        {
	    char c = *(cp-1);
	    PARSE_NUMERIC((v->type = T_REAL, v->u.real = f1+res),
			  (v->type = T_NUMBER, v->u.number = res),
			  return ROB_NUMERAL_ERROR);
	}

    default: {
	    v->type = T_NUMBER;
	    v->u.number = 0;
	}
    }

    return 0;
}

/* for this case, we're being careful and want to leave the value alone on
   an error */
INLINE int
safe_restore_svalue P2(char *, cp, svalue_t *, v)
{
    int ret;
    svalue_t val;

    val.type = T_NUMBER;
    switch(*cp++) {
    case '\"':
	if ((ret = restore_string(cp, &val))) return ret;
	break;
    case '(':
	{
	    if (*cp == '{'){
		cp++;
		ret = restore_array(&cp, &val);
	    } else if (*cp == '[') {
		cp++;
		ret = restore_mapping(&cp, &val);
	    } else if (*cp++ == '/') {
		ret = restore_class(&cp, &val);
	    }
	    else return ROB_GENERAL_ERROR;

	    if (save_svalue_depth) {
		save_svalue_depth = max_depth = 0;
		if (sizes)
		    FREE((char *) sizes);
		sizes = (int *) 0;
	    }
	    if (ret) 
		return ret;
	    break;
	}
	
    case '-':
	{
	    char c;
	    
	    if ((c = *cp++) < '0' || (c > '9')) return ROB_NUMERAL_ERROR;
	    PARSE_NUMERIC((val.type = T_REAL, val.u.real = -f1-res),
			  (val.type = T_NUMBER, val.u.number = -res),
			  return ROB_NUMERAL_ERROR);
	}
	
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
        {
	    char c = *(cp-1);
	    PARSE_NUMERIC((val.type = T_REAL, val.u.real = f1+res),
			  (val.type = T_NUMBER, val.u.number = res),
			  return ROB_NUMERAL_ERROR);
	}

    default:
	val.type = T_NUMBER;
	val.u.number = 0;
    }
    free_svalue(v, "safe_restore_svalue");
    *v = val;
    return 0;
}

static int var_index = 0;
/* optimized to start where we left off, so that walking the variables in
   order is O(N) instead of O(N^2) -Beek */

variable_t *find_status P1(char *, str) {
    int i;
    variable_t *vars = current_object->prog->variable_names;
    int n = current_object->prog->num_variables;
    
    if ((str = findstring(str))) {
	for (i=var_index; i < n; i++) {
	    if (vars[i].name == str) {
		var_index = i+1;
		return &vars[i];
	    }
	}
	for (i=0; i < var_index; i++) {
	    if (vars[i].name == str) {
		var_index = i+1;
		return &vars[i];
	    }
	}
    }
    /* leave var_index alone; they might have just deleted this var */
    return 0;
}

void
restore_object_from_buff P4(object_t *, ob, char *, theBuff, char *, name,
			    int, noclear)
{
    char *buff, *nextBuff, *tmp,  *space;
    char var[100];
    variable_t *p, *var_start = ob->prog->variable_names;
    svalue_t *sv = ob->variables;
    int rc;
 
    var_index = 0;

    nextBuff = theBuff;
    while ((buff = nextBuff) && *buff) {
        svalue_t *v;
 
        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 || ((space - buff) >= sizeof(var))) {
            FREE(name);
            FREE(theBuff);
            error("restore_object(): Illegal file format.\n");
        }
        (void)strncpy(var, buff, space - buff);
        var[space - buff] = '\0';
        p = find_status(var);
        if (!p || (p->type & TYPE_MOD_STATIC))
            continue;

        v = &sv[p - var_start];
	if (noclear)
	    rc = safe_restore_svalue(space+1, v);
	else
	    rc = restore_svalue(space+1, v);
        if (rc & ROB_ERROR) {
            FREE(name);
            FREE(theBuff);

	    if (rc & ROB_GENERAL_ERROR)
		error("restore_object(): Illegal general format while restoring %s.\n", var);
	    else if (rc & ROB_NUMERAL_ERROR)
		error("restore_object(): Illegal numeric format while restoring %s.\n", var);
	    else if (rc & ROB_ARRAY_ERROR)
                error("restore_object(): Illegal array format while restoring %s.\n", var);
            else if (rc & ROB_MAPPING_ERROR)
                error("restore_object(): Illegal mapping format while restoring %s.\n", var);
	    else if (rc & ROB_STRING_ERROR)
		error("restore_object(): Illegal string format while restoring %s.\n", var);
	    else if (rc & ROB_CLASS_ERROR)
		error("restore_object(): Illegal class format while restoring %s.\n", var);
        }
    }
}

/*
 * 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 P3(object_t *, ob, char *, file, int, save_zeros)
{
    char *name;
    static char tmp_name[80];
    int len, i;
    FILE *f;
    int failed = 0;
    char *use_name, *new_str, *p;
    int free_use_name = 0, theSize;
    variable_t *var = ob->prog->variable_names;
    svalue_t *v = ob->variables;

    if (ob->flags & O_DESTRUCTED)
        return 0;
    if (file[0] != '/')
    {
	use_name = DXALLOC((len = strlen(file)) + 2, TAG_TEMPORARY, "save_object: 1");
	strcpy(use_name, "/");
	strcpy(use_name+1, file);
	free_use_name = 1;
    } else {
	use_name = file;
	len = 0;
    }

    file = check_valid_path(use_name, ob, "save_object", 1);
    /* WARNING: file may point at use_name */

    if (file == 0) {
	if (free_use_name)
	    FREE(use_name);
        error("Denied write permission in save_object().\n");
    }
    if (!len) len = strlen(file);
    name = DXALLOC(len + strlen(SAVE_EXTENSION) + 1, TAG_TEMPORARY, "save_object: 1");
    (void)strcpy(name, file);
    (void)strcat(name + len, SAVE_EXTENSION);

    /* we don't use file after this.  It's safe to free it. */
    if (free_use_name)
	FREE(use_name);
    /*
     * Write the save-files to different directories, just in case
     * they are on different file systems.
     */
    sprintf(tmp_name, "%s.tmp", name);

#ifdef WIN32
    if (!(f = fopen(tmp_name, "wb"))){
#else
    if (!(f = fopen(tmp_name, "w"))){
#endif
	FREE(name);
        error("Could not open /%s for a save.\n", tmp_name);
    }
    fprintf(f, "#%s\n", ob->prog->name);

    i = ob->prog->num_variables;
    while (i--){
	if (var->type & TYPE_MOD_STATIC) { v++; var++; continue; }

	save_svalue_depth = 0;
	theSize = svalue_save_size(v);
	new_str = (char *)DXALLOC(theSize, TAG_TEMPORARY, "save_object: 2");
	*new_str = '\0';
	p = new_str;	
	save_svalue(v++, &p);
	if (save_zeros || strcmp(new_str,"0")) /* Armidale */
	    if (fprintf(f, "%s %s\n", var->name, new_str) == EOF) failed = 1;
	FREE(new_str);
	var++;
    }
    if (failed) 
	debug_message("Failed to completely save file. Disk could be full.\n");
    else {
	(void) fclose(f);
#ifdef WIN32
        /* Need to erase it to write over it. */
        unlink(name);
#endif
	if (rename(tmp_name, name) < 0)
	{
#ifdef LATTICE
                /* AmigaDOS won't overwrite when renaming */
                if (errno == EEXIST) {
                    unlink(name);
                    if (rename(tmp_name, name) >= 0) {
                        FREE(name);
                        return 1;
                    }
                }
#endif
		debug_perror("save_object", name);
		debug_message("Failed to rename /%s to /%s\n", tmp_name, name);
		debug_message("Failed to save object!\n");
	}
    }
    FREE(name);
    if (failed) {   
	debug_message("Failed to save to file. Disk could be full.\n");
	return 0;
    }
    return 1;
}


/*
 * return a string representing an svalue in the form that save_object()
 * would write it.
 */
char *
save_variable P1(svalue_t *, var)
{
    int theSize;
    char *new_str, *p;
    
    save_svalue_depth = 0;
    theSize = svalue_save_size(var);
    new_str = new_string(theSize - 1, "save_variable");
    *new_str = '\0';
    p = new_str;
    save_svalue(var, &p);
    return new_str;
}


int restore_object P3(object_t *, ob, char *, file, int, noclear)
{
    char *name, *theBuff;
    int len, i;
    FILE *f;
    object_t *save = current_object;
    struct stat st;

    if (ob->flags & O_DESTRUCTED)
        return 0;

    file = check_valid_path(file, ob, "restore_object", 0);
    if (!file) error("Denied read permission in restore_object().\n");

    len = strlen(file);
    name = DXALLOC(len + strlen(SAVE_EXTENSION) + 1, TAG_TEMPORARY, "restore_object: 2");
    (void)strcpy(name, file);
    if (name[len-2] == '.' && name[len - 1] == 'c')
        name[len-2] = 0;
    (void)strcat(name, SAVE_EXTENSION);
#ifdef LATTICE
    f = NULL;
    if ((stat(name, &st) == -1) || !(f = fopen(name, "r"))) {
#else
    f = fopen(name, "r");
    if (!f || fstat(fileno(f), &st) == -1) {
#endif
        FREE(name);
        if (f) 
            (void)fclose(f);
        return 0;
    }

    if (!(i = st.st_size)) {
        (void)fclose(f);
        FREE(name);
        return 0;
    }
    theBuff = DXALLOC(i + 1, TAG_TEMPORARY, "restore_object: 4");
    fread(theBuff, 1, i, f);
    fclose(f);
    theBuff[i] = '\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) {
	variable_t *v = ob->prog->variable_names;
	svalue_t *sv = ob->variables;

	i = ob->prog->num_variables; 
	while (i--) {
	    if (!((v++)->type & TYPE_MOD_STATIC))
		assign_svalue(sv++, &const0n);
	    else sv++;
	}
    }
    
    restore_object_from_buff(ob, theBuff, name, noclear);
    current_object = save;
#ifdef DEBUG
    if (d_flag > 1)
        debug_message("Object /%s restored from /%s.\n", ob->name, name);
#endif
    FREE(name);
    FREE(theBuff);
    return 1;
}

void restore_variable P2(svalue_t *, var, char *, str)
{
    int rc;

    rc = restore_svalue(str, var);
    if (rc & ROB_ERROR) {
	*var = const0; /* clean up */
	if (rc & ROB_GENERAL_ERROR)
	    error("restore_object(): Illegal general format.\n");
	else if (rc & ROB_NUMERAL_ERROR)
	    error("restore_object(): Illegal numeric format.\n");
	else if (rc & ROB_ARRAY_ERROR)
	    error("restore_object(): Illegal array format.\n");
	else if (rc & ROB_MAPPING_ERROR)
	    error("restore_object(): Illegal mapping format.\n");
	else if (rc & ROB_STRING_ERROR)
	    error("restore_object(): Illegal string format.\n");
    }
}

void tell_npc P2(object_t *, ob, char *, str)
{
    push_string(str, STRING_MALLOC);
    (void) apply(APPLY_CATCH_TELL, ob, 1, ORIGIN_DRIVER);
}

 /* save some space snoop */
#define ALM_BREAK LARGEST_PRINTABLE_STRING - 10

static void add_long_message P2(object_t *, who, char *, s) {
    char save;
    int len;

    len = strlen(s);
    while (len > ALM_BREAK) {
	save = s[ALM_BREAK];
	s[ALM_BREAK] = 0;
	add_message(who, s);
	s[ALM_BREAK] = save;
	s += ALM_BREAK;
	len -= ALM_BREAK;
    }
    add_message(who, s);
}

/*
 * 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 P2(object_t *, ob, char *, str)
{
    if (!ob || (ob->flags & O_DESTRUCTED)) {
	add_long_message(0, str);
	return;
    }
#ifdef INTERACTIVE_CATCH_TELL
    tell_npc(ob, str);
    return;
#else
    if (ob->interactive) {
	add_long_message(ob, str);
	return;
    }
    tell_npc(ob, str);
#endif
}

void dealloc_object P2(object_t *, ob, char *, from)
{
#ifndef NO_ADD_ACTION
    sentence_t *s;
#endif

#ifdef DEBUG
    if (d_flag)
	debug_message("free_object: %s.\n", ob->name);
#endif
    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);
    }
    DEBUG_CHECK(ob->interactive, "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->num_variables - 1) * sizeof(svalue_t) +
	    sizeof(object_t);
	free_prog(ob->prog, 1);
	ob->prog = 0;
    }
#ifndef NO_ADD_ACTION
    for (s = ob->sent; s;) {
	sentence_t *next;
	
	next = s->next;
	free_sentence(s);
	s = next;
    }
#endif
#ifdef PRIVS
    if (ob->privs)
	free_string(ob->privs);
#endif
    if (ob->name) {
#ifdef DEBUG
	if (d_flag > 1)
	    debug_message("Free object %s\n", ob->name);
#endif
	DEBUG_CHECK1(lookup_object_hash(ob->name) == ob,
		     "Freeing object %s but name still in name table", ob->name);
	FREE(ob->name);
	ob->name = 0;
    }
    tot_alloc_object--;
    FREE((char *) ob);
}

void free_object P2(object_t *, ob, char *, from)
{
    ob->ref--;

    if (ob->ref > 0)
	return;
    dealloc_object(ob, from);
}

/*
 * Allocate an empty object, and set all variables to 0. Note that a
 * 'object_t' already has space for one variable. So, if no variables
 * are needed, we allocate a space that is smaller than 'object_t'. This
 * unused (last) part must of course (and will not) be referenced.
 */
object_t *get_empty_object P1(int, num_var)
{
    static object_t NULL_object;
    object_t *ob;
    int size = sizeof(object_t) +
    (num_var - !!num_var) * sizeof(svalue_t);
    int i;

    tot_alloc_object++;
    tot_alloc_object_size += size;
    ob = (object_t *) DXALLOC(size, TAG_OBJECT, "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;
}

#ifndef NO_ADD_ACTION
static object_t *hashed_living[LIVING_HASH_SIZE];

static int num_living_names, num_searches = 1, search_length = 1;

static INLINE int hash_living_name P1(char *, str)
{
    return whashstr(str, 20) & (LIVING_HASH_SIZE - 1);
}

object_t *find_living_object P2(char *, str, int, user)
{
    object_t **obp, *tmp;
    object_t **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 P2(object_t *, ob, char *, str)
{
    object_t **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 P1(object_t *, ob)
{
    object_t **hl;

    num_living_names--;
    DEBUG_CHECK(!ob->living_name, "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;
    }
    DEBUG_CHECK1(*hl == 0, 
		 "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 P1(outbuffer_t *, out)
{
    outbuf_add(out, "Hash table of living objects:\n");
    outbuf_add(out, "-----------------------------\n");
    outbuf_addv(out, "%d living named objects, average search length: %4.2f\n",
		num_living_names, (double) search_length / num_searches);
}
#endif /* NO_ADD_ACTION */

void reset_object P1(object_t *, ob)
{
    object_t *save_command_giver;

    /* Be sure to update time first ! */
    ob->next_reset = current_time + TIME_TO_RESET / 2 +
	random_number(TIME_TO_RESET / 2);

    save_command_giver = command_giver;
    command_giver = (object_t *) 0;
    if (!apply(APPLY_RESET, ob, 0, ORIGIN_DRIVER)) {
	/* 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;
}

void call_create P2(object_t *, ob, int, num_arg)
{
    /* Be sure to update time first ! */
    ob->next_reset = current_time + TIME_TO_RESET / 2 +
	random_number(TIME_TO_RESET / 2);

    call___INIT(ob);

    if (ob->flags & O_DESTRUCTED) {
	pop_n_elems(num_arg);
	return; /* sigh */
    }

    apply(APPLY_CREATE, ob, num_arg, ORIGIN_DRIVER);

    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 P2(object_t *, 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(APPLY_CATCH_TELL, ob)) {
	    push_constant_string(str);
	    if (apply(APPLY_CATCH_TELL, ob, 1, ORIGIN_DRIVER))	
		/* this will work, since we know the */
		/* function is defined */
		return 1;
	}
	ob = ob->shadowing;
    }
    return 0;
}
#endif

INLINE int object_visible P1(object_t *, 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 P1(object_t *, obj)
{
    int i;

    if (!obj->prog)
	return;
    for (i = 0; i < (int) obj->prog->num_variables; i++) {
	free_svalue(&obj->variables[i], "reload_object");
	obj->variables[i] = const0n;
    }
#ifdef PACKAGE_SOCKETS
    if (obj->flags & O_EFUN_SOCKET) {
	close_referencing_sockets(obj);
    }
#endif

    if (obj->flags & O_SWAPPED)
	load_ob_from_swap(obj);
    
    /*
     * 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) {
	svalue_t svp;
	object_t *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
#ifndef NO_ADD_ACTION
    if (obj->living_name)
	remove_living_name(obj);
    obj->flags &= ~O_ENABLE_COMMANDS;
#endif
    set_heart_beat(obj, 0);
    remove_all_call_out(obj);
#ifndef NO_LIGHT
    add_light(obj, -(obj->total_light));
#endif
#ifdef PACKAGE_UIDS
#ifdef AUTO_SETEUID
    obj->euid = obj->uid;
#else
    obj->euid = NULL;
#endif
#endif
    call_create(obj, 0);
}