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