blob: 228e3361f906afe6071f442f1328972f2c1cf117 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001/*
2 * MGtool-1.3
3 * File: MGtool.c
4 * Maintainer: Kirk@MorgenGrauen
5 */
6
7/*------------------------------------------*/
8/* the original Xtool is copyrighted by Hyp */
9/*------------------------------------------*/
10
Zesstra7e95e3f2019-10-19 11:15:05 +020011#pragma strict_types,rtt_checks
MG Mud User88f12472016-06-24 23:31:02 +020012
Zesstra4bf2e1f2018-11-08 20:47:58 +010013protected functions virtual inherit "/std/util/path";
MG Mud User88f12472016-06-24 23:31:02 +020014inherit "/std/thing/properties";
15
Zesstra9f0401a2019-11-06 22:06:02 +010016#include <driver_info.h>
17#include <rtlimits.h>
MG Mud User88f12472016-06-24 23:31:02 +020018#include <properties.h>
Zesstraf54f03a2021-12-29 19:55:08 +010019#include <thing/properties.h>
Zesstra4bf2e1f2018-11-08 20:47:58 +010020
MG Mud User88f12472016-06-24 23:31:02 +020021#include <moving.h>
22#include "/secure/wizlevels.h"
23#include "/secure/config.h"
24#include <userinfo.h>
25#include <defines.h>
Zesstra7e95e3f2019-10-19 11:15:05 +020026#include <hook.h>
MG Mud User88f12472016-06-24 23:31:02 +020027
28#include "MGtool/tool.h"
29
30#include "MGtool/toollib.h"
31
32#define XDBG 1
33
34nosave object cloner;
35nosave object msgto=NULL;
36nosave string *manpath=({TOOL_PATH+"/man.d/",
37 "/doc/efun/",
38 "/doc/lfun/",
39 "/doc/w/",
40 "/doc/helpdir/",
41 "/doc/LPC/",
42 "/doc/std/",
43 "/doc/concepts/",
44 ""});
45nosave string morefile=NULL;
46nosave string *scriptline=NULL;
47nosave string *history=allocate(MAX_HISTORY);
48nosave int moreflag=FALSE;
49nosave int moreoffset=1;
50nosave int term=NULL;
51nosave int scriptsize=NULL;
52nosave int nostore=FALSE;
53nosave int xlight=0;
54nosave int pipe_in=FALSE;
55nosave int pipe_out=FALSE;
56nosave int pipe_ovr=TRUE;
57nosave string pipe_if=NULL;
58nosave string pipe_of=NULL;
59nosave int xtk=FALSE;
60nosave mapping variable=([]);
61nosave string *cmds;
MG Mud User88f12472016-06-24 23:31:02 +020062private nosave string more_searchexpr="";
63int morelines=MORE_LINES;
64int modi=(MODE_FIRST|MODE_PROTECT|MODE_SHORT);
65
66#include "MGtool/toollib.c"
67#include "MGtool/toolcmd.c"
68
69#define SafeReturn(x) \
70{ \
71 cmds=({}); \
72 pipe_in=FALSE; \
73 pipe_of=NULL; \
74 pipe_ovr=TRUE; \
75 pipe_out=FALSE; \
76 pipe_of=NULL; \
77 return (x); \
78}
79
80/*----------------------------------------------------------------------
81 * check some security aspects
82 */
83
84static int security()
85{
86 object prev;
87
88 if( process_call() || getuid()!=getuid(cloner) ) return FALSE; // Rumata
89
90 TK("UID: "+getuid(ME)+" cloner:"+getuid(cloner));
91// TK("prev: "+object_name(PREV)+" me:"+object_name(ME));
92 if(prev=PREV)
93 {
94 if(!cloner)
95 return TRUE;
96 if(getuid(prev)==ROOTID||IS_ARCH(prev))
97 return TRUE;
98 if(prev==ME)
99 return TRUE;
100 return secure_level()>=query_wiz_level(cloner); // Rumata
101 //return getuid(prev)==getuid()&&geteuid(prev)==geteuid()&&cloner==RTP;
102 }
103 else
104 return cloner==NULL;
105}
106
107/*----------------------------------------------------------------------
108 * own write function
109 */
110
111static int Write(string str)
112{
113 if(!stringp(str) || str=="")
114 return FALSE;
115 if(!cloner && objectp(this_player()))
116 write(str);
117 else
118 tell_object(cloner, str);
119 return TRUE;
120}
121
122/*----------------------------------------------------------------------
123 * own command function
124 */
125
126static int Command(string str)
127{
128 int i;
129 TK("Command: str: "+(str?str:"(NULL)"));
130 nostore++;
131 if(MODE(MODE_ECHO))
132 WLN("Doing: "+str);
bugfixaf2be4f2020-03-22 19:13:07 +0100133 i=({int})cloner->command_me(str);
MG Mud User88f12472016-06-24 23:31:02 +0200134 nostore--;
135 return i;
136}
137
138/*----------------------------------------------------------------------
139 * object searching
140 */
141
142static varargs object XFindObj(string str, int silent)
143{
144 object obj, env;
145 string *strs;
Arathorn65b45e02022-02-07 22:03:57 +0100146 int i, s;
MG Mud User88f12472016-06-24 23:31:02 +0200147
148 if(!str)
149 return NULL;
150 TK("XFindObj: str: "+(str?str:"(NULL)"));
151 env=ENV(cloner);
Zesstra4daab9a2019-11-06 22:38:18 +0100152 str=string_replace(str, "\\.","\u00B0" "01");
153 str=string_replace(str, "\\^", "\u00B0" "02");
154 str=string_replace(str, "\\$", "\u00B0" "03");
MG Mud User88f12472016-06-24 23:31:02 +0200155 str=string_replace(str, "\\\\", "\\");
156 if (find_object(str)) return find_object(str);
157 if (file_size(str)>1) {
Zesstrabc791ab2017-01-30 23:06:48 +0100158 return load_object(str);
MG Mud User88f12472016-06-24 23:31:02 +0200159 }
160 s=sizeof(strs=strip_explode(str, "."));
161 while(s--)
162 {
163 if(strs[i]=="m"||strs[i]=="me")
164 strs[i]=RNAME(cloner);
165 else if(strs[i]=="h"||strs[i]=="here")
166 strs[i]=object_name(ENV(cloner));
167 if(obj=FindObj(strs[i++],env,(silent?1:0)))
168 env=obj;
169 else
170 break;
171 }
172 return obj;
173}
174
175static varargs object FindObj(string str, object env, int silent)
176{
177 object obj, *inv;
MG Mud User88f12472016-06-24 23:31:02 +0200178 int num, e;
179
180 if (!stringp(str) || !sizeof(str) || !objectp(env))
181 return NULL;
Zesstra4daab9a2019-11-06 22:38:18 +0100182 str=string_replace(str, "\u00B0" "01", ".");
MG Mud User88f12472016-06-24 23:31:02 +0200183 while(str[e++]=='^')
184 ;
185 str=str[--e..<1];
Zesstra4daab9a2019-11-06 22:38:18 +0100186 str=string_replace(str, "\u00B0" "02", "^");
MG Mud User88f12472016-06-24 23:31:02 +0200187 if(obj=VarToObj(str))
188 ;
189 else if(str[0]=='')
Zesstra4daab9a2019-11-06 22:38:18 +0100190 str=string_replace(str, "\u00B0" "03", "$");
MG Mud User88f12472016-06-24 23:31:02 +0200191 else if(sscanf(str, "%d", num)&&(inv=all_inventory(env)))
192 {
193 if(num>0&&num<=sizeof(inv))
194 obj=inv[num-1];
195 else
196 {
197 WDLN("Specified object number out of range [1-"+sizeof(inv)+"]");
198 return NULL;
199 }
200 }
201 if(obj||(obj=present(str, env))||
202 (obj=find_player(LOWER(str)))||
203 (obj=find_living(str))||
204 (obj=find_object(long_path(str))))
205 {
206 while(e--)
207 {
208 if(!(obj=ENV(obj)))
209 {
210 W("Specified object has no environment [");
211 while(e--)
212 W("^");
213 WDLN(str+"]");
214 return NULL;
215 }
216 }
217 }
218 else
219 if(!silent)
220 WDLN("Specified object does not exist ["+str+"]");
221 return obj;
222}
223
224/*----------------------------------------------------------------------
225 * object variable handling
226 */
227
228static object VarToObj(string str)
229{
230 if (!stringp(str) || !sizeof(str) || str[0]!='$')
231 return NULL;
232 switch(str)
233 {
234 case "$m":
235 case "$me":
236 return cloner;
237 case "$h":
238 case "$here":
239 return ENV(cloner);
240 default:
241 return variable[str[1..<1]];
242 }
MG Mud User88f12472016-06-24 23:31:02 +0200243}
244
245static string VarToFile(string str)
246{
247 return source_file_name(VarToObj(str));
248}
249
250static string VarToPureFile(string str)
251{
252 return pure_file_name(VarToObj(str));
253}
254
255/*----------------------------------------------------------------------
256 * object description printing
257 */
258
259static void PrintObj(object obj, string file)
260{
Zesstra600da752020-04-30 11:46:30 +0200261 object item;
262 string|closure long_desc;
MG Mud User88f12472016-06-24 23:31:02 +0200263 int i;
264
265 SECURE1();
266 if(!obj)
267 return;
268 PrintShort("Short: ", obj, file);
269 if(!file||file=="")
270 WLN("Long :");
271 else
272 write_file(file,"Long :\n");
273 if(query_once_interactive(obj))
Zesstra600da752020-04-30 11:46:30 +0200274 long_desc=capitalize(getuid(obj));
MG Mud User88f12472016-06-24 23:31:02 +0200275 else
276 {
Zesstraf47f8502020-05-02 14:57:53 +0200277 if( !((long_desc=({string|closure})obj->QueryProp(P_INT_LONG))
278 || (long_desc=({string|closure})obj->QueryProp(P_LONG))) )
Zesstra600da752020-04-30 11:46:30 +0200279 long_desc="- no long description -\n";
280 if (closurep(long_desc))
281 long_desc = funcall(long_desc);
MG Mud User88f12472016-06-24 23:31:02 +0200282 }
283 if(!file||file=="")
Zesstra600da752020-04-30 11:46:30 +0200284 W(long_desc);
MG Mud User88f12472016-06-24 23:31:02 +0200285 else
Zesstra600da752020-04-30 11:46:30 +0200286 write_file(file,long_desc);
MG Mud User88f12472016-06-24 23:31:02 +0200287 FORALL(item, obj)
288 {
289 if(!i)
290 if(!file||file=="")
291 WLN("Content:");
292 else
293 write_file(file,"Content:\n");
294 PrintShort(ARIGHT(++i+". ", 4, " "), item, file);
295 }
296}
297
298static string ObjFile(object obj)
299{
300 return "["+short_path(object_name(obj))+"]";
301}
302
303static varargs void PrintShort(string pre, object obj, string file)
304{
Zesstra600da752020-04-30 11:46:30 +0200305 string|closure shrt;
MG Mud User88f12472016-06-24 23:31:02 +0200306
307 SECURE1();
308 if(!obj)
309 return;
310 if(MODE(MODE_SHORT))
311 {
312 if (query_once_interactive(obj))
Zesstra600da752020-04-30 11:46:30 +0200313 shrt=capitalize(getuid(obj));
MG Mud User88f12472016-06-24 23:31:02 +0200314 else
315 {
Zesstra600da752020-04-30 11:46:30 +0200316 if(!((shrt=({string|closure})obj->QueryProp(P_INT_SHORT))
317 || (shrt=({string|closure})obj->QueryProp(P_SHORT))))
318 if(is_player(obj))
319 shrt=CRNAME(obj)+" (invisible)";
320 else
321 shrt="- no short description -";
322 if (closurep(shrt))
323 shrt = funcall(shrt);
MG Mud User88f12472016-06-24 23:31:02 +0200324 }
Zesstra600da752020-04-30 11:46:30 +0200325 shrt=ALEFT(sprintf("%O ",shrt), 34, ".")+" ";
MG Mud User88f12472016-06-24 23:31:02 +0200326 }
327 else
Zesstra600da752020-04-30 11:46:30 +0200328 shrt="";
MG Mud User88f12472016-06-24 23:31:02 +0200329 if(interactive(obj))
Zesstra600da752020-04-30 11:46:30 +0200330 shrt+="i";
MG Mud User88f12472016-06-24 23:31:02 +0200331 else if(living(obj))
Zesstra600da752020-04-30 11:46:30 +0200332 shrt+="l";
MG Mud User88f12472016-06-24 23:31:02 +0200333 else
Zesstra600da752020-04-30 11:46:30 +0200334 shrt+=".";
335 shrt+=(shadow(obj, 0) ? "s" : ".");
MG Mud User88f12472016-06-24 23:31:02 +0200336 if(!file||file=="")
Zesstra600da752020-04-30 11:46:30 +0200337 WLN((pre+CAP(shrt)+" "+ObjFile(obj))[0..79]);
MG Mud User88f12472016-06-24 23:31:02 +0200338 else
Zesstra600da752020-04-30 11:46:30 +0200339 write_file(file,(pre+CAP(shrt)+" "+ObjFile(obj))[0..79]+"\n");
MG Mud User88f12472016-06-24 23:31:02 +0200340}
341
342static varargs void DeepPrintShort(object env, int indent, string pre, string file)
343{
344 int i;
345 object item;
346 string str;
347
348 SECURE1();
349 if(!env)
350 return;
351 for(i=indent,str=""; i--; str+=" ");
352 if(pre)
353 str+=pre;
354 if(!file||file=="")
355 W(str);
356 else
357 write_file(file,str);
358 i++;
359 PrintShort("",env,file);
360 FORALL(item, env)
361 DeepPrintShort(item,indent+1,ARIGHT((++i)+". ",4," "),file);
362}
363
364static string break_string_hard(string str, int len, string pre)
365{
366 int s,p,t;
367 string tmp;
368
369 if(!str||!(s=sizeof(str)))
370 return "";
371 t=len-(p=sizeof(pre))-1;
372
373 tmp="";
374 while(p+s>len)
375 {
376 tmp+=pre+str[0..t]+"\n";
377 str=str[t+1..];
378 s-=t;
379 }
380 if(sizeof(str))
381 tmp+=pre+str[0..]+"\n";
382 return tmp;
383}
384
385static void dprop(string key, mixed data, object obj)
386{
387 if(pipe_out&&pipe_of)
bugfixd94d0932020-04-08 11:27:13 +0200388 write_file(pipe_of,break_string_hard(mixed_to_string(({mixed})obj->QueryProp(key),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
MG Mud User88f12472016-06-24 23:31:02 +0200389 else
bugfixd94d0932020-04-08 11:27:13 +0200390 W(break_string_hard(mixed_to_string(({mixed})obj->QueryProp(key),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
MG Mud User88f12472016-06-24 23:31:02 +0200391}
392
393static string propflags(string key, object ob)
394{
395 int tmp;
396 string *flags;
bugfixaf2be4f2020-03-22 19:13:07 +0100397 tmp=({mixed})ob->Query(key,1);
MG Mud User88f12472016-06-24 23:31:02 +0200398 flags=({});
399 tmp&SAVE ? flags+=({"SAV"}) : flags+=({" "});
400 tmp&PROTECTED ? flags+=({"PRO"}) : flags+=({" "});
401 tmp&SECURED ? flags+=({"SEC"}) : flags+=({" "});
402 tmp&NOSETMETHOD ? flags+=({"NSM"}) : flags+=({" "});
Zesstraf54f03a2021-12-29 19:55:08 +0100403 tmp&SETMAPPED ? flags+=({"SMA"}) : flags+=({" "});
MG Mud User88f12472016-06-24 23:31:02 +0200404 return ""+implode(flags,"|");
405}
406
407static void dprop2(string key, mixed data, object ob)
408{
409 if(pipe_out&&pipe_of)
410 write_file(pipe_of,break_string_hard(mixed_to_string(propflags(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
411 else
412 W(break_string_hard(mixed_to_string(propflags(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
413}
414
415static mixed propmethods(string key, object ob)
416{
bugfixaf2be4f2020-03-22 19:13:07 +0100417 return ({mixed})ob->Query(key,2);
MG Mud User88f12472016-06-24 23:31:02 +0200418}
419
420static void dprop3(string key, mixed data, object ob)
421{
422 if(pipe_out&&pipe_of)
423 write_file(pipe_of,break_string_hard(mixed_to_string(propmethods(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
424 else
425 W(break_string_hard(mixed_to_string(propmethods(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
426}
427
428static void DumpProperties(object obj, int flag)
429{
430 SECURE1();
431 if(!obj)
432 return;
433 PIPE_DELETE(pipe_of);
434 switch (flag) {
435 case 0:
bugfixaf2be4f2020-03-22 19:13:07 +0100436 walk_mapping(((({mixed*})obj->__query_properties()))[0],#'dprop,obj);
MG Mud User88f12472016-06-24 23:31:02 +0200437 break;
438 case 1:
bugfixaf2be4f2020-03-22 19:13:07 +0100439 walk_mapping(((({mixed*})obj->__query_properties()))[0],#'dprop2,obj);
MG Mud User88f12472016-06-24 23:31:02 +0200440 break;
441 case 2:
bugfixaf2be4f2020-03-22 19:13:07 +0100442 walk_mapping(((({mixed*})obj->__query_properties()))[0],#'dprop3,obj);
MG Mud User88f12472016-06-24 23:31:02 +0200443 break;
444 }
445}
446
447/*----------------------------------------------------------------------
448 * moving objects
449 */
450
451static int MoveObj(object obj1, object obj2, int silent)
452{
453 int err;
454 object oldenv;
455
456 SECURE2(FALSE);
457 if(!(obj1&&obj2))
458 return FALSE;
459 oldenv=ENV(obj1);
bugfixaf2be4f2020-03-22 19:13:07 +0100460 err=({int})obj1->move(obj2, M_SILENT|M_NOCHECK);
MG Mud User88f12472016-06-24 23:31:02 +0200461 if(!silent)
462 switch(err)
463 {
464 case ME_PLAYER:
465 WDLN("Object returned ME_PLAYER");
466 if(oldenv && oldenv != ENV(obj1))
467 WDLN("Object has been moved");
468 return FALSE;
469 case ME_TOO_HEAVY:
470 WDLN("Object returned ME_TOO_HEAVY");
471 if(oldenv && oldenv != ENV(obj1))
472 WDLN("Object has been moved");
473 return FALSE;
474 case ME_CANT_TPORT_IN:
475 WDLN("Object returned ME_CANT_TPORT_IN");
476 if(oldenv && oldenv != ENV(obj1))
477 WDLN("Object has been moved");
478 return FALSE;
479 case ME_CANT_TPORT_OUT:
480 WDLN("Object returned ME_CANT_TPORT_OUT");
481 if(oldenv && oldenv != ENV(obj1))
482 WDLN("Object has been moved");
483 return FALSE;
484 default:
485 WDLN("Object returned unknown return value");
486 return FALSE;
487 }
488 return TRUE;
489}
490
491/*----------------------------------------------------------------------
492 * save destructing of objects
493 */
494
495static void Destruct(object obj)
496{
497 if(!obj || !this_object())
498 return;
bugfixaf2be4f2020-03-22 19:13:07 +0100499 catch(({int})obj->remove());
MG Mud User88f12472016-06-24 23:31:02 +0200500 if(objectp(obj) && !query_once_interactive(obj))
501 destruct(obj);
502}
503
504static void DeepClean(object obj)
505{
506 if(!obj)
507 return;
508 filter(filter(deep_inventory(obj), "is_not_player", ME),
509 "Destruct", ME);
510 if(is_not_player(obj))
511 Destruct(obj);
512}
513
514/*----------------------------------------------------------------------
515 * Show the inheritance tree of objects
516 */
517
518static object *SubNodes(object obj)
519{
520 int s;
521 object *objs;
522 string *inlist;
523
524 if(!obj)
525 return NULL;
526 inlist=inherit_list(obj);
527 s=sizeof(inlist);
528 objs=({});
529 while(s-->1)
530 objs=({find_object(inlist[s])})+objs;
531 return objs;
532}
533
534static void Inheritance(object obj, string func, string pre)
535{
536 int i, s;
537 object *ln;
538 string str;
539
540 if(!obj)
541 return;
542 str=pre+" "+ObjFile(obj);
543 if(func)
544 {
545 str=ALEFT(str+" ", 50, ".");
546 if(function_exists(func, obj)==object_name(obj))
547 str+=ARIGHT(" "+func, 19, ".");
548 else
549 str+=ARIGHT("", 19, ".");
550 }
551 if(pipe_out&&pipe_of)
552 write_file(pipe_of,str+"\n");
553 else
554 WLN(str);
555 ln=SubNodes(obj);
556 for(i=0; i<sizeof(ln); i++)
557 ln=ln-SubNodes(ln[i]);
558 s=sizeof(ln);
559 for(i=0; i<s; i++)
560 Inheritance(ln[i], func, pre+".....");
561}
562
563/*----------------------------------------------------------------------
564 * file name handling
565 */
566
567static string XFile(string file)
568{
569 TK("XFile: file: "+(file?file:"(NULL)"));
570 if(file)
571 switch(file[0])
572 {
573 case '@':
574 return source_file_name(XFindObj(file[1..<1]));
575 case '$':
576 return source_file_name(XFindObj(file));
577 default:
578 return old_explode(long_path(file),"#")[0];
579 }
580 return NULL;
581}
582
583static string XFindFile(string file)
584{
585 TK("XFindFile: file: "+(file?file:"(NULL)"));
586 if(file=XFile(file))
587 {
588 if(file_size(file)>=0)
589 return file;
590 if(file[<3..<1]!=".c" && file_size(file+".c")>0)
591 return file+".c";
592 }
593 WDLN("File not found or not readable ["+short_path(file)+"]");
594 return NULL;
595}
596
597/*----------------------------------------------------------------------
598 * file printing, searching and executing
599 */
600
601static void XMoreFile(string file, int flag)
602{
Arathorn65b45e02022-02-07 22:03:57 +0100603 int size;
MG Mud User88f12472016-06-24 23:31:02 +0200604
605 SECURE1();
606 if(!file)
607 return;
608
609 // term=(string)cloner->QueryProp(P_TTY)!="dumb";
610 if((size=(file_size(morefile=long_path(file))))>0)
611 {
612 if(size>100000)
613 WDLN("Warning: large file");
614 MoreFile(NULL);
615 }
616 else if(flag)
617 WDLN("Cannot read file");
618}
619
MG Mud User88f12472016-06-24 23:31:02 +0200620
621static void MoreFile(string str)
622{
Arathorn65b45e02022-02-07 22:03:57 +0100623 int i;
624 string l;
MG Mud User88f12472016-06-24 23:31:02 +0200625
626 SECURE1();
627
628 if (str /*&& sizeof(str)*/)
629 {
630 if( !sizeof(str) ) str="\0";
631 if(term)
632 W("M");
633 switch(str[0])
634 {
635 case 'q':
636 case 'x':
637 moreflag=FALSE;
638 moreoffset=1;
639 if(morefile==TMP_FILE||morefile==PIPE_FILE)
640 rm(morefile);
641 return NULL;
MG Mud User88f12472016-06-24 23:31:02 +0200642 case 'P':
643 case 'U':
644 moreflag=FALSE;
645 moreoffset=moreoffset-morelines;
646 case 'p':
647 case 'u':
648 moreoffset=moreoffset-2*morelines;
649 break;
650 case 'D':
651 case 'N':
652 moreoffset+=morelines;
653 case 0: /* RETURN */
654 case 'd':
655 if(moreflag)
656 {
657 moreflag=FALSE;
658 moreoffset=1;
659 if(morefile==TMP_FILE)
660 rm(morefile);
661 return;
662 }
663 break;
664 case '/':
665 moreoffset--;
666 more_searchexpr=str[1..<1];
667 case 'n':
668 i=moreoffset-morelines+1;
669 if(more_searchexpr=="")
670 {
671 WDLN("No previous regular expression");
672 return;
673 }
674 if(!regexp(({"dummy"}), more_searchexpr))
675 WDLN("Bad regular expression");
676 else
677 while((l=read_file(morefile, i++, 1))&&
678 !sizeof(regexp(({l}), more_searchexpr)))
679 ;
680 if(l)
681 {
682 WLN("*** Skipping ...");
683 moreoffset=i-1;
684 }
685 else
686 {
687 WLN("*** Pattern not found");
688 moreoffset-=morelines;
689 }
690 break;
691 case '0'..'9':
692 sscanf(str, "%d", i);
693 moreoffset=i;
694 break;
695 }
696 }
697 else
698 {
699 moreoffset=1;
700 moreflag=FALSE;
701 }
702 if(moreoffset<1)
703 moreoffset=1;
704 if(CatFile())
705 W("*** More: q,u,U,d,D,/<regexp>,<line> ["+(moreoffset-1)+"] *** ");
706 else
707 {
708 W("*** More: q,u,U,d,D,/<regexp>,<line> ["+(moreoffset-1)+"=EOF] *** ");
709 moreflag=TRUE;
710 }
711 input_to("MoreFile");
712 return;
713}
714
715// Schade eigentlich das ich es neuschreiben musste, aber es ist
716// schneller neu geschrieben als durch die undokumentieren alten Funktionen
717// durchzusteigen... *seufz*
Zesstra9f0401a2019-11-06 22:06:02 +0100718// Padreic
719
720// +++ Kommentarlos: Programmierer schreibt unverstaendlichen Code neu,
721// indem er unverstaendlichen Code schreibt. +++
722// +++ Kommentarlos: Programmierer ersetzt unverstaendlichen Code durch
723// unverstaendlichen Code. +++
724// Arathorn (mit Zesstra im Sinn)
MG Mud User88f12472016-06-24 23:31:02 +0200725
Zesstracda79c22019-11-06 23:42:13 +0100726// Zustandsvariablen fuer den Cache von sread_line.
727static string last_file, *last_file_lines;
728static int last_file_date, last_file_linecount, last_file_complete;
MG Mud User88f12472016-06-24 23:31:02 +0200729
Zesstracda79c22019-11-06 23:42:13 +0100730// Liest die ersten x Bytes des Files ein und cached diese (aber nur
731// vollstaendige Zeilen). Liefert dann gewuenschte Zeilen aus dem Cache ohne
732// Plattenzugriff.
733protected string sread_line(int num)
MG Mud User88f12472016-06-24 23:31:02 +0200734{
735 if (!morefile) return "";
Zesstracda79c22019-11-06 23:42:13 +0100736 // wenn sich das morefile geaendert hat, wird das neue File eingelesen.
737 if (last_file!=morefile || last_file_date!=file_time(morefile))
738 {
739 bytes byte_buf=read_bytes(morefile, 0,
740 driver_info(DI_CURRENT_RUNTIME_LIMITS)[LIMIT_BYTE]);
741 if (!byte_buf) return "";
742 // letzte unvollstaendige Zeile abschneiden
743 int linebreak_index = strrstr(byte_buf, b"\n");
744 if (linebreak_index > -1
745 && linebreak_index < sizeof(byte_buf)-1 )
746 {
747 byte_buf = byte_buf[0..linebreak_index];
748 // dann ist das File auch unvollstaendig.
749 last_file_complete=0;
MG Mud User88f12472016-06-24 23:31:02 +0200750 }
Zesstracda79c22019-11-06 23:42:13 +0100751 // aber auch wenn byte_buf mit Zeilenumbruch endet, noch schauen, ob das
752 // File nicht laenger ist als byte_buf.
753 else if (sizeof(byte_buf) < file_size(morefile))
754 last_file_complete=0;
755 // ansonsten ist es vollstaendig.
756 else
757 last_file_complete=1;
758
759 // In string konvertieren und cache speichern.
760 last_file = to_text(byte_buf, "UTF-8");
761 last_file_lines = explode(last_file, "\n");
762 // und Daten vom gecachten File speichern
763 last_file_linecount=sizeof(last_file_lines);
764 last_file_date=file_time(morefile);
765 last_file = morefile; //speichert jetzt den Filenamen
MG Mud User88f12472016-06-24 23:31:02 +0200766 }
767 if (num==0) num=1;
Zesstracda79c22019-11-06 23:42:13 +0100768
769 // wenn die angefragte Zeile nicht da ist und das File nicht vollstaendig
770 // ist, wird direkt von der Platte gelesen.
771 if (num > last_file_linecount)
772 {
MG Mud User88f12472016-06-24 23:31:02 +0200773 if (last_file_complete) return "";
Zesstracda79c22019-11-06 23:42:13 +0100774 return read_file(morefile, num, 1) || "";
MG Mud User88f12472016-06-24 23:31:02 +0200775 }
Zesstracda79c22019-11-06 23:42:13 +0100776 // Sonst kommt die Zeile aus dem Cache.
777 return last_file_lines[num-1]+"\n";
MG Mud User88f12472016-06-24 23:31:02 +0200778}
779
780static int CatFile()
781{
782 int end;
783 string l;
784
785 end=moreoffset+morelines;
786 while(moreoffset<end)
MG Mud User88f12472016-06-24 23:31:02 +0200787 if((l=sread_line(moreoffset))!="")
788 {
789 moreoffset++;
790 W(l);
791 }
792 else
793 return FALSE;
Zesstrab25537b2019-11-06 23:36:23 +0100794
MG Mud User88f12472016-06-24 23:31:02 +0200795 if(sread_line(moreoffset+1)!="")
796 return TRUE;
797 else
798 return FALSE;
799}
800
801static int XGrepFile(string pat, string file, int mode)
802{
MG Mud User88f12472016-06-24 23:31:02 +0200803 SECURE2(FALSE);
804 TK("XGrepFile: pat: "+pat+" file: "+file+" mode: "+mode);
805 if(!(pat&&file))
806 return FALSE;
Zesstra9f0401a2019-11-06 22:06:02 +0100807
808 // max. Anzahl von Zeilen pro Portion (ueberschlag: 100 Bytes pro Zeile)
809 int maxlines = driver_info(DI_CURRENT_RUNTIME_LIMITS)[LIMIT_FILE] / 100;
810 int start;
811 string buf;
812 // File portionsweise einlesen und verarbeiten
813 while(buf = read_file(file, start, maxlines))
814 {
815 string *lines = strip_explode(buf,"\n");
816 int f; // Pro Treffer erhoeht, benutzt zur einmaligen Ausgabe des Files
817 // ueber alle Zeilen laufen und regexpen
818 foreach(string line : lines)
MG Mud User88f12472016-06-24 23:31:02 +0200819 {
Zesstra9f0401a2019-11-06 22:06:02 +0100820 string *ts=regexp(({(mode&XGREP_ICASE?lower_case(line):line)}),
821 pat);
822 if(sizeof(ts))
MG Mud User88f12472016-06-24 23:31:02 +0200823 {
Zesstra9f0401a2019-11-06 22:06:02 +0100824 if(!(mode&XGREP_REVERT))
825 {
826 if(!f++)
827 write_file(TMP_FILE, "*** File: "+file+" ***\n");
828 write_file(TMP_FILE, line+"\n");
829 }
830 }
831 else if(mode&XGREP_REVERT)
832 {
833 if(!f++)
834 write_file(TMP_FILE, "*** File: "+file+" ***\n");
835 write_file(TMP_FILE, line+"\n");
MG Mud User88f12472016-06-24 23:31:02 +0200836 }
837 }
Zesstra9f0401a2019-11-06 22:06:02 +0100838 if (sizeof(lines) < maxlines)
839 break;
840 else
841 start += sizeof(lines);
842 }
MG Mud User88f12472016-06-24 23:31:02 +0200843 return TRUE;
844}
845
846static void XExecFile(int line)
847{
848 int i;
849
850 if(!scriptline)
851 return;
852 for(i=line; i<scriptsize&&i<line+EXEC_LINES; i++)
853 {
854 if(!scriptline[i])
855 continue;
856 if(!Command(scriptline[i]))
857 {
858 scriptline=NULL;
859 return;
860 }
861 }
862 if(i<scriptsize)
863 call_out("XExecFile", EXEC_TIME, i);
864 else
865 scriptline=NULL;
866}
867
868static void XmtpScript(string dir, string file, string opt)
869{
870 int s, t;
871 string *files;
872
873 s=sizeof(files=get_dir(dir+"/*"));
874 while(s--)
875 {
876 t=sizeof(files[s])-1;
877 if(files[s] == ".." || files[s] == "." || files[s][t] == '~' ||
878 (files[s][0] == '#' && files[s][t] == '#'))
879 continue;
880 if(file_size(dir+"/"+files[s])==-2)
881 {
882 write_file(file, "mkdir "+files[s]+" ; cd "+files[s]+"\n");
883 XmtpScript(dir+"/"+files[s], file, opt);
884 write_file(file, "cd ..\n");
885 }
886 else
887 write_file(file, "mtp -r "+opt+" "+dir+"/"+files[s]+"\n");
888 }
889}
890
891/*----------------------------------------------------------------------
892 * player properties handling
893 */
894
895static string PlayerIdle(object obj)
896{
897 string str;
Arathorn65b45e02022-02-07 22:03:57 +0100898 int i;
MG Mud User88f12472016-06-24 23:31:02 +0200899
900 if(!obj)
901 return NULL;
902 if((i=query_idle(obj))>=60)
903 {
904 str=ARIGHT(""+(i/3600), 2, "0");
905 i-=(i/3600)*3600;
906 str+="'"+ARIGHT(""+(i/60), 2, "0");
907 }
908 else
909 str=".....";
910 return str;
911}
912
913static string PlayerAge(object obj)
914{
915 string str;
Arathorn65b45e02022-02-07 22:03:57 +0100916 int i;
MG Mud User88f12472016-06-24 23:31:02 +0200917
918 if(!obj)
919 return NULL;
bugfixaf2be4f2020-03-22 19:13:07 +0100920 i=({int})obj->QueryProp(P_AGE);
MG Mud User88f12472016-06-24 23:31:02 +0200921 str=" "+ARIGHT(""+(i/43200), 4, ".");
922 i-=(i/43200)*43200;
923 return str+":"+ARIGHT(""+(i/1800), 2, "0");
924}
925
926static string crname(object who)
927{
928 string uid, lname;
929
930 if((uid=getuid(who))==ROOTID &&
931 object_name(who)[0..7]=="/secure/" &&
bugfixaf2be4f2020-03-22 19:13:07 +0100932 (lname=({string})who->loginname()))
MG Mud User88f12472016-06-24 23:31:02 +0200933 return CAP(lname);
934 return CAP(uid);
935}
936
937static string PlayerWho(object obj)
938{
939 object tmp;
Arathorn65b45e02022-02-07 22:03:57 +0100940 string str;
MG Mud User88f12472016-06-24 23:31:02 +0200941 str=ARIGHT(""+LEVEL(obj) , 3, " ");
942 str+=ALEFT(" "+crname(obj)+" ", 12, ".");
943 str+=PlayerAge(obj);
bugfixaf2be4f2020-03-22 19:13:07 +0100944 str+=(({int})obj->QueryProp(P_GENDER)==1 ? " m " : " f ");
945 str+=(({int})obj->QueryProp(P_FROG)) ? "f" : ".";
946 str+=(({int})obj->QueryProp(P_GHOST)) ? "g" : ".";
947 str+=(({int})obj->QueryProp(P_INVIS)) ? "i" : ".";
MG Mud User88f12472016-06-24 23:31:02 +0200948 str+=(query_editing(obj)||query_input_pending(obj) ? "e" : ".");
bugfixaf2be4f2020-03-22 19:13:07 +0100949 str+=(({string})obj->QueryProp(P_AWAY)) ? "a" : ".";
MG Mud User88f12472016-06-24 23:31:02 +0200950 str+=" "+PlayerIdle(obj)+" ";
951 str+=(tmp=ENV(obj)) ? ObjFile(tmp) : "- fabric of space -";
952 return str;
953}
954
955static string PlayerMail(object obj, int flag)
956{
957 string pre;
958
959 pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
bugfixaf2be4f2020-03-22 19:13:07 +0100960 return pre+"mail: "+({string})obj->QueryProp(P_MAILADDR);
MG Mud User88f12472016-06-24 23:31:02 +0200961}
962
963static string PlayerIP(object obj, int flag)
964{
965 string pre;
966
967 pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
968 return pre+"host: "+query_ip_name(obj)+" ("+query_ip_number(obj)+")";
969}
970
971static string PlayerRace(object obj, int flag)
972{
973 string tmp, pre;
974
975 pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
bugfixaf2be4f2020-03-22 19:13:07 +0100976 pre=pre+"race: "+ALEFT(({string})obj->QueryProp(P_RACE)+" ", 10, ".")+
977 " guild: ";
978 tmp=({string})obj->QueryProp(P_GUILD);
MG Mud User88f12472016-06-24 23:31:02 +0200979 return tmp ? pre+tmp : pre+"- none -";
980}
981
982static string PlayerDomain(object obj, int flag)
983{
Zesstraee2fb382020-01-21 21:31:19 +0100984 string pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
MG Mud User88f12472016-06-24 23:31:02 +0200985 pre+="domainlord of: ";
bugfixaf2be4f2020-03-22 19:13:07 +0100986 string *domains = ({string*})master()->query_userlist(getuid(obj),
987 USER_DOMAIN);
Zesstraee2fb382020-01-21 21:31:19 +0100988 if(sizeof(domains))
989 pre += CountUp(domains, ", ", ", ");
MG Mud User88f12472016-06-24 23:31:02 +0200990 return pre;
991}
992
993static string PlayerStats(object obj, int flag)
994{
995 string pre;
996
997 pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
bugfixaf2be4f2020-03-22 19:13:07 +0100998 pre+="hp="+ARIGHT(({int})obj->QueryProp(P_HP), 3, "0");
999 pre+="/"+ARIGHT(({int})obj->QueryProp(P_MAX_HP), 3, "0");
1000 pre+=" sp="+ARIGHT(({int})obj->QueryProp(P_SP), 3, "0");
1001 pre+="/"+ARIGHT(({int})obj->QueryProp(P_MAX_SP), 3, "0");
1002 pre+=" food="+ARIGHT(({int})obj->QueryProp(P_FOOD), 3, "0");
1003 pre+="/"+ARIGHT(({int})obj->QueryProp(P_MAX_FOOD), 3, "0");
1004 pre+=" drink="+ARIGHT(({int})obj->QueryProp(P_DRINK), 3, "0");
1005 pre+="/"+ARIGHT(({int})obj->QueryProp(P_MAX_DRINK), 3, "0");
1006 pre+=" exps="+({int})obj->QueryProp(P_XP);
MG Mud User88f12472016-06-24 23:31:02 +02001007 return pre;
1008}
1009
1010static string PlayerSnoop(object obj, int flag)
1011{
Arathorn65b45e02022-02-07 22:03:57 +01001012 string pre;
MG Mud User88f12472016-06-24 23:31:02 +02001013 object victim;
1014
1015 pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
1016 pre=pre+"is snooped by: ";
1017 if(victim=query_snoop(obj))
1018 pre+=ARIGHT(" "+crname(victim), 12, ".");
1019 else
1020 pre+="............";
1021 return pre;
1022}
1023
1024static string PlayerCmdAvg(object obj, int flag)
1025{
1026 string pre;
1027
1028 pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
bugfixaf2be4f2020-03-22 19:13:07 +01001029 return pre+"cmdavg: "+({int})obj->_query_command_average();
MG Mud User88f12472016-06-24 23:31:02 +02001030}
1031
1032
1033/*----------------------------------------------------------------------
1034 * msg input to objects
1035 */
1036
1037static void XMsgSay(string str)
1038{
1039 if(str=="."||str=="**")
1040 {
1041 WLN("[End of message]");
1042 say("[End of message]\n");
1043 }
1044 else
1045 {
1046 say(str+"\n");
1047 input_to("XMsgSay");
1048 }
1049}
1050
1051static void XMsgTell(string str)
1052{
1053 if(str=="."||str=="**")
1054 {
1055 WLN("[End of message]");
1056 tell_object(msgto, "[End of message]\n");
1057 }
1058 else
1059 {
1060 tell_object(msgto, str+"\n");
1061 input_to("XMsgTell");
1062 }
1063}
1064
1065static void XMsgShout(string str)
1066{
1067 if(str=="."||str=="**")
1068 {
1069 WLN("[End of message]");
1070 shout("[End of message]\n");
1071 }
1072 else
1073 {
1074 shout(str+"\n");
1075 input_to("XMsgShout");
1076 }
1077}
1078
1079/*----------------------------------------------------------------------
1080 * own object moving
1081 */
1082
1083int move(mixed dest)
1084{
1085 move_object(ME, cloner?cloner:dest);
1086 return TRUE;
1087}
1088
1089/*----------------------------------------------------------------------
1090 * object id
1091 */
1092
1093int id(string str)
1094{
1095 if(!security()&&MODE(MODE_SCANCHK)&&RTP&&!IS_ARCH(RTP))
1096 WDLN(crname(RTP)+" scanned you (id) ["+query_verb()+"] "+
1097 (PREV ? ObjFile(PREV) : "[destructed object]"));
1098 return LOWER(str)==LOWER(TOOL_NAME);
1099}
1100
1101/*----------------------------------------------------------------------
1102 * short and long description
1103 */
1104
1105string short()
1106{
1107 return _query_short()+".\n";
1108}
1109
1110string _query_short()
1111{
1112 string sh; // added by Rumata
1113 if(cloner)
1114 {
1115 if((!security())&&MODE(MODE_SCANCHK)&&!IS_ARCH(RTP))
1116 WDLN(crname(RTP)+" scanned you (short) ["+query_verb()+"] "+
1117 (PREV ? ObjFile(PREV) : "[destructed object]"));
1118 if( sh=Query(P_SHORT) ) return sh; // added by Rumata
bugfixaf2be4f2020-03-22 19:13:07 +01001119 return ({string})cloner->name(WESSEN)+" "+TOOL_TITLE+" ["+
MG Mud User88f12472016-06-24 23:31:02 +02001120 ctime(time())[11..18]+"]";
1121 }
1122 return TOOL_TITLE;
1123}
1124
1125string long()
1126{
1127 return _query_long();
1128}
1129
1130string _query_long()
1131{
1132 if(cloner&&!security()&&MODE(MODE_SCANCHK)&&!IS_ARCH(RTP))
1133 {
1134 WDLN(crname(RTP)+" scanned you (long) ["+query_verb()+"] "+
1135 (PREV ? ObjFile(PREV) : "[destructed object]"));
1136 }
1137 return
1138 "This is "+TOOL_NAME+" version "+TOOL_VERSION+
1139 " (maintained by Kirk@MorgenGrauen)\n"+
1140 "Original copyright held by Hyp.\n"+
1141 "Gamedriver patchlevel: "+__VERSION__+" master object: "+__MASTER_OBJECT__+
1142 "\n\nDo 'xhelp' for more information.\n";
1143}
1144
1145string name(mixed dummy1, mixed dummy2)
1146{
1147 return _query_short();
1148}
1149
1150/*----------------------------------------------------------------------
1151 * light stuff
1152 */
1153
1154int _query_light()
1155{
1156 return xlight;
1157}
1158
1159int _set_light(int x)
1160{
1161 return xlight;
1162}
1163
1164/*----------------------------------------------------------------------
1165 * Autoloading
1166 */
1167
1168mixed *_query_autoloadobj()
1169{
1170 return AUTOLOAD_ARGS;
1171}
1172
1173void _set_autoloadobj(mixed *args)
1174{
1175 WLN(TOOL_TITLE+" ...");
1176 if(!pointerp(args))
1177 ;
1178 else if(sizeof(args)!=3)
1179 ;
1180 else if(!stringp(args[0]))
1181 ;
1182 else if(!intp(args[1]))
1183 ;
1184 else if(!intp(args[2]))
1185 ;
1186 else
1187 {
bugfixaf2be4f2020-03-22 19:13:07 +01001188 if(args[0]!=TOOL_INTERNAL)
MG Mud User88f12472016-06-24 23:31:02 +02001189 {
1190 WLN("*****************************");
1191 WLN("*** NEW EDITION ***");
1192 WLN("*** do 'xtool news' for ***");
1193 WLN("*** more information ***");
1194 WLN("*****************************");
1195 }
bugfixaf2be4f2020-03-22 19:13:07 +01001196 modi=args[1];
1197 morelines=args[2];
MG Mud User88f12472016-06-24 23:31:02 +02001198 return;
1199 }
1200 W("(bad autoload, using default)\n");
1201}
1202
1203/*----------------------------------------------------------------------
1204 * creation, updating and initialization stuff
1205 */
1206
1207void update_tool(mixed *args, object obj)
1208{
1209 SECURE1();
1210 if(!(obj&&args))
1211 return;
1212 Destruct(PREV);
1213 _set_autoloadobj(args);
1214 move(obj);
1215}
1216
1217void create()
1218{
MG Mud User88f12472016-06-24 23:31:02 +02001219 if(member(object_name(),'#')<0)
1220 return;
1221 if(!cloner&&!((cloner=TP)||(cloner=ENV(ME)))&&!interactive(cloner))
1222 destruct(ME);
Arathorn55c9f322020-12-28 11:31:49 +01001223 if(!IS_LEARNER(cloner)) {
MG Mud User88f12472016-06-24 23:31:02 +02001224 destruct(ME);
Arathorn55c9f322020-12-28 11:31:49 +01001225 return;
1226 }
MG Mud User88f12472016-06-24 23:31:02 +02001227 SetProp(P_NODROP,"Das waere zu gefaehrlich.\n");
1228 SetProp(P_NEVERDROP,1);
1229 SetProp(P_NOBUY,1);
1230 if(file_size(SAVE_FILE+".o")>0)
1231 {
1232 WDLN("Loading "+TOOL_TITLE+" settings");
1233 restore_object(SAVE_FILE);
1234 }
1235 if(MODE(MODE_FIRST))
1236 call_out("move",0,cloner);
Zesstra7e95e3f2019-10-19 11:15:05 +02001237 call_out("add_insert_hook",1);
MG Mud User88f12472016-06-24 23:31:02 +02001238}
1239
1240void TK(string str)
1241{
1242 if (!xtk)
1243 return;
1244 tell_object(cloner,"XTOOL: "+str+"\n");
1245}
1246
1247int Xtk(string str)
1248{
1249 xtk=!xtk;
1250 WDLN("Xtool internal tracing "+(xtk?"enabled":"disabled"));
1251 return TRUE;
1252}
1253
MG Mud User88f12472016-06-24 23:31:02 +02001254void init()
1255{
MG Mud User88f12472016-06-24 23:31:02 +02001256 if(member(object_name(),'#')<0) return;
Arathorn65b45e02022-02-07 22:03:57 +01001257 object first=first_inventory(ENV(ME));
MG Mud User88f12472016-06-24 23:31:02 +02001258 if(MODE(MODE_PROTECT)&&is_player(first)&&!IS_ARCH(first))
1259 {
1260 WDLN("WARNING: "+crname(first)+" tried to move into your inventory");
1261 tell_object(first, "You cannot move yourself into "+
1262 crname(cloner)+"'s inventory.\n");
1263 call_out("DropObj",0,first);
1264 return;
1265 }
1266 else if(MODE(MODE_FIRST)&&first!=ME)
1267 move(cloner);
1268 else actions();
1269}
1270
1271void DropObj(object obj)
1272{
1273 if(!obj||!objectp(obj))
1274 return;
bugfixaf2be4f2020-03-22 19:13:07 +01001275 ({int})obj->move(ENV(cloner),M_NOCHECK|M_NO_SHOW);
MG Mud User88f12472016-06-24 23:31:02 +02001276}
1277
1278#define ACTIONS\
1279([\
1280 "xcallouts" : "Xcallouts";0;1,\
1281 "xcall" : "Xcall";0;1,\
1282 "xcat" : "Xcat";1;1,\
1283 "xcd" : "Xcd";0;0,\
1284 "xclean" : "Xclean";0;0,\
1285 "xclone" : "Xclone";0;0,\
1286 "xuclone" : "Xuclone";0;0,\
1287 "xcmds" : "Xcmds";0;1,\
1288 "xdbg" : "Xdbg";0;0,\
1289 "xdclean" : "Xdclean";0;0,\
1290 "xddes" : "Xddes";0;0,\
1291 "xdes" : "Xdes";0;0,\
1292 "xdest" : "Xdes";0;0,\
1293 "xdlook" : "Xdlook";0;1,\
1294 "xdo" : "Xdo";0;0,\
1295 "xdupdate" : "Xdupdate";0;0,\
1296 "xecho" : "Xecho";0;0,\
1297 "xeval" : "Xeval";0;1,\
1298 "xforall" : "Xforall";0;0,\
1299 "xgoto" : "Xgoto";0;0,\
1300 "xhbeats" : "Xhbeats";0;1,\
1301 "xgrep" : "Xgrep";1;1,\
1302 "xhead" : "Xhead";1;1,\
1303 "xhelp" : "Xhelp";0;0,\
1304 "xinventory": "Xinventory";0;1,\
1305 "xids" : "Xids";0;0,\
1306 "xinfo" : "Xinfo";0;0,\
1307 "xinherit" : "Xinherit";0;1,\
1308 "xlag" : "Xlag";0;0,\
1309 "xlight" : "Xlight";0;0,\
1310 "xload" : "Xload";0;0,\
1311 "xlook" : "Xlook";0;1,\
1312 "xlpc" : "Xlpc";0;0,\
1313 "xman" : "Xman";0;0,\
1314 "xmore" : "Xmore";1;0,\
1315 "xmove" : "Xmove";0;0,\
1316 "xmsg" : "Xmsg";1;0,\
1317 "xmtp" : "Xmtp";0;0,\
1318 "xproc" : "Xproc";0;1,\
1319 "xprof" : "Xprof";0;0,\
1320 "xprops" : "Xprops";0;1,\
1321 "xquit" : "Xquit";0;0,\
1322 "xscan" : "Xscan";0;0,\
1323 "xset" : "Xset";0;0,\
1324 "xsh" : "Xsh";0;0,\
1325 "xsort" : "Xsort";1;1,\
1326 "xtail" : "Xtail";1;1,\
1327 "xtk" : "Xtk";0;0,\
1328 "xtool" : "Xtool";0;0,\
1329 "xtrace" : "Xtrace";0;0,\
1330 "xtrans" : "Xtrans";0;0,\
1331 "xupdate" : "Xupdate";0;0,\
1332 "xwc" : "Xwc";1;0,\
1333 "xwho" : "Xwho";0;1,\
1334 ])
1335
1336static string PrepareLine(string str)
1337{
1338 return str;
1339}
1340
1341static string QuoteLine(string str)
1342{
1343 string *tmp,*tmp2;
1344 int i, i2, len, len2, qd, qs;
1345
Zesstra4daab9a2019-11-06 22:38:18 +01001346 str=string_replace(str,"\\\\","\u00B0""BSHL");
1347 str=string_replace(str,"\\\"","\u00B0""DBLQ");
1348 str=string_replace(str,"\\\'","\u00B0""SGLQ");
1349 str=string_replace(str,"\\|","\u00B0""PIPE");
1350 str=string_replace(str,"||","\u00B0""OROR");
1351 str=string_replace(str,"->","\u00B0""LARR");
1352 str=string_replace(str,"\\$","\u00B0""DOLR");
MG Mud User88f12472016-06-24 23:31:02 +02001353 tmp=regexplode(str,"(\"|')");
1354 len=sizeof(tmp);
1355 qd=qs=0;
1356 for(i=0;i<len;i++)
1357 {
1358 if(i%2)
1359 {
1360 if(tmp[i]=="'")
1361 qd=(!qs?!qd:qd);
1362 else
1363 qs=(!qd?!qs:qs);
1364 if((tmp[i]=="\""&&!qd)||(tmp[i]=="'"&&!qs))
1365 tmp[i]="";
1366 }
1367 else
1368 {
1369 if(!qd)
1370 {
1371 len2=sizeof(tmp2=regexplode(tmp[i],"\\$[^ ][^ ]*"));
1372 for(i2=0;i2<len2;i2++)
1373 if(i2%2)
1374 {
1375 TK("QuoteLine: "+tmp2[i2][1..]);
1376 tmp2[i2]=(string)XFindObj((tmp2[i2])[1..]);
1377 }
1378 tmp[i]=implode(tmp2,"");
1379 }
1380 }
1381 }
1382 if(qd||qs)
1383 return NULL;
1384 str=implode(tmp,"");
1385 TK("QuoteLine: str: "+str);
1386 return str;
1387}
1388
1389static string UnquoteLine(string str)
1390{
Zesstra4daab9a2019-11-06 22:38:18 +01001391 str=string_replace(str,"\u00B0""BSHL","\\");
1392 str=string_replace(str,"\u00B0""DBLQ","\"");
1393 str=string_replace(str,"\u00B0""SGLQ","\'");
1394 str=string_replace(str,"\u00B0""DQUO","");
1395 str=string_replace(str,"\u00B0""SQUO","");
1396 str=string_replace(str,"\u00B0""PIPE","|");
1397 str=string_replace(str,"\u00B0""OROR","||");
1398 str=string_replace(str,"\u00B0""LARR","->");
1399 str=string_replace(str,"\u00B0""DOLR","$");
MG Mud User88f12472016-06-24 23:31:02 +02001400 TK("UnquoteLine: str: "+str);
1401 return str;
1402}
1403
1404static string *ExplodeCmds(string str)
1405{
1406 string *tmp;
1407
1408 tmp=regexplode(str,"\\||>|>>");
1409 while(tmp[<1]=="")
1410 tmp=tmp[0..<2];
1411 return tmp;
1412}
1413
1414varargs int ParseLine(string str)
1415{
1416 string verb, arg;
1417 int ret;
1418
1419 TK("ParseLine: str: "+(str?str:""));
1420 if(!sizeof(cmds))
1421 {
1422 // this is a single command or beginning of a command pipe
1423 verb=query_verb();
1424
1425 // return on unknown commands
1426 if(!verb||!sizeof(verb)||!GetFunc(verb,TRUE))
1427 return FALSE;
1428
bugfixaf2be4f2020-03-22 19:13:07 +01001429 str=({string})this_player()->_unparsed_args();
MG Mud User88f12472016-06-24 23:31:02 +02001430 pipe_in=FALSE;
1431 pipe_of=NULL;
1432 pipe_ovr=TRUE;
1433 pipe_out=FALSE;
1434 pipe_of=NULL;
1435 // pass arguments to some special functions unparsed
1436 if(member(({"xlpc","xcall","xeval"}),verb)>=0)
1437 {
1438#ifdef XDBG
1439 TK("ParseLine: special func: "+verb);
1440#endif
1441 ret=CallFunc(verb,str);
1442 SafeReturn(ret);
1443 }
1444 // ok, here we go
1445 pipe_in=pipe_out=FALSE;
1446 pipe_if=pipe_of=NULL;
1447 pipe_ovr=TRUE;
1448 if(file_size(PIPE_FILE)>=0)
1449 rm(PIPE_FILE);
1450 if (str&&str!="")
1451 {
1452 if(!(str=QuoteLine(str)))
1453 {
1454 WDLN("Unterminated quotation");
1455 SafeReturn(TRUE);
1456 }
1457 cmds=ExplodeCmds(str);
1458 }
1459 else
1460 cmds=({""});
1461 arg=strip_string(cmds[0]);
1462 }
1463 else
1464 {
1465 cmds[0]=strip_string(cmds[0]);
1466 TK("ParseLine: cmds[0]: "+cmds[0]);
1467 if(sscanf(cmds[0],"%s %s",verb,arg)!=2)
1468 {
1469 verb=cmds[0];
1470 arg="";
1471 }
1472 }
1473 cmds=cmds[1..];
1474 TK("ParseLine: verb: "+verb);
1475 if (!verb||!sizeof(verb)||!GetFunc(verb,TRUE))
1476 SafeReturn(FALSE);
1477 TK("ParseLine(1): arg: "+arg+" cmds: "+sprintf("%O",cmds));
1478 switch(sizeof(cmds))
1479 {
1480 case 0:
1481 ret=CallFunc(verb,strip_string(UnquoteLine(arg)));
1482 SafeReturn(ret);
MG Mud User88f12472016-06-24 23:31:02 +02001483
1484 case 1:
1485 WDLN("Missing rhs of command pipe");
1486 SafeReturn(TRUE);
MG Mud User88f12472016-06-24 23:31:02 +02001487
1488 default:
1489 pipe_out=TRUE;
1490 switch(cmds[0])
1491 {
1492 case "|":
1493 pipe_of=PIPE_FILE;
1494 pipe_ovr=TRUE;
1495 cmds=cmds[1..];
1496 break;
1497
1498 case ">":
1499 pipe_ovr=TRUE;
1500 if(sizeof(cmds)!=2)
1501 {
1502 WDLN("Illegal IO redirection");
1503 SafeReturn(TRUE);
1504 }
1505 pipe_of=cmds[1];
1506 cmds=({});
1507 break;
1508
1509 case ">>":
1510 pipe_ovr=FALSE;
1511 if(sizeof(cmds)!=2)
1512 {
1513 WDLN("Illegal IO redirection");
1514 SafeReturn(TRUE);
1515 }
1516 pipe_of=cmds[1];
1517 cmds=({});
1518 break;
1519 }
1520 }
1521 TK("ParseLine(2): arg: "+arg+" cmds: "+sprintf("%O",cmds));
1522 if(!CallFunc(verb,strip_string(arg)))
1523 SafeReturn(FALSE);
1524 pipe_in=pipe_out;
1525 pipe_if=pipe_of;
1526 pipe_ovr=FALSE;
1527 pipe_out=FALSE;
1528 pipe_of=NULL;
1529 if(sizeof(cmds))
1530 call_out("ParseLine",0);
1531 else
1532 SafeReturn(TRUE);
1533 return TRUE;
1534}
1535
1536static int CallFunc(string verb, string str)
1537{
1538 string fun;
1539
1540 fun=GetFunc(verb,FALSE);
1541#ifdef XDBG
1542 TK("CallFunc: verb: "+verb+" str: "+str);
1543 TK("CallFunc: resolved function: "+(fun?fun:"(unresolved)"));
1544#endif
1545 if(str=="")
1546 str=NULL;
bugfixaf2be4f2020-03-22 19:13:07 +01001547 return fun?({int})call_other(ME,fun,str):FALSE;
MG Mud User88f12472016-06-24 23:31:02 +02001548}
1549
1550static string GetFunc(string verb, int test)
1551{
1552 string fun,*keys,key;
1553 int i,len;
1554
1555 TK("GetFunc: verb: "+verb);
1556
1557 if(verb[0..0]!="x") // Assume all commands start with "x"
1558 return 0;
1559
bugfixaf2be4f2020-03-22 19:13:07 +01001560 if (!(fun=ACTIONS[verb,0])) { // Try exact hit first
MG Mud User88f12472016-06-24 23:31:02 +02001561 key="";
1562 len=sizeof(verb);
1563 for (i=sizeof(keys=m_indices(ACTIONS))-1;i>=0;i--) {
1564 TK(" trying: "+keys[i]);
1565 if(sizeof(keys[i])>=len&&keys[i][0..len-1]==verb) {
1566 if(sizeof(key)) {
1567 WLN("Das ist nicht eindeutig ...");
1568 return 0;
1569 }
1570 fun=ACTIONS[keys[i],0];
1571 key=keys[i];
1572 //break;
1573 }
1574 }
1575 } else
1576 key=verb;
1577
1578 if(test)
1579 return fun;
1580
1581 if (key) {
1582#ifdef XDBG
1583 TK("GetFunc: fun: "+fun+" (key: "+key+")\n"+
1584 "pipe_in: "+(pipe_in?"TRUE ":"FALSE ")+(pipe_if?pipe_if:"(NULL)")+"\n"+
1585 "pipe_out: "+(pipe_out?"TRUE ":"FALSE ")+(pipe_of?pipe_of:"(NULL)")+"\n"+
1586 "pipe_ovr: "+(pipe_ovr?"TRUE":"FALSE"));
1587#endif
1588 if (pipe_in&&!ACTIONS[key,PIPE_IN])
1589 {
1590 // this command does not read pipes
1591#ifdef XDBG
1592 TK("Illegal rhs of command pipe \""+fun+"\"\n");
1593#endif
1594 notify_fail("Illegal rhs of command pipe \""+fun+"\"\n");
1595 return 0;
1596 }
1597 else if (pipe_out&&!ACTIONS[key,PIPE_OUT])
1598 {
1599 // this command does not feed pipes
1600#ifdef XDBG
1601 TK("Illegal lhs of command pipe \""+fun+"\"\n");
1602#endif
1603 notify_fail("Illegal lhs of command pipe \""+fun+"\"\n");
1604 return 0;
1605 }
1606 }
1607 return fun;
1608}
1609
1610void actions()
1611{
1612 if (!cloner||!RTP||cloner==RTP||query_wiz_level(cloner)<=query_wiz_level(RTP))
1613 add_action("ParseLine","",1);
1614 add_action("CommandScan", "", 1);
1615}
1616
1617/*----------------------------------------------------------------------
1618 * the checking stuff
1619 */
1620
Zesstra7e95e3f2019-10-19 11:15:05 +02001621public <int|object>* insert_hook(object pl, int hookid, object ob)
MG Mud User88f12472016-06-24 23:31:02 +02001622{
Zesstra7e95e3f2019-10-19 11:15:05 +02001623 if(cloner && cloner == pl && hookid == H_HOOK_INSERT)
1624 {
1625 if(MODE(MODE_FIRST) && find_call_out("move")==-1)
1626 call_out("move",0,cloner);
1627 if(MODE(MODE_INVCHECK))
1628 write_newinvobj(ob);
1629 }
1630 return ({H_NO_MOD, ob});
1631}
1632
1633void add_insert_hook()
1634{
1635 if(objectp(cloner))
bugfixaf2be4f2020-03-22 19:13:07 +01001636 ({int})cloner->HRegisterToHook(H_HOOK_INSERT, #'insert_hook,
Zesstra7e95e3f2019-10-19 11:15:05 +02001637 H_HOOK_LIBPRIO(2), H_LISTENER, 0);
MG Mud User88f12472016-06-24 23:31:02 +02001638}
1639
1640static void VarCheck(int show)
1641{
MG Mud User88f12472016-06-24 23:31:02 +02001642 foreach(string k, mixed v : variable)
1643 {
1644 if (v) continue;
1645 if(show) WDLN("*** Variable $"+k+" has been destructed");
1646 m_delete(variable, k);
1647 }
1648}
1649
1650
1651int write_newinvobj(object obj)
1652{
1653 if(obj) WDLN("*** New object in inventory "+ObjFile(obj));
1654 return(1);
1655}
1656
1657/*----------------------------------------------------------------------
1658 * catch all commands, absorb forces and check history
1659 */
1660
1661int CommandScan(string arg)
1662{
Arathorn65b45e02022-02-07 22:03:57 +01001663 string verb;
MG Mud User88f12472016-06-24 23:31:02 +02001664 object rtp;
1665 rtp=RTP;
1666
1667 if(!cloner&&!(cloner=rtp)) destruct(ME);
1668
1669 if((!MODE(MODE_PROTECT))||security()||
1670 query_wiz_level(cloner)<query_wiz_level(rtp))
1671 {
1672 verb=query_verb();
1673 if(verb&&DoHistory(verb+(arg ? " "+arg : "")))
1674 return TRUE;
1675 nostore=FALSE;
1676 return FALSE;
1677 }
1678 else
1679 {
1680 if(rtp)
1681 {
1682 WDLN("Your "+TOOL_TITLE+" protects you from a force by "+crname(rtp)+
1683 " ["+query_verb()+(arg ? " "+arg+"]" : "]"));
1684 tell_object(rtp, crname(cloner)+"'s "+TOOL_TITLE+
1685 " absorbes your force.\n");
1686 }
1687 else
1688 {
1689 WDLN("Your "+TOOL_TITLE+" protects you from a force ["+
1690 query_verb()+(arg ? " "+arg+"]" : "]"));
1691 }
1692 return TRUE;
1693 }
1694}
1695
1696int DoHistory(string line)
1697{
1698 int i;
1699 string cmd, *strs;
1700
1701 SECURE2(FALSE);
1702 if(!stringp(line) || !sizeof(line))
1703 return TRUE;
1704 else if(line=="%!")
1705 {
1706 WLN("Current command history:");
1707 for(i=MAX_HISTORY; i; --i)
1708 if(history[i-1])
1709 {
1710 W(" "+ARIGHT(""+i, 2, " ")+": ");
1711 if(sizeof(history[i-1])>70)
1712 WLN(ALEFT(history[i-1], 70, " "));
1713 else
1714 WLN(history[i-1]);
1715 }
1716 return TRUE;
1717 }
1718 else if(line[0..1]=="%%" && (cmd=history[0]+line[2..<1]))
1719 {
1720 Command(cmd);
1721 return TRUE;
1722 }
1723 else if(line[0]=='^'&&(strs=strip_explode(line, "^")))
1724 {
1725 if(sizeof(strs)&&strs[0]&&(cmd=history[0]))
1726 {
1727 if(sizeof(strs)==2)
1728 cmd=string_replace(cmd, strs[0], strs[1]);
1729 else
1730 cmd=string_replace(cmd, strs[0], "");
1731 nostore--;
1732 Command(cmd);
1733 nostore++;
1734 return TRUE;
1735 }
1736 }
1737 else if(line[0]=='%' && (sscanf(line[1..<1], "%d", i)))
1738 {
1739 i= i>0 ? i : 1;
1740 i= i<=MAX_HISTORY ? i : MAX_HISTORY;
1741 if(cmd=history[i-1])
1742 Command(cmd);
1743 return TRUE;
1744 }
1745 else if(line[0]=='%')
1746 {
1747 for(i=0; i<MAX_HISTORY; i++)
1748 {
1749 if(history[i]&&
1750 history[i][0..sizeof(line)-2]==line[1.. <1])
1751 {
1752 Command(history[i]);
1753 return TRUE;
1754 }
1755 }
1756 }
1757 else if(nostore<1)
1758 history=({line})+history[0..MAX_HISTORY-2];
1759 return FALSE;
1760}
1761