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