Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/obj/tools/MGtool.c b/obj/tools/MGtool.c
new file mode 100644
index 0000000..727f34f
--- /dev/null
+++ b/obj/tools/MGtool.c
@@ -0,0 +1,1823 @@
+/*
+ *  MGtool-1.3
+ *  File: MGtool.c
+ *  Maintainer: Kirk@MorgenGrauen
+ */
+
+/*------------------------------------------*/
+/* the original Xtool is copyrighted by Hyp */
+/*------------------------------------------*/
+
+#pragma strict_types
+
+inherit "/std/thing/properties";
+
+#include <properties.h>
+#if !defined(QUERYCACHED)
+#define QUERYCACHED 4096
+#endif
+#if !defined (SETCACHED)
+#define SETCACHED 4096
+#endif
+#include <moving.h>
+#include "/secure/wizlevels.h"
+#include "/secure/config.h"
+#include <userinfo.h>
+#include <defines.h>
+
+#include "MGtool/tool.h"
+
+#include "MGtool/toollib.h"
+
+#define XDBG 1
+
+nosave object  cloner;
+nosave object  msgto=NULL;
+nosave string *manpath=({TOOL_PATH+"/man.d/",
+			 "/doc/efun/",
+			 "/doc/lfun/",
+			 "/doc/w/",
+			 "/doc/helpdir/",
+			 "/doc/LPC/",
+			 "/doc/std/",
+			 "/doc/concepts/",
+			 ""});
+nosave string  morefile=NULL;
+nosave string *scriptline=NULL;
+nosave string *history=allocate(MAX_HISTORY);
+nosave int     moreflag=FALSE;
+nosave int     moreoffset=1;
+nosave int     term=NULL;
+nosave int     scriptsize=NULL;
+nosave int     nostore=FALSE;
+nosave int     xlight=0;
+nosave int     pipe_in=FALSE;
+nosave int     pipe_out=FALSE;
+nosave int     pipe_ovr=TRUE;
+nosave string  pipe_if=NULL;
+nosave string  pipe_of=NULL;
+nosave int     xtk=FALSE;
+nosave mapping variable=([]);
+nosave string *cmds;
+private nosave mapping line_buffer=([]);
+private nosave string line_buffer_name="";
+private nosave string more_searchexpr="";
+int    morelines=MORE_LINES;
+int    modi=(MODE_FIRST|MODE_PROTECT|MODE_SHORT);
+
+#include "MGtool/toollib.c"
+#include "MGtool/toolcmd.c"
+
+#define SafeReturn(x) \
+{ \
+  cmds=({}); \
+  pipe_in=FALSE; \
+  pipe_of=NULL; \
+  pipe_ovr=TRUE; \
+  pipe_out=FALSE; \
+  pipe_of=NULL; \
+  return (x); \
+}
+
+/*----------------------------------------------------------------------
+ * check some security aspects
+ */
+
+static int security()
+{
+  object prev;
+ 
+  if( process_call() || getuid()!=getuid(cloner) ) return FALSE; // Rumata
+
+  TK("UID: "+getuid(ME)+" cloner:"+getuid(cloner));
+//  TK("prev: "+object_name(PREV)+" me:"+object_name(ME));
+  if(prev=PREV)
+  {
+    if(!cloner)
+      return TRUE;
+    if(getuid(prev)==ROOTID||IS_ARCH(prev))
+      return TRUE;
+    if(prev==ME)
+      return TRUE;
+    return secure_level()>=query_wiz_level(cloner); // Rumata
+    //return getuid(prev)==getuid()&&geteuid(prev)==geteuid()&&cloner==RTP;
+  }
+  else
+    return cloner==NULL;
+}
+
+/*----------------------------------------------------------------------
+ * own write function
+ */
+
+static int Write(string str)
+{
+  if(!stringp(str) || str=="")
+    return FALSE;
+  if(!cloner && objectp(this_player()))
+    write(str);
+  else
+    tell_object(cloner, str);
+  return TRUE;
+}
+
+/*----------------------------------------------------------------------
+ * own command function
+ */
+
+static int Command(string str)
+{
+  int i;
+  TK("Command: str: "+(str?str:"(NULL)"));
+  nostore++;
+  if(MODE(MODE_ECHO))
+    WLN("Doing: "+str);
+  i=(int)cloner->command_me(str);
+  nostore--;
+  return i;
+}
+
+/*----------------------------------------------------------------------
+ * object searching
+ */
+
+static varargs object XFindObj(string str, int silent)
+{
+  object obj, env;
+  string *strs;
+  int i, s, cnt;
+  
+  if(!str)
+    return NULL;
+  TK("XFindObj: str: "+(str?str:"(NULL)"));
+  env=ENV(cloner);
+  str=string_replace(str, "\\.","°01");
+  str=string_replace(str, "\\^", "°02");
+  str=string_replace(str, "\\$", "°03");
+  str=string_replace(str, "\\\\", "\\");
+  if (find_object(str)) return find_object(str);
+  if (file_size(str)>1) {
+     call_other(str, "???");
+     return find_object(str);
+  }
+  s=sizeof(strs=strip_explode(str, "."));
+  while(s--)
+  {
+    if(strs[i]=="m"||strs[i]=="me")
+      strs[i]=RNAME(cloner);
+    else if(strs[i]=="h"||strs[i]=="here")
+      strs[i]=object_name(ENV(cloner));
+    if(obj=FindObj(strs[i++],env,(silent?1:0)))
+      env=obj;
+    else
+      break;
+  }
+  return obj;
+}
+
+static varargs object FindObj(string str, object env, int silent)
+{
+  object obj, *inv;
+  string tmp;
+  int num, e;
+  
+  if (!stringp(str) || !sizeof(str) || !objectp(env))
+    return NULL;
+  str=string_replace(str, "°01", ".");
+  while(str[e++]=='^')
+    ;
+  str=str[--e..<1];
+  str=string_replace(str, "°02", "^");
+  if(obj=VarToObj(str))
+    ;
+  else if(str[0]=='')
+    str=string_replace(str, "°03", "$");
+  else if(sscanf(str, "%d", num)&&(inv=all_inventory(env)))
+  {
+    if(num>0&&num<=sizeof(inv))
+      obj=inv[num-1];
+    else
+    {
+      WDLN("Specified object number out of range [1-"+sizeof(inv)+"]");
+      return NULL;
+    }
+  }
+  if(obj||(obj=present(str, env))||
+     (obj=find_player(LOWER(str)))||
+     (obj=find_living(str))||
+     (obj=find_object(long_path(str))))
+  {
+    while(e--)
+    {
+      if(!(obj=ENV(obj)))
+      {
+	W("Specified object has no environment [");
+	while(e--)
+	  W("^");
+	WDLN(str+"]");
+	return NULL;
+      }
+    }
+  }
+  else
+    if(!silent)
+      WDLN("Specified object does not exist ["+str+"]");
+  return obj;
+}
+
+/*----------------------------------------------------------------------
+ * object variable handling
+ */
+
+static object VarToObj(string str)
+{
+  if (!stringp(str) || !sizeof(str) || str[0]!='$')
+    return NULL;
+  switch(str)
+  {
+    case "$m":
+    case "$me":
+    return cloner;
+    case "$h":
+    case "$here":
+    return ENV(cloner);
+    default:
+    return variable[str[1..<1]];
+  }
+  return(NULL); //never reached
+}
+
+static string VarToFile(string str)
+{
+  return source_file_name(VarToObj(str));
+}
+
+static string VarToPureFile(string str)
+{
+  return pure_file_name(VarToObj(str));
+}
+
+/*----------------------------------------------------------------------
+ * object description printing
+ */
+
+static void PrintObj(object obj, string file)
+{
+  object item, tmp;
+  string str;
+  int i;
+
+  SECURE1();
+  if(!obj)
+    return;
+  PrintShort("Short: ", obj, file);
+  if(!file||file=="")
+    WLN("Long :");
+  else
+    write_file(file,"Long :\n");
+  if(query_once_interactive(obj))
+    str=capitalize(getuid(obj));
+  else
+  {
+    if(object_name(obj)[0..26]=="/d/unterwelt/objekte/teddy#" &&
+       IS_ARCH(this_interactive()))
+      str="Ein Teddy (stumm)";
+    else
+    {
+      if(str=(string)obj->QueryProp(P_INT_LONG))
+	;
+      else if(str=(string)obj->QueryProp(P_LONG))
+	;
+      else
+	str="- no long description -\n";
+    }
+  }
+  if(!file||file=="")
+    W(str);
+  else
+    write_file(file,str);
+  FORALL(item, obj)
+  {
+    if(!i)
+      if(!file||file=="")
+	WLN("Content:");
+      else
+	write_file(file,"Content:\n");
+    PrintShort(ARIGHT(++i+". ", 4, " "), item, file);
+  }
+}
+
+static string ObjFile(object obj)
+{
+  return "["+short_path(object_name(obj))+"]";
+}
+
+static varargs void PrintShort(string pre, object obj, string file)
+{
+  string str;
+
+  SECURE1();
+  if(!obj)
+    return;
+  if(MODE(MODE_SHORT))
+  {
+    if (query_once_interactive(obj))
+      str=capitalize(getuid(obj));
+    else
+    {
+      if(!((str=(string)obj->QueryProp(P_INT_SHORT))||
+	   (str=(string)obj->QueryProp(P_SHORT))))
+	if(is_player(obj))
+	  str=CRNAME(obj)+" (invisible)";
+	else
+	  str="- no short description -";
+    }
+    str=ALEFT(sprintf("%O ",str), 34, ".")+" ";
+  }
+  else
+    str="";
+  if(interactive(obj))
+    str+="i";
+  else if(living(obj))
+    str+="l";
+  else
+    str+=".";
+  str+=(shadow(obj, 0) ? "s" : ".");
+  if(!file||file=="")
+    WLN((pre+CAP(str)+" "+ObjFile(obj))[0..79]);
+  else
+    write_file(file,(pre+CAP(str)+" "+ObjFile(obj))[0..79]+"\n");
+}
+
+static varargs void DeepPrintShort(object env, int indent, string pre, string file)
+{
+  int i;
+  object item;
+  string str;
+  
+  SECURE1();
+  if(!env)
+    return;
+  for(i=indent,str=""; i--; str+="  ");
+  if(pre)
+    str+=pre;
+  if(!file||file=="")
+    W(str);
+  else
+    write_file(file,str);
+  i++;
+  PrintShort("",env,file);
+  FORALL(item, env)
+    DeepPrintShort(item,indent+1,ARIGHT((++i)+". ",4," "),file);
+}
+
+static string break_string_hard(string str, int len, string pre)
+{
+  int s,p,t;
+  string tmp;
+
+  if(!str||!(s=sizeof(str)))
+    return "";
+  t=len-(p=sizeof(pre))-1;
+
+  tmp="";
+  while(p+s>len)
+  {
+    tmp+=pre+str[0..t]+"\n";
+	str=str[t+1..];
+	s-=t;
+  }
+  if(sizeof(str))
+    tmp+=pre+str[0..]+"\n";
+  return tmp;
+}
+  
+static void dprop(string key, mixed data, object obj)
+{
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,break_string_hard(mixed_to_string(obj->QueryProp(key),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+  else
+    W(break_string_hard(mixed_to_string(obj->QueryProp(key),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+}
+
+static string propflags(string key, object ob)
+{
+  int tmp;
+  string *flags;
+  tmp=(int)ob->Query(key,1);
+  flags=({});
+  tmp&SAVE ?	       flags+=({"SAV"}) : flags+=({"   "});
+  tmp&PROTECTED ?      flags+=({"PRO"}) : flags+=({"   "});
+  tmp&SECURED ?        flags+=({"SEC"}) : flags+=({"   "});
+  tmp&NOSETMETHOD ?    flags+=({"NSM"}) : flags+=({"   "});
+  tmp&SETMNOTFOUND ?   flags+=({"SMN"}) : flags+=({"   "});
+  tmp&QUERYMNOTFOUND ? flags+=({"QMN"}) : flags+=({"   "});
+  tmp&QUERYCACHED ?    flags+=({"QCA"}) : flags+=({"   "});
+  tmp&SETCACHED ?      flags+=({"SCA"}) : flags+=({"   "});
+  return ""+implode(flags,"|");
+}
+
+static void dprop2(string key, mixed data, object ob)
+{
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,break_string_hard(mixed_to_string(propflags(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+  else
+    W(break_string_hard(mixed_to_string(propflags(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+}
+
+static mixed propmethods(string key, object ob)
+{
+  return (mixed)ob->Query(key,2);
+}
+
+static void dprop3(string key, mixed data, object ob)
+{
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,break_string_hard(mixed_to_string(propmethods(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+  else
+    W(break_string_hard(mixed_to_string(propmethods(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+}
+
+static void DumpProperties(object obj, int flag)
+{
+  SECURE1();
+  if(!obj)
+    return;
+  PIPE_DELETE(pipe_of);
+  switch (flag) {
+    case 0:
+      walk_mapping(((mapping *)(obj->__query_properties()))[0],#'dprop,obj); //')
+      break;
+    case 1:
+      walk_mapping(((mapping *)(obj->__query_properties()))[0],#'dprop2,obj); //')
+      break;
+    case 2:
+      walk_mapping(((mapping *)(obj->__query_properties()))[0],#'dprop3,obj); //')
+      break;
+  }
+}
+
+/*----------------------------------------------------------------------
+ * moving objects
+ */
+
+static int MoveObj(object obj1, object obj2, int silent)
+{
+  int err;
+  object oldenv;
+
+  SECURE2(FALSE);
+  if(!(obj1&&obj2))
+    return FALSE;
+  oldenv=ENV(obj1);
+  err=(int)obj1->move(obj2, M_SILENT|M_NOCHECK);
+  if(!silent)
+    switch(err)
+    {
+      case ME_PLAYER:
+      WDLN("Object returned ME_PLAYER");
+      if(oldenv && oldenv != ENV(obj1))
+	WDLN("Object has been moved");
+      return FALSE;
+      case ME_TOO_HEAVY:
+      WDLN("Object returned ME_TOO_HEAVY");
+      if(oldenv && oldenv != ENV(obj1))
+	WDLN("Object has been moved");
+      return FALSE;
+      case ME_CANT_TPORT_IN:
+      WDLN("Object returned ME_CANT_TPORT_IN");
+      if(oldenv && oldenv != ENV(obj1))
+	WDLN("Object has been moved");
+      return FALSE;
+      case ME_CANT_TPORT_OUT:
+      WDLN("Object returned ME_CANT_TPORT_OUT");
+      if(oldenv && oldenv != ENV(obj1))
+	WDLN("Object has been moved");
+      return FALSE;
+      default:
+      WDLN("Object returned unknown return value");
+      return FALSE;
+    }
+  return TRUE;
+}
+
+/*----------------------------------------------------------------------
+ * save destructing of objects
+ */
+
+static void Destruct(object obj)
+{
+  if(!obj || !this_object())
+    return;
+  catch(obj->remove());
+  if(objectp(obj) && !query_once_interactive(obj))
+    destruct(obj);
+}
+
+static void DeepClean(object obj)
+{
+  if(!obj)
+    return;
+  filter(filter(deep_inventory(obj), "is_not_player", ME),
+	       "Destruct", ME);
+  if(is_not_player(obj))
+    Destruct(obj);
+}
+
+/*----------------------------------------------------------------------
+ * Show the inheritance tree of objects
+ */
+
+static object *SubNodes(object obj)
+{
+  int s;
+  object *objs;
+  string *inlist;
+  
+  if(!obj)
+    return NULL;
+  inlist=inherit_list(obj);
+  s=sizeof(inlist);
+  objs=({});
+  while(s-->1)
+    objs=({find_object(inlist[s])})+objs;
+  return objs;
+}
+
+static void Inheritance(object obj, string func, string pre)
+{
+  int i, s;
+  object *ln;
+  string str;
+  
+  if(!obj)
+    return;
+  str=pre+" "+ObjFile(obj);
+  if(func)
+  {
+    str=ALEFT(str+" ", 50, ".");
+    if(function_exists(func, obj)==object_name(obj))
+      str+=ARIGHT(" "+func, 19, ".");
+    else
+      str+=ARIGHT("", 19, ".");
+  }
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,str+"\n");
+  else
+    WLN(str);
+  ln=SubNodes(obj);
+  for(i=0; i<sizeof(ln); i++)
+    ln=ln-SubNodes(ln[i]);
+  s=sizeof(ln);
+  for(i=0; i<s; i++)
+    Inheritance(ln[i], func, pre+".....");
+}
+
+/*----------------------------------------------------------------------
+ * file name handling
+ */
+
+static string XFile(string file)
+{
+  TK("XFile: file: "+(file?file:"(NULL)"));
+  if(file)
+    switch(file[0])
+    {
+      case '@':
+	return source_file_name(XFindObj(file[1..<1]));
+      case '$':
+	return source_file_name(XFindObj(file));
+      default:
+	return old_explode(long_path(file),"#")[0];
+    }
+  return NULL;
+}
+
+static string XFindFile(string file)
+{
+  TK("XFindFile: file: "+(file?file:"(NULL)"));
+  if(file=XFile(file))
+  {
+    if(file_size(file)>=0)
+      return file;
+    if(file[<3..<1]!=".c" && file_size(file+".c")>0)
+      return file+".c";
+  }
+  WDLN("File not found or not readable ["+short_path(file)+"]");
+  return NULL;
+}
+
+/*----------------------------------------------------------------------
+ * file printing, searching and executing
+ */
+
+static void XMoreFile(string file, int flag)
+{
+  int s,size;
+  
+  SECURE1();
+  if(!file)
+    return;
+  
+  // term=(string)cloner->QueryProp(P_TTY)!="dumb";
+  if((size=(file_size(morefile=long_path(file))))>0)
+  {
+    if(size>100000)
+      WDLN("Warning: large file");
+    MoreFile(NULL);
+  }
+  else if(flag)
+    WDLN("Cannot read file");
+}
+
+/*
+static string read_buffer(string filename, int start, int number)
+{
+  mixed tmp;
+  string result;
+  int current;
+
+  if(!filename||filename=="")
+    return "";
+  if(number<=1)
+    number=1;
+  number--;
+  result="";
+  if(filename!=line_buffer_name)
+  {
+    // TK("read_buffer: initializing");
+    line_buffer=([]);
+    line_buffer_name=filename;
+    current=0;
+  }
+  else
+    current=start;
+
+  while(current<=start+number && stringp((tmp=load_buffer(current++))))
+    if(current>=start)
+      result+=tmp;
+  return result;
+}
+
+static string load_buffer(int line)
+{
+  int i, current, start, length, *idx;
+
+  if(intp(line_buffer[line]))
+  {
+    // TK("load_buffer: Notfound: "+line);
+    current=line;
+    while(current>0&&intp(line_buffer[--current]));
+    for(i=current;i<=line;i++)
+    {
+      // TK("load_buffer: trying: "+i);
+      if(i>0)
+	idx=line_buffer[i-1];
+      else
+	idx=({0,0});
+      length=read_line((start=idx[0]+idx[1]));
+      // TK("load_buffer: "+start+"/"+length);
+      line_buffer[i]=({start,length});
+    }
+  }
+  idx=line_buffer[line];
+  return
+    read_bytes(line_buffer_name,idx[0],idx[1]);
+}
+
+static int read_line(int offset)
+{
+  mixed loc_buf;
+  int length, pos;
+
+  // TK("read_line: "+offset);
+  length=0;
+  while(!intp((loc_buf=read_bytes(line_buffer_name,offset+length,1024))))
+  {
+    if((pos=member(loc_buf,'\n'))>=0)
+      return length+pos+1;
+    length+=1024;
+  }
+  return length;
+}
+*/
+
+static void MoreFile(string str)
+{
+  int i, off;
+  string f, l, r;
+  
+  SECURE1();
+  
+  if (str /*&& sizeof(str)*/)
+  {
+		if( !sizeof(str) ) str="\0";
+    if(term)
+      W("M");
+    switch(str[0])
+    {
+      case 'q':
+      case 'x':
+      moreflag=FALSE;
+      moreoffset=1;
+      if(morefile==TMP_FILE||morefile==PIPE_FILE)
+	rm(morefile);
+      return NULL;
+      break;
+      case 'P':
+      case 'U':
+      moreflag=FALSE;
+      moreoffset=moreoffset-morelines;
+      case 'p':
+      case 'u':
+      moreoffset=moreoffset-2*morelines;
+      break;
+      case 'D':
+      case 'N':
+      moreoffset+=morelines;
+      case 0:	/* RETURN */
+      case 'd':
+      if(moreflag)
+      {
+	moreflag=FALSE;
+	moreoffset=1;
+	if(morefile==TMP_FILE)
+	  rm(morefile);
+	return;
+      }
+      break;
+      case '/':
+      moreoffset--;
+      more_searchexpr=str[1..<1];
+      case 'n':
+      i=moreoffset-morelines+1;
+      if(more_searchexpr=="")
+      {
+	WDLN("No previous regular expression");
+	return;
+      }
+      if(!regexp(({"dummy"}), more_searchexpr))
+	WDLN("Bad regular expression");
+      else
+	while((l=read_file(morefile, i++, 1))&&
+	      !sizeof(regexp(({l}), more_searchexpr)))
+	  ;
+      if(l)
+      {
+	WLN("*** Skipping ...");
+	moreoffset=i-1;
+      }
+      else
+      {
+	WLN("*** Pattern not found");
+	moreoffset-=morelines;
+      }
+      break;
+      case '0'..'9':
+      sscanf(str, "%d", i);
+      moreoffset=i;
+      break;
+    }
+  }
+  else
+  {
+    moreoffset=1;
+    moreflag=FALSE;
+  }
+  if(moreoffset<1)
+    moreoffset=1;
+  if(CatFile())
+    W("*** More: q,u,U,d,D,/<regexp>,<line> ["+(moreoffset-1)+"] *** ");
+  else
+  {
+    W("*** More: q,u,U,d,D,/<regexp>,<line> ["+(moreoffset-1)+"=EOF] *** ");
+    moreflag=TRUE;
+  }
+  input_to("MoreFile");
+  return;
+}
+
+// Schade eigentlich das ich es neuschreiben musste, aber es ist
+// schneller neu geschrieben als durch die undokumentieren alten Funktionen
+// durchzusteigen... *seufz*
+//					  Padreic
+
+static string last_file, *last_file_buffer;
+static int    last_file_date, last_file_size, last_file_complete;
+
+static string sread_line(int num)
+{
+   if (!morefile) return "";
+   if (last_file!=morefile || last_file_date!=file_time(morefile)) {
+      if (!(last_file=read_bytes(morefile, 0, 50000))) return "";
+      last_file_date=file_time(morefile);
+      last_file_buffer=explode(last_file, "\n");
+      last_file_size=sizeof(last_file_buffer);
+      if (sizeof(last_file)==50000 && last_file[<1]!='\n') {
+	 last_file_size--; // letzte Zeile nicht vollstaendig gelesen
+	 last_file_complete=0;
+      }
+      else if (file_size(morefile)>50000)
+	last_file_complete=0;
+      else last_file_complete=1;
+      last_file=morefile;
+   }
+   if (num==0) num=1;
+   // bei zu grossen Files nicht mehr alles buffern...
+   if (num>last_file_size) {
+      if (last_file_complete) return "";
+      return (read_file(morefile, num, 1) || "");
+   }
+   return last_file_buffer[num-1]+"\n";
+}
+
+static int CatFile()
+{
+  int end;
+  string l;
+  
+  end=moreoffset+morelines;
+  while(moreoffset<end)
+//    if((l=read_file(morefile, moreoffset, 1))!="")
+    if((l=sread_line(moreoffset))!="")
+    {
+      moreoffset++;
+      W(l);
+    }
+    else
+      return FALSE;
+//  if(read_file(morefile, moreoffset+1, 1)!="")
+  if(sread_line(moreoffset+1)!="")
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static int XGrepFile(string pat, string file, int mode)
+{
+  int i, j, f, s, fsize;
+  string tfile, *tmp, *ts, t;
+  
+  SECURE2(FALSE);
+  TK("XGrepFile: pat: "+pat+" file: "+file+" mode: "+mode);
+  if(!(pat&&file))
+    return FALSE;
+  tfile=TMP_FILE;
+  fsize=file_size(file);
+  for(i=0,f=0; i<fsize && t=read_bytes(file, i, 50000); i+=50000)
+    tmp=strip_explode(t,"\n");
+    if (t && t[<1]!='\n' && sizeof(t)==50000) {
+       i-=sizeof(tmp[<1]);
+       tmp=tmp[0..<2];
+    }
+    if(s=sizeof(tmp))
+    {
+      for(j=0;j<s;j++)
+      {
+	if(sizeof(ts=regexp(({(mode&XGREP_ICASE?lower_case(tmp[j]):tmp[j])}),pat)))
+	{
+	  if(!(mode&XGREP_REVERT))
+	   {
+	     if(!f++)
+	       write_file(tfile, "*** File: "+file+" ***\n");
+	     write_file(tfile, tmp[j]+"\n");
+	   }
+	}
+	else if(mode&XGREP_REVERT)
+	{
+	  if(!f++)
+	    write_file(tfile, "*** File: "+file+" ***\n");
+	  write_file(tfile, tmp[j]+"\n");
+	}
+      }
+    }
+  return TRUE;
+}
+
+static void XExecFile(int line)
+{
+  int i;
+  
+  if(!scriptline)
+    return;
+  for(i=line; i<scriptsize&&i<line+EXEC_LINES; i++)
+  {
+    if(!scriptline[i])
+      continue;
+    if(!Command(scriptline[i]))
+    {
+      scriptline=NULL;
+      return;
+    }
+  }
+  if(i<scriptsize)
+    call_out("XExecFile", EXEC_TIME, i);
+  else
+    scriptline=NULL;
+}
+
+static void XmtpScript(string dir, string file, string opt)
+{
+  int s, t;
+  string *files;
+  
+  s=sizeof(files=get_dir(dir+"/*"));
+  while(s--)
+  {
+    t=sizeof(files[s])-1;
+    if(files[s] == ".." || files[s] == "." || files[s][t] == '~' ||
+       (files[s][0] == '#' && files[s][t] == '#'))
+      continue;
+    if(file_size(dir+"/"+files[s])==-2)
+    {
+      write_file(file, "mkdir "+files[s]+" ; cd "+files[s]+"\n");
+      XmtpScript(dir+"/"+files[s], file, opt);
+      write_file(file, "cd ..\n");
+    }
+    else
+      write_file(file, "mtp -r "+opt+" "+dir+"/"+files[s]+"\n");
+  }
+}
+
+/*----------------------------------------------------------------------
+ *  player properties handling
+ */
+
+static string PlayerIdle(object obj)
+{
+  string str;
+  int i, tmp;
+  
+  if(!obj)
+    return NULL;
+  if((i=query_idle(obj))>=60)
+  {
+    str=ARIGHT(""+(i/3600), 2, "0");
+    i-=(i/3600)*3600;
+    str+="'"+ARIGHT(""+(i/60), 2, "0");
+  }
+  else
+    str=".....";
+  return str;
+}
+
+static string PlayerAge(object obj)
+{
+  string str;
+  int i, tmp;
+  
+  if(!obj)
+    return NULL;
+  i=(int)obj->QueryProp(P_AGE);
+  str=" "+ARIGHT(""+(i/43200), 4, ".");
+  i-=(i/43200)*43200;
+  return str+":"+ARIGHT(""+(i/1800), 2, "0");
+}
+
+static string crname(object who)
+{
+  string uid, lname;
+
+  if((uid=getuid(who))==ROOTID && 
+     object_name(who)[0..7]=="/secure/" &&
+     (lname=(string)who->loginname()))
+    return CAP(lname);
+  return CAP(uid);
+}
+
+static string PlayerWho(object obj)
+{
+  object tmp;
+  string str, stmp;
+  str=ARIGHT(""+LEVEL(obj)  ,  3, " ");
+  str+=ALEFT(" "+crname(obj)+" ", 12, ".");
+  str+=PlayerAge(obj);
+  str+=((int)obj->QueryProp(P_GENDER)==1 ? " m " : " f ");
+  str+=(obj->QueryProp(P_FROG))  ? "f" : ".";
+  str+=(obj->QueryProp(P_GHOST)) ? "g" : ".";
+  str+=(obj->QueryProp(P_INVIS)) ? "i" : ".";
+  str+=(query_editing(obj)||query_input_pending(obj) ? "e" : ".");
+  str+=(obj->QueryProp(P_AWAY))  ? "a" : ".";
+  str+=" "+PlayerIdle(obj)+" ";
+  str+=(tmp=ENV(obj)) ? ObjFile(tmp) : "- fabric of space -";
+  return str;
+}
+
+static string PlayerMail(object obj, int flag)
+{
+  string pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  return pre+"mail: "+obj->QueryProp(P_MAILADDR);
+}
+
+static string PlayerIP(object obj, int flag)
+{
+  string pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  return pre+"host: "+query_ip_name(obj)+" ("+query_ip_number(obj)+")";
+}
+
+static string PlayerRace(object obj, int flag)
+{
+  string tmp, pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  pre=pre+"race: "+ALEFT(obj->QueryProp(P_RACE)+" ", 10, ".")+" guild: ";
+  tmp=(string)obj->QueryProp(P_GUILD);
+  return tmp ? pre+tmp : pre+"- none -";
+}
+
+static string PlayerDomain(object obj, int flag)
+{
+  int i, s;
+  mixed *uinfo;
+  string *domains, pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  pre+="domainlord of: ";
+  uinfo=(mixed*)MASTER->get_userinfo(getuid(obj));
+  if(uinfo&&(domains=uinfo[3])&&(s=sizeof(domains)))
+  {
+    for(i=0; i<s; i++)
+    {
+      pre += CAP(domains[i]);
+      if(i<s-1)
+	pre += ", ";
+    }
+  }
+  return pre;
+}
+
+static string PlayerStats(object obj, int flag)
+{
+  string pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  pre+="hp="+ARIGHT(obj->QueryProp(P_HP), 3, "0");
+  pre+="/"+ARIGHT(obj->QueryProp(P_MAX_HP), 3, "0");
+  pre+=" sp="+ARIGHT(obj->QueryProp(P_SP), 3, "0");
+  pre+="/"+ARIGHT(obj->QueryProp(P_MAX_SP), 3, "0");
+  pre+=" food="+ARIGHT(obj->QueryProp(P_FOOD), 3, "0");
+  pre+="/"+ARIGHT(obj->QueryProp(P_MAX_FOOD), 3, "0");
+  pre+=" drink="+ARIGHT(obj->QueryProp(P_DRINK), 3, "0");
+  pre+="/"+ARIGHT(obj->QueryProp(P_MAX_DRINK), 3, "0");
+  pre+=" exps="+obj->QueryProp(P_XP);
+  return pre;
+}
+
+static string PlayerSnoop(object obj, int flag)
+{
+  string tmp, pre;
+  object victim;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  pre=pre+"is snooped by: ";
+  if(victim=query_snoop(obj))
+    pre+=ARIGHT(" "+crname(victim), 12, ".");
+  else
+    pre+="............";
+  return pre;
+}
+
+static string PlayerCmdAvg(object obj, int flag)
+{
+  string pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  return pre+"cmdavg: "+(int)obj->_query_command_average();
+}
+
+
+/*----------------------------------------------------------------------
+ * msg input to objects
+ */
+
+static void XMsgSay(string str)
+{
+  if(str=="."||str=="**")
+  {
+    WLN("[End of message]");
+    say("[End of message]\n");
+  }
+  else
+  {
+    say(str+"\n");
+    input_to("XMsgSay");
+  }
+}
+
+static void XMsgTell(string str)
+{
+  if(str=="."||str=="**")
+  {
+    WLN("[End of message]");
+    tell_object(msgto, "[End of message]\n");
+  }
+  else
+  {
+    tell_object(msgto, str+"\n");
+    input_to("XMsgTell");
+  }
+}
+
+static void XMsgShout(string str)
+{
+  if(str=="."||str=="**")
+  {
+    WLN("[End of message]");
+    shout("[End of message]\n");
+  }
+  else
+  {
+    shout(str+"\n");
+    input_to("XMsgShout");
+  }
+}
+
+/*----------------------------------------------------------------------
+ * own object moving
+ */
+
+int move(mixed dest)
+{
+  move_object(ME, cloner?cloner:dest);
+  return TRUE;
+}
+
+/*----------------------------------------------------------------------
+ * object id
+ */
+
+int id(string str)
+{
+  if(!security()&&MODE(MODE_SCANCHK)&&RTP&&!IS_ARCH(RTP))
+    WDLN(crname(RTP)+" scanned you (id) ["+query_verb()+"] "+
+	 (PREV ? ObjFile(PREV) : "[destructed object]"));
+  return LOWER(str)==LOWER(TOOL_NAME);
+}
+
+/*----------------------------------------------------------------------
+ * short and long description
+ */
+
+string short()
+{
+  return _query_short()+".\n";
+}
+
+string _query_short()
+{
+  string sh; // added by Rumata
+  if(cloner)
+  {
+    if((!security())&&MODE(MODE_SCANCHK)&&!IS_ARCH(RTP))
+      WDLN(crname(RTP)+" scanned you (short) ["+query_verb()+"] "+
+	   (PREV ? ObjFile(PREV) : "[destructed object]"));
+	if( sh=Query(P_SHORT) ) return sh; // added by Rumata
+    return cloner->name(WESSEN)+" "+TOOL_TITLE+" ["+
+      ctime(time())[11..18]+"]";
+  }
+  return TOOL_TITLE;
+}
+
+string long()
+{
+  return _query_long();
+}
+
+string _query_long()
+{
+  if(cloner&&!security()&&MODE(MODE_SCANCHK)&&!IS_ARCH(RTP))
+  {
+    WDLN(crname(RTP)+" scanned you (long) ["+query_verb()+"] "+
+	 (PREV ? ObjFile(PREV) : "[destructed object]"));
+  }
+  return 
+    "This is "+TOOL_NAME+" version "+TOOL_VERSION+
+    " (maintained by Kirk@MorgenGrauen)\n"+
+    "Original copyright held by Hyp.\n"+
+    "Gamedriver patchlevel: "+__VERSION__+" master object: "+__MASTER_OBJECT__+
+    "\n\nDo 'xhelp' for more information.\n";
+}
+
+string name(mixed dummy1, mixed dummy2)
+{
+  return _query_short();
+}
+
+/*----------------------------------------------------------------------
+ * light stuff
+ */
+
+int _query_light()
+{
+  return xlight;
+}
+
+int _set_light(int x)
+{
+  return xlight;
+}
+
+/*----------------------------------------------------------------------
+ * Autoloading
+ */
+
+mixed *_query_autoloadobj()
+{
+  return AUTOLOAD_ARGS;
+}
+
+void _set_autoloadobj(mixed *args)
+{
+  WLN(TOOL_TITLE+" ...");
+  if(!pointerp(args))
+    ;
+  else if(sizeof(args)!=3)
+    ;
+  else if(!stringp(args[0]))
+    ;
+  else if(!intp(args[1]))
+    ;
+  else if(!intp(args[2]))
+    ;
+  else
+  {
+    if((string)args[0]!=TOOL_INTERNAL)
+    {
+      WLN("*****************************");
+      WLN("***	    NEW EDITION      ***");
+      WLN("***	do 'xtool news' for  ***");
+      WLN("***	 more information    ***");
+      WLN("*****************************");
+    }
+    modi=(int)args[1];
+    morelines=(int)args[2];
+    return;
+  }
+  W("(bad autoload, using default)\n");
+}
+
+/*----------------------------------------------------------------------
+ * creation, updating and initialization stuff
+ */
+
+void update_tool(mixed *args, object obj)
+{
+  SECURE1();
+  if(!(obj&&args))
+    return;
+  Destruct(PREV);
+  _set_autoloadobj(args);
+  move(obj);
+}
+
+void create()
+{
+  object obj;
+  
+  if(member(object_name(),'#')<0)
+    return;
+  if(!cloner&&!((cloner=TP)||(cloner=ENV(ME)))&&!interactive(cloner))
+    destruct(ME);
+  if(!IS_LEARNER(cloner))
+    destruct(ME);
+  SetProp(P_NODROP,"Das waere zu gefaehrlich.\n");
+  SetProp(P_NEVERDROP,1);
+  SetProp(P_NOBUY,1);
+  if(file_size(SAVE_FILE+".o")>0)
+  {
+    WDLN("Loading "+TOOL_TITLE+" settings");
+    restore_object(SAVE_FILE);
+  }
+  if(MODE(MODE_FIRST))
+    call_out("move",0,cloner);
+  call_out("add_insert_hook",1,ME);
+}
+
+void TK(string str)
+{
+  if (!xtk)
+    return;
+  tell_object(cloner,"XTOOL: "+str+"\n");
+}
+
+int Xtk(string str)
+{
+  xtk=!xtk;
+  WDLN("Xtool internal tracing "+(xtk?"enabled":"disabled"));
+  return TRUE;
+}
+
+void add_insert_hook(object obj)
+{
+  if(objectp(obj))
+    cloner->AddInsertHook(obj);
+}
+
+void init()
+{
+  object first, prev;
+  
+  if(member(object_name(),'#')<0) return;
+  first=first_inventory(ENV(ME));
+  if(MODE(MODE_PROTECT)&&is_player(first)&&!IS_ARCH(first))
+  {
+    WDLN("WARNING: "+crname(first)+" tried to move into your inventory");
+    tell_object(first, "You cannot move yourself into "+
+		crname(cloner)+"'s inventory.\n");
+    call_out("DropObj",0,first);
+    return;
+  }
+  else if(MODE(MODE_FIRST)&&first!=ME)
+    move(cloner);
+  else actions();
+}
+
+void DropObj(object obj)
+{
+  if(!obj||!objectp(obj))
+    return;
+  obj->move(ENV(cloner),M_NOCHECK|M_NO_SHOW);
+}
+
+#define ACTIONS\
+([\
+  "xcallouts" : "Xcallouts";0;1,\
+  "xcall"     : "Xcall";0;1,\
+  "xcat"      : "Xcat";1;1,\
+  "xcd"       : "Xcd";0;0,\
+  "xclean"    : "Xclean";0;0,\
+  "xclone"    : "Xclone";0;0,\
+  "xuclone"   : "Xuclone";0;0,\
+  "xcmds"     : "Xcmds";0;1,\
+  "xdbg"      : "Xdbg";0;0,\
+  "xdclean"   : "Xdclean";0;0,\
+  "xddes"     : "Xddes";0;0,\
+  "xdes"      : "Xdes";0;0,\
+  "xdest"     : "Xdes";0;0,\
+  "xdlook"    : "Xdlook";0;1,\
+  "xdo"       : "Xdo";0;0,\
+  "xdupdate"  : "Xdupdate";0;0,\
+  "xecho"     : "Xecho";0;0,\
+  "xeval"     : "Xeval";0;1,\
+  "xforall"   : "Xforall";0;0,\
+  "xgoto"     : "Xgoto";0;0,\
+  "xhbeats"   : "Xhbeats";0;1,\
+  "xgrep"     : "Xgrep";1;1,\
+  "xhead"     : "Xhead";1;1,\
+  "xhelp"     : "Xhelp";0;0,\
+  "xinventory": "Xinventory";0;1,\
+  "xids"      : "Xids";0;0,\
+  "xinfo"     : "Xinfo";0;0,\
+  "xinherit"  : "Xinherit";0;1,\
+  "xlag"      : "Xlag";0;0,\
+  "xlight"    : "Xlight";0;0,\
+  "xload"     : "Xload";0;0,\
+  "xlook"     : "Xlook";0;1,\
+  "xlpc"      : "Xlpc";0;0,\
+  "xman"      : "Xman";0;0,\
+  "xmore"     : "Xmore";1;0,\
+  "xmove"     : "Xmove";0;0,\
+  "xmsg"      : "Xmsg";1;0,\
+  "xmtp"      : "Xmtp";0;0,\
+  "xproc"     : "Xproc";0;1,\
+  "xprof"     : "Xprof";0;0,\
+  "xprops"    : "Xprops";0;1,\
+  "xquit"     : "Xquit";0;0,\
+  "xscan"     : "Xscan";0;0,\
+  "xset"      : "Xset";0;0,\
+  "xsh"       : "Xsh";0;0,\
+  "xsort"     : "Xsort";1;1,\
+  "xtail"     : "Xtail";1;1,\
+  "xtk"       : "Xtk";0;0,\
+  "xtool"     : "Xtool";0;0,\
+  "xtrace"    : "Xtrace";0;0,\
+  "xtrans"    : "Xtrans";0;0,\
+  "xupdate"   : "Xupdate";0;0,\
+  "xwc"       : "Xwc";1;0,\
+  "xwho"      : "Xwho";0;1,\
+  ])
+
+static string PrepareLine(string str)
+{
+  return str;
+}
+
+static string QuoteLine(string str)
+{
+  string *tmp,*tmp2;
+  int i, i2, len, len2, qd, qs;
+
+  str=string_replace(str,"\\\\","°BSHL");
+  str=string_replace(str,"\\\"","°DBLQ");
+  str=string_replace(str,"\\\'","°SGLQ");
+  str=string_replace(str,"\\|","°PIPE");
+  str=string_replace(str,"||","°OROR");
+  str=string_replace(str,"->","°LARR");
+  str=string_replace(str,"\\$","°DOLR");
+  tmp=regexplode(str,"(\"|')");
+  len=sizeof(tmp);
+  qd=qs=0;
+  for(i=0;i<len;i++)
+  {
+    if(i%2)
+    {
+      if(tmp[i]=="'")
+	qd=(!qs?!qd:qd);
+      else
+	qs=(!qd?!qs:qs);
+      if((tmp[i]=="\""&&!qd)||(tmp[i]=="'"&&!qs))
+	tmp[i]="";
+    }
+    else
+    {
+      if(!qd)
+      {
+	len2=sizeof(tmp2=regexplode(tmp[i],"\\$[^ ][^ ]*"));
+	for(i2=0;i2<len2;i2++)
+	  if(i2%2)
+	  {
+	    TK("QuoteLine: "+tmp2[i2][1..]);
+	    tmp2[i2]=(string)XFindObj((tmp2[i2])[1..]);
+	  }
+	tmp[i]=implode(tmp2,"");
+      }
+    }
+  }
+  if(qd||qs)
+    return NULL;
+  str=implode(tmp,"");
+  TK("QuoteLine: str: "+str);
+  return str;
+}
+
+static string UnquoteLine(string str)
+{
+  str=string_replace(str,"°BSHL","\\");
+  str=string_replace(str,"°DBLQ","\"");
+  str=string_replace(str,"°SGLQ","\'");
+  str=string_replace(str,"°DQUO","");
+  str=string_replace(str,"°SQUO","");
+  str=string_replace(str,"°PIPE","|");
+  str=string_replace(str,"°OROR","||");
+  str=string_replace(str,"°LARR","->");
+  str=string_replace(str,"°DOLR","$");
+  TK("UnquoteLine: str: "+str);
+  return str;
+}
+
+static string *ExplodeCmds(string str)
+{
+  string *tmp;
+  
+  tmp=regexplode(str,"\\||>|>>");
+  while(tmp[<1]=="")
+    tmp=tmp[0..<2];
+  return tmp;
+}
+
+varargs int ParseLine(string str)
+{
+  string verb, arg;
+  int ret;
+  
+  TK("ParseLine: str: "+(str?str:""));
+  if(!sizeof(cmds))
+  {
+    // this is a single command or beginning of a command pipe
+    verb=query_verb();
+
+    // return on unknown commands
+    if(!verb||!sizeof(verb)||!GetFunc(verb,TRUE))
+      return FALSE;
+
+    str=(string)this_player()->_unparsed_args();
+    pipe_in=FALSE;
+    pipe_of=NULL;
+    pipe_ovr=TRUE;
+    pipe_out=FALSE;
+    pipe_of=NULL;
+    // pass arguments to some special functions unparsed
+    if(member(({"xlpc","xcall","xeval"}),verb)>=0)
+    {
+#ifdef XDBG
+      TK("ParseLine: special func: "+verb);
+#endif
+      ret=CallFunc(verb,str);
+      SafeReturn(ret);
+    }
+    // ok, here we go
+    pipe_in=pipe_out=FALSE;
+    pipe_if=pipe_of=NULL;
+    pipe_ovr=TRUE;
+    if(file_size(PIPE_FILE)>=0)
+      rm(PIPE_FILE);
+    if (str&&str!="")	  
+    {
+      if(!(str=QuoteLine(str)))
+      {
+	WDLN("Unterminated quotation");
+	SafeReturn(TRUE);
+      }
+      cmds=ExplodeCmds(str);
+    }
+    else
+      cmds=({""});
+    arg=strip_string(cmds[0]);
+  }
+  else
+  {
+    cmds[0]=strip_string(cmds[0]);
+    TK("ParseLine: cmds[0]: "+cmds[0]);
+    if(sscanf(cmds[0],"%s %s",verb,arg)!=2)
+    {
+      verb=cmds[0];
+      arg="";
+    }
+  }
+  cmds=cmds[1..];
+  TK("ParseLine: verb: "+verb);
+  if (!verb||!sizeof(verb)||!GetFunc(verb,TRUE))
+    SafeReturn(FALSE);
+  TK("ParseLine(1): arg: "+arg+" cmds: "+sprintf("%O",cmds));
+  switch(sizeof(cmds))
+  {
+    case 0:
+    ret=CallFunc(verb,strip_string(UnquoteLine(arg)));
+    SafeReturn(ret);
+    break;
+    
+    case 1:
+    WDLN("Missing rhs of command pipe");
+    SafeReturn(TRUE);
+    break;
+    
+    default:
+    pipe_out=TRUE;
+    switch(cmds[0])
+    {
+      case "|":
+      pipe_of=PIPE_FILE;
+      pipe_ovr=TRUE;
+      cmds=cmds[1..];
+      break;
+      
+      case ">":
+      pipe_ovr=TRUE;
+      if(sizeof(cmds)!=2)
+      {
+	WDLN("Illegal IO redirection");
+	SafeReturn(TRUE);
+      }
+      pipe_of=cmds[1];
+      cmds=({});
+      break;
+      
+      case ">>":
+      pipe_ovr=FALSE;
+      if(sizeof(cmds)!=2)
+      {
+	WDLN("Illegal IO redirection");
+	SafeReturn(TRUE);
+      }
+      pipe_of=cmds[1];
+      cmds=({});
+      break;
+    }
+  }
+  TK("ParseLine(2): arg: "+arg+" cmds: "+sprintf("%O",cmds));
+  if(!CallFunc(verb,strip_string(arg)))
+    SafeReturn(FALSE);
+  pipe_in=pipe_out;
+  pipe_if=pipe_of;
+  pipe_ovr=FALSE;
+  pipe_out=FALSE;
+  pipe_of=NULL;
+  if(sizeof(cmds))
+    call_out("ParseLine",0);
+  else
+    SafeReturn(TRUE);
+  return TRUE;
+}
+
+static int CallFunc(string verb, string str)
+{
+  string fun;
+
+  fun=GetFunc(verb,FALSE);
+#ifdef XDBG
+  TK("CallFunc: verb: "+verb+" str: "+str);
+  TK("CallFunc: resolved function: "+(fun?fun:"(unresolved)"));
+#endif
+  if(str=="")
+    str=NULL;
+  return fun?(int)call_other(ME,fun,str):FALSE;
+}
+
+static string GetFunc(string verb, int test)
+{
+  string fun,*keys,key;
+  int i,len;
+
+  TK("GetFunc: verb: "+verb);
+
+  if(verb[0..0]!="x") // Assume all commands start with "x"
+    return 0;
+
+  if (!(fun=(string)ACTIONS[verb,0])) { // Try exact hit first
+    key="";
+    len=sizeof(verb);
+    for (i=sizeof(keys=m_indices(ACTIONS))-1;i>=0;i--) {
+      TK("  trying: "+keys[i]);
+      if(sizeof(keys[i])>=len&&keys[i][0..len-1]==verb) {
+	if(sizeof(key)) {
+	  WLN("Das ist nicht eindeutig ...");
+	  return 0;
+	}
+	fun=ACTIONS[keys[i],0];
+	key=keys[i];
+	//break;
+      }
+    }
+  } else
+    key=verb;
+
+  if(test)
+    return fun;
+  
+  if (key) {
+#ifdef XDBG
+    TK("GetFunc: fun: "+fun+" (key: "+key+")\n"+
+       "pipe_in: "+(pipe_in?"TRUE  ":"FALSE ")+(pipe_if?pipe_if:"(NULL)")+"\n"+
+       "pipe_out: "+(pipe_out?"TRUE  ":"FALSE ")+(pipe_of?pipe_of:"(NULL)")+"\n"+
+       "pipe_ovr: "+(pipe_ovr?"TRUE":"FALSE"));
+#endif
+    if (pipe_in&&!ACTIONS[key,PIPE_IN])
+    {
+      // this command does not read pipes
+#ifdef XDBG
+      TK("Illegal rhs of command pipe \""+fun+"\"\n");
+#endif
+      notify_fail("Illegal rhs of command pipe \""+fun+"\"\n");
+      return 0;
+    }
+    else if (pipe_out&&!ACTIONS[key,PIPE_OUT])
+    {
+      // this command does not feed pipes
+#ifdef XDBG
+      TK("Illegal lhs of command pipe \""+fun+"\"\n");
+#endif
+      notify_fail("Illegal lhs of command pipe \""+fun+"\"\n");
+      return 0;
+    }
+  }
+  return fun;
+}
+
+void actions()
+{
+  if (!cloner||!RTP||cloner==RTP||query_wiz_level(cloner)<=query_wiz_level(RTP))
+    add_action("ParseLine","",1);
+  add_action("CommandScan", "", 1);
+}
+
+/*----------------------------------------------------------------------
+ *  the checking stuff
+ */
+
+void InsertNotify(object obj)
+{
+  if(!cloner)
+    return;
+  if(MODE(MODE_FIRST) && find_call_out("move")==-1)
+    call_out("move",0,cloner);
+  if(MODE(MODE_INVCHECK))
+    write_newinvobj(obj);
+}
+
+static void VarCheck(int show)
+{
+  int i, s;
+  foreach(string k, mixed v : variable)
+  {
+    if (v) continue;
+    if(show) WDLN("*** Variable $"+k+" has been destructed");
+    m_delete(variable, k);
+  }
+}
+
+
+int write_newinvobj(object obj)
+{
+  if(obj) WDLN("*** New object in inventory "+ObjFile(obj));
+  return(1);
+}
+
+/*----------------------------------------------------------------------
+ *  catch all commands, absorb forces and check history
+ */
+
+int CommandScan(string arg)
+{
+  string verb, cmd;
+  object rtp;
+  rtp=RTP;
+
+  if(!cloner&&!(cloner=rtp)) destruct(ME);
+
+  if((!MODE(MODE_PROTECT))||security()||
+     query_wiz_level(cloner)<query_wiz_level(rtp))
+  {
+    verb=query_verb();
+    if(verb&&DoHistory(verb+(arg ? " "+arg : "")))
+      return TRUE;
+    nostore=FALSE;
+    return FALSE;
+  }
+  else
+  {
+    if(rtp)
+    {
+      WDLN("Your "+TOOL_TITLE+" protects you from a force by "+crname(rtp)+
+	   " ["+query_verb()+(arg ? " "+arg+"]" : "]"));
+      tell_object(rtp, crname(cloner)+"'s "+TOOL_TITLE+
+		  " absorbes your force.\n");
+    }
+    else
+    {
+      WDLN("Your "+TOOL_TITLE+" protects you from a force ["+
+	   query_verb()+(arg ? " "+arg+"]" : "]"));
+    }
+    return TRUE;
+  }
+}
+
+int DoHistory(string line)
+{
+  int i;
+  string cmd, *strs;
+  
+  SECURE2(FALSE);
+  if(!stringp(line) || !sizeof(line))
+    return TRUE;
+  else if(line=="%!")
+  {
+    WLN("Current command history:");
+    for(i=MAX_HISTORY; i; --i)
+      if(history[i-1])
+      {
+	W(" "+ARIGHT(""+i, 2, " ")+": ");
+	if(sizeof(history[i-1])>70)
+	  WLN(ALEFT(history[i-1], 70, " "));
+	else
+	  WLN(history[i-1]);
+      }
+    return TRUE;
+  }
+  else if(line[0..1]=="%%" && (cmd=history[0]+line[2..<1]))
+  {
+    Command(cmd);
+    return TRUE;
+  }
+  else if(line[0]=='^'&&(strs=strip_explode(line, "^")))
+  {
+    if(sizeof(strs)&&strs[0]&&(cmd=history[0]))
+    {
+      if(sizeof(strs)==2)
+	cmd=string_replace(cmd, strs[0], strs[1]);
+      else
+	cmd=string_replace(cmd, strs[0], "");
+      nostore--;
+      Command(cmd);
+      nostore++;
+      return TRUE;
+    }
+  }
+  else if(line[0]=='%' && (sscanf(line[1..<1], "%d", i)))
+  {
+    i= i>0 ? i : 1;
+    i= i<=MAX_HISTORY ? i : MAX_HISTORY;
+    if(cmd=history[i-1])
+      Command(cmd);
+    return TRUE;
+  }
+  else if(line[0]=='%')
+  {
+    for(i=0; i<MAX_HISTORY; i++)
+    {
+      if(history[i]&&
+	 history[i][0..sizeof(line)-2]==line[1.. <1])
+      {
+	Command(history[i]);
+	return TRUE;
+      }
+    }
+  }
+  else if(nostore<1)
+    history=({line})+history[0..MAX_HISTORY-2];
+  return FALSE;
+}
+