From welshbyte at sucs.org Fri Mar 16 19:57:09 2018 From: welshbyte at sucs.org (Andrew Price) Date: Fri, 16 Mar 2018 19:57:09 +0000 Subject: [mw-devel] [Git][arthur/mw][master] New mwjs API Message-ID: <5aac21958d56_68b33f8ce4e3e620464b2@gitlab.mail> Andrew Price pushed to branch master at Justin Mitchell / mw Commits: 76808c88 by Andrew Price at 2018-03-16T19:53:08+00:00 New mwjs API - Better namespacing. - Useful message objects. - New generic event mechanism instead of just message received events. - mw.onevent array of function refs instead of weird bind() calls: > mw.onevent.push(myhandler); - Discoverable: > mw.print(Object.getOwnPropertyNames(mw)); onevent,print,exec,say,wholist,urlget,beep,input,termsize Example: function handler(ev) { if (ev.type == "message_received") { msg = ev.data; // Some fields only defined for certain message types mw.print("msg.text: " + msg.text); mw.print("msg.unixtime: " + msg.unixtime); mw.print("msg.serial: " + msg.serial); mw.print("msg.ipc_type: " + msg.ipc_type); mw.print("msg.from_name: " + msg.from_name); mw.print("msg.to_name: " + msg.to_name); mw.print("msg.type: " + msg.type); mw.print("msg.excluded_name: " + msg.excluded_name); mw.print("msg.suffix: " + msg.suffix); } } mw.onevent.push(handler); - - - - - 7 changed files: - + src/client/event.h - src/client/incoming.c - src/client/incoming.h - src/client/js-duk.c - src/client/js.h - src/ipc.c - src/ipc.h Changes: ===================================== src/client/event.h ===================================== --- /dev/null +++ b/src/client/event.h @@ -0,0 +1,18 @@ +#ifndef __EVENT_H__ +#define __EVENT_H__ + +#include + +typedef enum { + MWEV_TYPE_NONE = 0, + MWEV_TYPE_MSG, +} mwev_type_t; + +struct mwevent { + mwev_type_t ev_type; + union { + ipc_message_t *msg; /* MWEV_TYPE_MSG */ + } ev_data; +}; + +#endif /* __EVENT_H__ */ ===================================== src/client/incoming.c ===================================== --- a/src/client/incoming.c +++ b/src/client/incoming.c @@ -29,6 +29,8 @@ #include "util.h" #include "incoming.h" #include "gaglist.h" +#include "js.h" +#include "event.h" #include #include @@ -41,7 +43,6 @@ int mesg_waiting = 0; char *mrod_user=NULL; static int MesgStacked=0; -static int events_cancelled = 0; static struct mstack *MesgStack=NULL; static void accept_pipe_cmd(ipc_message_t *msg, struct user *mesg_user); @@ -64,38 +65,20 @@ static void force_protpower(char *text); #define _MIN(a,b) (anext; + new->next=NULL; - if (MesgStack==NULL) - { - MesgStack=new; - MesgStacked=1; - events_cancelled = 0; - }else - { - struct mstack *ptr; - ptr=MesgStack; - while (ptr->next!=NULL) ptr=ptr->next; - ptr->next=new; - /* Stack must be emptied before events can start again */ - if (events_cancelled) - ptr->flags &= ~MST_EVENT; - MesgStacked++; - } -} + new->msg = msg; + if (msg != NULL) + msg->refcount++; -void StackMesg(char *text, char *from, int flags) -{ - struct mstack *new; - new=(struct mstack *)malloc(sizeof(struct mstack)); - new->text=(char *)malloc(strlen(text)+1); - new->from=(char *)malloc(strlen(from)+1); - strcpy(new->text,text); - strcpy(new->from,from); - new->flags = flags; - new->preamble = 0; - InsertMesg(new); + *mp = new; + MesgStacked++; } static void StackEvent(char *text, char *from, int flags) @@ -108,7 +91,7 @@ static void StackEvent(char *text, char *from, int flags) strcpy(new->from,from); new->flags = MST_SCREV; new->preamble = flags; - InsertMesg(new); + InsertMesg(new, NULL); } void ClearStack(void) { @@ -118,6 +101,8 @@ void ClearStack(void) { while (MesgStack!=NULL) { old=MesgStack; MesgStack=old->next; + if (old->msg) + ipcmsg_destroy(old->msg); free(old->text); free(old->from); free(old); @@ -142,6 +127,11 @@ void DisplayStack(void) script_output=1; while ((event_name = NextLink(event_list, event_name)) != NULL) { + struct mwevent ev = { + .ev_type = MWEV_TYPE_MSG, + .ev_data.msg = new->msg, + }; + js_handle_event(&ev); ExecEvent(event_name, new->text, "text", new->from, new->preamble); } if (script_output) display_message(new->text, new->flags & MST_BEEP, 1); @@ -559,24 +549,11 @@ static void display_content(ipc_message_t *msg) snprintf(tb, len, "%s", text); } else if (strcmp(type, "emote")==0) { - int plural = json_getint(j, "plural"); - switch (plural) { - case 1: - snprintf(tb, len, "%s's %s", whom, text); - break; - case 2: - snprintf(tb, len, "%s' %s", whom, text); - break; - case 3: - snprintf(tb, len, "%s'd %s", whom, text); - break; - case 4: - snprintf(tb, len, "%s'll %s", whom, text); - break; - default: - snprintf(tb, len, "%s %s", whom, text); - break; - } + unsigned p = json_getint(j, "plural"); + + if (p >= plural_suffix_size()) + p = 0; + snprintf(tb, len, "%s%s %s", whom, plural_suffix[p], text); } else if (strcmp(type, "notsayto")==0) { const char *exclude = json_getstring(j, "exclude"); @@ -851,15 +828,19 @@ static void force_text(ipc_message_t *msg, const char *text, const char *from) mesg->text = strdup(tb); mesg->from = strdup(from); - /*printf("\n\n", from, text);*/ - InsertMesg(mesg); + InsertMesg(mesg, msg); } static void force_wiz(ipc_message_t *msg, char *newbuff, char *from) { char tb[MAXTEXTLENGTH]; - tb[0]=0; + struct mstack *mesg; + + mesg = malloc(sizeof(*mesg)); + mesg->flags = 0; + mesg->preamble = 0; + tb[0]=0; if (s_timestamp(user)) { time_t t; @@ -875,8 +856,10 @@ static void force_wiz(ipc_message_t *msg, char *newbuff, char *from) strncat(tb, "> ", MAXTEXTLENGTH - strlen(tb) - 1); } strncat(tb, newbuff, MAXTEXTLENGTH - strlen(tb) - 1); + mesg->text = strdup(tb); + mesg->from = strdup(from); - StackMesg(tb, from, 0); + InsertMesg(mesg, msg); } static void force_checkonoff(char *text, char *from) ===================================== src/client/incoming.h ===================================== --- a/src/client/incoming.h +++ b/src/client/incoming.h @@ -22,6 +22,8 @@ struct mstack int flags; int preamble; /* Extra chars added by global/timestamp/spy */ struct mstack *next; + ipc_message_t *msg; + struct user *sender; }; extern int new_mail_waiting; @@ -29,8 +31,6 @@ extern char *mrod_user; void incoming_mesg(int ignore); void handle_mesg(void); -void InsertMesg(struct mstack *mesg); -void StackMesg(char *text, char *from, int event); extern int MesgIsStacked(void); void DisplayStack(void); void ClearStack(void); ===================================== src/client/js-duk.c ===================================== --- a/src/client/js-duk.c +++ b/src/client/js-duk.c @@ -135,6 +135,70 @@ static char *cesu8_to_utf8(const char *cesu8) return utf8; } +static int js_push_username(int32_t id) +{ + struct user u; + int ret; + + ret = fetch_user(&u, id); + if (ret == 0) + duk_push_string(ctx, u.record.name); + else + duk_push_undefined(ctx); + return ret; +} + +static int js_push_ipcmsg_event(ipc_message_t *msg) +{ + duk_idx_t idx; + json_t *json; + json_t *val; + + /* Only support json-formatted messages */ + if (msg == NULL || msg->head.type <= 26) + return 1; + + json = ipcmsg_json_decode(msg); + if (json == NULL) { + fprintf(stderr, "mwjs error: failed to unmarshall message\n"); + return -1; + } + idx = duk_push_bare_object(ctx); /* msg object */ + + /* The message object will have common members which are set from the + * msg header and the rest (json encoded) will be dependent on the + * message type. + */ + duk_push_number(ctx, msg->head.when); + duk_put_prop_string(ctx, idx, "unixtime"); + duk_push_number(ctx, msg->head.serial); + duk_put_prop_string(ctx, idx, "serial"); + duk_push_string(ctx, ipc_nametype(msg->head.type)); + duk_put_prop_string(ctx, idx, "ipc_type"); + js_push_username(msg->head.src); + duk_put_prop_string(ctx, idx, "from_name"); + js_push_username(msg->head.dst); + duk_put_prop_string(ctx, idx, "to_name"); + + duk_push_string(ctx, json_getstring(json, "type")); + duk_put_prop_string(ctx, idx, "type"); + duk_push_string(ctx, json_getstring(json, "text")); + duk_put_prop_string(ctx, idx, "text"); + duk_push_string(ctx, json_getstring(json, "exclude")); + duk_put_prop_string(ctx, idx, "excluded_name"); + + val = json_object_get(json, "plural"); + if (val != NULL && json_is_integer(val)) { + unsigned p = json_integer_value(val); + + if (p >= plural_suffix_size()) + p = 0; + duk_push_string(ctx, plural_suffix[p]); + duk_put_prop_string(ctx, idx, "suffix"); + } + return 0; +} + static duk_ret_t js_print(duk_context *cx) { int argc = duk_get_top(cx); @@ -621,15 +685,9 @@ int js_isrunning(void) return (interrupt == 0 && timeout_event != NULL); } -int js_exec(char *name, int argc, const char **argv) +static int mwjs_call_fn(int argc) { duk_int_t ret; - int i; - - if (!duk_get_global_string(ctx, name) || !duk_is_function(ctx, -1)) - return 0; - for (i = 0; i < argc; i++) - duk_push_string(ctx, argv[i]); interrupt = 0; start_timeout(); @@ -649,6 +707,88 @@ int js_exec(char *name, int argc, const char **argv) script_output = 0; } duk_pop(ctx); + return (ret == DUK_EXEC_SUCCESS) ? 0 : -1; +} + +int js_exec(char *name, int argc, const char **argv) +{ + int i; + + if (!duk_get_global_string(ctx, name) || !duk_is_function(ctx, -1)) + return 0; + for (i = 0; i < argc; i++) + duk_push_string(ctx, argv[i]); + + (void)mwjs_call_fn(argc); + return 0; +} + +static int js_push_event(struct mwevent *ev) +{ + duk_idx_t idx = duk_push_bare_object(ctx); + int ret = -1; + + /* Although incoming IPC messages are the only kind of event we + * currently handle, the message is set as a member of an event object + * and the event handler is passed the event object. This allows us to + * extend this mechanism in future to handle other kinds of client + * events. + */ + switch (ev->ev_type) { + case MWEV_TYPE_MSG: + duk_push_string(ctx, "message_received"); + duk_put_prop_string(ctx, idx, "type"); + ret = js_push_ipcmsg_event(ev->ev_data.msg); + if (ret == 0) + duk_put_prop_string(ctx, idx, "data"); + break; + case MWEV_TYPE_NONE: + /* Fall through */ + default: + fprintf(stderr, "mwjs warning: Unhandled event type: %d\n", ev->ev_type); + duk_pop(ctx); + break; + } + if (ret != 0) + duk_pop(ctx); + return ret; +} + +int js_handle_event(struct mwevent *ev) +{ + unsigned len; + unsigned i; + + if (!duk_get_global_string(ctx, "mw")) { + fprintf(stderr, "mwjs error: failed to lookup mw namespace\n"); + return -1; + } + if (!duk_get_prop_string(ctx, -1, "onevent")) { + fprintf(stderr, "mwjs error: failed to lookup 'mw.onevent'\n"); + return -1; + } + if (!duk_is_array(ctx, -1)) { + fprintf(stderr, "mwjs error: 'mw.onevent' is not an array\n"); + return -1; + } + len = duk_get_length(ctx, -1); + if (len == 0) + return 0; + + for (i = 0; i < len; i++) { + duk_get_prop_index(ctx, -1, i); + if (!duk_is_function(ctx, -1)) { + duk_int_t t = duk_get_type(ctx, -1); + fprintf(stderr, "mwjs warning: mw.onevent[%u] is of type %d, " + "not a function\n", i, t); + continue; + } + if (js_push_event(ev) != 0) { + duk_pop(ctx); /* Pop the function */ + continue; + } + mwjs_call_fn(1); /* Pops the return value */ + } return 0; } @@ -798,6 +938,30 @@ static void define_constants(void) define_string("whoami", user->record.name); } +static void define_api(void) +{ + duk_push_string(ctx, "mw"); + duk_push_bare_object(ctx); + + /* mw.onevent = new Array(); */ + duk_push_string(ctx, "onevent"); + duk_push_array(ctx); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE); + + /* Define values under the 'mw' namespace */ + define_func("print", js_print, DUK_VARARGS); + define_func("exec", js_mwexec, 1); + define_func("say", js_say, 1); + define_func("wholist", js_wholist, 0); + define_func("urlget", js_urlget, 1); + define_func("beep", js_beep, 1); + define_func("input", js_input, 1); + define_func("termsize", js_termsize, 0); + + /* mw object */ + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE); +} + int setup_js(void) { int is_local = 1; @@ -810,6 +974,10 @@ int setup_js(void) return -1; duk_push_global_object(ctx); + /* New API, namespaced under 'mw' */ + define_api(); + + /* Old mwscripty API */ define_constants(); define_func("print", js_print, DUK_VARARGS); define_func("exec", js_mwexec, 1); ===================================== src/client/js.h ===================================== --- a/src/client/js.h +++ b/src/client/js.h @@ -2,6 +2,7 @@ #define JS_H #include +#include "event.h" int js_isrunning(void); int js_exec(char *name, int argc, const char **argvc); @@ -10,6 +11,7 @@ int is_js(char *name); void js_stop_execution(void); int stop_js(void); int setup_js(void); +int js_handle_event(struct mwevent *ev); enum bindtype { K_BIND = 0, ===================================== src/ipc.c ===================================== --- a/src/ipc.c +++ b/src/ipc.c @@ -16,6 +16,19 @@ char *ipc_host = NULL; char *ipc_port = NULL; struct user *ipc_user; +const char *_plural_suffix[] = { + [0] = "", + [1] = "'s", + [2] = "'", + [3] = "'d", + [4] = "'ll", +}; +const char **plural_suffix = _plural_suffix; +int plural_suffix_size(void) +{ + return sizeof(_plural_suffix) / sizeof(_plural_suffix[0]); +} + const char *get_nonce(void); int ipc_connected(void) ===================================== src/ipc.h ===================================== --- a/src/ipc.h +++ b/src/ipc.h @@ -76,4 +76,7 @@ void ipcmsg_transmit(ipc_message_t * msg); int msg_queue(ipc_message_t *msg, ipc_connection_t *conn); const char * ipc_nametype(enum ipc_types msgtype); +const char **plural_suffix; +int plural_suffix_size(void); + #endif View it on GitLab: https://projects.sucs.org/arthur/mw/commit/76808c8847eb4f404ab0c6dbb49ce53be2b74830 --- View it on GitLab: https://projects.sucs.org/arthur/mw/commit/76808c8847eb4f404ab0c6dbb49ce53be2b74830 You're receiving this email because of your account on projects.sucs.org. -------------- next part -------------- An HTML attachment was scrubbed... URL: