psyced/world/net/library/signature.c

149 lines
6.3 KiB
C

// $Id: signature.c,v 1.17 2008/03/11 13:42:26 lynx Exp $ // vim:syntax=lpc
//
// generic implementation of http://about.psyc.eu/Signature
//
// maps an anonymous ordered value list to a named parametric var mapping
// according to the signature of a method. probably should
// also be able to do it in the other direction.
//
// needed both for converting slash commands into _request commands
// and to implement a protocol level optimization where variable names
// can be left out as long as the values are in the proper order.
//
// currently we only do the first of the two applications (actually the
// second being an extension of the first). to achieve that we hold a hash
// of command methods which point to their respective signatures.
#include <net.h>
#include <signature.h>
/* we still don't use structs because they make the driver a lot fatter
* we'll get by using arrays and a couple of macros from signature.h
*
struct Signature {
closure handler;
mixed extra;
array(string) vars;
};
*
* also, we don't use closures as closures stick to the blueprint instead
* of operating on the working object. apparently bind_lambda() can rebind
* a CLOSURE_LFUN, but i didn't find out how to create that type of closure
* and having something like
* if (blueprintp(ME)) return
* previous_object()->myself(myargs);
* in *each* handler is a very ugly enterprise. I finally decided that the
* little performance trade off in looking up an object's function in its
* function table is well worth the simplicity we get by keeping all
* signatures in a central place, being here. first I tried to make a custom
* signature hash for each blueprint of a place, but that is just too messy
* to go for.
*/
// these could be generated by an external tool
private volatile mapping _sigs = ([
// "OFFICIAL" METHODS as seen on [[Command]].
// ahem... _do? consistency please!
"_request_do_show_log": ({ "_request_history", 0, "_parameter" }),
// can either be _amount or _match or _parameter
"_request_history": ({ "_request_history", 0, "_parameter" }),
"_request_kick": ({ "_request_kick", 0, "_person" }),
"_request_nickname": ({ "_request_nick_local", 0, "_nick_local", "_INTERNAL_stuss" }),
// the real thing, maybe? method inheritance could even lead to here
// for all of the _request_set_something methods. good? bad?
"_request_set": ({ "_request_set", 0, "_key", "_value" }),
// when called by _request_set(), value might be in _value
"_request_set_masquerade": ({ "_request_masquerade", 0, "_flag_masquerade" }),
"_request_set_owners": ({ "_request_owners", 0, "_list_owners" }), // _tab
"_request_set_public": ({ "_request_public", 0, "_flag_public" }),
"_request_set_style": ({ "_request_set_style", 0, "_uniform_style" }),
// "INTERNAL" METHODS
// all of the following "fake" _request methods are just the psyced
// way to handle command name variations and shortcuts. never use this
// in your clients. always use the official versions listed on
// [[Command]]. when sending arbitrary commands, use _request_execute,
// don't make up _request_something like psyced does internally.
"_request_hist": ({ "_request_history", 0, "_parameter" }),
"_request_style": ({ "_request_set_style", 0, "_uniform_style" }),
"_request_owners": ({ "_request_owners", 0, "_list_owners" }), // _tab
"_request_elrid": ({ "_request_kick", 0, "_person" }),
"_request_masquerade": ({ "_request_masquerade", 0, "_flag_masquerade" }),
"_request_masq": ({ "_request_masquerade", 0, "_flag_masquerade" }),
"_request_nick": ({ "_request_nick_local", 0, "_nick_local", "_INTERNAL_stuss" }),
"_request_ni": ({ "_request_nick_local", 0, "_nick_local", "_INTERNAL_stuss" }),
"_request_public": ({ "_request_public", 0, "_flag_public" }),
"_request_pub": ({ "_request_public", 0, "_flag_public" }),
"_request_entries": ({ "_request_entries", 0, "_num" }),
"_request_entry": ({ "_request_entry", 0, "_id" }),
"_request_comment": ({ "_request_comment", 0, "_id", "_text" }),
"_request_thread": ({ "_request_thread", 0, "_id", "_title" }),
"_request_addentry": ({ "_request_addentry", 0, "_text" }),
"_request_submit": ({ "_request_addentry", 0, "_text" }),
"_request_blog": ({ "_request_addentry", 0, "_text" }),
"_request_delentry": ({ "_request_delentry", 0, "_id" }),
"_request_unsubmit": ({ "_request_delentry", 0, "_id" }),
"_request_unblog": ({ "_request_delentry", 0, "_id" }),
#ifdef _flag_enable_module_microblogging
"_request_add": ({ "_request_add", 0, "_person" }),
"_request_remove": ({ "_request_remove", 0, "_person" }),
"_request_priv": ({ "_request_privacy", 0, "_privacy" }),
"_request_privacy": ({ "_request_privacy", 0, "_privacy" }),
"_request_tw": ({ "_request_twitter", 0, "_switch" }),
"_request_twitter": ({ "_request_twitter", 0, "_switch" }),
#endif
#ifdef EXPERIMENTAL
// stuff to play around with
"_request_pset": ({ "_request_set", 0, "_key", "_value" }),
"_request_cset": ({ "_request_set", 0, "_key_set", "_value" }),
"_request_tt": ({ "_request_set", ([ "_key": "_topic" ]), "_value" }),
#endif
]);
varargs int call_signature(string source, string mc, mixed data,
mapping origvars, varargs array(mixed) more) {
Signature s = _sigs[mc];
mapping vars;
string last;
int i, j;
unless (s) return 0;
// ASSERT("call_signature:closurep", closurep(s[SHandler]), s)
// vars = ([ "_INTERNAL_method_signature": mc ]);
vars = ([]);
if (mappingp(origvars) && sizeof(origvars))
vars += origvars;
if (mappingp(s[SPreset])) vars += s[SPreset];
/* else if (s[SPreset])
vars["_INTERNAL_preset"] = s[SPreset]; */
if (pointerp(data) && sizeof(data)) {
vars["_INTERNAL_command"] = data[0];
for (i=1, j=SKeys; i<sizeof(data); i++, j++) {
if (j < sizeof(s))
// check var type here.. TODO
vars[ last = s[j] ] = data[i];
else
vars[ last ] += " "+ data[i];
}
data = 0;
}
P2(("call_signature: created %O as vars\n", vars))
// this is how i tried to do it with closures..
//bind_lambda(s[SHandler]);
//return apply(s[SHandler], vars, more);
return call_direct(previous_object(), s[SHandler],
source, mc, data, vars, more...);
}
#ifdef _flag_extend_backend
mapping register_signature(mapping newsigs) {
if (newsigs) {
_sigs = newsigs;
ASSERT("register_signature",
mappingp(_sigs) && sizeof(_sigs), _sigs)
}
return _sigs;
}
#endif