Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/secure/simul_efun/simul_efun.c b/secure/simul_efun/simul_efun.c
new file mode 100644
index 0000000..84e0b78
--- /dev/null
+++ b/secure/simul_efun/simul_efun.c
@@ -0,0 +1,2016 @@
+// MorgenGrauen MUDlib
+//
+// simul_efun.c -- simul efun's
+//
+// $Id: simul_efun.c 7408 2010-02-06 00:27:25Z Zesstra $
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone,no_shadow,no_inherit
+#pragma pedantic,range_check,warn_deprecated
+#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
+
+// Absolute Pfade erforderlich - zum Zeitpunkt, wo der Master geladen
+// wird, sind noch keine Include-Pfade da ...
+
+#define SNOOPLOGFILE "SNOOP"
+#define ASNOOPLOGFILE "ARCH/SNOOP"
+
+#include "/secure/config.h"
+#include "/secure/wizlevels.h"
+#include "/sys/snooping.h"
+#include "/sys/language.h"
+#include "/sys/thing/properties.h"
+#include "/sys/wizlist.h"
+#include "/sys/erq.h"
+#include "/sys/lpctypes.h"
+#include "/sys/daemon.h"
+#include "/sys/player/base.h"
+#include "/sys/thing/description.h"
+#include "/sys/container.h"
+#include "/sys/defines.h"
+#include "/sys/telnet.h"
+#include "/sys/objectinfo.h"
+#include "/sys/files.h"
+#include "/sys/strings.h"
+#include "/sys/time.h"
+#include "/sys/lpctypes.h"
+#include "/sys/notify_fail.h"
+#include "/sys/tls.h"
+#include "/sys/input_to.h"
+#include "/sys/object_info.h"
+
+/* function prototypes
+ */
+string dtime(int wann);
+varargs int log_file(string file, string txt, int size_to_break);
+int query_wiz_level(mixed player);
+nomask varargs int snoop(object me, object you);
+varargs string country(mixed ip, string num);
+int query_wiz_grp(mixed wiz);
+public varargs object deep_present(mixed what, object ob);
+nomask int secure_level();
+nomask string secure_euid();
+public nomask int process_call();
+nomask mixed __create_player_dummy(string name);
+varargs string replace_personal(string str, mixed *obs, int caps);
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+varargs string extract(string str, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(slice_array)
+varargs mixed slice_array(mixed array, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(member_array)
+int member_array(mixed item, mixed arraystring);
+#endif
+
+// Include the different sub 'modules' of the simul_efun
+#include __DIR__"debug_info.c"
+#include __DIR__"enable_commands.c"
+#include __DIR__"hash.c"
+#include __DIR__"object_info.c"
+#include __DIR__"query_editing.c"
+#include __DIR__"query_idle.c"
+#include __DIR__"query_input_pending.c"
+#include __DIR__"query_ip_name.c"
+#include __DIR__"query_limits.c"
+#include __DIR__"query_load_average.c"
+#include __DIR__"query_mud_port.c"
+#include __DIR__"query_once_interactive.c"
+#include __DIR__"query_snoop.c"
+#include __DIR__"set_heart_beat.c"
+#if __BOOT_TIME__ < 1456261859
+#include __DIR__"set_prompt.c"
+#endif
+#include __DIR__"shadow.c"
+#include __DIR__"livings.c"
+#include __DIR__"comm.c"
+
+#define TO efun::this_object()
+#define TI efun::this_interactive()
+#define TP efun::this_player()
+#define PO efun::previous_object(0)
+#define LEVEL(x) query_wiz_level(x)
+#define NAME(x) capitalize(getuid(x))
+
+#define DEBUG(x) if (find_player("zesstra")) \
+ tell_object(find_player("zesstra"),x)
+
+mixed dtime_cache = ({-1,""});
+
+#ifdef IOSTATS
+struct iostat_s {
+ string oname;
+ int time;
+ int size;
+};
+mixed saveo_stat = ({ 0,allocate(200, 0) });
+mixed restoreo_stat = ({ 0,allocate(200,0) });
+//mixed writefile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed readfile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed log_stat = ({ 0,allocate(100,(<iostat_s>)) });
+
+mixed ___iostats(int type) {
+ switch(type) {
+ case 1:
+ return saveo_stat;
+ case 2:
+ return restoreo_stat;
+/* case 3:
+ return writefile_stat;
+ case 4:
+ return readfile_stat;
+ case 5:
+ return log_stat;
+ */
+ }
+ return 0;
+}
+#endif
+
+// Nicht jeder Magier muss die simul_efun entsorgen koennen.
+string NotifyDestruct(object caller) {
+ if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
+ return "Du darfst das simul_efun Objekt nicht zerstoeren!\n";
+ }
+ return 0;
+}
+
+public nomask void remove_interactive( object ob )
+{
+ if ( objectp(ob) && previous_object()
+ && object_name(previous_object())[0..7] != "/secure/"
+ && ((previous_object() != ob
+ && (ob != this_player() || ob != this_interactive()))
+ || (previous_object() == ob
+ && (this_player() && this_player() != ob
+ || this_interactive() && this_interactive() != ob)) ) )
+
+ log_file( "PLAYERDEST",
+ sprintf( "%s: %O ausgeloggt von PO %O, TI %O, TP %O\n",
+ dtime(time()), ob, previous_object(),
+ this_interactive(), this_player() ) );
+
+ efun::remove_interactive(ob);
+}
+
+
+void ___updmaster()
+{
+ object ob;
+
+ //sollte nicht jeder duerfen.
+ if (process_call() || !ARCH_SECURITY)
+ raise_error("Illegal use of ___updmaster()!");
+
+ write("Removing old master ... ");
+ foreach(string file:
+ get_dir("/secure/master/*.c",GETDIR_NAMES|GETDIR_UNSORTED|GETDIR_PATH)) {
+ if (ob = find_object(file))
+ efun::destruct(ob);
+ }
+ efun::destruct(efun::find_object("/secure/master"));
+ write("done.\nLoading again ... ");
+ load_object("/secure/master");
+
+ write("done.\n");
+}
+
+varargs string country(mixed ip, string num) {
+ mixed ret;
+
+ if(ret = (string)"/p/daemon/iplookup"->country(num || ip)) {
+ return ret;
+ } else return "???";
+}
+
+
+// * Snoopen und was dazugehoert
+static object find_snooped(object who)
+{
+ object *u;
+ int i;
+
+ for (i=sizeof(u=users())-1;i>=0;i--)
+ if (who==efun::interactive_info(u[i], II_SNOOP_NEXT))
+ return u[i];
+ return 0;
+}
+
+private string Lcut(string str) {
+ return str[5..11]+str[18..];
+}
+
+nomask varargs int snoop( object me, object you )
+{
+ int ret;
+ object snooper0, snooper, snooper2, snooper3;
+
+ if( !objectp(me) || me == you || !PO )
+ return 0;
+
+ snooper0 = find_snooped(me);
+
+ if(you){
+ if ( PO != me && query_wiz_grp(me) >= query_wiz_grp(geteuid(PO)) )
+ return 0;
+
+ if ( query_wiz_grp(me) <= query_wiz_grp(you) &&
+ !(you->QueryAllowSnoop(me)) )
+ if ( !IS_DEPUTY(me) || IS_ARCH(you) )
+ return 0;
+
+ if ( (snooper = efun::interactive_info(you, II_SNOOP_NEXT)) &&
+ query_wiz_grp(snooper) >= query_wiz_grp(me) ){
+ if ( (int)snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
+ return 0;
+
+ tell_object( snooper, sprintf( "%s snooped jetzt %s.\n",
+ me->name(WER), you->name(WER) ) );
+
+ snooper2 = me;
+
+ while ( snooper3 = interactive_info(snooper2, II_SNOOP_NEXT) ){
+ tell_object( snooper,
+ sprintf( "%s wird seinerseits von %s gesnooped.\n"
+ ,snooper2->name(WER),
+ snooper3->name(WEM) ) );
+ snooper2 = snooper3;
+ }
+
+ efun::snoop( snooper, snooper2 );
+
+ if ( efun::interactive_info(snooper2, II_SNOOP_NEXT) != snooper )
+ tell_object( snooper, sprintf( "Du kannst %s nicht snoopen.\n",
+ snooper2->name(WEN) ) );
+ else{
+ tell_object( snooper, sprintf( "Du snoopst jetzt %s.\n",
+ snooper2->name(WEN) ) );
+ if ( !IS_DEPUTY(snooper) ){
+ log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
+ dtime(time()),
+ snooper,
+ snooper2,
+ environment(snooper2) ),
+ 100000 );
+ if (snooper0)
+ CHMASTER->send( "Snoop", snooper,
+ sprintf( "%s *OFF* %s (%O)",
+ capitalize(getuid(snooper)),
+ capitalize(getuid(snooper0)),
+ environment(snooper0) ) );
+
+ CHMASTER->send( "Snoop", snooper,
+ sprintf("%s -> %s (%O)",
+ capitalize(getuid(snooper)),
+ capitalize(getuid(snooper2)),
+ environment(snooper2)));
+ }
+ else{
+ log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+ dtime(time()),
+ snooper,
+ snooper2,
+ environment(snooper2) )
+ ,100000 );
+ }
+ }
+ }
+ else
+ if (snooper)
+ if ( !me->QueryProp(P_SNOOPFLAGS) & SF_LOCKED ){
+ printf( "%s wird bereits von %s gesnooped. Benutze das "
+ "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
+ you->name(WER), snooper->name(WEM) );
+ return 0;
+ }
+
+ ret = efun::snoop( me, you );
+
+ if ( !IS_DEPUTY(me) && efun::interactive_info(you, II_SNOOP_NEXT) == me){
+ log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+ Lcut(dtime(time())),
+ me, you, environment(you) ),
+ 100000 );
+
+ if (snooper0)
+ CHMASTER->send( "Snoop", me,
+ sprintf( "%s *OFF* %s (%O).",
+ capitalize(getuid(me)),
+ capitalize(getuid(snooper0)),
+ environment(snooper0) ) );
+
+ CHMASTER->send( "Snoop", me, sprintf( "%s -> %s (%O).",
+ capitalize(getuid(me)),
+ capitalize(getuid(you)),
+ environment(you) ) );
+ }
+ else{
+ if ( efun::interactive_info(you, II_SNOOP_NEXT) == me ){
+ log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+ Lcut(dtime(time())),
+ me, you, environment(you) ),
+ 100000 );
+ }
+ }
+
+ if ( ret && query_wiz_grp(me) <= query_wiz_grp(you) &&
+ !IS_DEPUTY(me) )
+ tell_object( you, "*** " + NAME(me) + " snoopt Dich!\n" );
+
+ return ret;
+ }
+ else {
+ if ( (me == PO ||
+ query_wiz_grp(geteuid(PO)) > query_wiz_grp(me) ||
+ (query_wiz_grp(geteuid(PO)) == query_wiz_grp(me) &&
+ efun::interactive_info(PO, II_SNOOP_NEXT) == me)) && snooper0 ){
+ if ( !IS_DEPUTY(me) ){
+ log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+ Lcut(dtime(time())), me,
+ snooper0,
+ environment(snooper0) ),
+ 100000 );
+
+ CHMASTER->send( "Snoop", me,
+ sprintf( "%s *OFF* %s (%O).",
+ capitalize(getuid(me)),
+ capitalize(getuid(snooper0)),
+ environment(snooper0) ) );
+ }
+ else{
+ log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+ Lcut(dtime(time())), me,
+ snooper0,
+ environment(snooper0) ),
+ 100000 );
+ }
+
+ return efun::snoop(me);
+ }
+ }
+ return 0;
+}
+
+
+
+// * Emulation des 'alten' explode durch das neue
+string *old_explode(string str, string del) {
+ int s, t;
+ string *strs;
+
+ if (!stringp(str)) {
+ set_this_object(previous_object());
+ raise_error(sprintf(
+ "Invalid argument 1 to old_explode()! Expected <string>, got: "
+ "%.30O\n",str));
+ }
+ if (!stringp(del)) {
+ set_this_object(previous_object());
+ raise_error(sprintf(
+ "Invalid argument 2 to old_explode()! Expected <string>, got: "
+ "%.30O\n",del));
+ }
+ if(del == "")
+ return ({str});
+ strs=efun::explode(str, del);
+ t=sizeof(strs)-1;
+ while(s<=t && strs[s++] == "");s--;
+ while(t>=0 && strs[t--] == "");t++;
+ if(s<=t)
+ return strs[s..t];
+ return ({});
+}
+
+int file_time(string path) {
+ mixed *v;
+
+ set_this_object(previous_object());
+ if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
+ return(0); //sonst
+}
+
+// * Bei 50k Groesse Log-File rotieren
+varargs int log_file(string file, string txt, int size_to_break) {
+ mixed *st;
+
+ file="/log/"+file;
+ file=implode((efun::explode(file,"/")-({".."})),"/");
+// tell_object(find_player("jof"),sprintf("LOG FILE: %O -> %O\n",previous_object(),file));
+ if (!funcall(bind_lambda(#'efun::call_other,PO),"secure/master",//')
+ "valid_write",file,geteuid(PO),"log_file",PO))
+ return 0;
+ if ( size_to_break >= 0 & (
+ sizeof(st = get_dir(file,2) ) && st[0] >= (size_to_break|MAX_LOG_SIZE)))
+ catch(rename(file, file + ".old");publish); /* No panic if failure */
+ return(write_file(file,txt));
+}
+
+// * Magier-Level abfragen
+int query_wiz_level(mixed player) {
+ return (int)"/secure/master"->query_wiz_level(player);
+}
+
+#ifdef __ALISTS__
+// * Element aus Alist loeschen (by key)
+mixed *remove_alist(mixed key,mixed *alist)
+{
+ int i,j;
+
+ if (!pointerp(alist) || !sizeof(alist))
+ return alist;
+ if (!pointerp(alist[0]))
+ {
+ if ((i=assoc(key,alist))<0)
+ return alist;
+ return alist[0..i-1]+alist[i+1..];
+ }
+ i = assoc(key,alist[0]);
+ if ((i=assoc(key,alist[0]))<0)
+ return alist;
+ alist=alist[0..];
+ for (j=sizeof(alist)-1;j>=0;j--)
+ alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+ return alist;
+}
+
+// * Element aus Alist loeschen (by pos)
+mixed *exclude_alist(int i,mixed *alist)
+{
+ int j;
+ if (!pointerp(alist) || !sizeof(alist) || i<0)
+ return alist;
+ if (!pointerp(alist[0]))
+ return alist[0..i-1]+alist[i+1..];
+ alist=alist[0..]; /* Create PHYSICAL copy of alist */
+ for (j=sizeof(alist)-1;j>=0;j--)
+ alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+ return alist; /* order_alist is NOT necessary - see /doc/LPC/alist */
+}
+#endif // __ALISTS__
+
+// * German version of ctime()
+#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
+#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
+ "Sep","Okt","Nov","Dez"})
+string dtime(int wann) {
+
+ if (wann == dtime_cache[0])
+ return(dtime_cache[1]);
+
+ int *lt = localtime(wann);
+ return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
+ TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]],
+ lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
+}
+
+// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
+// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
+// ein ordentliches Datum/Uhrzeit.
+#if !__EFUN_DEFINED__(strftime)
+varargs string strftime(mixed fmt, int clock, int localized) {
+ if (intp(clock) && clock >= 0)
+ return dtime(clock);
+ else if (intp(fmt) && fmt >= 0)
+ return dtime(fmt);
+
+ return dtime(time());
+}
+#endif //!__EFUN_DEFINED__(strftime)
+
+// * Shutdown mit zusaetzlichem logging
+nomask int shutdown(string reason)
+{
+ string name;
+ string obname;
+ string output;
+
+ if (!reason)
+ return 0;
+ if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
+ object_name(previous_object())!="/obj/shut" )
+ {
+ write("You have no permission to shut down the gamedriver!\n");
+ return 0;
+ }
+ if ((this_interactive())&&(name=getuid(this_interactive())))
+ {
+ name=capitalize(name);
+ filter(users(),#'tell_object,//'
+ capitalize(name)+" faehrt das Spiel herunter!\n");
+ }
+ else
+ name="ANONYMOUS";
+ if (previous_object()) obname=capitalize(getuid(previous_object()));
+ output=name;
+ if (obname && name!=obname) output=output+" ("+obname+")";
+ if (previous_object()&&object_name(previous_object())=="/obj/shut"){
+ output+=" faehrt das Spiel via Armageddon herunter.\n";
+ output=dtime(time())+": "+output;
+ log_file("GAME_LOG",output+"\n",-1);
+ efun::shutdown();
+ return 1;
+ }
+ output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
+ output+=" Grund: "+reason;
+ log_file("GAME_LOG",output+"\n",-1);
+ efun::shutdown();
+ return 1;
+}
+
+// * lowerchar
+
+int lowerchar(int char) {
+ if (char<'A' || char>'Z') return char;
+ return char+32;
+}
+
+// * upperstring
+
+string upperstring(string s)
+{
+#if __EFUN_DEFINED__(upper_case)
+ return(upper_case(s));
+#else
+ int i;
+ if (!stringp(s)) return 0;
+ for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
+ return s;
+#endif
+}
+
+// * lowerstring
+
+string lowerstring(string s)
+{
+ if (!stringp(s)) return 0;
+ return lower_case(s);
+}
+
+
+// * GD version
+string version()
+{
+ return __VERSION__;
+}
+
+// * break_string
+// stretch() -- stretch a line to fill a given width
+private string stretch(string s, int width) {
+ int len=sizeof(s);
+ if (len==width) return s;
+
+ // reine Leerzeilen, direkt zurueckgeben
+ string trimmed=trim(s,TRIM_LEFT," ");
+ if (trimmed=="") return s;
+ int start_spaces = len - sizeof(trimmed);
+
+ string* words = explode(trimmed, " ");
+ // der letzte kriegt keine Spaces
+ int word_count=sizeof(words) - 1;
+ // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
+ if (!word_count)
+ return " "*start_spaces + words[0];
+
+ int space_count = width - len;
+
+ int space_per_word=(word_count+space_count) / word_count;
+ // Anz.Woerter mit Zusatz-Space
+ int rest=(word_count+space_count) % word_count;
+ // Rest-Spaces Verteilen
+ foreach (int pos : rest) words[pos]+=" ";
+ return (" "*start_spaces) + implode( words, " "*space_per_word );
+}
+
+// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
+// eigene Funktion bekommen:
+private varargs string block_string(string s, int width, int flags) {
+ // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
+ // einem Leerzeichen begonnen.
+ // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
+ // ersetzt.
+ if ( (flags & BS_LEAVE_MY_LFS)
+ && !(flags & BS_NO_PARINDENT))
+ {
+ s = " "+regreplace(s,"\n","\n ",1);
+ }
+
+ // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
+ // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
+ // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
+ // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
+ // wieder rueckgaengig zu machen.
+ s = sprintf( "%-*=s", width, s);
+
+ string *tmp=explode(s, "\n");
+ // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
+ // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
+ // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
+ // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
+ // Ende der letzten Zeile hat), das macht der Aufrufer...
+ if (sizeof(tmp) > 1)
+ return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" )
+ + "\n" + tmp[<1];
+
+ return s;
+}
+
+public varargs string break_string(string s, int w, mixed indent, int flags)
+{
+ if ( !s || s == "" ) return "";
+
+ if ( !w ) w=78;
+
+ if( intp(indent) )
+ indent = indent ? " "*indent : "";
+
+ int indentlen=stringp(indent) ? sizeof(indent) : 0;
+
+ if (indentlen>w) {
+ set_this_object(previous_object());
+ raise_error(sprintf("break_string: indent longer %d than width %d\n",
+ indentlen,w));
+ // w=((indentlen/w)+1)*w;
+ }
+
+ if (!(flags & BS_LEAVE_MY_LFS))
+ s=regreplace( s, "\n", " ", 1 );
+
+ if ( flags & BS_SINGLE_SPACE )
+ s = regreplace( s, "(^|\n| ) *", "\\1", 1 );
+
+ string prefix="";
+ if (indentlen && flags & BS_PREPEND_INDENT) {
+ if (indentlen+sizeof(s) > w ||
+ (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
+ prefix=indent+"\n";
+ indent=(flags & BS_NO_PARINDENT) ? "" : " ";
+ indentlen=sizeof(indent);
+ }
+ }
+
+ if ( flags & BS_BLOCK ) {
+ /*
+ s = implode( map( explode( s, "\n" ),
+ #'block_string, w, indentlen, flags),
+ "" );
+ */
+ s = block_string( s , w - indentlen, flags );
+ }
+ else {
+ s = sprintf("%-1.*=s",w-indentlen,s);
+ }
+ if ( s[<1] != '\n' ) s += "\n";
+
+ if ( !indentlen ) return prefix + s;
+
+ string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
+
+ return prefix + indent +
+ regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
+ /*
+ string *buf;
+
+ buf = explode( s, "\n" );
+ return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
+ */
+}
+
+// * Elemente aus mapping loeschen - mapping vorher kopieren
+
+mapping m_copy_delete(mapping m, mixed key) {
+ return m_delete(copy(m), key);
+}
+
+// * times
+int last_reboot_time()
+{
+ return __BOOT_TIME__;
+}
+
+int first_boot_time()
+{
+ return 701517600;
+}
+
+int exist_days()
+{
+ return (((time()-first_boot_time())/8640)+5)/10;
+}
+
+// * uptime :)
+string uptime()
+{
+ int t;
+ int tmp;
+ string s;
+
+ t=time()-__BOOT_TIME__;
+ s="";
+ if (t>=86400)
+ s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
+ if (t>=3600)
+ s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
+ if (t>60)
+ s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
+ return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
+}
+
+// * Was tun bei 'dangling' lfun-closures ?
+void dangling_lfun_closure() {
+ raise_error("dangling lfun closure\n");
+}
+
+// * Sperren ausser fuer master/simul_efun
+
+#if __EFUN_DEFINED__(set_environment)
+nomask void set_environment(object o1, object o2) {
+ raise_error("Available only for root\n");
+}
+#endif
+
+nomask void set_this_player(object pl) {
+ raise_error("Available only for root\n");
+}
+
+#if __EFUN_DEFINED__(export_uid)
+nomask void export_uid(object ob) {
+ raise_error("Available only for root\n");
+}
+#endif
+
+// * Jetzt auch closures
+int process_flag;
+
+public nomask int process_call()
+{
+ if (process_flag>0)
+ return process_flag;
+ else return(0);
+}
+
+private nomask string _process_string(string str,object po) {
+ set_this_object(po);
+ return(efun::process_string(str));
+}
+
+nomask string process_string( mixed str )
+{
+ string tmp, err;
+ int flag;
+
+ if ( closurep(str) ) {
+ set_this_object( previous_object() );
+ return funcall(str);
+ }
+ else if (str==0)
+ return((string)str);
+ else if ( !stringp(str) ) {
+ return to_string(str);
+ }
+
+ if ( !(flag = process_flag > time() - 60))
+ process_flag=time();
+
+ err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
+
+ if ( !flag )
+ process_flag=0;
+
+ if (err) {
+ // Verarbeitung abbrechen
+ set_this_object(previous_object());
+ raise_error(err);
+ }
+ return tmp;
+}
+
+// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
+// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
+// zurueckgeliefert, sonst 0.
+// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
+public int mkdirp(string dir) {
+ // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
+ if (strstr(dir, "/") != 0)
+ raise_error("mkdirp(): Pfad ist nicht absolute.\n");
+ // cut off trailing /...
+ if (dir[<1]=='/')
+ dir = dir[0..<2];
+
+ int fstat = file_size(dir);
+ // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
+ if (fstat == FSIZE_DIR)
+ return 1;
+ // wenn schon ne Datei existiert, geht es nicht.
+ if (fstat != FSIZE_NOFILE)
+ return 0;
+ // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
+ // toplevel-verzeichnis, was direkt angelegt wird.
+ if (strrstr(dir,"/")==0) {
+ return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+ }
+
+ // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
+ // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
+ // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
+
+ // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
+ // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
+ if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
+ return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+}
+
+
+// * Properties ggfs. mitspeichern
+mixed save_object(mixed name)
+{
+ mapping properties;
+ mapping save;
+ mixed index, res;
+ int i;
+
+ // nur Strings und 0 zulassen
+ if ((!stringp(name) || !sizeof(name)) &&
+ (!intp(name) || name!=0)) {
+ set_this_object(previous_object());
+ raise_error(sprintf(
+ "Only non-empty strings and 0 may be used as filename in "
+ "sefun::save_object()! Argument was %O\n",name));
+ }
+
+ save = m_allocate(0, 2);
+ properties = (mapping)previous_object()->QueryProperties();
+
+ if(mappingp(properties))
+ {
+ // delete all entries in mapping properties without SAVE flag!
+ index = m_indices(properties);
+ for(i = sizeof(index)-1; i>=0;i--)
+ {
+ if(properties[index[i], F_MODE] & SAVE)
+ {
+ save[index[i]] = properties[index[i]];
+ save[index[i], F_MODE] =
+ properties[index[i], F_MODE] &
+ (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
+ }
+ }
+ }
+ else save = ([]);
+
+ // save object!
+ previous_object()->_set_save_data(save);
+ // format: wie definiert in config.h
+ if (stringp(name))
+ res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
+ __LIB__SAVE_FORMAT_VERSION__);
+ else
+ res = funcall(bind_lambda(#'efun::save_object, previous_object()),
+ __LIB__SAVE_FORMAT_VERSION__);
+ previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+ // Stats...
+ struct iostat_s stat = (<iostat_s>);
+ stat->oname = object_name(previous_object());
+ stat->time = time();
+ //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+ // OIM_TOTAL_DATA_SIZE);
+ if (stringp(name))
+ stat->size = file_size(name + ".o");
+ else
+ stat->sizeof(res);
+ //debug_message("saveo: "+saveo_stat[0]+"\n");
+ saveo_stat[1][saveo_stat[0]] = stat;
+ saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
+ //debug_message("saveo 2: "+saveo_stat[0]+"\n");
+#endif
+
+ return res;
+}
+
+// * Auch Properties laden
+int restore_object(string name)
+{
+ int result;
+ mixed index;
+ mixed save;
+ mapping properties;
+ int i;
+ closure cl;
+
+ // get actual property settings (by create())
+ properties = (mapping)previous_object()->QueryProperties();
+
+// DEBUG(sprintf("RESTORE %O\n",name));
+ // restore object
+ result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
+ //'))
+ //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein
+ //mapping sein sollte.
+ save = (mixed)previous_object()->_get_save_data();
+ if(mappingp(save))
+ {
+ index = m_indices(save);
+ for(i = sizeof(index)-1; i>=0; i--)
+ {
+ properties[index[i]] = save[index[i]];
+ properties[index[i], F_MODE] = save[index[i], F_MODE]
+ &~(SETCACHED|QUERYCACHED);
+ }
+ }
+ else properties = ([]);
+
+ // restore properties
+ funcall(
+ bind_lambda(
+ unbound_lambda(({'arg}), //'})
+ ({#'call_other,({#'this_object}),
+ "SetProperties",'arg})),//')
+ previous_object()),properties);
+ previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+ // Stats...
+ //debug_message("restoreo: "+restoreo_stat[0]+"\n");
+ struct iostat_s stat = (<iostat_s>);
+ stat->oname = object_name(previous_object());
+ stat->time = time();
+ //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+ // OIM_TOTAL_DATA_SIZE);
+ stat->size = file_size(name + ".o");
+ restoreo_stat[1][restoreo_stat[0]] = stat;
+
+ restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
+#endif
+
+ return result;
+}
+
+// * HB eines Objektes ein/ausschalten
+int set_object_heart_beat(object ob, int flag)
+{
+ if (objectp(ob))
+ return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
+}
+
+// * Magierlevelgruppen ermitteln
+int query_wiz_grp(mixed wiz)
+{
+ int lev;
+
+ lev=query_wiz_level(wiz);
+ if (lev<SEER_LVL) return 0;
+ if (lev>=GOD_LVL) return lev;
+ if (lev>=ARCH_LVL) return ARCH_GRP;
+ if (lev>=ELDER_LVL) return ELDER_GRP;
+ if (lev>=LORD_LVL) return LORD_GRP;
+ if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
+ if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
+ if (lev>=WIZARD_LVL) return WIZARD_GRP;
+ if (lev>=LEARNER_LVL) return LEARNER_GRP;
+ return SEER_GRP;
+}
+
+mixed *wizlist_info()
+{
+ if (ARCH_SECURITY || !extern_call())
+ return efun::wizlist_info();
+ return 0;
+}
+
+// * wizlist ausgeben
+varargs void wizlist(string name, int sortkey ) {
+
+ if (!name)
+ {
+ if (this_player())
+ name = getuid(this_player());
+ if (!name)
+ return;
+ }
+
+ // Schluessel darf nur in einem gueltigen Bereich sein
+ if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
+
+ mixed** wl = efun::wizlist_info();
+ // nach <sortkey> sortieren
+ wl = sort_array(wl, function int (mixed a, mixed b)
+ {return a[sortkey] < b[sortkey]; } );
+
+ // Summe ueber alle Kommandos ermitteln.
+ int total_cmd, i;
+ int pos=-1;
+ foreach(mixed entry : wl)
+ {
+ total_cmd += entry[WL_COMMANDS];
+ if (entry[WL_NAME] == name)
+ pos = i;
+ ++i;
+ }
+
+ if (pos < 0 && name != "ALL" && name != "TOP100")
+ return;
+
+ if (name == "TOP100")
+ {
+ if (sizeof(wl) > 100)
+ wl = wl[0..100];
+ else
+ wl = wl;
+ }
+ // um name herum schneiden
+ else if (name != "ALL")
+ {
+ if (sizeof(wl) <= 21)
+ wl = wl;
+ else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
+ wl = wl[pos-10..pos+10];
+ else if (pos <=21)
+ wl = wl[0..20];
+ else if (pos >= sizeof(wl) - 21)
+ wl = wl[<21..];
+ else
+ wl = wl;
+ }
+
+ write("\nWizard top score list\n\n");
+ if (total_cmd == 0)
+ total_cmd = 1;
+ printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
+ "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
+ foreach(mixed e: wl)
+ {
+ printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
+ e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
+ e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
+ e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
+ );
+ }
+ printf("\nTotal %7d (%d)\n\n", total_cmd, sizeof(wl));
+}
+
+
+// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
+
+// ermittelt das Objekt des Callouts.
+private object _call_out_obj( mixed call_out ) {
+ return pointerp(call_out) ? call_out[0] : 0;
+}
+
+private void _same_object( object ob, mapping m ) {
+ // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
+ if ( m[ob] )
+ m[ob] += ({ ob });
+ else
+ m[ob] = ({ ob });
+}
+
+// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
+// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
+// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
+private void _same_path( object key, object *obs, mapping m ) {
+ string path;
+ if (!objectp(key) || !pointerp(obs)) return;
+
+ path = load_name(key);
+
+ if ( m[path] )
+ m[path] += obs;
+ else
+ m[path] = obs;
+}
+
+// key kann object oder string sein.
+private int _too_many( mixed key, mapping m, int i ) {
+ return sizeof(m[key]) >= i;
+}
+
+// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
+// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
+// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
+private void _destroy( mixed key, object *obs, string text, int uid ) {
+ if (!pointerp(obs)) return;
+ // Array mit unique Eintraege erzeugen.
+ obs = m_indices( mkmapping(obs) );
+ // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
+ // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
+ // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
+ // laeuft.
+ catch( efun::raise_error(
+ sprintf( text,
+ uid ? (string)master()->creator_file(key) : key,
+ sizeof(obs), object_name(obs[<1]) ) );publish);
+ // Und weg mit dem Kram...
+ filter( obs, #'efun::destruct/*'*/ );
+}
+
+// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
+// sind dabei nicht unique.
+private void _same_uid( string key, object *obs, mapping m, closure cf ) {
+ string uid;
+
+ if ( !pointerp(obs) || !sizeof(obs) )
+ return;
+
+ uid = funcall( cf, key );
+
+ if ( m[uid] )
+ m[uid] += obs; // obs ist nen Array
+ else
+ m[uid] = obs;
+}
+
+private int _log_call_out(mixed co)
+{
+ log_file("TOO_MANY_CALLOUTS",
+ sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
+ 200000);
+ return 0;
+}
+
+private int last_callout_log=0;
+
+nomask varargs void call_out( varargs mixed *args )
+{
+ mixed tmp, *call_outs;
+
+ // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
+ // haben.
+ if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
+ && geteuid(previous_object()) != ROOTID )
+ {
+ // Log erzeugen...
+ if (last_callout_log <
+ efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
+ && get_eval_cost() > 200000)
+ {
+ last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+ log_file("TOO_MANY_CALLOUTS",
+ sprintf("\n%s: ############ Too many callouts: %d ##############\n",
+ strftime("%y%m%d-%H%M%S"),
+ efun::driver_info(DI_NUM_CALLOUTS)));
+ filter(efun::call_out_info(), #'_log_call_out);
+ }
+ // Objekte aller Callouts ermitteln
+ call_outs = map( efun::call_out_info(), #'_call_out_obj );
+ mapping objectmap = ([]);
+ filter( call_outs, #'_same_object, &objectmap );
+ // Master nicht grillen...
+ efun::m_delete( objectmap, master(1) );
+ // alle Objekte raussuchen, die zuviele haben...
+ mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
+ // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
+ // Objekten uebergeben (in diesem Fall sind Keys und Array mit
+ // Objekten jeweils das gleiche Objekt).
+ if ( sizeof(res) )
+ walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "
+ "object [%O]. Destructed %d objects. [%s]\n", 0 );
+
+ // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
+ // Objekte killen, die mehr als 50 Callouts laufen haben - und
+ // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
+ // Clones einer BP gekillt, die Callouts laufen haben, falls alle
+ // diese Objekte _zusammen_ mehr als 50 Callouts haben!
+ if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
+ // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
+ // mehr drin, da sie dort als Keys verwendet wurden.
+ mapping pathmap=([]);
+ // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
+ // von Objekten als Werte.
+ walk_mapping( objectmap, #'_same_path, &pathmap);
+ // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
+ res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
+ // und ueber alle Keys gehen, an _destroy() werden die Clones
+ // uebergeben, die Callouts haben.
+ if ( sizeof(res) )
+ walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
+ "'%s'. Destructed %d objects. [%s]\n", 0 );
+
+ // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
+ // noch >1000 Callouts laufen, werden diesmal alle Callouts
+ // einer UID zusammengezaehlt.
+ // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
+ // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
+ if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
+ {
+ // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
+ // zusammensortieren. Zerstoerte Clones filter _same_uid()
+ // raus.
+ mapping uidmap=([]);
+ walk_mapping( pathmap, #'_same_uid, &uidmap,
+ symbol_function( "creator_file",
+ "/secure/master" ) );
+ // In res nun UIDs als Keys und Arrays von Objekten als Werte.
+ // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
+ // 100 Callouts!) haben.
+ res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
+ // und erneut ueber die Keys laufen und jeweils die Arrays mit
+ // den Objekten zur Zerstoerung an _destroy()...
+ if ( sizeof(res) )
+ walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
+ "UID '%s'. Destructed %d objects. [%s]\n",
+ 1 );
+ }
+ }
+ }
+
+ // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
+ if ( !previous_object() )
+ return;
+
+ set_this_object( previous_object() );
+ apply( #'efun::call_out, args );
+ return;
+}
+
+mixed call_out_info() {
+
+ object po = previous_object();
+ mixed coi = efun::call_out_info();
+
+ // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
+ // /obj haben die BackboneID.
+ if (query_wiz_level(getuid(po)) >= ARCH_LVL
+ || (string)master()->creator_file(load_name(po)) == BACKBONEID ) {
+ return coi;
+ }
+ else {
+ return filter(coi, function mixed (mixed arr) {
+ if (pointerp(arr) && arr[0]==po)
+ return 1;
+ else return 0; });
+ }
+}
+
+// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
+// NICHT das Objekt, was ggf. die lfun definiert!
+mixed query_closure_object(closure c) {
+ return
+ CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
+ 0 :
+ (to_object(c) || -1);
+}
+
+// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
+varargs void move_object(mixed what, mixed where)
+{
+ object po,tmp;
+
+ po=previous_object();
+ if (!where)
+ {
+ where=what;
+ what=po;
+ }
+ if (((stringp(where) && where==NETDEAD_ROOM ) ||
+ (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
+ objectp(what) && object_name(what)!="/obj/sperrer")
+ {
+ if (!query_once_interactive(what))
+ {
+ what->remove();
+ if (what) destruct(what);
+ return;
+ }
+ if (living(what) || interactive(what))
+ {
+ log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
+ return;
+ }
+ set_object_heart_beat(what,0);
+ }
+ tmp=what;
+ while (tmp=environment(tmp))
+ // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+ // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+ // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+ // da um jedes bisschen Rechenzeit geht.
+ // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+ //
+ // Tiamak
+ tmp->_set_last_content_change();
+ funcall(bind_lambda(#'efun::move_object,po),what,where);
+ if (tmp=what)
+ while (tmp=environment(tmp))
+ tmp->_set_last_content_change();
+}
+
+
+void start_simul_efun() {
+ mixed *info;
+
+ // Falls noch nicht getan, extra_wizinfo initialisieren
+ if ( !pointerp(info = get_extra_wizinfo(0)) )
+ set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
+
+ InitLivingData(info);
+
+ set_next_reset(10); // direkt mal aufraeumen
+}
+
+protected void reset() {
+ set_next_reset(7200);
+ CleanLivingData();
+}
+
+#if !__EFUN_DEFINED__(absolute_hb_count)
+int absolute_hb_count() {
+ return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+}
+#endif
+
+void __set_environment(object ob, mixed target)
+{
+ string path;
+ object obj;
+
+ if (!objectp(ob))
+ return;
+ if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
+ return;
+ if (objectp(target))
+ {
+ efun::set_environment(ob,target);
+ return;
+ }
+ path=(string)MASTER->_get_path(target,this_interactive());
+ if (stringp(path) && file_size(path+".c")>=0 &&
+ !catch(load_object(path);publish) )
+ {
+ obj=find_object(path);
+ efun::set_environment(ob,obj);
+ return;
+ }
+}
+
+void _dump_wizlist(string file, int sortby) {
+ int i;
+ mixed *a;
+
+ if (!LORD_SECURITY)
+ return;
+ if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
+ {
+ write("NO WRITE PERMISSION\n");
+ return;
+ }
+ a = wizlist_info();
+ a = sort_array(a, lambda( ({'a,'b}),
+ ({#'<,
+ ({#'[,'a,sortby}),
+ ({#'[,'b,sortby})
+ })));
+ rm(file);
+ for (i=sizeof(a)-1;i>=0;i--)
+ write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
+ a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
+ a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
+}
+
+public varargs object deep_present(mixed what, object ob) {
+
+ if(!objectp(ob))
+ ob=previous_object();
+ // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
+ // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
+ if(objectp(what)) {
+ object *envs=all_environment(what);
+ // wenn ob kein Environment hat, ist es offensichtlich nicht in what
+ // enthalten.
+ if (!pointerp(envs)) return 0;
+ if (member(envs, ob) != -1) return what;
+ }
+ // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
+ // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
+ // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
+ else if (stringp(what)) {
+ int cnt;
+ string newwhat;
+ if(sscanf(what,"%s %d",newwhat,cnt)!=2)
+ cnt=1;
+ else
+ what=newwhat;
+ foreach(object invob: deep_inventory(ob)) {
+ if (invob->id(what) && !--cnt)
+ return invob;
+ }
+ }
+ else {
+ set_this_object(previous_object());
+ raise_error(sprintf("Wrong argument 1 to deep_present(). "
+ "Expected \"object\" or \"string\", got %.50O.\n",
+ what));
+ }
+ return 0;
+}
+
+mapping dump_ip_mapping()
+{
+ return 0;
+}
+
+nomask void swap(object obj)
+{
+ write("Your are not allowed to swap objects by hand!\n");
+ return;
+}
+
+nomask varargs void garbage_collection(string str)
+{
+ if(previous_object()==0 || !IS_ARCH(geteuid(previous_object()))
+ || !ARCH_SECURITY)
+ {
+ write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
+ return;
+ }
+ else if (stringp(str))
+ {
+ return efun::garbage_collection(str);
+ }
+ else
+ return efun::garbage_collection();
+}
+
+varargs void notify_fail(mixed nf, int prio) {
+ object po,oldo;
+ int oldprio;
+
+ if (!PL || !objectp(po=previous_object())) return;
+ if (!stringp(nf) && !closurep(nf)) {
+ set_this_object(po);
+ raise_error(sprintf(
+ "Only strings and closures allowed for notify_fail! "
+ "Argument was: %.50O...\n",nf));
+ }
+
+ // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
+ // und vergleichen.
+ if (objectp(oldo=query_notify_fail(1)) && po!=oldo) {
+ if (!prio) {
+ //Prioritaet dieses notify_fail() 'abschaetzen'
+ if (po==PL) // Spieler-interne (soul-cmds)
+ prio=NF_NL_OWN;
+ else if (living(po))
+ prio=NF_NL_LIVING;
+ else if ((int)po->IsRoom())
+ prio=NF_NL_ROOM;
+ else
+ prio=NF_NL_THING;
+ }
+ //Prioritaet des alten Setzers abschaetzen
+ if (oldo==PL)
+ oldprio=NF_NL_OWN;
+ else if (living(oldo))
+ oldprio=NF_NL_LIVING;
+ else if ((int)oldo->IsRoom())
+ oldprio=NF_NL_ROOM;
+ else
+ oldprio=NF_NL_THING;
+ }
+ else // wenn es noch kein Notify_fail gibt:
+ oldprio=NF_NL_NONE;
+
+ //vergleichen und ggf. setzen
+ if (prio >= oldprio) {
+ set_this_object(po);
+ efun::notify_fail(nf);
+ }
+
+ return;
+}
+
+void _notify_fail(string str)
+{
+ //query_notify_fail() benutzen, um das Objekt
+ //des letzten notify_fail() zu ermitteln
+ object o;
+ if ((o=query_notify_fail(1)) && o!=previous_object())
+ return;
+ //noch kein notify_fail() fuer dieses Kommando gesetzt, auf gehts.
+ set_this_object(previous_object());
+ efun::notify_fail(str);
+ return;
+}
+
+string time2string( string format, int zeit )
+{
+ int i,ch,maxunit,dummy;
+ string *parts, fmt;
+
+ int secs = zeit;
+ int mins = (zeit/60);
+ int hours = (zeit/3600);
+ int days = (zeit/86400);
+ int weeks = (zeit/604800);
+ int months = (zeit/2419200);
+ int abbr = 0;
+
+ parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
+
+ for( i=1; i<sizeof(parts); i+=2 )
+ {
+ ch = parts[i][<1];
+ switch( parts[i][<1] )
+ {
+ case 'x': case 'X':
+ abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
+ // NO break !
+ case 'n': case 'N':
+ maxunit |= 31;
+ break;
+ case 'w': case 'W':
+ maxunit |= 15;
+ break;
+ case 'd': case 'D':
+ maxunit |= 7;
+ break;
+ case 'h': case 'H':
+ maxunit |= 3;
+ break;
+ case 'm': case 'M':
+ maxunit |= 1;
+ break;
+ }
+ }
+ if( maxunit & 16 ) weeks %= 4;
+ if( maxunit & 8 ) days %= 7;
+ if( maxunit & 4 ) hours %= 24;
+ if( maxunit & 2 ) mins %= 60;
+ if( maxunit ) secs %= 60;
+
+ for( i=1; i<sizeof(parts); i+=2 )
+ {
+ fmt = parts[i][0..<2];
+ ch = parts[i][<1];
+ if( ch=='x' )
+ {
+ if (months > 0) ch='n';
+ else if( weeks>0 ) ch='w';
+ else if( days>0 ) ch='d';
+ else if( hours>0 ) ch='h';
+ else if(mins > 0) ch = 'm';
+ else ch = 's';
+ }
+ else if( ch=='X' )
+ {
+ if (months > 0) ch='N';
+ else if( weeks>0 ) ch='W';
+ else if( days>0 ) ch='D';
+ else if( hours>0 ) ch='H';
+ else if(mins > 0) ch = 'M';
+ else ch = 'S';
+ }
+
+ switch( ch )
+ {
+ case 'n': parts[i] = sprintf( fmt+"d", months ); break;
+ case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
+ case 'd': parts[i] = sprintf( fmt+"d", days ); break;
+ case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
+ case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
+ case 's': parts[i] = sprintf( fmt+"d", secs ); break;
+ case 'N':
+ if(abbr) parts[i] = "M";
+ else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
+ break;
+ case 'W':
+ if(abbr) parts[i] = "w"; else
+ parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
+ break;
+ case 'D':
+ if(abbr) parts[i] = "d"; else
+ parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
+ break;
+ case 'H':
+ if(abbr) parts[i] = "h"; else
+ parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
+ break;
+ case 'M':
+ if(abbr) parts[i] = "m"; else
+ parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
+ break;
+ case 'S':
+ if(abbr) parts[i] = "s"; else
+ parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
+ break;
+ case '%':
+ parts[i] = "%";
+ break;
+ }
+ }
+ return implode( parts, "" );
+}
+
+nomask mixed __create_player_dummy(string name)
+{
+ string err;
+ object ob;
+ mixed m;
+ //hat nen Scherzkeks die Blueprint bewegt?
+ if ((ob=find_object("/secure/login")) && environment(ob))
+ catch(destruct(ob);publish);
+ err = catch(ob = clone_object("secure/login");publish);
+ if (err)
+ {
+ write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
+ return 0;
+ }
+ if (objectp(m=(mixed)ob->new_logon(name))) netdead[name]=m;
+ return m;
+}
+
+nomask int secure_level()
+{
+ int *level;
+ //kette der Caller durchlaufen, den niedrigsten Level in der Kette
+ //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
+ //von 0.
+ //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
+ //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
+ //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
+ //INteractive geben muss.
+ level=map(caller_stack(1),function int (object caller)
+ {if (objectp(caller))
+ return(query_wiz_level(geteuid(caller)));
+ return(0); // kein Objekt da, 0.
+ } );
+ return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
+}
+
+nomask string secure_euid()
+{
+ string euid;
+
+ if (!this_interactive()) // Es muss einen interactive geben
+ return 0;
+ euid=geteuid(this_interactive());
+ // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
+ // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
+ // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
+ foreach(object caller: caller_stack()) {
+ if (!objectp(caller) ||
+ (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
+ return 0;
+ }
+ return euid; // 'sichere' euid zurueckgeben
+}
+
+// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
+// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
+//#if __BOOT_TIME__ < 1360017213
+varargs void input_to( mixed fun, int flags, varargs mixed *args )
+{
+ mixed *arr;
+ int i;
+
+ if ( !this_player() || !previous_object() )
+ return;
+
+ // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
+ // vorkommt...
+ if ( flags&INPUT_PROMPT ) {
+ arr = ({ fun, flags }) + args;
+ }
+ else {
+ // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
+ flags |= INPUT_PROMPT;
+ arr = ({ fun, flags, "" }) + args;
+ }
+
+ // Arrays gegen flatten quoten.
+ for ( i = sizeof(arr) - 1; i > 1; i-- )
+ if ( pointerp(arr[i]) )
+ arr[i] = quote(arr[i]);
+
+ apply( bind_lambda( unbound_lambda( ({}),
+ ({ #'efun::input_to/*'*/ }) + arr ),
+ previous_object() ) );
+}
+//#endif
+
+nomask int set_light(int i)
+// erhoeht das Lichtlevel eines Objekts um i
+// result: das Lichtlevel innerhalb des Objekts
+{
+ object ob, *inv;
+ int lall, light, dark, tmp;
+
+ if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
+
+ // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
+ if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
+
+ // Lichtberechnung findet eigentlich in der Mudlib statt.
+ return (int)ob->QueryProp(P_INT_LIGHT);
+}
+
+
+public string iso2ascii( string str )
+{
+ if ( !stringp(str) || !sizeof(str) )
+ return "";
+
+ str = regreplace( str, "ä", "ae", 1 );
+ str = regreplace( str, "ö", "oe", 1 );
+ str = regreplace( str, "ü", "ue", 1 );
+ str = regreplace( str, "Ä", "Ae", 1 );
+ str = regreplace( str, "Ö", "Oe", 1 );
+ str = regreplace( str, "Ü", "Ue", 1 );
+ str = regreplace( str, "ß", "ss", 1 );
+ str = regreplace( str, "[^ -~]", "?", 1 );
+
+ return str;
+}
+
+
+public varargs string CountUp( string *s, string sep, string lastsep )
+{
+ string ret;
+
+ if ( !pointerp(s) )
+ return "";
+
+ if (!sep) sep = ", ";
+ if (!lastsep) lastsep = " und ";
+
+ switch (sizeof(s)) {
+ case 0: ret=""; break;
+ case 1: ret=s[0]; break;
+ default:
+ ret = implode(s[0..<2], sep);
+ ret += lastsep + s[<1];
+ }
+ return ret;
+}
+
+nomask varargs int query_next_reset(object ob) {
+
+ // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
+ if (ob && !objectp(ob))
+ raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
+ "expected object.\n",ob));
+
+ // Defaultobjekt PO, wenn 0 uebergeben.
+ if ( !objectp(ob) )
+ ob = previous_object();
+
+ return efun::object_info(ob, OI_NEXT_RESET_TIME);
+}
+
+
+#if !__EFUN_DEFINED__(copy_file)
+#define MAXLEN 50000
+nomask int copy_file(string source, string dest)
+{
+
+ int ptr;
+ string bytes;
+
+ set_this_object(previous_object());
+ if (!sizeof(source)||!sizeof(dest)||source==dest||(file_size(source)==-1)||
+ (!call_other(master(),"valid_read",source,
+ getuid(this_interactive()||
+ previous_object()),"read_file",previous_object()))||
+ (!call_other(master(),"valid_read",source,
+ getuid(this_interactive()||
+ previous_object()),"write_file",previous_object())))
+ return 1;
+ switch (file_size(dest))
+ {
+ case -1:
+ break;
+ case -2:
+ if (dest[<1]!='/') dest+="/";
+ dest+=efun::explode(source,"/")[<1];
+ if (file_size(dest)==-2) return 1;
+ if (file_size(dest)!=-1) break;
+ default:
+ if (!rm(dest)) return 1;
+ break;
+ }
+ do
+ {
+ bytes = read_bytes(source, ptr, MAXLEN); ptr += MAXLEN;
+ if (!bytes) bytes="";
+ write_file(dest, bytes);
+ }
+ while(sizeof(bytes) == MAXLEN);
+ return 0;
+}
+#endif //!__EFUN_DEFINED__(copy_file)
+
+
+// ### Ersatzaufloesung in Strings ###
+varargs string replace_personal(string str, mixed *obs, int caps) {
+ int i;
+ string *parts;
+
+ parts = regexplode(str, "@WE[A-SU]*[0-9]");
+ i = sizeof(parts);
+
+ if (i>1) {
+ int j, t;
+ closure *name_cls;
+
+ t = j = sizeof(obs);
+
+ name_cls = allocate(j);
+ while (j--)
+ if (objectp(obs[j]))
+ name_cls[j] = symbol_function("name", obs[j]);
+ else if (stringp(obs[j]))
+ name_cls[j] = obs[j];
+
+ while ((i-= 2)>0) {
+ int ob_nr;
+ // zu ersetzendes Token in Fall und Objektindex aufspalten
+ ob_nr = parts[i][<1]-'1';
+ if (ob_nr<0 || ob_nr>=t) {
+ set_this_object(previous_object());
+ raise_error(sprintf("replace_personal: using wrong object index %d\n",
+ ob_nr));
+ return implode(parts, "");
+ }
+
+ // casus kann man schon hier entscheiden
+ int casus;
+ string part = parts[i];
+ switch (part[3]) {
+ case 'R': casus = WER; break;
+ case 'S': casus = WESSEN; break;
+ case 'M': casus = WEM; break;
+ case 'N': casus = WEN; break;
+ default: continue; // passt schon jetzt nicht in das Hauptmuster
+ }
+
+ // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
+ mixed tmp;
+ switch (part[3..<2]) {
+ case "R": case "SSEN": case "M": case "N": // Name
+ parts[i] = funcall(name_cls[ob_nr], casus, 1); break;
+ case "RU": case "SSENU": case "MU": case "NU": // unbestimmt
+ parts[i] = funcall(name_cls[ob_nr], casus); break;
+ case "RQP": case "SSENQP": case "MQP": case "NQP": // Pronoun
+ if (objectp(tmp = obs[ob_nr]))
+ parts[i] = (string)tmp->QueryPronoun(casus);
+ break;
+ case "RQA": case "SSENQA": case "MQA": case "NQA": // Article
+ if (objectp(tmp = obs[ob_nr]))
+ tmp = (string)tmp->QueryArticle(casus, 1, 1);
+ if (stringp(tmp) && !(tmp[<1]^' '))
+ tmp = tmp[0..<2]; // Extra-Space wieder loeschen
+ break;
+ case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
+ if (objectp(tmp = obs[ob_nr]))
+ parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, SINGULAR);
+ break;
+ case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
+ if (objectp(tmp = obs[ob_nr]))
+ parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
+ break;
+ case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
+ if (objectp(tmp = obs[ob_nr]))
+ parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
+ break;
+ case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
+ if (objectp(tmp = obs[ob_nr]))
+ parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, PLURAL);
+ break;
+ case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
+ if (objectp(tmp = obs[ob_nr]))
+ parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
+ break;
+ case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
+ if (objectp(tmp = obs[ob_nr]))
+ parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
+ break;
+ default:
+ continue;
+ }
+
+ // wenn tmp ein String war, weisen wir es hier pauschal zu
+ if (stringp(tmp))
+ parts[i] = tmp;
+
+ // auf Wunsch wird nach Satzenden gross geschrieben
+ if (caps)
+ switch (parts[i-1][<2..]) {
+ case ". ": case "! ": case "? ":
+ case ".": case "!": case "?":
+ case ".\n": case "!\n": case "?\n":
+ case "\" ": case "\"\n":
+ parts[i] = capitalize(parts[i]);
+ break;
+ }
+ }
+ return implode(parts, "");
+ }
+ return str;
+}
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+deprecated varargs string extract(string str, int from, int to) {
+
+ if(!stringp(str)) {
+ set_this_object(previous_object());
+ raise_error(sprintf("Bad argument 1 to extract(): %O",str));
+ }
+ if (intp(from) && intp(to)) {
+ if (from>=0 && to>=0)
+ return(str[from .. to]);
+ else if (from>=0 && to<0)
+ return(str[from .. <abs(to)]);
+ else if (from<0 && to>=0)
+ return(str[<abs(from) .. to]);
+ else
+ return(str[<abs(from) .. <abs(to)]);
+ }
+ else if (intp(from)) {
+ if (from>=0)
+ return(str[from .. ]);
+ else
+ return(str[<abs(from) .. ]);
+ }
+ else {
+ return(str);
+ }
+}
+#endif // !__EFUN_DEFINED__(extract)
+
+#if !__EFUN_DEFINED__(slice_array)
+deprecated varargs mixed slice_array(mixed array, int from, int to) {
+
+ if(!pointerp(array)) {
+ set_this_object(previous_object());
+ raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
+ }
+ if (intp(from) && intp(to)) {
+ if (from>=0 && to>=0)
+ return(array[from .. to]);
+ else if (from>=0 && to<0)
+ return(array[from .. <abs(to)]);
+ else if (from<0 && to>=0)
+ return(array[<abs(from) .. to]);
+ else
+ return(array[<abs(from) .. <abs(to)]);
+ }
+ else if (intp(from)) {
+ if (from>=0)
+ return(array[from .. ]);
+ else
+ return(array[<abs(from) .. ]);
+ }
+ else {
+ return(array);
+ }
+}
+#endif // !__EFUN_DEFINED__(slice_array)
+
+#if !__EFUN_DEFINED__(member_array)
+deprecated int member_array(mixed item, mixed arraystring) {
+
+ if (pointerp(arraystring)) {
+ return(efun::member(arraystring,item));
+ }
+ else if (stringp(arraystring)) {
+ return(efun::member(arraystring,to_int(item)));
+ }
+ else {
+ set_this_object(previous_object());
+ raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
+ }
+}
+#endif // !__EFUN_DEFINED__(member_array)
+
+// The digit at the i'th position is the number of bits set in 'i'.
+string count_table =
+ "0112122312232334122323342334344512232334233434452334344534454556";
+int broken_count_bits( string s ) {
+ int i, res;
+ if( !stringp(s) || !(i=sizeof(s)) ) return 0;
+ for( ; i-->0; ) {
+ // We are counting 6 bits at a time using a precompiled table.
+ res += count_table[(s[i]-' ')&63]-'0';
+ }
+ return res;
+}
+
+#if !__EFUN_DEFINED__(count_bits)
+int count_bits( string s ) {
+ return(broken_count_bits(s));
+}
+#endif
+
+
+// * Teile aus einem Array entfernen *** OBSOLETE
+deprecated mixed *exclude_array(mixed *arr,int from,int to)
+{
+ if (to<from)
+ to = from;
+ return arr[0..from-1]+arr[to+1..];
+}
+