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