Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/std/thing/commands.c b/std/thing/commands.c
new file mode 100644
index 0000000..f745b67
--- /dev/null
+++ b/std/thing/commands.c
@@ -0,0 +1,532 @@
+// MorgenGrauen MUDlib
+//
+// thing/commands.c -- thing description
+//
+// $Id: commands.c 9514 2016-02-23 20:33:09Z Gloinson $
+//
+// Aus Regenbogen MUDLib
+// aus Gueldenland/Wunderland MUDlib (Morgengrauen MUDlib)
+//
+// P_COMMANDS data-structure:
+//
+// AddCmd(verb,fun1,1);
+// AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c,fun2,
+//	   error1_notify|error2_notify^error2_write);
+// -->
+// ([verb:({fun1,fun2});					// funs
+//	  ({1,({error1_notify, error2_write^error2_say, 1})});  // flags
+//        ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})});	// rules
+//        0])							// IDs
+//
+// Rules: ({<Regelsatz fuer fun1>, ({<1. Synonymgruppe>,
+//				     <2. Synonymgruppe, ...}), ...})
+// Flags: ({<Flag fuer fun1>, ({<Fehlermeldung 1. Synonymgruppe>, ... ,
+//				[, Index fuer write anstatt notify_fail]}),
+//	    ... })
+// IDs:   0 oder ({<ID fuer fun1>}) oder ({0, <ID fuer fun2>}) ...
+//
+// IDEA: save no 0s in rules/flags if possible (as in IDs)
+//       (adressing especially old-style-AddCmd-MUDs)
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <moving.h>
+#include <thing/language.h>
+#include <exploration.h>
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(x) printf("Object %O tmpstr=%s\n", explode(object_name(this_object()),"#")[1], x);
+
+private nosave mapping added_cmds;
+
+protected void create()
+{
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}     
+
+varargs void AddCmd(mixed cmd, mixed func, mixed flag, mixed cmdid) {
+ int i,j;
+ closure cl;
+ mixed *rule;
+
+ // potentielle AddCmd mit Regel?
+ if(stringp(cmd)) {
+  // eine Regel? - aufsplitten
+  if((i=member(cmd,'&'))>0) {
+   // ... in Array mit Verknuepfungselementen
+   rule=explode(cmd[(i+1)..],"&");
+   j=sizeof(rule);
+   // ... in Array mit Arrays mit Alternativelementen:
+   // "p|q&r|s" -> ({ ({"p","q"}), ({"r","s"}} })
+   while(j--)
+    rule[j]=explode(rule[j],"|");
+
+   // Regeln von Kommandoverben abschneiden
+   cmd=cmd[0..(i-1)];
+  }
+  // Kommandoverben extrahieren
+  cmd=explode(cmd,"|");
+
+  // Satz von Regeln existiert: Aufsplitten von Fehlermeldungen
+  if(rule)
+   if(stringp(flag)) {
+    mixed *fail;
+    // in einfaches Array mit jeweiligen Fehlermeldungen
+    fail=explode(flag,"|");
+    j=0;
+    i=sizeof(fail);
+    while(j<i) {
+     // write - Fehlermeldung entdeckt - Position ggf. eintragen
+     if(member(fail[j],'^')>=0 && !intp(fail[<1]))
+      fail+=({j});
+     if(member(fail[j],'@')>=0) {
+      int s;
+      flag=regexplode(fail[j], "@WE[A-SU]*[0-9]");
+      s=sizeof(flag);
+      while((s-=2)>0) {
+       int tmpint;
+       tmpint=flag[s][<1]-'1';
+       if(tmpint<0 || tmpint>j)
+        raise_error(sprintf(
+         "AddCmd: error-message %d contains out-of-bounds @WExx-rule.\n",j+1));
+      }
+     }
+     j++;
+    }
+    // "Was?|Wie das?" -> ({"Was?","Wie das?"})
+    // "Was?|Wie das?^|Womit das?|Worauf das?^@WER1 macht was." ->
+    //  ({"Was?",
+    //    "Wie das?^Womit das?",
+    //	  "Worauf das?^@WER1 macht was.",1})
+    flag=sizeof(fail);
+    if(flag && flag<sizeof(rule))
+     raise_error(
+      "AddCmd: number of error-messages does not match number of rules.\n");
+    flag=fail; // ueberschreiben mit den parsefreundlichen Format
+  } else if(flag)
+   raise_error("AddCmd: rules exist but flags are not an error-string.\n");
+ } // end if(stringp(cmd)) ... kein Regelstring vorhanden
+
+ // kein Kommandoarray gewesen noch erzeugt?
+ if(!pointerp(cmd))
+   raise_error("AddCmd: missing string/pointer-parameter for command.\n");
+
+ // Closure aus einem String erzeugen, wenn moeglich und sicher
+ // (function_exists() filtert unnoetigerweise auch reine "static" funs,
+ //  die genaue Pruefung ueber functionlist() kostet jedoch zuviel)
+ if(stringp(func) &&
+    (!extern_call() || function_exists(func,this_object())) &&
+    closurep(cl=symbol_function(func,this_object())))
+  func=cl;
+
+ // jedes einzelne Verb mit seinen Regeln und Funktionen eintragen
+ i=sizeof(cmd);
+ if(!added_cmds) added_cmds=m_allocate(i,4);
+ while(i--) {
+  string str;
+  str=cmd[i];
+  if(!func)
+   if(extern_call()) func=previous_object();
+   else func=this_object();
+  if(!member(added_cmds,str))
+   added_cmds+=([str:allocate(0);allocate(0);allocate(0);0]);
+  // existierendes Verb ergaenzen
+  added_cmds[str,0]+=({func});
+  added_cmds[str,1]+=({flag});
+  added_cmds[str,2]+=({rule});
+  // ggf. id in das ID-Mapping eintragen
+  if(cmdid) {
+   mixed *tmp;
+   j=sizeof((string*)added_cmds[str,0]);
+   tmp=added_cmds[str,3]||allocate(j);
+   if(sizeof(tmp)<j) tmp+=allocate(j-sizeof(tmp));
+   tmp[<1]=cmdid;
+   added_cmds[str,3]=tmp;
+  }
+ }
+}
+
+// Auswertung fuer ein Verb loeschen
+varargs int RemoveCmd(mixed cmd, int del_norule, mixed onlyid) {
+ int ret;
+
+ // Falls Magier das RemoveCmd falsch nutzen (z.B. analog zu AddCmd)
+ // wird das Regelsystem verwirrt. Da das del_norule nur int sein darf,
+ // gibt es hier eine gute Chance den Fehler abwaertskompatibel zu ent-
+ // decken. Damit Spieler den Fehler nicht mitbekommen, wird hier auf
+ // ein raise_error verzichtet, und statt dessen in ein Logfile ge-
+ // schrieben.
+ if (!intp(del_norule))
+ {
+   log_file("REMOVE_CMD",
+     sprintf("\n-- %s --\nIllegal RemoveCommand() in Object [%O]:\n %O\n",
+       dtime(time()), this_object(), cmd));
+   del_norule=0;
+   onlyid=0;
+ }
+ 
+ if(!added_cmds || (!cmd && !del_norule && !onlyid))
+  added_cmds=(mapping)0; 
+ else {
+  int i, j;
+  mixed *rule, *flag, *fun, *delrule, *ids;
+
+  if(stringp(cmd)) {
+   // Regeln entdeckt - Zerlegen (wie AddCmd)
+   if((i=member(cmd,'&'))>0) {
+    delrule=explode(cmd[(i+1)..],"&");
+    j=sizeof(delrule);
+    while(j--)
+     delrule[j]=explode(delrule[j],"|");
+    cmd=cmd[0..(i-1)];
+   }
+   cmd=explode(cmd,"|");
+  } else if(del_norule || onlyid) cmd=m_indices(added_cmds);
+
+  if(!pointerp(cmd))
+   raise_error("RemoveCmd: missing string/pointer-parameter.\n");
+  i=sizeof(cmd);
+
+  while(i--) {
+   // keine Regeln da und Regeln loeschen erlaubt: alles loeschen
+   if(!delrule && !del_norule && !onlyid) m_delete(added_cmds,cmd[i]);
+   else if(m_contains(&fun, &flag, &rule, &ids, added_cmds, cmd[i])) {
+    j=sizeof(fun);
+    while(j--) {
+     int k;
+     // DBG(rule[j]);
+    	// Regeln nicht löschen und Regel?
+     if(!(del_norule && pointerp(rule[j])) &&
+        // nur bestimmte ID löschen und ID passt nicht?
+        !(onlyid && (!pointerp(ids) || sizeof(ids)<=j || ids[j]!=onlyid)) &&
+        // Löschregel existiert und passt nicht auf Regel?
+        !(delrule && (k=sizeof(rule[j]))!=sizeof(delrule))) {
+      // partielles Testen einer Löschregel ...
+      if(delrule) {
+       while(k--)
+        if(!sizeof(rule[j][k]&delrule[k])) break;
+       if(k>=0) continue;
+      }
+      // alles korrekt: Löschen!
+      // (Arraybereich durch leeres Array loeschen)
+      flag[j..j]  = allocate(0);
+      fun[j..j]   = allocate(0);
+      rule[j..j]  = allocate(0);
+      if(ids) {
+       ids[j..j] = allocate(0);
+       if(!sizeof(ids-allocate(1))) ids=(mixed*)0;
+      }
+      ret++;
+     }
+    } // end while(j--) {
+   }
+   // Funktions/Regelliste update oder ggf. Kommando voellig loeschen
+   if(sizeof(rule)) {
+    added_cmds[cmd[i],0]=fun;
+    added_cmds[cmd[i],1]=flag;
+    added_cmds[cmd[i],2]=rule;
+    added_cmds[cmd[i],3]=ids;
+   } else m_delete(added_cmds,cmd[i]);
+  }
+  if(!sizeof(added_cmds)) added_cmds=(mapping)0;
+ }
+ return ret;
+}
+
+// Ausfuehren samt geparstem Inputstring und getriggerten Parserergebnissen
+static int _execute(mixed fun, string str, mixed *parsed) {
+ if(closurep(fun))
+  return ((int)funcall(fun,str,&parsed));
+ if(stringp(fun))
+  return ((int)call_other(this_object(),fun,str,&parsed));
+ return 0;
+}
+
+#define CHECK_PRESENT     1
+#define CHECK_ID          2
+#define CHECK_PUTGETNONE  4
+#define CHECK_PUTGETDROP  8
+#define CHECK_PUTGETTAKE  16
+#define CHECK_PUTGET      (CHECK_PUTGETNONE|CHECK_PUTGETDROP|CHECK_PUTGETTAKE)
+
+// Wert fuer Fehlschlag, Fallback-Wert der benutzten while-- - Schleifen
+#define NOMATCHFOUND      -1
+
+// Regeln fuer ein (nun unwichtiges) Verb triggern
+static int _process_command(string str, string *noparsestr,
+			     mixed fun, mixed flag, mixed rule) {
+ mixed *parsed, *objmatches;
+
+ // eine Regel ... auswerten ...
+ if(pointerp(rule)) {
+  int nrul;
+  parsed=objmatches=allocate(0);
+  int lastmatchpos=NOMATCHFOUND;
+
+  // Abgleichen der gesplitteten Eingabe mit Regeln:
+  // vorwaerts durch die Synonymgruppen
+  int rs=sizeof(rule);
+  while(nrul<rs) {
+   int matchpos;
+   string *synonym;
+   mixed matchstr;
+
+   matchpos=NOMATCHFOUND;
+   matchstr=0;
+
+   // Synonyme extrahieren
+   int nrsynonyms=sizeof(synonym=rule[nrul]);
+
+   // egal wie durch Synonyme bis Match - Abgleich mit Eingabe
+   while(nrsynonyms--) {
+    int tmppos = member(noparsestr,synonym[nrsynonyms]);
+    // ist Synonym im Eingabestring und kommt spaeter als vorheriges Synonym?
+    if(tmppos>=0 && tmppos>lastmatchpos) {
+     // Erfolg: merken der Position im Eingabestring und den matchenden String
+     matchpos=tmppos;
+     matchstr=noparsestr[tmppos];
+     break;
+    }
+   }
+
+   // kein Match durch Synonyme? Pruefe die @-Spezialvariablen.
+   if(matchpos == NOMATCHFOUND) {
+    int check_present;
+    // ist Abpruefen von ID/PRESENT in der Synonymgruppe verlangt
+    // bei present()/find_obs gleich Voraussetzung gueltiger TP mitprufen
+    if(member(synonym,"@ID")>=0) check_present=CHECK_ID;
+    if(this_player()) {
+     if(member(synonym,"@PRESENT")>=0) check_present|=CHECK_PRESENT;
+     else if(member(synonym,"@PUT_GET_NONE")>=0)
+		check_present|=CHECK_PUTGETNONE;
+     else if(member(synonym,"@PUT_GET_TAKE")>=0)
+		check_present|=CHECK_PUTGETDROP;
+     else if(member(synonym,"@PUT_GET_DROP")>=0)
+		check_present|=CHECK_PUTGETTAKE;
+    }
+
+    if(check_present) {
+     // wir fangen hinter dem letzten Match an
+     int q_start=lastmatchpos+1;
+     int r_end=sizeof(noparsestr)-1;
+
+     int range;
+     while((range=r_end-q_start)>=0) {
+      mixed tmpobj;
+
+      // wie weit wollen wir zurückgehen?
+      if(range)
+        if(!(check_present&CHECK_PUTGET))
+          range=range>2?2:range; // 3 Fragmente fuer @ID/@PRESENT (Adverb/Nr)
+        else if(range>4)
+          range=4;               // 5 Fragmente fuer @PUT_XXX 
+
+      // und jetzt die Substrings pruefen
+      while(range>=0 && !matchstr) {
+       string tmpstr;
+
+       // zu pruefenden String aus den Teilen zusammensetzen
+       if(range) tmpstr=implode(noparsestr[q_start..(q_start+range)]," ");
+       else tmpstr=noparsestr[q_start];
+
+       //DBG(tmpstr);
+       if(check_present&CHECK_PRESENT &&			// PRESENT ?
+          ((tmpobj=present(tmpstr,this_player())) ||
+           (tmpobj=present(tmpstr,environment(this_player())))))
+        matchstr=tmpobj;
+       else if(check_present&CHECK_ID && id(tmpstr))	// ID ?
+        matchstr=this_object();
+       else if((check_present&CHECK_PUTGET) &&	// PUT_GET_??? ?
+               (tmpobj=(object*)
+                  this_player()->find_obs(tmpstr,
+                      ([CHECK_PUTGETNONE:PUT_GET_NONE,
+                        CHECK_PUTGETDROP:PUT_GET_TAKE,
+                        CHECK_PUTGETTAKE:PUT_GET_DROP])
+                                        [CHECK_PUTGET&check_present])) &&
+               sizeof(tmpobj)) {
+        if(sizeof(tmpobj)==1) matchstr=tmpobj[0];
+        else {	// Arrays werden zwischengespeichert ...
+         objmatches+=({sizeof(parsed),tmpobj});
+         matchstr=tmpstr;
+        }
+       } else { // dieser Substring hat nicht gematcht
+        // ab weniger als 3 Teilen ist das Nichtmatching eines Substrings mit
+        // beendendem Numeral Kriterium fuer Abbruch ("objekt 2" soll matchen)
+        string numeralcheck;
+        if(range && range<=2 &&
+           sizeof(numeralcheck=noparsestr[q_start+range]) &&
+           to_string(to_int(numeralcheck))==numeralcheck)
+          break;
+
+        // Substringlaenge verkuerzen und weiter
+        range--;
+       }
+      }
+
+      // Match gefunden!
+      if(matchstr) {
+       matchpos=range+q_start;
+       // DBG(matchpos);
+       break;
+      }
+      q_start++;
+     } // end while
+    }
+   }
+
+   // Fehlermeldung fuer diese fehlgeschlagene Synonymgruppe setzen
+   if(matchpos == NOMATCHFOUND) {
+    // Fehlermeldungen und ein Eintrag an der Fehlerstelle?
+    if(pointerp(flag) && sizeof(flag)>nrul) {
+
+     matchstr=flag[nrul];
+
+     if(stringp(matchstr) && sizeof(matchstr)) {
+      if(member(matchstr,'@')>=0) {
+       matchstr=replace_personal(&matchstr,({this_player()})+parsed,1);
+       string stamm=((query_verb()[<1]^'e')?query_verb():query_verb()[0..<2]);
+       matchstr=regreplace(matchstr,"@VERB",capitalize(stamm),1);
+       matchstr=regreplace(matchstr,"@verb",stamm,1);
+      }
+
+      // ist Fehlermeldung ein WRITE?
+      // dann return 1 !
+      if(intp(flag[<1]) && flag[<1]<=nrul) {
+       if(member(matchstr,'^')>=0) {
+        matchstr=explode(matchstr,"^");
+        write(capitalize(break_string(matchstr[0],78,0,1)));
+        if(sizeof(matchstr[1]))
+         say(capitalize(break_string(matchstr[1],78,0,1)),({this_player()}) );
+       } else write(capitalize(break_string(matchstr,78,0,1)));
+       return 1;
+      } else notify_fail(capitalize(break_string(matchstr,78,0,1)));
+     }
+    }
+    return 0;
+   }
+
+   // Updaten der Hilfsvariablen
+   parsed+=({matchstr});
+   lastmatchpos=matchpos;
+   nrul++;
+  } // end while(nrul<rs) ... Erfolg ... ab zum naechsten Regelteil
+ }
+
+ // Arrays der @-Objectmatches in Parameter fuer Methode resubstituieren
+ int objsize;
+ if((objsize=sizeof(objmatches)))
+  while((objsize-=2)>=0)
+   parsed[objmatches[objsize]]=objmatches[objsize+1];
+
+ // erfolgreich Methode gefunden/eine Regel bis zum Ende durchgeparst:
+ return(_execute(&fun,&str,&parsed));
+}
+
+// Auswertung - add_action-Fun 
+public int _cl(string str) {
+ int nindex;
+ string *keys;
+ mixed *flag;
+ // Verb existiert, Kommandos auch, Eintrag fuer Verb gefunden
+ if(mappingp(added_cmds) && query_verb()) {
+  mixed *fun, *rule, *ids;
+
+  // ist das Verb ein Key im Kommandomapping?
+  if(m_contains(&fun, &flag, &rule, &ids, added_cmds, query_verb())) {
+   string *noparsestr;
+   nindex=sizeof(fun);
+   noparsestr=explode((str||"")," ")-({""});
+   // dann matche ungeparsten Input gegen etwaige Regeln
+   // -- nicht aendern: neue Kommandos sollen alte "ueberschreiben" koennen
+   while(nindex--)
+    if(_process_command(&str, noparsestr,
+		       fun[nindex], flag[nindex], rule[nindex]))
+    {
+      GiveEP(EP_CMD, query_verb());
+      return 1;
+    }
+  }
+
+  // keine Regel passte, unscharfe Auswertung auf alle
+  // AddCmdverben ausdehnen
+  keys=m_indices(added_cmds);
+  nindex=sizeof(keys);
+  while(nindex--)
+   if(!strstr(query_verb(),keys[nindex]) &&
+      member((flag=added_cmds[keys[nindex],1]),1)>=0) {
+    int i,ret;
+    i=sizeof(flag);
+    // Reihenfolge nicht aendern !
+    while(i--)
+      if(flag[i]==1) {
+        mixed *exec = added_cmds[keys[nindex],0];
+        if(!pointerp(exec) || sizeof(exec)<=i)
+          catch(raise_error(
+            "AddCmd-Auswertung: CatchAll-Kommando \""+keys[nindex]+"\""
+            " wurde waehrend der Ausfuehrung veraendert oder geloescht. "
+            "Klartext: Das ist schlecht. Sucht ein RemoveCmd? ");
+            publish);
+        else if(_execute(exec[i],str,0)) {
+          GiveEP(EP_CMD, query_verb());
+          return 1;
+        }
+      }
+   }
+ }
+ return 0;
+}
+
+void init() {
+ add_action("_cl","",1);
+}
+
+
+static void _check_copy_commands(string ind, mixed *fun,
+				  mixed *flag, mixed *rule) {
+ if(pointerp(fun)) added_cmds[ind,0]=fun+allocate(0);
+ else added_cmds[ind,0]=({fun});
+ if(pointerp(flag)) added_cmds[ind,1]=flag+allocate(0);
+ else if(flag) added_cmds[ind,1]=({flag});
+ else added_cmds[ind,1]=allocate(sizeof(added_cmds[ind]));
+ if(pointerp(rule)) added_cmds[ind,2]=rule+allocate(0);
+ else added_cmds[ind,2]=allocate(sizeof(added_cmds[ind]));
+
+ if(sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,1]) ||
+    sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,2])) {
+  added_cmds=(mapping)0;
+  raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
+ }
+}
+
+static mapping _set_commands(mapping commands) {
+ if(!commands) added_cmds=(mapping)0;
+ else if(mappingp(commands)) {
+  added_cmds=m_allocate(sizeof(commands),4);
+  walk_mapping(commands,#'_check_copy_commands);
+ }
+ if (mappingp(added_cmds))
+   return(deep_copy(added_cmds));
+ else
+   return (mapping)0;
+}
+
+static mapping _query_commands() {
+ if (mappingp(added_cmds))
+   return(deep_copy(added_cmds));
+ else
+   return (mapping)0;
+}
+