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