Skip to content

Instantly share code, notes, and snippets.

@Maxdamantus
Created May 11, 2011 03:09
Show Gist options
  • Save Maxdamantus/965852 to your computer and use it in GitHub Desktop.
Save Maxdamantus/965852 to your computer and use it in GitHub Desktop.
XJS thing from a while ago
XJS - JavaScript (SpiderMonkey) plugin for XChat, by Maxdamantus
http://ntus.uni.cc/xjs
http://ntus.uni.cc/xjs/xjs-8.c
== Description ==
This is a fairly simple extension, which allows for JavaScript scripts and code
to be used in XChat. The /js command usage:
Usage: /js e <code> - evaluate code in the "<commandline>" context
/js o <file> - create new context, executing code from <file>
/js i <id> <code> - inject <code> into context <id>
/js l - list existing contexts
/js k <id> - kill context <id>
== Compilation ==
You'll need to have xchat-plugin.h in the compilation directory (http://xchat.org/docs/xchat-plugin.h),
and know where the include path is for SpiderMonkey (usually /usr/include/js), and the library name
(seems to differ between libjs and libmozjs, and if the library is in a funny path, that path
(specify with -L))
There's a compiler command (uses gcc - might work with other compilers, who knows? Not me (I'm lazy))
in the comments of the .c file
It doesn't want to work in Windows - the DLL system seems a real mess (that's all
that should be in the way, other than removal of the XP_UNIX define), if you get it to load, let me
know (Freenode)
== Stuff ==
The xjs directory is "xjs", under your XChat home directory, eg: ($HOME/.xchat2/xjs)
It holds scripts which are loaded by the /js command, or the load() function
== Wrapper interface ==
I made a wrapper interface which is more JS-like, http://ntus.uni.cc/xjs/xchat.js
Put it in the xjs directory and load it with load("xchat.js") in scripts
== Functions ==
A brief overview of JavaScript functions provided by the extension, most are somewhat simplified
versions of the normal XChat plugin interface:
* print(str, ..)
- Prints (to the current context) each argument passed, separated by a newline
* xchat_print(str)
- Prints the string, <str>
* xchat_emit_print(name, arg, ..)
- Check XChat plugin docs for description
- Returns 1 on success, 0 on failure
* xchat_command(cmd)
- Does <cmd> in the current context (no first slash)
* xchat_get_prefs(name)
- Gets the value of the XChat preference <name>
- Returns the value (boolean, number, or string) or undefined on failure
* xchat_send_modes(nicks [,perline ], sign, mode)
- <nicks> is an array of nicks to set <sign><mode> on, with optionally <perline> modes per line
eg: xchat_send_modes(["Alex", "Pete", "Georgie", "Dim"], 2, "-", "v")
* xchat_nickcmp(a, b)
- Compare nicks/channels a and b, according to the context's server
- Returns < 0, 0, or > 0, refer to XChat plugin docs
* xchat_hook_command(name, pri, callback, help)
- Hooks the callback function <callback> to command <name> with priority <pri> and help text <help>
Refer to XChat plugin docs for callback function, though there's no userdata here
- Returns a unique hook object
* xchat_hook_timer(timeout, callback)
- Creates a timer that calls <callback> after <timeout> ms (no args)
If the callback returns non-zero, the callback is called again after <timeout> ms
- Returns a unique hook object
* xchat_hook_server(name, pri, callback)
- Hooks the callback <callback> to server message <name>, refer to XChat plugin docs
Callback format the same as the xchat_hook_command callback
- Returns a unique hook object
* xchat_hook_print(name, pri, callback)
- Refer to XChat plugin docs, though there's no userdata here
- Returns a unique hook object
* xchat_unhook(hook)
- Unhooks hook object <hook>, which must've been returned from a xchat_hook_* function
* xchat_unhook_all()
- Unhooks all hooks in this JS context
* xchat_strip(str [, flags])
- Strips IRC colours and text attributes from str, with optional flags <flags> (XChat plugin docs)
flags defaults to 2
- Returns the new string, which has been stripped of stuff
* xchat_list_get(name)
- See XChat plugin docs #lists for a list of lists - this function returns a whole list (array of objects)
rather than a handle to a list
eg: xchat_list_get("users")[0].nick will be the first (apparently alphabetically) nick in the context's
channel
Member of an object could be a string, number, or context object
- Returns the array of list member objects (all duplicated at call time)
* xchat_get_context()
- Returns a context object representing the current context
* xchat_set_context(context)
- Sets the current context to the context represented by the <context> context object
* xchat_cmp_context(a, b)
- Returns true if the two context objects <a> and <b> represent the same context
* xchat_find_context(servername, channel)
- Check XChat plugin docs (NULL is non-string in this case)
- Returns a context object representing the context found, or undefined
* xchat_get_info(id)
- Check XChat plugin docs for list of values for <id>
- Returns the string of information assosciated with <id> (will not handle win_ptr), or undefined
* JsInfo()
- Get information about the current JS context
- Returns an object with the members:
* jsver: String representing current JS version according to SpiderMonkey (to do with feature availability)
* scriptname: Name of the script (the one that's always open is called <commandline>)
* scriptid: Integer representing the JS context
* JsSet(name, value)
- Sets values that affect the current JS context
Only value so far is jsver (string), list at https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_VersionToString
* load(name)
- Evaluates contents frome the file <name> in the current JS context
- Returns the value of the last statement evaluated
* DynObj(callback)
- Creates an object that allows the function <callback> to define all sets and gets on that object
The function will be passed two or three arguments:
- Boolean representing whether the action is a set or a get (true for set)
- The member's key (could essentially be anything: foo[/bar/])
- If the action is a set, the value being set
The function should return the value being get/set
- Returns the DynObj object
* DBG()
- Prints some stuff that you shouldn't need to care about
== Number constants ==
These are here for return values of callbacks etc, and have the values from xchat-plugin.h
* XCHAT_IFACE_MAJOR
* XCHAT_IFACE_MINOR
* XCHAT_IFACE_MICRO
* XCHAT_PRI_HIGHEST
* XCHAT_PRI_HIGH
* XCHAT_PRI_NORM
* XCHAT_PRI_LOW
* XCHAT_PRI_LOWEST
* XCHAT_FD_READ
* XCHAT_FD_WRITE
* XCHAT_FD_EXCEPTION
* XCHAT_FD_NOTSOCKET
* XCHAT_EAT_NONE
* XCHAT_EAT_XCHAT
* XCHAT_EAT_PLUGIN
* XCHAT_EAT_ALL
== External libraries ==
I couldn't find any good looking external SpiderMonkey libraries that would work on x86_64 atleast, so.. Meh
I'm on Freenode if you have any suggestions
var xchat = (function(){
var xchatt, config, saylock = false;
function newhook(hook){
return {
unhook : function(){
xchat_unhook(hook);
}
}
}
function parse2space(str){
var bd, qs, x;
for(x = bd = qs = 0; str[x]; x++)
switch(str[x]){
case "(": case "[": case "{":
bd++; break;
case ")": case "]": case "}":
if(--bd < 0)
return x;
break;
case ",": case ".": case "?":
if(!str[x+1] || str[x+1] == " ")
return x;
break;
case "/": qs++; case "\"": qs++; case "'": qs++;
if(!bd && x > 0 && (str[x-1].toLowerCase() > "a" && str[x-1].toLowerCase() < "z" || ")]}'\"".indexOf(str[x-1]) >= 0))
return x;
for(x++; str[x]; x++)
if(str[x] == "\\") x++;
else if(str[x] == (qs == 3? "/" : qs == 2? "\"" : "'")) break;
qs = 0; break;
case " ":
if(bd == 0)
return x;
}
return x;
}
function newcx(cx){
var cxself, ocx;
function scx(){cx && (ocx = xchat_get_context(), xchat_set_context(cx))}
function ecx(){cx && xchat_set_context(ocx)}
return cxself = {
p2s: parse2space,
get rawcx(){
return cx? cx : xchat_get_context();
},
get config(){
return config;
},
hkcmd : function(name, func){ var rv;
return newhook(xchat_hook_command(name, XCHAT_PRI_NORM, function(){ scx();
rv = func.apply(global, arguments);
ecx(); return rv;}));
},
hkprint : function(name, func){ var rv;
return newhook(xchat_hook_print(name, XCHAT_PRI_NORM, function(){ scx();
rv = func.apply(global, arguments);
ecx(); return rv;}));
},
hkserver : function(name, func){ var rv;
return newhook(xchat_hook_server(name, XCHAT_PRI_NORM, function(){ scx();
rv = func.apply(global, arguments);
ecx(); return rv;}));
},
timer : function(ms, func){ var rv;
return newhook(xchat_hook_timer(ms, function(){ scx();
rv = func.apply(global, arguments);
ecx(); return rv;}));
},
cmd : function(text){ scx();
xchat_command(text);
ecx(); },
print : function(){ var x; scx();
for(x = 0; x < arguments.length; x++)
xchat_print(arguments[x]);
ecx(); },
ls : DynObj(function(set, key, val){ var rv; scx();
rv = set? val : xchat_list_get(key);
if(!set && rv && rv[0] && rv[0].context)
for(x in rv)
rv[x].context = newcx(rv[x].context);
ecx(); return rv; }),
info : DynObj(function(set, key, val){ var x, rv; scx();
rv = set? val : xchat_get_info(key);
ecx(); return rv; }),
prefs : DynObj(function(set, key, val){ var rv; scx();
rv = set? (xchat.cmd("SET " + key + " " + (typeof val == "boolean"? val? 1 : 0 : val)), val) : xchat_get_prefs(key);
ecx(); return rv; }),
get chan(){ return cxself.info.channel; },
get serv(){ return cxself.info.server; },
get net(){ return cxself.info.network; },
get me(){ return cxself.info.nick; },
set me(nick){ cxself.cmd("NICK " + nick); return nick; },
get topic(){ return cxself.info.topic; },
get modes(){ return cxself.info.modes; },
get ver(){ return cxself.info.version; },
strip : function(str, flags){
return xchat_strip(str, flags == undefined? 3 : flags);
},
ncmp : function(s1, s2){ var rv; scx();
rv = xchat_nickcmp(s1, s2);
ecx(); return rv; },
cmp : function(s1, s2){ return cxself.ncmp(s1, s2); },
chancx : function(a, b){ var rv; scx();
(rv = xchat_find_context(b == undefined? undefined : a, b == undefined? a : b)) && (rv = newcx(rv));
ecx(); return rv; },
say : function(text){
for each(x in text.toString().split("\n"))
cxself.cmd("SAY " + x);
},
msg : function(to, text){
for each(x in text.toString().split("\n"))
cxself.cmd("MSG " + to + " " + x);
}
}
}
(xchatt = {
get cx(){
return newcx(xchat_get_context());
},
eat: {
none: XCHAT_EAT_NONE,
xchat: XCHAT_EAT_XCHAT,
plugin: XCHAT_EAT_PLUGIN,
all: XCHAT_EAT_ALL
}
}).__proto__ = newcx();
config = {
evchar: ","
};
xchat_unhook_all();
if(JsInfo().scriptname == "<commandline>")
xchatt.hkcmd("", function(w, we){
var line, x, y, p, ret;
if(saylock || we[1].substr(0, config.evchar.length) != config.evchar)
return xchat.eat.none;
if((line = we[1].substr(config.evchar.length)).substr(0, config.evchar.length) == config.evchar)
return saylock = true, xchat.cmd("SAY " + line), saylock = false, XCHAT_EAT_ALL;
for(x = 0, ret = ""; (p = line.indexOf("@", x)) >= 0;){
ret += line.substr(x, p - x);
if(line[p+1] == "@")
x = p + 2;
else{
with(xchat)
ret += eval(line.substr(p + 1, y = parse2space(line.substr(p + 1))), xchat);
x = p + y + 1;
}
}
xchat.say(ret += line.substr(x));
return xchat.eat.all;
});
return xchatt;
})();
/*
* xchat.cx - object representing the current context, has functions that are done in that context, can take xchat.cx and save for later use
* xchat - the same functions as in xchat.cx, but not context-specific, xchat.timer(10000, (){xchat.cmd(..)}) will execute the command in
* the channel that's active in 10 secs
* This will also install a hook, if loaded in the <commandline> context, which will allow for quick JS evaluation in messages, which start with
* a comma (,) - JS statements start with @ and end with a space, unless there's a bracket, in which case it'll do crazy stuff to figure out the
* end. Use a double comma to escape the evals (like the double slash for commands)
* ,5+5 is @5+5
* -> 5+5 is 10
* ,@"this is a test".split(" ") test
* -> this,is,a,test test
* ,Sum of {1..100} is @{for(y = x = 0; x <= 100; x++) y += x}
* -> Sum of {1..100} is 5050
* ,,@5+5
* -> ,@5+5
*/
/*
* xjs-8.c
*
* gcc -Wl,--export-dynamic -shared -fPIC -I/usr/include/js xjs.c -o xjs.so -ljs
*
* XJS - JavaScript (SpiderMonkey) plugin for XChat, by Maxdamantus
* http://ntus.uni.cc/xjs
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define XP_UNIX
#include <jsapi.h>
#include "xchat-plugin.h"
#define PLUGNAME "XJS"
#define PLUGDESC "SpiderMonkey plugin"
#define PLUGVER "8"
#define XCMD_JS_HELP \
"Usage: /js e <code> - evaluate code in the \"<commandline>\" context\n" \
" /js o <file> - create new context, executing code from <file>\n" \
" /js i <id> <code> - inject <code> into context <id>\n" \
" /js l - list existing contexts\n" \
" /js k <id> - kill context <id>\n"
xchat_plugin *ph;
JSRuntime *rt;
JSClass xjs_class_scriptglobal = {
"ScriptGlobal", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
struct xjs_hookll {
struct xjs_hookll *next;
xchat_hook *hook;
JSObject *oval;
};
struct xjs_instance {
struct xjs_instance *next;
xchat_context *xcx;
JSContext *cx;
JSObject *global;
struct xjs_hookll *hookf, *hookl;
int hookp, id;
char name[];
} *xjs_instf, *xjs_instl;
struct xjs_instance *xjs_cmdline;
struct jscb {
int type;
jsval func;
struct xjs_instance *inst;
xchat_hook *hook;
int cpid;
};
void js_errorhandler(JSContext *cx, const char *msg, JSErrorReport *report){
xchat_printf(ph, "Error [%s:%u] %s\n", ((struct xjs_instance*) JS_GetContextPrivate(cx))->name, report->lineno, msg);
}
JSClass xjs_class_hook = {
"XChatHook", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
JSClass xjs_class_context = {
"XChatContext", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
JSBool xjs_dynobj_set(JSContext *cx, JSObject *obj, jsval idval, jsval *rval){
jsval func, argv[3];
JS_GetReservedSlot(cx, obj, 0, &func);
argv[0] = JSVAL_TRUE;
argv[1] = idval;
argv[2] = *rval;
JS_CallFunctionValue(cx, JS_GetGlobalObject(cx), func, 3, argv, rval);
return JS_TRUE;
}
JSBool xjs_dynobj_get(JSContext *cx, JSObject *obj, jsval idval, jsval *rval){
jsval func, argv[2];
JS_GetReservedSlot(cx, obj, 0, &func);
argv[0] = JSVAL_FALSE;
argv[1] = idval;
JS_CallFunctionValue(cx, JS_GetGlobalObject(cx), func, 2, argv, rval);
return JS_TRUE;
}
JSClass xjs_class_dynobj = {
"DynObj", JSCLASS_HAS_RESERVED_SLOTS(1),
xjs_dynobj_set, JS_PropertyStub, xjs_dynobj_get, xjs_dynobj_set,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
};
void xjs_reghook(JSContext *cx, xchat_hook *hook, JSObject *oval){
struct xjs_instance *xjs;
xjs = JS_GetContextPrivate(cx);
xjs->hookl = *(xjs->hookl? &xjs->hookl->next : &xjs->hookf) = JS_malloc(cx, sizeof(*xjs->hookl));
xjs->hookl->next = NULL;
xjs->hookl->hook = hook;
xjs->hookl->oval = oval;
JS_SetPrivate(cx, oval, hook);
JS_AddRoot(cx, &xjs->hookl->oval);
}
void xjs_unreghook(JSContext *cx, xchat_hook *hook){
struct xjs_instance *xjs;
struct xjs_hookll *ll, *ll2;
xjs = JS_GetContextPrivate(cx);
for(ll = xjs->hookf; ll; ll2 = ll, ll = ll->next)
if(ll->hook == hook)
break;
JS_SetPrivate(cx, ll->oval, NULL);
JS_RemoveRoot(cx, &ll->oval);
if(ll == xjs->hookf){
if(!(xjs->hookf = ll->next))
xjs->hookl = NULL;
}else
ll2->next = ll->next;
JS_free(cx, ll);
}
void xjs_clearhooks(struct xjs_instance *inst){
while(inst->hookf){
xchat_unhook(ph, inst->hookf->hook);
xjs_unreghook(inst->cx, inst->hookf->hook);
}
}
jsval xjs_hook_to_jsval(JSContext *cx, xchat_hook *hook){
JSObject *oval;
oval = JS_NewObject(cx, &xjs_class_hook, NULL, NULL);
xjs_reghook(cx, hook, oval);
return OBJECT_TO_JSVAL(oval);
}
jsval xjs_context_to_jsval(JSContext *cx, xchat_context *context){
JSObject *oval;
oval = JS_NewObject(cx, &xjs_class_context, NULL, NULL);
JS_SetPrivate(cx, oval, context);
return OBJECT_TO_JSVAL(oval);
}
JSBool xjs_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
int x;
for(x = 0; x < argc; x++)
xchat_printf(ph, "%s\n", JS_GetStringBytes(JS_ValueToString(cx, argv[x])));
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
xchat_print(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_emit_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
int x;
char *s[6];
for(x = 0; x < 6; x++)
s[x] = x < argc? JS_EncodeString(cx, JS_ValueToString(cx, argv[x])) : NULL;
JS_NewNumberValue(cx, xchat_emit_print(ph, s[0], s[1], s[2], s[3], s[4], s[5]), rval);
for(x = 0; x < 6; x++)
if(s[x])
JS_free(cx, s[x]);
return JS_TRUE;
}
JSBool xjs_xchat_command(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
xchat_command(ph, JS_EncodeString(cx, JS_ValueToString(cx, argv[0])));
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_get_prefs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
char *a;
const char *string;
int integer;
switch(xchat_get_prefs(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])), &string, &integer)){
case 0:
*rval = JSVAL_VOID;
break;
case 1:
a = JS_strdup(cx, string);
*rval = STRING_TO_JSVAL(JS_NewString(cx, a, strlen(a)));
break;
case 2:
JS_NewNumberValue(cx, integer, rval);
break;
case 3:
*rval = integer? JSVAL_TRUE : JSVAL_FALSE;
break;
}
return JS_TRUE;
}
JSBool xjs_xchat_send_modes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *nicks;
jsuint len;
jsval vp;
int x;
char *t1, *t2, **targets;
*rval = JSVAL_VOID;
if(JSVAL_IS_NULL(argv[0]) || !JSVAL_IS_OBJECT(argv[0]) || !JS_ValueToObject(cx, argv[0], &nicks) || !JS_IsArrayObject(cx, nicks))
return JS_TRUE;
JS_GetArrayLength(cx, nicks, &len);
targets = JS_malloc(cx, (len > 10000? (len = 10000) : len) * sizeof(*targets));
for(x = 0; x < len; x++){
JS_GetElement(cx, nicks, x, &vp);
targets[x] = JS_EncodeString(cx, JS_ValueToString(cx, vp));
}
if(argc <= 3 || !JS_ValueToInt32(cx, argv[1], &x))
x = 0;
xchat_send_modes(ph, (const char**) targets, len, x, /* fuck you, const keyword */
*(t1 = JS_EncodeString(cx, JS_ValueToString(cx, argv[1 + (argc > 3)]))),
*(t2 = JS_EncodeString(cx, JS_ValueToString(cx, argv[2 + (argc > 3)]))));
JS_free(cx, t1);
JS_free(cx, t2);
for(x = 0; x < len; x++)
JS_free(cx, targets[x]);
return JS_TRUE;
}
JSBool xjs_xchat_nickcmp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
char *t1, *t2;
JS_NewNumberValue(cx, xchat_nickcmp(ph, t1 = JS_EncodeString(cx, JS_ValueToString(cx, argv[0])), t2 = JS_EncodeString(cx, JS_ValueToString(cx, argv[1]))), rval);
JS_free(cx, t1);
JS_free(cx, t2);
return JS_TRUE;
}
int xjs_cmdcb(char **word, char **word_eol, void *udata){
jsval rval, argv[2];
JSObject *aword, *aword_eol;
struct jscb *cb;
int x;
cb = udata;
aword = JS_NewArrayObject(cb->inst->cx, 0, NULL);
for(x = 1; word[x] && *word[x]; x++)
JS_DefineElement(cb->inst->cx, aword, x, STRING_TO_JSVAL(JS_NewStringCopyZ(cb->inst->cx, word[x])), NULL, NULL, JSPROP_ENUMERATE);
aword_eol = JS_NewArrayObject(cb->inst->cx, 0, NULL);
for(x = 1; word_eol[x] && *word_eol[x]; x++)
JS_DefineElement(cb->inst->cx, aword_eol, x, STRING_TO_JSVAL(JS_NewStringCopyZ(cb->inst->cx, word_eol[x])), NULL, NULL, JSPROP_ENUMERATE);
argv[0] = OBJECT_TO_JSVAL(aword);
argv[1] = OBJECT_TO_JSVAL(aword_eol);
if(!JS_CallFunctionValue(cb->inst->cx, cb->inst->global, cb->func, 2, argv, &rval) || !JS_ValueToInt32(cb->inst->cx, rval, &x))
x = 0;
JS_MaybeGC(cb->inst->cx);
return x;
}
int xjs_hpcb(char **word, void *udata){
jsval rval, argv[1];
JSObject *aword;
struct jscb *cb;
int x = 0;
cb = udata;
aword = JS_NewArrayObject(cb->inst->cx, 0, NULL);
for(x = 1; word[x] && *word[x]; x++)
JS_DefineElement(cb->inst->cx, aword, x, STRING_TO_JSVAL(JS_NewStringCopyZ(cb->inst->cx, word[x])), NULL, NULL, JSPROP_ENUMERATE);
argv[0] = OBJECT_TO_JSVAL(aword);
if(!JS_CallFunctionValue(cb->inst->cx, cb->inst->global, cb->func, 1, argv, &rval) || !JS_ValueToInt32(cb->inst->cx, rval, &x))
x = 0;
JS_MaybeGC(cb->inst->cx);
return x;
}
int xjs_timecb(void *udata){
jsval rval;
struct jscb *cb;
int x;
cb = udata;
JS_CallFunctionValue(cb->inst->cx, cb->inst->global, cb->func, 0, NULL, &rval);
if(JSVAL_IS_VOID(rval) || !JS_ValueToInt32(cb->inst->cx, rval, &x))
x = 0;
if(!x){
xjs_unreghook(cb->inst->cx, cb->hook);
xchat_unhook(ph, cb->hook);
JS_RemoveRoot(cb->inst->cx, &cb->func);
JS_free(cb->inst->cx, cb);
}
JS_MaybeGC(cb->inst->cx);
return x;
}
JSBool xjs_xchat_hook_command(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct jscb *cb;
char *t1, *t2;
int x;
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 0;
cb->func = argv[2];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
if(!JS_ValueToInt32(cx, argv[1], &x))
x = 0;
*rval = xjs_hook_to_jsval(cx,
cb->hook = xchat_hook_command(ph, t1 = JS_EncodeString(cx, JS_ValueToString(cx, argv[0])), x, xjs_cmdcb, t2 = JS_EncodeString(cx, JS_ValueToString(cx, argv[3])), cb));
JS_free(cx, t1);
JS_free(cx, t2);
return JS_TRUE;
}
JSBool xjs_xchat_hook_timer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct jscb *cb;
int x;
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 1;
cb->func = argv[1];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
if(!JS_ValueToInt32(cx, argv[0], &x))
x = 0;
*rval = xjs_hook_to_jsval(cx, cb->hook = xchat_hook_timer(ph, x, xjs_timecb, cb));
return JS_TRUE;
}
JSBool xjs_xchat_hook_server(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct jscb *cb;
int x;
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 2;
cb->func = argv[2];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
if(!JS_ValueToInt32(cx, argv[1], &x))
x = 0;
*rval = xjs_hook_to_jsval(cx, cb->hook = xchat_hook_server(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])), x, xjs_cmdcb, cb));
return JS_TRUE;
}
JSBool xjs_xchat_hook_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct jscb *cb;
int x;
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 3;
cb->func = argv[2];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
if(!JS_ValueToInt32(cx, argv[1], &x))
x = 0;
*rval = xjs_hook_to_jsval(cx, cb->hook = xchat_hook_print(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])), x, xjs_hpcb, cb));
return JS_TRUE;
}
JSBool xjs_xchat_unhook(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *oval;
xchat_hook *hook;
if(!JSVAL_IS_OBJECT(argv[0]) || JS_GET_CLASS(cx, oval = JSVAL_TO_OBJECT(argv[0])) != &xjs_class_hook)
JS_ReportWarning(cx, "Not an XChatHook object");
else if(hook = JS_GetPrivate(cx, oval)){
xjs_unreghook(cx, hook);
xchat_unhook(ph, hook);
}
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_unhook_all(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
xjs_clearhooks(JS_GetContextPrivate(cx));
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_strip(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
char *r;
int x;
if(JSVAL_IS_VOID(argv[1]) || !JS_ValueToInt32(cx, argv[1], &x))
x = 2;
if(r = xchat_strip(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])), -1, x))
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, r));
else
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_list_get(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *listobj, *listarray;
jsval listval;
xchat_list *xcl;
int x;
char *lname;
const char *const *sfields, *const *fields;
printf("lname = %s\n", lname = JS_EncodeString(cx, JS_ValueToString(cx, argv[0])));
if(xcl = xchat_list_get(ph, lname)){
*rval = OBJECT_TO_JSVAL(listarray = JS_NewArrayObject(cx, 0, NULL));
sfields = xchat_list_fields(ph, lname);
for(x = 0; xchat_list_next(ph, xcl); x++){
listobj = JS_NewObject(cx, NULL, NULL, NULL);
for(fields = sfields; *fields; fields++){
switch(**fields){
case 's':
listval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, xchat_list_str(ph, xcl, *fields+1)));
break;
case 'i':
JS_NewNumberValue(cx, xchat_list_int(ph, xcl, *fields+1), &listval);
break;
case 'p':
if(!strcmp(*fields+1, "context")){
listval = xjs_context_to_jsval(cx, (xchat_context*)xchat_list_str(ph, xcl, *fields+1));
break;
}
default:
continue; /* Any more pointers? */
}
JS_SetProperty(cx, listobj, *fields+1, &listval);
}
JS_DefineElement(cx, listarray, x, OBJECT_TO_JSVAL(listobj), NULL, NULL, JSPROP_ENUMERATE);
}
xchat_list_free(ph, xcl);
}else
*rval = JSVAL_VOID;
JS_free(cx, lname);
return JS_TRUE;
}
JSBool xjs_xchat_get_context(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
*rval = xjs_context_to_jsval(cx, xchat_get_context(ph));
return JS_TRUE;
}
JSBool xjs_xchat_cmp_context(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *oval1, *oval2;
if(!JSVAL_IS_OBJECT(argv[0]) || JS_GET_CLASS(cx, oval1 = JSVAL_TO_OBJECT(argv[0])) != &xjs_class_context
|| !JSVAL_IS_OBJECT(argv[1]) || JS_GET_CLASS(cx, oval2 = JSVAL_TO_OBJECT(argv[1])) != &xjs_class_context){
JS_ReportWarning(cx, "Not an XChatContext object");
*rval = JSVAL_VOID;
}else
*rval = JS_GetPrivate(cx, oval1) == JS_GetPrivate(cx, oval2)? JSVAL_TRUE : JSVAL_FALSE;
return JS_TRUE;
}
JSBool xjs_xchat_set_context(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *oval;
if(!JSVAL_IS_OBJECT(argv[0]) || JS_GET_CLASS(cx, oval = JSVAL_TO_OBJECT(argv[0])) != &xjs_class_context){
JS_ReportWarning(cx, "Not an XChatContext object");
*rval = JSVAL_VOID;
}else
JS_NewNumberValue(cx, xchat_set_context(ph, JS_GetPrivate(cx, oval)), rval);
return JS_TRUE;
}
JSBool xjs_xchat_find_context(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
xchat_context *xccx;
char *t1, *t2;
*rval = (xccx = xchat_find_context(ph,
t1 = JSVAL_IS_STRING(argv[0])? JS_GetStringBytes(JS_ValueToString(cx, argv[0])) : NULL,
t2 = JSVAL_IS_STRING(argv[1])? JS_GetStringBytes(JS_ValueToString(cx, argv[1])) : NULL))?
xjs_context_to_jsval(cx, xccx) : JSVAL_VOID;
if(t1) JS_free(cx, t1);
if(t2) JS_free(cx, t2);
return JS_TRUE;
}
JSBool xjs_jsinfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_instance *xjs;
JSObject *oval;
xjs = JS_GetContextPrivate(cx);
oval = JS_NewObject(cx, NULL, NULL, NULL);
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, JS_VersionToString(JS_GetVersion(cx))));
JS_SetProperty(cx, oval, "jsver", rval);
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, xjs->name));
JS_SetProperty(cx, oval, "scriptname", rval);
JS_NewNumberValue(cx, xjs->id, rval);
JS_SetProperty(cx, oval, "scriptid", rval);
*rval = OBJECT_TO_JSVAL(oval);
return JS_TRUE;
}
JSBool xjs_jsset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSVersion jsver;
char *key, *val;
if(JSVAL_IS_STRING(argv[0])){
key = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
if(!strcmp(key, "jsver")){
if((jsver = JS_StringToVersion(val = JS_GetStringBytes(JS_ValueToString(cx, argv[1])))) != JSVERSION_UNKNOWN){
JS_SetVersion(cx, jsver);
*rval = JSVAL_TRUE;
}else
*rval = JSVAL_FALSE;
JS_free(cx, val);
}else
*rval = JSVAL_VOID;
JS_free(cx, key);
}
return JS_TRUE;
}
JSBool xjs_load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
static char fnbuf[1024];
JSScript *script;
snprintf(fnbuf, 1024, "%s/xjs/%s", xchat_get_info(ph, "xchatdirfs"), JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
if(script = JS_CompileFile(cx, JS_GetGlobalObject(cx), fnbuf)){
JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, rval);
JS_DestroyScript(cx, script);
}else
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_dynobj(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *oval;
oval = JS_NewObject(cx, &xjs_class_dynobj, NULL, NULL);
*rval = OBJECT_TO_JSVAL(oval);
JS_SetReservedSlot(cx, oval, 0, argv[0]);
return JS_TRUE;
}
JSBool xjs_DBG(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_instance *xjs;
struct xjs_hookll *ll;
xchat_printf(ph, "** cx = %p **\n", (void*)cx);
xchat_printf(ph, "** hook list **\n");
xjs = JS_GetContextPrivate(cx);
for(ll = xjs->hookf; ll; ll = ll->next)
xchat_printf(ph, "-> %p: %p, %p\n", (void*)ll, (void*)ll->hook, (void*)ll->oval);
xchat_printf(ph, "** end **\n");
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_get_info(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
char *s;
if(!strcmp("win_ptr", s = JS_GetStringBytes(JS_ValueToString(cx, argv[0]))))
*rval = JSVAL_VOID;
else
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, xchat_get_info(ph, s)));
JS_free(cx, s);
return JS_TRUE;
}
JSFunctionSpec global_functions[] = {
{"print", xjs_print, 1, 0, 0},
{"xchat_print", xjs_xchat_print, 1, 0, 0},
{"xchat_emit_print", xjs_xchat_emit_print, 0, 0, 0},
{"xchat_command", xjs_xchat_command, 1, 0, 0},
{"xchat_get_prefs", xjs_xchat_get_prefs, 1, 0, 0},
{"xchat_send_modes", xjs_xchat_send_modes, 3, 0, 0},
{"xchat_nickcmp", xjs_xchat_nickcmp, 2, 0, 0},
{"xchat_hook_command", xjs_xchat_hook_command, 4, 0, 0},
{"xchat_hook_timer", xjs_xchat_hook_timer, 2, 0, 0},
{"xchat_hook_server", xjs_xchat_hook_server, 3, 0, 0},
{"xchat_hook_print", xjs_xchat_hook_print, 3, 0, 0},
{"xchat_unhook", xjs_xchat_unhook, 1, 0, 0},
{"xchat_unhook_all", xjs_xchat_unhook_all, 0, 0, 0},
{"xchat_strip", xjs_xchat_strip, 2, 0, 0},
{"xchat_list_get", xjs_xchat_list_get, 1, 0, 0},
{"xchat_get_context", xjs_xchat_get_context, 0, 0, 0},
{"xchat_cmp_context", xjs_xchat_cmp_context, 2, 0, 0},
{"xchat_set_context", xjs_xchat_set_context, 1, 0, 0},
{"xchat_find_context", xjs_xchat_find_context, 2, 0, 0},
{"xchat_get_info", xjs_xchat_get_info, 1, 0, 0},
{"DynObj", xjs_dynobj, 1, 0, 0},
{"JsInfo", xjs_jsinfo, 0, 0, 0},
{"JsSet", xjs_jsset, 2, 0, 0},
{"load", xjs_load, 1, 0, 0},
{"DBG", xjs_DBG, 0, 0, 0},
{}
};
JSConstDoubleSpec global_properties[] = {
{XCHAT_IFACE_MAJOR, "XCHAT_IFACE_MAJOR", JSPROP_READONLY},
{XCHAT_IFACE_MINOR, "XCHAT_IFACE_MINOR", JSPROP_READONLY},
{XCHAT_IFACE_MICRO, "XCHAT_IFACE_MICRO", JSPROP_READONLY},
{XCHAT_PRI_HIGHEST, "XCHAT_PRI_HIGHEST", JSPROP_READONLY},
{XCHAT_PRI_HIGH, "XCHAT_PRI_HIGH", JSPROP_READONLY},
{XCHAT_PRI_NORM, "XCHAT_PRI_NORM", JSPROP_READONLY},
{XCHAT_PRI_LOW, "XCHAT_PRI_LOW", JSPROP_READONLY},
{XCHAT_PRI_LOWEST, "XCHAT_PRI_LOWEST", JSPROP_READONLY},
{XCHAT_FD_READ, "XCHAT_FD_READ", JSPROP_READONLY},
{XCHAT_FD_WRITE, "XCHAT_FD_WRITE", JSPROP_READONLY},
{XCHAT_FD_EXCEPTION, "XCHAT_FD_EXCEPTION", JSPROP_READONLY},
{XCHAT_FD_NOTSOCKET, "XCHAT_FD_NOTSOCKET", JSPROP_READONLY},
{XCHAT_EAT_NONE, "XCHAT_EAT_NONE", JSPROP_READONLY},
{XCHAT_EAT_XCHAT, "XCHAT_EAT_XCHAT", JSPROP_READONLY},
{XCHAT_EAT_PLUGIN, "XCHAT_EAT_PLUGIN", JSPROP_READONLY},
{XCHAT_EAT_ALL, "XCHAT_EAT_ALL", JSPROP_READONLY},
{}
};
struct xjs_instance *xjs_init(char *name){
struct xjs_instance *inst;
jsval oval;
inst = malloc(sizeof(*inst) + strlen(name) + 1);
strcpy(inst->name, name);
inst->cx = JS_NewContext(rt, 0x1000);
JS_SetContextPrivate(inst->cx, inst);
oval = OBJECT_TO_JSVAL(inst->global = JS_NewObject(inst->cx, &xjs_class_scriptglobal, NULL, NULL));
inst->hookf = inst->hookl = NULL;
inst->next = NULL;
inst->id = xjs_instl? xjs_instl->id + 1 : 0;
xjs_instl = *(xjs_instl? &xjs_instl->next : &xjs_instf) = inst;
JS_SetProperty(inst->cx, inst->global, "global", &oval);
JS_SetErrorReporter(inst->cx, js_errorhandler);
JS_InitStandardClasses(inst->cx, inst->global);
JS_DefineFunctions(inst->cx, inst->global, global_functions);
JS_DefineConstDoubles(inst->cx, inst->global, global_properties);
return inst;
}
void xjs_exit(struct xjs_instance *inst, int destroyall){
struct xjs_instance *inst1, *inst2;
for(inst1 = xjs_instf; inst1; inst2 = inst1, inst1 = inst1->next)
if(inst1 == inst)
break;
if(inst == xjs_instf)
(xjs_instf = inst->next) == NULL && (xjs_instl = NULL);
else
inst2->next = inst->next;
xjs_clearhooks(inst);
JS_DestroyContext(inst->cx);
free(inst);
if(!destroyall && inst == xjs_cmdline)
xjs_cmdline = xjs_init("<commandline>");
}
int xjs_eval(struct xjs_instance *inst, char *str, jsval *rval){
return !!JS_EvaluateScript(inst->cx, inst->global, str, strlen(str), inst->name, 0, rval);
}
char *xjs_eval2str(struct xjs_instance *inst, char *str){
jsval rval;
if(xjs_eval(inst, str, &rval))
return JSVAL_IS_VOID(rval)? NULL : JS_GetStringBytes(JS_ValueToString(inst->cx, rval));
else
return NULL;
}
struct xjs_instance *xjs_run(char *fname){
struct xjs_instance *inst;
char *name;
int x;
jsval rval;
JSScript *script;
for(name = fname, x = 0; fname[x]; x++)
if(fname[x] == '/')
name = fname + x + 1;
inst = xjs_init(name);
if(!(script = JS_CompileFile(inst->cx, inst->global, fname)))
return NULL;
else{
JS_ExecuteScript(inst->cx, inst->global, script, &rval);
JS_DestroyScript(inst->cx, script);
}
return inst;
}
int xcmd_js(char **word, char **word_eol, void *udata){
struct xjs_instance *instl;
int id;
static char fnbuf[1024], *t;
if(!strcasecmp(word[2], "l")){
xchat_printf(ph, " Ref# Name\n");
for(instl = xjs_instf; instl; instl = instl->next)
xchat_printf(ph, "%5i %s\n", instl->id, instl->name);
}else if(!strcasecmp(word[2], "e")){
if(t = xjs_eval2str(xjs_cmdline, word_eol[3]))
xchat_printf(ph, "%s\n", t);
}else if(!strcasecmp(word[2], "i") && *word[3] && *word_eol[4]){
id = atoi(word[3]);
for(instl = xjs_instf; instl; instl = instl->next)
if(instl->id == id)
break;
if(instl && instl->id == id){
if(t = xjs_eval2str(instl, word_eol[4]))
xchat_printf(ph, "%s\n", t);
}else
xchat_printf(ph, "Couldn't find context ID %i\n", id);
}else if(!strcasecmp(word[2], "o") && *word_eol[3]){
snprintf(fnbuf, 1024, "%s/xjs/%s", xchat_get_info(ph, "xchatdirfs"), word_eol[3]);
if(instl = xjs_run(fnbuf))
xchat_printf(ph, "Loaded %s with ID=%i\n", instl->name, instl->id);
else
xchat_printf(ph, "Failed to load %s\n", fnbuf);
}else if(!strcasecmp(word[2], "k") && *word[3]){
id = atoi(word[3]);
for(instl = xjs_instf; instl; instl = instl->next)
if(instl->id == id)
break;
if(instl && instl->id == id){
if(!JS_IsRunning(instl->cx)){
xchat_printf(ph, "Killing %s with ID=%i\n", instl->name, instl->id);
xjs_exit(instl, 0);
}else
xchat_printf(ph, "Cannot kill active context\n");
}
}else
xchat_print(ph, XCMD_JS_HELP);
return XCHAT_EAT_ALL;
}
void xchat_plugin_get_info(char **name, char **desc, char **version, void **reserved){
*name = PLUGNAME;
*desc = PLUGDESC;
*version = PLUGVER;
}
int xchat_plugin_init(xchat_plugin *xph, char **name, char **desc, char **version, char *arg){
ph = xph;
xchat_plugin_get_info(name, desc, version, NULL);
xchat_hook_command(ph, "JS", XCHAT_PRI_NORM, xcmd_js, XCMD_JS_HELP, NULL);
rt = JS_NewRuntime(0x100000);
xjs_cmdline = xjs_init("<commandline>");
xchat_print(ph, "XJS (JavaScript plugin) loaded!");
return 1;
}
int xchat_plugin_deinit(){
while(xjs_instf)
xjs_exit(xjs_instf, 1);
JS_DestroyRuntime(rt);
JS_ShutDown();
return 1;
}
/*
* xjs-9.c
*
* gcc -Wl,--export-dynamic -shared -fPIC -I/usr/include/js xjs.c -o xjs.so -ljs
*
* XJS - JavaScript (SpiderMonkey) plugin for XChat, by Maxdamantus
* http://ntus.uni.cc/xjs
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#ifndef __WIN32__
#include <unistd.h>
#include <fcntl.h>
#endif
#define XP_UNIX
#include <jsapi.h>
#include "xchat-plugin.h"
#define PLUGNAME "XJS"
#define PLUGDESC "SpiderMonkey plugin"
#define PLUGVER "9"
#define XCMD_JS_HELP \
"Usage: /js e <code> - evaluate code in the \"<commandline>\" context\n" \
" /js o <file> - create new context, executing code from <file>\n" \
" /js i <id> <code> - inject <code> into context <id>\n" \
" /js l - list existing contexts\n" \
" /js k <id> - kill context <id>\n"
enum xjs_fpipetypes {
xjs_pipe_file
};
xchat_plugin *ph;
JSRuntime *rt;
JSClass xjs_class_scriptglobal = {
"ScriptGlobal", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
JSClass xjs_class_file = {
"File", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
struct xjs_hookll {
struct xjs_hookll *next;
xchat_hook *hook;
JSObject *oval;
};
struct xjs_instance {
struct xjs_instance *next;
xchat_context *xcx;
JSContext *cx;
JSObject *global;
struct xjs_hookll *hookf, *hookl;
int hookp, id;
char name[];
} *xjs_instf, *xjs_instl, *xjs_cmdline;
struct xjs_fpipe {
int type, fd;
};
struct jscb {
int type;
jsval func;
struct xjs_instance *inst;
xchat_hook *hook;
int cpid;
};
void js_errorhandler(JSContext *cx, const char *msg, JSErrorReport *report){
xchat_printf(ph, "Error [%s:%u] %s\n", ((struct xjs_instance*) JS_GetContextPrivate(cx))->name, report->lineno, msg);
}
JSClass xjs_class_hook = {
"XChatHook", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
JSClass xjs_class_context = {
"XChatContext", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
void xjs_reghook(JSContext *cx, xchat_hook *hook, JSObject *oval){
struct xjs_instance *xjs;
xjs = JS_GetContextPrivate(cx);
xjs->hookl = *(xjs->hookl? &xjs->hookl->next : &xjs->hookf) = JS_malloc(cx, sizeof(*xjs->hookl));
xjs->hookl->next = NULL;
xjs->hookl->hook = hook;
xjs->hookl->oval = oval;
JS_SetPrivate(cx, oval, hook);
JS_AddRoot(cx, &xjs->hookl->oval);
}
void xjs_unreghook(JSContext *cx, xchat_hook *hook){
struct xjs_instance *xjs;
struct xjs_hookll *ll, *ll2;
xjs = JS_GetContextPrivate(cx);
for(ll = xjs->hookf; ll; ll2 = ll, ll = ll->next)
if(ll->hook == hook)
break;
JS_SetPrivate(cx, ll->oval, NULL);
JS_RemoveRoot(cx, &ll->oval);
if(ll == xjs->hookf){
if(!(xjs->hookf = ll->next))
xjs->hookl = NULL;
}else
ll2->next = ll->next;
JS_free(cx, ll);
}
jsval xjs_hook_to_jsval(JSContext *cx, xchat_hook *hook){
JSObject *oval;
oval = JS_NewObject(cx, &xjs_class_hook, NULL, NULL);
xjs_reghook(cx, hook, oval);
return OBJECT_TO_JSVAL(oval);
}
jsval xjs_context_to_jsval(JSContext *cx, xchat_context *context){
JSObject *oval;
oval = JS_NewObject(cx, &xjs_class_context, NULL, NULL);
JS_SetPrivate(cx, oval, context);
return OBJECT_TO_JSVAL(oval);
}
JSBool xjs_file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_fpipe *fpipe;
int rs;
static char rbuf[32768];
JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), 0, rval);
fpipe = JS_GetPrivate(cx, JSVAL_TO_OBJECT(*rval));
if(!JS_ValueToInt32(cx, argv[0], &rs))
rs = 32768;
rs = read(fpipe->fd, rbuf, rs);
*rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, rbuf, rs > 0? rs : 0));
return JS_TRUE;
}
JSBool xjs_file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_fpipe *fpipe;
JSString *str;
JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), 0, rval);
fpipe = JS_GetPrivate(cx, JSVAL_TO_OBJECT(*rval));
str = JS_ValueToString(cx, argv[0]);
JS_NewNumberValue(cx, write(fpipe->fd, JS_GetStringBytes(str), JS_GetStringLength(str)), rval);
return JS_TRUE;
}
JSBool xjs_file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_fpipe *fpipe;
JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), 0, rval);
fpipe = JS_GetPrivate(cx, JSVAL_TO_OBJECT(*rval));
if(fpipe->fd >= 0){
close(fpipe->fd);
fpipe->fd = -1;
*rval = JSVAL_TRUE;
}else
*rval = JSVAL_FALSE;
return JS_TRUE;
}
int xjs_fdcb(int fd, int flags, void *udata){
int xjs_timecb(void*);
return xjs_timecb(udata);
}
JSBool xjs_file_onread(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_fpipe *fpipe;
struct jscb *cb;
JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), 0, rval);
fpipe = JS_GetPrivate(cx, JSVAL_TO_OBJECT(*rval));
if(fpipe->fd < 0){
*rval = JSVAL_VOID;
return JS_TRUE;
}
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 4;
cb->func = argv[0];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
*rval = xjs_hook_to_jsval(cx, cb->hook = xchat_hook_fd(ph, fpipe->fd, XCHAT_FD_READ, xjs_fdcb, cb));
return JS_TRUE;
}
JSBool xjs_file_onwrite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_fpipe *fpipe;
struct jscb *cb;
JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), 0, rval);
fpipe = JS_GetPrivate(cx, JSVAL_TO_OBJECT(*rval));
if(fpipe->fd < 0){
*rval = JSVAL_VOID;
return JS_TRUE;
}
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 4;
cb->func = argv[0];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
*rval = xjs_hook_to_jsval(cx, cb->hook = xchat_hook_fd(ph, fpipe->fd, XCHAT_FD_WRITE, xjs_fdcb, cb));
return JS_TRUE;
}
void xjs_file_finalhandle(JSContext *cx, JSObject *obj){
struct xjs_fpipe *fpipe;
fpipe = JS_GetPrivate(cx, obj);
if(fpipe->fd >= 0)
close(fpipe->fd);
free(fpipe);
}
JSPropertySpec xjs_file_props[] = {
{"read", 0, JSPROP_ENUMERATE},
{"write", 1, JSPROP_ENUMERATE},
{"close", 2, JSPROP_ENUMERATE},
{"onread", 3, JSPROP_ENUMERATE},
{"onwrite", 4, JSPROP_ENUMERATE},
{NULL}
};
JSBool xjs_file_prophandle(JSContext *cx, JSObject *obj, jsval id, jsval *rv){
JSFunction *fv;
fv = NULL;
if(JSVAL_IS_INT(id))
switch(JSVAL_TO_INT(id)){
case 0:
fv = JS_NewFunction(cx, xjs_file_read, 1, 0, NULL, "File.read");
break;
case 1:
fv = JS_NewFunction(cx, xjs_file_write, 1, 0, NULL, "File.write");
break;
case 2:
fv = JS_NewFunction(cx, xjs_file_close, 0, 0, NULL, "File.close");
break;
case 3:
fv = JS_NewFunction(cx, xjs_file_onread, 1, 0, NULL, "File.onread");
break;
case 4:
fv = JS_NewFunction(cx, xjs_file_onwrite, 1, 0, NULL, "File.onwrite");
break;
}
if(fv){
JS_SetReservedSlot(cx, (JSObject*)fv, 0, OBJECT_TO_JSVAL(obj));
*rv = OBJECT_TO_JSVAL((JSObject*)fv);
}else
*rv = JSVAL_VOID;
return JS_TRUE;
}
JSClass xjs_socket_class = {
"Socket", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, xjs_file_prophandle, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xjs_file_finalhandle
};
JSBool xjs_dynobj_set(JSContext *cx, JSObject *obj, jsval idval, jsval *rval){
jsval func, argv[3];
JS_GetReservedSlot(cx, obj, 0, &func);
argv[0] = JSVAL_TRUE;
argv[1] = idval;
argv[2] = *rval;
JS_CallFunctionValue(cx, JS_GetGlobalObject(cx), func, 3, argv, rval);
return JS_TRUE;
}
JSBool xjs_dynobj_get(JSContext *cx, JSObject *obj, jsval idval, jsval *rval){
jsval func, argv[2];
JS_GetReservedSlot(cx, obj, 0, &func);
argv[0] = JSVAL_FALSE;
argv[1] = idval;
JS_CallFunctionValue(cx, JS_GetGlobalObject(cx), func, 2, argv, rval);
return JS_TRUE;
}
JSClass xjs_class_dynobj = {
"DynObj", JSCLASS_HAS_RESERVED_SLOTS(1),
xjs_dynobj_set, JS_PropertyStub, xjs_dynobj_get, xjs_dynobj_set,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
};
void xjs_clearhooks(struct xjs_instance *inst){
while(inst->hookf){
xchat_unhook(ph, inst->hookf->hook);
xjs_unreghook(inst->cx, inst->hookf->hook);
}
}
JSBool xjs_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
int x;
for(x = 0; x < argc; x++)
xchat_printf(ph, "%s\n", JS_GetStringBytes(JS_ValueToString(cx, argv[x])));
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
xchat_print(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_emit_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
int x;
char *s[6];
for(x = 0; x < 6; x++)
s[x] = x < argc? JS_EncodeString(cx, JS_ValueToString(cx, argv[x])) : NULL;
JS_NewNumberValue(cx, xchat_emit_print(ph, s[0], s[1], s[2], s[3], s[4], s[5]), rval);
for(x = 0; x < 6; x++)
if(s[x])
JS_free(cx, s[x]);
return JS_TRUE;
}
JSBool xjs_xchat_command(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
xchat_command(ph, JS_EncodeString(cx, JS_ValueToString(cx, argv[0])));
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_get_prefs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
char *a;
const char *string;
int integer;
switch(xchat_get_prefs(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])), &string, &integer)){
case 0:
*rval = JSVAL_VOID;
break;
case 1:
a = JS_strdup(cx, string);
*rval = STRING_TO_JSVAL(JS_NewString(cx, a, strlen(a)));
break;
case 2:
JS_NewNumberValue(cx, integer, rval);
break;
case 3:
*rval = integer? JSVAL_TRUE : JSVAL_FALSE;
break;
}
return JS_TRUE;
}
JSBool xjs_xchat_send_modes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *nicks;
jsuint len;
jsval vp;
int x;
char *t1, *t2, **targets;
*rval = JSVAL_VOID;
if(JSVAL_IS_NULL(argv[0]) || !JSVAL_IS_OBJECT(argv[0]) || !JS_ValueToObject(cx, argv[0], &nicks) || !JS_IsArrayObject(cx, nicks))
return JS_TRUE;
JS_GetArrayLength(cx, nicks, &len);
targets = JS_malloc(cx, (len > 10000? (len = 10000) : len) * sizeof(*targets));
for(x = 0; x < len; x++){
JS_GetElement(cx, nicks, x, &vp);
targets[x] = JS_EncodeString(cx, JS_ValueToString(cx, vp));
}
if(argc <= 3 || !JS_ValueToInt32(cx, argv[1], &x))
x = 0;
xchat_send_modes(ph, (const char**) targets, len, x, /* fuck you, const keyword */
*(t1 = JS_EncodeString(cx, JS_ValueToString(cx, argv[1 + (argc > 3)]))),
*(t2 = JS_EncodeString(cx, JS_ValueToString(cx, argv[2 + (argc > 3)]))));
JS_free(cx, t1);
JS_free(cx, t2);
for(x = 0; x < len; x++)
JS_free(cx, targets[x]);
return JS_TRUE;
}
JSBool xjs_xchat_nickcmp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
char *t1, *t2;
JS_NewNumberValue(cx, xchat_nickcmp(ph, t1 = JS_EncodeString(cx, JS_ValueToString(cx, argv[0])), t2 = JS_EncodeString(cx, JS_ValueToString(cx, argv[1]))), rval);
JS_free(cx, t1);
JS_free(cx, t2);
return JS_TRUE;
}
int xjs_cmdcb(char **word, char **word_eol, void *udata){
jsval rval, argv[2];
JSObject *aword, *aword_eol;
struct jscb *cb;
int x;
cb = udata;
aword = JS_NewArrayObject(cb->inst->cx, 0, NULL);
for(x = 1; word[x] && *word[x]; x++)
JS_DefineElement(cb->inst->cx, aword, x, STRING_TO_JSVAL(JS_NewStringCopyZ(cb->inst->cx, word[x])), NULL, NULL, JSPROP_ENUMERATE);
aword_eol = JS_NewArrayObject(cb->inst->cx, 0, NULL);
for(x = 1; word_eol[x] && *word_eol[x]; x++)
JS_DefineElement(cb->inst->cx, aword_eol, x, STRING_TO_JSVAL(JS_NewStringCopyZ(cb->inst->cx, word_eol[x])), NULL, NULL, JSPROP_ENUMERATE);
argv[0] = OBJECT_TO_JSVAL(aword);
argv[1] = OBJECT_TO_JSVAL(aword_eol);
if(!JS_CallFunctionValue(cb->inst->cx, cb->inst->global, cb->func, 2, argv, &rval) || !JS_ValueToInt32(cb->inst->cx, rval, &x))
x = 0;
JS_MaybeGC(cb->inst->cx);
return x;
}
int xjs_hpcb(char **word, void *udata){
jsval rval, argv[1];
JSObject *aword;
struct jscb *cb;
int x = 0;
cb = udata;
aword = JS_NewArrayObject(cb->inst->cx, 0, NULL);
for(x = 1; word[x] && *word[x]; x++)
JS_DefineElement(cb->inst->cx, aword, x, STRING_TO_JSVAL(JS_NewStringCopyZ(cb->inst->cx, word[x])), NULL, NULL, JSPROP_ENUMERATE);
argv[0] = OBJECT_TO_JSVAL(aword);
if(!JS_CallFunctionValue(cb->inst->cx, cb->inst->global, cb->func, 1, argv, &rval) || !JS_ValueToInt32(cb->inst->cx, rval, &x))
x = 0;
JS_MaybeGC(cb->inst->cx);
return x;
}
int xjs_timecb(void *udata){
jsval rval;
struct jscb *cb;
int x;
JSContext *cx;
cb = udata;
JS_CallFunctionValue(cx = cb->inst->cx, cb->inst->global, cb->func, 0, NULL, &rval);
if(JSVAL_IS_VOID(rval) || !JS_ValueToInt32(cx, rval, &x))
x = 0;
if(!x){
xjs_unreghook(cb->inst->cx, cb->hook);
xchat_unhook(ph, cb->hook);
JS_RemoveRoot(cb->inst->cx, &cb->func);
JS_free(cb->inst->cx, cb);
}
JS_MaybeGC(cx);
return x;
}
JSBool xjs_xchat_hook_command(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct jscb *cb;
char *t1, *t2;
int x;
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 0;
cb->func = argv[2];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
if(!JS_ValueToInt32(cx, argv[1], &x))
x = 0;
*rval = xjs_hook_to_jsval(cx,
cb->hook = xchat_hook_command(ph, t1 = JS_EncodeString(cx, JS_ValueToString(cx, argv[0])), x, xjs_cmdcb, t2 = JS_EncodeString(cx, JS_ValueToString(cx, argv[3])), cb));
JS_free(cx, t1);
JS_free(cx, t2);
return JS_TRUE;
}
JSBool xjs_xchat_hook_timer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct jscb *cb;
int x;
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 1;
cb->func = argv[1];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
if(!JS_ValueToInt32(cx, argv[0], &x))
x = 0;
*rval = xjs_hook_to_jsval(cx, cb->hook = xchat_hook_timer(ph, x, xjs_timecb, cb));
return JS_TRUE;
}
JSBool xjs_xchat_hook_server(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct jscb *cb;
int x;
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 2;
cb->func = argv[2];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
if(!JS_ValueToInt32(cx, argv[1], &x))
x = 0;
*rval = xjs_hook_to_jsval(cx, cb->hook = xchat_hook_server(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])), x, xjs_cmdcb, cb));
return JS_TRUE;
}
JSBool xjs_xchat_hook_print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct jscb *cb;
int x;
cb = JS_malloc(cx, sizeof(*cb));
cb->type = 3;
cb->func = argv[2];
cb->inst = JS_GetContextPrivate(cx);
JS_AddRoot(cx, &cb->func);
if(!JS_ValueToInt32(cx, argv[1], &x))
x = 0;
*rval = xjs_hook_to_jsval(cx, cb->hook = xchat_hook_print(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])), x, xjs_hpcb, cb));
return JS_TRUE;
}
JSBool xjs_xchat_unhook(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *oval;
xchat_hook *hook;
if(!JSVAL_IS_OBJECT(argv[0]) || JS_GET_CLASS(cx, oval = JSVAL_TO_OBJECT(argv[0])) != &xjs_class_hook)
JS_ReportWarning(cx, "Not an XChatHook object");
else if(hook = JS_GetPrivate(cx, oval)){
xjs_unreghook(cx, hook);
xchat_unhook(ph, hook);
}
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_unhook_all(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
xjs_clearhooks(JS_GetContextPrivate(cx));
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_strip(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
char *r;
int x;
if(JSVAL_IS_VOID(argv[1]) || !JS_ValueToInt32(cx, argv[1], &x))
x = 2;
if(r = xchat_strip(ph, JS_GetStringBytes(JS_ValueToString(cx, argv[0])), -1, x))
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, r));
else
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_list_get(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *listobj, *listarray;
jsval listval;
xchat_list *xcl;
int x;
char *lname;
const char *const *sfields, *const *fields;
printf("lname = %s\n", lname = JS_EncodeString(cx, JS_ValueToString(cx, argv[0])));
if(xcl = xchat_list_get(ph, lname)){
*rval = OBJECT_TO_JSVAL(listarray = JS_NewArrayObject(cx, 0, NULL));
sfields = xchat_list_fields(ph, lname);
for(x = 0; xchat_list_next(ph, xcl); x++){
listobj = JS_NewObject(cx, NULL, NULL, NULL);
for(fields = sfields; *fields; fields++){
switch(**fields){
case 's':
listval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, xchat_list_str(ph, xcl, *fields+1)));
break;
case 'i':
JS_NewNumberValue(cx, xchat_list_int(ph, xcl, *fields+1), &listval);
break;
case 'p':
if(!strcmp(*fields+1, "context")){
listval = xjs_context_to_jsval(cx, (xchat_context*)xchat_list_str(ph, xcl, *fields+1));
break;
}
default:
continue; /* Any more pointers? */
}
JS_SetProperty(cx, listobj, *fields+1, &listval);
}
JS_DefineElement(cx, listarray, x, OBJECT_TO_JSVAL(listobj), NULL, NULL, JSPROP_ENUMERATE);
}
xchat_list_free(ph, xcl);
}else
*rval = JSVAL_VOID;
JS_free(cx, lname);
return JS_TRUE;
}
JSBool xjs_xchat_get_context(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
*rval = xjs_context_to_jsval(cx, xchat_get_context(ph));
return JS_TRUE;
}
JSBool xjs_xchat_cmp_context(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *oval1, *oval2;
if(!JSVAL_IS_OBJECT(argv[0]) || JS_GET_CLASS(cx, oval1 = JSVAL_TO_OBJECT(argv[0])) != &xjs_class_context
|| !JSVAL_IS_OBJECT(argv[1]) || JS_GET_CLASS(cx, oval2 = JSVAL_TO_OBJECT(argv[1])) != &xjs_class_context){
JS_ReportWarning(cx, "Not an XChatContext object");
*rval = JSVAL_VOID;
}else
*rval = JS_GetPrivate(cx, oval1) == JS_GetPrivate(cx, oval2)? JSVAL_TRUE : JSVAL_FALSE;
return JS_TRUE;
}
JSBool xjs_xchat_set_context(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *oval;
if(!JSVAL_IS_OBJECT(argv[0]) || JS_GET_CLASS(cx, oval = JSVAL_TO_OBJECT(argv[0])) != &xjs_class_context){
JS_ReportWarning(cx, "Not an XChatContext object");
*rval = JSVAL_VOID;
}else
JS_NewNumberValue(cx, xchat_set_context(ph, JS_GetPrivate(cx, oval)), rval);
return JS_TRUE;
}
JSBool xjs_xchat_find_context(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
xchat_context *xccx;
char *t1, *t2;
*rval = (xccx = xchat_find_context(ph,
t1 = JSVAL_IS_STRING(argv[0])? JS_GetStringBytes(JS_ValueToString(cx, argv[0])) : NULL,
t2 = JSVAL_IS_STRING(argv[1])? JS_GetStringBytes(JS_ValueToString(cx, argv[1])) : NULL))?
xjs_context_to_jsval(cx, xccx) : JSVAL_VOID;
/* if(t1) JS_free(cx, t1);
if(t2) JS_free(cx, t2);*/
return JS_TRUE;
}
JSBool xjs_jsinfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_instance *xjs;
JSObject *oval;
xjs = JS_GetContextPrivate(cx);
oval = JS_NewObject(cx, NULL, NULL, NULL);
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, JS_VersionToString(JS_GetVersion(cx))));
JS_SetProperty(cx, oval, "jsver", rval);
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, xjs->name));
JS_SetProperty(cx, oval, "scriptname", rval);
JS_NewNumberValue(cx, xjs->id, rval);
JS_SetProperty(cx, oval, "scriptid", rval);
*rval = OBJECT_TO_JSVAL(oval);
return JS_TRUE;
}
JSBool xjs_jsset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSVersion jsver;
char *key, *val;
if(JSVAL_IS_STRING(argv[0])){
key = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
if(!strcmp(key, "jsver")){
if((jsver = JS_StringToVersion(val = JS_GetStringBytes(JS_ValueToString(cx, argv[1])))) != JSVERSION_UNKNOWN){
JS_SetVersion(cx, jsver);
*rval = JSVAL_TRUE;
}else
*rval = JSVAL_FALSE;
JS_free(cx, val);
}else
*rval = JSVAL_VOID;
JS_free(cx, key);
}
return JS_TRUE;
}
JSBool xjs_load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
static char fnbuf[1024];
JSScript *script;
snprintf(fnbuf, 1024, "%s/xjs/%s", xchat_get_info(ph, "xchatdirfs"), JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
if(script = JS_CompileFile(cx, JS_GetGlobalObject(cx), fnbuf)){
JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, rval);
JS_DestroyScript(cx, script);
}else
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_dynobj(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
JSObject *oval;
oval = JS_NewObject(cx, &xjs_class_dynobj, NULL, NULL);
*rval = OBJECT_TO_JSVAL(oval);
JS_SetReservedSlot(cx, oval, 0, argv[0]);
return JS_TRUE;
}
JSBool xjs_DBG(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
struct xjs_instance *xjs;
struct xjs_hookll *ll;
xchat_printf(ph, "** cx = %p **\n", (void*)cx);
xchat_printf(ph, "** hook list **\n");
xjs = JS_GetContextPrivate(cx);
for(ll = xjs->hookf; ll; ll = ll->next)
xchat_printf(ph, "-> %p: %p, %p\n", (void*)ll, (void*)ll->hook, (void*)ll->oval);
xchat_printf(ph, "** end **\n");
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool xjs_xchat_get_info(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
char *s;
if(!strcmp("win_ptr", s = JS_GetStringBytes(JS_ValueToString(cx, argv[0]))))
*rval = JSVAL_VOID;
else
*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, xchat_get_info(ph, s)));
JS_free(cx, s);
return JS_TRUE;
}
void xjs_fd2obj(JSContext *cx, int fd, jsval *rval){
JSObject *obj;
struct xjs_fpipe *pipes;
*rval = OBJECT_TO_JSVAL(obj = JS_NewObject(cx, &xjs_socket_class, NULL, NULL));
JS_SetPrivate(cx, obj, pipes = malloc(sizeof(*pipes)));
JS_DefineProperties(cx, obj, xjs_file_props);
pipes->type = xjs_pipe_file;
pipes->fd = fd;
}
JSBool xjs_file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
int x, l, fd, fl, mo, r, w;
jschar *fo;
if(JSVAL_IS_STRING(argv[1])){
l = JS_GetStringLength(JSVAL_TO_STRING(argv[1]));
fo = JS_GetStringChars(JSVAL_TO_STRING(argv[1]));
for(x = w = r = fl = 0; x < l; x++){
fl |= fo[x] == 'a'? O_APPEND : 0;
fl |= fo[x] == 'c'? O_CREAT : 0;
fl |= fo[x] == 't'? O_TRUNC : 0;
r |= fo[x] == 'r';
w |= fo[x] == 'w';
}
fl |= r && w? O_RDWR : w? O_WRONLY : O_RDONLY;
}else
fl = O_RDONLY;
if(!JS_ValueToInt32(cx, argv[2], &mo))
mo = 0644;
if((fd = open(JS_GetStringBytes(JS_ValueToString(cx, argv[0])), fl, mo)) < 0)
*rval = JSVAL_FALSE;
else
xjs_fd2obj(cx, fd, rval);
return JS_TRUE;
}
JSBool xjs_file_tcp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
int sock, po;
struct hostent *she;
struct sockaddr_in saddr;
if((she = gethostbyname(JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) == NULL){
*rval = JSVAL_FALSE;
return JS_TRUE;
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if(!JS_ValueToInt32(cx, argv[1], &po))
po = 0;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(po);
memcpy(&saddr.sin_addr, she->h_addr, she->h_length);
memset(saddr.sin_zero, 0, sizeof(saddr.sin_zero));
if(connect(sock, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
*rval = JSVAL_FALSE;
else
xjs_fd2obj(cx, sock, rval);
return JS_TRUE;
}
JSFunctionSpec file_functions[] = {
{"open", xjs_file_open, 3, 0, 0},
{"tcp", xjs_file_tcp, 2, 0, 0},
{NULL}
};
JSFunctionSpec global_functions[] = {
{"print", xjs_print, 1, 0, 0},
{"xchat_print", xjs_xchat_print, 1, 0, 0},
{"xchat_emit_print", xjs_xchat_emit_print, 0, 0, 0},
{"xchat_command", xjs_xchat_command, 1, 0, 0},
{"xchat_get_prefs", xjs_xchat_get_prefs, 1, 0, 0},
{"xchat_send_modes", xjs_xchat_send_modes, 3, 0, 0},
{"xchat_nickcmp", xjs_xchat_nickcmp, 2, 0, 0},
{"xchat_hook_command", xjs_xchat_hook_command, 4, 0, 0},
{"xchat_hook_timer", xjs_xchat_hook_timer, 2, 0, 0},
{"xchat_hook_server", xjs_xchat_hook_server, 3, 0, 0},
{"xchat_hook_print", xjs_xchat_hook_print, 3, 0, 0},
{"xchat_unhook", xjs_xchat_unhook, 1, 0, 0},
{"xchat_unhook_all", xjs_xchat_unhook_all, 0, 0, 0},
{"xchat_strip", xjs_xchat_strip, 2, 0, 0},
{"xchat_list_get", xjs_xchat_list_get, 1, 0, 0},
{"xchat_get_context", xjs_xchat_get_context, 0, 0, 0},
{"xchat_cmp_context", xjs_xchat_cmp_context, 2, 0, 0},
{"xchat_set_context", xjs_xchat_set_context, 1, 0, 0},
{"xchat_find_context", xjs_xchat_find_context, 2, 0, 0},
{"xchat_get_info", xjs_xchat_get_info, 1, 0, 0},
{"DynObj", xjs_dynobj, 1, 0, 0},
{"JsInfo", xjs_jsinfo, 0, 0, 0},
{"JsSet", xjs_jsset, 2, 0, 0},
{"load", xjs_load, 1, 0, 0},
{"DBG", xjs_DBG, 0, 0, 0},
{NULL}
};
JSConstDoubleSpec global_properties[] = {
{XCHAT_IFACE_MAJOR, "XCHAT_IFACE_MAJOR", JSPROP_READONLY},
{XCHAT_IFACE_MINOR, "XCHAT_IFACE_MINOR", JSPROP_READONLY},
{XCHAT_IFACE_MICRO, "XCHAT_IFACE_MICRO", JSPROP_READONLY},
{XCHAT_PRI_HIGHEST, "XCHAT_PRI_HIGHEST", JSPROP_READONLY},
{XCHAT_PRI_HIGH, "XCHAT_PRI_HIGH", JSPROP_READONLY},
{XCHAT_PRI_NORM, "XCHAT_PRI_NORM", JSPROP_READONLY},
{XCHAT_PRI_LOW, "XCHAT_PRI_LOW", JSPROP_READONLY},
{XCHAT_PRI_LOWEST, "XCHAT_PRI_LOWEST", JSPROP_READONLY},
{XCHAT_FD_READ, "XCHAT_FD_READ", JSPROP_READONLY},
{XCHAT_FD_WRITE, "XCHAT_FD_WRITE", JSPROP_READONLY},
{XCHAT_FD_EXCEPTION, "XCHAT_FD_EXCEPTION", JSPROP_READONLY},
{XCHAT_FD_NOTSOCKET, "XCHAT_FD_NOTSOCKET", JSPROP_READONLY},
{XCHAT_EAT_NONE, "XCHAT_EAT_NONE", JSPROP_READONLY},
{XCHAT_EAT_XCHAT, "XCHAT_EAT_XCHAT", JSPROP_READONLY},
{XCHAT_EAT_PLUGIN, "XCHAT_EAT_PLUGIN", JSPROP_READONLY},
{XCHAT_EAT_ALL, "XCHAT_EAT_ALL", JSPROP_READONLY},
{0}
};
struct xjs_instance *xjs_init(char *name){
struct xjs_instance *inst;
JSObject *file;
jsval oval, fval;
inst = malloc(sizeof(*inst) + strlen(name) + 1);
strcpy(inst->name, name);
inst->cx = JS_NewContext(rt, 0x1000);
JS_SetContextPrivate(inst->cx, inst);
oval = OBJECT_TO_JSVAL(inst->global = JS_NewObject(inst->cx, &xjs_class_scriptglobal, NULL, NULL));
JS_AddRoot(inst->cx, &inst->global);
inst->hookf = inst->hookl = NULL;
inst->next = NULL;
inst->id = xjs_instl? xjs_instl->id + 1 : 0;
xjs_instl = *(xjs_instl? &xjs_instl->next : &xjs_instf) = inst;
JS_SetProperty(inst->cx, inst->global, "global", &oval);
JS_SetErrorReporter(inst->cx, js_errorhandler);
JS_InitStandardClasses(inst->cx, inst->global);
JS_DefineFunctions(inst->cx, inst->global, global_functions);
JS_DefineConstDoubles(inst->cx, inst->global, global_properties);
fval = OBJECT_TO_JSVAL(file = JS_NewObject(inst->cx, &xjs_class_file, NULL, NULL));
JS_SetProperty(inst->cx, inst->global, "File", &fval);
JS_DefineFunctions(inst->cx, file, file_functions);
return inst;
}
void xjs_exit(struct xjs_instance *inst, int destroyall){
struct xjs_instance *inst1, *inst2;
for(inst1 = xjs_instf; inst1; inst2 = inst1, inst1 = inst1->next)
if(inst1 == inst)
break;
if(inst == xjs_instf)
(xjs_instf = inst->next) == NULL && (xjs_instl = NULL);
else
inst2->next = inst->next;
xjs_clearhooks(inst);
JS_RemoveRoot(inst->cx, &inst->global);
JS_DestroyContext(inst->cx);
free(inst);
if(!destroyall && inst == xjs_cmdline)
xjs_cmdline = xjs_init("<commandline>");
}
int xjs_eval(struct xjs_instance *inst, char *str, jsval *rval){
return !!JS_EvaluateScript(inst->cx, inst->global, str, strlen(str), inst->name, 0, rval);
}
char *xjs_eval2str(struct xjs_instance *inst, char *str){
jsval rval;
if(xjs_eval(inst, str, &rval))
return JSVAL_IS_VOID(rval)? NULL : JS_GetStringBytes(JS_ValueToString(inst->cx, rval));
else
return NULL;
}
struct xjs_instance *xjs_run(char *fname){
struct xjs_instance *inst;
char *name;
int x;
jsval rval;
JSScript *script;
for(name = fname, x = 0; fname[x]; x++)
if(fname[x] == '/')
name = fname + x + 1;
inst = xjs_init(name);
if(!(script = JS_CompileFile(inst->cx, inst->global, fname)))
return NULL;
else{
JS_ExecuteScript(inst->cx, inst->global, script, &rval);
JS_DestroyScript(inst->cx, script);
}
return inst;
}
int xcmd_js(char **word, char **word_eol, void *udata){
struct xjs_instance *instl;
int id;
static char fnbuf[1024], *t;
if(!strcasecmp(word[2], "l")){
xchat_printf(ph, " Ref# Name\n");
for(instl = xjs_instf; instl; instl = instl->next)
xchat_printf(ph, "%5i %s\n", instl->id, instl->name);
}else if(!strcasecmp(word[2], "e")){
if(t = xjs_eval2str(xjs_cmdline, word_eol[3]))
xchat_printf(ph, "%s\n", t);
}else if(!strcasecmp(word[2], "i") && *word[3] && *word_eol[4]){
id = atoi(word[3]);
for(instl = xjs_instf; instl; instl = instl->next)
if(instl->id == id)
break;
if(instl && instl->id == id){
if(t = xjs_eval2str(instl, word_eol[4]))
xchat_printf(ph, "%s\n", t);
}else
xchat_printf(ph, "Couldn't find context ID %i\n", id);
}else if(!strcasecmp(word[2], "o") && *word_eol[3]){
snprintf(fnbuf, 1024, "%s/xjs/%s", xchat_get_info(ph, "xchatdirfs"), word_eol[3]);
if(instl = xjs_run(fnbuf))
xchat_printf(ph, "Loaded %s with ID=%i\n", instl->name, instl->id);
else
xchat_printf(ph, "Failed to load %s\n", fnbuf);
}else if(!strcasecmp(word[2], "k") && *word[3]){
id = atoi(word[3]);
for(instl = xjs_instf; instl; instl = instl->next)
if(instl->id == id)
break;
if(instl && instl->id == id){
if(!JS_IsRunning(instl->cx)){
xchat_printf(ph, "Killing %s with ID=%i\n", instl->name, instl->id);
xjs_exit(instl, 0);
}else
xchat_printf(ph, "Cannot kill active context\n");
}
}else
xchat_print(ph, XCMD_JS_HELP);
return XCHAT_EAT_ALL;
}
void xchat_plugin_get_info(char **name, char **desc, char **version, void **reserved){
*name = PLUGNAME;
*desc = PLUGDESC;
*version = PLUGVER;
}
int xchat_plugin_init(xchat_plugin *xph, char **name, char **desc, char **version, char *arg){
ph = xph;
xchat_plugin_get_info(name, desc, version, NULL);
xchat_hook_command(ph, "JS", XCHAT_PRI_NORM, xcmd_js, XCMD_JS_HELP, NULL);
rt = JS_NewRuntime(0x100000);
xjs_cmdline = xjs_init("<commandline>");
xchat_print(ph, "XJS (JavaScript plugin) loaded!");
return 1;
}
int xchat_plugin_deinit(){
while(xjs_instf)
xjs_exit(xjs_instf, 1);
JS_DestroyRuntime(rt);
JS_ShutDown();
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment