// $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 #include /* 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_ents": ({ "_request_entries", 0, "_num" }), "_request_entry": ({ "_request_entry", 0, "_id" }), "_request_ent": ({ "_request_entry", 0, "_id" }), "_request_comment": ({ "_request_comment", 0, "_id", "_text" }), "_request_com": ({ "_request_comment", 0, "_id", "_text" }), "_request_title": ({ "_request_title", 0, "_id", "_title" }), "_request_addentry": ({ "_request_addentry", 0, "_text" }), "_request_addent": ({ "_request_addentry", 0, "_text" }), "_request_submit": ({ "_request_addentry", 0, "_text" }), "_request_blog": ({ "_request_addentry", 0, "_text" }), "_request_delentry": ({ "_request_delentry", 0, "_id" }), "_request_delent": ({ "_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" }), #ifdef TWITTER "_request_tw": ({ "_request_twitter", 0, "_switch" }), "_request_twitter": ({ "_request_twitter", 0, "_switch" }), #endif #ifdef IDENTICA "_request_ica": ({ "_request_identica", 0, "_switch" }), "_request_identica": ({ "_request_identica", 0, "_switch" }), #endif #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