home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
t
/
toaster.zip
/
Compat
/
simulate.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-03-09
|
90KB
|
3,547 lines
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <fcntl.h>
#include <setjmp.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <memory.h>
#if defined(sun)
#include <alloca.h>
#endif
#include <ndir.h>
#include "lint.h"
#include "config.h"
#include "stdio.h"
#include "lang.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "wiz_list.h"
#include "exec.h"
#include "comm.h"
extern int errno;
extern int comp_flag;
extern char *init_file;
char *inherit_file;
#if !defined(ultrix) && !defined(M_UNIX) && !defined(hpux) && !defined(sgi)
extern int rename PROT((char *, char *));
#endif
extern int readlink PROT((char *, char *, int));
extern int symlink PROT((char *, char *));
#ifndef rindex
extern char *rindex PROT((const char *, int));
#endif
#ifndef MSDOS
extern int lstat PROT((char *, struct stat *));
#else
#define lstat stat
#endif
extern int fchmod PROT((int, int));
char *last_verb, *last_arg;
extern int special_parse PROT((char *)),
set_call PROT((struct object *, struct sentence *, int)),
legal_path PROT((char *));
void pre_compile PROT((char *)),
remove_interactive PROT((struct object *)),
add_light PROT((struct object *, int)),
add_action PROT((char *, char *, int)),
add_verb PROT((char *, int)),
print_local_commands(), ipc_remove(),
show_info_about PROT((char *, char *, struct interactive *)),
set_snoop PROT((struct object *, struct object *)),
print_lnode_status PROT((int)),
remove_all_players(), start_new_file PROT((FILE *)), end_new_file(),
move_or_destruct PROT((struct object *, struct object *)),
load_ob_from_swap PROT((struct object *)), dump_malloc_data(),
print_svalue PROT((struct svalue *)),
debug_message_value(),
destruct2();
extern int d_flag;
struct object *obj_list, *obj_list_destruct, *master_ob;
extern int current_line;
extern struct wiz_list *back_bone_uid;
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 variable *find_status(str, must_find)
char *str;
int must_find;
{
int i;
for (i=0; i < current_object->prog->num_variables; i++) {
if (strcmp(current_object->prog->variable_names[i].name, str) == 0)
return ¤t_object->prog->variable_names[i];
}
if (!must_find)
return 0;
error("--Status %s not found in prog for %s\n", str,
current_object->name);
return 0;
}
/*
* Give the correct uid and euid to a created object.
*/
int give_uid_to_object(ob)
struct object *ob;
{
struct svalue *ret;
char *creator_name;
struct object *tmp_ob;
#ifdef COMPAT_MODE
char wiz_name[100];
#endif
if (master_ob == 0)
tmp_ob = ob;
else {
assert_master_ob_loaded();
tmp_ob = master_ob;
}
#ifdef COMPAT_MODE
/* Is this object wizard defined ? */
if (sscanf(ob->name, "players/%s", wiz_name) == 1) {
char *np;
np = strchr(wiz_name, '/');
if (np)
*np = '\0';
ob->user = add_name(wiz_name);
} else {
ob->user = 0;
}
ob->eff_user = ob->user; /* Initial state */
return 1;
#else
if (!current_object || !current_object->user) {
/*
* Only for the master and void object. Note that
* back_bone_uid is not defined when master.c is being loaded.
*/
ob->user = add_name("NONAME");
ob->eff_user = 0;
return 1;
}
/*
* Ask master.c who the creator of this object is.
*/
push_constant_string(ob->name);
ret = apply("creator_file", tmp_ob, 1);
if (!ret)
error("No function 'creator_file' in master.c!\n");
if (ret->type != T_STRING) {
struct svalue arg;
/* This can be the case for objects in /ftp and /open. */
arg.type = T_OBJECT;
arg.u.ob = ob;
destruct_object(&arg, 0);
error("Illegal object to load.\n");
}
creator_name = ret->u.string;
/*
* Now we are sure that we have a creator name.
* Do not call apply() again, because creator_name will be lost !
*/
if (strcmp(current_object->user->name, creator_name) == 0) {
/*
* The loaded object has the same uid as the loader.
*/
ob->user = current_object->eff_user;
ob->eff_user = current_object->eff_user;
return 1;
}
if (strcmp(back_bone_uid->name, creator_name) == 0) {
/*
* The object is loaded from backbone. This is trusted, so we
* let it inherit the value of eff_user.
*/
ob->user = current_object->eff_user;
ob->eff_user = current_object->eff_user;
return 1;
}
/*
* The object is not loaded from backbone, nor from
* from the loading objects path. That should be an object
* defined by another wizard. It can't be trusted, so we give it the
* same uid as the creator. Also give it eff_user 0, which means that
* player 'a' can't use objects from player 'b' to load new objects nor
* modify files owned by player 'b'.
*
* If this effect is wanted, player 'b' must let his object do
* 'seteuid()' to himself. That is the case for most rooms.
*/
ob->user = add_name(creator_name);
ob->eff_user = (struct wiz_list *)0;
return 1;
#endif /* COMPAT_MODE */
}
/*
* 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.
*/
struct object *load_object(lname, dont_reset)
char *lname;
int dont_reset;
{
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;
#ifdef LEX_lexical
char *current_i_file;
struct stat i_st;
#endif
struct stat c_st;
int name_length;
char real_name[200], name[200];
#ifndef COMPAT_MODE
if (current_object && current_object->eff_user == 0)
error("Can't load objects when no effective user.\n");
#endif
/* Truncate possible .c in the object name. */
/* Remove leading '/' if any. */
while(lname[0] == '/')
lname++;
strncpy(name, lname, sizeof(name));
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;
}
/*
* 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;
}
/*
* Check if it's a legal name.
*/
if (!legal_path(real_name)) {
fprintf(stderr, "Illegal pathname: %s\n", real_name);
error("Illegal path name.\n");
return 0;
}
#ifdef LEX_lexical
real_name[name_length+1] = 'i';
if (stat(real_name, &i_st) == -1 || i_st.st_mtime < c_st.st_mtime) {
real_name[name_length+1] = 'c';
pre_compile(name);
real_name[name_length+1] = 'i';
}
if (stat(real_name, &i_st) == -1 || i_st.st_mtime < c_st.st_mtime) {
if (command_giver)
add_message("Failed to compile the new file.\n");
}
current_i_file = real_name;
#endif
if (comp_flag)
fprintf(stderr, " compiling %s ...", real_name);
f = fopen(real_name, "r");
if (f == 0) {
perror(real_name);
error("Could not read the file.\n");
}
start_new_file(f);
current_file = string_copy(real_name); /* This one is freed below */
#ifdef LEX_lexical
current_line = 0;
num_parse_error = 0;
#endif
compile_file();
end_new_file();
if (comp_flag)
fprintf(stderr, " done\n");
update_compile_av(total_lines);
total_lines = 0;
(void)fclose(f);
#ifdef LEX_lexical
unlink(current_i_file);
#endif
free(current_file);
current_file = 0;
/* Sorry, can't 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");
}
/*
* 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;
}
if (strcmp(inherit_file, name) == 0) {
free(inherit_file);
inherit_file = 0;
error("Illegal to inherit self.\n");
}
inherit_file = 0;
#ifdef MUDLIB3_NEED
#ifdef COMPAT_MODE
load_object(tmp, 0);
#else
load_object(tmp, 1);
#endif
#else
load_object(tmp, 0);
#endif
free(tmp);
ob = load_object(name, dont_reset);
return ob;
}
ob = get_empty_object(prog->num_variables);
/*
* Can we approve of this object ?
*/
if (approved_object || strcmp(prog->name, "std/object.c") == 0)
ob->flags |= O_APPROVED;
ob->name = string_copy(name); /* Shared string is no good here */
add_ref(ob, "load_object");
ob->prog = prog;
ob->next_all = obj_list;
obj_list = ob;
enter_object_hash(ob); /* add name to fast object lookup table */
if (give_uid_to_object(ob) && !dont_reset)
reset_object(ob, 0);
if (!(ob->flags & O_DESTRUCTED) && function_exists("clean_up",ob) ) {
ob->flags |= O_WILL_CLEAN_UP;
}
command_giver = save_command_giver;
if (d_flag > 1 && ob)
debug_message("--%s loaded\n", ob->name);
return ob;
}
struct object *previous_ob;
char *make_new_name(str)
char *str;
{
static int i;
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(str1)
char *str1;
{
struct object *ob, *new_ob;
struct object *save_command_giver = command_giver;
#ifndef COMPAT_MODE
if (current_object && current_object->eff_user == 0)
error("Illegal to call clone_object() with no effective user.\n");
#endif
ob = find_object(str1);
if (ob == 0 || ob->super || (ob->flags & O_CLONE))
error("Cloning a bad object!\n");
/* We do not want the heart beat to be running for unused copied objects */
if (ob->flags & O_HEART_BEAT)
(void)set_heart_beat(ob, 0);
new_ob = get_empty_object(ob->prog->num_variables);
new_ob->name = make_new_name(ob->name);
new_ob->flags |= O_CLONE | ob->flags & ( O_APPROVED | O_WILL_CLEAN_UP ) ;
#if 0
if (ob->flags & O_APPROVED)
new_ob->flags |= O_APPROVED;
#endif
add_ref(new_ob, "clone_object");
new_ob->prog = ob->prog;
reference_prog(ob->prog, "clone_object");
#ifdef COMPAT_MODE
if (current_object && current_object->user && !ob->user)
new_ob->user = current_object->user;
else
new_ob->user = ob->user; /* Possibly a null pointer */
new_ob->eff_user = new_ob->user;
#else
if (!current_object)
fatal("clone_object() from no current_object!\n");
give_uid_to_object(new_ob);
#endif
new_ob->next_all = obj_list;
obj_list = new_ob;
enter_object_hash(new_ob); /* Add name to fast object lookup table */
reset_object(new_ob, 0);
command_giver = save_command_giver;
/* Never know what can happen ! :-( */
if (new_ob->flags & O_DESTRUCTED)
return 0;
return new_ob;
}
struct object *environment(arg)
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() of 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(str, ob)
char *str;
struct object *ob;
{
char buff[1001];
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, ob!=current_object))
return eval_cost - save_eval_cost;
else
return 0;
}
/*
* To find if an object is present, we have to look in two inventory
* lists. The first list is the inventory of the current object.
* The second list is all things that have the same ->super as
* current_object.
* Also test the environment.
* If the second argument 'ob' is non zero, only search in the
* inventory of 'ob'. The argument 'ob' will be mandatory, later.
*/
static struct object *object_present2 PROT((char *, struct object *));
struct object *object_present(v, ob)
struct svalue *v;
struct object *ob;
{
struct svalue *ret;
struct object *ret_ob;
int specific = 0;
if (ob == 0)
ob = current_object;
else
specific = 1;
if (ob->flags & O_DESTRUCTED)
return 0;
if (v->type == T_OBJECT) {
if (specific) {
if (v->u.ob->super == ob)
return v->u.ob;
else
return 0;
}
if (v->u.ob->super == ob ||
(v->u.ob->super == ob->super && ob->super != 0))
return v->u.ob;
return 0;
}
ret_ob = object_present2(v->u.string, ob->contains);
if (ret_ob)
return ret_ob;
if (specific)
return 0;
if (ob->super) {
push_string(v->u.string, STRING_CONSTANT);
ret = apply("id", ob->super, 1);
if (ob->super->flags & O_DESTRUCTED)
return 0;
if (ret && !(ret->type == T_NUMBER && ret->u.number == 0))
return ob->super;
return object_present2(v->u.string, ob->super->contains);
}
return 0;
}
static struct object *object_present2(str, ob)
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_CONSTANT);
ret = apply("id", ob, 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;
}
/* KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE */
/* ALL THIS TO AVOID 1 BYTE PER OBJECT */
/* not used very often however -- Raistlin */
struct destructor_stack
{
struct object *element;
struct destructor_stack *next;
} *d_stack = NULL;
static struct object *top_of_stack()
{
if (!d_stack)
return NULL;
else
return d_stack->element;
}
/* cleans up things, just in case of errors... */
static void clean_stack()
{
struct destructor_stack *current, *previous=NULL;
current = d_stack;
while(current)
{
struct destructor_stack *next;
next = current->next;
if (current->element->flags&O_DESTRUCTED)
{
if (!previous) d_stack = current->next;
else previous->next = current->next;
free((char *)current);
}
else
previous = current;
current = next;
}
}
static void push_destructor(ob)
struct object *ob;
{
struct destructor_stack *new;
new = (struct destructor_stack *) xalloc(sizeof(struct destructor_stack));
new->element = ob;
new->next = NULL;
if (d_stack)
new->next = d_stack;
d_stack = new;
clean_stack();
}
static struct object *pop_destructor()
{
struct destructor_stack *popped;
struct object *ob;
popped = d_stack;
d_stack = d_stack->next;
ob = popped->element;
free((char *)popped);
clean_stack();
return ob;
}
static int test_stack(ob)
struct object *ob;
{
struct destructor_stack *current = d_stack;
while(current)
{
if (current->element == ob) return 1;
current = current->next;
}
return 0;
}
/*
* Remove an object. It is first moved into the destruct list, and
* not really destructed until later. (see destruct2()).
*/
void destruct_object(v, really)
struct svalue *v;
int really;
{
struct object *ob, *super;
struct object **pp;
int removed;
if (v->type == T_OBJECT)
ob = v->u.ob;
else {
ob = find_object2(v->u.string);
if (ob == 0)
error("destruct_object: Could not find %s\n", v->u.string);
}
if (ob->flags & O_DESTRUCTED)
return;
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
remove_object_from_stack(ob);
/*
* Added 4/26/91 -- Raistlin. destructor() is called on destructed objects
*
* This means that the object is already in the process of being destructed
* and another destructor (or it's own) tried to destruct it.
* The "really" flag means "Don't call the destructor", in case of error
*/
if (!really && test_stack(ob)) return;
if (!really && function_exists("destructor", ob))
{
push_destructor(ob);
(void)apply("destructor", ob, 0);
pop_destructor();
}
/*
* 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;
svp.u.ob->shadowed = 0;
svp.u.ob->shadowing = 0;
destruct_object(&svp, 0);
}
}
/*
* The chain of shadows is a double linked list. Take care to update
* it correctly.
*/
if (ob->shadowed)
ob->shadowed->shadowing = ob->shadowing;
if (ob->shadowing)
ob->shadowing->shadowed = ob->shadowed;
ob->shadowed = ob->shadowing = 0;
if (d_flag > 1)
debug_message("Destruct object %s (ref %d)\n", ob->name, ob->ref);
super = ob->super;
#ifdef COMPAT_MODE
if (super) {
struct svalue *weight;
/* Call exit in current room, if player or npc not in mudlib 3.0 */
if((ob->flags & O_ENABLE_COMMANDS)) {
push_object(ob, "destruct_object");
(void)apply("exit",super,1);
}
weight = apply("query_weight", ob, 0);
if (weight && weight->type == T_NUMBER) {
push_number(-weight->u.number);
(void)apply("add_weight", super, 1);
}
}
#endif
if (super == 0)
{
struct svalue svp;
svp.type = T_OBJECT;
while(ob->contains) {
svp.u.ob = ob->contains;
push_object(ob->contains, "destruct");
/*
* An errer here will not leave destruct() in an inconsistant
* stage.
*/
apply_master_ob("destruct_environment_of", 1);
if (svp.u.ob == ob->contains)
destruct_object(&svp, 0);
}
}
else
while(ob->contains)
move_or_destruct(ob->contains, super);
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;
}
set_heart_beat(ob, 0);
/*
* 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);
add_light(ob->super, - ob->total_light);
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;
}
}
/*
* 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.
*/
removed = 0;
/*
* If any of the calls above (move_or_destruct) caused a call
* to the efun destruct, then this object will already be destructed
* and removed from the list, so everything after this for loop will
* already have been done, so we can just return here.
*/
if (ob->flags & O_DESTRUCTED)
return;
else
for (pp = &obj_list; *pp; pp = &(*pp)->next_all) {
if (*pp != ob)
continue;
*pp = (*pp)->next_all;
removed = 1;
remove_object_hash(ob);
break;
}
if (!removed)
fatal("Failed to delete object.\n");
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;
obj_list_destruct = ob;
ob->flags |= O_DESTRUCTED;
}
/*
* This one is called when no program is executing.
* The super pointer is still maintained.
*/
void destruct2(ob)
struct object *ob;
{
if (d_flag > 1) {
debug_message("Destruct-2 object %s (ref %d)\n", ob->name, ob->ref);
}
if (ob->interactive)
remove_interactive(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.
*/
if (ob->prog->num_variables > 0) {
/*
* Deallocate variables in this object.
* The space of the variables are not deallocated until
* the object structure is freed in free_object().
*/
int i;
for (i=0; i<ob->prog->num_variables; i++) {
free_svalue(&ob->variables[i], "destruct_object");
ob->variables[i].type = T_NUMBER;
ob->variables[i].u.number = 0;
}
}
free_object(ob, "destruct_object");
}
#ifdef F_CREATE_WIZARD
/*
* This is the efun create_wizard(). Create a home dir for a wizard,
* and other files he may want.
*
* The real job is done by the master.c object, so the call could as well
* have been done to it directly. But, this function remains for
* compatibility.
*
* It should be replaced by a function in simul_efun.c !
*/
char *create_wizard(owner, domain)
char *owner;
char *domain;
{
struct svalue *ret;
#if 0
struct stat st;
char cmd[200], lbuf[128];
static char name[200], name2[200];
FILE *f;
struct object *owner_obj;
#endif
/*
* Let the master object do the job.
*/
push_constant_string(owner);
push_constant_string(domain);
push_object(current_object, "create_wizard");
ret = apply_master_ob("master_create_wizard", 3);
if (ret && ret->type == T_STRING)
return ret->u.string;
return 0;
#if 0
fprintf(stderr, "c_w called with %s and %s from %s\n",
owner, domain ? domain : "without domain", current_object->name);
/*
* Verify that it is a valid call of create_wizard(). This is done
* by a function in master.c. It will take the calling object as
* argument, and must return a non-zero value.
*/
push_object(current_object, "create_wizard");
ret = apply_master_ob("verify_create_wizard", 1);
if (ret == 0)
error("No wizards allowed ! (because no verify_create_wizard() in master.c)\n");
if (ret->type == T_NUMBER && ret->u.number == 0)
error("Illegal use of create_wizard() !\n");
/*
* Even if the object that called create_wizard() is trusted, we won't
* allow it to use funny names for the owner.
*/
if (!legal_path(owner))
error("Bad name to create_wizard: %s\n", owner);
owner_obj = find_living_object(owner, 1);
if (owner_obj == 0) {
fprintf(stderr,
"create_wizard: Could not find living object %s.\n", owner);
return 0;
}
(void)sprintf(name, "%s/%s", PLAYER_DIR, owner);
if(domain) {
(void)sprintf(name2, "%s/%s/%s", DOMAIN_DIR, domain, owner);
fprintf(stderr, "name = %s, name2 = %s\n", name, name2);
if (stat(name, &st) == 0) {
if((st.st_mode & S_IFMT) == S_IFDIR) {
rename(name, name2);
}
} else {
if (mkdir(name2, 0777) == -1) {
perror(name);
error("Could not mkdir %s\n", name2);
}
}
} else {
if (stat(name, &st) == 0)
error("Player %s already has a castle!\n", owner);
else
if (mkdir(name, 0777) == -1) {
perror(name);
error("Could not mkdir %s\n", name);
}
}
/* add castle */
if(domain) {
(void)sprintf(name, "%s/%s/common/domain.c", DOMAIN_DIR, domain);
} else {
(void)sprintf(name, "%s/%s/%s", PLAYER_DIR, owner, "castle.c");
}
if(stat(name, &st) == 0) {
fprintf(stderr, "castle file %s already exists.\n", name);
} else {
f = fopen(name, "w");
if (f == NULL)
error("Could not create a castle file %s!\n", name);
(void)fprintf(f, "#define NAME \"%s\"\n", domain ? domain : owner);
#ifdef CASTLE_ROOM
(void)fprintf(f, "#define DEST \"%s\"\n", CASTLE_ROOM);
#else
(void)fprintf(f, "#define DEST \"%s\"\n", current_object->super->name);
#endif
(void)fclose(f);
(void)sprintf(cmd, "cat %s >> %s", DEFAULT_CASTLE, name);
(void)system(cmd);
}
#ifdef COMPAT_MODE
f = fopen(init_file, "a");
if (f == NULL)
error("Could not add the new castle to the %s\n", init_file);
(void)fprintf(f, "%s\n", name);
(void)fclose(f);
#endif
return name;
#endif
}
#endif F_CREATE_WIZARD
/*
* A message from an object will reach
* all objects in the inventory,
* all objects in the same environment and
* the surrounding object.
* Non interactive objects gets no messages.
*
* There are two cases to take care of. If this routine is called from
* a player (indirectly), then the message goes to all in his
* environment. Otherwise, the message goes to all in the current_object's
* environment (as the case when called from a heart_beat()).
*
* If there is a second argument 'avoid_ob', then do not send the message
* to that object.
*/
#if 0
void say(v, avoid_ob)
struct svalue *v;
struct object *avoid_ob;
{
struct object *ob, *save_command_giver = command_giver;
struct object *origin;
char buff[257];
if (current_object->flags & O_ENABLE_COMMANDS)
command_giver = current_object;
else if (current_object->shadowing)
command_giver = current_object->shadowing;
if (command_giver)
origin = command_giver;
else
origin = current_object;
switch(v->type) {
case T_STRING:
strncpy(buff, v->u.string, sizeof buff);
buff[sizeof buff - 1] = '\0';
break;
case T_OBJECT:
strncpy(buff, v->u.ob->name, sizeof buff);
buff[sizeof buff - 1] = '\0';
break;
case T_NUMBER:
sprintf(buff, "%d", v->u.number);
break;
default:
error("Invalid argument %d to say()\n", v->type);
}
buff[sizeof buff - 1] = 0;
for (ob = origin->contains; ob; ob = ob->next_inv)
{
struct object *save_again;
if (ob->interactive == 0)
{
if ((ob->flags & O_ENABLE_COMMANDS) && ob != command_giver &&
ob != avoid_ob)
tell_npc(ob, buff);
if (ob->flags & O_DESTRUCTED)
break;
continue;
}
if (ob == save_command_giver || ob == avoid_ob)
continue;
save_again = command_giver;
command_giver = ob;
add_message("%s", buff);
command_giver = save_again;
}
if (origin->super)
{
if (origin->super->interactive && origin != command_giver &&
origin->super != avoid_ob)
{
command_giver = origin->super;
add_message("%s", buff);
}
else if (origin->super->interactive == 0 &&
origin->super != avoid_ob &&
(origin->super->flags & O_ENABLE_COMMANDS) &&
ob != command_giver)
tell_npc(origin->super, buff);
for (ob = origin->super->contains; ob; ob = ob->next_inv)
{
struct object *save_again;
if (ob == avoid_ob)
continue;
if (ob->interactive == 0)
{
if ((ob->flags & O_ENABLE_COMMANDS) && ob != command_giver)
tell_npc(ob, buff);
if (ob->interactive)
break;
continue;
}
if (ob == command_giver)
continue;
save_again = command_giver;
command_giver = ob;
add_message("%s", buff);
command_giver = save_again;
}
}
command_giver = save_command_giver;
}
#else
void say(v, avoid)
struct svalue *v;
struct vector *avoid;
{
extern struct vector *order_alist PROT((struct vector *));
struct vector *vtmpp;
static struct vector vtmp = { 1, 1,
#ifdef DEBUG
1,
#endif
(struct wiz_list *)NULL,
{ { T_POINTER } }
};
extern int assoc PROT((struct svalue *key, struct vector *));
struct object *ob, *save_command_giver = command_giver;
struct object *origin;
char buff[256];
#define INITAL_MAX_RECIPIENTS 20
int max_recipients = INITAL_MAX_RECIPIENTS;
struct object *first_recipients[INITAL_MAX_RECIPIENTS];
struct object **recipients = first_recipients;
struct object **curr_recipient = first_recipients;
struct object **last_recipients =
&first_recipients[INITAL_MAX_RECIPIENTS-1];
struct object *save_again;
static struct svalue stmp = { T_OBJECT };
if (current_object->flags & O_ENABLE_COMMANDS)
command_giver = current_object;
else if (current_object->shadowing)
command_giver = current_object->shadowing;
if (command_giver) {
origin = command_giver;
if (avoid->item[0].type == T_NUMBER) {
avoid->item[0].type = T_OBJECT;
avoid->item[0].u.ob = command_giver;
add_ref(command_giver, "ass to var");
}
} else
origin = current_object;
vtmp.item[0].u.vec = avoid;
vtmpp = order_alist(&vtmp);
avoid = vtmpp->item[0].u.vec;
if (ob = origin->super) {
if (ob->flags & O_ENABLE_COMMANDS || ob->interactive) {
*curr_recipient++ = ob;
}
for (ob = origin->super->contains; ob; ob = ob->next_inv) {
if (ob->flags & O_ENABLE_COMMANDS || ob->interactive) {
if (curr_recipient >= last_recipients) {
max_recipients <<= 1;
curr_recipient = (struct object **)
alloca(max_recipients * sizeof(struct object *));
memcpy((char*)curr_recipient, (char*)recipients,
max_recipients * sizeof(struct object *)>>1);
recipients = curr_recipient;
last_recipients = &recipients[max_recipients-1];
}
*curr_recipient++ = ob;
}
}
}
for (ob = origin->contains; ob; ob = ob->next_inv) {
if (ob->flags & O_ENABLE_COMMANDS || ob->interactive) {
if (curr_recipient >= last_recipients) {
max_recipients <<= 1;
curr_recipient = (struct object **)alloca(max_recipients);
memcpy((char*)curr_recipient, (char*)recipients,
max_recipients * sizeof(struct object *)>>1);
recipients = curr_recipient;
last_recipients = &recipients[max_recipients-1];
}
*curr_recipient++ = ob;
}
}
*curr_recipient = (struct object *)0;
switch(v->type) {
case T_STRING:
strncpy(buff, v->u.string, sizeof buff);
buff[sizeof buff - 1] = '\0';
break;
case T_OBJECT:
strncpy(buff, v->u.ob->name, sizeof buff);
buff[sizeof buff - 1] = '\0';
break;
case T_NUMBER:
sprintf(buff, "%d", v->u.number);
break;
case T_POINTER:
for (curr_recipient = recipients; ob = *curr_recipient++; ) {
extern void push_vector PROT((struct vector *));
if (ob->flags & O_DESTRUCTED) continue;
stmp.u.ob = ob;
if (assoc(&stmp, avoid) >= 0) continue;
push_vector(v->u.vec);
push_object(command_giver, "say");
apply("catch_msg", ob, 2);
}
break;
default:
error("Invalid argument %d to say()\n", v->type);
}
save_again = command_giver;
for (curr_recipient = recipients; ob = *curr_recipient++; ) {
if (ob->flags & O_DESTRUCTED) continue;
stmp.u.ob = ob;
if (assoc(&stmp, avoid) >= 0) continue;
if (ob->interactive == 0) {
tell_npc(ob, buff);
continue;
}
command_giver = ob;
add_message("%s", buff);
command_giver = save_again;
}
free_vector(vtmpp);
command_giver = save_command_giver;
}
#endif
/*
* Send a message to all objects inside an object.
* Non interactive objects gets no messages.
* Compare with say().
*/
void tell_room(room, v, avoid)
struct object *room;
struct svalue *v;
struct vector *avoid; /* has to be in alist order */
{
struct object *ob, *save_command_giver = command_giver;
char buff[257];
switch(v->type) {
case T_STRING:
strncpy(buff, v->u.string, sizeof buff);
buff[sizeof buff - 1] = '\0';
break;
case T_OBJECT:
strncpy(buff, v->u.ob->name, sizeof buff);
buff[sizeof buff - 1] = '\0';
break;
case T_NUMBER:
sprintf(buff, "%d", v->u.number);
break;
default:
error("Invalid argument %d to tell_room()\n", v->type);
}
buff[sizeof buff - 1] = 0;
for (ob = room->contains; ob; ob = ob->next_inv) {
int assoc PROT((struct svalue *key, struct vector *));
static struct svalue stmp = { T_OBJECT, } ;
stmp.u.ob = ob;
if (assoc(&stmp, avoid) >= 0) continue;
if (ob->interactive == 0) {
if (ob->flags & O_ENABLE_COMMANDS) {
/*
* We want the monster code to have a correct this_player()
*/
command_giver = save_command_giver;
tell_npc(ob, buff);
}
if (ob->flags & O_DESTRUCTED)
break;
continue;
}
command_giver = ob;
add_message("%s", buff);
}
command_giver = save_command_giver;
}
void shout_string(str)
char *str;
{
struct object *ob, *save_command_giver = command_giver;
int emergency = 0, i;
extern int num_player;
#ifdef LOG_SHOUT
FILE *f = 0;
#endif
char *p;
if (str[0] == '!') { emergency = 1; }
str = string_copy(str+emergency);
#ifdef LOG_SHOUT
if (command_giver) {
struct svalue *v = 0;
v = apply("query_real_name", command_giver, 0);
if (v && v->type == T_STRING)
p = v->u.string;
} else if (current_object && current_object->user)
p = current_object->user->name;
if (p)
f = fopen("log/SHOUTS", "a");
if (f) {
fprintf(f, "%s: %s\n", p, str);
fclose(f);
}
#endif
#if 0
for (ob = obj_list; ob; ob = ob->next_all) {
struct svalue *ret;
if (!ob->interactive || ob == save_command_giver || !ob->super)
continue;
push_object(ob, "shout_string");
ret = apply_master_ob("valid_shout", 1);
if (!emergency && (!ret || ret->type != T_NUMBER || ret->u.number == 0))
continue;
command_giver = ob;
add_message("%s", str);
}
#endif
for (i = 0; i < num_player; ++i)
{
struct svalue *ret;
ob = get_interactive_object(i);
if (!ob->interactive || ob == save_command_giver || !ob->super)
continue;
ret = 0;
if (!emergency)
{
push_object(ob, "shout_string");
ret = apply_master_ob("valid_shout", 1);
if (ret && ret->type == T_NUMBER && ret->u.number == 0)
continue;
}
command_giver = ob;
add_message("%s", str);
}
command_giver = save_command_giver;
free(str);
}
struct object *first_inventory(arg)
struct svalue *arg;
{
struct object *ob;
if (arg->type == T_STRING)
ob = find_object(arg->u.string);
else
ob = arg->u.ob;
if (ob == 0)
error("No object to first_inventory()");
if (ob->contains == 0)
return 0;
return ob->contains;
}
/*
* 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(num)
int num;
{
if (current_object->flags & O_DESTRUCTED)
return;
if (d_flag > 1) {
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;
command_giver = 0;
}
}
/*
* Set up a function in this object to be called with the next
* user input string.
*/
int input_to(fun, flag)
char *fun;
int flag;
{
struct sentence *s;
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");
return 1;
}
free_sentence(s);
return 0;
}
#define MAX_LINES 50
#define MAXLSCOLUMNS 6
struct entry {
int len;
int size;
char file[34];
};
int pstrcmp(p1, p2)
char **p1, **p2;
{
return strcmp(((struct entry *)p1)->file,((struct entry *)p2)->file);
}
/*
* List files in a directory. The standard 'ls' could be used, but
* it takes too much time.
* Prepared for flag decoding.
*
* Look at the the last component of the path name. It is a regular
* expression, select only matching files.
*
* All .i files and '.' and '..' are only listed if they match the regexp.
*/
void list_files(path)
char *path;
{
DIR *dirp;
struct direct *de;
struct stat st;
char regexp[80], *p, path2[150];
int do_match = 0;
struct entry name[MAXLSCOLUMNS*MAX_LINES];
/*
struct entry *nbuf;
*/
int colwidth[MAXLSCOLUMNS];
char *fname;
int num, i, j, k, ncols, npercol, max, total, size;
if (!path)
path = ".";
#ifdef COMPAT_MODE
path = check_file_name(path, 0);
#else
path = check_valid_path(path, current_object->eff_user,
"list_files", 0);
#endif
if (path == 0)
return;
strncpy(path2, path, sizeof path2);
path2[sizeof path2 - 1] = '\0';
if(path2[0]==0)
strcpy(path2, "./");
p = path2 + strlen(path2) - 2;
if (p >= path2 && p[0] == '/' && p[1] == '.')
p[0] = '\0';
if (stat(path2, &st) == -1)
{
/* Either the directory does not exist, or it is a regexp
* file name. Strip of the last component.
*/
p = strrchr(path2, '/');
if (p == 0 || p[1] == '\0')
return;
strncpy(regexp, p+1, sizeof regexp);
regexp[sizeof regexp - 1] = '\0';
*p = '\0';
do_match = 1;
}
dirp = opendir(path2);
if (dirp == 0)
{
add_message("No such directory '%s'\n", path2);
return;
}
num = 0;
total = 0;
if (path[0]) strcat(path2,"/");
i = strlen(path2);
fname = &path2[i];
i = sizeof(path2) - i - 1;
/*
nbuf = (struct entry *)xalloc((sizeof(struct entry))*MAXLSCOLUMNS*MAX_LINES);
for (j=0; j<MAXLSCOLUMNS*MAX_LINES; j++)
name[j] = nbuf+j;
*/
for(de = readdir(dirp); (de && num<(MAXLSCOLUMNS*MAX_LINES));
de = readdir(dirp))
{
if (!do_match && (strcmp(de-> d_name, ".") == 0 ||
strcmp(de-> d_name, "..") == 0))
continue;
if (do_match && !match_string(regexp, de-> d_name))
continue;
strncpy(fname, de-> d_name, i);
j = 0;
if (stat(path2, &st) != -1)
{
size = (int)st.st_size / 1024 + ((int)st.st_size % 1024 > 0);
total += size;
if (S_IFDIR & st.st_mode) j = 1;
}
else size=0;
strncpy(name[num].file, de->d_name, sizeof(name[num].file));
name[num].file[sizeof(name[num].file)-1]=0;
name[num].len = (int) (de-> d_namlen+j);
name[num].size = (int) size;
if (j)
strcat(name[num].file,"/");
num++;
}
closedir(dirp);
if (num == 0)
{
add_message(do_match?"No matching files.\n":"No files.\n");
/*
free(nbuf);
*/
return;
}
/* Sort the names. */
qsort((char *)name, num, sizeof (struct entry), pstrcmp);
ncols = num/(num/MAXLSCOLUMNS+(num%MAXLSCOLUMNS > 0))+
((num%(num/MAXLSCOLUMNS+(num%MAXLSCOLUMNS > 0))) > 0);
while (1)
{
npercol = num / ncols + (num % ncols > 0);
for (j=0; j<ncols; j++)
{
max = 0;
for (i=j*npercol; i<(j+1)*npercol && i<num; i++)
if (name[i].len > max)
max=name[i].len;
colwidth[j] = max+6;
}
for (i=0, max=0; i<ncols; i++)
max+=colwidth[i];
if (max<79)
break;
ncols--;
if (ncols<1)
{
ncols=1;
npercol=num;
break;
}
}
for (i=1; i < ncols; i++)
colwidth[i]+=colwidth[i-1];
add_message("Total %d\n", total);
max = 0; i = 0; j = 0;
strcpy(path2,
" ");
while (max < num)
{
k = (i * npercol) + j;
if (i >= ncols)
{
j++; i=0;
add_message("%s\n", path2);
strcpy(path2,
" ");
continue;
}
if (k < num)
{
if (name[k].size > 9999)
sprintf(&path2[(i > 0 ? colwidth[i - 1] : 0)],
"BIG! %s ", name[k].file);
else
sprintf(&path2[(i > 0 ? colwidth[i-1] : 0)],
"%4d %s ",
name[k].size,
name[k].file);
if ((i + 1) * npercol + j < num)
path2[(i > 0 ? colwidth[i - 1] : 0) +
6 + name[k].len]=' ';
max++;
}
i++;
}
add_message("%s\n", path2);
if (num == MAXLSCOLUMNS * MAX_LINES)
add_message("***Too many files, listing truncated***\n");
/*
free(nbuf);
*/
}
int fstrcmp(a, b)
char **a, **b;
{
return strcmp((char *)a, (char *)b);
}
struct vector *get_files(path)
char *path;
{
DIR *dirp;
struct direct *de;
struct stat st;
char regexp[80], *p, path2[150], *fname;
char name[MAX_ARRAY_SIZE][34];/*dunno where 34 comes from, but they use it */
int do_match = 0;
int num, i, j;
struct vector *ret;
if (!path)
path = ".";
#ifdef COMPAT_MODE
path = check_file_name(path, 0);
#else
path = check_valid_path(path, current_object->eff_user,
"files", 0);
#endif
if (path == 0)
return 0;
strncpy(path2, path, sizeof path2);
path2[sizeof path2 - 1] = '\0';
p = path2 + strlen(path2) - 2;
if (p >= path2 && p[0] == '/' && p[1] == '.')
p[0] = '\0';
if (stat(path2, &st) == -1)
{
/* Either the directory does not exist, or it is a regexp
* file name. Strip of the last component.
*/
p = strrchr(path2, '/');
if (p == 0 || p[1] == '\0')
return 0;
strncpy(regexp, p+1, sizeof regexp);
regexp[sizeof regexp - 1] = '\0';
*p = '\0';
do_match = 1;
}
dirp = opendir(path2);
if (dirp == 0)
return 0;
num = 0;
if (path[0]) strcat(path2,"/");
i = strlen(path2);
fname = path2+i;
i = sizeof(path2) - i - 1;
for(de = readdir(dirp); de; de = readdir(dirp))
{
if (!do_match && (strcmp(de-> d_name, ".") == 0 ||
strcmp(de-> d_name, "..") == 0))
continue;
if (do_match && !match_string(regexp, de-> d_name))
continue;
j = 0;
strncpy(fname, de-> d_name, i);
if (stat(path2, &st) != -1)
if (S_IFDIR & st.st_mode) j = 1;
strncpy(name[num], de->d_name, sizeof name[num]);
name[num][sizeof(name[num])-1] = 0;
if (j)
strcat(name[num],"/");
num++;
}
closedir(dirp);
/* Sort the names. */
if (num)
qsort((char *)name, num, sizeof name[0], fstrcmp);
ret = allocate_array(num);
for (i=0; i<num; ++i)
{
ret->item[i].type = T_STRING;
ret->item[i].u.string = make_shared_string(name[i]);
ret->item[i].string_type = STRING_SHARED;
}
return ret;
}
/*
* List files in directory. This function do same as standard list_files
* 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.
* Differenceies 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(path)
char *path;
{
struct vector *v;
int i, count = 0;
DIR *dirp;
int namelen, do_match = 0;
struct direct *de;
struct stat st;
char *temppath;
char *p;
char *regexp = 0;
if (!path)
return 0;
#ifdef COMPAT_MODE
path = check_file_name(path, 0);
#else
path = check_valid_path(path, current_object->eff_user, "get_dir", 0);
#endif
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)) {
namelen = de->d_namlen;
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)) {
namelen = de->d_namlen;
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(path)
char *path;
{
char buff[1000];
FILE *f;
struct stat st;
int offset;
#ifdef COMPAT_MODE
path = check_file_name(path, 0);
#else
path = check_valid_path(path, current_object->eff_user, "tail", 0);
#endif
if (path == 0)
return 0;
f = fopen(path, "r");
if (f == 0)
return 0;
if (fstat(fileno(f), &st) == -1)
error("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 print_file(path, start, len)
char *path;
int start, len;
{
char buff[1000];
FILE *f;
int i, num_read = 0;;
if (len < 0)
return -1;
#ifdef COMPAT_MODE
path = check_file_name(path, 0);
#else
path = check_valid_path(path, current_object->eff_user,
"print_file", 0);
#endif
if (path == 0)
return -1;
f = fopen(path, "r");
if (f == 0)
return -1;
if (start < 0) {
int lines = 0;
while (fgets(buff, sizeof buff, f) != 0)
lines++;
rewind(f);
start = lines - len + 1;
}
for (i = 1; i < start; i++) {
if (fgets(buff, sizeof buff, f) == 0)
break;
}
if (!len || len > MAX_LINES)
len = MAX_LINES;
for (i = 0; i < len; i++) {
if (fgets(buff, sizeof buff, f) == 0)
break;
buff[sizeof buff - 1] = 0;
num_read++;
add_message("%s", buff);
}
if (i == MAX_LINES)
add_message("*****TRUNCATED*****\n");
fclose(f);
return num_read;
}
int remove_file(path)
char *path;
{
#ifdef COMPAT_MODE
path = check_file_name(path, 1);
#else
path = check_valid_path(path, current_object->eff_user,
"remove_file", 1);
#endif
if (path == 0)
return 0;
if (unlink(path) == -1)
return 0;
return 1;
}
void log_file(file, str)
char *file, *str;
{
FILE *f;
char file_name[100];
struct stat st;
sprintf(file_name, "/log/%s", file);
#ifdef COMPAT_MODE
if (strchr(file, '/') || file[0] == '.' || strlen(file) > 30
#ifdef MSDOS
|| !valid_msdos(file)
#endif
)
error("Illegal file name to log_file(%s)\n", file);
#else
if (!check_valid_path(file_name, current_object->eff_user,
"log_file", 1))
return;
#endif
if (stat(file_name+1, &st) != -1 && st.st_size > MAX_LOG_SIZE) {
char file_name2[sizeof file_name + 4];
sprintf(file_name2, "%s.old", file_name+1);
rename(file_name+1, file_name2); /* No panic if failure */
}
f = fopen(file_name+1, "a"); /* Skip leading '/' */
if (f == 0)
return;
fwrite(str, strlen(str), 1, f);
fclose(f);
}
void
print_svalue(arg)
struct svalue *arg;
{
if (arg == 0)
add_message("<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 (command_giver && (command_giver->flags & O_ENABLE_COMMANDS) &&
!command_giver->interactive)
tell_npc(command_giver, arg->u.string);
else
add_message("%s", arg->u.string);
} else if (arg->type == T_OBJECT)
add_message("OBJ(%s)", arg->u.ob->name);
else if (arg->type == T_NUMBER)
add_message("%d", arg->u.number);
else if (arg->type == T_POINTER)
add_message("<ARRAY>");
else
add_message("<UNKNOWN>");
}
void do_write(arg)
struct svalue *arg;
{
struct object *save_command_giver = command_giver;
if (command_giver == 0 && current_object->shadowing)
command_giver = current_object;
if (command_giver)
while (command_giver->shadowing)
command_giver = command_giver->shadowing;
print_svalue(arg);
command_giver = save_command_giver;
}
/* Find an object. If not loaded, load it ! */
struct object *find_object(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, 0);
if (ob->flags & O_DESTRUCTED) /* *sigh* */
return 0;
if (ob && ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
return ob;
}
/* Look for a loaded object. Return 0 if non found. */
struct object *find_object2(str)
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)) {
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
return ob;
}
return 0;
}
#if 0
void apply_command(com)
char *com;
{
struct value *ret;
if (command_giver == 0)
error("command_giver == 0 !\n");
ret = apply(com, command_giver->super, 0);
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 */
/*
* 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(item, dest)
struct object *item, *dest;
{
struct object **pp, *ob, *next_ob;
struct object *save_cmd = command_giver;
extern int current_time;
#ifndef COMPAT_MODE
if (item != current_object)
error("Illegal to move other object than this_object()\n");
#endif
/* Recursive moves are not allowed. */
for (ob = dest; ob; ob = ob->super)
if (ob == item)
return;
if (item->shadowing)
error("Can't move an object that is shadowing.\n");
#if 0 /* Not now /Lars */
/*
* Objects must have inherited std/object if they are to be allowed to
* be moved.
*/
#ifndef COMPAT_MODE
if (!(item->flags & O_APPROVED) ||
!(dest->flags & O_APPROVED)) {
error("Trying to move object where src or dest not inherit std/object\n");
return;
}
#endif
#endif
/* if it's past due for a reset, reset the object immediately */
/* else mark it to be reset. Set command_giver to zero to */
/* simulate a normal reset, where there is no command_giver */
/* also, reset the eval_cost, since this shouldn't add to the */
/* current execution thread's eval_cost */
#ifdef COMPAT_MODE
{
extern int eval_cost;
if (item->next_reset < current_time)
{
struct object *s = command_giver;
int save = eval_cost;
eval_cost = 0;
command_giver = 0;
reset_object(item, 1);
command_giver = s;
eval_cost = save;
if (item->flags & O_DESTRUCTED)
return;
}
else
item->flags &= ~O_RESET_STATE;
if (dest->next_reset < current_time)
{
struct object *s = command_giver;
int save = eval_cost;
eval_cost = 0;
command_giver = 0;
reset_object(dest, 1);
eval_cost = save;
command_giver = s;
if (dest->flags & O_DESTRUCTED)
return;
}
else
dest->flags &= ~O_RESET_STATE;
}
#endif
add_light(dest, item->total_light);
if (item->super) {
int okey = 0;
if (item->flags & O_ENABLE_COMMANDS) {
#ifdef COMPAT_MODE
command_giver = item;
push_object(item, "move_object");
(void)apply("exit", item->super, 1);
if (item->flags & O_DESTRUCTED || dest->flags & O_DESTRUCTED)
return; /* Give up */
#endif
remove_sent(item->super, item);
}
if (item->super->flags & O_ENABLE_COMMANDS)
remove_sent(item, item->super);
add_light(item->super, - item->total_light);
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
* (except in the -o mode). It might be too slow, though :-(
*/
if (item->flags & O_ENABLE_COMMANDS) {
command_giver = item;
(void)apply("init", dest, 0);
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)
continue;
#if 0 /* Raistlin */
error("An object was destructed at call of init()\n");
#endif
if (ob->flags & O_ENABLE_COMMANDS) {
command_giver = ob;
(void)apply("init", item, 0);
if (dest != item->super) {
command_giver = save_cmd; /* marion */
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;
(void)apply("init", ob, 0);
if (dest != item->super) {
command_giver = save_cmd; /* marion */
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;
(void)apply("init", item, 0);
}
command_giver = save_cmd;
}
/*
* Every object as a count of number of light sources it contains.
* Update this.
*/
void add_light(p, n)
struct object *p;
int n;
{
if (n == 0)
return;
p->total_light += n;
if (p->super)
add_light(p->super, n);
}
struct sentence *sent_free = 0;
int tot_alloc_sentence;
struct sentence *alloc_sentence() {
struct sentence *p;
if (sent_free == 0) {
p = (struct sentence *)xalloc(sizeof *p);
tot_alloc_sentence++;
} else {
p = sent_free;
sent_free = sent_free->next;
}
/*
p = (struct sentence *)xalloc(sizeof(struct sentence));
tot_alloc_sentence++;
*/
p->verb = 0;
p->ob = 0;
p->function = 0;
p->next = 0;
p->short_verb = 0;
p->no_space = 0;
return p;
}
#ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
void free_all_sent() {
struct sentence *p;
for (;sent_free; sent_free = p) {
p = sent_free->next;
free((char *)sent_free);
}
}
#endif
void free_sentence(p)
struct sentence *p;
{
if (p->function)
free_string(p->function);
if (p->verb)
free_string(p->verb);
/*
memset((char *)p, '\0', sizeof(struct sentence));
free((char *)p);
tot_alloc_sentence--;
*/
p->verb = 0;
p->function = 0;
p->ob = 0;
p->next = sent_free;
sent_free = p;
}
compress_spaces(buff)
char *buff;
{
char *p, *q;
p = buff;
while(isspace(*p)) p++;
strcpy(buff, p);
p = buff;
while (*p)
{
if (isspace(*p) && *p != ' ') *p = ' ';
if (isspace(*p) && isspace(*(p+1)))
{
q = ++p;
while (isspace(*q)) ++q;
strcpy(p, q);
}
else
++p;
}
}
/*
* Find the sentence for a command from the player.
* Return success status.
*/
int player_parser(buff)
char *buff;
{
struct sentence *s;
char *p;
int length;
struct object *save_current_object = current_object;
char verb_copy[100], verb_arg[1000];
if (d_flag > 1)
debug_message("cmd [%s]: %s\n", command_giver->name, buff);
#if 0
/* strip trailing spaces. */
compress_spaces(buff);
#endif /* not used yet */
for (p = buff + strlen(buff) - 1; p >= buff; p--) {
if (*p != ' ')
break;
*p = '\0';
}
if (buff[0] == '\0')
return 0;
if (special_parse(buff))
return 1;
p = strchr(buff, ' ');
if (p == 0)
length = strlen(buff);
else
length = p - buff;
clear_notify();
for (s=command_giver->sent; s; s = s->next) {
struct svalue *ret;
struct object *command_object;
int len;
if (s->verb == 0)
error("An 'action' did something, but returned 0 or had an undefined verb.\n");
len = strlen(s->verb);
if (s->no_space) {
if (strncmp(buff, s->verb, len) != 0)
continue;
}
else if (s->short_verb) {
#ifdef OLD_ADD_ACTION
if (strncmp(buff, s->verb, len))
#else
if (length < s->short_verb || strncmp(buff, s->verb, length) != 0)
#endif
continue;
} else {
if (len != length) continue;
if (strncmp(buff, s->verb, length) != 0)
continue;
}
/*
* Now we have found a special sentence !
*/
if (d_flag > 1)
debug_message("Local command %s on %s\n", s->function, s->ob->name);
if (!s->no_space)
if (length < sizeof verb_copy)
len = length;
else
len = sizeof verb_copy - 1;
strncpy(verb_copy, 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.
*/
#ifndef COMMAND_STATIC
if (current_object == 0)
#endif
current_object = s->ob;
command_object = s->ob;
if (s->no_space) {
push_constant_string(&buff[strlen(s->verb)]);
strncpy(verb_arg, buff+strlen(s->verb), sizeof verb_arg -1);
verb_arg[sizeof verb_arg - 1] = 0;
last_arg = verb_arg;
ret = apply(s->function, s->ob, 1);
}
else if (buff[length] == ' ') {
push_constant_string(&buff[length+1]);
strncpy(verb_arg, buff+length+1, sizeof verb_arg -1);
verb_arg[sizeof verb_arg - 1] = 0;
last_arg = verb_arg;
ret = apply(s->function, s->ob, 1);
} else {
last_arg = 0;
ret = apply(s->function, s->ob, 0);
}
if (current_object->flags & O_DESTRUCTED)
{
if (!command_giver)
return 1;
s = command_giver->sent; /* Restart :-( */
}
current_object = save_current_object;
last_verb = 0;
last_arg = 0;
/* If we get fail from the call, it was wrong second argument. */
if (ret && ret->type == T_NUMBER && ret->u.number == 0)
continue;
if (s && command_object->user && command_giver->interactive &&
!(command_giver->flags & O_IS_WIZARD))
command_object->user->score++;
if (ret == 0)
add_message("Error: function %s not found.\n", s->function);
break;
}
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. If the command name
* is not given here, it should be given with add_verb().
*
* The optinal 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.
*/
void add_action(str, cmd, flag)
char *str, *cmd;
int flag;
{
struct sentence *p;
struct object *ob;
if (str[0] == ':')
error("Illegal function name: %s\n", str);
if (current_object->flags & O_DESTRUCTED)
return;
ob = current_object;
while (ob->shadowing)
ob = ob->shadowing;
if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED))
return;
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");
if (d_flag > 1)
debug_message("--Add action %s\n", str);
#ifdef COMPAT_MODE
if (strcmp(str, "exit") == 0)
error("Illegal to define a command to the exit() function.\n");
#endif
p = alloc_sentence();
p->function = make_shared_string(str);
p->ob = ob;
p->next = command_giver->sent;
p->short_verb = flag;
p->no_space = 0;
if (cmd)
p->verb = make_shared_string(cmd);
else
p->verb = 0;
command_giver->sent = p;
}
void add_verb(str, no_space)
char *str;
int no_space;
{
if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED))
return;
if (command_giver->sent == 0)
error("No add_action().\n");
if (command_giver->sent->verb != 0)
error("Tried to set verb again.\n");
command_giver->sent->verb = make_shared_string(str);
command_giver->sent->no_space = no_space;
if (d_flag > 1)
debug_message("--Adding verb %s to action %s\n", str,
command_giver->sent->function);
}
/*
* Remove all commands (sentences) defined by object 'ob' in object
* 'player'.
*/
void remove_sent(ob, player)
struct object *ob, *player;
{
struct sentence **s;
for (s = &player->sent; *s;)
{
struct sentence *tmp;
if ((*s)->ob == ob)
{
if (d_flag > 1)
debug_message("--Unlinking sentence %s\n", (*s)->function);
tmp = *s;
*s = tmp->next;
free_sentence(tmp);
}
else
s = &((*s)->next);
}
}
char debug_parse_buff[51]; /* Used for debugging */
/*
* Hard coded commands, that will be available to all players. They can not
* be redefined, so the command name should be something obscure, not likely
* to be used in the game.
*/
int special_parse(buff)
char *buff;
{
int verbose = 0;
strncpy(debug_parse_buff, buff, sizeof debug_parse_buff-1);
debug_parse_buff[sizeof debug_parse_buff - 1] = 0;
if (strcmp(buff, "malloc") == 0) {
#if defined(MALLOC_malloc) || defined(MALLOC_smalloc)
dump_malloc_data();
#endif
#ifdef MALLOC_gmalloc
add_message("Using Gnu malloc.\n");
#endif
#ifdef MALLOC_sysmalloc
add_message("Using system malloc.\n");
#endif
return 1;
}
if (strcmp(buff, "dumpallobj") == 0) {
dumpstat();
return 1;
}
#if defined(MALLOC_malloc) || defined(MALLOC_smalloc)
if (strcmp(buff, "debugmalloc") == 0) {
extern int debugmalloc;
debugmalloc = !debugmalloc;
if (debugmalloc)
add_message("On.\n");
else
add_message("Off.\n");
return 1;
}
#endif
if (strcmp(buff, "status") == 0 ||
(strcmp(buff, "status tables") == 0 && (verbose = 1))) {
int tot, res;
extern int tot_alloc_sentence, tot_alloc_object,
num_swapped, total_bytes_swapped, reserved_area,
num_arrays, total_array_size, total_object_size;
extern int total_num_prog_blocks, total_prog_block_size;
#ifdef COMM_STAT
extern int add_message_calls,inet_packets,inet_volume;
#endif
if (reserved_area)
res = RESERVED_SIZE;
else
res = 0;
#ifdef COMM_STAT
if (verbose)
add_message(
"Calls to add_message: %d Packets: %d Average packet size: %f\n",
add_message_calls, inet_packets, (float)inet_volume/inet_packets);
#endif
if (!verbose)
{
add_message("Sentences: %5d %7d\n", tot_alloc_sentence,
tot_alloc_sentence * sizeof (struct sentence));
add_message("Objects: %5d %7d\n",
tot_alloc_object, total_object_size);
add_message("Arrays: %5d %7d\n", num_arrays, total_array_size);
add_message("Prog blocks: %5d %7d (%d swapped, %d Kbyte)\n",
total_num_prog_blocks, total_prog_block_size,
num_swapped, total_bytes_swapped / 1024);
add_message("Memory reserved: %7d\n", res);
}
/* status_lnodes_matched(); */
if (verbose)
stat_living_objects();
tot = total_prog_block_size +
show_otable_status(verbose) +
heart_beat_status(verbose) +
add_string_status(verbose) +
print_call_out_usage(verbose) +
total_array_size +
total_object_size +
res;
if (!verbose)
{
add_message("\t\t\t\t\t --------\n");
add_message("Total:\t\t\t\t\t %8d\n", tot);
}
return 1;
}
#ifdef NEED_DIRECTIONS
if (strcmp(buff, "e") == 0) {
(void)strcpy(buff, "east");
return 0;
}
if (strcmp(buff, "w") == 0) {
(void)strcpy(buff, "west");
return 0;
}
if (strcmp(buff, "s") == 0) {
(void)strcpy(buff, "south");
return 0;
}
if (strcmp(buff, "n") == 0) {
(void)strcpy(buff, "north");
return 0;
}
if (strcmp(buff, "d") == 0) {
(void)strcpy(buff, "down");
return 0;
}
if (strcmp(buff, "u") == 0) {
(void)strcpy(buff, "up");
return 0;
}
if (strcmp(buff, "nw") == 0) {
(void)strcpy(buff, "northwest");
return 0;
}
if (strcmp(buff, "ne") == 0) {
(void)strcpy(buff, "northeast");
return 0;
}
if (strcmp(buff, "sw") == 0) {
(void)strcpy(buff, "southwest");
return 0;
}
if (strcmp(buff, "se") == 0) {
(void)strcpy(buff, "southeast");
return 0;
}
#endif
return 0;
}
void print_local_commands() {
struct sentence *s;
add_message("Current local commands:\n");
for (s = command_giver->sent; s; s = s->next)
add_message("%s ", s->verb);
add_message("\n");
}
/*VARARGS1*/
void fatal(fmt, a, b, c, d, e, f, g, h)
char *fmt;
int a, b, c, d, e, f, g, h;
{
static int in_fatal = 0;
/* Prevent double fatal. */
if (in_fatal)
abort();
in_fatal = 1;
(void)fprintf(stderr, fmt, a, b, c, d, e, f, g, h);
fflush(stderr);
if (current_object)
(void)fprintf(stderr, "Current object was %s\n",
current_object->name);
debug_message(fmt, a, b, c, d, e, f, g, h);
if (current_object)
debug_message("Current object was %s\n", current_object->name);
debug_message("Dump of variables:\n");
#if defined(DEBUG) && defined(TRACE_CODE)
(void)dump_trace(1);
#else
(void)dump_trace();
#endif
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;
if (error_recovery_context_exists > 1) {
longjmp(error_recovery_context, 1);
fatal("Throw_error failed!");
}
error("Throw with no catch.\n");
}
static char emsg_buf[2000];
/*VARARGS1*/
void error(fmt, a, b, c, d, e, f, g, h)
char *fmt;
int a, b, c, d, e, f, g, h;
{
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;
sprintf(emsg_buf+1, fmt, a, b, c, d, e, f, g, h);
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)
error("Too many simultaneous errors.\n");
debug_message("%s", emsg_buf+1);
if (current_object) {
debug_message("Current object was %s, line %d\n",
current_object->name, current_line);
save_error(emsg_buf, current_object->name, current_line);
}
#if defined(DEBUG) && defined(TRACE_CODE)
object_name = dump_trace(1);
#else
object_name = dump_trace();
#endif
fflush(stdout);
if (object_name) {
struct object *ob;
ob = find_object2(object_name);
if (!ob) {
if (command_giver)
add_message("error when executing program in destroyed object %s\n",
object_name);
debug_message("error when executing program in destroyed object %s\n",
object_name);
}
}
if (command_giver && command_giver->interactive) {
struct svalue *v = 0;
num_error--;
/*
* 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_constant_string("error messages");
v = apply_master_ob("query_player_level", 1);
num_error++;
if (v && v->u.number != 0) {
add_message("%s", emsg_buf+1);
if (current_object)
add_message("program: %s, object: %s line %d\n",
current_prog ? current_prog->name : "",
current_object->name,
get_line_number_if_any());
} else {
add_message("Your sensitive mind notices a wrongness in the fabric of space.\n");
}
}
if (current_heart_beat) {
set_heart_beat(current_heart_beat, 0);
debug_message("Heart beat in %s turned off.\n",
current_heart_beat->name);
if (current_heart_beat->interactive) {
struct object *save_cmd = command_giver;
command_giver = current_heart_beat;
add_message("Game driver tells you: You have no heart beat !\n");
command_giver = save_cmd;
}
current_heart_beat = 0;
}
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(path)
char *path;
{
char *p;
if (path == NULL || strchr(path, ' '))
return 0;
if (path[0] == '/')
return 0;
#ifdef MSDOS
if (!valid_msdos(path)) return(0);
#endif
for(p = strchr(path, '.'); p; p = strchr(p+1, '.')) {
if (p[1] == '.')
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.
*/
void smart_log(error_file, line, what)
char *error_file, *what;
int line;
{
char buff[500];
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 file name]...";
sprintf(buff, "%s line %d:%s\n", error_file, line, what);
push_constant_string(error_file);
push_constant_string(buff);
apply_master_ob("log_error", 2);
}
/*
* Check that a file name is valid for read or write.
* Also change the name as if the current directory was at the players
* own directory.
* This is done by functions in the player object.
*
* The master object (master.c) always have permissions to access
* any file in the mudlib hierarchy, but only inside the mudlib.
*
* WARNING: The string returned will (mostly) be deallocated at next
* call of apply().
*/
#ifdef COMPAT_MODE
char debug_check_file[50];
char *check_file_name(file, writeflg)
char *file;
int writeflg;
{
struct svalue *ret;
struct object *stuff = command_giver;
char *str;
if (current_object == master_ob)
{
/* Master object can read/write anywhere */
if (file[0] == '/')
str = file+1;
else
str = file;
}
else
{
if (!stuff)
stuff = find_object("room/church");
if (!stuff)
{
char *temp;
struct object *ob;
ob=(command_giver?command_giver:current_object);
if (!ob) return 0;
if (!legal_path(file)) return 0;
if (ob->user)
{
int tl;
tl = strlen(ob->user->name) + 10;
temp=(char *)xalloc(tl);
strcpy(temp,"players/");
strcat(temp,ob->user->name);
strcat(temp,"/");
if (strncmp(file, temp, tl-1)==0 ||
strncmp(file, "open/", 5)==0 ||
(!strchr(file, '/') && !writeflg))
{
free(temp);
return file;
}
else
{
free(temp);
return 0;
}
}
else if (strncmp(ob->name, "open/", 5) == 0 &&
strncmp(file, "open/", 5) == 0)
return file;
else if (strncmp(ob->name, "obj/", 4) == 0||
strncmp(ob->name, "room/",5) == 0)
return file;
else return 0;
}
/*
* We don't have to free the string in ret. This is done
* by the garbage collection.
*/
else
{
push_constant_string(file);
if (writeflg)
ret = apply("valid_write", stuff, 1);
else
ret = apply("valid_read", stuff, 1);
if (stuff->flags&O_DESTRUCTED)
return 0;
if (!ret || ret->type != T_STRING)
{
add_message("Bad file name %s.\n", file?file:"<null>");
return 0;
}
str = ret->u.string;
}
}
strncpy(debug_check_file, str, sizeof debug_check_file);
debug_check_file[sizeof debug_check_file- 1] = 0;
if (!legal_path(str))
{
add_message("Illegal path: %s\n", str?str:"<null>");
return 0;
}
if (str[0] == '\0')
return ".";
return str;
}
#else
/*
* 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(path, eff_user, call_fun, writeflg)
char *path;
struct wiz_list *eff_user;
char *call_fun;
int writeflg;
{
struct svalue *v;
if (eff_user == 0)
return 0;
push_string(make_shared_string(path), STRING_SHARED);/**MALLOC**/
push_string(eff_user->name, STRING_CONSTANT);
push_string(call_fun, STRING_CONSTANT);
if (writeflg)
v = apply_master_ob("valid_write", 3);
else
v = apply_master_ob("valid_read", 3);
if (v && v->type == T_NUMBER && v->u.number == 0)
return 0;
if (path[0] == '/')
path++;
if (legal_path(path))
return path;
else
return 0;
}
#endif
/*
* This one is called from HUP.
*/
int game_is_being_shut_down;
#ifndef _AIX
void startshutdowngame() {
(void)signal(SIGHUP, startshutdowngame);
game_is_being_shut_down = 1;
}
#else
void startshutdowngame(int arg) {
game_is_being_shut_down = 1;
}
#endif
/*
* 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() {
shout_string("Game driver shouts: LPmud shutting down immediately.\n");
save_wiz_file();
ipc_remove();
remove_all_players();
unlink_swap_file();
#ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
remove_all_objects();
free_all_sent();
free_all_call_outs();
remove_wiz_list();
dump_malloc_data();
#endif
#ifdef OPCPROF
opcdump();
#endif
exit(0);
}
/*
* Transfer an object from an object to an object.
* Call add_weight(), drop(), get(), prevent_insert(), add_weight(),
* and can_put_and_get() where needed.
* Return 0 on success, and special code on failure:
*
* 1: To heavy for destination.
* 2: Can't be dropped.
* 3: Can't take it out of it's container.
* 4: The object can't be inserted into bags etc.
* 5: The destination doesn't allow insertions of objects.
* 6: The object can't be picked up.
*/
#ifdef F_TRANSFER
int transfer_object(ob, to)
struct object *ob, *to;
{
struct svalue *v_weight, *ret;
int weight;
struct object *from = ob->super;
/*
* Get the weight of the object
*/
#ifndef COMPAT_MODE
error("transfer() not allowed.\n");
#endif
if (ob->shadowing)
error("transfer: cannot move object that is shadowing.\n");
v_weight = apply("query_weight", ob, 0);
if (v_weight && v_weight->type != T_NUMBER)
error("Bad type the weight of object in transfer()\n");
weight = 0;
if (v_weight)
weight = v_weight->u.number;
if (ob->flags & O_DESTRUCTED)
return 3;
/*
* If the original place of the object is a living object,
* then we must call drop() to check that the object can be dropped.
*/
if (from && (from->flags & O_ENABLE_COMMANDS)) {
ret = apply("drop", ob, 0);
if (ret && (ret->type != T_NUMBER || ret->u.number != 0))
return 2;
/* This shold not happen, but we can not trust LPC hackers. :-) */
if (ob->flags & O_DESTRUCTED)
return 2;
}
/*
* If 'from' is not a room and not a player, check that we may
* remove things out of it.
*/
if (from && from->super && !(from->flags & O_ENABLE_COMMANDS)) {
ret = apply("can_put_and_get", from, 0);
if (!ret || (ret->type != T_NUMBER && ret->u.number != 1) ||
(from->flags & O_DESTRUCTED))
return 3;
}
/*
* If the destination is not a room, and not a player,
* Then we must test 'prevent_insert', and 'can_put_and_get'.
*/
if (to->super && !(to->flags & O_ENABLE_COMMANDS)) {
ret = apply("prevent_insert", ob, 0);
if (ret && (ret->type != T_NUMBER || ret->u.number != 0))
return 4;
ret = apply("can_put_and_get", to, 0);
if (!ret || (ret->type != T_NUMBER && ret->type != 0) ||
(to->flags & O_DESTRUCTED) || (ob->flags & O_DESTRUCTED))
return 5;
}
/*
* If the destination is a player, check that he can pick it up.
*/
if (to->flags & O_ENABLE_COMMANDS) {
ret = apply("get", ob, 0);
if (!ret || (ret->type == T_NUMBER && ret->u.number == 0) ||
(ob->flags & O_DESTRUCTED))
return 6;
}
/*
* If it is not a room, correct the total weight in the destination.
*/
if (to->super && weight) {
/*
* Check if the destination can carry that much.
*/
push_number(weight);
ret = 0;
ret = apply("add_weight", to, 1);
if (ret && ret->type == T_NUMBER && ret->u.number == 0)
return 1;
if (to->flags & O_DESTRUCTED)
return 1;
}
/*
* If it is not a room, correct the weight in the 'from' object.
*/
if (from && from->super && weight) {
push_number(-weight);
(void)apply("add_weight", from, 1);
if (from->flags & O_DESTRUCTED)
return 1;
}
move_object(ob, to);
return 0;
}
#endif /* F_TRANSFER */
/*
* Move or destruct one object.
*/
void move_or_destruct(what, to)
struct object *what, *to;
{
int res;
struct svalue v;
#ifdef COMPAT_MODE
res = transfer_object(what, to);
if (res == 0 || (what->flags & O_DESTRUCTED))
return;
if (res == 1 || res == 4 || res == 5) {
if (to->super) {
move_or_destruct(what, to->super);
return;
}
}
#else
{
struct svalue *svp;
push_object(to, "move_or_destruct");
push_number(1);
svp = apply("move", what, 2);
if (svp && svp->type == T_NUMBER && svp->u.number == 0)
return;
if (what->flags & O_DESTRUCTED)
return;
}
#endif
v.type = T_OBJECT;
v.u.ob = what;
destruct_object(&v, 0);
}
/*
* Call this one when there is only little memory left. It will start
* Armageddon.
*/
void slow_shut_down(minutes)
int minutes;
{
struct object *ob;
/*
* Swap out objects, and free some memory.
*/
ob = find_object("obj/shut");
if (!ob) {
struct object *save_current = current_object,
*save_command = command_giver;
command_giver = 0;
current_object = 0;
shout_string("Game driver shouts: Out of memory.\n");
command_giver = save_command;
current_object = save_current;
#ifndef _AIX
startshutdowngame();
#else
startshutdowngame(1);
#endif
return;
}
shout_string("Game driver shouts: The memory is getting low !\n");
push_number(minutes);
(void)apply("shut", ob, 1);
}
int match_string(match, str)
char *match, *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 (path)
char *path;
{
struct stat stats;
return stat (path, &stats) == 0 && S_ISDIR (stats.st_mode);
}
void
strip_trailing_slashes (path)
char *path;
{
int last;
last = strlen (path) - 1;
while (last > 0 && path[last] == '/')
path[last--] = '\0';
}
struct stat to_stats, from_stats;
int
copy (from, to)
char *from, *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;
}
#if 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 (from, to)
char *from;
char *to;
{
if (lstat (from, &from_stats) != 0)
return 0;
if (lstat (to, &to_stats) == 0)
{
#ifndef MSDOS
if (from_stats.st_dev == to_stats.st_dev
&& from_stats.st_ino == to_stats.st_ino)
#else
if (same_file(from, to))
#endif
return 0;
if (S_ISDIR (to_stats.st_mode))
return 0;
}
else if (errno != ENOENT)
return 0;
#ifdef SYSV
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 1;
}
if (errno != EXDEV)
return 0;
/* rename failed on cross-filesystem link. Copy the file instead. */
if (copy (from, to))
return 0;
if (unlink (from))
return 0;
return 1;
}
/*
* 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.
*/
#ifdef F_RENAME
int
do_rename(fr, t)
char *fr, *t;
{
char *from, *to;
from = check_valid_path(fr, current_object->eff_user, "do_rename", 1);
if(!from)
return 1;
to = check_valid_path(t, current_object->eff_user, "do_rename", 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);
}
#endif
#ifdef LEX_lexical
void pre_compile(str)
char *str;
{
char *c_name, *i_name, buff[1000];
FILE *f;
if(!legal_path(str))
error("Illegal attempt to access %s\n",str);
i_name = xalloc(strlen(str)+3);
(void)strcpy(i_name, str);
(void)strcat(i_name, ".i");
c_name = (char *)xalloc(strlen(str)+3);
(void)strcpy(c_name, str);
(void)strcat(c_name, ".c");
sprintf(buff, "%s %s %s", PRE_COMPILE, c_name, i_name);
f = (FILE *)vpopen(buff, "r");
if (f == 0) {
error("Unable to invoke precompiler!\n");
alarm(0);
}
while(1) {
if (fgets(buff, sizeof buff, f) == 0)
break;
add_message("%s", buff);
}
vpclose(f);
free((char *)c_name);
free((char *)i_name);
}
#endif
#endif /* F_RENAME */