blob: 8134f4b38e8cf743193de1bcea0613c8fa391393 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// thing/commands.c -- thing description
4//
5// $Id: commands.c 9514 2016-02-23 20:33:09Z Gloinson $
6//
7// Aus Regenbogen MUDLib
8// aus Gueldenland/Wunderland MUDlib (Morgengrauen MUDlib)
9//
10// P_COMMANDS data-structure:
11//
12// AddCmd(verb,fun1,1);
13// AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c,fun2,
14// error1_notify|error2_notify^error2_write);
15// -->
16// ([verb:({fun1,fun2}); // funs
17// ({1,({error1_notify, error2_write^error2_say, 1})}); // flags
18// ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})}); // rules
19// 0]) // IDs
20//
21// Rules: ({<Regelsatz fuer fun1>, ({<1. Synonymgruppe>,
22// <2. Synonymgruppe, ...}), ...})
23// Flags: ({<Flag fuer fun1>, ({<Fehlermeldung 1. Synonymgruppe>, ... ,
24// [, Index fuer write anstatt notify_fail]}),
25// ... })
26// IDs: 0 oder ({<ID fuer fun1>}) oder ({0, <ID fuer fun2>}) ...
27//
28// IDEA: save no 0s in rules/flags if possible (as in IDs)
29// (adressing especially old-style-AddCmd-MUDs)
30#pragma strict_types
31#pragma save_types
32#pragma range_check
33#pragma no_clone
34#pragma pedantic
35
36#include <moving.h>
37#include <thing/language.h>
38#include <exploration.h>
Zesstrabab5a9d2016-07-30 12:53:35 +020039#include <defines.h>
40#include <living/comm.h>
MG Mud User88f12472016-06-24 23:31:02 +020041
42#define NEED_PROTOTYPES
Zesstrabab5a9d2016-07-30 12:53:35 +020043#include <thing/properties.h>
MG Mud User88f12472016-06-24 23:31:02 +020044#include <thing/description.h>
45#include <thing/commands.h>
46#undef NEED_PROTOTYPES
47
48#ifdef DBG
49#undef DBG
50#endif
51#define DBG(x) printf("Object %O tmpstr=%s\n", explode(object_name(this_object()),"#")[1], x);
52
53private nosave mapping added_cmds;
54
Zesstra2a4b5702016-08-16 19:08:47 +020055protected int _cmd_syntaxhelp(string str, mixed *args)
Zesstrabab5a9d2016-07-30 12:53:35 +020056{
57 mapping|closure restr;
58 mixed help = QueryProp(P_SYNTAX_HELP);
59 if (pointerp(help))
60 {
61 restr = help[1];
62 help = help[0];
63 }
64 // Restriktionen vor dem Anzeigen pruefen.
65 if (mappingp(restr))
66 {
67 string res = "/std/restriction_checker"->check_restrictions(PL,restr);
68 if (res)
69 {
70 PL->ReceiveMsg("Fuer " + name(WEN,1) + " darfst Du "
71 "die Syntaxhilfe (noch) nicht lesen:\n"
72 + res,
73 MT_NOTIFICATION|MSG_BS_LEAVE_LFS,
74 "syntaxhilfe",0,this_object());
75 return 1;
76 }
77 }
78 else if (closurep(restr))
79 {
80 string res = funcall(restr, ME);
81 if (res)
82 {
83 if (intp(res))
84 PL->ReceiveMsg("Fuer " + name(WEN,1) + " darfst Du "
85 "die Syntaxhilfe (noch) nicht lesen.",
86 MT_NOTIFICATION|MSG_BS_LEAVE_LFS,
87 "syntaxhilfe",0,this_object());
88 else if (stringp(res))
89 PL->ReceiveMsg(res,
90 MT_NOTIFICATION|MSG_BS_LEAVE_LFS,
91 "syntaxhilfe",0,this_object());
92 return 1;
93 }
94 }
95
96 if (stringp(help))
97 {
98 help = "Fuer " + name(WEN,1) + " gibt es folgende Syntaxhilfe:\n"
99 + help;
100 }
101 else if (closurep(help))
102 {
103 help = funcall(help, this_object());
104 }
105 else
106 {
107 // wenn das Objekt keine Syntaxhilfe hat, braucht es das Kommando auch
108 // nicht.
109 notify_fail("Fuer " + name(WEN,1)
110 + " gibt es keine Syntaxhilfe.\n");
111 RemoveCmd(0,0, "_cmd_syntaxhelp");
112 return 0;
113 }
114 if (stringp(help) && sizeof(help))
115 PL->ReceiveMsg(help, MT_NOTIFICATION|MSG_BS_LEAVE_LFS,
116 "syntaxhilfe",0,this_object());
117
118 return 1;
119}
120
MG Mud User88f12472016-06-24 23:31:02 +0200121protected void create()
122{
Zesstrabab5a9d2016-07-30 12:53:35 +0200123 AddCmd("syntaxhilfe&@ID", #'_cmd_syntaxhelp,
124 "Fuer WAS moechtest Du eine Syntaxhilfe?\n",
125 "_cmd_syntaxhelp");
MG Mud User88f12472016-06-24 23:31:02 +0200126}
127
128protected void create_super() {
129 set_next_reset(-1);
Zesstrabab5a9d2016-07-30 12:53:35 +0200130}
MG Mud User88f12472016-06-24 23:31:02 +0200131
132varargs void AddCmd(mixed cmd, mixed func, mixed flag, mixed cmdid) {
133 int i,j;
134 closure cl;
135 mixed *rule;
136
137 // potentielle AddCmd mit Regel?
138 if(stringp(cmd)) {
139 // eine Regel? - aufsplitten
140 if((i=member(cmd,'&'))>0) {
141 // ... in Array mit Verknuepfungselementen
142 rule=explode(cmd[(i+1)..],"&");
143 j=sizeof(rule);
144 // ... in Array mit Arrays mit Alternativelementen:
145 // "p|q&r|s" -> ({ ({"p","q"}), ({"r","s"}} })
146 while(j--)
147 rule[j]=explode(rule[j],"|");
148
149 // Regeln von Kommandoverben abschneiden
150 cmd=cmd[0..(i-1)];
151 }
152 // Kommandoverben extrahieren
153 cmd=explode(cmd,"|");
154
155 // Satz von Regeln existiert: Aufsplitten von Fehlermeldungen
156 if(rule)
157 if(stringp(flag)) {
158 mixed *fail;
159 // in einfaches Array mit jeweiligen Fehlermeldungen
160 fail=explode(flag,"|");
161 j=0;
162 i=sizeof(fail);
163 while(j<i) {
164 // write - Fehlermeldung entdeckt - Position ggf. eintragen
165 if(member(fail[j],'^')>=0 && !intp(fail[<1]))
166 fail+=({j});
167 if(member(fail[j],'@')>=0) {
168 int s;
169 flag=regexplode(fail[j], "@WE[A-SU]*[0-9]");
170 s=sizeof(flag);
171 while((s-=2)>0) {
172 int tmpint;
173 tmpint=flag[s][<1]-'1';
174 if(tmpint<0 || tmpint>j)
175 raise_error(sprintf(
176 "AddCmd: error-message %d contains out-of-bounds @WExx-rule.\n",j+1));
177 }
178 }
179 j++;
180 }
181 // "Was?|Wie das?" -> ({"Was?","Wie das?"})
182 // "Was?|Wie das?^|Womit das?|Worauf das?^@WER1 macht was." ->
183 // ({"Was?",
184 // "Wie das?^Womit das?",
185 // "Worauf das?^@WER1 macht was.",1})
186 flag=sizeof(fail);
187 if(flag && flag<sizeof(rule))
188 raise_error(
189 "AddCmd: number of error-messages does not match number of rules.\n");
190 flag=fail; // ueberschreiben mit den parsefreundlichen Format
191 } else if(flag)
192 raise_error("AddCmd: rules exist but flags are not an error-string.\n");
193 } // end if(stringp(cmd)) ... kein Regelstring vorhanden
194
195 // kein Kommandoarray gewesen noch erzeugt?
196 if(!pointerp(cmd))
197 raise_error("AddCmd: missing string/pointer-parameter for command.\n");
198
199 // Closure aus einem String erzeugen, wenn moeglich und sicher
200 // (function_exists() filtert unnoetigerweise auch reine "static" funs,
201 // die genaue Pruefung ueber functionlist() kostet jedoch zuviel)
202 if(stringp(func) &&
203 (!extern_call() || function_exists(func,this_object())) &&
204 closurep(cl=symbol_function(func,this_object())))
205 func=cl;
206
207 // jedes einzelne Verb mit seinen Regeln und Funktionen eintragen
208 i=sizeof(cmd);
209 if(!added_cmds) added_cmds=m_allocate(i,4);
210 while(i--) {
211 string str;
212 str=cmd[i];
213 if(!func)
214 if(extern_call()) func=previous_object();
215 else func=this_object();
216 if(!member(added_cmds,str))
217 added_cmds+=([str:allocate(0);allocate(0);allocate(0);0]);
218 // existierendes Verb ergaenzen
219 added_cmds[str,0]+=({func});
220 added_cmds[str,1]+=({flag});
221 added_cmds[str,2]+=({rule});
222 // ggf. id in das ID-Mapping eintragen
223 if(cmdid) {
224 mixed *tmp;
225 j=sizeof((string*)added_cmds[str,0]);
226 tmp=added_cmds[str,3]||allocate(j);
227 if(sizeof(tmp)<j) tmp+=allocate(j-sizeof(tmp));
228 tmp[<1]=cmdid;
229 added_cmds[str,3]=tmp;
230 }
231 }
232}
233
234// Auswertung fuer ein Verb loeschen
235varargs int RemoveCmd(mixed cmd, int del_norule, mixed onlyid) {
236 int ret;
237
238 // Falls Magier das RemoveCmd falsch nutzen (z.B. analog zu AddCmd)
239 // wird das Regelsystem verwirrt. Da das del_norule nur int sein darf,
240 // gibt es hier eine gute Chance den Fehler abwaertskompatibel zu ent-
241 // decken. Damit Spieler den Fehler nicht mitbekommen, wird hier auf
242 // ein raise_error verzichtet, und statt dessen in ein Logfile ge-
243 // schrieben.
244 if (!intp(del_norule))
245 {
246 log_file("REMOVE_CMD",
247 sprintf("\n-- %s --\nIllegal RemoveCommand() in Object [%O]:\n %O\n",
248 dtime(time()), this_object(), cmd));
249 del_norule=0;
250 onlyid=0;
251 }
252
253 if(!added_cmds || (!cmd && !del_norule && !onlyid))
254 added_cmds=(mapping)0;
255 else {
256 int i, j;
257 mixed *rule, *flag, *fun, *delrule, *ids;
258
259 if(stringp(cmd)) {
260 // Regeln entdeckt - Zerlegen (wie AddCmd)
261 if((i=member(cmd,'&'))>0) {
262 delrule=explode(cmd[(i+1)..],"&");
263 j=sizeof(delrule);
264 while(j--)
265 delrule[j]=explode(delrule[j],"|");
266 cmd=cmd[0..(i-1)];
267 }
268 cmd=explode(cmd,"|");
269 } else if(del_norule || onlyid) cmd=m_indices(added_cmds);
270
271 if(!pointerp(cmd))
272 raise_error("RemoveCmd: missing string/pointer-parameter.\n");
273 i=sizeof(cmd);
274
275 while(i--) {
276 // keine Regeln da und Regeln loeschen erlaubt: alles loeschen
277 if(!delrule && !del_norule && !onlyid) m_delete(added_cmds,cmd[i]);
278 else if(m_contains(&fun, &flag, &rule, &ids, added_cmds, cmd[i])) {
279 j=sizeof(fun);
280 while(j--) {
281 int k;
282 // DBG(rule[j]);
283 // Regeln nicht löschen und Regel?
284 if(!(del_norule && pointerp(rule[j])) &&
285 // nur bestimmte ID löschen und ID passt nicht?
286 !(onlyid && (!pointerp(ids) || sizeof(ids)<=j || ids[j]!=onlyid)) &&
287 // Löschregel existiert und passt nicht auf Regel?
288 !(delrule && (k=sizeof(rule[j]))!=sizeof(delrule))) {
289 // partielles Testen einer Löschregel ...
290 if(delrule) {
291 while(k--)
292 if(!sizeof(rule[j][k]&delrule[k])) break;
293 if(k>=0) continue;
294 }
295 // alles korrekt: Löschen!
296 // (Arraybereich durch leeres Array loeschen)
297 flag[j..j] = allocate(0);
298 fun[j..j] = allocate(0);
299 rule[j..j] = allocate(0);
300 if(ids) {
301 ids[j..j] = allocate(0);
302 if(!sizeof(ids-allocate(1))) ids=(mixed*)0;
303 }
304 ret++;
305 }
306 } // end while(j--) {
307 }
308 // Funktions/Regelliste update oder ggf. Kommando voellig loeschen
309 if(sizeof(rule)) {
310 added_cmds[cmd[i],0]=fun;
311 added_cmds[cmd[i],1]=flag;
312 added_cmds[cmd[i],2]=rule;
313 added_cmds[cmd[i],3]=ids;
314 } else m_delete(added_cmds,cmd[i]);
315 }
316 if(!sizeof(added_cmds)) added_cmds=(mapping)0;
317 }
318 return ret;
319}
320
321// Ausfuehren samt geparstem Inputstring und getriggerten Parserergebnissen
322static int _execute(mixed fun, string str, mixed *parsed) {
323 if(closurep(fun))
324 return ((int)funcall(fun,str,&parsed));
325 if(stringp(fun))
326 return ((int)call_other(this_object(),fun,str,&parsed));
327 return 0;
328}
329
330#define CHECK_PRESENT 1
331#define CHECK_ID 2
332#define CHECK_PUTGETNONE 4
333#define CHECK_PUTGETDROP 8
334#define CHECK_PUTGETTAKE 16
335#define CHECK_PUTGET (CHECK_PUTGETNONE|CHECK_PUTGETDROP|CHECK_PUTGETTAKE)
336
337// Wert fuer Fehlschlag, Fallback-Wert der benutzten while-- - Schleifen
338#define NOMATCHFOUND -1
339
340// Regeln fuer ein (nun unwichtiges) Verb triggern
341static int _process_command(string str, string *noparsestr,
Zesstra@Morgengrauen4ba72dc2016-09-08 23:13:26 +0200342 mixed fun, mixed flag, mixed rule, mixed id)
343{
MG Mud User88f12472016-06-24 23:31:02 +0200344 mixed *parsed, *objmatches;
345
346 // eine Regel ... auswerten ...
347 if(pointerp(rule)) {
348 int nrul;
349 parsed=objmatches=allocate(0);
350 int lastmatchpos=NOMATCHFOUND;
351
352 // Abgleichen der gesplitteten Eingabe mit Regeln:
353 // vorwaerts durch die Synonymgruppen
354 int rs=sizeof(rule);
355 while(nrul<rs) {
356 int matchpos;
357 string *synonym;
358 mixed matchstr;
359
360 matchpos=NOMATCHFOUND;
361 matchstr=0;
362
363 // Synonyme extrahieren
364 int nrsynonyms=sizeof(synonym=rule[nrul]);
365
366 // egal wie durch Synonyme bis Match - Abgleich mit Eingabe
367 while(nrsynonyms--) {
368 int tmppos = member(noparsestr,synonym[nrsynonyms]);
369 // ist Synonym im Eingabestring und kommt spaeter als vorheriges Synonym?
370 if(tmppos>=0 && tmppos>lastmatchpos) {
371 // Erfolg: merken der Position im Eingabestring und den matchenden String
372 matchpos=tmppos;
373 matchstr=noparsestr[tmppos];
374 break;
375 }
376 }
377
378 // kein Match durch Synonyme? Pruefe die @-Spezialvariablen.
379 if(matchpos == NOMATCHFOUND) {
380 int check_present;
381 // ist Abpruefen von ID/PRESENT in der Synonymgruppe verlangt
382 // bei present()/find_obs gleich Voraussetzung gueltiger TP mitprufen
383 if(member(synonym,"@ID")>=0) check_present=CHECK_ID;
384 if(this_player()) {
385 if(member(synonym,"@PRESENT")>=0) check_present|=CHECK_PRESENT;
386 else if(member(synonym,"@PUT_GET_NONE")>=0)
387 check_present|=CHECK_PUTGETNONE;
388 else if(member(synonym,"@PUT_GET_TAKE")>=0)
389 check_present|=CHECK_PUTGETDROP;
390 else if(member(synonym,"@PUT_GET_DROP")>=0)
391 check_present|=CHECK_PUTGETTAKE;
392 }
393
394 if(check_present) {
395 // wir fangen hinter dem letzten Match an
396 int q_start=lastmatchpos+1;
397 int r_end=sizeof(noparsestr)-1;
398
399 int range;
400 while((range=r_end-q_start)>=0) {
401 mixed tmpobj;
402
403 // wie weit wollen wir zurückgehen?
404 if(range)
405 if(!(check_present&CHECK_PUTGET))
406 range=range>2?2:range; // 3 Fragmente fuer @ID/@PRESENT (Adverb/Nr)
407 else if(range>4)
408 range=4; // 5 Fragmente fuer @PUT_XXX
409
410 // und jetzt die Substrings pruefen
411 while(range>=0 && !matchstr) {
412 string tmpstr;
413
414 // zu pruefenden String aus den Teilen zusammensetzen
415 if(range) tmpstr=implode(noparsestr[q_start..(q_start+range)]," ");
416 else tmpstr=noparsestr[q_start];
417
418 //DBG(tmpstr);
419 if(check_present&CHECK_PRESENT && // PRESENT ?
420 ((tmpobj=present(tmpstr,this_player())) ||
421 (tmpobj=present(tmpstr,environment(this_player())))))
422 matchstr=tmpobj;
423 else if(check_present&CHECK_ID && id(tmpstr)) // ID ?
424 matchstr=this_object();
425 else if((check_present&CHECK_PUTGET) && // PUT_GET_??? ?
426 (tmpobj=(object*)
427 this_player()->find_obs(tmpstr,
428 ([CHECK_PUTGETNONE:PUT_GET_NONE,
429 CHECK_PUTGETDROP:PUT_GET_TAKE,
430 CHECK_PUTGETTAKE:PUT_GET_DROP])
431 [CHECK_PUTGET&check_present])) &&
432 sizeof(tmpobj)) {
433 if(sizeof(tmpobj)==1) matchstr=tmpobj[0];
434 else { // Arrays werden zwischengespeichert ...
435 objmatches+=({sizeof(parsed),tmpobj});
436 matchstr=tmpstr;
437 }
438 } else { // dieser Substring hat nicht gematcht
439 // ab weniger als 3 Teilen ist das Nichtmatching eines Substrings mit
440 // beendendem Numeral Kriterium fuer Abbruch ("objekt 2" soll matchen)
441 string numeralcheck;
442 if(range && range<=2 &&
443 sizeof(numeralcheck=noparsestr[q_start+range]) &&
444 to_string(to_int(numeralcheck))==numeralcheck)
445 break;
446
447 // Substringlaenge verkuerzen und weiter
448 range--;
449 }
450 }
451
452 // Match gefunden!
453 if(matchstr) {
454 matchpos=range+q_start;
455 // DBG(matchpos);
456 break;
457 }
458 q_start++;
459 } // end while
460 }
461 }
462
463 // Fehlermeldung fuer diese fehlgeschlagene Synonymgruppe setzen
464 if(matchpos == NOMATCHFOUND) {
465 // Fehlermeldungen und ein Eintrag an der Fehlerstelle?
466 if(pointerp(flag) && sizeof(flag)>nrul) {
467
468 matchstr=flag[nrul];
469
470 if(stringp(matchstr) && sizeof(matchstr)) {
471 if(member(matchstr,'@')>=0) {
472 matchstr=replace_personal(&matchstr,({this_player()})+parsed,1);
473 string stamm=((query_verb()[<1]^'e')?query_verb():query_verb()[0..<2]);
474 matchstr=regreplace(matchstr,"@VERB",capitalize(stamm),1);
475 matchstr=regreplace(matchstr,"@verb",stamm,1);
476 }
477
478 // ist Fehlermeldung ein WRITE?
479 // dann return 1 !
480 if(intp(flag[<1]) && flag[<1]<=nrul) {
481 if(member(matchstr,'^')>=0) {
482 matchstr=explode(matchstr,"^");
483 write(capitalize(break_string(matchstr[0],78,0,1)));
484 if(sizeof(matchstr[1]))
485 say(capitalize(break_string(matchstr[1],78,0,1)),({this_player()}) );
486 } else write(capitalize(break_string(matchstr,78,0,1)));
487 return 1;
488 } else notify_fail(capitalize(break_string(matchstr,78,0,1)));
489 }
490 }
491 return 0;
492 }
493
494 // Updaten der Hilfsvariablen
495 parsed+=({matchstr});
496 lastmatchpos=matchpos;
497 nrul++;
498 } // end while(nrul<rs) ... Erfolg ... ab zum naechsten Regelteil
499 }
500
501 // Arrays der @-Objectmatches in Parameter fuer Methode resubstituieren
502 int objsize;
503 if((objsize=sizeof(objmatches)))
504 while((objsize-=2)>=0)
505 parsed[objmatches[objsize]]=objmatches[objsize+1];
506
507 // erfolgreich Methode gefunden/eine Regel bis zum Ende durchgeparst:
508 return(_execute(&fun,&str,&parsed));
509}
510
511// Auswertung - add_action-Fun
512public int _cl(string str) {
513 int nindex;
514 string *keys;
515 mixed *flag;
516 // Verb existiert, Kommandos auch, Eintrag fuer Verb gefunden
517 if(mappingp(added_cmds) && query_verb()) {
518 mixed *fun, *rule, *ids;
519
520 // ist das Verb ein Key im Kommandomapping?
521 if(m_contains(&fun, &flag, &rule, &ids, added_cmds, query_verb())) {
522 string *noparsestr;
523 nindex=sizeof(fun);
524 noparsestr=explode((str||"")," ")-({""});
525 // dann matche ungeparsten Input gegen etwaige Regeln
526 // -- nicht aendern: neue Kommandos sollen alte "ueberschreiben" koennen
527 while(nindex--)
Zesstra@Morgengrauen4ba72dc2016-09-08 23:13:26 +0200528 {
529 mixed cmd_id = (pointerp(ids) && sizeof(ids)>nindex) ? ids[nindex] : 0;
530 if(_process_command(&str, noparsestr, fun[nindex], flag[nindex],
531 rule[nindex], cmd_id))
MG Mud User88f12472016-06-24 23:31:02 +0200532 {
533 GiveEP(EP_CMD, query_verb());
534 return 1;
535 }
Zesstra@Morgengrauen4ba72dc2016-09-08 23:13:26 +0200536 }
MG Mud User88f12472016-06-24 23:31:02 +0200537 }
538
539 // keine Regel passte, unscharfe Auswertung auf alle
540 // AddCmdverben ausdehnen
541 keys=m_indices(added_cmds);
542 nindex=sizeof(keys);
543 while(nindex--)
544 if(!strstr(query_verb(),keys[nindex]) &&
545 member((flag=added_cmds[keys[nindex],1]),1)>=0) {
546 int i,ret;
547 i=sizeof(flag);
548 // Reihenfolge nicht aendern !
549 while(i--)
550 if(flag[i]==1) {
551 mixed *exec = added_cmds[keys[nindex],0];
552 if(!pointerp(exec) || sizeof(exec)<=i)
553 catch(raise_error(
554 "AddCmd-Auswertung: CatchAll-Kommando \""+keys[nindex]+"\""
555 " wurde waehrend der Ausfuehrung veraendert oder geloescht. "
556 "Klartext: Das ist schlecht. Sucht ein RemoveCmd? ");
557 publish);
558 else if(_execute(exec[i],str,0)) {
559 GiveEP(EP_CMD, query_verb());
560 return 1;
561 }
562 }
563 }
564 }
565 return 0;
566}
567
568void init() {
569 add_action("_cl","",1);
570}
571
572
573static void _check_copy_commands(string ind, mixed *fun,
574 mixed *flag, mixed *rule) {
575 if(pointerp(fun)) added_cmds[ind,0]=fun+allocate(0);
576 else added_cmds[ind,0]=({fun});
577 if(pointerp(flag)) added_cmds[ind,1]=flag+allocate(0);
578 else if(flag) added_cmds[ind,1]=({flag});
579 else added_cmds[ind,1]=allocate(sizeof(added_cmds[ind]));
580 if(pointerp(rule)) added_cmds[ind,2]=rule+allocate(0);
581 else added_cmds[ind,2]=allocate(sizeof(added_cmds[ind]));
582
583 if(sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,1]) ||
584 sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,2])) {
585 added_cmds=(mapping)0;
586 raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
587 }
588}
589
590static mapping _set_commands(mapping commands) {
591 if(!commands) added_cmds=(mapping)0;
592 else if(mappingp(commands)) {
593 added_cmds=m_allocate(sizeof(commands),4);
594 walk_mapping(commands,#'_check_copy_commands);
595 }
596 if (mappingp(added_cmds))
597 return(deep_copy(added_cmds));
598 else
599 return (mapping)0;
600}
601
602static mapping _query_commands() {
603 if (mappingp(added_cmds))
604 return(deep_copy(added_cmds));
605 else
606 return (mapping)0;
607}
608