#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);
}