#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#ifdef AMIGA
#include "hosts/amiga/nsignal.h"
#else
#include <signal.h>
#include <sys/times.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <math.h>
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "wiz_list.h"
#include "exec.h"
#include "comm.h"
struct error_recovery_info toplevel_error_recovery_info = {
(struct error_recovery_info*)0,
ERROR_RECOVERY_NONE
};
struct error_recovery_info *error_recovery_pointer =
&toplevel_error_recovery_info;
/*
* The 'current_time' is updated at every heart beat.
*/
int current_time;
static void cycle_hb_list PROT((void));
extern struct object *command_giver, *current_interactive, *obj_list_destruct;
extern int num_player, d_flag;
extern struct object *previous_ob, *master_ob;
extern int trace_level;
extern int tracedepth;
struct object *current_heart_beat;
void call_heart_beat(), catch_alarm();
void load_first_objects(), prepare_ipc(),
shutdowngame(), ed_cmd PROT((char *)),
print_prompt(), call_out(),
destruct2 PROT((struct object *));
extern int get_message PROT((char *, int)), player_parser PROT((char *)),
call_function_interactive PROT((struct interactive *, char *)),
resort_free_list(), swap PROT((struct object *));
extern void flush_all_player_mess();
extern int t_flag;
int time_to_call_heart_beat;
int comm_time_to_call_heart_beat = 0; /* this is set by interrupt, */
/* comm sets time_to_call_heart_beat sometime after */
mp_int total_alarms = 0;
/*
* There are global variables that must be zeroed before any execution.
* In case of errors, there will be a longjmp(), and the variables will
* have to be cleared explicitely. They are normally maintained by the
* code that use them.
*
* This routine must only be called from top level, not from inside
* stack machine execution (as stack will be cleared).
*/
void clear_state() {
extern struct object *previous_ob;
extern char *current_file;
current_file = 0;
current_object = 0;
command_giver = 0;
current_interactive = 0;
previous_ob = 0;
current_prog = 0;
reset_machine(0); /* Pop down the stack. */
}
extern int check_state();
void logon(ob)
struct object *ob;
{
struct svalue *ret;
struct object *save = current_object;
/*
* current_object must be set here, so that the static "logon" in
* player.c can be called.
*/
current_object = ob;
ret = apply("logon", ob, 0);
if (ret == 0) {
add_message("prog %s:\n", ob->name);
error("Could not find logon on the player %s\n", ob->name);
}
current_object = save;
}
/*
* Take a player command and parse it.
* The command can also come from a NPC.
* Beware that 'str' can be modified and extended !
*/
int parse_command(str, ob)
char *str;
struct object *ob;
{
struct object *save = command_giver;
int res;
command_giver = ob;
res = player_parser(str);
command_giver = check_object(save);
return res;
}
#ifdef AMIGA
/* Clean up the alarm timer, this is set as atexit() function */
void exit_alarm_timer() { alarm(0); }
#endif
/*
* This is the backend. We will stay here for ever (almost).
*/
int32 initial_eval_cost = -MAX_COST;
int32 eval_cost, assigned_eval_cost;
int extra_jobs_to_do = 0;
int garbage_collect_to_do = 0;
void backend()
{
char buff[2000];
extern int game_is_being_shut_down;
extern int slow_shut_down_to_do;
extern int master_will_be_updated;
extern mp_int num_dirty_mappings;
extern int malloc_privilege;
(void)printf("Setting up ipc.\n");
fflush(stdout);
prepare_ipc();
(void)signal(SIGHUP, (RETSIGTYPE(*)PROT((int)))startshutdowngame);
(void)signal(SIGUSR1, (RETSIGTYPE(*)PROT((int)))startmasterupdate);
if (!t_flag) {
catch_alarm();
call_heart_beat();
}
#ifdef AMIGA
atexit(exit_alarm_timer);
#endif
toplevel_error_recovery_info.type = ERROR_RECOVERY_BACKEND;
setjmp(toplevel_error_recovery_info.context);
/*
* We come here after errors, and have to clear some global variables.
*/
clear_state();
flush_all_player_mess();
while(1) {
/*
* The call of clear_state() should not really have to be done
* once every loop. However, there seem to be holes where the
* state is not consistent. If these holes are removed,
* then the call of clear_state() can be moved to just before the
* while() - statment. *sigh* /Lars
*/
/* amylaar: I think inconsistencys should be found, rather than
* the effects patched
*/
#ifdef DEBUF
if ( check_state() ) {
debug_message("Inconsistency in main loop\n");
dump_trace(1);
shout_string("Gamedriver shouts: I feel inconsistent!\n");
clear_state();
}
#endif
CLEAR_EVAL_COST;
#ifndef __GNUC__
alloca(0); /* free alloca'd values from deeper levels of nesting */
#endif
remove_destructed_objects(); /* marion - before ref checks! */
#ifdef DEBUG
if (d_flag > 1)
check_a_lot_ref_counts(0);
#endif
if (extra_jobs_to_do) {
current_interactive = 0;
if (game_is_being_shut_down) {
command_giver = 0; /* This statement was removed from the end
* of the main loop. We have to compensate
* for this.
*/
shutdowngame();
}
if (master_will_be_updated) {
extern struct object dummy_current_object_for_loads;
emergency_destruct(master_ob);
master_will_be_updated = 0;
/* maybe you'll want the new master to reload the player files
* as well :-)
*/
current_object = &dummy_current_object_for_loads;
apply_master_ob("external_master_reload", 0);
current_object = 0;
}
if (garbage_collect_to_do) {
extern void garbage_collection();
command_giver = 0;
garbage_collection();
garbage_collect_to_do = 0;
if (slow_shut_down_to_do) {
int tmp = slow_shut_down_to_do;
slow_shut_down_to_do = 0;
malloc_privilege = MALLOC_MASTER;
slow_shut_down(tmp);
}
malloc_privilege = MALLOC_USER;
}
extra_jobs_to_do = 0;
if (num_dirty_mappings) {
extern void compact_mappings PROT((mp_int));
compact_mappings(num_dirty_mappings+80 >> 5);
malloc_privilege = MALLOC_USER;
}
}
if (get_message(buff, sizeof buff)) {
void update_load_av PROT((void));
update_load_av();
/*
* Now we have a string from the player. This string can go to
* one of several places. If it is prepended with a '!', then
* it is an escape from the 'ed' editor, so we send it
* as a command to the parser.
* If any object function is waiting for an input string, then
* send it there.
* Otherwise, send the string to the parser.
* The player_parser() will find that current_object is 0, and
* then set current_object to point to the object that defines
* the command. This will enable such functions to be static.
*/
current_object = 0;
current_interactive = command_giver;
#ifdef DEBUG
if (!command_giver->interactive)
fatal("Non interactive player in main loop !\n");
#endif
tracedepth = 0;
if (buff[0] == '!' && command_giver->super) {
if (command_giver->interactive->noecho) {
add_message("%s\n", buff);
}
parse_command(buff+1, command_giver);
} else if (command_giver->interactive->ed_buffer)
ed_cmd(buff);
else if (call_function_interactive(command_giver->interactive,buff))
; /* Do nothing ! */
else
parse_command(buff, command_giver);
/*
* Print a prompt if player is still here.
*/
if (command_giver && command_giver->interactive)
print_prompt();
}
if (time_to_call_heart_beat)
call_heart_beat();
} /* end of main loop */
}
/*
* Despite the name, this routine takes care of several things.
* It will loop through all objects once every 15 minutes.
*
* If an object is found in a state of not having done reset, and the
* delay to next reset has passed, then reset() will be done.
*
* If the object has a existed more than the time limit given for swapping,
* then 'clean_up' will first be called in the object, after which it will
* be swapped out if it still exists.
*
* There are some problems if the object self-destructs in clean_up, so
* special care has to be taken of how the linked list is used.
*/
static void look_for_objects_to_swap() {
extern long time_to_swap; /* marion - for invocation parameter */
static int next_time;
static struct object *next_ob; /* don't change back with longjmp() */
struct object *ob;
struct error_recovery_info error_recovery_info;
union {
double dummy; /* force alignment */
char c[sizeof(struct program)+SCAN_SWAP_BUFSIZE];
} swapbuf;
if (current_time < next_time)
return; /* Not time to look yet */
#if TIME_TO_SWAP >= 15 * 60 || !TIME_TO_SWAP
next_time = current_time + 15 * 60; /* Next time is in 15 minutes */
#else
next_time = current_time + TIME_TO_SWAP;
#endif
/*
* Objects object can be destructed, which means that
* next object to investigate is saved in next_ob. If very unlucky,
* that object can be destructed too. In that case, the loop is simply
* restarted.
*/
set_swapbuf(swapbuf.c);
next_ob = obj_list;
error_recovery_info.last = error_recovery_pointer;
error_recovery_info.type = ERROR_RECOVERY_BACKEND;
error_recovery_pointer = &error_recovery_info;
if (setjmp(error_recovery_info.context)) { /* amylaar */
clear_state();
debug_message("Error in look_for_objects_to_swap.\n");
}
for (; ob = next_ob; ) {
int time_since_ref;
if (ob->flags & O_DESTRUCTED) {
ob = obj_list; /* restart */
}
next_ob = ob->next_all;
/*
* Check reference time before reset() is called.
*/
time_since_ref = current_time - ob->time_of_ref;
/*
* Should this object have reset(1) called ?
*/
if (ob->next_reset < current_time && !(ob->flags & O_RESET_STATE)) {
if (d_flag)
fprintf(stderr, "RESET %s\n", ob->name);
CLEAR_EVAL_COST;
command_giver = 0;
trace_level = 0;
reset_object(ob, 1);
if (ob->flags & O_DESTRUCTED)
continue;
}
#if TIME_TO_CLEAN_UP > 0
/*
* Has enough time passed, to give the object a chance
* to self-destruct ? Save the O_RESET_STATE, which will be cleared.
*
* Only call clean_up in objects that has defined such a function.
*
* Only if the clean_up returns a non-zero value, will it be called
* again.
*/
else if (time_since_ref > TIME_TO_CLEAN_UP &&
(ob->flags & O_WILL_CLEAN_UP))
{
int was_swapped = ob->flags & O_SWAPPED ;
int save_reset_state = ob->flags & O_RESET_STATE;
struct svalue *svp;
if (d_flag)
fprintf(stderr, "clean up %s\n", ob->name);
/*
* Supply a flag to the object that says if this program
* is inherited by other objects. Cloned objects might as well
* believe they are not inherited. Swapped objects will not
* have a ref count > 1 (and will have an invalid ob->prog
* pointer).
*/
push_number(ob->flags & O_CLONE ? 0 :
( ob->flags & O_SWAPPED ? 1 : ob->prog->ref) );
CLEAR_EVAL_COST;
command_giver = 0;
trace_level = 0;
svp = apply("clean_up", ob, 1);
if (ob->flags & O_DESTRUCTED)
continue;
if ((!svp || (svp->type == T_NUMBER && svp->u.number == 0)) &&
was_swapped )
ob->flags &= ~O_WILL_CLEAN_UP;
ob->flags |= save_reset_state;
}
#endif /* TIME_TO_CLEAN_UP > 0 */
#if TIME_TO_SWAP > 0
/*
* At last, there is a possibility that the object can be swapped
* out.
*/
if (ob->flags & O_SWAPPED || time_since_ref < time_to_swap)
continue;
if (ob->flags & O_HEART_BEAT)
continue;
if (d_flag)
fprintf(stderr, "swap %s\n", ob->name);
swap(ob); /* See if it is possible to swap out to disk */
#endif
}
set_swapbuf((char *)0);
error_recovery_pointer = error_recovery_info.last;
}
/*
* Call all heart_beat() functions in all objects. Also call the next reset,
* and the call out.
* We do heart beats by moving each object done to the end of the heart beat
* list before we call its function, and always using the item at the head
* of the list as our function to call. We keep calling heart beats until
* a timeout or we have done num_heart_objs calls. It is done this way so
* that objects can delete heart beating objects from the list from within
* their heart beat without truncating the current round of heart beats.
*
* Set command_giver to current_object if it is a living object. If the object
* is shadowed, check the shadowed object if living. There is no need to save
* the value of the command_giver, as the caller resets it to 0 anyway.
*/
static struct object * hb_list = 0; /* head */
static struct object * hb_tail = 0; /* for sane wrap around */
static int num_hb_objs = 0; /* so we know when to stop! */
static int num_hb_calls = 0; /* stats */
static float perc_hb_probes = 100.0; /* decaying avge of how many complete */
void call_heart_beat() {
struct object *ob, *hide_current = current_object;
int num_done = 0;
time_to_call_heart_beat = 0; /* interrupt loop if we take too long */
comm_time_to_call_heart_beat = 0;
#ifndef MSDOS
/* If the host is swapping madly, it can be too late here to reinstate
* the SIGALRM handler here.
*/
alarm(2);
#else
start_timer(2);
#endif
current_time = get_current_time();
current_interactive = 0;
if ((num_player > 0) && hb_list) {
num_hb_calls++;
while (hb_list &&
#ifndef MSDOS
!comm_time_to_call_heart_beat
#else
!timer_expired()
#endif
&& (num_done < num_hb_objs)) {
num_done++;
cycle_hb_list();
ob = hb_tail; /* now at end */
#ifdef DEBUG
if (!(ob->flags & O_HEART_BEAT))
fatal("Heart beat not set in object on heart beat list!");
if (ob->flags & O_SWAPPED)
fatal("Heart beat in swapped object.\n");
#endif
/* move ob to end of list, do ob */
if (ob->prog->heart_beat == -1)
continue;
current_prog = ob->prog;
current_object = ob;
current_heart_beat = ob;
command_giver = ob;
while(command_giver->shadowing)
command_giver = command_giver->shadowing;
if (!(command_giver->flags & O_ENABLE_COMMANDS)) {
command_giver = 0;
trace_level = 0;
} else
trace_level = command_giver->interactive ?
command_giver->interactive->trace_level : 0;
if (ob->user)
ob->user->heart_beats++;
CLEAR_EVAL_COST;
call_function(ob->prog, ob->prog->heart_beat);
}
if (num_hb_objs)
perc_hb_probes = 100 * (float) num_done / num_hb_objs;
else
perc_hb_probes = 100.0;
}
current_heart_beat = 0;
current_object = 0;
look_for_objects_to_swap();
call_out(); /* some things depend on this, even without players! */
command_giver = 0;
trace_level = 0;
current_object = hide_current;
flush_all_player_mess();
wiz_decay();
}
/*
* Take the first object off the heart beat list, place it at the end
*/
static void cycle_hb_list()
{
struct object * ob;
if (!hb_list)
fatal("Cycle heart beat list with empty list!");
if (hb_list == hb_tail)
return; /* 1 object on list */
ob = hb_list;
hb_list = hb_list -> next_heart_beat;
hb_tail -> next_heart_beat = ob;
hb_tail = ob;
ob->next_heart_beat = 0;
}
/*
* add or remove an object from the heart beat list; does the major check...
* If an object removes something from the list from within a heart beat,
* various pointers in call_heart_beat could be stuffed, so we must
* check current_heart_beat and adjust pointers.
*/
int set_heart_beat(ob, to)
struct object * ob;
int to;
{
struct object * o = hb_list;
struct object * oprev = 0;
if (ob->flags & O_DESTRUCTED)
return 0;
if (to)
to = 1;
while (o && o != ob) {
if (!(o->flags & O_HEART_BEAT))
fatal("Found disabled object in the active heart beat list!\n");
oprev = o;
o = o->next_heart_beat;
}
if (!o && (ob->flags & O_HEART_BEAT))
fatal("Couldn't find enabled object in heart beat list!");
if (to == ((ob->flags & O_HEART_BEAT) != 0))
return(0);
if (to) {
ob->flags |= O_HEART_BEAT;
if (ob->next_heart_beat)
fatal("Dangling pointer to next_heart_beat in object!");
ob->next_heart_beat = hb_list;
hb_list = ob;
if (!hb_tail) hb_tail = ob;
num_hb_objs++;
cycle_hb_list(); /* Added by Linus. 911104 */
}
else { /* remove all refs */
ob->flags &= ~O_HEART_BEAT;
if (hb_list == ob)
hb_list = ob->next_heart_beat;
if (hb_tail == ob)
hb_tail = oprev;
if (oprev)
oprev->next_heart_beat = ob->next_heart_beat;
ob->next_heart_beat = 0;
num_hb_objs--;
}
return(1);
}
/*
* sigh. Another status function.
*/
int heart_beat_status(verbose)
int verbose;
{
char buf[20];
if (verbose) {
add_message("\nHeart beat information:\n");
add_message("-----------------------\n");
add_message("Number of objects with heart beat: %d, starts: %d\n",
num_hb_objs, num_hb_calls);
sprintf(buf, "%.2f", perc_hb_probes);
add_message("Percentage of HB calls completed last time: %s\n", buf);
}
return 0;
}
#ifdef COMPAT_MODE
/*
* There is a file with a list of objects to be initialized at
* start up.
*/
void load_first_objects() { /* Old version used when o_flag true /JnA */
FILE *f;
char buff[1000];
char *p;
extern int e_flag;
#ifdef AMIGA
#define tms_total(tms) (tms)
#define times(tmsp) ( *(tmsp) = clock() )
clock_t tms1, tms2;
#define TIMES_FREQ CLOCKS_PER_SEC
#else
#ifdef MSDOS
#define tms_total(tms) (tms)
#define times(tmsp) milliseconds(tmsp)
long tms1 = 0L, tms2;
#define TIMES_FREQ 1000
#else
#define TIMES_FREQ CLK_TCK
#ifndef CLK_TCK
#define CLK_TCK CLOCKS_PER_SEC
#ifndef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC 60
#endif
#endif
#define tms_total(tms) ((tms).tms_utime + (tms).tms_stime)
struct tms tms1, tms2;
#endif
#endif
if (e_flag)
return;
(void)printf("Loading init file %s\n", INIT_FILE);
f = fopen(INIT_FILE, "r");
if (f == 0)
return;
toplevel_error_recovery_info.type = ERROR_RECOVERY_BACKEND;
if (setjmp(toplevel_error_recovery_info.context)) {
clear_state();
add_message("Anomaly in the fabric of world space.\n");
}
times(&tms1);
current_object = master_ob;
while(1) {
if (fgets(buff, sizeof buff, f) == NULL)
break;
if (buff[0] == '#')
continue;
p = strchr(buff, '\n');
if (p != 0)
*p = 0;
if (buff[0] == '\0')
continue;
(void)printf("Preloading: %s", buff);
fflush(stdout);
CLEAR_EVAL_COST;
(void)find_object(buff);
#ifdef MALLOC_malloc
resort_free_list();
#endif
times(&tms2);
(void)printf(" %.2f\n", ( tms_total(tms2) - tms_total(tms1) ) /
(double)TIMES_FREQ);
tms1 = tms2;
fflush(stdout);
}
toplevel_error_recovery_info.type = ERROR_RECOVERY_NONE;
fclose(f);
}
#else
/*
* New version used when not in -o mode. The epilog() in master.c is
* supposed to return an array of files (castles in 2.4.5) to load. The array
* returned by apply() will be freed at next call of apply(), which means that
* the ref count has to be incremented to protect against deallocation.
*
* The master object is asked to do the actual loading.
*/
void preload_objects(eflag)
int eflag;
{
struct vector *prefiles;
struct svalue *ret;
static int ix0;
int ix;
push_number(eflag);
ret = apply_master_ob("epilog", 1);
if ((ret == 0) || (ret->type != T_POINTER))
return;
else
prefiles = ret->u.vec;
if ((prefiles == 0) || (prefiles->size < 1))
return;
prefiles->ref++;
ix0 = -1;
toplevel_error_recovery_info.type = ERROR_RECOVERY_BACKEND;
if (setjmp(toplevel_error_recovery_info.context)) {
clear_state();
add_message("Anomaly in the fabric of world space.\n");
}
while ((ix = ++ix0) < prefiles->size) {
if (prefiles->item[ix].type != T_STRING)
continue;
CLEAR_EVAL_COST;
push_string_malloced(prefiles->item[ix].u.string);
(void)apply_master_ob("preload", 1);
#ifdef MALLOC_malloc
resort_free_list();
#endif
}
free_vector(prefiles);
toplevel_error_recovery_info.type = ERROR_RECOVERY_NONE;
}
#endif /* COMPAT_MODE */
/*
* catch alarm, set flag for comms code and heart_beat to catch.
* comms code sets time_to_call_heart_beat for the backend when
* it has completed the current round of player commands.
*/
void catch_alarm() {
#ifndef MSDOS
(void)signal(SIGALRM, (RETSIGTYPE(*)PROT((int)))catch_alarm);
#endif
#ifdef OS2
(void)signal(SIGALRM, SIG_ACK);
#endif
comm_time_to_call_heart_beat = 1;
total_alarms++;
}
/*
* All destructed objects are moved int a sperate linked list,
* and deallocated after program execution.
*/
void remove_destructed_objects()
{
struct object *ob, *next;
if (obj_list_replace) {
extern void replace_programs();
replace_programs();
}
for (ob=obj_list_destruct; ob; ob = next) {
next = ob->next_all;
destruct2(ob);
}
obj_list_destruct = 0;
}
/*
* Append string to file. Return 0 for failure, otherwise 1.
*/
int write_file(file, str)
char *file;
char *str;
{
FILE *f;
file = check_valid_path(file, current_object, "write_file", 1);
if (!file)
return 0;
f = fopen(file, "a");
if (f == 0) {
if (errno == EMFILE
#ifdef ENFILE
|| errno == ENFILE
#endif
) {
extern void lex_close();
extern void push_apply_value(), pop_apply_value();
/* lex_close() calls lexerror(). lexerror() calls yyerror().
* yyerror calls smart_log(). smart_log calls apply_master_ob().
* This is why the value of file needs to be preserved.
*/
push_apply_value();
lex_close();
pop_apply_value();
f = fopen(file, "a");
}
if (f == 0) {
perror("write_file");
error("Wrong permissions for opening file %s for append.\n", file);
}
}
fwrite(str, strlen(str), 1, f);
fclose(f);
return 1;
}
#if ( defined( atarist ) && !defined ( minix ) ) || defined( MSDOS )
#define MSDOS_FS
#endif
char *read_file(file,start,len)
char *file;
int start,len;
{
struct stat st;
FILE *f;
char *str,*p,*p2,*end,c;
int size;
if (len < 0 && len != -1) return 0;
file = check_valid_path(file, current_object, "read_file", 0);
if (!file)
return 0;
f = fopen(file, "r");
if (f == 0)
return 0;
if (fstat(fileno(f), &st) == -1)
fatal("Could not stat an open file.\n");
size = st.st_size;
if (size > READ_FILE_MAX_SIZE) {
if ( start || len ) size = READ_FILE_MAX_SIZE;
else {
fclose(f);
return 0;
}
}
if (!start) start = 1;
if (!len) len = READ_FILE_MAX_SIZE;
str = xalloc(size + 2);
if (!str) {
fclose(f);
return 0;
}
*str++ = ' '; /* this way, we can always read the 'previous' char... */
str[size] = '\0';
do {
if (size > st.st_size)
size = st.st_size;
if (!size && start > 1 || fread(str, size, 1, f) != 1) {
fclose(f);
xfree(str-1);
return 0;
}
st.st_size -= size;
end = str+size;
for (p=str; ( p2=memchr(p,'\n',end-p) ) && --start; ) p=p2+1;
} while ( start > 1 );
for (p2=str; p != end; ) {
c = *p++;
if ( !isprint(c) && !isspace(c) ) c=' ';
if ( c == '\n' ) {
#ifdef MSDOS_FS
if ( p2[-1] == '\r' ) p2--;
#endif
if (!--len) {
*p2++=c;
break;
}
}
*p2++=c;
}
if ( len && st.st_size ) {
size -= ( p2-str) ;
if (size > st.st_size)
size = st.st_size;
if (fread(p2, size, 1, f) != 1) {
fclose(f);
xfree(str-1);
return 0;
}
st.st_size -= size;
end = p2+size;
for (p=p2; p != end; ) {
c = *p++;
if ( !isprint(c) && !isspace(c) ) c=' ';
if ( c == '\n' ) {
#ifdef MSDOS_FS
if ( p2[-1] == '\r' ) p2--;
#endif
if (!--len) {
*p2++ = c;
break;
}
}
*p2++ = c;
}
if ( st.st_size && len > 0) {
/* tried to read more than READ_MAX_FILE_SIZE */
fclose(f);
xfree(str-1);
return 0;
}
}
*p2='\0';
fclose(f);
#if 0
if ( st.st_size = (p2-str) )
return str;
#endif
p2=string_copy(str);
xfree(str-1);
return p2;
}
char *read_bytes(file,start,len)
char *file;
int start,len;
{
struct stat st;
char *str,*p;
int size, f;
if (len < 0)
return 0;
if(len > MAX_BYTE_TRANSFER)
return 0;
file = check_valid_path(file, current_object, "read_bytes", 0);
if (!file)
return 0;
f = ixopen(file, O_RDONLY);
if (f < 0)
return 0;
if (fstat(f, &st) == -1)
fatal("Could not stat an open file.\n");
size = st.st_size;
if(start < 0)
start = size + start;
if (start >= size) {
close(f);
return 0;
}
if ((start+len) > size)
len = (size - start);
if ((size = lseek(f,start, 0)) < 0) {
close(f);
return 0;
}
str = xalloc(len + 1);
if (!str) {
close(f);
return 0;
}
size = read(f, str, len);
close(f);
if (size <= 0) {
xfree(str);
return 0;
}
/* We want to allow all characters to pass untouched!
for (il=0;il<size;il++)
if (!isprint(str[il]) && !isspace(str[il]))
str[il] = ' ';
str[il] = 0;
*/
/*
* The string has to end to '\0'!!!
*/
str[size] = '\0';
p = string_copy(str);
xfree(str);
return p;
}
int write_bytes(file,start,str)
char *file, *str;
int start;
{
struct stat st;
mp_int size, len;
int f;
file = check_valid_path(file, current_object, "write_bytes", 1);
if (!file)
return 0;
len = strlen(str);
if(len > MAX_BYTE_TRANSFER)
return 0;
f = ixopen(file, O_WRONLY);
if (f < 0)
return 0;
if (fstat(f, &st) == -1)
fatal("Could not stat an open file.\n");
size = st.st_size;
if(start < 0)
start = size + start;
if (start > size) {
close(f);
return 0;
}
if ((size = lseek(f,start, 0)) < 0) {
close(f);
return 0;
}
size = write(f, str, len);
close(f);
if (size <= 0) {
return 0;
}
return 1;
}
int file_size(file)
char *file;
{
struct stat st;
file = check_valid_path(file, current_object, "file_size", 0);
if (!file)
return -1;
if (ixstat(file, &st) == -1)
return -1;
if (S_IFDIR & st.st_mode)
return -2;
return st.st_size;
}
static double load_av = 0.0;
void update_load_av() {
extern double consts[5];
extern int current_time;
static int last_time;
int n;
double c;
static int acc = 0;
acc++;
if (current_time == last_time)
return;
n = current_time - last_time;
if (n < sizeof consts / sizeof consts[0])
c = consts[n];
else
c = exp(- n / 900.0);
load_av = c * load_av + acc * (1 - c) / n;
last_time = current_time;
acc = 0;
}
static double compile_av = 0.0;
void update_compile_av(lines)
int lines;
{
extern double consts[5];
extern int current_time;
static int last_time;
int n;
double c;
static int acc = 0;
acc += lines;
if (current_time == last_time)
return;
n = current_time - last_time;
if (n < sizeof consts / sizeof consts[0])
c = consts[n];
else
c = exp(- n / 900.0);
compile_av = c * compile_av + acc * (1 - c) / n;
last_time = current_time;
acc = 0;
}
char *query_load_av() {
static char buff[100];
sprintf(buff, "%.2f cmds/s, %.2f comp lines/s", load_av, compile_av);
return buff;
}
/*
* Constructs an array of all objects that have a heart_beat.
*/
struct svalue *heart_beat_info(sp)
struct svalue *sp;
{
int i;
struct object *ob;
struct vector *vec;
struct svalue *v;
for (i = 0, ob = hb_list; ob; i++, ob = ob->next_heart_beat);
vec = allocate_array(i);
for (v = vec->item, ob = hb_list; ob; v++, ob = ob->next_heart_beat) {
v->type = T_OBJECT;
v->u.ob = ob;
add_ref(ob, "heart_beat_info");
}
sp++;
sp->type = T_POINTER;
sp->u.vec = vec;
return sp;
}