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