blob: 7bf096c66bef6408bb5f986b2bd2824eb8e3031b [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// simul_efun.c -- simul efun's
4//
5// $Id: simul_efun.c 7408 2010-02-06 00:27:25Z Zesstra $
6#pragma strict_types,save_types,rtt_checks
7#pragma no_clone,no_shadow,no_inherit
8#pragma pedantic,range_check,warn_deprecated
9#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
10
11// Absolute Pfade erforderlich - zum Zeitpunkt, wo der Master geladen
12// wird, sind noch keine Include-Pfade da ...
13
14#define SNOOPLOGFILE "SNOOP"
15#define ASNOOPLOGFILE "ARCH/SNOOP"
16
17#include "/secure/config.h"
18#include "/secure/wizlevels.h"
19#include "/sys/snooping.h"
20#include "/sys/language.h"
21#include "/sys/thing/properties.h"
22#include "/sys/wizlist.h"
23#include "/sys/erq.h"
24#include "/sys/lpctypes.h"
25#include "/sys/daemon.h"
26#include "/sys/player/base.h"
27#include "/sys/thing/description.h"
28#include "/sys/container.h"
29#include "/sys/defines.h"
30#include "/sys/telnet.h"
31#include "/sys/objectinfo.h"
32#include "/sys/files.h"
33#include "/sys/strings.h"
34#include "/sys/time.h"
35#include "/sys/lpctypes.h"
36#include "/sys/notify_fail.h"
37#include "/sys/tls.h"
38#include "/sys/input_to.h"
39#include "/sys/object_info.h"
40
41/* function prototypes
42 */
43string dtime(int wann);
44varargs int log_file(string file, string txt, int size_to_break);
45int query_wiz_level(mixed player);
46nomask varargs int snoop(object me, object you);
47varargs string country(mixed ip, string num);
48int query_wiz_grp(mixed wiz);
49public varargs object deep_present(mixed what, object ob);
50nomask int secure_level();
51nomask string secure_euid();
52public nomask int process_call();
53nomask mixed __create_player_dummy(string name);
54varargs string replace_personal(string str, mixed *obs, int caps);
55
56
57//replacements for dropped efuns in LD
58#if !__EFUN_DEFINED__(extract)
59varargs string extract(string str, int from, int to);
60#endif
61#if !__EFUN_DEFINED__(slice_array)
62varargs mixed slice_array(mixed array, int from, int to);
63#endif
64#if !__EFUN_DEFINED__(member_array)
65int member_array(mixed item, mixed arraystring);
66#endif
67
68// Include the different sub 'modules' of the simul_efun
69#include __DIR__"debug_info.c"
70#include __DIR__"enable_commands.c"
71#include __DIR__"hash.c"
72#include __DIR__"object_info.c"
73#include __DIR__"query_editing.c"
74#include __DIR__"query_idle.c"
75#include __DIR__"query_input_pending.c"
76#include __DIR__"query_ip_name.c"
77#include __DIR__"query_limits.c"
78#include __DIR__"query_load_average.c"
79#include __DIR__"query_mud_port.c"
80#include __DIR__"query_once_interactive.c"
81#include __DIR__"query_snoop.c"
82#include __DIR__"set_heart_beat.c"
83#if __BOOT_TIME__ < 1456261859
84#include __DIR__"set_prompt.c"
85#endif
86#include __DIR__"shadow.c"
87#include __DIR__"livings.c"
88#include __DIR__"comm.c"
89
90#define TO efun::this_object()
91#define TI efun::this_interactive()
92#define TP efun::this_player()
93#define PO efun::previous_object(0)
94#define LEVEL(x) query_wiz_level(x)
95#define NAME(x) capitalize(getuid(x))
96
97#define DEBUG(x) if (find_player("zesstra")) \
98 tell_object(find_player("zesstra"),x)
99
100mixed dtime_cache = ({-1,""});
101
102#ifdef IOSTATS
103struct iostat_s {
104 string oname;
105 int time;
106 int size;
107};
108mixed saveo_stat = ({ 0,allocate(200, 0) });
109mixed restoreo_stat = ({ 0,allocate(200,0) });
110//mixed writefile_stat = ({ 0,allocate(100,(<iostat_s>)) });
111//mixed readfile_stat = ({ 0,allocate(100,(<iostat_s>)) });
112//mixed log_stat = ({ 0,allocate(100,(<iostat_s>)) });
113
114mixed ___iostats(int type) {
115 switch(type) {
116 case 1:
117 return saveo_stat;
118 case 2:
119 return restoreo_stat;
120/* case 3:
121 return writefile_stat;
122 case 4:
123 return readfile_stat;
124 case 5:
125 return log_stat;
126 */
127 }
128 return 0;
129}
130#endif
131
132// Nicht jeder Magier muss die simul_efun entsorgen koennen.
133string NotifyDestruct(object caller) {
134 if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
135 return "Du darfst das simul_efun Objekt nicht zerstoeren!\n";
136 }
137 return 0;
138}
139
140public nomask void remove_interactive( object ob )
141{
142 if ( objectp(ob) && previous_object()
143 && object_name(previous_object())[0..7] != "/secure/"
144 && ((previous_object() != ob
145 && (ob != this_player() || ob != this_interactive()))
146 || (previous_object() == ob
147 && (this_player() && this_player() != ob
148 || this_interactive() && this_interactive() != ob)) ) )
149
150 log_file( "PLAYERDEST",
151 sprintf( "%s: %O ausgeloggt von PO %O, TI %O, TP %O\n",
152 dtime(time()), ob, previous_object(),
153 this_interactive(), this_player() ) );
154
155 efun::remove_interactive(ob);
156}
157
158
159void ___updmaster()
160{
161 object ob;
162
163 //sollte nicht jeder duerfen.
164 if (process_call() || !ARCH_SECURITY)
165 raise_error("Illegal use of ___updmaster()!");
166
167 write("Removing old master ... ");
168 foreach(string file:
169 get_dir("/secure/master/*.c",GETDIR_NAMES|GETDIR_UNSORTED|GETDIR_PATH)) {
170 if (ob = find_object(file))
171 efun::destruct(ob);
172 }
173 efun::destruct(efun::find_object("/secure/master"));
174 write("done.\nLoading again ... ");
175 load_object("/secure/master");
176
177 write("done.\n");
178}
179
180varargs string country(mixed ip, string num) {
181 mixed ret;
182
183 if(ret = (string)"/p/daemon/iplookup"->country(num || ip)) {
184 return ret;
185 } else return "???";
186}
187
188
189// * Snoopen und was dazugehoert
190static object find_snooped(object who)
191{
192 object *u;
193 int i;
194
195 for (i=sizeof(u=users())-1;i>=0;i--)
196 if (who==efun::interactive_info(u[i], II_SNOOP_NEXT))
197 return u[i];
198 return 0;
199}
200
201private string Lcut(string str) {
202 return str[5..11]+str[18..];
203}
204
205nomask varargs int snoop( object me, object you )
206{
207 int ret;
208 object snooper0, snooper, snooper2, snooper3;
209
210 if( !objectp(me) || me == you || !PO )
211 return 0;
212
213 snooper0 = find_snooped(me);
214
215 if(you){
216 if ( PO != me && query_wiz_grp(me) >= query_wiz_grp(geteuid(PO)) )
217 return 0;
218
219 if ( query_wiz_grp(me) <= query_wiz_grp(you) &&
220 !(you->QueryAllowSnoop(me)) )
221 if ( !IS_DEPUTY(me) || IS_ARCH(you) )
222 return 0;
223
224 if ( (snooper = efun::interactive_info(you, II_SNOOP_NEXT)) &&
225 query_wiz_grp(snooper) >= query_wiz_grp(me) ){
226 if ( (int)snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
227 return 0;
228
229 tell_object( snooper, sprintf( "%s snooped jetzt %s.\n",
230 me->name(WER), you->name(WER) ) );
231
232 snooper2 = me;
233
234 while ( snooper3 = interactive_info(snooper2, II_SNOOP_NEXT) ){
235 tell_object( snooper,
236 sprintf( "%s wird seinerseits von %s gesnooped.\n"
237 ,snooper2->name(WER),
238 snooper3->name(WEM) ) );
239 snooper2 = snooper3;
240 }
241
242 efun::snoop( snooper, snooper2 );
243
244 if ( efun::interactive_info(snooper2, II_SNOOP_NEXT) != snooper )
245 tell_object( snooper, sprintf( "Du kannst %s nicht snoopen.\n",
246 snooper2->name(WEN) ) );
247 else{
248 tell_object( snooper, sprintf( "Du snoopst jetzt %s.\n",
249 snooper2->name(WEN) ) );
250 if ( !IS_DEPUTY(snooper) ){
251 log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
252 dtime(time()),
253 snooper,
254 snooper2,
255 environment(snooper2) ),
256 100000 );
257 if (snooper0)
258 CHMASTER->send( "Snoop", snooper,
259 sprintf( "%s *OFF* %s (%O)",
260 capitalize(getuid(snooper)),
261 capitalize(getuid(snooper0)),
262 environment(snooper0) ) );
263
264 CHMASTER->send( "Snoop", snooper,
265 sprintf("%s -> %s (%O)",
266 capitalize(getuid(snooper)),
267 capitalize(getuid(snooper2)),
268 environment(snooper2)));
269 }
270 else{
271 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
272 dtime(time()),
273 snooper,
274 snooper2,
275 environment(snooper2) )
276 ,100000 );
277 }
278 }
279 }
280 else
281 if (snooper)
282 if ( !me->QueryProp(P_SNOOPFLAGS) & SF_LOCKED ){
283 printf( "%s wird bereits von %s gesnooped. Benutze das "
284 "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
285 you->name(WER), snooper->name(WEM) );
286 return 0;
287 }
288
289 ret = efun::snoop( me, you );
290
291 if ( !IS_DEPUTY(me) && efun::interactive_info(you, II_SNOOP_NEXT) == me){
292 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
293 Lcut(dtime(time())),
294 me, you, environment(you) ),
295 100000 );
296
297 if (snooper0)
298 CHMASTER->send( "Snoop", me,
299 sprintf( "%s *OFF* %s (%O).",
300 capitalize(getuid(me)),
301 capitalize(getuid(snooper0)),
302 environment(snooper0) ) );
303
304 CHMASTER->send( "Snoop", me, sprintf( "%s -> %s (%O).",
305 capitalize(getuid(me)),
306 capitalize(getuid(you)),
307 environment(you) ) );
308 }
309 else{
310 if ( efun::interactive_info(you, II_SNOOP_NEXT) == me ){
311 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
312 Lcut(dtime(time())),
313 me, you, environment(you) ),
314 100000 );
315 }
316 }
317
318 if ( ret && query_wiz_grp(me) <= query_wiz_grp(you) &&
319 !IS_DEPUTY(me) )
320 tell_object( you, "*** " + NAME(me) + " snoopt Dich!\n" );
321
322 return ret;
323 }
324 else {
325 if ( (me == PO ||
326 query_wiz_grp(geteuid(PO)) > query_wiz_grp(me) ||
327 (query_wiz_grp(geteuid(PO)) == query_wiz_grp(me) &&
328 efun::interactive_info(PO, II_SNOOP_NEXT) == me)) && snooper0 ){
329 if ( !IS_DEPUTY(me) ){
330 log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
331 Lcut(dtime(time())), me,
332 snooper0,
333 environment(snooper0) ),
334 100000 );
335
336 CHMASTER->send( "Snoop", me,
337 sprintf( "%s *OFF* %s (%O).",
338 capitalize(getuid(me)),
339 capitalize(getuid(snooper0)),
340 environment(snooper0) ) );
341 }
342 else{
343 log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
344 Lcut(dtime(time())), me,
345 snooper0,
346 environment(snooper0) ),
347 100000 );
348 }
349
350 return efun::snoop(me);
351 }
352 }
353 return 0;
354}
355
356
357
358// * Emulation des 'alten' explode durch das neue
359string *old_explode(string str, string del) {
360 int s, t;
361 string *strs;
362
363 if (!stringp(str)) {
364 set_this_object(previous_object());
365 raise_error(sprintf(
366 "Invalid argument 1 to old_explode()! Expected <string>, got: "
367 "%.30O\n",str));
368 }
369 if (!stringp(del)) {
370 set_this_object(previous_object());
371 raise_error(sprintf(
372 "Invalid argument 2 to old_explode()! Expected <string>, got: "
373 "%.30O\n",del));
374 }
375 if(del == "")
376 return ({str});
377 strs=efun::explode(str, del);
378 t=sizeof(strs)-1;
379 while(s<=t && strs[s++] == "");s--;
380 while(t>=0 && strs[t--] == "");t++;
381 if(s<=t)
382 return strs[s..t];
383 return ({});
384}
385
386int file_time(string path) {
387 mixed *v;
388
389 set_this_object(previous_object());
390 if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
391 return(0); //sonst
392}
393
394// * Bei 50k Groesse Log-File rotieren
395varargs int log_file(string file, string txt, int size_to_break) {
396 mixed *st;
397
398 file="/log/"+file;
399 file=implode((efun::explode(file,"/")-({".."})),"/");
400// tell_object(find_player("jof"),sprintf("LOG FILE: %O -> %O\n",previous_object(),file));
401 if (!funcall(bind_lambda(#'efun::call_other,PO),"secure/master",//')
402 "valid_write",file,geteuid(PO),"log_file",PO))
403 return 0;
404 if ( size_to_break >= 0 & (
405 sizeof(st = get_dir(file,2) ) && st[0] >= (size_to_break|MAX_LOG_SIZE)))
406 catch(rename(file, file + ".old");publish); /* No panic if failure */
407 return(write_file(file,txt));
408}
409
410// * Magier-Level abfragen
411int query_wiz_level(mixed player) {
412 return (int)"/secure/master"->query_wiz_level(player);
413}
414
415#ifdef __ALISTS__
416// * Element aus Alist loeschen (by key)
417mixed *remove_alist(mixed key,mixed *alist)
418{
419 int i,j;
420
421 if (!pointerp(alist) || !sizeof(alist))
422 return alist;
423 if (!pointerp(alist[0]))
424 {
425 if ((i=assoc(key,alist))<0)
426 return alist;
427 return alist[0..i-1]+alist[i+1..];
428 }
429 i = assoc(key,alist[0]);
430 if ((i=assoc(key,alist[0]))<0)
431 return alist;
432 alist=alist[0..];
433 for (j=sizeof(alist)-1;j>=0;j--)
434 alist[j]=alist[j][0..i-1]+alist[j][i+1..];
435 return alist;
436}
437
438// * Element aus Alist loeschen (by pos)
439mixed *exclude_alist(int i,mixed *alist)
440{
441 int j;
442 if (!pointerp(alist) || !sizeof(alist) || i<0)
443 return alist;
444 if (!pointerp(alist[0]))
445 return alist[0..i-1]+alist[i+1..];
446 alist=alist[0..]; /* Create PHYSICAL copy of alist */
447 for (j=sizeof(alist)-1;j>=0;j--)
448 alist[j]=alist[j][0..i-1]+alist[j][i+1..];
449 return alist; /* order_alist is NOT necessary - see /doc/LPC/alist */
450}
451#endif // __ALISTS__
452
453// * German version of ctime()
454#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
455#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
456 "Sep","Okt","Nov","Dez"})
457string dtime(int wann) {
458
459 if (wann == dtime_cache[0])
460 return(dtime_cache[1]);
461
462 int *lt = localtime(wann);
463 return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
464 TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]],
465 lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
466}
467
468// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
469// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
470// ein ordentliches Datum/Uhrzeit.
471#if !__EFUN_DEFINED__(strftime)
472varargs string strftime(mixed fmt, int clock, int localized) {
473 if (intp(clock) && clock >= 0)
474 return dtime(clock);
475 else if (intp(fmt) && fmt >= 0)
476 return dtime(fmt);
477
478 return dtime(time());
479}
480#endif //!__EFUN_DEFINED__(strftime)
481
482// * Shutdown mit zusaetzlichem logging
483nomask int shutdown(string reason)
484{
485 string name;
486 string obname;
487 string output;
488
489 if (!reason)
490 return 0;
491 if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
492 object_name(previous_object())!="/obj/shut" )
493 {
494 write("You have no permission to shut down the gamedriver!\n");
495 return 0;
496 }
497 if ((this_interactive())&&(name=getuid(this_interactive())))
498 {
499 name=capitalize(name);
500 filter(users(),#'tell_object,//'
501 capitalize(name)+" faehrt das Spiel herunter!\n");
502 }
503 else
504 name="ANONYMOUS";
505 if (previous_object()) obname=capitalize(getuid(previous_object()));
506 output=name;
507 if (obname && name!=obname) output=output+" ("+obname+")";
508 if (previous_object()&&object_name(previous_object())=="/obj/shut"){
509 output+=" faehrt das Spiel via Armageddon herunter.\n";
510 output=dtime(time())+": "+output;
511 log_file("GAME_LOG",output+"\n",-1);
512 efun::shutdown();
513 return 1;
514 }
515 output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
516 output+=" Grund: "+reason;
517 log_file("GAME_LOG",output+"\n",-1);
518 efun::shutdown();
519 return 1;
520}
521
522// * lowerchar
523
524int lowerchar(int char) {
525 if (char<'A' || char>'Z') return char;
526 return char+32;
527}
528
529// * upperstring
530
531string upperstring(string s)
532{
533#if __EFUN_DEFINED__(upper_case)
534 return(upper_case(s));
535#else
536 int i;
537 if (!stringp(s)) return 0;
538 for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
539 return s;
540#endif
541}
542
543// * lowerstring
544
545string lowerstring(string s)
546{
547 if (!stringp(s)) return 0;
548 return lower_case(s);
549}
550
551
552// * GD version
553string version()
554{
555 return __VERSION__;
556}
557
558// * break_string
559// stretch() -- stretch a line to fill a given width
560private string stretch(string s, int width) {
561 int len=sizeof(s);
562 if (len==width) return s;
563
564 // reine Leerzeilen, direkt zurueckgeben
565 string trimmed=trim(s,TRIM_LEFT," ");
566 if (trimmed=="") return s;
567 int start_spaces = len - sizeof(trimmed);
568
569 string* words = explode(trimmed, " ");
570 // der letzte kriegt keine Spaces
571 int word_count=sizeof(words) - 1;
572 // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
573 if (!word_count)
574 return " "*start_spaces + words[0];
575
576 int space_count = width - len;
577
578 int space_per_word=(word_count+space_count) / word_count;
579 // Anz.Woerter mit Zusatz-Space
580 int rest=(word_count+space_count) % word_count;
581 // Rest-Spaces Verteilen
582 foreach (int pos : rest) words[pos]+=" ";
583 return (" "*start_spaces) + implode( words, " "*space_per_word );
584}
585
586// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
587// eigene Funktion bekommen:
588private varargs string block_string(string s, int width, int flags) {
589 // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
590 // einem Leerzeichen begonnen.
591 // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
592 // ersetzt.
593 if ( (flags & BS_LEAVE_MY_LFS)
594 && !(flags & BS_NO_PARINDENT))
595 {
596 s = " "+regreplace(s,"\n","\n ",1);
597 }
598
599 // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
600 // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
601 // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
602 // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
603 // wieder rueckgaengig zu machen.
604 s = sprintf( "%-*=s", width, s);
605
606 string *tmp=explode(s, "\n");
607 // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
608 // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
609 // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
610 // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
611 // Ende der letzten Zeile hat), das macht der Aufrufer...
612 if (sizeof(tmp) > 1)
613 return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" )
614 + "\n" + tmp[<1];
615
616 return s;
617}
618
619public varargs string break_string(string s, int w, mixed indent, int flags)
620{
621 if ( !s || s == "" ) return "";
622
623 if ( !w ) w=78;
624
625 if( intp(indent) )
626 indent = indent ? " "*indent : "";
627
628 int indentlen=stringp(indent) ? sizeof(indent) : 0;
629
630 if (indentlen>w) {
631 set_this_object(previous_object());
632 raise_error(sprintf("break_string: indent longer %d than width %d\n",
633 indentlen,w));
634 // w=((indentlen/w)+1)*w;
635 }
636
637 if (!(flags & BS_LEAVE_MY_LFS))
638 s=regreplace( s, "\n", " ", 1 );
639
640 if ( flags & BS_SINGLE_SPACE )
641 s = regreplace( s, "(^|\n| ) *", "\\1", 1 );
642
643 string prefix="";
644 if (indentlen && flags & BS_PREPEND_INDENT) {
645 if (indentlen+sizeof(s) > w ||
646 (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
647 prefix=indent+"\n";
648 indent=(flags & BS_NO_PARINDENT) ? "" : " ";
649 indentlen=sizeof(indent);
650 }
651 }
652
653 if ( flags & BS_BLOCK ) {
654 /*
655 s = implode( map( explode( s, "\n" ),
656 #'block_string, w, indentlen, flags),
657 "" );
658 */
659 s = block_string( s , w - indentlen, flags );
660 }
661 else {
662 s = sprintf("%-1.*=s",w-indentlen,s);
663 }
664 if ( s[<1] != '\n' ) s += "\n";
665
666 if ( !indentlen ) return prefix + s;
667
668 string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
669
670 return prefix + indent +
671 regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
672 /*
673 string *buf;
674
675 buf = explode( s, "\n" );
676 return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
677 */
678}
679
680// * Elemente aus mapping loeschen - mapping vorher kopieren
681
682mapping m_copy_delete(mapping m, mixed key) {
683 return m_delete(copy(m), key);
684}
685
686// * times
687int last_reboot_time()
688{
689 return __BOOT_TIME__;
690}
691
692int first_boot_time()
693{
694 return 701517600;
695}
696
697int exist_days()
698{
699 return (((time()-first_boot_time())/8640)+5)/10;
700}
701
702// * uptime :)
703string uptime()
704{
705 int t;
706 int tmp;
707 string s;
708
709 t=time()-__BOOT_TIME__;
710 s="";
711 if (t>=86400)
712 s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
713 if (t>=3600)
714 s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
715 if (t>60)
716 s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
717 return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
718}
719
720// * Was tun bei 'dangling' lfun-closures ?
721void dangling_lfun_closure() {
722 raise_error("dangling lfun closure\n");
723}
724
725// * Sperren ausser fuer master/simul_efun
726
727#if __EFUN_DEFINED__(set_environment)
728nomask void set_environment(object o1, object o2) {
729 raise_error("Available only for root\n");
730}
731#endif
732
733nomask void set_this_player(object pl) {
734 raise_error("Available only for root\n");
735}
736
737#if __EFUN_DEFINED__(export_uid)
738nomask void export_uid(object ob) {
739 raise_error("Available only for root\n");
740}
741#endif
742
743// * Jetzt auch closures
744int process_flag;
745
746public nomask int process_call()
747{
748 if (process_flag>0)
749 return process_flag;
750 else return(0);
751}
752
753private nomask string _process_string(string str,object po) {
754 set_this_object(po);
755 return(efun::process_string(str));
756}
757
758nomask string process_string( mixed str )
759{
760 string tmp, err;
761 int flag;
762
763 if ( closurep(str) ) {
764 set_this_object( previous_object() );
765 return funcall(str);
766 }
767 else if (str==0)
768 return((string)str);
769 else if ( !stringp(str) ) {
770 return to_string(str);
771 }
772
773 if ( !(flag = process_flag > time() - 60))
774 process_flag=time();
775
776 err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
777
778 if ( !flag )
779 process_flag=0;
780
781 if (err) {
782 // Verarbeitung abbrechen
783 set_this_object(previous_object());
784 raise_error(err);
785 }
786 return tmp;
787}
788
789// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
790// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
791// zurueckgeliefert, sonst 0.
792// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
793public int mkdirp(string dir) {
794 // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
795 if (strstr(dir, "/") != 0)
796 raise_error("mkdirp(): Pfad ist nicht absolute.\n");
797 // cut off trailing /...
798 if (dir[<1]=='/')
799 dir = dir[0..<2];
800
801 int fstat = file_size(dir);
802 // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
803 if (fstat == FSIZE_DIR)
804 return 1;
805 // wenn schon ne Datei existiert, geht es nicht.
806 if (fstat != FSIZE_NOFILE)
807 return 0;
808 // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
809 // toplevel-verzeichnis, was direkt angelegt wird.
810 if (strrstr(dir,"/")==0) {
811 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
812 }
813
814 // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
815 // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
816 // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
817
818 // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
819 // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
820 if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
821 return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
822}
823
824
825// * Properties ggfs. mitspeichern
826mixed save_object(mixed name)
827{
828 mapping properties;
829 mapping save;
830 mixed index, res;
831 int i;
Zesstraf84b6272013-01-26 00:14:12 +0100832 string oldpath;
MG Mud User88f12472016-06-24 23:31:02 +0200833
834 // nur Strings und 0 zulassen
835 if ((!stringp(name) || !sizeof(name)) &&
836 (!intp(name) || name!=0)) {
837 set_this_object(previous_object());
838 raise_error(sprintf(
839 "Only non-empty strings and 0 may be used as filename in "
840 "sefun::save_object()! Argument was %O\n",name));
841 }
842
Zesstraf84b6272013-01-26 00:14:12 +0100843 if (stringp(name)) {
844 // abs. Pfad erzeugen. *seufz*
845 if (name[0]!='/')
846 name = "/" + name;
847 oldpath = name;
848 // automatisch in LIBDATADIR speichern
849 if (strstr(name,"/"LIBDATADIR"/") != 0) {
850 name = "/"LIBDATADIR + name;
851 // wenn das Verzeichnis nicht existiert, ggf. anlegen
852 string dir = name[0..strrstr(name,"/")-1];
853 if (file_size(dir) != FSIZE_DIR) {
854 if (mkdirp(dir) != 1)
855 raise_error("save_object(): kann Verzeichnis " + dir
856 + " nicht anlegen!");
857 }
858 }
859 }
860
MG Mud User88f12472016-06-24 23:31:02 +0200861 save = m_allocate(0, 2);
862 properties = (mapping)previous_object()->QueryProperties();
863
864 if(mappingp(properties))
865 {
866 // delete all entries in mapping properties without SAVE flag!
867 index = m_indices(properties);
868 for(i = sizeof(index)-1; i>=0;i--)
869 {
870 if(properties[index[i], F_MODE] & SAVE)
871 {
872 save[index[i]] = properties[index[i]];
873 save[index[i], F_MODE] =
874 properties[index[i], F_MODE] &
875 (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
876 }
877 }
878 }
879 else save = ([]);
880
881 // save object!
882 previous_object()->_set_save_data(save);
883 // format: wie definiert in config.h
Zesstraf84b6272013-01-26 00:14:12 +0100884 if (stringp(name)) {
MG Mud User88f12472016-06-24 23:31:02 +0200885 res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
886 __LIB__SAVE_FORMAT_VERSION__);
Zesstraf84b6272013-01-26 00:14:12 +0100887 // wenn erfolgreich und noch nen Savefile existiert, was nicht unter
888 // /data/ liegt, wird das geloescht.
889 if (!res && oldpath
890 && file_size(oldpath+".o") >= 0)
891 rm(oldpath+".o");
892 }
MG Mud User88f12472016-06-24 23:31:02 +0200893 else
894 res = funcall(bind_lambda(#'efun::save_object, previous_object()),
895 __LIB__SAVE_FORMAT_VERSION__);
Zesstraf84b6272013-01-26 00:14:12 +0100896
MG Mud User88f12472016-06-24 23:31:02 +0200897 previous_object()->_set_save_data(0);
898
899#ifdef IOSTATS
900 // Stats...
901 struct iostat_s stat = (<iostat_s>);
902 stat->oname = object_name(previous_object());
903 stat->time = time();
904 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
905 // OIM_TOTAL_DATA_SIZE);
906 if (stringp(name))
907 stat->size = file_size(name + ".o");
908 else
909 stat->sizeof(res);
910 //debug_message("saveo: "+saveo_stat[0]+"\n");
911 saveo_stat[1][saveo_stat[0]] = stat;
912 saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
913 //debug_message("saveo 2: "+saveo_stat[0]+"\n");
914#endif
915
916 return res;
917}
918
919// * Auch Properties laden
920int restore_object(string name)
921{
922 int result;
923 mixed index;
924 mixed save;
925 mapping properties;
926 int i;
927 closure cl;
928
Zesstraf84b6272013-01-26 00:14:12 +0100929 // abs. Pfad erzeugen. *seufz*
930 if (name[0]!='/')
931 name = "/" + name;
932
933 // wenn kein /data/ vorn steht, erstmal gucken, ob das Savefile unter /data/
934 // existiert. Wenn ja, wird das geladen.
935 if (strstr(name,"/"LIBDATADIR"/") != 0) {
936 string path = "/"LIBDATADIR + name;
937 if (file_size(path + ".o") >= 0)
938 name = path;
939 }
940
MG Mud User88f12472016-06-24 23:31:02 +0200941 // get actual property settings (by create())
942 properties = (mapping)previous_object()->QueryProperties();
943
944// DEBUG(sprintf("RESTORE %O\n",name));
945 // restore object
946 result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
947 //'))
948 //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein
949 //mapping sein sollte.
950 save = (mixed)previous_object()->_get_save_data();
951 if(mappingp(save))
952 {
953 index = m_indices(save);
954 for(i = sizeof(index)-1; i>=0; i--)
955 {
956 properties[index[i]] = save[index[i]];
957 properties[index[i], F_MODE] = save[index[i], F_MODE]
958 &~(SETCACHED|QUERYCACHED);
959 }
960 }
961 else properties = ([]);
962
963 // restore properties
964 funcall(
965 bind_lambda(
966 unbound_lambda(({'arg}), //'})
967 ({#'call_other,({#'this_object}),
968 "SetProperties",'arg})),//')
969 previous_object()),properties);
970 previous_object()->_set_save_data(0);
971
972#ifdef IOSTATS
973 // Stats...
974 //debug_message("restoreo: "+restoreo_stat[0]+"\n");
975 struct iostat_s stat = (<iostat_s>);
976 stat->oname = object_name(previous_object());
977 stat->time = time();
978 //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
979 // OIM_TOTAL_DATA_SIZE);
980 stat->size = file_size(name + ".o");
981 restoreo_stat[1][restoreo_stat[0]] = stat;
982
983 restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
984#endif
985
986 return result;
987}
988
989// * HB eines Objektes ein/ausschalten
990int set_object_heart_beat(object ob, int flag)
991{
992 if (objectp(ob))
993 return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
994}
995
996// * Magierlevelgruppen ermitteln
997int query_wiz_grp(mixed wiz)
998{
999 int lev;
1000
1001 lev=query_wiz_level(wiz);
1002 if (lev<SEER_LVL) return 0;
1003 if (lev>=GOD_LVL) return lev;
1004 if (lev>=ARCH_LVL) return ARCH_GRP;
1005 if (lev>=ELDER_LVL) return ELDER_GRP;
1006 if (lev>=LORD_LVL) return LORD_GRP;
1007 if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
1008 if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
1009 if (lev>=WIZARD_LVL) return WIZARD_GRP;
1010 if (lev>=LEARNER_LVL) return LEARNER_GRP;
1011 return SEER_GRP;
1012}
1013
1014mixed *wizlist_info()
1015{
1016 if (ARCH_SECURITY || !extern_call())
1017 return efun::wizlist_info();
1018 return 0;
1019}
1020
1021// * wizlist ausgeben
1022varargs void wizlist(string name, int sortkey ) {
1023
1024 if (!name)
1025 {
1026 if (this_player())
1027 name = getuid(this_player());
1028 if (!name)
1029 return;
1030 }
1031
1032 // Schluessel darf nur in einem gueltigen Bereich sein
1033 if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
1034
1035 mixed** wl = efun::wizlist_info();
1036 // nach <sortkey> sortieren
1037 wl = sort_array(wl, function int (mixed a, mixed b)
1038 {return a[sortkey] < b[sortkey]; } );
1039
1040 // Summe ueber alle Kommandos ermitteln.
1041 int total_cmd, i;
1042 int pos=-1;
1043 foreach(mixed entry : wl)
1044 {
1045 total_cmd += entry[WL_COMMANDS];
1046 if (entry[WL_NAME] == name)
1047 pos = i;
1048 ++i;
1049 }
1050
1051 if (pos < 0 && name != "ALL" && name != "TOP100")
1052 return;
1053
1054 if (name == "TOP100")
1055 {
1056 if (sizeof(wl) > 100)
1057 wl = wl[0..100];
1058 else
1059 wl = wl;
1060 }
1061 // um name herum schneiden
1062 else if (name != "ALL")
1063 {
1064 if (sizeof(wl) <= 21)
1065 wl = wl;
1066 else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
1067 wl = wl[pos-10..pos+10];
1068 else if (pos <=21)
1069 wl = wl[0..20];
1070 else if (pos >= sizeof(wl) - 21)
1071 wl = wl[<21..];
1072 else
1073 wl = wl;
1074 }
1075
1076 write("\nWizard top score list\n\n");
1077 if (total_cmd == 0)
1078 total_cmd = 1;
1079 printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
1080 "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
1081 foreach(mixed e: wl)
1082 {
1083 printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
1084 e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
1085 e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
1086 e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
1087 );
1088 }
1089 printf("\nTotal %7d (%d)\n\n", total_cmd, sizeof(wl));
1090}
1091
1092
1093// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
1094
1095// ermittelt das Objekt des Callouts.
1096private object _call_out_obj( mixed call_out ) {
1097 return pointerp(call_out) ? call_out[0] : 0;
1098}
1099
1100private void _same_object( object ob, mapping m ) {
1101 // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
1102 if ( m[ob] )
1103 m[ob] += ({ ob });
1104 else
1105 m[ob] = ({ ob });
1106}
1107
1108// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
1109// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
1110// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
1111private void _same_path( object key, object *obs, mapping m ) {
1112 string path;
1113 if (!objectp(key) || !pointerp(obs)) return;
1114
1115 path = load_name(key);
1116
1117 if ( m[path] )
1118 m[path] += obs;
1119 else
1120 m[path] = obs;
1121}
1122
1123// key kann object oder string sein.
1124private int _too_many( mixed key, mapping m, int i ) {
1125 return sizeof(m[key]) >= i;
1126}
1127
1128// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
1129// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
1130// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
1131private void _destroy( mixed key, object *obs, string text, int uid ) {
1132 if (!pointerp(obs)) return;
1133 // Array mit unique Eintraege erzeugen.
1134 obs = m_indices( mkmapping(obs) );
1135 // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
1136 // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
1137 // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
1138 // laeuft.
1139 catch( efun::raise_error(
1140 sprintf( text,
1141 uid ? (string)master()->creator_file(key) : key,
1142 sizeof(obs), object_name(obs[<1]) ) );publish);
1143 // Und weg mit dem Kram...
1144 filter( obs, #'efun::destruct/*'*/ );
1145}
1146
1147// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
1148// sind dabei nicht unique.
1149private void _same_uid( string key, object *obs, mapping m, closure cf ) {
1150 string uid;
1151
1152 if ( !pointerp(obs) || !sizeof(obs) )
1153 return;
1154
1155 uid = funcall( cf, key );
1156
1157 if ( m[uid] )
1158 m[uid] += obs; // obs ist nen Array
1159 else
1160 m[uid] = obs;
1161}
1162
1163private int _log_call_out(mixed co)
1164{
1165 log_file("TOO_MANY_CALLOUTS",
1166 sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
1167 200000);
1168 return 0;
1169}
1170
1171private int last_callout_log=0;
1172
1173nomask varargs void call_out( varargs mixed *args )
1174{
1175 mixed tmp, *call_outs;
1176
1177 // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
1178 // haben.
1179 if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
1180 && geteuid(previous_object()) != ROOTID )
1181 {
1182 // Log erzeugen...
1183 if (last_callout_log <
1184 efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
1185 && get_eval_cost() > 200000)
1186 {
1187 last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1188 log_file("TOO_MANY_CALLOUTS",
1189 sprintf("\n%s: ############ Too many callouts: %d ##############\n",
1190 strftime("%y%m%d-%H%M%S"),
1191 efun::driver_info(DI_NUM_CALLOUTS)));
1192 filter(efun::call_out_info(), #'_log_call_out);
1193 }
1194 // Objekte aller Callouts ermitteln
1195 call_outs = map( efun::call_out_info(), #'_call_out_obj );
1196 mapping objectmap = ([]);
1197 filter( call_outs, #'_same_object, &objectmap );
1198 // Master nicht grillen...
1199 efun::m_delete( objectmap, master(1) );
1200 // alle Objekte raussuchen, die zuviele haben...
1201 mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
1202 // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
1203 // Objekten uebergeben (in diesem Fall sind Keys und Array mit
1204 // Objekten jeweils das gleiche Objekt).
1205 if ( sizeof(res) )
1206 walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "
1207 "object [%O]. Destructed %d objects. [%s]\n", 0 );
1208
1209 // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
1210 // Objekte killen, die mehr als 50 Callouts laufen haben - und
1211 // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
1212 // Clones einer BP gekillt, die Callouts laufen haben, falls alle
1213 // diese Objekte _zusammen_ mehr als 50 Callouts haben!
1214 if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
1215 // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
1216 // mehr drin, da sie dort als Keys verwendet wurden.
1217 mapping pathmap=([]);
1218 // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
1219 // von Objekten als Werte.
1220 walk_mapping( objectmap, #'_same_path, &pathmap);
1221 // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
1222 res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
1223 // und ueber alle Keys gehen, an _destroy() werden die Clones
1224 // uebergeben, die Callouts haben.
1225 if ( sizeof(res) )
1226 walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
1227 "'%s'. Destructed %d objects. [%s]\n", 0 );
1228
1229 // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
1230 // noch >1000 Callouts laufen, werden diesmal alle Callouts
1231 // einer UID zusammengezaehlt.
1232 // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
1233 // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
1234 if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
1235 {
1236 // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
1237 // zusammensortieren. Zerstoerte Clones filter _same_uid()
1238 // raus.
1239 mapping uidmap=([]);
1240 walk_mapping( pathmap, #'_same_uid, &uidmap,
1241 symbol_function( "creator_file",
1242 "/secure/master" ) );
1243 // In res nun UIDs als Keys und Arrays von Objekten als Werte.
1244 // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
1245 // 100 Callouts!) haben.
1246 res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
1247 // und erneut ueber die Keys laufen und jeweils die Arrays mit
1248 // den Objekten zur Zerstoerung an _destroy()...
1249 if ( sizeof(res) )
1250 walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
1251 "UID '%s'. Destructed %d objects. [%s]\n",
1252 1 );
1253 }
1254 }
1255 }
1256
1257 // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
1258 if ( !previous_object() )
1259 return;
1260
1261 set_this_object( previous_object() );
1262 apply( #'efun::call_out, args );
1263 return;
1264}
1265
1266mixed call_out_info() {
1267
1268 object po = previous_object();
1269 mixed coi = efun::call_out_info();
1270
1271 // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
1272 // /obj haben die BackboneID.
1273 if (query_wiz_level(getuid(po)) >= ARCH_LVL
1274 || (string)master()->creator_file(load_name(po)) == BACKBONEID ) {
1275 return coi;
1276 }
1277 else {
1278 return filter(coi, function mixed (mixed arr) {
1279 if (pointerp(arr) && arr[0]==po)
1280 return 1;
1281 else return 0; });
1282 }
1283}
1284
1285// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
1286// NICHT das Objekt, was ggf. die lfun definiert!
1287mixed query_closure_object(closure c) {
1288 return
1289 CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
1290 0 :
1291 (to_object(c) || -1);
1292}
1293
1294// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
1295varargs void move_object(mixed what, mixed where)
1296{
1297 object po,tmp;
1298
1299 po=previous_object();
1300 if (!where)
1301 {
1302 where=what;
1303 what=po;
1304 }
1305 if (((stringp(where) && where==NETDEAD_ROOM ) ||
1306 (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
1307 objectp(what) && object_name(what)!="/obj/sperrer")
1308 {
1309 if (!query_once_interactive(what))
1310 {
1311 what->remove();
1312 if (what) destruct(what);
1313 return;
1314 }
1315 if (living(what) || interactive(what))
1316 {
1317 log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
1318 return;
1319 }
1320 set_object_heart_beat(what,0);
1321 }
1322 tmp=what;
1323 while (tmp=environment(tmp))
1324 // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
1325 // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
1326 // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
1327 // da um jedes bisschen Rechenzeit geht.
1328 // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
1329 //
1330 // Tiamak
1331 tmp->_set_last_content_change();
1332 funcall(bind_lambda(#'efun::move_object,po),what,where);
1333 if (tmp=what)
1334 while (tmp=environment(tmp))
1335 tmp->_set_last_content_change();
1336}
1337
1338
1339void start_simul_efun() {
1340 mixed *info;
1341
1342 // Falls noch nicht getan, extra_wizinfo initialisieren
1343 if ( !pointerp(info = get_extra_wizinfo(0)) )
1344 set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
1345
1346 InitLivingData(info);
1347
1348 set_next_reset(10); // direkt mal aufraeumen
1349}
1350
1351protected void reset() {
1352 set_next_reset(7200);
1353 CleanLivingData();
1354}
1355
1356#if !__EFUN_DEFINED__(absolute_hb_count)
1357int absolute_hb_count() {
1358 return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
1359}
1360#endif
1361
1362void __set_environment(object ob, mixed target)
1363{
1364 string path;
1365 object obj;
1366
1367 if (!objectp(ob))
1368 return;
1369 if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
1370 return;
1371 if (objectp(target))
1372 {
1373 efun::set_environment(ob,target);
1374 return;
1375 }
1376 path=(string)MASTER->_get_path(target,this_interactive());
1377 if (stringp(path) && file_size(path+".c")>=0 &&
1378 !catch(load_object(path);publish) )
1379 {
1380 obj=find_object(path);
1381 efun::set_environment(ob,obj);
1382 return;
1383 }
1384}
1385
1386void _dump_wizlist(string file, int sortby) {
1387 int i;
1388 mixed *a;
1389
1390 if (!LORD_SECURITY)
1391 return;
1392 if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
1393 {
1394 write("NO WRITE PERMISSION\n");
1395 return;
1396 }
1397 a = wizlist_info();
1398 a = sort_array(a, lambda( ({'a,'b}),
1399 ({#'<,
1400 ({#'[,'a,sortby}),
1401 ({#'[,'b,sortby})
1402 })));
1403 rm(file);
1404 for (i=sizeof(a)-1;i>=0;i--)
1405 write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
1406 a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
1407 a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
1408}
1409
1410public varargs object deep_present(mixed what, object ob) {
1411
1412 if(!objectp(ob))
1413 ob=previous_object();
1414 // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
1415 // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
1416 if(objectp(what)) {
1417 object *envs=all_environment(what);
1418 // wenn ob kein Environment hat, ist es offensichtlich nicht in what
1419 // enthalten.
1420 if (!pointerp(envs)) return 0;
1421 if (member(envs, ob) != -1) return what;
1422 }
1423 // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
1424 // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
1425 // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
1426 else if (stringp(what)) {
1427 int cnt;
1428 string newwhat;
1429 if(sscanf(what,"%s %d",newwhat,cnt)!=2)
1430 cnt=1;
1431 else
1432 what=newwhat;
1433 foreach(object invob: deep_inventory(ob)) {
1434 if (invob->id(what) && !--cnt)
1435 return invob;
1436 }
1437 }
1438 else {
1439 set_this_object(previous_object());
1440 raise_error(sprintf("Wrong argument 1 to deep_present(). "
1441 "Expected \"object\" or \"string\", got %.50O.\n",
1442 what));
1443 }
1444 return 0;
1445}
1446
1447mapping dump_ip_mapping()
1448{
1449 return 0;
1450}
1451
1452nomask void swap(object obj)
1453{
1454 write("Your are not allowed to swap objects by hand!\n");
1455 return;
1456}
1457
1458nomask varargs void garbage_collection(string str)
1459{
1460 if(previous_object()==0 || !IS_ARCH(geteuid(previous_object()))
1461 || !ARCH_SECURITY)
1462 {
1463 write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
1464 return;
1465 }
1466 else if (stringp(str))
1467 {
1468 return efun::garbage_collection(str);
1469 }
1470 else
1471 return efun::garbage_collection();
1472}
1473
1474varargs void notify_fail(mixed nf, int prio) {
1475 object po,oldo;
1476 int oldprio;
1477
1478 if (!PL || !objectp(po=previous_object())) return;
1479 if (!stringp(nf) && !closurep(nf)) {
1480 set_this_object(po);
1481 raise_error(sprintf(
1482 "Only strings and closures allowed for notify_fail! "
1483 "Argument was: %.50O...\n",nf));
1484 }
1485
1486 // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
1487 // und vergleichen.
1488 if (objectp(oldo=query_notify_fail(1)) && po!=oldo) {
1489 if (!prio) {
1490 //Prioritaet dieses notify_fail() 'abschaetzen'
1491 if (po==PL) // Spieler-interne (soul-cmds)
1492 prio=NF_NL_OWN;
1493 else if (living(po))
1494 prio=NF_NL_LIVING;
1495 else if ((int)po->IsRoom())
1496 prio=NF_NL_ROOM;
1497 else
1498 prio=NF_NL_THING;
1499 }
1500 //Prioritaet des alten Setzers abschaetzen
1501 if (oldo==PL)
1502 oldprio=NF_NL_OWN;
1503 else if (living(oldo))
1504 oldprio=NF_NL_LIVING;
1505 else if ((int)oldo->IsRoom())
1506 oldprio=NF_NL_ROOM;
1507 else
1508 oldprio=NF_NL_THING;
1509 }
1510 else // wenn es noch kein Notify_fail gibt:
1511 oldprio=NF_NL_NONE;
1512
1513 //vergleichen und ggf. setzen
1514 if (prio >= oldprio) {
1515 set_this_object(po);
1516 efun::notify_fail(nf);
1517 }
1518
1519 return;
1520}
1521
1522void _notify_fail(string str)
1523{
1524 //query_notify_fail() benutzen, um das Objekt
1525 //des letzten notify_fail() zu ermitteln
1526 object o;
1527 if ((o=query_notify_fail(1)) && o!=previous_object())
1528 return;
1529 //noch kein notify_fail() fuer dieses Kommando gesetzt, auf gehts.
1530 set_this_object(previous_object());
1531 efun::notify_fail(str);
1532 return;
1533}
1534
1535string time2string( string format, int zeit )
1536{
1537 int i,ch,maxunit,dummy;
1538 string *parts, fmt;
1539
1540 int secs = zeit;
1541 int mins = (zeit/60);
1542 int hours = (zeit/3600);
1543 int days = (zeit/86400);
1544 int weeks = (zeit/604800);
1545 int months = (zeit/2419200);
1546 int abbr = 0;
1547
1548 parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
1549
1550 for( i=1; i<sizeof(parts); i+=2 )
1551 {
1552 ch = parts[i][<1];
1553 switch( parts[i][<1] )
1554 {
1555 case 'x': case 'X':
1556 abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
1557 // NO break !
1558 case 'n': case 'N':
1559 maxunit |= 31;
1560 break;
1561 case 'w': case 'W':
1562 maxunit |= 15;
1563 break;
1564 case 'd': case 'D':
1565 maxunit |= 7;
1566 break;
1567 case 'h': case 'H':
1568 maxunit |= 3;
1569 break;
1570 case 'm': case 'M':
1571 maxunit |= 1;
1572 break;
1573 }
1574 }
1575 if( maxunit & 16 ) weeks %= 4;
1576 if( maxunit & 8 ) days %= 7;
1577 if( maxunit & 4 ) hours %= 24;
1578 if( maxunit & 2 ) mins %= 60;
1579 if( maxunit ) secs %= 60;
1580
1581 for( i=1; i<sizeof(parts); i+=2 )
1582 {
1583 fmt = parts[i][0..<2];
1584 ch = parts[i][<1];
1585 if( ch=='x' )
1586 {
1587 if (months > 0) ch='n';
1588 else if( weeks>0 ) ch='w';
1589 else if( days>0 ) ch='d';
1590 else if( hours>0 ) ch='h';
1591 else if(mins > 0) ch = 'm';
1592 else ch = 's';
1593 }
1594 else if( ch=='X' )
1595 {
1596 if (months > 0) ch='N';
1597 else if( weeks>0 ) ch='W';
1598 else if( days>0 ) ch='D';
1599 else if( hours>0 ) ch='H';
1600 else if(mins > 0) ch = 'M';
1601 else ch = 'S';
1602 }
1603
1604 switch( ch )
1605 {
1606 case 'n': parts[i] = sprintf( fmt+"d", months ); break;
1607 case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
1608 case 'd': parts[i] = sprintf( fmt+"d", days ); break;
1609 case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
1610 case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
1611 case 's': parts[i] = sprintf( fmt+"d", secs ); break;
1612 case 'N':
1613 if(abbr) parts[i] = "M";
1614 else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
1615 break;
1616 case 'W':
1617 if(abbr) parts[i] = "w"; else
1618 parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
1619 break;
1620 case 'D':
1621 if(abbr) parts[i] = "d"; else
1622 parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
1623 break;
1624 case 'H':
1625 if(abbr) parts[i] = "h"; else
1626 parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
1627 break;
1628 case 'M':
1629 if(abbr) parts[i] = "m"; else
1630 parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
1631 break;
1632 case 'S':
1633 if(abbr) parts[i] = "s"; else
1634 parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
1635 break;
1636 case '%':
1637 parts[i] = "%";
1638 break;
1639 }
1640 }
1641 return implode( parts, "" );
1642}
1643
1644nomask mixed __create_player_dummy(string name)
1645{
1646 string err;
1647 object ob;
1648 mixed m;
1649 //hat nen Scherzkeks die Blueprint bewegt?
1650 if ((ob=find_object("/secure/login")) && environment(ob))
1651 catch(destruct(ob);publish);
1652 err = catch(ob = clone_object("secure/login");publish);
1653 if (err)
1654 {
1655 write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
1656 return 0;
1657 }
1658 if (objectp(m=(mixed)ob->new_logon(name))) netdead[name]=m;
1659 return m;
1660}
1661
1662nomask int secure_level()
1663{
1664 int *level;
1665 //kette der Caller durchlaufen, den niedrigsten Level in der Kette
1666 //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
1667 //von 0.
1668 //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
1669 //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
1670 //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
1671 //INteractive geben muss.
1672 level=map(caller_stack(1),function int (object caller)
1673 {if (objectp(caller))
1674 return(query_wiz_level(geteuid(caller)));
1675 return(0); // kein Objekt da, 0.
1676 } );
1677 return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
1678}
1679
1680nomask string secure_euid()
1681{
1682 string euid;
1683
1684 if (!this_interactive()) // Es muss einen interactive geben
1685 return 0;
1686 euid=geteuid(this_interactive());
1687 // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
1688 // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
1689 // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
1690 foreach(object caller: caller_stack()) {
1691 if (!objectp(caller) ||
1692 (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
1693 return 0;
1694 }
1695 return euid; // 'sichere' euid zurueckgeben
1696}
1697
1698// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
1699// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
1700//#if __BOOT_TIME__ < 1360017213
1701varargs void input_to( mixed fun, int flags, varargs mixed *args )
1702{
1703 mixed *arr;
1704 int i;
1705
1706 if ( !this_player() || !previous_object() )
1707 return;
1708
1709 // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
1710 // vorkommt...
1711 if ( flags&INPUT_PROMPT ) {
1712 arr = ({ fun, flags }) + args;
1713 }
1714 else {
1715 // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
1716 flags |= INPUT_PROMPT;
1717 arr = ({ fun, flags, "" }) + args;
1718 }
1719
1720 // Arrays gegen flatten quoten.
1721 for ( i = sizeof(arr) - 1; i > 1; i-- )
1722 if ( pointerp(arr[i]) )
1723 arr[i] = quote(arr[i]);
1724
1725 apply( bind_lambda( unbound_lambda( ({}),
1726 ({ #'efun::input_to/*'*/ }) + arr ),
1727 previous_object() ) );
1728}
1729//#endif
1730
1731nomask int set_light(int i)
1732// erhoeht das Lichtlevel eines Objekts um i
1733// result: das Lichtlevel innerhalb des Objekts
1734{
1735 object ob, *inv;
1736 int lall, light, dark, tmp;
1737
1738 if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
1739
1740 // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
1741 if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
1742
1743 // Lichtberechnung findet eigentlich in der Mudlib statt.
1744 return (int)ob->QueryProp(P_INT_LIGHT);
1745}
1746
1747
1748public string iso2ascii( string str )
1749{
1750 if ( !stringp(str) || !sizeof(str) )
1751 return "";
1752
1753 str = regreplace( str, "ä", "ae", 1 );
1754 str = regreplace( str, "ö", "oe", 1 );
1755 str = regreplace( str, "ü", "ue", 1 );
1756 str = regreplace( str, "Ä", "Ae", 1 );
1757 str = regreplace( str, "Ö", "Oe", 1 );
1758 str = regreplace( str, "Ü", "Ue", 1 );
1759 str = regreplace( str, "ß", "ss", 1 );
1760 str = regreplace( str, "[^ -~]", "?", 1 );
1761
1762 return str;
1763}
1764
1765
1766public varargs string CountUp( string *s, string sep, string lastsep )
1767{
1768 string ret;
1769
1770 if ( !pointerp(s) )
1771 return "";
1772
1773 if (!sep) sep = ", ";
1774 if (!lastsep) lastsep = " und ";
1775
1776 switch (sizeof(s)) {
1777 case 0: ret=""; break;
1778 case 1: ret=s[0]; break;
1779 default:
1780 ret = implode(s[0..<2], sep);
1781 ret += lastsep + s[<1];
1782 }
1783 return ret;
1784}
1785
1786nomask varargs int query_next_reset(object ob) {
1787
1788 // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
1789 if (ob && !objectp(ob))
1790 raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
1791 "expected object.\n",ob));
1792
1793 // Defaultobjekt PO, wenn 0 uebergeben.
1794 if ( !objectp(ob) )
1795 ob = previous_object();
1796
1797 return efun::object_info(ob, OI_NEXT_RESET_TIME);
1798}
1799
1800
1801#if !__EFUN_DEFINED__(copy_file)
1802#define MAXLEN 50000
1803nomask int copy_file(string source, string dest)
1804{
1805
1806 int ptr;
1807 string bytes;
1808
1809 set_this_object(previous_object());
1810 if (!sizeof(source)||!sizeof(dest)||source==dest||(file_size(source)==-1)||
1811 (!call_other(master(),"valid_read",source,
1812 getuid(this_interactive()||
1813 previous_object()),"read_file",previous_object()))||
1814 (!call_other(master(),"valid_read",source,
1815 getuid(this_interactive()||
1816 previous_object()),"write_file",previous_object())))
1817 return 1;
1818 switch (file_size(dest))
1819 {
1820 case -1:
1821 break;
1822 case -2:
1823 if (dest[<1]!='/') dest+="/";
1824 dest+=efun::explode(source,"/")[<1];
1825 if (file_size(dest)==-2) return 1;
1826 if (file_size(dest)!=-1) break;
1827 default:
1828 if (!rm(dest)) return 1;
1829 break;
1830 }
1831 do
1832 {
1833 bytes = read_bytes(source, ptr, MAXLEN); ptr += MAXLEN;
1834 if (!bytes) bytes="";
1835 write_file(dest, bytes);
1836 }
1837 while(sizeof(bytes) == MAXLEN);
1838 return 0;
1839}
1840#endif //!__EFUN_DEFINED__(copy_file)
1841
1842
1843// ### Ersatzaufloesung in Strings ###
1844varargs string replace_personal(string str, mixed *obs, int caps) {
1845 int i;
1846 string *parts;
1847
1848 parts = regexplode(str, "@WE[A-SU]*[0-9]");
1849 i = sizeof(parts);
1850
Zesstra19102132016-09-01 22:50:36 +02001851 if (i>1)
1852 {
MG Mud User88f12472016-06-24 23:31:02 +02001853 int j, t;
1854 closure *name_cls;
1855
1856 t = j = sizeof(obs);
1857
1858 name_cls = allocate(j);
1859 while (j--)
1860 if (objectp(obs[j]))
1861 name_cls[j] = symbol_function("name", obs[j]);
1862 else if (stringp(obs[j]))
1863 name_cls[j] = obs[j];
1864
Zesstra19102132016-09-01 22:50:36 +02001865 while ((i-= 2)>0)
1866 {
MG Mud User88f12472016-06-24 23:31:02 +02001867 int ob_nr;
1868 // zu ersetzendes Token in Fall und Objektindex aufspalten
1869 ob_nr = parts[i][<1]-'1';
1870 if (ob_nr<0 || ob_nr>=t) {
1871 set_this_object(previous_object());
1872 raise_error(sprintf("replace_personal: using wrong object index %d\n",
1873 ob_nr));
1874 return implode(parts, "");
1875 }
1876
1877 // casus kann man schon hier entscheiden
1878 int casus;
1879 string part = parts[i];
1880 switch (part[3]) {
1881 case 'R': casus = WER; break;
1882 case 'S': casus = WESSEN; break;
1883 case 'M': casus = WEM; break;
1884 case 'N': casus = WEN; break;
1885 default: continue; // passt schon jetzt nicht in das Hauptmuster
1886 }
1887
1888 // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
1889 mixed tmp;
1890 switch (part[3..<2]) {
1891 case "R": case "SSEN": case "M": case "N": // Name
1892 parts[i] = funcall(name_cls[ob_nr], casus, 1); break;
1893 case "RU": case "SSENU": case "MU": case "NU": // unbestimmt
1894 parts[i] = funcall(name_cls[ob_nr], casus); break;
1895 case "RQP": case "SSENQP": case "MQP": case "NQP": // Pronoun
1896 if (objectp(tmp = obs[ob_nr]))
1897 parts[i] = (string)tmp->QueryPronoun(casus);
1898 break;
1899 case "RQA": case "SSENQA": case "MQA": case "NQA": // Article
1900 if (objectp(tmp = obs[ob_nr]))
1901 tmp = (string)tmp->QueryArticle(casus, 1, 1);
1902 if (stringp(tmp) && !(tmp[<1]^' '))
1903 tmp = tmp[0..<2]; // Extra-Space wieder loeschen
1904 break;
1905 case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
1906 if (objectp(tmp = obs[ob_nr]))
1907 parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, SINGULAR);
1908 break;
1909 case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
1910 if (objectp(tmp = obs[ob_nr]))
1911 parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
1912 break;
1913 case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
1914 if (objectp(tmp = obs[ob_nr]))
1915 parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
1916 break;
1917 case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
1918 if (objectp(tmp = obs[ob_nr]))
1919 parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, PLURAL);
1920 break;
1921 case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
1922 if (objectp(tmp = obs[ob_nr]))
1923 parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
1924 break;
1925 case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
1926 if (objectp(tmp = obs[ob_nr]))
1927 parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
1928 break;
1929 default:
1930 continue;
1931 }
Zesstra19102132016-09-01 22:50:36 +02001932
MG Mud User88f12472016-06-24 23:31:02 +02001933 // wenn tmp ein String war, weisen wir es hier pauschal zu
1934 if (stringp(tmp))
1935 parts[i] = tmp;
1936
1937 // auf Wunsch wird nach Satzenden gross geschrieben
1938 if (caps)
Zesstra19102132016-09-01 22:50:36 +02001939 {
1940 // Wenn das vorhergehende parts[i] == "" ist, sind wir am Anfang vom
1941 // String und dies wird wie ein Satzende vorher behandelt.
1942 if (parts[i-1] == "")
MG Mud User88f12472016-06-24 23:31:02 +02001943 parts[i] = capitalize(parts[i]);
Zesstra19102132016-09-01 22:50:36 +02001944 else
1945 {
1946 switch (parts[i-1][<2..])
1947 {
1948 case ". ": case "! ": case "? ":
1949 case ".": case "!": case "?":
1950 case ".\n": case "!\n": case "?\n":
1951 case "\" ": case "\"\n":
1952 parts[i] = capitalize(parts[i]);
1953 break;
1954 }
MG Mud User88f12472016-06-24 23:31:02 +02001955 }
Zesstra19102132016-09-01 22:50:36 +02001956 }
MG Mud User88f12472016-06-24 23:31:02 +02001957 }
1958 return implode(parts, "");
1959 }
1960 return str;
1961}
1962
MG Mud User88f12472016-06-24 23:31:02 +02001963//replacements for dropped efuns in LD
1964#if !__EFUN_DEFINED__(extract)
1965deprecated varargs string extract(string str, int from, int to) {
1966
1967 if(!stringp(str)) {
1968 set_this_object(previous_object());
1969 raise_error(sprintf("Bad argument 1 to extract(): %O",str));
1970 }
1971 if (intp(from) && intp(to)) {
1972 if (from>=0 && to>=0)
1973 return(str[from .. to]);
1974 else if (from>=0 && to<0)
1975 return(str[from .. <abs(to)]);
1976 else if (from<0 && to>=0)
1977 return(str[<abs(from) .. to]);
1978 else
1979 return(str[<abs(from) .. <abs(to)]);
1980 }
1981 else if (intp(from)) {
1982 if (from>=0)
1983 return(str[from .. ]);
1984 else
1985 return(str[<abs(from) .. ]);
1986 }
1987 else {
1988 return(str);
1989 }
1990}
1991#endif // !__EFUN_DEFINED__(extract)
1992
1993#if !__EFUN_DEFINED__(slice_array)
1994deprecated varargs mixed slice_array(mixed array, int from, int to) {
1995
1996 if(!pointerp(array)) {
1997 set_this_object(previous_object());
1998 raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
1999 }
2000 if (intp(from) && intp(to)) {
2001 if (from>=0 && to>=0)
2002 return(array[from .. to]);
2003 else if (from>=0 && to<0)
2004 return(array[from .. <abs(to)]);
2005 else if (from<0 && to>=0)
2006 return(array[<abs(from) .. to]);
2007 else
2008 return(array[<abs(from) .. <abs(to)]);
2009 }
2010 else if (intp(from)) {
2011 if (from>=0)
2012 return(array[from .. ]);
2013 else
2014 return(array[<abs(from) .. ]);
2015 }
2016 else {
2017 return(array);
2018 }
2019}
2020#endif // !__EFUN_DEFINED__(slice_array)
2021
2022#if !__EFUN_DEFINED__(member_array)
2023deprecated int member_array(mixed item, mixed arraystring) {
2024
2025 if (pointerp(arraystring)) {
2026 return(efun::member(arraystring,item));
2027 }
2028 else if (stringp(arraystring)) {
2029 return(efun::member(arraystring,to_int(item)));
2030 }
2031 else {
2032 set_this_object(previous_object());
2033 raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
2034 }
2035}
2036#endif // !__EFUN_DEFINED__(member_array)
2037
2038// The digit at the i'th position is the number of bits set in 'i'.
2039string count_table =
2040 "0112122312232334122323342334344512232334233434452334344534454556";
2041int broken_count_bits( string s ) {
2042 int i, res;
2043 if( !stringp(s) || !(i=sizeof(s)) ) return 0;
2044 for( ; i-->0; ) {
2045 // We are counting 6 bits at a time using a precompiled table.
2046 res += count_table[(s[i]-' ')&63]-'0';
2047 }
2048 return res;
2049}
2050
2051#if !__EFUN_DEFINED__(count_bits)
2052int count_bits( string s ) {
2053 return(broken_count_bits(s));
2054}
2055#endif
2056
2057
2058// * Teile aus einem Array entfernen *** OBSOLETE
2059deprecated mixed *exclude_array(mixed *arr,int from,int to)
2060{
2061 if (to<from)
2062 to = from;
2063 return arr[0..from-1]+arr[to+1..];
2064}
2065