#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <setjmp.h> #include <string.h> #include <errno.h> #include <stdio.h> #include <memory.h> #include <varargs.h> #if defined(sun) #include <alloca.h> #endif #if defined(M_UNIX) || defined(_SEQUENT_) || defined(SOLARIS) || defined(__NetBSD__) #include <dirent.h> #else #include <sys/dir.h> #endif #include "config.h" #include "lint.h" #include "stdio.h" #include "interpret.h" #include "object.h" #include "sent.h" #include "exec.h" #include "comm.h" #include "mudstat.h" #include "incralloc.h" extern int errno; extern int comp_flag; char *inherit_file; #if !defined(NeXT) && !defined(LINUX) extern int lstat (const char *, struct stat *); #else #define lstat stat #endif #ifndef LINUX extern int fchmod (int, mode_t); #endif char *last_verb; extern int current_time, tot_alloc_dest_object, tot_removed_object; extern int special_parse (char *), set_call (struct object *, struct sentence *, int), legal_path (char *); void pre_compile (char *), remove_interactive (struct object *, int); int add_action (struct svalue *, struct svalue *, int); void ipc_remove(), show_info_about (char *, char *, struct interactive *), print_lnode_status (int), remove_all_players(), end_new_file(), load_ob_from_swap (struct object *), print_svalue (struct svalue *, struct object *), debug_message_value(), destruct2(); char *dump_malloc_data(); void start_new_file (FILE *); extern int d_flag, s_flag; struct object *obj_list, *obj_list_destruct, *master_ob; struct object *current_object; /* The object interpreting a function. */ struct object *command_giver; /* Where the current command came from. */ struct object *current_interactive; /* The user who caused this execution */ int num_parse_error; /* Number of errors in the parser. */ void shutdowngame(); extern void flush_all_player_mess(); struct object* find_object_no_create(); int find_status(struct program *prog, char *str, int not_type) { int i, j; char *super_name = 0; char *sub_name; char *real_name; char *search; variable_index_found = variable_inherit_found = 255; real_name = strrchr(str, ':') + 1; sub_name = strchr(str, ':') + 2; if(!(real_name = (findstring((real_name == (char *)1) ? str : real_name)))) return -1; if (sub_name == (char *)2) { access_program(prog); for (i = 0; i < (int)prog->num_variables; i++) { if (prog->variable_names[i].name == real_name && !(prog->variable_names[i].type & not_type)) { variable_index_found = i; variable_inherit_found = prog->num_inherited - 1; variable_type_mod_found = prog->variable_names[i].type; return prog->inherit[variable_inherit_found].variable_index_offset + i; } } } else if (sub_name - str > 2) { super_name = xalloc(sub_name - str - 1); memcpy(super_name, str, sub_name - str - 2); super_name[sub_name - str - 2] = 0; if (!strcmp(super_name, "this")) return find_status(prog, sub_name, not_type); } else str = sub_name; for(j = prog->num_inherited - 2; j >= 0 ;j -= prog->inherit[j].prog->num_inherited) { int type; if (super_name && !strcmp(super_name, prog->inherit[j].name)) search = sub_name; else search = str; if (find_status(prog->inherit[j].prog, search, not_type) != -1) { int type = prog->inherit[j].type; if (variable_type_mod_found & TYPE_MOD_PUBLIC) type &= ~TYPE_MOD_PRIVATE; if (variable_type_mod_found & TYPE_MOD_PRIVATE) type &= ~TYPE_MOD_PUBLIC; variable_type_mod_found |= type & TYPE_MOD_MASK; if (variable_type_mod_found & not_type) continue; variable_inherit_found += j - (prog->inherit[j].prog->num_inherited - 1); return prog->inherit[variable_inherit_found].variable_index_offset + variable_index_found; } } return -1; } void swap_objects(struct object *ob1, struct object *ob2) { extern void call_out_swap_objects(struct object *, struct object *); extern void stack_swap_objects(struct object *, struct object *); struct program *tmp_prog; struct svalue *tmp_var; remove_ob_from_swap(ob1, 1); remove_ob_from_swap(ob2, 1); /* swap the objects */ call_out_swap_objects(ob1, ob2); stack_swap_objects(ob1, ob2); tmp_var = ob1->variables; ob1->variables = ob2->variables; ob2->variables = tmp_var; tmp_prog = ob1->prog; ob1->prog = ob2->prog; ob2->prog = tmp_prog; /* Swap the O_CREATED bit */ ob1->flags ^= ob2->flags & O_CREATED; ob2->flags ^= ob1->flags & O_CREATED; ob1->flags ^= ob2->flags & O_CREATED; } /* * Load an object definition from file. If the object wants to inherit * from an object that is not loaded, discard all, load the inherited object, * and reload again. * * In mudlib3.0 when loading inherited objects, their reset() is not called. * * Save the command_giver, because reset() in the new object might change * it. * * */ char *current_loaded_file = 0; #ifdef BINARIES extern int save_binary(struct program *); #endif struct object * load_object(char *lname, int dont_reset, struct object *old_ob) { FILE *f; extern int total_lines; extern int approved_object; struct object *ob, *save_command_giver = command_giver; extern struct program *prog; extern char *current_file; struct stat c_st; int name_length; char real_name[200], name[200]; extern struct object *swap_ob; char new_ob_name[200]; extern int pragma_resident; extern void init_smart_log(), dump_smart_log(); #ifdef BINARIES extern int pragma_save_binary, driver_mtime; int errbin = 1; int file_mtime; char *binname; FILE *binfile; #endif /* Truncate possible .c in the object name. */ /* Remove leading '/' if any. */ while(lname[0] == '/') lname++; strncpy(name, lname, sizeof(name) - 1); name[sizeof name - 1] = '\0'; name_length = strlen(name); if (name_length > sizeof name - 4) name_length = sizeof name - 4; name[name_length] = '\0'; if (name[name_length-2] == '.' && name[name_length-1] == 'c') { name[name_length-2] = '\0'; name_length -= 2; } if (old_ob) { struct object *invalid_ob; strcpy(new_ob_name, name); strcat(new_ob_name, "#0"); if (invalid_ob = find_object2(new_ob_name)) { destruct_object(invalid_ob); } } /* * First check that the c-file exists. */ (void)strcpy(real_name, name); (void)strcat(real_name, ".c"); if (stat(real_name, &c_st) == -1) { fprintf(stderr, "Could not load descr for %s\n", real_name); error("Failed to load file.\n"); return 0; } #ifdef BINARIES file_mtime = c_st.st_mtime; #endif /* * Check if it is a legal name. */ if (!legal_path(real_name)) { fprintf(stderr, "Illegal pathname: %s\n", real_name); error("Illegal path name.\n"); return 0; } #ifdef BINARIES binname = (char *)xalloc(strlen(BINARIES) + strlen(real_name) + 1); sprintf(binname, "%s%s", BINARIES, real_name); errbin = 1; if (stat(binname, &c_st) != -1) if ((file_mtime < c_st.st_mtime)) if ((binfile = fopen(binname, "r")) != NULL) { errbin = load_binary(binfile, real_name); if (!errbin && !inherit_file) { if (comp_flag) fprintf(stderr," %s loaded from binary: %s\n", real_name, binname); } } free(binname); pragma_save_binary = 0; if (errbin) { #endif /* BINARIES */ if (comp_flag) fprintf(stderr, " compiling %s ...", real_name); f = fopen(real_name, "r"); if (s_flag) { num_fileread++; num_compile++; } if (f == 0) { perror(real_name); error("Could not read the file.\n"); } init_smart_log(); start_new_file(f); current_file = string_copy(real_name); /* This one is freed below */ current_loaded_file = real_name; compile_file(); end_new_file(); current_loaded_file = 0; if (comp_flag) { if (inherit_file == 0) fprintf(stderr, " done\n"); else fprintf(stderr, " suspended, compiling inherited file(s)\n"); } update_compile_av(total_lines); total_lines = 0; (void)fclose(f); free(current_file); current_file = 0; dump_smart_log(); /* Sorry, can not handle objects without programs yet. */ if (inherit_file == 0 && (num_parse_error > 0 || prog == 0)) { if (prog) free_prog(prog, 1); if (num_parse_error == 0 && prog == 0) error("No program in object !\n"); error("Error in loading object\n"); } #ifdef BINARIES } #endif /* * This is an iterative process. If this object wants to inherit an * unloaded object, then discard current object, load the object to be * inherited and reload the current object again. The global variable * "inherit_file" will be set by lang.y to point to a file name. */ if (inherit_file) { char *tmp = inherit_file; if (prog) { free_prog(prog, 1); prog = 0; } { char *t1 = tmp, *t2 = strrchr(tmp,'\000'); while (*t1=='/') t1++; if (t2-t1 > 1) if (*(--t2)=='c') if (*(--t2)=='.') *t2='\000'; if (strcmp(t1, name) == 0) { free(inherit_file); inherit_file = 0; error("Illegal to inherit self.\n"); } } /* Extreme ugliness begins. inherit_file must be 0 when we call load_object, but tmp is used as a parameter, so we can not free the string until after the call */ inherit_file = 0; load_object(tmp, 1, 0); free(tmp); /* Extreme ugliness ends */ ob = load_object(name, dont_reset, old_ob); return ob; } ob = get_empty_object(); if (!old_ob) ob->name = string_copy(name); /* Shared string is no good here */ else { ob->name = xalloc(strlen(name) + 3); strcpy(ob->name, name); strcat(ob->name, "#0"); } ob->prog = prog; #ifdef BINARIES if (pragma_save_binary) { save_binary(ob->prog); pragma_save_binary = 0; } #endif /* BINARIES */ swap_lineno(ob->prog); if (obj_list) { ob->next_all = obj_list; ob->prev_all = obj_list->prev_all; obj_list->prev_all->next_all = ob; obj_list->prev_all = ob; obj_list = ob; } else obj_list = swap_ob = ob->next_all = ob->prev_all = ob; if (current_object) access_object(current_object); /* add name to fast object lookup table */ enter_object_hash(ob); { int num_var, i; extern int tot_alloc_variable_size; extern struct svalue const0; num_var = ob->prog->num_variables + ob->prog->inherit[ob->prog->num_inherited - 1] .variable_index_offset + 1; if (ob->variables) fatal("Object allready initialized!\n"); ob->variables = (struct svalue *) xalloc(num_var * sizeof(struct svalue)); tot_alloc_variable_size += num_var * sizeof(struct svalue); for (i= 0; i<num_var; i++) ob->variables[i] = const0; ob->variables++; } /* We ought to add a flag here marking the object as unfinished so it can be removed if the following code causes an LPC error */ if (master_ob) { push_object(current_object); push_object(ob); apply_master_ob(M_LOADED_OBJECT, 2); } if (!(ob->flags & (O_CREATED | O_DESTRUCTED)) && !dont_reset) create_object(ob); if (ob->flags & O_DESTRUCTED) return 0; command_giver = save_command_giver; if (d_flag & DEBUG_LOAD && ob) debug_message("--%s loaded\n", ob->name); if (master_ob && ob->prog->flags & PRAGMA_RESIDENT) { struct svalue *ret; push_object(ob); ret = apply_master_ob(M_VALID_RESIDENT, 1); if (!ret || ret->u.number == 0) ob->prog->flags &= ~PRAGMA_RESIDENT; } return ob; } char * make_new_name(char *str) { static int i = 1; char *p = xalloc(strlen(str) + 10); (void) sprintf(p, "%s#%d", str, i); i++; return p; } /* * Save the command_giver, because reset() in the new object might change * it. */ struct object * clone_object(char *str1) { struct object *ob, *new_ob; struct object *save_command_giver = command_giver; extern struct object *swap_ob; ob = find_object_no_create(str1); if (ob == 0 || ob->super || (ob->flags & O_CLONE) || ob->prog->flags & PRAGMA_NO_CLONE) error("Cloning a bad object! (No Master, Master in environment or Master is clone)\n"); /* We do not want the heart beat to be running for unused copied objects */ new_ob = get_empty_object(); new_ob->name = make_new_name(ob->name); new_ob->flags |= O_CLONE; new_ob->prog = ob->prog; reference_prog (ob->prog, "clone_object"); if (!current_object) fatal("clone_object() from no current_object !\n"); if (obj_list) { new_ob->next_all = obj_list; new_ob->prev_all = obj_list->prev_all; obj_list->prev_all->next_all = new_ob; obj_list->prev_all = new_ob; obj_list = new_ob; } else obj_list = swap_ob = new_ob->next_all = new_ob->prev_all = new_ob; enter_object_hash(new_ob); /* Add name to fast object lookup table */ { int num_var, i; extern int tot_alloc_variable_size; extern struct svalue const0; num_var = new_ob->prog->num_variables + new_ob->prog->inherit[ob->prog->num_inherited - 1] .variable_index_offset + 1; if (new_ob->variables) fatal("Object allready initialized!\n"); new_ob->variables = (struct svalue *) xalloc(num_var * sizeof(struct svalue)); tot_alloc_variable_size += num_var * sizeof(struct svalue); for (i= 0; i<num_var; i++) new_ob->variables[i] = const0; new_ob->variables++; } if (master_ob) { push_object(current_object); push_object(new_ob); apply_master_ob(M_CLONED_OBJECT, 2); if (new_ob->flags & O_DESTRUCTED) return 0; } if (!(ob->flags & O_CREATED)) create_object(new_ob); command_giver = save_command_giver; /* Never know what can happen ! :-( */ if (new_ob->flags & O_DESTRUCTED) return 0; return new_ob; } struct object * environment(struct svalue *arg) { struct object *ob = current_object; if (arg && arg->type == T_OBJECT) ob = arg->u.ob; else if (arg && arg->type == T_STRING) ob = find_object2(arg->u.string); if (ob == 0 || ob->super == 0 || (ob->flags & O_DESTRUCTED)) return 0; if (ob->flags & O_DESTRUCTED) error("environment() off destructed object.\n"); return ob->super; } /* * Execute a command for an object. Copy the command into a * new buffer, because 'parse_command()' can modify the command. * If the object is not current object, static functions will not * be executed. This will prevent forcing players to do illegal things. * * Return cost of the command executed if success (> 0). * When failure, return 0. */ int command_for_object(char *str, struct object *ob) { char buff[1000]; extern int eval_cost; int save_eval_cost = eval_cost - 1000; if (strlen(str) > sizeof(buff) - 1) error("Too long command.\n"); if (ob == 0) ob = current_object; else if (ob->flags & O_DESTRUCTED) return 0; strncpy(buff, str, sizeof buff); buff[sizeof buff - 1] = '\0'; if (parse_command(buff, ob)) return eval_cost - save_eval_cost; else return 0; } /* * Search the inventory of the second argument 'ob' for instances * of the first. */ static struct object *object_present2 (char *, struct object *); struct object * object_present(struct svalue *v, struct svalue *where) { int i; struct object *ret; if (v->type == T_OBJECT) if (v->u.ob->flags & O_DESTRUCTED) return 0; switch (where->type) { case T_OBJECT: if (where->u.ob->flags & O_DESTRUCTED) return 0; if (v->type == T_OBJECT) { if (v->u.ob->super == where->u.ob) return v->u.ob; else return 0; } else return object_present2(v->u.string, where->u.ob->contains); break; case T_POINTER: if (v->type == T_OBJECT) { for (i = 0 ; i < where->u.vec->size ; i++) { if (where->u.vec->item[i].type != T_OBJECT) continue; if (v->u.ob->super == where->u.vec->item[i].u.ob) return v->u.ob; } } else { for (i = 0 ; i < where->u.vec->size ; i++) { if (where->u.vec->item[i].type != T_OBJECT) continue; if (ret = object_present2(v->u.string, where->u.vec->item[i].u.ob->contains)) return ret; } } return 0; break; default: error("Strange type %d in object_present.\n", where->type); return 0; break; } } static struct object * object_present2(char *str, struct object *ob) { struct svalue *ret; char *p; int count = 0, length; char *item; item = string_copy(str); length = strlen(item); p = item + length - 1; if (*p >= '0' && *p <= '9') { while(p > item && *p >= '0' && *p <= '9') p--; if (p > item && *p == ' ') { count = atoi(p+1) - 1; *p = '\0'; length = p - item; /* This is never used again ! */ } } for (; ob; ob = ob->next_inv) { push_string(item, STRING_MALLOC); ret = apply("id", ob, 1, 1); if (ob->flags & O_DESTRUCTED) { free(item); return 0; } if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0)) continue; if (count-- > 0) continue; free(item); return ob; } free(item); return 0; } /* * Remove an object. It is first moved into the destruct list, and * not really destructed until later. (see destruct2()). * * We will simply not allow the master object to be destructed, Ha! */ #ifdef BINARIES extern int save_binary(struct program *); #endif void destruct_object(struct object *ob) { extern struct object *swap_ob; extern struct object *vbfc_object; struct svalue *ret; struct object *super; struct object **pp; int removed; char *name = 0; int i; FILE *f; if (!ob || ob->flags & O_DESTRUCTED) return; /* * If we are to destruct the master ob or the simul_efun ob * it must be reloadable. */ if ( (!(ob->flags & O_CLONE) && ob->prog->flags & PRAGMA_RESIDENT) || ob == master_ob || ob == (struct object *)query_simul_efun_ob()) { struct object *new_ob; new_ob = load_object(ob->name, 1, ob); if (!new_ob) error("Can not compile the new %s, aborting destruction of the old.\n", ob->prog->name); /* Make sure that the master object and the simul_efun object * stays loaded */ if (ob == master_ob || ob == (struct object *)query_simul_efun_ob()) ob->prog->flags |= PRAGMA_RESIDENT; swap_objects(ob, new_ob); if (ob == master_ob) { extern void master_ob_loaded(void); master_ob_loaded(); } if (new_ob && new_ob->flags & O_CREATED) recreate_object(ob, new_ob); ob = new_ob; ob->prog->flags &= ~PRAGMA_RESIDENT; } push_object(ob); apply_master_ob(M_DESTRUCTED_OBJECT, 1); if (ob->flags & O_DESTRUCTED) return; /* * If this is the first object being shadowed by another object, then * destruct the whole list of shadows. */ if (ob->shadowed && !ob->shadowing) { struct svalue svp; struct object *ob2; svp.type = T_OBJECT; for (ob2 = ob->shadowed; ob2; ) { svp.u.ob = ob2; ob2 = ob2->shadowed; if (svp.u.ob->shadowed) { free_object(svp.u.ob->shadowed, "destruct_object-5"); svp.u.ob->shadowed = 0; } if (svp.u.ob->shadowing) { free_object(svp.u.ob->shadowing, "destruct_object-6"); svp.u.ob->shadowing = 0; } destruct_object(ob2); if (ob->flags & O_DESTRUCTED) return; } } /* * The chain of shadows is a double linked list. Take care to update * it correctly. */ if (ob->shadowing) { change_ref(ob->shadowing->shadowed, ob->shadowed, "destruct_object-1"); ob->shadowing->shadowed = ob->shadowed; } if (ob->shadowed) { change_ref(ob->shadowed->shadowing, ob->shadowing, "destruct_object-2"); ob->shadowed->shadowing = ob->shadowing; } if (ob->shadowing) { free_object(ob->shadowing, "destruct_object-3"); ob->shadowing = 0; } if (ob->shadowed) { free_object(ob->shadowed, "destruct_object-4"); ob->shadowed = 0; } if (d_flag & DEBUG_DESTRUCT) debug_message("Destruct object %s (ref %d)\n", ob->name, ob->ref); super = ob->super; /* * There is nowhere to move the objects. */ { struct svalue svp; svp.type = T_OBJECT; while(ob->contains) { svp.u.ob = ob->contains; push_object(ob->contains); /* An error here will not leave destruct() in an inconsistent * stage. */ apply_master_ob(M_DESTRUCT_ENVIRONMENT_OF,1); if (svp.u.ob == ob->contains) destruct_object(ob->contains); if (ob->flags & O_DESTRUCTED) return; } } if (ob->interactive) { struct object *save=command_giver; command_giver=ob; if (ob->interactive->ed_buffer) { extern void save_ed_buffer(); save_ed_buffer(); } flush_all_player_mess(); command_giver=save; remove_interactive(ob, 0); if (ob->flags & O_DESTRUCTED) return; } /* * Remove us out of this current room (if any). * Remove all sentences defined by this object from all objects here. */ if (ob->super) { if (ob->super->flags & O_ENABLE_COMMANDS) remove_sent(ob, ob->super); for (pp = &ob->super->contains; *pp; ) { if ((*pp)->flags & O_ENABLE_COMMANDS) remove_sent(ob, *pp); if (*pp != ob) pp = &(*pp)->next_inv; else *pp = (*pp)->next_inv; } } /* Call destructors */ if (ob->flags & O_CREATED) { for (i = ob->prog->num_inherited - 1; i >= 0; i--) if (!(ob->prog->inherit[i].type & TYPE_MOD_SECOND) && ob->prog->inherit[i].prog->dtor_index != (unsigned short) -1) { call_function(ob, i, ob->prog->inherit[i].prog-> dtor_index, 0); pop_stack(); if (ob->flags & O_DESTRUCTED) return; } } remove_object_from_stack(ob); /* * Now remove us out of the list of all objects. * This must be done last, because an error in the above code would * halt execution. */ if (ob == swap_ob) swap_ob = ob->prev_all; if (ob == obj_list) obj_list = ob->next_all; ob->prev_all->next_all = ob->next_all; ob->next_all->prev_all = ob->prev_all; if (ob->next_all == ob) obj_list = swap_ob = 0; delete_all_calls(ob); remove_object_hash(ob); if (ob->living_name) remove_living_name(ob); ob->super = 0; ob->next_inv = 0; ob->contains = 0; ob->flags &= ~O_ENABLE_COMMANDS; ob->next_all = obj_list_destruct; ob->prev_all = 0; obj_list_destruct = ob; ob->flags |= O_DESTRUCTED; if (!(ob->flags & O_CLONE)) ob->prog->flags |= P_OLD; tot_alloc_dest_object++; if (ob == vbfc_object) { free_object(vbfc_object, "destruct_object"); vbfc_object = 0; ret = apply_master_ob(M_GET_VBFC_OBJECT, 0); if (ret && ret->type == T_OBJECT) { vbfc_object = ret->u.ob; vbfc_object->ref++; } } } /* * This one is called when no program is executing from the main loop. */ void destruct2(struct object *ob) { struct sentence *s; int num_var; extern int tot_alloc_variable_size; if (d_flag & DEBUG_DESTRUCT) debug_message("Destruct-2 object %s (ref %d)\n", ob->name, ob->ref); if (ob->interactive) remove_interactive(ob, 0); remove_ob_from_swap(ob); /* * We must deallocate variables here, not in 'free_object()'. * That is because one of the local variables may point to this object, * and deallocation of this pointer will also decrease the reference * count of this object. Otherwise, an object with a variable pointing * to itself, would never be freed. * Just in case the program in this object would continue to * execute, change string and object variables into the number 0. */ { /* * Deallocate variables in this object. */ int i; num_var = ob->prog->num_variables + ob->prog->inherit[ob->prog->num_inherited - 1]. variable_index_offset + 1; ob->variables--; for (i=0; i < num_var; i++) { free_svalue(&ob->variables[i]); ob->variables[i].type = T_NUMBER; ob->variables[i].u.number = 0; } free((char *)(ob->variables)); ob->variables = 0; tot_alloc_variable_size -= num_var * sizeof(struct svalue); } free_prog(ob->prog, 1); ob->prog = 0; for (s = ob->sent; s;) { struct sentence *next; next = s->next; free_sentence(s); s = next; } delete_all_calls(ob); free_object(ob, "destruct_object"); } /* * This will enable an object to use commands normally only * accessible by interactive players. * Also check if the player is a wizard. Wizards must not affect the * value of the wizlist ranking. */ void enable_commands(int num) { if (current_object->flags & O_DESTRUCTED) return; if (d_flag & DEBUG_LIVING) { debug_message("Enable commands %s (ref %d)\n", current_object->name, current_object->ref); } if (num) { current_object->flags |= O_ENABLE_COMMANDS; command_giver = current_object; } else { current_object->flags &= ~O_ENABLE_COMMANDS; if (command_giver == current_object) command_giver = 0; } } /* * Set up a function in this object to be called with the next * user input string. */ int input_to(char *fun, int flag, struct vector *v) { struct sentence *s; struct svalue *ret; if (!command_giver || command_giver->flags & O_DESTRUCTED) return 0; s = alloc_sentence(); if (set_call(command_giver, s, flag)) { s->function = make_shared_string(fun); s->ob = current_object; add_ref(current_object, "input_to"); free_vector(command_giver->interactive->carryover); command_giver->interactive->carryover = v; return 1; } else free_sentence(s); return 0; } #define MAX_LINES 50 /* * This one is used by qsort in get_dir(). */ static int pstrcmp(struct svalue *p1, struct svalue *p2) { return strcmp(p1->u.string, p2->u.string); } /* * List files in directory. This function do same as standard list_files did, * but instead writing files right away to player this returns an array * containing those files. Actually most of code is copied from list_files() * function. * Differences with list_files: * * - file_list("/w"); returns ({ "w" }) * * - file_list("/w/"); and file_list("/w/."); return contents of directory * "/w" * * - file_list("/");, file_list("."); and file_list("/."); return contents * of directory "/" */ struct vector * get_dir(char *path) { struct vector *v; int i, count = 0; DIR *dirp; int namelen, do_match = 0; #if defined(_AIX) || defined(M_UNIX) || defined(__osf__) || defined(_SEQUENT_) || defined(SOLARIS) || defined(__NetBSD__) struct dirent *de; #else struct direct *de; #endif struct stat st; char *temppath; char *p; char *regexp = 0; if (!path) return 0; path = check_valid_path(path, current_object, "get_dir", 0); if (path == 0) return 0; /* * We need to modify the returned path, and thus to make a * writeable copy. * The path "" needs 2 bytes to store ".\0". */ temppath = (char *) alloca(strlen(path) + 2); if (strlen(path)<2) { temppath[0] = path[0] ? path[0] : '.'; temppath[1] = '\000'; p = temppath; } else { strcpy(temppath, path); /* * If path ends with '/' or "/." remove it */ if ((p = strrchr(temppath, '/')) == 0) p = temppath; if ((p[0] == '/' && p[1] == '.' && p[2] == '\0') || (p[0] == '/' && p[1] == '\0')) *p = '\0'; } if (stat(temppath, &st) < 0) { if (*p == '\0') return 0; regexp = (char *)alloca(strlen(p)+2); if (p != temppath) { strcpy(regexp, p + 1); *p = '\0'; } else { strcpy(regexp, p); strcpy(temppath, "."); } do_match = 1; } else if (*p != '\0' && strcmp(temppath, ".")) { if (*p == '/' && *(p + 1) != '\0') p++; v = allocate_array(1); v->item[0].type = T_STRING; v->item[0].string_type = STRING_MALLOC; v->item[0].u.string = string_copy(p); return v; } if ((dirp = opendir(temppath)) == 0) return 0; /* * Count files */ for (de = readdir(dirp); de; de = readdir(dirp)) { #if defined(M_UNIX) || defined(_SEQUENT_) || defined(SOLARIS) namelen = strlen(de->d_name); #else namelen = de->d_namlen; #endif if (!do_match && (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)) continue; if (do_match && !match_string(regexp, de->d_name)) continue; count++; if ( count >= MAX_ARRAY_SIZE) break; } /* * Make array and put files on it. */ v = allocate_array(count); if (count == 0) { /* This is the easy case */ closedir(dirp); return v; } rewinddir(dirp); for(i = 0, de = readdir(dirp); i < count; de = readdir(dirp)) { #if defined(M_UNIX) || defined(_SEQUENT_) || defined(SOLARIS) namelen = strlen(de->d_name); #else namelen = de->d_namlen; #endif if (!do_match && (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)) continue; if (do_match && !match_string(regexp, de->d_name)) continue; de->d_name[namelen] = '\0'; v->item[i].type = T_STRING; v->item[i].string_type = STRING_MALLOC; v->item[i].u.string = string_copy(de->d_name); i++; } closedir(dirp); /* Sort the names. */ qsort((char *)v->item, count, sizeof v->item[0], pstrcmp); return v; } int tail(char *path) { char buff[1000]; FILE *f; struct stat st; int offset; path = check_valid_path(path, current_object, "tail", 0); if (path == 0) return 0; f = fopen(path, "r"); if (s_flag) num_fileread++; if (f == 0) return 0; if (fstat(fileno(f), &st) == -1) fatal("Could not stat an open file.\n"); offset = st.st_size - 54 * 20; if (offset < 0) offset = 0; if (fseek(f, offset, 0) == -1) fatal("Could not seek.\n"); /* Throw away the first incomplete line. */ if (offset > 0) (void) fgets(buff, sizeof buff, f); while(fgets(buff, sizeof buff, f)) add_message("%s", buff); fclose(f); return 1; } int remove_file(char *path) { path = check_valid_path(path, current_object, "remove_file", 1); if (path == 0) return 0; if (unlink(path) == -1) return 0; return 1; } void print_svalue(struct svalue *arg, struct object *ob) { if (arg == 0) a_msg(ob,"<NULL>"); else if (arg->type == T_STRING) { if (strlen(arg->u.string) > 9500) /* Not pretty */ error("Too long string.\n"); /* Strings sent to monsters are now delivered */ if (ob && (ob->flags & O_ENABLE_COMMANDS) && !ob->interactive) tell_npc(ob, arg->u.string); else a_msg(ob,"%s", arg->u.string); } else if (arg->type == T_OBJECT) a_msg(ob,"OBJ(%s)", arg->u.ob->name); else if (arg->type == T_NUMBER) a_msg(ob,"%d", arg->u.number); else if (arg->type == T_POINTER) a_msg(ob,"<ARRAY>"); else a_msg(ob,"<UNKNOWN>"); } a_msg(ob,a,b,c,d,e) struct object *ob; char *a; int b,c,d,e; { struct object *save_cmd_giver = command_giver; command_giver = ob; add_message(a,b,c,d,e); command_giver = save_cmd_giver; } /* Find an object. If not loaded, load it ! * The object may selfdestruct, which is the only case when 0 will be * returned. */ struct object * find_object(char *str) { struct object *ob; /* Remove leading '/' if any. */ while(str[0] == '/') str++; ob = find_object2(str); if (ob) return ob; ob = load_object(str, 0, 0); if (!ob || ob->flags & O_DESTRUCTED) /* *sigh* */ return 0; return ob; } /* same as find_object, but don't call 'create()' if not loaded */ struct object *find_object_no_create(str) char *str; { struct object *ob; /* Remove leading '/' if any. */ while(str[0] == '/') str++; ob = find_object2(str); if (ob) return ob; ob = load_object(str, 1, 0); if (!ob || ob->flags & O_DESTRUCTED) /* *sigh* */ return 0; return ob; } /* Look for a loaded object. Return 0 if non found. */ struct object * find_object2(char *str) { register struct object *ob; register int length; /* Remove leading '/' if any. */ while(str[0] == '/') str++; /* Truncate possible .c in the object name. */ length = strlen(str); if (str[length-2] == '.' && str[length-1] == 'c') { /* A new writreable copy of the name is needed. */ char *p; p = (char *)alloca(strlen(str)+1); strcpy(p, str); str = p; str[length-2] = '\0'; } if (ob = lookup_object_hash(str)) { return ob; } return 0; } #if 0 void apply_command(char *com) { struct value *ret; if (command_giver == 0) error("command_giver == 0 !\n"); ret = apply(com, command_giver->super, 0, 1); if (ret != 0) { add_message("Result:"); if (ret->type == T_STRING) add_message("%s\n", ret->u.string); if (ret->type == T_NUMBER) add_message("%d\n", ret->u.number); } else add_message("Error apply_command: function %s not found.\n", com); } #endif /* 0 */ /* * Update actions * * Update an objects set of actions in all 'nearby' living objects. * * NOTE! * It removes ALL actions defined by itself, including those added * to itself. As actions added to onself is normally not done in init() * this will have to be taken care of specifically. This should be of * little problem as living objects seldom export actions and would * therefore have little use of this efun. */ void update_actions(struct object *aob) { struct object *pp, *ob, *next_ob; struct object *save_cmd = command_giver, *item = aob; struct svalue *ret; /* * Remove actions in objects environment and from objects in the * same environment. (parent and siblings) */ if (item->super) { if (item->super->flags & O_ENABLE_COMMANDS) remove_sent(item, item->super); for (pp = item->super->contains; pp;) { if (pp != item && (pp->flags & O_ENABLE_COMMANDS)) remove_sent(item, pp); pp = item->next_inv; } } /* * Remove actions from objects inside the object. (children) */ for (pp = item->contains; pp;) { if (pp->flags & O_ENABLE_COMMANDS) remove_sent(item, pp); pp = item->next_inv; } /* * Setup the new commands. The order is very important, as commands * in the room should override commands defined by the room. * Beware that init() may have moved 'item' ! */ /* Save where object is, so as to note a move in init() */ ob = item->super; /* * Add actions in objects environment and to objects in the * same environment. (parent and siblings) */ if (item->super) { if (item->super->flags & O_ENABLE_COMMANDS) { command_giver = item->super; #ifdef USE_ENCOUNTER_NOT_INIT push_object(item); ret = apply("encounter", item->super, 1, 1); if (!ret) (void)apply("init", item, 0, 1); #else (void)apply("init", item, 0, 1); #endif if (ob != item->super || item->flags & O_DESTRUCTED || ob->flags & O_DESTRUCTED) { command_giver = save_cmd; /* marion */ return; } } for (pp = item->super->contains; pp;) { next_ob = pp->next_inv; if (pp != item && (pp->flags & O_ENABLE_COMMANDS)) { command_giver = pp; #ifdef USE_ENCOUNTER_NOT_INIT push_object(item); ret = apply("encounter", pp, 1, 1); if (!ret) (void)apply("init", item, 0, 1); #else (void)apply("init", item, 0, 1); #endif if (ob != item->super || item->flags & O_DESTRUCTED || ob->flags & O_DESTRUCTED || next_ob->flags & O_DESTRUCTED) { command_giver = save_cmd; /* marion */ return; } } pp = next_ob; } } /* * Add actions to objects inside the object. (children) */ for (pp = item->contains; pp;) { next_ob = pp->next_inv; if (pp->flags & O_ENABLE_COMMANDS) { command_giver = pp; #ifdef USE_ENCOUNTER_NOT_INIT push_object(item); ret = apply("encounter", pp, 1, 1); if (!ret) (void)apply("init", item, 0, 1); #else (void)apply("init", item, 0, 1); #endif if (ob != item->super || item->flags & O_DESTRUCTED || ob->flags & O_DESTRUCTED || next_ob->flags & O_DESTRUCTED) { command_giver = save_cmd; /* marion */ return; } } pp = next_ob; } } /* * Transfer an object. * The object has to be taken from one inventory list and added to another. * The main work is to update all command definitions, depending on what is * living or not. Note that all objects in the same inventory are affected. * */ void move_object(struct object *dest) { struct object **pp, *ob, *next_ob; struct object *save_cmd = command_giver, *item = current_object; struct svalue *ret; if (item->flags & O_DESTRUCTED) return; /* Recursive moves are not allowed. */ for (ob = dest; ob; ob = ob->super) if (ob == item) error("Can't move object inside itself.\n"); if (item->shadowing) error("Can't move an object that is shadowing.\n"); /* * The master object approves objects. Approved and nonapproved * objects can not be moved into each other. */ if (s_flag) num_move++; if (item->super) { int okey = 0; if (item->flags & O_ENABLE_COMMANDS) remove_sent(item->super, item); if (item->super->flags & O_ENABLE_COMMANDS) remove_sent(item, item->super); for (pp = &item->super->contains; *pp;) { if (*pp != item) { if ((*pp)->flags & O_ENABLE_COMMANDS) remove_sent(item, *pp); if (item->flags & O_ENABLE_COMMANDS) remove_sent(*pp, item); pp = &(*pp)->next_inv; continue; } *pp = item->next_inv; okey = 1; } if (!okey) fatal("Failed to find object %s in super list of %s.\n", item->name, item->super->name); } item->next_inv = dest->contains; dest->contains = item; item->super = dest; /* * Setup the new commands. The order is very important, as commands * in the room should override commands defined by the room. * Beware that init() in the room may have moved 'item' ! * * The call of init() should really be done by the object itself * It might be too slow, though :-( */ if (item->flags & O_ENABLE_COMMANDS) { command_giver = item; #ifdef USE_ENCOUNTER_NOT_INIT push_object(dest); ret = apply("encounter", item, 1, 1); if (!ret) (void)apply("init", dest, 0, 1); #else (void)apply("init", dest, 0, 1); #endif if ((dest->flags & O_DESTRUCTED) || item->super != dest) { command_giver = save_cmd; /* marion */ return; } } /* * Run init of the item once for every present player, and * for the environment (which can be a player). */ for (ob = dest->contains; ob; ob=next_ob) { next_ob = ob->next_inv; if (ob == item) continue; if (ob->flags & O_DESTRUCTED) error("An object was destructed at call of init()\n"); if (ob->flags & O_ENABLE_COMMANDS) { command_giver = ob; #ifdef USE_ENCOUNTER_NOT_INIT push_object(item); ret = apply("encounter", ob, 1, 1); if (!ret) (void)apply("init", item, 0, 1); #else (void)apply("init", item, 0, 1); #endif if (dest != item->super) { command_giver = save_cmd; return; } } if (item->flags & O_DESTRUCTED) /* marion */ error("The object to be moved was destructed at call of init()\n"); if (item->flags & O_ENABLE_COMMANDS) { command_giver = item; #ifdef USE_ENCOUNTER_NOT_INIT push_object(ob); ret = apply("encounter", item, 1, 1); if (!ret) (void)apply("init", ob, 0, 1); #else (void) apply("init", ob, 0, 1); #endif if (dest != item->super) { command_giver = save_cmd; return; } } } if (dest->flags & O_DESTRUCTED) /* marion */ error("The destination to move to was destructed at call of init()\n"); if (dest->flags & O_ENABLE_COMMANDS) { command_giver = dest; #ifdef USE_ENCOUNTER_NOT_INIT push_object(item); ret = apply("encounter", dest, 1, 1); if (!ret) (void)apply("init", item, 0, 1); #else (void)apply("init", item, 0, 1); #endif } command_giver = save_cmd; } struct sentence *sent_free = 0; int tot_alloc_sentence; int tot_current_alloc_sentence; struct sentence * alloc_sentence() { struct sentence *p; p = (struct sentence *) xalloc(sizeof *p); tot_alloc_sentence++; tot_current_alloc_sentence++; p->verb = 0; p->function = 0; p->next = 0; p->ob = 0; return p; } #ifdef free void free_all_sent() { struct sentence *p; for (;sent_free; sent_free = p) { p = sent_free->next; free(sent_free); } } #endif void free_sentence(struct sentence *p) { if (p->ob) free_object(p->ob, "free_sentence"); p->ob = 0; if (p->function) free_string(p->function); p->function = 0; if (p->verb) free_string(p->verb); p->verb = 0; free((char *)p); tot_current_alloc_sentence--; tot_alloc_sentence--; } /* * Find the sentence for a command from the player. * Return success status. */ int player_parser(char *buff) { struct sentence *s; char *p; int length; struct object *save_current_object = current_object; char verb_copy[100], *subst_buff = NULL; struct svalue *ret; struct object *cmd_giver = command_giver; #ifdef DEBUG if (d_flag & DEBUG_COMMAND) debug_message("cmd [%s]: %s\n", cmd_giver->name, buff); #endif /* * Strip trailing spaces. */ for (p = buff + strlen(buff) - 1; p >= buff; p--) { if (*p != ' ') break; *p = '\0'; } /* * Strip leading spaces. */ while(*buff == ' ') buff++; if (buff[0] == '\0') return 0; /* * Quicktyper hook. */ push_string(buff, STRING_MALLOC); push_object(cmd_giver); ret = apply_master_ob(M_MODIFY_COMMAND, 2); if (ret && ret->type == T_STRING) subst_buff = string_copy(ret->u.string); if (subst_buff == NULL) return 1; p = strchr(subst_buff, ' '); if (p == 0) length = strlen(subst_buff); else length = p - subst_buff; if (!*subst_buff || !cmd_giver || cmd_giver->flags & O_DESTRUCTED) return 1; clear_notify(); for (s = cmd_giver->sent; s; s = s->next) { struct svalue *ret; int len; struct object *command_object; if (s->verb == 0) error("An 'action' did something, but returned 0 or had an undefined verb.\n"); len = strlen(s->verb); if (s->short_verb) { if (strncmp(s->verb, subst_buff, len) != 0) continue; } else { if (len != length) continue; if (strncmp(subst_buff, s->verb, length)) continue; } /* * Now we have found a special sentence ! */ #ifdef DEBUG if (d_flag & DEBUG_COMMAND) debug_message("Local command %s on %s\n", s->function, s->ob->name); #endif if (length >= sizeof verb_copy) len = sizeof verb_copy - 1; else len = length; strncpy(verb_copy, subst_buff, len); verb_copy[len] = '\0'; last_verb = verb_copy; /* * If the function is static and not defined by current object, * then it will fail. If this is called directly from player input, * then we set current_object so that static functions are allowed. * current_object is reset just after the call to apply(). */ if (current_object == 0) current_object = s->ob; /* * Remember the object, to update score. */ command_object = s->ob; command_giver = cmd_giver; #ifdef STATIC_ADD_ACTIONS if (search_for_function(s->function, s->ob->prog)) { if (function_type_mod_found & TYPE_MOD_PRIVATE /* && inh_offset < function_inherit_found - function_prog_found->num_inherited + 1 */ ) error("Atempted call of private function.\n"); if (subst_buff[length] == ' ') { push_string(&subst_buff[length+1], STRING_MALLOC); call_function(s->ob, function_inherit_found, function_index_found, 1); } else { call_function(s->ob, function_inherit_found, function_index_found, 0); } if (cmd_giver == 0) { if (subst_buff) free(subst_buff); return 1; } current_object = save_current_object; last_verb = 0; /* If we get fail from the call, it was wrong second argument. */ if (sp->type == T_NUMBER && sp->u.number == 0) { /* Has the sentences been lost? */ /* Does anyone know a better way of doing this? */ struct sentence *s0; pop_stack(); for (s0 = cmd_giver->sent; s0 && s0 != s; s0 = s0->next) ; if (!s0) { s = 0; break; } continue; } else pop_stack(); } else add_message("Error: function %s not found.\n", s->function); #else if (subst_buff[length] == ' ') { push_string(&subst_buff[length+1], STRING_MALLOC); ret = apply(s->function, s->ob, 1, 0); } else { ret = apply(s->function, s->ob, 0, 0); } if (cmd_giver == 0) { if (subst_buff) free(subst_buff); return 1; } current_object = save_current_object; last_verb = 0; /* If we get fail from the call, it was wrong second argument. */ if (ret && ret->type == T_NUMBER && ret->u.number == 0) { /* Has the sentences been lost? */ /* Does anyone know a better way of doing this? */ struct sentence *s0; for (s0 = cmd_giver->sent; s0 && s0 != s; s0 = s0->next) ; if (!s0) { s = 0; break; } continue; } if (ret == 0) add_message("Error: function %s not found.\n", s->function); #endif break; } if (subst_buff) free(subst_buff); if (s == 0) { notify_no_command(); return 0; } return 1; } /* * Associate a command with function in this object. * The optional second argument is the command name. * * The optional third argument is a flag that will state that the verb should * only match against leading characters. * * The object must be near the command giver, so that we ensure that the * sentence is removed when the command giver leaves. * * If the call is from a shadow, make it look like it is really from * the shadowed object. */ int add_action(struct svalue *func, struct svalue *cmd, int flag) { struct sentence *p; struct object *ob; if (current_object->flags & O_DESTRUCTED) return -1; ob = current_object; if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED)) return -1; if (ob != command_giver && ob->super != command_giver && ob->super != command_giver->super && ob != command_giver->super) error("add_action from object that was not present.\n"); #ifdef DEBUG if (d_flag & DEBUG_ADD_ACTION) debug_message("--Add action %s\n", func); #endif if (func->u.string[0] == ':' || func->u.string[0] == '.') error("Illegal function name: %s\n", func->u.string); p = alloc_sentence(); p->function = make_shared_string(func->u.string); p->ob = ob; add_ref(ob, "add_action"); p->next = command_giver->sent; p->short_verb = flag; if (cmd) p->verb = make_shared_string(cmd->u.string); else p->verb = 0; command_giver->sent = p; return 0; } /* * Remove all commands (sentences) defined by object 'ob' in object * 'player' */ void remove_sent(struct object *ob, struct object *player) { struct sentence **s; for (s= &player->sent; *s;) { struct sentence *tmp; if ((*s)->ob == ob) { if (d_flag & DEBUG_SENTENCE) debug_message("--Unlinking sentence %s\n", (*s)->function); tmp = *s; *s = tmp->next; free_sentence(tmp); } else s = &((*s)->next); } } static char debinf[8192]; int swap_out_obj_sec, swap_out_obj_min, swap_out_obj_hour; int swap_out_prog_sec, swap_out_prog_min, swap_out_prog_hour; int swap_in_obj_sec, swap_in_obj_min, swap_in_obj_hour; int swap_in_prog_sec, swap_in_prog_min, swap_in_prog_hour; char * get_gamedriver_info(char *str) { char tmp[1024]; int tries, hits; float proc; int tot, tot_num, res, verbose = 0; extern char *reserved_area; extern int tot_alloc_sentence, tot_current_alloc_sentence, tot_alloc_object, tot_alloc_object_size, num_swapped, total_bytes_swapped, num_arrays, total_array_size, num_mappings, total_mapping_size; extern int num_strings_swapped, size_strings_swapped; extern int obj_swapped, obj_bytes_swapped, total_lineno_swapped; extern int total_num_prog_blocks, total_prog_block_size; extern int num_distinct_strings, bytes_distinct_strings; extern int overhead_bytes(); extern int num_call, call_out_size; extern int globcache_tries, globcache_hits, funmap_tries, funmap_hits; extern struct vector null_vector; extern int cache_tries, cache_hits; extern int program_bytes_swapped, total_program_size, programs_swapped; extern int tot_alloc_variable_size; extern int allocated_swap, allocated_swap_blocks; extern int last_address; extern int num_swapped_arrays, size_swapped_arrays; extern int num_swapped_mappings, size_swapped_mappings; extern int max_swap_memory, min_swap_memory; extern int max_swap_time, min_swap_time; int meg, kilo, byte, hour, min, sec; extern int used_memory; extern struct program *swap_prog; extern struct object *swap_ob; #ifdef COMM_STAT extern int add_message_calls,inet_packets,inet_volume; #endif strcpy(debinf,""); if (strcmp(str, "status") == 0 || strcmp(str, "status tables") == 0) { #ifdef COMM_STAT extern int add_message_calls,inet_packets,inet_volume; #endif if (strcmp(str, "status tables") == 0) verbose = 1; if (reserved_area) res = RESERVED_SIZE; else res = 0; #ifdef COMM_STAT if (verbose) { sprintf(tmp,"Calls to add_message: %d Packets: %d Average packet size: %f\n\n",add_message_calls,inet_packets,(float)inet_volume/inet_packets); strcat(debinf, tmp); } #endif if (!verbose) { sprintf(tmp,"Sentences:\t\t\t%8d %8d\n", tot_alloc_sentence, tot_alloc_sentence * sizeof (struct sentence)); strcat(debinf, tmp); sprintf(tmp,"Objects:\t\t\t%8d %8d (%d dest, %d rmd)\n", tot_alloc_object, tot_alloc_object_size, tot_alloc_dest_object, tot_removed_object); strcat(debinf, tmp); sprintf(tmp,"Variables:\t\t\t\t %8d (%d swapped, %d Kbytes)\n", tot_alloc_variable_size, obj_swapped, (unsigned int)obj_bytes_swapped / 1024); strcat(debinf, tmp); sprintf(tmp,"Arrays:\t\t\t\t%8d %8d (%d swapped, %d Kbytes)\n", num_arrays, total_array_size, num_swapped_arrays, size_swapped_arrays / 1024); strcat(debinf, tmp); sprintf(tmp,"Call_outs:\t\t\t%8d %8d\n", num_call, call_out_size); strcat(debinf, tmp); sprintf(tmp,"Mappings:\t\t\t%8d %8d (%d swapped, %d Kbytes)\n", num_mappings, total_mapping_size, num_swapped_mappings, size_swapped_mappings / 1024); strcat(debinf, tmp); sprintf(tmp,"Strings:\t\t\t%8d %8d (%d overhead)\n", num_distinct_strings, bytes_distinct_strings + overhead_bytes(), overhead_bytes()); strcat(debinf, tmp); sprintf(tmp,"\t\t\t\t\t\t (%d swapped, %d Kbytes)\n", num_strings_swapped, size_strings_swapped >> 10); strcat(debinf, tmp); sprintf(tmp,"Prog blocks:\t\t\t%8d %8d\n", total_num_prog_blocks, total_prog_block_size); strcat(debinf, tmp); sprintf(tmp,"Programs:\t\t\t\t %8d (%d swapped, %d Kbytes)\n", total_program_size, programs_swapped, program_bytes_swapped / 1024); strcat(debinf, tmp); sprintf(tmp,"Memory reserved:\t\t\t %8d\n", res); strcat(debinf, tmp); } if (verbose) { #ifdef CACHE_STATS extern int call_cache_saves; extern int global_cache_saves; extern int searches_needed; extern int searches_done; extern int global_first_saves; extern int call_first_saves; #endif if (globcache_tries < 1) globcache_tries = 1; if (funmap_tries < 1) funmap_tries = 1; strcat(debinf, stat_living_objects()); strcat(debinf, "\nFunction calls:\n--------------------------\n"); proc = 100.0 * ((cache_hits * 1.0) / cache_tries); sprintf(tmp,"Call cache, Tries: %10d Hits: %10d Miss: %10d Rate: %3.1f%%\n", cache_tries, cache_hits, cache_tries - cache_hits, proc); strcat(debinf, tmp); #ifdef CACHE_STATS sprintf(tmp, "searches saved %10d %3d%%\n", call_first_saves, (int)(100.0 * ((call_first_saves * 1.0) / (searches_needed + global_first_saves + call_first_saves)))); strcat(debinf, tmp); #endif #ifdef GLOBAL_CACHE proc = 100.0 * ((globcache_hits * 1.0) / globcache_tries); sprintf(tmp,"Global cache, Tries: %10d Hits: %10d Miss: %10d Rate: %3.1f%%\n", globcache_tries, globcache_hits, globcache_tries -globcache_hits, proc); strcat(debinf, tmp); #ifdef CACHE_STATS sprintf(tmp, "searches saved %10d %3d%%\n", global_first_saves, (int)(100.0 * ((global_first_saves * 1.0) / (searches_needed + global_first_saves + call_first_saves)))); strcat(debinf, tmp); #endif #endif #ifdef CACHE_STATS strcat(debinf, "Secondary hits:\n"); sprintf(tmp,"searches needed : %10d\n", searches_needed); strcat(debinf, tmp); sprintf(tmp,"call cache saves : %10d %3d%%\n", call_cache_saves, (int)(100.0 * ((call_cache_saves * 1.0) / searches_needed))); strcat(debinf, tmp); sprintf(tmp,"global cache saves : %10d %3d%%\n", global_cache_saves, (int)(100.0 * ((global_cache_saves * 1.0) / searches_needed))); strcat(debinf, tmp); sprintf(tmp,"searches done : %10d\n", searches_done); strcat(debinf, tmp); sprintf(tmp,"Total needed : %10d %3d%% done\n", searches_needed + global_first_saves + call_first_saves, (int)(100.0 * ((searches_done * 1.0) / (searches_needed + global_first_saves + call_first_saves)))); strcat(debinf, tmp); #endif #ifdef USE_SWAP sprintf(tmp, "\nSwap:\n--------------------------------------\n"); strcat(debinf, tmp); sprintf(tmp, "Allocated: %8d Kbytes in %8d blocks.\n", allocated_swap >> 10, allocated_swap_blocks); strcat(debinf, tmp); sprintf(tmp, "Swapped: %8d Kbytes in %8d blocks.\n", total_bytes_swapped >> 10, num_swapped); strcat(debinf, tmp); sprintf(tmp, "File: %8d Kbytes.\n", last_address >> 10); strcat(debinf, tmp); sprintf(tmp, "Time: %8d seconds.\n", current_time - (swap_prog->time_of_ref < swap_ob->time_of_ref ? swap_prog->time_of_ref : swap_prog->time_of_ref)); strcat(debinf, tmp); sprintf(tmp,"Objects: %d/%d/%d in, %d/%d/%d out.\n", swap_in_obj_sec, swap_in_obj_min, swap_in_obj_hour, swap_out_obj_sec, swap_out_obj_min, swap_out_obj_hour); strcat(debinf, tmp); sprintf(tmp,"Programs: %d/%d/%d in, %d/%d/%d out.\n", swap_in_prog_sec, swap_in_prog_min, swap_in_prog_hour, swap_out_prog_sec, swap_out_prog_min, swap_out_prog_hour); strcat(debinf, tmp); #endif add_string_status(debinf); sprintf(tmp, "\nReferences:\n--------------------------------\n"); strcat(debinf, tmp); sprintf(tmp, "Null vector: %d\n", null_vector.ref); strcat(debinf, tmp); } tot = total_prog_block_size + total_program_size + tot_alloc_sentence * sizeof(struct sentence) + total_array_size + tot_alloc_variable_size + call_out_size + total_mapping_size + bytes_distinct_strings + overhead_bytes() + tot_alloc_object_size + res; #if 0 + show_otable_status(verbose) + heart_beat_status(verbose) + print_call_out_usage(verbose) + res; #endif if (!verbose) { #ifdef USE_SWAP if (used_memory) { sprintf(tmp,"Other:\t\t\t\t\t %8d\n", used_memory - tot); strcat(debinf, tmp); tot = used_memory; } #endif sprintf(tmp,"\t\t\t\t\t --------\n"); strcat(debinf, tmp); sprintf(tmp,"Total:\t\t\t\t\t %8d\n", tot); strcat(debinf, tmp); } } return string_copy(debinf); } struct vector * get_local_commands(struct object *ob) { struct sentence *s; struct vector *ret; struct vector *ret2; int num; for (num = 0, s = ob->sent; s; s = s->next) num++; ret2 = allocate_array(num); for (num = 0, s = ob->sent; s; s = s->next, num++) { ret2->item[num].type = T_POINTER; ret2->item[num].u.vec = ret = allocate_array(4); ret->item[0].type = T_STRING; ret->item[0].string_type = STRING_SHARED; ret->item[0].u.string = s->verb; increment_string_ref(s->verb); ret->item[1].type = T_NUMBER; ret->item[1].u.number = s->short_verb; ret->item[2].type = T_OBJECT; ret->item[2].u.ob = s->ob; add_ref(s->ob,"get_local_commands"); ret->item[3].type = T_STRING; ret->item[3].string_type = STRING_SHARED; ret->item[3].u.string = s->function; increment_string_ref(s->function); } return ret2; } /*VARARGS1*/ void fatal(va_alist) va_dcl { va_list argp; char *fmt, *f; char buf[512]; static int in_fatal = 0; /* Prevent double fatal. */ if (in_fatal) abort(); in_fatal = 1; va_start(argp); fmt = va_arg(argp, char *); va_end(argp); vfprintf(stderr, fmt, argp); fflush(stderr); if (current_object) (void) fprintf(stderr, "Current object was %s\n", current_object->name); vsprintf(buf, fmt, argp); debug_message("%s", buf); if (current_object) debug_message("Current object was %s\n", current_object->name); debug_message("Dump of variables:\n"); (void) dump_trace(1); abort(); } int num_error = 0; /* * Error() has been "fixed" so that users can catch and throw them. * To catch them nicely, we really have to provide decent error information. * Hence, all errors that are to be caught * (error_recovery_context_exists == 2) construct a string containing * the error message, which is returned as the * thrown value. Users can throw their own error values however they choose. */ /* * This is here because throw constructs its own return value; we dont * want to replace it with the system's error string. */ void throw_error() { extern int error_recovery_context_exists; extern jmp_buf error_recovery_context; extern struct svalue catch_value; if (error_recovery_context_exists > 1) { longjmp(error_recovery_context, 1); fatal("Throw_error failed!"); } if (catch_value.type == T_STRING) error(catch_value.u.string); else error("Throw with no catch.\n"); } static char emsg_buf[2000]; /*VARARGS1*/ void error(va_alist) va_dcl { extern int error_recovery_context_exists; extern jmp_buf error_recovery_context; extern struct object *current_heart_beat; extern struct svalue catch_value; char *object_name; va_list argp; char *fmt, *f; int i; va_start(argp); fmt = va_arg(argp, char *); vsprintf(emsg_buf + 1, fmt, argp); va_end(argp); emsg_buf[0] = '*'; /* all system errors get a * at the start */ if (error_recovery_context_exists > 1) { /* user catches this error */ struct svalue v; v.type = T_STRING; v.u.string = emsg_buf; v.string_type = STRING_MALLOC; /* Always reallocate */ assign_svalue(&catch_value, &v); longjmp(error_recovery_context, 1); fatal("Catch() longjump failed"); } num_error++; if (num_error <= 1) { debug_message("%s", emsg_buf+1); object_name = dump_trace(0); fflush(stdout); if (object_name) { struct object *ob; ob = find_object2(object_name); if (!ob) { debug_message("error when executing program in destroyed object %s\n", object_name); } } /* * The stack must be brought in a usable state. After the * call to reset_machine(), all arguments to error() are invalid, * and may not be used any more. The reason is that some strings * may have been on the stack machine stack, and has been deallocated. */ reset_machine (0); push_string(emsg_buf, STRING_MALLOC); if (current_object) { push_object(current_object); if (current_prog) push_string(current_prog->name, STRING_MALLOC); else push_constant_string("<???>"); push_string(get_srccode_position_if_any(), STRING_MALLOC); } else { push_number(0); push_constant_string("<???>"); push_constant_string(""); } apply_master_ob(M_RUNTIME_ERROR, 4); } else { debug_message("Too many simultaneous errors.\n"); } num_error--; if (error_recovery_context_exists) longjmp(error_recovery_context, 1); abort(); } /* * Check that it is an legal path. No '..' are allowed. */ int legal_path(char *path) { char *p; if (path == NULL || strchr(path, ' ')) return 0; if (path[0] == '/') return 0; for(p = strchr(path, '.'); p; p = strchr(p+1, '.')) { if (p[1] == '.') return 0; } return 1; } /* * There is an error in a specific file. Ask the game driver to log the * message somewhere. */ static struct error_msg { struct error_msg *next; char *file; char *msg; } *smart_log_msg = NULL; void init_smart_log() { struct error_msg *prev; while (smart_log_msg) { prev = smart_log_msg; smart_log_msg = smart_log_msg->next; free(prev->file); free(prev->msg); free((char *)prev); } } void dump_smart_log() { struct error_msg *prev; if (!master_ob) { init_smart_log(); return; } while (smart_log_msg) { prev = smart_log_msg; smart_log_msg = smart_log_msg->next; push_malloced_string(prev->file); push_malloced_string(prev->msg); free((char *)prev); apply_master_ob(M_LOG_ERROR, 2); } } void smart_log(char *error_file, int line, char *what) { char buff[500]; struct error_msg **last; for (last = &smart_log_msg; *last; last = &(*last)->next) if (error_file == 0) return; if (strlen(what) + strlen(error_file) > sizeof buff - 100) what = "...[too long error message]..."; if (strlen(what) + strlen(error_file) > sizeof buff - 100) error_file = "...[too long filename]..."; sprintf(buff, "%s line %d:%s\n", error_file, line, what); *last = (struct error_msg *)xalloc(sizeof(struct error_msg)); (*last)->next = NULL; (*last)->file = string_copy(error_file); (*last)->msg = string_copy(buff); } /* * Check that a path to a file is valid for read or write. * This is done by functions in the master object. * The path is always treated as an absolute path, and is returned without * a leading '/'. * If the path was '/', then '.' is returned. * The returned string may or may not be residing inside the argument 'path', * so don't deallocate arg 'path' until the returned result is used no longer. * Otherwise, the returned path is temporarily allocated by apply(), which * means it will be dealocated at next apply(). */ char * check_valid_path(char *path, struct object *ob, char *call_fun, int writeflg) { struct svalue *v; push_string(path, STRING_MALLOC); push_object(ob); push_string(call_fun, STRING_MALLOC); if (writeflg) v = apply_master_ob(M_VALID_WRITE, 3); else v = apply_master_ob(M_VALID_READ, 3); if (v && v->type == T_NUMBER && v->u.number == 0) return 0; if (path[0] == '/') path++; if (path[0] == '\0') path = "."; if (legal_path(path)) return path; return 0; } /* * This one is called from HUP. */ int game_is_being_shut_down; extern volatile int interupted; void startshutdowngame(int arg) { game_is_being_shut_down = 1; interupted = 1; } /* * This one is called from the command "shutdown". * We don't call it directly from HUP, because it is dangerous when being * in an interrupt. */ void shutdowngame() { ipc_remove(); remove_all_players(); unlink_swap_file(); #ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN remove_all_objects(); free_all_sent(); #ifndef MALLOC_sysmalloc dump_malloc_data(); #endif find_alloced_data(); #endif #ifdef OPCPROF opcdump(); #endif exit(0); } /* * Call this one when there is only little memory left. * We tell master object of our troubles and hope it does something * intelligent, like starting Armageddon. */ void slow_shut_down(int minutes) { struct object *ob; /* * Swap out objects, and free some memory. */ push_number(minutes); (void) apply_master_ob(M_MEMORY_FAILURE, 1); } int match_string(char *match, char *str) { int i; again: if (*str == '\0' && *match == '\0') return 1; switch(*match) { case '?': if (*str == '\0') return 0; str++; match++; goto again; case '*': match++; if (*match == '\0') return 1; for (i=0; str[i] != '\0'; i++) if (match_string(match, str+i)) return 1; return 0; case '\0': return 0; case '\\': match++; if (*match == '\0') return 0; /* Fall through ! */ default: if (*match == *str) { match++; str++; goto again; } return 0; } } /* * Credits for some of the code below goes to Free Software Foundation * Copyright (C) 1990 Free Software Foundation, Inc. * See the GNU General Public License for more details. */ #ifndef S_ISDIR #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) #endif int isdir(char *path) { struct stat stats; return stat (path, &stats) == 0 && S_ISDIR (stats.st_mode); } void strip_trailing_slashes(char *path) { int last; last = strlen (path) - 1; while (last > 0 && path[last] == '/') path[last--] = '\0'; } struct stat to_stats, from_stats; int copy (char *from, char *to) { int ifd; int ofd; char buf[1024 * 8]; int len; /* Number of bytes read into buf. */ if (!S_ISREG (from_stats.st_mode)) { error("cannot move `%s' across filesystems: Not a regular file\n", from); return 1; } if (unlink (to) && errno != ENOENT) { error ("cannot remove `%s'\n", to); return 1; } ifd = open (from, O_RDONLY, 0); if (ifd < 0) { error ("%s: open failed\n", from); return errno; } ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (ofd < 0) { error ("%s: open failed\n", to); close (ifd); return 1; } #ifndef FCHMOD_MISSING if (fchmod (ofd, from_stats.st_mode & 0777)) { error ("%s: fchmod failed\n", to); close (ifd); close (ofd); unlink (to); return 1; } #endif while ((len = read (ifd, buf, sizeof (buf))) > 0) { int wrote = 0; char *bp = buf; do { wrote = write (ofd, bp, len); if (wrote < 0) { error ("%s: write failed\n", to); close (ifd); close (ofd); unlink (to); return 1; } bp += wrote; len -= wrote; } while (len > 0); } if (len < 0) { error ("%s: read failed\n", from); close (ifd); close (ofd); unlink (to); return 1; } if (close (ifd) < 0) { error ("%s: close failed", from); close (ofd); return 1; } if (close (ofd) < 0) { error ("%s: close failed", to); return 1; } #ifdef FCHMOD_MISSING if (chmod (to, from_stats.st_mode & 0777)) { error ("%s: chmod failed\n", to); return 1; } #endif return 0; } /* Move FROM onto TO. Handles cross-filesystem moves. If TO is a directory, FROM must be also. Return 0 if successful, 1 if an error occurred. */ int do_move (char *from, char *to) { if (lstat (from, &from_stats) != 0) { error ("%s: lstat failed\n", from); return 1; } if (lstat (to, &to_stats) == 0) { if (from_stats.st_dev == to_stats.st_dev && from_stats.st_ino == to_stats.st_ino) { error ("`%s' and `%s' are the same file", from, to); return 1; } if (S_ISDIR (to_stats.st_mode)) { error ("%s: cannot overwrite directory", to); return 1; } } else if (errno != ENOENT) { error ("%s: unknown error\n", to); return 1; } #if defined(SYSV) && !defined(_SEQUENT_) if (isdir(from)) { char cmd_buf[100]; sprintf(cmd_buf, "/usr/lib/mv_dir %s %s", from, to); return system(cmd_buf); } else #endif /* SYSV */ if (rename (from, to) == 0) { return 0; } if (errno != EXDEV) { error ("cannot move `%s' to `%s'", from, to); return 1; } /* rename failed on cross-filesystem link. Copy the file instead. */ if (copy (from, to)) return 1; if (unlink (from)) { error ("cannot remove `%s'", from); return 1; } return 0; } /* * do_rename is used by the efun rename. It is basically a combination * of the unix system call rename and the unix command mv. Please shoot * the people at ATT who made Sys V. * * WARNING! (Thanks to Sul@Genesis, pmidden@tolkien.helios.nd.edu) * * If you have a typical 'lpmud ftpdaemon' that allow wizards to ftp * to their homedirs and if they are allowed to move subdirs from * under their wizdirs to for example /open then they can use ftp to * access the entire host machine. * * It is therefore important to disallow these directory moves in * your /secure/master.c */ int do_rename(char * fr, char *t) { char *from, *to; from = check_valid_path(fr, current_object, "do_rename_from", 1); if(!from) return 1; to = check_valid_path(t, current_object, "do_rename_to", 1); if(!to) return 1; if(!strlen(to) && !strcmp(t, "/")) { to = (char *)alloca(3); sprintf(to, "./"); } strip_trailing_slashes (from); if (isdir (to)) { /* Target is a directory; build full target filename. */ char *cp; char *newto; cp = strrchr (from, '/'); if (cp) cp++; else cp = from; newto = (char *) alloca (strlen (to) + 1 + strlen (cp) + 1); sprintf (newto, "%s/%s", to, cp); return do_move (from, newto); } else return do_move (from, to); }