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